Skip to content

Commit

Permalink
feat(Table): add fixed head, column width
Browse files Browse the repository at this point in the history
  • Loading branch information
ej9x committed Jan 16, 2019
1 parent 053bd2a commit b61b147
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 66 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion e2e/__tests__/table.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const SUITES = [
.setEnhancer(async (iframe) => {
await (await await iframe.waitForXPath('//*[contains(text(),"Id")]')).click();
await (await await iframe.waitForXPath('//*[contains(text(),"Id")]')).click();
await (await await iframe.waitForXPath('//*[contains(text(),"Email")]')).click();
await (await await iframe.waitForXPath('//*[contains(text(),"Created At")]')).click();
}),
baisy.suite('Components/Table', 'with selection')
.setEnhancer(async (iframe) => {
Expand Down
1 change: 0 additions & 1 deletion src/components/Table/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const name = 'tablePlate';

const tableTheme = createComponentTheme(name, {
root: {
overflow: 'auto',
},
modifiers: {
},
Expand Down
4 changes: 3 additions & 1 deletion src/components/Table/Table.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react';
const TABLE_COLUMNS = [{
name: 'id',
title: 'Id',
width: '300px',
width: '50px',
}, {
name: 'createdAt',
title: 'Created At',
Expand Down Expand Up @@ -424,11 +424,13 @@ export default (asStory) => {
action="Create Client" onActionClick={ () => alert('Create') }
onChange={ setTableState }
tableState={ tableState }
withMultipleSort
/>
) }
</TableState>
</div>
))

.add('with selection', () => (
<div style={{ display: 'flex', height: '600px' }}>
<TableState>
Expand Down
6 changes: 4 additions & 2 deletions src/components/Table/TableBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ const theme = createComponentTheme(name, ({ COLORS }: *): * => ({
}));

const TableBodyTag = createStyledTag(name, {
display: 'flex',
display: 'grid',
gridTemplateColumns: '1fr',
gridTemplateRows: '1fr',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%',
overflow: 'auto',
});

const TableBodyInnerTag = createStyledTag(`${name}Inner`, {
Expand Down
186 changes: 130 additions & 56 deletions src/components/Table/TableBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,57 +11,117 @@ import { TableBodyCell } from './TableBodyCell';
import { Table } from './Table';
import { Checkbox } from '../Checkbox';

const DEFAULT_SORT_ENABLE = true;

type Sort = {
name: string,
order: 'ASC' | 'DESC',
}

type ColumnType = {
title: string,
name: string,
sortEnable?: boolean,
width?: string,
}

type TableState = {
sort?: Array<{ name: string, order: 'ASC' | 'DESC' }>,
selection?: Array<string>
sort?: Sort[],
selectedIds?: Array<string>
}

type TableBulderProps = {
/** Options of the columns */
columns: Array<ColumnType>,
/** Data to show in table */
data: Array<Object>,
/** Callback to execute custom table action */
onActionClick?: () => void,
/** Text of the action or component to replace standart action button*/
action?: React$Node,

/** Callback to pass state of the table */
onChange: TableState => void,
/** States of the table sort and selections. */
tableState: TableState,
/** Options to enable row selections */
withSelection?: boolean,
onChange: TableState => void,
/** Options to enable multiple columns sorting */
withMultipleSort?: boolean,
/** Options to show loader */
loading?: boolean,
/** Calback to render cell */
renderCell?: (column: ColumnType, data: any) => React$Node,
/** Callback to render head cell */
renderHeadCell?: (column: ColumnType) => React$Node,
}

class TableBuilder extends Component<TableBulderProps> {

static defaultProps = {
columns: [],
data: [],
withMultipleSort: false,
withSelection: false,
tableState: {
sort: [],
selection: [],
selectedIds: [],
},
}

getGridColumns = () => {
const { columns, withSelection } = this.props;

return withSelection
? `80px repeat(${columns.length}, 1fr)`
: `repeat(${columns.length}, 1fr)`;
const gridColumns = [];

if (withSelection) {
gridColumns.push('80px');
}

for (let i = 0; i < columns.length; i++) {
gridColumns.push(columns[i].width || '1fr');
}

return gridColumns.join(' ');
}

getColumn = (columnName: string) => {
const { columns } = this.props;

return fp.find(
({ name }) => columnName === name,
columns,
);
}

getColumnSortEnable = (name: string) => {
const column = this.getColumn(name);

const sortEnable = fp.isNil(column.sortEnable)
? DEFAULT_SORT_ENABLE
: column.sortEnable;

return sortEnable;
}

onChange = (tableState: *) => {
const { onChange } = this.props;

onChange && onChange(tableState);
}

onSort = fp.memoize((name: string) => (order: 'ASC' | 'DESC') => {
const { onChange, tableState = {}} = this.props;
onSort = fp.memoize((name: string) => (orderDirection: 'ASC' | 'DESC') => {
const { tableState = {}, withMultipleSort } = this.props;
const previousSort = tableState.sort || [];

const sort = previousSort
.filter(({ name: columnName }) => columnName !== name)
.concat([{ name, order }]);
const currentSort = [{ name, order: orderDirection }];

const sort = withMultipleSort
? previousSort
.filter(({ name: columnName }) => columnName !== name)
.concat(currentSort)
: currentSort;

onChange({
this.onChange({
...tableState,
sort,
});
Expand All @@ -79,97 +139,111 @@ class TableBuilder extends Component<TableBulderProps> {
}


selectAllRows = () => {
const { onChange, tableState = {}, data } = this.props;
onSelectAllRows = () => {
const { tableState = {}, data } = this.props;
const isAllRowsSelected = this.hasAllRowsSelection();
const allIds = fp.map('id', data);

const selection = isAllRowsSelected
const selectedIds = isAllRowsSelected
? []
: allIds;

onChange({
this.onChange({
...tableState,
selection,
selectedIds,
});
}

selectRow = fp.memoize((id: string) => () => {
const { onChange, tableState = {}} = this.props;
const previousSelection = tableState.selection || [];
onSelectRow = fp.memoize((id: string) => () => {
const { tableState = {}} = this.props;
const previousSelectedIds = tableState.selectedIds || [];
const isRowSelected = this.hasRowSelection(id);

const selection = isRowSelected
? fp.xor(previousSelection, [id])
: fp.uniq([...previousSelection, id]);
const selectedIds = isRowSelected
? fp.xor(previousSelectedIds, [id])
: fp.uniq([...previousSelectedIds, id]);

onChange({
this.onChange({
...tableState,
selection,
selectedIds,
});
})

hasRowSelection = (id: string) => {
const { tableState: { selection } = {}} = this.props;
const { tableState: { selectedIds } = {}} = this.props;

return fp.findIndex(
fp.equals(id),
selection,
selectedIds,
) >= 0;
}

hasAllRowsSelection = () => {
const { tableState: { selection } = {}, data } = this.props;
const { tableState: { selectedIds } = {}, data } = this.props;
const allIds = fp.map('id', data);

return fp.isEmpty(fp.xor(selection, allIds));
return fp.isEmpty(fp.xor(selectedIds, allIds));
}


renderHeader = () => {
const { columns, withSelection } = this.props;
const { columns, withSelection, renderHeadCell } = this.props;

return (
<TableHeader columns={ this.getGridColumns() }>
<If condition={ !!withSelection }>
<TableHeaderCell justifyContent="center">
<Checkbox onChange={ this.selectAllRows } checked={ this.hasAllRowsSelection() } />
<Checkbox
onChange={ this.onSelectAllRows }
checked={ this.hasAllRowsSelection() }
/>
</TableHeaderCell>
</If>
{ columns.map(({ title, name }) => (
{ columns.map((column) => (
<TableHeaderCell
key={ name }
key={ column.name }
cursor="pointer"
onSort={ this.onSort(name) }
order={ this.getColumnOrder(name) }
enableSort={ this.getColumnSortEnable(column.name) }
onSort={ this.onSort(column.name) }
order={ this.getColumnOrder(column.name) }
>
{ title }
{ renderHeadCell ? renderHeadCell(column) : column.title }
</TableHeaderCell>
))
}
)) }
</TableHeader>
);
}

renderBody = () => {
const { columns, onActionClick, action, data, withSelection } = this.props;
const { columns, onActionClick, action, data, withSelection, renderCell, loading } = this.props;

return (
<TableBody data={ data } onActionClick={ onActionClick } action={ action } >
{
(rowData) => (
<TableBodyRow columns={ this.getGridColumns() } key={ rowData.id }>
<If condition={ !!withSelection }>
<TableBodyCell justifyContent="center">
<Checkbox onChange={ this.selectRow(rowData.id) } checked={ this.hasRowSelection(rowData.id) } />
</TableBodyCell>
</If>
{ columns.map(({ name }) => (
<TableBodyCell key={ name }>{ rowData[name] }</TableBodyCell>
)) }
</TableBodyRow>
)
}
<TableBody
data={ data }
onActionClick={ onActionClick }
action={ action }
loading={ loading }
>
{ (rowData) => (
<TableBodyRow
columns={ this.getGridColumns() }
key={ rowData.id }
>
<If condition={ !!withSelection }>
<TableBodyCell justifyContent="center">
<Checkbox
onChange={ this.onSelectRow(rowData.id) }
checked={ this.hasRowSelection(rowData.id) }
/>
</TableBodyCell>
</If>
{ columns.map((column) => (
<TableBodyCell key={ column.name }>
{ renderCell ? renderCell(column, rowData) : rowData[column.name] }
</TableBodyCell>
)) }
</TableBodyRow>
) }
</TableBody >
);
}
Expand Down
13 changes: 8 additions & 5 deletions src/components/Table/TableHeaderCell.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const DEFAULT_SORT = 'DESC';
type TableHeaderCellProps = {
children?: React$Node,
onSort?: ('ASC' | 'DESC') => void,
enableSort?: boolean,
order?: 'ASC' | 'DESC',
cursor?: 'pointer' | 'default' | 'inherit',
};
Expand Down Expand Up @@ -50,12 +51,14 @@ const IconTransform = styled('div')(props => {
class TableHeaderCell extends PureComponent<TableHeaderCellProps> {

onSort = () => {
const { onSort, order } = this.props;
const { onSort, order, enableSort } = this.props;

switch (order) {
case 'ASC': return onSort && onSort('DESC');
case 'DESC': return onSort && onSort('ASC');
default: return onSort && onSort(DEFAULT_SORT);
if (enableSort && onSort) {
switch (order) {
case 'ASC': return onSort && onSort('DESC');
case 'DESC': return onSort && onSort('ASC');
default: return onSort && onSort(DEFAULT_SORT);
}
}
}

Expand Down

0 comments on commit b61b147

Please sign in to comment.