diff --git a/src/demo-app/expansion/expansion-demo.html b/src/demo-app/expansion/expansion-demo.html
index 29c4cecc995..414078a94e1 100644
--- a/src/demo-app/expansion/expansion-demo.html
+++ b/src/demo-app/expansion/expansion-demo.html
@@ -1,6 +1,8 @@
Single Expansion Panel
-
+
This is a panel description.
Panel Title
@@ -29,6 +31,11 @@
+Expansion Panel Animation Events
+
+ {{event}}
+
+
matAccordion
Accordion Options
@@ -56,7 +63,7 @@
+ class="demo-expansion-width">
Section 1
This is the content text that makes sense here.
diff --git a/src/demo-app/expansion/expansion-demo.scss b/src/demo-app/expansion/expansion-demo.scss
index 089784b09d8..09ccb98e40f 100644
--- a/src/demo-app/expansion/expansion-demo.scss
+++ b/src/demo-app/expansion/expansion-demo.scss
@@ -2,3 +2,16 @@
width: 600px;
display: block;
}
+
+.demo-expansion-code {
+ background: #d3d3d3;
+ display: block;
+ height: 10em;
+ overflow: auto;
+ padding: 0 5px;
+ width: 400px;
+
+ pre {
+ margin: 0.25em 0;
+ }
+}
diff --git a/src/demo-app/expansion/expansion-demo.ts b/src/demo-app/expansion/expansion-demo.ts
index 565ab4b1d53..54e6737af38 100644
--- a/src/demo-app/expansion/expansion-demo.ts
+++ b/src/demo-app/expansion/expansion-demo.ts
@@ -26,4 +26,9 @@ export class ExpansionDemo {
showPanel3 = true;
expandedHeight: string;
collapsedHeight: string;
+ events: string[] = [];
+
+ addEvent(eventName: string) {
+ this.events.push(`${eventName} - ${new Date().toISOString()}`);
+ }
}
diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts
index d7348f9da96..e40071b8d40 100644
--- a/src/lib/expansion/expansion-panel.ts
+++ b/src/lib/expansion/expansion-panel.ts
@@ -18,10 +18,12 @@ import {
Component,
ContentChild,
Directive,
+ EventEmitter,
Input,
OnChanges,
OnDestroy,
Optional,
+ Output,
SimpleChanges,
SkipSelf,
ViewContainerRef,
@@ -69,7 +71,7 @@ let uniqueId = 0;
}
})
export class MatExpansionPanel extends CdkAccordionItem
- implements AfterContentInit, OnChanges, OnDestroy {
+ implements AfterContentInit, OnChanges, OnDestroy {
/** Whether the toggle indicator should be hidden. */
@Input()
get hideToggle(): boolean { return this._hideToggle; }
@@ -78,6 +80,12 @@ export class MatExpansionPanel extends CdkAccordionItem
}
private _hideToggle = false;
+ /** An event emitted after the body's expansion animation happens. */
+ @Output() afterExpand = new EventEmitter();
+
+ /** An event emitted after the body's collapse animation happens. */
+ @Output() afterCollapse = new EventEmitter();
+
/** Stream that emits for changes in `@Input` properties. */
readonly _inputChanges = new Subject();
@@ -147,7 +155,7 @@ export class MatExpansionPanel extends CdkAccordionItem
_bodyAnimation(event: AnimationEvent) {
const classList = event.element.classList;
const cssClass = 'mat-expanded';
- const {phaseName, toState} = event;
+ const {phaseName, toState, fromState} = event;
// Toggle the body's `overflow: hidden` class when closing starts or when expansion ends in
// order to prevent the cases where switching too early would cause the animation to jump.
@@ -155,9 +163,17 @@ export class MatExpansionPanel extends CdkAccordionItem
// with doing it via change detection.
if (phaseName === 'done' && toState === 'expanded') {
classList.add(cssClass);
- } else if (phaseName === 'start' && toState === 'collapsed') {
+ }
+ if (phaseName === 'start' && toState === 'collapsed') {
classList.remove(cssClass);
}
+
+ if (phaseName === 'done' && toState === 'expanded' && fromState !== 'void') {
+ this.afterExpand.emit();
+ }
+ if (phaseName === 'done' && toState === 'collapsed' && fromState !== 'void') {
+ this.afterCollapse.emit();
+ }
}
}
diff --git a/src/lib/expansion/expansion.spec.ts b/src/lib/expansion/expansion.spec.ts
index 914dd95a2a8..4af9dce54d3 100644
--- a/src/lib/expansion/expansion.spec.ts
+++ b/src/lib/expansion/expansion.spec.ts
@@ -11,8 +11,8 @@ describe('MatExpansionPanel', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
+ MatExpansionModule,
NoopAnimationsModule,
- MatExpansionModule
],
declarations: [
PanelWithContent,
@@ -44,8 +44,9 @@ describe('MatExpansionPanel', () => {
}));
it('should be able to render panel content lazily', fakeAsync(() => {
- let fixture = TestBed.createComponent(LazyPanelWithContent);
- let content = fixture.debugElement.query(By.css('.mat-expansion-panel-content')).nativeElement;
+ const fixture = TestBed.createComponent(LazyPanelWithContent);
+ const content = fixture.debugElement.query(
+ By.css('.mat-expansion-panel-content')).nativeElement;
fixture.detectChanges();
expect(content.textContent.trim()).toBe('', 'Expected content element to be empty.');
@@ -58,8 +59,9 @@ describe('MatExpansionPanel', () => {
}));
it('should render the content for a lazy-loaded panel that is opened on init', fakeAsync(() => {
- let fixture = TestBed.createComponent(LazyPanelOpenOnLoad);
- let content = fixture.debugElement.query(By.css('.mat-expansion-panel-content')).nativeElement;
+ const fixture = TestBed.createComponent(LazyPanelOpenOnLoad);
+ const content = fixture.debugElement.query(
+ By.css('.mat-expansion-panel-content')).nativeElement;
fixture.detectChanges();
expect(content.textContent.trim())
@@ -161,10 +163,10 @@ describe('MatExpansionPanel', () => {
}));
it('should not override the panel margin if it is not inside an accordion', fakeAsync(() => {
- let fixture = TestBed.createComponent(PanelWithCustomMargin);
+ const fixture = TestBed.createComponent(PanelWithCustomMargin);
fixture.detectChanges();
- let panel = fixture.debugElement.query(By.css('mat-expansion-panel'));
+ const panel = fixture.debugElement.query(By.css('mat-expansion-panel'));
let styles = getComputedStyle(panel.nativeElement);
expect(panel.componentInstance._hasSpacing()).toBe(false);
@@ -221,7 +223,7 @@ describe('MatExpansionPanel', () => {
}));
it('should make sure accordion item runs ngOnDestroy when expansion panel is destroyed', () => {
- let fixture = TestBed.createComponent(PanelWithContentInNgIf);
+ const fixture = TestBed.createComponent(PanelWithContentInNgIf);
fixture.detectChanges();
let destroyedOk = false;
fixture.componentInstance.panel.destroyed.subscribe(() => destroyedOk = true);
@@ -264,6 +266,27 @@ describe('MatExpansionPanel', () => {
'Expected class to be added after the animation has finished');
}));
+ it('should emit events for body expanding and collapsing animations', fakeAsync(() => {
+ const fixture = TestBed.createComponent(PanelWithContent);
+ fixture.detectChanges();
+ let afterExpand = 0;
+ let afterCollapse = 0;
+ fixture.componentInstance.panel.afterExpand.subscribe(() => afterExpand++);
+ fixture.componentInstance.panel.afterCollapse.subscribe(() => afterCollapse++);
+
+ fixture.componentInstance.expanded = true;
+ fixture.detectChanges();
+ flush();
+ expect(afterExpand).toBe(1);
+ expect(afterCollapse).toBe(0);
+
+ fixture.componentInstance.expanded = false;
+ fixture.detectChanges();
+ flush();
+ expect(afterExpand).toBe(1);
+ expect(afterCollapse).toBe(1);
+ }));
+
describe('disabled state', () => {
let fixture: ComponentFixture;
let panel: HTMLElement;
@@ -373,7 +396,7 @@ class PanelWithContentInNgIf {
`
})
class PanelWithCustomMargin {
- expanded: boolean = false;
+ expanded = false;
}
@Component({