Skip to content

CRDCDH-3760 Implement Rich Text Editor and add to SRF review comments#40

Open
Alejandro-Vega wants to merge 61 commits into
3.7.0from
CRDCDH-3760
Open

CRDCDH-3760 Implement Rich Text Editor and add to SRF review comments#40
Alejandro-Vega wants to merge 61 commits into
3.7.0from
CRDCDH-3760

Conversation

@Alejandro-Vega

@Alejandro-Vega Alejandro-Vega commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Overview

Created a Rich Text Editor component and replaced existing review comment input for both approval and inquire dialogs.

Change Details (Specifics)

  • Created a reusable Rich Text Editor input component that provides a toolbar for formatting, keyboard shortcuts, and text modifications within the textarea
  • The editor uses markdown and sends markdown to API
  • Created a reusable Rich Text viewer to ensure consistent view of the rich text markdown value
  • Added slate packages to assist managing WYSIWYG behavior
  • Updated Approve/Inquire modals in the review page within an SRF to include the rich text editor
  • Updated "View Comments" dialog to show the formatted content. Couldn't find another area where this is shown

Related Ticket(s)

CRDCDH-3659 (US)
CRDCDH-3760 (Task)

@relativeci

relativeci Bot commented Jun 10, 2026

Copy link
Copy Markdown

#144 Bundle Size — 12.86MiB (+3.42%).

3a9ea21(current) vs a8ce1bb 3.7.0#141(baseline)

Important

Bundle introduced 4 and removed 1 duplicate packages – View changed duplicate packages

Warning

Bundle introduced 22 new packages: direction, compute-scroll-into-view, scroll-into-view-if-needed and 19 more – View changed packages

Bundle metrics  Change 7 changes Regression 3 regressions
                 Current
#144
     Baseline
#141
Regression  Initial JS 876.35KiB(~+0.01%) 876.35KiB
No change  Initial CSS 0B 0B
Change  Cache Invalidation 28.67% 6.13%
Change  Chunks 74(+4.23%) 71
Change  Assets 117(+2.63%) 114
Change  Modules 9157(+1.72%) 9002
No change  Duplicate Modules 0 0
No change  Duplicate Code 0% 0%
Regression  Packages 320(+8.47%) 295
Regression  Duplicate Packages 53(+6%) 50
Bundle size by type  Change 1 change Regression 1 regression
                 Current
#144
     Baseline
#141
Regression  JS 10.34MiB (+4.29%) 9.91MiB
No change  IMG 1.72MiB 1.72MiB
No change  Fonts 805.07KiB 805.07KiB
No change  CSS 18KiB 18KiB

Bundle analysis reportBranch CRDCDH-3760Project dashboard


Generated by RelativeCIDocumentationReport issue

@Alejandro-Vega Alejandro-Vega marked this pull request as ready for review June 11, 2026 07:03
@Alejandro-Vega Alejandro-Vega requested review from a team and Copilot June 11, 2026 07:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new Slate-based Rich Text Editor (storing a constrained markdown subset) and updates SRF review comment entry/display to use rich text instead of a plain textarea.

Changes:

  • Added RichTextEditor component (toolbar, Slate controller/hook, markdown serialize/deserialize, keyboard handlers) plus unit/accessibility tests and Storybook stories.
  • Added RichTextViewer to render stored markdown in the review comments dialog.
  • Updated review dialogs/tests and added frontend dependencies (slate*, rehype-raw) to support the editor/viewer.

Reviewed changes

Copilot reviewed 46 out of 47 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/frontend/src/content/questionnaire/FormView.test.tsx Mocks the new RichTextEditor for FormView tests.
apps/frontend/src/config/EditorConfig.ts Defines mark/block formats (bold/italic + lists) used by the editor.
apps/frontend/src/components/RichTextViewer/index.tsx New markdown viewer for rendering stored rich text in the UI.
apps/frontend/src/components/RichTextViewer/index.test.tsx Viewer accessibility/basic rendering tests.
apps/frontend/src/components/RichTextViewer/index.stories.tsx Storybook stories for viewer formatting/edge cases.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownUtils.ts Markdown line-ending normalization + list-line parsing helpers.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownUtils.test.ts Unit tests for markdown utils.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownSerializer.ts Slate → markdown serialization and plain-text-length computation.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownSerializer.test.ts Unit tests for serializer/plain-text-length.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownInlineParser.ts Inline markdown → Slate text node parsing for supported marks.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownInlineParser.test.ts Unit tests for inline parser.
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownDeserializer.ts Markdown → Slate document deserialization (paragraphs/lists).
apps/frontend/src/components/RichTextEditor/utils/markdown/markdownDeserializer.test.ts Unit tests for deserializer.
apps/frontend/src/components/RichTextEditor/utils/keyboard/keyboardSelectionUtils.ts Keyboard selection helpers for indentation/list behaviors.
apps/frontend/src/components/RichTextEditor/utils/keyboard/keyboardSelectionUtils.test.ts Unit tests for keyboard selection helpers.
apps/frontend/src/components/RichTextEditor/utils/keyboard/keyboardListHandlers.ts Tab/Enter/Backspace/list-shortcut handlers for editor UX.
apps/frontend/src/components/RichTextEditor/utils/keyboard/keyboardListHandlers.test.ts Unit tests for list/keyboard handlers.
apps/frontend/src/components/RichTextEditor/utils/keyboard/keyboardHotkeyHandlers.ts Ctrl/Cmd hotkeys for undo/redo and enabled marks.
apps/frontend/src/components/RichTextEditor/utils/keyboard/keyboardHotkeyHandlers.test.ts Unit tests for hotkey handlers.
apps/frontend/src/components/RichTextEditor/utils/index.ts Barrel exports for editor utilities.
apps/frontend/src/components/RichTextEditor/utils/editorTransforms.ts Slate transforms for mark/block toggling and list wrapping.
apps/frontend/src/components/RichTextEditor/utils/editorTransforms.test.ts Unit tests for editor transforms.
apps/frontend/src/components/RichTextEditor/utils/editorGuards.ts Type guards for list formats/elements.
apps/frontend/src/components/RichTextEditor/utils/editorGuards.test.ts Unit tests for editor guards.
apps/frontend/src/components/RichTextEditor/utils/documentUtils.ts Helpers for creating/normalizing Slate documents and emptiness checks.
apps/frontend/src/components/RichTextEditor/utils/documentUtils.test.ts Unit tests for document utils.
apps/frontend/src/components/RichTextEditor/types.ts Shared editor types (marks, blocks, Slate custom types).
apps/frontend/src/components/RichTextEditor/ToolbarButton.tsx Toolbar icon-button component.
apps/frontend/src/components/RichTextEditor/ToolbarButton.test.tsx ToolbarButton accessibility/basic tests.
apps/frontend/src/components/RichTextEditor/Toolbar.tsx Editor toolbar (marks, lists, undo/redo).
apps/frontend/src/components/RichTextEditor/Toolbar.test.tsx Toolbar rendering + interaction tests.
apps/frontend/src/components/RichTextEditor/index.tsx Lazy-loaded editor entrypoint + exports.
apps/frontend/src/components/RichTextEditor/index.test.tsx Editor wrapper accessibility/basic tests.
apps/frontend/src/components/RichTextEditor/index.stories.tsx Storybook stories for editor usage and states.
apps/frontend/src/components/RichTextEditor/hooks/useRichTextEditor.tsx Hook owning editor instance, serialization, handlers, reset.
apps/frontend/src/components/RichTextEditor/hooks/useRichTextEditor.test.tsx Hook unit tests (serialization calls, reset behavior).
apps/frontend/src/components/RichTextEditor/EditorLeaf.tsx Renders marked inline leaves using configured HTML tags.
apps/frontend/src/components/RichTextEditor/EditorLeaf.test.tsx EditorLeaf accessibility/basic tests.
apps/frontend/src/components/RichTextEditor/EditorElement.tsx Renders block elements (p/ul/ol/li).
apps/frontend/src/components/RichTextEditor/EditorElement.test.tsx EditorElement accessibility/basic tests.
apps/frontend/src/components/RichTextEditor/Controller.tsx Slate controller wiring toolbar + editable area + imperative reset.
apps/frontend/src/components/RichTextEditor/Controller.test.tsx Controller behavior tests (toolbar toggle, aria/readOnly).
apps/frontend/src/components/ReviewCommentsDialog/index.tsx Uses RichTextViewer to display stored review comments.
apps/frontend/src/components/Questionnaire/ReviewFormDialog.tsx Replaces textarea with RichTextEditor + markdown-based validation.
apps/frontend/src/components/Questionnaire/ReviewFormDialog.test.tsx Updates dialog tests to mock the new editor and validate behavior.
apps/frontend/package.json Adds dependencies for viewer/editor (rehype-raw, slate*).
apps/frontend/package-lock.json Lockfile updates for new dependencies/transitives.
Files not reviewed (1)
  • apps/frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/frontend/src/components/RichTextViewer/index.tsx

@amattu2 amattu2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall there's really not that many issues. I left some comments to consider.

Some comments were written before you made some additional commits, so if they're outdated, just ignore them.

</Suspense>
));

RichTextEditor.displayName = "RichTextEditor";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both this component and the controller have the same displayName. Is that intentional?

Comment on lines +32 to +39
format: "underline",
tooltip: "Underline (Ctrl+U)",
icon: FormatUnderlinedIcon,
enabled: false,
hotkey: "u",
htmlTag: "u",
markdownSyntax: ["<u>", "</u>"],
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove?

value={field.value}
onChange={field.onChange}
onTextLengthChange={setPlainTextLength}
placeholder={`${reviewCommentLimitLabel} characters allowed`}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The placeholder itself is getting formatted.

Placeholder-Formatting-Issue.png

import { useRichTextEditor } from "./hooks/useRichTextEditor";
import Toolbar from "./Toolbar";

const StyledEditorWrapper = styled(Box)({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The background of the editor itself probably should be white, not transparent.

Textarea-Background-Color.png

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or a slightly off-white is fine too. Whatever is consistent with the rest of the app.

"&:hover": {
borderColor: "#6B7294",
},
"&:focus-within": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not be an issue, but when focusing on the textarea, the box shadow is applied to the entire text editor, rather than just the textarea itself. I'm not sure that there's a better alternative, as it would look weird if the box shadow was between the textarea and the toolbar. It might be worth just confirming that what we've done here is consistent with other WYSIWYG editors.

Focus-Highlighting.png

* Owns the Slate editor instance, initial value, serialization, render callbacks,
* and keyboard handler for the rich text editor component.
*/
export const useRichTextEditor = ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exact same text produces different counts between the old text editor and the markdown editor. Can this be safely mitigated?

Text-Count-Issue.png

className?: string;
};

const RichTextViewer = ({ content, className }: Props): ReactElement | null => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a challenging issue because of the nature of markdown, but single dashes become bullet points, and triple dashes disappear.

Any chance we can fix this?

Conversion-Of-Dashes.png

}

return (
<StyledMarkdown

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether this can be fixed, but HTML is just stripped out completely. We obviously don't want to render it, but can we still visually preserve the content without treating it as markdown+HTML?

I also noticed here that the x2 newlines between the headings gets removed. This is probably not a deal breaker, just pointing it out.

Heading-Spacing-Missing-Link.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants