fix(form-core): remove fieldset label from input-field aria-labelledby, add opt-in via data-label
This commit is contained in:
parent
87005afbd2
commit
92a5cd1e3d
4 changed files with 32 additions and 56 deletions
6
.changeset/slow-ties-carry.md
Normal file
6
.changeset/slow-ties-carry.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
'@lion/ui': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
[form-core] remove fieldset label from input-field aria-labelledby
|
||||||
|
[form-core] remove fieldset help-text from input-field aria-describedby
|
||||||
|
|
@ -46,7 +46,7 @@ const FormGroupMixinImplementation = superclass =>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The host element with role group (or radigroup or form) containing neccessary aria attributes
|
* The host element with role group (or radio-group or form) containing necessary aria attributes
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
get _inputNode() {
|
get _inputNode() {
|
||||||
|
|
@ -478,7 +478,7 @@ const FormGroupMixinImplementation = superclass =>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traverses the _parentFormGroup tree, and gathers all aria description elements
|
* Traverses the _parentFormGroup tree, and gathers all aria description elements
|
||||||
* (feedback and helptext) that should be provided to children.
|
* (feedback) that should be provided to children.
|
||||||
*
|
*
|
||||||
* In the example below, when the input for 'street' has focus, a screenreader user
|
* In the example below, when the input for 'street' has focus, a screenreader user
|
||||||
* would hear the #group-error.
|
* would hear the #group-error.
|
||||||
|
|
@ -503,7 +503,9 @@ const FormGroupMixinImplementation = superclass =>
|
||||||
const descriptionElements = parent._getAriaDescriptionElements();
|
const descriptionElements = parent._getAriaDescriptionElements();
|
||||||
const orderedEls = getAriaElementsInRightDomOrder(descriptionElements, { reverse: true });
|
const orderedEls = getAriaElementsInRightDomOrder(descriptionElements, { reverse: true });
|
||||||
orderedEls.forEach(el => {
|
orderedEls.forEach(el => {
|
||||||
|
if (el.getAttribute('slot') === 'feedback') {
|
||||||
this.__descriptionElementsInParentChain.add(el);
|
this.__descriptionElementsInParentChain.add(el);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Also check if the newly added child needs to refer grandparents
|
// Also check if the newly added child needs to refer grandparents
|
||||||
parent = parent._parentFormGroup;
|
parent = parent._parentFormGroup;
|
||||||
|
|
@ -549,9 +551,6 @@ const FormGroupMixinImplementation = superclass =>
|
||||||
this.__linkParentMessages(child);
|
this.__linkParentMessages(child);
|
||||||
this.validate({ clearCurrentResult: true });
|
this.validate({ clearCurrentResult: true });
|
||||||
|
|
||||||
if (typeof child.addToAriaLabelledBy === 'function' && this._labelNode) {
|
|
||||||
child.addToAriaLabelledBy(this._labelNode, { reorder: false });
|
|
||||||
}
|
|
||||||
if (!child.modelValue) {
|
if (!child.modelValue) {
|
||||||
const pVals = this.__pendingValues;
|
const pVals = this.__pendingValues;
|
||||||
if (pVals.modelValue && pVals.modelValue[child.name]) {
|
if (pVals.modelValue && pVals.modelValue[child.name]) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { LitElement } from 'lit';
|
import { LitElement } from 'lit';
|
||||||
import '@lion/ui/define/lion-field.js';
|
import '@lion/ui/define/lion-field.js';
|
||||||
import '@lion/ui/define/lion-validation-feedback.js';
|
import '@lion/ui/define/lion-validation-feedback.js';
|
||||||
import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js';
|
|
||||||
import { LionInput } from '@lion/ui/input.js';
|
import { LionInput } from '@lion/ui/input.js';
|
||||||
import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
|
import { localizeTearDown } from '@lion/ui/localize-test-helpers.js';
|
||||||
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
||||||
|
|
@ -63,41 +62,6 @@ export function runFormGroupMixinInputSuite(cfg = {}) {
|
||||||
'custom[]': ['custom 1', ''],
|
'custom[]': ['custom 1', ''],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('suffixes child labels with group label, just like in <fieldset>', async () => {
|
|
||||||
const el = /** @type {FormGroup} */ (
|
|
||||||
await fixture(html`
|
|
||||||
<${tag} label="set">
|
|
||||||
<${childTag} name="A" label="fieldA"></${childTag}>
|
|
||||||
<${childTag} name="B" label="fieldB"></${childTag}>
|
|
||||||
</${tag}>
|
|
||||||
`)
|
|
||||||
);
|
|
||||||
const { _labelNode } = getFormControlMembers(el);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {LionInput} formControl
|
|
||||||
*/
|
|
||||||
function getLabels(formControl) {
|
|
||||||
const control = getFormControlMembers(formControl);
|
|
||||||
|
|
||||||
return /** @type {string} */ (control._inputNode.getAttribute('aria-labelledby')).split(
|
|
||||||
' ',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const field1 = el.formElements[0];
|
|
||||||
const field2 = el.formElements[1];
|
|
||||||
|
|
||||||
expect(getLabels(field1)).to.eql([field1._labelNode.id, _labelNode.id]);
|
|
||||||
expect(getLabels(field2)).to.eql([field2._labelNode.id, _labelNode.id]);
|
|
||||||
|
|
||||||
// Test the cleanup on disconnected
|
|
||||||
el.removeChild(field1);
|
|
||||||
|
|
||||||
// TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901
|
|
||||||
// await field1.updateComplete;
|
|
||||||
// expect(getLabels(field1)).to.eql([field1._labelNode.id]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Screen reader relations (aria-describedby) for child inputs and fieldsets', () => {
|
describe('Screen reader relations (aria-describedby) for child inputs and fieldsets', () => {
|
||||||
|
|
@ -307,21 +271,10 @@ export function runFormGroupMixinInputSuite(cfg = {}) {
|
||||||
await childAriaTest(await childAriaFixture('feedback'));
|
await childAriaTest(await childAriaFixture('feedback'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`reads help-text message belonging to fieldset when child input is focused
|
|
||||||
(via aria-describedby)`, async () => {
|
|
||||||
await childAriaTest(await childAriaFixture('help-text'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901
|
// TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901
|
||||||
it.skip(`cleans up feedback message belonging to fieldset on disconnect`, async () => {
|
it.skip(`cleans up feedback message belonging to fieldset on disconnect`, async () => {
|
||||||
const el = await childAriaFixture('feedback');
|
const el = await childAriaFixture('feedback');
|
||||||
await childAriaTest(el, { cleanupPhase: true });
|
await childAriaTest(el, { cleanupPhase: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: wait for updated on disconnected to be fixed: https://github.com/lit/lit/issues/1901
|
|
||||||
it.skip(`cleans up help-text message belonging to fieldset on disconnect`, async () => {
|
|
||||||
const el = await childAriaFixture('help-text');
|
|
||||||
await childAriaTest(el, { cleanupPhase: true });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1356,6 +1356,24 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
expect(el.hasAttribute('aria-labelledby')).to.equal(true);
|
expect(el.hasAttribute('aria-labelledby')).to.equal(true);
|
||||||
expect(el.getAttribute('aria-labelledby')).contains(label.id);
|
expect(el.getAttribute('aria-labelledby')).contains(label.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets an aria-describedby to its children from element with slot="feedback"', async () => {
|
||||||
|
const el = /** @type {FormGroup} */ (
|
||||||
|
await fixture(html`
|
||||||
|
<${tag}>
|
||||||
|
<label slot="label">My Label</label>
|
||||||
|
<div slot="feedback">My Feedback</div>
|
||||||
|
${inputSlots}
|
||||||
|
</${tag}>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
const feedback = /** @type {HTMLElement} */ (
|
||||||
|
Array.from(el.children).find(child => child.slot === 'feedback')
|
||||||
|
);
|
||||||
|
expect(el.formElements[0]._inputNode.getAttribute('aria-describedby')).contains(
|
||||||
|
feedback.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Dynamically rendered children', () => {
|
describe('Dynamically rendered children', () => {
|
||||||
|
|
@ -1395,7 +1413,7 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
const dynamicChildrenTag = unsafeStatic(dynamicChildrenTagString);
|
const dynamicChildrenTag = unsafeStatic(dynamicChildrenTagString);
|
||||||
|
|
||||||
it(`when rendering children right from the start, sets their values correctly
|
it(`when rendering children right from the start, sets their values correctly
|
||||||
based on prefilled model/seriazedValue`, async () => {
|
based on prefilled model/serializedValue`, async () => {
|
||||||
const el = /** @type {DynamicCWrapper} */ (
|
const el = /** @type {DynamicCWrapper} */ (
|
||||||
await fixture(html`
|
await fixture(html`
|
||||||
<${dynamicChildrenTag}
|
<${dynamicChildrenTag}
|
||||||
|
|
@ -1430,7 +1448,7 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`when rendering children delayed, sets their values
|
it(`when rendering children delayed, sets their values
|
||||||
correctly based on prefilled model/seriazedValue`, async () => {
|
correctly based on prefilled model/serializedValue`, async () => {
|
||||||
const el = /** @type {DynamicCWrapper} */ (
|
const el = /** @type {DynamicCWrapper} */ (
|
||||||
await fixture(html`
|
await fixture(html`
|
||||||
<${dynamicChildrenTag} .modelValue="${{ firstName: 'foo', lastName: 'bar' }}">
|
<${dynamicChildrenTag} .modelValue="${{ firstName: 'foo', lastName: 'bar' }}">
|
||||||
|
|
@ -1463,7 +1481,7 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`when rendering children partly delayed, sets their values correctly based on
|
it(`when rendering children partly delayed, sets their values correctly based on
|
||||||
prefilled model/seriazedValue`, async () => {
|
prefilled model/serializedValue`, async () => {
|
||||||
const el = /** @type {DynamicCWrapper} */ (
|
const el = /** @type {DynamicCWrapper} */ (
|
||||||
await fixture(html`
|
await fixture(html`
|
||||||
<${dynamicChildrenTag} .fields="${['firstName']}" .modelValue="${{
|
<${dynamicChildrenTag} .fields="${['firstName']}" .modelValue="${{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue