Skip to content

Commit 96bc72b

Browse files
authoredMar 14, 2024
fix(portable-text-editor): fix and test issue with merge block operation (#5996)
* test(portable-text-editor): fix wrong path used in package script (dev) * test(portable-text-editor): fix debug statement and remove redundant var * test(portable-text-editr): add collab test for merge empty block
1 parent 51d3bbd commit 96bc72b

File tree

3 files changed

+70
-5
lines changed

3 files changed

+70
-5
lines changed
 

‎packages/@sanity/portable-text-editor/e2e-tests/__tests__/writingTogether.collaborative.test.ts

+61
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,67 @@ describe('collaborate editing', () => {
6565
})
6666
})
6767

68+
it('should not remove content for both users if one user backspaces into a block that starts with a mark.', async () => {
69+
const exampleValue = [
70+
{
71+
_key: 'randomKey0',
72+
_type: 'block',
73+
children: [
74+
{
75+
_key: 'randomKey1',
76+
_type: 'span',
77+
marks: ['strong'],
78+
text: 'Example Text: ',
79+
},
80+
{
81+
_type: 'span',
82+
marks: [],
83+
text: "This is a very long example text that will completely disappear later on. It's kind of a bad magic trick, really. Just writing more text so the disappearance becomes more apparent. This is a very long example text that will completely disappear later on. It's kind of a bad magic trick, really. Just writing more text so the disappearance becomes more apparent. This is a very long example text that will completely disappear later on. It's kind of a bad magic trick, really. Just writing more text so the disappearance becomes more apparent.",
84+
_key: 'randomKey2',
85+
},
86+
],
87+
markDefs: [],
88+
style: 'normal',
89+
},
90+
]
91+
await setDocumentValue(exampleValue)
92+
const [editorA, editorB] = await getEditors()
93+
await editorA.setSelection({
94+
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey2'}], offset: 542},
95+
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey2'}], offset: 542},
96+
})
97+
await editorA.pressKey('Enter')
98+
await editorA.pressKey('Backspace')
99+
100+
await new Promise((resolve) => setTimeout(resolve, 1000))
101+
102+
const valA = await editorA.getValue()
103+
const valB = await editorB.getValue()
104+
expect(valA).toEqual(valB)
105+
expect(valB).toEqual([
106+
{
107+
_key: 'randomKey0',
108+
_type: 'block',
109+
children: [
110+
{
111+
_key: 'randomKey1',
112+
_type: 'span',
113+
marks: ['strong'],
114+
text: 'Example Text: ',
115+
},
116+
{
117+
_type: 'span',
118+
marks: [],
119+
text: "This is a very long example text that will completely disappear later on. It's kind of a bad magic trick, really. Just writing more text so the disappearance becomes more apparent. This is a very long example text that will completely disappear later on. It's kind of a bad magic trick, really. Just writing more text so the disappearance becomes more apparent. This is a very long example text that will completely disappear later on. It's kind of a bad magic trick, really. Just writing more text so the disappearance becomes more apparent.",
120+
_key: 'randomKey2',
121+
},
122+
],
123+
markDefs: [],
124+
style: 'normal',
125+
},
126+
])
127+
})
128+
68129
it('will reset the value when someone deletes everything, and when they start to type again, they will produce their own respective blocks.', async () => {
69130
await setDocumentValue(initialValue)
70131
const [editorA, editorB] = await getEditors()

‎packages/@sanity/portable-text-editor/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"clean": "rimraf lib",
5858
"lint": "eslint .",
5959
"prettier": "prettier --write './**/*.{ts,tsx,js,css,html}'",
60-
"dev": "cd ./test/ && ts-node serve",
60+
"dev": "cd ./e2e-tests/ && ts-node serve",
6161
"test": "jest",
6262
"test:e2e": "jest --config=e2e-tests/e2e.config.cjs",
6363
"test:watch": "jest --watch",

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

+8-4
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,22 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
250250
} else if (Element.isElement(block) && patch.path.length === 1 && blockPath) {
251251
debug('Setting block property')
252252
const {children, ...nextRest} = value as unknown as PortableTextBlock
253-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
253+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
254254
const {children: prevChildren, ...prevRest} = block || {children: undefined}
255+
// Set any block properties
255256
editor.apply({
256257
type: 'set_node',
257258
path: blockPath,
258259
properties: {...prevRest},
259260
newProperties: nextRest,
260261
})
262+
// Replace the children in the block
263+
// Note that children must be explicitly inserted, and can't be set with set_node
264+
debug('Setting children')
261265
block.children.forEach((c, cIndex) => {
262266
editor.apply({
263267
type: 'remove_node',
264-
path: blockPath.concat(cIndex),
268+
path: blockPath.concat(block.children.length - 1 - cIndex),
265269
node: c,
266270
})
267271
})
@@ -306,6 +310,7 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
306310
return true
307311
}
308312
const {block, blockPath, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
313+
309314
// Single blocks
310315
if (patch.path.length === 1) {
311316
if (!block || !blockPath) {
@@ -327,11 +332,10 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
327332
debug('Child not found')
328333
return false
329334
}
330-
const childIndex = childPath[1]
331335
debug(`Unsetting child at path ${JSON.stringify(childPath)}`)
332336
debugState(editor, 'before')
333337
if (debugVerbose) {
334-
debug(`Removing child at path ${JSON.stringify([childPath, childIndex])}`)
338+
debug(`Removing child at path ${JSON.stringify(childPath)}`)
335339
}
336340
Transforms.removeNodes(editor, {at: childPath})
337341
debugState(editor, 'after')

0 commit comments

Comments
 (0)
Please sign in to comment.