Skip to content

Commit

Permalink
(fix) Rtf editor markdown not working properly (#4445)
Browse files Browse the repository at this point in the history
* changes text editor to support markdown mode

* usage of text editor markdown instead of remark component

* fixes failing tests

* fixes linting issues

* fixes tests

* removes duplicates in text editor nodes

* handling the case of no description provided for markdown
  • Loading branch information
AimeurAmin authored Nov 20, 2024
1 parent c983b08 commit 52d7f58
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 46 deletions.
4 changes: 4 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { render } from '@testing-library/react';

import OutputDetailPage from '../OutputDetailPage';

beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation();
});
describe('OutputDetailPage', () => {
it('displays edit and duplicate buttons if user is administrator', () => {
const { queryByTitle } = render(
Expand Down
1 change: 1 addition & 0 deletions packages/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@babel/runtime-corejs3": "7.23.8",
"@emotion/react": "11.11.1",
"@emotion/serialize": "1.1.2",
"@lexical/code": "0.18.0",
"@lexical/link": "0.18.0",
"@lexical/list": "0.18.0",
"@lexical/markdown": "0.18.0",
Expand Down
123 changes: 85 additions & 38 deletions packages/react-components/src/atoms/TextEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
import { useEffect } from 'react';
import { css } from '@emotion/react';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { CodeNode } from '@lexical/code';
import { AutoLinkNode, LinkNode } from '@lexical/link';
import { ListItemNode, ListNode } from '@lexical/list';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { ListNode, ListItemNode } from '@lexical/list';
import { AutoLinkNode, LinkNode } from '@lexical/link';
import { HeadingNode } from '@lexical/rich-text';
import { EditorState } from 'lexical';
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import { useEffect } from 'react';

import {
$convertFromMarkdownString,
$convertToMarkdownString,
TRANSFORMERS,
} from '@lexical/markdown';
import ToolbarPlugin from './TextEditorToolbar';
import { useValidation, styles, validationMessageStyles } from '../form';
import { noop } from '../utils';
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin';
import { EditorState } from 'lexical';
import { ember } from '../colors';
import { styles, useValidation, validationMessageStyles } from '../form';
import { noop } from '../utils';
import ToolbarPlugin from './TextEditorToolbar';

const theme = {
paragraph: 'editor-paragraph',
Expand Down Expand Up @@ -149,6 +152,28 @@ const placeholderStyles = css({
pointerEvents: 'none',
});

const markdownStyles = css({
fontSize: '1em',

'& .editor-paragraph': {
margin: 0,
marginBottom: '16px',
position: 'relative',
'&:last-child': {
marginBottom: 0,
},
},

'& .editor-list-ol, & .editor-list-ul': {
margin: '0 0 16px 16px',
padding: 0,
},

'& .editor-listItem': {
margin: '8px 0',
},
});

const onChangeHandler = (
editorState: EditorState,
onChange: (content: string) => void,
Expand All @@ -162,7 +187,8 @@ export type TextEditorProps = {
readonly maxLength?: number;
readonly value: string;
readonly enabled?: boolean;
onChange: (content: string) => void;
readonly isMarkdown?: boolean;
onChange?: (content: string) => void;
};

const EnablePlugin = ({ enabled }: { enabled: boolean }) => {
Expand All @@ -182,31 +208,46 @@ const TextEditor = ({
customValidationMessage = '',
enabled = true,
getValidationMessage,
isMarkdown = false,
}: TextEditorProps) => {
const { validationMessage, validationTargetProps } =
useValidation<HTMLTextAreaElement>(
customValidationMessage,
getValidationMessage,
isMarkdown,
);

const initialConfig = {
editorState: () => {
$convertFromMarkdownString(value, TRANSFORMERS);
},
namespace: 'Editor',
nodes: [AutoLinkNode, LinkNode, ListNode, ListItemNode, HeadingNode],
nodes: [
AutoLinkNode,
LinkNode,
ListNode,
ListItemNode,
HeadingNode,
QuoteNode,
CodeNode,
],
theme,
// eslint-disable-next-line no-console
onError: console.error,
};

if (isMarkdown && !value) return <></>;

return (
<LexicalComposer initialConfig={initialConfig}>
<div css={containerStyles}>
<ToolbarPlugin />
<OnChangePlugin
onChange={(editorState) => onChangeHandler(editorState, onChange)}
/>
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
{!isMarkdown && <ToolbarPlugin />}
{onChange && (
<OnChangePlugin
onChange={(editorState) => onChangeHandler(editorState, onChange)}
/>
)}
<EnablePlugin enabled={enabled} />
<div css={innerStyles}>
<RichTextPlugin
Expand All @@ -215,18 +256,22 @@ const TextEditor = ({
id={id}
required={required}
data-testid={'editor'}
css={({ colors }) => [
styles,
inputStyles,
validationMessage && {
borderColor: ember.rgb,
},
colors?.primary500 && {
':focus': {
borderColor: colors?.primary500.rgba,
},
},
]}
css={({ colors }) =>
!isMarkdown
? [
styles,
inputStyles,
validationMessage && {
borderColor: ember.rgb,
},
colors?.primary500 && {
':focus': {
borderColor: colors?.primary500.rgba,
},
},
]
: [markdownStyles]
}
/>
}
placeholder={
Expand All @@ -244,14 +289,16 @@ const TextEditor = ({
}
ErrorBoundary={LexicalErrorBoundary}
/>
<textarea
{...validationTargetProps}
onChange={noop}
css={{ display: 'none' }}
required={required}
maxLength={maxLength}
value={value}
/>
{!isMarkdown && (
<textarea
{...validationTargetProps}
onChange={noop}
css={{ display: 'none' }}
required={required}
maxLength={maxLength}
value={value}
/>
)}
<ListPlugin />
<HistoryPlugin />
<AutoFocusPlugin />
Expand Down
13 changes: 8 additions & 5 deletions packages/react-components/src/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type ValidationTarget =
export function useValidation<T extends ValidationTarget>(
customValidationMessage: string,
getValidationMessage?: (validityState: ValidityState) => string | undefined,
skipValidation: boolean = false,
): {
validationMessage: string;
validationTargetProps: {
Expand All @@ -76,18 +77,20 @@ export function useValidation<T extends ValidationTarget>(
const [validationMessage, setValidationMessage] = useState('');
useEffect(() => {
const input = inputRef.current!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
input.setCustomValidity(customValidationMessage);
if (!skipValidation) input.setCustomValidity(customValidationMessage);

if (validationMessage || customValidationMessage) {
if ((validationMessage || customValidationMessage) && !skipValidation) {
setValidationMessage(customValidationMessage);
input.reportValidity();
}

return () => input.setCustomValidity('');
}, [customValidationMessage, validationMessage]);
return () => {
!skipValidation && input.setCustomValidity('');
};
}, [customValidationMessage, validationMessage, skipValidation]);

const validate = () => {
if (inputRef.current) {
if (inputRef.current && !skipValidation) {
const inputField = inputRef.current;
const inputFieldValidity = inputField.validity.valid;
setValidationMessage(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ResearchOutputResponse } from '@asap-hub/model';

import { Card, Divider, Headline2, Markdown, Paragraph } from '../atoms';
import { RichText, TagList } from '..';
import { Card, Divider, Headline2, Paragraph, TextEditor } from '../atoms';
import { perRem } from '../pixels';

type SharedResearchDetailsTagsCardProps = Pick<
Expand All @@ -19,7 +19,12 @@ const SharedResearchDetailsTagsCard: React.FC<
{displayDescription && (
<div css={{ paddingBottom: `${12 / perRem}em` }}>
<Headline2 noMargin>Description</Headline2>
<Markdown value={descriptionMD}></Markdown>
<TextEditor
id="description"
value={descriptionMD}
enabled={false}
isMarkdown
></TextEditor>
{descriptionMD === '' && <RichText poorText text={description} />}
</div>
)}
Expand All @@ -43,5 +48,4 @@ const SharedResearchDetailsTagsCard: React.FC<
)}
</Card>
);

export default SharedResearchDetailsTagsCard;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ const sharedResearchDetailsTagsCardProps: ComponentProps<
displayDescription: true,
tags: [],
};

beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation();
});

it('renders only description section if there are no tags', () => {
const { getByText, queryByRole } = render(
<SharedResearchDetailsTagsCard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const props: ComponentProps<typeof SharedResearchOutput> = {
onPublish: jest.fn(() => Promise.resolve()),
};

beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation();
});

describe('Grant Documents', () => {
it('renders an output with title and content', () => {
const { getByText } = render(
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,7 @@ __metadata:
"@emotion/jest": ^11.10.5
"@emotion/react": 11.11.1
"@emotion/serialize": 1.1.2
"@lexical/code": 0.18.0
"@lexical/link": 0.18.0
"@lexical/list": 0.18.0
"@lexical/markdown": 0.18.0
Expand Down

0 comments on commit 52d7f58

Please sign in to comment.