diff --git a/src/lib/radio/radio.html b/src/lib/radio/radio.html index 4c77e164b2a2..4a799f777fe3 100644 --- a/src/lib/radio/radio.html +++ b/src/lib/radio/radio.html @@ -18,6 +18,7 @@ [id]="inputId" [checked]="checked" [disabled]="disabled" + [tabIndex]="tabIndex" [attr.name]="name" [required]="required" [attr.aria-label]="ariaLabel" diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index aa275181a417..919d6d6ddf9a 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -661,12 +661,26 @@ describe('MatRadio', () => { let inputEl = fixture.debugElement.query(By.css('.mat-radio-input')).nativeElement; radioButtonEl.focus(); - // Focus events don't always fire in tests, so we needc to fake it. + // Focus events don't always fire in tests, so we need to fake it. dispatchFakeEvent(radioButtonEl, 'focus'); fixture.detectChanges(); expect(document.activeElement).toBe(inputEl); }); + + it('should allow specifying an explicit tabindex for a single radio-button', () => { + const radioButtonInput = fixture.debugElement + .query(By.css('.mat-radio-button input')).nativeElement as HTMLInputElement; + + expect(radioButtonInput.tabIndex) + .toBe(0, 'Expected the tabindex to be set to "0" by default.'); + + fixture.componentInstance.tabIndex = 4; + fixture.detectChanges(); + + expect(radioButtonInput.tabIndex) + .toBe(4, 'Expected the tabindex to be set to "4".'); + }); }); describe('group interspersed with other tags', () => { @@ -781,9 +795,11 @@ class RadioGroupWithFormControl { } @Component({ - template: `` + template: `` }) -class FocusableRadioButton {} +class FocusableRadioButton { + tabIndex: number; +} @Component({ template: ` diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index f73d5b164fa0..7ed460607d26 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -34,10 +34,12 @@ import { CanColor, CanDisable, CanDisableRipple, + HasTabIndex, MatRipple, mixinColor, mixinDisabled, mixinDisableRipple, + mixinTabIndex, RippleConfig, RippleRef, } from '@angular/material/core'; @@ -315,12 +317,17 @@ export class MatRadioGroup extends _MatRadioGroupMixinBase // Boilerplate for applying mixins to MatRadioButton. /** @docs-private */ export class MatRadioButtonBase { + // Since the disabled property is manually defined for the MatRadioButton and isn't set up in + // the mixin base class. To be able to use the tabindex mixin, a disabled property must be + // defined to properly work. + disabled: boolean; + constructor(public _elementRef: ElementRef) {} } // As per Material design specifications the selection control radio should use the accent color // palette by default. https://material.io/guidelines/components/selection-controls.html export const _MatRadioButtonMixinBase = - mixinColor(mixinDisableRipple(MatRadioButtonBase), 'accent'); + mixinColor(mixinDisableRipple(mixinTabIndex(MatRadioButtonBase)), 'accent'); /** * A Material design radio-button. Typically placed inside of `` elements. @@ -330,7 +337,7 @@ export const _MatRadioButtonMixinBase = selector: 'mat-radio-button', templateUrl: 'radio.html', styleUrls: ['radio.css'], - inputs: ['color', 'disableRipple'], + inputs: ['color', 'disableRipple', 'tabIndex'], encapsulation: ViewEncapsulation.None, preserveWhitespaces: false, exportAs: 'matRadioButton', @@ -347,7 +354,7 @@ export const _MatRadioButtonMixinBase = changeDetection: ChangeDetectionStrategy.OnPush, }) export class MatRadioButton extends _MatRadioButtonMixinBase - implements OnInit, AfterViewInit, OnDestroy, CanColor, CanDisableRipple { + implements OnInit, AfterViewInit, OnDestroy, CanColor, CanDisableRipple, HasTabIndex { private _uniqueId: string = `mat-radio-${++nextUniqueId}`;