diff --git a/.changeset/thick-fishes-dance.md b/.changeset/thick-fishes-dance.md new file mode 100644 index 000000000..a9aa4d92b --- /dev/null +++ b/.changeset/thick-fishes-dance.md @@ -0,0 +1,7 @@ +--- +'slate-react': minor +--- + +Fixes a bug with `ReactEditor.focus` where it would throw an error if the editor was in the middle of applying pending operations. +With this change, setting focus will be retried until the editor no longer has any pending operations. +Calling `ReactEditor.focus` on a editor without a current selection, will now make a selection in the top of the document. diff --git a/packages/slate-react/package.json b/packages/slate-react/package.json index b83d0b3c8..f091eb21b 100644 --- a/packages/slate-react/package.json +++ b/packages/slate-react/package.json @@ -26,15 +26,14 @@ }, "devDependencies": { "@babel/runtime": "^7.23.2", + "@testing-library/react": "^14.0.0", "@types/jest": "29.5.6", "@types/jsdom": "^21.1.4", "@types/react": "^18.2.28", "@types/react-dom": "^18.2.13", - "@types/react-test-renderer": "^18.0.3", "@types/resize-observer-browser": "^0.1.8", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-test-renderer": "^18.2.0", "slate": "^0.100.0", "slate-hyperscript": "^0.100.0", "source-map-loader": "^4.0.1" diff --git a/packages/slate-react/src/plugin/react-editor.ts b/packages/slate-react/src/plugin/react-editor.ts index 6c4d5e817..422b1e816 100644 --- a/packages/slate-react/src/plugin/react-editor.ts +++ b/packages/slate-react/src/plugin/react-editor.ts @@ -117,7 +117,7 @@ export interface ReactEditorInterface { /** * Focus the editor. */ - focus: (editor: ReactEditor) => void + focus: (editor: ReactEditor, options?: { retries: number }) => void /** * Return the host window of the current editor. @@ -411,19 +411,44 @@ export const ReactEditor: ReactEditorInterface = { ) }, - focus: editor => { + focus: (editor, options = { retries: 5 }) => { + // Return if already focused + if (IS_FOCUSED.get(editor)) { + return + } + + // Retry setting focus if the editor has pending operations. + // The DOM (selection) is unstable while changes are applied. + // Retry until retries are exhausted or editor is focused. + if (options.retries <= 0) { + throw new Error( + 'Could not set focus, editor seems stuck with pending operations' + ) + } + if (editor.operations.length > 0) { + setTimeout(() => { + ReactEditor.focus(editor, { retries: options.retries - 1 }) + }, 10) + return + } + const el = ReactEditor.toDOMNode(editor, editor) const root = ReactEditor.findDocumentOrShadowRoot(editor) - IS_FOCUSED.set(editor, true) - if (root.activeElement !== el) { + // Ensure that the DOM selection state is set to the editor's selection if (editor.selection && root instanceof Document) { const domSelection = root.getSelection() const domRange = ReactEditor.toDOMRange(editor, editor.selection) domSelection?.removeAllRanges() domSelection?.addRange(domRange) } + // Create a new selection in the top of the document if missing + if (!editor.selection) { + Transforms.select(editor, Editor.start(editor, [])) + editor.onChange() + } el.focus({ preventScroll: true }) + IS_FOCUSED.set(editor, true) } }, diff --git a/packages/slate-react/test/editable.spec.tsx b/packages/slate-react/test/editable.spec.tsx new file mode 100644 index 000000000..d907cc1e5 --- /dev/null +++ b/packages/slate-react/test/editable.spec.tsx @@ -0,0 +1,204 @@ +import React, { useEffect } from 'react' +import { createEditor, Text, Transforms } from 'slate' +import { act, render } from '@testing-library/react' +import { Slate, withReact, Editable } from '../src' + +describe('slate-react', () => { + describe('Editable', () => { + describe('NODE_TO_KEY logic', () => { + test('should not unmount the node that gets split on a split_node operation', async () => { + const editor = withReact(createEditor()) + const initialValue = [{ type: 'block', children: [{ text: 'test' }] }] + const mounts = jest.fn() + + act(() => { + render( + {}} + > + { + useEffect(() => mounts(), []) + + return children + }} + /> + + ) + }) + + // slate updates at next tick, so we need this to be async + await act(async () => + Transforms.splitNodes(editor, { at: { path: [0, 0], offset: 2 } }) + ) + + // 2 renders, one for the main element and one for the split element + expect(mounts).toHaveBeenCalledTimes(2) + }) + + test('should not unmount the node that gets merged into on a merge_node operation', async () => { + const editor = withReact(createEditor()) + const initialValue = [ + { type: 'block', children: [{ text: 'te' }] }, + { type: 'block', children: [{ text: 'st' }] }, + ] + const mounts = jest.fn() + + act(() => { + render( + {}} + > + { + useEffect(() => mounts(), []) + + return children + }} + /> + + ) + }) + + // slate updates at next tick, so we need this to be async + await act(async () => + Transforms.mergeNodes(editor, { at: { path: [0, 0], offset: 0 } }) + ) + + // only 2 renders for the initial render + expect(mounts).toHaveBeenCalledTimes(2) + }) + }) + 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(() => { + render( + + + + ) + }) + + 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(() => { + render( + + + + ) + }) + + 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(() => { + render( + + + + ) + }) + + 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() + }) + + 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(() => { + render( + + + + ) + }) + + await act(async () => Transforms.insertText(editor, 'Hello word!')) + + expect(onValueChange).toHaveBeenCalled() + expect(onChange).toHaveBeenCalled() + expect(onSelectionChange).not.toHaveBeenCalled() + }) + }) +}) diff --git a/packages/slate-react/test/index.spec.tsx b/packages/slate-react/test/index.spec.tsx deleted file mode 100644 index 47de4135b..000000000 --- a/packages/slate-react/test/index.spec.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React, { useEffect } from 'react' -import { createEditor, Text, Transforms } from 'slate' -import { create, act, ReactTestRenderer } from 'react-test-renderer' -import { Slate, withReact, Editable } from '../src' - -const createNodeMock = () => ({ - ownerDocument: global.document, - getRootNode: () => global.document, -}) - -class MockResizeObserver { - observe() {} - - unobserve() {} - - disconnect() {} -} - -describe('slate-react', () => { - window.ResizeObserver = MockResizeObserver as any - - describe('Editable', () => { - describe('NODE_TO_KEY logic', () => { - test('should not unmount the node that gets split on a split_node operation', async () => { - const editor = withReact(createEditor()) - const initialValue = [{ type: 'block', children: [{ text: 'test' }] }] - const mounts = jest.fn() - - let el: ReactTestRenderer - - act(() => { - el = create( - {}} - > - { - useEffect(() => mounts(), []) - - return children - }} - /> - , - { createNodeMock } - ) - }) - - // slate updates at next tick, so we need this to be async - await act(async () => - Transforms.splitNodes(editor, { at: { path: [0, 0], offset: 2 } }) - ) - - // 2 renders, one for the main element and one for the split element - expect(mounts).toHaveBeenCalledTimes(2) - }) - - test('should not unmount the node that gets merged into on a merge_node operation', async () => { - const editor = withReact(createEditor()) - const initialValue = [ - { type: 'block', children: [{ text: 'te' }] }, - { type: 'block', children: [{ text: 'st' }] }, - ] - const mounts = jest.fn() - - let el: ReactTestRenderer - - act(() => { - el = create( - {}} - > - { - useEffect(() => mounts(), []) - - return children - }} - /> - , - { createNodeMock } - ) - }) - - // slate updates at next tick, so we need this to be async - await act(async () => - Transforms.mergeNodes(editor, { at: { path: [0, 0], offset: 0 } }) - ) - - // only 2 renders for the initial render - expect(mounts).toHaveBeenCalledTimes(2) - }) - }) - }) - - 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( - - - , - { 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( - - - , - { 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( - - - , - { 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() - }) -}) diff --git a/packages/slate-react/test/react-editor.spec.tsx b/packages/slate-react/test/react-editor.spec.tsx new file mode 100644 index 000000000..6ebd5c310 --- /dev/null +++ b/packages/slate-react/test/react-editor.spec.tsx @@ -0,0 +1,90 @@ +import React, { useEffect } from 'react' +import { createEditor, Text, Transforms } from 'slate' +import { act, render } from '@testing-library/react' +import { Slate, withReact, Editable, ReactEditor } from '../src' + +describe('slate-react', () => { + describe('ReactEditor', () => { + describe('.focus', () => { + test('should set focus in top of document with no editor selection', async () => { + const editor = withReact(createEditor()) + const initialValue = [{ type: 'block', children: [{ text: 'test' }] }] + const testSelection = { + anchor: { path: [0, 0], offset: 0 }, + focus: { path: [0, 0], offset: 0 }, + } + + act(() => { + render( + + + + ) + }) + + expect(editor.selection).toBe(null) + + await act(async () => { + ReactEditor.focus(editor) + }) + + expect(editor.selection).toEqual(testSelection) + + await act(async () => { + const windowSelection = ReactEditor.getWindow(editor).getSelection() + expect(windowSelection?.focusNode?.textContent).toBe('test') + expect(windowSelection?.anchorNode?.textContent).toBe('test') + expect(windowSelection?.anchorOffset).toBe( + testSelection.anchor.offset + ) + expect(windowSelection?.focusOffset).toBe(testSelection.focus.offset) + }) + }) + + test('should be able to call .focus without getting toDOMNode errors', async () => { + const editor = withReact(createEditor()) + const initialValue = [{ type: 'block', children: [{ text: 'test' }] }] + const propagatedValue = [ + { type: 'block', children: [{ text: 'foo' }] }, + { type: 'block', children: [{ text: 'bar' }] }, + ] + + const testSelection = { + anchor: { path: [1, 0], offset: 0 }, + focus: { path: [1, 0], offset: 3 }, + } + + act(() => { + render( + + + + ) + }) + + await act(async () => { + Transforms.removeNodes(editor, { at: [0] }) + Transforms.insertNodes(editor, propagatedValue) + ReactEditor.focus(editor) // Note: calling focus in the middle of these transformations. + Transforms.select(editor, testSelection) + }) + + expect(editor.selection).toEqual(testSelection) + + await act(async () => { + ReactEditor.focus(editor) + }) + + await act(async () => { + const windowSelection = ReactEditor.getWindow(editor).getSelection() + expect(windowSelection?.focusNode?.textContent).toBe('bar') + expect(windowSelection?.anchorNode?.textContent).toBe('bar') + expect(windowSelection?.anchorOffset).toBe( + testSelection.anchor.offset + ) + expect(windowSelection?.focusOffset).toBe(testSelection.focus.offset) + }) + }) + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index 0ff5dfcc5..9b0d4c048 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3223,6 +3223,36 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^9.0.0": + version: 9.3.3 + resolution: "@testing-library/dom@npm:9.3.3" + dependencies: + "@babel/code-frame": ^7.10.4 + "@babel/runtime": ^7.12.5 + "@types/aria-query": ^5.0.1 + aria-query: 5.1.3 + chalk: ^4.1.0 + dom-accessibility-api: ^0.5.9 + lz-string: ^1.5.0 + pretty-format: ^27.0.2 + checksum: 34e0a564da7beb92aa9cc44a9080221e2412b1a132eb37be3d513fe6c58027674868deb9f86195756d98d15ba969a30fe00632a4e26e25df2a5a4f6ac0686e37 + languageName: node + linkType: hard + +"@testing-library/react@npm:^14.0.0": + version: 14.0.0 + resolution: "@testing-library/react@npm:14.0.0" + dependencies: + "@babel/runtime": ^7.12.5 + "@testing-library/dom": ^9.0.0 + "@types/react-dom": ^18.0.0 + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 4a54c8f56cc4a39b50803205f84f06280bb76521d6d5d4b3b36651d760c7c7752ef142d857d52aaf4fad4848ed7a8be49afc793a5dda105955d2f8bef24901ac + languageName: node + linkType: hard + "@tootallnate/once@npm:1": version: 1.1.2 resolution: "@tootallnate/once@npm:1.1.2" @@ -3254,6 +3284,13 @@ __metadata: languageName: node linkType: hard +"@types/aria-query@npm:^5.0.1": + version: 5.0.4 + resolution: "@types/aria-query@npm:5.0.4" + checksum: ad8b87e4ad64255db5f0a73bc2b4da9b146c38a3a8ab4d9306154334e0fc67ae64e76bfa298eebd1e71830591fb15987e5de7111bdb36a2221bdc379e3415fb0 + languageName: node + linkType: hard + "@types/babel__core@npm:^7.1.14": version: 7.1.15 resolution: "@types/babel__core@npm:7.1.15" @@ -3470,6 +3507,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^18.0.0": + version: 18.2.15 + resolution: "@types/react-dom@npm:18.2.15" + dependencies: + "@types/react": "*" + checksum: 8e9631600c21ff561328e38a951d1991b3b3b20f538af4c0efbd1327c883a5573a63f50e1b945c34fa51b114b30e1ca5e62317bd54f21e063d6697b4be843a03 + languageName: node + linkType: hard + "@types/react-dom@npm:^18.2.13": version: 18.2.13 resolution: "@types/react-dom@npm:18.2.13" @@ -3479,15 +3525,6 @@ __metadata: languageName: node linkType: hard -"@types/react-test-renderer@npm:^18.0.3": - version: 18.0.3 - resolution: "@types/react-test-renderer@npm:18.0.3" - dependencies: - "@types/react": "*" - checksum: 6834c7c998d9e699b7b9aca31ce0f550a29a63526a9ab15f62764eeade28fcce634030e5cfd223a8417e8c53234e01bfb961906b35fd9a72dd513aba549181f0 - languageName: node - linkType: hard - "@types/react@npm:*, @types/react@npm:^18.2.28": version: 18.2.28 resolution: "@types/react@npm:18.2.28" @@ -4014,6 +4051,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: ^2.0.5 + checksum: 929ff95f02857b650fb4cbcd2f41072eee2f46159a6605ea03bf63aa572e35ffdff43d69e815ddc462e16e07de8faba3978afc2813650b4448ee18c9895d982b + languageName: node + linkType: hard + "array-buffer-byte-length@npm:^1.0.0": version: 1.0.0 resolution: "array-buffer-byte-length@npm:1.0.0" @@ -5671,6 +5717,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.2 + resolution: "deep-equal@npm:2.2.2" + dependencies: + array-buffer-byte-length: ^1.0.0 + call-bind: ^1.0.2 + es-get-iterator: ^1.1.3 + get-intrinsic: ^1.2.1 + is-arguments: ^1.1.1 + is-array-buffer: ^3.0.2 + is-date-object: ^1.0.5 + is-regex: ^1.1.4 + is-shared-array-buffer: ^1.0.2 + isarray: ^2.0.5 + object-is: ^1.1.5 + object-keys: ^1.1.1 + object.assign: ^4.1.4 + regexp.prototype.flags: ^1.5.0 + side-channel: ^1.0.4 + which-boxed-primitive: ^1.0.2 + which-collection: ^1.0.1 + which-typed-array: ^1.1.9 + checksum: eb61c35157b6ecb96a5359b507b083fbff8ddb4c86a78a781ee38485f77a667465e45d63ee2ebd8a00e86d94c80e499906900cd82c2debb400237e1662cd5397 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -5897,6 +5969,13 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.5.9": + version: 0.5.16 + resolution: "dom-accessibility-api@npm:0.5.16" + checksum: 005eb283caef57fc1adec4d5df4dd49189b628f2f575af45decb210e04d634459e3f1ee64f18b41e2dcf200c844bc1d9279d80807e686a30d69a4756151ad248 + languageName: node + linkType: hard + "domexception@npm:^4.0.0": version: 4.0.0 resolution: "domexception@npm:4.0.0" @@ -6153,6 +6232,23 @@ __metadata: languageName: node linkType: hard +"es-get-iterator@npm:^1.1.3": + version: 1.1.3 + resolution: "es-get-iterator@npm:1.1.3" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 + has-symbols: ^1.0.3 + is-arguments: ^1.1.1 + is-map: ^2.0.2 + is-set: ^2.0.2 + is-string: ^1.0.7 + isarray: ^2.0.5 + stop-iteration-iterator: ^1.0.0 + checksum: 8fa118da42667a01a7c7529f8a8cca514feeff243feec1ce0bb73baaa3514560bd09d2b3438873cf8a5aaec5d52da248131de153b28e2638a061b6e4df13267d + languageName: node + linkType: hard + "es-iterator-helpers@npm:^1.0.12": version: 1.0.15 resolution: "es-iterator-helpers@npm:1.0.15" @@ -7065,6 +7161,13 @@ __metadata: languageName: node linkType: hard +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 + languageName: node + linkType: hard + "function.prototype.name@npm:^1.1.5, function.prototype.name@npm:^1.1.6": version: 1.1.6 resolution: "function.prototype.name@npm:1.1.6" @@ -7162,6 +7265,18 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.2": + version: 1.2.2 + resolution: "get-intrinsic@npm:1.2.2" + dependencies: + function-bind: ^1.1.2 + has-proto: ^1.0.1 + has-symbols: ^1.0.3 + hasown: ^2.0.0 + checksum: 447ff0724df26829908dc033b62732359596fcf66027bc131ab37984afb33842d9cd458fd6cecadfe7eac22fd8a54b349799ed334cf2726025c921c7250e7417 + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -7631,6 +7746,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.0": + version: 2.0.0 + resolution: "hasown@npm:2.0.0" + dependencies: + function-bind: ^1.1.2 + checksum: 6151c75ca12554565098641c98a40f4cc86b85b0fd5b6fe92360967e4605a4f9610f7757260b4e8098dd1c2ce7f4b095f2006fe72a570e3b6d2d28de0298c176 + languageName: node + linkType: hard + "he@npm:1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -8016,6 +8140,17 @@ __metadata: languageName: node linkType: hard +"internal-slot@npm:^1.0.4": + version: 1.0.6 + resolution: "internal-slot@npm:1.0.6" + dependencies: + get-intrinsic: ^1.2.2 + hasown: ^2.0.0 + side-channel: ^1.0.4 + checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549 + languageName: node + linkType: hard + "internal-slot@npm:^1.0.5": version: 1.0.5 resolution: "internal-slot@npm:1.0.5" @@ -8041,6 +8176,16 @@ __metadata: languageName: node linkType: hard +"is-arguments@npm:^1.1.1": + version: 1.1.1 + resolution: "is-arguments@npm:1.1.1" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27 + languageName: node + linkType: hard + "is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": version: 3.0.2 resolution: "is-array-buffer@npm:3.0.2" @@ -8278,7 +8423,7 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.1": +"is-map@npm:^2.0.1, is-map@npm:^2.0.2": version: 2.0.2 resolution: "is-map@npm:2.0.2" checksum: ace3d0ecd667bbdefdb1852de601268f67f2db725624b1958f279316e13fecb8fa7df91fd60f690d7417b4ec180712f5a7ee967008e27c65cfd475cc84337728 @@ -8399,7 +8544,7 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.1": +"is-set@npm:^2.0.1, is-set@npm:^2.0.2": version: 2.0.2 resolution: "is-set@npm:2.0.2" checksum: b64343faf45e9387b97a6fd32be632ee7b269bd8183701f3b3f5b71a7cf00d04450ed8669d0bd08753e08b968beda96fca73a10fd0ff56a32603f64deba55a57 @@ -9892,6 +10037,15 @@ __metadata: languageName: node linkType: hard +"lz-string@npm:^1.5.0": + version: 1.5.0 + resolution: "lz-string@npm:1.5.0" + bin: + lz-string: bin/bin.js + checksum: 1ee98b4580246fd90dd54da6e346fb1caefcf05f677c686d9af237a157fdea3fd7c83a4bc58f858cd5b10a34d27afe0fdcbd0505a47e0590726a873dc8b8f65d + languageName: node + linkType: hard + "magic-string@npm:^0.22.5": version: 0.22.5 resolution: "magic-string@npm:0.22.5" @@ -11036,6 +11190,16 @@ __metadata: languageName: node linkType: hard +"object-is@npm:^1.1.5": + version: 1.1.5 + resolution: "object-is@npm:1.1.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe + languageName: node + linkType: hard + "object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" @@ -11790,6 +11954,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^27.0.2": + version: 27.5.1 + resolution: "pretty-format@npm:27.5.1" + dependencies: + ansi-regex: ^5.0.1 + ansi-styles: ^5.0.0 + react-is: ^17.0.1 + checksum: cf610cffcb793885d16f184a62162f2dd0df31642d9a18edf4ca298e909a8fe80bdbf556d5c9573992c102ce8bf948691da91bf9739bee0ffb6e79c8a8a6e088 + languageName: node + linkType: hard + "pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" @@ -12020,13 +12195,6 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.2.0": - version: 18.2.0 - resolution: "react-is@npm:18.2.0" - checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e - languageName: node - linkType: hard - "react-is@npm:^16.13.1, react-is@npm:^16.8.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -12034,6 +12202,20 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^17.0.1": + version: 17.0.2 + resolution: "react-is@npm:17.0.2" + checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8 + languageName: node + linkType: hard + +"react-is@npm:^18.0.0": + version: 18.2.0 + resolution: "react-is@npm:18.2.0" + checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e + languageName: node + linkType: hard + "react-router-dom@npm:^6.17.0": version: 6.17.0 resolution: "react-router-dom@npm:6.17.0" @@ -12058,31 +12240,6 @@ __metadata: languageName: node linkType: hard -"react-shallow-renderer@npm:^16.15.0": - version: 16.15.0 - resolution: "react-shallow-renderer@npm:16.15.0" - dependencies: - object-assign: ^4.1.1 - react-is: ^16.12.0 || ^17.0.0 || ^18.0.0 - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: 6052c7e3e9627485120ebd8257f128aad8f56386fe8d42374b7743eac1be457c33506d153c7886b4e32923c0c352d402ab805ef9ca02dbcd8393b2bdeb6e5af8 - languageName: node - linkType: hard - -"react-test-renderer@npm:^18.2.0": - version: 18.2.0 - resolution: "react-test-renderer@npm:18.2.0" - dependencies: - react-is: ^18.2.0 - react-shallow-renderer: ^16.15.0 - scheduler: ^0.23.0 - peerDependencies: - react: ^18.2.0 - checksum: 6b6980ced93fa2b72662d5e4ab3b4896833586940047ce52ca9aca801e5432adf05fcbe28289b0af3ce6a2a7c590974e25dcc8aa43d0de658bfe8bbcd686f958 - languageName: node - linkType: hard - "react-values@npm:^0.3.3": version: 0.3.3 resolution: "react-values@npm:0.3.3" @@ -13227,13 +13384,13 @@ __metadata: dependencies: "@babel/runtime": ^7.23.2 "@juggle/resize-observer": ^3.4.0 + "@testing-library/react": ^14.0.0 "@types/is-hotkey": ^0.1.8 "@types/jest": 29.5.6 "@types/jsdom": ^21.1.4 "@types/lodash": ^4.14.200 "@types/react": ^18.2.28 "@types/react-dom": ^18.2.13 - "@types/react-test-renderer": ^18.0.3 "@types/resize-observer-browser": ^0.1.8 direction: ^1.0.4 is-hotkey: ^0.2.0 @@ -13241,7 +13398,6 @@ __metadata: lodash: ^4.17.21 react: ^18.2.0 react-dom: ^18.2.0 - react-test-renderer: ^18.2.0 scroll-into-view-if-needed: ^3.1.0 slate: ^0.100.0 slate-hyperscript: ^0.100.0 @@ -13528,6 +13684,15 @@ __metadata: languageName: node linkType: hard +"stop-iteration-iterator@npm:^1.0.0": + version: 1.0.0 + resolution: "stop-iteration-iterator@npm:1.0.0" + dependencies: + internal-slot: ^1.0.4 + checksum: d04173690b2efa40e24ab70e5e51a3ff31d56d699550cfad084104ab3381390daccb36652b25755e420245f3b0737de66c1879eaa2a8d4fc0a78f9bf892fcb42 + languageName: node + linkType: hard + "stream-transform@npm:^2.1.3": version: 2.1.3 resolution: "stream-transform@npm:2.1.3"