diff --git a/.changeset/funny-carrots-laugh.md b/.changeset/funny-carrots-laugh.md new file mode 100644 index 000000000..6edbf2117 --- /dev/null +++ b/.changeset/funny-carrots-laugh.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[form-core] make \_setFocusOnFirstErroneousFormElement work for checkbox-group, radio-group and nested fieldsets diff --git a/packages/ui/components/form/src/LionForm.js b/packages/ui/components/form/src/LionForm.js index 8848a4b1f..57014307b 100644 --- a/packages/ui/components/form/src/LionForm.js +++ b/packages/ui/components/form/src/LionForm.js @@ -10,15 +10,6 @@ const throwFormNodeError = () => { ); }; -/** - * @param {FormRegistrarHost} formEl - * @returns {boolean} - */ -function hasFocusableChildren(formEl) { - // this implies all children have the same type (either all of them are focusable or none of them are) - return formEl.formElements?.some(child => child._focusableNode); -} - /** * LionForm: form wrapper providing extra features and integration with lion-field elements. * @@ -104,10 +95,10 @@ export class LionForm extends LionFieldset { element.formElements.find(child => child.hasFeedbackFor.includes('error')) || element.formElements[0]; - if (hasFocusableChildren(firstFormElWithError)) { - this._setFocusOnFirstErroneousFormElement(firstFormElWithError); - } else { + if (firstFormElWithError._focusableNode) { firstFormElWithError._focusableNode.focus(); + } else { + this._setFocusOnFirstErroneousFormElement(firstFormElWithError); } } diff --git a/packages/ui/components/form/test/lion-form.test.js b/packages/ui/components/form/test/lion-form.test.js index cbf55bfd7..f1dcc0403 100644 --- a/packages/ui/components/form/test/lion-form.test.js +++ b/packages/ui/components/form/test/lion-form.test.js @@ -5,6 +5,10 @@ import '@lion/ui/define/lion-field.js'; import '@lion/ui/define/lion-validation-feedback.js'; import '@lion/ui/define/lion-listbox.js'; import '@lion/ui/define/lion-option.js'; +import '@lion/ui/define/lion-checkbox-group.js'; +import '@lion/ui/define/lion-checkbox.js'; +import '@lion/ui/define/lion-radio-group.js'; +import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-form.js'; import { aTimeout, @@ -264,10 +268,31 @@ describe('', () => { 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); }); + it('sets focus on submit to the first form element within a erroneous fieldset within another fieldset', async () => { + const el = await fixture(html` + +
+ + + <${childTag} name="firstName"> + + + + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + const parentFieldSetEl = el.formElements[0]; + const childFieldsetEl = parentFieldSetEl.formElements[0]; + const inputEl = childFieldsetEl.formElements[0]; + button.click(); + expect(document.activeElement).to.equal(inputEl._focusableNode); + }); + it('sets focus on submit to the first form element within a erroneous listbox', async () => { const el = await fixture(html` @@ -285,4 +310,66 @@ describe('', () => { const listboxEl = el.formElements[0]; expect(document.activeElement).to.equal(listboxEl._inputNode); }); + + it('sets focus on submit to the first form element within a erroneous listbox within a fieldset', async () => { + const el = await fixture(html` + +
+ + + a + b + + + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + button.click(); + const fieldsetEl = el.formElements[0]; + const listboxEl = fieldsetEl.formElements[0]; + expect(document.activeElement).to.equal(listboxEl._inputNode); + }); + + it('sets focus on submit to the first form element within a erroneous checkbox-group', async () => { + const el = await fixture(html` + +
+ + + + + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + const checkboxGroupEl = el.formElements[0]; + const checkboxEl = checkboxGroupEl.formElements[0]; + button.click(); + expect(document.activeElement).to.equal(checkboxEl._focusableNode); + }); + + it('sets focus on submit to the first form element within a erroneous radio-group', async () => { + const el = await fixture(html` + +
+ + + + + + + +
+
+ `); + const button = /** @type {HTMLButtonElement} */ (el.querySelector('button')); + + const radioGroupEl = el.formElements[0]; + const radioEl = radioGroupEl.formElements[0]; + button.click(); + expect(document.activeElement).to.equal(radioEl._focusableNode); + }); });