diff --git a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/BlocksInput/index.js b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/BlocksInput/index.js
index 40afcc10c3..04b3c0e307 100644
--- a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/BlocksInput/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/BlocksInput/index.js
@@ -62,11 +62,23 @@ const BlocksInput = ({ disabled }) => {
}
};
+ const handleBackspaceEvent = (event) => {
+ const selectedNode = editor.children[editor.selection.anchor.path[0]];
+ const selectedBlock = Object.values(blocks).find((block) => block.matchNode(selectedNode));
+
+ if (selectedBlock.handleBackspaceKey) {
+ selectedBlock.handleBackspaceKey(editor, event);
+ }
+ };
+
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
event.preventDefault();
handleEnter();
}
+ if (event.key === 'Backspace') {
+ handleBackspaceEvent(event);
+ }
};
return (
diff --git a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js
index 226ff335f3..e2810790b3 100644
--- a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js
+++ b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js
@@ -18,6 +18,12 @@ const initialValue = [
},
];
+const mockEvent = {
+ preventDefault: jest.fn(),
+ target: {
+ value: '',
+ },
+};
const user = userEvent.setup();
const baseEditor = createEditor();
@@ -608,6 +614,103 @@ describe('useBlocksStore', () => {
]);
});
+ it('handles the backspace key on a very first list with single empty list item', () => {
+ const { result } = renderHook(useBlocksStore);
+
+ baseEditor.children = [
+ {
+ type: 'list',
+ format: 'unordered',
+ children: [
+ {
+ type: 'list-item',
+ children: [
+ {
+ type: 'text',
+ text: '',
+ },
+ ],
+ },
+ ],
+ },
+ ];
+
+ // Set the cursor on the first list item
+ Transforms.select(baseEditor, {
+ anchor: { path: [0, 0, 0], offset: 0 },
+ focus: { path: [0, 0, 0], offset: 0 },
+ });
+
+ // Simulate the backspace key
+ result.current['list-unordered'].handleBackspaceKey(baseEditor, mockEvent);
+
+ // Should remove the empty list item and replace with empty paragraph
+ expect(baseEditor.children).toEqual([
+ {
+ type: 'paragraph',
+ children: [
+ {
+ type: 'text',
+ text: '',
+ },
+ ],
+ },
+ ]);
+ });
+
+ it('handles the backspace key on a list with single empty list item', () => {
+ const { result } = renderHook(useBlocksStore);
+
+ baseEditor.children = [
+ {
+ type: 'paragraph',
+ children: [
+ {
+ type: 'text',
+ text: 'some text',
+ },
+ ],
+ },
+ {
+ type: 'list',
+ format: 'ordered',
+ children: [
+ {
+ type: 'list-item',
+ children: [
+ {
+ type: 'text',
+ text: '',
+ },
+ ],
+ },
+ ],
+ },
+ ];
+
+ // Set the cursor on the first list item
+ Transforms.select(baseEditor, {
+ anchor: { path: [1, 0, 0], offset: 0 },
+ focus: { path: [1, 0, 0], offset: 0 },
+ });
+
+ // Simulate the backspace key
+ result.current['list-ordered'].handleBackspaceKey(baseEditor, mockEvent);
+
+ // Should remove the empty list item
+ expect(baseEditor.children).toEqual([
+ {
+ type: 'paragraph',
+ children: [
+ {
+ type: 'text',
+ text: 'some text',
+ },
+ ],
+ },
+ ]);
+ });
+
it('handles enter key on a quote', () => {
const { result } = renderHook(useBlocksStore);
diff --git a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js
index 5e6a7264fc..019b7428f3 100644
--- a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js
+++ b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js
@@ -151,35 +151,34 @@ List.propTypes = {
}).isRequired,
};
-const Img = styled.img`
- max-width: 100%;
-`;
+/**
+ * Common handler for the backspace event on ordered and unordered lists
+ * @param {import('slate').Editor} editor
+ * @param {Event} event
+ */
+const handleBackspaceKeyOnList = (editor, event) => {
+ const [currentListItem, currentListItemPath] = Editor.parent(editor, editor.selection.anchor);
+ const [currentList, currentListPath] = Editor.parent(editor, currentListItemPath);
+ const isListEmpty = currentList.children.length === 1 && currentListItem.children[0].text === '';
-const Image = ({ attributes, children, element }) => {
- if (!element.image) return null;
- const { url, alternativeText, width, height } = element.image;
+ if (isListEmpty) {
+ event.preventDefault();
+ // Delete the empty list
+ Transforms.removeNodes(editor, { at: currentListPath });
- return (
-
- {children}
-
-
-
-
- );
-};
-
-Image.propTypes = {
- attributes: PropTypes.object.isRequired,
- children: PropTypes.node.isRequired,
- element: PropTypes.shape({
- image: PropTypes.shape({
- url: PropTypes.string.isRequired,
- alternativeText: PropTypes.string,
- width: PropTypes.number,
- height: PropTypes.number,
- }),
- }).isRequired,
+ if (currentListPath[0] === 0) {
+ // If the list was the only(or first) block element then insert empty paragraph as editor needs default value
+ Transforms.insertNodes(
+ editor,
+ {
+ type: 'paragraph',
+ children: [{ type: 'text', text: '' }],
+ },
+ { at: currentListPath }
+ );
+ Transforms.select(editor, currentListPath);
+ }
+ }
};
/**
@@ -218,6 +217,37 @@ const handleEnterKeyOnList = (editor) => {
}
};
+const Img = styled.img`
+ max-width: 100%;
+`;
+
+const Image = ({ attributes, children, element }) => {
+ if (!element.image) return null;
+ const { url, alternativeText, width, height } = element.image;
+
+ return (
+
+ {children}
+
+
+
+
+ );
+};
+
+Image.propTypes = {
+ attributes: PropTypes.object.isRequired,
+ children: PropTypes.node.isRequired,
+ element: PropTypes.shape({
+ image: PropTypes.shape({
+ url: PropTypes.string.isRequired,
+ alternativeText: PropTypes.string,
+ width: PropTypes.number,
+ height: PropTypes.number,
+ }),
+ }).isRequired,
+};
+
const Link = React.forwardRef(({ element, children, ...attributes }, forwardedRef) => {
const { formatMessage } = useIntl();
const editor = useSlate();
@@ -381,6 +411,7 @@ Link.propTypes = {
* matchNode: (node: Object) => boolean,
* isInBlocksSelector: true,
* handleEnterKey: (editor: import('slate').Editor) => void,
+ * handleBackspaceKey?:(editor: import('slate').Editor, event: Event) => void,
* }
* }} an object containing rendering functions and metadata for different blocks, indexed by name.
*/
@@ -603,6 +634,7 @@ export function useBlocksStore() {
// TODO add icon and label and set isInBlocksEditor to true
isInBlocksSelector: false,
handleEnterKey: handleEnterKeyOnList,
+ handleBackspaceKey: handleBackspaceKeyOnList,
},
'list-unordered': {
renderElement: (props) =>
,
@@ -614,6 +646,7 @@ export function useBlocksStore() {
// TODO add icon and label and set isInBlocksEditor to true
isInBlocksSelector: false,
handleEnterKey: handleEnterKeyOnList,
+ handleBackspaceKey: handleBackspaceKeyOnList,
},
'list-item': {
renderElement: (props) => (