diff --git a/packages/@sanity/portable-text-editor/e2e-tests/__tests__/selectionAdjustment.collaborative.test.ts b/packages/@sanity/portable-text-editor/e2e-tests/__tests__/selectionAdjustment.collaborative.test.ts index 056a611fac6..48a61782569 100644 --- a/packages/@sanity/portable-text-editor/e2e-tests/__tests__/selectionAdjustment.collaborative.test.ts +++ b/packages/@sanity/portable-text-editor/e2e-tests/__tests__/selectionAdjustment.collaborative.test.ts @@ -298,13 +298,13 @@ describe('selection adjustment', () => { ], }, { - _key: 'B-7', + _key: 'B-6', _type: 'block', markDefs: [], style: 'normal', children: [ { - _key: 'B-6', + _key: 'B-5', _type: 'span', text: '', marks: [], diff --git a/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withInsertBreak.test.tsx b/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withInsertBreak.test.tsx index 962ac64cf94..488a2b04f30 100644 --- a/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withInsertBreak.test.tsx +++ b/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withInsertBreak.test.tsx @@ -83,6 +83,65 @@ describe('plugin:withInsertBreak: "enter"', () => { } }) }) + it('inserts the new block after if key enter is pressed at the start of the block, creating a new one in "after" position if the block is empty', async () => { + const initialSelection = { + focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0}, + anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0}, + } + const emptyBlock = { + _key: 'a', + _type: 'myTestBlockType', + children: [ + { + _key: 'a1', + _type: 'span', + marks: [], + text: '', + }, + ], + markDefs: [], + style: 'normal', + } + + const editorRef: RefObject = createRef() + const onChange = jest.fn() + render( + , + ) + const editor = editorRef.current + const inlineType = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject') + await waitFor(async () => { + if (editor && inlineType) { + PortableTextEditor.focus(editor) + PortableTextEditor.select(editor, initialSelection) + PortableTextEditor.insertBreak(editor) + + const value = PortableTextEditor.getValue(editor) + expect(value).toEqual([ + emptyBlock, + { + _key: '2', + _type: 'myTestBlockType', + markDefs: [], + style: 'normal', + children: [ + { + _key: '1', + _type: 'span', + marks: [], + text: '', + }, + ], + }, + ]) + } + }) + }) it('splits the text block key if enter is pressed at the middle of the block', async () => { const initialSelection = { focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 2}, diff --git a/packages/@sanity/portable-text-editor/src/editor/plugins/createWithInsertBreak.ts b/packages/@sanity/portable-text-editor/src/editor/plugins/createWithInsertBreak.ts index 7245763ede7..37b0a64f032 100644 --- a/packages/@sanity/portable-text-editor/src/editor/plugins/createWithInsertBreak.ts +++ b/packages/@sanity/portable-text-editor/src/editor/plugins/createWithInsertBreak.ts @@ -1,15 +1,16 @@ import {Editor, Node, Path, Range, Transforms} from 'slate' -import {type PortableTextSlateEditor} from '../../types/editor' +import {type PortableTextMemberSchemaTypes, type PortableTextSlateEditor} from '../../types/editor' import {type SlateTextBlock, type VoidElement} from '../../types/slate' +import {isEqualToEmptyEditor} from '../../utils/values' /** * Changes default behavior of insertBreak to insert a new block instead of splitting current when the cursor is at the * start of the block. */ -export function createWithInsertBreak(): ( - editor: PortableTextSlateEditor, -) => PortableTextSlateEditor { +export function createWithInsertBreak( + types: PortableTextMemberSchemaTypes, +): (editor: PortableTextSlateEditor) => PortableTextSlateEditor { return function withInsertBreak(editor: PortableTextSlateEditor): PortableTextSlateEditor { const {insertBreak} = editor @@ -23,7 +24,8 @@ export function createWithInsertBreak(): ( const [, end] = Range.edges(editor.selection) // If it's at the start of block, we want to preserve the current block key and insert a new one in the current position instead of splitting the node. const isEndAtStartOfNode = Editor.isStart(editor, end, end.path) - if (isEndAtStartOfNode) { + const isEmptyTextBlock = focusBlock && isEqualToEmptyEditor([focusBlock], types) + if (isEndAtStartOfNode && !isEmptyTextBlock) { Editor.insertNode(editor, editor.pteCreateEmptyBlock()) const [nextBlockPath] = Path.next(focusBlockPath) Transforms.select(editor, { diff --git a/packages/@sanity/portable-text-editor/src/editor/plugins/index.ts b/packages/@sanity/portable-text-editor/src/editor/plugins/index.ts index 769e9f7e728..0a52b351899 100644 --- a/packages/@sanity/portable-text-editor/src/editor/plugins/index.ts +++ b/packages/@sanity/portable-text-editor/src/editor/plugins/index.ts @@ -83,7 +83,7 @@ export const withPlugins = ( const withPlaceholderBlock = createWithPlaceholderBlock() - const withInsertBreak = createWithInsertBreak() + const withInsertBreak = createWithInsertBreak(schemaTypes) const withUtils = createWithUtils({keyGenerator, schemaTypes, portableTextEditor}) const withPortableTextSelections = createWithPortableTextSelections(change$, schemaTypes) diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/PresenceCursors.spec.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/PresenceCursors.spec.tsx index 43601922fb8..eee3e000438 100644 --- a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/PresenceCursors.spec.tsx +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/PresenceCursors.spec.tsx @@ -65,8 +65,8 @@ async function getSiblingTextContent(page: Page) { const cursorB = document.querySelector('[data-testid="presence-cursor-User-B"]') return { - cursorA: cursorA?.nextElementSibling?.textContent, - cursorB: cursorB?.nextElementSibling?.textContent, + cursorA: cursorA?.nextElementSibling?.nextElementSibling?.textContent, + cursorB: cursorB?.nextElementSibling?.nextElementSibling?.textContent, } }) } diff --git a/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/UserPresenceCursor.tsx b/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/UserPresenceCursor.tsx index d7a0b50a090..84e6966edf0 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/UserPresenceCursor.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/UserPresenceCursor.tsx @@ -108,11 +108,12 @@ const UserText = styled(motion(Text))` ` interface UserPresenceCursorProps { + children?: React.ReactNode user: User } export function UserPresenceCursor(props: UserPresenceCursorProps): JSX.Element { - const {user} = props + const {children, user} = props const {tints} = useUserColor(user.id) const [hovered, setHovered] = useState(false) @@ -125,39 +126,42 @@ export function UserPresenceCursor(props: UserPresenceCursorProps): JSX.Element ) return ( - - - {hovered && ( - - + + + {hovered && ( + - {user.displayName} - - - )} - - - - + + {user.displayName} + + + )} + + + + + {children} + ) } diff --git a/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/usePresenceCursorDecorations.tsx b/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/usePresenceCursorDecorations.tsx index 2de9fb711fb..bfe5c85f2fe 100644 --- a/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/usePresenceCursorDecorations.tsx +++ b/packages/sanity/src/core/form/inputs/PortableText/presence-cursors/usePresenceCursorDecorations.tsx @@ -79,7 +79,9 @@ export function usePresenceCursorDecorations( const cursorPoint = {focus: presence.selection.focus, anchor: presence.selection.focus} return { - component: () => , + component: ({children}) => ( + {children} + ), selection: cursorPoint, onMoved: handleRangeDecorationMoved, payload: {sessionId: presence.sessionId},