Skip to content

Commit 7bf4c9a

Browse files
committed
fixup! fix(material/button-toggle): changed after checked error when updating tabindex (#31172)
1 parent 5a8e266 commit 7bf4c9a

File tree

6 files changed

+52
-4
lines changed

6 files changed

+52
-4
lines changed

src/cdk-experimental/radio/radio.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,14 @@ describe('CdkRadioGroup', () => {
508508
setupRadioGroup({options: []});
509509
expect(radioButtons.length).toBe(0);
510510
});
511+
512+
describe('bad accessibility violations', () => {
513+
it('should report when the selected radio button is disabled and skipDisabled is true', () => {
514+
spyOn(console, 'error');
515+
setupRadioGroup({value: 1, skipDisabled: true, disabledOptions: [1]});
516+
expect(console.error).toHaveBeenCalled();
517+
});
518+
});
511519
});
512520
});
513521

src/cdk-experimental/radio/radio.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ export class CdkRadioGroup<V> {
137137
private _hasFocused = signal(false);
138138

139139
constructor() {
140+
afterRenderEffect(() => {
141+
const violations = this.pattern.validate();
142+
for (const violation of violations) {
143+
console.error(violation);
144+
}
145+
});
146+
140147
afterRenderEffect(() => {
141148
if (!this._hasFocused()) {
142149
this.pattern.setDefaultState();

src/cdk-experimental/ui-patterns/behaviors/list-selection/list-selection.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {signal} from '@angular/core';
9+
import {computed, signal} from '@angular/core';
1010
import {SignalLike, WritableSignalLike} from '../signal-like/signal-like';
1111
import {ListFocus, ListFocusInputs, ListFocusItem} from '../list-focus/list-focus';
1212

@@ -36,6 +36,11 @@ export class ListSelection<T extends ListSelectionItem<V>, V> {
3636
/** The end index to use for range selection. */
3737
rangeEndIndex = signal<number>(0);
3838

39+
/** The currently selected items. */
40+
selectedItems = computed(() =>
41+
this.inputs.items().filter(item => this.inputs.value().includes(item.value())),
42+
);
43+
3944
constructor(readonly inputs: ListSelectionInputs<T, V> & {focusManager: ListFocus<T>}) {}
4045

4146
/** Selects the item at the current active index. */

src/cdk-experimental/ui-patterns/radio/radio-group.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ export class RadioGroupPattern<V> {
4848
/** Whether the radio group is disabled. */
4949
disabled = computed(() => this.inputs.disabled() || this.focusManager.isListDisabled());
5050

51+
/** The currently selected radio button. */
52+
selectedItem = computed(() => this.selection.selectedItems()[0]);
53+
5154
/** Whether the radio group is readonly. */
52-
readonly: SignalLike<boolean>;
55+
readonly = computed(() => this.selectedItem()?.disabled() || this.inputs.readonly());
5356

5457
/** The tabindex of the radio group (if using activedescendant). */
5558
tabindex = computed(() => this.focusManager.getListTabindex());
@@ -111,7 +114,6 @@ export class RadioGroupPattern<V> {
111114
});
112115

113116
constructor(readonly inputs: RadioGroupInputs<V>) {
114-
this.readonly = inputs.readonly;
115117
this.orientation = inputs.orientation;
116118

117119
this.focusManager = new ListFocus(inputs);
@@ -194,6 +196,19 @@ export class RadioGroupPattern<V> {
194196
}
195197
}
196198

199+
/** Validates the state of the radio group and returns a list of accessibility violations. */
200+
validate(): string[] {
201+
const violations: string[] = [];
202+
203+
if (this.selectedItem()?.disabled() && this.inputs.skipDisabled()) {
204+
violations.push(
205+
"Accessibility Violation: The selected radio button is disabled while 'skipDisabled' is true, making the selection unreachable via keyboard.",
206+
);
207+
}
208+
209+
return violations;
210+
}
211+
197212
/** Safely performs a navigation operation and updates selection if needed. */
198213
private _navigate(opts: SelectOptions = {}, operation: () => boolean) {
199214
const moved = operation();

src/cdk-experimental/ui-patterns/radio/radio.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,4 +298,16 @@ describe('RadioGroup Pattern', () => {
298298
expect(radioGroup.inputs.activeIndex()).toBe(0); // Defaults to first focusable
299299
});
300300
});
301+
302+
describe('validate', () => {
303+
it('should report a violation if the selected item is disabled and skipDisabled is true', () => {
304+
const {radioGroup, radioButtons} = getDefaultPatterns({
305+
value: signal(['Banana']),
306+
skipDisabled: signal(true),
307+
});
308+
radioButtons[1].disabled.set(true); // Disable the selected item.
309+
const violations = radioGroup.validate();
310+
expect(violations.length).toBe(1);
311+
});
312+
});
301313
});

src/components-examples/cdk-experimental/radio/cdk-radio/cdk-radio-example.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,5 @@
5959
</li>
6060
}
6161
</ul>
62-
<!-- #enddocregion radio-group -->
62+
<!-- #enddocregion radio-group -->
63+

0 commit comments

Comments
 (0)