diff --git a/projects/igniteui-angular/package.json b/projects/igniteui-angular/package.json index d78eaa88cc7..0eddf69b446 100644 --- a/projects/igniteui-angular/package.json +++ b/projects/igniteui-angular/package.json @@ -68,7 +68,7 @@ "web-animations-js": "^2.3.1" }, "devDependencies": { - "igniteui-cli": "~3.1.0" + "igniteui-cli": "~3.2.0" }, "ng-update": { "migrations": "./migrations/migration-collection.json" diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 1d7f2a47a17..2b4d7b731d4 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -382,23 +382,7 @@ export class IgxGridCellComponent implements OnInit, AfterViewInit { @HostBinding('style.max-width') @HostBinding('style.flex-basis') get width() { - const hasVerticalScroll = !this.grid.verticalScrollContainer.dc.instance.notVirtual; - const colWidth = this.column.width; - const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1; - - if (colWidth && !isPercentageWidth) { - let cellWidth = this.isLastUnpinned && hasVerticalScroll && - (this.grid.unpinnedWidth - this.grid.totalWidth < 0) ? - parseInt(colWidth, 10) - 18 + '' : colWidth; - - if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) { - cellWidth += 'px'; - } - - return cellWidth; - } else { - return colWidth; - } + return this.column.getCellWidth(); } /** diff --git a/projects/igniteui-angular/src/lib/grids/column.component.ts b/projects/igniteui-angular/src/lib/grids/column.component.ts index fe7059c031e..0097d33553c 100644 --- a/projects/igniteui-angular/src/lib/grids/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/column.component.ts @@ -1198,6 +1198,31 @@ export class IgxColumnComponent implements AfterContentInit { } } + /** + *@hidden + */ + public getCellWidth() { + const hasVerticalScroll = !this.grid.verticalScrollContainer.dc.instance.notVirtual; + const colWidth = this.width; + const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1; + + if (colWidth && !isPercentageWidth) { + const unpinnedColumns = this.grid.unpinnedColumns; + const isLastUnpinned = unpinnedColumns[unpinnedColumns.length - 1] === this; + + let cellWidth = isLastUnpinned && hasVerticalScroll && + (this.grid.unpinnedWidth - this.grid.totalWidth < 0) ? + parseInt(colWidth, 10) - 18 + '' : colWidth; + + if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) { + cellWidth += 'px'; + } + + return cellWidth; + } else { + return colWidth; + } + } } 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 74fa509ebb0..6b8c0fa334d 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts @@ -2302,8 +2302,8 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements this.initColumns(this.columnList, (col: IgxColumnComponent) => this.onColumnInit.emit(col)); this.columnListDiffer.diff(this.columnList); - this._derivePossibleHeight(); this.markForCheck(); + this._derivePossibleHeight(); this.columnList.changes .pipe(takeUntil(this.destroy$)) @@ -3558,7 +3558,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements if ((this._height && this._height.indexOf('%') === -1) || !this._height) { return; } - if (!this.nativeElement.parentNode.clientHeight) { + if (!this.nativeElement.parentNode || !this.nativeElement.parentNode.clientHeight) { const viewPortHeight = document.documentElement.clientHeight; this._height = this.rowBasedHeight <= viewPortHeight ? null : viewPortHeight.toString(); } else { @@ -3692,24 +3692,32 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements * @hidden */ protected calculateGridWidth() { + let width; const computed = this.document.defaultView.getComputedStyle(this.nativeElement); + const el = this.document.getElementById(this.nativeElement.id); if (this._width && this._width.indexOf('%') !== -1) { /* width in %*/ - const width = computed.getPropertyValue('width').indexOf('%') === -1 ? - parseInt(computed.getPropertyValue('width'), 10) : - this.document.getElementById(this.nativeElement.id).offsetWidth; - - if (Number.isFinite(width) && width !== this.calcWidth) { - this.calcWidth = width; - - this.cdr.markForCheck(); - } + width = computed.getPropertyValue('width').indexOf('%') === -1 ? + parseInt(computed.getPropertyValue('width'), 10) : null; } else { - this.calcWidth = parseInt(this._width, 10); + width = parseInt(this._width, 10); + } + + if (!width && el) { + width = el.offsetWidth; } this._derivePossibleWidth(); + + if (!width) { + width = this.columnList.reduce((sum, item) => sum + parseInt((item.width || item.defaultWidth), 10), 0); + } + + if (Number.isFinite(width) && width !== this.calcWidth) { + this.calcWidth = width; + this.cdr.markForCheck(); + } } /** @@ -4461,16 +4469,20 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements private checkIfGridIsAdded(node): boolean { if (node === this.nativeElement) { return true; - } else { - for (const childNode of node.childNodes) { - const added = this.checkIfGridIsAdded(childNode); - if (added) { - return true; - } - } + } + + if (!node.childNodes) { return false; } + + for (const childNode of node.childNodes) { + const added = this.checkIfGridIsAdded(childNode); + if (added) { + return true; + } + } + return false; } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts index 7afd742d3fe..a0f4c543b45 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-summary.spec.ts @@ -39,7 +39,8 @@ describe('IgxGrid - Summaries', () => { FilteringComponent, ColumnGroupFourLevelTestComponent, SummarieGroupByComponent, - SummarieGroupByWithScrollsComponent + SummarieGroupByWithScrollsComponent, + SummaryColumnsWithSpecificWidthsComponent ], imports: [BrowserAnimationsModule, IgxGridModule.forRoot(), NoopAnimationsModule] }) @@ -251,6 +252,26 @@ describe('IgxGrid - Summaries', () => { } })); + it('Last column summary cell should be aligned according to its data cells', ((() => { + const fixture = TestBed.createComponent(SummaryColumnsWithSpecificWidthsComponent); + fixture.detectChanges(); + + // Get last cell of first data row + const dataRow = fixture.debugElement.queryAll(By.css('igx-grid-row'))[0]; + const lastColumnNormalCell = dataRow.queryAll(By.css('igx-grid-cell'))[4]; + const lastColumnNormalCellRect = (lastColumnNormalCell.nativeElement).getBoundingClientRect(); + + // Get last summary cell of the summary row + const summaryRow = HelperUtils.getSummaryRowByDataRowIndex(fixture, 0); + const lastColumnSummaryCell = HelperUtils.getSummaryCellByVisibleIndex(summaryRow, 4); + const lastColumnSummaryCellRect = (lastColumnSummaryCell.nativeElement).getBoundingClientRect(); + + expect(lastColumnSummaryCellRect.left).toBe(lastColumnNormalCellRect.left, + 'summary cell and data cell are not left aligned'); + expect(lastColumnSummaryCellRect.right).toBe(lastColumnNormalCellRect.right, + 'summary cell and data cell are not right aligned'); + }))); + describe('', () => { let fix; let grid: IgxGridComponent; @@ -1514,8 +1535,8 @@ class EarliestSummary extends IgxDateSummaryOperand { - + [summaries]="earliest"> + ` }) @@ -1528,3 +1549,27 @@ export class CustomSummariesComponent { public dealsSummaryMinMax = DealsSummaryMinMax; public earliest = EarliestSummary; } + +@Component({ + template: ` + + + + + + + + + + + + + ` +}) +export class SummaryColumnsWithSpecificWidthsComponent { + + @ViewChild('grid1', { read: IgxGridComponent }) + public grid1: IgxGridComponent; + + public data = SampleTestData.foodProductData(); +} 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 621c29b03ed..0de68159bc7 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 @@ -26,6 +26,7 @@ import { IgxGridCellComponent } from '../cell.component'; import { TransactionType, Transaction } from '../../services'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { DefaultSortingStrategy } from '../../data-operations/sorting-strategy'; +import { IgxTabsModule, IgxTabsComponent } from '../../tabs'; const DEBOUNCETIME = 30; @@ -2984,6 +2985,35 @@ describe('IgxGrid Component Tests', () => { })); }); }); + + describe('IgxGrid - Integration with other Igx Controls', () => { + configureTestSuite(); + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + IgxGridInsideIgxTabsComponent + ], + imports: [ + NoopAnimationsModule, IgxGridModule, IgxTabsModule] + }).compileComponents(); + })); + + it('IgxTabs: should initialize a grid with correct width/height', fakeAsync(() => { + const fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent); + fix.detectChanges(); + + const grid = fix.componentInstance.grid; + const tab = fix.componentInstance.tabs; + tab.tabs.toArray()[2].select(); + tick(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); + expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(510); + })); + }); }); @Component({ @@ -3033,7 +3063,7 @@ export class IgxGridTestComponent { public isVerticalScrollbarVisible() { const scrollbar = this.grid.verticalScrollContainer.getVerticalScroll(); - if (scrollbar) { + if (scrollbar && scrollbar.offsetHeight > 0) { return scrollbar.offsetHeight < scrollbar.children[0].offsetHeight; } return false; @@ -3507,3 +3537,56 @@ export class IgxGridRowEditingWithFeaturesComponent extends DataParent { this.currentSortExpressions = sortExpr; } } + +@Component({ + template: ` +
+ + This is Tab 1 content. + This is Tab 2 content. + + + + + + + +
+ ` +}) +export class IgxGridInsideIgxTabsComponent { + + @ViewChild(IgxGridComponent, { read: IgxGridComponent }) + public grid: IgxGridComponent; + + @ViewChild(IgxTabsComponent, { read: IgxTabsComponent }) + public tabs: IgxTabsComponent; + + public columns = [ + { field: 'id', width: 100}, + { field: '1', width: 100}, + { field: '2', width: 100}, + { field: '3', width: 100} + ]; + + public data = []; + + constructor() { + const data = []; + for (let j = 1; j <= 10; j++) { + const item = {}; + item['id'] = j; + for (let k = 2, len = this.columns.length; k <= len; k++) { + const field = this.columns[k - 1].field; + item[field] = `item${j}-${k}`; + } + data.push(item); + } + this.data = data; + } +} diff --git a/projects/igniteui-angular/src/lib/grids/summaries/summary-cell.component.ts b/projects/igniteui-angular/src/lib/grids/summaries/summary-cell.component.ts index 60119a30d6a..bb027d18311 100644 --- a/projects/igniteui-angular/src/lib/grids/summaries/summary-cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/summaries/summary-cell.component.ts @@ -145,22 +145,7 @@ export class IgxSummaryCellComponent { @HostBinding('style.max-width') @HostBinding('style.flex-basis') get width() { - const hasVerticalScroll = !this.grid.verticalScrollContainer.dc.instance.notVirtual; - const colWidth = this.column.width; - const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1; - - if (colWidth && !isPercentageWidth) { - let cellWidth = this.isLastUnpinned && hasVerticalScroll ? - parseInt(colWidth, 10) - 18 + '' : colWidth; - - if (typeof cellWidth !== 'string' || cellWidth.endsWith('px') === false) { - cellWidth += 'px'; - } - - return cellWidth; - } else { - return colWidth; - } + return this.column.getCellWidth(); } get nativeElement(): any { diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.spec.ts new file mode 100644 index 00000000000..137f256fa68 --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.spec.ts @@ -0,0 +1,94 @@ +import { async, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { IgxTreeGridModule } from './index'; +import { IgxTreeGridComponent } from './tree-grid.component'; +import { DisplayDensity } from '../../core/displayDensity'; +import { configureTestSuite } from '../../test-utils/configure-suite'; +import { By } from '@angular/platform-browser'; +import { IgxTreeGridWrappedInContComponent } from '../../test-utils/tree-grid-components.spec'; + +describe('IgxTreeGrid Component Tests', () => { + + describe('IgxTreeGrid - default rendering for rows and columns', () => { + configureTestSuite(); + let fix; + let grid: IgxTreeGridComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + IgxTreeGridWrappedInContComponent + ], + imports: [ + NoopAnimationsModule, IgxTreeGridModule] + }).compileComponents(); + })); + + beforeEach(async(() => { + fix = TestBed.createComponent(IgxTreeGridWrappedInContComponent); + grid = fix.componentInstance.treeGrid; + })); + + it('should match width and height of parent container when width/height are set in %', fakeAsync(() => { + fix.componentInstance.outerWidth = 800; + fix.componentInstance.outerHeight = 600; + grid.width = '50%'; + grid.height = '50%'; + tick(); + fix.detectChanges(); + + expect(window.getComputedStyle(grid.nativeElement).height).toMatch('300px'); + expect(window.getComputedStyle(grid.nativeElement).width).toMatch('400px'); + expect(grid.rowList.length).toBeGreaterThan(0); + })); + + it('should render 10 records if height is unset and parent container\'s height is unset', () => { + fix.detectChanges(); + const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + expect(defaultHeight).not.toBeNull(); + expect(parseInt(defaultHeight, 10)).toBeGreaterThan(400); + expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); + expect(grid.rowList.length).toBeGreaterThanOrEqual(10); + }); + + it('should render 10 records if height is 100% and parent container\'s height is unset', fakeAsync(() => { + grid.height = '600px'; + tick(); + fix.detectChanges(); + const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + expect(defaultHeight).not.toBeNull(); + expect(parseInt(defaultHeight, 10)).toBeGreaterThan(400); + expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); + expect(grid.rowList.length).toBeGreaterThanOrEqual(10); + })); + + it(`should render all records exactly if height is 100% and parent container\'s height is unset and + there are fewer than 10 records in the data view`, fakeAsync(() => { + grid.height = '100%'; + fix.componentInstance.data = fix.componentInstance.data.slice(0, 1); + tick(); + fix.detectChanges(); + const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + expect(defaultHeight).not.toBeNull(); + expect(parseInt(defaultHeight, 10)).toBeGreaterThan(200); + expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeFalsy(); + expect(grid.rowList.length).toEqual(6); + })); + + it(`should render 11 records if height is 100% and parent container\'s height is unset and + display density is changed`, fakeAsync(() => { + grid.height = '100%'; + fix.componentInstance.density = DisplayDensity.compact; + tick(); + fix.detectChanges(); + const defaultHeight = fix.debugElement.query(By.css('.igx-grid__tbody')).styles.height; + const defaultHeightNum = parseInt(defaultHeight, 10); + expect(defaultHeight).not.toBeNull(); + expect(defaultHeightNum).toBeGreaterThan(300); + expect(defaultHeightNum).toBeLessThan(330); + expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); + expect(grid.rowList.length).toEqual(11); + })); + }); + +}); diff --git a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts index f74d1130cb5..90bee09c0eb 100644 --- a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts @@ -2,7 +2,7 @@ import { Component, ViewChild } from '@angular/core'; import { IgxTreeGridComponent } from '../grids/tree-grid/tree-grid.component'; import { SampleTestData } from './sample-test-data.spec'; import { IgxNumberSummaryOperand, IgxSummaryResult } from '../grids'; -import { IgxTransactionService, IgxHierarchicalTransactionService } from '../../public_api'; +import { IgxTransactionService, IgxHierarchicalTransactionService, DisplayDensity } from '../../public_api'; import { IgxGridTransaction } from '../grids/grid-base.component'; @Component({ @@ -389,3 +389,55 @@ export class IgxTreeGridRowEditingHierarchicalDSTransactionComponent { @ViewChild('treeGrid', { read: IgxTreeGridComponent }) public treeGrid: IgxTreeGridComponent; public paging = false; } + +@Component({ + template: + `
+ + + + + + +
` +}) + +export class IgxTreeGridWrappedInContComponent { + @ViewChild(IgxTreeGridComponent) public treeGrid: IgxTreeGridComponent; + public data = SampleTestData.employeeTreeData(); + + public height = null; + public paging = false; + public pageSize = 5; + public density = DisplayDensity.comfortable; + public outerWidth = 800; + public outerHeight: number; + + public isHorizontalScrollbarVisible() { + const scrollbar = this.treeGrid.parentVirtDir.getHorizontalScroll(); + if (scrollbar) { + return scrollbar.offsetWidth < scrollbar.children[0].offsetWidth; + } + + return false; + } + + public getVerticalScrollHeight() { + const scrollbar = this.treeGrid.verticalScrollContainer.getVerticalScroll(); + if (scrollbar) { + return parseInt(scrollbar.style.height, 10); + } + + return 0; + } + + public isVerticalScrollbarVisible() { + const scrollbar = this.treeGrid.verticalScrollContainer.getVerticalScroll(); + if (scrollbar && scrollbar.offsetHeight > 0) { + return scrollbar.offsetHeight < scrollbar.children[0].offsetHeight; + } + return false; + } + +}