diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts index fae51e07748..a443455e39a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsBG_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts index 44ef453dd06..5277d85ed4d 100644 --- a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsCS_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts index 8994cd6371d..af6adda5945 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsDA_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts index df9fe3a5223..975a538b659 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsDE_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts index b307c7210be..32c431be944 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsES_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts index 9191c1ced7f..d14a7d4da2e 100644 --- a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsFR_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts index ef9e57611a2..a7c2f2bc413 100644 --- a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts @@ -151,7 +151,8 @@ const GridResourceStringsHU_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts index 66257b55bfd..ab2af817789 100644 --- a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsIT_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts index 89835022c16..692bd0afd97 100644 --- a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsJA_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts index ef5612d3a64..8ec504d29d0 100644 --- a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsKO_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; diff --git a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts index 94eaf4db937..0a49102c68b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsNB_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts index 08726683408..cc3d332f4d2 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsNL_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts index 17837c03e31..3227c59b144 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsPL_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts index 756771dc5c7..ab9bb5818ae 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsPT_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts index 4628d936430..cf5b66ee714 100644 --- a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsRO_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts index c3269c56772..3503d0451ea 100644 --- a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsSV_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts index c3d5aac6136..3f495bf810d 100644 --- a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsTR_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts index 7adc9eaf29b..01c8034e80b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsZHHANS_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts index 63b6c4bf147..f02d4021017 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts @@ -150,7 +150,8 @@ const GridResourceStringsZHHANT_: ExpandRequire = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; /** diff --git a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts index bb64e6ab182..849db7150e7 100644 --- a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts +++ b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts @@ -148,6 +148,7 @@ export interface IGridResourceStrings { igx_grid_pivot_column_drop_chip?: string; igx_grid_pivot_filter_drop_chip?: string; igx_grid_pivot_value_drop_chip?: string; + igx_grid_pivot_empty_message?: string; } export const GridResourceStringsEN: IGridResourceStrings = { @@ -299,5 +300,6 @@ export const GridResourceStringsEN: IGridResourceStrings = { igx_grid_pivot_row_drop_chip: 'Drop here to use as row', igx_grid_pivot_column_drop_chip: 'Drop here to use as column', igx_grid_pivot_filter_drop_chip: 'Drop here to use as filter', - igx_grid_pivot_value_drop_chip: 'Drop here to use as value' + igx_grid_pivot_value_drop_chip: 'Drop here to use as value', + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' }; diff --git a/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts b/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts index 6a09ca8c7e5..050ed0942a2 100644 --- a/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/pivot-strategy.ts @@ -1,6 +1,6 @@ import { GridType, PivotGridType } from '../grids/common/grid.interface'; -import { DEFAULT_PIVOT_KEYS, IPivotDimension, IPivotDimensionStrategy, IPivotKeys, IPivotValue, PivotDimensionType } from '../grids/pivot-grid/pivot-grid.interface'; +import { DEFAULT_PIVOT_KEYS, IPivotDimension, IPivotDimensionStrategy, IPivotGridRecord, IPivotKeys, IPivotValue, PivotDimensionType } from '../grids/pivot-grid/pivot-grid.interface'; import { PivotUtil } from '../grids/pivot-grid/pivot-util'; import { FilteringStrategy } from './filtering-strategy'; import { GridColumnDataType } from './data-util'; @@ -32,9 +32,9 @@ export class PivotRowDimensionsStrategy implements IPivotDimensionStrategy { rows: IPivotDimension[], values?: IPivotValue[], pivotKeys: IPivotKeys = DEFAULT_PIVOT_KEYS - ): any[] { + ): IPivotGridRecord[] { let hierarchies; - let data; + let data: IPivotGridRecord[]; const prevRowDims = []; let prevDim; let prevDimTopRecords = []; @@ -44,7 +44,7 @@ export class PivotRowDimensionsStrategy implements IPivotDimensionStrategy { if (currRows.length === 0) { hierarchies = PivotUtil.getFieldsHierarchy(collection, [{ memberName: '', enabled: true }], PivotDimensionType.Row, pivotKeys); // generate flat data from the hierarchies - data = PivotUtil.processHierarchy(hierarchies, collection[0] ?? [], pivotKeys, 0, true); + data = PivotUtil.processHierarchy(hierarchies, pivotKeys, 0, true); return data; } @@ -53,41 +53,12 @@ export class PivotRowDimensionsStrategy implements IPivotDimensionStrategy { // build hierarchies - groups and subgroups hierarchies = PivotUtil.getFieldsHierarchy(collection, [row], PivotDimensionType.Row, pivotKeys); // generate flat data from the hierarchies - data = PivotUtil.processHierarchy(hierarchies, collection[0] ?? [], pivotKeys, 0, true); + data = PivotUtil.processHierarchy(hierarchies, pivotKeys, 0, true); prevRowDims.push(row); prevDim = row; prevDimTopRecords = data; } else { - const newData = [...data]; - const curDimTopRecords = []; - for (let i = 0; i < newData.length; i++) { - const currData = newData[i][prevDim.memberName + '_' + pivotKeys.records]; - const leafData = PivotUtil.getDirectLeafs(currData, pivotKeys); - const hierarchyFields = PivotUtil - .getFieldsHierarchy(leafData, [row], PivotDimensionType.Row, pivotKeys); - const siblingData = PivotUtil - .processHierarchy(hierarchyFields, newData[i] ?? [], pivotKeys, 0); - PivotUtil.processSiblingProperties(newData[i], siblingData, pivotKeys); - - PivotUtil.processSubGroups(row, prevRowDims.slice(0), siblingData, pivotKeys); - if ((prevDimTopRecords[i].length != undefined && prevDimTopRecords[i].length < siblingData.length) || prevDimTopRecords.length < siblingData.length) { - // Add the sibling data as child records because the previous dimension contains more dense version of the previous dimension records. - newData[i][row.memberName + '_' + pivotKeys.records] = siblingData; - } else { - // Replace the current record with the sibling records because the current dimension is a denser version or produces the same amount of records. - newData.splice(i, 1, ...siblingData); - // Shift the prevDimTopRecords item to the right because of the previous row transforms the newData and increases the elements in newData - prevDimTopRecords.splice(siblingData.length, prevDimTopRecords.length - siblingData.length, ...prevDimTopRecords); - // Increase the index the amount of sibling record that replaces the current one. Subtract 1 because there is already i++ in the for cycle. - i += siblingData.length - 1; - } - // Add the current top sibling elements for the dimension - curDimTopRecords.push(cloneArray(siblingData, true)); - } - data = newData; - prevDim = row; - prevDimTopRecords = curDimTopRecords; - prevRowDims.push(row); + PivotUtil.processGroups(data, row, pivotKeys); } } return data; @@ -102,7 +73,7 @@ export class PivotColumnDimensionsStrategy implements IPivotDimensionStrategy { } public process( - collection: any[], + collection: IPivotGridRecord[], columns: IPivotDimension[], values: IPivotValue[], pivotKeys: IPivotKeys = DEFAULT_PIVOT_KEYS @@ -111,66 +82,35 @@ export class PivotColumnDimensionsStrategy implements IPivotDimensionStrategy { return res; } - private processHierarchy(collection, columns: IPivotDimension[], values, pivotKeys) { - const result = []; - collection.forEach(hierarchy => { + private processHierarchy(collection: IPivotGridRecord[], columns: IPivotDimension[], values, pivotKeys) { + const result: IPivotGridRecord[] = []; + collection.forEach(rec => { // apply aggregations based on the created groups and generate column fields based on the hierarchies - this.groupColumns(hierarchy, columns, values, pivotKeys); - if (hierarchy[pivotKeys.children] && hierarchy[pivotKeys.children].size) { - let flatCols = {}; - PivotUtil.flattenColumnHierarchy(hierarchy[pivotKeys.children], values, pivotKeys).forEach(o => { - delete o[pivotKeys.records]; - flatCols = { ...flatCols, ...o }; - }); - delete hierarchy[pivotKeys.children]; /* or we can keep it - and use when creating the columns in pivot grid instead of recreating it */ - const keys = Object.keys(hierarchy); - //remove all record keys from final data since we don't need them anymore. - hierarchy.processed = true; - keys.forEach(k => { - if (k.indexOf(pivotKeys.records) !== -1) { - if (hierarchy[k] && hierarchy[k].length > 0 && k !== pivotKeys.records) { - const unprocessed = hierarchy[k].filter(r => !r.processed); - this.processHierarchy(unprocessed, columns, values, pivotKeys); - } - //delete hierarchy[k]; - } - if (k === pivotKeys.level) { - delete hierarchy[k]; - } - }); - for (const property in flatCols) { - if (flatCols.hasOwnProperty(property)) { - hierarchy[property] = flatCols[property]; - } - } - result.push(hierarchy); - } else { - result.push(hierarchy); - } + this.groupColumns(rec, columns, values, pivotKeys); + result.push(rec); }); return result; } - private groupColumns(hierarchy, columns, values, pivotKeys) { - const children = hierarchy[pivotKeys.children]; - const hierarchyRecords = hierarchy[pivotKeys.records] ? hierarchy[pivotKeys.records] : [hierarchy]; - if (children) { - this.groupColumns(children, columns, values, pivotKeys); - } else if (hierarchyRecords) { - const leafRecords = this.getLeafs(hierarchyRecords, pivotKeys); - hierarchy[pivotKeys.children] = PivotUtil.getFieldsHierarchy(leafRecords, columns, PivotDimensionType.Column, pivotKeys); - if (hierarchy[pivotKeys.children].size) { - PivotUtil.applyAggregations(hierarchy[pivotKeys.children], values, pivotKeys); - } else { - hierarchy[pivotKeys.children].set('Root', leafRecords); - const aggrResult = PivotUtil.aggregate(leafRecords, values); - const keys = Object.keys(aggrResult); - keys.forEach(key => { - hierarchy[key] = aggrResult[key]; - }); - } + private groupColumns(rec: IPivotGridRecord, columns, values, pivotKeys) { + const children = rec.children; + if (children && children.size > 0) { + children.forEach((childRecs, key) => { + if (childRecs) { + childRecs.forEach(child => { + this.groupColumns(child, columns, values, pivotKeys); + }) + } + }); + } + this.applyAggregates(rec, columns, values, pivotKeys); + } + + private applyAggregates(rec, columns, values, pivotKeys) { + const leafRecords = this.getLeafs(rec.records, pivotKeys); + const hierarchy = PivotUtil.getFieldsHierarchy(leafRecords, columns, PivotDimensionType.Column, pivotKeys); + PivotUtil.applyAggregations(rec, hierarchy, values, pivotKeys) } private getLeafs(records, pivotKeys) { @@ -204,7 +144,7 @@ export class DimensionValuesFilteringStrategy extends FilteringStrategy { protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: PivotGridType): any { const config = grid.pivotConfiguration; - const allDimensions = config.rows.concat(config.columns).concat(config.filters).filter(x => x !== null && x !== undefined); + const allDimensions = grid.allDimensions; const enabledDimensions = allDimensions.filter(x => x && x.enabled); const dim = PivotUtil.flatten(enabledDimensions).find(x => x.memberName === fieldName); return PivotUtil.extractValueFromDimension(dim, rec); @@ -226,8 +166,7 @@ export class DefaultPivotSortingStrategy extends DefaultSortingStrategy { isTime?: boolean, grid?: PivotGridType) { const key = fieldName; - const config = grid.pivotConfiguration; - const allDimensions = config.rows.concat(config.columns).concat(config.filters).filter(x => x !== null && x !== undefined); + const allDimensions = grid.allDimensions; const enabledDimensions = allDimensions.filter(x => x && x.enabled); this.dimension = PivotUtil.flatten(enabledDimensions).find(x => x.memberName === key); const reverse = (dir === SortingDirection.Desc ? -1 : 1); @@ -247,3 +186,26 @@ export class DefaultPivotSortingStrategy extends DefaultSortingStrategy { return resolvedValue; } } + +export class DefaultPivotGridRecordSortingStrategy extends DefaultSortingStrategy { + protected static _instance: DefaultPivotGridRecordSortingStrategy = null; + public static instance(): DefaultPivotGridRecordSortingStrategy { + return this._instance || (this._instance = new this()); + } + public sort(data: any[], + fieldName: string, + dir: SortingDirection, + ignoreCase: boolean, + valueResolver: (obj: any, key: string, isDate?: boolean) => any, + isDate?: boolean, + isTime?: boolean, + grid?: PivotGridType) { + const reverse = (dir === SortingDirection.Desc ? -1 : 1); + const cmpFunc = (obj1, obj2) => this.compareObjects(obj1, obj2, fieldName, reverse, ignoreCase, this.getFieldValue, isDate, isTime); + return this.arraySort(data, cmpFunc); + } + + protected getFieldValue(obj: IPivotGridRecord, key: string, isDate: boolean = false, isTime: boolean = false): any { + return obj.aggregationValues.get(key); + } +} diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 6c35242174d..f555aa05ac5 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -626,6 +626,7 @@ export interface HierarchicalGridType extends GridType { export interface PivotGridType extends GridType { pivotConfiguration: IPivotConfiguration; + allDimensions: IPivotDimension[], showPivotConfigurationUI: boolean; columnDimensions: IPivotDimension[]; rowDimensions: IPivotDimension[]; @@ -637,12 +638,12 @@ export interface PivotGridType extends GridType { setupColumns(): void; toggleRow(rowID: any): void; resolveDataTypes(field: any): GridColumnDataType; - resolveRowDimensionWidth(dim: IPivotDimension): number; moveDimension(dimension: IPivotDimension, targetCollectionType: PivotDimensionType, index? : number); getDimensionsByType(dimension: PivotDimensionType); toggleDimension(dimension: IPivotDimension); toggleValue(value: IPivotValue); moveValue(value: IPivotValue, index?: number); + rowDimensionWidthToPixels(dim: IPivotDimension): number; dimensionsChange: EventEmitter; valuesChange: EventEmitter; pivotKeys: IPivotKeys; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts index f2a9a826e5a..fa5fd23585e 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts @@ -306,9 +306,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { const anyFiltered = this.esf.listData.some(i => i.isFiltered); const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); - if (anyFiltered && anyUnfiltered) { - searchAllBtn.indeterminate = true; - } + searchAllBtn.indeterminate = anyFiltered && anyUnfiltered; this.esf.listData.forEach(i => i.isSelected = i.isFiltered); this.displayedListData = this.esf.listData; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 870c43f97d0..95685cbc2c8 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -605,7 +605,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent if (operand instanceof FilteringExpressionsTree) { const columnExprTree = operand as FilteringExpressionsTree; if (columnExprTree.fieldName === this.column.field) { - break; + continue; } } expressionsTree.filteringOperands.push(operand); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts index 58aee1172c6..80c2cc24271 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts @@ -168,15 +168,13 @@ export class IgxFilteringService implements OnDestroy { const filteringIgnoreCase = ignoreCase || (col ? col.filteringIgnoreCase : false); const filteringTree = grid.filteringExpressionsTree; - const columnFilteringExpressionsTree = grid.filteringExpressionsTree.find(field) as IFilteringExpressionsTree; + const columnFilteringExpressionsTree = filteringTree.find(field) as IFilteringExpressionsTree; conditionOrExpressionTree = conditionOrExpressionTree ?? columnFilteringExpressionsTree; const fieldFilterIndex = filteringTree.findIndex(field); - if (fieldFilterIndex > -1) { - filteringTree.filteringOperands.splice(fieldFilterIndex, 1); - } + const newFilteringTree: FilteringExpressionsTree = this.prepare_filtering_expression(filteringTree, field, value, conditionOrExpressionTree, - filteringIgnoreCase, fieldFilterIndex, true); + filteringIgnoreCase, fieldFilterIndex, true); const eventArgs: IFilteringEventArgs = { owner: grid, @@ -522,12 +520,11 @@ export class IgxFilteringService implements OnDestroy { if (fieldFilterIndex > -1) { filteringTree.filteringOperands.splice(fieldFilterIndex, 1); } - this.prepare_filtering_expression(filteringTree, fieldName, term, conditionOrExpressionsTree, ignoreCase, fieldFilterIndex); grid.filteringExpressionsTree = filteringTree; } - /** Modifies the filteringState object to contain the newly added fitering conditions/expressions. + /** Modifies the filteringState object to contain the newly added filtering conditions/expressions. * If createNewTree is true, filteringState will not be modified (because it directly affects the grid.filteringExpressionsTree), * but a new object is created and returned. */ @@ -540,7 +537,6 @@ export class IgxFilteringService implements OnDestroy { insertAtIndex = -1, createNewTree = false): FilteringExpressionsTree { - const oldExpressionsTreeIndex = filteringState.findIndex(fieldName); const expressionsTree = conditionOrExpressionsTree instanceof FilteringExpressionsTree ? conditionOrExpressionsTree as IFilteringExpressionsTree : null; const condition = conditionOrExpressionsTree instanceof FilteringExpressionsTree ? @@ -550,20 +546,18 @@ export class IgxFilteringService implements OnDestroy { const newExpressionsTree: FilteringExpressionsTree = createNewTree ? new FilteringExpressionsTree(filteringState.operator, filteringState.fieldName) : filteringState as FilteringExpressionsTree; - if (oldExpressionsTreeIndex === -1) { - // no expressions tree found for this field - if (expressionsTree) { - if (insertAtIndex > -1) { - newExpressionsTree.filteringOperands.splice(insertAtIndex, 0, expressionsTree); - } else { - newExpressionsTree.filteringOperands.push(expressionsTree); - } - } else if (condition) { - // create expressions tree for this field and add the new expression to it - const newExprTree: FilteringExpressionsTree = new FilteringExpressionsTree(filteringState.operator, fieldName); - newExprTree.filteringOperands.push(newExpression); - newExpressionsTree.filteringOperands.push(newExprTree); + // no expressions tree found for this field + if (expressionsTree) { + if (insertAtIndex > -1) { + newExpressionsTree.filteringOperands[insertAtIndex] = expressionsTree; + } else { + newExpressionsTree.filteringOperands.push(expressionsTree); } + } else if (condition) { + // create expressions tree for this field and add the new expression to it + const newExprTree: FilteringExpressionsTree = new FilteringExpressionsTree(filteringState.operator, fieldName); + newExprTree.filteringOperands.push(newExpression); + newExpressionsTree.filteringOperands.push(newExprTree); } return newExpressionsTree; @@ -580,7 +574,7 @@ export class IgxFilteringService implements OnDestroy { if (expressionsTree.operator === FilteringLogic.Or) { const andOperatorsCount = this.getChildAndOperatorsCount(expressionsTree); - // having more that 'And' and operator in the sub-tree means that the filter could not be represented without parentheses. + // having more than one 'And' operator in the sub-tree means that the filter could not be represented without parentheses. return andOperatorsCount > 1; } 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 c383dc0d778..841e9a66de4 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -3551,7 +3551,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements public _zoneBegoneListeners() { this.zone.runOutsideAngular(() => { this.verticalScrollContainer.getScroll().addEventListener('scroll', this.verticalScrollHandler.bind(this)); - this.headerContainer.getScroll().addEventListener('scroll', this.horizontalScrollHandler.bind(this)); + this.headerContainer?.getScroll().addEventListener('scroll', this.horizontalScrollHandler.bind(this)); fromEvent(window, 'resize').pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next()); resizeObservable(this.nativeElement).pipe(takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next()); }); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 327b953d351..3592cea2a52 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -47,9 +47,9 @@ import { import { GridSelectionMode, FilterMode } from '../common/enums'; import { ControlsFunction } from '../../test-utils/controls-functions.spec'; import { FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy'; -import { IgxCalendarComponent } from '../../calendar/calendar.component'; import { IgxInputGroupComponent } from '../../input-group/public_api'; import { formatDate } from '../../core/utils'; +import { IgxCalendarComponent } from '../../calendar/calendar.component'; const DEBOUNCETIME = 30; const FILTER_UI_ROW = 'igx-grid-filtering-row'; @@ -4346,8 +4346,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { tick(100); fix.detectChanges(); verifyExcelStyleFilterAvailableOptions(fix, - ['Select All', '(Blanks)', 'False', 'True'], - [null, true, true, false]); + ['Select All', '(Blanks)', 'False'], + [true, true, true]); GridFunctions.clickExcelFilterIcon(fix, 'ProductName'); tick(100); diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-filtering.service.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-filtering.service.ts index 900c7a4e1cf..ebbecc0229d 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-filtering.service.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-filtering.service.ts @@ -19,10 +19,10 @@ export class IgxPivotFilteringService extends IgxFilteringService { public clear_filter(fieldName: string) { super.clear_filter(fieldName); - const grid = this.grid; - const config = (grid as IgxPivotGridComponent).pivotConfiguration; - const allDimensions = PivotUtil.flatten(config.rows.concat(config.columns).concat(config.filters).filter(x => x !== null && x !== undefined)); - const dim = allDimensions.find(x => x.memberName === fieldName || x.member === fieldName); + const grid = this.grid as IgxPivotGridComponent; + const allDimensions = grid.allDimensions; + const allDimensionsFlat = PivotUtil.flatten(allDimensions); + const dim = allDimensionsFlat.find(x => x.memberName === fieldName); dim.filter = undefined; grid.filteringPipeTrigger++; if (allDimensions.indexOf(dim) !== -1) { diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html index e0b5b858024..e4d6d82db49 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.html @@ -35,7 +35,8 @@ | pivotGridSort:pivotConfiguration:sortStrategy:id:pipeTrigger | pivotGridRow:pivotConfiguration:expansionStates:pipeTrigger:sortingExpressions | pivotGridColumn:pivotConfiguration:expansionStates:pipeTrigger:sortingExpressions - | pivotGridColumnSort:sortingExpressions:sortStrategy:pipeTrigger:pivotKeys + | pivotGridAutoTransform:pivotConfiguration:pipeTrigger + | pivotGridColumnSort:sortingExpressions:sortStrategy:pipeTrigger | pivotGridRowExpansion:pivotConfiguration:expansionStates:defaultExpandState:pipeTrigger" let-rowIndex="index" [igxForScrollOrientation]="'vertical'" [igxForScrollContainer]='verticalScroll' [igxForContainerSize]='calcHeight' @@ -126,32 +127,40 @@ -
+
+ | pivotGridCellMerging:pivotConfiguration:dim:pipeTrigger" + let-rowIndex="index" [igxForScrollOrientation]="'vertical'" [igxForScrollContainer]='verticalScroll' + [igxForContainerSize]='calcHeight' + [igxForItemSize]="renderedRowHeight" + [igxForSizePropName]='"height"' + #verticalRowDimScrollContainer> + [dimension]='rowData.dimensions[dimIndex]' + [rootDimension]='dim' + [rowIndex]='rowIndex' [rowData]='rowData' + [density]="displayDensity" [width]="rowDimensionWidthToPixels(dim)">
-
+
+ [rootDimension]='emptyRowDimension' + [rowIndex]='0' [rowData]='dataView[0]' + [density]="displayDensity" [width]="rowDimensionWidthToPixels(emptyRowDimension)">
+ + + {{resourceStrings.igx_grid_pivot_empty_message}} + + +
diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts index ccfdba0da6c..ed12ed37be8 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts @@ -135,7 +135,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } public get pivotConfiguration() { - return this._pivotConfiguration; + return this._pivotConfiguration || { rows: null, columns: null, values: null, filters: null }; } @Input() @@ -168,7 +168,17 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni */ @HostBinding('class.igx-grid__pivot--super-compact') @Input() - public superCompactMode = false; + public get superCompactMode() { + return this._superCompactMode; + } + + public set superCompactMode(value) { + Promise.resolve().then(() => { + // wait for the current detection cycle to end before triggering a new one. + this._superCompactMode = value; + this.cdr.detectChanges(); + }); + } /** * Returns the theme of the component. @@ -223,6 +233,24 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni @ViewChild(IgxPivotGridColumnResizerComponent) public resizeLine: IgxPivotGridColumnResizerComponent; + /** + * @hidden @internal + */ + @ViewChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent }) + public excelStyleFilteringComponents: QueryList; + + /** + * @hidden @internal + */ + @ViewChildren(IgxPivotRowDimensionContentComponent) + protected rowDimensionContentCollection: QueryList; + + /** + * @hidden @internal + */ + @ViewChildren('verticalRowDimScrollContainer', { read: IgxGridForOfDirective }) + public verticalRowDimScrollContainers: QueryList>; + /** * @hidden @interal */ @@ -318,11 +346,6 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni */ @Output() public rowDragStart = new EventEmitter(); - /** - * @hidden @internal - */ - @ViewChildren(IgxGridExcelStyleFilteringComponent, { read: IgxGridExcelStyleFilteringComponent }) - public excelStyleFilteringComponents: QueryList; /** * @hidden @internal @@ -393,7 +416,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni /** * @hidden @internal */ - private _emptyRowDimension: IPivotDimension = { memberName: '', enabled: true }; + private _emptyRowDimension: IPivotDimension = { memberName: '', enabled: true, level: 0 }; public get emptyRowDimension(): IPivotDimension { return this._emptyRowDimension; } @@ -403,7 +426,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni private _filteredData; private _pivotConfiguration: IPivotConfiguration = { rows: null, columns: null, values: null, filters: null }; private p_id = `igx-pivot-grid-${NEXT_ID++}`; - + private _superCompactMode = false; /** * Gets/Sets the default expand state for all rows. @@ -553,7 +576,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni let currDim = dim; let shouldBreak = false; do { - const key = PivotUtil.getRecordKey(record, currDim, prev, this.pivotKeys); + const key = PivotUtil.getRecordKey(record, currDim); if (this.selectionService.isPivotRowSelected(key) && !selectedRowIds.find(x => x === record)) { selectedRowIds.push(record); shouldBreak = true; @@ -679,14 +702,26 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni public uniqueDimensionValuesStrategy(column: IgxColumnComponent, exprTree: IFilteringExpressionsTree, done: (uniqueValues: any[]) => void) { const config = this.pivotConfiguration; - const allDimensions = config.rows.concat(config.columns).concat(config.filters).filter(x => x !== null && x !== undefined); - const enabledDimensions = allDimensions.filter(x => x && x.enabled); + const enabledDimensions = this.allDimensions.filter(x => x && x.enabled); const dim = PivotUtil.flatten(enabledDimensions).find(x => x.memberName === column.field); if (dim) { this.getDimensionData(dim, exprTree, uniqueValues => done(uniqueValues)); } } + /** + * Gets the full list of dimensions. + * + * @example + * ```typescript + * const dimensions = this.grid.allDimensions; + * ``` + */ + public get allDimensions() { + const config = this.pivotConfiguration; + return (config.rows || []).concat((config.columns || [])).concat(config.filters || []).filter(x => x !== null && x !== undefined); + } + public getDimensionData(dim: IPivotDimension, dimExprTree: IFilteringExpressionsTree, done: (colVals: any[]) => void) { @@ -819,11 +854,11 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } public get pivotRowWidths() { - return this.rowDimensions.length ? this.rowDimensions.reduce((accumulator, dim) => accumulator + this.resolveRowDimensionWidth(dim), 0) : - this.resolveRowDimensionWidth(this.emptyRowDimension); + return this.rowDimensions.length ? this.rowDimensions.reduce((accumulator, dim) => accumulator + this.rowDimensionWidthToPixels(dim), 0) : + this.rowDimensionWidthToPixels(this.emptyRowDimension); } - public resolveRowDimensionWidth(dim: IPivotDimension): number { + public rowDimensionWidthToPixels(dim: IPivotDimension): number { if (!dim.width) { return MINIMUM_COLUMN_WIDTH; } @@ -835,12 +870,16 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } } + public reverseDimensionWidthToPercent(width: number): number { + return (width * 100 / this.calcWidth); + } + public get rowDimensions() { - return this.pivotConfiguration.rows.filter(x => x.enabled) || []; + return this.pivotConfiguration.rows?.filter(x => x.enabled) || []; } public get columnDimensions() { - return this.pivotConfiguration.columns.filter(x => x.enabled) || []; + return this.pivotConfiguration.columns?.filter(x => x.enabled) || []; } public get filterDimensions() { @@ -848,7 +887,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } public get values() { - return this.pivotConfiguration.values.filter(x => x.enabled) || []; + return this.pivotConfiguration.values?.filter(x => x.enabled) || []; } public toggleColumn(col: IgxColumnComponent) { @@ -1335,9 +1374,18 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } } + public resizeRowDimensionPixels(dimension: IPivotDimension, newWidth: number) { + const isPercentageWidth = dimension.width && typeof dimension.width === 'string' && dimension.width.indexOf('%') !== -1; + if (isPercentageWidth) { + dimension.width = this.reverseDimensionWidthToPercent(newWidth).toFixed(2) + '%'; + } else { + dimension.width = newWidth + 'px'; + } - @ViewChildren(IgxPivotRowDimensionContentComponent) - protected rowDimensionContentCollection: QueryList; + // Notify the grid to reflow, to update if horizontal scrollbar needs to be rendered/removed. + this.pipeTrigger++; + this.cdr.detectChanges(); + } protected getDimensionType(dimension: IPivotDimension): PivotDimensionType { return PivotUtil.flatten(this.pivotConfiguration.rows).indexOf(dimension) !== -1 ? PivotDimensionType.Row : @@ -1383,7 +1431,8 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni groupColumn.children.filter(x => x.columnGroup && x.children.filter(y => !y.columnGroup).length === 0) : groupColumn.children.filter(x => x.columnGroup); childrenTotal.forEach(group => { - if (state) { + const newState = this.columnGroupStates.get(group.field) || state; + if (newState) { group.headerTemplate = this.headerTemplate; } else { group.headerTemplate = undefined; @@ -1391,7 +1440,8 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni }); if (!groupColumn.hidden && childrenSubgroups.length > 0) { childrenSubgroups.forEach(group => { - this.resolveToggle(group, state); + const newState = this.columnGroupStates.get(group.field) || state; + this.resolveToggle(group, newState); }); } } @@ -1423,12 +1473,6 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni super.horizontalScrollHandler(event); } - /** - * @hidden @internal - */ - @ViewChildren('verticalRowDimScrollContainer', { read: IgxGridForOfDirective }) - public verticalRowDimScrollContainers: QueryList>; - protected verticalScrollHandler(event) { super.verticalScrollHandler(event); this.verticalRowDimScrollContainers.forEach(x => { @@ -1491,9 +1535,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } protected generateDimensionColumns(): IgxColumnComponent[] { - const config = this.pivotConfiguration; - const allDimensions = config.rows.concat(config.columns).concat(config.filters).filter(x => x !== null && x !== undefined); - const leafFields = PivotUtil.flatten(allDimensions, 0).filter(x => !x.childLevel).map(x => x.memberName); + const leafFields = PivotUtil.flatten(this.allDimensions, 0).filter(x => !x.childLevel).map(x => x.memberName); const columns = []; const factory = this.resolver.resolveComponentFactory(IgxColumnComponent); leafFields.forEach((field) => { @@ -1666,4 +1708,33 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni public getPropName(dim: IPivotDimension) { return !!dim ?? dim.memberName + this.pivotKeys.rowDimensionSeparator + 'height'; } + + /** + * @hidden @internal + */ + @ViewChild('emptyPivotGridTemplate', { read: TemplateRef, static: true }) + public defaultEmptyPivotGridTemplate: TemplateRef; + + /** + * Gets/Sets a custom template when pivot grid is empty. + * + * @example + * ```html + * + * ``` + */ + @Input() + public emptyPivotGridTemplate: TemplateRef; + + /** + * @hidden @internal + */ + public get template(): TemplateRef { + const allEnabledDimensions = this.rowDimensions.concat(this.columnDimensions); + if (allEnabledDimensions.length === 0 && this.values.length === 0) { + // no enabled values and dimensions + return this.emptyPivotGridTemplate || this.defaultEmptyPivotGridTemplate; + } + super.template; + } } diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.interface.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.interface.ts index 19734088f82..7b62e160fc9 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.interface.ts @@ -23,18 +23,18 @@ export interface IDimensionsChange { dimensionCollectionType: PivotDimensionType } - /** - * Event emitted when values list is changed. - */ +/** +* Event emitted when values list is changed. +*/ export interface IValuesChange { /** The new list of values. */ values: IPivotValue[] } - /** - * Interface describing Pivot data processing for dimensions. - * Should contain a process method and return records hierarchy based on the provided dimensions. - */ +/** +* Interface describing Pivot data processing for dimensions. +* Should contain a process method and return records hierarchy based on the provided dimensions. +*/ export interface IPivotDimensionStrategy { process(collection: any, dimensions: IPivotDimension[], @@ -42,16 +42,16 @@ export interface IPivotDimensionStrategy { pivotKeys?: IPivotKeys): any[]; } - /** - * Interface describing a PivotAggregation function. - * Accepts an array of extracted data members and a array of the original data records. - */ +/** +* Interface describing a PivotAggregation function. +* Accepts an array of extracted data members and a array of the original data records. +*/ export type PivotAggregation = (members: any[], data: any[]) => any; - /** - * Interface describing a IPivotAggregator class. - * Used for specifying custom aggregator lists. - */ +/** +* Interface describing a IPivotAggregator class. +* Used for specifying custom aggregator lists. +*/ export interface IPivotAggregator { /** Aggregation unique key. */ key: string; @@ -64,9 +64,9 @@ export interface IPivotAggregator { aggregator: (members: any[], data?: any[]) => any; } - /** - * Configuration of the pivot grid. - */ +/** +* Configuration of the pivot grid. +*/ export interface IPivotConfiguration { /** A strategy to transform the rows. */ rowStrategy?: IPivotDimensionStrategy | null; @@ -84,9 +84,9 @@ export interface IPivotConfiguration { pivotKeys?: IPivotKeys; } - /** - * Configuration of a pivot dimension. - */ +/** +* Configuration of a pivot dimension. +*/ export interface IPivotDimension { /** Allows defining a hierarchy when multiple sub groups need to be extracted from single member. */ childLevel?: IPivotDimension; @@ -110,10 +110,12 @@ export interface IPivotDimension { dataType?: GridColumnDataType; /** The width of the dimension cells to be rendered.Can be pixel or %. */ width?: string; + /** Level of the dimension. */ + level?: number; } - /** - * Configuration of a pivot value aggregation. - */ +/** +* Configuration of a pivot value aggregation. +*/ export interface IPivotValue { /** Field name to use in order to extract value. */ member: string; @@ -183,3 +185,23 @@ export interface PivotRowHeaderGroupType { headerID: string; grid: any; } + +export interface IPivotGridRecord { + /** Gets/Sets the group value associated with the related row dimension by its memberName. **/ + dimensionValues: Map; + /** Gets/Sets the aggregation value associated with the value path. Value path depends on configured column dimension hierarchy and values.**/ + aggregationValues: Map; + /** List of children records in case any row dimension member contain a hierarchy. Each dimension member contains its own hierarchy, which you can get by its memberName. **/ + children?: Map; + /** List of original data records associated with the current pivoted data. **/ + records?: any[]; + /** Record level**/ + level?: number; + /** List of dimensions associated with the record.**/ + dimensions: IPivotDimension[]; +} + +export interface IPivotGridGroupRecord extends IPivotGridRecord { + height?: number; + rowSpan?: number; +} diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.module.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.module.ts index d3142dc966c..63eefbeabac 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.module.ts @@ -3,7 +3,7 @@ import { IgxGridModule } from '../grid/grid.module'; import { IgxPivotGridComponent } from './pivot-grid.component'; import { IgxPivotRowComponent } from './pivot-row.component'; import { IgxPivotRowPipe, IgxPivotColumnPipe, IgxPivotGridFilterPipe, - IgxPivotRowExpansionPipe, IgxPivotGridSortingPipe, IgxPivotGridColumnSortingPipe, IgxPivotCellMergingPipe } from './pivot-grid.pipes'; + IgxPivotRowExpansionPipe, IgxPivotGridSortingPipe, IgxPivotGridColumnSortingPipe, IgxPivotCellMergingPipe, IgxPivotAutoTransform } from './pivot-grid.pipes'; import { IgxGridComponent } from '../grid/grid.component'; import { IgxPivotHeaderRowComponent } from './pivot-header-row.component'; import { IgxPivotRowDimensionContentComponent } from './pivot-row-dimension-content.component'; @@ -23,6 +23,7 @@ import { IgxPivotRowDimensionHeaderComponent } from './pivot-row-dimension-heade IgxPivotRowDimensionHeaderGroupComponent, IgxPivotRowPipe, IgxPivotRowExpansionPipe, + IgxPivotAutoTransform, IgxPivotColumnPipe, IgxPivotGridFilterPipe, IgxPivotGridSortingPipe, @@ -38,6 +39,7 @@ import { IgxPivotRowDimensionHeaderComponent } from './pivot-row-dimension-heade IgxPivotRowDimensionHeaderComponent, IgxPivotRowDimensionHeaderGroupComponent, IgxPivotRowExpansionPipe, + IgxPivotAutoTransform, IgxPivotRowPipe, IgxPivotColumnPipe, IgxPivotGridFilterPipe, diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts index b317d044bc1..0924e8cb659 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.spec.ts @@ -4,7 +4,7 @@ import { IgxNumberSummaryOperand } from '../summaries/grid-summary'; import { IgxPivotDateDimension } from './pivot-grid-dimensions'; import { IgxPivotNumericAggregate } from './pivot-grid-aggregate'; import { IPivotConfiguration } from './pivot-grid.interface'; -import { IgxPivotColumnPipe, IgxPivotRowExpansionPipe, IgxPivotRowPipe } from './pivot-grid.pipes'; +import { IgxPivotAutoTransform, IgxPivotColumnPipe, IgxPivotRowExpansionPipe, IgxPivotRowPipe } from './pivot-grid.pipes'; import { PivotGridFunctions } from '../../test-utils/pivot-grid-functions.spec'; import { DATA } from 'src/app/shared/pivot-data'; @@ -12,6 +12,7 @@ describe('Pivot pipes #pivotGrid', () => { let rowPipe: IgxPivotRowPipe; let rowStatePipe: IgxPivotRowExpansionPipe; let columnPipe: IgxPivotColumnPipe; + let autoTransformPipe: IgxPivotAutoTransform; let expansionStates: Map; let data: any[]; let pivotConfig: IPivotConfiguration; @@ -69,28 +70,28 @@ describe('Pivot pipes #pivotGrid', () => { rowPipe = new IgxPivotRowPipe(); rowStatePipe = new IgxPivotRowExpansionPipe(); columnPipe = new IgxPivotColumnPipe(); + autoTransformPipe = new IgxPivotAutoTransform(); }); it('transforms flat data to pivot data', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, expansionStates); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - expect(rowStatePipeResult).toEqual([ - { All: 2127, 'All-Bulgaria': 774, 'All-USA': 829, 'All-Uruguay': 524, AllCategory: 'All', AllCategory_level: 0 }, - { ProductCategory: 'Clothing', All: 1526, 'All-Bulgaria': 774, 'All-USA': 296, 'All-Uruguay': 456, ProductCategory_level: 1 }, - { - ProductCategory: 'Bikes', All: 68, 'All-Uruguay': 68, ProductCategory_level: 1, UnitPrice: 3.56, SellerName: 'Lydia', Country: 'Uruguay', - Date: '01/06/2020', UnitsSold: 68 - }, - { - ProductCategory: 'Accessories', All: 293, 'All-USA': 293, ProductCategory_level: 1, UnitPrice: 85.58, SellerName: 'David', Country: 'USA', - Date: '04/07/2021', UnitsSold: 293 - }, - { - ProductCategory: 'Components', All: 240, 'All-USA': 240, ProductCategory_level: 1, UnitPrice: 18.13, SellerName: 'John', Country: 'USA', - Date: '12/08/2021', UnitsSold: 240 - } - ]); + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + expect(dimensionValues).toEqual([ + { 'AllCategory': 'All' }, + { 'ProductCategory': 'Clothing' }, + { 'ProductCategory': 'Bikes' }, + { 'ProductCategory': 'Accessories' }, + { 'ProductCategory': 'Components' }]); + + const aggregations = PivotGridFunctions.getAggregationValues(rowStatePipeResult); + expect(aggregations).toEqual([ + { 'All-Bulgaria': 774, 'All-USA': 829, 'All-Uruguay': 524, 'All': 2127 }, + { 'All-Bulgaria': 774, 'All-USA': 296, 'All-Uruguay': 456, 'All': 1526 }, + { 'All-Uruguay': 68, 'All': 68 }, + { 'All-USA': 293, 'All': 293 }, + { 'All-USA': 240, 'All': 240 }]); }); it('transforms flat data to pivot data single row dimension and no children are defined', () => { @@ -99,94 +100,15 @@ describe('Pivot pipes #pivotGrid', () => { enabled: true }]; const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); - expect(rowPipeResult).toEqual([ - { - ProductCategory: 'Clothing', records: [ - { - ProductCategory: 'Clothing', UnitPrice: 12.81, SellerName: 'Stanley', - Country: 'Bulgaria', Date: '01/01/2021', UnitsSold: 282 - }, - { - ProductCategory: 'Clothing', UnitPrice: 49.57, SellerName: 'Elisa', - Country: 'USA', Date: '01/05/2019', UnitsSold: 296 - }, - { - ProductCategory: 'Clothing', UnitPrice: 68.33, SellerName: 'Larry', - Country: 'Uruguay', Date: '05/12/2020', UnitsSold: 456 - }, - { - ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Walter', - Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 492 - }], - ProductCategory_records: [ - { - ProductCategory: 'Clothing', UnitPrice: 12.81, SellerName: 'Stanley', - Country: 'Bulgaria', Date: '01/01/2021', UnitsSold: 282 - }, - { - ProductCategory: 'Clothing', UnitPrice: 49.57, SellerName: 'Elisa', - Country: 'USA', Date: '01/05/2019', UnitsSold: 296 - }, - { - ProductCategory: 'Clothing', UnitPrice: 68.33, SellerName: 'Larry', - Country: 'Uruguay', Date: '05/12/2020', UnitsSold: 456 - }, - { - ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Walter', - Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 492 - } - ], level: 0 - }, - { - ProductCategory: 'Bikes', records: [ - { - ProductCategory: 'Bikes', UnitPrice: 3.56, SellerName: 'Lydia', - Country: 'Uruguay', Date: '01/06/2020', UnitsSold: 68 - } - ], ProductCategory_records: [ - { - ProductCategory: 'Bikes', UnitPrice: 3.56, SellerName: 'Lydia', - Country: 'Uruguay', Date: '01/06/2020', UnitsSold: 68 - } - ], level: 0 - }, - { - ProductCategory: 'Accessories', records: [ - { - ProductCategory: 'Accessories', UnitPrice: 85.58, SellerName: 'David', - Country: 'USA', Date: '04/07/2021', UnitsSold: 293 - } - ], ProductCategory_records: [ - { - ProductCategory: 'Accessories', UnitPrice: 85.58, SellerName: 'David', - Country: 'USA', Date: '04/07/2021', UnitsSold: 293 - } - ], level: 0 - }, - { - ProductCategory: 'Components', records: [ - { - ProductCategory: 'Components', UnitPrice: 18.13, SellerName: 'John', - Country: 'USA', Date: '12/08/2021', UnitsSold: 240 - } - ], ProductCategory_records: [ - { - ProductCategory: 'Components', UnitPrice: 18.13, SellerName: 'John', - Country: 'USA', Date: '12/08/2021', UnitsSold: 240 - } - ], level: 0 - }]); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, expansionStates); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - expect(rowStatePipeResult).toEqual([ - { - ProductCategory: 'Clothing', All: 1526, 'All-Bulgaria': 774, 'All-USA': 296, - 'All-Uruguay': 456, ProductCategory_level: 0 - }, - { ProductCategory: 'Bikes', All: 68, 'All-Uruguay': 68, ProductCategory_level: 0 }, - { ProductCategory: 'Accessories', All: 293, 'All-USA': 293, ProductCategory_level: 0 }, - { ProductCategory: 'Components', All: 240, 'All-USA': 240, ProductCategory_level: 0 } - ]); + + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + expect(dimensionValues).toEqual([ + { 'ProductCategory': 'Clothing' }, + { 'ProductCategory': 'Bikes' }, + { 'ProductCategory': 'Accessories' }, + { 'ProductCategory': 'Components' }]); }); it('allows setting expand/collapse state.', () => { @@ -195,34 +117,20 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expanded); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, expansionStates); const rowPipeCollapseResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expanded, true); - expect(rowPipeCollapseResult).toEqual([ - { - AllCategory: 'All', - AllCategory_level: 0, - All: 2127, - 'All-Bulgaria': 774, - 'All-USA': 829, - 'All-Uruguay': 524 - } + let dimensionValues = PivotGridFunctions.getDimensionValues(rowPipeCollapseResult); + expect(dimensionValues).toEqual([ + { 'AllCategory': 'All' } ]); expanded.set('All', true); const rowPipeExpandResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expanded, true); - expect(rowPipeExpandResult).toEqual([ - { AllCategory: 'All', All: 2127, 'All-Bulgaria': 774, 'All-USA': 829, 'All-Uruguay': 524, AllCategory_level: 0 }, - { ProductCategory: 'Clothing', All: 1526, 'All-Bulgaria': 774, 'All-USA': 296, 'All-Uruguay': 456, ProductCategory_level: 1 }, - { - ProductCategory: 'Bikes', All: 68, 'All-Uruguay': 68, ProductCategory_level: 1, UnitPrice: 3.56, SellerName: 'Lydia', Country: 'Uruguay', - Date: '01/06/2020', UnitsSold: 68 - }, - { - ProductCategory: 'Accessories', All: 293, 'All-USA': 293, ProductCategory_level: 1, UnitPrice: 85.58, SellerName: 'David', Country: 'USA', - Date: '04/07/2021', UnitsSold: 293 - }, - { - ProductCategory: 'Components', All: 240, 'All-USA': 240, ProductCategory_level: 1, UnitPrice: 18.13, SellerName: 'John', Country: 'USA', - Date: '12/08/2021', UnitsSold: 240 - }]); + dimensionValues = PivotGridFunctions.getDimensionValues(rowPipeExpandResult); + expect(dimensionValues).toEqual([ + { 'AllCategory': 'All' }, + { 'ProductCategory': 'Clothing' }, + { 'ProductCategory': 'Bikes' }, + { 'ProductCategory': 'Accessories' }, + { 'ProductCategory': 'Components' }]); }); it('transforms flat data to pivot data multiple row dimensions', () => { @@ -239,14 +147,18 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, expansionStates); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - expect(rowStatePipeResult).toEqual([ - { Date: '01/01/2021', ProductCategory: 'Clothing', All: 282, 'All-Bulgaria': 282, ProductCategory_level: 0, Date_level: 0 }, - { Date: '01/05/2019', ProductCategory: 'Clothing', All: 296, 'All-USA': 296, ProductCategory_level: 0, Date_level: 0 }, - { Date: '05/12/2020', ProductCategory: 'Clothing', All: 456, 'All-Uruguay': 456, ProductCategory_level: 0, Date_level: 0 }, - { Date: '02/19/2020', ProductCategory: 'Clothing', All: 492, 'All-Bulgaria': 492, ProductCategory_level: 0, Date_level: 0 }, - { Date: '01/06/2020', ProductCategory: 'Bikes', All: 68, 'All-Uruguay': 68, ProductCategory_level: 0, Date_level: 0 }, - { Date: '04/07/2021', ProductCategory: 'Accessories', All: 293, 'All-USA': 293, ProductCategory_level: 0, Date_level: 0 }, - { Date: '12/08/2021', ProductCategory: 'Components', All: 240, 'All-USA': 240, ProductCategory_level: 0, Date_level: 0 }]); + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + expect(dimensionValues).toEqual( + [ + { 'ProductCategory': 'Clothing', 'Date': '01/01/2021' }, + { 'ProductCategory': 'Clothing', 'Date': '01/05/2019' }, + { 'ProductCategory': 'Clothing', 'Date': '05/12/2020' }, + { 'ProductCategory': 'Clothing', 'Date': '02/19/2020', }, + { 'ProductCategory': 'Bikes', 'Date': '01/06/2020' }, + { 'ProductCategory': 'Accessories', 'Date': '04/07/2021' }, + { 'ProductCategory': 'Components', 'Date': '12/08/2021' } + ] + ); }); it('transforms flat data to pivot data with multiple nested row dimensions', () => { @@ -271,87 +183,27 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, expansionStates); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - expect(rowStatePipeResult).toEqual([ - { - 'AllDate': 'AllDate', 'AllProd': 'AllProd', 'All': 2127, 'All-Bulgaria': 774, 'All-USA': 829, 'All-Uruguay': 524, 'AllProd_level': 0, - 'AllDate_level': 0 - }, - { - 'Date': '01/01/2021', 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, 'SellerName': 'Stanley', 'Country': 'Bulgaria', - 'UnitsSold': 282, 'AllProd': 'AllProd', 'All': 282, 'All-Bulgaria': 282, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'Date': '01/05/2019', 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', 'Country': 'USA', - 'UnitsSold': 296, 'AllProd': 'AllProd', 'All': 296, 'All-USA': 296, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'Date': '05/12/2020', 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, 'SellerName': 'Larry', 'Country': 'Uruguay', - 'UnitsSold': 456, 'AllProd': 'AllProd', 'All': 456, 'All-Uruguay': 456, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'Date': '02/19/2020', 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, 'SellerName': 'Walter', 'Country': 'Bulgaria', - 'UnitsSold': 492, 'AllProd': 'AllProd', 'All': 492, 'All-Bulgaria': 492, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'Date': '01/06/2020', 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', 'Country': 'Uruguay', - 'UnitsSold': 68, 'AllProd': 'AllProd', 'All': 68, 'All-Uruguay': 68, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'Date': '04/07/2021', 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', - 'UnitsSold': 293, 'AllProd': 'AllProd', 'All': 293, 'All-USA': 293, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'Date': '12/08/2021', 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', - 'UnitsSold': 240, 'AllProd': 'AllProd', 'All': 240, 'All-USA': 240, 'Date_level': 1, 'AllProd_level': 0 - }, - { - 'ProductCategory': 'Clothing', 'AllDate': 'AllDate', 'All': 1526, 'All-Bulgaria': 774, 'All-USA': 296, - 'All-Uruguay': 456, 'ProductCategory_level': 1, 'AllDate_level': 0 - }, - { - 'Date': '01/01/2021', - 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, 'SellerName': 'Stanley', 'Country': 'Bulgaria', - 'UnitsSold': 282, 'All': 282, 'All-Bulgaria': 282, 'Date_level': 1, 'ProductCategory_level': 1 - }, - { - 'Date': '01/05/2019', 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', - 'Country': 'USA', 'UnitsSold': 296, 'All': 296, 'All-USA': 296, 'Date_level': 1, 'ProductCategory_level': 1 - }, - { - 'Date': '05/12/2020', 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, 'SellerName': 'Larry', 'Country': 'Uruguay', - 'UnitsSold': 456, 'All': 456, 'All-Uruguay': 456, 'Date_level': 1, 'ProductCategory_level': 1 - }, - { - 'Date': '02/19/2020', 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, 'SellerName': 'Walter', - 'Country': 'Bulgaria', 'UnitsSold': 492, 'All': 492, 'All-Bulgaria': 492, 'Date_level': 1, - 'ProductCategory_level': 1 - }, { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', - 'Country': 'Uruguay', 'Date': '01/06/2020', 'UnitsSold': 68, 'AllDate': 'AllDate', 'All': 68, - 'All-Uruguay': 68, 'ProductCategory_level': 1, 'AllDate_level': 0 - }, - { - 'Date': '01/06/2020', 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', - 'Country': 'Uruguay', 'UnitsSold': 68, 'All': 68, 'All-Uruguay': 68, 'Date_level': 1, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'Date': '04/07/2021', 'UnitsSold': 293, - 'AllDate': 'AllDate', 'All': 293, 'All-USA': 293, 'ProductCategory_level': 1, 'AllDate_level': 0 - }, - { - 'Date': '04/07/2021', 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'UnitsSold': 293, - 'All': 293, 'All-USA': 293, 'Date_level': 1, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, - 'SellerName': 'John', 'Country': 'USA', 'Date': '12/08/2021', 'UnitsSold': 240, 'AllDate': 'AllDate', 'All': 240, 'All-USA': 240, - 'ProductCategory_level': 1, 'AllDate_level': 0 - }, - { - 'Date': '12/08/2021', 'ProductCategory': 'Components', - 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', 'UnitsSold': 240, 'All': 240, 'All-USA': 240, 'Date_level': 1, - 'ProductCategory_level': 1 - }]); + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + expect(dimensionValues).toEqual([ + { 'AllProd': 'AllProd', 'AllDate': 'AllDate' }, + { 'AllProd': 'AllProd', 'Date': '01/01/2021' }, + { 'AllProd': 'AllProd', 'Date': '01/05/2019' }, + { 'AllProd': 'AllProd', 'Date': '05/12/2020' }, + { 'AllProd': 'AllProd', 'Date': '02/19/2020', }, + { 'AllProd': 'AllProd', 'Date': '01/06/2020' }, + { 'AllProd': 'AllProd', 'Date': '04/07/2021' }, + { 'AllProd': 'AllProd', 'Date': '12/08/2021' }, + { 'ProductCategory': 'Clothing', 'AllDate': 'AllDate' }, + { 'ProductCategory': 'Clothing', 'Date': '01/01/2021' }, + { 'ProductCategory': 'Clothing', 'Date': '01/05/2019' }, + { 'ProductCategory': 'Clothing', 'Date': '05/12/2020' }, + { 'ProductCategory': 'Clothing', 'Date': '02/19/2020' }, + { 'ProductCategory': 'Bikes', 'AllDate': 'AllDate', }, + { 'ProductCategory': 'Bikes', 'Date': '01/06/2020' }, + { 'ProductCategory': 'Accessories', 'AllDate': 'AllDate' }, + { 'ProductCategory': 'Accessories', 'Date': '04/07/2021' }, + { 'ProductCategory': 'Components', 'AllDate': 'AllDate' }, + { 'ProductCategory': 'Components', 'Date': '12/08/2021' }]); }); it('transforms flat data to pivot data 2 column dimensions', () => { @@ -366,28 +218,21 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, new Map()); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, new Map(), true); - /* eslint-disable quote-props */ - expect(rowStatePipeResult).toEqual([ - { - 'AllCategory': 'All', 'Bulgaria': 774, 'Bulgaria-01/01/2021': 282, 'Bulgaria-02/19/2020': 492, 'USA': 829, 'USA-01/05/2019': 296, - 'USA-04/07/2021': 293, 'USA-12/08/2021': 240, 'Uruguay': 524, 'Uruguay-05/12/2020': 456, 'Uruguay-01/06/2020': 68, 'AllCategory_level': 0 - }, - { - 'ProductCategory': 'Clothing', 'Bulgaria': 774, 'Bulgaria-01/01/2021': 282, 'Bulgaria-02/19/2020': 492, 'USA': 296, 'USA-01/05/2019': 296, - 'Uruguay': 456, 'Uruguay-05/12/2020': 456, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', 'Country': 'Uruguay', 'Date': '01/06/2020', 'UnitsSold': 68, 'Uruguay': 68, - 'Uruguay-01/06/2020': 68, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'Date': '04/07/2021', 'UnitsSold': 293, - 'USA': 293, 'USA-04/07/2021': 293, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', 'Date': '12/08/2021', 'UnitsSold': 240, - 'USA': 240, 'USA-12/08/2021': 240, 'ProductCategory_level': 1 - } + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + expect(dimensionValues).toEqual([ + { 'AllCategory': 'All' }, + { 'ProductCategory': 'Clothing' }, + { 'ProductCategory': 'Bikes' }, + { 'ProductCategory': 'Accessories' }, + { 'ProductCategory': 'Components' }]); + // for columns we need to check aggregations + const aggregations = PivotGridFunctions.getAggregationValues(rowStatePipeResult); + expect(aggregations).toEqual([ + { 'Bulgaria-01/01/2021': 282, 'Bulgaria-02/19/2020': 492, 'Bulgaria': 774, 'USA-01/05/2019': 296, 'USA-04/07/2021': 293, 'USA-12/08/2021': 240, 'USA': 829, 'Uruguay-05/12/2020': 456, 'Uruguay-01/06/2020': 68, 'Uruguay': 524 }, + { 'Bulgaria-01/01/2021': 282, 'Bulgaria-02/19/2020': 492, 'Bulgaria': 774, 'USA-01/05/2019': 296, 'USA': 296, 'Uruguay-05/12/2020': 456, 'Uruguay': 456 }, + { 'Uruguay-01/06/2020': 68, 'Uruguay': 68 }, + { 'USA-04/07/2021': 293, 'USA': 293 }, + { 'USA-12/08/2021': 240, 'USA': 240 } ]); }); @@ -407,34 +252,13 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, new Map()); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); const rowStateResult = rowStatePipe.transform(columnPipeResult, pivotConfig, new Map(), true); - /* eslint-disable quote-props */ - expect(rowStateResult).toEqual( - [ - { - 'AllCategory': 'All', 'Bulgaria': 774, 'Bulgaria-Stanley': 282, 'Bulgaria-Stanley-01/01/2021': 282, 'Bulgaria-Walter': 492, - 'Bulgaria-Walter-02/19/2020': 492, 'USA': 829, 'USA-Elisa': 296, 'USA-Elisa-01/05/2019': 296, 'USA-David': 293, - 'USA-David-04/07/2021': 293, 'USA-John': 240, 'USA-John-12/08/2021': 240, 'Uruguay': 524, 'Uruguay-Larry': 456, - 'Uruguay-Larry-05/12/2020': 456, 'Uruguay-Lydia': 68, 'Uruguay-Lydia-01/06/2020': 68, 'AllCategory_level': 0 - }, - { - 'ProductCategory': 'Clothing', 'Bulgaria': 774, 'Bulgaria-Stanley': 282, 'Bulgaria-Stanley-01/01/2021': 282, - 'Bulgaria-Walter': 492, 'Bulgaria-Walter-02/19/2020': 492, 'USA': 296, 'USA-Elisa': 296, 'USA-Elisa-01/05/2019': 296, - 'Uruguay': 456, 'Uruguay-Larry': 456, 'Uruguay-Larry-05/12/2020': 456, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', 'Country': 'Uruguay', 'Date': '01/06/2020', - 'UnitsSold': 68, 'Uruguay': 68, 'Uruguay-Lydia': 68, 'Uruguay-Lydia-01/06/2020': 68, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'Date': '04/07/2021', - 'UnitsSold': 293, 'USA': 293, 'USA-David': 293, 'USA-David-04/07/2021': 293, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', 'Date': '12/08/2021', - 'UnitsSold': 240, 'USA': 240, 'USA-John': 240, 'USA-John-12/08/2021': 240, 'ProductCategory_level': 1 - } - ] - ); + const aggregations = PivotGridFunctions.getAggregationValues(rowStateResult); + expect(aggregations).toEqual([ + { 'Bulgaria-Stanley-01/01/2021': 282, 'Bulgaria-Stanley': 282, 'Bulgaria-Walter-02/19/2020': 492, 'Bulgaria-Walter': 492, 'Bulgaria': 774, 'USA-Elisa-01/05/2019': 296, 'USA-Elisa': 296, 'USA-David-04/07/2021': 293, 'USA-David': 293, 'USA-John-12/08/2021': 240, 'USA-John': 240, 'USA': 829, 'Uruguay-Larry-05/12/2020': 456, 'Uruguay-Larry': 456, 'Uruguay-Lydia-01/06/2020': 68, 'Uruguay-Lydia': 68, 'Uruguay': 524 }, + { 'Bulgaria-Stanley-01/01/2021': 282, 'Bulgaria-Stanley': 282, 'Bulgaria-Walter-02/19/2020': 492, 'Bulgaria-Walter': 492, 'Bulgaria': 774, 'USA-Elisa-01/05/2019': 296, 'USA-Elisa': 296, 'USA': 296, 'Uruguay-Larry-05/12/2020': 456, 'Uruguay-Larry': 456, 'Uruguay': 456 }, + { 'Uruguay-Lydia-01/06/2020': 68, 'Uruguay-Lydia': 68, 'Uruguay': 68 }, + { 'USA-David-04/07/2021': 293, 'USA-David': 293, 'USA': 293 }, + { 'USA-John-12/08/2021': 240, 'USA-John': 240, 'USA': 240 }]); }); it('transforms flat data to pivot data 2 value dimensions', () => { @@ -461,35 +285,13 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, expansionStates); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, new Map(), true); - expect(rowStatePipeResult).toEqual( - [ - { - 'AllCategory': 'All', 'All-UnitsSold': 2127, 'All-Bulgaria-UnitsSold': 774, 'All-Bulgaria-UnitPrice': 28.86, 'All-USA-UnitsSold': 829, - 'All-USA-UnitPrice': 153.28, 'All-Uruguay-UnitsSold': 524, 'All-Uruguay-UnitPrice': 71.89, 'All-UnitPrice': 254.02999999999997, - 'AllCategory_level': 0 - }, - { - 'ProductCategory': 'Clothing', 'All-UnitsSold': 1526, 'All-Bulgaria-UnitsSold': 774, 'All-Bulgaria-UnitPrice': 28.86, - 'All-USA-UnitsSold': 296, 'All-USA-UnitPrice': 49.57, 'All-Uruguay-UnitsSold': 456, 'All-Uruguay-UnitPrice': 68.33, - 'All-UnitPrice': 146.76, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', - 'Country': 'Uruguay', 'Date': '01/06/2020', 'UnitsSold': 68, 'All-UnitsSold': 68, 'All-Uruguay-UnitsSold': 68, - 'All-Uruguay-UnitPrice': 3.56, 'All-UnitPrice': 3.56, 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'Date': '04/07/2021', - 'UnitsSold': 293, 'All-UnitsSold': 293, 'All-USA-UnitsSold': 293, 'All-USA-UnitPrice': 85.58, 'All-UnitPrice': 85.58, - 'ProductCategory_level': 1 - }, - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', 'Date': '12/08/2021', - 'UnitsSold': 240, 'All-UnitsSold': 240, 'All-USA-UnitsSold': 240, 'All-USA-UnitPrice': 18.13, 'All-UnitPrice': 18.13, - 'ProductCategory_level': 1 - } - ] - ); + const aggregations = PivotGridFunctions.getAggregationValues(rowStatePipeResult); + + expect(aggregations).toEqual([ + { 'All-Bulgaria-UnitsSold': 774, 'All-Bulgaria-UnitPrice': 28.86, 'All-USA-UnitsSold': 829, 'All-USA-UnitPrice': 153.28, 'All-Uruguay-UnitsSold': 524, 'All-Uruguay-UnitPrice': 71.89, 'All-UnitsSold': 2127, 'All-UnitPrice': 254.02999999999997 }, + { 'All-Bulgaria-UnitsSold': 774, 'All-Bulgaria-UnitPrice': 28.86, 'All-USA-UnitsSold': 296, 'All-USA-UnitPrice': 49.57, 'All-Uruguay-UnitsSold': 456, 'All-Uruguay-UnitPrice': 68.33, 'All-UnitsSold': 1526, 'All-UnitPrice': 146.76 }, + { 'All-Uruguay-UnitsSold': 68, 'All-Uruguay-UnitPrice': 3.56, 'All-UnitsSold': 68, 'All-UnitPrice': 3.56 }, { 'All-USA-UnitsSold': 293, 'All-USA-UnitPrice': 85.58, 'All-UnitsSold': 293, 'All-UnitPrice': 85.58 }, + { 'All-USA-UnitsSold': 240, 'All-USA-UnitPrice': 18.13, 'All-UnitsSold': 240, 'All-UnitPrice': 18.13 }]); }); it('allow setting NoopPivotDimensionsStrategy for rows/columns', () => { @@ -509,31 +311,17 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(preprocessedData, pivotConfig, new Map()); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); - const rowStateResult = rowStatePipe.transform(columnPipeResult, pivotConfig, new Map(), true); - - // same data but expanded - expect(rowStateResult).toEqual( - [ - { - 'All': 2127, 'AllCategory': 'All', 'All-Bulgaria': 774, 'All-USA': 829, - 'All-Uruguay': 524, 'AllCategory_level': 0, 'AllCategory_records': [ - { - 'ProductCategory': 'Clothing', 'All': 1526, 'All-Bulgaria': 774, - 'All-USA': 296, 'All-Uruguay': 456, 'ProductCategory_level': 1 - }, - { 'ProductCategory': 'Bikes', 'All': 68, 'All-Uruguay': 68, 'ProductCategory_level': 1 }, - { 'ProductCategory': 'Accessories', 'All': 293, 'All-USA': 293, 'ProductCategory_level': 1 }, - { 'ProductCategory': 'Components', 'All': 240, 'All-USA': 240, 'ProductCategory_level': 1 } - ] - }, - { - 'ProductCategory': 'Clothing', 'All': 1526, 'All-Bulgaria': 774, - 'All-USA': 296, 'All-Uruguay': 456, 'ProductCategory_level': 1 - }, - { 'ProductCategory': 'Bikes', 'All': 68, 'All-Uruguay': 68, 'ProductCategory_level': 1 }, - { 'ProductCategory': 'Accessories', 'All': 293, 'All-USA': 293, 'ProductCategory_level': 1 }, - { 'ProductCategory': 'Components', 'All': 240, 'All-USA': 240, 'ProductCategory_level': 1 }] - ); + const autoTransformResult = autoTransformPipe.transform(columnPipeResult, pivotConfig); + const rowStateResult = rowStatePipe.transform(autoTransformResult, pivotConfig, new Map(), true); + + // same data but expanded and transformed to IPivotRecord + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStateResult); + expect(dimensionValues).toEqual([ + { 'AllCategory': 'All' }, + { 'ProductCategory': 'Clothing' }, + { 'ProductCategory': 'Bikes' }, + { 'ProductCategory': 'Accessories' }, + { 'ProductCategory': 'Components' }]); }); it('should generate correct levels when using predefined date dimension', () => { @@ -580,190 +368,21 @@ describe('Pivot pipes #pivotGrid', () => { ]; const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); - - expect(rowPipeResult[0]).toEqual( - { - 'AllPeriods': 'All Periods', 'AllPeriods_records': [ - { - 'Years': '2021', 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, 'SellerName': 'Stanley', 'Country': 'Bulgaria', 'City': 'Sofia', - 'Date': '01/01/2021', 'UnitsSold': 282 - }, - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'City': 'New York', - 'Date': '04/07/2021', 'UnitsSold': 293 - }, { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', - 'Country': 'USA', 'City': 'New York', 'Date': '12/08/2021', 'UnitsSold': 240 - } - ], 'Years_records': [ - { - 'Date': '01/01/2021', 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, 'SellerName': 'Stanley', 'Country': 'Bulgaria', 'City': 'Sofia', - 'Date': '01/01/2021', 'UnitsSold': 282 - }], 'Date_records': [{ - 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, - 'SellerName': 'Stanley', 'Country': 'Bulgaria', 'City': 'Sofia', 'Date': '01/01/2021', 'UnitsSold': 282 - } - ], 'level': 2, 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, 'SellerName': 'Stanley', - 'Country': 'Bulgaria', 'City': 'Sofia', 'UnitsSold': 282 - }, - { - 'Date': '04/07/2021', 'records': [ - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', 'City': 'New York', - 'Date': '04/07/2021', 'UnitsSold': 293 - } - ], 'Date_records': [ - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', - 'City': 'New York', 'Date': '04/07/2021', 'UnitsSold': 293 - } - ], 'level': 2, 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, 'SellerName': 'David', 'Country': 'USA', - 'City': 'New York', 'UnitsSold': 293 - }, - { - 'Date': '12/08/2021', 'records': [ - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', - 'City': 'New York', 'Date': '12/08/2021', 'UnitsSold': 240 - } - ], 'Date_records': [ - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', - 'City': 'New York', 'Date': '12/08/2021', 'UnitsSold': 240 - } - ], 'level': 2, 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', 'Country': 'USA', - 'City': 'New York', 'UnitsSold': 240 - } - ], 'level': 1 - }, - { - 'Years': '2019', 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', 'Country': 'USA', - 'City': 'New York', 'Date': '01/05/2019', 'UnitsSold': 296 - } - ], 'Years_records': [ - { - 'Date': '01/05/2019', 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', - 'Country': 'USA', 'City': 'New York', 'Date': '01/05/2019', 'UnitsSold': 296 - } - ], 'Date_records': [{ - 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', - 'Country': 'USA', 'City': 'New York', 'Date': '01/05/2019', 'UnitsSold': 296 - } - ], 'level': 2, 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, - 'SellerName': 'Elisa', 'Country': 'USA', 'City': 'New York', 'UnitsSold': 296 - } - ], 'level': 1, 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', - 'Country': 'USA', 'City': 'New York', 'Date': '01/05/2019', 'UnitsSold': 296 - }, - { - 'Years': '2020', 'records': [ - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', - 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', 'Date': '01/06/2020', - 'UnitsSold': 68 - }, - { - 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, 'SellerName': 'Larry', - 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', 'Date': '05/12/2020', - 'UnitsSold': 456 - }, - { - 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, 'SellerName': 'Walter', - 'Country': 'Bulgaria', 'City': 'Plovdiv', 'Date': '02/19/2020', 'UnitsSold': 492 - } - ], 'Years_records': [{ - 'Date': '01/06/2020', 'records': [ - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', - 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', 'Date': '01/06/2020', - 'UnitsSold': 68 - } - ], 'Date_records': [ - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', - 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', 'Date': '01/06/2020', - 'UnitsSold': 68 - } - ], 'level': 2, 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, - 'SellerName': 'Lydia', 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', - 'UnitsSold': 68 - }, - { - 'Date': '05/12/2020', 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, 'SellerName': 'Larry', - 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', 'Date': '05/12/2020', - 'UnitsSold': 456 - } - ], 'Date_records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, 'SellerName': 'Larry', - 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', - 'Date': '05/12/2020', 'UnitsSold': 456 - } - ], 'level': 2, 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, - 'SellerName': 'Larry', 'Country': 'Uruguay', 'City': 'Ciudad de la Costa', - 'UnitsSold': 456 - }, - { - 'Date': '02/19/2020', 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, - 'SellerName': 'Walter', 'Country': 'Bulgaria', 'City': 'Plovdiv', - 'Date': '02/19/2020', 'UnitsSold': 492 - } - ], 'Date_records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, - 'SellerName': 'Walter', 'Country': 'Bulgaria', 'City': 'Plovdiv', - 'Date': '02/19/2020', 'UnitsSold': 492 - } - ], 'level': 2, 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, - 'SellerName': 'Walter', 'Country': 'Bulgaria', 'City': 'Plovdiv', - 'UnitsSold': 492 - } - ], 'level': 1 - } - ], 'level': 0, 'records': [ - { - 'ProductCategory': 'Clothing', 'UnitPrice': 12.81, - 'SellerName': 'Stanley', 'Country': 'Bulgaria', 'City': 'Sofia', - 'Date': '01/01/2021', 'UnitsSold': 282 - }, - { - 'ProductCategory': 'Accessories', 'UnitPrice': 85.58, - 'SellerName': 'David', 'Country': 'USA', 'City': 'New York', - 'Date': '04/07/2021', 'UnitsSold': 293 - }, - { - 'ProductCategory': 'Components', 'UnitPrice': 18.13, 'SellerName': 'John', - 'Country': 'USA', 'City': 'New York', 'Date': '12/08/2021', 'UnitsSold': 240 - }, - { - 'ProductCategory': 'Clothing', 'UnitPrice': 49.57, 'SellerName': 'Elisa', 'Country': 'USA', 'City': 'New York', - 'Date': '01/05/2019', 'UnitsSold': 296 - }, - { - 'ProductCategory': 'Bikes', 'UnitPrice': 3.56, 'SellerName': 'Lydia', 'Country': 'Uruguay', - 'City': 'Ciudad de la Costa', 'Date': '01/06/2020', 'UnitsSold': 68 - }, - { - 'ProductCategory': 'Clothing', 'UnitPrice': 68.33, 'SellerName': 'Larry', 'Country': 'Uruguay', - 'City': 'Ciudad de la Costa', 'Date': '05/12/2020', 'UnitsSold': 456 - }, - { - 'ProductCategory': 'Clothing', 'UnitPrice': 16.05, 'SellerName': 'Walter', 'Country': 'Bulgaria', - 'City': 'Plovdiv', 'Date': '02/19/2020', 'UnitsSold': 492 - }] - } + const rowStateResult = rowStatePipe.transform(rowPipeResult, pivotConfig, new Map(), true); + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStateResult); + expect(dimensionValues).toEqual( + [ + { 'AllPeriods': 'All Periods' }, + { 'Years': '2021' }, + { 'Date': '01/01/2021' }, + { 'Date': '04/07/2021' }, + { 'Date': '12/08/2021' }, + { 'Years': '2019' }, + { 'Date': '01/05/2019' }, + { 'Years': '2020' }, + { 'Date': '01/06/2020' }, + { 'Date': '05/12/2020' }, + { 'Date': '02/19/2020' }] ); }); @@ -826,7 +445,7 @@ describe('Pivot pipes #pivotGrid', () => { const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - const date_city_product = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const date_city_product = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(rowStatePipeResult.length).toEqual(37); const allPeriodsRecords = date_city_product.filter(x => x['AllPeriods'] === 'All Periods'); expect(allPeriodsRecords).toEqual([ @@ -881,7 +500,7 @@ describe('Pivot pipes #pivotGrid', () => { let rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(24); - const date_product = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const date_product = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(date_product).toEqual( [ @@ -933,7 +552,7 @@ describe('Pivot pipes #pivotGrid', () => { columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(24); - const product_date = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const product_date = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(product_date).toEqual( [ @@ -999,7 +618,7 @@ describe('Pivot pipes #pivotGrid', () => { let columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); let rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - const date_prod_seller = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const date_prod_seller = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(rowStatePipeResult.length).toBe(42); const allPeriodsRecords = date_prod_seller.filter(x => x['AllPeriods'] === 'All Periods'); @@ -1045,7 +664,7 @@ describe('Pivot pipes #pivotGrid', () => { columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - const prod_date_seller = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const prod_date_seller = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(rowStatePipeResult.length).toBe(42); const allProdsRecords = prod_date_seller.filter(x => x['AllProduct'] === 'All Products'); expect(allProdsRecords).toEqual([ @@ -1097,7 +716,7 @@ describe('Pivot pipes #pivotGrid', () => { columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(42); - const seller_prod_date = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const seller_prod_date = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const stanleyRecords = seller_prod_date.filter(x => x['SellerName'] === 'Stanley'); expect(stanleyRecords).toEqual([ { SellerName: 'Stanley', AllProduct: 'All Products', AllPeriods: 'All Periods' }, @@ -1152,7 +771,7 @@ describe('Pivot pipes #pivotGrid', () => { let columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); let rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - const date_prod_country_seller = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const date_prod_country_seller = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(rowStatePipeResult.length).toBe(42); const allPeriodsData = date_prod_country_seller.filter(x => x['AllPeriods'] === 'All Periods'); expect(allPeriodsData).toEqual([ @@ -1201,7 +820,7 @@ describe('Pivot pipes #pivotGrid', () => { rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(42); - const country_prod_seller_date = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const country_prod_seller_date = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const bgRecords = country_prod_seller_date.filter(x => x['Country'] === 'Bulgaria'); expect(bgRecords).toEqual([ { Country: 'Bulgaria', AllProduct: 'All Products', SellerName: 'Stanley', AllPeriods: 'All Periods' }, @@ -1231,7 +850,7 @@ describe('Pivot pipes #pivotGrid', () => { rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(42); - const prod_country_date_seller = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const prod_country_date_seller = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const allProdsRecords = prod_country_date_seller.filter(x => x['AllProduct'] === 'All Products'); expect(allProdsRecords).toEqual([ { AllProduct: 'All Products', Country: 'Bulgaria', AllPeriods: 'All Periods', SellerName: 'Stanley' }, @@ -1341,7 +960,7 @@ describe('Pivot pipes #pivotGrid', () => { let columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); let rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(84); - const prod_country_date_seller_discontinued = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const prod_country_date_seller_discontinued = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const allPeriods_allProducts_records = prod_country_date_seller_discontinued.filter(x => x['AllPeriods'] === 'All Periods' && x['AllProduct'] === 'All Products'); expect(allPeriods_allProducts_records).toEqual( @@ -1366,9 +985,9 @@ describe('Pivot pipes #pivotGrid', () => { const allPeriods_clothing_records = prod_country_date_seller_discontinued.filter(x => x['AllPeriods'] === 'All Periods' && x['ProductCategory'] === 'Clothing'); expect(allPeriods_clothing_records).toEqual([ { AllPeriods: 'All Periods', ProductCategory: 'Clothing', AllCountries: 'All Countries', SellerName: 'Stanley', Discontinued: 'false' }, + { AllPeriods: 'All Periods', ProductCategory: 'Clothing', AllCountries: 'All Countries', SellerName: 'Walter', Discontinued: 'false' }, { AllPeriods: 'All Periods', ProductCategory: 'Clothing', AllCountries: 'All Countries', SellerName: 'Elisa', Discontinued: 'true' }, { AllPeriods: 'All Periods', ProductCategory: 'Clothing', AllCountries: 'All Countries', SellerName: 'Larry', Discontinued: 'true' }, - { AllPeriods: 'All Periods', ProductCategory: 'Clothing', AllCountries: 'All Countries', SellerName: 'Walter', Discontinued: 'false' }, { AllPeriods: 'All Periods', ProductCategory: 'Clothing', Country: 'Bulgaria', SellerName: 'Stanley', Discontinued: 'false' }, { AllPeriods: 'All Periods', ProductCategory: 'Clothing', Country: 'Bulgaria', SellerName: 'Walter', Discontinued: 'false' }, { AllPeriods: 'All Periods', ProductCategory: 'Clothing', Country: 'USA', SellerName: 'Elisa', Discontinued: 'true' }, @@ -1428,7 +1047,7 @@ describe('Pivot pipes #pivotGrid', () => { columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(84); - const discontinued_prod_country_date_seller = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const discontinued_prod_country_date_seller = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const ongoing_records = discontinued_prod_country_date_seller.filter(x => x['Discontinued'] === 'false'); const discontinued_records = discontinued_prod_country_date_seller.filter(x => x['Discontinued'] === 'true'); expect(discontinued_records.length).toBe(36); @@ -1482,7 +1101,7 @@ describe('Pivot pipes #pivotGrid', () => { columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(84); - const seller_country_date_prod_disc = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const seller_country_date_prod_disc = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const stanley_allCountries_allPeriods = seller_country_date_prod_disc.filter(x => x['SellerName'] === 'Stanley' && x['AllCountries'] === 'All Countries' && x['AllPeriods'] === 'All Periods'); expect(stanley_allCountries_allPeriods).toEqual([ @@ -1512,7 +1131,7 @@ describe('Pivot pipes #pivotGrid', () => { columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); expect(rowStatePipeResult.length).toBe(84); - const date_prod_disc_seller = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const date_prod_disc_seller = PivotGridFunctions.getDimensionValues(rowStatePipeResult); const date_allPeriods_allProducts_records = date_prod_disc_seller.filter(x => x['AllPeriods'] === 'All Periods' && x['AllProduct'] === 'All Products'); expect(date_allPeriods_allProducts_records).toEqual([ @@ -1541,7 +1160,7 @@ describe('Pivot pipes #pivotGrid', () => { { Years: '2021', AllProduct: 'All Products', Discontinued: 'false', Country: 'USA', SellerName: 'John' } ]); }); - // automation for https://github.com/IgniteUI/igniteui-angular/issues/10545 + // // automation for https://github.com/IgniteUI/igniteui-angular/issues/10545 it('should generate last dimension values for all records.', () => { data = [ { @@ -1608,12 +1227,13 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - const sellers = rowStatePipeResult.map(x => x.SellerName); + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + const sellers = dimensionValues.map(x => x['SellerName']); // there should be no empty values. expect(sellers.filter(x => x === undefined).length).toBe(0); }); - // automation for https://github.com/IgniteUI/igniteui-angular/issues/10662 + // // automation for https://github.com/IgniteUI/igniteui-angular/issues/10662 it('should retain processed values for last dimension when bound to complex object.', () => { data = DATA; pivotConfig.rows = [ @@ -1649,8 +1269,8 @@ describe('Pivot pipes #pivotGrid', () => { const rowPipeResult = rowPipe.transform(data, pivotConfig, expansionStates); const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - - const res = rowStatePipeResult.filter(x => x.AllSeller === undefined).map(x => x.Seller); + const dimensionValues = PivotGridFunctions.getDimensionValues(rowStatePipeResult); + const res = dimensionValues.filter(x => x['AllSeller'] === undefined).map(x => x['Seller']); // all values should be strings as the result of the processed member function is string. expect(res.filter(x => typeof x !== 'string').length).toBe(0); }); @@ -1688,7 +1308,7 @@ describe('Pivot pipes #pivotGrid', () => { const columnPipeResult = columnPipe.transform(rowPipeResult, pivotConfig, new Map()); const rowStatePipeResult = rowStatePipe.transform(columnPipeResult, pivotConfig, expansionStates, true); - const dateData = PivotGridFunctions.getDimensionData(rowStatePipeResult, pivotConfig.rows); + const dateData = PivotGridFunctions.getDimensionValues(rowStatePipeResult); expect(dateData).toEqual([ { 'AllPeriods': 'All Periods' }, { 'Years': '2020' }, diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts index 38e99840092..342d89fba7e 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts @@ -3,8 +3,9 @@ import { cloneArray } from '../../core/utils'; import { DataUtil } from '../../data-operations/data-util'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { IFilteringStrategy } from '../../data-operations/filtering-strategy'; -import { DEFAULT_PIVOT_KEYS, IPivotConfiguration, IPivotDimension, IPivotKeys, PivotDimensionType } from './pivot-grid.interface'; +import { DEFAULT_PIVOT_KEYS, IPivotConfiguration, IPivotDimension, IPivotGridGroupRecord, IPivotGridRecord, IPivotKeys, PivotDimensionType } from './pivot-grid.interface'; import { + DefaultPivotGridRecordSortingStrategy, DefaultPivotSortingStrategy, DimensionValuesFilteringStrategy, NoopPivotDimensionsStrategy, PivotColumnDimensionsStrategy, PivotRowDimensionsStrategy } from '../../data-operations/pivot-strategy'; @@ -15,8 +16,6 @@ import { GridType, IGX_GRID_BASE } from '../common/grid.interface'; import { GridBaseAPIService } from '../api.service'; import { IgxGridBaseDirective } from '../grid-base.directive'; import { IGridSortingStrategy } from '../common/strategy'; -import { IGroupByResult } from '../../data-operations/grouping-result.interface'; -import { IGroupingExpression } from '../../data-operations/grouping-expression.interface'; /** * @hidden @@ -35,15 +34,90 @@ export class IgxPivotRowPipe implements PipeTransform { _: Map, _pipeTrigger?: number, __? - ): any[] { + ): IPivotGridRecord[] { const pivotKeys = config.pivotKeys || DEFAULT_PIVOT_KEYS; - const enabledRows = config.rows.filter(x => x.enabled); + const enabledRows = config.rows?.filter(x => x.enabled) || []; + const enabledColumns = config.columns?.filter(x => x.enabled) || []; + const enabledValues = config.values?.filter(x => x.enabled) || []; + if (enabledRows.length === 0 && enabledColumns.length === 0 && enabledValues.length === 0) { + // nothing to group and aggregate by ... + return []; + } const rowStrategy = config.rowStrategy || PivotRowDimensionsStrategy.instance(); const data = cloneArray(collection, true); return rowStrategy.process(data, enabledRows, config.values, pivotKeys); } } +/** + * @hidden + * Transforms generic array data into IPivotGridRecord[] + */ +@Pipe({ + name: 'pivotGridAutoTransform', + pure: true +}) +export class IgxPivotAutoTransform implements PipeTransform { + public transform( + collection: any[], + config: IPivotConfiguration, + _pipeTrigger?: number, + __?, + ): IPivotGridRecord[] { + let needsTransformation = false; + if (collection.length > 0) { + needsTransformation = !this.isPivotRecord(collection[0]); + } + + if (!needsTransformation) return collection; + + const res = this.processCollectionToPivotRecord(config, collection); + return res; + } + + protected isPivotRecord(arg: IPivotGridRecord): arg is IPivotGridRecord { + return !!(arg as IPivotGridRecord).aggregationValues; + } + + protected processCollectionToPivotRecord(config: IPivotConfiguration, collection: any[]): IPivotGridRecord[] { + const pivotKeys: IPivotKeys = config.pivotKeys || DEFAULT_PIVOT_KEYS; + const enabledRows = config.rows.filter(x => x.enabled); + const allFlat: IPivotDimension[] = PivotUtil.flatten(enabledRows); + const result: IPivotGridRecord[] = []; + for (const rec of collection) { + const pivotRec: IPivotGridRecord = { + dimensionValues: new Map(), + aggregationValues: new Map(), + children: new Map(), + dimensions: [] + }; + const keys = Object.keys(rec) + for (const key of keys) { + const dim = allFlat.find(x => x.memberName === key); + if (dim) { + //field has matching dimension + pivotRec.dimensions.push(dim); + pivotRec.dimensionValues.set(key, rec[key]); + } else if (key.indexOf(pivotKeys.rowDimensionSeparator + pivotKeys.records) !== -1) { + // field that contains child collection + const dimKey = key.slice(0, key.indexOf(pivotKeys.rowDimensionSeparator + pivotKeys.records)); + const childData = rec[key]; + const childPivotData = this.processCollectionToPivotRecord(config, childData); + pivotRec.children.set(dimKey, childPivotData); + } else { + // an aggregation + pivotRec.aggregationValues.set(key, rec[key]); + } + } + const flattened = PivotUtil.flatten(config.rows); + pivotRec.dimensions.sort((x,y) => flattened.indexOf(x) - flattened.indexOf(y)); + result.push(pivotRec); + } + return result; + } + +} + /** * @hidden */ @@ -56,56 +130,26 @@ export class IgxPivotRowExpansionPipe implements PipeTransform { constructor(@Inject(IGX_GRID_BASE) private grid?: GridType) { } public transform( - collection: any[], + collection: IPivotGridRecord[], config: IPivotConfiguration, expansionStates: Map, defaultExpand: boolean, _pipeTrigger?: number, __?, - ): any[] { - const pivotKeys = config.pivotKeys || DEFAULT_PIVOT_KEYS; - const enabledRows = config.rows.filter(x => x.enabled); + ): IPivotGridRecord[] { + const enabledRows = config.rows?.filter(x => x.enabled) || []; const data = collection ? cloneArray(collection, true) : []; - let totalLlv = 0; - const prevDims = []; for (const row of enabledRows) { - const lvl = PivotUtil.getDimensionDepth(row); - totalLlv += lvl; - PivotUtil.flattenHierarchy(data, config, row, expansionStates, defaultExpand, pivotKeys, totalLlv, prevDims, 0, lvl); - prevDims.push(row); + PivotUtil.flattenGroups(data, row, expansionStates, defaultExpand); } - const finalData = config.columnStrategy ? data : - enabledRows.length ? data.filter(x => x[pivotKeys.records]) : data; - - this.cleanState(finalData, pivotKeys, config); + const finalData = enabledRows.length > 0 ? + data.filter(x => x.dimensions.length === enabledRows.length) : data; if (this.grid) { this.grid.setFilteredSortedData(finalData, false); } return finalData; } - - private cleanState(data, pivotKeys, config) { - data.forEach(rec => { - const keys = Object.keys(rec); - delete rec.processed; - delete rec.sorted; - //remove all record keys from final data since we don't need them anymore. - if (!(config.rowStrategy instanceof NoopPivotDimensionsStrategy || config.columnStrategy instanceof NoopPivotDimensionsStrategy)) { - keys.forEach(k => { - if (k.indexOf(pivotKeys.records) !== -1) { - delete rec[k]; - } - }); - } else { - keys.forEach(k => { - if (k.indexOf(pivotKeys.records) !== -1 && k !== pivotKeys.records) { - this.cleanState(rec[k], pivotKeys, config); - } - }); - } - }); - } } /** @@ -118,42 +162,40 @@ export class IgxPivotRowExpansionPipe implements PipeTransform { export class IgxPivotCellMergingPipe implements PipeTransform { constructor(@Inject(IGX_GRID_BASE) private grid: GridType) { } public transform( - collection: any[], + collection: IPivotGridRecord[], config: IPivotConfiguration, dim: IPivotDimension, - pivotKeys: IPivotKeys, _pipeTrigger?: number - ): any[] { + ): IPivotGridGroupRecord[] { if (collection.length === 0 || config.rows.length === 0) return collection; - const data = collection ? cloneArray(collection, true) : []; - const res = []; + const data: IPivotGridGroupRecord[] = collection ? cloneArray(collection, true) : []; + const res: IPivotGridGroupRecord[] = []; - const enabledRows = config.rows.filter(x => x.enabled); - - const prevDims = enabledRows.filter((d, ind) => ind < enabledRows.indexOf(dim)); - let groupData = []; + const enabledRows = config.rows?.filter(x => x.enabled); + let groupData: IPivotGridGroupRecord[] = []; let prevDim; let prevDimRoot; let prevId; + const index = enabledRows.indexOf(dim); for (let rec of data) { - const dimData = PivotUtil.getDimensionLevel(dim, rec, pivotKeys); - const id = PivotUtil.getRecordKey(rec, dimData.dimension, prevDims, pivotKeys); + const currentDim = rec.dimensions[index]; + const id = PivotUtil.getRecordKey(rec, currentDim); if (groupData.length > 0 && prevId !== id) { const h = groupData.length > 1 ? groupData.length * this.grid.renderedRowHeight : undefined; - groupData[0][prevDimRoot.memberName + pivotKeys.rowDimensionSeparator + 'height'] = h; - groupData[0][prevDim.dimension.memberName + pivotKeys.rowDimensionSeparator + 'rowSpan'] = groupData.length; + groupData[0].height = h; + groupData[0].rowSpan = groupData.length; res.push(groupData[0]); groupData = []; } groupData.push(rec); - prevDim = dimData; + prevDim = currentDim; prevDimRoot = dim; prevId = id; } if (groupData.length > 0) { const h = groupData.length > 1 ? groupData.length * this.grid.rowHeight + (groupData.length - 1) + 1 : undefined; - groupData[0][prevDimRoot.memberName + pivotKeys.rowDimensionSeparator + 'height'] = h; - groupData[0][prevDim.dimension.memberName + pivotKeys.rowDimensionSeparator + 'rowSpan'] = groupData.length; + groupData[0].height = h; + groupData[0].rowSpan = groupData.length; res.push(groupData[0]); } return res; @@ -171,15 +213,15 @@ export class IgxPivotCellMergingPipe implements PipeTransform { export class IgxPivotColumnPipe implements PipeTransform { public transform( - collection: any, + collection: IPivotGridRecord[], config: IPivotConfiguration, _: Map, _pipeTrigger?: number, __? - ): any[] { + ): IPivotGridRecord[] { const pivotKeys = config.pivotKeys || DEFAULT_PIVOT_KEYS; - const enabledColumns = config.columns.filter(x => x.enabled); - const enabledValues = config.values.filter(x => x.enabled); + const enabledColumns = config.columns?.filter(x => x.enabled) || []; + const enabledValues = config.values?.filter(x => x.enabled) || []; const colStrategy = config.columnStrategy || PivotColumnDimensionsStrategy.instance(); const data = cloneArray(collection, true); @@ -230,18 +272,20 @@ export class IgxPivotGridFilterPipe implements PipeTransform { }) export class IgxPivotGridColumnSortingPipe implements PipeTransform { public transform( - collection: any[], + collection: IPivotGridRecord[], expressions: ISortingExpression[], sorting: IGridSortingStrategy, - pipeTrigger: number, - pivotKeys: IPivotKeys = DEFAULT_PIVOT_KEYS - ): any[] { - let result: any[]; + pipeTrigger: number + ): IPivotGridRecord[] { + let result: IPivotGridRecord[]; if (!expressions.length) { result = collection; } else { - result = PivotUtil.sort(cloneArray(collection, true), expressions, sorting, pivotKeys); + for(const expr of expressions) { + expr.strategy = DefaultPivotGridRecordSortingStrategy.instance(); + } + result = PivotUtil.sort(cloneArray(collection, true), expressions, sorting); } return result; } @@ -259,7 +303,7 @@ export class IgxPivotGridSortingPipe implements PipeTransform { public transform(collection: any[], config: IPivotConfiguration, sorting: IGridSortingStrategy, id: string, pipeTrigger: number, pinned?): any[] { let result: any[]; - const allDimensions = config.rows; + const allDimensions = config.rows || []; const enabledDimensions = allDimensions.filter(x => x && x.enabled); const expressions: ISortingExpression[] = []; PivotUtil.flatten(enabledDimensions).forEach(x => { diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts index 1ef15e7992d..3e875064b45 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts @@ -8,1627 +8,1673 @@ import { configureTestSuite } from '../../test-utils/configure-suite'; import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec'; import { IgxPivotGridTestBaseComponent, IgxPivotGridTestComplexHierarchyComponent, IgxTotalSaleAggregate } from '../../test-utils/pivot-grid-samples.spec'; import { UIInteractions } from '../../test-utils/ui-interactions.spec'; -import { PivotDimensionType } from './pivot-grid.interface'; +import { IPivotGridRecord, PivotDimensionType } from './pivot-grid.interface'; import { IgxPivotHeaderRowComponent } from './pivot-header-row.component'; import { IgxPivotDateDimension, IgxPivotGridModule } from './public_api'; import { IgxPivotRowDimensionHeaderComponent } from './pivot-row-dimension-header.component'; -import { IgxPivotDateAggregate } from './pivot-grid-aggregate'; +import { IgxPivotDateAggregate, IgxPivotNumericAggregate } from './pivot-grid-aggregate'; +import { IgxPivotRowComponent } from './pivot-row.component'; +import { PivotGridFunctions } from '../../test-utils/pivot-grid-functions.spec'; const CSS_CLASS_DROP_DOWN_BASE = 'igx-drop-down'; const CSS_CLASS_LIST = 'igx-drop-down__list'; const CSS_CLASS_ITEM = 'igx-drop-down__item'; +describe('IgxPivotGrid #pivotGrid', () => { + + describe('Basic IgxPivotGrid #pivotGrid', () => { + let fixture; + configureTestSuite((() => { + TestBed.configureTestingModule({ + declarations: [ + IgxPivotGridTestBaseComponent + ], + imports: [ + NoopAnimationsModule, IgxPivotGridModule] + }); + })); -describe('Basic IgxPivotGrid #pivotGrid', () => { - let fixture; - configureTestSuite((() => { - TestBed.configureTestingModule({ - declarations: [ - IgxPivotGridTestBaseComponent - ], - imports: [ - NoopAnimationsModule, IgxPivotGridModule] - }); - })); - - beforeEach(fakeAsync(() => { - fixture = TestBed.createComponent(IgxPivotGridTestBaseComponent); - fixture.detectChanges(); - })); - - it('should apply formatter and dataType from measures', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.width = '1500px'; - fixture.detectChanges(); - - const actualFormatterValue = pivotGrid.rowList.first.cells.first.title; - expect(actualFormatterValue).toEqual('774$'); - const actualDataTypeValue = pivotGrid.rowList.first.cells.last.title; - expect(actualDataTypeValue).toEqual('$71.89'); - }); + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxPivotGridTestBaseComponent); + fixture.detectChanges(); + })); + it('should show empty template when there are no dimensions and values', () => { + // whole pivotConfiguration is undefined + const pivotGrid = fixture.componentInstance.pivotGrid as IgxPivotGridComponent; + pivotGrid.pivotConfiguration = undefined; + fixture.detectChanges(); - it('should apply css class to cells from measures', () => { - fixture.detectChanges(); - const pivotGrid = fixture.componentInstance.pivotGrid; - const cells = pivotGrid.rowList.first.cells; - expect(cells.first.nativeElement.classList).toContain('test'); - expect(cells.last.nativeElement.classList).not.toContain('test'); - }); + // no rows, just empty message + expect(pivotGrid.rowList.length).toBe(0); + expect(pivotGrid.tbody.nativeElement.textContent).toBe('Pivot grid has no dimensions and values.'); + + // configuration is defined but all collections are empty + pivotGrid.pivotConfiguration = { + columns: [], + rows: [], + values: [] + }; + fixture.detectChanges(); + + // no rows, just empty message + expect(pivotGrid.rowList.length).toBe(0); + expect(pivotGrid.tbody.nativeElement.textContent).toBe('Pivot grid has no dimensions and values.'); + + + // has dimensions and values, but they are disabled + pivotGrid.pivotConfiguration = { + columns: [ + { + enabled: false, + memberName: 'Country' + } + ], + rows: [ + { + enabled: false, + memberName: 'ProductCategory' + } + ], + values: [ + { + enabled: false, + member: 'UnitsSold', + aggregate: { + aggregator: IgxPivotNumericAggregate.sum, + key: 'SUM', + label: 'Sum', + }, + } + ] + }; + fixture.detectChanges(); - it('should remove row dimensions from chip', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.rows.push({ - memberName: 'SellerName', - enabled: true + // no rows, just empty message + expect(pivotGrid.rowList.length).toBe(0); + expect(pivotGrid.tbody.nativeElement.textContent).toBe('Pivot grid has no dimensions and values.'); }); - pivotGrid.pipeTrigger++; - fixture.detectChanges(); - expect(pivotGrid.rowDimensions.length).toBe(2); - expect(pivotGrid.rowList.first.data['SellerName']).not.toBeUndefined(); - - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="SellerName"]'); - const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; - removeIcon.click(); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.rows[1].enabled).toBeFalse(); - expect(pivotGrid.rowDimensions.length).toBe(1); - expect(pivotGrid.rowList.first.data['SellerName']).toBeUndefined(); - }); + it('should show allow setting custom empty template.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid as IgxPivotGridComponent; + pivotGrid.emptyPivotGridTemplate = fixture.componentInstance.emptyTemplate; + pivotGrid.pivotConfiguration = undefined; + fixture.detectChanges(); - it('should remove column dimensions from chip', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - expect(pivotGrid.columns.length).toBe(9); - pivotGrid.pivotConfiguration.columns.push({ - memberName: 'SellerName', - enabled: true + // no rows, just empty message + expect(pivotGrid.rowList.length).toBe(0); + expect(pivotGrid.tbody.nativeElement.textContent).toBe('Custom empty template.'); }); - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); - fixture.detectChanges(); - expect(pivotGrid.columnDimensions.length).toBe(2); - expect(pivotGrid.columns.length).not.toBe(9); - - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="SellerName"]'); - const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; - removeIcon.click(); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.columns[1].enabled).toBeFalse(); - expect(pivotGrid.columnDimensions.length).toBe(1); - expect(pivotGrid.columns.length).toBe(9); - }); - it('should remove value from chip', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - expect(pivotGrid.columns.length).toBe(9); - expect(pivotGrid.values.length).toBe(2); - - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="UnitsSold"]'); - const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; - removeIcon.click(); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.values[0].enabled).toBeFalse(); - expect(pivotGrid.values.length).toBe(1); - expect(pivotGrid.columns.length).not.toBe(9); - }); + it('should apply formatter and dataType from measures', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.width = '1500px'; + fixture.detectChanges(); - it('should remove filter dimension from chip', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; + const actualFormatterValue = pivotGrid.rowList.first.cells.first.title; + expect(actualFormatterValue).toEqual('774$'); + const actualDataTypeValue = pivotGrid.rowList.first.cells.last.title; + expect(actualDataTypeValue).toEqual('$71.89'); + }); - const filteringExpressionTree = new FilteringExpressionsTree(FilteringLogic.And); - filteringExpressionTree.filteringOperands = [ - { - condition: IgxStringFilteringOperand.instance().condition('equals'), - fieldName: 'SellerName', - searchVal: 'Stanley' - } - ]; - const filterDimension = { - memberName: 'SellerName', - enabled: true, - filter: filteringExpressionTree - }; - pivotGrid.pivotConfiguration.filters = [filterDimension]; - pivotGrid.pipeTrigger++; - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.filters[0].enabled).toBeTrue(); - expect(pivotGrid.rowList.length).toBe(2); - - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="SellerName"]'); - const removeIcon = rowChip.querySelectorAll('igx-icon')[2]; - removeIcon.click(); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.filters[0].enabled).toBeFalse(); - expect(pivotGrid.rowList.length).toBe(5); - }); + it('should apply css class to cells from measures', () => { + fixture.detectChanges(); + const pivotGrid = fixture.componentInstance.pivotGrid; + const cells = pivotGrid.rowList.first.cells; + expect(cells.first.nativeElement.classList).toContain('test'); + expect(cells.last.nativeElement.classList).not.toContain('test'); + }); - it('should collapse column with 1 value dimension', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.values.pop(); - pivotGrid.pivotConfiguration.columns = [{ - memberName: 'AllCountries', - memberFunction: () => 'All Countries', - enabled: true, - childLevel: { - memberName: 'Country', - enabled: true - } - }]; - pivotGrid.pivotConfiguration.rows[0] = new IgxPivotDateDimension( - { - memberName: 'Date', + it('should remove row dimensions from chip', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.rows.push({ + memberName: 'SellerName', enabled: true - }, { - total: false - } - ); - pivotGrid.notifyDimensionChange(true); - expect(pivotGrid.columns.length).toBe(5); - expect(pivotGrid.columnGroupStates.size).toBe(0); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const header = headerRow.querySelector('igx-grid-header-group'); - const expander = header.querySelectorAll('igx-icon')[0]; - expander.click(); - fixture.detectChanges(); - expect(pivotGrid.columnGroupStates.size).toBe(1); - const value = pivotGrid.columnGroupStates.entries().next().value; - expect(value[0]).toEqual('All Countries'); - expect(value[1]).toBeTrue(); - }); + }); + pivotGrid.pipeTrigger++; + fixture.detectChanges(); + expect(pivotGrid.rowDimensions.length).toBe(2); + let pivotRecord = (pivotGrid.rowList.first as IgxPivotRowComponent).data; + expect(pivotRecord.dimensionValues.get('SellerName')).not.toBeUndefined(); + + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="SellerName"]'); + const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; + removeIcon.click(); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.rows[1].enabled).toBeFalse(); + expect(pivotGrid.rowDimensions.length).toBe(1); + pivotRecord = (pivotGrid.rowList.first as IgxPivotRowComponent).data; + expect(pivotRecord.dimensionValues.get('SellerName')).toBeUndefined(); + }); - it('should collapse column with 2 value dimension', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.columns = [{ - memberName: 'AllCountries', - memberFunction: () => 'All Countries', - enabled: true, - childLevel: { - memberName: 'Country', + it('should remove column dimensions from chip', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + expect(pivotGrid.columns.length).toBe(9); + pivotGrid.pivotConfiguration.columns.push({ + memberName: 'SellerName', enabled: true - } - }, - { - memberName: 'SellerName', - enabled: true - }]; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - expect(pivotGrid.columnGroupStates.size).toBe(0); - let headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - let header = headerRow.querySelector('igx-grid-header-group'); - let expander = header.querySelectorAll('igx-icon')[0]; - expander.click(); - fixture.detectChanges(); - expect(pivotGrid.columnGroupStates.size).toBe(1); - let value = pivotGrid.columnGroupStates.entries().next().value; - expect(value[0]).toEqual('All Countries'); - expect(value[1]).toBeTrue(); - - headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - header = headerRow.querySelector('igx-grid-header-group'); - expander = header.querySelectorAll('igx-icon')[0]; - expander.click(); - fixture.detectChanges(); - value = pivotGrid.columnGroupStates.entries().next().value; - expect(value[0]).toEqual('All Countries'); - expect(value[1]).toBeFalse(); - }); + }); + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + expect(pivotGrid.columnDimensions.length).toBe(2); + expect(pivotGrid.columns.length).not.toBe(9); - it('should display aggregations when no row dimensions are enabled', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.columns = [ - new IgxPivotDateDimension( - { - memberName: 'Date', - enabled: true - }, - { - months: false - } - ) - ]; - pivotGrid.pivotConfiguration.rows = []; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - expect(pivotGrid.rowList.first.cells.length).toBeGreaterThanOrEqual(1); - expect(pivotGrid.rowList.first.cells.first.title).toEqual('282$'); - }); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="SellerName"]'); + const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; + removeIcon.click(); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.columns[1].enabled).toBeFalse(); + expect(pivotGrid.columnDimensions.length).toBe(1); + expect(pivotGrid.columns.length).toBe(9); + }); - it('should display aggregations when no col dimensions are enabled', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.rows = [ - { - memberName: 'City', - enabled: true, - } - ]; - pivotGrid.pivotConfiguration.columns = []; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - expect(pivotGrid.rowList.first.cells.length).toEqual(pivotGrid.values.length); - expect(pivotGrid.rowList.first.cells.first.title).toEqual('2127$'); - }); + it('should remove value from chip', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + expect(pivotGrid.columns.length).toBe(9); + expect(pivotGrid.values.length).toBe(2); - it('should display aggregations when neither col nor row dimensions are set', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.rows = []; - pivotGrid.pivotConfiguration.columns = []; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="UnitsSold"]'); + const removeIcon = rowChip.querySelectorAll('igx-icon')[3]; + removeIcon.click(); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.values[0].enabled).toBeFalse(); + expect(pivotGrid.values.length).toBe(1); + expect(pivotGrid.columns.length).not.toBe(9); + }); - expect(pivotGrid.rowList.first.cells.length).toEqual(pivotGrid.values.length); - expect(pivotGrid.rowList.first.cells.first.title).toEqual('2127$'); - }); + it('should remove filter dimension from chip', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; - it('should reevaluate aggregated values when all row dimensions are removed', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.height = '700px'; - pivotGrid.width = '1000px'; - pivotGrid.pivotConfiguration.columns = [ - new IgxPivotDateDimension( + const filteringExpressionTree = new FilteringExpressionsTree(FilteringLogic.And); + filteringExpressionTree.filteringOperands = [ { - memberName: 'Date', - enabled: true - }, - { - months: false + condition: IgxStringFilteringOperand.instance().condition('equals'), + fieldName: 'SellerName', + searchVal: 'Stanley' } - ) - ]; - pivotGrid.pivotConfiguration.rows = [ - { - memberName: 'AllSeller', - memberFunction: () => 'All Sellers', + ]; + const filterDimension = { + memberName: 'SellerName', + enabled: true, + filter: filteringExpressionTree + }; + pivotGrid.pivotConfiguration.filters = [filterDimension]; + pivotGrid.pipeTrigger++; + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.filters[0].enabled).toBeTrue(); + expect(pivotGrid.rowList.length).toBe(2); + + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="SellerName"]'); + const removeIcon = rowChip.querySelectorAll('igx-icon')[2]; + removeIcon.click(); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.filters[0].enabled).toBeFalse(); + expect(pivotGrid.rowList.length).toBe(5); + }); + + it('should collapse column with 1 value dimension', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.values.pop(); + pivotGrid.pivotConfiguration.columns = [{ + memberName: 'AllCountries', + memberFunction: () => 'All Countries', enabled: true, childLevel: { - enabled: true, - memberName: 'SellerName' + memberName: 'Country', + enabled: true } - } - ]; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - - let uniqueVals = Array.from(new Set(pivotGrid.data.map(x => x.SellerName))).length; - expect(pivotGrid.rowList.length).toEqual(uniqueVals + 1); - expect(pivotGrid.rowList.first.cells.first.title).toEqual('282$'); - expect(pivotGrid.rowDimensions.length).toEqual(1); - - pivotGrid.pivotConfiguration.rows = []; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - expect(pivotGrid.rowList.length).toEqual(1); - expect(pivotGrid.rowDimensions.length).toEqual(0); - }); - - it('should reevaluate aggregated values when all col dimensions are removed', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.height = '700px'; - pivotGrid.width = '1000px'; - pivotGrid.pivotConfiguration.columns = [ - new IgxPivotDateDimension( + }]; + pivotGrid.pivotConfiguration.rows[0] = new IgxPivotDateDimension( { memberName: 'Date', enabled: true - }, - { - months: false - } - ) - ]; - pivotGrid.pivotConfiguration.rows = [ - { - memberName: 'AllSeller', - memberFunction: () => 'All Sellers', - enabled: true, - childLevel: { - enabled: true, - memberName: 'SellerName' - } + }, { + total: false } - ]; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - - let uniqueVals = Array.from(new Set(pivotGrid.data.map(x => x.SellerName))).length; - expect(pivotGrid.rowList.length).toEqual(uniqueVals + 1); - expect(pivotGrid.rowList.first.cells.first.title).toEqual('282$'); - expect(pivotGrid.rowList.first.cells.length).toEqual(5); - expect(pivotGrid.columnDimensions.length).toEqual(1); - - pivotGrid.pivotConfiguration.columns = []; - pivotGrid.notifyDimensionChange(true); - fixture.detectChanges(); - - expect(pivotGrid.rowList.first.cells.length).toEqual(pivotGrid.values.length); - expect(pivotGrid.columnDimensions.length).toEqual(0); - }); - - describe('IgxPivotGrid Features #pivotGrid', () => { - it('should show excel style filtering via dimension chip.', () => { - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + ); + pivotGrid.notifyDimensionChange(true); + expect(pivotGrid.columns.length).toBe(5); + expect(pivotGrid.columnGroupStates.size).toBe(0); const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="All"]'); - const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; - - expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); - filterIcon.click(); + const header = headerRow.querySelector('igx-grid-header-group'); + const expander = header.querySelectorAll('igx-icon')[0]; + expander.click(); fixture.detectChanges(); - const esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); - const checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); - // should show and should display correct checkboxes. - expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); - expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); - expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Bikes'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Components'); + expect(pivotGrid.columnGroupStates.size).toBe(1); + const value = pivotGrid.columnGroupStates.entries().next().value; + expect(value[0]).toEqual('All Countries'); + expect(value[1]).toBeTrue(); }); - it('should filter rows via excel style filtering dimension chip.', () => { + it('should collapse column with 2 value dimension', () => { const pivotGrid = fixture.componentInstance.pivotGrid; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="All"]'); - const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; - filterIcon.click(); - fixture.detectChanges(); - - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; - const checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); - - // uncheck Accessories - checkboxes[1].click(); + pivotGrid.pivotConfiguration.columns = [{ + memberName: 'AllCountries', + memberFunction: () => 'All Countries', + enabled: true, + childLevel: { + memberName: 'Country', + enabled: true + } + }, + { + memberName: 'SellerName', + enabled: true + }]; + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - - // uncheck Bikes - checkboxes[2].click(); + expect(pivotGrid.columnGroupStates.size).toBe(0); + let headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + let header = headerRow.querySelector('igx-grid-header-group'); + let expander = header.querySelectorAll('igx-icon')[0]; + expander.click(); fixture.detectChanges(); - - // Click 'apply' button to apply filter. - GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); + expect(pivotGrid.columnGroupStates.size).toBe(1); + let value = pivotGrid.columnGroupStates.entries().next().value; + expect(value[0]).toEqual('All Countries'); + expect(value[1]).toBeTrue(); + + headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + header = headerRow.querySelector('igx-grid-header-group'); + expander = header.querySelectorAll('igx-icon')[0]; + expander.click(); fixture.detectChanges(); - - // check rows - const rows = pivotGrid.rowList.toArray(); - expect(rows.length).toBe(3); - const expectedHeaders = ['All', 'Clothing', 'Components']; - const rowHeaders = fixture.debugElement.queryAll( - By.directive(IgxPivotRowDimensionHeaderComponent)); - const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); - expect(rowDimensionHeaders).toEqual(expectedHeaders); + value = pivotGrid.columnGroupStates.entries().next().value; + expect(value[0]).toEqual('All Countries'); + expect(value[1]).toBeFalse(); }); - it('should filter columns via excel style filtering dimension chip.', () => { + it('should display aggregations when no row dimensions are enabled', () => { const pivotGrid = fixture.componentInstance.pivotGrid; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="Country"]'); - const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; - filterIcon.click(); - fixture.detectChanges(); - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; - const checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); - - // uncheck Bulgaria - checkboxes[1].click(); - fixture.detectChanges(); - - // uncheck Uruguay - checkboxes[2].click(); + pivotGrid.pivotConfiguration.columns = [ + new IgxPivotDateDimension( + { + memberName: 'Date', + enabled: true + }, + { + months: false + } + ) + ]; + pivotGrid.pivotConfiguration.rows = []; + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); + expect(pivotGrid.rowList.first.cells.length).toBeGreaterThanOrEqual(1); + expect(pivotGrid.rowList.first.cells.first.title).toEqual('282$'); + }); - - // Click 'apply' button to apply filter. - GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); + it('should display aggregations when no col dimensions are enabled', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'City', + enabled: true, + } + ]; + pivotGrid.pivotConfiguration.columns = []; + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - - // check columns - const colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); - const expected = ['USA']; - expect(colHeaders).toEqual(expected); + expect(pivotGrid.rowList.first.cells.length).toEqual(pivotGrid.values.length); + expect(pivotGrid.rowList.first.cells.first.title).toEqual('2127$'); }); - it('should show filters chips', () => { + it('should display aggregations when neither col nor row dimensions are set', () => { const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.filters = [{ - memberName: 'SellerName', - enabled: true - }]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + pivotGrid.pivotConfiguration.rows = []; + pivotGrid.pivotConfiguration.columns = []; + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const filtersChip = headerRow.querySelector('igx-chip[id="SellerName"]'); - const filterIcon = filtersChip.querySelectorAll('igx-icon')[1]; - expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); - filterIcon.click(); - fixture.detectChanges(); - const esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); - const checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); - // should show and should display correct checkboxes. - expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); - expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); - expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Elisa'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('John'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Larry'); + expect(pivotGrid.rowList.first.cells.length).toEqual(pivotGrid.values.length); + expect(pivotGrid.rowList.first.cells.first.title).toEqual('2127$'); }); - it('should show filters in chips dropdown button', () => { + it('should reevaluate aggregated values when all row dimensions are removed', () => { const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.filters = [ - { - memberName: 'SellerName', - enabled: true - }, + pivotGrid.height = '700px'; + pivotGrid.width = '1000px'; + pivotGrid.pivotConfiguration.columns = [ + new IgxPivotDateDimension( + { + memberName: 'Date', + enabled: true + }, + { + months: false + } + ) + ]; + pivotGrid.pivotConfiguration.rows = [ { - memberName: 'ProductCategory', - enabled: true + memberName: 'AllSeller', + memberFunction: () => 'All Sellers', + enabled: true, + childLevel: { + enabled: true, + memberName: 'SellerName' + } } ]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[0]; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[1]; - expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); - dropdownIcon.click(); - fixture.detectChanges(); + let uniqueVals = Array.from(new Set(pivotGrid.data.map(x => x.SellerName))).length; + expect(pivotGrid.rowList.length).toEqual(uniqueVals + 1); + expect(pivotGrid.rowList.first.cells.first.title).toEqual('282$'); + expect(pivotGrid.rowDimensions.length).toEqual(1); - const chips = excelMenu.querySelectorAll('igx-chip'); - expect(chips[0].id).toBe('SellerName'); - expect(chips[0].attributes.getNamedItem('ng-reflect-selected').nodeValue).toEqual('true'); - expect(chips[1].id).toBe('ProductCategory'); - expect(chips[1].attributes.getNamedItem('ng-reflect-selected').nodeValue).toEqual('false'); - - let esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); - let checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); - // should show and should display correct checkboxes. - expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); - expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); - expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Elisa'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('John'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Larry'); - - // switch to the `ProductCategory` filters - const chipAreaElement = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const chipComponents = chipAreaElement[4].queryAll(By.directive(IgxChipComponent)); - chipComponents[1].triggerEventHandler('chipClick', { - owner: { - id: chips[1].id - } - }); + pivotGrid.pivotConfiguration.rows = []; + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - - esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); - checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); - expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); - expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); - expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Bikes'); - expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); - expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Components'); + expect(pivotGrid.rowList.length).toEqual(1); + expect(pivotGrid.rowDimensions.length).toEqual(0); }); - it('should be able to filter from chips dropdown button', () => { + it('should reevaluate aggregated values when all col dimensions are removed', () => { const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.filters = [ - { - memberName: 'SellerName', - enabled: true - }, + pivotGrid.height = '700px'; + pivotGrid.width = '1000px'; + pivotGrid.pivotConfiguration.columns = [ + new IgxPivotDateDimension( + { + memberName: 'Date', + enabled: true + }, + { + months: false + } + ) + ]; + pivotGrid.pivotConfiguration.rows = [ { - memberName: 'ProductCategory', - enabled: true + memberName: 'AllSeller', + memberFunction: () => 'All Sellers', + enabled: true, + childLevel: { + enabled: true, + memberName: 'SellerName' + } } ]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[0]; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[1]; - expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); - dropdownIcon.click(); + let uniqueVals = Array.from(new Set(pivotGrid.data.map(x => x.SellerName))).length; + expect(pivotGrid.rowList.length).toEqual(uniqueVals + 1); + expect(pivotGrid.rowList.first.cells.first.title).toEqual('282$'); + expect(pivotGrid.rowList.first.cells.length).toEqual(5); + expect(pivotGrid.columnDimensions.length).toEqual(1); + + pivotGrid.pivotConfiguration.columns = []; + pivotGrid.notifyDimensionChange(true); fixture.detectChanges(); - const checkBoxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); + expect(pivotGrid.rowList.first.cells.length).toEqual(pivotGrid.values.length); + expect(pivotGrid.columnDimensions.length).toEqual(0); + }); - // uncheck David - checkBoxes[1].click(); - fixture.detectChanges(); - // uncheck Elisa - checkBoxes[5].click(); - fixture.detectChanges(); + describe('IgxPivotGrid Features #pivotGrid', () => { + it('should show excel style filtering via dimension chip.', () => { + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="All"]'); + const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; + + expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); + filterIcon.click(); + fixture.detectChanges(); + const esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); + const checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); + // should show and should display correct checkboxes. + expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); + expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); + expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Bikes'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); + expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Components'); + }); - // Click 'apply' button to apply filter. - GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); - fixture.detectChanges(); + it('should filter rows via excel style filtering dimension chip.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="All"]'); + const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; + filterIcon.click(); + fixture.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + const checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); + + // uncheck Accessories + checkboxes[1].click(); + fixture.detectChanges(); + + // uncheck Bikes + checkboxes[2].click(); + fixture.detectChanges(); + + // Click 'apply' button to apply filter. + GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); + fixture.detectChanges(); + + // check rows + const rows = pivotGrid.rowList.toArray(); + expect(rows.length).toBe(3); + const expectedHeaders = ['All', 'Clothing', 'Components']; + const rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); + expect(rowDimensionHeaders).toEqual(expectedHeaders); + }); - // check rows - const expectedHeaders = ['All', 'Clothing', 'Components']; - const rowHeaders = fixture.debugElement.queryAll( - By.directive(IgxPivotRowDimensionHeaderComponent)); - const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); - expect(rowHeaders.length).toBe(3); - expect(rowDimensionHeaders).toEqual(expectedHeaders); - }); + it('should filter columns via excel style filtering dimension chip.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="Country"]'); + const filterIcon = rowChip.querySelectorAll('igx-icon')[2]; + filterIcon.click(); + fixture.detectChanges(); + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + const checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); + + // uncheck Bulgaria + checkboxes[1].click(); + fixture.detectChanges(); + + // uncheck Uruguay + checkboxes[2].click(); + fixture.detectChanges(); + + + // Click 'apply' button to apply filter. + GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); + fixture.detectChanges(); + + // check columns + const colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); + const expected = ['USA']; + expect(colHeaders).toEqual(expected); + }); - it('should show chips and dropdown if enough space', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.filters = [ - { - memberName: 'Date', + it('should show filters chips', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.filters = [{ + memberName: 'SellerName', enabled: true - }, - { - memberName: 'ProductCategory', + }]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[1]; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const filtersChip = headerRow.querySelector('igx-chip[id="SellerName"]'); + const filterIcon = filtersChip.querySelectorAll('igx-icon')[1]; + + expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); + filterIcon.click(); + fixture.detectChanges(); + const esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); + const checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); + // should show and should display correct checkboxes. + expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); + expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); + expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Elisa'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('John'); + expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Larry'); + }); + + it('should show filters in chips dropdown button', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.filters = [ + { + memberName: 'SellerName', + enabled: true + }, + { + memberName: 'ProductCategory', + enabled: true + } + ]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[0]; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[1]; + + expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); + dropdownIcon.click(); + fixture.detectChanges(); + + const chips = excelMenu.querySelectorAll('igx-chip'); + expect(chips[0].id).toBe('SellerName'); + expect(chips[0].attributes.getNamedItem('ng-reflect-selected').nodeValue).toEqual('true'); + expect(chips[1].id).toBe('ProductCategory'); + expect(chips[1].attributes.getNamedItem('ng-reflect-selected').nodeValue).toEqual('false'); + + let esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); + let checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); + // should show and should display correct checkboxes. + expect(excelMenu.parentElement.parentElement.attributes.hidden).toBeUndefined(); + expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('David'); + expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Elisa'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('John'); + expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Larry'); + + // switch to the `ProductCategory` filters + const chipAreaElement = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + const chipComponents = chipAreaElement[4].queryAll(By.directive(IgxChipComponent)); + chipComponents[1].triggerEventHandler('chipClick', { + owner: { + id: chips[1].id + } + }); + fixture.detectChanges(); + + esfSearch = GridFunctions.getExcelFilteringSearchComponent(fixture, excelMenu, 'igx-pivot-grid'); + checkBoxes = esfSearch.querySelectorAll('igx-checkbox'); + expect((checkBoxes[0].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Select All'); + expect((checkBoxes[1].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Accessories'); + expect((checkBoxes[2].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Bikes'); + expect((checkBoxes[3].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Clothing'); + expect((checkBoxes[4].querySelector('.igx-checkbox__label') as HTMLElement).innerText).toEqual('Components'); + }); + + it('should be able to filter from chips dropdown button', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.filters = [ + { + memberName: 'SellerName', + enabled: true + }, + { + memberName: 'ProductCategory', + enabled: true + } + ]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + const excelMenu = GridFunctions.getExcelStyleFilteringComponents(fixture, 'igx-pivot-grid')[0]; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[1]; + + expect(excelMenu.parentElement.parentElement.attributes.hidden).not.toBeUndefined(); + dropdownIcon.click(); + fixture.detectChanges(); + + const checkBoxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fixture, excelMenu, 'igx-pivot-grid')); + + // uncheck David + checkBoxes[1].click(); + fixture.detectChanges(); + + // uncheck Elisa + checkBoxes[5].click(); + fixture.detectChanges(); + + // Click 'apply' button to apply filter. + GridFunctions.clickApplyExcelStyleFiltering(fixture, excelMenu, 'igx-pivot-grid'); + fixture.detectChanges(); + + // check rows + const expectedHeaders = ['All', 'Clothing', 'Components']; + const rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); + expect(rowHeaders.length).toBe(3); + expect(rowDimensionHeaders).toEqual(expectedHeaders); + }); + + it('should show chips and dropdown if enough space', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.filters = [ + { + memberName: 'Date', + enabled: true + }, + { + memberName: 'ProductCategory', + enabled: true + } + ]; + + pivotGrid.pivotConfiguration.rows = [{ + memberName: 'SellerName', enabled: true - } - ]; + }]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[4]; + expect(dropdownIcon).not.toBeUndefined(); + expect(headerRow.querySelector('igx-badge').innerText).toBe('1'); + const filtersChip = headerRow.querySelector('igx-chip[id="Date"]'); + expect(filtersChip).not.toBeUndefined(); + }); - pivotGrid.pivotConfiguration.rows = [{ - memberName: 'SellerName', - enabled: true - }]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); - fixture.detectChanges(); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const dropdownIcon = headerRow.querySelector('.igx-grid__tr-pivot--filter').querySelectorAll('igx-icon')[4]; - expect(dropdownIcon).not.toBeUndefined(); - expect(headerRow.querySelector('igx-badge').innerText).toBe('1'); - const filtersChip = headerRow.querySelector('igx-chip[id="Date"]'); - expect(filtersChip).not.toBeUndefined(); - }); + it('should apply sorting for dimension via row chip', () => { + fixture.detectChanges(); + const pivotGrid = fixture.componentInstance.pivotGrid; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const rowChip = headerRow.querySelector('igx-chip[id="All"]'); + rowChip.click(); + fixture.detectChanges(); + let rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + let expectedOrder = ['All', 'Accessories', 'Bikes', 'Clothing', 'Components']; + let rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); + expect(rowDimensionHeaders).toEqual(expectedOrder); + + rowChip.click(); + fixture.detectChanges(); + expectedOrder = ['All', 'Components', 'Clothing', 'Bikes', 'Accessories']; + rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); + expect(rowDimensionHeaders).toEqual(expectedOrder); + }); - it('should apply sorting for dimension via row chip', () => { - fixture.detectChanges(); - const pivotGrid = fixture.componentInstance.pivotGrid; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const rowChip = headerRow.querySelector('igx-chip[id="All"]'); - rowChip.click(); - fixture.detectChanges(); - let rowHeaders = fixture.debugElement.queryAll( - By.directive(IgxPivotRowDimensionHeaderComponent)); - let expectedOrder = ['All', 'Accessories', 'Bikes', 'Clothing', 'Components']; - let rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); - expect(rowDimensionHeaders).toEqual(expectedOrder); + it('should apply sorting for dimension via column chip', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const colChip = headerRow.querySelector('igx-chip[id="Country"]'); - rowChip.click(); - fixture.detectChanges(); - expectedOrder = ['All', 'Components', 'Clothing', 'Bikes', 'Accessories']; - rowHeaders = fixture.debugElement.queryAll( - By.directive(IgxPivotRowDimensionHeaderComponent)); - rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); - expect(rowDimensionHeaders).toEqual(expectedOrder); - }); + // sort + colChip.click(); + fixture.detectChanges(); - it('should apply sorting for dimension via column chip', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const colChip = headerRow.querySelector('igx-chip[id="Country"]'); + let colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); + let expected = ['Bulgaria', 'USA', 'Uruguay']; + expect(colHeaders).toEqual(expected); - // sort - colChip.click(); - fixture.detectChanges(); + // sort + colChip.click(); + fixture.detectChanges(); - let colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); - let expected = ['Bulgaria', 'USA', 'Uruguay']; - expect(colHeaders).toEqual(expected); + colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); + expected = ['Uruguay', 'USA', 'Bulgaria']; + expect(colHeaders).toEqual(expected); + }); - // sort - colChip.click(); - fixture.detectChanges(); + it('should sort on column for single row dimension.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + let headerCell = GridFunctions.getColumnHeader('USA-UnitsSold', fixture); + + // sort asc + GridFunctions.clickHeaderSortIcon(headerCell); + fixture.detectChanges(); + expect(pivotGrid.sortingExpressions.length).toBe(1); + let expectedOrder = [829, undefined, 240, 293, 296]; + let columnValues = pivotGrid.dataView.map(x => (x as IPivotGridRecord).aggregationValues.get('USA-UnitsSold')); + expect(columnValues).toEqual(expectedOrder); + + headerCell = GridFunctions.getColumnHeader('USA-UnitsSold', fixture); + // sort desc + GridFunctions.clickHeaderSortIcon(headerCell); + fixture.detectChanges(); + expect(pivotGrid.sortingExpressions.length).toBe(1); + expectedOrder = [829, 296, 293, 240, undefined]; + columnValues = pivotGrid.dataView.map(x => (x as IPivotGridRecord).aggregationValues.get('USA-UnitsSold')); + expect(columnValues).toEqual(expectedOrder); + + // remove sort + headerCell = GridFunctions.getColumnHeader('USA-UnitsSold', fixture); + GridFunctions.clickHeaderSortIcon(headerCell); + fixture.detectChanges(); + expect(pivotGrid.sortingExpressions.length).toBe(0); + expectedOrder = [829, 293, undefined, 296, 240]; + columnValues = pivotGrid.dataView.map(x => (x as IPivotGridRecord).aggregationValues.get('USA-UnitsSold')); + expect(columnValues).toEqual(expectedOrder); + }); - colHeaders = pivotGrid.columns.filter(x => x.level === 0).map(x => x.header); - expected = ['Uruguay', 'USA', 'Bulgaria']; - expect(colHeaders).toEqual(expected); - }); + // xit-ing because of https://github.com/IgniteUI/igniteui-angular/issues/10546 + xit('should sort on column for all sibling dimensions.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.height = '1500px'; + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'ProductCategory', + enabled: true + }, + { + memberName: 'SellerName', + enabled: true + } + ]; + // add a bit more data to sort. + pivotGrid.data = [ + { + ProductCategory: 'Clothing', UnitPrice: 12.81, SellerName: 'Stanley', + Country: 'Bulgaria', Date: '01/01/2021', UnitsSold: 282 + }, + { + ProductCategory: 'Clothing', UnitPrice: 49.57, SellerName: 'Elisa', + Country: 'USA', Date: '01/05/2019', UnitsSold: 296 + }, + { + ProductCategory: 'Bikes', UnitPrice: 3.56, SellerName: 'Lydia', + Country: 'Uruguay', Date: '01/06/2020', UnitsSold: 68 + }, + { + ProductCategory: 'Accessories', UnitPrice: 85.58, SellerName: 'David', + Country: 'USA', Date: '04/07/2021', UnitsSold: 293 + }, + { + ProductCategory: 'Components', UnitPrice: 18.13, SellerName: 'John', + Country: 'USA', Date: '12/08/2021', UnitsSold: 240 + }, + { + ProductCategory: 'Clothing', UnitPrice: 68.33, SellerName: 'Larry', + Country: 'Uruguay', Date: '05/12/2020', UnitsSold: 456 + }, + { + ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Walter', + Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 492 + }, + { + ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Elisa', + Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 267 + }, + { + ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Larry', + Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 100 + } + ]; + pivotGrid.pipeTrigger++; + fixture.detectChanges(); + const headerCell = GridFunctions.getColumnHeader('Bulgaria-UnitsSold', fixture); + // sort asc + GridFunctions.clickHeaderSortIcon(headerCell); + fixture.detectChanges(); + expect(pivotGrid.sortingExpressions.length).toBe(1); + let expectedOrder = [undefined, undefined, undefined, 100, 267, 282, 492]; + let columnValues = pivotGrid.dataView.map(x => x['Bulgaria-UnitsSold']); + expect(columnValues).toEqual(expectedOrder); + + // sort desc + GridFunctions.clickHeaderSortIcon(headerCell); + fixture.detectChanges(); + expect(pivotGrid.sortingExpressions.length).toBe(1); + expectedOrder = [492, 282, 267, 100, undefined, undefined, undefined]; + columnValues = pivotGrid.dataView.map(x => x['Bulgaria-UnitsSold']); + expect(columnValues).toEqual(expectedOrder); + }); - it('should sort on column for single row dimension.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - let headerCell = GridFunctions.getColumnHeader('USA-UnitsSold', fixture); + it('should allow changing default aggregation via value chip drop-down.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.width = '1500px'; + fixture.detectChanges(); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const valueChip = headerRow.querySelector('igx-chip[id="UnitsSold"]'); + let content = valueChip.querySelector('.igx-chip__content'); + expect(content.textContent.trim()).toBe('SUM(UnitsSold)'); + + const aggregatesIcon = valueChip.querySelectorAll('igx-icon')[1]; + aggregatesIcon.click(); + fixture.detectChanges(); + const items = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`)); + expect(items.length).toBe(5); + // select count + items[0].triggerEventHandler('click', UIInteractions.getMouseEvent('click')); + fixture.detectChanges(); + + // check chip and row + content = valueChip.querySelector('.igx-chip__content'); + expect(content.textContent.trim()).toBe('COUNT(UnitsSold)'); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Bulgaria-UnitsSold').value).toBe(2); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 'USA-UnitsSold').value).toBe(3); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Uruguay-UnitsSold').value).toBe(2); - // sort asc - GridFunctions.clickHeaderSortIcon(headerCell); - fixture.detectChanges(); - expect(pivotGrid.sortingExpressions.length).toBe(1); - let expectedOrder = [829, undefined, 240, 293, 296]; - let columnValues = pivotGrid.dataView.map(x => x['USA-UnitsSold']); - expect(columnValues).toEqual(expectedOrder); - - headerCell = GridFunctions.getColumnHeader('USA-UnitsSold', fixture); - // sort desc - GridFunctions.clickHeaderSortIcon(headerCell); - fixture.detectChanges(); - expect(pivotGrid.sortingExpressions.length).toBe(1); - expectedOrder = [829, 296, 293, 240, undefined]; - columnValues = pivotGrid.dataView.map(x => x['USA-UnitsSold']); - expect(columnValues).toEqual(expectedOrder); - - // remove sort - headerCell = GridFunctions.getColumnHeader('USA-UnitsSold', fixture); - GridFunctions.clickHeaderSortIcon(headerCell); - fixture.detectChanges(); - expect(pivotGrid.sortingExpressions.length).toBe(0); - expectedOrder = [829, 293, undefined, 296, 240]; - columnValues = pivotGrid.dataView.map(x => x['USA-UnitsSold']); - expect(columnValues).toEqual(expectedOrder); - }); + }); + it('should allow showing custom aggregations via pivot configuration.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.values = []; + pivotGrid.pivotConfiguration.values.push({ + member: 'AmountOfSale', + displayName: 'Amount of Sale', + aggregate: { + key: 'SUM', + aggregator: IgxTotalSaleAggregate.totalSale, + label: 'Sum of Sale' + }, + aggregateList: [{ + key: 'SUM', + aggregator: IgxTotalSaleAggregate.totalSale, + label: 'Sum of Sale' + }, { + key: 'MIN', + aggregator: IgxTotalSaleAggregate.totalMin, + label: 'Minimum of Sale' + }, { + key: 'MAX', + aggregator: IgxTotalSaleAggregate.totalMax, + label: 'Maximum of Sale' + }], + enabled: true + }); + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const valueChip = headerRow.querySelector('igx-chip[id="Amount of Sale"]'); + let content = valueChip.querySelector('.igx-chip__content'); + expect(content.textContent.trim()).toBe('SUM(Amount of Sale)'); + + const aggregatesIcon = valueChip.querySelectorAll('igx-icon')[1]; + aggregatesIcon.click(); + fixture.detectChanges(); + + const items = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`)); + expect(items.length).toBe(3); + // select min + items[1].triggerEventHandler('click', UIInteractions.getMouseEvent('click')); + fixture.detectChanges(); + // check chip and row values + content = valueChip.querySelector('.igx-chip__content'); + expect(content.textContent.trim()).toBe('MIN(Amount of Sale)'); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Bulgaria').value).toBe(3612.42); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 'USA').value).toBe(0); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Uruguay').value).toBe(242.08); + }); + it('should show one aggregations drop-down at a time', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.width = '1500px'; + fixture.detectChanges(); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const valueChipUnitsSold = headerRow.querySelector('igx-chip[id="UnitsSold"]'); - // xit-ing because of https://github.com/IgniteUI/igniteui-angular/issues/10546 - xit('should sort on column for all sibling dimensions.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.height = '1500px'; - pivotGrid.pivotConfiguration.rows = [ - { - memberName: 'ProductCategory', + const aggregatesIconUnitsSold = valueChipUnitsSold.querySelectorAll('igx-icon')[1]; + aggregatesIconUnitsSold.click(); + fixture.detectChanges(); + + let dropDown = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_LIST}`)); + expect(dropDown.length).toBe(1); + + const valueChipUnitPrice = headerRow.querySelector('igx-chip[id="UnitPrice"]'); + + const aggregatesIconUnitPrice = valueChipUnitPrice.querySelectorAll('igx-icon')[1]; + aggregatesIconUnitPrice.click(); + fixture.detectChanges(); + + dropDown = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_LIST}`)); + expect(dropDown.length).toBe(1); + }); + + it('should allow reorder in row chip area.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'ProductCategory', + enabled: true + }, + { + memberName: 'SellerName', + enabled: true + } + ]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + + const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; + const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + const rowChipArea: IgxChipsAreaComponent = chipAreas[3].componentInstance; + const rowChip1 = rowChipArea.chipsList.toArray()[0]; + const rowChip2 = rowChipArea.chipsList.toArray()[1]; + + // start drag in row chip area. + headerRow.onDimDragStart({}, rowChipArea); + fixture.detectChanges(); + + // move first chip over the second one + headerRow.onDimDragOver({ + dragChip: { + id: 'ProductCategory' + }, + owner: rowChip2, + originalEvent: { + offsetX: 100 + } + }, PivotDimensionType.Row); + fixture.detectChanges(); + + // check drop indicator has shown after the second chip + expect((rowChip2.nativeElement.nextElementSibling as any).style.visibility).toBe(''); + + // drop chip + headerRow.onDimDrop({ + dragChip: { + id: 'ProductCategory' + }, + owner: rowChip2 + }, rowChipArea, PivotDimensionType.Row); + pivotGrid.cdr.detectChanges(); + //check chip order is updated. + expect(rowChipArea.chipsList.toArray()[0].id).toBe(rowChip2.id); + expect(rowChipArea.chipsList.toArray()[1].id).toBe(rowChip1.id); + // check dimension order is updated. + expect(pivotGrid.pivotConfiguration.rows.map(x => x.memberName)).toEqual(['SellerName', 'ProductCategory']); + }); + + it('should allow reorder in column chip area.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.columns.push({ + memberName: 'SellerName', enabled: true - }, - { + }); + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + + const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; + const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + const colChipArea: IgxChipsAreaComponent = chipAreas[1].componentInstance; + const colChip1 = colChipArea.chipsList.toArray()[0]; + const colChip2 = colChipArea.chipsList.toArray()[1]; + + // start drag in col chip area. + headerRow.onDimDragStart({}, colChipArea); + fixture.detectChanges(); + + // move first chip over the second one + headerRow.onDimDragOver({ + dragChip: { + id: 'Country' + }, + owner: colChip2, + originalEvent: { + offsetX: 100 + } + }, PivotDimensionType.Column); + fixture.detectChanges(); + + // check drop indicator has shown after the second chip + expect((colChip2.nativeElement.nextElementSibling as any).style.visibility).toBe(''); + + // drop chip + headerRow.onDimDrop({ + dragChip: { + id: 'Country' + }, + owner: colChip2 + }, colChipArea, PivotDimensionType.Column); + pivotGrid.cdr.detectChanges(); + //check chip order is updated. + expect(colChipArea.chipsList.toArray()[0].id).toBe(colChip2.id); + expect(colChipArea.chipsList.toArray()[1].id).toBe(colChip1.id); + // check dimension order is updated. + expect(pivotGrid.pivotConfiguration.columns.map(x => x.memberName)).toEqual(['SellerName', 'Country']); + // check columns reflect new order of dims + const cols = pivotGrid.columns; + expect(cols.filter(x => x.level === 0).map(x => x.field)).toEqual(['Stanley', 'Elisa', 'Lydia', 'David', 'John', 'Larry', 'Walter']); + }); + it('should allow reorder in the value chip area', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + fixture.detectChanges(); + const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; + const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + const valuesChipArea: IgxChipsAreaComponent = chipAreas[2].componentInstance; + const valChip1 = valuesChipArea.chipsList.toArray()[0]; + const valChip2 = valuesChipArea.chipsList.toArray()[1]; + + // move first chip over the second one + headerRow.onDimDragOver({ + dragChip: { + id: 'UnitsSold' + }, + owner: valChip2, + originalEvent: { + offsetX: 100 + } + }); + fixture.detectChanges(); + + // check drop indicator has shown after the second chip + expect((valChip2.nativeElement.nextElementSibling as any).style.visibility).toBe(''); + + // drop chip + headerRow.onValueDrop({ + dragChip: valChip1, + owner: valChip2 + }, valuesChipArea); + pivotGrid.cdr.detectChanges(); + //check chip order is updated. + expect(valuesChipArea.chipsList.toArray()[0].id).toBe(valChip2.id); + expect(valuesChipArea.chipsList.toArray()[1].id).toBe(valChip1.id); + // check dimension order is updated. + expect(pivotGrid.pivotConfiguration.values.map(x => x.member)).toEqual(['UnitPrice', 'UnitsSold']); + + }); + it('should allow moving dimension between rows, columns and filters.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.filters = [{ memberName: 'SellerName', enabled: true - } - ]; - // add a bit more data to sort. - pivotGrid.data = [ - { - ProductCategory: 'Clothing', UnitPrice: 12.81, SellerName: 'Stanley', - Country: 'Bulgaria', Date: '01/01/2021', UnitsSold: 282 - }, - { - ProductCategory: 'Clothing', UnitPrice: 49.57, SellerName: 'Elisa', - Country: 'USA', Date: '01/05/2019', UnitsSold: 296 - }, - { - ProductCategory: 'Bikes', UnitPrice: 3.56, SellerName: 'Lydia', - Country: 'Uruguay', Date: '01/06/2020', UnitsSold: 68 - }, - { - ProductCategory: 'Accessories', UnitPrice: 85.58, SellerName: 'David', - Country: 'USA', Date: '04/07/2021', UnitsSold: 293 - }, - { - ProductCategory: 'Components', UnitPrice: 18.13, SellerName: 'John', - Country: 'USA', Date: '12/08/2021', UnitsSold: 240 - }, - { - ProductCategory: 'Clothing', UnitPrice: 68.33, SellerName: 'Larry', - Country: 'Uruguay', Date: '05/12/2020', UnitsSold: 456 - }, - { - ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Walter', - Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 492 - }, - { - ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Elisa', - Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 267 - }, - { - ProductCategory: 'Clothing', UnitPrice: 16.05, SellerName: 'Larry', - Country: 'Bulgaria', Date: '02/19/2020', UnitsSold: 100 - } - ]; - pivotGrid.pipeTrigger++; - fixture.detectChanges(); - const headerCell = GridFunctions.getColumnHeader('Bulgaria-UnitsSold', fixture); - // sort asc - GridFunctions.clickHeaderSortIcon(headerCell); - fixture.detectChanges(); - expect(pivotGrid.sortingExpressions.length).toBe(1); - let expectedOrder = [undefined, undefined, undefined, 100, 267, 282, 492]; - let columnValues = pivotGrid.dataView.map(x => x['Bulgaria-UnitsSold']); - expect(columnValues).toEqual(expectedOrder); + }]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; + const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + const colChipArea: IgxChipsAreaComponent = chipAreas[1].componentInstance; + const rowChipArea: IgxChipsAreaComponent = chipAreas[3].componentInstance; + const filterChipArea: IgxChipsAreaComponent = chipAreas[0].componentInstance; + const filterChip = filterChipArea.chipsList.first; + // start drag in filter chip area. + headerRow.onDimDragStart({}, filterChipArea); + fixture.detectChanges(); + + // check drop here chips are displayed in other areas + expect(headerRow.notificationChips.toArray()[1].nativeElement.hidden).toBeFalse(); + expect(headerRow.notificationChips.toArray()[2].nativeElement.hidden).toBeFalse(); + + const dropHereRowChip = headerRow.notificationChips.toArray()[2]; + // move Seller onto the drop here chip + + // drop chip + headerRow.onDimDrop({ + dragChip: filterChip, + owner: dropHereRowChip + }, rowChipArea, PivotDimensionType.Row); + fixture.detectChanges(); + pivotGrid.cdr.detectChanges(); + + // check dimensions + expect(pivotGrid.pivotConfiguration.filters.filter(x => x.enabled).length).toBe(0); + expect(pivotGrid.pivotConfiguration.rows.filter(x => x.enabled).length).toBe(2); + + const rowSellerChip = rowChipArea.chipsList.toArray()[1]; + const colChip = colChipArea.chipsList.first; + // start drag in row chip area. + headerRow.onDimDragStart({}, rowChipArea); + fixture.detectChanges(); + + // drag Seller from row dimension as first chip in columns + headerRow.onDimDragOver({ + dragChip: rowSellerChip, + owner: colChip, + originalEvent: { + offsetX: 0 + } + }, PivotDimensionType.Column); + fixture.detectChanges(); + //check drop indicator between chips + expect((colChip.nativeElement.previousElementSibling as any).style.visibility).toBe(''); + expect((colChip.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); + + // drop chip + headerRow.onDimDrop({ + dragChip: rowSellerChip, + owner: colChip + }, colChipArea, PivotDimensionType.Column); + pivotGrid.cdr.detectChanges(); + fixture.detectChanges(); + + // check dimensions + expect(pivotGrid.pivotConfiguration.filters.filter(x => x.enabled).length).toBe(0); + expect(pivotGrid.pivotConfiguration.rows.filter(x => x.enabled).length).toBe(1); + expect(pivotGrid.pivotConfiguration.columns.filter(x => x.enabled).length).toBe(2); + + // drag Seller over filter area + const colSellerChip = colChipArea.chipsList.toArray()[0]; + // start drag in col chip area. + headerRow.onDimDragStart({}, colChipArea); + // drop chip + headerRow.onDimDrop({ + dragChip: colSellerChip, + owner: {} + }, filterChipArea, PivotDimensionType.Filter); + pivotGrid.cdr.detectChanges(); + pivotGrid.pipeTrigger++; + fixture.detectChanges(); + + expect(pivotGrid.pivotConfiguration.filters.filter(x => x.enabled).length).toBe(1); + expect(pivotGrid.pivotConfiguration.rows.filter(x => x.enabled).length).toBe(1); + expect(pivotGrid.pivotConfiguration.columns.filter(x => x.enabled).length).toBe(1); + }); - // sort desc - GridFunctions.clickHeaderSortIcon(headerCell); - fixture.detectChanges(); - expect(pivotGrid.sortingExpressions.length).toBe(1); - expectedOrder = [492, 282, 267, 100, undefined, undefined, undefined]; - columnValues = pivotGrid.dataView.map(x => x['Bulgaria-UnitsSold']); - expect(columnValues).toEqual(expectedOrder); + it('should hide drop indicators when moving out of the drop area.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'ProductCategory', + enabled: true + }, + { + memberName: 'SellerName', + enabled: true + } + ]; + pivotGrid.pipeTrigger++; + pivotGrid.setupColumns(); + fixture.detectChanges(); + + const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; + const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); + const rowChipArea: IgxChipsAreaComponent = chipAreas[3].componentInstance; + const rowChip1 = rowChipArea.chipsList.toArray()[0]; + const rowChip2 = rowChipArea.chipsList.toArray()[1]; + + // start drag in row chip area. + headerRow.onDimDragStart({}, rowChipArea); + fixture.detectChanges(); + + // drag second chip before prev chip + headerRow.onDimDragOver({ + dragChip: rowChip2, + owner: rowChip1, + originalEvent: { + offsetX: 0 + } + }, PivotDimensionType.Row); + fixture.detectChanges(); + + // should show the prev drop indicator for the 1st chip + expect((rowChip1.nativeElement.previousElementSibling as any).style.visibility).toBe(''); + expect((rowChip1.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); + + // simulate drag area leave + headerRow.onAreaDragLeave({}, rowChipArea); + + expect((rowChip1.nativeElement.previousElementSibling as any).style.visibility).toBe('hidden'); + expect((rowChip1.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); + }); + + it('should auto-size row dimension via the API.', () => { + const pivotGrid = fixture.componentInstance.pivotGrid; + const rowDimension = pivotGrid.pivotConfiguration.rows[0]; + expect(rowDimension.width).toBeUndefined(); + expect(pivotGrid.rowDimensionWidthToPixels(rowDimension)).toBe(200); + pivotGrid.autoSizeRowDimension(rowDimension); + fixture.detectChanges(); + expect(rowDimension.width).toBe('186px'); + expect(pivotGrid.rowDimensionWidthToPixels(rowDimension)).toBe(186); + }); }); + }); - it('should allow changing default aggregation via value chip drop-down.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.width = '1500px'; + describe('IgxPivotGrid complex hierarchy #pivotGrid', () => { + let fixture; + configureTestSuite((() => { + TestBed.configureTestingModule({ + declarations: [ + IgxPivotGridTestComplexHierarchyComponent + ], + imports: [ + NoopAnimationsModule, IgxPivotGridModule] + }); + })); + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent); fixture.detectChanges(); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const valueChip = headerRow.querySelector('igx-chip[id="UnitsSold"]'); - let content = valueChip.querySelector('.igx-chip__content'); - expect(content.textContent.trim()).toBe('SUM(UnitsSold)'); + })); - const aggregatesIcon = valueChip.querySelectorAll('igx-icon')[1]; - aggregatesIcon.click(); + it('should select/deselect the correct row', () => { fixture.detectChanges(); - const items = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`)); - expect(items.length).toBe(5); - // select count - items[0].triggerEventHandler('click', UIInteractions.getMouseEvent('click')); + const pivotGrid = fixture.componentInstance.pivotGrid; + const pivotRows = GridFunctions.getPivotRows(fixture); + const row = pivotRows[2].componentInstance; + const rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const secondDimCell = rowHeaders.find(x => x.componentInstance.column.header === 'Clothing'); + secondDimCell.nativeElement.click(); fixture.detectChanges(); - - // check chip and row - content = valueChip.querySelector('.igx-chip__content'); - expect(content.textContent.trim()).toBe('COUNT(UnitsSold)'); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Bulgaria-UnitsSold').value).toBe(2); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 'USA-UnitsSold').value).toBe(3); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Uruguay-UnitsSold').value).toBe(2); - + expect(row.selected).toBeTrue(); + expect(pivotGrid.selectedRows).not.toBeNull(); + expect(pivotGrid.selectedRows.length).toBe(1); + expect((pivotGrid.selectedRows[0] as IPivotGridRecord).dimensionValues.get('All cities')).toBe('All Cities'); + expect((pivotGrid.selectedRows[0] as IPivotGridRecord).dimensionValues.get('ProductCategory')).toBe('Clothing'); + + //deselect + secondDimCell.nativeElement.click(); + fixture.detectChanges(); + expect(row.selected).toBeFalse(); + expect(pivotGrid.selectedRows.length).toBe(0); }); - it('should allow showing custom aggregations via pivot configuration.', () => { + + it('should select/deselect the correct group of rows', () => { + fixture.detectChanges(); const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.values = []; - pivotGrid.pivotConfiguration.values.push({ - member: 'AmountOfSale', - displayName: 'Amount of Sale', - aggregate: { - key: 'SUM', - aggregator: IgxTotalSaleAggregate.totalSale, - label: 'Sum of Sale' - }, - aggregateList: [{ - key: 'SUM', - aggregator: IgxTotalSaleAggregate.totalSale, - label: 'Sum of Sale' - }, { - key: 'MIN', - aggregator: IgxTotalSaleAggregate.totalMin, - label: 'Minimum of Sale' - }, { - key: 'MAX', - aggregator: IgxTotalSaleAggregate.totalMax, - label: 'Maximum of Sale' - }], - enabled: true - }); - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + const pivotRows = GridFunctions.getPivotRows(fixture); + const row = pivotRows[2].componentInstance; + const rowHeaders = fixture.debugElement.queryAll( + By.directive(IgxPivotRowDimensionHeaderComponent)); + const firstDimCell = rowHeaders.find(x => x.componentInstance.column.header === 'All Cities'); + firstDimCell.nativeElement.click(); fixture.detectChanges(); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const valueChip = headerRow.querySelector('igx-chip[id="Amount of Sale"]'); - let content = valueChip.querySelector('.igx-chip__content'); - expect(content.textContent.trim()).toBe('SUM(Amount of Sale)'); + for (let i = 0; i < 5; ++i) { + expect(pivotRows[i].componentInstance.selected).toBeTrue(); + } + expect(pivotGrid.selectedRows).not.toBeNull(); + expect(pivotGrid.selectedRows.length).toBe(5); + const dimensionValues = PivotGridFunctions.getDimensionValues(pivotGrid.selectedRows); + const expected = + [ + { + AllProducts: 'AllProducts', 'All cities': 'All Cities' + }, { + ProductCategory: 'Bikes', 'All cities': 'All Cities' + }, { + ProductCategory: 'Clothing', 'All cities': 'All Cities' + }, { + ProductCategory: 'Accessories', 'All cities': 'All Cities' + }, { + ProductCategory: 'Components', 'All cities': 'All Cities' + } + ]; + expect(dimensionValues).toEqual(expected); + }); - const aggregatesIcon = valueChip.querySelectorAll('igx-icon')[1]; - aggregatesIcon.click(); + it('should select/deselect the correct column', () => { fixture.detectChanges(); + const pivotGrid = fixture.componentInstance.pivotGrid; + const unitsSold = pivotGrid.getColumnByName('Bulgaria-UnitsSold'); + GridFunctions.clickColumnHeaderUI('Bulgaria-UnitsSold', fixture); + GridSelectionFunctions.verifyColumnAndCellsSelected(unitsSold); + }); - const items = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_ITEM}`)); - expect(items.length).toBe(3); - // select min - items[1].triggerEventHandler('click', UIInteractions.getMouseEvent('click')); + it('should select/deselect the correct column group', () => { fixture.detectChanges(); - // check chip and row values - content = valueChip.querySelector('.igx-chip__content'); - expect(content.textContent.trim()).toBe('MIN(Amount of Sale)'); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Bulgaria').value).toBe(3612.42); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 'USA').value).toBe(0); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 'Uruguay').value).toBe(242.08); - }); - it('should show one aggregations drop-down at a time', () => { const pivotGrid = fixture.componentInstance.pivotGrid; pivotGrid.width = '1500px'; fixture.detectChanges(); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const valueChipUnitsSold = headerRow.querySelector('igx-chip[id="UnitsSold"]'); + const group = GridFunctions.getColGroup(pivotGrid, 'Bulgaria'); + const unitsSold = pivotGrid.getColumnByName('Bulgaria-UnitsSold'); + const amountOfSale = pivotGrid.getColumnByName('Bulgaria-AmountOfSale'); + const unitsSoldUSA = pivotGrid.getColumnByName('US-UnitsSold'); + const amountOfSaleUSA = pivotGrid.getColumnByName('US-AmountOfSale'); - const aggregatesIconUnitsSold = valueChipUnitsSold.querySelectorAll('igx-icon')[1]; - aggregatesIconUnitsSold.click(); + GridFunctions.clickColumnGroupHeaderUI('Bulgaria', fixture); fixture.detectChanges(); - let dropDown = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_LIST}`)); - expect(dropDown.length).toBe(1); + GridSelectionFunctions.verifyColumnSelected(unitsSold); + GridSelectionFunctions.verifyColumnSelected(amountOfSale); + GridSelectionFunctions.verifyColumnGroupSelected(fixture, group); - const valueChipUnitPrice = headerRow.querySelector('igx-chip[id="UnitPrice"]'); + GridSelectionFunctions.verifyColumnsSelected([unitsSoldUSA, amountOfSaleUSA], false); - const aggregatesIconUnitPrice = valueChipUnitPrice.querySelectorAll('igx-icon')[1]; - aggregatesIconUnitPrice.click(); - fixture.detectChanges(); + GridFunctions.clickColumnGroupHeaderUI('Bulgaria', fixture); - dropDown = fixture.debugElement.queryAll(By.css(`.${CSS_CLASS_LIST}`)); - expect(dropDown.length).toBe(1); + GridSelectionFunctions.verifyColumnSelected(unitsSold, false); + GridSelectionFunctions.verifyColumnSelected(amountOfSale, false); + GridSelectionFunctions.verifyColumnGroupSelected(fixture, group, false); }); + }); - it('should allow reorder in row chip area.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.rows = [ - { - memberName: 'ProductCategory', - enabled: true - }, - { - memberName: 'SellerName', - enabled: true - } - ]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + describe('IgxPivotGrid Resizing #pivotGrid', () => { + let fixture: ComponentFixture; + let pivotGrid: IgxPivotGridComponent; + + configureTestSuite((() => { + TestBed.configureTestingModule({ + declarations: [ + IgxPivotGridTestComplexHierarchyComponent + ], + imports: [ + NoopAnimationsModule, + IgxPivotGridModule + ] + }); + })); + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent); fixture.detectChanges(); - const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; - const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const rowChipArea: IgxChipsAreaComponent = chipAreas[3].componentInstance; - const rowChip1 = rowChipArea.chipsList.toArray()[0]; - const rowChip2 = rowChipArea.chipsList.toArray()[1]; + pivotGrid = fixture.componentInstance.pivotGrid; + })); - // start drag in row chip area. - headerRow.onDimDragStart({}, rowChipArea); - fixture.detectChanges(); + it('should define grid with resizable columns.', fakeAsync(() => { + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - // move first chip over the second one - headerRow.onDimDragOver({ - dragChip: { - id: 'ProductCategory' - }, - owner: rowChip2, - originalEvent: { - offsetX: 100 - } - }, PivotDimensionType.Row); - fixture.detectChanges(); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.resizable).toBeTrue(); + expect(rowHeaders[3].componentInstance.column.resizable).toBeTrue(); - // check drop indicator has shown after the second chip - expect((rowChip2.nativeElement.nextElementSibling as any).style.visibility).toBe(''); + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.resizable).toBeTrue(); + expect(rowHeaders[1].componentInstance.column.resizable).toBeTrue(); + expect(rowHeaders[5].componentInstance.column.resizable).toBeTrue(); + expect(rowHeaders[7].componentInstance.column.resizable).toBeTrue(); + })); - // drop chip - headerRow.onDimDrop({ - dragChip: { - id: 'ProductCategory' - }, - owner: rowChip2 - }, rowChipArea, PivotDimensionType.Row); - pivotGrid.cdr.detectChanges(); - //check chip order is updated. - expect(rowChipArea.chipsList.toArray()[0].id).toBe(rowChip2.id); - expect(rowChipArea.chipsList.toArray()[1].id).toBe(rowChip1.id); - // check dimension order is updated. - expect(pivotGrid.pivotConfiguration.rows.map(x => x.memberName)).toEqual(['SellerName', 'ProductCategory']); - }); + it('should update grid after resizing a top dimension header to be bigger.', fakeAsync(() => { + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - it('should allow reorder in column chip area.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.columns.push({ - memberName: 'SellerName', - enabled: true - }); - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); - fixture.detectChanges(); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[3].componentInstance.column.width).toEqual('200px'); + + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); - const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; - const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const colChipArea: IgxChipsAreaComponent = chipAreas[1].componentInstance; - const colChip1 = colChipArea.chipsList.toArray()[0]; - const colChip2 = colChipArea.chipsList.toArray()[1]; + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[0]).nativeElement; - // start drag in col chip area. - headerRow.onDimDragStart({}, colChipArea); + // Resize first column + UIInteractions.simulateMouseEvent('mousedown', headerResArea, 100, 0); + tick(200); fixture.detectChanges(); - // move first chip over the second one - headerRow.onDimDragOver({ - dragChip: { - id: 'Country' - }, - owner: colChip2, - originalEvent: { - offsetX: 100 - } - }, PivotDimensionType.Column); + const resizer = GridFunctions.getResizer(fixture).nativeElement; + expect(resizer).toBeDefined(); + UIInteractions.simulateMouseEvent('mousemove', resizer, 300, 5); + UIInteractions.simulateMouseEvent('mouseup', resizer, 300, 5); fixture.detectChanges(); - // check drop indicator has shown after the second chip - expect((colChip2.nativeElement.nextElementSibling as any).style.visibility).toBe(''); + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('400px'); + expect(rowHeaders[3].componentInstance.column.width).toEqual('400px'); - // drop chip - headerRow.onDimDrop({ - dragChip: { - id: 'Country' - }, - owner: colChip2 - }, colChipArea, PivotDimensionType.Column); - pivotGrid.cdr.detectChanges(); - //check chip order is updated. - expect(colChipArea.chipsList.toArray()[0].id).toBe(colChip2.id); - expect(colChipArea.chipsList.toArray()[1].id).toBe(colChip1.id); - // check dimension order is updated. - expect(pivotGrid.pivotConfiguration.columns.map(x => x.memberName)).toEqual(['SellerName', 'Country']); - // check columns reflect new order of dims - const cols = pivotGrid.columns; - expect(cols.filter(x => x.level === 0).map(x => x.field)).toEqual(['Stanley', 'Elisa', 'Lydia', 'David', 'John', 'Larry', 'Walter']); - }); - it('should allow reorder in the value chip area', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - fixture.detectChanges(); - const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; - const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const valuesChipArea: IgxChipsAreaComponent = chipAreas[2].componentInstance; - const valChip1 = valuesChipArea.chipsList.toArray()[0]; - const valChip2 = valuesChipArea.chipsList.toArray()[1]; - - // move first chip over the second one - headerRow.onDimDragOver({ - dragChip: { - id: 'UnitsSold' - }, - owner: valChip2, - originalEvent: { - offsetX: 100 - } - }); - fixture.detectChanges(); + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); + })); - // check drop indicator has shown after the second chip - expect((valChip2.nativeElement.nextElementSibling as any).style.visibility).toBe(''); - - // drop chip - headerRow.onValueDrop({ - dragChip: valChip1, - owner: valChip2 - }, valuesChipArea); - pivotGrid.cdr.detectChanges(); - //check chip order is updated. - expect(valuesChipArea.chipsList.toArray()[0].id).toBe(valChip2.id); - expect(valuesChipArea.chipsList.toArray()[1].id).toBe(valChip1.id); - // check dimension order is updated. - expect(pivotGrid.pivotConfiguration.values.map(x => x.member)).toEqual(['UnitPrice', 'UnitsSold']); + it('should update grid after resizing a child dimension header to be bigger.', fakeAsync(() => { + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - }); - it('should allow moving dimension between rows, columns and filters.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.filters = [{ - memberName: 'SellerName', - enabled: true - }]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[3].componentInstance.column.width).toEqual('200px'); + + const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[3]).nativeElement; + + // Resize first column + UIInteractions.simulateMouseEvent('mousedown', headerResArea, 100, 0); + tick(200); fixture.detectChanges(); - const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; - const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const colChipArea: IgxChipsAreaComponent = chipAreas[1].componentInstance; - const rowChipArea: IgxChipsAreaComponent = chipAreas[3].componentInstance; - const filterChipArea: IgxChipsAreaComponent = chipAreas[0].componentInstance; - const filterChip = filterChipArea.chipsList.first; - // start drag in filter chip area. - headerRow.onDimDragStart({}, filterChipArea); + + const resizer = GridFunctions.getResizer(fixture).nativeElement; + expect(resizer).toBeDefined(); + UIInteractions.simulateMouseEvent('mousemove', resizer, 300, 5); + UIInteractions.simulateMouseEvent('mouseup', resizer, 300, 5); fixture.detectChanges(); - // check drop here chips are displayed in other areas - expect(headerRow.notificationChips.toArray()[1].nativeElement.hidden).toBeFalse(); - expect(headerRow.notificationChips.toArray()[2].nativeElement.hidden).toBeFalse(); + rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('400px'); + expect(rowHeaders[3].componentInstance.column.width).toEqual('400px'); + + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); + expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); + })); + }); - const dropHereRowChip = headerRow.notificationChips.toArray()[2]; - // move Seller onto the drop here chip + describe('IgxPivotGrid APIs #pivotGrid', () => { + let fixture: ComponentFixture; + let pivotGrid: IgxPivotGridComponent; + + configureTestSuite((() => { + TestBed.configureTestingModule({ + declarations: [ + IgxPivotGridTestComplexHierarchyComponent + ], + imports: [ + NoopAnimationsModule, + IgxPivotGridModule + ] + }); + })); - // drop chip - headerRow.onDimDrop({ - dragChip: filterChip, - owner: dropHereRowChip - }, rowChipArea, PivotDimensionType.Row); + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent); fixture.detectChanges(); - pivotGrid.cdr.detectChanges(); - // check dimensions - expect(pivotGrid.pivotConfiguration.filters.filter(x => x.enabled).length).toBe(0); - expect(pivotGrid.pivotConfiguration.rows.filter(x => x.enabled).length).toBe(2); + pivotGrid = fixture.componentInstance.pivotGrid; + })); - const rowSellerChip = rowChipArea.chipsList.toArray()[1]; - const colChip = colChipArea.chipsList.first; - // start drag in row chip area. - headerRow.onDimDragStart({}, rowChipArea); + it('should allow inserting new dimension at index.', () => { + // insert in rows + pivotGrid.insertDimensionAt({ memberName: 'SellerName', enabled: true }, PivotDimensionType.Row, 1); fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.rows[1].memberName).toBe('SellerName'); + // check rows + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + let rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const first = rowHeaders.map(x => x.componentInstance.column.header)[0]; + expect(first).toBe('Larry Lieb'); - // drag Seller from row dimension as first chip in columns - headerRow.onDimDragOver({ - dragChip: rowSellerChip, - owner: colChip, - originalEvent: { - offsetX: 0 - } - }, PivotDimensionType.Column); - fixture.detectChanges(); - //check drop indicator between chips - expect((colChip.nativeElement.previousElementSibling as any).style.visibility).toBe(''); - expect((colChip.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); - - // drop chip - headerRow.onDimDrop({ - dragChip: rowSellerChip, - owner: colChip - }, colChipArea, PivotDimensionType.Column); - pivotGrid.cdr.detectChanges(); + // insert in columns + pivotGrid.insertDimensionAt({ memberName: 'SellerNameColumn', memberFunction: (rec) => rec.SellerName, enabled: true }, PivotDimensionType.Column, 0); fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.columns[0].memberName).toBe('SellerNameColumn'); + expect(pivotGrid.columnDimensions.length).toBe(2); + expect(pivotGrid.columns.length).toBe(28); - // check dimensions - expect(pivotGrid.pivotConfiguration.filters.filter(x => x.enabled).length).toBe(0); - expect(pivotGrid.pivotConfiguration.rows.filter(x => x.enabled).length).toBe(1); - expect(pivotGrid.pivotConfiguration.columns.filter(x => x.enabled).length).toBe(2); - - // drag Seller over filter area - const colSellerChip = colChipArea.chipsList.toArray()[0]; - // start drag in col chip area. - headerRow.onDimDragStart({}, colChipArea); - // drop chip - headerRow.onDimDrop({ - dragChip: colSellerChip, - owner: {} - }, filterChipArea, PivotDimensionType.Filter); - pivotGrid.cdr.detectChanges(); - pivotGrid.pipeTrigger++; + // insert in filter + pivotGrid.insertDimensionAt({ memberName: 'SellerNameFilter', memberFunction: (rec) => rec.SellerName, enabled: true }, PivotDimensionType.Filter, 1); fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); + expect(pivotGrid.pivotConfiguration.filters[0].memberName).toBe('SellerNameFilter'); - expect(pivotGrid.pivotConfiguration.filters.filter(x => x.enabled).length).toBe(1); - expect(pivotGrid.pivotConfiguration.rows.filter(x => x.enabled).length).toBe(1); - expect(pivotGrid.pivotConfiguration.columns.filter(x => x.enabled).length).toBe(1); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const chip = headerRow.querySelector('igx-chip[id="SellerNameFilter"]'); + expect(chip).not.toBeNull(); }); - it('should hide drop indicators when moving out of the drop area.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.pivotConfiguration.rows = [ - { - memberName: 'ProductCategory', - enabled: true - }, - { - memberName: 'SellerName', - enabled: true - } - ]; - pivotGrid.pipeTrigger++; - pivotGrid.setupColumns(); + it('should allow removing dimension.', () => { + const filter = { memberName: 'SellerNameFilter', memberFunction: (rec) => rec.SellerName, enabled: true }; + pivotGrid.pivotConfiguration.filters = [filter]; fixture.detectChanges(); - const headerRow: IgxPivotHeaderRowComponent = fixture.debugElement.query(By.directive(IgxPivotHeaderRowComponent)).componentInstance; - const chipAreas = fixture.debugElement.queryAll(By.directive(IgxChipsAreaComponent)); - const rowChipArea: IgxChipsAreaComponent = chipAreas[3].componentInstance; - const rowChip1 = rowChipArea.chipsList.toArray()[0]; - const rowChip2 = rowChipArea.chipsList.toArray()[1]; - - // start drag in row chip area. - headerRow.onDimDragStart({}, rowChipArea); + // remove row + pivotGrid.removeDimension(pivotGrid.pivotConfiguration.rows[0]); fixture.detectChanges(); - // drag second chip before prev chip - headerRow.onDimDragOver({ - dragChip: rowChip2, - owner: rowChip1, - originalEvent: { - offsetX: 0 - } - }, PivotDimensionType.Row); + expect(pivotGrid.pivotConfiguration.rows.length).toBe(1); + expect(pivotGrid.pivotConfiguration.rows[0].memberName).toBe('AllProducts'); + + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const headers = rowHeaders.map(x => x.componentInstance.column.header); + expect(headers.length).toBe(5); + + // remove col + pivotGrid.removeDimension(pivotGrid.pivotConfiguration.columns[0]); fixture.detectChanges(); - // should show the prev drop indicator for the 1st chip - expect((rowChip1.nativeElement.previousElementSibling as any).style.visibility).toBe(''); - expect((rowChip1.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); + expect(pivotGrid.pivotConfiguration.columns.length).toBe(0); + expect(pivotGrid.columnDimensions.length).toBe(0); + expect(pivotGrid.columns.length).toBe(pivotGrid.values.length); - // simulate drag area leave - headerRow.onAreaDragLeave({}, rowChipArea); + // remove filter + pivotGrid.removeDimension(filter); + fixture.detectChanges(); - expect((rowChip1.nativeElement.previousElementSibling as any).style.visibility).toBe('hidden'); - expect((rowChip1.nativeElement.nextElementSibling as any).style.visibility).toBe('hidden'); - }); + expect(pivotGrid.pivotConfiguration.filters.length).toBe(0); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const chip = headerRow.querySelector('igx-chip[id="SellerNameFilter"]'); + expect(chip).toBeNull(); - it('should auto-size row dimension via the API.', () => { - const pivotGrid = fixture.componentInstance.pivotGrid; - const rowDimension = pivotGrid.pivotConfiguration.rows[0]; - expect(rowDimension.width).toBeUndefined(); - expect(pivotGrid.resolveRowDimensionWidth(rowDimension)).toBe(200); - pivotGrid.autoSizeRowDimension(rowDimension); + // remove something that is not part of the config + pivotGrid.removeDimension({ memberName: 'Test', enabled: true }); fixture.detectChanges(); - expect(rowDimension.width).toBe('186px'); - expect(pivotGrid.resolveRowDimensionWidth(rowDimension)).toBe(186); + // nothing should change + expect(pivotGrid.pivotConfiguration.filters.length).toBe(0); + expect(pivotGrid.pivotConfiguration.columns.length).toBe(0); + expect(pivotGrid.pivotConfiguration.rows.length).toBe(1); }); - }); -}); - -describe('IgxPivotGrid complex hierarchy #pivotGrid', () => { - let fixture; - configureTestSuite((() => { - TestBed.configureTestingModule({ - declarations: [ - IgxPivotGridTestComplexHierarchyComponent - ], - imports: [ - NoopAnimationsModule, IgxPivotGridModule] - }); - })); - - beforeEach(fakeAsync(() => { - fixture = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent); - fixture.detectChanges(); - })); - - it('should select/deselect the correct row', () => { - fixture.detectChanges(); - const pivotGrid = fixture.componentInstance.pivotGrid; - const pivotRows = GridFunctions.getPivotRows(fixture); - const row = pivotRows[2].componentInstance; - const rowHeaders = fixture.debugElement.queryAll( - By.directive(IgxPivotRowDimensionHeaderComponent)); - const secondDimCell = rowHeaders.find(x => x.componentInstance.column.header === 'Clothing'); - secondDimCell.nativeElement.click(); - fixture.detectChanges(); - expect(row.selected).toBeTrue(); - expect(pivotGrid.selectedRows).not.toBeNull(); - expect(pivotGrid.selectedRows.length).toBe(1); - const expected = - { - 'All cities': 'All Cities', 'All cities_level': 0, - ProductCategory: 'Clothing', ProductCategory_level: 1, - 'Bulgaria-AmountOfSale': 3612.42, 'Bulgaria-UnitsSold': 282, - 'US-AmountOfSale': 14672.72, 'US-UnitsSold': 296, - 'Uruguay-AmountOfSale': 31158.48, 'Uruguay-UnitsSold': 456 - }; - expect(pivotGrid.selectedRows[0]).toEqual(expected); - - //deselect - secondDimCell.nativeElement.click(); - fixture.detectChanges(); - expect(row.selected).toBeFalse(); - expect(pivotGrid.selectedRows.length).toBe(0); - }); - it('should select/deselect the correct group of rows', () => { - fixture.detectChanges(); - const pivotGrid = fixture.componentInstance.pivotGrid; - const pivotRows = GridFunctions.getPivotRows(fixture); - const row = pivotRows[2].componentInstance; - const rowHeaders = fixture.debugElement.queryAll( - By.directive(IgxPivotRowDimensionHeaderComponent)); - const firstDimCell = rowHeaders.find(x => x.componentInstance.column.header === 'All Cities'); - firstDimCell.nativeElement.click(); - fixture.detectChanges(); - for (let i = 0; i < 5; ++i) { - expect(pivotRows[i].componentInstance.selected).toBeTrue(); - } - expect(pivotGrid.selectedRows).not.toBeNull(); - expect(pivotGrid.selectedRows.length).toBe(5); - const expected = - [ - { - AllProducts: 'AllProducts', 'All cities': 'All Cities', - 'All cities_level': 0, AllProducts_level: 0, 'Bulgaria-UnitsSold': 774, - 'Bulgaria-AmountOfSale': 11509.02, 'US-UnitsSold': 296, 'US-AmountOfSale': 14672.72, - 'Uruguay-UnitsSold': 524, 'Uruguay-AmountOfSale': 31400.56, - 'UK-UnitsSold': 293, 'UK-AmountOfSale': 25074.94, - 'Japan-UnitsSold': 240, 'Japan-AmountOfSale': 4351.2, - }, { - ProductCategory: 'Bikes', 'All cities': 'All Cities', - ProductCategory_level: 1, 'All cities_level': 0, - 'Uruguay-UnitsSold': 68, 'Uruguay-AmountOfSale': 242.08, - City: 'Ciudad de la Costa', Country: 'Uruguay', - Date: '01/06/2011', SellerName: 'Lydia Burson', - UnitPrice: 3.56, UnitsSold: 68 - }, { - ProductCategory: 'Clothing', 'All cities': 'All Cities', - ProductCategory_level: 1, 'All cities_level': 0, 'Bulgaria-UnitsSold': 282, - 'Bulgaria-AmountOfSale': 3612.42, 'US-UnitsSold': 296, 'US-AmountOfSale': 14672.72, - 'Uruguay-UnitsSold': 456, 'Uruguay-AmountOfSale': 31158.48 - }, { - ProductCategory: 'Accessories', 'All cities': 'All Cities', - ProductCategory_level: 1, 'All cities_level': 0, - 'UK-UnitsSold': 293, 'UK-AmountOfSale': 25074.94, - City: 'London', Country: 'UK', Date: '04/07/2012', - SellerName: 'David Haley', UnitPrice: 85.58, UnitsSold: 293 - }, { - ProductCategory: 'Components', 'All cities': 'All Cities', - ProductCategory_level: 1, 'All cities_level': 0, - 'Japan-UnitsSold': 240, 'Japan-AmountOfSale': 4351.2, - 'Bulgaria-UnitsSold': 492, 'Bulgaria-AmountOfSale': 7896.6 - } - ]; - expect(pivotGrid.selectedRows).toEqual(expected); - }); - - it('should select/deselect the correct column', () => { - fixture.detectChanges(); - const pivotGrid = fixture.componentInstance.pivotGrid; - const unitsSold = pivotGrid.getColumnByName('Bulgaria-UnitsSold'); - GridFunctions.clickColumnHeaderUI('Bulgaria-UnitsSold', fixture); - GridSelectionFunctions.verifyColumnAndCellsSelected(unitsSold); - }); + it('should allow toggling dimension.', () => { + const filter = { memberName: 'SellerNameFilter', memberFunction: (rec) => rec.SellerName, enabled: true }; + pivotGrid.pivotConfiguration.filters = [filter]; + fixture.detectChanges(); - it('should select/deselect the correct column group', () => { - fixture.detectChanges(); - const pivotGrid = fixture.componentInstance.pivotGrid; - pivotGrid.width = '1500px'; - fixture.detectChanges(); - const group = GridFunctions.getColGroup(pivotGrid, 'Bulgaria'); - const unitsSold = pivotGrid.getColumnByName('Bulgaria-UnitsSold'); - const amountOfSale = pivotGrid.getColumnByName('Bulgaria-AmountOfSale'); - const unitsSoldUSA = pivotGrid.getColumnByName('US-UnitsSold'); - const amountOfSaleUSA = pivotGrid.getColumnByName('US-AmountOfSale'); + // toggle row + pivotGrid.toggleDimension(pivotGrid.pivotConfiguration.rows[0]); + fixture.detectChanges(); - GridFunctions.clickColumnGroupHeaderUI('Bulgaria', fixture); - fixture.detectChanges(); + // there are still 2 + expect(pivotGrid.pivotConfiguration.rows.length).toBe(2); + // 1 is disabled + expect(pivotGrid.rowDimensions.length).toBe(1); - GridSelectionFunctions.verifyColumnSelected(unitsSold); - GridSelectionFunctions.verifyColumnSelected(amountOfSale); - GridSelectionFunctions.verifyColumnGroupSelected(fixture, group); + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const headers = rowHeaders.map(x => x.componentInstance.column.header); + expect(headers.length).toBe(5); - GridSelectionFunctions.verifyColumnsSelected([unitsSoldUSA, amountOfSaleUSA], false); + // toggle column + pivotGrid.toggleDimension(pivotGrid.pivotConfiguration.columns[0]); + fixture.detectChanges(); - GridFunctions.clickColumnGroupHeaderUI('Bulgaria', fixture); + expect(pivotGrid.pivotConfiguration.columns.length).toBe(1); + expect(pivotGrid.columnDimensions.length).toBe(0); + expect(pivotGrid.columns.length).toBe(pivotGrid.values.length); - GridSelectionFunctions.verifyColumnSelected(unitsSold, false); - GridSelectionFunctions.verifyColumnSelected(amountOfSale, false); - GridSelectionFunctions.verifyColumnGroupSelected(fixture, group, false); - }); -}); - -describe('IgxPivotGrid Resizing #pivotGrid', () => { - let fixture: ComponentFixture; - let pivotGrid: IgxPivotGridComponent; - - configureTestSuite((() => { - TestBed.configureTestingModule({ - declarations: [ - IgxPivotGridTestComplexHierarchyComponent - ], - imports: [ - NoopAnimationsModule, - IgxPivotGridModule - ] - }); - })); - - beforeEach(fakeAsync(() => { - fixture = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent); - fixture.detectChanges(); - - pivotGrid = fixture.componentInstance.pivotGrid; - })); - - it('should define grid with resizable columns.', fakeAsync(() => { - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - - let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.resizable).toBeTrue(); - expect(rowHeaders[3].componentInstance.column.resizable).toBeTrue(); - - rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.resizable).toBeTrue(); - expect(rowHeaders[1].componentInstance.column.resizable).toBeTrue(); - expect(rowHeaders[5].componentInstance.column.resizable).toBeTrue(); - expect(rowHeaders[7].componentInstance.column.resizable).toBeTrue(); - })); - - it('should update grid after resizing a top dimension header to be bigger.', fakeAsync(() => { - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - - let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[3].componentInstance.column.width).toEqual('200px'); - - rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); - - rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[0]).nativeElement; - - // Resize first column - UIInteractions.simulateMouseEvent('mousedown', headerResArea, 100, 0); - tick(200); - fixture.detectChanges(); - - const resizer = GridFunctions.getResizer(fixture).nativeElement; - expect(resizer).toBeDefined(); - UIInteractions.simulateMouseEvent('mousemove', resizer, 300, 5); - UIInteractions.simulateMouseEvent('mouseup', resizer, 300, 5); - fixture.detectChanges(); - - rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('400px'); - expect(rowHeaders[3].componentInstance.column.width).toEqual('400px'); - - rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); - })); - - it('should update grid after resizing a child dimension header to be bigger.', fakeAsync(() => { - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - - let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[3].componentInstance.column.width).toEqual('200px'); - - const headerResArea = GridFunctions.getHeaderResizeArea(rowHeaders[3]).nativeElement; - - // Resize first column - UIInteractions.simulateMouseEvent('mousedown', headerResArea, 100, 0); - tick(200); - fixture.detectChanges(); - - const resizer = GridFunctions.getResizer(fixture).nativeElement; - expect(resizer).toBeDefined(); - UIInteractions.simulateMouseEvent('mousemove', resizer, 300, 5); - UIInteractions.simulateMouseEvent('mouseup', resizer, 300, 5); - fixture.detectChanges(); - - rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('400px'); - expect(rowHeaders[3].componentInstance.column.width).toEqual('400px'); - - rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - expect(rowHeaders[0].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[1].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[5].componentInstance.column.width).toEqual('200px'); - expect(rowHeaders[7].componentInstance.column.width).toEqual('200px'); - })); -}); - -describe('IgxPivotGrid APIs #pivotGrid', () => { - let fixture: ComponentFixture; - let pivotGrid: IgxPivotGridComponent; - - configureTestSuite((() => { - TestBed.configureTestingModule({ - declarations: [ - IgxPivotGridTestComplexHierarchyComponent - ], - imports: [ - NoopAnimationsModule, - IgxPivotGridModule - ] - }); - })); - - beforeEach(fakeAsync(() => { - fixture = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent); - fixture.detectChanges(); - - pivotGrid = fixture.componentInstance.pivotGrid; - })); - - it('should allow inserting new dimension at index.', () => { - // insert in rows - pivotGrid.insertDimensionAt({ memberName: 'SellerName', enabled: true }, PivotDimensionType.Row, 1); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.rows[1].memberName).toBe('SellerName'); - // check rows - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - let rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - const first = rowHeaders.map(x => x.componentInstance.column.header)[0]; - expect(first).toBe('Larry Lieb'); - - // insert in columns - pivotGrid.insertDimensionAt({ memberName: 'SellerNameColumn', memberFunction: (rec) => rec.SellerName, enabled: true }, PivotDimensionType.Column, 0); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.columns[0].memberName).toBe('SellerNameColumn'); - expect(pivotGrid.columnDimensions.length).toBe(2); - expect(pivotGrid.columns.length).toBe(28); - - // insert in filter - pivotGrid.insertDimensionAt({ memberName: 'SellerNameFilter', memberFunction: (rec) => rec.SellerName, enabled: true }, PivotDimensionType.Filter, 1); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); - expect(pivotGrid.pivotConfiguration.filters[0].memberName).toBe('SellerNameFilter'); - - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const chip = headerRow.querySelector('igx-chip[id="SellerNameFilter"]'); - expect(chip).not.toBeNull(); - }); - - it('should allow removing dimension.', () => { - const filter = { memberName: 'SellerNameFilter', memberFunction: (rec) => rec.SellerName, enabled: true }; - pivotGrid.pivotConfiguration.filters = [filter]; - fixture.detectChanges(); - - // remove row - pivotGrid.removeDimension(pivotGrid.pivotConfiguration.rows[0]); - fixture.detectChanges(); - - expect(pivotGrid.pivotConfiguration.rows.length).toBe(1); - expect(pivotGrid.pivotConfiguration.rows[0].memberName).toBe('AllProducts'); - - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - const headers = rowHeaders.map(x => x.componentInstance.column.header); - expect(headers.length).toBe(5); - - // remove col - pivotGrid.removeDimension(pivotGrid.pivotConfiguration.columns[0]); - fixture.detectChanges(); - - expect(pivotGrid.pivotConfiguration.columns.length).toBe(0); - expect(pivotGrid.columnDimensions.length).toBe(0); - expect(pivotGrid.columns.length).toBe(pivotGrid.values.length); - - // remove filter - pivotGrid.removeDimension(filter); - fixture.detectChanges(); - - expect(pivotGrid.pivotConfiguration.filters.length).toBe(0); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const chip = headerRow.querySelector('igx-chip[id="SellerNameFilter"]'); - expect(chip).toBeNull(); - - // remove something that is not part of the config - pivotGrid.removeDimension({ memberName: 'Test', enabled: true }); - fixture.detectChanges(); - // nothing should change - expect(pivotGrid.pivotConfiguration.filters.length).toBe(0); - expect(pivotGrid.pivotConfiguration.columns.length).toBe(0); - expect(pivotGrid.pivotConfiguration.rows.length).toBe(1); - }); + // toggle filter + pivotGrid.toggleDimension(filter); + fixture.detectChanges(); - it('should allow toggling dimension.', () => { - const filter = { memberName: 'SellerNameFilter', memberFunction: (rec) => rec.SellerName, enabled: true }; - pivotGrid.pivotConfiguration.filters = [filter]; - fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); + expect(pivotGrid.filterDimensions.length).toBe(0); + const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + const chip = headerRow.querySelector('igx-chip[id="SellerNameFilter"]'); + expect(chip).toBeNull(); - // toggle row - pivotGrid.toggleDimension(pivotGrid.pivotConfiguration.rows[0]); - fixture.detectChanges(); + // toggle something that is not part of the config + pivotGrid.toggleDimension({ memberName: 'Test', enabled: false }); + fixture.detectChanges(); - // there are still 2 - expect(pivotGrid.pivotConfiguration.rows.length).toBe(2); - // 1 is disabled - expect(pivotGrid.rowDimensions.length).toBe(1); + // nothing should change + expect(pivotGrid.filterDimensions.length).toBe(0); + expect(pivotGrid.columnDimensions.length).toBe(0); + expect(pivotGrid.rowDimensions.length).toBe(1); - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - const headers = rowHeaders.map(x => x.componentInstance.column.header); - expect(headers.length).toBe(5); + expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); + expect(pivotGrid.pivotConfiguration.columns.length).toBe(1); + expect(pivotGrid.pivotConfiguration.rows.length).toBe(2); - // toggle column - pivotGrid.toggleDimension(pivotGrid.pivotConfiguration.columns[0]); - fixture.detectChanges(); + }); - expect(pivotGrid.pivotConfiguration.columns.length).toBe(1); - expect(pivotGrid.columnDimensions.length).toBe(0); - expect(pivotGrid.columns.length).toBe(pivotGrid.values.length); + it('should allow moving dimension.', () => { + const dim = pivotGrid.pivotConfiguration.rows[0]; - // toggle filter - pivotGrid.toggleDimension(filter); - fixture.detectChanges(); + // from row to column + pivotGrid.moveDimension(dim, PivotDimensionType.Column, 0); + fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); - expect(pivotGrid.filterDimensions.length).toBe(0); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - const chip = headerRow.querySelector('igx-chip[id="SellerNameFilter"]'); - expect(chip).toBeNull(); + expect(pivotGrid.rowDimensions.length).toBe(1); + expect(pivotGrid.columnDimensions.length).toBe(2); - // toggle something that is not part of the config - pivotGrid.toggleDimension({ memberName: 'Test', enabled: false }); - fixture.detectChanges(); + let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const headers = rowHeaders.map(x => x.componentInstance.column.header); + expect(headers.length).toBe(5); - // nothing should change - expect(pivotGrid.filterDimensions.length).toBe(0); - expect(pivotGrid.columnDimensions.length).toBe(0); - expect(pivotGrid.rowDimensions.length).toBe(1); + expect(pivotGrid.pivotConfiguration.columns[0].memberName).toBe(dim.memberName); + expect(pivotGrid.columnDimensions.length).toBe(2); + expect(pivotGrid.columns.length).toBe(28); - expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); - expect(pivotGrid.pivotConfiguration.columns.length).toBe(1); - expect(pivotGrid.pivotConfiguration.rows.length).toBe(2); + // from column to filter + pivotGrid.moveDimension(dim, PivotDimensionType.Filter, 0); + fixture.detectChanges(); - }); + expect(pivotGrid.pivotConfiguration.columns.length).toBe(1); + expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); + expect(pivotGrid.columns.length).toBe(15); - it('should allow moving dimension.', () => { - const dim = pivotGrid.pivotConfiguration.rows[0]; + let headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); + let chip = headerRow.querySelector('igx-chip[id="All cities"]'); + expect(chip).not.toBeNull(); - // from row to column - pivotGrid.moveDimension(dim, PivotDimensionType.Column, 0); - fixture.detectChanges(); - expect(pivotGrid.rowDimensions.length).toBe(1); - expect(pivotGrid.columnDimensions.length).toBe(2); + // from filter to row + pivotGrid.moveDimension(dim, PivotDimensionType.Row, 1); + fixture.detectChanges(); - let dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - let rowHeaders = dimensionContents[0].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - const headers = rowHeaders.map(x => x.componentInstance.column.header); - expect(headers.length).toBe(5); + expect(pivotGrid.pivotConfiguration.rows.length).toBe(2); + expect(pivotGrid.pivotConfiguration.rows[1].memberName).toBe('All cities'); + expect(pivotGrid.pivotConfiguration.filters.length).toBe(0); - expect(pivotGrid.pivotConfiguration.columns[0].memberName).toBe(dim.memberName); - expect(pivotGrid.columnDimensions.length).toBe(2); - expect(pivotGrid.columns.length).toBe(28); + dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); + rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); + const first = rowHeaders.map(x => x.componentInstance.column.header)[0]; + expect(first).toBe('All Cities'); + }); - // from column to filter - pivotGrid.moveDimension(dim, PivotDimensionType.Filter, 0); - fixture.detectChanges(); + it('should allow inserting new value at index.', () => { + const value = { + member: 'Date', + aggregate: { + aggregator: IgxPivotDateAggregate.latest, + key: 'LATEST', + label: 'Latest' + }, + enabled: true + }; + pivotGrid.insertValueAt(value, 1); + fixture.detectChanges(); + expect(pivotGrid.values.length).toBe(3); + expect(pivotGrid.values[1].member).toBe('Date'); + expect(pivotGrid.columns.length).toBe(20); + }); - expect(pivotGrid.pivotConfiguration.columns.length).toBe(1); - expect(pivotGrid.pivotConfiguration.filters.length).toBe(1); - expect(pivotGrid.columns.length).toBe(15); + it('should allow removing value.', () => { + pivotGrid.removeValue(pivotGrid.values[1]); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.values.length).toBe(1); + expect(pivotGrid.values[0].member).toBe('UnitsSold'); + expect(pivotGrid.columns.length).toBe(5); + }); - let headerRow = fixture.nativeElement.querySelector('igx-pivot-header-row'); - let chip = headerRow.querySelector('igx-chip[id="All cities"]'); - expect(chip).not.toBeNull(); + it('should allow toggling value.', () => { + // toggle off + pivotGrid.toggleValue(pivotGrid.pivotConfiguration.values[1]); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.values.length).toBe(2); + expect(pivotGrid.values.length).toBe(1); + expect(pivotGrid.values[0].member).toBe('UnitsSold'); + expect(pivotGrid.columns.length).toBe(5); + // toggle on + pivotGrid.toggleValue(pivotGrid.pivotConfiguration.values[1]); + fixture.detectChanges(); + expect(pivotGrid.pivotConfiguration.values.length).toBe(2); + expect(pivotGrid.values.length).toBe(2); + expect(pivotGrid.values[0].member).toBe('UnitsSold'); + expect(pivotGrid.values[1].member).toBe('AmountOfSale'); + expect(pivotGrid.columns.length).toBe(15); + }); + it('should allow moving value.', () => { + const val = pivotGrid.pivotConfiguration.values[0]; + // move after + pivotGrid.moveValue(val, 1); + fixture.detectChanges(); - // from filter to row - pivotGrid.moveDimension(dim, PivotDimensionType.Row, 1); - fixture.detectChanges(); + expect(pivotGrid.values[0].member).toBe('AmountOfSale'); + expect(pivotGrid.values[1].member).toBe('UnitsSold'); - expect(pivotGrid.pivotConfiguration.rows.length).toBe(2); - expect(pivotGrid.pivotConfiguration.rows[1].memberName).toBe('All cities'); - expect(pivotGrid.pivotConfiguration.filters.length).toBe(0); + let valueCols = pivotGrid.columns.filter(x => x.level === 1); + expect(valueCols[0].header).toBe('Amount of Sale'); + expect(valueCols[1].header).toBe('UnitsSold'); - dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); - rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); - const first = rowHeaders.map(x => x.componentInstance.column.header)[0]; - expect(first).toBe('All Cities'); - }); + // move before + pivotGrid.moveValue(val, 0); + fixture.detectChanges(); - it('should allow inserting new value at index.', () => { - const value = { - member: 'Date', - aggregate: { - aggregator: IgxPivotDateAggregate.latest, - key: 'LATEST', - label: 'Latest' - }, - enabled: true - }; - pivotGrid.insertValueAt(value, 1); - fixture.detectChanges(); - expect(pivotGrid.values.length).toBe(3); - expect(pivotGrid.values[1].member).toBe('Date'); - expect(pivotGrid.columns.length).toBe(20); - }); - - it('should allow removing value.', () => { - pivotGrid.removeValue(pivotGrid.values[1]); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.values.length).toBe(1); - expect(pivotGrid.values[0].member).toBe('UnitsSold'); - expect(pivotGrid.columns.length).toBe(5); - }); - - it('should allow toggling value.', () => { - // toggle off - pivotGrid.toggleValue(pivotGrid.pivotConfiguration.values[1]); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.values.length).toBe(2); - expect(pivotGrid.values.length).toBe(1); - expect(pivotGrid.values[0].member).toBe('UnitsSold'); - expect(pivotGrid.columns.length).toBe(5); - // toggle on - pivotGrid.toggleValue(pivotGrid.pivotConfiguration.values[1]); - fixture.detectChanges(); - expect(pivotGrid.pivotConfiguration.values.length).toBe(2); - expect(pivotGrid.values.length).toBe(2); - expect(pivotGrid.values[0].member).toBe('UnitsSold'); - expect(pivotGrid.values[1].member).toBe('AmountOfSale'); - expect(pivotGrid.columns.length).toBe(15); - }); - - it('should allow moving value.', () => { - const val = pivotGrid.pivotConfiguration.values[0]; - // move after - pivotGrid.moveValue(val, 1); - fixture.detectChanges(); - - expect(pivotGrid.values[0].member).toBe('AmountOfSale'); - expect(pivotGrid.values[1].member).toBe('UnitsSold'); - - let valueCols = pivotGrid.columns.filter(x => x.level === 1); - expect(valueCols[0].header).toBe('Amount of Sale'); - expect(valueCols[1].header).toBe('UnitsSold'); - - // move before - pivotGrid.moveValue(val, 0); - fixture.detectChanges(); - - expect(pivotGrid.values[0].member).toBe('UnitsSold'); - expect(pivotGrid.values[1].member).toBe('AmountOfSale'); - valueCols = pivotGrid.columns.filter(x => x.level === 1); - expect(valueCols[0].header).toBe('UnitsSold'); - expect(valueCols[1].header).toBe('Amount of Sale'); - }); + expect(pivotGrid.values[0].member).toBe('UnitsSold'); + expect(pivotGrid.values[1].member).toBe('AmountOfSale'); + valueCols = pivotGrid.columns.filter(x => x.level === 1); + expect(valueCols[0].header).toBe('UnitsSold'); + expect(valueCols[1].header).toBe('Amount of Sale'); + }); + }); }); \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts index 114bf5a2442..5ed7b228b8c 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-header-row.component.ts @@ -237,7 +237,7 @@ export class IgxPivotHeaderRowComponent extends IgxGridHeaderRowComponent implem * @internal */ public ngOnChanges(changes: SimpleChanges) { - if (changes.unpinnedColumnCollection && this.unpinnedColumnCollection.length > 0) { + if (changes.unpinnedColumnCollection) { this.populateColumnDimensionsByLevel(); } } diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html index b529d20eb04..e53ee83a3ff 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.html @@ -3,8 +3,8 @@
@@ -12,7 +12,7 @@
-
+
{{ getExpandState(column) ? 'expand_more' : 'chevron_right'}} {{column.header}} @@ -21,7 +21,7 @@ -
+
{{column.header}} diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts index 21153eebe9e..4802168e83c 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row-dimension-content.component.ts @@ -20,7 +20,7 @@ import { IgxColumnComponent } from '../columns/column.component'; import { IGX_GRID_BASE, PivotGridType } from '../common/grid.interface'; import { IgxGridHeaderRowComponent } from '../headers/grid-header-row.component'; import { IgxRowDirective } from '../row.directive'; -import { IPivotDimension, IPivotDimensionData } from './pivot-grid.interface'; +import { IPivotDimension, IPivotDimensionData, IPivotGridGroupRecord } from './pivot-grid.interface'; import { IgxPivotRowDimensionHeaderGroupComponent } from './pivot-row-dimension-header-group.component'; import { PivotUtil } from './pivot-util'; @@ -49,7 +49,10 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon public dimension: IPivotDimension; @Input() - public rowData: any; + public rootDimension: IPivotDimension; + + @Input() + public rowData: IPivotGridGroupRecord; /** * @hidden @internal @@ -99,9 +102,8 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon this.viewRef.clear(); } if (changes.width && this.rowDimensionData) { - const dimData = PivotUtil.getDimensionLevel(this.dimension, this.rowData, this.grid.pivotKeys); const data = this.rowDimensionData; - data.column.width = this.grid.resolveRowDimensionWidth(this.dimension) + 'px'; + data.column.width = this.grid.rowDimensionWidthToPixels(this.rootDimension) + 'px'; } } @@ -121,7 +123,7 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon */ public getRowDimensionKey(col: IgxColumnComponent) { const dimData = this.rowDimensionData; - const key = PivotUtil.getRecordKey(this.rowData, dimData.dimension, dimData.prevDimensions, this.grid.pivotKeys); + const key = PivotUtil.getRecordKey(this.rowData, dimData.dimension); return key; } @@ -130,11 +132,11 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon } public getLevel(col: IgxColumnComponent) { - return this.rowData[col.field + this.grid.pivotKeys.rowDimensionSeparator + this.grid.pivotKeys.level]; + return (this.dimension as any).level; } public get rowSpan() { - return this.rowData[this.rowDimensionData?.dimension?.memberName + this.grid.pivotKeys.rowDimensionSeparator + 'rowSpan'] || 1; + return this.rowData.rowSpan || 1; } public get headerHeight() { @@ -143,51 +145,30 @@ export class IgxPivotRowDimensionContentComponent extends IgxGridHeaderRowCompon } protected extractFromDimensions() { - let lvl = 0; - let dimData; - let prevDims = []; - - if (this.dimension) { - dimData = PivotUtil.getDimensionLevel(this.dimension, this.rowData, this.grid.pivotKeys); - prevDims = this.getPrevDims(this.dimension); - prevDims.forEach(prev => { - lvl += prev.level; - }); - } - const col = this.extractFromDimension(dimData, this.rowData, lvl); + const col = this.extractFromDimension(this.dimension, this.rowData); + const prevDims = []; this.rowDimensionData = { column: col, - dimension: dimData?.dimension, + dimension: this.dimension, prevDimensions: prevDims }; } - protected getPrevDims(currDim) { - const ind = this.grid.rowDimensions.indexOf(currDim); - const prevDims = []; - for (let i = 0; i < ind; i++) { - const prevDim = this.grid.rowDimensions[i]; - const dimData = PivotUtil.getDimensionLevel(prevDim, this.rowData, this.grid.pivotKeys); - prevDims.push(dimData.dimension); - } - return prevDims; - } - - protected extractFromDimension(dimData, rowData: any[], lvl) { - const field = dimData?.dimension?.memberName || 'placeholder'; - const header = rowData[field] || ''; - const col = this._createColComponent(field, header, dimData?.dimension, lvl); + protected extractFromDimension(dim: IPivotDimension, rowData: IPivotGridGroupRecord) { + const field = dim.memberName; + const header = rowData.dimensionValues.get(field); + const col = this._createColComponent(field, header, dim); return col; } - protected _createColComponent(field: string, header: string, dim: IPivotDimension, lvl) { + protected _createColComponent(field: string, header: string, dim: IPivotDimension) { const ref = this.viewRef.createComponent(IgxColumnComponent); ref.instance.field = field; ref.instance.header = header; + ref.instance.width = this.grid.rowDimensionWidthToPixels(this.rootDimension) + 'px'; ref.instance.resizable = this.grid.rowDimensionResizing; - ref.instance.width = this.grid.resolveRowDimensionWidth(this.dimension) + 'px'; (ref as any).instance._vIndex = this.grid.columns.length + this.rowIndex + this.rowIndex * this.grid.pivotConfiguration.rows.length; - if (dim.childLevel && lvl >= PivotUtil.getTotalLvl(this.rowData, this.grid.pivotKeys)) { + if (dim.childLevel) { ref.instance.headerTemplate = this.headerTemplate; } else { ref.instance.headerTemplate = this.headerTemplateDefault; diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.html b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.html index 2c9fe193c30..11b9449b75d 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.html @@ -1,7 +1,3 @@ - - - - - - - -
- - - - - diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.ts index e05cd6800a7..09b69281ddc 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-row.component.ts @@ -11,6 +11,7 @@ import { IgxColumnComponent } from '../columns/column.component'; import { IGX_GRID_BASE, PivotGridType } from '../common/grid.interface'; import { IgxRowDirective } from '../row.directive'; import { IgxGridSelectionService } from '../selection/selection.service'; +import { IPivotDimension, IPivotDimensionData, IPivotGridRecord } from './pivot-grid.interface'; import { PivotUtil } from './pivot-util'; @@ -29,14 +30,11 @@ export class IgxPivotRowComponent extends IgxRowDirective { @HostBinding('attr.aria-selected') public get selected(): boolean { let isSelected = false; - let prevDims = []; - for (let rowDim of this.grid.rowDimensions) { - const dimData = PivotUtil.getDimensionLevel(rowDim, this.data, this.grid.pivotKeys); - const key = PivotUtil.getRecordKey(this.data, dimData.dimension, prevDims, this.grid.pivotKeys); + for (let rowDim of this.data.dimensions) { + const key = PivotUtil.getRecordKey(this.data, rowDim); if (this.selectionService.isPivotRowSelected(key)) { isSelected = true; } - prevDims.push(dimData.dimension); } return isSelected; } @@ -60,6 +58,36 @@ export class IgxPivotRowComponent extends IgxRowDirective { return this.index; } + /** + * The pivot record data passed to the row component. + * + * ```typescript + * // get the pivot row data for the first selected row + * let selectedRowData = this.grid.selectedRows[0].data; + * ``` + */ + @Input() + public get data(): IPivotGridRecord { + return this._data; + } + + public set data(v: IPivotGridRecord) { + this._data = v; + } + + /** + * @hidden + * @internal + */ + public get pivotAggregationData() { + const aggregations = this.data.aggregationValues; + const obj = {}; + aggregations.forEach((value, key) => { + obj[key] = value; + }); + return obj; + } + public getCellClass(col: IgxColumnComponent) { const configuration = this.grid.pivotConfiguration; if (configuration.values.length === 1) { diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-util.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-util.ts index fd8495996ce..a0544903009 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-util.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-util.ts @@ -4,9 +4,87 @@ import { FilteringLogic } from '../../data-operations/filtering-expression.inter import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { ISortingExpression } from '../../data-operations/sorting-strategy'; import { IGridSortingStrategy, IgxSorting } from '../common/strategy'; -import { IPivotConfiguration, IPivotDimension, IPivotKeys, IPivotValue, PivotDimensionType } from './pivot-grid.interface'; +import { IPivotConfiguration, IPivotDimension, IPivotGridRecord, IPivotKeys, IPivotValue, PivotDimensionType } from './pivot-grid.interface'; export class PivotUtil { + + // go through all children and apply new dimension groups as child + public static processGroups(recs: IPivotGridRecord[], dimension: IPivotDimension, pivotKeys: IPivotKeys) { + for (const rec of recs) { + // process existing children + if (rec.children && rec.children.size > 0) { + // process hierarchy in dept + rec.children.forEach((values, key) => { + this.processGroups(values, dimension, pivotKeys); + }); + } + // add children for current dimension + const hierarchyFields = PivotUtil + .getFieldsHierarchy(rec.records, [dimension], PivotDimensionType.Row, pivotKeys); + const values = Array.from(hierarchyFields.values()).find(x => x.dimension.memberName === dimension.memberName); + const siblingData = PivotUtil + .processHierarchy(hierarchyFields, pivotKeys, 0); + rec.children.set(dimension.memberName, siblingData); + } + } + + public static flattenGroups(data: IPivotGridRecord[], dimension: IPivotDimension, expansionStates, defaultExpand: boolean, parent?: IPivotDimension, parentRec?: IPivotGridRecord) { + for (let i = 0; i < data.length; i++) { + const rec = data[i]; + const field = dimension.memberName; + if (!field) { + continue; + } + + let recordsData = rec.children.get(field); + if (!recordsData && parent) { + // check parent + recordsData = rec.children.get(parent.memberName); + if (recordsData) { + dimension = parent; + } + } + + if (parentRec) { + parentRec.dimensionValues.forEach((value, key) => { + if (parent.memberName !== key) { + rec.dimensionValues.set(key, value); + const dim = parentRec.dimensions.find(x => x.memberName === key); + rec.dimensions.unshift(dim); + } + + }); + } + + + const expansionRowKey = PivotUtil.getRecordKey(rec, dimension); + const isExpanded = expansionStates.get(expansionRowKey) === undefined ? + defaultExpand : + expansionStates.get(expansionRowKey); + const shouldExpand = isExpanded || !dimension.childLevel || !rec.dimensionValues.get(dimension.memberName); + if (shouldExpand && recordsData) { + if (dimension.childLevel) { + this.flattenGroups(recordsData, dimension.childLevel, expansionStates, defaultExpand, dimension, rec); + } else { + // copy parent values and dims in child + recordsData.forEach(x => { + rec.dimensionValues.forEach((value, key) => { + if (dimension.memberName !== key) { + x.dimensionValues.set(key, value); + const dim = rec.dimensions.find(y => y.memberName === key); + x.dimensions.unshift(dim); + } + + }); + }); + } + + data.splice(i + 1, 0, ...recordsData); + i += recordsData.length; + + } + } + } public static assignLevels(dims) { for (const dim of dims) { let currDim = dim; @@ -38,17 +116,14 @@ export class PivotUtil { return hierarchy; } - public static sort(data: any[], expressions: ISortingExpression[], sorting: IGridSortingStrategy = new IgxSorting(), pivotKeys): any[] { + public static sort(data: IPivotGridRecord[], expressions: ISortingExpression[], sorting: IGridSortingStrategy = new IgxSorting()): any[] { data.forEach(rec => { - const keys = Object.keys(rec); - rec.sorted = true; - keys.forEach(k => { - if (k.indexOf(pivotKeys.records) !== -1 && k !== pivotKeys.records) { - const unsorted = rec[k].filter(x => !x.sorted); - // sort all child record collections based on expression recursively. - rec[k] = this.sort(unsorted, expressions, sorting, pivotKeys); - } - }); + const children = rec.children; + if (children) { + children.forEach(x => { + this.sort(x, expressions, sorting); + }); + } }); return DataUtil.sort(data, expressions, sorting); } @@ -66,104 +141,6 @@ export class PivotUtil { return lvl; } - public static getDimensionLevel(dim: IPivotDimension, rec: any, pivotKeys: IPivotKeys) { - let level = rec[dim.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.level]; - while (dim.childLevel && level === undefined) { - dim = dim.childLevel; - level = rec[dim.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.level]; - } - return { level, dimension: dim }; - } - - public static flattenHierarchy(records: any[], - config: IPivotConfiguration, - dim: IPivotDimension, - expansionStates: any, - defaultExpandState: boolean, - pivotKeys: IPivotKeys, - lvl: number, - prevDims: IPivotDimension[], - currDimLvl: number, - maxDimLvl: number) { - const data = records; - for (let i = 0; i < data.length; i++) { - const rec = data[i]; - const field = dim.memberName; - if (!field) { - continue; - } - rec[field + pivotKeys.rowDimensionSeparator + pivotKeys.level] = currDimLvl; - const expansionRowKey = PivotUtil.getRecordKey(rec, dim, prevDims, pivotKeys); - const isExpanded = expansionStates.get(expansionRowKey) === undefined ? - defaultExpandState : - expansionStates.get(expansionRowKey); - const recordsData = rec[field + pivotKeys.rowDimensionSeparator + pivotKeys.records]; - if (recordsData && recordsData.length > 0 && - ((isExpanded && lvl > 0) || (maxDimLvl == currDimLvl))) { - let dimData = recordsData; - if (dim.childLevel) { - if (PivotUtil.getDimensionDepth(dim) > 1) { - dimData = this.flattenHierarchy(dimData, config, dim.childLevel, - expansionStates, defaultExpandState, pivotKeys, lvl - 1, prevDims, currDimLvl + 1, maxDimLvl); - } else { - dimData.forEach(d => { - d[dim.childLevel.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.level] = currDimLvl + 1; - }); - } - } - - let prevDimRecs = []; - const dimLevel = rec[field + pivotKeys.rowDimensionSeparator + pivotKeys.level]; - let prevDimLevel; - let shouldConcat = true; - const prevDim = prevDims ? prevDims[prevDims.length - 1] : null; - if (prevDim) { - let prevDimName = prevDim.memberName; - prevDimRecs = rec[prevDimName + pivotKeys.rowDimensionSeparator + pivotKeys.records]; - if (!prevDimRecs) { - prevDimName = prevDim.childLevel.memberName; - prevDimRecs = rec[prevDimName + pivotKeys.rowDimensionSeparator + pivotKeys.records]; - } - prevDimLevel = rec[prevDimName + pivotKeys.rowDimensionSeparator + pivotKeys.level]; - shouldConcat = !!rec[field] && (prevDimLevel === undefined || prevDimLevel >= dimLevel); - } - dimData.forEach(d => { - if (prevDims && prevDims.length > 0) { - if (!shouldConcat) { - d[dim.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.level] = currDimLvl; - } - prevDims.forEach(prev => { - const dimInfo = PivotUtil.getDimensionLevel(prev, rec, pivotKeys); - d[dimInfo.dimension.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.level] = dimInfo.level; - }); - } - }); - if (shouldConcat) { - // concat - data.splice(i + 1, 0, ...dimData); - i += dimData.length; - } else { - // merge - data.splice(i, 1, ...dimData); - i += dimData.length - 1; - } - } else if (isExpanded) { - // this is leaf - let leafDim = dim; - let currLvl = currDimLvl; - while (leafDim.childLevel) { - leafDim = leafDim.childLevel; - currLvl++; - } - rec[leafDim.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.level] = currLvl; - if (leafDim.memberName !== field) { - delete rec[field + pivotKeys.rowDimensionSeparator + pivotKeys.level]; - } - } - } - return data; - } - public static extractValuesForRow(dims: IPivotDimension[], recData: any, pivotKeys: IPivotKeys) { const values = new Map(); for (const col of dims) { @@ -217,25 +194,40 @@ export class PivotUtil { return newArr; } - public static applyAggregations(hierarchies, values, pivotKeys) { + public static applyAggregations(rec: IPivotGridRecord, hierarchies, values, pivotKeys: IPivotKeys) { + if (hierarchies.size === 0) { + // no column groups + const aggregationResult = this.aggregate(rec.records, values); + this.applyAggregationRecordData(aggregationResult, undefined, rec, pivotKeys); + return; + } hierarchies.forEach((hierarchy) => { const children = hierarchy[pivotKeys.children]; if (children && children.size > 0) { - this.applyAggregations(children, values, pivotKeys); + this.applyAggregations(rec, children, values, pivotKeys); const childRecords = this.collectRecords(children, pivotKeys); hierarchy[pivotKeys.aggregations] = this.aggregate(childRecords, values); + this.applyAggregationRecordData(hierarchy[pivotKeys.aggregations], hierarchy.value, rec, pivotKeys); } else if (hierarchy[pivotKeys.records]) { hierarchy[pivotKeys.aggregations] = this.aggregate(hierarchy[pivotKeys.records], values); - } else { - const aggregationResult = this.aggregate([hierarchy], values); - const keys = Object.keys(aggregationResult); - keys.forEach(key => { - hierarchy[key] = aggregationResult[key]; - }); + this.applyAggregationRecordData(hierarchy[pivotKeys.aggregations], hierarchy.value, rec, pivotKeys); } }); } + protected static applyAggregationRecordData(aggregationData: any, groupName: string, rec: IPivotGridRecord, pivotKeys: IPivotKeys) { + const aggregationKeys = Object.keys(aggregationData); + if (aggregationKeys.length > 1) { + aggregationKeys.forEach((key) => { + const aggregationKey = groupName ? groupName + pivotKeys.columnDimensionSeparator + key : key; + rec.aggregationValues.set(aggregationKey, aggregationData[key]); + }); + } else if (aggregationKeys.length === 1) { + const aggregationKey = aggregationKeys[0]; + rec.aggregationValues.set(groupName || aggregationKey, aggregationData[aggregationKey]); + } + } + public static aggregate(records, values: IPivotValue[]) { const result = {}; for (const pivotValue of values) { @@ -245,112 +237,38 @@ export class PivotUtil { return result; } - public static processSiblingProperties(parentRec, siblingData, pivotKeys) { - if (!siblingData) { - return; - } - for (const property in parentRec) { - if (parentRec.hasOwnProperty(property) && - Object.values(pivotKeys).indexOf(property) === -1) { - siblingData.forEach(s => { - s[property] = parentRec[property]; - }); - } - } - } - - public static processSubGroups(row, prevRowDims, siblingData, pivotKeys, lvl = 0) { - const prevRowDimsIter = prevRowDims.slice(0); - // process combined groups - while (prevRowDimsIter.length > 0) { - const prevRowDim = prevRowDimsIter.pop(); - const prevRowField = prevRowDim.memberName; - for (const sibling of siblingData) { - const childCollection = sibling[prevRowField + pivotKeys.rowDimensionSeparator + pivotKeys.records] || []; - for (const child of childCollection) { - if (!child[pivotKeys.records]) { - continue; - } - const recordsAccessKey = row.memberName + pivotKeys.rowDimensionSeparator + pivotKeys.records; - child[recordsAccessKey] = []; - const keys = Object.assign({}, pivotKeys) as any; - keys[row.memberName] = row.memberName; - const hierarchyFields2 = PivotUtil - .getFieldsHierarchy(child[pivotKeys.records], [row], PivotDimensionType.Row, pivotKeys); - const siblingData2 = PivotUtil - .processHierarchy(hierarchyFields2, child ?? [], keys, 0); - if (siblingData2.length === 1) { - child[row.memberName] = sibling[row.memberName]; - // add children to current level if dimensions have same depth - for (const sib of siblingData2) { - if (sib[recordsAccessKey]) { - child[recordsAccessKey] = child[recordsAccessKey].concat(sib[recordsAccessKey]); - child[row.memberName] = sib[row.memberName]; - } - } - } else { - // otherwise overwrite direct child collection - child[recordsAccessKey] = siblingData2; - } - const sibs = prevRowDims.filter(x => x.memberName !== prevRowField); - if (sibs.length > 0) { - // Process sibling dimensions in depth - this.processSubGroups(row, [...sibs], [child], pivotKeys, lvl); - } - PivotUtil.processSiblingProperties(child, siblingData2, keys); - } - const prevRowDimsFromLvl = prevRowDims.filter(x => x.level === lvl); - if (prevRowDimsFromLvl.some(x => x.childLevel)) { - // Get child dimensions now as well since we go a level deeper into the hierarchy. - // Keep above level dims as well since lower level dims correspond to upper sibling dims as well. - const childDimensions = prevRowDimsFromLvl.filter(dim => !!dim.childLevel).map(dim => dim.childLevel); - this.processSubGroups(row, [...prevRowDimsFromLvl, ...childDimensions], childCollection, pivotKeys, lvl + 1); - } - } - } - } - - public static processHierarchy(hierarchies, rec, pivotKeys, level = 0, rootData = false) { - const flatData = []; + public static processHierarchy(hierarchies, pivotKeys, level = 0, rootData = false): IPivotGridRecord[] { + const flatData: IPivotGridRecord[] = []; hierarchies.forEach((h, key) => { const field = h.dimension.memberName; - const keys = Object.assign({}, pivotKeys) as any; - let obj = {}; - obj[field] = key; + const rec: IPivotGridRecord = { + dimensionValues: new Map(), + aggregationValues: new Map(), + children: new Map(), + dimensions: [h.dimension] + }; + rec.dimensionValues.set(field, key); if (h[pivotKeys.records]) { - obj[pivotKeys.records] = this.getDirectLeafs(h[pivotKeys.records], pivotKeys); + rec.records = this.getDirectLeafs(h[pivotKeys.records]); } - obj[field + pivotKeys.rowDimensionSeparator + pivotKeys.records] = h[pivotKeys.records]; - obj = { ...obj, ...h[pivotKeys.aggregations] }; - obj[pivotKeys.level] = level; - flatData.push(obj); + rec.level = level; + flatData.push(rec); if (h[pivotKeys.children] && h[pivotKeys.children].size > 0) { - const nestedData = this.processHierarchy(h[pivotKeys.children], rec, + const nestedData = this.processHierarchy(h[pivotKeys.children], pivotKeys, level + 1, rootData); - for (const nested of nestedData) { - if (nested[pivotKeys.records] && nested[pivotKeys.records].length === 1) { - // only 1 child record, apply same props to parent. - const memberName = h[pivotKeys.children].entries().next().value[1].dimension.memberName; - keys[memberName] = memberName; - PivotUtil.processSiblingProperties(nested[pivotKeys.records][0], [nested], keys); - } - } - obj[pivotKeys.records] = this.getDirectLeafs(nestedData, pivotKeys); - obj[field + pivotKeys.rowDimensionSeparator + pivotKeys.records] = nestedData; - if (!rootData) { - PivotUtil.processSiblingProperties(rec, obj[field + pivotKeys.rowDimensionSeparator + pivotKeys.records], keys); - } + rec.records = this.getDirectLeafs(nestedData); + rec.children.set(field, nestedData); } }); return flatData; } - public static getDirectLeafs(records, pivotKeys) { + public static getDirectLeafs(records: IPivotGridRecord[]) { let leafs = []; for (const rec of records) { - if (rec[pivotKeys.records]) { - const data = rec[pivotKeys.records].filter(x => !x[pivotKeys.records] && leafs.indexOf(x) === -1); + if (rec.records) { + const data = rec.records.filter(x => !x.records && leafs.indexOf(x) === -1); leafs = leafs.concat(data); } else { leafs.push(rec); @@ -359,16 +277,15 @@ export class PivotUtil { return leafs; } - public static getRecordKey(rec, currentDim: IPivotDimension, prevDims: IPivotDimension[], pivotKeys: IPivotKeys) { + public static getRecordKey(rec: IPivotGridRecord, currentDim: IPivotDimension,) { const parentFields = []; - const field = currentDim.memberName; - const value = rec[field]; + const currentDimIndex = rec.dimensions.findIndex(x => x.memberName === currentDim.memberName) + 1; + const prevDims = rec.dimensions.slice(0, currentDimIndex); for (const prev of prevDims) { - const dimData = PivotUtil.getDimensionLevel(prev, rec, pivotKeys); - parentFields.push(rec[prev.memberName] || rec[dimData.dimension.memberName]); + const prevValue = rec.dimensionValues.get(prev.memberName); + parentFields.push(prevValue); } - parentFields.push(value); - return parentFields.join(pivotKeys.rowDimensionSeparator); + return parentFields.join('-'); } public static getTotalLvl(rec, pivotKeys: IPivotKeys) { @@ -415,7 +332,7 @@ export class PivotUtil { } public static buildExpressionTree(config: IPivotConfiguration) { - const allDimensions = config.rows.concat(config.columns).concat(config.filters).filter(x => x !== null && x !== undefined); + const allDimensions = (config.rows || []).concat((config.columns || [])).concat(config.filters || []).filter(x => x !== null && x !== undefined); const enabledDimensions = allDimensions.filter(x => x && x.enabled); const expressionsTree = new FilteringExpressionsTree(FilteringLogic.And); diff --git a/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resize-handle.directive.ts b/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resize-handle.directive.ts index f6e1bc8be21..a3bc538745d 100644 --- a/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resize-handle.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resize-handle.directive.ts @@ -47,7 +47,7 @@ export class IgxPivotResizeHandleDirective extends IgxResizeHandleDirective { public onDoubleClick() { this._dblClick = true; this.initResizeService(); - this.rowHeaderGroup.grid.autoSizeRowDimension(this.rowHeaderGroup.parent.dimension); + this.rowHeaderGroup.grid.autoSizeRowDimension(this.rowHeaderGroup.parent.rootDimension); } /** diff --git a/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resizing.service.ts b/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resizing.service.ts index 28cd3849316..5839249d328 100644 --- a/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resizing.service.ts +++ b/projects/igniteui-angular/src/lib/grids/resizing/pivot-grid/pivot-resizing.service.ts @@ -23,23 +23,22 @@ export class IgxPivotColumnResizingService extends IgxColumnResizingService { } protected _handlePixelResize(diff: number, column: ColumnType) { - const rowDim = this.rowHeaderGroup.parent.dimension; + const rowDim = this.rowHeaderGroup.parent.rootDimension; if (!rowDim) return; const currentColWidth = parseFloat(column.width); const colMinWidth = column.minWidthPx; const colMaxWidth = column.maxWidthPx; + let newWidth = currentColWidth; if (currentColWidth + diff < colMinWidth) { - rowDim.width = colMinWidth + 'px'; + newWidth = colMinWidth; } else if (colMaxWidth && (currentColWidth + diff > colMaxWidth)) { - rowDim.width = colMaxWidth + 'px'; + newWidth = colMaxWidth; } else { - rowDim.width = (currentColWidth + diff) + 'px'; + newWidth = (currentColWidth + diff); } - // Notify the grid to reflow, to update if horizontal scrollbar needs to be rendered/removed. - this.rowHeaderGroup.grid.pipeTrigger++; - this.rowHeaderGroup.grid.notifyChanges(true); + this.rowHeaderGroup.grid.resizeRowDimensionPixels(rowDim, newWidth); } protected _handlePercentageResize(diff: number, column: ColumnType) { } diff --git a/projects/igniteui-angular/src/lib/test-utils/pivot-grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/pivot-grid-functions.spec.ts index a9d27f4b1ff..25a21844e37 100644 --- a/projects/igniteui-angular/src/lib/test-utils/pivot-grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/pivot-grid-functions.spec.ts @@ -1,22 +1,24 @@ -import { DEFAULT_PIVOT_KEYS, IPivotDimension } from '../grids/pivot-grid/pivot-grid.interface'; +import { DEFAULT_PIVOT_KEYS, IPivotDimension, IPivotGridRecord } from '../grids/pivot-grid/pivot-grid.interface'; import { PivotUtil } from '../grids/pivot-grid/pivot-util'; export class PivotGridFunctions { - /* Extract the value populated for each record for the given dimension and its children.*/ - public static getDimensionData(data: any[], dimensions: IPivotDimension[]) { - return data.map(x => { - const obj = {}; - for (let dim of dimensions) { - const dimData = PivotUtil.getDimensionLevel(dim, x, DEFAULT_PIVOT_KEYS); - const field = dimData.dimension.memberName; - const value = x[field]; - obj[field] = value; - } - return obj; - }); - } public static checkUniqueValuesCount(data: any[], value: string, count:number) { expect(data.filter(x => x === data).length).toBe(count); } + + public static getDimensionValues(records: IPivotGridRecord[]) { + return records.map(x => this.transformMapToObject(x.dimensionValues)); + } + + public static getAggregationValues(records: IPivotGridRecord[]) { + return records.map(x => this.transformMapToObject(x.aggregationValues)); + } + public static transformMapToObject(map: Map) { + const obj = {}; + map.forEach((value, key) => { + obj[key] = value; + }); + return obj; + } } \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts index d8203fe1951..f2a6e245e63 100644 --- a/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, TemplateRef, ViewChild } from '@angular/core'; import { IgxPivotNumericAggregate } from '../grids/pivot-grid/pivot-grid-aggregate'; import { IgxPivotGridComponent } from '../grids/pivot-grid/pivot-grid.component'; import { IPivotConfiguration, PivotAggregation } from '../grids/pivot-grid/pivot-grid.interface'; @@ -7,10 +7,15 @@ import { IPivotConfiguration, PivotAggregation } from '../grids/pivot-grid/pivot template: ` - ` + + + Custom empty template. + + ` }) export class IgxPivotGridTestBaseComponent { public defaultExpand = true; + @ViewChild('emptyTemplate', { read: TemplateRef, static: true }) public emptyTemplate: TemplateRef; @ViewChild('grid', { read: IgxPivotGridComponent, static: true }) public pivotGrid: IgxPivotGridComponent; public data; diff --git a/src/app/pivot-grid-noop/pivot-grid-noop.sample.html b/src/app/pivot-grid-noop/pivot-grid-noop.sample.html index f7665775c0e..9cff7feae1c 100644 --- a/src/app/pivot-grid-noop/pivot-grid-noop.sample.html +++ b/src/app/pivot-grid-noop/pivot-grid-noop.sample.html @@ -1,5 +1,6 @@ + [data]="mockData" + [pivotConfiguration]="pivotConfigHierarchy" + height="500px"> diff --git a/src/app/pivot-grid-noop/pivot-grid-noop.sample.ts b/src/app/pivot-grid-noop/pivot-grid-noop.sample.ts index d8038c16b5c..61531f99381 100644 --- a/src/app/pivot-grid-noop/pivot-grid-noop.sample.ts +++ b/src/app/pivot-grid-noop/pivot-grid-noop.sample.ts @@ -1,103 +1,154 @@ import { Component, ViewChild } from '@angular/core'; import { - IgxPivotGridComponent, IgxPivotNumericAggregate, IPivotConfiguration, IPivotDimension, - IPivotValue, - NoopPivotDimensionsStrategy + IgxPivotGridComponent, IgxPivotNumericAggregate, IPivotConfiguration, IPivotDimension, + IPivotValue, + NoopPivotDimensionsStrategy } from 'igniteui-angular'; export class MyRowStrategy extends NoopPivotDimensionsStrategy { - public process(collection: any[], _: IPivotDimension[], __: IPivotValue[]): any[] { - return collection; - } + public process(collection: any[], _: IPivotDimension[], __: IPivotValue[]): any[] { + return collection; + } } export class MyColumnStrategy extends NoopPivotDimensionsStrategy { - public process(collection: any[], _: IPivotDimension[], __: IPivotValue[]): any[] { - return collection; - } + public process(collection: any[], _: IPivotDimension[], __: IPivotValue[]): any[] { + return collection; + } } @Component({ - providers: [], - selector: 'app-tree-grid-sample', - styleUrls: ['pivot-grid-noop.sample.css'], - templateUrl: 'pivot-grid-noop.sample.html' + providers: [], + selector: 'app-tree-grid-sample', + styleUrls: ['pivot-grid-noop.sample.css'], + templateUrl: 'pivot-grid-noop.sample.html' }) export class PivotGridNoopSampleComponent { - @ViewChild('grid1', { static: true }) public grid1: IgxPivotGridComponent; - public pivotConfigHierarchy: IPivotConfiguration = { - pivotKeys: { - aggregations: 'aggregations', - records: 'records', - children: 'children', - level: 'level', - columnDimensionSeparator: '_', - rowDimensionSeparator: '-' + @ViewChild('grid1', { static: true }) public grid1: IgxPivotGridComponent; + public pivotConfigHierarchy: IPivotConfiguration = { + columnStrategy: NoopPivotDimensionsStrategy.instance(), + rowStrategy: NoopPivotDimensionsStrategy.instance(), + columns: [ + { + memberName: 'Country', + enabled: true + }, + ] + , + rows: [ + { + memberFunction: () => 'All', + memberName: 'AllProducts', + enabled: true, + width: '25%', + childLevel: { + memberName: 'ProductCategory', + enabled: true + } + }, + { + memberName: 'AllSeller', + memberFunction: () => 'All Sellers', + enabled: true, + childLevel: { + enabled: true, + memberName: 'SellerName' + } + }, + ], + values: [ + { + member: 'UnitsSold', + aggregate: { + aggregator: IgxPivotNumericAggregate.sum, + key: 'sum', + label: 'Sum' }, - columnStrategy: NoopPivotDimensionsStrategy.instance(), - rowStrategy: NoopPivotDimensionsStrategy.instance(), - columns: [ + enabled: true + }, + ], + filters: null + }; + + public mockData = [ + { + 'AllProducts': 'All', 'USA': 829, 'Uruguay': 524, 'Bulgaria': 282, + 'AllSeller_records': [ + { + 'AllSeller': 'All Sellers', + 'USA': 829, 'Uruguay': 524, 'Bulgaria': 282, + 'AllSeller_records': [ + { 'SellerName': 'David', 'USA': 293 }, + { 'SellerName': 'Lydia', 'Uruguay': 68 }, + { 'SellerName': 'Elisa', 'USA': 296 }, + { 'SellerName': 'Larry', 'Uruguay': 456 }, + { 'SellerName': 'Stanley', 'Bulgaria': 282 }, + { 'SellerName': 'John', 'USA': 240 } + ] + } + ], + 'AllProducts_records': [ + { + 'ProductCategory': 'Accessories', + 'USA': 293, + 'AllSeller_records': [ { - memberName: 'All', - memberFunction: () => 'All', - enabled: true, - childLevel: { - memberName: 'Country', - enabled: true - } + 'AllSeller': 'All Sellers', 'USA': 293, + 'AllSeller_records': [{ 'SellerName': 'David', 'USA': 293 }] } - ] - , - rows: [ + ], + }, + { + 'ProductCategory': 'Bikes', 'Uruguay': 68, + 'AllSeller_records': [ { - memberName: 'AllProducts', - memberFunction: () => 'All Products', - enabled: true, - childLevel: { - memberName: 'ProductCategory', - enabled: true - } + 'AllSeller': 'All Sellers', 'Uruguay': 68, + 'AllSeller_records': [{ 'SellerName': 'Lydia', 'Uruguay': 68 }] } - ], - values: [ + ] + }, + { + 'ProductCategory': 'Clothing', 'USA': 296, 'Uruguay': 456, 'Bulgaria': 282, + 'AllSeller_records': [ { - member: 'UnitsSold', - aggregate: { - aggregator: IgxPivotNumericAggregate.sum, - key: 'sum', - label: 'Sum' - }, - enabled: true - }, - ], - filters: null - }; - - public mockRemoteData = [ + 'AllSeller': 'All Sellers', 'USA': 296, 'Uruguay': 456, 'Bulgaria': 282, + 'AllSeller_records': [ + { 'SellerName': 'Elisa', 'USA': 296 }, + { 'SellerName': 'Larry', 'Uruguay': 456 }, + { 'SellerName': 'Stanley', 'Bulgaria': 282 } + ] + } + ] + }, { - ProductCategory: 'All', AllProducts: 'All Products', All: 1000, 'All-Bulgaria': 774, 'All-USA': 829, 'All-Uruguay': 524, AllProducts_records: [ - { ProductCategory: 'Clothing', 'All-Bulgaria': 774, 'All-USA': 296, 'All-Uruguay': 456 }, - { ProductCategory: 'Bikes', 'All-Uruguay': 68 }, - { ProductCategory: 'Accessories', 'All-USA': 293 }, - { ProductCategory: 'Components', 'All-USA': 240 } - ] + 'ProductCategory': 'Components', 'USA': 240, + 'AllSeller_records': [ + { + 'AllSeller': 'All Sellers', 'USA': 240, + 'AllSeller_records': [ + { + 'SellerName': 'John', 'USA': 240 + }] + } + ] } - ]; - + ], + } + ]; - public mockRemoteDataDifferentSeparator = [ - { - ProductCategory: 'All', AllProducts: 'All Products', All: 2127, 'All_Country-Bulgaria': 774, 'All_Country-USA': 829, 'All_Country-Uruguay': 524, 'AllProducts-records': [ - { ProductCategory: 'Clothing', All: 1523, 'All_Country-Bulgaria': 774, 'All_Country-USA': 296, 'All_Country-Uruguay': 456, }, - { ProductCategory: 'Bikes', All: 68, 'All_Country-Uruguay': 68 }, - { ProductCategory: 'Accessories', All: 293, 'All_Country-USA': 293 }, - { ProductCategory: 'Components', All: 240, 'All_Country-USA': 240 } - ] - } - ]; + public mockRemoteDataDifferentSeparator = [ + { + AllProducts: 'All Products', All: 2127, 'All_Country-Bulgaria': 774, 'All_Country-USA': 829, 'All_Country-Uruguay': 524, 'AllProducts-records': [ + { ProductCategory: 'Clothing', All: 1523, 'All_Country-Bulgaria': 774, 'All_Country-USA': 296, 'All_Country-Uruguay': 456, }, + { ProductCategory: 'Bikes', All: 68, 'All_Country-Uruguay': 68 }, + { ProductCategory: 'Accessories', All: 293, 'All_Country-USA': 293 }, + { ProductCategory: 'Components', All: 240, 'All_Country-USA': 240 } + ] + } + ]; } diff --git a/src/app/pivot-grid/pivot-grid.sample.ts b/src/app/pivot-grid/pivot-grid.sample.ts index 8d402ed1ccb..2d748ef254e 100644 --- a/src/app/pivot-grid/pivot-grid.sample.ts +++ b/src/app/pivot-grid/pivot-grid.sample.ts @@ -78,7 +78,7 @@ export class PivotGridSampleComponent { enabled: true }, { - months: false, + months: true, quarters: true } ), @@ -111,11 +111,16 @@ export class PivotGridSampleComponent { this.dimensions[1] ], rows: [ - this.dimensions[2], { memberName: 'City', enabled: true, }, + this.dimensions[2], + { + memberName: 'SellerName', + enabled: true, + //filter: this.filterExpTree + } ], values: [ { @@ -163,11 +168,7 @@ export class PivotGridSampleComponent { } ], filters: [ - { - memberName: 'SellerName', - enabled: true, - //filter: this.filterExpTree - } + ] };