@squiz/formatted-text-editor 1.5.1-alpha.6 → 1.12.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +6 -1
- package/CHANGELOG.md +41 -1
- package/README.md +29 -4
- package/build.js +11 -0
- package/cypress/e2e/bold.spec.cy.ts +18 -0
- package/cypress/global.d.ts +9 -0
- package/cypress/support/commands.ts +130 -0
- package/cypress/support/e2e.ts +20 -0
- package/cypress/tsconfig.json +8 -0
- package/cypress.config.ts +7 -0
- package/{src → demo}/App.tsx +1 -1
- package/{index.html → demo/index.html} +1 -1
- package/demo/index.scss +11 -0
- package/demo/main.tsx +10 -0
- package/{lib → demo/public}/favicon-dxp.svg +0 -0
- package/{src → demo}/vite-env.d.ts +0 -0
- package/file-transformer.js +1 -0
- package/jest.config.ts +27 -0
- package/lib/Editor/Editor.d.ts +6 -0
- package/lib/Editor/Editor.js +26 -0
- package/lib/EditorToolbar/EditorToolbar.d.ts +7 -0
- package/lib/EditorToolbar/EditorToolbar.js +21 -0
- package/lib/EditorToolbar/Tools/Bold/BoldButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/Bold/BoldButton.js +17 -0
- package/lib/EditorToolbar/Tools/Italic/ItalicButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/Italic/ItalicButton.js +17 -0
- package/lib/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.js +17 -0
- package/lib/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.js +17 -0
- package/lib/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.js +17 -0
- package/lib/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.js +17 -0
- package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.d.ts +2 -0
- package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.js +16 -0
- package/lib/EditorToolbar/Tools/Underline/UnderlineButton.d.ts +2 -0
- package/lib/EditorToolbar/Tools/Underline/UnderlineButton.js +17 -0
- package/lib/FormattedTextEditor.d.ts +2 -0
- package/lib/FormattedTextEditor.js +7 -0
- package/lib/index.css +3849 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -36344
- package/lib/ui/ToolbarButton/ToolbarButton.d.ts +10 -0
- package/lib/ui/ToolbarButton/ToolbarButton.js +5 -0
- package/package.json +47 -30
- package/src/Editor/Editor.spec.tsx +88 -0
- package/src/Editor/Editor.tsx +44 -0
- package/src/Editor/editor.scss +36 -0
- package/src/EditorToolbar/EditorToolbar.tsx +40 -0
- package/src/EditorToolbar/Tools/Bold/BoldButton.spec.tsx +19 -0
- package/src/EditorToolbar/Tools/Bold/BoldButton.tsx +30 -0
- package/src/EditorToolbar/Tools/Italic/ItalicButton.spec.tsx +19 -0
- package/src/EditorToolbar/Tools/Italic/ItalicButton.tsx +30 -0
- package/src/EditorToolbar/Tools/TextAlign/CenterAlign/CenterAlignButton.tsx +31 -0
- package/src/EditorToolbar/Tools/TextAlign/JustifyAlign/JustifyAlignButton.tsx +31 -0
- package/src/EditorToolbar/Tools/TextAlign/LeftAlign/LeftAlignButton.tsx +31 -0
- package/src/EditorToolbar/Tools/TextAlign/RightAlign/RightAlignButton.tsx +31 -0
- package/src/EditorToolbar/Tools/TextAlign/TextAlignButtons.tsx +21 -0
- package/src/EditorToolbar/Tools/Underline/Underline.spec.tsx +19 -0
- package/src/EditorToolbar/Tools/Underline/UnderlineButton.tsx +30 -0
- package/src/EditorToolbar/editor-toolbar.scss +26 -0
- package/src/FormattedTextEditor.tsx +4 -17
- package/src/index.scss +4 -0
- package/src/index.ts +3 -0
- package/src/ui/ToolbarButton/ToolbarButton.tsx +25 -0
- package/src/ui/ToolbarButton/toolbar-button.scss +40 -0
- package/tsconfig.json +5 -5
- package/vite.config.ts +3 -9
- package/lib/style.css +0 -1
- package/public/favicon-dxp.svg +0 -3
- package/src/Menu.tsx +0 -19
- package/src/Toolbar.tsx +0 -10
- package/src/index.css +0 -3
- package/src/main.tsx +0 -13
- package/src/toolbar.css +0 -9
- package/tsconfig.node.json +0 -9
@@ -0,0 +1,10 @@
|
|
1
|
+
import { ReactElement } from 'react';
|
2
|
+
type ToolbarButtonProps = {
|
3
|
+
handleOnClick: () => void;
|
4
|
+
isDisabled: boolean;
|
5
|
+
isActive: boolean;
|
6
|
+
icon: ReactElement;
|
7
|
+
label: string;
|
8
|
+
};
|
9
|
+
declare const ToolbarButton: ({ handleOnClick, isDisabled, isActive, icon, label }: ToolbarButtonProps) => JSX.Element;
|
10
|
+
export default ToolbarButton;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
const ToolbarButton = ({ handleOnClick, isDisabled, isActive, icon, label }) => {
|
3
|
+
return (React.createElement("button", { "aria-label": label, title: label, type: "button", onClick: handleOnClick, disabled: isDisabled, className: `btn toolbar-button ${isActive ? 'is-active' : ''}` }, icon));
|
4
|
+
};
|
5
|
+
export default ToolbarButton;
|
package/package.json
CHANGED
@@ -1,42 +1,59 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/formatted-text-editor",
|
3
|
-
"
|
4
|
-
"version": "1.5.1-alpha.6",
|
3
|
+
"version": "1.12.0-alpha.14",
|
5
4
|
"main": "lib/index.js",
|
5
|
+
"types": "lib/index.d.ts",
|
6
6
|
"scripts": {
|
7
7
|
"dev": "vite",
|
8
|
-
"
|
8
|
+
"clean": "rimraf ./lib",
|
9
|
+
"compile": "npm run clean && npm run compile:styles && npm run compile:code",
|
10
|
+
"compile:code": "tsc",
|
11
|
+
"compile:styles": "node build.js",
|
9
12
|
"preview": "vite preview",
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
13
|
-
|
14
|
-
"husky": {
|
15
|
-
"hooks": {
|
16
|
-
"pre-commit": "pretty-quick --staged"
|
17
|
-
}
|
13
|
+
"build": "vite build",
|
14
|
+
"test": "jest",
|
15
|
+
"test:watch": "jest --watch",
|
16
|
+
"test:e2e": "vite build && vite preview --port 8080 & cypress open"
|
18
17
|
},
|
19
18
|
"dependencies": {
|
20
|
-
"@
|
21
|
-
"@remirror/react": "
|
22
|
-
"eslint-config-prettier": "^8.6.0",
|
23
|
-
"eslint-plugin-jsx-a11y": "^6.7.1",
|
24
|
-
"eslint-plugin-react": "^7.32.2",
|
25
|
-
"react": "^16.14.0",
|
26
|
-
"react-dom": "^16.14.0",
|
27
|
-
"remirror": "^2.0.24"
|
19
|
+
"@mui/icons-material": "5.11.0",
|
20
|
+
"@remirror/react": "2.0.25"
|
28
21
|
},
|
29
22
|
"devDependencies": {
|
30
|
-
"@
|
31
|
-
"@
|
32
|
-
"@
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
23
|
+
"@testing-library/cypress": "9.0.0",
|
24
|
+
"@testing-library/jest-dom": "5.16.5",
|
25
|
+
"@testing-library/react": "12.1.5",
|
26
|
+
"@types/react": "18.0.26",
|
27
|
+
"@types/react-dom": "18.0.9",
|
28
|
+
"@vitejs/plugin-react": "3.0.0",
|
29
|
+
"cypress": "12.5.1",
|
30
|
+
"eslint-config-prettier": "8.6.0",
|
31
|
+
"eslint-plugin-cypress": "2.12.1",
|
32
|
+
"eslint-plugin-jsx-a11y": "6.7.1",
|
33
|
+
"eslint-plugin-react": "7.32.2",
|
34
|
+
"jest-environment-jsdom": "29.4.1",
|
35
|
+
"react": "18.2.0",
|
36
|
+
"react-dom": "18.2.0",
|
37
|
+
"rimraf": "4.1.2",
|
38
|
+
"typescript": "4.9.3",
|
39
|
+
"vite": "4.0.0"
|
40
|
+
},
|
41
|
+
"peerDependencies": {
|
42
|
+
"@types/react": "^16.14.0 || ^17 || ^18",
|
43
|
+
"@types/react-dom": "^16.9.0 || ^17 || ^18",
|
44
|
+
"react": "^16.14.0 || ^17 || ^18",
|
45
|
+
"react-dom": "^16.14.0 || ^17 || ^18"
|
46
|
+
},
|
47
|
+
"peerDependenciesMeta": {
|
48
|
+
"@types/react": {
|
49
|
+
"optional": true
|
50
|
+
},
|
51
|
+
"@types/react-dom": {
|
52
|
+
"optional": true
|
53
|
+
}
|
38
54
|
},
|
39
55
|
"volta": {
|
40
|
-
"node": "
|
41
|
-
}
|
42
|
-
|
56
|
+
"node": "16.19.0"
|
57
|
+
},
|
58
|
+
"gitHead": "76fa4ca4f3c3fa3b0a808232474df640550871cc"
|
59
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import '@testing-library/jest-dom';
|
3
|
+
import Editor from './Editor';
|
4
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
5
|
+
|
6
|
+
describe('Formatted text editor', () => {
|
7
|
+
it('Renders the text editor', () => {
|
8
|
+
render(<Editor />);
|
9
|
+
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
10
|
+
});
|
11
|
+
|
12
|
+
it('Renders the placeholder if there is no content', () => {
|
13
|
+
render(<Editor />);
|
14
|
+
expect(document.querySelector(`[data-placeholder='Write something']`)).toBeInTheDocument();
|
15
|
+
});
|
16
|
+
|
17
|
+
it('Renders the bold button', () => {
|
18
|
+
render(<Editor />);
|
19
|
+
expect(screen.getByRole('button', { name: 'Bold (cmd+B)' })).toBeInTheDocument();
|
20
|
+
});
|
21
|
+
|
22
|
+
it('Renders the italic button', () => {
|
23
|
+
render(<Editor />);
|
24
|
+
expect(screen.getByRole('button', { name: 'Italic (cmd+I)' })).toBeInTheDocument();
|
25
|
+
});
|
26
|
+
|
27
|
+
it('Renders the underline button', () => {
|
28
|
+
render(<Editor />);
|
29
|
+
expect(screen.getByRole('button', { name: 'Underline (cmd+U)' })).toBeInTheDocument();
|
30
|
+
});
|
31
|
+
|
32
|
+
it('Renders the align left button', () => {
|
33
|
+
render(<Editor />);
|
34
|
+
expect(screen.getByRole('button', { name: 'Align left' })).toBeInTheDocument();
|
35
|
+
});
|
36
|
+
|
37
|
+
it('Applies left alignment styling to text when clicked', () => {
|
38
|
+
const { baseElement } = render(<Editor />);
|
39
|
+
expect(baseElement).toBeTruthy();
|
40
|
+
|
41
|
+
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeFalsy();
|
42
|
+
|
43
|
+
const leftAlignButton = baseElement.querySelector('button[title="Align left"]') as HTMLButtonElement;
|
44
|
+
expect(leftAlignButton).toBeTruthy();
|
45
|
+
|
46
|
+
fireEvent.click(leftAlignButton);
|
47
|
+
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeTruthy();
|
48
|
+
});
|
49
|
+
|
50
|
+
it('Applies center alignment styling to text when clicked', () => {
|
51
|
+
const { baseElement } = render(<Editor />);
|
52
|
+
expect(baseElement).toBeTruthy();
|
53
|
+
|
54
|
+
expect(baseElement.querySelector('p[data-node-text-align="center"]')).toBeFalsy();
|
55
|
+
|
56
|
+
const centerAlignButton = baseElement.querySelector('button[title="Align center"]') as HTMLButtonElement;
|
57
|
+
expect(centerAlignButton).toBeTruthy();
|
58
|
+
|
59
|
+
fireEvent.click(centerAlignButton);
|
60
|
+
expect(baseElement.querySelector('p[data-node-text-align="center"]')).toBeTruthy();
|
61
|
+
});
|
62
|
+
|
63
|
+
it('Applies right alignment styling to text when clicked', () => {
|
64
|
+
const { baseElement } = render(<Editor />);
|
65
|
+
expect(baseElement).toBeTruthy();
|
66
|
+
|
67
|
+
expect(baseElement.querySelector('p[data-node-text-align="right"]')).toBeFalsy();
|
68
|
+
|
69
|
+
const rightAlignButton = baseElement.querySelector('button[title="Align right"]') as HTMLButtonElement;
|
70
|
+
expect(rightAlignButton).toBeTruthy();
|
71
|
+
|
72
|
+
fireEvent.click(rightAlignButton);
|
73
|
+
expect(baseElement.querySelector('p[data-node-text-align="right"]')).toBeTruthy();
|
74
|
+
});
|
75
|
+
|
76
|
+
it('Applies justify alignment styling to text when clicked', () => {
|
77
|
+
const { baseElement } = render(<Editor />);
|
78
|
+
expect(baseElement).toBeTruthy();
|
79
|
+
|
80
|
+
expect(baseElement.querySelector('p[data-node-text-align="justify"]')).toBeFalsy();
|
81
|
+
|
82
|
+
const justifyAlignButton = baseElement.querySelector('button[title="Justify"]') as HTMLButtonElement;
|
83
|
+
expect(justifyAlignButton).toBeTruthy();
|
84
|
+
|
85
|
+
fireEvent.click(justifyAlignButton);
|
86
|
+
expect(baseElement.querySelector('p[data-node-text-align="justify"]')).toBeTruthy();
|
87
|
+
});
|
88
|
+
});
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import {
|
3
|
+
BoldExtension,
|
4
|
+
ItalicExtension,
|
5
|
+
NodeFormattingExtension,
|
6
|
+
UnderlineExtension,
|
7
|
+
wysiwygPreset,
|
8
|
+
} from 'remirror/extensions';
|
9
|
+
import { EditorComponent, Remirror, useRemirror } from '@remirror/react';
|
10
|
+
import { RemirrorContentType, Extension } from '@remirror/core';
|
11
|
+
import { EditorToolbar } from '../EditorToolbar/EditorToolbar';
|
12
|
+
|
13
|
+
type EditorProps = {
|
14
|
+
content?: RemirrorContentType;
|
15
|
+
};
|
16
|
+
|
17
|
+
const Editor = ({ content }: EditorProps) => {
|
18
|
+
const { manager, state, setState } = useRemirror({
|
19
|
+
extensions: () => [
|
20
|
+
...(wysiwygPreset() as Extension[]),
|
21
|
+
new BoldExtension(),
|
22
|
+
new ItalicExtension(),
|
23
|
+
new NodeFormattingExtension(),
|
24
|
+
new UnderlineExtension(),
|
25
|
+
],
|
26
|
+
content,
|
27
|
+
selection: 'start',
|
28
|
+
stringHandler: 'html',
|
29
|
+
});
|
30
|
+
|
31
|
+
const handleChange = (parameter: { state: any }) => {
|
32
|
+
setState(parameter.state);
|
33
|
+
};
|
34
|
+
|
35
|
+
return (
|
36
|
+
<Remirror manager={manager} state={state} onChange={handleChange} placeholder="Write something">
|
37
|
+
<EditorToolbar manager={manager} />
|
38
|
+
<EditorComponent />
|
39
|
+
<EditorToolbar manager={manager} isPopover />
|
40
|
+
</Remirror>
|
41
|
+
);
|
42
|
+
};
|
43
|
+
|
44
|
+
export default Editor;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
.formatted-text-editor {
|
2
|
+
&.editor-wrapper {
|
3
|
+
background: white;
|
4
|
+
border: 2px solid var(--grey-300);
|
5
|
+
border-radius: 4px;
|
6
|
+
}
|
7
|
+
|
8
|
+
.remirror-editor-wrapper {
|
9
|
+
padding-top: 0;
|
10
|
+
color: var(--grey-600);
|
11
|
+
}
|
12
|
+
|
13
|
+
.remirror-editor {
|
14
|
+
box-shadow: none;
|
15
|
+
background: white;
|
16
|
+
border-radius: 0 0 4px 4px;
|
17
|
+
|
18
|
+
&:active,
|
19
|
+
:focus {
|
20
|
+
/* some focus state */
|
21
|
+
}
|
22
|
+
|
23
|
+
p {
|
24
|
+
/* Make sure content aligned with "text-align: justify" is justified */
|
25
|
+
display: block;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
/* remove existing style */
|
31
|
+
.remirror-theme .ProseMirror {
|
32
|
+
&:active,
|
33
|
+
&:focus {
|
34
|
+
box-shadow: none;
|
35
|
+
}
|
36
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Toolbar, FloatingToolbar } from '@remirror/react-components';
|
3
|
+
import { RemirrorManager } from 'remirror';
|
4
|
+
import ItalicButton from './Tools/Italic/ItalicButton';
|
5
|
+
import UnderlineButton from './Tools/Underline/UnderlineButton';
|
6
|
+
import BoldButton from './Tools/Bold/BoldButton';
|
7
|
+
import TextAlignButtons from './Tools/TextAlign/TextAlignButtons';
|
8
|
+
|
9
|
+
type EditorToolbarProps = {
|
10
|
+
manager: RemirrorManager<any>;
|
11
|
+
isPopover?: boolean;
|
12
|
+
};
|
13
|
+
|
14
|
+
// The editor main toolbar
|
15
|
+
export const EditorToolbar = ({ manager, isPopover }: EditorToolbarProps) => {
|
16
|
+
const extensionNames: Record<string, true> = {};
|
17
|
+
|
18
|
+
manager.extensions.forEach((extension) => {
|
19
|
+
extensionNames[extension.name] = true;
|
20
|
+
});
|
21
|
+
|
22
|
+
return (
|
23
|
+
<>
|
24
|
+
{!isPopover ? (
|
25
|
+
<Toolbar className="remirror-toolbar editor-toolbar">
|
26
|
+
{extensionNames.bold && <BoldButton />}
|
27
|
+
{extensionNames.italic && <ItalicButton />}
|
28
|
+
{extensionNames.underline && <UnderlineButton />}
|
29
|
+
{extensionNames.nodeFormatting && <TextAlignButtons />}
|
30
|
+
</Toolbar>
|
31
|
+
) : (
|
32
|
+
<FloatingToolbar className="remirror-floating-popover">
|
33
|
+
{extensionNames.bold && <BoldButton />}
|
34
|
+
{extensionNames.italic && <ItalicButton />}
|
35
|
+
{extensionNames.underline && <UnderlineButton />}
|
36
|
+
</FloatingToolbar>
|
37
|
+
)}
|
38
|
+
</>
|
39
|
+
);
|
40
|
+
};
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import '@testing-library/jest-dom';
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
4
|
+
import Editor from '../../../Editor/Editor';
|
5
|
+
|
6
|
+
describe('Bold button', () => {
|
7
|
+
it('Renders the bold button', () => {
|
8
|
+
render(<Editor />);
|
9
|
+
expect(screen.getByRole('button', { name: 'Bold (cmd+B)' })).toBeInTheDocument();
|
10
|
+
});
|
11
|
+
|
12
|
+
it('Activates the button if clicked', () => {
|
13
|
+
render(<Editor />);
|
14
|
+
expect(screen.getByRole('button', { name: 'Bold (cmd+B)' }).classList.contains('btn')).toBeTruthy();
|
15
|
+
const bold = screen.getByRole('button', { name: 'Bold (cmd+B)' });
|
16
|
+
fireEvent.click(bold);
|
17
|
+
expect(bold.classList.contains('is-active')).toBeTruthy();
|
18
|
+
});
|
19
|
+
});
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useActive, useChainedCommands } from '@remirror/react';
|
3
|
+
import { BoldExtension } from '@remirror/extension-bold';
|
4
|
+
import ToolbarButton from '../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatBoldRoundedIcon from '@mui/icons-material/FormatBoldRounded';
|
6
|
+
|
7
|
+
const BoldButton = () => {
|
8
|
+
const { toggleBold } = useCommands();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const active = useActive<BoldExtension>();
|
12
|
+
const enabled = toggleBold.enabled();
|
13
|
+
const handleSelect = () => {
|
14
|
+
if (toggleBold.enabled()) {
|
15
|
+
chain.toggleBold().focus().run();
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
return (
|
20
|
+
<ToolbarButton
|
21
|
+
handleOnClick={handleSelect}
|
22
|
+
isDisabled={!enabled}
|
23
|
+
isActive={active.bold()}
|
24
|
+
icon={<FormatBoldRoundedIcon />}
|
25
|
+
label="Bold (cmd+B)"
|
26
|
+
/>
|
27
|
+
);
|
28
|
+
};
|
29
|
+
|
30
|
+
export default BoldButton;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import '@testing-library/jest-dom';
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
4
|
+
import Editor from '../../../Editor/Editor';
|
5
|
+
|
6
|
+
describe('Italic button', () => {
|
7
|
+
it('Renders the italic button', () => {
|
8
|
+
render(<Editor />);
|
9
|
+
expect(screen.getByRole('button', { name: 'Italic (cmd+I)' })).toBeInTheDocument();
|
10
|
+
});
|
11
|
+
|
12
|
+
it('Activates the button if clicked', () => {
|
13
|
+
render(<Editor />);
|
14
|
+
expect(screen.getByRole('button', { name: 'Italic (cmd+I)' }).classList.contains('btn')).toBeTruthy();
|
15
|
+
const italic = screen.getByRole('button', { name: 'Italic (cmd+I)' });
|
16
|
+
fireEvent.click(italic);
|
17
|
+
expect(italic.classList.contains('is-active')).toBeTruthy();
|
18
|
+
});
|
19
|
+
});
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useActive, useChainedCommands } from '@remirror/react';
|
3
|
+
import { ItalicExtension } from '@remirror/extension-italic';
|
4
|
+
import ToolbarButton from '../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatItalicRoundedIcon from '@mui/icons-material/FormatItalicRounded';
|
6
|
+
|
7
|
+
const ItalicButton = () => {
|
8
|
+
const { toggleItalic } = useCommands();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const active = useActive<ItalicExtension>();
|
12
|
+
const enabled = toggleItalic.enabled();
|
13
|
+
const handleSelect = () => {
|
14
|
+
if (toggleItalic.enabled()) {
|
15
|
+
chain.toggleItalic().focus().run();
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
return (
|
20
|
+
<ToolbarButton
|
21
|
+
handleOnClick={handleSelect}
|
22
|
+
isDisabled={!enabled}
|
23
|
+
isActive={active.italic()}
|
24
|
+
icon={<FormatItalicRoundedIcon />}
|
25
|
+
label="Italic (cmd+I)"
|
26
|
+
/>
|
27
|
+
);
|
28
|
+
};
|
29
|
+
|
30
|
+
export default ItalicButton;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useChainedCommands } from '@remirror/react';
|
3
|
+
import { NodeFormattingExtension } from '@remirror/extension-node-formatting';
|
4
|
+
import ToolbarButton from '../../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatAlignCenterIcon from '@mui/icons-material/FormatAlignCenter';
|
6
|
+
|
7
|
+
const CenterAlignButton = () => {
|
8
|
+
const { centerAlign } = useCommands<NodeFormattingExtension>();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const handleSelect = () => {
|
12
|
+
if (centerAlign.enabled()) {
|
13
|
+
chain.centerAlign().focus().run();
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
const active = centerAlign.active?.() || false;
|
18
|
+
const enabled = centerAlign.enabled();
|
19
|
+
|
20
|
+
return (
|
21
|
+
<ToolbarButton
|
22
|
+
handleOnClick={handleSelect}
|
23
|
+
isDisabled={!enabled}
|
24
|
+
isActive={active}
|
25
|
+
icon={<FormatAlignCenterIcon />}
|
26
|
+
label="Align center"
|
27
|
+
/>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default CenterAlignButton;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useChainedCommands } from '@remirror/react';
|
3
|
+
import { NodeFormattingExtension } from '@remirror/extension-node-formatting';
|
4
|
+
import ToolbarButton from '../../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatAlignJustifyIcon from '@mui/icons-material/FormatAlignJustify';
|
6
|
+
|
7
|
+
const JustifyAlignButton = () => {
|
8
|
+
const { justifyAlign } = useCommands<NodeFormattingExtension>();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const handleSelect = () => {
|
12
|
+
if (justifyAlign.enabled()) {
|
13
|
+
chain.justifyAlign().focus().run();
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
const active = justifyAlign.active?.() || false;
|
18
|
+
const enabled = justifyAlign.enabled();
|
19
|
+
|
20
|
+
return (
|
21
|
+
<ToolbarButton
|
22
|
+
handleOnClick={handleSelect}
|
23
|
+
isDisabled={!enabled}
|
24
|
+
isActive={active}
|
25
|
+
icon={<FormatAlignJustifyIcon />}
|
26
|
+
label="Justify"
|
27
|
+
/>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default JustifyAlignButton;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useChainedCommands } from '@remirror/react';
|
3
|
+
import { NodeFormattingExtension } from '@remirror/extension-node-formatting';
|
4
|
+
import ToolbarButton from '../../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatAlignLeftIcon from '@mui/icons-material/FormatAlignLeft';
|
6
|
+
|
7
|
+
const LeftAlignButton = () => {
|
8
|
+
const { leftAlign } = useCommands<NodeFormattingExtension>();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const handleSelect = () => {
|
12
|
+
if (leftAlign.enabled()) {
|
13
|
+
chain.leftAlign().focus().run();
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
const active = leftAlign.active?.() || false;
|
18
|
+
const enabled = leftAlign.enabled();
|
19
|
+
|
20
|
+
return (
|
21
|
+
<ToolbarButton
|
22
|
+
handleOnClick={handleSelect}
|
23
|
+
isDisabled={!enabled}
|
24
|
+
isActive={active}
|
25
|
+
icon={<FormatAlignLeftIcon />}
|
26
|
+
label="Align left"
|
27
|
+
/>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default LeftAlignButton;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useChainedCommands } from '@remirror/react';
|
3
|
+
import { NodeFormattingExtension } from '@remirror/extension-node-formatting';
|
4
|
+
import ToolbarButton from '../../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatAlignRightIcon from '@mui/icons-material/FormatAlignRight';
|
6
|
+
|
7
|
+
const RightAlignButton = () => {
|
8
|
+
const { rightAlign } = useCommands<NodeFormattingExtension>();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const handleSelect = () => {
|
12
|
+
if (rightAlign.enabled()) {
|
13
|
+
chain.rightAlign().focus().run();
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
const active = rightAlign.active?.() || false;
|
18
|
+
const enabled = rightAlign.enabled();
|
19
|
+
|
20
|
+
return (
|
21
|
+
<ToolbarButton
|
22
|
+
handleOnClick={handleSelect}
|
23
|
+
isDisabled={!enabled}
|
24
|
+
isActive={active}
|
25
|
+
icon={<FormatAlignRightIcon />}
|
26
|
+
label="Align right"
|
27
|
+
/>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default RightAlignButton;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import LeftAlignButton from './LeftAlign/LeftAlignButton';
|
3
|
+
import CenterAlignButton from './CenterAlign/CenterAlignButton';
|
4
|
+
import RightAlignButton from './RightAlign/RightAlignButton';
|
5
|
+
import JustifyAlignButton from './JustifyAlign/JustifyAlignButton';
|
6
|
+
import { VerticalDivider } from '@remirror/react-components';
|
7
|
+
|
8
|
+
const TextAlignButtons = () => {
|
9
|
+
return (
|
10
|
+
<>
|
11
|
+
<VerticalDivider className="editor-divider" />
|
12
|
+
<LeftAlignButton />
|
13
|
+
<CenterAlignButton />
|
14
|
+
<RightAlignButton />
|
15
|
+
<JustifyAlignButton />
|
16
|
+
<VerticalDivider className="editor-divider" />
|
17
|
+
</>
|
18
|
+
);
|
19
|
+
};
|
20
|
+
|
21
|
+
export default TextAlignButtons;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import '@testing-library/jest-dom';
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
4
|
+
import Editor from '../../../Editor/Editor';
|
5
|
+
|
6
|
+
describe('Underline button', () => {
|
7
|
+
it('Renders the underline button', () => {
|
8
|
+
render(<Editor />);
|
9
|
+
expect(screen.getByRole('button', { name: 'Underline (cmd+U)' })).toBeInTheDocument();
|
10
|
+
});
|
11
|
+
|
12
|
+
it('Activates the button if clicked', () => {
|
13
|
+
render(<Editor />);
|
14
|
+
expect(screen.getByRole('button', { name: 'Underline (cmd+U)' }).classList.contains('btn')).toBeTruthy();
|
15
|
+
const underline = screen.getByRole('button', { name: 'Underline (cmd+U)' });
|
16
|
+
fireEvent.click(underline);
|
17
|
+
expect(underline.classList.contains('is-active')).toBeTruthy();
|
18
|
+
});
|
19
|
+
});
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useCommands, useActive, useChainedCommands } from '@remirror/react';
|
3
|
+
import { UnderlineExtension } from '@remirror/extension-underline';
|
4
|
+
import ToolbarButton from '../../../ui/ToolbarButton/ToolbarButton';
|
5
|
+
import FormatUnderlinedRoundedIcon from '@mui/icons-material/FormatUnderlinedRounded';
|
6
|
+
|
7
|
+
const UnderlineButton = () => {
|
8
|
+
const { toggleUnderline } = useCommands();
|
9
|
+
const chain = useChainedCommands();
|
10
|
+
|
11
|
+
const active = useActive<UnderlineExtension>();
|
12
|
+
const enabled = toggleUnderline.enabled();
|
13
|
+
const handleSelect = () => {
|
14
|
+
if (toggleUnderline.enabled()) {
|
15
|
+
chain.toggleUnderline().focus().run();
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
return (
|
20
|
+
<ToolbarButton
|
21
|
+
handleOnClick={handleSelect}
|
22
|
+
isDisabled={!enabled}
|
23
|
+
isActive={active.underline()}
|
24
|
+
icon={<FormatUnderlinedRoundedIcon />}
|
25
|
+
label="Underline (cmd+U)"
|
26
|
+
/>
|
27
|
+
);
|
28
|
+
};
|
29
|
+
|
30
|
+
export default UnderlineButton;
|
@@ -0,0 +1,26 @@
|
|
1
|
+
.formatted-text-editor .editor-toolbar {
|
2
|
+
border-bottom: 2px solid var(--grey-200);
|
3
|
+
background-color: white;
|
4
|
+
padding: 0.25rem;
|
5
|
+
border-radius: 0.25rem;
|
6
|
+
|
7
|
+
display: flex;
|
8
|
+
justify-items: center;
|
9
|
+
|
10
|
+
> *:not(:first-child) {
|
11
|
+
margin: 0 0 0 2px;
|
12
|
+
}
|
13
|
+
|
14
|
+
.editor-divider {
|
15
|
+
margin: -4px 2px -4px 4px;
|
16
|
+
border-width: 1px;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
.remirror-floating-popover {
|
21
|
+
padding: 4px;
|
22
|
+
border: 1px var(--grey-200);
|
23
|
+
border-radius: 6px;
|
24
|
+
background-color: white;
|
25
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04), 0 1px 12px 4px rgba(0, 0, 0, 0.12);
|
26
|
+
}
|