diff --git a/.changeset/large-ligers-begin.md b/.changeset/large-ligers-begin.md new file mode 100644 index 0000000000..738d5c41d8 --- /dev/null +++ b/.changeset/large-ligers-begin.md @@ -0,0 +1,5 @@ +--- +'@remirror/react-hooks': patch +--- + +Fix bug in `useMentionAtom` where asynchronous exits, like clicking, would not trigger an exit. Now the state is manually reset when the command is run. diff --git a/packages/@remirror/react-hooks/src/__tests__/use-mention-atom.spec.tsx b/packages/@remirror/react-hooks/src/__tests__/use-mention-atom.spec.tsx index 8924af48f9..24f9a9ce2b 100644 --- a/packages/@remirror/react-hooks/src/__tests__/use-mention-atom.spec.tsx +++ b/packages/@remirror/react-hooks/src/__tests__/use-mention-atom.spec.tsx @@ -45,6 +45,11 @@ describe('useMentionAtom', () => { () => { result.state?.command({ ...result.items[0], appendText: NON_BREAKING_SPACE_CHAR }); }, + ]); + + expect(result.state?.command).toBeUndefined(); + + acts([ () => { editor.insertText('more to come'); }, @@ -66,6 +71,43 @@ describe('useMentionAtom', () => { `); }); + it('should correctly add the mention when the command is called in a controlled editor', () => { + const { editor, Wrapper, result } = createEditor(true); + + strictRender(); + + for (const char of '@a') { + act(() => { + editor.insertText(char); + }); + } + + act(() => { + result.state?.command({ ...result.items[0], appendText: NON_BREAKING_SPACE_CHAR }); + }); + + for (const char of 'more to come') { + act(() => { + editor.insertText(char); + }); + } + + expect(editor.innerHTML).toMatchInlineSnapshot(` +

+ Initial content + + @aa + +  more to come +

+ `); + }); + it('should not trap the cursor in the mention', () => { const { editor, Wrapper } = createEditor(); @@ -227,7 +269,7 @@ describe('useMentionAtom', () => { /** * This function is used as a helper when testing the mention hooks. */ -function createEditor() { +function createEditor(controlled = false) { const manager = createReactManager(() => [ new MentionAtomExtension({ extraAttributes: { role: 'presentation', href: { default: null } }, @@ -310,7 +352,25 @@ function createEditor() { ); }; - return { editor, Wrapper, result }; + const ControlledWrapper: FC = (props) => { + const [state, setState] = useState(() => + manager.createState({ content: doc(p('Initial content ')), selection: 'end' }), + ); + return ( + { + setState(parameter.state); + }} + > + + + ); + }; + + return { editor, Wrapper: controlled ? ControlledWrapper : Wrapper, result }; } function acts(methods: Array<() => void | undefined>) { diff --git a/packages/@remirror/react-hooks/src/use-mention-atom.ts b/packages/@remirror/react-hooks/src/use-mention-atom.ts index a7460d6a97..6dc1226370 100644 --- a/packages/@remirror/react-hooks/src/use-mention-atom.ts +++ b/packages/@remirror/react-hooks/src/use-mention-atom.ts @@ -67,7 +67,18 @@ function useMentionHandlers { + command(attrs); + setState(null); + }, + index, + }); }, [currentIndex, setState], ); @@ -99,7 +110,6 @@ function useMentionAtomKeyBindings< } const { index } = state; - const direction = arrowKey === 'down' ? 'next' : 'previous'; const activeIndex = indexFromArrowPress({ diff --git a/support/website/src/pages/playground.tsx b/support/website/src/pages/playground.tsx index 5045f68c33..b44eab77a2 100644 --- a/support/website/src/pages/playground.tsx +++ b/support/website/src/pages/playground.tsx @@ -30,8 +30,8 @@ const PlaygroundPage = (props: any) => { {/* TODO: Do not assume that it is in english language site */} - - + + Remirror Playground {favicon ?? }