Repository: jbetancur/react-data-table-component
Pull request #819 (2021-06-12 17:19:49)

refactor expandableComponent


Patch:
diff --git a/README.md b/README.md
index 54f6756..650162a 100644
--- a/README.md
+++ b/README.md
@@ -180,7 +180,7 @@ When the breakpoint is reached the column will be hidden. These are the built-in
 | selectableRowsNoSelectAll | bool | no | false | Whether to show the select all rows checkbox |
 | clearSelectedRows | bool | no | false | Toggling this property clears the selectedRows. If you use redux or react state you need to make sure that you pass a toggled value or the component will not update. See [Clearing Selected Rows](#clearing-selected-rows)|
 | onSelectedRowsChange | func | no |  | Callback that fires anytime the rows selected state changes. Returns ({ allSelected, selectedCount, selectedRows }).<br /><br />**Note** It's highly recommended that you memoize the callback that you pass to `onSelectedRowsChange` if it updates the state of your parent component. This prevents `DataTable` from unnecessary re-renders every time your parent component is re-rendered |
-| selectableRowsComponent | func | no |  | Override the default checkbox component - must be passed as a function (e.g. `Checkbox` not `<Checkbox />`). You can also find UI Library Integration examples [here](#ui-library-integration) |
+| selectableRowsComponent | component | no |  | Overrides the default checkbox component. Must be passed as: `Checkbox` not `<Checkbox />`. You can also find UI Library Integration examples [here](#ui-library-integration) |
 | selectableRowsComponentProps | object | no |  | Additional props you want to pass to `selectableRowsComponent`. See [Overriding with Ui Component Library](#overriding-with-ui-component-library) to learn how you can override indeterminate state |
 | selectableRowSelected | func | no |  | Select a row based on a property in your data. e.g. `row => row.isSelected`. `selectableRowSelected` must return a boolean to determine if the row should be programatically selected. Note that changing the state of selectableRowSelected will NOT re-render RDT, instead you should change your data if you want to update the items that are selected. |
 | selectableRowDisabled | func | no |  | Disable row select based on a property in your data. e.g. `row => row.isDisabled`. `selectableRowDisabled` must return a boolean to determine if the row should be programatically disabled.  |
@@ -193,7 +193,8 @@ When the breakpoint is reached the column will be hidden. These are the built-in
 | expandableIcon | object | no | default expander icons | you may pass in your own custom icons using the `expandableIcon: { collapsed: <svg>...</svg>, expanded: <svg>...</svg>` |
 | expandableRowExpanded | func | no |  | Expand a row based on a property in your data. e.g. `row => row.expandMe`. `expandableRowExpanded` must return a boolean to determine if the row should be programatically expanded. |
 | expandableRowDisabled | func | no |  | Disable a row expand based on a property in your data. e.g. `row => row.expandDisabled`. `expandableRowDisabled` must return a boolean to determine if the row should be programatically disabled. |
-| expandableRowsComponent | string or component | no |  | A custom component to display in the expanded row. It will have the `data` prop composed  so that you may access the row data |
+| expandableRowsComponent | component | no |  | A custom component to display in the expanded row. Must be passed as: `MyExpander` not `<MyExpander />`. Your component will have access to the row `data`. For example: `const MyExpander = props => <div>{props.data.name}</div>` |
+| expandableRowsComponentProps | object | no |  | Additional props you want to pass to your custom `expandableRowsComponent`. By default `expandableRowsComponent` will have a `data` prop that you may access the row data with. New props added via `expandableRowsComponentProps` wil be composed |
 | expandOnRowClicked | bool | no |  | The default behavior is to expand the row when the expander button is clicked. `expandOnRowClicked` allows expanding the row when an area within the row is clicked. Requires `expandableRows` be set to true. <br /><br />**Note:** that if you are using custom cells (`column[].cell`), you will need to apply the `data-tag="allowRowEvents"` to the innermost element or on the elements you want to propagate the `expandOnRowClicked` event to. |
 | expandOnRowDoubleClicked | bool | no |  | The default behavior is to expand the row when the expander button is clicked. `expandOnRowDoubleClicked` allows expanding the row when an area within the row is double clicked. Requires `expandableRows` be set to true. <br /><br />**Note:** that if you are using custom cells (`column[].cell`), you will need to apply the `data-tag="allowRowEvents"` to the innermost element or on the elements you want to propagate the `expandOnRowDoubleClicked` event to. t |
 | expandableRowsHideExpander | bool | no | false | Do not render the expander button. This is useful when using `expandOnRowClicked` or `expandOnRowDoubleClicked` |
diff --git a/src/DataTable/DataTable.tsx b/src/DataTable/DataTable.tsx
index 26409d6..db6fee1 100644
--- a/src/DataTable/DataTable.tsx
+++ b/src/DataTable/DataTable.tsx
@@ -98,6 +98,7 @@ function DataTable<T extends RowRecord>(props: TableProps<T>): JSX.Element {
 		sortFunction = defaultProps.sortFunction,
 		sortServer = defaultProps.sortServer,
 		expandableRowsComponent = defaultProps.expandableRowsComponent,
+		expandableRowsComponentProps = defaultProps.expandableRowsComponentProps,
 		expandableRowDisabled = defaultProps.expandableRowDisabled,
 		expandableRowsHideExpander = defaultProps.expandableRowsHideExpander,
 		expandOnRowClicked = defaultProps.expandOnRowClicked,
@@ -152,7 +153,6 @@ function DataTable<T extends RowRecord>(props: TableProps<T>): JSX.Element {
 	const Pagination = paginationComponent || NativePagination;
 
 	const currentTheme = React.useMemo(() => createStyles(customStyles, theme), [customStyles, theme]);
-	const expandableRowsComponentMemo = React.useMemo(() => expandableRowsComponent, [expandableRowsComponent]);
 	const wrapperProps = React.useMemo(() => ({ ...(direction !== 'auto' && { dir: direction }) }), [direction]);
 
 	const calculatedRows = React.useMemo(() => {
@@ -406,7 +406,8 @@ function DataTable<T extends RowRecord>(props: TableProps<T>): JSX.Element {
 											dense={dense}
 											expandOnRowClicked={expandOnRowClicked}
 											expandOnRowDoubleClicked={expandOnRowDoubleClicked}
-											expandableRowsComponent={expandableRowsComponentMemo}
+											expandableRowsComponent={expandableRowsComponent}
+											expandableRowsComponentProps={expandableRowsComponentProps}
 											expandableRowsHideExpander={expandableRowsHideExpander}
 											defaultExpanderDisabled={expanderDisabled}
 											defaultExpanded={expanderExpander}
diff --git a/src/DataTable/ExpanderRow.tsx b/src/DataTable/ExpanderRow.tsx
index 6626316..41407d1 100644
--- a/src/DataTable/ExpanderRow.tsx
+++ b/src/DataTable/ExpanderRow.tsx
@@ -1,11 +1,6 @@
 import * as React from 'react';
 import styled, { CSSObject } from 'styled-components';
-
-// Make "data" available on our any child component
-// eslint-disable-next-line arrow-body-style
-function renderChildren<T>(children: React.ReactElement, data: T) {
-	return React.Children.map(children, child => React.cloneElement(child, { data }));
-}
+import { ComponentProps, ExpandableRowsComponent } from './types';
 
 const ExpanderRowStyle = styled.div<{
 	extendedRowStyle: CSSObject;
@@ -18,16 +13,19 @@ const ExpanderRowStyle = styled.div<{
 
 type ExpanderRowProps<T> = {
 	data: T;
-	children: React.ReactElement;
+	component: ExpandableRowsComponent;
 	extendedRowStyle: CSSObject;
+	componentProps: ComponentProps;
 };
 
-function ExpanderRow<T>({ data, children, extendedRowStyle }: ExpanderRowProps<T>): JSX.Element {
+function ExpanderRow<T>({ data, component, componentProps, extendedRowStyle }: ExpanderRowProps<T>): JSX.Element {
+	const ExpandableComponent = component;
+
 	return (
 		<ExpanderRowStyle className="rdt_ExpanderRow" extendedRowStyle={extendedRowStyle}>
-			{renderChildren(children, data)}
+			<ExpandableComponent data={data} {...componentProps} />
 		</ExpanderRowStyle>
 	);
 }
 
-export default ExpanderRow;
+export default React.memo(ExpanderRow);
diff --git a/src/DataTable/TableRow.tsx b/src/DataTable/TableRow.tsx
index dd6c982..fe33a6d 100644
--- a/src/DataTable/TableRow.tsx
+++ b/src/DataTable/TableRow.tsx
@@ -5,7 +5,6 @@ import TableCellCheckbox from './TableCellCheckbox';
 import TableCellExpander from './TableCellExpander';
 import ExpanderRow from './ExpanderRow';
 import { getConditionalStyle, getProperty, isOdd, noop } from './util';
-import { defaultProps } from './defaultProps';
 import { STOP_PROP_TAG } from './constants';
 import { RowRecord, SingleRowAction, TableProps } from './types';
 
@@ -53,6 +52,7 @@ type DProps<T> = Pick<
 	| 'expandableIcon'
 	| 'expandableRows'
 	| 'expandableRowsComponent'
+	| 'expandableRowsComponentProps'
 	| 'expandableRowsHideExpander'
 	| 'expandOnRowClicked'
 	| 'expandOnRowDoubleClicked'
@@ -89,16 +89,17 @@ function TableRow<T extends RowRecord>({
 	defaultExpanded = false,
 	defaultExpanderDisabled = false,
 	dense = false,
-	expandableIcon = defaultProps.expandableIcon,
+	expandableIcon,
 	expandableRows = false,
-	expandableRowsComponent = defaultProps.expandableRowsComponent,
+	expandableRowsComponent,
+	expandableRowsComponentProps,
 	expandableRowsHideExpander,
 	expandOnRowClicked = false,
 	expandOnRowDoubleClicked = false,
 	highlightOnHover = false,
 	id,
 	expandableInheritConditionalStyles,
-	keyField = defaultProps.keyField,
+	keyField,
 	onRowClicked = noop,
 	onRowDoubleClicked = noop,
 	onRowExpandToggled = noop,
@@ -109,8 +110,8 @@ function TableRow<T extends RowRecord>({
 	rowIndex,
 	selectableRowDisabled = null,
 	selectableRows = false,
-	selectableRowsComponent = defaultProps.selectableRowsComponent,
-	selectableRowsComponentProps = defaultProps.selectableRowsComponentProps,
+	selectableRowsComponent,
+	selectableRowsComponentProps,
 	selectableRowsHighlight = false,
 	selected,
 	striped = false,
@@ -226,9 +227,13 @@ function TableRow<T extends RowRecord>({
 			</TableRowStyle>
 
 			{expandableRows && expanded && (
-				<ExpanderRow key={`expander--${row[keyField]}`} data={row} extendedRowStyle={inheritStyles}>
-					{expandableRowsComponent}
-				</ExpanderRow>
+				<ExpanderRow
+					key={`expander-${row[keyField]}`}
+					data={row}
+					extendedRowStyle={inheritStyles}
+					component={expandableRowsComponent}
+					componentProps={expandableRowsComponentProps}
+				/>
 			)}
 		</>
 	);
diff --git a/src/DataTable/defaultProps.tsx b/src/DataTable/defaultProps.tsx
index a593b84..d40b223 100644
--- a/src/DataTable/defaultProps.tsx
+++ b/src/DataTable/defaultProps.tsx
@@ -29,16 +29,22 @@ export const defaultProps = {
 	expandableRowsHideExpander: false,
 	expandOnRowDoubleClicked: false,
 	expandableInheritConditionalStyles: false,
-	progressPending: false,
-	progressComponent: <div style={{ fontSize: '24px', fontWeight: 700, padding: '24px' }}>Loading...</div>,
-	persistTableHead: false,
-	expandableRowsComponent: (
-		<div style={{ padding: '24px' }}>Add a custom expander component. Use props.data for row data</div>
-	),
+	expandableRowsComponent: function DefaultExpander(): JSX.Element {
+		return (
+			<div>
+				To add an expander pass in a component instance via <strong>expandableRowsComponent</strong>. You can then
+				access props.data from this component.
+			</div>
+		);
+	},
 	expandableIcon: {
 		collapsed: <ExpanderCollapsedIcon />,
 		expanded: <ExpanderExpandedIcon />,
 	},
+	expandableRowsComponentProps: {},
+	progressPending: false,
+	progressComponent: <div style={{ fontSize: '24px', fontWeight: 700, padding: '24px' }}>Loading...</div>,
+	persistTableHead: false,
 	sortIcon: null,
 	sortFunction: null,
 	sortServer: false,
diff --git a/src/DataTable/types.ts b/src/DataTable/types.ts
index 348c5be..1bb74a1 100644
--- a/src/DataTable/types.ts
+++ b/src/DataTable/types.ts
@@ -13,9 +13,13 @@ export type SortDirection = 'asc' | 'desc';
 export type SortFunction<T> = (rows: T[], field: Selector<T>, sortDirection: 'asc' | 'desc') => T[];
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export type RowRecord = Record<string, any>;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type ExpandableRowsComponent = React.ComponentType<any>;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type PaginationComponent = React.ComponentType<any>;
+export type ComponentProps = Record<string, unknown>;
 
 export type TableProps<T = RowRecord> = {
-	/** actions */
 	actions?: React.ReactNode | React.ReactNode[];
 	className?: string;
 	clearSelectedRows?: boolean;
@@ -36,7 +40,8 @@ export type TableProps<T = RowRecord> = {
 	expandableRowDisabled?: RowState<T>;
 	expandableRowExpanded?: RowState<T>;
 	expandableRows?: boolean;
-	expandableRowsComponent?: React.ReactNode;
+	expandableRowsComponent?: ExpandableRowsComponent;
+	expandableRowsComponentProps?: ComponentProps;
 	expandableRowsHideExpander?: boolean;
 	expandOnRowClicked?: boolean;
 	expandOnRowDoubleClicked?: boolean;
@@ -59,8 +64,7 @@ export type TableProps<T = RowRecord> = {
 	overflowY?: boolean;
 	overflowYOffset?: string;
 	pagination?: boolean;
-	// eslint-disable-next-line @typescript-eslint/no-explicit-any
-	paginationComponent?: React.ComponentType<any>;
+	paginationComponent?: PaginationComponent;
 	paginationComponentOptions?: PaginationOptions;
 	paginationDefaultPage?: number;
 	paginationIconFirstPage?: React.ReactNode;
@@ -81,12 +85,12 @@ export type TableProps<T = RowRecord> = {
 	selectableRowDisabled?: RowState<T>;
 	selectableRows?: boolean;
 	selectableRowsComponent?: 'input' | React.ReactNode;
-	selectableRowsComponentProps?: RowRecord;
+	selectableRowsComponentProps?: ComponentProps;
 	selectableRowSelected?: RowState<T>;
 	selectableRowsHighlight?: boolean;
 	selectableRowsNoSelectAll?: boolean;
 	selectableRowsVisibleOnly?: boolean;
-	sortFunction?: SortFunction<T>;
+	sortFunction?: SortFunction<T> | null;
 	sortIcon?: React.ReactNode;
 	sortServer?: boolean;
 	striped?: boolean;
diff --git a/stories/DataTable/Basic/Basic.stories.js b/stories/DataTable/Basic/Basic.stories.js
deleted file mode 100644
index fa9781d..0000000
--- a/stories/DataTable/Basic/Basic.stories.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import { storiesOf } from '@storybook/react';
-import data from '../constants/sampleMovieData';
-import DataTable from '../../../src/index';
-
-const columns = [
-	{
-		name: 'Title',
-		selector: 'title',
-		sortable: true,
-	},
-	{
-		name: 'Director',
-		selector: 'director',
-		sortable: true,
-	},
-	{
-		name: 'Year',
-		selector: 'year',
-		sortable: true,
-	},
-];
-
-const BasicTable = () => <DataTable title="Movie List" columns={columns} data={data} defaultSortField="title" />;
-
-storiesOf('General', module).add('Sortable', BasicTable);


Test patch:
diff --git a/src/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap
index fb49cd7..f1d82f6 100644
--- a/src/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap
+++ b/src/DataTable/__tests__/__snapshots__/DataTable.test.tsx.snap
@@ -14014,10 +14014,7 @@ exports[`DataTable::expandableRows should expand a row if expandableRows is true
         <div
           class="c17 rdt_ExpanderRow"
         >
-          <div
-            data="[object Object]"
-            style="padding: 24px;"
-          >
+          <div>
             Add a custom expander component. Use props.data for row data
           </div>
         </div>
@@ -14440,10 +14437,7 @@ exports[`DataTable::expandableRows should expand a row if expandableRows is true
         <div
           class="c17 rdt_ExpanderRow"
         >
-          <div
-            data="[object Object]"
-            style="padding: 24px;"
-          >
+          <div>
             Add a custom expander component. Use props.data for row data
           </div>
         </div>
@@ -15933,10 +15927,7 @@ exports[`DataTable::expandableRows should render correctly when expandableRowExp
         <div
           class="c17 rdt_ExpanderRow"
         >
-          <div
-            data="[object Object]"
-            style="padding: 24px;"
-          >
+          <div>
             Add a custom expander component. Use props.data for row data
           </div>
         </div>
@@ -16760,10 +16751,7 @@ exports[`DataTable::expandableRows should render correctly when expandableRows i
         <div
           class="c17 rdt_ExpanderRow"
         >
-          <div
-            data="[object Object]"
-            style="padding: 24px;"
-          >
+          <div>
             Add a custom expander component. Use props.data for row data
           </div>
         </div>
@@ -26760,10 +26748,7 @@ exports[`should render correctly when conditionalRowStyles is used with an expan
         <div
           class="c17 rdt_ExpanderRow"
         >
-          <div
-            data="[object Object]"
-            style="padding: 24px;"
-          >
+          <div>
             Add a custom expander component. Use props.data for row data
           </div>
         </div>