wix-style-react
Version:
wix-style-react
229 lines (210 loc) • 8.09 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import defaultTo from 'lodash/defaultTo';
import classNames from 'classnames';
import style from './Table.st.css';
import DataTable from '../DataTable';
import Checkbox from '../Checkbox';
import { TableContext } from './TableContext';
import { BulkSelection, BulkSelectionState } from './BulkSelection';
import Tooltip from '../Tooltip/Tooltip';
import {
TableToolbarContainer,
TableTitleBar,
TableContent,
TableEmptyState,
} from './components';
export function createColumns({ tableProps, bulkSelectionContext }) {
const createCheckboxColumn = ({
toggleAll,
bulkSelectionState,
toggleSelectionById,
isSelected,
}) => {
return {
title: (
<Checkbox
dataHook="table-select"
checked={bulkSelectionState === BulkSelectionState.ALL}
indeterminate={bulkSelectionState === BulkSelectionState.SOME}
onChange={() => toggleAll()}
/>
),
render: (row, rowNum) => {
const id = defaultTo(row.id, rowNum);
return (
<div onClick={e => e.stopPropagation()}>
<Checkbox
dataHook="row-select"
checked={isSelected(id)}
onChange={() => toggleSelectionById(id)}
/>
</div>
);
},
width: '12px',
};
};
return tableProps.showSelection
? [createCheckboxColumn(bulkSelectionContext), ...tableProps.columns]
: tableProps.columns;
}
export function getDataTableProps(tableProps) {
/* eslint-disable no-unused-vars */
const {
showSelection,
selectedIds,
onSelectionChanged,
dataHook,
newDesign,
hideHeader,
...props
} = tableProps;
return {
...props,
newDesign: true,
rowClass: classNames(tableProps.rowClass, style.tableRow),
};
}
/**
* Table is a composit component that allows adding SelectionColumn, Toolbar (on top of the TitleBar).
* It is a context provider, and thus the Table.Consumer, Table.TitleBar and Table.Content can be rendered separatly.
*/
export class Table extends React.Component {
static ToolbarContainer = TableToolbarContainer;
static Titlebar = TableTitleBar;
static Content = TableContent;
static EmptyState = TableEmptyState;
shouldComponentUpdate() {
// Table is not really a PureComponent
return true;
}
setSelectedIds(selectedIds) {
this.bulkSelection.setSelectedIds(selectedIds);
}
renderChildren() {
const children = this.props.children;
return this.props.withWrapper ? (
<div
data-hook={this.props.dataHook}
{...style(
'root',
{ isRowClickable: !!this.props.onRowClick },
this.props,
)}
>
{children}
</div>
) : (
children
);
}
render() {
return (
<TableContext.Provider value={this.props}>
{this.props.showSelection ? (
<BulkSelection
ref={_ref => (this.bulkSelection = _ref)}
selectedIds={this.props.selectedIds}
allIds={this.props.data.map((rowData, rowIndex) =>
defaultTo(rowData.id, rowIndex),
)}
onSelectionChanged={this.props.onSelectionChanged}
>
{this.renderChildren()}
</BulkSelection>
) : (
this.renderChildren()
)}
</TableContext.Provider>
);
}
}
Table.displayName = 'Table';
Table.defaultProps = {
...DataTable.defaultProps,
showSelection: false,
children: [<Table.Content key="content" />],
withWrapper: true,
showLastRowDivider: false,
};
Table.propTypes = {
children: PropTypes.any,
dataHook: PropTypes.string,
//DataTable Props
/** Allows to open multiple row details */
allowMultiDetailsExpansion: PropTypes.bool,
/** The data to display. (If data.id exists then it will be used as the React key value for each row, otherwise, the rowIndex will be used) */
data: PropTypes.array, // Not performing any shape validation to not hurt performance.
/** Configuration of the table's columns. See table below */
columns: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]).isRequired,
render: PropTypes.func.isRequired,
sortable: PropTypes.bool,
infoTooltipProps: PropTypes.shape(Tooltip.propTypes),
sortDescending: PropTypes.bool,
align: PropTypes.oneOf(['start', 'center', 'end']),
}),
).isRequired,
/** A func that gets row data and returns a class(es) to apply to that specific row */
dynamicRowClass: PropTypes.func,
/** Whether there are more items to be loaded. Event listeners are removed if false. */
hasMore: PropTypes.bool,
/** Should we hide the header of the table. */
hideHeader: PropTypes.bool,
/** An id to pass to the table */
id: PropTypes.string,
/** If true, table will not render all data to begin with, but will gradually render the data as the user scrolls */
infiniteScroll: PropTypes.bool,
/** If infiniteScroll is on, this prop will determine how many rows will be rendered on each load */
itemsPerPage: PropTypes.number,
/** The loader to show when loading more items. */
loader: PropTypes.node,
/** A callback when more items are requested by the user. */
loadMore: PropTypes.func,
/** A callback method to be called on row click. Signature: `onRowClick(rowData, rowNum)` */
onRowClick: PropTypes.func,
/** A callback method to be called on row mouse enter. Signature: `onMouseEnterRow(rowData, rowNum)` */
onMouseEnterRow: PropTypes.func,
/** A callback method to be called on row mouse leave. Signature: `onMouseLeaveRow(rowData, rowNum)` */
onMouseLeaveRow: PropTypes.func,
/** Add scroll listeners to the window, or else, the component's parentNode. */
useWindow: PropTypes.bool,
/** Add scroll listeners to specified DOM Object. */
scrollElement: PropTypes.object,
/** Table cell vertical padding. should be 'medium' or 'large' */
rowVerticalPadding: PropTypes.oneOf(['medium', 'large']),
/** Function that returns React component that will be rendered in row details section. Example: `rowDetails={(row, rowNum) => <MyRowDetailsComponent {...row} />}` */
rowDetails: PropTypes.func,
/** A string data-hook to apply to all table body rows. or a func which calculates the data-hook for each row - Signature: `(rowData, rowNum) => string` */
rowDataHook: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
/** A class to apply to all table body rows */
rowClass: PropTypes.string,
/** Should the table show the header when data is empty */
showHeaderWhenEmpty: PropTypes.bool,
// Table props
/** Called when row selection changes.
* Receives 2 arguments: `selectedIds` array, and a `change` object ( in this order).
* `selectedIds` is the updated selected ids.
* `change` object has a `type` property with the following possible values: 'ALL', 'NONE', 'SINGLE_TOGGLE'.
* In case of 'SINGLE_TOGGLE' the `change` object will also include an `id` prop with the item's id,
* and a `value` prop with the new boolean selection state of the item. */
onSelectionChanged: PropTypes.func,
/** Indicates wether to show a selection column (with checkboxes) */
showSelection: PropTypes.bool,
/** Array of selected row ids.
* Idealy, id should be a property on the data row object.
* If data objects do not have id property, then the data row's index would be used as an id. */
selectedIds: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string),
PropTypes.arrayOf(PropTypes.number),
]),
/** The width of the fixed table. Can be in percentages or pixels. */
width: PropTypes.string,
/**
* When false then Table would not create a `<div/>` wrapper around it's children.
* Useful when using `<Table/>` to wrap a `<Page/>` component, in that case we use the `<Table/>` only as a context provider and it doesn't render anything to the DOM by itself.*/
withWrapper: PropTypes.bool,
};
// export default Table;