Skip to content

Instantly share code, notes, and snippets.

@brennancheung
Created January 16, 2019 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brennancheung/04282c1b63712683b5ced5430f70348b to your computer and use it in GitHub Desktop.
Save brennancheung/04282c1b63712683b5ced5430f70348b to your computer and use it in GitHub Desktop.
import React from 'react'
import DataLoader from 'core/DataLoader'
import CRUDListContainer from 'core/common/CRUDListContainer'
import requiresAuthentication from 'openstack/util/requiresAuthentication'
import ListTable from 'core/common/list_table/ListTable'
import createCRUDActions from 'core/helpers/createCRUDActions'
import { compose } from 'core/fp'
import { withAppContext } from 'core/AppContext'
import { withRouter } from 'react-router-dom'
import { withScopedPreferences } from 'core/PreferencesProvider'
/**
* This helper removes a lot of boilerplate from standard CRUD operations.
*
* We separate them out into the following components:
* - ListPage:
* Responsible for fetching the data. A render prop that receives ({ ListContainer })
* - ListContainer:
* Responsible for handling CRUD operations (add, delete, update).
* - List:
* Responsible for specifying the columns and rendering the list.
*
* The reason for separating them into 3 different components is so that they
* can be tested individually; mocks can be substituted for data loading and
* actions.
*
* Please see CRUDListContainer and ListTable for a better understanding what
* some of the `options` are.
*/
const createCRUDComponents = options => {
const defaults = {
columns: [],
rowActions: () => [],
uniqueIdentifier: 'id',
}
const {
actions,
baseUrl,
columns,
dataKey,
debug,
deleteFn,
loaderFn,
name,
rowActions,
title,
uniqueIdentifier,
} = { ...defaults, ...options }
const crudActions = actions ? createCRUDActions(actions) : null
// List
const List = withScopedPreferences(name)(({
onAdd, onDelete, onEdit, rowActions, data,
preferences: { visibleColumns, columnsOrder, rowsPerPage },
updatePreferences
}) => {
if (!data || data.length === 0) {
return <h1>No data found.</h1>
}
return (
<ListTable
title={title}
columns={columns}
data={data}
onAdd={onAdd}
onDelete={onDelete}
onEdit={onEdit}
rowActions={rowActions}
searchTarget="name"
uniqueIdentifier={uniqueIdentifier}
visibleColumns={visibleColumns}
columnsOrder={columnsOrder}
rowsPerPage={rowsPerPage}
onRowsPerPageChange={rowsPerPage => updatePreferences({ rowsPerPage })}
onColumnsChange={updatePreferences}
/>
)
})
List.displayName = `${name}List`
// ListContainer
class ContainerBase extends React.Component {
handleRemove = id => {
const { context, setContext } = this.props
return (deleteFn || crudActions.delete)({ id, context, setContext })
}
render () {
let moreProps = {}
if (rowActions && rowActions.length > 0) {
moreProps.rowActions = rowActions
}
return (
<CRUDListContainer
items={this.props.data}
addUrl={`${baseUrl}/add`}
editUrl={`${baseUrl}/edit`}
onRemove={this.handleRemove}
uniqueIdentifier={uniqueIdentifier}
>
{handlers => <List data={this.props.data} {...handlers} {...moreProps} />}
</CRUDListContainer>
)
}
}
const ListContainer = compose(
withAppContext,
withRouter,
)(ContainerBase)
ListContainer.displayName = `${name}ListContainer`
// ListPage
const StandardListPage = () => (
<DataLoader dataKey={dataKey} loaderFn={loaderFn || crudActions.list}>
{({ data }) =>
<React.Fragment>
<ListContainer data={data} />
{debug && <pre>{JSON.stringify(data, null, 4)}</pre>}
</React.Fragment>
}
</DataLoader>
)
const ListPage = requiresAuthentication(options.ListPage({ ListContainer }) || StandardListPage)
ListPage.displayName = `${name}ListPage`
return {
ListPage,
ListContainer,
List,
}
}
export default createCRUDComponents
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment