diff --git a/packages/volto-slate/news/6570.bugfix b/packages/volto-slate/news/6570.bugfix new file mode 100644 index 0000000000..9032558059 --- /dev/null +++ b/packages/volto-slate/news/6570.bugfix @@ -0,0 +1 @@ +In `RichTextWidget` and `HtmlSlateWidget`, fix breaking a list by typing Enter. @nileshgulia1 \ No newline at end of file diff --git a/packages/volto-slate/src/blocks/Text/extensions/breakListInWidget.js b/packages/volto-slate/src/blocks/Text/extensions/breakListInWidget.js new file mode 100644 index 0000000000..15b4422439 --- /dev/null +++ b/packages/volto-slate/src/blocks/Text/extensions/breakListInWidget.js @@ -0,0 +1,67 @@ +import { Editor, Range, Transforms } from 'slate'; + +import config from '@plone/volto/registry'; +import { isCursorAtBlockEnd } from '@plone/volto-slate/utils/selection'; +import { getCurrentListItem } from '@plone/volto-slate/utils/lists'; +import { createEmptyParagraph } from '@plone/volto-slate/utils/blocks'; + +export const breakListInWidget = (editor) => { + const { insertBreak } = editor; + + editor.insertBreak = () => { + if (!(editor.selection && Range.isCollapsed(editor.selection))) { + insertBreak(); + return false; + } + + const { slate } = config.settings; + const { anchor } = editor.selection; + + const ref = Editor.rangeRef(editor, editor.selection, { + affinity: 'inward', + }); + + const [listItem, listItemPath] = getCurrentListItem(editor); + if (listItem) { + if (Editor.string(editor, listItemPath)) { + Transforms.splitNodes(editor, { + at: editor.selection, + match: (node) => node.type === slate.listItemType, + always: true, + }); + + return true; + } + } + + const [parent] = Editor.parent(editor, anchor.path); + + if (parent.type !== slate.listItemType || anchor.offset > 0) { + insertBreak(); + return; + } + + Editor.deleteBackward(editor, { unit: 'line' }); + // also account for empty nodes [{text: ''}] + if (Editor.isEmpty(editor, parent)) { + Transforms.removeNodes(editor, { at: ref.current }); + + Transforms.insertNodes(editor, createEmptyParagraph(), { + at: [editor.children.length], + }); + Transforms.select(editor, Editor.end(editor, [])); + + return true; + } + + Transforms.removeNodes(editor, { at: ref.current }); + + if (isCursorAtBlockEnd(editor)) { + Editor.insertNode(editor, createEmptyParagraph()); + return true; + } + return true; + }; + + return editor; +}; diff --git a/packages/volto-slate/src/blocks/Text/extensions/index.js b/packages/volto-slate/src/blocks/Text/extensions/index.js index 06b79dfe7a..981aa3ab73 100644 --- a/packages/volto-slate/src/blocks/Text/extensions/index.js +++ b/packages/volto-slate/src/blocks/Text/extensions/index.js @@ -4,3 +4,4 @@ export * from './breakList'; export * from './withLists'; export * from './isSelected'; export * from './normalizeExternalData'; +export * from './breakListInWidget'; diff --git a/packages/volto-slate/src/blocks/Text/index.jsx b/packages/volto-slate/src/blocks/Text/index.jsx index 64c0a8017f..d8e4993e51 100644 --- a/packages/volto-slate/src/blocks/Text/index.jsx +++ b/packages/volto-slate/src/blocks/Text/index.jsx @@ -22,6 +22,7 @@ import { import { withDeleteSelectionOnEnter } from '@plone/volto-slate/editor/extensions'; import { breakList, + breakListInWidget, withDeserializers, withLists, withSplitBlocksOnBreak, @@ -47,6 +48,7 @@ export default function applyConfig(config) { breakList, normalizeExternalData, ], + slateWidgetExtensions: [breakListInWidget], // Pluggable handlers for the onKeyDown event of // Order matters here. A handler can return `true` to stop executing any diff --git a/packages/volto-slate/src/widgets/HtmlSlateWidget.jsx b/packages/volto-slate/src/widgets/HtmlSlateWidget.jsx index cdd37527ea..226d17e4e6 100644 --- a/packages/volto-slate/src/widgets/HtmlSlateWidget.jsx +++ b/packages/volto-slate/src/widgets/HtmlSlateWidget.jsx @@ -12,6 +12,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import { FormFieldWrapper } from '@plone/volto/components/manage/Widgets'; import SlateEditor from '@plone/volto-slate/editor/SlateEditor'; import { serializeNodes } from '@plone/volto-slate/editor/render'; +import { handleKeyDetached } from '@plone/volto-slate/blocks/Text/keyboard'; import { makeEditor } from '@plone/volto-slate/utils/editor'; import deserialize from '@plone/volto-slate/editor/deserialize'; @@ -19,6 +20,8 @@ import { createEmptyParagraph, normalizeExternalData, } from '@plone/volto-slate/utils'; +import config from '@plone/volto/registry'; + import { ErrorBoundary } from './ErrorBoundary'; import './style.css'; @@ -44,6 +47,8 @@ const HtmlSlateWidget = (props) => { intl, } = props; + const { slateWidgetExtensions } = config.settings.slate; + const [selected, setSelected] = React.useState(focus); const editor = React.useMemo(() => makeEditor(), []); @@ -127,7 +132,10 @@ const HtmlSlateWidget = (props) => { block={block} selected={selected} properties={properties} + extensions={slateWidgetExtensions} + onKeyDown={handleKeyDetached} placeholder={placeholder} + editableProps={{ 'aria-multiline': 'true' }} /> diff --git a/packages/volto-slate/src/widgets/RichTextWidget.jsx b/packages/volto-slate/src/widgets/RichTextWidget.jsx index 6cc151ebc6..f26817fa28 100644 --- a/packages/volto-slate/src/widgets/RichTextWidget.jsx +++ b/packages/volto-slate/src/widgets/RichTextWidget.jsx @@ -7,7 +7,9 @@ import React from 'react'; import isUndefined from 'lodash/isUndefined'; import isString from 'lodash/isString'; import { FormFieldWrapper } from '@plone/volto/components/manage/Widgets'; +import { handleKeyDetached } from '@plone/volto-slate/blocks/Text/keyboard'; import SlateEditor from '@plone/volto-slate/editor/SlateEditor'; +import config from '@plone/volto/registry'; import { createEmptyParagraph, createParagraph } from '../utils/blocks'; @@ -37,6 +39,7 @@ const SlateRichTextWidget = (props) => { readOnly = false, } = props; const [selected, setSelected] = React.useState(focus); + const { slateWidgetExtensions } = config.settings.slate; return ( @@ -62,7 +65,10 @@ const SlateRichTextWidget = (props) => { block={block} selected={selected} properties={properties} + extensions={slateWidgetExtensions} + onKeyDown={handleKeyDetached} placeholder={placeholder} + editableProps={{ 'aria-multiline': 'true' }} /> diff --git a/packages/volto/cypress/tests/coresandbox/fields.js b/packages/volto/cypress/tests/coresandbox/fields.js index 14c27281b0..e5069abf78 100644 --- a/packages/volto/cypress/tests/coresandbox/fields.js +++ b/packages/volto/cypress/tests/coresandbox/fields.js @@ -105,6 +105,33 @@ context('Special fields Acceptance Tests', () => { '

hello world

', ); }); + + it('break list on empty li element', () => { + cy.intercept('PATCH', '/**/document').as('save'); + cy.getSlate().click(); + cy.get('.button .block-add-button').click({ force: true }); + cy.get('.blocks-chooser .mostUsed .button.testBlock').click(); + cy.get('#fieldset-default-field-label-html').click(); + cy.get('.slate_wysiwyg_box [contenteditable=true]') + .type('hello welcome to plone') + .scrollIntoView(); + + cy.setSlateSelection('hello'); + + cy.wait(1000); // th + cy.get('.slate-inline-toolbar').should('be.visible'); + cy.clickSlateButton('Bulleted list'); + cy.get('.slate_wysiwyg_box [contenteditable=true]').should( + 'have.descendants', + 'ul li', + ); + cy.setSlateCursor('plone').type('{enter}').type('{enter}'); + + cy.get('#toolbar-save').click(); + cy.wait('@save'); + + cy.get('.test-block').should('contain.text', '

'); + }); }); describe('ObjectListWidget', () => { diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index 597e954b7a..1b80a4e983 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -628,7 +628,7 @@ msgstr "Wijzigingen zijn opgeslagen." #. Default: "Check this box to customize the title, description, or image of the target content item for this teaser. Leave it unchecked to show updates to the target content item if it is edited later." #: components/manage/Blocks/Teaser/schema msgid "Check this box to customize the title, description, or image of the target content item for this teaser. Leave it unchecked to show updates to the target content item if it is edited later." -msgstr "Vink dit vakje aan om titel, beschrijving of afbeelding van een doel inhoudsitem voor dit voorproefje te wijzigen. Laat het uitgevinkt om updates te tonen voor het doel inhoudsitem indien het later bewerkt zou worden." +msgstr "Vink dit vakje aan om titel, beschrijving of afbeelding van een doel inhoudsitem voor deze teaser te wijzigen. Laat het uitgevinkt om updates te tonen voor het doel inhoudsitem indien het later bewerkt zou worden." #. Default: "Checkbox" #: components/manage/Widgets/SchemaWidget @@ -923,7 +923,7 @@ msgstr "Huidig wachtwoord" #. Default: "Customize teaser content" #: components/manage/Blocks/Teaser/schema msgid "Customize teaser content" -msgstr "Wijzig inhoud voorproefje" +msgstr "Wijzig inhoud teaser" #. Default: "Cut" #: components/manage/Actions/Actions @@ -1649,7 +1649,7 @@ msgstr "Google Maps insluit blok" #. Default: "Grid" #: components/manage/Blocks/Grid/schema msgid "Grid" -msgstr "Rooster" +msgstr "Grid" #. Default: "Group" #: components/manage/Sharing/Sharing @@ -1922,7 +1922,7 @@ msgstr "Ongeldig blok - wordt verwijderd bij opslaan" #. Default: "Invalid teaser source" #: components/manage/Blocks/Teaser/Data msgid "Invalid teaser source" -msgstr "Ongeldige bron voorproefje" +msgstr "Ongeldige bron teaser" #. Default: "It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them." #: helpers/MessageLabels/MessageLabels @@ -3020,7 +3020,7 @@ msgstr "Resultaten limiet" #. Default: "Results preview" #: components/manage/Blocks/Listing/Edit msgid "Results preview" -msgstr "Resultaten voorproefje" +msgstr "Resultaten voorvertoning" #. Default: "Results template" #: components/manage/Blocks/Search/SearchBlockEdit @@ -3686,7 +3686,7 @@ msgstr "Doel aantal objecten in geheugen per cache" #. Default: "Teaser" #: components/manage/Blocks/Teaser/schema msgid "Teaser" -msgstr "Voorproefje" +msgstr "teaser" #. Default: "Text" #: components/manage/Widgets/SchemaWidget diff --git a/packages/volto/news/6476.bugfix b/packages/volto/news/6476.bugfix new file mode 100644 index 0000000000..214e4f3f84 --- /dev/null +++ b/packages/volto/news/6476.bugfix @@ -0,0 +1 @@ +Update Dutch translations. @mauritsvanrees diff --git a/packages/volto/news/6570.internal b/packages/volto/news/6570.internal new file mode 100644 index 0000000000..71c415192b --- /dev/null +++ b/packages/volto/news/6570.internal @@ -0,0 +1 @@ +Test(cypress): fix breaking a list by typing Enter refs- #6586 @nileshgulia1 \ No newline at end of file