Skip to content

Commit 611832b

Browse files
authored
fix(core): inherit aria attributes on host elements (#25156)
Resolves #20127
1 parent c6afacb commit 611832b

File tree

11 files changed

+119
-19
lines changed

11 files changed

+119
-19
lines changed

core/src/components/back-button/back-button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getIonMode } from '../../global/ionic-global';
77
import type { AnimationBuilder, Color } from '../../interface';
88
import type { ButtonInterface } from '../../utils/element-interface';
99
import type { Attributes } from '../../utils/helpers';
10-
import { inheritAttributes } from '../../utils/helpers';
10+
import { inheritAriaAttributes } from '../../utils/helpers';
1111
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
1212

1313
/**
@@ -70,7 +70,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
7070
@Prop() routerAnimation: AnimationBuilder | undefined;
7171

7272
componentWillLoad() {
73-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
73+
this.inheritedAttributes = inheritAriaAttributes(this.el);
7474

7575
if (this.defaultHref === undefined) {
7676
this.defaultHref = config.get('backButtonDefaultHref');

core/src/components/breadcrumb/breadcrumb.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
55
import { getIonMode } from '../../global/ionic-global';
66
import type { AnimationBuilder, BreadcrumbCollapsedClickEventDetail, Color, RouterDirection } from '../../interface';
77
import type { Attributes } from '../../utils/helpers';
8-
import { inheritAttributes } from '../../utils/helpers';
8+
import { inheritAriaAttributes } from '../../utils/helpers';
99
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
1010

1111
/**
@@ -124,7 +124,7 @@ export class Breadcrumb implements ComponentInterface {
124124
@Event() collapsedClick!: EventEmitter<BreadcrumbCollapsedClickEventDetail>;
125125

126126
componentWillLoad() {
127-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
127+
this.inheritedAttributes = inheritAriaAttributes(this.el);
128128
}
129129

130130
private isClickable(): boolean {

core/src/components/button/button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getIonMode } from '../../global/ionic-global';
55
import type { AnimationBuilder, Color, RouterDirection } from '../../interface';
66
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
77
import type { Attributes } from '../../utils/helpers';
8-
import { hasShadowDom, inheritAttributes } from '../../utils/helpers';
8+
import { inheritAriaAttributes, hasShadowDom } from '../../utils/helpers';
99
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
1010

1111
/**
@@ -137,7 +137,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
137137
this.inToolbar = !!this.el.closest('ion-buttons');
138138
this.inListHeader = !!this.el.closest('ion-list-header');
139139
this.inItem = !!this.el.closest('ion-item') || !!this.el.closest('ion-item-divider');
140-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
140+
this.inheritedAttributes = inheritAriaAttributes(this.el);
141141
}
142142

143143
private get hasIconOnly() {

core/src/components/header/header.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Component, Element, Host, Prop, h, writeTask } from '@stencil/core';
44
import { getIonMode } from '../../global/ionic-global';
55
import { findIonContent, getScrollElement, printIonContentErrorMsg } from '../../utils/content';
66
import type { Attributes } from '../../utils/helpers';
7-
import { inheritAttributes } from '../../utils/helpers';
7+
import { inheritAriaAttributes } from '../../utils/helpers';
88
import { hostContext } from '../../utils/theme';
99

1010
import {
@@ -55,7 +55,7 @@ export class Header implements ComponentInterface {
5555
@Prop() translucent = false;
5656

5757
componentWillLoad() {
58-
this.inheritedAttributes = inheritAttributes(this.el, ['role']);
58+
this.inheritedAttributes = inheritAriaAttributes(this.el);
5959
}
6060

6161
componentDidLoad() {

core/src/components/input/input.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {
1010
TextFieldTypes,
1111
} from '../../interface';
1212
import type { Attributes } from '../../utils/helpers';
13-
import { debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
13+
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
1414
import { createColorClasses } from '../../utils/theme';
1515

1616
/**
@@ -257,7 +257,10 @@ export class Input implements ComponentInterface {
257257
}
258258

259259
componentWillLoad() {
260-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label', 'tabindex', 'title']);
260+
this.inheritedAttributes = {
261+
...inheritAriaAttributes(this.el),
262+
...inheritAttributes(this.el, ['tabindex', 'title']),
263+
};
261264
}
262265

263266
connectedCallback() {

core/src/components/menu-button/menu-button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getIonMode } from '../../global/ionic-global';
77
import type { Color } from '../../interface';
88
import type { ButtonInterface } from '../../utils/element-interface';
99
import type { Attributes } from '../../utils/helpers';
10-
import { inheritAttributes } from '../../utils/helpers';
10+
import { inheritAriaAttributes } from '../../utils/helpers';
1111
import { menuController } from '../../utils/menu-controller';
1212
import { createColorClasses, hostContext } from '../../utils/theme';
1313
import { updateVisibility } from '../menu-toggle/menu-toggle-util';
@@ -61,7 +61,7 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
6161
@Prop() type: 'submit' | 'reset' | 'button' = 'button';
6262

6363
componentWillLoad() {
64-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
64+
this.inheritedAttributes = inheritAriaAttributes(this.el);
6565
}
6666

6767
componentDidLoad() {

core/src/components/menu/menu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Animation, Gesture, GestureDetail, MenuChangeEventDetail, MenuI, S
77
import { getTimeGivenProgression } from '../../utils/animation/cubic-bezier';
88
import { GESTURE_CONTROLLER } from '../../utils/gesture';
99
import type { Attributes } from '../../utils/helpers';
10-
import { assert, clamp, inheritAttributes, isEndSide as isEnd } from '../../utils/helpers';
10+
import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '../../utils/helpers';
1111
import { menuController } from '../../utils/menu-controller';
1212
import { getOverlay } from '../../utils/overlays';
1313

@@ -226,7 +226,7 @@ export class Menu implements ComponentInterface, MenuI {
226226
}
227227

228228
componentWillLoad() {
229-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
229+
this.inheritedAttributes = inheritAriaAttributes(this.el);
230230
}
231231

232232
async componentDidLoad() {

core/src/components/range/range.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type {
1414
StyleEventDetail,
1515
} from '../../interface';
1616
import type { Attributes } from '../../utils/helpers';
17-
import { clamp, debounceEvent, getAriaLabel, inheritAttributes, renderHiddenInput } from '../../utils/helpers';
17+
import { inheritAriaAttributes, clamp, debounceEvent, getAriaLabel, renderHiddenInput } from '../../utils/helpers';
1818
import { isRTL } from '../../utils/rtl';
1919
import { createColorClasses, hostContext } from '../../utils/theme';
2020

@@ -237,7 +237,7 @@ export class Range implements ComponentInterface {
237237
*/
238238
this.rangeId = this.el.hasAttribute('id') ? this.el.getAttribute('id')! : `ion-r-${rangeIds++}`;
239239

240-
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
240+
this.inheritedAttributes = inheritAriaAttributes(this.el);
241241
}
242242

243243
componentDidLoad() {

core/src/components/textarea/textarea.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h,
44
import { getIonMode } from '../../global/ionic-global';
55
import type { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface';
66
import type { Attributes } from '../../utils/helpers';
7-
import { debounceEvent, findItemLabel, inheritAttributes, raf } from '../../utils/helpers';
7+
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes, raf } from '../../utils/helpers';
88
import { createColorClasses } from '../../utils/theme';
99

1010
/**
@@ -220,7 +220,10 @@ export class Textarea implements ComponentInterface {
220220
}
221221

222222
componentWillLoad() {
223-
this.inheritedAttributes = inheritAttributes(this.el, ['title']);
223+
this.inheritedAttributes = {
224+
...inheritAriaAttributes(this.el),
225+
...inheritAttributes(this.el, ['title']),
226+
};
224227
}
225228

226229
componentDidLoad() {

core/src/utils/helpers.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,74 @@ export const inheritAttributes = (el: HTMLElement, attributes: string[] = []) =>
103103
return attributeObject;
104104
};
105105

106+
/**
107+
* List of available ARIA attributes + `role`.
108+
* Removed deprecated attributes.
109+
* https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes
110+
*/
111+
const ariaAttributes = [
112+
'role',
113+
'aria-activedescendant',
114+
'aria-atomic',
115+
'aria-autocomplete',
116+
'aria-braillelabel',
117+
'aria-brailleroledescription',
118+
'aria-busy',
119+
'aria-checked',
120+
'aria-colcount',
121+
'aria-colindex',
122+
'aria-colindextext',
123+
'aria-colspan',
124+
'aria-controls',
125+
'aria-current',
126+
'aria-describedby',
127+
'aria-description',
128+
'aria-details',
129+
'aria-disabled',
130+
'aria-errormessage',
131+
'aria-expanded',
132+
'aria-flowto',
133+
'aria-haspopup',
134+
'aria-hidden',
135+
'aria-invalid',
136+
'aria-keyshortcuts',
137+
'aria-label',
138+
'aria-labelledby',
139+
'aria-level',
140+
'aria-live',
141+
'aria-multiline',
142+
'aria-multiselectable',
143+
'aria-orientation',
144+
'aria-owns',
145+
'aria-placeholder',
146+
'aria-posinset',
147+
'aria-pressed',
148+
'aria-readonly',
149+
'aria-relevant',
150+
'aria-required',
151+
'aria-roledescription',
152+
'aria-rowcount',
153+
'aria-rowindex',
154+
'aria-rowindextext',
155+
'aria-rowspan',
156+
'aria-selected',
157+
'aria-setsize',
158+
'aria-sort',
159+
'aria-valuemax',
160+
'aria-valuemin',
161+
'aria-valuenow',
162+
'aria-valuetext',
163+
];
164+
165+
/**
166+
* Returns an array of aria attributes that should be copied from
167+
* the shadow host element to a target within the light DOM.
168+
* @param el The element that the attributes should be copied from.
169+
*/
170+
export const inheritAriaAttributes = (el: HTMLElement) => {
171+
return inheritAttributes(el, ariaAttributes);
172+
};
173+
106174
export const addEventListener = (el: any, eventName: string, callback: any, opts?: any) => {
107175
if (typeof (window as any) !== 'undefined') {
108176
const win = window as any;

core/src/utils/test/attributes.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { inheritAttributes } from '../helpers';
1+
import { inheritAttributes, inheritAriaAttributes } from '../helpers';
22

33
describe('inheritAttributes()', () => {
44
it('should create an attribute inheritance object', () => {
@@ -37,3 +37,29 @@ describe('inheritAttributes()', () => {
3737
});
3838
});
3939
});
40+
41+
describe('inheritAriaAttributes()', () => {
42+
it('should inherit ARIA attributes defined on the HTML element', () => {
43+
const el = document.createElement('div');
44+
el.setAttribute('aria-label', 'myLabel');
45+
el.setAttribute('aria-describedby', 'myDescription');
46+
47+
const attributeObject = inheritAriaAttributes(el);
48+
49+
expect(attributeObject).toEqual({
50+
'aria-label': 'myLabel',
51+
'aria-describedby': 'myDescription',
52+
});
53+
});
54+
55+
it('should inherit the role attribute defined on the HTML element', () => {
56+
const el = document.createElement('div');
57+
el.setAttribute('role', 'button');
58+
59+
const attributeObject = inheritAriaAttributes(el);
60+
61+
expect(attributeObject).toEqual({
62+
role: 'button',
63+
});
64+
});
65+
});

0 commit comments

Comments
 (0)