diff --git a/src/dev-app/button-toggle/button-toggle-demo.html b/src/dev-app/button-toggle/button-toggle-demo.html index b2cfc8186f3e..474b5383790b 100644 --- a/src/dev-app/button-toggle/button-toggle-demo.html +++ b/src/dev-app/button-toggle/button-toggle-demo.html @@ -9,7 +9,7 @@

Exclusive Selection

- + format_align_left @@ -26,7 +26,7 @@

Exclusive Selection

- + format_align_left diff --git a/src/material/button-toggle/button-toggle.html b/src/material/button-toggle/button-toggle.html index 9a2113709715..81a8b18b23c5 100644 --- a/src/material/button-toggle/button-toggle.html +++ b/src/material/button-toggle/button-toggle.html @@ -1,17 +1,16 @@ - + + { buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle)); buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance); innerButtons = buttonToggleDebugElements.map( - debugEl => debugEl.query(By.css('button'))!.nativeElement, + debugEl => debugEl.query(By.css('input'))!.nativeElement, ); fixture.detectChanges(); @@ -256,7 +256,7 @@ describe('MatButtonToggle with forms', () => { const fixture = TestBed.createComponent(ButtonToggleGroupWithIndirectDescendantToggles); fixture.detectChanges(); - const button = fixture.nativeElement.querySelector('.mat-button-toggle button'); + const button = fixture.nativeElement.querySelector('.mat-button-toggle input'); const groupDebugElement = fixture.debugElement.query(By.directive(MatButtonToggleGroup))!; const groupInstance = groupDebugElement.injector.get(MatButtonToggleGroup); @@ -359,7 +359,7 @@ describe('MatButtonToggle without forms', () => { buttonToggleNativeElements = buttonToggleDebugElements.map(debugEl => debugEl.nativeElement); buttonToggleLabelElements = fixture.debugElement - .queryAll(By.css('button')) + .queryAll(By.css('input')) .map(debugEl => debugEl.nativeElement); buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance); @@ -401,7 +401,7 @@ describe('MatButtonToggle without forms', () => { }); it('should disable the underlying button when the group is disabled', () => { - const buttons = buttonToggleNativeElements.map(toggle => toggle.querySelector('button')!); + const buttons = buttonToggleNativeElements.map(toggle => toggle.querySelector('input')!); expect(buttons.every(input => input.disabled)).toBe(false); @@ -595,7 +595,7 @@ describe('MatButtonToggle without forms', () => { buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MatButtonToggle)); buttonToggleNativeElements = buttonToggleDebugElements.map(debugEl => debugEl.nativeElement); buttonToggleLabelElements = fixture.debugElement - .queryAll(By.css('button')) + .queryAll(By.css('input')) .map(debugEl => debugEl.nativeElement); buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance); })); @@ -612,7 +612,7 @@ describe('MatButtonToggle without forms', () => { expect(buttonToggleInstances.every(buttonToggle => !buttonToggle.checked)).toBe(true); const nativeCheckboxLabel = buttonToggleDebugElements[0].query( - By.css('button'), + By.css('input'), )!.nativeElement; nativeCheckboxLabel.click(); @@ -638,7 +638,7 @@ describe('MatButtonToggle without forms', () => { it('should check a button toggle upon interaction with underlying native checkbox', () => { const nativeCheckboxButton = buttonToggleDebugElements[0].query( - By.css('button'), + By.css('input'), )!.nativeElement; nativeCheckboxButton.click(); @@ -722,7 +722,7 @@ describe('MatButtonToggle without forms', () => { )!.nativeElement; buttonToggleInstance = buttonToggleDebugElement.componentInstance; buttonToggleButtonElement = buttonToggleNativeElement.querySelector( - 'button', + 'input', )! as HTMLButtonElement; })); @@ -761,7 +761,7 @@ describe('MatButtonToggle without forms', () => { })); it('should focus on underlying input element when focus() is called', () => { - const nativeButton = buttonToggleDebugElement.query(By.css('button'))!.nativeElement; + const nativeButton = buttonToggleDebugElement.query(By.css('input'))!.nativeElement; expect(document.activeElement).not.toBe(nativeButton); buttonToggleInstance.focus(); @@ -790,7 +790,7 @@ describe('MatButtonToggle without forms', () => { const fixture = TestBed.createComponent(StandaloneButtonToggle); const checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!; const checkboxNativeElement = checkboxDebugElement.nativeElement; - const buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement; + const buttonElement = checkboxNativeElement.querySelector('input') as HTMLButtonElement; fixture.detectChanges(); expect(buttonElement.hasAttribute('aria-label')).toBe(false); @@ -800,7 +800,7 @@ describe('MatButtonToggle without forms', () => { const fixture = TestBed.createComponent(ButtonToggleWithAriaLabel); const checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!; const checkboxNativeElement = checkboxDebugElement.nativeElement; - const buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement; + const buttonElement = checkboxNativeElement.querySelector('input') as HTMLButtonElement; fixture.detectChanges(); expect(buttonElement.getAttribute('aria-label')).toBe('Super effective'); @@ -825,7 +825,7 @@ describe('MatButtonToggle without forms', () => { const fixture = TestBed.createComponent(ButtonToggleWithAriaLabelledby); checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!; checkboxNativeElement = checkboxDebugElement.nativeElement; - buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement; + buttonElement = checkboxNativeElement.querySelector('input') as HTMLButtonElement; fixture.detectChanges(); expect(buttonElement.getAttribute('aria-labelledby')).toBe('some-id'); @@ -835,7 +835,7 @@ describe('MatButtonToggle without forms', () => { const fixture = TestBed.createComponent(StandaloneButtonToggle); checkboxDebugElement = fixture.debugElement.query(By.directive(MatButtonToggle))!; checkboxNativeElement = checkboxDebugElement.nativeElement; - buttonElement = checkboxNativeElement.querySelector('button') as HTMLButtonElement; + buttonElement = checkboxNativeElement.querySelector('input') as HTMLButtonElement; fixture.detectChanges(); expect(buttonElement.getAttribute('aria-labelledby')).toBe(null); @@ -847,7 +847,7 @@ describe('MatButtonToggle without forms', () => { const fixture = TestBed.createComponent(ButtonToggleWithTabindex); fixture.detectChanges(); - const button = fixture.nativeElement.querySelector('.mat-button-toggle button'); + const button = fixture.nativeElement.querySelector('.mat-button-toggle input'); expect(button.getAttribute('tabindex')).toBe('3'); }); @@ -866,7 +866,7 @@ describe('MatButtonToggle without forms', () => { fixture.detectChanges(); const host = fixture.nativeElement.querySelector('.mat-button-toggle'); - const button = host.querySelector('button'); + const button = host.querySelector('input'); expect(document.activeElement).not.toBe(button); @@ -891,7 +891,7 @@ describe('MatButtonToggle without forms', () => { const hostNode: HTMLElement = fixture.nativeElement.querySelector('.mat-button-toggle'); expect(hostNode.hasAttribute('name')).toBe(false); - expect(hostNode.querySelector('button')!.getAttribute('name')).toBe('custom-name'); + expect(hostNode.querySelector('input')!.getAttribute('name')).toBe('custom-name'); }); it( diff --git a/src/material/button-toggle/button-toggle.ts b/src/material/button-toggle/button-toggle.ts index b5ea5c549226..9baa8919b77d 100644 --- a/src/material/button-toggle/button-toggle.ts +++ b/src/material/button-toggle/button-toggle.ts @@ -106,8 +106,8 @@ export class MatButtonToggleChange { {provide: MAT_BUTTON_TOGGLE_GROUP, useExisting: MatButtonToggleGroup}, ], host: { - 'role': 'group', 'class': 'mat-button-toggle-group', + '[role]': "multiple ? 'group' : 'radiogroup'", '[attr.aria-disabled]': 'disabled', '[class.mat-button-toggle-vertical]': 'vertical', '[class.mat-button-toggle-group-appearance-standard]': 'appearance === "standard"', @@ -417,6 +417,11 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { */ @Input('aria-labelledby') ariaLabelledby: string | null = null; + /** Type of the button toggle. Either 'radio' or 'button'. */ + get type(): string { + return this._isSingleSelector() ? 'radio' : 'button'; + } + /** Underlying native `button` element. */ @ViewChild('button') _buttonElement: ElementRef; @@ -573,6 +578,15 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { return this.name || null; } + /** Get the aria-pressed attribute value. */ + _getAriaPressed(): boolean | null { + // When the toggle stands alone, or in multiple selection mode, use aria-pressed attribute. + if (!this._isSingleSelector()) { + return this.checked; + } + return null; + } + /** Whether the toggle is in single selection mode. */ private _isSingleSelector(): boolean { return this.buttonToggleGroup && !this.buttonToggleGroup.multiple;