Skip to content

Commit 9720cb1

Browse files
committed
add API for isRowDisabled
1 parent 58bfe5e commit 9720cb1

File tree

9 files changed

+379
-3
lines changed

9 files changed

+379
-3
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
InfiniteTable,
3+
DataSource,
4+
DataSourceData,
5+
type InfiniteTablePropColumns,
6+
DataSourceApi,
7+
RowDisabledStateObject,
8+
} from '@infinite-table/infinite-react';
9+
10+
import * as React from 'react';
11+
import { useState } from 'react';
12+
13+
type Developer = {
14+
id: number;
15+
firstName: string;
16+
lastName: string;
17+
country: string;
18+
city: string;
19+
currency: string;
20+
21+
email: string;
22+
preferredLanguage: string;
23+
stack: string;
24+
canDesign: 'yes' | 'no';
25+
hobby: string;
26+
salary: number;
27+
age: number;
28+
};
29+
30+
const dataSource: DataSourceData<Developer> = ({}) => {
31+
return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers10-sql`)
32+
.then((r) => r.json())
33+
.then((data: Developer[]) => data);
34+
};
35+
36+
const columns: InfiniteTablePropColumns<Developer> = {
37+
preferredLanguage: { field: 'preferredLanguage' },
38+
id: { field: 'id' },
39+
country: { field: 'country' },
40+
salary: {
41+
field: 'salary',
42+
type: 'number',
43+
},
44+
age: { field: 'age' },
45+
canDesign: { field: 'canDesign' },
46+
firstName: { field: 'firstName' },
47+
stack: { field: 'stack' },
48+
49+
hobby: { field: 'hobby' },
50+
city: { field: 'city' },
51+
currency: { field: 'currency' },
52+
};
53+
54+
export default function KeyboardNavigationForRows() {
55+
const [activeRowIndex, setActiveRowIndex] = useState(0);
56+
57+
const [rowDisabledState, setRowDisabledState] =
58+
useState<RowDisabledStateObject>({
59+
enabledRows: true,
60+
disabledRows: [3, 5, 6],
61+
});
62+
63+
const [dataSourceApi, setDataSourceApi] =
64+
useState<DataSourceApi<Developer>>();
65+
66+
(globalThis as any).activeRowIndex = activeRowIndex;
67+
return (
68+
<>
69+
<div style={{ marginBottom: '10px' }}>
70+
<button onClick={() => dataSourceApi?.disableAllRows()}>
71+
Disable All Rows
72+
</button>
73+
<button
74+
onClick={() => dataSourceApi?.enableAllRows()}
75+
style={{ marginLeft: '10px' }}
76+
>
77+
Enable All Rows
78+
</button>
79+
<button
80+
onClick={() => {
81+
if (dataSourceApi) {
82+
const isRowDisabled = dataSourceApi.isRowDisabled(1);
83+
dataSourceApi.setRowEnabledAt(1, isRowDisabled);
84+
}
85+
}}
86+
style={{ marginLeft: '10px' }}
87+
>
88+
Toggle Row 1
89+
</button>
90+
</div>
91+
<DataSource<Developer>
92+
onReady={setDataSourceApi}
93+
primaryKey="id"
94+
data={dataSource}
95+
selectionMode="multi-row"
96+
rowDisabledState={rowDisabledState}
97+
onRowDisabledStateChange={(s) => setRowDisabledState(s.getState())}
98+
>
99+
<InfiniteTable<Developer>
100+
columns={columns}
101+
activeRowIndex={activeRowIndex}
102+
onActiveRowIndexChange={setActiveRowIndex}
103+
keyboardNavigation="row"
104+
domProps={{
105+
autoFocus: true,
106+
style: {
107+
height: 800,
108+
},
109+
}}
110+
/>
111+
</DataSource>
112+
</>
113+
);
114+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { test, expect } from '@testing';
2+
3+
export default test.describe('Disabled rows controlled', () => {
4+
test('should work fine', async ({ page, rowModel }) => {
5+
await page.waitForInfinite();
6+
7+
expect(await rowModel.isRowDisabled(0)).toBe(false);
8+
9+
await page.getByRole('button', { name: 'Disable All Rows' }).click();
10+
11+
expect(await rowModel.isRowDisabled(0)).toBe(true);
12+
expect(await rowModel.isRowDisabled(1)).toBe(true);
13+
expect(await rowModel.isRowDisabled(2)).toBe(true);
14+
expect(await rowModel.isRowDisabled(3)).toBe(true);
15+
16+
await page.getByRole('button', { name: 'Enable All Rows' }).click();
17+
18+
expect(await rowModel.isRowDisabled(0)).toBe(false);
19+
expect(await rowModel.isRowDisabled(1)).toBe(false);
20+
expect(await rowModel.isRowDisabled(2)).toBe(false);
21+
expect(await rowModel.isRowDisabled(3)).toBe(false);
22+
23+
const toggleButton = page.getByRole('button', { name: 'Toggle Row 1' });
24+
await toggleButton.click();
25+
expect(await rowModel.isRowDisabled(1)).toBe(true);
26+
27+
await toggleButton.click();
28+
expect(await rowModel.isRowDisabled(1)).toBe(false);
29+
});
30+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
InfiniteTable,
3+
DataSource,
4+
DataSourceData,
5+
type InfiniteTablePropColumns,
6+
} from '@infinite-table/infinite-react';
7+
8+
import * as React from 'react';
9+
import { useState } from 'react';
10+
11+
type Developer = {
12+
id: number;
13+
firstName: string;
14+
lastName: string;
15+
country: string;
16+
city: string;
17+
currency: string;
18+
19+
email: string;
20+
preferredLanguage: string;
21+
stack: string;
22+
canDesign: 'yes' | 'no';
23+
hobby: string;
24+
salary: number;
25+
age: number;
26+
};
27+
28+
const dataSource: DataSourceData<Developer> = ({}) => {
29+
return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers10-sql`)
30+
.then((r) => r.json())
31+
.then((data: Developer[]) => data);
32+
};
33+
34+
const columns: InfiniteTablePropColumns<Developer> = {
35+
preferredLanguage: { field: 'preferredLanguage' },
36+
id: { field: 'id' },
37+
country: { field: 'country' },
38+
salary: {
39+
field: 'salary',
40+
type: 'number',
41+
},
42+
age: { field: 'age' },
43+
canDesign: { field: 'canDesign' },
44+
firstName: { field: 'firstName' },
45+
stack: { field: 'stack' },
46+
47+
hobby: { field: 'hobby' },
48+
city: { field: 'city' },
49+
currency: { field: 'currency' },
50+
};
51+
52+
export default function KeyboardNavigationForRows() {
53+
const [activeRowIndex, setActiveRowIndex] = useState(0);
54+
55+
(globalThis as any).activeRowIndex = activeRowIndex;
56+
return (
57+
<DataSource<Developer>
58+
primaryKey="id"
59+
data={dataSource}
60+
selectionMode="multi-row"
61+
isRowDisabled={(rowInfo) =>
62+
rowInfo.indexInAll === 3 ||
63+
rowInfo.indexInAll === 5 ||
64+
rowInfo.indexInAll === 6
65+
}
66+
>
67+
<InfiniteTable<Developer>
68+
columns={columns}
69+
activeRowIndex={activeRowIndex}
70+
onActiveRowIndexChange={setActiveRowIndex}
71+
keyboardNavigation="row"
72+
domProps={{
73+
autoFocus: true,
74+
style: {
75+
height: 800,
76+
},
77+
}}
78+
/>
79+
</DataSource>
80+
);
81+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { test, expect } from '@testing';
2+
3+
export default test.describe('Disabled rows using isRowDisabled fn', () => {
4+
test('should work fine', async ({ page, rowModel }) => {
5+
await page.waitForInfinite();
6+
7+
expect(await rowModel.isRowDisabled(0)).toBe(false);
8+
expect(await rowModel.isRowDisabled(3)).toBe(true);
9+
expect(await rowModel.isRowDisabled(5)).toBe(true);
10+
expect(await rowModel.isRowDisabled(6)).toBe(true);
11+
12+
await rowModel.clickRow(2);
13+
expect(await page.evaluate(() => (globalThis as any).activeRowIndex)).toBe(
14+
2,
15+
);
16+
17+
// clicking a disabled row should not change the active row
18+
await rowModel.clickRow(3);
19+
expect(await page.evaluate(() => (globalThis as any).activeRowIndex)).toBe(
20+
2,
21+
);
22+
23+
// arrow down should skip over disabled row
24+
await page.keyboard.press('ArrowDown');
25+
expect(await page.evaluate(() => (globalThis as any).activeRowIndex)).toBe(
26+
4,
27+
);
28+
29+
// again, arrow down should skip over disabled rows
30+
await page.keyboard.press('ArrowDown');
31+
expect(await page.evaluate(() => (globalThis as any).activeRowIndex)).toBe(
32+
7,
33+
);
34+
35+
// arrow up should skip over disabled rows
36+
await page.keyboard.press('ArrowUp');
37+
expect(await page.evaluate(() => (globalThis as any).activeRowIndex)).toBe(
38+
4,
39+
);
40+
41+
// again, arrow up should skip over disabled row
42+
await page.keyboard.press('ArrowUp');
43+
expect(await page.evaluate(() => (globalThis as any).activeRowIndex)).toBe(
44+
2,
45+
);
46+
});
47+
});

source/src/components/DataSource/RowDisabledState.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ export class RowDisabledState<KeyType = any> extends BooleanCollectionState<
5656
return !this.isRowEnabled(key);
5757
}
5858

59-
public setRowEnabled(key: KeyType, shouldExpand: boolean) {
60-
return this.setItemValue(key, shouldExpand);
59+
public setRowEnabled(key: KeyType, enabled: boolean) {
60+
return this.setItemValue(key, enabled);
6161
}
6262

6363
public disableRow(key: KeyType) {

source/src/components/DataSource/getDataSourceApi.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { raf } from '../../utils/raf';
99
import { InfiniteTableRowInfo } from '../InfiniteTable/types';
1010
import { DataSourceCache } from './DataSourceCache';
1111
import { getRowInfoAt, getRowInfoArray } from './dataSourceGetters';
12-
12+
import { RowDisabledState } from './RowDisabledState';
1313
import { DataSourceInsertParam } from './types';
1414

1515
type GetDataSourceApiParam<T> = {
@@ -342,6 +342,86 @@ class DataSourceApiImpl<T> implements DataSourceApi<T> {
342342
this.actions.sortInfo = sortInfo;
343343
return;
344344
};
345+
346+
isRowDisabledAt = (rowIndex: number) => {
347+
const rowInfo = this.getRowInfoByIndex(rowIndex);
348+
349+
return rowInfo?.rowDisabled ?? false;
350+
};
351+
352+
isRowDisabled = (primaryKey: any) => {
353+
const rowInfo = this.getRowInfoByPrimaryKey(primaryKey);
354+
355+
return rowInfo?.rowDisabled ?? false;
356+
};
357+
358+
setRowEnabledAt = (rowIndex: number, enabled: boolean) => {
359+
const currentRowDisabledState = this.getState().rowDisabledState;
360+
361+
const rowDisabledState = currentRowDisabledState
362+
? new RowDisabledState<T>(currentRowDisabledState)
363+
: new RowDisabledState<T>({
364+
enabledRows: true,
365+
disabledRows: [],
366+
});
367+
368+
const rowInfo = this.getRowInfoByIndex(rowIndex);
369+
if (!rowInfo) {
370+
return;
371+
}
372+
rowDisabledState.setRowEnabled(rowInfo.id, enabled);
373+
374+
this.actions.rowDisabledState = rowDisabledState;
375+
};
376+
377+
setRowEnabled = (primaryKey: any, enabled: boolean) => {
378+
const rowInfo = this.getRowInfoByPrimaryKey(primaryKey);
379+
if (!rowInfo) {
380+
return;
381+
}
382+
this.setRowEnabledAt(rowInfo.indexInAll, enabled);
383+
};
384+
385+
enableAllRows = () => {
386+
const currentRowDisabledState = this.getState().rowDisabledState;
387+
388+
if (!currentRowDisabledState) {
389+
this.actions.rowDisabledState = new RowDisabledState<T>({
390+
enabledRows: true,
391+
disabledRows: [],
392+
});
393+
return;
394+
}
395+
396+
const rowDisabledState = new RowDisabledState(currentRowDisabledState);
397+
rowDisabledState.enableAll();
398+
this.actions.rowDisabledState = rowDisabledState;
399+
};
400+
disableAllRows = () => {
401+
const currentRowDisabledState = this.getState().rowDisabledState;
402+
403+
if (!currentRowDisabledState) {
404+
this.actions.rowDisabledState = new RowDisabledState<T>({
405+
disabledRows: true,
406+
enabledRows: [],
407+
});
408+
return;
409+
}
410+
411+
const rowDisabledState = new RowDisabledState(currentRowDisabledState);
412+
rowDisabledState.disableAll();
413+
this.actions.rowDisabledState = rowDisabledState;
414+
};
415+
416+
areAllRowsEnabled = () => {
417+
const rowDisabledState = this.getState().rowDisabledState;
418+
return rowDisabledState ? rowDisabledState.areAllEnabled() : true;
419+
};
420+
421+
areAllRowsDisabled = () => {
422+
const rowDisabledState = this.getState().rowDisabledState;
423+
return rowDisabledState ? rowDisabledState.areAllDisabled() : false;
424+
};
345425
}
346426

347427
export function getCacheAffectedParts<T>(state: DataSourceState<T>): {

source/src/components/DataSource/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { InfiniteTableRowInfo } from '../InfiniteTable';
2828
// import { DataSourceCmp } from './DataSourceCmp';
2929
import { useDataSourceInternal } from './privateHooks/useDataSource';
3030
import { DataSourceProps } from './types';
31+
import { RowDisabledState } from './RowDisabledState';
3132

3233
const {
3334
// ManagedComponentContextProvider: ManagedDataSourceContextProvider,
@@ -102,6 +103,7 @@ export {
102103
GroupRowsState,
103104
RowSelectionState,
104105
CellSelectionState,
106+
RowDisabledState,
105107
multisort,
106108
defaultFilterTypes as filterTypes,
107109
useRowInfoReducers,

0 commit comments

Comments
 (0)