diff --git a/packages/@sanity/portable-text-editor/src/editor/PortableTextEditor.tsx b/packages/@sanity/portable-text-editor/src/editor/PortableTextEditor.tsx index aea1215e027..e2d09dc2b65 100644 --- a/packages/@sanity/portable-text-editor/src/editor/PortableTextEditor.tsx +++ b/packages/@sanity/portable-text-editor/src/editor/PortableTextEditor.tsx @@ -280,4 +280,8 @@ export class PortableTextEditor extends Component { debug(`Host toggling mark`, mark) editor.editable?.toggleMark(mark) } + static getFragment = (editor: PortableTextEditor): PortableTextBlock[] | undefined => { + debug(`Host getting fragment`) + return editor.editable?.getFragment() + } } diff --git a/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx b/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx new file mode 100644 index 00000000000..81572ada7c7 --- /dev/null +++ b/packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx @@ -0,0 +1,142 @@ +import {describe, expect, it, jest} from '@jest/globals' +import {isPortableTextTextBlock} from '@sanity/types' +import {render, waitFor} from '@testing-library/react' +import {createRef, type RefObject} from 'react' + +import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester' +import {PortableTextEditor} from '../../PortableTextEditor' + +const initialValue = [ + { + _key: 'a', + _type: 'myTestBlockType', + children: [ + { + _key: 'a1', + _type: 'span', + marks: [], + text: 'Block A', + }, + ], + markDefs: [], + style: 'normal', + }, + { + _key: 'b', + _type: 'myTestBlockType', + children: [ + { + _key: 'b1', + _type: 'span', + marks: [], + text: 'Block B ', + }, + { + _key: 'b2', + _type: 'someObject', + }, + { + _key: 'b3', + _type: 'span', + marks: [], + text: ' contains a inline object', + }, + ], + markDefs: [], + style: 'normal', + }, +] + +describe('plugin:withEditableAPI: .getFragment()', () => { + it('can get a Portable Text fragment of the current selection in a single block', async () => { + const editorRef: RefObject = createRef() + const onChange = jest.fn() + render( + , + ) + const initialSelection = { + focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6}, + anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 7}, + } + await waitFor(() => { + if (editorRef.current) { + PortableTextEditor.focus(editorRef.current) + PortableTextEditor.select(editorRef.current, initialSelection) + const fragment = PortableTextEditor.getFragment(editorRef.current) + expect( + fragment && isPortableTextTextBlock(fragment[0]) && fragment[0]?.children[0]?.text, + ).toBe('A') + } + }) + }) + it('can get a Portable Text fragment of the current selection in multiple blocks', async () => { + const editorRef: RefObject = createRef() + const onChange = jest.fn() + render( + , + ) + const initialSelection = { + anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6}, + focus: {path: [{_key: 'b'}, 'children', {_key: 'b3'}], offset: 9}, + } + await waitFor(() => { + if (editorRef.current) { + PortableTextEditor.focus(editorRef.current) + PortableTextEditor.select(editorRef.current, initialSelection) + const fragment = PortableTextEditor.getFragment(editorRef.current) + expect(fragment).toMatchInlineSnapshot(` + Array [ + Object { + "_key": "a", + "_type": "myTestBlockType", + "children": Array [ + Object { + "_key": "a1", + "_type": "span", + "marks": Array [], + "text": "A", + }, + ], + "markDefs": Array [], + "style": "normal", + }, + Object { + "_key": "b", + "_type": "myTestBlockType", + "children": Array [ + Object { + "_key": "b1", + "_type": "span", + "marks": Array [], + "text": "Block B ", + }, + Object { + "_key": "b2", + "_type": "someObject", + }, + Object { + "_key": "b3", + "_type": "span", + "marks": Array [], + "text": " contains", + }, + ], + "markDefs": Array [], + "style": "normal", + }, + ] + `) + } + }) + }) +}) diff --git a/packages/@sanity/portable-text-editor/src/editor/plugins/createWithEditableAPI.ts b/packages/@sanity/portable-text-editor/src/editor/plugins/createWithEditableAPI.ts index 531f05c6965..b89cd5b4cbb 100644 --- a/packages/@sanity/portable-text-editor/src/editor/plugins/createWithEditableAPI.ts +++ b/packages/@sanity/portable-text-editor/src/editor/plugins/createWithEditableAPI.ts @@ -512,6 +512,9 @@ export function createWithEditableAPI( editor.insertBreak() editor.onChange() }, + getFragment: () => { + return fromSlateValue(editor.getFragment(), types.block.name) + }, }) return editor } diff --git a/packages/@sanity/portable-text-editor/src/types/editor.ts b/packages/@sanity/portable-text-editor/src/types/editor.ts index 314d23bfafa..c6952903844 100644 --- a/packages/@sanity/portable-text-editor/src/types/editor.ts +++ b/packages/@sanity/portable-text-editor/src/types/editor.ts @@ -49,6 +49,7 @@ export interface EditableAPI { focusBlock: () => PortableTextBlock | undefined focusChild: () => PortableTextChild | undefined getSelection: () => EditorSelection + getFragment: () => PortableTextBlock[] | undefined getValue: () => PortableTextBlock[] | undefined hasBlockStyle: (style: string) => boolean hasListStyle: (listStyle: string) => boolean