Skip to content

Commit b3977d0

Browse files
committed
fix(selection-list): disabling list doesn't disable ripples of options
* Fixes that dynamically changing the `[disabled]` binding does not update the state of the list options (e.g. ripple disabled state; checkbox disabled styling) Fixes #9952
1 parent d96adbe commit b3977d0

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

src/lib/list/selection-list.spec.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ViewChildren,
1414
} from '@angular/core';
1515
import {async, ComponentFixture, fakeAsync, TestBed, tick, flush} from '@angular/core/testing';
16+
import {MatRipple} from '@angular/material/core';
1617
import {By} from '@angular/platform-browser';
1718
import {
1819
MatListModule,
@@ -626,6 +627,23 @@ describe('MatSelectionList without forms', () => {
626627

627628
expect(selectList.selected.length).toBe(0);
628629
});
630+
631+
it('should update state of options if list state has changed', () => {
632+
// To verify that the template of the list options has been re-rendered after the disabled
633+
// property of the selection list has been updated, the ripple directive can be used.
634+
// Inspecting the host classes of the options doesn't work because those update as part
635+
// of the parent template (of the selection-list).
636+
const listOptionRipple = listOption[2].query(By.directive(MatRipple)).injector.get(MatRipple);
637+
638+
expect(listOptionRipple.disabled)
639+
.toBe(true, 'Expected ripples of list option to be disabled');
640+
641+
fixture.componentInstance.disabled = false;
642+
fixture.detectChanges();
643+
644+
expect(listOptionRipple.disabled)
645+
.toBe(false, 'Expected ripples of list option to be enabled');
646+
});
629647
});
630648

631649
describe('with checkbox position after', () => {
@@ -977,7 +995,7 @@ class SelectionListWithCheckboxPositionAfter {
977995
}
978996

979997
@Component({template: `
980-
<mat-selection-list id="selection-list-3" [disabled]=true>
998+
<mat-selection-list id="selection-list-3" [disabled]="disabled">
981999
<mat-list-option checkboxPosition="after">
9821000
Inbox (disabled selection-option)
9831001
</mat-list-option>
@@ -992,6 +1010,7 @@ class SelectionListWithCheckboxPositionAfter {
9921010
</mat-list-option>
9931011
</mat-selection-list>`})
9941012
class SelectionListWithListDisabled {
1013+
disabled: boolean = true;
9951014
}
9961015

9971016
@Component({template: `

src/lib/list/selection-list.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
forwardRef,
2323
Inject,
2424
Input,
25+
OnChanges,
2526
OnDestroy,
2627
OnInit,
2728
Output,
@@ -96,6 +97,7 @@ export class MatListOption extends _MatListOptionMixinBase
9697

9798
private _selected = false;
9899
private _disabled = false;
100+
private _parentStateSubscription: Subscription;
99101

100102
/** Whether the option has focus. */
101103
_hasFocus: boolean = false;
@@ -140,6 +142,9 @@ export class MatListOption extends _MatListOptionMixinBase
140142
/** @docs-private */
141143
@Inject(forwardRef(() => MatSelectionList)) public selectionList: MatSelectionList) {
142144
super();
145+
146+
this._parentStateSubscription = this.selectionList._stateChanges
147+
.subscribe(() => this._changeDetector.markForCheck());
143148
}
144149

145150
ngOnInit() {
@@ -172,6 +177,7 @@ export class MatListOption extends _MatListOptionMixinBase
172177
}
173178

174179
this.selectionList._removeOptionFromList(this);
180+
this._parentStateSubscription.unsubscribe();
175181
}
176182

177183
/** Toggles the selection state of the option. */
@@ -265,11 +271,14 @@ export class MatListOption extends _MatListOptionMixinBase
265271
changeDetection: ChangeDetectionStrategy.OnPush
266272
})
267273
export class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption,
268-
CanDisable, CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy {
274+
CanDisable, CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy, OnChanges {
269275

270276
/** The FocusKeyManager which handles focus. */
271277
_keyManager: FocusKeyManager<MatListOption>;
272278

279+
/** Used to notify any child components listening to state changes. */
280+
readonly _stateChanges = new EventEmitter<void>();
281+
273282
/** The option components contained within this selection-list. */
274283
@ContentChildren(MatListOption) options: QueryList<MatListOption>;
275284

@@ -296,6 +305,7 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
296305
/** Used for storing the values that were assigned before the options were initialized. */
297306
private _tempValues: string[]|null;
298307

308+
/** Subscription to sync value changes in the SelectionModel back to the SelectionList. */
299309
private _modelChanges = Subscription.EMPTY;
300310

301311
/** View to model callback that should be called if the list or its options lost focus. */
@@ -335,6 +345,10 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
335345
});
336346
}
337347

348+
ngOnChanges() {
349+
this._stateChanges.next();
350+
}
351+
338352
ngOnDestroy() {
339353
this._modelChanges.unsubscribe();
340354
}

0 commit comments

Comments
 (0)