Skip to content

Commit

Permalink
feat(expansion-panel): add the ability to disable an expansion panel (#…
Browse files Browse the repository at this point in the history
…6529)

Fixes #6521.
  • Loading branch information
crisbeto authored and kara committed Aug 22, 2017
1 parent 449ed19 commit 921432a
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 28 deletions.
5 changes: 3 additions & 2 deletions src/demo-app/expansion/expansion-demo.html
Expand Up @@ -17,6 +17,7 @@ <h1>Accordion</h1>
<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>
Expand All @@ -37,7 +38,7 @@ <h1>Accordion</h1>
<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>
Expand All @@ -49,4 +50,4 @@ <h1>Accordion</h1>
<button md-button (click)="panel3.expanded = false">CLOSE</button>
</md-action-row>
</md-expansion-panel>
</md-accordion>
</md-accordion>
5 changes: 3 additions & 2 deletions src/demo-app/expansion/expansion-demo.ts
Expand Up @@ -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;
}
11 changes: 10 additions & 1 deletion src/lib/expansion/_expansion-theme.scss
Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down
11 changes: 10 additions & 1 deletion src/lib/expansion/accordion-item.ts
Expand Up @@ -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. */
Expand Down Expand Up @@ -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 &&
Expand Down
2 changes: 1 addition & 1 deletion src/lib/expansion/expansion-panel-header.html
Expand Up @@ -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>
5 changes: 4 additions & 1 deletion 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;
Expand All @@ -15,6 +14,10 @@
&.mat-expanded:hover, {
background: inherit;
}

&:not([aria-disabled='true']) {
cursor: pointer;
}
}

.mat-content {
Expand Down
15 changes: 9 additions & 6 deletions src/lib/expansion/expansion-panel-header.ts
Expand Up @@ -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)',
Expand Down Expand Up @@ -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());

Expand All @@ -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. */
Expand All @@ -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. */
Expand Down
1 change: 1 addition & 0 deletions src/lib/expansion/expansion-panel.ts
Expand Up @@ -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',
Expand Down
35 changes: 26 additions & 9 deletions src/lib/expansion/expansion.md
Expand Up @@ -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
Expand All @@ -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>
Expand All @@ -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
Expand All @@ -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>
Expand Down
73 changes: 68 additions & 5 deletions 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', () => {
Expand Down Expand Up @@ -137,13 +137,74 @@ 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');
});

});
});


@Component({
template: `
<md-expansion-panel [expanded]="expanded"
[hideToggle]="hideToggle"
[disabled]="disabled"
(opened)="openCallback()"
(closed)="closeCallback()">
<md-expansion-panel-header>Panel Title</md-expansion-panel-header>
Expand All @@ -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;
}


Expand Down

0 comments on commit 921432a

Please sign in to comment.