Skip to content

Commit 5df9410

Browse files
asyncLizcopybara-github
authored andcommitted
fix!: aria-labels announcing twice with "group" on components
BREAKING CHANGE: `querySelector` for `[role]` and `[aria-*]` attributes may no longer work. See `@material/web/migrations/v2/README.md` and `@material/web/migrations/v2/query-selector-aria.ts`. Browser/SR test results (go/mwc-double-aria-test-results) - ✅ VoiceOver on Chrome - ✅ VoiceOver on iOS Safari - ✅ TalkBack on Chrome - ✅ ChromeVox on Chrome - ✅ NVDA on Chrome - ✅ NVDA on Firefox - ✅ JAWS on Chrome - ✅ JAWS on Firefox (Optional) - ❓ VoiceOver on Safari - ❓ VoiceOver on Firefox PiperOrigin-RevId: 648859827
1 parent 352607d commit 5df9410

File tree

28 files changed

+815
-203
lines changed

28 files changed

+815
-203
lines changed

button/internal/button.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {html, isServer, LitElement, nothing} from 'lit';
1111
import {property, query, queryAssignedElements} from 'lit/decorators.js';
1212

1313
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
14-
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
14+
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
1515
import {
1616
FormSubmitter,
1717
setupFormSubmitter,
@@ -27,14 +27,13 @@ import {
2727
} from '../../labs/behaviors/element-internals.js';
2828

2929
// Separate variable needed for closure.
30-
const buttonBaseClass = mixinElementInternals(LitElement);
30+
const buttonBaseClass = mixinDelegatesAria(mixinElementInternals(LitElement));
3131

3232
/**
3333
* A button component.
3434
*/
3535
export abstract class Button extends buttonBaseClass implements FormSubmitter {
3636
static {
37-
requestUpdateOnAriaChange(Button);
3837
setupFormSubmitter(Button);
3938
}
4039

checkbox/internal/checkbox.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {property, query, state} from 'lit/decorators.js';
1212
import {classMap} from 'lit/directives/class-map.js';
1313

1414
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
15-
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
15+
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
1616
import {
1717
dispatchActivationClick,
1818
isActivationClick,
@@ -32,8 +32,10 @@ import {
3232
import {CheckboxValidator} from '../../labs/behaviors/validators/checkbox-validator.js';
3333

3434
// Separate variable needed for closure.
35-
const checkboxBaseClass = mixinConstraintValidation(
36-
mixinFormAssociated(mixinElementInternals(LitElement)),
35+
const checkboxBaseClass = mixinDelegatesAria(
36+
mixinConstraintValidation(
37+
mixinFormAssociated(mixinElementInternals(LitElement)),
38+
),
3739
);
3840

3941
/**
@@ -48,10 +50,6 @@ const checkboxBaseClass = mixinConstraintValidation(
4850
* --bubbles --composed
4951
*/
5052
export class Checkbox extends checkboxBaseClass {
51-
static {
52-
requestUpdateOnAriaChange(Checkbox);
53-
}
54-
5553
/** @nocollapse */
5654
static override shadowRootOptions = {
5755
...LitElement.shadowRootOptions,

chips/internal/chip.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,17 @@ import {html, LitElement, PropertyValues, TemplateResult} from 'lit';
1111
import {property} from 'lit/decorators.js';
1212
import {ClassInfo, classMap} from 'lit/directives/class-map.js';
1313

14-
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
14+
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
15+
16+
// Separate variable needed for closure.
17+
const chipBaseClass = mixinDelegatesAria(LitElement);
1518

1619
/**
1720
* A chip component.
1821
*
1922
* @fires update-focus {Event} Dispatched when `disabled` is toggled. --bubbles
2023
*/
21-
export abstract class Chip extends LitElement {
22-
static {
23-
requestUpdateOnAriaChange(Chip);
24-
}
25-
24+
export abstract class Chip extends chipBaseClass {
2625
/** @nocollapse */
2726
static override shadowRootOptions = {
2827
...LitElement.shadowRootOptions,

dialog/internal/dialog.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {property, query, state} from 'lit/decorators.js';
1111
import {classMap} from 'lit/directives/class-map.js';
1212

1313
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
14-
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
14+
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
1515
import {redispatchEvent} from '../../internal/events/redispatch-event.js';
1616

1717
import {
@@ -21,6 +21,9 @@ import {
2121
DialogAnimationArgs,
2222
} from './animations.js';
2323

24+
// Separate variable needed for closure.
25+
const dialogBaseClass = mixinDelegatesAria(LitElement);
26+
2427
/**
2528
* A dialog component.
2629
*
@@ -31,11 +34,7 @@ import {
3134
* @fires cancel {Event} Dispatched when the dialog has been canceled by clicking
3235
* on the scrim or pressing Escape.
3336
*/
34-
export class Dialog extends LitElement {
35-
static {
36-
requestUpdateOnAriaChange(Dialog);
37-
}
38-
37+
export class Dialog extends dialogBaseClass {
3938
// We do not use `delegatesFocus: true` due to a Chromium bug with
4039
// selecting text.
4140
// See https://bugs.chromium.org/p/chromium/issues/detail?id=950357

fab/internal/shared.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,18 @@ import {property} from 'lit/decorators.js';
1313
import {classMap} from 'lit/directives/class-map.js';
1414

1515
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
16-
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
16+
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
1717

1818
/**
1919
* Sizes variants available to non-extended FABs.
2020
*/
2121
export type FabSize = 'medium' | 'small' | 'large';
2222

23-
// tslint:disable-next-line:enforce-comments-on-exported-symbols
24-
export abstract class SharedFab extends LitElement {
25-
static {
26-
requestUpdateOnAriaChange(SharedFab);
27-
}
23+
// Separate variable needed for closure.
24+
const fabBaseClass = mixinDelegatesAria(LitElement);
2825

26+
// tslint:disable-next-line:enforce-comments-on-exported-symbols
27+
export abstract class SharedFab extends fabBaseClass {
2928
/** @nocollapse */
3029
static override shadowRootOptions: ShadowRootInit = {
3130
mode: 'open' as const,

iconbutton/internal/icon-button.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {classMap} from 'lit/directives/class-map.js';
1313
import {literal, html as staticHtml} from 'lit/static-html.js';
1414

1515
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
16-
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
16+
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
1717
import {
1818
FormSubmitter,
1919
setupFormSubmitter,
@@ -28,7 +28,9 @@ import {
2828
type LinkTarget = '_blank' | '_parent' | '_self' | '_top';
2929

3030
// Separate variable needed for closure.
31-
const iconButtonBaseClass = mixinElementInternals(LitElement);
31+
const iconButtonBaseClass = mixinDelegatesAria(
32+
mixinElementInternals(LitElement),
33+
);
3234

3335
/**
3436
* A button for rendering icons.
@@ -39,7 +41,6 @@ const iconButtonBaseClass = mixinElementInternals(LitElement);
3941
*/
4042
export class IconButton extends iconButtonBaseClass implements FormSubmitter {
4143
static {
42-
requestUpdateOnAriaChange(IconButton);
4344
setupFormSubmitter(IconButton);
4445
}
4546

internal/aria/aria.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const ARIA_ATTRIBUTES = ARIA_PROPERTIES.map(ariaPropertyToAttribute);
7373
* @return True if the attribute is an aria attribute, or false if not.
7474
*/
7575
export function isAriaAttribute(attribute: string): attribute is ARIAAttribute {
76-
return attribute.startsWith('aria-') || attribute === 'role';
76+
return ARIA_ATTRIBUTES.includes(attribute as ARIAAttribute);
7777
}
7878

7979
/**

internal/aria/aria_test.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ describe('aria', () => {
1616
.toBeTrue();
1717
});
1818

19-
it('should return true for aria idref attributes', () => {
19+
it('should return false for aria idref attributes', () => {
2020
expect(isAriaAttribute('aria-labelledby'))
2121
.withContext('aria-labelledby input')
22-
.toBeTrue();
22+
.toBeFalse();
2323
});
2424

2525
it('should return true for role', () => {
@@ -29,6 +29,12 @@ describe('aria', () => {
2929
it('should return false for non-aria attributes', () => {
3030
expect(isAriaAttribute('label')).withContext('label input').toBeFalse();
3131
});
32+
33+
it('should return false for custom aria-* attributes', () => {
34+
expect(isAriaAttribute('aria-label-custom'))
35+
.withContext('aria-label-custom input')
36+
.toBeFalse();
37+
});
3238
});
3339

3440
describe('ariaPropertyToAttribute()', () => {

0 commit comments

Comments
 (0)