From 7e4fecd13672c8813411dfbc06e0961b92670873 Mon Sep 17 00:00:00 2001 From: VictoriaBeilstenEdmands <45741274+VictoriaBeilsten-Edmands@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:43:42 +0100 Subject: [PATCH] Add onBlur submit option to VisitInput --- changelog.md | 1 + .../controls/VisitInput.stories.tsx | 9 ++ src/components/controls/VisitInput.test.tsx | 111 ++++++++++++++++++ src/components/controls/VisitInput.tsx | 13 ++ 4 files changed, 134 insertions(+) diff --git a/changelog.md b/changelog.md index 3797b903..8c3a0b64 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ ### Changed - **Breaking** `keycloak-js` has been moved from a direct dependency to a peer and optional dependency, so must now be installed by the consuming application. +- Added submitOnBlur boolean prop to VisitInput which defaults to false. ### Fixed - Icon imports were causing issues downstream when components are unit tested. diff --git a/src/components/controls/VisitInput.stories.tsx b/src/components/controls/VisitInput.stories.tsx index f5d7e202..d8869608 100644 --- a/src/components/controls/VisitInput.stories.tsx +++ b/src/components/controls/VisitInput.stories.tsx @@ -34,6 +34,15 @@ export const InitialVisitWithSubmitButton: Story = { }, }; +export const InitialVisitWithSubmitOnBlur: Story = { + args: { + onSubmit: handleSubmit, + visit: { proposalCode: "xx", proposalNumber: 99999, number: 7 }, + submitButton: false, + submitOnBlur: true, + }, +}; + export const InitialVisitWithoutReturnKeySubmission: Story = { args: { onSubmit: handleSubmit, diff --git a/src/components/controls/VisitInput.test.tsx b/src/components/controls/VisitInput.test.tsx index 47f7be14..738ce9d4 100644 --- a/src/components/controls/VisitInput.test.tsx +++ b/src/components/controls/VisitInput.test.tsx @@ -226,3 +226,114 @@ it("should update visit on submit", () => { }, ); }); + +it("should not produce visit on blur by default", () => { + const onSubmit = vi.fn(); + const { getByTestId } = render(); + const visitField = within(getByTestId("visit-field")).getByRole("textbox"); + fireEvent.change(visitField, { target: { value: "zz12345-7" } }); + fireEvent.blur(visitField, { + key: "Enter", + code: "Enter", + keyCode: 13, + charCode: 13, + }); + expect(onSubmit).not.toHaveBeenCalledWith( + { + proposalCode: "zz", + proposalNumber: 12345, + number: 7, + }, + undefined, + ); +}); + +it("should produce visit on blur without submit button", () => { + const onSubmit = vi.fn(); + const { getByTestId } = render( + , + ); + const visitField = within(getByTestId("visit-field")).getByRole("textbox"); + fireEvent.change(visitField, { target: { value: "zz12345-7" } }); + fireEvent.blur(visitField, { + key: "Enter", + code: "Enter", + keyCode: 13, + charCode: 13, + }); + expect(onSubmit).toHaveBeenCalledWith( + { + proposalCode: "zz", + proposalNumber: 12345, + number: 7, + }, + undefined, + ); +}); + +it("should not produce visit on blur", () => { + const onSubmit = vi.fn(); + const { getByTestId } = render( + , + ); + const visitField = within(getByTestId("visit-field")).getByRole("textbox"); + fireEvent.change(visitField, { target: { value: "zz12345-7" } }); + fireEvent.blur(visitField, { + key: "Enter", + code: "Enter", + keyCode: 13, + charCode: 13, + }); + expect(onSubmit).not.toHaveBeenCalledWith( + { + proposalCode: "zz", + proposalNumber: 12345, + number: 7, + }, + undefined, + ); +}); + +it("should produce visit on blur", () => { + const onSubmit = vi.fn(); + const { getByTestId } = render( + , + ); + const visitField = within(getByTestId("visit-field")).getByRole("textbox"); + fireEvent.change(visitField, { target: { value: "zz12345-7" } }); + fireEvent.blur(visitField, { + key: "Enter", + code: "Enter", + keyCode: 13, + charCode: 13, + }); + expect(onSubmit).toHaveBeenCalledWith( + { + proposalCode: "zz", + proposalNumber: 12345, + number: 7, + }, + undefined, + ); +}); + +it("should not produce visit on blur with no onSubmit", () => { + const onSubmit = vi.fn(); + const { getByTestId } = render(); + const visitField = within(getByTestId("visit-field")).getByRole("textbox"); + fireEvent.change(visitField, { target: { value: "zz12345-7" } }); + fireEvent.blur(visitField, { + key: "Enter", + code: "Enter", + keyCode: 13, + charCode: 13, + }); + expect(onSubmit).not.toHaveBeenCalledWith( + { + proposalCode: "zz", + proposalNumber: 12345, + number: 7, + }, + undefined, + ); +}); diff --git a/src/components/controls/VisitInput.tsx b/src/components/controls/VisitInput.tsx index ba0997d7..d5410091 100644 --- a/src/components/controls/VisitInput.tsx +++ b/src/components/controls/VisitInput.tsx @@ -14,6 +14,7 @@ interface VisitInputTextProps { setIsValid: (v: boolean) => void; handleSubmit?: () => void; submitOnReturn?: boolean; + submitOnBlur?: boolean; } const VisitInputText: React.FC = ({ @@ -23,6 +24,7 @@ const VisitInputText: React.FC = ({ setIsValid, handleSubmit, submitOnReturn, + submitOnBlur, }) => { const handleInputChange = (event: ChangeEvent) => { const value = event.target.value; @@ -36,12 +38,19 @@ const VisitInputText: React.FC = ({ } }; + const handleBlur = () => { + if (submitOnBlur && handleSubmit) { + handleSubmit(); + } + }; + return ( = ({ @@ -64,6 +74,7 @@ const VisitInput: React.FC = ({ parameters, submitButton = true, submitOnReturn = true, + submitOnBlur = false, }) => { const [visitText, setVisitText] = useState(visitToText(visit)); const [isValid, setIsValid] = useState(true); @@ -88,6 +99,7 @@ const VisitInput: React.FC = ({ setIsValid={setIsValid} handleSubmit={handleSubmit} submitOnReturn={submitOnReturn} + submitOnBlur={submitOnBlur} />