fix(form-core): add focusableNode to choiceGroupMixin to make _setFo… (#2382)

* fix(form-core): add focusableNode to choiceGroupMixin to make \_setFocusOnFirstErroneousFormElement function work for checkbox-group and radio-group

* chore: rewrite check for _setFocusOnFirstErroneousFormElement to make it work without focusableNode inside LionChoiceGroup
This commit is contained in:
gerjanvangeest 2024-10-22 15:44:01 +02:00 committed by GitHub
parent 905ed37823
commit 2d4fb0ecdb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 96 additions and 13 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ui': patch
---
[form-core] make \_setFocusOnFirstErroneousFormElement work for checkbox-group, radio-group and nested fieldsets

View file

@ -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. * 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.find(child => child.hasFeedbackFor.includes('error')) ||
element.formElements[0]; element.formElements[0];
if (hasFocusableChildren(firstFormElWithError)) { if (firstFormElWithError._focusableNode) {
this._setFocusOnFirstErroneousFormElement(firstFormElWithError);
} else {
firstFormElWithError._focusableNode.focus(); firstFormElWithError._focusableNode.focus();
} else {
this._setFocusOnFirstErroneousFormElement(firstFormElWithError);
} }
} }

View file

@ -5,6 +5,10 @@ import '@lion/ui/define/lion-field.js';
import '@lion/ui/define/lion-validation-feedback.js'; import '@lion/ui/define/lion-validation-feedback.js';
import '@lion/ui/define/lion-listbox.js'; import '@lion/ui/define/lion-listbox.js';
import '@lion/ui/define/lion-option.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 '@lion/ui/define/lion-form.js';
import { import {
aTimeout, aTimeout,
@ -264,10 +268,31 @@ describe('<lion-form>', () => {
button.click(); button.click();
expect(dispatchSpy.args[0][0].type).to.equal('submit'); expect(dispatchSpy.args[0][0].type).to.equal('submit');
const fieldset = el.formElements[0]; const fieldset = el.formElements[0];
// @ts-ignore [allow-protected] in test
expect(document.activeElement).to.equal(fieldset.formElements[0]._inputNode); 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`
<lion-form>
<form>
<lion-fieldset name="parentFieldset">
<lion-fieldset name="childFieldset" .validators="${[new Required()]}">
<${childTag} name="firstName"></${childTag}>
</lion-fieldset>
</lion-fieldset>
<button type="submit">submit</button>
</form>
</lion-form>
`);
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 () => { it('sets focus on submit to the first form element within a erroneous listbox', async () => {
const el = await fixture(html` const el = await fixture(html`
<lion-form> <lion-form>
@ -285,4 +310,66 @@ describe('<lion-form>', () => {
const listboxEl = el.formElements[0]; const listboxEl = el.formElements[0];
expect(document.activeElement).to.equal(listboxEl._inputNode); 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`
<lion-form>
<form>
<lion-fieldset name="fieldset">
<lion-listbox name="name" .validators="${[new Required()]}">
<lion-option value="a">a</lion-option>
<lion-option value="b">b</lion-option>
</lion-listbox>
</lion-fieldset>
<button type="submit">submit</button>
</form>
</lion-form>
`);
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`
<lion-form>
<form>
<lion-checkbox-group name="name" .validators="${[new Required()]}">
<lion-checkbox .choiceValue=${'a'} label="a"></lion-checkbox>
<lion-checkbox .choiceValue=${'b'} label="b"></lion-checkbox>
</lion-checkbox-group>
<button type="submit">submit</button>
</form>
</lion-form>
`);
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`
<lion-form>
<form>
<lion-radio-group name="name" .validators="${[new Required()]}">
<lion-radio .choiceValue=${'a'} label="a"></lion-radio>
<lion-radio .choiceValue=${'b'} label="b"></lion-radio>
</lion-radio-group>
</lion-fieldset>
<button type="submit">submit</button>
</form>
</lion-form>
`);
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);
});
}); });