diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts index 5a8c721f003..18d163a4c41 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts @@ -2359,7 +2359,6 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements this._keydownListener = this.keydownHandler.bind(this); this.nativeElement.addEventListener('keydown', this._keydownListener); }); - this.calculateGridWidth(); this.initPinning(); this.calculateGridSizes(); this.onDensityChanged.pipe(takeUntil(this.destroy$)).subscribe(() => { @@ -2374,20 +2373,19 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements // In some rare cases we get the AfterViewInit before the grid is added to the DOM // and as a result we get 0 width and can't size ourselves properly. // In order to prevent that add a mutation observer that watches if we have been added. - if (!this.calcWidth && this._width !== undefined) { + if (!this.isAttachedToDom) { const config = { childList: true, subtree: true }; let observer: MutationObserver = null; const callback = (mutationsList) => { mutationsList.forEach((mutation) => { if (mutation.type === 'childList') { - const addedNodes = new Array(...mutation.addedNodes); - addedNodes.forEach((node) => { - const added = this.checkIfGridIsAdded(node); + for (let i = 0; i < mutation.addedNodes.length; i++) { + const added = this.checkIfGridIsAdded(mutation.addedNodes[i]); if (added) { - this.calculateGridWidth(); + this.reflow(); observer.disconnect(); } - }); + } } }); }; @@ -3570,9 +3568,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements /** * @hidden + * Sets this._height */ protected _derivePossibleHeight() { - if ((this._height && this._height.indexOf('%') === -1) || !this._height) { + if ((this._height && this._height.indexOf('%') === -1) || !this._height || !this.isAttachedToDom) { return; } if (!this.nativeElement.parentNode || !this.nativeElement.parentNode.clientHeight) { @@ -3582,18 +3581,17 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements const parentHeight = this.nativeElement.parentNode.getBoundingClientRect().height; this._height = this.rowBasedHeight <= parentHeight ? null : this._height; } - this.calculateGridHeight(); - this.cdr.detectChanges(); } /** * @hidden + * Sets columns defaultWidth property */ protected _derivePossibleWidth() { if (!this._columnWidthSetByUser) { this._columnWidth = this.getPossibleColumnWidth(); this.columnList.forEach((column: IgxColumnComponent) => { - column.defaultWidth = this.columnWidth; + column.defaultWidth = this._columnWidth; }); } } @@ -3609,10 +3607,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements /** * @hidden + * Sets TBODY height i.e. this.calcHeight */ protected calculateGridHeight() { - const computed = this.document.defaultView.getComputedStyle(this.nativeElement); - + this._derivePossibleHeight(); // TODO: Calculate based on grid density if (this.maxLevelHeaderDepth) { this.theadRow.nativeElement.style.height = `${(this.maxLevelHeaderDepth + 1) * this.defaultRowHeight + @@ -3627,56 +3625,77 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements return; } + if (this.hasSummarizedColumns && this.rootSummariesEnabled) { + this.summariesHeight = this.summaryService.calcMaxSummaryHeight(); + } + + this.calcHeight = this._calculateGridBodyHeight(); + } + + /** + * @hidden + */ + protected getGroupAreaHeight(): number { + return 0; + } + + /** + * @hidden + */ + protected getToolbarHeight(): number { let toolbarHeight = 0; if (this.showToolbar && this.toolbarHtml != null) { toolbarHeight = this.toolbarHtml.nativeElement.firstElementChild ? this.toolbarHtml.nativeElement.offsetHeight : 0; } + return toolbarHeight; + } + /** + * @hidden + */ + protected getPagingHeight(): number { let pagingHeight = 0; if (this.paging && this.paginator) { pagingHeight = this.paginator.nativeElement.firstElementChild ? this.paginator.nativeElement.offsetHeight : 0; } + return pagingHeight; + } - if (this.hasSummarizedColumns && this.rootSummariesEnabled) { - this.summariesHeight = this.summaryService.calcMaxSummaryHeight(); - } + /** + * @hidden + */ + protected _calculateGridBodyHeight() { + const footerBordersAndScrollbars = this.tfoot.nativeElement.offsetHeight - + this.tfoot.nativeElement.clientHeight; + const computed = this.document.defaultView.getComputedStyle(this.nativeElement); + const toolbarHeight = this.getToolbarHeight(); + const pagingHeight = this.getPagingHeight(); const groupAreaHeight = this.getGroupAreaHeight(); + let gridHeight; + + if (!this.isAttachedToDom) { + return null; + } if (this._height && this._height.indexOf('%') !== -1) { /*height in %*/ - this.calcHeight = this._calculateGridBodyHeight( - parseInt(computed.getPropertyValue('height'), 10), toolbarHeight, pagingHeight, groupAreaHeight); + gridHeight = parseInt(computed.getPropertyValue('height'), 10); } else { - this.calcHeight = this._calculateGridBodyHeight( - parseInt(this._height, 10), toolbarHeight, pagingHeight, groupAreaHeight); + gridHeight = parseInt(this._height, 10); } - } - - /** - * @hidden - */ - protected getGroupAreaHeight(): number { - return 0; - } + const height = Math.abs(gridHeight - toolbarHeight - + this.theadRow.nativeElement.offsetHeight - + this.summariesHeight - pagingHeight - groupAreaHeight - + footerBordersAndScrollbars - + this.scr.nativeElement.clientHeight); - /** - * @hidden - */ - protected _calculateGridBodyHeight(gridHeight: number, - toolbarHeight: number, pagingHeight: number, groupAreaHeight: number) { - const footerBordersAndScrollbars = this.tfoot.nativeElement.offsetHeight - - this.tfoot.nativeElement.clientHeight; - if (isNaN(gridHeight)) { + if (height === 0 || isNaN(gridHeight)) { return this.defaultTargetBodyHeight; } - return Math.abs(gridHeight - toolbarHeight - - this.theadRow.nativeElement.offsetHeight - - this.summariesHeight - pagingHeight - groupAreaHeight - - footerBordersAndScrollbars - - this.scr.nativeElement.clientHeight); + return height; } /** @@ -3714,6 +3733,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements /** * @hidden + * Sets grid width i.e. this.calcWidth */ protected calculateGridWidth() { let width; @@ -4757,4 +4777,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements return rowData.summaries && (rowData.summaries instanceof Map); } + /** + * @hidden + */ + protected get isAttachedToDom(): boolean { + return this.document.body.contains(this.nativeElement); + } } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 1d78cf33c55..6e14647fe4c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -39,6 +39,8 @@ describe('IgxGrid Component Tests', () => { const ROW_EDITING_OUTLET_CLASS = '.igx-grid__row-editing-outlet'; const BANNER = 'igx-banner'; const EDIT_OVERLAY_CONTENT = 'igx-overlay__content'; + const TBODY_CLASS = '.igx-grid__tbody'; + const THEAD_CLASS = '.igx-grid__thead'; describe('IgxGrid - input properties', () => { configureTestSuite(); @@ -145,8 +147,8 @@ describe('IgxGrid Component Tests', () => { fix.detectChanges(); const grid = fix.componentInstance.grid; - const gridBody = fix.debugElement.query(By.css('.igx-grid__tbody')); - const gridHeader = fix.debugElement.query(By.css('.igx-grid__thead')); + const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); + const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS)); const gridFooter = fix.debugElement.query(By.css('.igx-grid__tfoot')); const gridScroll = fix.debugElement.query(By.css('.igx-grid__scroll')); let gridBodyHeight; @@ -220,8 +222,8 @@ describe('IgxGrid Component Tests', () => { fix.detectChanges(); const grid = fix.componentInstance.grid; - const gridBody = fix.debugElement.query(By.css('.igx-grid__tbody')); - const gridHeader = fix.debugElement.query(By.css('.igx-grid__thead')); + const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); + const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS)); expect(window.getComputedStyle(gridBody.children[0].nativeElement).width).toEqual( window.getComputedStyle(gridHeader.children[0].nativeElement).width @@ -235,8 +237,8 @@ describe('IgxGrid Component Tests', () => { fixture.detectChanges(); const grid = fixture.componentInstance.grid; - const headerHight = fixture.debugElement.query(By.css('.igx-grid__thead')).query(By.css('.igx-grid__tr')).nativeElement; - const rowHeight = fixture.debugElement.query(By.css('.igx-grid__tbody')).query(By.css('.igx-grid__tr')).nativeElement; + const headerHight = fixture.debugElement.query(By.css(THEAD_CLASS)).query(By.css('.igx-grid__tr')).nativeElement; + const rowHeight = fixture.debugElement.query(By.css(TBODY_CLASS)).query(By.css('.igx-grid__tr')).nativeElement; const summaryItemHeigh = fixture.debugElement.query(By.css('.igx-grid__tfoot')) .query(By.css('.igx-grid-summary__item')).nativeElement; @@ -268,7 +270,7 @@ describe('IgxGrid Component Tests', () => { fixture.detectChanges(); const grid = fixture.componentInstance.grid; - const gridBody = fixture.debugElement.query(By.css('.igx-grid__tbody')); + const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); // Check for loaded rows in grid's container fixture.componentInstance.generateData(30); @@ -635,7 +637,7 @@ describe('IgxGrid Component Tests', () => { it('should render 10 records if height is unset and parent container\'s height is unset', () => { const fix = TestBed.createComponent(IgxGridWrappedInContComponent); fix.detectChanges(); - const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).not.toBeNull(); expect(parseInt(defaultHeight, 10)).toBeGreaterThan(400); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); @@ -647,7 +649,7 @@ describe('IgxGrid Component Tests', () => { fix.componentInstance.grid.height = '700px'; tick(); fix.detectChanges(); - const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).not.toBeNull(); expect(parseInt(defaultHeight, 10)).toBeGreaterThan(400); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); @@ -661,7 +663,7 @@ describe('IgxGrid Component Tests', () => { fix.componentInstance.data = fix.componentInstance.data.slice(0, 5); tick(); fix.detectChanges(); - const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; expect(defaultHeight).not.toBeNull(); expect(parseInt(defaultHeight, 10)).toBeGreaterThan(200); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeFalsy(); @@ -676,11 +678,10 @@ describe('IgxGrid Component Tests', () => { fix.componentInstance.density = DisplayDensity.compact; tick(); fix.detectChanges(); - const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; const defaultHeightNum = parseInt(defaultHeight, 10); expect(defaultHeight).not.toBeNull(); - expect(defaultHeightNum).toBeGreaterThan(300); - expect(defaultHeightNum).toBeLessThan(330); + expect(defaultHeightNum).toBe(320); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); expect(fix.componentInstance.grid.rowList.length).toEqual(11); })); @@ -3214,21 +3215,82 @@ describe('IgxGrid Component Tests', () => { }).compileComponents(); })); - it('IgxTabs: should initialize a grid with correct width/height', fakeAsync(() => { + it('IgxTabs: should initialize a grid with correct width/height', async() => { const fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent); fix.detectChanges(); - - const grid = fix.componentInstance.grid; + const grid = fix.componentInstance.grid3; const tab = fix.componentInstance.tabs; tab.tabs.toArray()[2].select(); - tick(100); + await wait(100); fix.detectChanges(); - const gridHeader = fix.debugElement.query(By.css('.igx-grid__thead')); - const gridBody = fix.debugElement.query(By.css('.igx-grid__tbody')); - expect(parseInt(window.getComputedStyle(gridHeader.nativeElement).width, 10)).toBe(400); - expect(parseInt(window.getComputedStyle(gridBody.nativeElement).width, 10)).toBe(400); + await wait(100); + grid.cdr.detectChanges(); + const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS)); + const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); + const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); + expect(parseInt(window.getComputedStyle(gridHeader.nativeElement).width, 10)).toBe(600); + expect(headers.length).toBe(4); + expect(parseInt(window.getComputedStyle(gridBody.nativeElement).width, 10)).toBe(600); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(510); - })); + }); + + it('IgxTabs: should initialize a grid with correct width/height when there is no column width set', async () => { + const fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent); + fix.detectChanges(); + + const grid = fix.componentInstance.grid2; + const tab = fix.componentInstance.tabs; + tab.tabs.toArray()[1].select(); + await wait(100); + fix.detectChanges(); + await wait(100); + grid.cdr.detectChanges(); + const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); + expect(headers.length).toBe(4); + const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); + expect(parseInt(window.getComputedStyle(gridBody.nativeElement).width, 10)).toBe(500); + expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(230); + }); + + it('IgxTabs: should initialize a grid with correct height when paging and summaries are enabled', async () => { + const fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent); + fix.detectChanges(); + + const grid = fix.componentInstance.grid4; + const tab = fix.componentInstance.tabs; + tab.tabs.toArray()[3].select(); + await wait(100); + fix.detectChanges(); + await wait(100); + grid.cdr.detectChanges(); + const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); + const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); + const paging = fix.debugElement.query(By.css('.igx-paginator')); + const summaries = fix.debugElement.queryAll(By.css('.igx-grid-summary')); + expect(headers.length).toBe(4); + expect(summaries.length).toBe(4); + expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(133); + expect(parseInt(window.getComputedStyle(paging.nativeElement).height, 10)).toBe(47); + }); + + it('IgxTabs: should initialize a grid with correct height when height = 100%', async () => { + const fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent); + fix.detectChanges(); + + const grid = fix.componentInstance.grid5; + const tab = fix.componentInstance.tabs; + tab.tabs.toArray()[4].select(); + await wait(100); + fix.detectChanges(); + await wait(100); + grid.cdr.detectChanges(); + const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); + const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); + const paging = fix.debugElement.query(By.css('.igx-paginator')); + expect(headers.length).toBe(4); + expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(204); + expect(parseInt(window.getComputedStyle(paging.nativeElement).height, 10)).toBe(47); + }); }); }); @@ -3770,19 +3832,51 @@ export class IgxGridRowEditingWithFeaturesComponent extends DataParent { @Component({ template: ` -
+
This is Tab 1 content. - This is Tab 2 content. + + + + + + - - + - + > + + + + + + + + + + + + + @@ -3790,10 +3884,14 @@ export class IgxGridRowEditingWithFeaturesComponent extends DataParent { ` }) export class IgxGridInsideIgxTabsComponent { - @ViewChild(IgxGridComponent, { read: IgxGridComponent }) - public grid: IgxGridComponent; - + public grid2: IgxGridComponent; + @ViewChild(IgxGridComponent, { read: IgxGridComponent }) + public grid3: IgxGridComponent; + @ViewChild(IgxGridComponent, { read: IgxGridComponent }) + public grid4: IgxGridComponent; + @ViewChild(IgxGridComponent, { read: IgxGridComponent }) + public grid5: IgxGridComponent; @ViewChild(IgxTabsComponent, { read: IgxTabsComponent }) public tabs: IgxTabsComponent;