diff --git a/apps/angular-demo/src/app/input-component/input-component.component.html b/apps/angular-demo/src/app/input-component/input-component.component.html index 0471c640a..6b1c37b8d 100644 --- a/apps/angular-demo/src/app/input-component/input-component.component.html +++ b/apps/angular-demo/src/app/input-component/input-component.component.html @@ -66,6 +66,9 @@

Focus

(_blur)="onBlurEvent($event)" > +

Key Press

+ +

Prefix and Suffix

diff --git a/apps/angular-demo/src/app/input-component/input-component.component.ts b/apps/angular-demo/src/app/input-component/input-component.component.ts index b85e2ba82..ed5db9a70 100644 --- a/apps/angular-demo/src/app/input-component/input-component.component.ts +++ b/apps/angular-demo/src/app/input-component/input-component.component.ts @@ -72,6 +72,10 @@ export class InputComponentComponent implements OnInit { console.log("on Blur Event: ", event.detail); } + onKeyPressEvent(event: any) { + console.log("on Key Press Event: ", event.detail); + } + setDate(event: any) { const raw = event.detail.value; if (!raw) { diff --git a/apps/angular-demo/src/app/text-area/text-area.component.html b/apps/angular-demo/src/app/text-area/text-area.component.html index f89dbc7fd..d17f224a9 100644 --- a/apps/angular-demo/src/app/text-area/text-area.component.html +++ b/apps/angular-demo/src/app/text-area/text-area.component.html @@ -53,3 +53,10 @@

Reactive Binding - Disabled

[formControl]="reactiveFormCtrl" [attr.disabled]="true" > + +

On Key Press

+ diff --git a/apps/angular-demo/src/app/text-area/text-area.component.ts b/apps/angular-demo/src/app/text-area/text-area.component.ts index 41ba795da..37e3a50e4 100644 --- a/apps/angular-demo/src/app/text-area/text-area.component.ts +++ b/apps/angular-demo/src/app/text-area/text-area.component.ts @@ -10,9 +10,13 @@ export class TextAreaComponent { boundVal = ""; reactiveFormCtrl = new FormControl(); - constructor() {} + constructor() { } onChange(e: any) { console.log("changed", e.detail.name, e.detail.value); } + + onKeyPress(e: any) { + console.log("changed", e.detail.name, e.detail.value, e.detail.key); + } } diff --git a/apps/react-demo/src/routes/input.tsx b/apps/react-demo/src/routes/input.tsx index 170039236..5d266bccc 100644 --- a/apps/react-demo/src/routes/input.tsx +++ b/apps/react-demo/src/routes/input.tsx @@ -38,6 +38,10 @@ export default function Input() { console.log("onBlur -> Name :: Value ===", _name, " :: ", value); } + function onKeyPressEvent(_name: string, value: string, key: string) { + console.log("onKeyPress -> Name :: Value :: Key ===", _name, " :: ", value, " :: ", key); + } + return ( <>

Date with a min and max and Date values

@@ -121,6 +125,16 @@ export default function Input() { onBlur={onBlurChange} /> + +

Key Press

+ +

Prefix and Suffix

diff --git a/apps/react-demo/src/routes/textarea.tsx b/apps/react-demo/src/routes/textarea.tsx index 0169bd4fa..b931737d6 100644 --- a/apps/react-demo/src/routes/textarea.tsx +++ b/apps/react-demo/src/routes/textarea.tsx @@ -19,6 +19,11 @@ export default function TextArea() { console.log(name, value); } + function onKeyPress(name: string, value: string, key: string) { + setValue(value); + console.log(name, value, key); + } + function onChange4(name: string, value: string) { setValue4(value); console.log(name, value); @@ -95,6 +100,15 @@ export default function TextArea() { ml="xl" mr="2xl" /> + +

on Key Press

+ ); } diff --git a/libs/docs/src/components/common/Input.stories.mdx b/libs/docs/src/components/common/Input.stories.mdx index 3a71b7001..88b5fa8f8 100644 --- a/libs/docs/src/components/common/Input.stories.mdx +++ b/libs/docs/src/components/common/Input.stories.mdx @@ -272,6 +272,18 @@ import { Meta, Story } from "@storybook/addon-docs"; type="() => void" description="Function invoked when an element loses focus" /> + +
diff --git a/libs/docs/src/components/common/TextArea.stories.mdx b/libs/docs/src/components/common/TextArea.stories.mdx index 3c6bf8bb9..c2183492f 100644 --- a/libs/docs/src/components/common/TextArea.stories.mdx +++ b/libs/docs/src/components/common/TextArea.stories.mdx @@ -84,6 +84,24 @@ import { GoATextArea } from "@abgov/react-components"; type="(name: string, value: string) => void" description="Event triggered on each input value change" /> + + +
diff --git a/libs/react-components/src/lib/input/input.tsx b/libs/react-components/src/lib/input/input.tsx index 36df61eaf..527dbc7ee 100644 --- a/libs/react-components/src/lib/input/input.tsx +++ b/libs/react-components/src/lib/input/input.tsx @@ -93,46 +93,40 @@ interface BaseProps extends Margins { maxLength?: number; } -type OnChange = (name: string, value: string) => void; -type OnFocus = (name: string, value: string) => void; -type OnBlur = (name: string, value: string) => void; +type OnChange = (name: string, value: T) => void; +type OnFocus = (name: string, value: T) => void; +type OnBlur = (name: string, value: T) => void; +type OnKeyPress = (name: string, value: T, key: string) => void; export interface GoAInputProps extends BaseProps { - onChange: OnChange; + onChange: OnChange; value: string; min?: number | string; max?: number | string; step?: number; - onFocus?: OnFocus; - onBlur?: OnBlur; + onFocus?: OnFocus; + onBlur?: OnBlur; + onKeyPress?: OnKeyPress; } -// legacy -export type InputProps = GoAInputProps; - -type OnNumberChange = (name: string, value: number) => void; -type OnNumberFocus = (name: string, value: number) => void; -type OnNumberBlur = (name: string, value: number) => void; -export interface GoANumberInputProps extends BaseProps { - onChange: OnNumberChange; +interface GoANumberInputProps extends BaseProps { + onChange: OnChange; value: number; min?: number; max?: number; step?: number; - onFocus?: OnNumberFocus; - onBlur?: OnNumberBlur; + onFocus?: OnFocus; + onBlur?: OnBlur; + onKeyPress?: OnKeyPress; } - -type OnDateChange = (name: string, value: GoADate) => void; -type OnDateFocus = (name: string, value: GoADate) => void; -type OnDateBlur = (name: string, value: GoADate) => void; -export interface GoADateInputProps extends BaseProps { - onChange: OnDateChange; +interface GoADateInputProps extends BaseProps { + onChange: OnChange; value: GoADate; min?: GoADate; max?: GoADate; step?: number; - onFocus?: OnDateFocus; - onBlur?: OnDateBlur; + onFocus?: OnFocus; + onBlur?: OnBlur; + onKeyPress?: OnKeyPress; } export function GoAInput({ @@ -169,6 +163,7 @@ export function GoAInput({ onChange, onFocus, onBlur, + onKeyPress, }: GoAInputProps & { type?: GoAInputType }): JSX.Element { const ref = useRef(null); useEffect(() => { @@ -194,17 +189,24 @@ export function GoAInput({ onBlur?.(name, value); }; + const keypressListener = (e: unknown) => { + const { name, value, key } = (e as CustomEvent).detail; + onKeyPress?.(name, value, key); + } + current.addEventListener("_change", changeListener); current.addEventListener("_trailingIconClick", clickListener); current.addEventListener("_focus", focusListener); current.addEventListener("_blur", blurListener); + current.addEventListener("_keyPress", keypressListener); return () => { current.removeEventListener("_change", changeListener); current.removeEventListener("_trailingIconClick", clickListener); current.removeEventListener("_focus", focusListener); current.removeEventListener("_blur", blurListener); + current.removeEventListener("_keyPress", keypressListener); }; - }, [ref, onChange, onTrailingIconClick, onFocus, onBlur]); + }, [ref, onChange, onTrailingIconClick, onFocus, onBlur, onKeyPress]); return ( { +const onDateChangeHandler = (onChange: OnChange) => { return (name: string, value: string) => { if (!value) { @@ -385,6 +387,9 @@ export function GoAInputNumber({ const onBlur = (name: string, value: string) => { props.onBlur?.(name, parseInt(value)); }; + const onKeyPress = (name: string, value: string, key: string) => { + props.onKeyPress?.(name, parseInt(value), key); + }; return ( ); }; diff --git a/libs/react-components/src/lib/textarea/textarea.tsx b/libs/react-components/src/lib/textarea/textarea.tsx index f70393829..8c647f46a 100644 --- a/libs/react-components/src/lib/textarea/textarea.tsx +++ b/libs/react-components/src/lib/textarea/textarea.tsx @@ -38,6 +38,7 @@ export interface GoATextAreaProps extends Margins { testId?: string; ariaLabel?: string; onChange: (name: string, value: string) => void; + onKeyPress?: (name: string, value: string, key: string) => void; } export function GoATextArea({ @@ -57,6 +58,7 @@ export function GoATextArea({ mb, ml, onChange, + onKeyPress, }: GoATextAreaProps): JSX.Element { const el = useRef(null); @@ -70,9 +72,16 @@ export function GoATextArea({ onChange(name, value); }; + const keypressListener = (e: unknown) => { + const { name, value, key } = (e as CustomEvent).detail; + onKeyPress?.(name, value, key); + } + current.addEventListener("_change", listener); + current.addEventListener("_keyPress", keypressListener); return () => { current.removeEventListener("_change", listener); + current.removeEventListener("_keyPress", listener); }; }, [el, onChange]); diff --git a/libs/web-components/src/components/input/Input.spec.ts b/libs/web-components/src/components/input/Input.spec.ts index beea716e3..197bd133c 100644 --- a/libs/web-components/src/components/input/Input.spec.ts +++ b/libs/web-components/src/components/input/Input.spec.ts @@ -158,6 +158,7 @@ describe("GoAInput Component", () => { }); const input = await findByTestId("input-test"); const change = jest.fn(); + const keypress = jest.fn(); input.addEventListener("_change", (e: CustomEvent) => { expect(e.detail.name).toBe("test-name"); @@ -165,9 +166,17 @@ describe("GoAInput Component", () => { change(); }); - await fireEvent.keyUp(input, { target: { value: "foobar" } }); + input.addEventListener("_keyPress", (e: CustomEvent) => { + expect(e.detail.name).toBe("test-name"); + expect(e.detail.value).toBe("foobar"); + expect(e.detail.key).toBe("r"); + keypress(); + }); + + await fireEvent.keyUp(input, { target: { value: "foobar" }, key: 'r' }); await waitFor(() => { expect(change).toBeCalledTimes(1); + expect(keypress).toBeCalledTimes(1); }); }); diff --git a/libs/web-components/src/components/input/Input.svelte b/libs/web-components/src/components/input/Input.svelte index 94aaad16c..f1e3b198e 100644 --- a/libs/web-components/src/components/input/Input.svelte +++ b/libs/web-components/src/components/input/Input.svelte @@ -102,6 +102,12 @@ ); }, debounce); + input.dispatchEvent( + new CustomEvent("_keyPress", { + composed: true, + detail: { name, value: input.value, key: (e as KeyboardEvent).key } + }), + ); value = input.value; } diff --git a/libs/web-components/src/components/text-area/TextArea.spec.ts b/libs/web-components/src/components/text-area/TextArea.spec.ts index 3830c2422..68776aefd 100644 --- a/libs/web-components/src/components/text-area/TextArea.spec.ts +++ b/libs/web-components/src/components/text-area/TextArea.spec.ts @@ -45,6 +45,29 @@ describe("GoATextArea", () => { }) }) + it("handles the keypress event", async () => { + const onKeyPress = jest.fn(); + const result = render(GoATextArea, { + name: "name", + value: "foo", + testid: "keypress", + }); + + const textarea = result.queryByTestId("keypress"); + textarea.addEventListener("_keyPress", (e: CustomEvent) => { + expect(e.detail.name).toBe("name"); + expect(e.detail.value).toBe("foo"); + expect(e.detail.key).toBe("o"); + onKeyPress(); + }); + + await fireEvent.keyUp(textarea, { target: { value: 'foo' }, key: "o" }); + + await waitFor(() => { + expect(onKeyPress).toBeCalledTimes(1); + }) + }) + it("can be disabled", async () => { const onChange = jest.fn(); const result = render(GoATextArea, { diff --git a/libs/web-components/src/components/text-area/TextArea.svelte b/libs/web-components/src/components/text-area/TextArea.svelte index 38b5823fe..83e5781d4 100644 --- a/libs/web-components/src/components/text-area/TextArea.svelte +++ b/libs/web-components/src/components/text-area/TextArea.svelte @@ -46,6 +46,17 @@ ); }; } + + function onKeyPress(e: KeyboardEvent) { + if (_textAreaEl && !isDisabled) { + _textAreaEl.dispatchEvent( + new CustomEvent("_keyPress", { + composed: true, + detail: { name, value: value, key: e.key } + }), + ); + } + } @@ -68,6 +79,7 @@ data-testid={testid} bind:this={_textAreaEl} bind:value={value} + on:keyup={onKeyPress} /> {#if showCounter} {#if maxcharcount > 0}