Skip to content

Commit f52c9c9

Browse files
authored
Enhance row grouping by adding Grouping Toolbar (#278)
1 parent e493271 commit f52c9c9

File tree

78 files changed

+4162
-140
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+4162
-140
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ jobs:
121121
runs-on: ubuntu-latest
122122
env:
123123
NEXT_PUBLIC_BASE_URL: https://infinite-table.com/.netlify/functions/json-server
124+
NEXT_PUBLIC_INFINITE_LICENSE_KEY: ${{ secrets.NEXT_PUBLIC_INFINITE_LICENSE_KEY }}
124125
strategy:
125126
matrix:
126127
node-version: [20.x]
@@ -165,11 +166,13 @@ jobs:
165166

166167
- name: Build test app
167168
run: npm run ci:test:build
169+
env:
170+
NEXT_PUBLIC_INFINITE_LICENSE_KEY: ${{ secrets.NEXT_PUBLIC_INFINITE_LICENSE_KEY }}
168171

169172
- name: Running Playwright Tests
170173
run: npm run ci:test:run
171174
env:
172-
NEXT_PUBLIC_ADAPTABLE_LICENSE_KEY: ${{ secrets.NEXT_PUBLIC_ADAPTABLE_LICENSE_KEY }}
175+
NEXT_PUBLIC_INFINITE_LICENSE_KEY: ${{ secrets.NEXT_PUBLIC_INFINITE_LICENSE_KEY }}
173176

174177
- name: Save browser binaries for Playwright
175178
id: playwright-save

.github/workflows/release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ jobs:
7575

7676
- name: Build test app
7777
run: npm run ci:test:build
78+
env:
79+
NEXT_PUBLIC_INFINITE_LICENSE_KEY: ${{ secrets.NEXT_PUBLIC_INFINITE_LICENSE_KEY }}
7880

7981
- name: Running Playwright Tests
8082
run: npm run ci:test:run

examples/src/globals.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
@custom-variant dark (&:is(.dark *));
66
@source '../src';
7+
@source '../../source/';
78

89
@theme inline {
910
--radius-sm: calc(var(--radius) - 4px);

examples/src/pages/tests/pageGeometry/polyContainsPoint.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,21 @@ export default test.describe.parallel('polyContainsPoint', () => {
3131

3232
expect(contained).toBe(true);
3333
});
34+
35+
test('strange poly should work', async ({}) => {
36+
const contained = polyContainsPoint(
37+
[
38+
{ top: 86, left: 1 },
39+
{ top: 86, left: 1 },
40+
{ top: 56, left: 1 },
41+
{ top: 56, left: 1 },
42+
],
43+
{
44+
left: 901,
45+
top: 71,
46+
},
47+
);
48+
49+
expect(contained).toBe(false);
50+
});
3451
});

examples/src/pages/tests/pageGeometry/triangle.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ import { test, expect } from '@playwright/test';
33
import { Triangle } from '@src/utils/pageGeometry/Triangle';
44

55
export default test.describe.parallel('Triangle', () => {
6+
test('strange triangle should work', async ({}) => {
7+
const t = new Triangle([
8+
{ top: 86, left: 1 },
9+
{ top: 86, left: 1 },
10+
{ top: 56, left: 1 },
11+
]);
12+
13+
expect(t.containsPoint({ top: 71, left: 901 })).toBe(false);
14+
expect(t.containsPoint({ top: 86, left: 1 })).toBe(true);
15+
expect(t.containsPoint({ top: 86, left: 2 })).toBe(false);
16+
});
617
test('contains point should be fine - test moving point horizontally', async ({}) => {
718
const t = new Triangle([
819
{
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { DragManager } from '@src/components/InfiniteTable/components/draggable/DragManager';
2+
import { test, expect } from '@playwright/test';
3+
import { Rectangle } from '@infinite-table/infinite-react/src/utils/pageGeometry/Rectangle';
4+
import { PointCoords } from '@infinite-table/infinite-react/src/utils/pageGeometry/Point';
5+
import { DragInteractionTarget } from '@infinite-table/infinite-react/src/components/InfiniteTable/components/draggable/DragInteractionTarget';
6+
7+
export default test.describe.parallel('DND', () => {
8+
test('should work for simple vertical list, no other interaction', () => {
9+
/**
10+
* list1
11+
*
12+
* +--- top: 0, left: 0 ----------+
13+
* | item1 |
14+
* +----bottom: 100, right: 50 ---+
15+
*
16+
* +--- top: 120, left: 0 --------+
17+
* | item2 |
18+
* +----bottom: 220, right: 50 ---+
19+
*
20+
* +--- top: 240, left: 0 --------+
21+
* | item3 |
22+
* +----bottom: 340, right: 50 ---+
23+
*/
24+
25+
const draggableItems = [
26+
{
27+
id: 'item1',
28+
rect: Rectangle.from({
29+
top: 0,
30+
left: 0,
31+
height: 100,
32+
width: 50,
33+
}).toDOMRect(),
34+
},
35+
{
36+
id: 'item2',
37+
rect: Rectangle.from({
38+
top: 120,
39+
left: 0,
40+
height: 100,
41+
width: 50,
42+
}).toDOMRect(),
43+
},
44+
{
45+
id: 'item3',
46+
rect: Rectangle.from({
47+
top: 240,
48+
left: 0,
49+
height: 100,
50+
width: 50,
51+
}).toDOMRect(),
52+
},
53+
];
54+
55+
const list1 = new DragInteractionTarget({
56+
orientation: 'vertical',
57+
listId: 'list1',
58+
initial: true,
59+
listRectangle: Rectangle.from({
60+
top: 0,
61+
left: 0,
62+
height: 360,
63+
width: 50,
64+
}),
65+
draggableItems,
66+
});
67+
68+
DragManager.registerDragInteractionTarget(list1);
69+
const dragOperation = DragManager.startDrag(
70+
{
71+
listId: 'list1',
72+
dragIndex: 0,
73+
dragItem: draggableItems[0],
74+
},
75+
{
76+
top: 10,
77+
left: 10,
78+
},
79+
);
80+
81+
let theOffsets: PointCoords[][] = [];
82+
list1.on('move', ({ offsetsForItems }) => {
83+
theOffsets.push(offsetsForItems);
84+
});
85+
86+
dragOperation.move({
87+
top: 30,
88+
left: 10,
89+
});
90+
91+
expect(theOffsets).toEqual([
92+
[
93+
{ left: 0, top: 20 },
94+
{ left: 0, top: 0 },
95+
{ left: 0, top: 0 },
96+
],
97+
]);
98+
99+
dragOperation.move({
100+
top: 100,
101+
left: 10,
102+
});
103+
104+
expect(theOffsets.length).toEqual(2);
105+
106+
expect(theOffsets[1]).toEqual([
107+
{ left: 0, top: 90 },
108+
{ left: 0, top: -100 },
109+
{ left: 0, top: 0 },
110+
]);
111+
112+
dragOperation.move({
113+
top: 101,
114+
left: 10,
115+
});
116+
dragOperation.move({
117+
top: 102,
118+
left: 10,
119+
});
120+
121+
expect(theOffsets[theOffsets.length - 1]).toEqual([
122+
{ left: 0, top: 92 },
123+
{ left: 0, top: -100 },
124+
{ left: 0, top: 0 },
125+
]);
126+
});
127+
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as React from 'react';
2+
3+
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
4+
5+
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
6+
7+
type Developer = {
8+
id: number;
9+
10+
firstName: string;
11+
lastName: string;
12+
country: string;
13+
city: string;
14+
currency: string;
15+
preferredLanguage: string;
16+
stack: string;
17+
canDesign: 'yes' | 'no';
18+
hobby: string;
19+
salary: number;
20+
age: number;
21+
};
22+
23+
const dataSource = () => {
24+
return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
25+
.then((r) => r.json())
26+
.then((data: Developer[]) => data);
27+
};
28+
29+
const columns: InfiniteTablePropColumns<Developer> = {
30+
id: { field: 'id' },
31+
32+
firstName: {
33+
field: 'firstName',
34+
columnGroup: 'info',
35+
},
36+
37+
preferredLanguage: { field: 'preferredLanguage', columnGroup: 'info' },
38+
stack: { field: 'stack', columnGroup: 'other' },
39+
country: { field: 'country', columnGroup: 'other' },
40+
};
41+
42+
const domProps = {
43+
style: {
44+
height: '80vh',
45+
margin: 10,
46+
},
47+
};
48+
49+
function Cmp() {
50+
return <InfiniteTable.Body />;
51+
}
52+
53+
export default function App() {
54+
const [colWidth, setColWidth] = React.useState(200);
55+
return (
56+
<>
57+
<button
58+
onClick={() => {
59+
setColWidth(colWidth === 200 ? 400 : colWidth === 400 ? 100 : 200);
60+
}}
61+
>
62+
toggle col width
63+
</button>
64+
<DataSource<Developer>
65+
primaryKey="id"
66+
data={dataSource}
67+
defaultGroupBy={[{ field: 'country' }, { field: 'stack' }]}
68+
selectionMode="multi-cell"
69+
>
70+
<InfiniteTable<Developer>
71+
domProps={domProps}
72+
columns={columns}
73+
columnGroups={{
74+
info: {
75+
header: 'info',
76+
},
77+
other: {
78+
header: 'other',
79+
},
80+
}}
81+
keyboardNavigation={'cell'}
82+
columnDefaultWidth={colWidth}
83+
>
84+
<InfiniteTable.GroupingToolbar />
85+
<InfiniteTable.Header />
86+
87+
<Cmp />
88+
89+
<InfiniteTable.Header />
90+
{/* <Cmp /> */}
91+
</InfiniteTable>
92+
</DataSource>
93+
</>
94+
);
95+
}

examples/src/pages/tests/table/props/column-groups/test-column-menus.page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ const columns: InfiniteTablePropColumns<Person> = {
5656
field: 'lastName',
5757

5858
columnGroup: 'hello',
59-
header: ({ insideColumnMenu }) => {
60-
if (insideColumnMenu) {
59+
header: ({ renderLocation }) => {
60+
if (renderLocation === 'column-menu') {
6161
return 'Last name in col';
6262
}
6363
return 'Last Name!!';

examples/src/pages/tests/table/props/column/column-context-menu.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export default test.describe.parallel('Column context menu', () => {
1515
'Pin to start',
1616
'Unpin',
1717
'Columns',
18+
'Group by',
1819
'Close',
1920
]);
2021
});
@@ -36,6 +37,7 @@ export default test.describe.parallel('Column context menu', () => {
3637
'Pin to start',
3738
'Unpin',
3839
'Columns',
40+
'Group by',
3941
'Close',
4042
]);
4143
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
2+
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
3+
import * as React from 'react';
4+
5+
type Developer = {
6+
id: number;
7+
firstName: string;
8+
lastName: string;
9+
country: string;
10+
city: string;
11+
currency: string;
12+
preferredLanguage: string;
13+
stack: string;
14+
canDesign: 'yes' | 'no';
15+
hobby: string;
16+
salary: number;
17+
age: number;
18+
};
19+
20+
const dataSource = () => {
21+
return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
22+
.then((r) => r.json())
23+
.then((data: Developer[]) => data);
24+
};
25+
26+
const columns: InfiniteTablePropColumns<Developer> = {
27+
id: { field: 'id', defaultWidth: 80 },
28+
stack: {
29+
field: 'stack',
30+
},
31+
firstName: {
32+
field: 'firstName',
33+
header: () => 'First Name',
34+
renderSortIcon: ({ renderBag }) => {
35+
return <span>hey {renderBag.sortIcon}</span>;
36+
},
37+
},
38+
preferredLanguage: { field: 'preferredLanguage' },
39+
};
40+
41+
export default function ColumnCustomRenderExample() {
42+
return (
43+
<>
44+
<DataSource<Developer>
45+
primaryKey="id"
46+
data={dataSource}
47+
defaultSortInfo={{
48+
field: 'firstName',
49+
dir: 1,
50+
}}
51+
>
52+
<InfiniteTable<Developer>
53+
domProps={{
54+
style: { height: '80vh' },
55+
}}
56+
columns={columns}
57+
columnDefaultWidth={200}
58+
/>
59+
</DataSource>
60+
</>
61+
);
62+
}

0 commit comments

Comments
 (0)