Skip to content

Commit

Permalink
feat(VirtualTableGrid): Add a table grid styled component using react…
Browse files Browse the repository at this point in the history
…-virtualized (#818)

VirtualTableGrid uses react-virtualized to efficiently render large lists of data.

resolves #773
  • Loading branch information
jeff-phillips-18 authored and dtaylor113 committed Oct 29, 2018
1 parent 5c9a52f commit f8cc152
Show file tree
Hide file tree
Showing 11 changed files with 3,504 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '~react-virtualized/styles.css';

.table-grid-pf {
.row,
[class*='col-'] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"classnames": "^2.2.5",
"css-element-queries": "^1.0.1",
"patternfly": "^3.52.1",
"patternfly-react": "^2.22.0"
"patternfly-react": "^2.22.0",
"react-virtualized": "9.x"
},
"peerDependencies": {
"prop-types": "^15.6.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '~react-virtualized/styles.css';

.table-grid-pf {
.row,
[class*='col-'] {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { AutoSizer, List as VirtualList, WindowScroller, CellMeasurerCache, CellMeasurer } from 'react-virtualized';

import TableGridHead from './TableGridHead';
import TableGridColumnHeader from './TableGridColumnHeader';
import TableGridRow from './TableGridRow';
import TableGridCol from './TableGridCol';
import TableGrid from './TableGrid';

/**
* VirtualTableGrid Component for PatternFly
*/

const VirtualTableGrid = ({
className,
bordered,
selectType,
scrollable,
scrollableElementId,
header,
row,
bodyClassName,
empty,
emptyComponent,
measurementCache,
data,
tableData,
...props
}) => {
const cellMeasurementCache =
measurementCache ||
new CellMeasurerCache({
fixedWidth: true,
minHeight: 44
});

const rowRenderer = rowProps => {
const { index, style, key, parent } = rowProps;
const obj = data[index];

return (
<CellMeasurer cache={cellMeasurementCache} columnIndex={0} key={key} rowIndex={index} parent={parent}>
<div style={style}>{row({ obj, tableData, index })}</div>
</CellMeasurer>
);
};

const classes = classNames(
{
'table-grid-pf': true,
'container-fluid': true,
bordered,
'row-select': selectType === 'row',
'cell-select': selectType === 'cell',
'checkbox-select': selectType === 'checkbox'
},
className
);

const renderAutoSizer = sizerProps => {
const { height, isScrolling, registerChild, onChildScroll, scrollTop } = sizerProps;

return (
<AutoSizer disableHeight>
{({ width }) => (
<div ref={registerChild}>
<VirtualList
autoHeight
data={data}
height={height || 0}
deferredMeasurementCache={cellMeasurementCache}
rowHeight={cellMeasurementCache.rowHeight}
isScrolling={isScrolling}
onScroll={onChildScroll}
rowRenderer={rowRenderer}
rowCount={data.length}
scrollTop={scrollTop}
width={width}
tabIndex={null}
/>
</div>
)}
</AutoSizer>
);
};

const scrollElement = scrollable || document.getElementById(scrollableElementId) || window;

const bodyComponent = empty ? (
emptyComponent
) : (
<WindowScroller scrollElement={scrollElement}>{renderAutoSizer}</WindowScroller>
);

return (
<div className={classes} {...props}>
{header(tableData)}
<TableGrid.Body className={bodyClassName}>{bodyComponent}</TableGrid.Body>
</div>
);
};

VirtualTableGrid.propTypes = {
/** Additional css classes */
className: PropTypes.string,
/** Flag to use a bordered grid */
bordered: PropTypes.bool,
/** Type of selection for the grid */
selectType: PropTypes.oneOf(['row', 'cell', 'checkbox', 'none']),
/** Scrollable element for the list (use scrollableElementId or scrollable or neither to default to window) */
scrollable: PropTypes.any,
/** Id of the scrollable element for the list (use scrollableElementId or scrollable or neither to default to window) */
scrollableElementId: PropTypes.any,
/** Function(tableData) to return the header for the grid */
header: PropTypes.func.isRequired,
/** Function({obj, tableData, index}) to return a row */
row: PropTypes.func.isRequired,
/** Classes to add to the body */
bodyClassName: PropTypes.string,
/** Flag if there is no data */
empty: PropTypes.bool,
/** Component to show if empty */
emptyComponent: PropTypes.node,
/* react-virtualized: Cache to be shared between CellMeasurer and its parent Grid. */
measurementCache: PropTypes.object,
/** Data to fill the table */
data: PropTypes.array,
/** Global table data to be passed to each row (state values?) */
tableData: PropTypes.any
};

VirtualTableGrid.defaultProps = {
className: '',
bordered: true,
selectType: 'none',
scrollable: null,
scrollableElementId: '',
bodyClassName: '',
empty: false,
emptyComponent: null,
measurementCache: null,
data: [],
tableData: null
};

VirtualTableGrid.Head = TableGridHead;
VirtualTableGrid.ColumnHeader = TableGridColumnHeader;
VirtualTableGrid.Row = TableGridRow;
VirtualTableGrid.Col = TableGridCol;

export default VirtualTableGrid;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info/dist/index';
import { defaultTemplate } from 'storybook/decorators/storyTemplates';
import { storybookPackageName, STORYBOOK_CATEGORY } from 'storybook/constants/siteConstants';
import { MockVirtualTableGridExample, MockVirtualTableGridExampleSource } from './_mocks_/mockVirtualTableGridExample';

import { VirtualTableGrid, TableGridHead, TableGridColumnHeader, TableGridRow, TableGridCol } from './index';

import { name } from '../../../package.json';
import { boolean, select, withKnobs } from '@storybook/addon-knobs';

const stories = storiesOf(`${storybookPackageName(name)}/${STORYBOOK_CATEGORY.CONTENT_VIEWS}/VirtualTableGrid`, module);

stories.addDecorator(
defaultTemplate({
title: 'Virtual Table Grid',
description: (
<div>
The VirtualTableGrid is based on the Bootstrap Grid Layout and uses{' '}
<a href="https://github.com/bvaughn/react-virtualized">react-virtualized</a> to efficiently render large lists
of data.
<br />
<br />
The <b>VirtualTableGrid.ColumnHeaders</b> should have the same bootstrap column classes as the children of the{' '}
<b>VirtualTableGrid.Row</b> component in order to maintain equal widths.
<br />
<br />
When using <i>cell</i> selection, the <b>VirtualTableGrid.Col</b> component should be used in place of the
<b> Grid.Col</b> component for correct selection styling.
</div>
)
})
);

stories.addDecorator(withKnobs);
stories.add(
'VirtualTableGrid',
withInfo({
source: false,
propTables: [VirtualTableGrid, TableGridHead, TableGridColumnHeader, TableGridRow, TableGridCol],
propTablesExclude: [MockVirtualTableGridExample],
text: (
<div>
<h1>Story Source</h1>
<pre>{MockVirtualTableGridExampleSource}</pre>
</div>
)
})(() => {
const bordered = boolean('Bordered', true);
const selectType = select(
'Selection Type',
{ none: 'None', row: 'Row', checkbox: 'Checkbox', cell: 'Cell' },
'none'
);

return <MockVirtualTableGridExample bordered={bordered} selectType={selectType} />;
})
);
Loading

0 comments on commit f8cc152

Please sign in to comment.