Skip to content

Commit

Permalink
feat(textarea component): Enable control+enter form submission from w…
Browse files Browse the repository at this point in the history
…ithin a text area [WD-10959] (#1095)

Signed-off-by: Nkeiruka <[email protected]>
  • Loading branch information
Kxiru authored Jun 4, 2024
1 parent 3abb04f commit 1479b18
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/components/Textarea/Textarea.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ The Textarea component defines a multi-line text input control.
id: "textarea2",
rows: "3",
defaultValue: "Ubuntu",
onControlEnter: () => {alert("onControlEnter() successfully called.")}
}}
>
{Template.bind({})}
Expand Down
36 changes: 36 additions & 0 deletions src/components/Textarea/Textarea.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,40 @@ describe("Textarea ", () => {
rerender(<Textarea value={testValue} />);
expect(textarea).toHaveValue("");
});

it("adds Ctrl + Enter keydown event listener on mount and removes on unmount", async () => {
const addEventListenerSpy = jest.spyOn(document, "addEventListener");
const removeEventListenerSpy = jest.spyOn(document, "removeEventListener");

const { unmount } = render(
<Textarea
onControlEnter={() => {
console.log("OnControlEnter was here.");
}}
/>
);

// Capture the event handler
const [event, handler] = addEventListenerSpy.mock.calls[0];

// Assert that addEventListener was called with the correct event type and handler
expect(event).toBe("keydown");
expect(typeof handler).toBe("function");

const keyboardCombo = new KeyboardEvent("keydown", {
key: "Enter",
ctrlKey: true,
});
document.dispatchEvent(keyboardCombo);

// Unmount the component to trigger cleanup
unmount();

// Assert that removeEventListener was called with the same event type and handler
expect(removeEventListenerSpy).toHaveBeenCalledWith("keydown", handler);

// Clean up: restore the original implementations
addEventListenerSpy.mockRestore();
removeEventListenerSpy.mockRestore();
});
});
27 changes: 27 additions & 0 deletions src/components/Textarea/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import classNames from "classnames";
import React, {
useCallback,
useEffect,
useId,
useLayoutEffect,
Expand Down Expand Up @@ -69,6 +70,10 @@ export type Props = PropsWithSpread<
* Optional class(es) to pass to the wrapping Field component
*/
wrapperClassName?: string;
/**
* Function to occur on keyboard event, 'CTRL + Enter'
*/
onControlEnter?: () => void;
},
TextareaHTMLAttributes<HTMLTextAreaElement>
>;
Expand All @@ -83,6 +88,7 @@ const Textarea = ({
label,
labelClassName,
onKeyUp,
onControlEnter,
required,
stacked,
style,
Expand All @@ -99,6 +105,27 @@ const Textarea = ({
const defaultTextAreaId = useId();
const textAreaId = id || defaultTextAreaId;

const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (
event.key === "Enter" &&
(event.ctrlKey || event.metaKey) &&
document.activeElement === textareaRef.current
) {
onControlEnter();
}
},
[onControlEnter]
);

useEffect(() => {
document.addEventListener("keydown", handleKeyDown);

return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [handleKeyDown]);

useEffect(() => {
if (takeFocus) {
textareaRef.current.focus();
Expand Down

0 comments on commit 1479b18

Please sign in to comment.