Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ All notable changes for each version of this project will be documented in this
- 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.
- Added `disableClear` input that allows hiding the clear button even when items are selected. Defaults to `false`.

- `IgxPivotGrid`
- 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.

### General

- `IgxOverlayService`
Expand Down
45 changes: 38 additions & 7 deletions projects/igniteui-angular/grids/core/src/pivot-grid-dimensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ export class IgxPivotDateDimension implements IPivotDimension {
/** @hidden @internal */
public memberName = 'AllPeriods';
public displayName: string;
/**
* Gets/Sets the locale used for date dimension member formatting (e.g. month names).
* When set, overrides the global I18nManager locale for this dimension.
* Set automatically by the pivot grid row pipe when the grid locale changes.
* @hidden @internal
*/
public locale?: string;
private _resourceStrings: IGridResourceStrings = null;
private _baseDimension: IPivotDimension;
private _options: IPivotDateDimensionOptions = {};
Expand Down Expand Up @@ -139,13 +146,16 @@ export class IgxPivotDateDimension implements IPivotDimension {
this.enabled = inBaseDimension.enabled;
this.displayName = inBaseDimension.displayName || this.resourceStrings.igx_grid_pivot_date_dimension_total;

const baseDimension = options.fullDate ? inBaseDimension : null;
const baseDimension: IPivotDimension = options.fullDate
? this.createLeafDateDimension(inBaseDimension)
: null;

const monthDimensionDef: IPivotDimension = {
memberName: 'Months',
memberFunction: (rec) => {
const recordValue = PivotUtil.extractValueFromDimension(inBaseDimension, rec);
const dateValue = recordValue ? getDateFormatter().createDateFromValue(recordValue) : null;
return recordValue ? getDateFormatter().formatDateTime(dateValue, undefined, { month: 'long'}) : rec['Months'];
const dateValue = (recordValue != null && recordValue !== '') ? getDateFormatter().createDateFromValue(recordValue) : null;
return dateValue ? getDateFormatter().formatDateTime(dateValue, this.locale, { month: 'long'}) : rec['Months'];
},
enabled: true,
childLevel: baseDimension
Expand All @@ -156,8 +166,8 @@ export class IgxPivotDateDimension implements IPivotDimension {
memberName: 'Quarters',
memberFunction: (rec) => {
const recordValue = PivotUtil.extractValueFromDimension(inBaseDimension, rec);
const dateValue = recordValue ? getDateFormatter().createDateFromValue(recordValue) : null;
return recordValue ? `Q` + Math.ceil((dateValue.getMonth() + 1) / 3) : rec['Quarters'];
const dateValue = (recordValue != null && recordValue !== '') ? getDateFormatter().createDateFromValue(recordValue) : null;
return dateValue ? `Q` + Math.ceil((dateValue.getMonth() + 1) / 3) : rec['Quarters'];
},
enabled: true,
childLevel: monthDimension
Expand All @@ -168,8 +178,8 @@ export class IgxPivotDateDimension implements IPivotDimension {
memberName: 'Years',
memberFunction: (rec) => {
const recordValue = PivotUtil.extractValueFromDimension(inBaseDimension, rec);
const dateValue = recordValue ? getDateFormatter().createDateFromValue(recordValue) : null;
return recordValue ? dateValue.getFullYear().toString() : rec['Years'];
const dateValue = (recordValue != null && recordValue !== '') ? getDateFormatter().createDateFromValue(recordValue) : null;
return dateValue ? dateValue.getFullYear().toString() : rec['Years'];
},
enabled: true,
childLevel: quarterDimension
Expand All @@ -185,6 +195,27 @@ export class IgxPivotDateDimension implements IPivotDimension {
}
}

private createLeafDateDimension(inBaseDimension: IPivotDimension): IPivotDimension {
if (inBaseDimension.headerFormatter) {
// User supplied their own formatter — use the dimension as-is.
return inBaseDimension;
}
// No user-supplied formatter: create a new dimension object with a locale-aware
// formatter that shows dates in short-date format.
const dateFormatter = getDateFormatter();
return {
...inBaseDimension,
headerFormatter: (value: any) => {
const hasValue = value !== null && value !== undefined && value !== '';
const dateValue = hasValue ? dateFormatter.createDateFromValue(value) : null;
if (dateValue) {
return dateFormatter.formatDateTime(dateValue, this.locale, { dateStyle: 'short' });
}
return hasValue ? String(value) : '';
}
};
}

/** @hidden @internal */
public memberFunction = (_data) => this.resourceStrings.igx_grid_pivot_date_dimension_total;
}
16 changes: 16 additions & 0 deletions projects/igniteui-angular/grids/core/src/pivot-grid.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,22 @@ export interface IPivotDimension {
/** @hidden @internal */
autoWidth?: number;
horizontalSummary? : boolean;
/* csTreatAsEvent: PivotDimensionFormatterEventHandler */
/* blazorOnlyScript */
/**
* Optional function to format the display value of a dimension header.
* Unlike `memberFunction`, this does not affect the data key used for grouping or sorting —
* it is applied when rendering the dimension header text (both row and column dimension headers).
* When set, the return value of this function is shown instead of the raw dimension value.
* Return `null` or `undefined` to fall back to the raw value.
*
* @example
* ```typescript
* // Display dates in a locale-aware short date format.
* { memberName: 'Date', enabled: true, headerFormatter: (value) => new Date(value).toLocaleDateString() }
* ```
*/
headerFormatter?: (value: any, dimension?: IPivotDimension, rowData?: IPivotGridGroupRecord) => string | null | undefined;
}

/* marshalByValue */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -995,8 +995,10 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
this.setupColumns();
// Bind to onResourceChange after the columns have initialized the first time to avoid premature initialization.
onResourceChangeHandle(this.destroy$, () => {
// Since the columns are kinda static, due to assigning DisplayName on init, they need to be regenerated.
this.setupColumns();
// Columns are kinda static, due to assigning DisplayName on init, they need to be regenerated.
// Use notifyDimensionChange to also increment pipeTrigger and run detectChanges synchronously,
// since this callback fires outside Angular's zone and markForCheck() alone is not enough.
this.notifyDimensionChange(true);
}, this);
});
if (this.valueChipTemplateDirective) {
Expand Down Expand Up @@ -2380,7 +2382,9 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
const ref = isGroup ?
createComponent(IgxColumnGroupComponent, { environmentInjector: this.envInjector, elementInjector: this.injector }) :
createComponent(IgxColumnComponent, { environmentInjector: this.envInjector, elementInjector: this.injector });
ref.instance.header = parent != null ? key.split(parent.header + this.pivotKeys.columnDimensionSeparator)[1] : key;
const rawHeader = parent != null ? key.split(parent.field + this.pivotKeys.columnDimensionSeparator)[1] : key;
const dim = value.dimension as IPivotDimension;
ref.instance.header = dim?.headerFormatter != null ? (dim.headerFormatter(rawHeader, dim, undefined) ?? rawHeader) : rawHeader;
ref.instance.field = key;
ref.instance.parent = parent;
if (value.dimension.width) {
Expand Down
14 changes: 14 additions & 0 deletions projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.pipes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
IPivotGridRecord,
IPivotKeys,
IPivotValue,
IgxPivotDateDimension,
PivotColumnDimensionsStrategy,
PivotGridType,
PivotRowDimensionsStrategy,
Expand Down Expand Up @@ -51,6 +52,19 @@ export class IgxPivotRowPipe implements PipeTransform {
// nothing to group and aggregate by ...
return [];
}
const locale = this.grid?.locale;
if (locale) {
for (const dim of enabledRows) {
if (dim instanceof IgxPivotDateDimension) {
dim.locale = locale;
}
}
for (const dim of enabledColumns) {
if (dim instanceof IgxPivotDateDimension) {
dim.locale = locale;
}
}
}
const rowStrategy = config.rowStrategy || PivotRowDimensionsStrategy.instance();
const data = cloneArray(collection, true);
return rowStrategy.process(data, enabledRows, config.values, cloneStrategy, pivotKeys);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ describe('IgxPivotGrid #pivotGrid', () => {
expect(actualDataTypeValue).toEqual('$71.89');
});

it('should provide context to dimension header formatter', () => {
const pivotGrid = fixture.componentInstance.pivotGrid;
const rowDimension = pivotGrid.pivotConfiguration.rows[0];
const headerFormatter = jasmine.createSpy('headerFormatter')
.and.callFake((value, dimension, rowData) => {
expect(dimension).toEqual(jasmine.objectContaining({ memberName: rowDimension.memberName }));
expect(rowData).toBeDefined();
return `formatted-${value}`;
});
rowDimension.headerFormatter = headerFormatter;

pivotGrid.pipeTrigger++;
pivotGrid.setupColumns();
fixture.detectChanges();

const rowHeaders = fixture.debugElement.queryAll(By.directive(IgxPivotRowDimensionHeaderComponent));
expect(rowHeaders[0].componentInstance.column.header).toBe('formatted-All');

const [rawValue, dimension, rowData] = headerFormatter.calls.mostRecent().args;
expect(rawValue).toBe('All');
expect(dimension).toEqual(jasmine.objectContaining({ memberName: rowDimension.memberName }));
expect(rowData.dimensionValues).toBeDefined();
});

it('should apply css class to cells from measures', () => {
fixture.detectChanges();
const pivotGrid = fixture.componentInstance.pivotGrid;
Expand Down Expand Up @@ -1245,7 +1269,8 @@ describe('IgxPivotGrid #pivotGrid', () => {
// check rows
const rows = pivotGrid.rowList.toArray();
expect(rows.length).toBe(5);
const expectedHeaders = ['All Periods', '2021', 'Q4', 'December', '12/08/2021'];
const formattedDate = Intl.DateTimeFormat(undefined, { dateStyle: 'short' }).format(new Date(2021, 11, 8));
const expectedHeaders = ['All Periods', '2021', 'Q4', 'December', formattedDate];
const rowHeaders = fixture.debugElement.queryAll(
By.directive(IgxPivotRowDimensionHeaderComponent));
const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon

protected extractFromDimension(dim: IPivotDimension, rowData: IPivotGridGroupRecord) {
const field = dim.memberName;
const header = rowData?.dimensionValues.get(field);
const rawHeader = rowData?.dimensionValues.get(field);
let header = rawHeader;
if (dim.headerFormatter != null) {
header = dim.headerFormatter(rawHeader, dim, rowData) ?? rawHeader;
}
const col = this._createColComponent(field, header, dim);
return col;
}
Expand Down
Loading