Skip to content

Commit

Permalink
Merge pull request #164 from alexander-dryannov/feature/group-summary…
Browse files Browse the repository at this point in the history
…-render

Group Summary
  • Loading branch information
komarovalexander authored Jun 29, 2021
2 parents af8d0c5 + d0fb7ff commit 3344af0
Show file tree
Hide file tree
Showing 17 changed files with 410 additions and 15 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ka-table",
"version": "6.9.0",
"version": "6.10.0",
"license": "MIT",
"repository": "github:komarovalexander/ka-table",
"homepage": "https://komarovalexander.github.io/ka-table/#/overview",
Expand Down
2 changes: 2 additions & 0 deletions src/Demos/Demos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import GetDataByPropsDemo from './GetDataByPropsDemo/GetDataByPropsDemo';
import GroupingCustomCellDemo from './GroupingCustomCellDemo/GroupingCustomCellDemo';
import GroupingCustomRowDemo from './GroupingCustomRowDemo/GroupingCustomRowDemo';
import GroupingDemo from './GroupingDemo/GroupingDemo';
import GroupingSummaryDemo from './GroupingSummaryDemo/GroupingSummaryDemo';
import HoverRowDemo from './HoverRowDemo/HoverRowDemo';
import InfiniteScrollingDemo from './InfiniteScrollingDemo/InfiniteScrollingDemo';
import JsonDemo from './JsonDemo/JsonDemo';
Expand Down Expand Up @@ -105,6 +106,7 @@ const demos: Demo[] = [
new Demo(GroupingCustomCellDemo, '/grouping-custom-cell', 'Grouping Custom Cell', 'GroupingCustomCellDemo', 'https://stackblitz.com/edit/table-grouping-custom-cell-js', 'https://stackblitz.com/edit/table-grouping-custom-cell-ts', 'Grouping'),
new Demo(GroupingCustomRowDemo, '/grouping-custom-row', 'Grouping Custom Row', 'GroupingCustomRowDemo', 'https://stackblitz.com/edit/table-grouping-custom-row-js', 'https://stackblitz.com/edit/table-grouping-custom-row-ts', 'Grouping'),
new Demo(GroupingDemo, '/grouping', 'Grouping', 'GroupingDemo', 'https://stackblitz.com/edit/table-grouping-js', 'https://stackblitz.com/edit/table-grouping-ts', 'Grouping'),
new Demo(GroupingSummaryDemo, '/grouping-summary', 'Grouping Summary', 'GroupingSummaryDemo', 'https://stackblitz.com/edit/table-grouping-summary-js', 'https://stackblitz.com/edit/table-grouping-summary-ts', 'Grouping'),
new Demo(HoverRowDemo, '/hover-row', 'Hover Row', 'HoverRowDemo', 'https://stackblitz.com/edit/table-hover-row-js', 'https://stackblitz.com/edit/table-hover-row-ts', 'Rows'),
new Demo(JsonDemo, '/json', 'Json', 'JsonDemo', 'https://stackblitz.com/edit/table-json-js', 'https://stackblitz.com/edit/table-json-ts', 'Remote Data'),
new Demo(KeyboardNavigationDemo, '/keyboard-navigation', 'Keyboard Navigation', 'KeyboardNavigationDemo', 'https://stackblitz.com/edit/table-keyboard-navigation-js', 'https://stackblitz.com/edit/table-keyboard-navigation-ts', 'Miscellaneous'),
Expand Down
10 changes: 10 additions & 0 deletions src/Demos/GroupingSummaryDemo/GroupingSummaryDemo.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';

import GroupingSummaryDemo from './GroupingSummaryDemo';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<GroupingSummaryDemo />, div);
ReactDOM.unmountComponentAtNode(div);
});
51 changes: 51 additions & 0 deletions src/Demos/GroupingSummaryDemo/GroupingSummaryDemo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState } from 'react';

import { ITableProps, kaReducer, Table } from '../../lib';
import { DataType } from '../../lib/enums';
import { DispatchFunc } from '../../lib/types';

const dataArray = [
{ id: 1, type: 'Cat', name: 'Kas', country: 'Czech Republic', age: 2 },
{ id: 2, type: 'Dog', name: 'Rex', country: 'Montenegro', age: 6 },
{ id: 3, type: 'Cat', name: 'Simba', country: 'France', age: 12 },
{ id: 4, type: 'Dog', name: 'Beethoven', country: 'Czech Republic', age: 3 },
{ id: 5, type: 'Cat', name: 'Hash', country: 'Czech Republic', age: 8 },
];

const tablePropsInit: ITableProps = {
columns: [
{ key: 'type', title: 'TYPE', dataType: DataType.String },
{ key: 'name', title: 'NAME', dataType: DataType.String },
{ key: 'country', title: 'COUNTRY', dataType: DataType.String },
{ key: 'age', title: 'AGE', dataType: DataType.Number, style: { width: '50%' } },
],
data: dataArray,
groups: [{ columnKey: 'country', enableSummary: false }, { columnKey: 'type', enableSummary: true }],
rowKeyField: 'id',
};

const GroupingSummaryDemo: React.FC = () => {
const [tableProps, changeTableProps] = useState(tablePropsInit);
const dispatch: DispatchFunc = (action) => {
changeTableProps((prevState: ITableProps) => kaReducer(prevState, action));
};
return (
<Table
{...tableProps}
childComponents={{
groupSummaryCell: {
content: ({ groupData, column }) => {
switch (column.key) {
case 'age': return (
<b>Max age: {Math.max.apply(Math, groupData.map((o) => o.age))}</b>
);
}
}
}
}}
dispatch={dispatch}
/>
);
};

export default GroupingSummaryDemo;
4 changes: 2 additions & 2 deletions src/Demos/MenuItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export class MenuItem {
public isActive?: boolean;
}

const newItems: string[] = ['SummaryDemo'];
const updateItems: string[] = ['Miscellaneous'];
const newItems: string[] = ['SummaryDemo', 'GroupingSummaryDemo'];
const updateItems: string[] = ['Miscellaneous', 'Grouping'];

const MenuItems: React.FC<{ items: MenuItem[] }> = ({ items }) => {

Expand Down
21 changes: 21 additions & 0 deletions src/lib/Components/GroupSummaryCell/GroupSummaryCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';

import defaultOptions from '../../defaultOptions';
import { IGroupSummaryCellProps } from '../../props';
import { getElementCustomization } from '../../Utils/ComponentUtils';

export const GroupSummaryCell: React.FunctionComponent<IGroupSummaryCellProps> = (props) => {
const {
column: { style },
childComponents
} = props;
const { elementAttributes, content } = getElementCustomization({
className: defaultOptions.css.groupSummaryCell,
style
}, props, childComponents?.groupSummaryCell);
return (
<td {...elementAttributes}>
{content}
</td>
);
};
28 changes: 28 additions & 0 deletions src/lib/Components/GroupSummaryRow/GroupSummaryRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import defaultOptions from '../../defaultOptions';
import { IGroupSummaryRowProps } from '../../props';
import { getElementCustomization } from '../../Utils/ComponentUtils';
import EmptyCells from '../EmptyCells/EmptyCells';
import { GroupSummaryCell } from '../GroupSummaryCell/GroupSummaryCell';

export const GroupSummaryRow: React.FunctionComponent<IGroupSummaryRowProps> = (props) => {
const {
childComponents,
columns,
groupColumnsCount
} = props;

const { elementAttributes, content } = getElementCustomization({
className: defaultOptions.css.groupSummaryRow,
}, props, childComponents?.groupSummaryRow);
return (
<tr {...elementAttributes}>
{content || (
<>
<EmptyCells count={groupColumnsCount}/>
{columns.map((column) => <GroupSummaryCell key={column.key} {...props} column={column} />)}
</>
)}
</tr>
);
};
5 changes: 4 additions & 1 deletion src/lib/Components/Rows/Rows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import React, { RefObject, useEffect, useRef } from 'react';
import { ITableBodyProps } from '../../props';
import { getValueByField } from '../../Utils/DataUtils';
import { getRowEditableCells } from '../../Utils/FilterUtils';
import { getGroupMark, getGroupText } from '../../Utils/GroupUtils';
import { getGroupMark, getGroupText, groupSummaryMark } from '../../Utils/GroupUtils';
import DataAndDetailsRows from '../DataAndDetailsRows/DataAndDetailsRows';
import GroupRow from '../GroupRow/GroupRow';
import { GroupSummaryRow } from '../GroupSummaryRow/GroupSummaryRow';

export interface IRowsProps extends ITableBodyProps {
onFirstRowRendered: (firstRowRef: RefObject<HTMLElement>) => any;
Expand Down Expand Up @@ -57,6 +58,8 @@ const Rows: React.FunctionComponent<IRowsProps> = (props) => {
key={JSON.stringify(d.key)}
/>
);
} else if (d.groupSummaryMark === groupSummaryMark) {
return <GroupSummaryRow {...props} groupData={d.groupData} key={d.key} groupIndex={d.groupIndex} />;
} else {
const rowKeyValue = getValueByField(d, rowKeyField);
const isSelectedRow = selectedRows.some((s) => s === rowKeyValue);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/Components/SummaryCell/SummaryCell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const tableProps: any = {
};

it('renders without crashing', () => {
const div = document.createElement('table');
const div = document.createElement('tr');
ReactDOM.render(<SummaryCell {...tableProps} />, div);
ReactDOM.unmountComponentAtNode(div);
});
2 changes: 1 addition & 1 deletion src/lib/Components/SummaryRow/SummaryRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const tableProps: any = {
};

it('renders without crashing', () => {
const div = document.createElement('table');
const div = document.createElement('tbody');
ReactDOM.render(<SummaryRow {...tableProps} />, div);
ReactDOM.unmountComponentAtNode(div);
});
4 changes: 3 additions & 1 deletion src/lib/Models/ChildComponents.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ITableProps } from '../';
import {
ICellEditorProps, ICellProps, ICellTextProps, IDataRowProps, IFilterRowEditorProps,
IGroupRowProps, IHeadCellProps, IHeadCellResizeProps, IHeadRowProps, INoDataRowProps,
IGroupRowProps, IGroupSummaryCellProps, IGroupSummaryRowProps, IHeadCellProps, IHeadCellResizeProps, IHeadRowProps, INoDataRowProps,
IPagingIndexProps, IPagingPagesProps, IPagingProps, IPagingSizeProps, ISummaryCellProps,
ISummaryRowProps, ITableBodyProps, ITableFootProps, ITableHeadProps,
} from '../props';
Expand All @@ -17,6 +17,8 @@ export class ChildComponents {
public filterRowCell?: ChildComponent<IFilterRowEditorProps>;
public groupCell?: ChildComponent<IGroupRowProps>;
public groupRow?: ChildComponent<IGroupRowProps>;
public groupSummaryRow?: ChildComponent<IGroupSummaryRowProps>;
public groupSummaryCell?: ChildComponent<IGroupSummaryCellProps>;
public headCell?: ChildComponent<IHeadCellProps>;
public headCellContent?: ChildComponent<IHeadCellProps>;
public headCellResize?: ChildComponent<IHeadCellResizeProps>;
Expand Down
2 changes: 2 additions & 0 deletions src/lib/Models/CssClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ export class CssClasses {
public cellEditor: string = 'ka-cell-editor';
public cellText = 'ka-cell-text';
public groupCell = 'ka-group-cell';
public groupSummaryRow = 'ka-group-summary-row'
public detailsRow = 'ka-tr ka-details-row';
public groupRow = 'ka-tr ka-group-row';
public groupSummaryCell = 'ka-cell ka-group-summary-cell';
public kaCellEditorValidationError = 'ka-cell-editor-validation-error';
public row = 'ka-tr ka-row';
public rowSelected = 'ka-row-selected';
Expand Down
1 change: 1 addition & 0 deletions src/lib/Models/Group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

export class Group {
public columnKey!: string;
public enableSummary?: boolean;
}
46 changes: 45 additions & 1 deletion src/lib/Utils/GroupUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Group } from '../Models/Group';
import { GroupRowData } from '../Models/GroupRowData';
import {
convertToFlat, getExpandedGroups, getGroupedStructure, getGroupMark, getGroupText, groupBy,
updateExpandedGroups,
groupSummaryMark, updateExpandedGroups,
} from './GroupUtils';

describe('GroupUtils', () => {
Expand Down Expand Up @@ -38,6 +38,45 @@ describe('GroupUtils', () => {
const result = convertToFlat(mappedData);
expect(result).toMatchSnapshot();
});
it('inner group with summart', () => {
const mappedData = new Map([['France',
new Map([
['Cat', [{
country: 'France',
name: 'Simba',
type: 'Cat',
},
{
groupData: [
{
country: 'France',
name: 'Simba',
type: 'Cat',
}
],
groupIndex: 0,
groupSummaryMark,
key: [['France', 'Cat'], '--: + summary--\\'],
},
]],
[groupSummaryMark,
{
groupData: [
{
country: 'France',
name: 'Simba',
type: 'Cat',
}
],
groupIndex: 0,
groupSummaryMark,
key: [['France', 'Cat'], '--: + summary--\\'],
}]
]),
]]);
const result = convertToFlat(mappedData);
expect(result).toMatchSnapshot();
});

it('complex structure', () => {
const mappedData = new Map([['France',
Expand Down Expand Up @@ -149,6 +188,11 @@ describe('GroupUtils', () => {
const result = getGroupedStructure(data, groups, [], 0, [['Czech Republic', 'Cat'], ['Montenegro']]);
expect(result).toBeUndefined();
});
it('returns summary', () => {
const groupsWithSummary: Group[] = [{ columnKey: 'country', enableSummary: true }, { columnKey: 'type', enableSummary: true }];
const result = getGroupedStructure(data, groupsWithSummary, groupedColumns, 0);
expect(result).toMatchSnapshot();
});
});

describe('getGroupText', () => {
Expand Down
31 changes: 24 additions & 7 deletions src/lib/Utils/GroupUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { Group } from '../Models/Group';
import { FormatFunc } from '../types';
import { getValueByColumn } from './DataUtils';

const groupMark = {};
export const groupMark = {};
export const groupSummaryMark = {};

const getGroupSummary = (groupData: any[], key: any, groupIndex: any) => ({ groupData, groupSummaryMark, key: JSON.stringify([key, '--:+summary--']), groupIndex });

export const updateExpandedGroups = (groupsExpanded: any[][], groupKey: any[]): any[][] => {
const newGroupsExpanded =
groupsExpanded.filter((ge) => JSON.stringify(ge) !== JSON.stringify(groupKey));
Expand Down Expand Up @@ -32,10 +36,14 @@ export const getGroupedData = (
export const convertToFlat = (grouped: Map<any, any>, key: any[] = []) => {
let result: any[] = [];
grouped.forEach((value: any, groupValue: any) => {
const groupKey = [...key];
groupKey.push(groupValue);
result.push({ groupMark, key: groupKey, value: groupValue });
result = [...result, ...(Array.isArray(value) ? value : convertToFlat(value, groupKey))];
if (groupValue === groupSummaryMark){
result.push(value);
} else {
const groupKey = [...key];
groupKey.push(groupValue);
result.push({ groupMark, key: groupKey, value: groupValue });
result = [...result, ...(Array.isArray(value) ? value : convertToFlat(value, groupKey))];
}
});
return result;
};
Expand All @@ -46,27 +54,36 @@ export const getGroupedStructure = (
groupedColumns: Column[],
expandedDeep: number = 0,
groupsExpanded?: any[],
parentGroupKey: any[] = []
): Map<any, any> | void => {
groups = [...groups];
const group = groups.shift();
if (group) {
const column = groupedColumns && groupedColumns.find((g) => g.key === group.columnKey);
if (column) {
const grouped = groupBy(data, (item: any) => getValueByColumn(item, column));
grouped.forEach((value, key) => {
grouped.forEach((groupData, key) => {
const groupExpandedItems = groupsExpanded && groupsExpanded.filter((ge) => ge[expandedDeep] === key);
const isThisGroupExpanded = !groupExpandedItems
|| groupExpandedItems.some((ge) => ge.length === expandedDeep + 1);
if (isThisGroupExpanded) {
const fullKey = [...parentGroupKey, key];
const newStructure = getGroupedStructure(
value,
groupData,
groups,
groupedColumns,
expandedDeep + 1,
groupExpandedItems && groupExpandedItems.filter((ge) => ge.length > expandedDeep + 1),
fullKey
);

if (newStructure) {
if (group.enableSummary){
newStructure.set(groupSummaryMark, getGroupSummary(groupData, fullKey, expandedDeep));
}
grouped.set(key, newStructure);
} else if (group.enableSummary) {
groupData.push(getGroupSummary([...groupData], fullKey, expandedDeep));
}
} else {
grouped.set(key, []);
Expand Down
Loading

0 comments on commit 3344af0

Please sign in to comment.