Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(material/button-toggle): toggle name falling out of sync if name changes #24713

Merged
merged 1 commit into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/material/button-toggle/button-toggle.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[attr.tabindex]="disabled ? -1 : tabIndex"
[attr.aria-pressed]="checked"
[disabled]="disabled || null"
[attr.name]="name || null"
[attr.name]="_getButtonName()"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledby"
(click)="_onButtonClick()">
Expand Down
35 changes: 24 additions & 11 deletions src/material/button-toggle/button-toggle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,15 @@ describe('MatButtonToggle with forms', () => {

it('should set individual radio names based on the group name', () => {
expect(groupInstance.name).toBeTruthy();
for (let buttonToggle of buttonToggleInstances) {
expect(buttonToggle.name).toBe(groupInstance.name);
for (let buttonToggle of innerButtons) {
expect(buttonToggle.getAttribute('name')).toBe(groupInstance.name);
}

groupInstance.name = 'new name';
for (let buttonToggle of buttonToggleInstances) {
expect(buttonToggle.name).toBe(groupInstance.name);
fixture.detectChanges();

for (let buttonToggle of innerButtons) {
expect(buttonToggle.getAttribute('name')).toBe(groupInstance.name);
}
});

Expand All @@ -137,6 +139,15 @@ describe('MatButtonToggle with forms', () => {
.toBe(true);
});

it('should set the name of the buttons to the group name if the name of the button changes after init', () => {
const firstButton = innerButtons[0];
expect(firstButton.getAttribute('name')).toBe(fixture.componentInstance.groupName);

fixture.componentInstance.options[0].name = 'changed-name';
fixture.detectChanges();
expect(firstButton.getAttribute('name')).toBe(fixture.componentInstance.groupName);
});

it('should check the corresponding button toggle on a group value change', () => {
expect(groupInstance.value).toBeFalsy();
for (let buttonToggle of buttonToggleInstances) {
Expand Down Expand Up @@ -313,8 +324,8 @@ describe('MatButtonToggle without forms', () => {

it('should set individual button toggle names based on the group name', () => {
expect(groupInstance.name).toBeTruthy();
for (let buttonToggle of buttonToggleInstances) {
expect(buttonToggle.name).toBe(groupInstance.name);
for (let buttonToggle of buttonToggleLabelElements) {
expect(buttonToggle.getAttribute('name')).toBe(groupInstance.name);
}
});

Expand Down Expand Up @@ -930,8 +941,10 @@ class ButtonTogglesInsideButtonToggleGroup {
[name]="groupName"
[(ngModel)]="modelValue"
(change)="lastEvent = $event">
<mat-button-toggle *ngFor="let option of options" [value]="option.value"
[disableRipple]="disableRipple">
<mat-button-toggle *ngFor="let option of options"
[value]="option.value"
[disableRipple]="disableRipple"
[name]="option.name">
{{option.label}}
</mat-button-toggle>
</mat-button-toggle-group>
Expand All @@ -941,9 +954,9 @@ class ButtonToggleGroupWithNgModel {
groupName = 'group-name';
modelValue: string;
options = [
{label: 'Red', value: 'red'},
{label: 'Green', value: 'green'},
{label: 'Blue', value: 'blue'},
{label: 'Red', value: 'red', name: ''},
{label: 'Green', value: 'green', name: ''},
{label: 'Blue', value: 'blue', name: ''},
];
lastEvent: MatButtonToggleChange;
disableRipple = false;
Expand Down
40 changes: 22 additions & 18 deletions src/material/button-toggle/button-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
}
set name(value: string) {
this._name = value;

if (this._buttonToggles) {
this._buttonToggles.forEach(toggle => {
toggle.name = this._name;
toggle._markForCheck();
});
}
this._markButtonsForCheck();
}
private _name = `mat-button-toggle-group-${uniqueIdCounter++}`;

Expand Down Expand Up @@ -210,6 +204,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
}
set multiple(value: BooleanInput) {
this._multiple = coerceBooleanProperty(value);
this._markButtonsForCheck();
}

/** Whether multiple button toggle group is disabled. */
Expand All @@ -219,10 +214,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
}
set disabled(value: BooleanInput) {
this._disabled = coerceBooleanProperty(value);

if (this._buttonToggles) {
this._buttonToggles.forEach(toggle => toggle._markForCheck());
}
this._markButtonsForCheck();
}

/** Event emitted when the group's value changes. */
Expand Down Expand Up @@ -387,6 +379,11 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
// it is used by Angular to sync up the two-way data binding.
this.valueChange.emit(this.value);
}

/** Marks all of the child button toggles to be checked. */
private _markButtonsForCheck() {
this._buttonToggles?.forEach(toggle => toggle._markForCheck());
}
}

// Boilerplate for applying mixins to the MatButtonToggle class.
Expand Down Expand Up @@ -420,7 +417,6 @@ export class MatButtonToggle
extends _MatButtonToggleBase
implements OnInit, AfterViewInit, CanDisableRipple, OnDestroy
{
private _isSingleSelector = false;
private _checked = false;

/**
Expand Down Expand Up @@ -521,13 +517,8 @@ export class MatButtonToggle

ngOnInit() {
const group = this.buttonToggleGroup;
this._isSingleSelector = group && !group.multiple;
this.id = this.id || `mat-button-toggle-${uniqueIdCounter++}`;

if (this._isSingleSelector) {
this.name = group.name;
}

if (group) {
if (group._isPrechecked(this)) {
this.checked = true;
Expand Down Expand Up @@ -564,7 +555,7 @@ export class MatButtonToggle

/** Checks the button toggle due to an interaction with the underlying native button. */
_onButtonClick() {
const newChecked = this._isSingleSelector ? true : !this._checked;
const newChecked = this._isSingleSelector() ? true : !this._checked;

if (newChecked !== this._checked) {
this._checked = newChecked;
Expand All @@ -587,4 +578,17 @@ export class MatButtonToggle
// Use `markForCheck` to explicit update button toggle's status.
this._changeDetectorRef.markForCheck();
}

/** Gets the name that should be assigned to the inner DOM node. */
_getButtonName(): string | null {
if (this._isSingleSelector()) {
return this.buttonToggleGroup.name;
}
return this.name || null;
}

/** Whether the toggle is in single selection mode. */
private _isSingleSelector(): boolean {
return this.buttonToggleGroup && !this.buttonToggleGroup.multiple;
}
}
1 change: 1 addition & 0 deletions tools/public_api_guard/material/button-toggle.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class MatButtonToggle extends _MatButtonToggleBase implements OnInit, Aft
get disabled(): boolean;
set disabled(value: BooleanInput);
focus(options?: FocusOptions): void;
_getButtonName(): string | null;
id: string;
_markForCheck(): void;
name: string;
Expand Down