diff --git a/src/demo-app/expansion/expansion-demo.html b/src/demo-app/expansion/expansion-demo.html
index fe2fa05517de..3b5976712c58 100644
--- a/src/demo-app/expansion/expansion-demo.html
+++ b/src/demo-app/expansion/expansion-demo.html
@@ -17,6 +17,7 @@
Accordion
Allow Multi Expansion
Hide Indicators
+ Disable Panel 2
Show Panel 3
Accordion Style
@@ -37,7 +38,7 @@ Accordion
Section 1
This is the content text that makes sense here.
-
+
Section 2
This is the content text that makes sense here.
@@ -49,4 +50,4 @@ Accordion
-
\ No newline at end of file
+
diff --git a/src/demo-app/expansion/expansion-demo.ts b/src/demo-app/expansion/expansion-demo.ts
index 170ebc3c441c..ac1076b020c0 100644
--- a/src/demo-app/expansion/expansion-demo.ts
+++ b/src/demo-app/expansion/expansion-demo.ts
@@ -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;
}
diff --git a/src/lib/expansion/_expansion-theme.scss b/src/lib/expansion/_expansion-theme.scss
index 11dde9e67994..75e2b528bc3b 100644
--- a/src/lib/expansion/_expansion-theme.scss
+++ b/src/lib/expansion/_expansion-theme.scss
@@ -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) {
diff --git a/src/lib/expansion/accordion-item.ts b/src/lib/expansion/accordion-item.ts
index 785a79755f71..bf4d6d042722 100644
--- a/src/lib/expansion/accordion-item.ts
+++ b/src/lib/expansion/accordion-item.ts
@@ -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();
/** 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 &&
diff --git a/src/lib/expansion/expansion-panel-header.html b/src/lib/expansion/expansion-panel-header.html
index 4394f4b5742d..20373a9cb3ef 100644
--- a/src/lib/expansion/expansion-panel-header.html
+++ b/src/lib/expansion/expansion-panel-header.html
@@ -3,5 +3,5 @@
-
diff --git a/src/lib/expansion/expansion-panel-header.scss b/src/lib/expansion/expansion-panel-header.scss
index d501ceb2e3df..73a6638a44be 100644
--- a/src/lib/expansion/expansion-panel-header.scss
+++ b/src/lib/expansion/expansion-panel-header.scss
@@ -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 {
diff --git a/src/lib/expansion/expansion-panel-header.ts b/src/lib/expansion/expansion-panel-header.ts
index 7f9d8ef96b74..2de7617f3851 100644
--- a/src/lib/expansion/expansion-panel-header.ts
+++ b/src/lib/expansion/expansion-panel-header.ts
@@ -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. */
diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts
index b36b8f7e9840..d545e99f3d9e 100644
--- a/src/lib/expansion/expansion-panel.ts
+++ b/src/lib/expansion/expansion-panel.ts
@@ -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',
diff --git a/src/lib/expansion/expansion.md b/src/lib/expansion/expansion.md
index fff3acc28d8a..c228a9784b36 100644
--- a/src/lib/expansion/expansion.md
+++ b/src/lib/expansion/expansion.md
@@ -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
@@ -52,6 +52,23 @@ expanded state.
```
+#### 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
+
+
+ This is the expansion title
+
+
+ This is a summary of the content
+
+
+```
+
+
### 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
-
+
This is the expansion 1 title
-
+
This the expansion 1 content
-
+
-
+
This is the expansion 2 title
-
+
This the expansion 2 content
-
+
diff --git a/src/lib/expansion/expansion.spec.ts b/src/lib/expansion/expansion.spec.ts
index e43cf3f323b1..5c0033a326e4 100644
--- a/src/lib/expansion/expansion.spec.ts
+++ b/src/lib/expansion/expansion.spec.ts
@@ -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;
+ 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: `
Panel Title
@@ -152,10 +213,12 @@ describe('MdExpansionPanel', () => {
`
})
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;
}