Skip to content

Commit 99d1dfd

Browse files
committed
fix(material/radio): required attribute being set on buttons
fixes when `MatRadioGroup` is set to be required it was setting all of its `MatRadioButton` to be required as-well which is confusing for assistive technologies, this commit ensures we only set aria-required on group rather than all buttons unless button is being used standalone of `MatRadioGroup` fixes #30399
1 parent 1b3c42e commit 99d1dfd

File tree

4 files changed

+44
-1
lines changed

4 files changed

+44
-1
lines changed

src/material/radio/radio.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
[disabled]="disabled && !disabledInteractive"
99
[attr.name]="name"
1010
[attr.value]="value"
11-
[required]="required"
11+
[required]="getInputRequiredAttribute()"
1212
[attr.aria-label]="ariaLabel"
1313
[attr.aria-labelledby]="ariaLabelledby"
1414
[attr.aria-describedby]="ariaDescribedby"

src/material/radio/radio.spec.ts

+27
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,33 @@ describe('MatRadio', () => {
524524
expect(groupInstance.selected).toBe(null);
525525
expect(groupInstance.value).toBe('fire');
526526
});
527+
528+
it('should set aria-required on radio group when required', () => {
529+
fixture.changeDetectorRef.markForCheck();
530+
fixture.detectChanges();
531+
532+
let group = fixture.debugElement.query(By.directive(MatRadioGroup)).nativeElement;
533+
534+
// by default it shouldn't be there
535+
expect(group.hasAttribute('aria-required')).toBeFalse();
536+
537+
testComponent.isGroupRequired = true;
538+
fixture.changeDetectorRef.markForCheck();
539+
fixture.detectChanges();
540+
541+
group = fixture.debugElement.query(By.directive(MatRadioGroup)).nativeElement;
542+
expect(group.hasAttribute('aria-required')).toBe(true);
543+
});
544+
545+
it('should set not set required attribute on matRadioButton when matRadioGroup is required', () => {
546+
testComponent.isGroupRequired = true;
547+
fixture.changeDetectorRef.markForCheck();
548+
fixture.detectChanges();
549+
550+
for (const radio of radioDebugElements) {
551+
expect(radio.nativeElement.hasAttribute('aria-required')).toBeFalse();
552+
}
553+
});
527554
});
528555

529556
describe('group with ngModel', () => {

src/material/radio/radio.ts

+14
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ export function MAT_RADIO_DEFAULT_OPTIONS_FACTORY(): MatRadioDefaultOptions {
118118
host: {
119119
'role': 'radiogroup',
120120
'class': 'mat-mdc-radio-group',
121+
'[attr.aria-required]': 'required ? required : null',
121122
},
122123
})
123124
export class MatRadioGroup implements AfterContentInit, OnDestroy, ControlValueAccessor {
@@ -804,4 +805,17 @@ export class MatRadioButton implements OnInit, AfterViewInit, DoCheck, OnDestroy
804805
}
805806
}
806807
}
808+
809+
protected getInputRequiredAttribute(): boolean | null {
810+
// incase if MatRadioGroup has one of its MatRadioButton explicitly as required, then we
811+
// we would wanna set it such as in radio-harness.spec.ts inside "should get required state"
812+
// case.
813+
if (this.radioGroup && this.required) return this.required;
814+
// we never want to set required attribute on input when we have MatRadioGroup as we will set
815+
// aria-required directly on MatRadioGroup as setting on all MatRadioButton for
816+
// it's MatRadioGroup would be confusing for assistive technology.
817+
if (this.radioGroup) return null;
818+
819+
return this.required;
820+
}
807821
}

tools/public_api_guard/material/radio.md

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export class MatRadioButton implements OnInit, AfterViewInit, DoCheck, OnDestroy
5050
// (undocumented)
5151
protected _elementRef: ElementRef<any>;
5252
focus(options?: FocusOptions, origin?: FocusOrigin): void;
53+
// (undocumented)
54+
protected getInputRequiredAttribute(): boolean | null;
5355
id: string;
5456
_inputElement: ElementRef<HTMLInputElement>;
5557
get inputId(): string;

0 commit comments

Comments
 (0)