import { Component, ViewChild } from '@angular/core'; import { TestBed, ComponentFixture, tick, fakeAsync, waitForAsync } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxExpansionPanelComponent } from './expansion-panel.component'; import { ExpansionPanelHeaderIconPosition, IgxExpansionPanelHeaderComponent } from './expansion-panel-header.component'; import { IgxGridComponent } from '../grids/grid/public_api'; import { IgxExpansionPanelDescriptionDirective, IgxExpansionPanelIconDirective, IgxExpansionPanelTitleDirective } from './expansion-panel.directives'; import { configureTestSuite } from '../test-utils/configure-suite'; import { By } from '@angular/platform-browser'; import { IgxExpansionPanelBodyComponent } from './expansion-panel-body.component'; import { IgxListComponent } from '../list/list.component'; import { IgxListItemComponent } from '../list/list-item.component'; import { NgIf } from '@angular/common'; import { IGX_EXPANSION_PANEL_DIRECTIVES } from './public_api'; const CSS_CLASS_EXPANSION_PANEL = 'igx-expansion-panel'; const CSS_CLASS_PANEL_HEADER = 'igx-expansion-panel__header'; const CSS_CLASS_PANEL_HEADER_TITLE = 'igx-expansion-panel__header-title'; const CSS_CLASS_PANEL_HEADER_DESCRIPTION = 'igx-expansion-panel__header-description'; const CSS_CLASS_PANEL_TITLE_WRAPPER = 'igx-expansion-panel__title-wrapper'; const CSS_CLASS_PANEL_BODY = 'igx-expansion-panel-body'; const CSS_CLASS_HEADER_EXPANDED = 'igx-expansion-panel__header--expanded'; const CSS_CLASS_HEADER_ICON_START = 'igx-expansion-panel__header-icon--start'; const CSS_CLASS_HEADER_ICON_END = 'igx-expansion-panel__header-icon--end'; const CSS_CLASS_HEADER_ICON_NONE = 'igx-expansion-panel__header-icon--none'; const CSS_CLASS_PANEL_ICON = 'igx-icon'; const CSS_CLASS_LIST = 'igx-list'; const CSS_CLASS_GRID = 'igx-grid'; const enum IconPositionClass { LEFT = 'igx-expansion-panel__header-icon--start', RIGHT = 'igx-expansion-panel__header-icon--end', NONE = 'igx-expansion-panel__header-icon--none', } describe('igxExpansionPanel', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, IgxExpansionPanelGridComponent, IgxExpansionPanelListComponent, IgxExpansionPanelSampleComponent, IgxExpansionPanelImageComponent, IgxExpansionPanelTooltipComponent ] }).compileComponents(); })); describe('General tests: ', () => { // configureTestSuite(); it('Should initialize the expansion panel component properly', () => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const header = fixture.componentInstance.header; expect(fixture.componentInstance).toBeDefined(); expect(panel).toBeDefined(); // expect(panel.toggleBtn).toBeDefined(); expect(header.disabled).toBeDefined(); expect(header.disabled).toEqual(false); // expect(panel.ariaLabelledBy).toBeDefined(); // expect(panel.ariaLabelledBy).toEqual(''); expect(panel.animationSettings).toBeDefined(); expect(panel.collapsed).toBeDefined(); expect(panel.collapsed).toBeTruthy(); panel.toggle(); fixture.detectChanges(); expect(panel.collapsed).toEqual(false); }); it('Should properly accept input properties', () => { const fixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; // expect(panel.disabled).toEqual(false); // expect(panel.collapsed).toEqual(true); // // expect(panel.ariaLabelledBy).toEqual(''); // panel.disabled = true; // expect(panel.disabled).toEqual(true); panel.collapsed = false; expect(panel.collapsed).toEqual(false); // panel.labelledby = 'test label area'; // expect(panel.labelledby).toEqual('test label area'); }); it('Should properly set base classes', () => { const fixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const header = document.getElementsByClassName(CSS_CLASS_PANEL_HEADER); const headerExpanded = document.getElementsByClassName(CSS_CLASS_HEADER_EXPANDED); const panelClass = document.getElementsByClassName(CSS_CLASS_EXPANSION_PANEL); expect(header.length).toEqual(1); expect(headerExpanded.length).toEqual(0); expect(panelClass.length).toEqual(1); }); it('Should properly emit events', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; const header = fixture.componentInstance.header; const mockEvent = new Event('click'); expect(panel).toBeTruthy(); expect(header).toBeTruthy(); expect(header.disabled).toEqual(false); expect(header.panel).toEqual(panel); expect(header.interaction).toBeDefined(); spyOn(panel.contentCollapsed, 'emit'); spyOn(panel.contentExpanded, 'emit'); spyOn(panel.contentCollapsing, 'emit'); spyOn(panel.contentExpanding, 'emit'); spyOn(header.interaction, 'emit').and.callThrough(); spyOn(panel, 'toggle').and.callThrough(); spyOn(panel, 'expand').and.callThrough(); spyOn(panel, 'collapse').and.callThrough(); header.onAction(mockEvent); tick(); fixture.detectChanges(); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(0); // Initially collapsed expect(panel.contentCollapsing.emit).toHaveBeenCalledTimes(0); expect(header.interaction.emit).toHaveBeenCalledTimes(1); expect(panel.toggle).toHaveBeenCalledTimes(1); expect(panel.toggle).toHaveBeenCalledWith(mockEvent); expect(panel.expand).toHaveBeenCalledTimes(1); expect(panel.expand).toHaveBeenCalledWith(mockEvent); expect(panel.collapse).toHaveBeenCalledTimes(0); expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanding.emit).toHaveBeenCalledTimes(1); expect(header.interaction.emit).toHaveBeenCalledWith({ event: mockEvent, owner: header.panel, cancel: false }); header.onAction(mockEvent); tick(); fixture.detectChanges(); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(1); // First Collapse expect(panel.contentCollapsing.emit).toHaveBeenCalledTimes(1); expect(header.interaction.emit).toHaveBeenCalledTimes(2); expect(panel.toggle).toHaveBeenCalledTimes(2); expect(panel.toggle).toHaveBeenCalledWith(mockEvent); expect(panel.expand).toHaveBeenCalledTimes(1); expect(panel.collapse).toHaveBeenCalledTimes(1); expect(panel.collapse).toHaveBeenCalledWith(mockEvent); header.disabled = true; header.onAction(mockEvent); tick(); fixture.detectChanges(); // No additional calls, because panel.disabled === true expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(1); expect(panel.contentCollapsing.emit).toHaveBeenCalledTimes(1); expect(header.interaction.emit).toHaveBeenCalledTimes(2); expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanding.emit).toHaveBeenCalledTimes(1); // cancel event header.disabled = false; const headerSub = header.interaction.subscribe((event) => { event.cancel = true; }); // currently collapsed expect(panel.collapsed).toBeTruthy(); header.onAction(mockEvent); tick(); fixture.detectChanges(); // still collapsed, no additional contentExpanded calls expect(panel.collapsed).toBeTruthy(); expect(header.interaction.emit).toHaveBeenCalledTimes(3); expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanding.emit).toHaveBeenCalledTimes(1); // expand via API panel.expand(); tick(); fixture.detectChanges(); // currently expanded expect(panel.collapsed).toBeFalsy(); header.onAction(mockEvent); tick(); fixture.detectChanges(); // still expanded, no additional contentCollapsed calls headerSub.unsubscribe(); expect(panel.collapsed).toBeFalsy(); expect(header.interaction.emit).toHaveBeenCalledTimes(4); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(1); // collapse when the panel has already started collapsing header.onAction(mockEvent); header.onAction(mockEvent); tick(); fixture.detectChanges(); expect(panel.collapsed).toBeTruthy(); expect(header.interaction.emit).toHaveBeenCalledTimes(6); expect(panel.contentCollapsing.emit).toHaveBeenCalledTimes(2); })); it('Should expand/collapse without animation when animationSettings === null', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; panel.animationSettings = null; expect(panel).toBeTruthy(); spyOn(panel.contentCollapsed, 'emit'); spyOn(panel.contentExpanded, 'emit'); spyOn(panel.contentCollapsing, 'emit'); spyOn(panel.contentExpanding, 'emit'); panel.toggle(); tick(); fixture.detectChanges(); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(0); // Initially collapsed expect(panel.contentCollapsing.emit).toHaveBeenCalledTimes(0); expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanding.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanding.emit).toHaveBeenCalledBefore(panel.contentExpanded.emit); expect(panel.collapsed).toBeFalsy(); panel.toggle(); tick(); fixture.detectChanges(); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(1); expect(panel.contentCollapsing.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(1); expect(panel.contentExpanding.emit).toHaveBeenCalledTimes(1); expect(panel.contentCollapsing.emit).toHaveBeenCalledBefore(panel.contentCollapsed.emit); expect(panel.collapsed).toBeTruthy(); })); it('Should allow expanding and collapsing events to be cancelled', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; spyOn(panel.contentExpanded, 'emit').and.callThrough(); spyOn(panel.contentCollapsed, 'emit').and.callThrough(); expect(panel).toBeDefined(); expect(panel.collapsed).toBeTruthy(); expect(panel.contentExpanded.emit).not.toHaveBeenCalled(); expect(panel.contentCollapsed.emit).not.toHaveBeenCalled(); let sub = panel.contentExpanding.subscribe((e) => e.cancel = true); panel.expand(); tick(); fixture.detectChanges(); expect(panel.collapsed).toBeTruthy(); expect(panel.contentExpanded.emit).not.toHaveBeenCalled(); sub.unsubscribe(); panel.expand(); tick(); fixture.detectChanges(); expect(panel.collapsed).toBeFalsy(); expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(1); sub = panel.contentCollapsing.subscribe((e) => e.cancel = true); panel.collapse(); tick(); fixture.detectChanges(); expect(panel.collapsed).toBeFalsy(); expect(panel.contentCollapsed.emit).not.toHaveBeenCalled(); sub.unsubscribe(); panel.collapse(); tick(); fixture.detectChanges(); expect(panel.collapsed).toBeTruthy(); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(1); })); it('Should NOT assign tabIndex to header when disabled', () => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panelHeader = fixture.componentInstance.header; expect(panelHeader).toBeDefined(); expect(panelHeader.disabled).toBeFalsy(); let innerElement = fixture.debugElement.queryAll(By.css('.igx-expansion-panel__header-inner'))[0]; expect(innerElement).toBeDefined(); expect(innerElement.nativeElement.attributes['tabindex'].value).toBe('0'); panelHeader.disabled = true; fixture.detectChanges(); innerElement = fixture.debugElement.queryAll(By.css('.igx-expansion-panel__header-inner'))[0]; expect(innerElement).toBeDefined(); expect(innerElement.nativeElement.attributes['tabindex']).toBeUndefined(); panelHeader.disabled = false; fixture.detectChanges(); innerElement = fixture.debugElement.queryAll(By.css('.igx-expansion-panel__header-inner'))[0]; expect(innerElement).toBeDefined(); expect(innerElement.nativeElement.attributes['tabindex'].value).toBe('0'); }); }); describe('Expansion tests: ', () => { // configureTestSuite(); const verifyPanelExpansionState = ( collapsed: boolean, panel: IgxExpansionPanelComponent, panelContainer: any, panelHeader: HTMLElement, button: HTMLElement, timesCollapsed = 0, timesExpanded = 0) => { expect(panel.collapsed).toEqual(collapsed); const ariaExpanded = collapsed ? 'false' : 'true'; expect(panelHeader.querySelector('div [role = \'button\']').getAttribute('aria-expanded')).toMatch(ariaExpanded); expect(panelHeader.classList.contains(CSS_CLASS_HEADER_EXPANDED)).toEqual(!collapsed); if (button.children.length > 1) { const iconName = collapsed ? 'expand_more' : 'expand_less'; expect(button.getAttribute('ng-reflect-icon-name')).toMatch(iconName); } if (collapsed) { expect(panelContainer.lastElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-HEADER'); } else { const panelBody = panelContainer.getElementsByTagName(CSS_CLASS_PANEL_BODY)[0]; expect(panelBody).toBeDefined(); const list = panelBody.getElementsByTagName(CSS_CLASS_LIST)[0]; expect(list).toBeDefined(); } expect(panel.contentExpanded.emit).toHaveBeenCalledTimes(timesExpanded); expect(panel.contentCollapsed.emit).toHaveBeenCalledTimes(timesCollapsed); }; it('Should change panel expansion state on header interaction', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const header = fixture.componentInstance.header; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; let timesCollapsed = 0; let timesExpanded = 0; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); spyOn(header.interaction, 'emit'); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panelHeader.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(1); panelHeader.click(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(2); panelHeader.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(3); // Remove expand/collapse button header.iconPosition = ExpansionPanelHeaderIconPosition.NONE; tick(); fixture.detectChanges(); tick(); panelHeader.click(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(4); panelHeader.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(5); })); it('Should change panel expansion state on button clicking', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const header = fixture.componentInstance.header; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; let button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; let timesCollapsed = 0; let timesExpanded = 0; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); spyOn(header.interaction, 'emit'); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); button.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(1); button.click(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(2); button.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(3); // Change expand/collapse button position header.iconPosition = ExpansionPanelHeaderIconPosition.RIGHT; tick(); fixture.detectChanges(); tick(); button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; button.click(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(4); button.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(5); button.click(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(6); })); it('Should change panel expansion state on collapsed property setting', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button); panel.collapsed = false; tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button); panel.collapsed = true; tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button); panel.collapsed = false; tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button); })); it('Should change panel expansion state using API methods', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; let timesCollapsed = 0; let timesExpanded = 0; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.expand(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.collapse(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.expand(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.collapse(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); })); it('Should change panel expansion state using toggle method', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; let timesCollapsed = 0; let timesExpanded = 0; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.toggle(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.toggle(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.toggle(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.toggle(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); })); it('Should change panel expansion state on key interaction', fakeAsync(() => { const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' }); const spaceEvent = new KeyboardEvent('keydown', { key: 'Space' }); const arrowUpEvent = new KeyboardEvent('keydown', { key: 'ArrowUp', altKey: true }); const arrowDownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', altKey: true }); const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const header = fixture.componentInstance.header; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; let timesCollapsed = 0; let timesExpanded = 0; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); spyOn(header.interaction, 'emit').and.callThrough(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panelHeader.dispatchEvent(enterEvent); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(1); panelHeader.dispatchEvent(spaceEvent); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(2); panelHeader.dispatchEvent(enterEvent); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(3); panelHeader.dispatchEvent(spaceEvent); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(4); panelHeader.dispatchEvent(arrowUpEvent); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(5); panelHeader.dispatchEvent(arrowDownEvent); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(6); panelHeader.dispatchEvent(arrowDownEvent); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(7); panelHeader.dispatchEvent(arrowUpEvent); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(8); // disabled interaction const headerSub = header.interaction.subscribe((event) => { event.cancel = true; }); // currently collapsed expect(panel.collapsed).toEqual(true); // cancel openening panelHeader.dispatchEvent(arrowDownEvent); fixture.detectChanges(); tick(); // do not iterate timesExpanded verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(9); // open through API panel.expand(); timesExpanded++; tick(); fixture.detectChanges(); // currently expanded expect(panel.collapsed).toEqual(false); // cancel closing panelHeader.dispatchEvent(arrowUpEvent); fixture.detectChanges(); tick(); // do not iterate timesCollapsed verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(10); headerSub.unsubscribe(); })); it('Should change panel expansion when using different methods', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const header = fixture.componentInstance.header; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; let timesCollapsed = 0; let timesExpanded = 0; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); spyOn(header.interaction, 'emit'); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.expand(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panelHeader.click(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(1); button.click(); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(2); panelHeader.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(3); panel.collapsed = false; tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panel.toggle(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panelHeader.dispatchEvent(new KeyboardEvent('keydown', { key: 'Space' })); tick(); fixture.detectChanges(); tick(); timesExpanded++; verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(4); panel.collapse(); tick(); fixture.detectChanges(); tick(); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); })); it('Should not be interactable when disabled', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const header = fixture.componentInstance.header; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const button = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_ICON) as HTMLElement; const headerButton = panelHeader.querySelector('div [role = \'button\']'); let timesCollapsed = 0; const timesExpanded = 1; spyOn(panel.contentCollapsed, 'emit').and.callThrough(); spyOn(panel.contentExpanded, 'emit').and.callThrough(); spyOn(header.interaction, 'emit'); panel.expand(); tick(); fixture.detectChanges(); tick(); expect(headerButton.getAttribute('aria-disabled')).toMatch('false'); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); header.disabled = true; tick(); fixture.detectChanges(); tick(); expect(headerButton.getAttribute('aria-disabled')).toMatch('true'); panelHeader.click(); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(0); button.click(); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(0); panelHeader.dispatchEvent(new KeyboardEvent('keydown', { key: 'Space' })); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(0); panel.toggle(); tick(); fixture.detectChanges(); tick(); expect(headerButton.getAttribute('aria-disabled')).toMatch('true'); timesCollapsed++; verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); panelHeader.click(); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(0); button.click(); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(0); panelHeader.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); tick(); fixture.detectChanges(); tick(); verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded); expect(header.interaction.emit).toHaveBeenCalledTimes(0); header.disabled = false; tick(); fixture.detectChanges(); tick(); expect(headerButton.getAttribute('aria-disabled')).toMatch('false'); })); it('Should display expand/collapse button according to its position', () => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const header = fixture.componentInstance.header; const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const headerButton = panelHeader.querySelector('div [role = \'button\']'); expect(header.iconPosition).toEqual('left'); expect(headerButton.children[0].className).toEqual(CSS_CLASS_PANEL_TITLE_WRAPPER); expect(headerButton.children[1].className).toEqual(CSS_CLASS_HEADER_ICON_START); expect(headerButton.children[1].getBoundingClientRect().left). toBeLessThan(headerButton.children[0].getBoundingClientRect().left); header.iconPosition = ExpansionPanelHeaderIconPosition.NONE; fixture.detectChanges(); expect(header.iconPosition).toEqual('none'); expect(headerButton.children[1].className).toEqual(CSS_CLASS_HEADER_ICON_NONE); header.iconPosition = ExpansionPanelHeaderIconPosition.RIGHT; fixture.detectChanges(); expect(header.iconPosition).toEqual('right'); expect(headerButton.children[0].className).toEqual(CSS_CLASS_PANEL_TITLE_WRAPPER); expect(headerButton.children[1].className).toEqual(CSS_CLASS_HEADER_ICON_END); expect(headerButton.children[0].getBoundingClientRect().left). toBeLessThan(headerButton.children[1].getBoundingClientRect().left); header.iconPosition = ExpansionPanelHeaderIconPosition.NONE; fixture.detectChanges(); expect(header.iconPosition).toEqual('none'); expect(headerButton.children[1].className).toEqual(CSS_CLASS_HEADER_ICON_NONE); header.iconPosition = ExpansionPanelHeaderIconPosition.LEFT; fixture.detectChanges(); expect(header.iconPosition).toEqual('left'); expect(headerButton.children[0].className).toEqual(CSS_CLASS_PANEL_TITLE_WRAPPER); expect(headerButton.children[1].className).toEqual(CSS_CLASS_HEADER_ICON_START); expect(headerButton.children[1].getBoundingClientRect().left). toBeLessThan(headerButton.children[0].getBoundingClientRect().left); }); it('Should override the default icon when an icon template is passed', () => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const header = fixture.componentInstance.header; const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const headerButton = panelHeader.querySelector('div [role = \'button\']'); header.iconPosition = ExpansionPanelHeaderIconPosition.LEFT; fixture.detectChanges(); // Buttons are wrapper in wrapper div to hold positioning class const iconContainer = headerButton.children[1]; const titleContainer = headerButton.children[0]; expect(headerButton.children.length).toEqual(2); expect(iconContainer.firstElementChild.nodeName).toEqual('IGX-ICON'); expect(titleContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-TITLE'); expect(header.iconRef).not.toBe(null); expect(header.iconRef.nativeElement).toEqual(iconContainer.firstElementChild); fixture.componentInstance.customIcon = true; fixture.detectChanges(); expect(iconContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-ICON'); expect(titleContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-TITLE'); expect(header.iconRef).not.toBe(null); expect(header.iconRef.nativeElement).toEqual(iconContainer.firstElementChild); fixture.componentInstance.header.iconPosition = ExpansionPanelHeaderIconPosition.NONE; fixture.detectChanges(); expect(header.iconRef).toEqual(null); fixture.componentInstance.customIcon = false; fixture.detectChanges(); expect(header.iconRef).toEqual(null); fixture.componentInstance.header.iconPosition = ExpansionPanelHeaderIconPosition.LEFT; fixture.detectChanges(); expect(header.iconRef).not.toBe(null); expect(iconContainer.firstElementChild.nodeName).toEqual('IGX-ICON'); expect(titleContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-TITLE'); }); it('Should properly appy positioning classes to icon', () => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const header = fixture.componentInstance.header; const panelHeader = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER) as HTMLElement; const headerButton = panelHeader.querySelector('div [role = \'button\']'); header.iconPosition = ExpansionPanelHeaderIconPosition.LEFT; fixture.detectChanges(); expect(headerButton.children[1].classList).toContain(IconPositionClass.LEFT); header.iconPosition = ExpansionPanelHeaderIconPosition.RIGHT; fixture.detectChanges(); expect(headerButton.children[1].classList).toContain(IconPositionClass.RIGHT); header.iconPosition = ExpansionPanelHeaderIconPosition.NONE; fixture.detectChanges(); expect(headerButton.children[1].classList).toContain(IconPositionClass.NONE); }); it('Should not call animate method when `collapse` is called on a collapsed panel', () => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; const animationSpy = spyOn(panel, 'playCloseAnimation'); panel.collapse(); expect(animationSpy).not.toHaveBeenCalled(); }); it('Should not call animate method when `expand` is called on an expanded panel', () => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; const animationSpy = spyOn(panel, 'playOpenAnimation'); panel.collapse(); expect(animationSpy).not.toHaveBeenCalled(); }); }); describe('Aria tests', () => { // configureTestSuite(); it('Should properly apply default aria properties', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelElement = fixture.debugElement.query(By.css('igx-expansion-panel')).nativeElement; const header = fixture.componentInstance.header; const headerElement = header.elementRef.nativeElement; const title = fixture.componentInstance.expansionPanel.header; fixture.detectChanges(); // IgxExpansionPanelHeaderComponent host expect(headerElement.getAttribute('aria-level')).toEqual('3'); expect(headerElement.getAttribute('role')).toEqual('heading'); // Body of IgxExpansionPanelComponent panel.expand(); tick(); fixture.detectChanges(); tick(); expect(panelElement.lastElementChild.getAttribute('role')).toEqual('region'); expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(title.id); // Button of IgxExpansionPanelHeaderComponent expect(headerElement.lastElementChild.getAttribute('role')).toEqual('button'); expect(headerElement.firstElementChild.getAttribute('aria-controls')).toEqual(panel.id); expect(headerElement.firstElementChild.getAttribute('aria-expanded')).toEqual('true'); expect(headerElement.firstElementChild.getAttribute('aria-disabled')).toEqual('false'); // Disabled header.disabled = true; expect(headerElement.firstElementChild.getAttribute('aria-disabled')).toEqual('false'); })); it('Should properly apply aria properties if no header is shown', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); fixture.componentInstance.showBody = true; fixture.componentInstance.showHeader = false; fixture.componentInstance.showTitle = false; fixture.detectChanges(); const panel = fixture.componentInstance.panel; const panelElement = fixture.debugElement.query(By.css('igx-expansion-panel')).nativeElement; const header = fixture.componentInstance.header; expect(header).toBeFalsy(); const title = fixture.componentInstance.title; expect(title).toBeFalsy(); panel.expand(); tick(); fixture.detectChanges(); tick(); // Body of IgxExpansionPanelComponent expect(panelElement.lastElementChild.getAttribute('role')).toEqual('region'); expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(''); expect(panelElement.lastElementChild.getAttribute('aria-label')).toEqual(`${panelElement.id}-region`); panel.expand(); tick(); fixture.detectChanges(); })); it('Should update aria properties recording to external change', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelElement = fixture.debugElement.query(By.css('igx-expansion-panel')).nativeElement; const header = fixture.componentInstance.header; const headerElement = header.elementRef.nativeElement; const title = panel.header; panel.expand(); tick(); fixture.detectChanges(); tick(); // Body of IgxExpansionPanelComponent expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(title.id); // Button of IgxExpansionPanelHeaderComponent expect(headerElement.firstElementChild.getAttribute('aria-controls')).toEqual(panel.id); panel.id = 'example-test-panel-id'; tick(); fixture.detectChanges(); tick(); expect(headerElement.firstElementChild.getAttribute('aria-controls')).toEqual('example-test-panel-id'); // title.id = 'example-title-id'; // not probable // tick(); // fixture.detectChanges(); // tick(); // expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual('example-title-id'); })); it('Should properly label the control region', fakeAsync(() => { const fixture = TestBed.createComponent(IgxExpansionPanelListComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelElement = fixture.debugElement.query(By.css('igx-expansion-panel')).nativeElement; const title = fixture.componentInstance.expansionPanel.header; panel.expand(); tick(); fixture.detectChanges(); tick(); expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(title.id); expect(panelElement.lastElementChild.getAttribute('aria-label')).toEqual(`${panel.id}-region`); // fixture.componentInstance.showTitle = false; // tick(); // fixture.detectChanges(); // tick(); // expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(''); // expect(panelElement.lastElementChild.getAttribute('aria-label')).toEqual(`${panel.id}-region`); panel.body.label = 'custom-test-label'; tick(); fixture.detectChanges(); expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(title.id); expect(panelElement.lastElementChild.getAttribute('aria-label')).toEqual(`custom-test-label`); panel.body.label = ''; tick(); fixture.detectChanges(); expect(panelElement.lastElementChild.getAttribute('aria-labelledby')).toEqual(title.id); expect(panelElement.lastElementChild.getAttribute('aria-label')).toEqual(`${panel.id}-region`); })); }); describe('Rendering tests: ', () => { // configureTestSuite(); it('Should apply all appropriate classes on combo initialization', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelSampleComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); expect(panelContainer).not.toBeNull(); expect(panelContainer.attributes.getNamedItem('id').nodeValue).toEqual(panel.id); expect(panelContainer.childElementCount).toEqual(1); const header = panelContainer.children[0]; expect(header.attributes.getNamedItem('class').nodeValue).toContain(CSS_CLASS_PANEL_HEADER); expect(header.attributes.getNamedItem('role').nodeValue).toEqual('heading'); expect(header.attributes.getNamedItem('aria-level').nodeValue).toEqual('3'); expect(header.childElementCount).toEqual(1); const headerBtn = header.children[0]; expect(headerBtn.attributes.getNamedItem('role').nodeValue).toEqual('button'); expect(headerBtn.attributes.getNamedItem('tabindex').nodeValue).toEqual('0'); expect(headerBtn.attributes.getNamedItem('aria-disabled').nodeValue).toEqual('false'); expect(headerBtn.attributes.getNamedItem('aria-expanded').nodeValue).toEqual('false'); expect(headerBtn.attributes.getNamedItem('aria-controls').nodeValue).toEqual(panel.id); expect(headerBtn.childElementCount).toEqual(2); // 2 Children - Title Wrapper + Icon const icon = headerBtn.children[1].firstElementChild; // Icon is wrapped in div expect(headerBtn.children[1].attributes.getNamedItem('class').nodeValue).toContain('igx-expansion-panel__header-icon--start'); expect(icon.attributes.getNamedItem('class').nodeValue).toContain('material-icons igx-icon'); // expect(icon.attributes.getNamedItem('ng-reflect-icon-name').nodeValue).toEqual('expand_more'); expect(icon.attributes.getNamedItem('aria-hidden').nodeValue).toEqual('true'); expect(icon.childElementCount).toEqual(0); const title = headerBtn.firstElementChild.firstElementChild; // expect(title.attributes.getNamedItem('id').nodeValue).toEqual('igx-expansion-panel-header-title-0'); expect(title.textContent).toEqual('Example Title'); const description = headerBtn.firstElementChild.lastElementChild; expect(description.textContent).toEqual('Example Description'); // expect(bodyWrapper.attributes.getNamedItem('aria-labelledby').nodeValue).toEqual('igx-expansion-panel-header-title-0'); expect(panelContainer.childElementCount).toEqual(1); header.click(); tick(); fixture.detectChanges(); tick(); const bodyWrapper = panelContainer.children[1]; expect(bodyWrapper.attributes.getNamedItem('role').nodeValue).toEqual('region'); expect(bodyWrapper.attributes.getNamedItem('aria-label').nodeValue).toEqual(panel.id + '-region'); expect(bodyWrapper.childElementCount).toEqual(0); expect(bodyWrapper.textContent.trim()).toEqual('Example body'); })); it('Should apply all appropriate classes on initialization_grid content', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelGridComponent); fixture.detectChanges(); const panel = fixture.componentInstance.expansionPanel; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); expect(panelContainer).not.toBeNull(); expect(panelContainer.attributes.getNamedItem('id').nodeValue).toEqual(panel.id); expect(panelContainer.childElementCount).toEqual(1); const header = panelContainer.children[0]; expect(header.attributes.getNamedItem('class').nodeValue).toContain(CSS_CLASS_PANEL_HEADER); expect(header.attributes.getNamedItem('role').nodeValue).toEqual('heading'); expect(header.attributes.getNamedItem('aria-level').nodeValue).toEqual('3'); expect(header.childElementCount).toEqual(1); const headerBtn = header.children[0]; expect(headerBtn.attributes.getNamedItem('role').nodeValue).toEqual('button'); expect(headerBtn.attributes.getNamedItem('tabindex').nodeValue).toEqual('0'); expect(headerBtn.attributes.getNamedItem('aria-disabled').nodeValue).toEqual('false'); expect(headerBtn.attributes.getNamedItem('aria-expanded').nodeValue).toEqual('false'); expect(headerBtn.attributes.getNamedItem('aria-controls').nodeValue).toEqual(panel.id); expect(headerBtn.childElementCount).toEqual(2); // 2 Children - Title Wrapper + Icon const icon = headerBtn.children[1].firstElementChild; // Icon is wrapped in div expect(headerBtn.children[1].attributes.getNamedItem('class').nodeValue).toContain('igx-expansion-panel__header-icon--start'); expect(icon.attributes.getNamedItem('class').nodeValue).toContain('material-icons igx-icon'); // expect(icon.attributes.getNamedItem('ng-reflect-icon-name').nodeValue).toEqual('expand_more'); expect(icon.attributes.getNamedItem('aria-hidden').nodeValue).toEqual('true'); expect(icon.childElementCount).toEqual(0); const title = headerBtn.firstElementChild.firstElementChild; // expect(title.attributes.getNamedItem('id').nodeValue).toEqual('igx-expansion-panel-header-title-0'); expect(title.textContent).toEqual('Product orders'); const description = headerBtn.firstElementChild.lastElementChild; expect(description.textContent).toEqual('Product orders details'); // expect(bodyWrapper.attributes.getNamedItem('aria-labelledby').nodeValue).toEqual('igx-expansion-panel-header-title-0'); expect(panelContainer.childElementCount).toEqual(1); header.click(); tick(); fixture.detectChanges(); tick(); const bodyWrapper = panelContainer.children[1]; expect(bodyWrapper.attributes.getNamedItem('role').nodeValue).toEqual('region'); expect(bodyWrapper.attributes.getNamedItem('aria-label').nodeValue).toEqual(panel.id + '-region'); expect(bodyWrapper.firstElementChild.nodeName).toEqual('IGX-GRID'); const grid = bodyWrapper.firstElementChild; // wrapping div expect(grid.attributes.getNamedItem('class').nodeValue).toContain(CSS_CLASS_GRID); expect(grid.attributes.getNamedItem('role').nodeValue).toEqual('grid'); expect(grid.attributes.getNamedItem('id').nodeValue).toEqual(fixture.componentInstance.grid1.id); expect(grid.attributes.getNamedItem('tabindex').nodeValue).toEqual('0'); expect(grid.attributes.getNamedItem('ng-reflect-auto-generate').nodeValue).toEqual('true'); expect(grid.attributes.getNamedItem('ng-reflect-width').nodeValue).toEqual(fixture.componentInstance.width); expect(grid.attributes.getNamedItem('ng-reflect-height').nodeValue).toEqual(fixture.componentInstance.height); expect(grid.childElementCount).toEqual(6); })); it('Should apply all appropriate classes on combo initialization_image + text content', fakeAsync(() => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelImageComponent); fixture.detectChanges(); const panel = fixture.componentInstance.panel; const panelContainer = fixture.nativeElement.querySelector('.' + CSS_CLASS_EXPANSION_PANEL); expect(panelContainer).not.toBeNull(); expect(panelContainer.attributes.getNamedItem('id').nodeValue).toEqual(panel.id); expect(panelContainer.childElementCount).toEqual(1); const header = panelContainer.children[0]; expect(header.attributes.getNamedItem('class').nodeValue).toContain(CSS_CLASS_PANEL_HEADER); expect(header.attributes.getNamedItem('role').nodeValue).toEqual('heading'); expect(header.attributes.getNamedItem('aria-level').nodeValue).toEqual('3'); expect(header.childElementCount).toEqual(1); const headerBtn = header.children[0]; expect(headerBtn.attributes.getNamedItem('role').nodeValue).toEqual('button'); expect(headerBtn.attributes.getNamedItem('tabindex').nodeValue).toEqual('0'); expect(headerBtn.attributes.getNamedItem('aria-disabled').nodeValue).toEqual('false'); expect(headerBtn.attributes.getNamedItem('aria-expanded').nodeValue).toEqual('false'); expect(headerBtn.attributes.getNamedItem('aria-controls').nodeValue).toEqual(panel.id); expect(headerBtn.childElementCount).toEqual(2); // 2 Children - Title Wrapper + Icon const icon = headerBtn.children[1].firstElementChild; // Icon is wrapped in div; Icon is the second element expect(headerBtn.children[1].attributes.getNamedItem('class').nodeValue).toContain('igx-expansion-panel__header-icon--start'); expect(icon.attributes.getNamedItem('class').nodeValue).toContain('material-icons igx-icon'); // expect(icon.attributes.getNamedItem('ng-reflect-icon-name').nodeValue).toEqual('expand_more'); expect(icon.attributes.getNamedItem('aria-hidden').nodeValue).toEqual('true'); expect(icon.childElementCount).toEqual(0); const title = headerBtn.firstElementChild.firstElementChild; // expect(title.attributes.getNamedItem('id').nodeValue).toEqual('igx-expansion-panel-header-title-0'); expect(title.textContent).toEqual('Frogs'); const description = headerBtn.firstElementChild.lastElementChild; expect(description.textContent).toEqual('Frog description'); // expect(bodyWrapper.attributes.getNamedItem('aria-labelledby').nodeValue).toEqual('igx-expansion-panel-header-title-0'); expect(panelContainer.childElementCount).toEqual(1); header.click(); tick(); fixture.detectChanges(); tick(); const bodyWrapper = panelContainer.children[1]; expect(bodyWrapper.attributes.getNamedItem('role').nodeValue).toEqual('region'); expect(bodyWrapper.attributes.getNamedItem('aria-label').nodeValue).toEqual(panel.id + '-region'); expect(bodyWrapper.childElementCount).toEqual(1); const textWrapper = bodyWrapper.firstElementChild; // wrapping div expect(textWrapper.attributes.getNamedItem('class').nodeValue).toContain('sample-wrapper'); expect(textWrapper.childElementCount).toEqual(1); const image = textWrapper.children[0] as HTMLElement; expect(image.tagName).toEqual('IMG'); expect (textWrapper.textContent.trim()).toEqual(fixture.componentInstance.text); })); it('Should display tooltip with the title and description text content', () => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelTooltipComponent); fixture.detectChanges(); const headerTitle = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER_TITLE); const headerDescription = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER_DESCRIPTION); const headerTitleTooltip = headerTitle.getAttribute('title'); const headerDescriptionTooltip = headerDescription.getAttribute('title'); expect(headerTitleTooltip).toEqual(headerTitle.textContent.trim()); expect(headerDescriptionTooltip).toEqual(headerDescription.textContent.trim()); }); it('Should display tooltip with the attr.title text content', () => { const fixture: ComponentFixture = TestBed.createComponent(IgxExpansionPanelTooltipComponent); fixture.componentInstance.titleTooltip = 'Custom Title Tooltip'; fixture.componentInstance.descriptionTooltip = 'Custom Description Tooltip'; fixture.detectChanges(); const headerTitle = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER_TITLE); const headerDescription = fixture.nativeElement.querySelector('.' + CSS_CLASS_PANEL_HEADER_DESCRIPTION); const headerTitleTooltip = headerTitle.getAttribute('title'); const headerDescriptionTooltip = headerDescription.getAttribute('title'); expect(headerTitleTooltip).toEqual('Custom Title Tooltip'); expect(headerDescriptionTooltip).toEqual('Custom Description Tooltip'); }); }); }); @Component({ template: ` Product orders Product orders details `, standalone: true, imports: [IgxExpansionPanelComponent, IgxExpansionPanelHeaderComponent, IgxExpansionPanelBodyComponent, IgxGridComponent, IgxExpansionPanelTitleDirective, IgxExpansionPanelDescriptionDirective] }) export class IgxExpansionPanelGridComponent { @ViewChild('expansionPanel', { read: IgxExpansionPanelComponent, static: true }) public expansionPanel: IgxExpansionPanelComponent; @ViewChild('grid1', { read: IgxGridComponent, static: true }) public grid1: IgxGridComponent; public width = '800px'; public height = '600px'; public data = [ { ProductID: 1, ProductName: 'Chai', InStock: true, UnitsInStock: 2760, OrderDate: '2005-03-21' }, { ProductID: 2, ProductName: 'Aniseed Syrup', InStock: false, UnitsInStock: 198, OrderDate: '2008-01-15' }, { ProductID: 3, ProductName: 'Chef Antons Cajun Seasoning', InStock: true, UnitsInStock: 52, OrderDate: '2010-11-20' }, { ProductID: 4, ProductName: 'Grandmas Boysenberry Spread', InStock: false, UnitsInStock: 0, OrderDate: '2007-10-11' }, { ProductID: 5, ProductName: 'Uncle Bobs Dried Pears', InStock: false, UnitsInStock: 0, OrderDate: '2001-07-27' }, { ProductID: 6, ProductName: 'Northwoods Cranberry Sauce', InStock: true, UnitsInStock: 1098, OrderDate: '1990-05-17' }, { ProductID: 7, ProductName: 'Queso Cabrales', InStock: false, UnitsInStock: 0, OrderDate: '2005-03-03' }, { ProductID: 8, ProductName: 'Tofu', InStock: true, UnitsInStock: 7898, OrderDate: '2017-09-09' }, { ProductID: 9, ProductName: 'Teatime Chocolate Biscuits', InStock: true, UnitsInStock: 6998, OrderDate: '2025-12-25' }, { ProductID: 10, ProductName: 'Chocolate', InStock: true, UnitsInStock: 20000, OrderDate: '2018-03-01' } ]; } @Component({ template: `
Product List Products Product 1 Product 2 Product 3 Product 4 Product 5
`, standalone: true, imports: [IgxExpansionPanelComponent, IgxExpansionPanelHeaderComponent, IgxExpansionPanelBodyComponent, IgxListComponent, IgxListItemComponent, IgxExpansionPanelTitleDirective, IgxExpansionPanelDescriptionDirective] }) export class IgxExpansionPanelListComponent { @ViewChild(IgxExpansionPanelHeaderComponent, { read: IgxExpansionPanelHeaderComponent, static: true }) public header: IgxExpansionPanelHeaderComponent; @ViewChild(IgxExpansionPanelComponent, { read: IgxExpansionPanelComponent, static: true }) public expansionPanel: IgxExpansionPanelComponent; } @Component({ template: ` Example Title Example Description TEST_ICON Example body `, standalone: true, imports: [IgxExpansionPanelComponent, IgxExpansionPanelHeaderComponent, IgxExpansionPanelBodyComponent, IgxExpansionPanelTitleDirective, IgxExpansionPanelDescriptionDirective, IgxExpansionPanelIconDirective, NgIf] }) export class IgxExpansionPanelSampleComponent { @ViewChild(IgxExpansionPanelHeaderComponent, { read: IgxExpansionPanelHeaderComponent }) public header: IgxExpansionPanelHeaderComponent; @ViewChild(IgxExpansionPanelComponent, { read: IgxExpansionPanelComponent, static: true }) public panel: IgxExpansionPanelComponent; @ViewChild(IgxExpansionPanelTitleDirective, { read: IgxExpansionPanelTitleDirective }) public title: IgxExpansionPanelTitleDirective; public disabled = false; public collapsed = true; public showTitle = true; public showBody = true; public showHeader = true; public customIcon = false; public handleExpanded() { } public handleCollapsed() { } public handleInterraction() { } } @Component({ template: ` Frogs Frog description

{{text}}

`, standalone: true, imports: [IgxExpansionPanelComponent, IgxExpansionPanelHeaderComponent, IgxExpansionPanelBodyComponent, IgxExpansionPanelTitleDirective, IgxExpansionPanelDescriptionDirective] }) export class IgxExpansionPanelImageComponent { @ViewChild(IgxExpansionPanelHeaderComponent, { read: IgxExpansionPanelHeaderComponent, static: true }) public header: IgxExpansionPanelHeaderComponent; @ViewChild(IgxExpansionPanelComponent, { read: IgxExpansionPanelComponent, static: true }) public panel: IgxExpansionPanelComponent; public imagePath = 'http://milewalk.com/wp-content/uploads/2016/01/My-2-Morning-Tricks-to-Eating-the-Frog.jpg'; // eslint-disable-next-line max-len public text = 'A frog is any member of a diverse and largely carnivorous group of short-bodied, tailless amphibians composing the order Anura. The oldest fossil \"proto-frog\" appeared in the early Triassic of Madagascar, but molecular clock dating suggests their origins may extend further back to the Permian, 265 million years ago. Frogs are widely distributed, ranging from the tropics to subarctic regions, but the greatest concentration of species diversity is in tropical rainforests. There are approximately 4,800 recorded species, accounting for over 85% of extant amphibian species. They are also one of the five most diverse vertebrate orders. The body plan of an adult frog is generally characterized by a stout body, protruding eyes, cleft tongue, limbs folded underneath, and the absence of a tail. Besides living in fresh water and on dry land, the adults of some species are adapted for living underground or in trees. The skins of frogs are glandular, with secretions ranging from distasteful to toxic. Warty species of frog tend to be called toads but the distinction between frogs and toads is based on informal naming conventions concentrating on the warts rather than taxonomy or evolutionary history.'; } @Component({ template: ` Example Title Example Description Example Body `, standalone: true, imports: [IGX_EXPANSION_PANEL_DIRECTIVES] }) export class IgxExpansionPanelTooltipComponent { public titleTooltip = ''; public descriptionTooltip = ''; }