Skip to content

Commit

Permalink
Fix child element decorations (#4910)
Browse files Browse the repository at this point in the history
* fix slate-react handling of nested element decorations

* chore: add changeset

* changes from review
  • Loading branch information
jasonphillips committed Mar 25, 2022
1 parent 43ca2b5 commit 2a8d86f
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/poor-hats-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'slate-react': patch
---

Fix decorations applied across nested elements
8 changes: 5 additions & 3 deletions packages/slate-react/src/hooks/use-children.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import ElementComponent from '../components/element'
import TextComponent from '../components/text'
import { ReactEditor } from '..'
import { useSlateStatic } from './use-slate-static'
import { useDecorate } from './use-decorate'
import { NODE_TO_INDEX, NODE_TO_PARENT } from '../utils/weak-maps'
import {
RenderElementProps,
Expand Down Expand Up @@ -34,7 +33,6 @@ const useChildren = (props: {
renderLeaf,
selection,
} = props
const decorate = useDecorate()
const editor = useSlateStatic()
const path = ReactEditor.findPath(editor, node)
const children = []
Expand All @@ -50,7 +48,11 @@ const useChildren = (props: {
const range = Editor.range(editor, p)
const sel = selection && Range.intersection(range, selection)

const ds = decorations.filter(dec => Range.intersection(dec, range))
const ds = decorations.reduce<Range[]>((acc, dec) => {
const intersection = Range.intersection(dec, range)
if (intersection) acc.push(intersection)
return acc
}, [])

if (Element.isElement(n)) {
children.push(
Expand Down
75 changes: 75 additions & 0 deletions packages/slate-react/test/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
withReact,
DefaultEditable,
RenderElementProps,
RenderLeafProps,
DefaultElement,
DefaultLeaf,
} from '../src'

describe('slate-react', () => {
Expand Down Expand Up @@ -96,6 +98,79 @@ describe('slate-react', () => {

expect(renderElement).toHaveBeenCalledTimes(3)
})

it('should pass the intersecting part of decorations to nested elements', () => {
const editor = withReact(createEditor())

const value = [
{
type: 'parent',
children: [
{ type: 'block', children: [{ text: 'foo', highlight: false }] },
{ type: 'block', children: [{ text: 'bar', highlight: false }] },
{ type: 'block', children: [{ text: 'baz', highlight: false }] },
],
},
]

const decorate = jest.fn<Range[], [NodeEntry]>(([node]) => {
if (node !== value[0]) {
return []
}
return [
{
anchor: { path: [0, 1, 0], offset: 1 },
focus: { path: [0, 2, 0], offset: 2 },
highlight: true,
},
]
})

const renderLeaf = jest.fn<JSX.Element, [RenderLeafProps]>(DefaultLeaf)
const onChange = jest.fn<void, []>()
let el: ReactTestRenderer

act(() => {
el = create(
<Slate editor={editor} value={value} onChange={onChange}>
<DefaultEditable decorate={decorate} renderLeaf={renderLeaf} />
</Slate>,
{ createNodeMock }
)
})

// 4 renders, for foo,b,ar,ba,z
expect(renderLeaf).toHaveBeenCalledTimes(5)
expect(renderLeaf.mock.calls).toEqual(
expect.arrayContaining([
[
expect.objectContaining({
leaf: { highlight: false, text: 'foo' },
}),
],
[
expect.objectContaining({
leaf: { highlight: false, text: 'b' },
}),
],
[
expect.objectContaining({
leaf: { highlight: true, text: 'ar' },
}),
],
[
expect.objectContaining({
leaf: { highlight: true, text: 'ba' },
}),
],
[
expect.objectContaining({
leaf: { highlight: false, text: 'z' },
}),
],
])
)
})
})
})
})

0 comments on commit 2a8d86f

Please sign in to comment.