mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(expansion-panel): add the ability to disable an expansion panel (#6529)
Fixes #6521.
This commit is contained in:
committed by
Kara
parent
449ed194d2
commit
921432acf7
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user