mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-01-17 13:38:37 +01:00
Add onSelectionChange
and onValueChange
in Slate React component (#5526)
* Add `onSelectorChange` and `onValueChange` in Slate React component * docs: add changeset * fix: fixed lint error * Rename Slate React component `onSelectorChange` to `onSelectionChange`. Add more unit tests. * docs: update changeset --------- Co-authored-by: willliu <willliu@distinctclinic.com>
This commit is contained in:
parent
0bdff51a08
commit
623f44521e
5
.changeset/curly-ligers-lay.md
Normal file
5
.changeset/curly-ligers-lay.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'slate-react': minor
|
||||
---
|
||||
|
||||
Add `onSelectionChange` and `onValueChange` in Slate React component
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Descendant, Editor, Node, Scrubber } from 'slate'
|
||||
import { Descendant, Editor, Node, Operation, Scrubber, Selection } from 'slate'
|
||||
import { FocusedContext } from '../hooks/use-focused'
|
||||
import { useIsomorphicLayoutEffect } from '../hooks/use-isomorphic-layout-effect'
|
||||
import { SlateContext, SlateContextValue } from '../hooks/use-slate'
|
||||
@ -22,8 +22,18 @@ export const Slate = (props: {
|
||||
initialValue: Descendant[]
|
||||
children: React.ReactNode
|
||||
onChange?: (value: Descendant[]) => void
|
||||
onSelectionChange?: (selection: Selection) => void
|
||||
onValueChange?: (value: Descendant[]) => void
|
||||
}) => {
|
||||
const { editor, children, onChange, initialValue, ...rest } = props
|
||||
const {
|
||||
editor,
|
||||
children,
|
||||
onChange,
|
||||
onSelectionChange,
|
||||
onValueChange,
|
||||
initialValue,
|
||||
...rest
|
||||
} = props
|
||||
|
||||
const [context, setContext] = React.useState<SlateContextValue>(() => {
|
||||
if (!Node.isNodeList(initialValue)) {
|
||||
@ -48,17 +58,28 @@ export const Slate = (props: {
|
||||
onChange: handleSelectorChange,
|
||||
} = useSelectorContext(editor)
|
||||
|
||||
const onContextChange = useCallback(() => {
|
||||
const onContextChange = useCallback(
|
||||
(options?: { operation?: Operation }) => {
|
||||
if (onChange) {
|
||||
onChange(editor.children)
|
||||
}
|
||||
|
||||
switch (options?.operation?.type) {
|
||||
case 'set_selection':
|
||||
onSelectionChange?.(editor.selection)
|
||||
break
|
||||
default:
|
||||
onValueChange?.(editor.children)
|
||||
}
|
||||
|
||||
setContext(prevContext => ({
|
||||
v: prevContext.v + 1,
|
||||
editor,
|
||||
}))
|
||||
handleSelectorChange(editor)
|
||||
}, [editor, handleSelectorChange, onChange])
|
||||
},
|
||||
[editor, handleSelectorChange, onChange, onSelectionChange, onValueChange]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
EDITOR_TO_ON_CHANGE.set(editor, onContextChange)
|
||||
|
@ -363,7 +363,7 @@ export const withReact = <T extends BaseEditor>(
|
||||
const onContextChange = EDITOR_TO_ON_CHANGE.get(e)
|
||||
|
||||
if (onContextChange) {
|
||||
onContextChange()
|
||||
onContextChange(options)
|
||||
}
|
||||
|
||||
onChange(options)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Ancestor, Editor, Node, Range, RangeRef, Text } from 'slate'
|
||||
import { Ancestor, Editor, Node, Operation, Range, RangeRef, Text } from 'slate'
|
||||
import { Action } from '../hooks/android-input-manager/android-input-manager'
|
||||
import { TextDiff } from './diff-text'
|
||||
import { Key } from './key'
|
||||
@ -47,7 +47,10 @@ export const EDITOR_TO_USER_SELECTION: WeakMap<
|
||||
* Weak map for associating the context `onChange` context with the plugin.
|
||||
*/
|
||||
|
||||
export const EDITOR_TO_ON_CHANGE = new WeakMap<Editor, () => void>()
|
||||
export const EDITOR_TO_ON_CHANGE = new WeakMap<
|
||||
Editor,
|
||||
(options?: { operation?: Operation }) => void
|
||||
>()
|
||||
|
||||
/**
|
||||
* Weak maps for saving pending state on composition stage.
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { createEditor, Element, Transforms } from 'slate'
|
||||
import { createEditor, Text, Transforms } from 'slate'
|
||||
import { create, act, ReactTestRenderer } from 'react-test-renderer'
|
||||
import { Slate, withReact, Editable } from '../src'
|
||||
|
||||
@ -95,4 +95,107 @@ describe('slate-react', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('calls onSelectionChange when editor select change', async () => {
|
||||
const editor = withReact(createEditor())
|
||||
const initialValue = [
|
||||
{ type: 'block', children: [{ text: 'te' }] },
|
||||
{ type: 'block', children: [{ text: 'st' }] },
|
||||
]
|
||||
const onChange = jest.fn()
|
||||
const onValueChange = jest.fn()
|
||||
const onSelectionChange = jest.fn()
|
||||
|
||||
act(() => {
|
||||
create(
|
||||
<Slate
|
||||
editor={editor}
|
||||
initialValue={initialValue}
|
||||
onChange={onChange}
|
||||
onValueChange={onValueChange}
|
||||
onSelectionChange={onSelectionChange}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>,
|
||||
{ createNodeMock }
|
||||
)
|
||||
})
|
||||
|
||||
await act(async () =>
|
||||
Transforms.select(editor, { path: [0, 0], offset: 2 })
|
||||
)
|
||||
|
||||
expect(onSelectionChange).toHaveBeenCalled()
|
||||
expect(onChange).toHaveBeenCalled()
|
||||
expect(onValueChange).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('calls onValueChange when editor children change', async () => {
|
||||
const editor = withReact(createEditor())
|
||||
const initialValue = [{ type: 'block', children: [{ text: 'test' }] }]
|
||||
const onChange = jest.fn()
|
||||
const onValueChange = jest.fn()
|
||||
const onSelectionChange = jest.fn()
|
||||
|
||||
act(() => {
|
||||
create(
|
||||
<Slate
|
||||
editor={editor}
|
||||
initialValue={initialValue}
|
||||
onChange={onChange}
|
||||
onValueChange={onValueChange}
|
||||
onSelectionChange={onSelectionChange}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>,
|
||||
{ createNodeMock }
|
||||
)
|
||||
})
|
||||
|
||||
await act(async () => Transforms.insertText(editor, 'Hello word!'))
|
||||
|
||||
expect(onValueChange).toHaveBeenCalled()
|
||||
expect(onChange).toHaveBeenCalled()
|
||||
expect(onSelectionChange).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('calls onValueChange when editor setNodes', async () => {
|
||||
const editor = withReact(createEditor())
|
||||
const initialValue = [{ type: 'block', children: [{ text: 'test' }] }]
|
||||
const onChange = jest.fn()
|
||||
const onValueChange = jest.fn()
|
||||
const onSelectionChange = jest.fn()
|
||||
|
||||
act(() => {
|
||||
create(
|
||||
<Slate
|
||||
editor={editor}
|
||||
initialValue={initialValue}
|
||||
onChange={onChange}
|
||||
onValueChange={onValueChange}
|
||||
onSelectionChange={onSelectionChange}
|
||||
>
|
||||
<Editable />
|
||||
</Slate>,
|
||||
{ createNodeMock }
|
||||
)
|
||||
})
|
||||
|
||||
await act(async () =>
|
||||
Transforms.setNodes(
|
||||
editor,
|
||||
// @ts-ignore
|
||||
{ bold: true },
|
||||
{
|
||||
at: { path: [0, 0], offset: 2 },
|
||||
match: Text.isText,
|
||||
split: true,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
expect(onChange).toHaveBeenCalled()
|
||||
expect(onValueChange).toHaveBeenCalled()
|
||||
expect(onSelectionChange).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user