Skip to content

Commit

Permalink
fix(list): allow for list and list items to be disabled
Browse files Browse the repository at this point in the history
Adds the ability to disable all variants of the list. Currently it's only the selection list that can be disabled.

Fixes angular#17574.
  • Loading branch information
crisbeto committed Nov 2, 2019
1 parent 0f6cd05 commit 5008f3a
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
6 changes: 5 additions & 1 deletion src/material/list/list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ mat-action-list {
font: inherit;
outline: inherit;
-webkit-tap-highlight-color: transparent;

text-align: left;

[dir='rtl'] & {
text-align: right;
}
Expand All @@ -300,6 +300,10 @@ mat-action-list {
outline: none;
}

.mat-list-item-disabled {
pointer-events: none;
}

@include cdk-high-contrast {
.mat-selection-list:focus {
outline-style: dotted;
Expand Down
60 changes: 59 additions & 1 deletion src/material/list/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('MatList', () => {
ListWithOneAnchorItem, ListWithOneItem, ListWithTwoLineItem, ListWithThreeLineItem,
ListWithAvatar, ListWithItemWithCssClass, ListWithDynamicNumberOfLines,
ListWithMultipleItems, ListWithManyLines, NavListWithOneAnchorItem, ActionListWithoutType,
ActionListWithType, ListWithIndirectDescendantLines
ActionListWithType, ListWithIndirectDescendantLines, ListWithDisabledItems,
],
});

Expand Down Expand Up @@ -275,6 +275,52 @@ describe('MatList', () => {
expect(listItems[0].nativeElement.className).toContain('mat-2-line');
expect(listItems[1].nativeElement.className).toContain('mat-2-line');
});

it('should be able to disable a single list item', () => {
const fixture = TestBed.createComponent(ListWithDisabledItems);
const listItems: HTMLElement[] =
Array.from(fixture.nativeElement.querySelectorAll('mat-list-item'));
fixture.detectChanges();

expect(listItems.map(item => {
return item.classList.contains('mat-list-item-disabled');
})).toEqual([false, false, false]);

fixture.componentInstance.firstItemDisabled = true;
fixture.detectChanges();

expect(listItems.map(item => {
return item.classList.contains('mat-list-item-disabled');
})).toEqual([true, false, false]);
});

it('should be able to disable the entire list', () => {
const fixture = TestBed.createComponent(ListWithDisabledItems);
const listItems: HTMLElement[] =
Array.from(fixture.nativeElement.querySelectorAll('mat-list-item'));
fixture.detectChanges();

expect(listItems.every(item => item.classList.contains('mat-list-item-disabled'))).toBe(false);

fixture.componentInstance.listDisabled = true;
fixture.detectChanges();

expect(listItems.every(item => item.classList.contains('mat-list-item-disabled'))).toBe(true);
});

it('should set aria-disabled on a disabled item', () => {
const fixture = TestBed.createComponent(ListWithDisabledItems);
const item: HTMLElement = fixture.nativeElement.querySelector('mat-list-item');
fixture.detectChanges();

expect(item.getAttribute('aria-disabled')).toBe('false');

fixture.componentInstance.firstItemDisabled = true;
fixture.detectChanges();

expect(item.getAttribute('aria-disabled')).toBe('true');
});

});


Expand Down Expand Up @@ -425,3 +471,15 @@ class ListWithMultipleItems extends BaseTestList { }
})
class ListWithIndirectDescendantLines extends BaseTestList {
}


@Component({template: `
<mat-list [disabled]="listDisabled">
<mat-list-item [disabled]="firstItemDisabled">One</mat-list-item>
<mat-list-item>Two</mat-list-item>
<mat-list-item>Three</mat-list-item>
</mat-list>`})
class ListWithDisabledItems {
firstItemDisabled = false;
listDisabled = false;
}
32 changes: 25 additions & 7 deletions src/material/list/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,27 @@ import {
OnChanges,
OnDestroy,
ChangeDetectorRef,
Input,
} from '@angular/core';
import {
CanDisable,
CanDisableCtor,
CanDisableRipple,
CanDisableRippleCtor,
MatLine,
setLines,
mixinDisableRipple,
mixinDisabled,
} from '@angular/material/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

// Boilerplate for applying mixins to MatList.
/** @docs-private */
class MatListBase {}
const _MatListMixinBase: CanDisableRippleCtor & typeof MatListBase =
mixinDisableRipple(MatListBase);
const _MatListMixinBase: CanDisableRippleCtor & CanDisableCtor & typeof MatListBase =
mixinDisabled(mixinDisableRipple(MatListBase));

// Boilerplate for applying mixins to MatListItem.
/** @docs-private */
Expand All @@ -53,12 +58,12 @@ const _MatListItemMixinBase: CanDisableRippleCtor & typeof MatListItemBase =
},
templateUrl: 'list.html',
styleUrls: ['list.css'],
inputs: ['disableRipple'],
inputs: ['disableRipple', 'disabled'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges,
OnDestroy {
export class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple,
OnChanges, OnDestroy {
/** Emits when the state of the list changes. */
_stateChanges = new Subject<void>();

Expand All @@ -82,11 +87,12 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple, O
'class': 'mat-list mat-list-base'
},
styleUrls: ['list.css'],
inputs: ['disableRipple'],
inputs: ['disableRipple', 'disabled'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
export class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges,
OnDestroy {
/** Emits when the state of the list changes. */
_stateChanges = new Subject<void>();

Expand Down Expand Up @@ -121,6 +127,7 @@ export class MatList extends _MatListMixinBase implements CanDisableRipple, OnCh
}

static ngAcceptInputType_disableRipple: boolean | string;
static ngAcceptInputType_disabled: boolean | string;
}

/**
Expand Down Expand Up @@ -160,6 +167,8 @@ export class MatListSubheaderCssMatStyler {}
exportAs: 'matListItem',
host: {
'class': 'mat-list-item',
'[class.mat-list-item-disabled]': 'disabled',
'[attr.aria-disabled]': 'disabled',
// @breaking-change 8.0.0 Remove `mat-list-item-avatar` in favor of `mat-list-item-with-avatar`.
'[class.mat-list-item-avatar]': '_avatar || _icon',
'[class.mat-list-item-with-avatar]': '_avatar || _icon',
Expand Down Expand Up @@ -204,6 +213,14 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
}
}

/** Whether the option is disabled. */
@Input()
get disabled() { return this._disabled || !!(this._list && this._list.disabled); }
set disabled(value: boolean) {
this._disabled = coerceBooleanProperty(value);
}
private _disabled = false;

ngAfterContentInit() {
setLines(this._lines, this._element);
}
Expand All @@ -225,4 +242,5 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
}

static ngAcceptInputType_disableRipple: boolean | string;
static ngAcceptInputType_disabled: boolean | string;
}
7 changes: 5 additions & 2 deletions tools/public_api_guard/material/list.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export declare const MAT_SELECTION_LIST_VALUE_ACCESSOR: any;

export declare class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
export declare class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy {
_stateChanges: Subject<void>;
constructor(_elementRef: ElementRef<HTMLElement>);
_getListType(): 'list' | 'action-list' | null;
ngOnChanges(): void;
ngOnDestroy(): void;
static ngAcceptInputType_disableRipple: boolean | string;
static ngAcceptInputType_disabled: boolean | string;
}

export declare class MatListAvatarCssMatStyler {
Expand All @@ -19,12 +20,14 @@ export declare class MatListItem extends _MatListItemMixinBase implements AfterC
_avatar: MatListAvatarCssMatStyler;
_icon: MatListIconCssMatStyler;
_lines: QueryList<MatLine>;
disabled: boolean;
constructor(_element: ElementRef<HTMLElement>, _changeDetectorRef: ChangeDetectorRef, navList?: MatNavList, list?: MatList);
_getHostElement(): HTMLElement;
_isRippleDisabled(): boolean;
ngAfterContentInit(): void;
ngOnDestroy(): void;
static ngAcceptInputType_disableRipple: boolean | string;
static ngAcceptInputType_disabled: boolean | string;
}

export declare class MatListModule {
Expand Down Expand Up @@ -64,7 +67,7 @@ export declare class MatListOption extends _MatListOptionMixinBase implements Af
export declare class MatListSubheaderCssMatStyler {
}

export declare class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
export declare class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy {
_stateChanges: Subject<void>;
ngOnChanges(): void;
ngOnDestroy(): void;
Expand Down

0 comments on commit 5008f3a

Please sign in to comment.