@squiz/formatted-text-editor 1.67.0 → 1.68.0
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/CHANGELOG.md +14 -0
- package/cypress/e2e/bold.spec.cy.ts +2 -2
- package/lib/EditorToolbar/Tools/Bold/BoldButton.js +2 -1
- package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.js +2 -1
- package/lib/EditorToolbar/Tools/Image/ImageButton.js +2 -1
- package/lib/EditorToolbar/Tools/Italic/ItalicButton.js +2 -1
- package/lib/EditorToolbar/Tools/Link/LinkButton.js +2 -1
- package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.js +2 -1
- package/lib/EditorToolbar/Tools/Redo/RedoButton.js +2 -1
- package/lib/EditorToolbar/Tools/Underline/UnderlineButton.js +2 -1
- package/lib/EditorToolbar/Tools/Undo/UndoButton.js +2 -1
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +4 -2
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +1 -1
- package/lib/utils/getShortcutSymbol.d.ts +1 -0
- package/lib/utils/getShortcutSymbol.js +8 -0
- package/package.json +3 -3
- package/src/Editor/Editor.spec.tsx +8 -8
- package/src/EditorToolbar/FloatingToolbar.spec.tsx +5 -5
- package/src/EditorToolbar/Tools/Bold/BoldButton.spec.tsx +3 -3
- package/src/EditorToolbar/Tools/Bold/BoldButton.tsx +2 -1
- package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.spec.tsx +2 -2
- package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.tsx +4 -1
- package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +1 -1
- package/src/EditorToolbar/Tools/Image/ImageButton.tsx +2 -1
- package/src/EditorToolbar/Tools/Italic/ItalicButton.spec.tsx +3 -3
- package/src/EditorToolbar/Tools/Italic/ItalicButton.tsx +2 -1
- package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +1 -1
- package/src/EditorToolbar/Tools/Link/LinkButton.tsx +2 -1
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +5 -5
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx +2 -1
- package/src/EditorToolbar/Tools/Redo/RedoButton.spec.tsx +6 -6
- package/src/EditorToolbar/Tools/Redo/RedoButton.tsx +2 -1
- package/src/EditorToolbar/Tools/Underline/Underline.spec.tsx +3 -3
- package/src/EditorToolbar/Tools/Underline/UnderlineButton.tsx +2 -1
- package/src/EditorToolbar/Tools/Undo/UndoButton.spec.tsx +5 -5
- package/src/EditorToolbar/Tools/Undo/UndoButton.tsx +2 -1
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +7 -3
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +1 -1
- package/src/utils/getShortcutSymbol.spec.ts +27 -0
- package/src/utils/getShortcutSymbol.ts +4 -0
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.68.0
|
4
|
+
|
5
|
+
### Minor Changes
|
6
|
+
|
7
|
+
- 4d3dbba: Updated FTE shortcuts to change tooltips depending on OS
|
8
|
+
|
9
|
+
## 1.67.1
|
10
|
+
|
11
|
+
### Patch Changes
|
12
|
+
|
13
|
+
- 928cc88: Made children optional for formatted text nodes/tags
|
14
|
+
- Updated dependencies [928cc88]
|
15
|
+
- @squiz/dx-json-schema-lib@1.65.1
|
16
|
+
|
3
17
|
## 1.67.0
|
4
18
|
|
5
19
|
### Minor Changes
|
@@ -7,11 +7,11 @@ describe('The formatted text editor renders', () => {
|
|
7
7
|
.type('It was the best of times it was the worst of times.')
|
8
8
|
.setSelection('best of times');
|
9
9
|
|
10
|
-
cy.findAllByRole('button', { name: 'Bold (
|
10
|
+
cy.findAllByRole('button', { name: 'Bold (Ctrl+B)' }).first().click();
|
11
11
|
|
12
12
|
cy.get('strong').should('include.text', 'best of times');
|
13
13
|
|
14
|
-
cy.findAllByRole('button', { name: 'Bold (
|
14
|
+
cy.findAllByRole('button', { name: 'Bold (Ctrl+B)' }).first().click();
|
15
15
|
|
16
16
|
cy.get('strong').should('not.exist');
|
17
17
|
});
|
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_2 = require("@remirror/react");
|
8
8
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
9
|
const FormatBoldRounded_1 = __importDefault(require("@mui/icons-material/FormatBoldRounded"));
|
10
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
10
11
|
const BoldButton = () => {
|
11
12
|
const { toggleBold } = (0, react_2.useCommands)();
|
12
13
|
const chain = (0, react_2.useChainedCommands)();
|
@@ -17,6 +18,6 @@ const BoldButton = () => {
|
|
17
18
|
chain.toggleBold().focus().run();
|
18
19
|
}
|
19
20
|
};
|
20
|
-
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.bold(), icon: react_1.default.createElement(FormatBoldRounded_1.default, null), label:
|
21
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.bold(), icon: react_1.default.createElement(FormatBoldRounded_1.default, null), label: `Bold (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+B)` }));
|
21
22
|
};
|
22
23
|
exports.default = BoldButton;
|
@@ -31,6 +31,7 @@ const react_2 = require("@remirror/react");
|
|
31
31
|
const react_components_1 = require("@remirror/react-components");
|
32
32
|
const FormatClearRounded_1 = __importDefault(require("@mui/icons-material/FormatClearRounded"));
|
33
33
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
34
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
34
35
|
const ClearFormattingButton = () => {
|
35
36
|
const { clearFormatting } = (0, react_2.useCommands)();
|
36
37
|
const { selection } = (0, react_2.useEditorState)();
|
@@ -51,6 +52,6 @@ const ClearFormattingButton = () => {
|
|
51
52
|
(0, react_2.useKeymap)('Mod-\\', handleShortcut);
|
52
53
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
53
54
|
react_1.default.createElement(react_components_1.VerticalDivider, null),
|
54
|
-
react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: false, isActive: false, icon: react_1.default.createElement(FormatClearRounded_1.default, null), label: `${contentSelected ? 'Clear formatting from selection' : 'Clear all formatting'} (
|
55
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: false, isActive: false, icon: react_1.default.createElement(FormatClearRounded_1.default, null), label: `${contentSelected ? 'Clear formatting from selection' : 'Clear all formatting'} (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+\\)` })));
|
55
56
|
};
|
56
57
|
exports.default = ClearFormattingButton;
|
@@ -32,6 +32,7 @@ const ImageRounded_1 = __importDefault(require("@mui/icons-material/ImageRounded
|
|
32
32
|
const ImageModal_1 = __importDefault(require("./ImageModal"));
|
33
33
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
34
34
|
const Extensions_1 = require("../../../Extensions/Extensions");
|
35
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
35
36
|
const ImageButton = ({ inPopover = false }) => {
|
36
37
|
const [showModal, setShowModal] = (0, react_1.useState)(false);
|
37
38
|
const { insertImage, insertAssetImage } = (0, react_2.useCommands)();
|
@@ -69,7 +70,7 @@ const ImageButton = ({ inPopover = false }) => {
|
|
69
70
|
(0, react_2.useKeymap)('Mod-l', disabled ? () => false : handleShortcut);
|
70
71
|
}
|
71
72
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
72
|
-
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.image() || active.assetImage(), icon: react_1.default.createElement(ImageRounded_1.default, null), label:
|
73
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.image() || active.assetImage(), icon: react_1.default.createElement(ImageRounded_1.default, null), label: `Image (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+L)`, isDisabled: disabled }),
|
73
74
|
showModal && react_1.default.createElement(ImageModal_1.default, { onCancel: () => setShowModal(false), onSubmit: handleSubmit })));
|
74
75
|
};
|
75
76
|
exports.default = ImageButton;
|
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_2 = require("@remirror/react");
|
8
8
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
9
|
const FormatItalicRounded_1 = __importDefault(require("@mui/icons-material/FormatItalicRounded"));
|
10
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
10
11
|
const ItalicButton = () => {
|
11
12
|
const { toggleItalic } = (0, react_2.useCommands)();
|
12
13
|
const chain = (0, react_2.useChainedCommands)();
|
@@ -17,6 +18,6 @@ const ItalicButton = () => {
|
|
17
18
|
chain.toggleItalic().focus().run();
|
18
19
|
}
|
19
20
|
};
|
20
|
-
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.italic(), icon: react_1.default.createElement(FormatItalicRounded_1.default, null), label:
|
21
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.italic(), icon: react_1.default.createElement(FormatItalicRounded_1.default, null), label: `Italic (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+I)` }));
|
21
22
|
};
|
22
23
|
exports.default = ItalicButton;
|
@@ -32,6 +32,7 @@ const LinkModal_1 = __importDefault(require("./LinkModal"));
|
|
32
32
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
33
33
|
const react_2 = require("@remirror/react");
|
34
34
|
const Extensions_1 = require("../../../Extensions/Extensions");
|
35
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
35
36
|
const LinkButton = ({ inPopover = false }) => {
|
36
37
|
const [showModal, setShowModal] = (0, react_1.useState)(false);
|
37
38
|
const { updateLink, updateAssetLink } = (0, react_2.useCommands)();
|
@@ -64,7 +65,7 @@ const LinkButton = ({ inPopover = false }) => {
|
|
64
65
|
(0, react_2.useKeymap)('Mod-k', disabled ? () => true : handleShortcut);
|
65
66
|
}
|
66
67
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
67
|
-
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.link() || active.assetLink(), icon: react_1.default.createElement(InsertLinkRounded_1.default, null), label:
|
68
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.link() || active.assetLink(), icon: react_1.default.createElement(InsertLinkRounded_1.default, null), label: `Link (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+K)`, isDisabled: disabled }),
|
68
69
|
showModal && react_1.default.createElement(LinkModal_1.default, { onCancel: () => setShowModal(false), onSubmit: handleSubmit })));
|
69
70
|
};
|
70
71
|
exports.default = LinkButton;
|
@@ -30,6 +30,7 @@ const react_1 = __importStar(require("react"));
|
|
30
30
|
const react_2 = require("@remirror/react");
|
31
31
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
32
32
|
const LinkOff_1 = __importDefault(require("@mui/icons-material/LinkOff"));
|
33
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
33
34
|
const RemoveLinkButton = ({ inPopover = false }) => {
|
34
35
|
const chain = (0, react_2.useChainedCommands)();
|
35
36
|
const active = (0, react_2.useActive)();
|
@@ -48,6 +49,6 @@ const RemoveLinkButton = ({ inPopover = false }) => {
|
|
48
49
|
// disable the shortcut if the button is disabled
|
49
50
|
(0, react_2.useKeymap)('Shift-Mod-k', disabled ? () => true : handleShortcut);
|
50
51
|
}
|
51
|
-
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: false, isDisabled: disabled, icon: react_1.default.createElement(LinkOff_1.default, null), label:
|
52
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: false, isDisabled: disabled, icon: react_1.default.createElement(LinkOff_1.default, null), label: `Remove link (Shift+${(0, getShortcutSymbol_1.getShortcutSymbol)()}+K)` }));
|
52
53
|
};
|
53
54
|
exports.default = RemoveLinkButton;
|
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_2 = require("@remirror/react");
|
8
8
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
9
|
const RedoRounded_1 = __importDefault(require("@mui/icons-material/RedoRounded"));
|
10
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
10
11
|
const RedoButton = () => {
|
11
12
|
const { redo } = (0, react_2.useCommands)();
|
12
13
|
const { redoDepth } = (0, react_2.useHelpers)(true);
|
@@ -16,6 +17,6 @@ const RedoButton = () => {
|
|
16
17
|
}
|
17
18
|
};
|
18
19
|
const enabled = redoDepth() > 0;
|
19
|
-
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: false, icon: react_1.default.createElement(RedoRounded_1.default, null), label:
|
20
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: false, icon: react_1.default.createElement(RedoRounded_1.default, null), label: `Redo (Shift+${(0, getShortcutSymbol_1.getShortcutSymbol)()}+Z)` }));
|
20
21
|
};
|
21
22
|
exports.default = RedoButton;
|
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_2 = require("@remirror/react");
|
8
8
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
9
|
const FormatUnderlinedRounded_1 = __importDefault(require("@mui/icons-material/FormatUnderlinedRounded"));
|
10
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
10
11
|
const UnderlineButton = () => {
|
11
12
|
const { toggleUnderline } = (0, react_2.useCommands)();
|
12
13
|
const chain = (0, react_2.useChainedCommands)();
|
@@ -17,6 +18,6 @@ const UnderlineButton = () => {
|
|
17
18
|
chain.toggleUnderline().focus().run();
|
18
19
|
}
|
19
20
|
};
|
20
|
-
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.underline(), icon: react_1.default.createElement(FormatUnderlinedRounded_1.default, null), label:
|
21
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.underline(), icon: react_1.default.createElement(FormatUnderlinedRounded_1.default, null), label: `Underline (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+U)` }));
|
21
22
|
};
|
22
23
|
exports.default = UnderlineButton;
|
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_2 = require("@remirror/react");
|
8
8
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
9
|
const UndoRounded_1 = __importDefault(require("@mui/icons-material/UndoRounded"));
|
10
|
+
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
10
11
|
const UndoButton = () => {
|
11
12
|
const { undo } = (0, react_2.useCommands)();
|
12
13
|
const { undoDepth } = (0, react_2.useHelpers)(true);
|
@@ -16,6 +17,6 @@ const UndoButton = () => {
|
|
16
17
|
}
|
17
18
|
};
|
18
19
|
const enabled = undoDepth() > 0;
|
19
|
-
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: false, icon: react_1.default.createElement(UndoRounded_1.default, null), label:
|
20
|
+
return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: false, icon: react_1.default.createElement(UndoRounded_1.default, null), label: `Undo (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+Z)` }));
|
20
21
|
};
|
21
22
|
exports.default = UndoButton;
|
@@ -121,7 +121,9 @@ const transformNode = (node) => {
|
|
121
121
|
* @return {FormattedNode}
|
122
122
|
*/
|
123
123
|
const wrapNodeIfNeeded = (node, wrappingNode, copyFont = true) => {
|
124
|
+
const wrappingNodeChildren = wrappingNode.children || [];
|
124
125
|
if (node.type === 'tag' && wrappingNode.type === 'tag' && (node.tag === 'span' || node.tag === wrappingNode.tag)) {
|
126
|
+
const nodeChildren = node.children || [];
|
125
127
|
// if the node we are wrapping with is a DOM node, and the node being wrapped is
|
126
128
|
// a plain looking DOM node merge the 2 nodes.
|
127
129
|
return {
|
@@ -141,13 +143,13 @@ const wrapNodeIfNeeded = (node, wrappingNode, copyFont = true) => {
|
|
141
143
|
...wrappingNode.font,
|
142
144
|
}
|
143
145
|
: {}),
|
144
|
-
children: [...
|
146
|
+
children: [...nodeChildren, ...wrappingNodeChildren],
|
145
147
|
};
|
146
148
|
}
|
147
149
|
// if the node we are wrapping or the wrapping nodes are not compatible merge them.
|
148
150
|
return {
|
149
151
|
...wrappingNode,
|
150
|
-
children: [node, ...
|
152
|
+
children: [node, ...wrappingNodeChildren],
|
151
153
|
};
|
152
154
|
};
|
153
155
|
const transformMark = (mark, node) => {
|
@@ -133,7 +133,7 @@ const unwrapNodeIfNeeded = (node) => {
|
|
133
133
|
const formatNode = (node) => {
|
134
134
|
const children = [];
|
135
135
|
if ('children' in node) {
|
136
|
-
node.children
|
136
|
+
node.children?.forEach((child) => {
|
137
137
|
children.push(...formatNode(child));
|
138
138
|
});
|
139
139
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const getShortcutSymbol: () => string;
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getShortcutSymbol = void 0;
|
4
|
+
const getShortcutSymbol = () => {
|
5
|
+
// If we can detect Mac then we return "⌘" otherwise default to "Ctrl"
|
6
|
+
return window.navigator.userAgent.toLowerCase().indexOf('mac') > -1 ? '⌘' : 'Ctrl';
|
7
|
+
};
|
8
|
+
exports.getShortcutSymbol = getShortcutSymbol;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/formatted-text-editor",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.68.0",
|
4
4
|
"main": "lib/index.js",
|
5
5
|
"types": "lib/index.d.ts",
|
6
6
|
"private": false,
|
@@ -23,7 +23,7 @@
|
|
23
23
|
"@headlessui/react": "1.7.11",
|
24
24
|
"@mui/icons-material": "5.11.16",
|
25
25
|
"@remirror/react": "2.0.25",
|
26
|
-
"@squiz/dx-json-schema-lib": "^1.65.
|
26
|
+
"@squiz/dx-json-schema-lib": "^1.65.1",
|
27
27
|
"@squiz/resource-browser": "^1.66.3",
|
28
28
|
"clsx": "1.2.1",
|
29
29
|
"react-hook-form": "7.43.2",
|
@@ -35,7 +35,7 @@
|
|
35
35
|
"@testing-library/jest-dom": "5.16.5",
|
36
36
|
"@testing-library/react": "14.0.0",
|
37
37
|
"@testing-library/user-event": "14.4.3",
|
38
|
-
"@types/node": "20.
|
38
|
+
"@types/node": "20.12.4",
|
39
39
|
"@types/react": "^18.2.45",
|
40
40
|
"@types/react-dom": "^18.2.18",
|
41
41
|
"@vitejs/plugin-react": "3.0.0",
|
@@ -23,17 +23,17 @@ describe('Formatted text editor', () => {
|
|
23
23
|
|
24
24
|
it('Renders the bold button', () => {
|
25
25
|
render(<Editor />);
|
26
|
-
expect(screen.getByRole('button', { name: 'Bold (
|
26
|
+
expect(screen.getByRole('button', { name: 'Bold (Ctrl+B)' })).toBeInTheDocument();
|
27
27
|
});
|
28
28
|
|
29
29
|
it('Renders the italic button', () => {
|
30
30
|
render(<Editor />);
|
31
|
-
expect(screen.getByRole('button', { name: 'Italic (
|
31
|
+
expect(screen.getByRole('button', { name: 'Italic (Ctrl+I)' })).toBeInTheDocument();
|
32
32
|
});
|
33
33
|
|
34
34
|
it('Renders the underline button', () => {
|
35
35
|
render(<Editor />);
|
36
|
-
expect(screen.getByRole('button', { name: 'Underline (
|
36
|
+
expect(screen.getByRole('button', { name: 'Underline (Ctrl+U)' })).toBeInTheDocument();
|
37
37
|
});
|
38
38
|
|
39
39
|
it('Renders the align left button', () => {
|
@@ -296,9 +296,9 @@ describe('Formatted text editor', () => {
|
|
296
296
|
|
297
297
|
it('Should not display the toolbar if is not editable', () => {
|
298
298
|
render(<Editor editable={false} />);
|
299
|
-
expect(screen.queryByRole('button', { name: 'Bold (
|
300
|
-
expect(screen.queryByRole('button', { name: 'Italic (
|
301
|
-
expect(screen.queryByRole('button', { name: 'Underline (
|
299
|
+
expect(screen.queryByRole('button', { name: 'Bold (Ctrl+B)' })).not.toBeInTheDocument();
|
300
|
+
expect(screen.queryByRole('button', { name: 'Italic (Ctrl+I)' })).not.toBeInTheDocument();
|
301
|
+
expect(screen.queryByRole('button', { name: 'Underline (Ctrl+U)' })).not.toBeInTheDocument();
|
302
302
|
});
|
303
303
|
|
304
304
|
it('Should not display the floating toolbar if is not editable', async () => {
|
@@ -415,7 +415,7 @@ describe('Formatted text editor', () => {
|
|
415
415
|
</ResourceBrowserContext.Provider>,
|
416
416
|
);
|
417
417
|
|
418
|
-
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Link (
|
418
|
+
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Link (Ctrl+K)' })));
|
419
419
|
await act(() => fireEvent.click(screen.getByText('Choose asset')));
|
420
420
|
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Drill down to My Matrix instance children' })));
|
421
421
|
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Drill down to Folder 1 children' })));
|
@@ -426,7 +426,7 @@ describe('Formatted text editor', () => {
|
|
426
426
|
// Toolbar (Link button) should be mounted.
|
427
427
|
// Toolbar should be shown (.show-toolbar class).
|
428
428
|
expect(screen.getByRole('button', { name: /My image/ })).toBeInTheDocument();
|
429
|
-
expect(within(document.body).getByRole('button', { name: 'Link (
|
429
|
+
expect(within(document.body).getByRole('button', { name: 'Link (Ctrl+K)', hidden: true })).toBeInTheDocument();
|
430
430
|
expect(document.querySelector('.show-toolbar')).toBeInTheDocument();
|
431
431
|
});
|
432
432
|
});
|
@@ -9,11 +9,11 @@ describe('FloatingToolbar', () => {
|
|
9
9
|
|
10
10
|
it.each([
|
11
11
|
['Nothing selected', 3, 3, []],
|
12
|
-
['Regular text selected', 3, 4, ['Bold (
|
13
|
-
['Regular text + link selected', 3, 17, ['Bold (
|
12
|
+
['Regular text selected', 3, 4, ['Bold (Ctrl+B)', 'Italic (Ctrl+I)', 'Underline (Ctrl+U)', 'Link (Ctrl+K)']],
|
13
|
+
['Regular text + link selected', 3, 17, ['Bold (Ctrl+B)', 'Italic (Ctrl+I)', 'Underline (Ctrl+U)']],
|
14
14
|
['Nothing selected, positioned directly on the left of a link', 12, 12, []],
|
15
15
|
['Nothing selected, positioned directly on the right of a link', 19, 19, []],
|
16
|
-
['Nothing selected, positioned within a link', 13, 13, ['Link (
|
16
|
+
['Nothing selected, positioned within a link', 13, 13, ['Link (Ctrl+K)', 'Remove link (Shift+Ctrl+K)']],
|
17
17
|
])(
|
18
18
|
'Renders formatting buttons when text is selected - %s',
|
19
19
|
async (description: string, from: number, to: number, expectedButtons: string[]) => {
|
@@ -29,8 +29,8 @@ describe('FloatingToolbar', () => {
|
|
29
29
|
);
|
30
30
|
|
31
31
|
it.each([
|
32
|
-
['Image selected', 1, ['Image (
|
33
|
-
['Asset image selected', 2, ['Image (
|
32
|
+
['Image selected', 1, ['Image (Ctrl+L)']],
|
33
|
+
['Asset image selected', 2, ['Image (Ctrl+L)']],
|
34
34
|
])(
|
35
35
|
'Renders formatting buttons when node is selected - %s',
|
36
36
|
async (description: string, pos: number, expectedButtons: string[]) => {
|
@@ -6,13 +6,13 @@ import Editor from '../../../Editor/Editor';
|
|
6
6
|
describe('Bold button', () => {
|
7
7
|
it('Renders the bold button', () => {
|
8
8
|
render(<Editor />);
|
9
|
-
expect(screen.getByRole('button', { name: 'Bold (
|
9
|
+
expect(screen.getByRole('button', { name: 'Bold (Ctrl+B)' })).toBeInTheDocument();
|
10
10
|
});
|
11
11
|
|
12
12
|
it('Activates the button if clicked', () => {
|
13
13
|
render(<Editor />);
|
14
|
-
expect(screen.getByRole('button', { name: 'Bold (
|
15
|
-
const bold = screen.getByRole('button', { name: 'Bold (
|
14
|
+
expect(screen.getByRole('button', { name: 'Bold (Ctrl+B)' }).classList.contains('squiz-fte-btn')).toBeTruthy();
|
15
|
+
const bold = screen.getByRole('button', { name: 'Bold (Ctrl+B)' });
|
16
16
|
fireEvent.click(bold);
|
17
17
|
expect(bold.classList.contains('squiz-fte-btn--is-active')).toBeTruthy();
|
18
18
|
});
|
@@ -3,6 +3,7 @@ import { useCommands, useActive, useChainedCommands } from '@remirror/react';
|
|
3
3
|
import { BoldExtension } from '@remirror/extension-bold';
|
4
4
|
import Button from '../../../ui/Button/Button';
|
5
5
|
import FormatBoldRoundedIcon from '@mui/icons-material/FormatBoldRounded';
|
6
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
6
7
|
|
7
8
|
const BoldButton = () => {
|
8
9
|
const { toggleBold } = useCommands();
|
@@ -22,7 +23,7 @@ const BoldButton = () => {
|
|
22
23
|
isDisabled={!enabled}
|
23
24
|
isActive={active.bold()}
|
24
25
|
icon={<FormatBoldRoundedIcon />}
|
25
|
-
label=
|
26
|
+
label={`Bold (${getShortcutSymbol()}+B)`}
|
26
27
|
/>
|
27
28
|
);
|
28
29
|
};
|
@@ -7,7 +7,7 @@ import ClearFormattingButton from './ClearFormattingButton';
|
|
7
7
|
describe('Clear formatting button', () => {
|
8
8
|
it('Renders the clear formatting button', async () => {
|
9
9
|
await renderWithEditor(<ClearFormattingButton />, { content: 'Some nonsense content here' });
|
10
|
-
expect(screen.getByRole('button', { name: 'Clear all formatting (
|
10
|
+
expect(screen.getByRole('button', { name: 'Clear all formatting (Ctrl+\\)' })).toBeInTheDocument();
|
11
11
|
});
|
12
12
|
|
13
13
|
it('Clears the formatting from editor content after clicking button', async () => {
|
@@ -15,7 +15,7 @@ describe('Clear formatting button', () => {
|
|
15
15
|
content: '<p>Hello <strong>Mr Bean</strong></p>',
|
16
16
|
});
|
17
17
|
|
18
|
-
const clearFormatting = screen.getByRole('button', { name: 'Clear all formatting (
|
18
|
+
const clearFormatting = screen.getByRole('button', { name: 'Clear all formatting (Ctrl+\\)' });
|
19
19
|
fireEvent.click(clearFormatting);
|
20
20
|
|
21
21
|
expect(getHtmlContent()).toBe('<p style="">Hello Mr Bean</p>');
|
@@ -4,6 +4,7 @@ import { VerticalDivider } from '@remirror/react-components';
|
|
4
4
|
import FormatClearRoundedIcon from '@mui/icons-material/FormatClearRounded';
|
5
5
|
import { ClearFormattingExtension } from '../../../Extensions/ClearFormattingExtension/ClearFormattingExtension';
|
6
6
|
import Button from '../../../ui/Button/Button';
|
7
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
7
8
|
|
8
9
|
const ClearFormattingButton = () => {
|
9
10
|
const { clearFormatting } = useCommands<ClearFormattingExtension>();
|
@@ -36,7 +37,9 @@ const ClearFormattingButton = () => {
|
|
36
37
|
isDisabled={false}
|
37
38
|
isActive={false}
|
38
39
|
icon={<FormatClearRoundedIcon />}
|
39
|
-
label={`${
|
40
|
+
label={`${
|
41
|
+
contentSelected ? 'Clear formatting from selection' : 'Clear all formatting'
|
42
|
+
} (${getShortcutSymbol()}+\\)`}
|
40
43
|
/>
|
41
44
|
</>
|
42
45
|
);
|
@@ -10,7 +10,7 @@ jest.mock('react-image-size');
|
|
10
10
|
|
11
11
|
describe('ImageButton', () => {
|
12
12
|
const openModal = async () => {
|
13
|
-
fireEvent.click(screen.getByRole('button', { name: 'Image (
|
13
|
+
fireEvent.click(screen.getByRole('button', { name: 'Image (Ctrl+L)' }));
|
14
14
|
await screen.findByRole('button', { name: 'Apply' });
|
15
15
|
fireEvent.click(screen.getByRole('button', { name: 'From URL' }));
|
16
16
|
};
|
@@ -8,6 +8,7 @@ import { ImageExtension } from '../../../Extensions/ImageExtension/ImageExtensio
|
|
8
8
|
import { NodeName } from '../../../Extensions/Extensions';
|
9
9
|
import { AssetImageExtension } from '../../../Extensions/ImageExtension/AssetImageExtension';
|
10
10
|
import { CodeBlockExtension } from 'remirror/dist-types/extensions';
|
11
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
11
12
|
|
12
13
|
type ImageButtonProps = {
|
13
14
|
inPopover?: boolean;
|
@@ -60,7 +61,7 @@ const ImageButton = ({ inPopover = false }: ImageButtonProps) => {
|
|
60
61
|
handleOnClick={handleClick}
|
61
62
|
isActive={active.image() || active.assetImage()}
|
62
63
|
icon={<ImageRoundedIcon />}
|
63
|
-
label=
|
64
|
+
label={`Image (${getShortcutSymbol()}+L)`}
|
64
65
|
isDisabled={disabled}
|
65
66
|
/>
|
66
67
|
{showModal && <ImageModal onCancel={() => setShowModal(false)} onSubmit={handleSubmit} />}
|
@@ -6,13 +6,13 @@ import Editor from '../../../Editor/Editor';
|
|
6
6
|
describe('Italic button', () => {
|
7
7
|
it('Renders the italic button', () => {
|
8
8
|
render(<Editor />);
|
9
|
-
expect(screen.getByRole('button', { name: 'Italic (
|
9
|
+
expect(screen.getByRole('button', { name: 'Italic (Ctrl+I)' })).toBeInTheDocument();
|
10
10
|
});
|
11
11
|
|
12
12
|
it('Activates the button if clicked', () => {
|
13
13
|
render(<Editor />);
|
14
|
-
expect(screen.getByRole('button', { name: 'Italic (
|
15
|
-
const italic = screen.getByRole('button', { name: 'Italic (
|
14
|
+
expect(screen.getByRole('button', { name: 'Italic (Ctrl+I)' }).classList.contains('squiz-fte-btn')).toBeTruthy();
|
15
|
+
const italic = screen.getByRole('button', { name: 'Italic (Ctrl+I)' });
|
16
16
|
fireEvent.click(italic);
|
17
17
|
expect(italic.classList.contains('squiz-fte-btn--is-active')).toBeTruthy();
|
18
18
|
});
|
@@ -3,6 +3,7 @@ import { useCommands, useActive, useChainedCommands } from '@remirror/react';
|
|
3
3
|
import { ItalicExtension } from '@remirror/extension-italic';
|
4
4
|
import Button from '../../../ui/Button/Button';
|
5
5
|
import FormatItalicRoundedIcon from '@mui/icons-material/FormatItalicRounded';
|
6
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
6
7
|
|
7
8
|
const ItalicButton = () => {
|
8
9
|
const { toggleItalic } = useCommands();
|
@@ -22,7 +23,7 @@ const ItalicButton = () => {
|
|
22
23
|
isDisabled={!enabled}
|
23
24
|
isActive={active.italic()}
|
24
25
|
icon={<FormatItalicRoundedIcon />}
|
25
|
-
label=
|
26
|
+
label={`Italic (${getShortcutSymbol()}+I)`}
|
26
27
|
/>
|
27
28
|
);
|
28
29
|
};
|
@@ -6,7 +6,7 @@ import LinkButton from './LinkButton';
|
|
6
6
|
|
7
7
|
describe('LinkButton', () => {
|
8
8
|
const openModal = async () => {
|
9
|
-
fireEvent.click(screen.getByRole('button', { name: 'Link (
|
9
|
+
fireEvent.click(screen.getByRole('button', { name: 'Link (Ctrl+K)' }));
|
10
10
|
await screen.findByRole('button', { name: 'Apply' });
|
11
11
|
fireEvent.click(screen.getByRole('button', { name: 'From URL' }));
|
12
12
|
};
|
@@ -10,6 +10,7 @@ import { AssetLinkExtension } from '../../../Extensions/LinkExtension/AssetLinkE
|
|
10
10
|
import { MarkName } from '../../../Extensions/Extensions';
|
11
11
|
import { ImageExtension } from '../../../Extensions/ImageExtension/ImageExtension';
|
12
12
|
import { CodeBlockExtension } from 'remirror/dist-types/extensions';
|
13
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
13
14
|
|
14
15
|
export type LinkButtonProps = {
|
15
16
|
inPopover?: boolean;
|
@@ -55,7 +56,7 @@ const LinkButton = ({ inPopover = false }: LinkButtonProps) => {
|
|
55
56
|
handleOnClick={handleClick}
|
56
57
|
isActive={active.link() || active.assetLink()}
|
57
58
|
icon={<InsertLinkRoundedIcon />}
|
58
|
-
label=
|
59
|
+
label={`Link (${getShortcutSymbol()}+K)`}
|
59
60
|
isDisabled={disabled}
|
60
61
|
/>
|
61
62
|
{showModal && <LinkModal onCancel={() => setShowModal(false)} onSubmit={handleSubmit} />}
|
@@ -40,7 +40,7 @@ describe('RemoveLinkButton', () => {
|
|
40
40
|
await act(() => editor.selectText('all'));
|
41
41
|
|
42
42
|
// remove the links.
|
43
|
-
fireEvent.click(screen.getByRole('button', { name: 'Remove link (
|
43
|
+
fireEvent.click(screen.getByRole('button', { name: 'Remove link (Shift+Ctrl+K)' }));
|
44
44
|
|
45
45
|
// make sure both types of link have been removed.
|
46
46
|
expect(getJsonContent()).toEqual({
|
@@ -101,11 +101,11 @@ describe('RemoveLinkButton', () => {
|
|
101
101
|
});
|
102
102
|
|
103
103
|
// expect remove button to be disabled
|
104
|
-
expect(screen.getByRole('button', { name: 'Remove link (
|
104
|
+
expect(screen.getByRole('button', { name: 'Remove link (Shift+Ctrl+K)' })).toBeDisabled();
|
105
105
|
// jump to the middle of the link.
|
106
106
|
await act(() => editor.selectText(3));
|
107
107
|
// expect remove button to be enabled
|
108
|
-
expect(screen.getByRole('button', { name: 'Remove link (
|
108
|
+
expect(screen.getByRole('button', { name: 'Remove link (Shift+Ctrl+K)' })).not.toBeDisabled();
|
109
109
|
});
|
110
110
|
|
111
111
|
it('Enables the Remove link button when asset link text is selected', async () => {
|
@@ -134,10 +134,10 @@ describe('RemoveLinkButton', () => {
|
|
134
134
|
});
|
135
135
|
|
136
136
|
// expect remove button to be disabled
|
137
|
-
expect(screen.getByRole('button', { name: 'Remove link (
|
137
|
+
expect(screen.getByRole('button', { name: 'Remove link (Shift+Ctrl+K)' })).toBeDisabled();
|
138
138
|
// jump to the middle of the link.
|
139
139
|
await act(() => editor.selectText(3));
|
140
140
|
// expect remove button to be enabled
|
141
|
-
expect(screen.getByRole('button', { name: 'Remove link (
|
141
|
+
expect(screen.getByRole('button', { name: 'Remove link (Shift+Ctrl+K)' })).not.toBeDisabled();
|
142
142
|
});
|
143
143
|
});
|
@@ -5,6 +5,7 @@ import LinkOffIcon from '@mui/icons-material/LinkOff';
|
|
5
5
|
import { AssetLinkExtension } from '../../../Extensions/LinkExtension/AssetLinkExtension';
|
6
6
|
import { LinkExtension } from '../../../Extensions/LinkExtension/LinkExtension';
|
7
7
|
import { LinkButtonProps } from './LinkButton';
|
8
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
8
9
|
|
9
10
|
const RemoveLinkButton = ({ inPopover = false }: LinkButtonProps) => {
|
10
11
|
const chain = useChainedCommands();
|
@@ -33,7 +34,7 @@ const RemoveLinkButton = ({ inPopover = false }: LinkButtonProps) => {
|
|
33
34
|
isActive={false}
|
34
35
|
isDisabled={disabled}
|
35
36
|
icon={<LinkOffIcon />}
|
36
|
-
label=
|
37
|
+
label={`Remove link (Shift+${getShortcutSymbol()}+K)`}
|
37
38
|
/>
|
38
39
|
);
|
39
40
|
};
|
@@ -6,12 +6,12 @@ import React from 'react';
|
|
6
6
|
describe('Redo button', () => {
|
7
7
|
it('Renders the redo button', () => {
|
8
8
|
render(<Editor />);
|
9
|
-
expect(screen.getByRole('button', { name: 'Redo (
|
9
|
+
expect(screen.getByRole('button', { name: 'Redo (Shift+Ctrl+Z)' })).toBeInTheDocument();
|
10
10
|
});
|
11
11
|
|
12
12
|
it('Renders a disabled button if you have not made any changes yet', () => {
|
13
13
|
render(<Editor />);
|
14
|
-
const redo = screen.getByRole('button', { name: 'Redo (
|
14
|
+
const redo = screen.getByRole('button', { name: 'Redo (Shift+Ctrl+Z)' });
|
15
15
|
expect(redo).toBeDisabled();
|
16
16
|
});
|
17
17
|
|
@@ -25,12 +25,12 @@ describe('Redo button', () => {
|
|
25
25
|
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeTruthy();
|
26
26
|
|
27
27
|
// Revert this action
|
28
|
-
const undo = screen.getByRole('button', { name: 'Undo (
|
28
|
+
const undo = screen.getByRole('button', { name: 'Undo (Ctrl+Z)' });
|
29
29
|
fireEvent.click(undo);
|
30
30
|
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeFalsy();
|
31
31
|
|
32
32
|
// Check that this enables the redo button
|
33
|
-
const redo = screen.getByRole('button', { name: 'Redo (
|
33
|
+
const redo = screen.getByRole('button', { name: 'Redo (Shift+Ctrl+Z)' });
|
34
34
|
expect(redo).not.toBeDisabled();
|
35
35
|
});
|
36
36
|
|
@@ -44,12 +44,12 @@ describe('Redo button', () => {
|
|
44
44
|
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeTruthy();
|
45
45
|
|
46
46
|
// Revert this action
|
47
|
-
const undo = screen.getByRole('button', { name: 'Undo (
|
47
|
+
const undo = screen.getByRole('button', { name: 'Undo (Ctrl+Z)' });
|
48
48
|
fireEvent.click(undo);
|
49
49
|
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeFalsy();
|
50
50
|
|
51
51
|
// Check that this enables the redo button
|
52
|
-
const redo = screen.getByRole('button', { name: 'Redo (
|
52
|
+
const redo = screen.getByRole('button', { name: 'Redo (Shift+Ctrl+Z)' });
|
53
53
|
expect(redo).not.toBeDisabled();
|
54
54
|
|
55
55
|
// Click the redo button and check that this has reverted the previous action
|
@@ -3,6 +3,7 @@ import { useCommands, useHelpers } from '@remirror/react';
|
|
3
3
|
import { HistoryExtension } from 'remirror/extensions';
|
4
4
|
import Button from '../../../ui/Button/Button';
|
5
5
|
import RedoRoundedIcon from '@mui/icons-material/RedoRounded';
|
6
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
6
7
|
|
7
8
|
const RedoButton = () => {
|
8
9
|
const { redo } = useCommands<HistoryExtension>();
|
@@ -22,7 +23,7 @@ const RedoButton = () => {
|
|
22
23
|
isDisabled={!enabled}
|
23
24
|
isActive={false}
|
24
25
|
icon={<RedoRoundedIcon />}
|
25
|
-
label=
|
26
|
+
label={`Redo (Shift+${getShortcutSymbol()}+Z)`}
|
26
27
|
/>
|
27
28
|
);
|
28
29
|
};
|
@@ -6,13 +6,13 @@ import Editor from '../../../Editor/Editor';
|
|
6
6
|
describe('Underline button', () => {
|
7
7
|
it('Renders the underline button', () => {
|
8
8
|
render(<Editor />);
|
9
|
-
expect(screen.getByRole('button', { name: 'Underline (
|
9
|
+
expect(screen.getByRole('button', { name: 'Underline (Ctrl+U)' })).toBeInTheDocument();
|
10
10
|
});
|
11
11
|
|
12
12
|
it('Activates the button if clicked', () => {
|
13
13
|
render(<Editor />);
|
14
|
-
expect(screen.getByRole('button', { name: 'Underline (
|
15
|
-
const underline = screen.getByRole('button', { name: 'Underline (
|
14
|
+
expect(screen.getByRole('button', { name: 'Underline (Ctrl+U)' }).classList.contains('squiz-fte-btn')).toBeTruthy();
|
15
|
+
const underline = screen.getByRole('button', { name: 'Underline (Ctrl+U)' });
|
16
16
|
fireEvent.click(underline);
|
17
17
|
expect(underline.classList.contains('squiz-fte-btn--is-active')).toBeTruthy();
|
18
18
|
});
|
@@ -3,6 +3,7 @@ import { useCommands, useActive, useChainedCommands } from '@remirror/react';
|
|
3
3
|
import { UnderlineExtension } from '@remirror/extension-underline';
|
4
4
|
import Button from '../../../ui/Button/Button';
|
5
5
|
import FormatUnderlinedRoundedIcon from '@mui/icons-material/FormatUnderlinedRounded';
|
6
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
6
7
|
|
7
8
|
const UnderlineButton = () => {
|
8
9
|
const { toggleUnderline } = useCommands();
|
@@ -22,7 +23,7 @@ const UnderlineButton = () => {
|
|
22
23
|
isDisabled={!enabled}
|
23
24
|
isActive={active.underline()}
|
24
25
|
icon={<FormatUnderlinedRoundedIcon />}
|
25
|
-
label=
|
26
|
+
label={`Underline (${getShortcutSymbol()}+U)`}
|
26
27
|
/>
|
27
28
|
);
|
28
29
|
};
|
@@ -8,12 +8,12 @@ import UndoButton from './UndoButton';
|
|
8
8
|
describe('Undo button', () => {
|
9
9
|
it('Renders the undo button', () => {
|
10
10
|
render(<Editor />);
|
11
|
-
expect(screen.getByRole('button', { name: 'Undo (
|
11
|
+
expect(screen.getByRole('button', { name: 'Undo (Ctrl+Z)' })).toBeInTheDocument();
|
12
12
|
});
|
13
13
|
|
14
14
|
it('Renders a disabled button if you have not made any changes yet', () => {
|
15
15
|
render(<Editor />);
|
16
|
-
const undo = screen.getByRole('button', { name: 'Undo (
|
16
|
+
const undo = screen.getByRole('button', { name: 'Undo (Ctrl+Z)' });
|
17
17
|
expect(undo).toBeDisabled();
|
18
18
|
});
|
19
19
|
|
@@ -26,7 +26,7 @@ describe('Undo button', () => {
|
|
26
26
|
|
27
27
|
fireEvent.click(leftAlignButton);
|
28
28
|
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeTruthy();
|
29
|
-
const undo = screen.getByRole('button', { name: 'Undo (
|
29
|
+
const undo = screen.getByRole('button', { name: 'Undo (Ctrl+Z)' });
|
30
30
|
expect(undo).not.toBeDisabled();
|
31
31
|
});
|
32
32
|
|
@@ -41,7 +41,7 @@ describe('Undo button', () => {
|
|
41
41
|
expect(baseElement.querySelector('p[data-node-text-align="left"]')).toBeTruthy();
|
42
42
|
|
43
43
|
// Check that this enables the undo button
|
44
|
-
const undo = screen.getByRole('button', { name: 'Undo (
|
44
|
+
const undo = screen.getByRole('button', { name: 'Undo (Ctrl+Z)' });
|
45
45
|
expect(undo).not.toBeDisabled();
|
46
46
|
|
47
47
|
// Click the undo button and check that this has reverted the previous action
|
@@ -60,7 +60,7 @@ describe('Undo button', () => {
|
|
60
60
|
content: [{ type: 'text', text: 'Initial content... with some updated content.' }],
|
61
61
|
});
|
62
62
|
|
63
|
-
fireEvent.click(screen.getByRole('button', { name: 'Undo (
|
63
|
+
fireEvent.click(screen.getByRole('button', { name: 'Undo (Ctrl+Z)' }));
|
64
64
|
expect(getJsonContent()).toEqual({
|
65
65
|
type: 'paragraph',
|
66
66
|
attrs: expect.any(Object),
|
@@ -3,6 +3,7 @@ import { useCommands, useHelpers } from '@remirror/react';
|
|
3
3
|
import { HistoryExtension } from 'remirror/extensions';
|
4
4
|
import Button from '../../../ui/Button/Button';
|
5
5
|
import UndoRoundedIcon from '@mui/icons-material/UndoRounded';
|
6
|
+
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
6
7
|
|
7
8
|
const UndoButton = () => {
|
8
9
|
const { undo } = useCommands<HistoryExtension>();
|
@@ -22,7 +23,7 @@ const UndoButton = () => {
|
|
22
23
|
isDisabled={!enabled}
|
23
24
|
isActive={false}
|
24
25
|
icon={<UndoRoundedIcon />}
|
25
|
-
label=
|
26
|
+
label={`Undo (${getShortcutSymbol()}+Z)`}
|
26
27
|
/>
|
27
28
|
);
|
28
29
|
};
|
@@ -13,7 +13,7 @@ type FontOptions = FormattedTextModels.v1.FormattedNodeFontProperties;
|
|
13
13
|
type FormattedText = FormattedTextModels.v1.FormattedText;
|
14
14
|
type FormattedNode = FormattedTextModels.v1.FormattedNodes;
|
15
15
|
type FormattedNodeFontProperties = FormattedTextModels.v1.FormattedNodeFontProperties;
|
16
|
-
type FormattedNodeWithChildren = Extract<FormattedNode, { children
|
16
|
+
type FormattedNodeWithChildren = Extract<FormattedNode, { children?: FormattedNode[] }>;
|
17
17
|
type RemirrorTextAlignment = Exclude<Remirror.Attributes['nodeTextAlignment'], undefined>;
|
18
18
|
type FormattedTextAlignment = FormattingOptions['alignment'];
|
19
19
|
|
@@ -164,7 +164,11 @@ const wrapNodeIfNeeded = (
|
|
164
164
|
wrappingNode: FormattedNodeWithChildren,
|
165
165
|
copyFont: boolean = true,
|
166
166
|
): FormattedNode => {
|
167
|
+
const wrappingNodeChildren = wrappingNode.children || [];
|
168
|
+
|
167
169
|
if (node.type === 'tag' && wrappingNode.type === 'tag' && (node.tag === 'span' || node.tag === wrappingNode.tag)) {
|
170
|
+
const nodeChildren = node.children || [];
|
171
|
+
|
168
172
|
// if the node we are wrapping with is a DOM node, and the node being wrapped is
|
169
173
|
// a plain looking DOM node merge the 2 nodes.
|
170
174
|
return {
|
@@ -186,14 +190,14 @@ const wrapNodeIfNeeded = (
|
|
186
190
|
}
|
187
191
|
: {},
|
188
192
|
),
|
189
|
-
children: [...
|
193
|
+
children: [...nodeChildren, ...wrappingNodeChildren],
|
190
194
|
};
|
191
195
|
}
|
192
196
|
|
193
197
|
// if the node we are wrapping or the wrapping nodes are not compatible merge them.
|
194
198
|
return {
|
195
199
|
...wrappingNode,
|
196
|
-
children: [node, ...
|
200
|
+
children: [node, ...wrappingNodeChildren],
|
197
201
|
};
|
198
202
|
};
|
199
203
|
|
@@ -146,7 +146,7 @@ const formatNode = (node: FormattedNodes): RemirrorJSON[] => {
|
|
146
146
|
const children: RemirrorJSON[] = [];
|
147
147
|
|
148
148
|
if ('children' in node) {
|
149
|
-
node.children
|
149
|
+
node.children?.forEach((child: FormattedNodes) => {
|
150
150
|
children.push(...formatNode(child));
|
151
151
|
});
|
152
152
|
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { getShortcutSymbol } from './getShortcutSymbol';
|
2
|
+
|
3
|
+
describe('getShortcutSymbol', () => {
|
4
|
+
it('Returns Mac command icon if user-agent is a Mac based user-agent', () => {
|
5
|
+
Object.defineProperty(window, `navigator`, {
|
6
|
+
value: {
|
7
|
+
userAgent:
|
8
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9',
|
9
|
+
},
|
10
|
+
writable: true,
|
11
|
+
});
|
12
|
+
|
13
|
+
expect(getShortcutSymbol()).toBe('⌘');
|
14
|
+
});
|
15
|
+
|
16
|
+
it('Returns Ctrl shortcut if user-agent is not a Mac based user-agent', () => {
|
17
|
+
Object.defineProperty(window, `navigator`, {
|
18
|
+
value: {
|
19
|
+
userAgent:
|
20
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246',
|
21
|
+
},
|
22
|
+
writable: true,
|
23
|
+
});
|
24
|
+
|
25
|
+
expect(getShortcutSymbol()).toBe('Ctrl');
|
26
|
+
});
|
27
|
+
});
|