diff --git a/packages/patternfly-4/react-table/src/components/Table/utils/decorators/selectable.js b/packages/patternfly-4/react-table/src/components/Table/utils/decorators/selectable.js index 3d0f357a10e..92faf861303 100644 --- a/packages/patternfly-4/react-table/src/components/Table/utils/decorators/selectable.js +++ b/packages/patternfly-4/react-table/src/components/Table/utils/decorators/selectable.js @@ -3,27 +3,40 @@ import { css } from '@patternfly/react-styles'; import { tableCheck } from '@patternfly/patternfly-next/components/Table/table.css'; import SelectColumn from '../../SelectColumn'; -export default (label, { column: { extraParams: { onSelect, rowLabeledBy = 'simple-node' } }, rowIndex, rowData }) => { +export default ( + label, + { + column: { + extraParams: { onSelect, rowLabeledBy = 'simple-node' } + }, + rowIndex, + rowData + } +) => { const rowId = rowIndex !== undefined ? rowIndex : -1; function selectClick(event) { - let selected = rowIndex === undefined ? event.target.checked : rowData && !rowData.selected; - onSelect && onSelect(selected, selected, rowId); + const selected = rowIndex === undefined ? event.target.checked : rowData && !rowData.selected; + onSelect && onSelect(event, selected, rowId, rowData); } const customProps = { - ...rowId !== -1 ? { - checked: rowData && !!rowData.selected, - 'aria-labelledby': rowLabeledBy + rowIndex - } : { - 'aria-label': 'Select all rows' - } - } + ...(rowId !== -1 + ? { + checked: rowData && !!rowData.selected, + 'aria-labelledby': rowLabeledBy + rowIndex + } + : { + 'aria-label': 'Select all rows' + }) + }; - return ({ + return { className: css(tableCheck), component: 'td', scope: '', children: ( - {label} + + {label} + ) - }); -} + }; +}; diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Body.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Body.js index 15bb5ecadca..3f4fc7ef0f8 100644 --- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Body.js +++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Body.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { isEqual } from 'lodash-es'; import { resolveRowKey } from 'reactabular-table'; import { TableBody, TableContext } from '@patternfly/react-table'; import calculateAverageHeight from './utils/calculateAverageHeight'; @@ -63,9 +62,10 @@ class Body extends React.Component { this.props.container().addEventListener('scroll', this.onScroll); }, 0); } - componentDidUpdate() { - this.checkMeasurements(); + componentDidUpdate(prevProps) { + this.checkMeasurements(prevProps); } + componentWillUnmount() { clearTimeout(this.timeoutId); } @@ -78,22 +78,6 @@ class Body extends React.Component { return props.height || props.style.maxHeight; } - // todo: convert `componentWillReceiveProps` to `getDerivedStateFromProps` - componentWillReceiveProps(nextProps) { - if (!isEqual(this.props.rows, nextProps.rows) || this.getHeight(this.props) !== this.getHeight(nextProps)) { - if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' && window.LOG_VIRTUALIZED) { - console.log('invalidating measurements'); // eslint-disable-line no-console - } - - const rows = this.calculateRows(nextProps); - - if (!rows) { - return; - } - - this.setState(rows); - } - } render() { const { onRow, rows, onScroll, container, ...props } = this.props; const { startIndex, amountOfRowsToRender, startHeight, endHeight, showExtraRow } = this.state; @@ -128,7 +112,7 @@ class Body extends React.Component { ); } - const tableBody = React.createElement(TableBody, { + const tableBodyProps = { ...props, style: { height, display: 'block', overflow: 'auto' }, onRow: (row, extra) => { @@ -142,7 +126,7 @@ class Body extends React.Component { }, rowsToRender, onScroll: this.onScroll - }); + }; return ( - {tableBody} + ); } @@ -184,29 +168,7 @@ class Body extends React.Component { this.setState(this.calculateRows(this.props)); } - // getRef() { - // const { ref } = this; - - // ref.scrollTo = index => { - // const startIndex = parseInt(index, 10); - // if (startIndex >= 0) { - // const startHeight = - // calculateAverageHeight({ - // measuredRows: this.measuredRows, - // rows: this.props.rows, - // rowKey: this.props.rowKey - // }) * startIndex; - - // this.scrollTop = startHeight; - // this.ref.scrollTop = startHeight; - - // this.setState(this.calculateRows(this.props)); - // } - // }; - - // return ref; - // } calculateRows(props) { return calculateRows({ scrollTop: this.scrollTop, @@ -216,11 +178,16 @@ class Body extends React.Component { rows: props.rows }); } - checkMeasurements() { - // If there are no valid measurements, calculate some after waiting a while. - // Without this styling solutions like Radium won't work as you might expect - // given they can take a while to set container height. - if (this.initialMeasurement) { + checkMeasurements(prevProps) { + // If there are no valid measurements or the rows have changed, + // calculate some after waiting a while. Without this styling solutions + // like Radium won't work as you might expect given they can take a while to set container height. + if (this.initialMeasurement || (prevProps && prevProps.rows !== this.props.rows)) { + // If the rows have changed, but the user has not scrolled, maintain the existing + // scroll position + if (this.ref.current) { + this.ref.current.scrollTop = this.scrollTop; + } this.timeoutId = setTimeout(() => { const rows = this.calculateRows(this.props); diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Virtualized.docs.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Virtualized.docs.js index 28b99660996..9ad9551b6b3 100644 --- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Virtualized.docs.js +++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/Virtualized.docs.js @@ -7,6 +7,8 @@ import { import AutoSizerExample from './examples/AutoSizerExample'; import VirtualizedExample from './examples/VirtualizedExample'; import SortableExample from './examples/SortableExample'; +import SelectableExample from './examples/SelectableExample'; +import './examples/common/styles'; export default { title: 'Virtualized', @@ -19,6 +21,7 @@ export default { examples: [ { component: AutoSizerExample, title: 'Simple AutoSizer Example' }, { component: VirtualizedExample, title: 'Simple Virtualized Example' }, - { component: SortableExample, title: 'Sortable Virtualized Example' } + { component: SortableExample, title: 'Sortable Virtualized Example' }, + { component: SelectableExample, title: 'Selectable Virtualized Example' } ] }; diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SelectableExample.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SelectableExample.js new file mode 100644 index 00000000000..11a65d1b4d1 --- /dev/null +++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SelectableExample.js @@ -0,0 +1,69 @@ +import React from 'react'; +import { Table, TableHeader, headerCol } from '@patternfly/react-table'; +import { + VirtualizedBody, + VirtualizedBodyWrapper, + VirtualizedRowWrapper +} from '@patternfly/react-virtualized-extension'; + +class SelectableExample extends React.Component { + constructor(props) { + super(props); + const rows = []; + for (let i = 0; i < 100; i++) { + rows.push({ + id: i, + cells: [`one-${i}`, `two-${i}`, `three-${i}`, `four-${i}`, `five-${i}`] + }); + } + this.state = { + columns: [ + { title: 'Repositories', cellTransforms: [headerCol()] }, + { title: 'Branches' }, + { title: 'Pull requests' }, + { title: 'Workspaces' }, + { title: 'Last Commit' } + ], + rows + }; + this.onSelect = this.onSelect.bind(this); + } + + onSelect(event, isSelected, virtualRowIndex, rowData) { + let rows; + if (virtualRowIndex === -1) { + rows = this.state.rows.map(oneRow => { + oneRow.selected = isSelected; + return oneRow; + }); + } else { + rows = [...this.state.rows]; + const rowIndex = rows.findIndex(r => r.id === rowData.id); + rows[rowIndex].selected = isSelected; + } + this.setState({ + rows + }); + } + + render() { + const { columns, rows } = this.state; + + return ( + + + +
+ ); + } +} + +export default SelectableExample; diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SortableExample.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SortableExample.js index 7ccd2bd760a..bffed0f2a7c 100644 --- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SortableExample.js +++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/SortableExample.js @@ -5,53 +5,28 @@ import { VirtualizedBodyWrapper, VirtualizedRowWrapper } from '@patternfly/react-virtualized-extension'; -// import './sample.css'; class SortableExample extends React.Component { - static title = 'Simple Table'; + static title = 'Sortable Table'; constructor(props) { super(props); this.tableBody = React.createRef(); - this.tableStyles = { - table: { - display: 'flex', - flexFlow: 'column' - }, - thead: { - display: 'table', - tableLayout: 'fixed', - width: '100%' - }, - tbody: { - display: 'block', - overflowY: 'scroll' - }, - tr: { - display: 'table', - tableLayout: 'fixed', - width: '100%' - }, - td: { - width: '20%' - } - }; const rows = []; for (let i = 0; i < 100; i++) { rows.push({ id: i, - props: { style: this.tableStyles.tr }, cells: [`one-${i}`, `two-${i}`, `three-${i}`, `four-${i}`, `five-${i}`] }); } this.state = { columns: [ - { title: 'Repositories', transforms: [sortable], props: { style: this.tableStyles.td } }, - { title: 'Branches', props: { style: this.tableStyles.td } }, - { title: 'Pull requests', transforms: [sortable], props: { style: this.tableStyles.td } }, - { title: 'Workspaces', props: { style: this.tableStyles.td } }, - { title: 'Last Commit', props: { style: this.tableStyles.td } } + { title: 'Repositories', transforms: [sortable] }, + { title: 'Branches' }, + { title: 'Pull requests', transforms: [sortable] }, + { title: 'Workspaces' }, + { title: 'Last Commit' } ], rows, sortBy: {} @@ -78,7 +53,6 @@ class SortableExample extends React.Component { return ( - - + +
); } diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/VirtualizedExample.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/VirtualizedExample.js index 4ffeb9c377d..8415b07f7df 100644 --- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/VirtualizedExample.js +++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/VirtualizedExample.js @@ -6,51 +6,24 @@ import { VirtualizedRowWrapper } from '@patternfly/react-virtualized-extension'; -// import './sample.css'; - class VirtualizedExample extends React.Component { static title = 'Simple Table'; constructor(props) { super(props); - - this.tableStyles = { - table: { - display: 'flex', - flexFlow: 'column' - }, - thead: { - display: 'table', - tableLayout: 'fixed', - width: '100%' - }, - tbody: { - display: 'block', - overflowY: 'scroll' - }, - tr: { - display: 'table', - tableLayout: 'fixed', - width: '100%' - }, - td: { - width: '20%' - } - }; const rows = []; for (let i = 0; i < 100; i++) { rows.push({ id: i, - props: { style: this.tableStyles.tr }, cells: [`one-${i}`, `two-${i}`, `three-${i}`, `four-${i}`, `five-${i}`] }); } this.state = { columns: [ - { title: 'Repositories', props: { style: this.tableStyles.td } }, - { title: 'Branches', props: { style: this.tableStyles.td } }, - { title: 'Pull requests', props: { style: this.tableStyles.td } }, - { title: 'Workspaces', props: { style: this.tableStyles.td } }, - { title: 'Last Commit', props: { style: this.tableStyles.td } } + { title: 'Repositories' }, + { title: 'Branches' }, + { title: 'Pull requests' }, + { title: 'Workspaces' }, + { title: 'Last Commit' } ], rows }; @@ -61,7 +34,6 @@ class VirtualizedExample extends React.Component { return ( - - + +
); } diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/common/styles.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/common/styles.js new file mode 100644 index 00000000000..69122542206 --- /dev/null +++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/common/styles.js @@ -0,0 +1,44 @@ +import { StyleSheet } from '@patternfly/react-styles'; + +const styles = StyleSheet.parse( + ` + .pf-c-virtualized.pf-c-table { + display: flex; + flex-flow: column; + } + + .pf-c-virtualized.pf-c-table thead, + .pf-c-virtualized.pf-c-table tbody tr { + display: table; + table-layout: fixed; + } + + .pf-c-virtualized.pf-c-table thead { + /* flex: 0 0 auto; */ + width: 100%; + } + + .pf-c-virtualized.pf-c-table thead tr { + /* 0.9em approximates scrollbar width */ + /* width: calc(100% - 0.9em); */ + width: 100%; + display: table; + } + + .pf-c-virtualized.pf-c-table tbody { + display: block; + /* flex: 1 1 auto; */ + overflow-y: scroll; + } + + .pf-c-virtualized.pf-c-table tbody tr { + width: 100%; + } + .pf-c-virtualized.pf-c-table th, + .pf-c-virtualized.pf-c-table td { + width: 20% + } + ` +); + +styles.inject(); diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/sample.css b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/sample.css deleted file mode 100644 index 802c1f1a187..00000000000 --- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/examples/sample.css +++ /dev/null @@ -1,37 +0,0 @@ -/* - Sample CSS from Reactabular relative column widths - https://reactabular.js.org/#/features/virtualization?a=using-relative-column-widths -*/ - -.pf-c-virtualized.pf-c-table { - display: flex; - flex-flow: column; -} - -.pf-c-virtualized.pf-c-table thead, -.pf-c-virtualized.pf-c-table tbody tr { - display: table; - table-layout: fixed; -} - -.pf-c-virtualized.pf-c-table thead { - /* flex: 0 0 auto; */ - width: 100%; -} - -.pf-c-virtualized.pf-c-table thead tr { - /* 0.9em approximates scrollbar width */ - /* width: calc(100% - 0.9em); */ - width: 100%; - display: table; -} - -.pf-c-virtualized.pf-c-table tbody { - display: block; - /* flex: 1 1 auto; */ - overflow-y: scroll; -} - -.pf-c-virtualized.pf-c-table tbody tr { - width: 100%; -}