Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react-grid): support custom grouping processing #395

Merged
merged 23 commits into from
Oct 24, 2017
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dx-grid-core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './plugins/grouping-state/reducers';
export * from './plugins/grouping-state/helpers';
export * from './plugins/grouping-state/computeds';
export * from './plugins/local-grouping/computeds';
export * from './plugins/custom-grouping/computeds';
export * from './plugins/grouping-panel/helpers';

export * from './plugins/paging-state/reducers';
Expand Down
54 changes: 54 additions & 0 deletions packages/dx-grid-core/src/plugins/custom-grouping/computeds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
GROUP_KEY_SEPARATOR,
} from '../grouping-state/constants';
import {
GRID_GROUP_TYPE,
GRID_GROUP_CHECK,
GRID_GROUP_LEVEL_KEY,
} from '../local-grouping/constants';

export const customGroupedRows = (
currentRows,
grouping,
getChildGroups,
rootRows = currentRows,
keyPrefix = '',
) => {
if (!currentRows) return [];
if (!grouping.length) return currentRows;

const groupedBy = grouping[0].columnName;
const nestedGrouping = grouping.slice(1);
return getChildGroups(currentRows, grouping[0], rootRows)
.reduce((acc, { key, value = key, childRows }) => {
const compoundKey = `${keyPrefix}${key}`;
acc.push({
[GRID_GROUP_CHECK]: true,
[GRID_GROUP_LEVEL_KEY]: `${GRID_GROUP_TYPE}_${groupedBy}`,
groupedBy,
compoundKey,
key,
value,
});
acc.push(...customGroupedRows(
childRows,
nestedGrouping,
getChildGroups,
rootRows,
`${compoundKey}${GROUP_KEY_SEPARATOR}`,
));
return acc;
}, []);
};

export const customGroupingRowIdGetter = (getRowId, rows) => {
const firstRow = rows.find(row => !row[GRID_GROUP_CHECK]);
if (!firstRow || getRowId(firstRow)) {
return getRowId;
}
const map = new Map(rows
.filter(row => !row[GRID_GROUP_CHECK])
.map((row, rowIndex) => [row, rowIndex]));

return row => map.get(row);
};
232 changes: 232 additions & 0 deletions packages/dx-grid-core/src/plugins/custom-grouping/computeds.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import {
customGroupedRows,
customGroupingRowIdGetter,
} from './computeds';
import {
GRID_GROUP_TYPE,
GRID_GROUP_CHECK,
GRID_GROUP_LEVEL_KEY,
} from '../local-grouping/constants';

describe('CustomGrouping Plugin computeds', () => {
const groupRow = ({ groupedBy, ...restParams }) => ({
...restParams,
groupedBy,
[GRID_GROUP_CHECK]: true,
[GRID_GROUP_LEVEL_KEY]: `${GRID_GROUP_TYPE}_${groupedBy}`,
});

describe('#customGroupedRows', () => {
it('should process hierarchical data by one column', () => {
const hierarchicalSource = [{
key: 1,
items: [
{ a: 1, b: 1 },
{ a: 1, b: 2 },
],
}, {
key: 2,
items: [
{ a: 2, b: 1 },
{ a: 2, b: 2 },
],
}];
const getHierarchicalChildGroups = groups => groups
.map(group => ({ key: String(group.key), value: group.key, childRows: group.items }));
const groupings = [{ columnName: 'a' }];
const groupedRows = [
groupRow({
groupedBy: 'a',
compoundKey: '1',
key: '1',
value: 1,
}),
{ a: 1, b: 1 },
{ a: 1, b: 2 },
groupRow({
groupedBy: 'a',
compoundKey: '2',
key: '2',
value: 2,
}),
{ a: 2, b: 1 },
{ a: 2, b: 2 },
];

const getChildGroups = jest.fn(getHierarchicalChildGroups);

expect(customGroupedRows(
hierarchicalSource,
groupings,
getChildGroups,
))
.toEqual(groupedRows);

expect(getChildGroups)
.toBeCalledWith(hierarchicalSource, groupings[0], hierarchicalSource);
});

it('should process hierarchical data by one column with remote expanded groups', () => {
const hierarchicalSource = [{
key: 1,
items: null,
}];
const getHierarchicalChildGroups = groups => groups
.map(group => ({ key: String(group.key), value: group.key, childRows: group.items }));
const groupings = [{ columnName: 'a' }];
const groupedRows = [
groupRow({
groupedBy: 'a',
compoundKey: '1',
key: '1',
value: 1,
}),
];

const getChildGroups = jest.fn(getHierarchicalChildGroups);

expect(customGroupedRows(
hierarchicalSource,
groupings,
getChildGroups,
))
.toEqual(groupedRows);
});

it('should process hierarchical data by several columns', () => {
const hierarchicalSource = [{
key: 1,
items: [{
key: 1,
items: [
{ a: 1, b: 1 },
],
}, {
key: 2,
items: [
{ a: 1, b: 2 },
],
}],
}, {
key: 2,
items: [{
key: 1,
items: [
{ a: 2, b: 1 },
],
}, {
key: 2,
items: [
{ a: 2, b: 2 },
],
}],
}];
const getHierarchicalChildGroups = groups => groups
.map(group => ({ key: String(group.key), value: group.key, childRows: group.items }));
const groupings = [{ columnName: 'a' }, { columnName: 'b' }];
const groupedRows = [
groupRow({
groupedBy: 'a',
compoundKey: '1',
key: '1',
value: 1,
}),
groupRow({
groupedBy: 'b',
compoundKey: '1|1',
key: '1',
value: 1,
}),
{ a: 1, b: 1 },
groupRow({
groupedBy: 'b',
compoundKey: '1|2',
key: '2',
value: 2,
}),
{ a: 1, b: 2 },
groupRow({
groupedBy: 'a',
compoundKey: '2',
key: '2',
value: 2,
}),
groupRow({
groupedBy: 'b',
compoundKey: '2|1',
key: '1',
value: 1,
}),
{ a: 2, b: 1 },
groupRow({
groupedBy: 'b',
compoundKey: '2|2',
key: '2',
value: 2,
}),
{ a: 2, b: 2 },
];

const getChildGroups = jest.fn(getHierarchicalChildGroups);

expect(customGroupedRows(
hierarchicalSource,
groupings,
getChildGroups,
))
.toEqual(groupedRows);

expect(getChildGroups)
.toBeCalledWith(hierarchicalSource, groupings[0], hierarchicalSource);
expect(getChildGroups)
.toBeCalledWith(hierarchicalSource[0].items, groupings[1], hierarchicalSource);
expect(getChildGroups)
.toBeCalledWith(hierarchicalSource[1].items, groupings[1], hierarchicalSource);
});
});

describe('#customGroupingRowIdGetter', () => {
it('should define row ids to rows if not present', () => {
const groupedRows = [
groupRow({
groupedBy: 'a',
key: '1',
value: 1,
}),
{ a: 1, b: 1 },
{ a: 1, b: 2 },
];
const parentGetRowId = () => undefined;
const getRowId = customGroupingRowIdGetter(parentGetRowId, groupedRows);

expect(getRowId(groupedRows[1]))
.toBe(0);
expect(getRowId(groupedRows[2]))
.toBe(1);
});

it('should not define row ids to empty rows', () => {
const parentGetRowId = () => undefined;
const getRowId = customGroupingRowIdGetter(parentGetRowId, [], []);

expect(getRowId(1))
.toBe(undefined);
});

it('should not define row ids if getRowId is defined', () => {
const groupedRows = [
groupRow({
groupedBy: 'a',
key: '1',
value: 1,
}),
{ a: 1, b: 1 },
];
const parentGetRowId = () => 1;
const getRowId = customGroupingRowIdGetter(parentGetRowId, groupedRows);

expect(getRowId(1))
.toBe(1);
});
});
});
67 changes: 43 additions & 24 deletions packages/dx-grid-core/src/plugins/local-grouping/computeds.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { GROUP_KEY_SEPARATOR } from '../grouping-state/constants';
import {
GROUP_KEY_SEPARATOR,
} from '../grouping-state/constants';
import {
GRID_GROUP_TYPE,
GRID_GROUP_CHECK,
Expand All @@ -19,6 +21,7 @@ export const groupedRows = (
grouping,
getCellValue,
getColumnIdentity,
keyPrefix = '',
) => {
if (!grouping.length) return rows;

Expand All @@ -38,35 +41,51 @@ export const groupedRows = (
return acc;
}, new Map());

const groupedBy = grouping[0].columnName;
const nestedGrouping = grouping.slice(1);
return [...groups.values()]
.map(([value, key, items]) => ({
value,
key,
items: groupedRows(items, nestedGrouping, getCellValue, getColumnIdentity),
}));
};

export const expandedGroupRows = (rows, grouping, expandedGroups, keyPrefix = '') => {
if (!grouping.length) return rows;

const nestedGrouping = grouping.slice(1);
return rows.reduce((acc, { value, key: groupKey, items }) => {
const groupedBy = grouping[0].columnName;
const key = `${keyPrefix}${groupKey}`;
const expanded = expandedGroups.has(key);
return [
...acc,
{
.reduce((acc, [value, key, items]) => {
const compoundKey = `${keyPrefix}${key}`;
acc.push({
[GRID_GROUP_CHECK]: true,
[GRID_GROUP_LEVEL_KEY]: `${GRID_GROUP_TYPE}_${groupedBy}`,
groupedBy,
compoundKey,
key,
value,
},
...expanded
? expandedGroupRows(items, nestedGrouping, expandedGroups, `${key}${GROUP_KEY_SEPARATOR}`)
: [],
];
});
acc.push(...groupedRows(
items,
nestedGrouping,
getCellValue,
getColumnIdentity,
`${compoundKey}${GROUP_KEY_SEPARATOR}`,
));
return acc;
}, []);
};

export const expandedGroupRows = (rows, grouping, expandedGroups) => {
if (!grouping.length) return rows;

const groupingColumnNames = grouping.map(columnGrouping => columnGrouping.columnName);
let currentGroupExpanded = true;
let currentGroupLevel = 0;
return rows.reduce((acc, row) => {
if (!row[GRID_GROUP_CHECK]) {
if (currentGroupExpanded) acc.push(row);
return acc;
}

const groupLevel = groupingColumnNames.indexOf(row.groupedBy);
if (groupLevel > currentGroupLevel && !currentGroupExpanded) {
return acc;
}

currentGroupExpanded = expandedGroups.has(row.compoundKey);
currentGroupLevel = groupLevel;

acc.push(row);
return acc;
}, []);
};
Loading