From 31e079d591ad7df2de3869e0435688ebd8c6af4f Mon Sep 17 00:00:00 2001 From: gerjanvangeest Date: Tue, 30 Jan 2024 10:19:09 +0100 Subject: [PATCH] feat(form): set focus to the first erroneous form element on submit --- .changeset/plenty-turtles-remember.md | 7 ++ docs/components/form/overview.md | 27 +++++--- docs/components/form/use-cases.md | 24 +++---- docs/fundamentals/systems/form/use-cases.md | 21 ++++-- packages/ui/components/form/src/LionForm.js | 24 +++++++ .../ui/components/form/test/lion-form.test.js | 68 ++++++++++++++++++- 6 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 .changeset/plenty-turtles-remember.md diff --git a/.changeset/plenty-turtles-remember.md b/.changeset/plenty-turtles-remember.md new file mode 100644 index 000000000..e6b58502b --- /dev/null +++ b/.changeset/plenty-turtles-remember.md @@ -0,0 +1,7 @@ +--- +'@lion/ui': minor +--- + +BREAKING CHANGE: + +[form] set focus to the first erroneous form element on submit diff --git a/docs/components/form/overview.md b/docs/components/form/overview.md index 02225f2ae..6d08d6860 100644 --- a/docs/components/form/overview.md +++ b/docs/components/form/overview.md @@ -11,14 +11,25 @@ A web component that enhances the functionality of the native `form` component. It is designed to interact with (instances of) the [form controls](../../fundamentals/systems/form/overview.md). ```js preview-story -export const main = () => html` - -
- - -
-
-`; +export const main = () => { + const submitHandler = ev => { + const formData = ev.target.serializedValue; + console.log('formData', formData); + fetch('/api/foo/', { + method: 'POST', + body: JSON.stringify(formData), + }); + }; + return html` + +
ev.preventDefault()}> + + + +
+
+ `; +}; ``` ## Features diff --git a/docs/components/form/use-cases.md b/docs/components/form/use-cases.md index 5fd0f74ee..d8f7f895f 100644 --- a/docs/components/form/use-cases.md +++ b/docs/components/form/use-cases.md @@ -10,32 +10,28 @@ import '@lion/ui/define/lion-form.js'; ## Submit & Reset -To submit a form, use a regular button (or `LionButtonSubmit`) somewhere inside the native `
`. +To submit a form, use a regular ` +
`); @@ -202,4 +202,68 @@ describe('', () => { expect(dispatchSpy.args[0][0].type).to.equal('reset'); expect(internalHandlerSpy).to.be.calledBefore(dispatchSpy); }); + + it('sets focus on submit to the first erroneous form element', async () => { + const el = await fixture(html` + +
+ <${childTag} name="firstName" .modelValue=${'Foo'} .validators=${[ + new Required(), + ]}> + <${childTag} name="lastName" .validators=${[new Required()]}> + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + const dispatchSpy = spy(el, 'dispatchEvent'); + button.click(); + expect(dispatchSpy.args[0][0].type).to.equal('submit'); + // @ts-ignore [allow-protected] in test + expect(document.activeElement).to.equal(el.formElements[1]._inputNode); + }); + + it('sets focus on submit to the first erroneous form element with a fieldset', async () => { + const el = await fixture(html` + +
+ + <${childTag} name="firstName" .modelValue=${'Foo'} .validators=${[ + new Required(), + ]}> + <${childTag} name="lastName" .validators=${[new Required()]}> + + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + const dispatchSpy = spy(el, 'dispatchEvent'); + button.click(); + expect(dispatchSpy.args[0][0].type).to.equal('submit'); + const fieldset = el.formElements[0]; + // @ts-ignore [allow-protected] in test + expect(document.activeElement).to.equal(fieldset.formElements[1]._inputNode); + }); + + it('sets focus on submit to the first form element within a erroneous fieldset', async () => { + const el = await fixture(html` + +
+ + <${childTag} name="firstName"> + <${childTag} name="lastName"> + + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + const dispatchSpy = spy(el, 'dispatchEvent'); + button.click(); + expect(dispatchSpy.args[0][0].type).to.equal('submit'); + const fieldset = el.formElements[0]; + // @ts-ignore [allow-protected] in test + expect(document.activeElement).to.equal(fieldset.formElements[0]._inputNode); + }); });