1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-17 04:34:00 +02:00

Fix stale decorations (#4876)

* test changes

* fix decoration not updating

* Add changeset

* Fix lint issues

* Tests with earlier version of Node.js

* Bump node version on CI

The base typescript config uses ESNext as target, so presumably the
latest node should be used.

Co-authored-by: Dylan Schiemann <dylan@dojotoolkit.org>
This commit is contained in:
Nemanja Tosic
2022-03-09 12:06:18 +01:00
committed by GitHub
parent 111f8ca9e3
commit 1b205c087b
11 changed files with 357 additions and 87 deletions

View File

@@ -25,9 +25,11 @@
},
"devDependencies": {
"@babel/runtime": "^7.7.4",
"@types/jest": "^27.4.1",
"@types/jsdom": "^16.2.14",
"@types/react": "^16.9.13",
"@types/react-dom": "^16.9.4",
"jsdom": "^16.6.0",
"@types/react-test-renderer": "^16.8.0",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"react-test-renderer": ">=16.8.0",

View File

@@ -563,7 +563,9 @@ export const Editable = (props: EditableProps) => {
}
}, [scheduleOnDOMSelectionChange])
const decorations = decorate([editor, []])
const decorations = [...Node.nodes(editor)].flatMap(([n, p]) =>
decorate([n, p])
)
if (
placeholder &&

View File

@@ -5,7 +5,7 @@ declare module 'slate' {
interface CustomTypes {
Editor: ReactEditor
Text: BaseText & {
placeholder: string
placeholder?: string
}
Range: BaseRange & {
placeholder?: string

View File

@@ -49,15 +49,8 @@ const useChildren = (props: {
const key = ReactEditor.findKey(editor, n)
const range = Editor.range(editor, p)
const sel = selection && Range.intersection(range, selection)
const ds = decorate([n, p])
for (const dec of decorations) {
const d = Range.intersection(dec, range)
if (d) {
ds.push(d)
}
}
const ds = decorations.filter(dec => Range.intersection(dec, range))
if (Element.isElement(n)) {
children.push(

View File

@@ -1,46 +0,0 @@
import * as Slate from 'slate'
import * as SlateReact from '..'
import { JSDOM } from 'jsdom'
import React from 'react'
import TestRenderer from 'react-test-renderer'
import assert from 'assert'
describe('slate-react', () => {
describe('Editable', () => {
describe('decorate', () => {
// stub out some DOM stuff to avoid crashes
beforeEach(() => {
const jsdom = new JSDOM()
global.window = jsdom.window
global.document = jsdom.window.document
global.Document = document.constructor
})
const createNodeMock = () => ({
ownerDocument: global.document,
getRootNode: () => global.document,
})
it('should be called on all nodes in document', () => {
const editor = SlateReact.withReact(Slate.createEditor())
const value = [{ type: 'block', children: [{ text: '' }] }]
let count = 0
const decorate = ([node, path]) => {
count++
return []
}
const el = React.createElement(
SlateReact.Slate,
{ editor, value },
React.createElement(SlateReact.Editable, { decorate })
)
TestRenderer.create(el, { createNodeMock })
// editor, block, text
assert.strictEqual(count, 3)
})
})
})
})

View File

@@ -0,0 +1,101 @@
import React from 'react'
import { createEditor, NodeEntry, Range } from 'slate'
import { create, act, ReactTestRenderer } from 'react-test-renderer'
import {
Slate,
withReact,
DefaultEditable,
RenderElementProps,
DefaultElement,
} from '../src'
describe('slate-react', () => {
describe('Editable', () => {
describe('decorate', () => {
const createNodeMock = () => ({
ownerDocument: global.document,
getRootNode: () => global.document,
})
it('should be called on all nodes in document', () => {
const editor = withReact(createEditor())
const value = [{ type: 'block', children: [{ text: '' }] }]
const decorate = jest.fn<Range[], [NodeEntry]>(entry => [])
let el: ReactTestRenderer
act(() => {
el = create(
<Slate editor={editor} value={value} onChange={() => {}}>
<DefaultEditable decorate={decorate} />
</Slate>,
{ createNodeMock }
)
})
expect(decorate).toHaveBeenCalledTimes(3)
})
it('should rerender the part of the tree that received an updated decoration', () => {
const editor = withReact(createEditor())
const value = [
{ type: 'block', children: [{ text: '' }] },
{ type: 'block', children: [{ text: '' }] },
]
// initial render does not return
const decorate = jest.fn<Range[], [NodeEntry]>(() => [])
const renderElement = jest.fn<JSX.Element, [RenderElementProps]>(
DefaultElement
)
const onChange = jest.fn<void, []>()
let el: ReactTestRenderer
act(() => {
el = create(
<Slate editor={editor} value={value} onChange={onChange}>
<DefaultEditable
decorate={decorate}
renderElement={renderElement}
/>
</Slate>,
{ createNodeMock }
)
})
expect(renderElement).toHaveBeenCalledTimes(2)
decorate.mockImplementation(([node]) => {
if (node !== value[0].children[0]) {
return []
}
return [
{
anchor: { path: [0, 0], offset: 0 },
focus: { path: [0, 0], offset: 0 },
},
]
})
act(() => {
el.update(
<Slate editor={editor} value={value} onChange={onChange}>
<DefaultEditable
decorate={decorate}
renderElement={renderElement}
/>
</Slate>
)
})
expect(renderElement).toHaveBeenCalledTimes(3)
})
})
})
})

View File

@@ -0,0 +1,4 @@
{
"extends": "../../../config/typescript/tsconfig.json",
"references": [{ "path": "../" }]
}