chore(accordion): move CdkAccordion to @angular/cdk (#7530)

* Move UniqueSelectionDispatcher to CDK.

BREAKING CHANGE: UniqueSelectionDispatcher, UniqueSelectionDispatcherListener and UNIQUE_SELECTION_DISPATCHER_PROVIDER are no longer
available from @angular/material and instead must be imported from @angular/cdk/collections

* Move UniqueSelectionDispatcher to CDK.

BREAKING CHANGE: UniqueSelectionDispatcher, UniqueSelectionDispatcherListener and UNIQUE_SELECTION_DISPATCHER_PROVIDER are no longer
available from @angular/material and instead must be imported from @angular/cdk/collections

* Move CdkAccordion to @angular/cdk/accordion

BREAKING CHANGE:
  - CdkAccordion is now in @angular/cdk/accordion
  - AccordionChild is renamed to CdkAccordionChild
  - CdkAccordion no longer has displayMode and hideToggle @Inputs
  - CdkAccordionItem is now a @Directive
  - CdkAccordion is now exportedAs CdkAccordion for template referencing
  - CdkAccordionItem is now exported as CdkAccordionItem for template referencing
This commit is contained in:
Joey Perrott
2017-10-11 22:18:54 -07:00
committed by Andrew Seguin
parent 291a87cb61
commit 4d04472d3f
19 changed files with 408 additions and 61 deletions
+155
View File
@@ -0,0 +1,155 @@
import {async, TestBed, ComponentFixture} from '@angular/core/testing';
import {Component} from '@angular/core';
import {By} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {CdkAccordionModule, CdkAccordionItem} from './public-api';
describe('CdkAccordionItem', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
CdkAccordionModule
],
declarations: [
SingleItem,
ItemGroupWithoutAccordion,
ItemGroupWithAccordion
],
});
TestBed.compileComponents();
}));
describe('single item', () => {
let fixture: ComponentFixture<SingleItem>;
let item: CdkAccordionItem;
beforeEach(() => {
fixture = TestBed.createComponent(SingleItem);
item = fixture.debugElement
.query(By.directive(CdkAccordionItem))
.injector.get(CdkAccordionItem);
});
it('should toggle its expanded state', () => {
expect(item.expanded).toBeFalsy();
item.toggle();
expect(item.expanded).toBeTruthy();
item.toggle();
expect(item.expanded).toBeFalsy();
});
it('should set its expanded state to expanded', () => {
item.expanded = false;
item.open();
expect(item.expanded).toBeTruthy();
});
it('should set its expanded state to closed', () => {
item.expanded = true;
item.close();
expect(item.expanded).toBeFalsy();
});
it('should emit a closed event', () => {
spyOn(item.closed, 'emit');
item.close();
fixture.detectChanges();
expect(item.closed.emit).toHaveBeenCalledWith();
});
it('should emit an opened event', () => {
spyOn(item.opened, 'emit');
item.open();
fixture.detectChanges();
expect(item.opened.emit).toHaveBeenCalledWith();
});
it('should emit an destroyed event', () => {
spyOn(item.destroyed, 'emit');
item.ngOnDestroy();
fixture.detectChanges();
expect(item.destroyed.emit).toHaveBeenCalledWith();
});
});
describe('items without accordion', () => {
let fixture: ComponentFixture<SingleItem>;
let firstItem: CdkAccordionItem;
let secondItem: CdkAccordionItem;
beforeEach(() => {
fixture = TestBed.createComponent(ItemGroupWithoutAccordion);
[firstItem, secondItem] = fixture.debugElement
.queryAll(By.directive(CdkAccordionItem))
.map(el => {
return el.injector.get(CdkAccordionItem) as CdkAccordionItem;
});
});
it('should not change expanded state based on unrelated items', () => {
expect(firstItem.expanded).toBeFalsy();
expect(secondItem.expanded).toBeFalsy();
firstItem.open();
fixture.detectChanges();
expect(firstItem.expanded).toBeTruthy();
expect(secondItem.expanded).toBeFalsy();
secondItem.open();
fixture.detectChanges();
expect(firstItem.expanded).toBeTruthy();
expect(secondItem.expanded).toBeTruthy();
});
});
describe('items in accordion', () => {
let fixture: ComponentFixture<SingleItem>;
let firstItem: CdkAccordionItem;
let secondItem: CdkAccordionItem;
beforeEach(() => {
fixture = TestBed.createComponent(ItemGroupWithAccordion);
[firstItem, secondItem] = fixture.debugElement
.queryAll(By.directive(CdkAccordionItem))
.map(el => {
return el.injector.get(CdkAccordionItem) as CdkAccordionItem;
});
});
it('should change expanded state based on related items', () => {
expect(firstItem.expanded).toBeFalsy();
expect(secondItem.expanded).toBeFalsy();
firstItem.open();
fixture.detectChanges();
expect(firstItem.expanded).toBeTruthy();
expect(secondItem.expanded).toBeFalsy();
secondItem.open();
fixture.detectChanges();
expect(firstItem.expanded).toBeFalsy();
expect(secondItem.expanded).toBeTruthy();
});
});
});
@Component({
template: `<cdk-accordion-item #item1></cdk-accordion-item>`
})
class SingleItem {}
@Component({
template: `
<cdk-accordion-item #item1></cdk-accordion-item>
<cdk-accordion-item #item2></cdk-accordion-item>
`
})
class ItemGroupWithoutAccordion {}
@Component({
template: `
<cdk-accordion>
<cdk-accordion-item #item1></cdk-accordion-item>
<cdk-accordion-item #item2></cdk-accordion-item>
</cdk-accordion>
`
})
class ItemGroupWithAccordion {}
@@ -8,9 +8,9 @@
import {
Output,
Directive,
EventEmitter,
Input,
Injectable,
OnDestroy,
Optional,
ChangeDetectorRef,
@@ -18,15 +18,18 @@ import {
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
import {CdkAccordion} from './accordion';
/** Used to generate unique ID for each expansion panel. */
/** Used to generate unique ID for each accordion item. */
let nextId = 0;
/**
* An abstract class to be extended and decorated as a component. Sets up all
* An basic directive expected 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 {
@Directive({
selector: 'cdk-accordion-item',
exportAs: 'cdkAccordionItem',
})
export class CdkAccordionItem implements OnDestroy {
/** Event emitted every time the AccordionItem is closed. */
@Output() closed = new EventEmitter<void>();
/** Event emitted every time the AccordionItem is opened. */
@@ -62,7 +65,7 @@ export class AccordionItem implements OnDestroy {
}
private _expanded: boolean;
/** Unregister function for _expansionDispatcher **/
/** Unregister function for _expansionDispatcher. */
private _removeUniqueSelectionListener: () => void = () => {};
constructor(@Optional() public accordion: CdkAccordion,
+19
View File
@@ -0,0 +1,19 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {UNIQUE_SELECTION_DISPATCHER_PROVIDER} from '@angular/cdk/collections';
import {CdkAccordion} from './accordion';
import {CdkAccordionItem} from './accordion-item';
@NgModule({
exports: [CdkAccordion, CdkAccordionItem],
declarations: [CdkAccordion, CdkAccordionItem],
providers: [UNIQUE_SELECTION_DISPATCHER_PROVIDER],
})
export class CdkAccordionModule {}
+67
View File
@@ -0,0 +1,67 @@
import {async, TestBed} from '@angular/core/testing';
import {Component, ViewChild} from '@angular/core';
import {By} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {CdkAccordionModule, CdkAccordionItem} from './public-api';
describe('CdkAccordion', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
CdkAccordionModule
],
declarations: [
SetOfItems
],
});
TestBed.compileComponents();
}));
it('should ensure only one item is expanded at a time', () => {
const fixture = TestBed.createComponent(SetOfItems);
const [firstPanel, secondPanel] = fixture.debugElement
.queryAll(By.directive(CdkAccordionItem))
.map(el => {
return el.injector.get(CdkAccordionItem) as CdkAccordionItem;
});
firstPanel.open();
fixture.detectChanges();
expect(firstPanel.expanded).toBeTruthy();
expect(secondPanel.expanded).toBeFalsy();
secondPanel.open();
fixture.detectChanges();
expect(firstPanel.expanded).toBeFalsy();
expect(secondPanel.expanded).toBeTruthy();
});
it('should allow multiple items to be expanded simultaneously', () => {
const fixture = TestBed.createComponent(SetOfItems);
const [firstPanel, secondPanel] = fixture.debugElement
.queryAll(By.directive(CdkAccordionItem))
.map(el => {
return el.injector.get(CdkAccordionItem) as CdkAccordionItem;
});
fixture.componentInstance.multi = true;
fixture.detectChanges();
firstPanel.expanded = true;
secondPanel.expanded = true;
fixture.detectChanges();
expect(firstPanel.expanded).toBeTruthy();
expect(secondPanel.expanded).toBeTruthy();
});
});
@Component({template: `
<cdk-accordion [multi]="multi">
<cdk-accordion-item #item1></cdk-accordion-item>
<cdk-accordion-item #item2></cdk-accordion-item>
</cdk-accordion>`})
class SetOfItems {
@ViewChild('item1') item1;
@ViewChild('item2') item2;
multi: boolean = false;
}
+30
View File
@@ -0,0 +1,30 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Input} from '@angular/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
/** Used to generate unique ID for each accordion. */
let nextId = 0;
/**
* Directive whose purpose is to manage the expanded state of CdkAccordionItem children.
*/
@Directive({
selector: 'cdk-accordion, [cdk-accordion]',
exportAs: 'cdkAccordion',
})
export class CdkAccordion {
/** A readonly id value to use for unique selection coordination. */
readonly id = `cdk-accordion-${nextId++}`;
/** Whether the accordion should allow multiple expanded accordion items simulateously. */
@Input() get multi(): boolean { return this._multi; }
set multi(multi: boolean) { this._multi = coerceBooleanProperty(multi); }
private _multi: boolean = false;
}
+9
View File
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './public-api';
+11
View File
@@ -0,0 +1,11 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export {CdkAccordionItem} from './accordion-item';
export {CdkAccordion} from './accordion';
export * from './accordion-module';
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig-build",
"files": [
"public-api.ts"
],
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"strictMetadataEmit": true,
"flatModuleOutFile": "index.js",
"flatModuleId": "@angular/cdk/accordion",
"skipTemplateCodegen": true
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig-es5",
"files": [
"public-api.ts"
],
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"strictMetadataEmit": true,
"flatModuleOutFile": "index.js",
"flatModuleId": "@angular/cdk/accordion",
"skipTemplateCodegen": true
}
}
+2
View File
@@ -33,6 +33,7 @@ import {
} from '@angular/material';
import {MatNativeDateModule, MatRippleModule} from '@angular/material';
import {CdkTableModule} from '@angular/cdk/table';
import {CdkAccordionModule} from '@angular/cdk/accordion';
import {A11yModule} from '@angular/cdk/a11y';
import {BidiModule} from '@angular/cdk/bidi';
import {OverlayModule} from '@angular/cdk/overlay';
@@ -80,6 +81,7 @@ import {PortalModule} from '@angular/cdk/portal';
CdkTableModule,
A11yModule,
BidiModule,
CdkAccordionModule,
ObserversModule,
OverlayModule,
PlatformModule,
+36 -1
View File
@@ -25,7 +25,7 @@
</mat-form-field>
<br>
<h1>Accordion</h1>
<h1>matAccordion</h1>
<div>
<p>Accordion Options</p>
<div>
@@ -65,3 +65,38 @@
</mat-action-row>
</mat-expansion-panel>
</mat-accordion>
<!--
<h1>CdkAccordion</h1>
<div>
<p>Accordion Options</p>
<div>
<mat-slide-toggle [(ngModel)]="accordion1.multi">Allow Multi Expansion</mat-slide-toggle>
</div>
</div>
<cdk-accordion class="mat-expansion-demo-width" #accordion1="cdkAccordion">
<cdk-accordion-item #item1="cdkAccordionItem">
<p>
Item 1:
<button mat-button (click)="item1.expanded = true">Expand</button>
<button mat-button (click)="item1.expanded = false">Collapse</button>
</p>
<p *ngIf="item1.expanded">I only show if item 1 is expanded</p>
</cdk-accordion-item>
<cdk-accordion-item #item2="cdkAccordionItem">
<p>
Item 2:
<button mat-button (click)="item2.expanded = true">Expand</button>
<button mat-button (click)="item2.expanded = false">Collapse</button>
</p>
<p *ngIf="item2.expanded">I only show if item 2 is expanded</p>
</cdk-accordion-item>
<cdk-accordion-item #item3="cdkAccordionItem">
<p>
Item 3:
<button mat-button (click)="item3.expanded = true">Expand</button>
<button mat-button (click)="item3.expanded = false">Collapse</button>
</p>
<p *ngIf="item3.expanded">I only show if item 3 is expanded</p>
</cdk-accordion-item>
</cdk-accordion>
-->
+1
View File
@@ -31,6 +31,7 @@ System.config({
'@angular/material': 'dist/packages/material/public-api.js',
'@angular/cdk': 'dist/packages/cdk/index.js',
'@angular/cdk/a11y': 'dist/packages/cdk/a11y/index.js',
'@angular/cdk/accordion': 'dist/packages/cdk/accordion/index.js',
'@angular/cdk/bidi': 'dist/packages/cdk/bidi/index.js',
'@angular/cdk/coercion': 'dist/packages/cdk/coercion/index.js',
'@angular/cdk/collections': 'dist/packages/cdk/collections/index.js',
+1
View File
@@ -28,6 +28,7 @@ System.config({
'@angular/material': 'dist/bundles/material.umd.js',
'@angular/cdk': 'dist/bundles/cdk.umd.js',
'@angular/cdk/a11y': 'dist/bundles/cdk-a11y.umd.js',
'@angular/cdk/accordion': 'dist/bundles/cdk-accordion.umd.js',
'@angular/cdk/bidi': 'dist/bundles/cdk-bidi.umd.js',
'@angular/cdk/coercion': 'dist/bundles/cdk-coercion.umd.js',
'@angular/cdk/collections': 'dist/bundles/cdk-collections.umd.js',
+11 -27
View File
@@ -8,29 +8,25 @@
import {Directive, Input} from '@angular/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {CdkAccordion} from '@angular/cdk/accordion';
/** Workaround for https://github.com/angular/angular/issues/17849 */
export const _CdkAccordion = CdkAccordion;
/** MatAccordion's display modes. */
export type MatAccordionDisplayMode = 'default' | 'flat';
/** Unique ID counter */
let nextId = 0;
/**
* Directive whose purpose is to manage the expanded state of CdkAccordionItem children.
* Directive for a Material Design Accordion.
*/
@Directive({
selector: 'cdk-accordion, [cdk-accordion]',
exportAs: 'cdkAccordion',
selector: 'mat-accordion',
exportAs: 'matAccordion',
host: {
class: 'mat-accordion'
}
})
export class CdkAccordion {
/** A readonly id value to use for unique selection coordination. */
readonly id = `cdk-accordion-${nextId++}`;
/** Whether the accordion should allow multiple expanded accordion items simulateously. */
@Input() get multi(): boolean { return this._multi; }
set multi(multi: boolean) { this._multi = coerceBooleanProperty(multi); }
private _multi: boolean = false;
export class MatAccordion extends _CdkAccordion {
/** Whether the expansion indicator should be hidden. */
@Input() get hideToggle(): boolean { return this._hideToggle; }
set hideToggle(show: boolean) { this._hideToggle = coerceBooleanProperty(show); }
@@ -46,15 +42,3 @@ export class CdkAccordion {
*/
@Input() displayMode: MatAccordionDisplayMode = 'default';
}
/**
* Directive for a Material Design Accordion.
*/
@Directive({
selector: 'mat-accordion',
exportAs: 'matAccordion',
host: {
class: 'mat-accordion'
}
})
export class MatAccordion extends CdkAccordion {}
+9 -5
View File
@@ -9,9 +9,14 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {UNIQUE_SELECTION_DISPATCHER_PROVIDER} from '@angular/cdk/collections';
import {CdkAccordionModule} from '@angular/cdk/accordion';
import {A11yModule} from '@angular/cdk/a11y';
import {CdkAccordion, MatAccordion} from './accordion';
import {MatExpansionPanel, MatExpansionPanelActionRow} from './expansion-panel';
import {MatAccordion} from './accordion';
import {
MatExpansionPanel,
MatExpansionPanelActionRow,
MatExpansionPanelBase
} from './expansion-panel';
import {
MatExpansionPanelDescription,
MatExpansionPanelHeader,
@@ -20,9 +25,8 @@ import {
@NgModule({
imports: [CommonModule, A11yModule],
imports: [CommonModule, A11yModule, CdkAccordionModule],
exports: [
CdkAccordion,
MatAccordion,
MatExpansionPanel,
MatExpansionPanelActionRow,
@@ -31,7 +35,7 @@ import {
MatExpansionPanelDescription
],
declarations: [
CdkAccordion,
MatExpansionPanelBase,
MatAccordion,
MatExpansionPanel,
MatExpansionPanelActionRow,
+16 -4
View File
@@ -21,16 +21,25 @@ import {
SimpleChanges,
ViewEncapsulation,
} from '@angular/core';
import {CdkAccordionItem} from '@angular/cdk/accordion';
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
import {CanDisable, mixinDisabled} from '@angular/material/core';
import {Subject} from 'rxjs/Subject';
import {MatAccordion} from './accordion';
import {AccordionItem} from './accordion-item';
/** Workaround for https://github.com/angular/angular/issues/17849 */
export const _CdkAccordionItem = CdkAccordionItem;
// Boilerplate for applying mixins to MatExpansionPanel.
/** @docs-private */
export class MatExpansionPanelBase extends AccordionItem {
@Component({
template: '',
moduleId: module.id,
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatExpansionPanelBase extends _CdkAccordionItem {
constructor(accordion: MatAccordion,
_changeDetectorRef: ChangeDetectorRef,
_uniqueSelectionDispatcher: UniqueSelectionDispatcher) {
@@ -49,7 +58,7 @@ export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,
* <mat-expansion-panel> component.
*
* This component can be used as a single element to show expandable content, or as one of
* multiple children of an element with the CdkAccordion directive attached.
* multiple children of an element with the MdAccordion directive attached.
*
* Please refer to README.md for examples on how to use it.
*/
@@ -69,7 +78,7 @@ export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,
'[class.mat-expansion-panel-spacing]': '_hasSpacing()',
},
providers: [
{provide: AccordionItem, useExisting: forwardRef(() => MatExpansionPanel)}
{provide: _MatExpansionPanelMixinBase, useExisting: forwardRef(() => MatExpansionPanel)}
],
animations: [
trigger('bodyExpansion', [
@@ -87,6 +96,9 @@ export class MatExpansionPanel extends _MatExpansionPanelMixinBase
/** Stream that emits for changes in `@Input` properties. */
_inputChanges = new Subject<SimpleChanges>();
/** Optionally defined accordion the expansion panel belongs to. */
accordion: MatAccordion;
constructor(@Optional() @Host() accordion: MatAccordion,
_changeDetectorRef: ChangeDetectorRef,
_uniqueSelectionDispatcher: UniqueSelectionDispatcher) {
+3 -16
View File
@@ -7,21 +7,8 @@
*/
export * from './expansion-module';
export {
CdkAccordion,
MatAccordion,
MatAccordionDisplayMode
} from './accordion';
export {AccordionItem} from './accordion-item';
export {
MatExpansionPanel,
MatExpansionPanelState,
MatExpansionPanelActionRow
} from './expansion-panel';
export {
MatExpansionPanelHeader,
MatExpansionPanelDescription,
MatExpansionPanelTitle
} from './expansion-panel-header';
export * from './accordion';
export * from './expansion-panel';
export * from './expansion-panel-header';
+2 -2
View File
@@ -8,14 +8,14 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {A11yModule} from '@angular/cdk/a11y';
import {UNIQUE_SELECTION_DISPATCHER_PROVIDER} from '@angular/cdk/collections';
import {VIEWPORT_RULER_PROVIDER} from '@angular/cdk/overlay';
import {
MatRippleModule,
MatCommonModule,
} from '@angular/material/core';
import {MatRadioGroup, MatRadioButton} from './radio';
import {UNIQUE_SELECTION_DISPATCHER_PROVIDER} from '@angular/cdk/collections';
import {A11yModule} from '@angular/cdk/a11y';
@NgModule({
imports: [CommonModule, MatRippleModule, MatCommonModule, A11yModule],
+1
View File
@@ -51,6 +51,7 @@ System.config({
'@angular/material': 'dist/packages/material/public-api.js',
'@angular/cdk': 'dist/packages/cdk/index.js',
'@angular/cdk/a11y': 'dist/packages/cdk/a11y/index.js',
'@angular/cdk/accordion': 'dist/packages/cdk/accordion/index.js',
'@angular/cdk/bidi': 'dist/packages/cdk/bidi/index.js',
'@angular/cdk/coercion': 'dist/packages/cdk/coercion/index.js',
'@angular/cdk/collections': 'dist/packages/cdk/collections/index.js',