Skip to content

Commit

Permalink
Merge pull request #2742 from glific/migration-from-draft-js
Browse files Browse the repository at this point in the history
migration-from-draft-js
  • Loading branch information
mdshamoon authored Feb 28, 2024
2 parents 16a4a3e + 0756adf commit dd5f286
Show file tree
Hide file tree
Showing 38 changed files with 1,018 additions and 581 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [master]
pull_request:
branches: [master]
branches: [master, migration-from-draft-js]

jobs:
glific:
Expand Down Expand Up @@ -95,6 +95,9 @@ jobs:
echo clone cypress repo
git clone https://github.com/glific/cypress-testing.git
echo done. go to dir.
cd cypress-testing
git checkout lexical-editor
cd ..
cp -r cypress-testing/cypress cypress
yarn add cypress
echo Create cypress.config.ts from example
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
"@appsignal/plugin-window-events": "^1.0.20",
"@appsignal/react": "^1.0.23",
"@date-io/dayjs": "^3.0.0",
"@draft-js-plugins/editor": "^4.1.4",
"@draft-js-plugins/mention": "^5.2.2",
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@glific/flow-editor": "^1.19.3-5",
"@lexical/react": "^0.13.1",
"@mui/icons-material": "^5.15.6",
"@mui/material": "^5.15.6",
"@mui/x-date-pickers": "^6.19.2",
Expand All @@ -32,7 +31,6 @@
"axios": "^1.6.7",
"buffer": "^6.0.3",
"dayjs": "^1.11.10",
"draft-js": "^0.11.7",
"emoji-mart": "^5.5.2",
"formik": "^2.4.5",
"graphql": "^16.8.1",
Expand All @@ -41,6 +39,8 @@
"i18next-browser-languagedetector": "^7.2.0",
"interweave": "^13.1.0",
"interweave-autolink": "^5.1.1",
"lexical": "^0.13.1",
"lexical-beautiful-mentions": "^0.1.30",
"pino": "^8.17.2",
"pino-logflare": "^0.4.2",
"react": "^18.2.0",
Expand Down
1 change: 1 addition & 0 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'mocks/matchMediaMock';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { waitFor, render } from '@testing-library/react';
Expand Down
20 changes: 20 additions & 0 deletions src/common/LexicalWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { BeautifulMentionNode } from 'lexical-beautiful-mentions';

interface LexicalWrapperProps {
children: any;
}

export const LexicalWrapper = ({ children }: LexicalWrapperProps) => {
return (
<LexicalComposer
initialConfig={{
namespace: 'template-input',
onError: (error) => console.log(error),
nodes: [BeautifulMentionNode],
}}
>
{children}
</LexicalComposer>
);
};
29 changes: 23 additions & 6 deletions src/common/RichEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EditorState, ContentState } from 'draft-js';
import CallIcon from '@mui/icons-material/Call';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { Interweave } from 'interweave';
Expand All @@ -8,12 +7,30 @@ import { UrlMatcher } from 'interweave-autolink';
const regexForLink =
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/gi;

// Convert Draft.js to WhatsApp message format.
export const getPlainTextFromEditor = (editorState: any) =>
editorState.getCurrentContent().getPlainText();
export const handleFormatterEvents = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.code === 'KeyB') {
return 'bold';
} else if ((event.ctrlKey || event.metaKey) && event.code === 'KeyI') {
return 'italic';
} else if ((event.ctrlKey || event.metaKey) && event.code === 'KeyS') {
return 'strikethrough';
}

return '';
};

export const getEditorFromContent = (text: string) =>
EditorState.createWithContent(ContentState.createFromText(text));
export const handleFormatting = (text: string, formatter: string) => {
switch (formatter) {
case 'bold':
return `*${text}*`;
case 'italic':
return `_${text}_`;
case 'strikethrough':
return `~${text}~`;
default:
return text;
}
};

const isAlphanumeric = (c: any) => {
const x = c.charCodeAt();
Expand Down
12 changes: 7 additions & 5 deletions src/components/UI/EmojiPicker/EmojiPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ export interface EmojiPickerProps {
onEmojiSelect: Function;
}

export const EmojiPicker = ({ displayStyle, onEmojiSelect }: EmojiPickerProps) => (
<div data-testid="emoji-container" style={displayStyle}>
<Picker data={data} onEmojiSelect={onEmojiSelect} />
</div>
);
export const EmojiPicker = ({ displayStyle, onEmojiSelect }: EmojiPickerProps) => {
return (
<div data-testid="emoji-container" style={displayStyle}>
<Picker data={data} onEmojiSelect={onEmojiSelect} />
</div>
);
};

export default EmojiPicker;
160 changes: 160 additions & 0 deletions src/components/UI/Form/EmojiInput/Editor.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
.HelperText {
margin-left: 16px !important;
color: #93a29b !important;
line-height: 1 !important;
font-size: 12px !important;
margin-top: 4px !important;
}

.Editor {
position: relative;
border: 2px solid rgba(0, 0, 0, 0.23);
border-radius: 12px;
height: 10rem;
position: relative;
padding: 1rem;
position: relative;
display: block;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}

.Label {
font-size: 16px;
font-weight: 500;
color: #93a29b !important;
}

.DangerText {
margin-left: 16px !important;
color: #fb5c5c;
}

.EditorInput {
resize: none;
font-size: 16px;
width: 100%;
min-height: 40px;
height: 100%;
margin-top: 4px;
position: relative;
tab-size: 1;
outline: 0;
}

.EditorInput p {
overflow: scroll !important;
margin: 0 !important;
}

.EditorWrapper {
margin: 1rem 0;
}

.editorScroller {
border: 0;
display: flex;
position: relative;
outline: 0;
z-index: 0;
height: 100%;
overflow: auto;
}

.disabled {
position: relative;
border: 2px solid rgba(0, 0, 0, 0.23);
border-radius: 12px;
height: 10rem;
position: relative;
padding: 1rem;
position: relative;
display: block;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
color: rgba(0, 0, 0, 0.38);
}

.editor {
flex: auto;
position: relative;
resize: vertical;
z-index: -1;
}

.editorPlaceholder {
color: #999;
overflow: hidden;
position: absolute;
text-overflow: ellipsis;
top: 1px;
left: 14px;
font-size: 16px;
user-select: none;
display: inline-block;
pointer-events: none;
}

/*
Overriding lexical mentions styles as per requirement
*/
.MentionMenu {
padding: 0;
background: #fff;
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3);
border-radius: 8px;
list-style: none;
margin: 0;
max-height: 200px;
overflow-y: scroll;
-ms-overflow-style: none;
width: 250px;
scrollbar-width: none;
}

.MentionMenu ul li {
margin: 0;
min-width: 180px;
font-size: 14px;
outline: none;
cursor: pointer;
border-radius: 8px;
}

.Selected {
background: #eee !important;
}

.MentionMenu li {
padding: 8px;
color: #050505;
cursor: pointer;
line-height: 16px;
font-size: 15px;
display: flex;
align-content: center;
flex-direction: row;
flex-shrink: 0;
background-color: #fff;
border-radius: 8px;
border: 0;
}

.MentionMenu li.active {
display: flex;
width: 20px;
height: 20px;
background-size: contain;
}

.MentionMenu li:first-child {
border-radius: 8px 8px 0px 0px;
}

.MentionMenu li:last-child {
border-radius: 0px 0px 8px 8px;
}

.MentionMenu li:hover {
background-color: #eee;
}
31 changes: 31 additions & 0 deletions src/components/UI/Form/EmojiInput/Editor.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'mocks/matchMediaMock';
import { render } from '@testing-library/react';
import { Editor } from './Editor';
import { LexicalWrapper } from 'common/LexicalWrapper';

const mockIntersectionObserver = class {
constructor() {}
observe() {}
unobserve() {}
disconnect() {}
};

const lexicalChange = vi.fn;

(window as any).IntersectionObserver = mockIntersectionObserver;

const wrapper = (
<LexicalWrapper>
<Editor
isEditing={false}
field={{ name: 'body', value: '', onBlur: () => {} }}
placeholder={''}
onChange={lexicalChange}
/>
</LexicalWrapper>
);

it('should render lexical editor', () => {
const { getByTestId } = render(wrapper);
expect(getByTestId('editor-body')).toBeInTheDocument();
});
Loading

0 comments on commit dd5f286

Please sign in to comment.