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..301b3826dbc1 100644
--- a/src/material/button-toggle/button-toggle.html
+++ b/src/material/button-toggle/button-toggle.html
@@ -1,20 +1,20 @@
-
+
+
diff --git a/src/material/button-toggle/button-toggle.scss b/src/material/button-toggle/button-toggle.scss
index e12094d2c5d5..ea5137c804ec 100644
--- a/src/material/button-toggle/button-toggle.scss
+++ b/src/material/button-toggle/button-toggle.scss
@@ -272,4 +272,12 @@ $_standard-tokens: (
&::-moz-focus-inner {
border: 0;
}
+
+ opacity: 0.01;
+ z-index: 100;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
}
diff --git a/src/material/button-toggle/button-toggle.spec.ts b/src/material/button-toggle/button-toggle.spec.ts
index 7f33cc6668f9..b0ff25da91c6 100644
--- a/src/material/button-toggle/button-toggle.spec.ts
+++ b/src/material/button-toggle/button-toggle.spec.ts
@@ -97,7 +97,7 @@ describe('MatButtonToggle with forms', () => {
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..5861c7fdb36b 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,8 +417,13 @@ 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;
+ @ViewChild('input') _inputElement: ElementRef;
/** The parent button toggle group (exclusive selection). Optional. */
buttonToggleGroup: MatButtonToggleGroup;
@@ -536,7 +541,7 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
/** Focuses the button. */
focus(options?: FocusOptions): void {
- this._buttonElement.nativeElement.focus(options);
+ this._inputElement.nativeElement.focus(options);
}
/** Checks the button toggle due to an interaction with the underlying native button. */
@@ -554,6 +559,15 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
this.change.emit(new MatButtonToggleChange(this, this.value));
}
+ /**
+ * Stop propagation on the change event.
+ * Otherwise the change event, from the input element, will bubble up and
+ * emit its event object to the `change` output.
+ */
+ _onInteractionEvent(event: Event) {
+ event.stopPropagation();
+ }
+
/**
* Marks the button toggle as needing checking for change detection.
* This method is exposed because the parent button toggle group will directly
@@ -573,6 +587,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;
diff --git a/tools/public_api_guard/material/button-toggle.md b/tools/public_api_guard/material/button-toggle.md
index 2c3fa8aaa840..9cb64bd630c3 100644
--- a/tools/public_api_guard/material/button-toggle.md
+++ b/tools/public_api_guard/material/button-toggle.md
@@ -34,7 +34,6 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
set appearance(value: MatButtonToggleAppearance);
ariaLabel: string;
ariaLabelledby: string | null;
- _buttonElement: ElementRef;
get buttonId(): string;
buttonToggleGroup: MatButtonToggleGroup;
readonly change: EventEmitter;
@@ -44,8 +43,10 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
set disabled(value: boolean);
disableRipple: boolean;
focus(options?: FocusOptions): void;
+ _getAriaPressed(): boolean | null;
_getButtonName(): string | null;
id: string;
+ _inputElement: ElementRef;
_markForCheck(): void;
name: string;
// (undocumented)
@@ -61,7 +62,9 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
// (undocumented)
ngOnInit(): void;
_onButtonClick(): void;
+ _onInteractionEvent(event: Event): void;
tabIndex: number | null;
+ get type(): string;
value: any;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration;