Skip to content

Commit 2b62863

Browse files
CopilotHristo313Copilot
authored
fix(pivot-grid): fix pivot grid localization (#17246)
* Initial plan * feat(pivot-grid): localize fullDate dimension display with shortDate format When IgxPivotDateDimension is used with fullDate enabled and no custom memberFunction, attach a locale-aware formatter that formats leaf-level date values using {dateStyle: 'short'} via Intl.DateTimeFormat. The formatter is added to a new optional IPivotDimension.formatter property and applied in the row dimension rendering layer only, so data keys, sorting, and filtering are unaffected. Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/0e38c414-30cb-4920-a7ea-3bc9882a866b Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * chore: revert package-lock.json to original state Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/0e38c414-30cb-4920-a7ea-3bc9882a866b Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * refactor(pivot-grid): improve readability of date formatter and test assertion Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/0e38c414-30cb-4920-a7ea-3bc9882a866b Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * fix(pivot-grid): address review comments on formatter type safety and date parsing Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/fc2cee95-07d2-43e3-873c-8aafce84bc93 Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * refactor(pivot-grid): simplify formatter coercion to use nullish coalescing Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/fc2cee95-07d2-43e3-873c-8aafce84bc93 Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix(pivot-grid): widen formatter return type to string | null | undefined Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/3b0f8373-547b-4ca5-9b05-35e30e883bcb Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * fix(pivot-grid): address remaining review comments on formatter and date dimension guards Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/4bfb1bbe-9cd9-4dd5-bc5a-ddb4c8c46b41 Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * feat(pivot-grid): apply formatter to column dimension headers in createColumnForDimension Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/87c1c6cd-af16-479a-abed-db7a37432579 Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * fix(pivot-grid): revert copilot fix line * fix(pivot-grid): update with fixes * fix(pivot-grid): update with fixes * fix(pivot-grid): fix date dimension headers being one cycle behind on locale change and add CHANGELOG entry Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/6927bc1b-5a3d-48bc-a8ba-94843c57edca Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> * fix(pivot-grid): fix the locale change * Changes before error encountered Agent-Logs-Url: https://github.com/IgniteUI/igniteui-angular/sessions/1e9ee9c9-0e29-4027-a550-3b3af4814838 * fix(pivot-grid): revert package-lock.json changes * fix(pivot-grid): add small fixes * fix(pivot-grid): fix columns locale cycle behind * fix(pivot-grid): revert pipes changes * fix(pivot-grid): fix failing test build * fix(pivot-grid): update with copilot suggestions * fix(pivot-grid): fix copilot suggestions * fix(pivot-grid): return old setupColumns logic --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Hristo313 <57346540+Hristo313@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Hristo Hristov <HHristov@infragistics.com>
1 parent a7dd688 commit 2b62863

6 files changed

Lines changed: 92 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ All notable changes for each version of this project will be documented in this
215215
- Introduced the `selectionChanged` event for both components. The event is not cancelable and is emitted after the selection is committed and the component state is updated.
216216
- Added `disableClear` input that allows hiding the clear button even when items are selected. Defaults to `false`.
217217

218+
- `IgxPivotGrid`
219+
- Added `headerFormatter` optional property to `IPivotDimension`. This is a display-only callback `(value, dimension?, rowData?) => string | null | undefined` applied when rendering row and column dimension header text. Returning `null` or `undefined` falls back to the raw dimension value. The `IgxPivotDateDimension` uses this to render `fullDate` leaf values in a locale-aware short-date format automatically.
220+
218221
### General
219222

220223
- `IgxOverlayService`

projects/igniteui-angular/grids/core/src/pivot-grid-dimensions.ts

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,14 @@ export class IgxPivotDateDimension implements IPivotDimension {
107107
public childLevel?: IPivotDimension;
108108
/** @hidden @internal */
109109
public memberName = 'AllPeriods';
110-
/** @hidden @internal */
111-
public locale?: string;
112110
public displayName!: string;
111+
/**
112+
* Gets/Sets the locale used for date dimension member formatting (e.g. month names).
113+
* When set, overrides the global I18nManager locale for this dimension.
114+
* Set automatically by the pivot grid component when the grid locale changes.
115+
* @hidden @internal
116+
*/
117+
public locale?: string;
113118
private _resourceStrings: IGridResourceStrings = null!;
114119
private _baseDimension: IPivotDimension;
115120
private _options: IPivotDateDimensionOptions = {};
@@ -142,13 +147,16 @@ export class IgxPivotDateDimension implements IPivotDimension {
142147
this.enabled = inBaseDimension.enabled;
143148
this.displayName = inBaseDimension.displayName || this.resourceStrings.igx_grid_pivot_date_dimension_total;
144149

145-
const baseDimension = options.fullDate ? inBaseDimension : null;
150+
const baseDimension: IPivotDimension = options.fullDate
151+
? this.createLeafDateDimension(inBaseDimension)
152+
: null!;
153+
146154
const monthDimensionDef: IPivotDimension = {
147155
memberName: 'Months',
148156
memberFunction: (rec) => {
149157
const recordValue = PivotUtil.extractValueFromDimension(inBaseDimension, rec);
150-
const dateValue = recordValue ? getDateFormatter().createDateFromValue(recordValue) : null;
151-
return recordValue ? getDateFormatter().formatDateTime(dateValue!, this.locale, { month: 'long'}) : rec['Months'];
158+
const dateValue = (recordValue != null && recordValue !== '') ? getDateFormatter().createDateFromValue(recordValue) : null;
159+
return dateValue ? getDateFormatter().formatDateTime(dateValue!, this.locale, { month: 'long'}) : rec['Months'];
152160
},
153161
enabled: true,
154162
childLevel: baseDimension
@@ -159,8 +167,8 @@ export class IgxPivotDateDimension implements IPivotDimension {
159167
memberName: 'Quarters',
160168
memberFunction: (rec) => {
161169
const recordValue = PivotUtil.extractValueFromDimension(inBaseDimension, rec);
162-
const dateValue = recordValue ? getDateFormatter().createDateFromValue(recordValue) : null;
163-
return recordValue ? `Q` + Math.ceil((dateValue!.getMonth() + 1) / 3) : rec['Quarters'];
170+
const dateValue = (recordValue != null && recordValue !== '') ? getDateFormatter().createDateFromValue(recordValue) : null;
171+
return dateValue ? `Q` + Math.ceil((dateValue!.getMonth() + 1) / 3) : rec['Quarters'];
164172
},
165173
enabled: true,
166174
childLevel: monthDimension
@@ -171,8 +179,8 @@ export class IgxPivotDateDimension implements IPivotDimension {
171179
memberName: 'Years',
172180
memberFunction: (rec) => {
173181
const recordValue = PivotUtil.extractValueFromDimension(inBaseDimension, rec);
174-
const dateValue = recordValue ? getDateFormatter().createDateFromValue(recordValue) : null;
175-
return recordValue ? dateValue!.getFullYear().toString() : rec['Years'];
182+
const dateValue = (recordValue != null && recordValue !== '') ? getDateFormatter().createDateFromValue(recordValue) : null;
183+
return dateValue ? dateValue!.getFullYear().toString() : rec['Years'];
176184
},
177185
enabled: true,
178186
childLevel: quarterDimension
@@ -182,12 +190,31 @@ export class IgxPivotDateDimension implements IPivotDimension {
182190

183191
if (!options.total) {
184192
this.memberName = yearsDimension.memberName;
185-
this.memberFunction = yearsDimension.memberFunction;
193+
this.memberFunction = yearsDimension.memberFunction!;
186194
this.childLevel = yearsDimension.childLevel;
187-
this.displayName = yearsDimension.displayName;
195+
this.displayName = yearsDimension.displayName!;
188196
}
189197
}
190198

199+
private createLeafDateDimension(inBaseDimension: IPivotDimension): IPivotDimension {
200+
if (inBaseDimension.headerFormatter || inBaseDimension.memberFunction) {
201+
// User supplied their own formatter/memberFunction — use the dimension as-is.
202+
return inBaseDimension;
203+
}
204+
// No user-supplied formatter: add a locale-aware formatter that shows dates
205+
// in short-date format while preserving the original dimension instance.
206+
const dateFormatter = getDateFormatter();
207+
inBaseDimension.headerFormatter = (value: any) => {
208+
const hasValue = value !== null && value !== undefined && value !== '';
209+
const dateValue = hasValue ? dateFormatter.createDateFromValue(value) : null;
210+
if (dateValue) {
211+
return dateFormatter.formatDateTime(dateValue, this.locale, { dateStyle: 'short' });
212+
}
213+
return hasValue ? String(value) : '';
214+
};
215+
return inBaseDimension;
216+
}
217+
191218
/** @hidden @internal */
192219
public memberFunction = (_data: any) => this.resourceStrings.igx_grid_pivot_date_dimension_total;
193220
}

projects/igniteui-angular/grids/core/src/pivot-grid.interface.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,22 @@ export interface IPivotDimension {
143143
/** @hidden @internal */
144144
autoWidth?: number;
145145
horizontalSummary? : boolean;
146+
/* csTreatAsEvent: PivotDimensionFormatterEventHandler */
147+
/* blazorOnlyScript */
148+
/**
149+
* Optional function to format the display value of a dimension header.
150+
* Unlike `memberFunction`, this does not affect the data key used for grouping or sorting —
151+
* it is applied when rendering the dimension header text (both row and column dimension headers).
152+
* When set, the return value of this function is shown instead of the raw dimension value.
153+
* Return `null` or `undefined` to fall back to the raw value.
154+
*
155+
* @example
156+
* ```typescript
157+
* // Display dates in a locale-aware short date format.
158+
* { memberName: 'Date', enabled: true, headerFormatter: (value) => new Date(value).toLocaleDateString() }
159+
* ```
160+
*/
161+
headerFormatter?: (value: any, dimension?: IPivotDimension, rowData?: IPivotGridGroupRecord) => string | null | undefined;
146162
}
147163

148164
export interface IPivotExpandableDimension extends IPivotDimension {

projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2495,7 +2495,10 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
24952495
const ref = isGroup ?
24962496
createComponent(IgxColumnGroupComponent, { environmentInjector: this.envInjector, elementInjector: this.injector }) :
24972497
createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
2498-
ref.instance.header = parent != null ? key.split(parent.header + this.pivotKeys.columnDimensionSeparator)[1] : key;
2498+
const parentPath = parent != null ? parent.field + this.pivotKeys.columnDimensionSeparator : null;
2499+
const rawHeader = parentPath != null && key.startsWith(parentPath) ? key.substring(parentPath.length) : key;
2500+
const dim = value.dimension as IPivotDimension;
2501+
ref.instance.header = dim?.headerFormatter != null ? (dim.headerFormatter(rawHeader, dim, undefined) ?? rawHeader) : rawHeader;
24992502
ref.instance.field = key;
25002503
ref.instance.parent = parent;
25012504
if (value.dimension.width) {

projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,30 @@ describe('IgxPivotGrid #pivotGrid', () => {
139139
expect(actualDataTypeValue).toEqual('$71.89');
140140
});
141141

142+
it('should provide context to dimension header formatter', () => {
143+
const pivotGrid = fixture.componentInstance.pivotGrid;
144+
const rowDimension = pivotGrid.pivotConfiguration.rows[0];
145+
const headerFormatter = jasmine.createSpy('headerFormatter')
146+
.and.callFake((value, dimension, rowData) => {
147+
expect(dimension).toEqual(jasmine.objectContaining({ memberName: rowDimension.memberName }));
148+
expect(rowData).toBeDefined();
149+
return `formatted-${value}`;
150+
});
151+
rowDimension.headerFormatter = headerFormatter;
152+
153+
pivotGrid.pipeTrigger++;
154+
pivotGrid.setupColumns();
155+
fixture.detectChanges();
156+
157+
const rowHeaders = fixture.debugElement.queryAll(By.directive(IgxPivotRowDimensionHeaderComponent));
158+
expect(rowHeaders[0].componentInstance.column.header).toBe('formatted-All');
159+
160+
const [rawValue, dimension, rowData] = headerFormatter.calls.mostRecent().args;
161+
expect(rawValue).toBe('All');
162+
expect(dimension).toEqual(jasmine.objectContaining({ memberName: rowDimension.memberName }));
163+
expect(rowData.dimensionValues).toBeDefined();
164+
});
165+
142166
it('should apply css class to cells from measures', () => {
143167
fixture.detectChanges();
144168
const pivotGrid = fixture.componentInstance.pivotGrid;
@@ -1241,7 +1265,8 @@ describe('IgxPivotGrid #pivotGrid', () => {
12411265
// check rows
12421266
const rows = pivotGrid.rowList.toArray();
12431267
expect(rows.length).toBe(5);
1244-
const expectedHeaders = ['All Periods', '2021', 'Q4', 'December', '12/08/2021'];
1268+
const formattedDate = Intl.DateTimeFormat(pivotGrid.locale, { dateStyle: 'short' }).format(new Date(2021, 11, 8));
1269+
const expectedHeaders = ['All Periods', '2021', 'Q4', 'December', formattedDate];
12451270
const rowHeaders = fixture.debugElement.queryAll(
12461271
By.directive(IgxPivotRowDimensionHeaderComponent));
12471272
const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header);

projects/igniteui-angular/grids/pivot-grid/src/pivot-row-dimension-content.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,11 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon
183183

184184
protected extractFromDimension(dim: IPivotDimension, rowData: IPivotGridGroupRecord) {
185185
const field = dim.memberName;
186-
const header = rowData?.dimensionValues.get(field);
186+
const rawHeader = rowData?.dimensionValues.get(field);
187+
let header = rawHeader;
188+
if (dim.headerFormatter != null) {
189+
header = dim.headerFormatter(rawHeader, dim, rowData) ?? rawHeader;
190+
}
187191
const col = this._createColComponent(field, header!, dim);
188192
return col;
189193
}

0 commit comments

Comments
 (0)