diff --git a/.changeset/lovely-pets-wink.md b/.changeset/lovely-pets-wink.md new file mode 100644 index 00000000..8e0d99a1 --- /dev/null +++ b/.changeset/lovely-pets-wink.md @@ -0,0 +1,5 @@ +--- +"@diceui/mention": minor +--- + +Allow mention trigger in middle of text diff --git a/packages/mention/src/mention-input.tsx b/packages/mention/src/mention-input.tsx index cd5c41c9..0429c9f2 100644 --- a/packages/mention/src/mention-input.tsx +++ b/packages/mention/src/mention-input.tsx @@ -219,7 +219,7 @@ const MentionInput = React.forwardRef( textAfterCursor.length > 0 && !isTextAfterCursorSeparated && !isTextAfterCursorPartOfMention; - + if (hasInterferingText) { if (context.open) { context.onOpenChange(false); diff --git a/packages/mention/test/mention.test.tsx b/packages/mention/test/mention.test.tsx index 448d6210..69d56e14 100644 --- a/packages/mention/test/mention.test.tsx +++ b/packages/mention/test/mention.test.tsx @@ -381,4 +381,37 @@ describe("Mention", () => { // Ensure the input now shows only the second mention expect(input.value).toBe("and @heelflip "); }); + + test("allows mention trigger in middle of text and preserves text after", async () => { + const onValueChange = vi.fn(); + const onInputValueChange = vi.fn(); + renderMention({ onValueChange, onInputValueChange }); + + const input = screen.getByPlaceholderText(placeholder); + + if (!(input instanceof HTMLInputElement)) { + throw new Error("Input element not found"); + } + + // Add first mention + await userEvent.type(input, "@kickflip"); + const kickflipOption = screen.getByRole("option", { name: "Kickflip" }); + await waitFor(() => { + fireEvent.click(kickflipOption); + }); + + // Add some text after the first mention + await userEvent.type(input, " and then "); + + // Add another mention after the text + await userEvent.type(input, "@heelflip"); + const heelflipOption = screen.getByRole("option", { name: "Heelflip" }); + await waitFor(() => { + fireEvent.click(heelflipOption); + }); + + // Verify both mentions are present and the text between them is preserved + expect(input.value).toBe("@kickflip and then @heelflip "); + expect(onValueChange).toHaveBeenLastCalledWith(["kickflip", "heelflip"]); + }); });