diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index ffa36f14e90..5fa49fb5c1b 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -902,8 +902,18 @@ map.get($grid-cell-padding-inline, 'cosy'), map.get($grid-cell-padding-inline, 'comfortable') ); + min-height: sizable( + rem(32px), + rem(40px), + rem(50px) + ); } @else { padding-inline: pad-inline(rem(8px), rem(12px), rem(16px)); + min-height: sizable( + rem(32px), + rem(40px), + rem(48px) + ); igx-icon { opacity: if($theme-variant == 'light', .75, .85); @@ -2736,6 +2746,19 @@ %igx-grid__tr-action { &:last-of-type { border-inline-end: var-get($theme, 'header-border-width') var-get($theme, 'header-border-style') var-get($theme, 'header-border-color'); + @if $variant != 'indigo' { + min-height: sizable( + rem(32px), + rem(40px), + rem(50px) + ); + } @else { + min-height: sizable( + rem(32px), + rem(40px), + rem(48px) + ); + } } } diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 80883a9302a..1f62ef8aa99 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -2517,7 +2517,7 @@ export abstract class IgxGridBaseDirective implements GridType, public get mergeStrategy() { return this._mergeStrategy; } - public set mergeStrategy(value) { + public set mergeStrategy(value) { this._mergeStrategy = value; } @@ -3119,7 +3119,7 @@ export abstract class IgxGridBaseDirective implements GridType, /** * @hidden @internal */ - public hoverIndex: number; + public hoverIndex: number; /** * @hidden @internal @@ -4001,13 +4001,13 @@ export abstract class IgxGridBaseDirective implements GridType, * @hidden * @internal */ - public get columnsToMerge() : ColumnType[] { + public get columnsToMerge(): ColumnType[] { if (this._columnsToMerge.length) { return this._columnsToMerge; } const cols = this.visibleColumns.filter( - x => x.merge && (this.cellMergeMode ==='always' || - (this.cellMergeMode === 'onSort' && !!this.sortingExpressions.find( y => y.fieldName === x.field))) + x => x.merge && (this.cellMergeMode === 'always' || + (this.cellMergeMode === 'onSort' && !!this.sortingExpressions.find(y => y.fieldName === x.field))) ); this._columnsToMerge = cols; return this._columnsToMerge; @@ -4015,8 +4015,8 @@ export abstract class IgxGridBaseDirective implements GridType, protected allowResetOfColumnsToMerge() { const cols = this.visibleColumns.filter( - x => x.merge && (this.cellMergeMode ==='always' || - (this.cellMergeMode === 'onSort' && !!this.sortingExpressions.find( y => y.fieldName === x.field))) + x => x.merge && (this.cellMergeMode === 'always' || + (this.cellMergeMode === 'onSort' && !!this.sortingExpressions.find(y => y.fieldName === x.field))) ); if (areEqualArrays(cols, this._columnsToMerge)) { return false; @@ -5650,6 +5650,9 @@ export abstract class IgxGridBaseDirective implements GridType, * The rowHeight input is bound to min-height css prop of rows that adds a 1px border in all cases */ public get renderedRowHeight(): number { + if (this.hasCellsToMerge) { + return this.rowHeight; + } return this.rowHeight + 1; } @@ -7740,9 +7743,9 @@ export abstract class IgxGridBaseDirective implements GridType, const virtRec = this.verticalScrollContainer.igxForOf[targetRowIndex]; const col = typeof (column) === 'number' ? this.visibleColumns[column] : column; const rowSpan = this.isRecordMerged(virtRec) ? virtRec?.cellMergeMeta.get(col)?.rowSpan : 1; - if (rowSpan > 1) { - targetRowIndex += Math.floor(rowSpan/2); - } + if (rowSpan > 1) { + targetRowIndex += Math.floor(rowSpan / 2); + } if (delayScrolling) { this.verticalScrollContainer.dataChanged.pipe(first(), takeUntil(this.destroy$)).subscribe(() => { this.scrollDirective(this.verticalScrollContainer, @@ -8096,10 +8099,10 @@ export abstract class IgxGridBaseDirective implements GridType, if (this.hasCellsToMerge) { let indexes = this.activeRowIndexes; if (this.page > 0) { - indexes = indexes.map(x => this.perPage * this.page + x ); + indexes = indexes.map(x => this.perPage * this.page + x); } - data = DataUtil.merge(cloneArray(this.filteredSortedData), this.columnsToMerge, this.mergeStrategy, indexes, this); + data = DataUtil.merge(cloneArray(this.filteredSortedData), this.columnsToMerge, this.mergeStrategy, indexes, this); } const columnItems = this.visibleColumns.filter((c) => !c.columnGroup).sort((c1, c2) => c1.visibleIndex - c2.visibleIndex); const columnsPathParts = columnItems.map(col => columnFieldPath(col.field)); @@ -8115,7 +8118,7 @@ export abstract class IgxGridBaseDirective implements GridType, : resolveNestedPath(currentRowData, columnsPathParts[cid]); if (value !== undefined && value !== null && c.searchable) { let searchValue = caseSensitive ? String(value) : String(value).toLowerCase(); - const isMergePlaceHolder = this.isRecordMerged(dataRow) ? !!dataRow?.cellMergeMeta.get(c.field)?.root : false; + const isMergePlaceHolder = this.isRecordMerged(dataRow) ? !!dataRow?.cellMergeMeta.get(c.field)?.root : false; if (exactMatch) { if (searchValue === searchText && !isMergePlaceHolder) { const mic: IMatchInfoCache = { @@ -8256,7 +8259,7 @@ export abstract class IgxGridBaseDirective implements GridType, this._rowCount += 1; // include header row } - private updateMergedData(){ + private updateMergedData() { // recalc merged data if (this.columnsToMerge.length > 0) { const startIndex = this.verticalScrollContainer.state.startIndex; @@ -8267,9 +8270,9 @@ export abstract class IgxGridBaseDirective implements GridType, if (rec.cellMergeMeta && // index + maxRowSpan is within view startIndex < (index + Math.max(...rec.cellMergeMeta.values().toArray().map(x => x.rowSpan)))) { - const visibleIndex = this.isRowPinningToTop ? index + this.pinnedRecordsCount : index; - data.push({record: rec, index: visibleIndex, dataIndex: index }); - } + const visibleIndex = this.isRowPinningToTop ? index + this.pinnedRecordsCount : index; + data.push({ record: rec, index: visibleIndex, dataIndex: index }); + } } this._mergedDataInView = data; this.notifyChanges(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/cell-merge.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/cell-merge.spec.ts index 4bcf4a4ef54..c053bbce81d 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/cell-merge.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/cell-merge.spec.ts @@ -1,12 +1,12 @@ import { Component, TemplateRef, ViewChild } from '@angular/core'; -import { fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; +import { TestBed, waitForAsync } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ByLevelTreeGridMergeStrategy, DefaultMergeStrategy, DefaultSortingStrategy, GridCellMergeMode, GridColumnDataType, GridType, IgxColumnComponent, IgxGridComponent, IgxHierarchicalGridComponent, IgxPaginatorComponent, IgxStringFilteringOperand, SortingDirection } from 'igniteui-angular'; +import { ByLevelTreeGridMergeStrategy, DefaultMergeStrategy, DefaultSortingStrategy, GridCellMergeMode, GridColumnDataType, GridType, IgxColumnComponent, IgxGridComponent, IgxHierarchicalGridComponent, IgxPaginatorComponent, IgxStringFilteringOperand, Size, SortingDirection } from 'igniteui-angular'; import { DataParent } from '../../test-utils/sample-test-data.spec'; import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec'; import { By } from '@angular/platform-browser'; import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; -import { hasClass } from '../../test-utils/helper-utils.spec'; +import { hasClass, setElementSize } from '../../test-utils/helper-utils.spec'; import { ColumnLayoutTestComponent } from './grid.multi-row-layout.spec'; import { IgxHierarchicalGridTestBaseComponent } from '../hierarchical-grid/hierarchical-grid.spec'; import { IgxHierarchicalRowComponent } from '../hierarchical-grid/hierarchical-row.component'; @@ -205,7 +205,7 @@ describe('IgxGrid - Cell merging #grid', () => { fix.componentInstance.height = '300px'; fix.detectChanges(); }); - it('should retain rows with merged cells that span multiple rows in DOM as long as merged cell is still in view.', async() => { + it('should retain rows with merged cells that span multiple rows in DOM as long as merged cell is still in view.', async () => { // initial row list is same as the virtualization chunk expect(grid.rowList.length).toBe(grid.virtualizationState.chunkSize); @@ -224,22 +224,22 @@ describe('IgxGrid - Cell merging #grid', () => { expect(grid.rowList.first.nativeElement.offsetTop).toBeLessThan(-50); }); - it('should remove row from DOM when merged cell is no longer in view.', async() => { + it('should remove row from DOM when merged cell is no longer in view.', async () => { // scroll so that first row with merged cell is not in view grid.navigateTo(grid.virtualizationState.chunkSize, 0); await wait(100); fix.detectChanges(); - //virtualization starts from 2 - expect(grid.virtualizationState.startIndex).toBe(2); + //virtualization starts from 2 + expect(grid.virtualizationState.startIndex).toBe(2); - // no merge cells from previous chunks - expect(grid.rowList.length).toBe(grid.virtualizationState.chunkSize); - // first row is from the virtualization - expect(grid.rowList.first.index).toBe(grid.virtualizationState.startIndex); + // no merge cells from previous chunks + expect(grid.rowList.length).toBe(grid.virtualizationState.chunkSize); + // first row is from the virtualization + expect(grid.rowList.first.index).toBe(grid.virtualizationState.startIndex); }); - it('horizontal virtualization should not be affected by vertically merged cells.', async() => { + it('horizontal virtualization should not be affected by vertically merged cells.', async () => { let mergedCell = grid.rowList.first.cells.find(x => x.column.field === 'ProductName'); expect(mergedCell.value).toBe('Ignite UI for JavaScript'); expect(mergedCell.nativeElement.parentElement.style.gridTemplateRows).toBe("50px 50px"); @@ -392,7 +392,7 @@ describe('IgxGrid - Cell merging #grid', () => { fix.detectChanges(); expect(grid.pinnedRows.length).toBe(2); - const pinnedRow = grid.pinnedRows[0]; + const pinnedRow = grid.pinnedRows[0]; expect(pinnedRow.metaData.cellMergeMeta.get(col.field)?.rowSpan).toBe(2); const mergedPinnedCell = pinnedRow.cells.find(x => x.column.field === 'ProductName'); expect(mergedPinnedCell.value).toBe('Ignite UI for JavaScript'); @@ -436,7 +436,7 @@ describe('IgxGrid - Cell merging #grid', () => { describe('Activation', () => { - it('should interrupt merge sequence so that active row has no merging.', async() => { + it('should interrupt merge sequence so that active row has no merging.', async () => { const col = grid.getColumnByName('ProductName'); GridFunctions.verifyColumnMergedState(grid, col, [ { value: 'Ignite UI for JavaScript', span: 2 }, @@ -474,7 +474,7 @@ describe('IgxGrid - Cell merging #grid', () => { fix.detectChanges(); }); - it('should edit the individual row values for the active row.', async() => { + it('should edit the individual row values for the active row.', async () => { const col = grid.getColumnByName('ProductName'); grid.rowEditable = true; fix.detectChanges(); @@ -498,7 +498,7 @@ describe('IgxGrid - Cell merging #grid', () => { ]); // enter new val - const cellInput = grid.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.querySelector('[igxinput]'); + const cellInput = grid.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.querySelector('[igxinput]'); UIInteractions.setInputElementValue(cellInput, "NewValue"); fix.detectChanges(); @@ -529,15 +529,15 @@ describe('IgxGrid - Cell merging #grid', () => { expect(cell.editMode).toBe(true); // enter new val - const cellInput = grid.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.querySelector('[igxinput]'); + const cellInput = grid.gridAPI.get_cell_by_index(0, 'ProductName').nativeElement.querySelector('[igxinput]'); UIInteractions.setInputElementValue(cellInput, "NewValue"); fix.detectChanges(); UIInteractions.triggerEventHandlerKeyDown('enter', GridFunctions.getGridContent(fix)); fix.detectChanges(); - // row with edit cell is not merged anymore - GridFunctions.verifyColumnMergedState(grid, col, [ + // row with edit cell is not merged anymore + GridFunctions.verifyColumnMergedState(grid, col, [ { value: 'NewValue', span: 1 }, { value: 'Ignite UI for JavaScript', span: 1 }, { value: 'Ignite UI for Angular', span: 1 }, @@ -563,7 +563,7 @@ describe('IgxGrid - Cell merging #grid', () => { const mergedIntersectedCell = grid.gridAPI.get_cell_by_index(0, 'ProductName'); // check cell has selected style - hasClass(mergedIntersectedCell.nativeElement,'igx-grid__td--merged-selected', true); + hasClass(mergedIntersectedCell.nativeElement, 'igx-grid__td--merged-selected', true); }); }); @@ -603,7 +603,7 @@ describe('IgxGrid - Cell merging #grid', () => { // check api expect(grid.getSelectedData().length).toBe(5); expect(grid.getSelectedData()).toEqual(grid.data.slice(0, 5).map(x => { - return { 'ID': x.ID, 'ProductName': x. ProductName}; + return { 'ID': x.ID, 'ProductName': x.ProductName }; })); }); }); @@ -630,7 +630,7 @@ describe('IgxGrid - Cell merging #grid', () => { fix.detectChanges(); expect(grid.getSelectedColumnsData()).toEqual(grid.data.map(x => { - return {'ProductName': x. ProductName}; + return { 'ProductName': x.ProductName }; })); }); }); @@ -676,7 +676,7 @@ describe('IgxGrid - Cell merging #grid', () => { expect(activeHighlight[0].closest("igx-grid-cell")).toBe(cell0); }); - it('should update matches if a cell becomes unmerged.', async() => { + it('should update matches if a cell becomes unmerged.', async () => { let matches = grid.findNext('JavaScript'); fix.detectChanges(); @@ -710,55 +710,71 @@ describe('IgxGrid - Cell merging #grid', () => { }); + describe('Sizing', () => { + it('should size correct when size is set to anything other than large', async () => { + fix.componentInstance.cols = [{ field: 'ProductName', dataType: GridColumnDataType.String, merge: true }] + fix.detectChanges(); + setElementSize(grid.nativeElement, Size.Small) + fix.detectChanges(); + await wait(100); + fix.detectChanges(); + const sizes = grid.rowList.map(x => parseFloat(getComputedStyle(x.nativeElement).getPropertyValue("height"))); + const expectedSizes = new Array(9).fill(32); + + expect(sizes).toEqual(expectedSizes); + }); + + }); + describe('HierarchicalGrid', () => { beforeEach(() => { fix = TestBed.createComponent(IgxHierarchicalGridTestBaseComponent); fix.componentInstance.data = [ { - ID: 1, ChildLevels: 1, ProductName: 'Product A' , Col1: 1, + ID: 1, ChildLevels: 1, ProductName: 'Product A', Col1: 1, childData: [ { - ID: 1, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 1, ChildLevels: 2, ProductName: 'Product A', Col1: 1, }, { - ID: 2, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 2, ChildLevels: 2, ProductName: 'Product A', Col1: 1, }, { - ID: 3, ChildLevels: 2, ProductName: 'Product B' , Col1: 1, + ID: 3, ChildLevels: 2, ProductName: 'Product B', Col1: 1, }, { - ID: 4, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 4, ChildLevels: 2, ProductName: 'Product A', Col1: 1, } ] }, { - ID: 2, ChildLevels: 1, ProductName: 'Product A' , Col1: 1, childData: [ + ID: 2, ChildLevels: 1, ProductName: 'Product A', Col1: 1, childData: [ { - ID: 1, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 1, ChildLevels: 2, ProductName: 'Product A', Col1: 1, }, { - ID: 2, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 2, ChildLevels: 2, ProductName: 'Product A', Col1: 1, }, { - ID: 3, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 3, ChildLevels: 2, ProductName: 'Product A', Col1: 1, }, { - ID: 4, ChildLevels: 2, ProductName: 'Product A' , Col1: 1, + ID: 4, ChildLevels: 2, ProductName: 'Product A', Col1: 1, } ] }, { - ID: 3, ChildLevels: 1, ProductName: 'Product B' , Col1: 1 + ID: 3, ChildLevels: 1, ProductName: 'Product B', Col1: 1 }, { - ID: 4, ChildLevels: 1, ProductName: 'Product B' , Col1: 1 + ID: 4, ChildLevels: 1, ProductName: 'Product B', Col1: 1 }, { - ID: 5, ChildLevels: 1, ProductName: 'Product C' , Col1: 1 + ID: 5, ChildLevels: 1, ProductName: 'Product C', Col1: 1 }, { - ID: 6, ChildLevels: 1, ProductName: 'Product B' , Col1: 1 + ID: 6, ChildLevels: 1, ProductName: 'Product B', Col1: 1 } ]; fix.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/grids/row.directive.ts b/projects/igniteui-angular/src/lib/grids/row.directive.ts index f895f59545a..81a0bced344 100644 --- a/projects/igniteui-angular/src/lib/grids/row.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/row.directive.ts @@ -124,7 +124,7 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy { } public get hasMergedCells(): boolean { - return this.grid.columnsToMerge.length > 0; + return this.grid.columnsToMerge.length > 0; } /** @@ -550,7 +550,7 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy { const rowSpan = this.metaData?.cellMergeMeta?.get(field)?.rowSpan; if (rowSpan > 1) { return node ? (node.row >= this.index && node.row < this.index + rowSpan) - && node.column === visibleColumnIndex : false; + && node.column === visibleColumnIndex : false; } return node ? node.row === this.index && node.column === visibleColumnIndex : false; } @@ -633,6 +633,9 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy { } protected getMergeCellSpan(col: ColumnType) { + if ((this.grid as any).shouldResize) { + return null; + } const rowCount = this.metaData.cellMergeMeta.get(col.field).rowSpan; let sizeSpans = ""; const isPinned = this.pinned && !this.disabled; @@ -674,20 +677,23 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy { recData = rec.recordRef; } - if(this.grid.isTreeRow && this.grid.isTreeRow(recData)){ + if (this.grid.isTreeRow && this.grid.isTreeRow(recData)) { recData = recData.data; } return this.grid.primaryKey ? recData[this.grid.primaryKey] : recData; } protected getRowHeight() { + if ((this.grid as any).shouldResize) { + return null; + } const isPinned = this.pinned && !this.disabled; const indexInData = this.grid.isRowPinningToTop && !isPinned ? this.index - this.grid.pinnedRecordsCount : this.index; if ((this.grid as any)._cdrRequests) { // recalc size if repaint is requested. this.grid.verticalScrollContainer.recalcUpdateSizes(); } - const size = this.grid.verticalScrollContainer.getSizeAt(indexInData) - 1; + const size = this.grid.verticalScrollContainer.getSizeAt(indexInData); return size || this.grid.rowHeight; }