Skip to content
This repository was archived by the owner on Feb 27, 2024. It is now read-only.

Commit 9f3589d

Browse files
committed
feat(datascrollercontext): add ability to use context instead of rowdata
1 parent 9ea3b09 commit 9f3589d

File tree

8 files changed

+310
-4
lines changed

8 files changed

+310
-4
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React from 'react';
2+
import { DataScrollerContextProvider, getRowData } from './DataScrollerContext';
3+
import { render, cleanup } from 'react-testing-library';
4+
5+
beforeEach(() => {
6+
cleanup();
7+
});
8+
9+
describe('getRowData', () => {
10+
it('get the data correctly', () => {
11+
const data = {
12+
foo: 'foo',
13+
bar: 'bar',
14+
bam: 'bam',
15+
};
16+
17+
const Child = (props: any) => {
18+
return (
19+
<div>
20+
{Object.entries(props.data).map(([key, value]) => (
21+
<div key={key}>{value}</div>
22+
))}
23+
</div>
24+
);
25+
};
26+
27+
const TestChild = getRowData()(Child);
28+
29+
const TestWrapper = () => (
30+
<DataScrollerContextProvider data={data}>
31+
<TestChild />
32+
</DataScrollerContextProvider>
33+
);
34+
35+
const { getByText } = render(<TestWrapper />);
36+
37+
expect(getByText('foo')).toBeTruthy();
38+
expect(getByText('bar')).toBeTruthy();
39+
expect(getByText('bam')).toBeTruthy();
40+
});
41+
it('gets only the requested correctly', () => {
42+
const data = {
43+
foo: 'foo',
44+
bar: 'bar',
45+
bam: 'bam',
46+
};
47+
48+
const Child = (props: any) => {
49+
return (
50+
<div>
51+
{Object.entries(props).map(([key, value]) => (
52+
<div key={key}>{value}</div>
53+
))}
54+
</div>
55+
);
56+
};
57+
58+
const TestChild = getRowData((_, data: any) => ({
59+
bar: data.bar,
60+
}))(Child);
61+
62+
const TestWrapper = () => (
63+
<DataScrollerContextProvider data={data}>
64+
<TestChild />
65+
</DataScrollerContextProvider>
66+
);
67+
68+
const { getByText } = render(<TestWrapper />);
69+
70+
expect(getByText('bar')).toBeTruthy();
71+
expect(() => getByText('foo')).toThrow();
72+
expect(() => getByText('bam')).toThrow();
73+
});
74+
75+
it('passes the props correctly', () => {
76+
const data = {
77+
bam: 'bam',
78+
};
79+
80+
const Child = (props: any) => {
81+
return (
82+
<div>
83+
{Object.entries(props).map(([key, value]) => (
84+
<div key={key}>{value}</div>
85+
))}
86+
</div>
87+
);
88+
};
89+
90+
const TestChild = getRowData((props: any, data: any) => {
91+
return {
92+
...props,
93+
bam: data.bam,
94+
};
95+
})(Child);
96+
97+
const TestWrapper = () => (
98+
<DataScrollerContextProvider data={data}>
99+
<TestChild foo="foo" bar="bar" />
100+
</DataScrollerContextProvider>
101+
);
102+
103+
const { getByText } = render(<TestWrapper />);
104+
105+
expect(getByText('foo')).toBeTruthy();
106+
expect(getByText('bar')).toBeTruthy();
107+
expect(getByText('bam')).toBeTruthy();
108+
});
109+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React, { useContext } from 'react';
2+
3+
type DataScrollerContextValue = {
4+
data: any;
5+
};
6+
7+
export const DataScrollerContext = React.createContext<
8+
DataScrollerContextValue
9+
>({
10+
data: {},
11+
});
12+
13+
type ComponentProps<Data = any> = {
14+
data: Data;
15+
children: React.ReactNode;
16+
};
17+
18+
export function DataScrollerContextProvider<Data>(props: ComponentProps<Data>) {
19+
return (
20+
<DataScrollerContext.Provider value={{ data: props.data }}>
21+
{props.children}
22+
</DataScrollerContext.Provider>
23+
);
24+
}
25+
26+
function defaultMapContextDataValueToProps<Props, Data>(
27+
props: Props,
28+
data: Data,
29+
): any {
30+
return { ...props, data };
31+
}
32+
33+
type MapContextValueToProps<OuterProps, Data, InnerProps> = (
34+
props: OuterProps,
35+
data: Data,
36+
) => InnerProps;
37+
38+
export function getRowData<OuterProps, Data, InnerProps>(
39+
mapContextValueToProps?: MapContextValueToProps<OuterProps, Data, InnerProps>,
40+
) {
41+
return function(Component: React.FC<InnerProps>) {
42+
const MemoizedComponent = (React.memo(Component) as any) as React.FC<
43+
InnerProps
44+
>;
45+
46+
return function<P extends OuterProps>(props: P) {
47+
const contextValue = useContext(DataScrollerContext);
48+
49+
const propsWithState = mapContextValueToProps
50+
? mapContextValueToProps(props, contextValue.data)
51+
: (defaultMapContextDataValueToProps<OuterProps, Data>(
52+
props,
53+
contextValue.data,
54+
) as InnerProps);
55+
56+
return <MemoizedComponent {...propsWithState} />;
57+
};
58+
};
59+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { DataScrollerContextProvider, getRowData } from './DataScrollerContext';

src/components/RowChildren/RowChildren.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const RowChildren = (props: RowChildrenProps) => {
1616
<CellRenderer
1717
columnIndex={adjustedColumnIndex}
1818
rowIndex={props.rowIndex}
19-
cellData={props.rowData[column.dataKey]}
19+
cellData={props.rowData ? props.rowData[column.dataKey] : {}}
2020
columnData={column.columnData}
2121
dataKey={column.dataKey}
2222
rowData={props.rowData}

src/components/Rows/Rows.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ function Rows({
5858
);
5959
}
6060

61-
export default Rows;
61+
export default React.memo(Rows);

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ export {
77
export { default as Group, Props as GroupProps } from './components/Group';
88
export { default as Column } from './components/Column';
99
export { default as Row } from './components/Row';
10+
export {
11+
DataScrollerContextProvider,
12+
getRowData,
13+
} from './components/DataScrollerContext';
1014

1115
/* Types */
1216
export * from './types';

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
22

3-
export type CellRendererArgs<ColumnData = any> = {
3+
export type CellRendererArgs<ColumnData = {}> = {
44
cellData: any;
5-
columnData?: ColumnData;
5+
columnData: ColumnData;
66
columnIndex: number;
77
dataKey: string;
88
rowData: any;

stories/index.stories.tsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { Column } from '../src/';
44
import Group from '../src/components/Group';
55
import Row from '../src/components/Row';
66
import { RowProps, ColumnProps, GetRowKey } from '../src/types';
7+
import {
8+
getRowData,
9+
DataScrollerContextProvider,
10+
} from '../src/components/DataScrollerContext/DataScrollerContext';
711

812
import { storiesOf } from '@storybook/react';
913
import DataScroller, {
@@ -284,3 +288,132 @@ storiesOf('react-data-scroller', module).add('custom rowRenderer', () => {
284288
/>
285289
);
286290
});
291+
292+
storiesOf('react-data-scroller', module).add('DataScrollerContext', () => {
293+
const DetachedIndexCell = ({ index }: { index: number }) => {
294+
return (
295+
<div
296+
style={{
297+
boxShadow: '0 0 5px 2px black',
298+
}}
299+
>
300+
{index}
301+
</div>
302+
);
303+
};
304+
305+
const DetachedFirstNameCell = ({ firstName }: { firstName: string }) => {
306+
return <CustomInput value={firstName} />;
307+
};
308+
309+
const DetachedLastNameCell = ({ lastName }: { lastName: string }) => {
310+
return <div>{lastName}</div>;
311+
};
312+
313+
const InjectedIndexCell = getRowData((props: any, data: any) => ({
314+
...props,
315+
index: data[props.rowIndex].index,
316+
}))(DetachedIndexCell);
317+
318+
const InjectedFirstNameCell = getRowData((props: any, data: any) => ({
319+
...props,
320+
firstName: data[props.rowIndex].firstName,
321+
}))(DetachedFirstNameCell);
322+
323+
const InjectedLastNameCell = getRowData((props: any, data: any) => ({
324+
...props,
325+
lastName: data[props.rowIndex].lastName,
326+
}))(DetachedLastNameCell);
327+
328+
const initialContextColumns = [
329+
{
330+
cellRenderer: InjectedIndexCell,
331+
columnData: {},
332+
dataKey: 'lastName',
333+
headerRenderer: ({ columnData }: HeaderRendererArgs) => (
334+
<div style={{ background: 'white' }}>
335+
Header {columnData.columnIndex}
336+
</div>
337+
),
338+
label: 'index',
339+
width: 200,
340+
},
341+
{
342+
cellRenderer: InjectedLastNameCell,
343+
columnData: {},
344+
dataKey: 'lastName',
345+
headerRenderer: ({ columnData }: HeaderRendererArgs) => (
346+
<div style={{ background: 'white' }}>
347+
Header {columnData.columnIndex}
348+
</div>
349+
),
350+
label: 'last name',
351+
width: 200,
352+
},
353+
{
354+
cellRenderer: InjectedFirstNameCell,
355+
columnData: {},
356+
dataKey: 'firstName',
357+
headerRenderer: ({ columnData }: HeaderRendererArgs) => (
358+
<div>Header{columnData.columnIndex}</div>
359+
),
360+
label: 'first name',
361+
width: 200,
362+
},
363+
];
364+
365+
const rowGetter = () => {};
366+
367+
let contextColumns: any[] = [];
368+
for (let counter = 0; counter < 10; counter += 1) {
369+
contextColumns = [...initialContextColumns, ...(contextColumns || [])];
370+
}
371+
372+
contextColumns = contextColumns.map((column, index) => ({
373+
...column,
374+
columnData: { ...(column.columnData || {}), columnIndex: index },
375+
}));
376+
377+
let frozenContextColumns: any[] = [];
378+
for (let counter = 0; counter < 2; counter += 1) {
379+
frozenContextColumns = [
380+
...initialContextColumns,
381+
...(frozenContextColumns || []),
382+
];
383+
}
384+
385+
frozenContextColumns = frozenContextColumns.map((column, index) => ({
386+
...column,
387+
columnData: { ...(column.columnData || {}), columnIndex: index },
388+
}));
389+
390+
return (
391+
<DataScrollerContextProvider data={rows}>
392+
<DataScroller
393+
rowCount={rowCount}
394+
rowGetter={rowGetter}
395+
rowHeight={50}
396+
height={500}
397+
headerHeight={100}
398+
width={500}
399+
initialTopRowIndex={0}
400+
groupHeaderHeight={30}
401+
columns={[
402+
<Group key="groupa" headerRenderer={GroupHeaderA}>
403+
{contextColumns.map((column, index) => (
404+
<Column key={index} {...column} />
405+
))}
406+
</Group>,
407+
<Group key="groupb" headerRenderer={GroupHeaderB}>
408+
{contextColumns.map((column, index) => (
409+
<Column key={index} {...column} />
410+
))}
411+
</Group>,
412+
]}
413+
frozenColumns={frozenContextColumns.map((column, index) => (
414+
<Column key={index} {...column} />
415+
))}
416+
/>
417+
</DataScrollerContextProvider>
418+
);
419+
});

0 commit comments

Comments
 (0)