@squiz/formatted-text-editor 1.22.1-alpha.0 → 1.22.1-alpha.2
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/lib/EditorToolbar/FloatingToolbar.js +4 -1
- package/lib/EditorToolbar/Toolbar.js +4 -1
- package/lib/EditorToolbar/Tools/Link/LinkButton.d.ts +1 -1
- package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.d.ts +2 -1
- package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.js +42 -3
- package/package.json +2 -2
- package/src/EditorToolbar/FloatingToolbar.spec.tsx +1 -1
- package/src/EditorToolbar/FloatingToolbar.tsx +4 -1
- package/src/EditorToolbar/Toolbar.tsx +7 -1
- package/src/EditorToolbar/Tools/Link/LinkButton.tsx +1 -1
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +54 -1
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx +26 -5
@@ -56,7 +56,10 @@ const FloatingToolbar = () => {
|
|
56
56
|
else if (marks?.[Extensions_1.MarkName.Link].isExclusivelyActive || marks?.[Extensions_1.MarkName.AssetLink].isExclusivelyActive) {
|
57
57
|
// if all of the selected text is a link show the options to update/remove the link instead of the regular
|
58
58
|
// formatting options.
|
59
|
-
buttons = [
|
59
|
+
buttons = [
|
60
|
+
react_1.default.createElement(LinkButton_1.default, { key: "update-link", inPopover: true }),
|
61
|
+
react_1.default.createElement(RemoveLinkButton_1.default, { key: "remove-link", inPopover: true }),
|
62
|
+
];
|
60
63
|
}
|
61
64
|
else if (!marks?.[Extensions_1.MarkName.Link].isActive && !marks?.[Extensions_1.MarkName.AssetLink].isActive) {
|
62
65
|
// if none of the selected text is a link show the option to create a link.
|
@@ -16,6 +16,7 @@ const TextTypeDropdown_1 = __importDefault(require("./Tools/TextType/TextTypeDro
|
|
16
16
|
const hooks_1 = require("../hooks");
|
17
17
|
const LinkButton_1 = __importDefault(require("./Tools/Link/LinkButton"));
|
18
18
|
const ImageButton_1 = __importDefault(require("./Tools/Image/ImageButton"));
|
19
|
+
const RemoveLinkButton_1 = __importDefault(require("./Tools/Link/RemoveLinkButton"));
|
19
20
|
const Toolbar = () => {
|
20
21
|
const extensionNames = (0, hooks_1.useExtensionNames)();
|
21
22
|
return (react_1.default.createElement(react_components_1.Toolbar, { className: "remirror-toolbar editor-toolbar" },
|
@@ -28,7 +29,9 @@ const Toolbar = () => {
|
|
28
29
|
extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, null),
|
29
30
|
extensionNames.underline && react_1.default.createElement(UnderlineButton_1.default, null),
|
30
31
|
extensionNames.nodeFormatting && react_1.default.createElement(TextAlignButtons_1.default, null),
|
31
|
-
extensionNames.link && react_1.default.createElement(
|
32
|
+
extensionNames.link && (react_1.default.createElement(react_1.default.Fragment, null,
|
33
|
+
react_1.default.createElement(LinkButton_1.default, null),
|
34
|
+
react_1.default.createElement(RemoveLinkButton_1.default, null))),
|
32
35
|
extensionNames.image && react_1.default.createElement(ImageButton_1.default, null)));
|
33
36
|
};
|
34
37
|
exports.Toolbar = Toolbar;
|
@@ -1,14 +1,53 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
27
|
};
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
const react_1 =
|
29
|
+
const react_1 = __importStar(require("react"));
|
7
30
|
const react_2 = require("@remirror/react");
|
8
31
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
9
32
|
const LinkOff_1 = __importDefault(require("@mui/icons-material/LinkOff"));
|
10
|
-
const RemoveLinkButton = () => {
|
33
|
+
const RemoveLinkButton = ({ inPopover = false }) => {
|
11
34
|
const chain = (0, react_2.useChainedCommands)();
|
12
|
-
|
35
|
+
const active = (0, react_2.useActive)();
|
36
|
+
const disabled = !active.link();
|
37
|
+
const handleClick = () => {
|
38
|
+
chain.removeLink().removeAssetLink().focus().run();
|
39
|
+
};
|
40
|
+
const handleShortcut = (0, react_1.useCallback)(() => {
|
41
|
+
handleClick();
|
42
|
+
// Prevent other key handlers being run
|
43
|
+
return true;
|
44
|
+
}, []);
|
45
|
+
// when Shift+Ctrl+k is pressed show the modal, only registered in the toolbar button instance to avoid the key press
|
46
|
+
// being double handled.
|
47
|
+
if (!inPopover) {
|
48
|
+
// disable the shortcut if the button is disabled
|
49
|
+
(0, react_2.useKeymap)('Shift-Mod-k', disabled ? () => true : handleShortcut);
|
50
|
+
}
|
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: "Remove link (shift+cmd+K)" }));
|
13
52
|
};
|
14
53
|
exports.default = RemoveLinkButton;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/formatted-text-editor",
|
3
|
-
"version": "1.22.1-alpha.
|
3
|
+
"version": "1.22.1-alpha.2",
|
4
4
|
"main": "lib/index.js",
|
5
5
|
"types": "lib/index.d.ts",
|
6
6
|
"scripts": {
|
@@ -74,5 +74,5 @@
|
|
74
74
|
"volta": {
|
75
75
|
"node": "18.15.0"
|
76
76
|
},
|
77
|
-
"gitHead": "
|
77
|
+
"gitHead": "a37688f9211c9c1a5294c85a749cf4e630271ce5"
|
78
78
|
}
|
@@ -13,7 +13,7 @@ describe('FloatingToolbar', () => {
|
|
13
13
|
['Regular text + link selected', 3, 17, ['Bold (cmd+B)', 'Italic (cmd+I)', 'Underline (cmd+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 (cmd+K)', 'Remove link']],
|
16
|
+
['Nothing selected, positioned within a link', 13, 13, ['Link (cmd+K)', 'Remove link (shift+cmd+K)']],
|
17
17
|
])(
|
18
18
|
'Renders formatting buttons when text is selected - %s',
|
19
19
|
async (description: string, from: number, to: number, expectedButtons: string[]) => {
|
@@ -32,7 +32,10 @@ export const FloatingToolbar = () => {
|
|
32
32
|
} else if (marks?.[MarkName.Link].isExclusivelyActive || marks?.[MarkName.AssetLink].isExclusivelyActive) {
|
33
33
|
// if all of the selected text is a link show the options to update/remove the link instead of the regular
|
34
34
|
// formatting options.
|
35
|
-
buttons = [
|
35
|
+
buttons = [
|
36
|
+
<LinkButton key="update-link" inPopover={true} />,
|
37
|
+
<RemoveLinkButton key="remove-link" inPopover={true} />,
|
38
|
+
];
|
36
39
|
} else if (!marks?.[MarkName.Link].isActive && !marks?.[MarkName.AssetLink].isActive) {
|
37
40
|
// if none of the selected text is a link show the option to create a link.
|
38
41
|
buttons.push(
|
@@ -10,6 +10,7 @@ import TextTypeDropdown from './Tools/TextType/TextTypeDropdown';
|
|
10
10
|
import { useExtensionNames } from '../hooks';
|
11
11
|
import LinkButton from './Tools/Link/LinkButton';
|
12
12
|
import ImageButton from './Tools/Image/ImageButton';
|
13
|
+
import RemoveLinkButton from './Tools/Link/RemoveLinkButton';
|
13
14
|
|
14
15
|
export const Toolbar = () => {
|
15
16
|
const extensionNames = useExtensionNames();
|
@@ -28,7 +29,12 @@ export const Toolbar = () => {
|
|
28
29
|
{extensionNames.italic && <ItalicButton />}
|
29
30
|
{extensionNames.underline && <UnderlineButton />}
|
30
31
|
{extensionNames.nodeFormatting && <TextAlignButtons />}
|
31
|
-
{extensionNames.link &&
|
32
|
+
{extensionNames.link && (
|
33
|
+
<>
|
34
|
+
<LinkButton />
|
35
|
+
<RemoveLinkButton />
|
36
|
+
</>
|
37
|
+
)}
|
32
38
|
{extensionNames.image && <ImageButton />}
|
33
39
|
</RemirrorToolbar>
|
34
40
|
);
|
@@ -10,7 +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
|
|
13
|
-
type LinkButtonProps = {
|
13
|
+
export type LinkButtonProps = {
|
14
14
|
inPopover?: boolean;
|
15
15
|
};
|
16
16
|
|
@@ -35,7 +35,7 @@ describe('RemoveLinkButton', () => {
|
|
35
35
|
await act(() => editor.selectText('all'));
|
36
36
|
|
37
37
|
// remove the links.
|
38
|
-
fireEvent.click(screen.getByRole('button', { name: 'Remove link' }));
|
38
|
+
fireEvent.click(screen.getByRole('button', { name: 'Remove link (shift+cmd+K)' }));
|
39
39
|
|
40
40
|
// make sure both types of link have been removed.
|
41
41
|
expect(getJsonContent()).toEqual({
|
@@ -44,4 +44,57 @@ describe('RemoveLinkButton', () => {
|
|
44
44
|
content: [{ type: 'text', text: 'Sample link with another link' }],
|
45
45
|
});
|
46
46
|
});
|
47
|
+
|
48
|
+
it('Removes the link when clicking the keyboard shortcut', async () => {
|
49
|
+
const { elements, editor, getJsonContent } = await renderWithEditor(<RemoveLinkButton />, {
|
50
|
+
context: { matrix: { matrixDomain: 'my-matrix.squiz.net' } },
|
51
|
+
content: {
|
52
|
+
type: 'doc',
|
53
|
+
content: [
|
54
|
+
{
|
55
|
+
type: 'paragraph',
|
56
|
+
content: [
|
57
|
+
{
|
58
|
+
type: 'text',
|
59
|
+
text: 'Sample link',
|
60
|
+
marks: [{ type: 'assetLink', attrs: { matrixAssetId: '123', target: '_blank' } }],
|
61
|
+
},
|
62
|
+
{ type: 'text', text: ' with ' },
|
63
|
+
{
|
64
|
+
type: 'text',
|
65
|
+
text: 'another link',
|
66
|
+
marks: [{ type: 'link', attrs: { href: 'https://www.example.org/another-link', target: '_self' } }],
|
67
|
+
},
|
68
|
+
],
|
69
|
+
},
|
70
|
+
],
|
71
|
+
},
|
72
|
+
});
|
73
|
+
|
74
|
+
// select all of the text.
|
75
|
+
await act(() => editor.selectText('all'));
|
76
|
+
|
77
|
+
// press the keyboard shortcut.
|
78
|
+
fireEvent.keyDown(elements.editor, { key: 'k', ctrlKey: true, shiftKey: true });
|
79
|
+
|
80
|
+
// make sure both types of link have been removed.
|
81
|
+
expect(getJsonContent()).toEqual({
|
82
|
+
type: 'paragraph',
|
83
|
+
attrs: expect.any(Object),
|
84
|
+
content: [{ type: 'text', text: 'Sample link with another link' }],
|
85
|
+
});
|
86
|
+
});
|
87
|
+
|
88
|
+
it('Enables the Remove link button when link text is selected', async () => {
|
89
|
+
const { editor } = await renderWithEditor(<RemoveLinkButton />, {
|
90
|
+
content: '<a href="https://www.example.org/my-link">Sample link</a> with some other content.',
|
91
|
+
});
|
92
|
+
|
93
|
+
// expect remove button to be disabled
|
94
|
+
expect(screen.getByRole('button', { name: 'Remove link (shift+cmd+K)' })).toBeDisabled();
|
95
|
+
// jump to the middle of the link.
|
96
|
+
await act(() => editor.selectText(3));
|
97
|
+
// expect remove button to be enabled
|
98
|
+
expect(screen.getByRole('button', { name: 'Remove link (shift+cmd+K)' })).not.toBeDisabled();
|
99
|
+
});
|
47
100
|
});
|
@@ -1,17 +1,38 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { useChainedCommands } from '@remirror/react';
|
1
|
+
import React, { useCallback } from 'react';
|
2
|
+
import { useChainedCommands, useActive, useKeymap } from '@remirror/react';
|
3
3
|
import Button from '../../../ui/Button/Button';
|
4
4
|
import LinkOffIcon from '@mui/icons-material/LinkOff';
|
5
|
+
import { LinkExtension } from '../../../Extensions/LinkExtension/LinkExtension';
|
6
|
+
import { LinkButtonProps } from './LinkButton';
|
5
7
|
|
6
|
-
const RemoveLinkButton = () => {
|
8
|
+
const RemoveLinkButton = ({ inPopover = false }: LinkButtonProps) => {
|
7
9
|
const chain = useChainedCommands();
|
10
|
+
const active = useActive<LinkExtension>();
|
11
|
+
const disabled = !active.link();
|
12
|
+
|
13
|
+
const handleClick = () => {
|
14
|
+
chain.removeLink().removeAssetLink().focus().run();
|
15
|
+
};
|
16
|
+
const handleShortcut = useCallback(() => {
|
17
|
+
handleClick();
|
18
|
+
// Prevent other key handlers being run
|
19
|
+
return true;
|
20
|
+
}, []);
|
21
|
+
|
22
|
+
// when Shift+Ctrl+k is pressed show the modal, only registered in the toolbar button instance to avoid the key press
|
23
|
+
// being double handled.
|
24
|
+
if (!inPopover) {
|
25
|
+
// disable the shortcut if the button is disabled
|
26
|
+
useKeymap('Shift-Mod-k', disabled ? () => true : handleShortcut);
|
27
|
+
}
|
8
28
|
|
9
29
|
return (
|
10
30
|
<Button
|
11
|
-
handleOnClick={
|
31
|
+
handleOnClick={handleClick}
|
12
32
|
isActive={false}
|
33
|
+
isDisabled={disabled}
|
13
34
|
icon={<LinkOffIcon />}
|
14
|
-
label="Remove link"
|
35
|
+
label="Remove link (shift+cmd+K)"
|
15
36
|
/>
|
16
37
|
);
|
17
38
|
};
|