Skip to content

Commit ad3c231

Browse files
MKirovaMKirova
authored andcommitted
feat(igxGrid): Apply min/max width constraints on user-set and auto-widths.
1 parent 66906e3 commit ad3c231

File tree

3 files changed

+299
-11
lines changed

3 files changed

+299
-11
lines changed

projects/igniteui-angular/src/lib/grids/columns/column.component.ts

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,15 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
523523
*/
524524
@WatchColumnChanges()
525525
@Input()
526-
public maxWidth: string;
526+
public set maxWidth(value: string) {
527+
this._maxWidth = value;
527528

529+
this.grid.notifyChanges(true);
530+
this.grid.cdr.detectChanges();
531+
}
532+
public get maxWidth(): string {
533+
return this._maxWidth;
534+
}
528535
/**
529536
* Sets/gets the class selector of the column header.
530537
* ```typescript
@@ -968,7 +975,8 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
968975
return;
969976
}
970977
this._defaultMinWidth = value;
971-
978+
this.grid.notifyChanges(true);
979+
this.grid.cdr.detectChanges();
972980
}
973981
public get minWidth(): string {
974982
return !this._defaultMinWidth ? this.defaultMinWidth : this._defaultMinWidth;
@@ -1732,6 +1740,11 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
17321740
*/
17331741
public destroy$ = new Subject<any>();
17341742

1743+
/**
1744+
* @hidden
1745+
*/
1746+
public widthConstrained = false;
1747+
17351748
/**
17361749
* @hidden
17371750
*/
@@ -1806,6 +1819,10 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
18061819
* @hidden
18071820
*/
18081821
protected _defaultMinWidth = '';
1822+
/**
1823+
* @hidden
1824+
*/
1825+
protected _maxWidth = '';
18091826
/**
18101827
* @hidden
18111828
*/
@@ -2091,7 +2108,8 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
20912108
if (size && !!size.width) {
20922109
result.push(size.width + 'px');
20932110
} else {
2094-
result.push(parseFloat(this.grid.getPossibleColumnWidth()) + 'px');
2111+
const currentWidth = parseFloat(this.grid.getPossibleColumnWidth());
2112+
result.push((this.getConstrainedSizePx(currentWidth)) + 'px');
20952113
}
20962114
}
20972115
return result;
@@ -2550,6 +2568,23 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
25502568
return res.join(' ');
25512569
}
25522570

2571+
/**
2572+
* @hidden
2573+
* @internal
2574+
*/
2575+
public getConstrainedSizePx(newSize){
2576+
if (this.maxWidth && newSize > this.maxWidthPx) {
2577+
this.widthConstrained = true;
2578+
return this.maxWidthPx;
2579+
} else if (this.minWidth && newSize < this.minWidthPx) {
2580+
this.widthConstrained = true;
2581+
return this.minWidthPx;
2582+
} else {
2583+
this.widthConstrained = false;
2584+
return newSize;
2585+
}
2586+
}
2587+
25532588
/**
25542589
* @hidden
25552590
* @internal
@@ -2559,14 +2594,17 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
25592594
const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1;
25602595
const isAutoWidth = colWidth && typeof colWidth === 'string' && colWidth === 'fit-content';
25612596
if (isPercentageWidth && this.grid.isColumnWidthSum) {
2562-
this._calcWidth = this.grid.minColumnWidth;
2597+
this._calcWidth = this.minWidthPx ?? this.grid.minColumnWidth;
25632598
} else if (isPercentageWidth) {
2564-
this._calcWidth = parseFloat(colWidth) / 100 * this.grid.calcWidth;
2599+
const currentCalcWidth = parseFloat(colWidth) / 100 * this.grid.calcWidth;
2600+
this._calcWidth = this.grid.calcWidth ? this.getConstrainedSizePx(currentCalcWidth) : 0;
25652601
} else if (!colWidth || isAutoWidth && !this.autoSize) {
25662602
// no width
2567-
this._calcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
2603+
const currentCalcWidth = this.defaultWidth || this.grid.getPossibleColumnWidth();
2604+
this._calcWidth = this.getConstrainedSizePx(currentCalcWidth);
25682605
} else {
2569-
this._calcWidth = this.width;
2606+
const currentCalcWidth = parseFloat(this.width);
2607+
this._calcWidth =this.getConstrainedSizePx(currentCalcWidth);
25702608
}
25712609
this.calcPixelWidth = parseFloat(this._calcWidth);
25722610
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5416,22 +5416,24 @@ export abstract class IgxGridBaseDirective implements GridType,
54165416

54175417
const columnsWithSetWidths = this.hasColumnLayouts ?
54185418
visibleCols.filter(c => c.widthSetByUser) :
5419-
visibleChildColumns.filter(c => c.widthSetByUser && c.width !== 'fit-content');
5419+
visibleChildColumns.filter(c => (c.widthSetByUser || c.widthConstrained) && c.width !== 'fit-content');
54205420

54215421
const columnsToSize = this.hasColumnLayouts ?
54225422
combinedBlocksSize - columnsWithSetWidths.length :
54235423
visibleChildColumns.length - columnsWithSetWidths.length;
54245424
const sumExistingWidths = columnsWithSetWidths
54255425
.reduce((prev, curr) => {
5426-
const colWidth = curr.width;
5426+
const colWidth = !curr.widthConstrained ? curr.width : curr.calcPixelWidth;
54275427
let widthValue = parseFloat(colWidth);
54285428
if (isNaN(widthValue)) {
54295429
widthValue = MINIMUM_COLUMN_WIDTH;
54305430
}
54315431
const currWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1 ?
54325432
widthValue / 100 * computedWidth :
54335433
widthValue;
5434-
return prev + currWidth;
5434+
// apply constraints, since constraint may change width
5435+
const constrainedWidth = curr.getConstrainedSizePx(currWidth);
5436+
return prev + constrainedWidth;
54355437
}, 0);
54365438

54375439
// When all columns are hidden, return 0px width

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

Lines changed: 249 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,254 @@ describe('IgxGrid Component Tests #grid', () => {
20072007
}));
20082008
});
20092009

2010+
describe('IgxGrid - min/max width constraints rules', () => {
2011+
beforeAll(waitForAsync(() => {
2012+
TestBed.configureTestingModule({
2013+
imports: [
2014+
NoopAnimationsModule,
2015+
IgxGridDefaultRenderingComponent
2016+
]
2017+
})
2018+
.compileComponents();
2019+
}));
2020+
2021+
describe('min/max in px', () => {
2022+
2023+
it('in column with no width should not go outside bounds.', async() => {
2024+
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
2025+
// 4 cols
2026+
fix.componentInstance.initColumnsRows(5, 4);
2027+
fix.detectChanges();
2028+
2029+
const grid = fix.componentInstance.grid;
2030+
const col1 = grid.columns[0];
2031+
const col2 = grid.columns[1];
2032+
const col3 = grid.columns[2];
2033+
const col4 = grid.columns[3];
2034+
2035+
// without constraint, they split width equally
2036+
expect(col1.calcPixelWidth).toBe(grid.calcWidth / 4);
2037+
expect(col2.calcPixelWidth).toBe(grid.calcWidth / 4);
2038+
expect(col3.calcPixelWidth).toBe(grid.calcWidth / 4);
2039+
expect(col4.calcPixelWidth).toBe(grid.calcWidth / 4);
2040+
2041+
// set smaller max in px
2042+
col1.maxWidth = '100px';
2043+
fix.detectChanges();
2044+
2045+
// first column takes new max
2046+
expect(col1.calcPixelWidth).toBe(100);
2047+
// the rest split the remaining width
2048+
expect(col2.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2049+
expect(col3.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2050+
expect(col4.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2051+
2052+
2053+
// set larger min in px
2054+
col1.maxWidth = null;
2055+
fix.detectChanges();
2056+
2057+
col1.minWidth = '600px';
2058+
fix.detectChanges();
2059+
await wait(16);
2060+
fix.detectChanges();
2061+
2062+
// first column takes new min
2063+
expect(col1.calcPixelWidth).toBe(600);
2064+
// the rest split the remaining width
2065+
expect(col2.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2066+
expect(col3.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2067+
expect(col4.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2068+
});
2069+
2070+
it('in column with pixel width should not go outside bounds.', async() => {
2071+
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
2072+
// 4 cols
2073+
fix.componentInstance.initColumnsRows(5, 4);
2074+
fix.detectChanges();
2075+
const grid = fix.componentInstance.grid;
2076+
const col1 = grid.columns[0];
2077+
col1.width = "150px";
2078+
fix.detectChanges();
2079+
2080+
expect(col1.calcPixelWidth).toBe(150);
2081+
2082+
// set smaller max in px
2083+
col1.maxWidth = '100px';
2084+
fix.detectChanges();
2085+
2086+
// first column takes new max
2087+
expect(col1.calcPixelWidth).toBe(100);
2088+
2089+
// set larger min in px
2090+
col1.maxWidth = null;
2091+
fix.detectChanges();
2092+
col1.minWidth = '500px';
2093+
fix.detectChanges();
2094+
await wait(100);
2095+
fix.detectChanges();
2096+
2097+
// first column takes new min
2098+
expect(col1.calcPixelWidth).toBe(500);
2099+
});
2100+
2101+
it('in column with auto width should not go outside bounds.', async() => {
2102+
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
2103+
// 4 cols
2104+
fix.componentInstance.initColumnsRows(5, 4);
2105+
fix.componentInstance.columns[0].header = "Some longer text to auto-size";
2106+
fix.componentInstance.columns[0].width = 'auto';
2107+
fix.detectChanges();
2108+
// wait for auto-sizing
2109+
await wait(100);
2110+
fix.detectChanges();
2111+
2112+
const grid = fix.componentInstance.grid;
2113+
const col1 = grid.columns[0];
2114+
2115+
// some autosize should be calculated
2116+
expect(col1.autoSize).not.toBeUndefined();
2117+
2118+
// set smaller max in px
2119+
col1.maxWidth = '100px';
2120+
fix.detectChanges();
2121+
2122+
// first column takes new max
2123+
expect(col1.calcPixelWidth).toBe(100);
2124+
2125+
// set larger min in px
2126+
col1.maxWidth = null;
2127+
fix.detectChanges();
2128+
col1.minWidth = '500px';
2129+
fix.detectChanges();
2130+
await wait(100);
2131+
fix.detectChanges();
2132+
2133+
// first column takes new min
2134+
expect(col1.calcPixelWidth).toBe(500);
2135+
});
2136+
});
2137+
2138+
2139+
describe('min/max in %', () => {
2140+
it('in column with no width should not go outside bounds.', async () => {
2141+
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
2142+
// 4 cols
2143+
fix.componentInstance.initColumnsRows(5, 4);
2144+
fix.detectChanges();
2145+
2146+
const grid = fix.componentInstance.grid;
2147+
const col1 = grid.columns[0];
2148+
const col2 = grid.columns[1];
2149+
const col3 = grid.columns[2];
2150+
const col4 = grid.columns[3];
2151+
2152+
// set smaller max in %
2153+
col1.maxWidth = '10%';
2154+
fix.detectChanges();
2155+
2156+
// first column takes new max
2157+
expect(col1.calcPixelWidth).toBe(grid.calcWidth * 0.1);
2158+
// the rest split the remaining width
2159+
expect(col2.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2160+
expect(col3.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2161+
expect(col4.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2162+
2163+
// set larger min in px
2164+
col1.maxWidth = null;
2165+
fix.detectChanges();
2166+
col1.minWidth = '50%';
2167+
fix.detectChanges();
2168+
await wait(100);
2169+
fix.detectChanges();
2170+
2171+
// first column takes new min
2172+
expect(col1.calcPixelWidth).toBe(grid.calcWidth * 0.5);
2173+
// the rest split the remaining width
2174+
expect(col2.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2175+
expect(col3.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2176+
expect(col4.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2177+
});
2178+
2179+
it('in column with pixel width should not go outside bounds.', async() => {
2180+
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
2181+
// 4 cols
2182+
fix.componentInstance.initColumnsRows(5, 4);
2183+
fix.componentInstance.columns[0].width = '400px';
2184+
fix.detectChanges();
2185+
2186+
const grid = fix.componentInstance.grid;
2187+
const col1 = grid.columns[0];
2188+
const col2 = grid.columns[1];
2189+
const col3 = grid.columns[2];
2190+
const col4 = grid.columns[3];
2191+
2192+
// set smaller max in %
2193+
col1.maxWidth = '10%';
2194+
fix.detectChanges();
2195+
2196+
// first column takes new max
2197+
expect(col1.calcPixelWidth).toBe(grid.calcWidth * 0.1);
2198+
// the rest split the remaining width
2199+
expect(col2.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2200+
expect(col3.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2201+
expect(col4.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2202+
2203+
// set larger min in px
2204+
col1.maxWidth = null;
2205+
fix.detectChanges();
2206+
col1.minWidth = '50%';
2207+
fix.detectChanges();
2208+
await wait(100);
2209+
fix.detectChanges();
2210+
2211+
// first column takes new min
2212+
expect(col1.calcPixelWidth).toBe(grid.calcWidth * 0.5);
2213+
// the rest split the remaining width
2214+
expect(col2.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2215+
expect(col3.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2216+
expect(col4.calcPixelWidth).toBe((grid.calcWidth - col1.calcPixelWidth) / 3);
2217+
});
2218+
2219+
it('in column with auto width should not go outside bounds.', async() => {
2220+
const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent);
2221+
// 4 cols
2222+
fix.componentInstance.initColumnsRows(5, 4);
2223+
fix.componentInstance.columns[0].header = "Some longer text to auto-size";
2224+
fix.componentInstance.columns[0].width = 'auto';
2225+
fix.detectChanges();
2226+
await wait(100);
2227+
fix.detectChanges();
2228+
2229+
2230+
const grid = fix.componentInstance.grid;
2231+
const col1 = grid.columns[0];
2232+
2233+
// some autosize should be calculated
2234+
expect(col1.autoSize).not.toBeUndefined();
2235+
2236+
// set smaller max in px
2237+
col1.maxWidth = '10%';
2238+
fix.detectChanges();
2239+
2240+
// first column takes new max
2241+
expect(col1.calcPixelWidth).toBe(grid.calcWidth * 0.1);
2242+
2243+
// set larger min in px
2244+
col1.maxWidth = null;
2245+
fix.detectChanges();
2246+
col1.minWidth = '50%';
2247+
fix.detectChanges();
2248+
await wait(100);
2249+
fix.detectChanges();
2250+
2251+
// first column takes new min
2252+
expect(col1.calcPixelWidth).toBe(grid.calcWidth * 0.5);
2253+
});
2254+
})
2255+
2256+
});
2257+
20102258
describe('IgxGrid - API methods', () => {
20112259
beforeAll(waitForAsync(() => {
20122260
TestBed.configureTestingModule({
@@ -3001,7 +3249,7 @@ export class IgxGridTestComponent {
30013249

30023250
@Component({
30033251
template: `<igx-grid #grid [data]="data" (columnInit)="initColumns($event)">
3004-
<igx-column *ngFor="let col of columns" [field]="col.key" [header]="col.key" [dataType]="col.dataType">
3252+
<igx-column *ngFor="let col of columns" [field]="col.key" [header]="col.header || col.key" [dataType]="col.dataType" [width]="col.width">
30053253
</igx-column>
30063254
<igx-paginator *ngIf="paging"></igx-paginator>
30073255
</igx-grid>`,

0 commit comments

Comments
 (0)