From 9f3589d17c12db46b18c112844198a081b897f24 Mon Sep 17 00:00:00 2001 From: benox3 Date: Tue, 3 Mar 2020 11:40:40 -0500 Subject: [PATCH] feat(datascrollercontext): add ability to use context instead of rowdata --- .../DataScrollerContext.test.tsx | 109 ++++++++++++++ .../DataScrollerContext.tsx | 59 ++++++++ src/components/DataScrollerContext/index.ts | 1 + src/components/RowChildren/RowChildren.tsx | 2 +- src/components/Rows/Rows.tsx | 2 +- src/index.ts | 4 + src/types.ts | 4 +- stories/index.stories.tsx | 133 ++++++++++++++++++ 8 files changed, 310 insertions(+), 4 deletions(-) create mode 100644 src/components/DataScrollerContext/DataScrollerContext.test.tsx create mode 100644 src/components/DataScrollerContext/DataScrollerContext.tsx create mode 100644 src/components/DataScrollerContext/index.ts diff --git a/src/components/DataScrollerContext/DataScrollerContext.test.tsx b/src/components/DataScrollerContext/DataScrollerContext.test.tsx new file mode 100644 index 00000000..d6e1a71b --- /dev/null +++ b/src/components/DataScrollerContext/DataScrollerContext.test.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { DataScrollerContextProvider, getRowData } from './DataScrollerContext'; +import { render, cleanup } from 'react-testing-library'; + +beforeEach(() => { + cleanup(); +}); + +describe('getRowData', () => { + it('get the data correctly', () => { + const data = { + foo: 'foo', + bar: 'bar', + bam: 'bam', + }; + + const Child = (props: any) => { + return ( +
+ {Object.entries(props.data).map(([key, value]) => ( +
{value}
+ ))} +
+ ); + }; + + const TestChild = getRowData()(Child); + + const TestWrapper = () => ( + + + + ); + + const { getByText } = render(); + + expect(getByText('foo')).toBeTruthy(); + expect(getByText('bar')).toBeTruthy(); + expect(getByText('bam')).toBeTruthy(); + }); + it('gets only the requested correctly', () => { + const data = { + foo: 'foo', + bar: 'bar', + bam: 'bam', + }; + + const Child = (props: any) => { + return ( +
+ {Object.entries(props).map(([key, value]) => ( +
{value}
+ ))} +
+ ); + }; + + const TestChild = getRowData((_, data: any) => ({ + bar: data.bar, + }))(Child); + + const TestWrapper = () => ( + + + + ); + + const { getByText } = render(); + + expect(getByText('bar')).toBeTruthy(); + expect(() => getByText('foo')).toThrow(); + expect(() => getByText('bam')).toThrow(); + }); + + it('passes the props correctly', () => { + const data = { + bam: 'bam', + }; + + const Child = (props: any) => { + return ( +
+ {Object.entries(props).map(([key, value]) => ( +
{value}
+ ))} +
+ ); + }; + + const TestChild = getRowData((props: any, data: any) => { + return { + ...props, + bam: data.bam, + }; + })(Child); + + const TestWrapper = () => ( + + + + ); + + const { getByText } = render(); + + expect(getByText('foo')).toBeTruthy(); + expect(getByText('bar')).toBeTruthy(); + expect(getByText('bam')).toBeTruthy(); + }); +}); diff --git a/src/components/DataScrollerContext/DataScrollerContext.tsx b/src/components/DataScrollerContext/DataScrollerContext.tsx new file mode 100644 index 00000000..418ed224 --- /dev/null +++ b/src/components/DataScrollerContext/DataScrollerContext.tsx @@ -0,0 +1,59 @@ +import React, { useContext } from 'react'; + +type DataScrollerContextValue = { + data: any; +}; + +export const DataScrollerContext = React.createContext< + DataScrollerContextValue +>({ + data: {}, +}); + +type ComponentProps = { + data: Data; + children: React.ReactNode; +}; + +export function DataScrollerContextProvider(props: ComponentProps) { + return ( + + {props.children} + + ); +} + +function defaultMapContextDataValueToProps( + props: Props, + data: Data, +): any { + return { ...props, data }; +} + +type MapContextValueToProps = ( + props: OuterProps, + data: Data, +) => InnerProps; + +export function getRowData( + mapContextValueToProps?: MapContextValueToProps, +) { + return function(Component: React.FC) { + const MemoizedComponent = (React.memo(Component) as any) as React.FC< + InnerProps + >; + + return function

(props: P) { + const contextValue = useContext(DataScrollerContext); + + const propsWithState = mapContextValueToProps + ? mapContextValueToProps(props, contextValue.data) + : (defaultMapContextDataValueToProps( + props, + contextValue.data, + ) as InnerProps); + + return ; + }; + }; +} diff --git a/src/components/DataScrollerContext/index.ts b/src/components/DataScrollerContext/index.ts new file mode 100644 index 00000000..14ea6749 --- /dev/null +++ b/src/components/DataScrollerContext/index.ts @@ -0,0 +1 @@ +export { DataScrollerContextProvider, getRowData } from './DataScrollerContext'; diff --git a/src/components/RowChildren/RowChildren.tsx b/src/components/RowChildren/RowChildren.tsx index b4b3a6da..e3a74bc7 100644 --- a/src/components/RowChildren/RowChildren.tsx +++ b/src/components/RowChildren/RowChildren.tsx @@ -16,7 +16,7 @@ const RowChildren = (props: RowChildrenProps) => { = { +export type CellRendererArgs = { cellData: any; - columnData?: ColumnData; + columnData: ColumnData; columnIndex: number; dataKey: string; rowData: any; diff --git a/stories/index.stories.tsx b/stories/index.stories.tsx index d58e649d..4bfe88c3 100644 --- a/stories/index.stories.tsx +++ b/stories/index.stories.tsx @@ -4,6 +4,10 @@ import { Column } from '../src/'; import Group from '../src/components/Group'; import Row from '../src/components/Row'; import { RowProps, ColumnProps, GetRowKey } from '../src/types'; +import { + getRowData, + DataScrollerContextProvider, +} from '../src/components/DataScrollerContext/DataScrollerContext'; import { storiesOf } from '@storybook/react'; import DataScroller, { @@ -284,3 +288,132 @@ storiesOf('react-data-scroller', module).add('custom rowRenderer', () => { /> ); }); + +storiesOf('react-data-scroller', module).add('DataScrollerContext', () => { + const DetachedIndexCell = ({ index }: { index: number }) => { + return ( +

+ {index} +
+ ); + }; + + const DetachedFirstNameCell = ({ firstName }: { firstName: string }) => { + return ; + }; + + const DetachedLastNameCell = ({ lastName }: { lastName: string }) => { + return
{lastName}
; + }; + + const InjectedIndexCell = getRowData((props: any, data: any) => ({ + ...props, + index: data[props.rowIndex].index, + }))(DetachedIndexCell); + + const InjectedFirstNameCell = getRowData((props: any, data: any) => ({ + ...props, + firstName: data[props.rowIndex].firstName, + }))(DetachedFirstNameCell); + + const InjectedLastNameCell = getRowData((props: any, data: any) => ({ + ...props, + lastName: data[props.rowIndex].lastName, + }))(DetachedLastNameCell); + + const initialContextColumns = [ + { + cellRenderer: InjectedIndexCell, + columnData: {}, + dataKey: 'lastName', + headerRenderer: ({ columnData }: HeaderRendererArgs) => ( +
+ Header {columnData.columnIndex} +
+ ), + label: 'index', + width: 200, + }, + { + cellRenderer: InjectedLastNameCell, + columnData: {}, + dataKey: 'lastName', + headerRenderer: ({ columnData }: HeaderRendererArgs) => ( +
+ Header {columnData.columnIndex} +
+ ), + label: 'last name', + width: 200, + }, + { + cellRenderer: InjectedFirstNameCell, + columnData: {}, + dataKey: 'firstName', + headerRenderer: ({ columnData }: HeaderRendererArgs) => ( +
Header{columnData.columnIndex}
+ ), + label: 'first name', + width: 200, + }, + ]; + + const rowGetter = () => {}; + + let contextColumns: any[] = []; + for (let counter = 0; counter < 10; counter += 1) { + contextColumns = [...initialContextColumns, ...(contextColumns || [])]; + } + + contextColumns = contextColumns.map((column, index) => ({ + ...column, + columnData: { ...(column.columnData || {}), columnIndex: index }, + })); + + let frozenContextColumns: any[] = []; + for (let counter = 0; counter < 2; counter += 1) { + frozenContextColumns = [ + ...initialContextColumns, + ...(frozenContextColumns || []), + ]; + } + + frozenContextColumns = frozenContextColumns.map((column, index) => ({ + ...column, + columnData: { ...(column.columnData || {}), columnIndex: index }, + })); + + return ( + + + {contextColumns.map((column, index) => ( + + ))} + , + + {contextColumns.map((column, index) => ( + + ))} + , + ]} + frozenColumns={frozenContextColumns.map((column, index) => ( + + ))} + /> + + ); +});