Skip to content

Commit 93d144f

Browse files
kdinevCopilothanastasov
authored
Fix column width issues in grid state persistence 20.1.x (#16916)
* fix(state): column width issue when all columns are hidden in grid state - Prevent setting _columnWidth to "0px" when all columns are hidden - Preserve valid column widths for when columns are unhidden - Add test to verify column widths are preserved after state restoration Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> * test(state): Update test to properly validate column width preservation - Adjust test expectations to handle columns constrained by minWidth - Verify that columns don't all end up with the same minimum width - Test passes successfully Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> * test(*): update column-hiding test to reflect correct behavior - column width should not be 0px when all columns hidden - This allows proper width restoration when columns are unhidden - All column hiding tests pass successfully Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> * chore(*): Address code review feedback - Use explicit check for '0px' instead of parseFloat for clarity - Remove hardcoded width assertion in test for better maintainability - All tests still pass successfully Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> * refactor(*): to eliminate code duplication - Extract _updateColumnDefaultWidths helper method - Remove redundant condition check - Improve code maintainability - All tests still pass Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> * fix(state): Only persist user-set column widths in grid state - Only save width to state if widthSetByUser or columnWidthSetByUser is true - Prevents auto-calculated widths from becoming fixed after state restore - Handle width restoration to skip if undefined - All tests pass Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> * test(pivot grid state): fixing test to not persist calculated column width * refactor(*): Extract duplicated width restoration logic into helper method - Extract restoreColumnState helper to avoid code duplication - Handles width extraction, assignment, and conditional restoration - Used for both column groups and regular columns - All tests pass Co-authored-by: hanastasov <14248932+hanastasov@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kdinev <1472513+kdinev@users.noreply.github.com> Co-authored-by: hanastasov <14248932+hanastasov@users.noreply.github.com>
1 parent 2926947 commit 93d144f

5 files changed

Lines changed: 91 additions & 12 deletions

File tree

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6832,23 +6832,33 @@ export abstract class IgxGridBaseDirective implements GridType,
68326832
const possibleWidth = this.getPossibleColumnWidth();
68336833
if (possibleWidth === "0px") {
68346834
// all columns - hidden
6835-
this._columnWidth = possibleWidth;
6835+
// Do not update _columnWidth to preserve valid column widths for when columns are unhidden
6836+
// Only update column defaultWidth if _columnWidth is already set and not '0px'
6837+
if (this._columnWidth && this._columnWidth !== '0px') {
6838+
this._updateColumnDefaultWidths();
6839+
}
6840+
this.resetCachedWidths();
6841+
return;
68366842
} else if (this.width !== null) {
68376843
this._columnWidth = Math.max(parseFloat(possibleWidth), this.minColumnWidth) + 'px'
68386844
} else {
68396845
this._columnWidth = this.minColumnWidth + 'px';
68406846
}
68416847
}
6848+
this._updateColumnDefaultWidths();
6849+
this.resetCachedWidths();
6850+
}
6851+
6852+
private _updateColumnDefaultWidths() {
68426853
this._columns.forEach((column: IgxColumnComponent) => {
6843-
if (this.hasColumnLayouts && parseFloat(this._columnWidth)) {
6854+
if (this.hasColumnLayouts) {
68446855
const columnWidthCombined = parseFloat(this._columnWidth) * (column.colEnd ? column.colEnd - column.colStart : 1);
68456856
column.defaultWidth = columnWidthCombined + 'px';
68466857
} else {
68476858
column.defaultWidth = this._columnWidth;
68486859
column.resetCaches();
68496860
}
68506861
});
6851-
this.resetCachedWidths();
68526862
}
68536863

68546864
protected resetNotifyChanges() {

projects/igniteui-angular/src/lib/grids/grid/column-hiding.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,10 @@ describe('Column Hiding UI #grid', () => {
537537
grid.columnList.forEach((col) => col.hidden = true);
538538
tick(30);
539539
fix.detectChanges();
540+
// Column widths should be preserved when all columns are hidden
541+
// This allows proper width restoration when columns are unhidden
540542
grid.columnList.forEach((col) => {
541-
expect(col.width).toBe('0px');
543+
expect(col.width).not.toBe('0px', 'Column width should not be 0px when hidden');
542544
});
543545
fixEl = fix.nativeElement;
544546
gridEl = grid.nativeElement;

projects/igniteui-angular/src/lib/grids/state-base.directive.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export class IgxGridStateBaseDirective {
208208
dataType: c.dataType,
209209
hasSummary: c.hasSummary,
210210
field: c.field,
211-
width: c.width,
211+
width: ((c as IgxColumnComponent).widthSetByUser || context.currGrid.columnWidthSetByUser) ? c.width : undefined,
212212
header: c.header,
213213
resizable: c.resizable,
214214
searchable: c.searchable,
@@ -231,6 +231,21 @@ export class IgxGridStateBaseDirective {
231231
},
232232
restoreFeatureState: (context: IgxGridStateBaseDirective, state: IColumnState[]): void => {
233233
const newColumns = [];
234+
235+
// Helper to restore column state without auto-persisting widths
236+
const restoreColumnState = (column: IgxColumnComponent | IgxColumnGroupComponent, colState: IColumnState) => {
237+
// Extract width to handle it separately
238+
const width = colState.width;
239+
delete colState.width;
240+
241+
Object.assign(column, colState);
242+
243+
// Only restore width if it was explicitly set by the user (not undefined)
244+
if (width !== undefined) {
245+
column.width = width;
246+
}
247+
};
248+
234249
state.forEach((colState) => {
235250
const hasColumnGroup = colState.columnGroup;
236251
const hasColumnLayouts = colState.columnLayout;
@@ -247,7 +262,9 @@ export class IgxGridStateBaseDirective {
247262
} else {
248263
ref1.children.reset([]);
249264
}
250-
Object.assign(ref1, colState);
265+
266+
restoreColumnState(ref1, colState);
267+
251268
ref1.grid = context.currGrid;
252269
if (colState.parent || colState.parentKey) {
253270
const columnGroup: IgxColumnGroupComponent = newColumns.find(e => e.columnGroup && (e.key ? e.key === colState.parentKey : e.header === ref1.parent));
@@ -264,7 +281,8 @@ export class IgxGridStateBaseDirective {
264281
component.changeDetectorRef.detectChanges();
265282
}
266283

267-
Object.assign(ref, colState);
284+
restoreColumnState(ref, colState);
285+
268286
ref.grid = context.currGrid;
269287
if (colState.parent || colState.parentKey) {
270288
const columnGroup: IgxColumnGroupComponent = newColumns.find(e => e.columnGroup && (e.key ? e.key === colState.parentKey : e.header === ref.parent));

projects/igniteui-angular/src/lib/grids/state.directive.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,55 @@ describe('IgxGridState - input properties #grid', () => {
799799
expect(prodIdColumn.colStart).toBe(1);
800800
expect(prodIdColumn.colEnd).toBe(1);
801801
});
802+
803+
it('should preserve column widths when restoring state with all columns hidden', () => {
804+
const fix = TestBed.createComponent(IgxGridStateComponent);
805+
fix.detectChanges();
806+
const grid = fix.componentInstance.grid;
807+
const state = fix.componentInstance.state;
808+
809+
// Store initial column widths - don't hardcode expected values
810+
const initialWidths = grid.columns.map(col => col.width);
811+
812+
// Hide all columns
813+
grid.columns.forEach(col => col.hidden = true);
814+
fix.detectChanges();
815+
816+
// Verify all columns are hidden
817+
expect(grid.columns.every(col => col.hidden)).toBe(true);
818+
819+
// Get and save the state with all columns hidden
820+
const gridState = state.getState(false) as IGridState;
821+
expect(gridState.columns.every(col => col.hidden)).toBe(true);
822+
823+
// Restore the state
824+
state.setState(gridState);
825+
fix.detectChanges();
826+
827+
// Verify all columns are still hidden
828+
expect(grid.columns.every(col => col.hidden)).toBe(true);
829+
830+
// Unhide all columns
831+
grid.columns.forEach(col => col.hidden = false);
832+
fix.detectChanges();
833+
834+
// Verify column widths are preserved and not set to minimum
835+
// The issue was that when all columns were hidden, _columnWidth would be set to "0px"
836+
// and all columns would end up with minimum width when unhidden
837+
grid.columns.forEach((col, index) => {
838+
expect(col.width).toBe(initialWidths[index], `Column ${index} width should be preserved`);
839+
// The calcWidth should be based on the column width or grid default, not forced to 0px
840+
const calcWidth = parseFloat(col.calcWidth);
841+
// Note: some columns may be constrained by minWidth which is expected
842+
// The key is they shouldn't all be the same minimum width
843+
expect(calcWidth).toBeGreaterThan(0, `Column ${index} calcWidth should be greater than 0`);
844+
});
845+
846+
// Verify that not all columns have the same width (which would indicate the bug)
847+
const calcWidths = grid.columns.map(col => parseFloat(col.calcWidth));
848+
const allSameWidth = calcWidths.every(w => w === calcWidths[0]);
849+
expect(allSameWidth).toBe(false, 'Columns should not all have the same width');
850+
});
802851
});
803852

804853
class HelperFunctions {

projects/igniteui-angular/src/lib/grids/state.pivotgrid.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ describe('IgxPivotGridState #pivotGrid :', () => {
3030
const jsonString = state.getState(true);
3131
const expectedObj = {
3232
"columns": [
33-
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "Bulgaria", "width": "220px", "header": "Bulgaria", "resizable": false, "searchable": true, "selectable": true, "key": "Bulgaria", "columnGroup": false, "disableHiding": false, "disablePinning": false },
34-
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "US", "width": "220px", "header": "US", "resizable": false, "searchable": true, "selectable": true, "key": "US", "columnGroup": false, "disableHiding": false, "disablePinning": false },
35-
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "Uruguay", "width": "220px", "header": "Uruguay", "resizable": false, "searchable": true, "selectable": true, "key": "Uruguay", "columnGroup": false, "disableHiding": false, "disablePinning": false },
36-
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "UK", "width": "220px", "header": "UK", "resizable": false, "searchable": true, "selectable": true, "key": "UK", "columnGroup": false, "disableHiding": false, "disablePinning": false },
37-
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "Japan", "width": "220px", "header": "Japan", "resizable": false, "searchable": true, "selectable": true, "key": "Japan", "columnGroup": false, "disableHiding": false, "disablePinning": false }
33+
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "Bulgaria", "header": "Bulgaria", "resizable": false, "searchable": true, "selectable": true, "key": "Bulgaria", "columnGroup": false, "disableHiding": false, "disablePinning": false },
34+
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "US", "header": "US", "resizable": false, "searchable": true, "selectable": true, "key": "US", "columnGroup": false, "disableHiding": false, "disablePinning": false },
35+
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "Uruguay", "header": "Uruguay", "resizable": false, "searchable": true, "selectable": true, "key": "Uruguay", "columnGroup": false, "disableHiding": false, "disablePinning": false },
36+
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "UK", "header": "UK", "resizable": false, "searchable": true, "selectable": true, "key": "UK", "columnGroup": false, "disableHiding": false, "disablePinning": false },
37+
{ "pinned": false, "sortable": true, "filterable": true, "sortingIgnoreCase": true, "filteringIgnoreCase": true, "headerClasses": "", "headerGroupClasses": "", "groupable": false, "hidden": false, "dataType": "number", "hasSummary": false, "field": "Japan", "header": "Japan", "resizable": false, "searchable": true, "selectable": true, "key": "Japan", "columnGroup": false, "disableHiding": false, "disablePinning": false }
3838
],
3939
"filtering": { "filteringOperands": [], "operator": 0, "type": 0 },
4040
"advancedFiltering": {}, "sorting": [], "cellSelection": [], "rowSelection": [], "columnSelection": [],

0 commit comments

Comments
 (0)