diff --git a/.changeset/witty-houses-argue.md b/.changeset/witty-houses-argue.md
new file mode 100644
index 0000000000..f89f2a6707
--- /dev/null
+++ b/.changeset/witty-houses-argue.md
@@ -0,0 +1,5 @@
+---
+'@lion/ui': patch
+---
+
+[form-core] order aria-labelledby and aria-describedby based on slot order instead of dom order
diff --git a/packages/ui/components/form-core/src/FormControlMixin.js b/packages/ui/components/form-core/src/FormControlMixin.js
index fd0c0c03fe..52df9d9537 100644
--- a/packages/ui/components/form-core/src/FormControlMixin.js
+++ b/packages/ui/components/form-core/src/FormControlMixin.js
@@ -390,8 +390,20 @@ const FormControlMixinImplementation = superclass =>
const insideNodes = nodes.filter(n => this.contains(n));
const outsideNodes = nodes.filter(n => !this.contains(n));
+ const insideSlots = insideNodes.map(n => n.assignedSlot || n);
+ const orderedInsideSlots = [...getAriaElementsInRightDomOrder(insideSlots)];
+ /** @type {Element[]} */
+ const orderedInsideNodes = [];
+ orderedInsideSlots.forEach(assignedNode => {
+ insideNodes.forEach(node => {
+ // @ts-ignore
+ if (assignedNode.name === node.slot) {
+ orderedInsideNodes.push(node);
+ }
+ });
+ });
// eslint-disable-next-line no-param-reassign
- nodes = [...getAriaElementsInRightDomOrder(insideNodes), ...outsideNodes];
+ nodes = [...orderedInsideNodes, ...outsideNodes];
}
const string = nodes.map(n => n.id).join(' ');
this._inputNode.setAttribute(attrName, string);
diff --git a/packages/ui/components/form-core/test/FormControlMixin.test.js b/packages/ui/components/form-core/test/FormControlMixin.test.js
index 7e83c1e9b3..123213732d 100644
--- a/packages/ui/components/form-core/test/FormControlMixin.test.js
+++ b/packages/ui/components/form-core/test/FormControlMixin.test.js
@@ -349,19 +349,18 @@ describe('FormControlMixin', () => {
);
});
- it('sorts internal elements, and allows opt-out', async () => {
+ it('sorts internal elements based on assigned slots, and allows opt-out', async () => {
const wrapper = await fixture(html`
-
- <${tag}>
-
-
-
- Added to description by default
-
- ${tag}>
-
should go after input internals
-
should go after input internals
-
`);
+
+ <${tag}>
+
+
+
+ Added to description by default
+
+ ${tag}>
+
+ `);
const el = /** @type {FormControlMixinClass} */ (wrapper.querySelector(tagString));
const { _inputNode } = getFormControlMembers(el);
@@ -370,6 +369,11 @@ describe('FormControlMixin', () => {
// A real life scenario would be for instance when
// a Field or FormGroup would be extended and an extra slot would be added in the template
const myInput = /** @type {HTMLElement} */ (wrapper.querySelector('#myInput'));
+ const internalLabel = /** @type {HTMLElement} */ (wrapper.querySelector('#internalLabel'));
+ const internalDescription = /** @type {HTMLElement} */ (
+ wrapper.querySelector('#internalDescription')
+ );
+
el.addToAriaLabelledBy(myInput);
await el.updateComplete;
el.addToAriaDescribedBy(myInput);
@@ -377,29 +381,29 @@ describe('FormControlMixin', () => {
expect(
/** @type {string} */ (_inputNode.getAttribute('aria-labelledby')).split(' '),
- ).to.eql(['myInput', 'internalLabel']);
+ ).to.eql(['internalLabel', 'myInput']);
expect(
/** @type {string} */ (_inputNode.getAttribute('aria-describedby')).split(' '),
- ).to.eql(['myInput', 'internalDescription']);
+ ).to.eql(['internalDescription', 'myInput']);
// cleanup
- el.removeFromAriaLabelledBy(myInput);
+ el.removeFromAriaLabelledBy(internalLabel);
await el.updateComplete;
- el.removeFromAriaDescribedBy(myInput);
+ el.removeFromAriaDescribedBy(internalDescription);
await el.updateComplete;
// opt-out of reorder
- el.addToAriaLabelledBy(myInput, { reorder: false });
+ el.addToAriaLabelledBy(internalLabel, { reorder: false });
await el.updateComplete;
- el.addToAriaDescribedBy(myInput, { reorder: false });
+ el.addToAriaDescribedBy(internalDescription, { reorder: false });
await el.updateComplete;
expect(
/** @type {string} */ (_inputNode.getAttribute('aria-labelledby')).split(' '),
- ).to.eql(['internalLabel', 'myInput']);
+ ).to.eql(['myInput', 'internalLabel']);
expect(
/** @type {string} */ (_inputNode.getAttribute('aria-describedby')).split(' '),
- ).to.eql(['internalDescription', 'myInput']);
+ ).to.eql(['myInput', 'internalDescription']);
});
it('respects provided order for external elements', async () => {