diff --git a/demo/src/app/components/+accordion/demos/basic/basic.html b/demo/src/app/components/+accordion/demos/basic/basic.html index 6c89d158a6..cefc561076 100644 --- a/demo/src/app/components/+accordion/demos/basic/basic.html +++ b/demo/src/app/components/+accordion/demos/basic/basic.html @@ -9,10 +9,10 @@ This is just some content to illustrate fancy headings. - -

Content 1

+ +

Some content

- -

Content 2

+ +

Some content

diff --git a/demo/src/app/components/+accordion/demos/basic/basic.ts b/demo/src/app/components/+accordion/demos/basic/basic.ts index e053a17de2..620e7fb695 100644 --- a/demo/src/app/components/+accordion/demos/basic/basic.ts +++ b/demo/src/app/components/+accordion/demos/basic/basic.ts @@ -4,4 +4,8 @@ import { Component } from '@angular/core'; selector: 'demo-accordion-basic', templateUrl: './basic.html' }) -export class DemoAccordionBasicComponent {} +export class DemoAccordionBasicComponent { + log(event: boolean) { + console.log(`Accordion has been ${event ? 'opened' : 'closed'}`); + } +} diff --git a/demo/src/ng-api-doc.ts b/demo/src/ng-api-doc.ts index 00c6f8898e..36265121bb 100644 --- a/demo/src/ng-api-doc.ts +++ b/demo/src/ng-api-doc.ts @@ -19,7 +19,7 @@ export const ngdoc: any = { { "name": "isOpen", "type": "boolean", - "description": "

Is accordion group open or closed

\n" + "description": "

Is accordion group open or closed. This property supports two-way binding

\n" }, { "name": "panelClass", @@ -27,7 +27,12 @@ export const ngdoc: any = { "description": "

Provides an ability to use Bootstrap's contextual panel classes\n(panel-primary, panel-success, panel-info, etc...).\nList of all available classes [available here]\n(http://getbootstrap.com/components/#panels-alternatives)

\n" } ], - "outputs": [], + "outputs": [ + { + "name": "isOpenChange", + "description": "

Emits when the opened state changes

\n" + } + ], "properties": [], "methods": [] }, diff --git a/src/accordion/accordion-group.component.ts b/src/accordion/accordion-group.component.ts index 12fa000a05..6cf3d2130d 100644 --- a/src/accordion/accordion-group.component.ts +++ b/src/accordion/accordion-group.component.ts @@ -1,5 +1,5 @@ import { - Component, HostBinding, Inject, Input, OnDestroy, OnInit + Component, HostBinding, Inject, Input, OnDestroy, OnInit, Output, EventEmitter } from '@angular/core'; import { isBs3 } from '../utils/theme-provider'; import { AccordionComponent } from './accordion.component'; @@ -29,9 +29,11 @@ export class AccordionPanelComponent implements OnInit, OnDestroy { @Input() panelClass: string; /** if true — disables accordion group */ @Input() isDisabled: boolean; + /** Emits when the opened state changes */ + @Output() isOpenChange: EventEmitter = new EventEmitter(); // Questionable, maybe .panel-open should be on child div.panel element? - /** Is accordion group open or closed */ + /** Is accordion group open or closed. This property supports two-way binding */ @HostBinding('class.panel-open') @Input() get isOpen(): boolean { @@ -39,9 +41,14 @@ export class AccordionPanelComponent implements OnInit, OnDestroy { } set isOpen(value: boolean) { - this._isOpen = value; - if (value) { - this.accordion.closeOtherPanels(this); + if (value !== this.isOpen) { + if (value) { + this.accordion.closeOtherPanels(this); + } + this._isOpen = value; + Promise.resolve(null).then(() => { + this.isOpenChange.emit(value); + }); } } @@ -49,7 +56,7 @@ export class AccordionPanelComponent implements OnInit, OnDestroy { return isBs3(); } - protected _isOpen: boolean; + protected _isOpen = false; protected accordion: AccordionComponent; constructor(@Inject(AccordionComponent) accordion: AccordionComponent) { diff --git a/src/spec/accordion.component.spec.ts b/src/spec/accordion.component.spec.ts index 29c98f3809..f4109b83da 100644 --- a/src/spec/accordion.component.spec.ts +++ b/src/spec/accordion.component.spec.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { AccordionConfig } from '../accordion/accordion.config'; import { AccordionModule } from '../accordion/accordion.module'; @@ -25,19 +25,19 @@ const html = ` Content of panel 1 Content of panel 2 Content of panel 3 @@ -166,4 +166,32 @@ describe('Component: Accordion', () => { fixture.detectChanges(); expectOpenPanels(element, [false, false, false]); }); + + it('should modify the parent isOpen state when changed internally (2 way binding)', fakeAsync(() => { + const headingLinks = element.querySelectorAll('.accordion-toggle'); + + // Clicking (internal state modified) + headingLinks[0].click(); + tick(); + fixture.detectChanges(); + expect(context.panels[0].isOpen).toBe(true); + expect(context.panels[1].isOpen).toBe(false); + expect(context.panels[2].isOpen).toBe(false); + + // State modified by parent component + headingLinks[2].click(); + tick(); + fixture.detectChanges(); + expect(context.panels[0].isOpen).toBe(false); + expect(context.panels[1].isOpen).toBe(false); + expect(context.panels[2].isOpen).toBe(true); + + // Modified by binding + context.panels[1].isOpen = true; + fixture.detectChanges(); + tick(); + expect(context.panels[0].isOpen).toBe(false); + expect(context.panels[1].isOpen).toBe(true); + expect(context.panels[2].isOpen).toBe(false); + })); });