diff --git a/docs/src/App.js b/docs/src/App.js
index 70e68b1..87a409c 100644
--- a/docs/src/App.js
+++ b/docs/src/App.js
@@ -66,6 +66,7 @@ const stories = [
{ name: 'Select Table (HOC)', component: CodeSandbox('7yq5ylw09j') },
{ name: 'Select Tree Table (HOC)', component: CodeSandbox('2p7jp4klwp') },
{ name: 'Foldable Table (HOC)', component: CodeSandbox('2p7jp4klwp') },
+ { name: 'Advanced Expand Table (HOC)', component: CodeSandbox('y2m39jz8v1') },
]
export default class App extends React.Component {
diff --git a/docs/src/examples/advancedexpandtable/index.js b/docs/src/examples/advancedexpandtable/index.js
new file mode 100644
index 0000000..b3d60da
--- /dev/null
+++ b/docs/src/examples/advancedexpandtable/index.js
@@ -0,0 +1,116 @@
+import React from 'react';
+import { render } from 'react-dom';
+import { makeData, Logo, Tips } from './Utils';
+import { advancedExpandTableHOC } from '../../../../lib/hoc/advancedExpandTable';
+
+// Import React Table
+import ReactTable from 'react-table';
+import 'react-table/react-table.css';
+
+const columns = [
+ {
+ Header: 'First Name',
+ accessor: 'firstName',
+ Cell: props => {
+ const {
+ // react table props
+ columnProps: { rest: { showRowSubComponent } },
+ nestingPath
+ } = props;
+ return (
+
+ showRowSubComponent({ nestingPath: nestingPath }, e)}
+ >
+ SHOW SUBCOMPONENT {props.value}
+
+
+ );
+ }
+ },
+ {
+ Header: 'Last Name',
+ id: 'lastName',
+ accessor: d => d.lastName
+ },
+ {
+ Header: 'Info',
+ columns: [
+ {
+ Header: 'Age',
+ accessor: 'age',
+ Cell: props => {
+ const {
+ // react table props
+ columnProps: { rest: { toggleRowSubComponent } },
+ nestingPath
+ } = props;
+ return (
+
+
+ toggleRowSubComponent({ nestingPath: nestingPath }, e)
+ }
+ >
+ TOGGLE SUBCOMPONENT {props.value}
+
+
+ );
+ }
+ },
+ {
+ Header: 'Status',
+ accessor: 'status'
+ }
+ ]
+ },
+ {
+ Header: 'Stats',
+ columns: [
+ {
+ Header: 'Visits',
+ accessor: 'visits'
+ }
+ ]
+ }
+];
+
+const AdvancedExpandReactTable = advancedExpandTableHOC(ReactTable);
+
+class App extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ data: makeData()
+ };
+ }
+ render() {
+ const { data } = this.state;
+ return (
+
+
{
+ return (
+
+ toggleRowSubComponent({ nestingPath }, e)}
+ >
+ CLOSE SUBCOMPONENT {row.firstName} {row.lastName}
+
+
+ );
+ }}
+ />
+
+
+
+
+ );
+ }
+}
+
+render( , document.getElementById('root'));
\ No newline at end of file
diff --git a/src/hoc/README.md b/src/hoc/README.md
index a68b6bc..0d01d39 100644
--- a/src/hoc/README.md
+++ b/src/hoc/README.md
@@ -205,6 +205,71 @@ const defaultFoldButtonComponent = ({ header, collapsed, icon, onClick }) => {
}
```
+### AdvancedExpandTable
+
+HOC which allows any Cell in the row to toggle the row's
+SubComponent (expand/collapse). Also allows the SubComponent to toggle itself. Technically supports toggling any row's SubComponent.
+
+Expand functions available to any SubComponent or Column Cell:
+ toggleRowSubComponent
+ showRowSubComponent
+ hideRowSubComponent
+
+They are available through the `props.columnProps.rest` object.
+
+On any change to the props, the table will reset the expanded state.
+
+Accepts a onExpandedChange callback to be called whenever the table expanded state changes
+
+Note: only supports 1 level of nesting.
+
+Example usage in a Column Cell Renderer:
+
+```javascript
+ Cell: props => {
+ const {
+ value
+ columnProps: { rest: { showRowSubComponent } },
+ nestingPath
+ } = props;
+ return (
+
+ showRowSubComponent(nestingPath, e)}
+ >
+ {value}
+
+
+ );
+ }
+```
+
+Example usage in the ReactTable SubComponent (toggle itself):
+
+```javascript
+ const AdvancedExpandReactTable = advancedExpandTableHOC(ReactTable);
+
+
+ ...
+ SubComponent={({ row, nestingPath, toggleRowSubComponent }) => {
+ return (
+
+ toggleRowSubComponent({ nestingPath }, e)}
+ >
+ {row.value}
+
+
+ );
+ }}
+ />
+```
+
+Each Column Renderer (E.g. Cell ) gets the expand functions in its props and each SubComponent gets the expand functions in its props
+
+Expand functions takes the `nestingPath` or `rowInfo` given to each
+Column Renderer and SubComponent already by ReactTable.
+
## HOC Guide for ReactTable
There are a few rules required when writing a HOC for ReactTable (other than meeting the normal lint standards - which are
diff --git a/src/hoc/advancedExpandTable/index.js b/src/hoc/advancedExpandTable/index.js
new file mode 100644
index 0000000..4162bd4
--- /dev/null
+++ b/src/hoc/advancedExpandTable/index.js
@@ -0,0 +1,199 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import set from 'lodash.set'
+import get from 'lodash.get'
+
+/*
+ AvancedExpandTableHOC for ReactTable
+
+ HOC which allows any Cell in the row to toggle the row's
+ SubComponent. Also allows the SubComponent to toggle itself.
+
+ Expand functions available to any SubComponent or Column Cell:
+ toggleRowSubComponent
+ showRowSubComponent
+ hideRowSubComponent
+
+ Each Column Renderer (E.g. Cell ) gets the expand functions in its props
+ And Each SubComponent gets the expand functions in its props
+
+ Expand functions takes the `rowInfo` given to each
+ Column Renderer and SubComponent already by ReactTable.
+*/
+
+export const subComponentWithToggle = (SubComponent, expandFuncs) => props => (
+
+)
+
+// each cell in the column gets passed the function to toggle a sub component
+export const columnsWithToggle = (columns, expandFuncs) =>
+ columns.map(column => {
+ if (column.columns) {
+ return {
+ ...column,
+ columns: columnsWithToggle(column.columns, expandFuncs),
+ }
+ }
+ return {
+ ...column,
+ getProps () {
+ return {
+ ...expandFuncs,
+ }
+ },
+ }
+ })
+
+export const advancedExpandTableHOC = TableComponent =>
+ class AdvancedExpandTable extends Component {
+ constructor (props) {
+ super(props)
+ this.state = {
+ expanded: {},
+ }
+ this.toggleRowSubComponent = this.toggleRowSubComponent.bind(this)
+ this.showRowSubComponent = this.showRowSubComponent.bind(this)
+ this.hideRowSubComponent = this.hideRowSubComponent.bind(this)
+ this.getTdProps = this.getTdProps.bind(this)
+ this.fireOnExpandedChange = this.fireOnExpandedChange.bind(this)
+ this.expandFuncs = {
+ toggleRowSubComponent: this.toggleRowSubComponent,
+ showRowSubComponent: this.showRowSubComponent,
+ hideRowSubComponent: this.hideRowSubComponent,
+ }
+ }
+
+ // after initial render if we get new
+ // data, columns, page changes, etc.
+ // we reset expanded state.
+ componentWillReceiveProps () {
+ this.setState({
+ expanded: {},
+ })
+ }
+
+ static propTypes = {
+ columns: PropTypes.array.isRequired,
+ SubComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element])
+ .isRequired,
+ onExpandedChange: PropTypes.func,
+ };
+
+ static defaultProps = {
+ onExpandedChange: null,
+ };
+
+ static DisplayName = 'AdvancedExpandTable';
+
+ // since we pass the expand functions to each Cell,
+ // we need to filter it out from being passed as an
+ // actual DOM attribute. See getProps in columnsWithToggle above.
+ static TdComponent ({
+ toggleRowSubComponent,
+ showRowSubComponent,
+ hideRowSubComponent,
+ ...rest
+ }) {
+ return
+ }
+
+ fireOnExpandedChange (rowInfo, e) {
+ // fire callback once state has changed.
+ if (this.props.onExpandedChange) {
+ this.props.onExpandedChange(rowInfo, e)
+ }
+ }
+
+ resolveNewTableState (rowInfoOrNestingPath, e, expandType) {
+ // derive nestingPath if only rowInfo is passed
+ let nestingPath = rowInfoOrNestingPath
+
+ if (rowInfoOrNestingPath.nestingPath) {
+ nestingPath = rowInfoOrNestingPath.nestingPath
+ }
+
+ this.setState(
+ prevState => {
+ const isExpanded = get(prevState.expanded, nestingPath)
+ // since we do not support nested rows, a shallow clone is okay.
+ const newExpanded = { ...prevState.expanded }
+
+ switch (expandType) {
+ case 'show':
+ set(newExpanded, nestingPath, {})
+ break
+ case 'hide':
+ set(newExpanded, nestingPath, false)
+ break
+ default:
+ // toggle
+ set(newExpanded, nestingPath, isExpanded ? false : {})
+ }
+ return {
+ ...prevState,
+ expanded: newExpanded,
+ }
+ },
+ () => this.fireOnExpandedChange(rowInfoOrNestingPath, e)
+ )
+ }
+
+ toggleRowSubComponent (rowInfo, e) {
+ this.resolveNewTableState(rowInfo, e)
+ }
+
+ showRowSubComponent (rowInfo, e) {
+ this.resolveNewTableState(rowInfo, e, 'show')
+ }
+
+ hideRowSubComponent (rowInfo, e) {
+ this.resolveNewTableState(rowInfo, e, 'hide')
+ }
+
+ getTdProps (tableState, rowInfo, column) {
+ const { expander } = column
+
+ if (!expander) {
+ // no overrides
+ return {}
+ }
+
+ return {
+ // only override onClick for column Td
+ onClick: e => {
+ this.toggleRowSubComponent(rowInfo, e)
+ },
+ }
+ }
+
+ getWrappedInstance () {
+ if (!this.wrappedInstance) { console.warn('AdvancedExpandTable - No wrapped instance') }
+ if (this.wrappedInstance.getWrappedInstance) {
+ return this.wrappedInstance.getWrappedInstance()
+ }
+ return this.wrappedInstance
+ }
+
+ render () {
+ const {
+ columns, SubComponent, onExpandedChange, ...rest
+ } = this.props
+
+ const wrappedColumns = columnsWithToggle(columns, this.expandFuncs)
+ const WrappedSubComponent = subComponentWithToggle(
+ SubComponent,
+ this.expandFuncs
+ )
+
+ return (
+
+ )
+ }
+ }