Skip to content

Commit f210112

Browse files
authoredFeb 21, 2024
feat(portable-text-editor): new API method getFragment (#5806)
* feat(portable-text-editor): new API method getFragment This function will get the current selection in the editor as Portable Text fragment. * test(portable-text-editor): add tests for getFragment
1 parent db8fd66 commit f210112

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed
 

‎packages/@sanity/portable-text-editor/src/editor/PortableTextEditor.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,8 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
280280
debug(`Host toggling mark`, mark)
281281
editor.editable?.toggleMark(mark)
282282
}
283+
static getFragment = (editor: PortableTextEditor): PortableTextBlock[] | undefined => {
284+
debug(`Host getting fragment`)
285+
return editor.editable?.getFragment()
286+
}
283287
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import {describe, expect, it, jest} from '@jest/globals'
2+
import {isPortableTextTextBlock} from '@sanity/types'
3+
import {render, waitFor} from '@testing-library/react'
4+
import {createRef, type RefObject} from 'react'
5+
6+
import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
7+
import {PortableTextEditor} from '../../PortableTextEditor'
8+
9+
const initialValue = [
10+
{
11+
_key: 'a',
12+
_type: 'myTestBlockType',
13+
children: [
14+
{
15+
_key: 'a1',
16+
_type: 'span',
17+
marks: [],
18+
text: 'Block A',
19+
},
20+
],
21+
markDefs: [],
22+
style: 'normal',
23+
},
24+
{
25+
_key: 'b',
26+
_type: 'myTestBlockType',
27+
children: [
28+
{
29+
_key: 'b1',
30+
_type: 'span',
31+
marks: [],
32+
text: 'Block B ',
33+
},
34+
{
35+
_key: 'b2',
36+
_type: 'someObject',
37+
},
38+
{
39+
_key: 'b3',
40+
_type: 'span',
41+
marks: [],
42+
text: ' contains a inline object',
43+
},
44+
],
45+
markDefs: [],
46+
style: 'normal',
47+
},
48+
]
49+
50+
describe('plugin:withEditableAPI: .getFragment()', () => {
51+
it('can get a Portable Text fragment of the current selection in a single block', async () => {
52+
const editorRef: RefObject<PortableTextEditor> = createRef()
53+
const onChange = jest.fn()
54+
render(
55+
<PortableTextEditorTester
56+
onChange={onChange}
57+
ref={editorRef}
58+
schemaType={schemaType}
59+
value={initialValue}
60+
/>,
61+
)
62+
const initialSelection = {
63+
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6},
64+
anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 7},
65+
}
66+
await waitFor(() => {
67+
if (editorRef.current) {
68+
PortableTextEditor.focus(editorRef.current)
69+
PortableTextEditor.select(editorRef.current, initialSelection)
70+
const fragment = PortableTextEditor.getFragment(editorRef.current)
71+
expect(
72+
fragment && isPortableTextTextBlock(fragment[0]) && fragment[0]?.children[0]?.text,
73+
).toBe('A')
74+
}
75+
})
76+
})
77+
it('can get a Portable Text fragment of the current selection in multiple blocks', async () => {
78+
const editorRef: RefObject<PortableTextEditor> = createRef()
79+
const onChange = jest.fn()
80+
render(
81+
<PortableTextEditorTester
82+
onChange={onChange}
83+
ref={editorRef}
84+
schemaType={schemaType}
85+
value={initialValue}
86+
/>,
87+
)
88+
const initialSelection = {
89+
anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6},
90+
focus: {path: [{_key: 'b'}, 'children', {_key: 'b3'}], offset: 9},
91+
}
92+
await waitFor(() => {
93+
if (editorRef.current) {
94+
PortableTextEditor.focus(editorRef.current)
95+
PortableTextEditor.select(editorRef.current, initialSelection)
96+
const fragment = PortableTextEditor.getFragment(editorRef.current)
97+
expect(fragment).toMatchInlineSnapshot(`
98+
Array [
99+
Object {
100+
"_key": "a",
101+
"_type": "myTestBlockType",
102+
"children": Array [
103+
Object {
104+
"_key": "a1",
105+
"_type": "span",
106+
"marks": Array [],
107+
"text": "A",
108+
},
109+
],
110+
"markDefs": Array [],
111+
"style": "normal",
112+
},
113+
Object {
114+
"_key": "b",
115+
"_type": "myTestBlockType",
116+
"children": Array [
117+
Object {
118+
"_key": "b1",
119+
"_type": "span",
120+
"marks": Array [],
121+
"text": "Block B ",
122+
},
123+
Object {
124+
"_key": "b2",
125+
"_type": "someObject",
126+
},
127+
Object {
128+
"_key": "b3",
129+
"_type": "span",
130+
"marks": Array [],
131+
"text": " contains",
132+
},
133+
],
134+
"markDefs": Array [],
135+
"style": "normal",
136+
},
137+
]
138+
`)
139+
}
140+
})
141+
})
142+
})

‎packages/@sanity/portable-text-editor/src/editor/plugins/createWithEditableAPI.ts

+3
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,9 @@ export function createWithEditableAPI(
512512
editor.insertBreak()
513513
editor.onChange()
514514
},
515+
getFragment: () => {
516+
return fromSlateValue(editor.getFragment(), types.block.name)
517+
},
515518
})
516519
return editor
517520
}

‎packages/@sanity/portable-text-editor/src/types/editor.ts

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export interface EditableAPI {
4949
focusBlock: () => PortableTextBlock | undefined
5050
focusChild: () => PortableTextChild | undefined
5151
getSelection: () => EditorSelection
52+
getFragment: () => PortableTextBlock[] | undefined
5253
getValue: () => PortableTextBlock[] | undefined
5354
hasBlockStyle: (style: string) => boolean
5455
hasListStyle: (listStyle: string) => boolean

0 commit comments

Comments
 (0)
Please sign in to comment.