feat(expansion-panel): add the ability to disable an expansion panel (#6529)

Fixes #6521.
This commit is contained in:
Kristiyan Kostadinov
2017-08-22 23:02:42 +02:00
committed by Kara
parent 449ed194d2
commit 921432acf7
10 changed files with 135 additions and 28 deletions
+3 -2
View File
@@ -17,6 +17,7 @@
<div>
<md-slide-toggle [(ngModel)]="multi">Allow Multi Expansion</md-slide-toggle>
<md-slide-toggle [(ngModel)]="hideToggle">Hide Indicators</md-slide-toggle>
<md-slide-toggle [(ngModel)]="disabled">Disable Panel 2</md-slide-toggle>
<md-slide-toggle [(ngModel)]="showPanel3">Show Panel 3</md-slide-toggle>
</div>
<p>Accordion Style</p>
@@ -37,7 +38,7 @@
<md-expansion-panel-header>Section 1</md-expansion-panel-header>
<p>This is the content text that makes sense here.</p>
</md-expansion-panel>
<md-expansion-panel #panel2 [hideToggle]="hideToggle">
<md-expansion-panel #panel2 [hideToggle]="hideToggle" [disabled]="disabled">
<md-expansion-panel-header>Section 2</md-expansion-panel-header>
<p>This is the content text that makes sense here.</p>
</md-expansion-panel>
@@ -49,4 +50,4 @@
<button md-button (click)="panel3.expanded = false">CLOSE</button>
</md-action-row>
</md-expansion-panel>
</md-accordion>
</md-accordion>
+3 -2
View File
@@ -9,7 +9,8 @@ import {Component, ViewEncapsulation} from '@angular/core';
})
export class ExpansionDemo {
displayMode: string = 'default';
multi: boolean = false;
hideToggle: boolean = false;
multi = false;
hideToggle = false;
disabled = false;
showPanel3 = true;
}
+10 -1
View File
@@ -15,7 +15,7 @@
border-top-color: mat-color($foreground, divider);
}
.mat-expansion-panel-header {
.mat-expansion-panel-header:not([aria-disabled='true']) {
&.cdk-keyboard-focused,
&.cdk-program-focused,
&:hover {
@@ -31,6 +31,15 @@
.mat-expansion-indicator::after {
color: mat-color($foreground, secondary-text);
}
.mat-expansion-panel-header[aria-disabled='true'] {
color: mat-color($foreground, disabled-button);
.mat-expansion-panel-header-title,
.mat-expansion-panel-header-description {
color: inherit;
}
}
}
@mixin mat-expansion-panel-typography($config) {
+10 -1
View File
@@ -17,16 +17,22 @@ import {
} from '@angular/core';
import {UniqueSelectionDispatcher} from '../core';
import {CdkAccordion} from './accordion';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
/** Used to generate unique ID for each expansion panel. */
let nextId = 0;
// Boilerplate for applying mixins to MdSlider.
/** @docs-private */
export class AccordionItemBase { }
export const _AccordionItemMixinBase = mixinDisabled(AccordionItemBase);
/**
* An abstract class to be extended and decorated as a component. Sets up all
* events and attributes needed to be managed by a CdkAccordion parent.
*/
@Injectable()
export class AccordionItem implements OnDestroy {
export class AccordionItem extends _AccordionItemMixinBase implements OnDestroy, CanDisable {
/** Event emitted every time the MdAccordionChild is closed. */
@Output() closed = new EventEmitter<void>();
/** Event emitted every time the MdAccordionChild is opened. */
@@ -68,6 +74,9 @@ export class AccordionItem implements OnDestroy {
constructor(@Optional() public accordion: CdkAccordion,
private _changeDetectorRef: ChangeDetectorRef,
protected _expansionDispatcher: UniqueSelectionDispatcher) {
super();
this._removeUniqueSelectionListener =
_expansionDispatcher.listen((id: string, accordionId: string) => {
if (this.accordion && !this.accordion.multi &&
@@ -3,5 +3,5 @@
<ng-content select="md-panel-description, mat-panel-description"></ng-content>
<ng-content></ng-content>
</span>
<span [@indicatorRotate]="_getExpandedState()" *ngIf="!_getHideToggle()"
<span [@indicatorRotate]="_getExpandedState()" *ngIf="_showToggle()"
class="mat-expansion-indicator"></span>
@@ -1,6 +1,5 @@
.mat-expansion-panel-header {
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
@@ -15,6 +14,10 @@
&.mat-expanded:hover, {
background: inherit;
}
&:not([aria-disabled='true']) {
cursor: pointer;
}
}
.mat-content {
+9 -6
View File
@@ -49,9 +49,10 @@ import {Subscription} from 'rxjs/Subscription';
host: {
'class': 'mat-expansion-panel-header',
'role': 'button',
'tabindex': '0',
'[attr.tabindex]': 'panel.disabled ? -1 : 0',
'[attr.aria-controls]': '_getPanelId()',
'[attr.aria-expanded]': '_isExpanded()',
'[attr.aria-disabled]': 'panel.disabled',
'[class.mat-expanded]': '_isExpanded()',
'(click)': '_toggle()',
'(keyup)': '_keyup($event)',
@@ -85,7 +86,7 @@ export class MdExpansionPanelHeader implements OnDestroy {
this._parentChangeSubscription = merge(
panel.opened,
panel.closed,
filter.call(panel._inputChanges, changes => !!changes.hideToggle)
filter.call(panel._inputChanges, changes => !!(changes.hideToggle || changes.disabled))
)
.subscribe(() => this._changeDetectorRef.markForCheck());
@@ -94,7 +95,9 @@ export class MdExpansionPanelHeader implements OnDestroy {
/** Toggles the expanded state of the panel. */
_toggle(): void {
this.panel.toggle();
if (!this.panel.disabled) {
this.panel.toggle();
}
}
/** Gets whether the panel is expanded. */
@@ -112,9 +115,9 @@ export class MdExpansionPanelHeader implements OnDestroy {
return this.panel.id;
}
/** Gets whether the expand indicator is hidden. */
_getHideToggle(): boolean {
return this.panel.hideToggle;
/** Gets whether the expand indicator should be shown. */
_showToggle(): boolean {
return !this.panel.hideToggle && !this.panel.disabled;
}
/** Handle keyup event calling to toggle() if appropriate. */
+1
View File
@@ -54,6 +54,7 @@ export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,
templateUrl: './expansion-panel.html',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
inputs: ['disabled'],
host: {
'class': 'mat-expansion-panel',
'[class.mat-expanded]': 'expanded',
+26 -9
View File
@@ -14,7 +14,7 @@ as the control for expanding and collapsing. This header may optionally contain
header to align with Material Design specifications.
By default, the expansion-panel header includes a toggle icon at the end of the
header to indicate the expansion state. This icon can be hidden via the
header to indicate the expansion state. This icon can be hidden via the
`hideToggle` property.
```html
@@ -35,8 +35,8 @@ header to indicate the expansion state. This icon can be hidden via the
#### Action bar
Actions may optionally be included at the bottom of the panel, visible only when the expansion is in its
expanded state.
Actions may optionally be included at the bottom of the panel, visible only when the expansion
is in its expanded state.
```html
<md-expansion-panel>
@@ -52,6 +52,23 @@ expanded state.
</md-expansion-panel>
```
#### Disabling a panel
Expansion panels can be disabled using the `disabled` attribute. A disabled expansion panel can't
be toggled by the user, but can still be manipulated using programmatically.
```html
<md-expansion-panel [disabled]="isDisabled">
<md-expansion-panel-header>
This is the expansion title
</md-expansion-panel-header>
<md-panel-description>
This is a summary of the content
</md-panel-description>
</md-expansion-panel>
```
### Accordion
Multiple expansion-panels can be combined into an accordion. The `multi="true"` input allows the
@@ -60,23 +77,23 @@ panel can be expanded at a given time:
```html
<md-accordion>
<md-expansion-panel>
<md-expansion-panel-header>
This is the expansion 1 title
</md-expansion-panel-header>
This the expansion 1 content
</md-expansion-panel>
<md-expansion-panel>
<md-expansion-panel-header>
This is the expansion 2 title
</md-expansion-panel-header>
This the expansion 2 content
</md-expansion-panel>
</md-accordion>
+68 -5
View File
@@ -1,8 +1,8 @@
import {async, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {Component} from '@angular/core';
import {async, TestBed, fakeAsync, tick, ComponentFixture} from '@angular/core/testing';
import {Component, ViewChild} from '@angular/core';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdExpansionModule} from './index';
import {MdExpansionModule, MdExpansionPanel} from './index';
describe('MdExpansionPanel', () => {
@@ -137,6 +137,66 @@ describe('MdExpansionPanel', () => {
expect(arrow.style.transform).toBe('rotate(180deg)', 'Expected 180 degree rotation.');
}));
describe('disabled state', () => {
let fixture: ComponentFixture<PanelWithContent>;
let panel: HTMLElement;
let header: HTMLElement;
beforeEach(() => {
fixture = TestBed.createComponent(PanelWithContent);
fixture.detectChanges();
panel = fixture.debugElement.query(By.css('md-expansion-panel')).nativeElement;
header = fixture.debugElement.query(By.css('md-expansion-panel-header')).nativeElement;
});
it('should toggle the aria-disabled attribute on the header', () => {
expect(header.getAttribute('aria-disabled')).toBe('false');
fixture.componentInstance.disabled = true;
fixture.detectChanges();
expect(header.getAttribute('aria-disabled')).toBe('true');
});
it('should toggle the expansion indicator', () => {
expect(panel.querySelector('.mat-expansion-indicator')).toBeTruthy();
fixture.componentInstance.disabled = true;
fixture.detectChanges();
expect(panel.querySelector('.mat-expansion-indicator')).toBeFalsy();
});
it('should not be able to toggle the panel via a user action if disabled', () => {
expect(fixture.componentInstance.panel.expanded).toBe(false);
expect(header.classList).not.toContain('mat-expanded');
fixture.componentInstance.disabled = true;
fixture.detectChanges();
header.click();
fixture.detectChanges();
expect(fixture.componentInstance.panel.expanded).toBe(false);
expect(header.classList).not.toContain('mat-expanded');
});
it('should be able to toggle a disabled expansion panel programmatically', () => {
expect(fixture.componentInstance.panel.expanded).toBe(false);
expect(header.classList).not.toContain('mat-expanded');
fixture.componentInstance.disabled = true;
fixture.detectChanges();
fixture.componentInstance.expanded = true;
fixture.detectChanges();
expect(fixture.componentInstance.panel.expanded).toBe(true);
expect(header.classList).toContain('mat-expanded');
});
});
});
@@ -144,6 +204,7 @@ describe('MdExpansionPanel', () => {
template: `
<md-expansion-panel [expanded]="expanded"
[hideToggle]="hideToggle"
[disabled]="disabled"
(opened)="openCallback()"
(closed)="closeCallback()">
<md-expansion-panel-header>Panel Title</md-expansion-panel-header>
@@ -152,10 +213,12 @@ describe('MdExpansionPanel', () => {
</md-expansion-panel>`
})
class PanelWithContent {
expanded: boolean = false;
hideToggle: boolean = false;
expanded = false;
hideToggle = false;
disabled = false;
openCallback = jasmine.createSpy('openCallback');
closeCallback = jasmine.createSpy('closeCallback');
@ViewChild(MdExpansionPanel) panel: MdExpansionPanel;
}