diff --git a/packages/patternfly-4/react-docs/gatsby-browser.js b/packages/patternfly-4/react-docs/gatsby-browser.js
index 608156d89b2..f9458f7b6c3 100644
--- a/packages/patternfly-4/react-docs/gatsby-browser.js
+++ b/packages/patternfly-4/react-docs/gatsby-browser.js
@@ -1,2 +1 @@
import '../react-core/dist/styles/base.css';
-import './src/pages/styles/global.css';
diff --git a/packages/patternfly-4/react-docs/src/pages/styles/global.css b/packages/patternfly-4/react-docs/src/pages/styles/global.css
deleted file mode 100644
index ec31914cda5..00000000000
--- a/packages/patternfly-4/react-docs/src/pages/styles/global.css
+++ /dev/null
@@ -1,36 +0,0 @@
-.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%;
-}
diff --git a/packages/patternfly-4/react-table/src/components/Table/index.d.ts b/packages/patternfly-4/react-table/src/components/Table/index.d.ts
index 2b0619fb15d..b8f983bbc1a 100644
--- a/packages/patternfly-4/react-table/src/components/Table/index.d.ts
+++ b/packages/patternfly-4/react-table/src/components/Table/index.d.ts
@@ -18,5 +18,5 @@ export { default as TableHeader, HeaderProps } from './Header';
export { default as TableBody, TableBodyProps } from './Body';
export { RowWrapper, RowWrapperRow } from './RowWrapper';
export { default as ExpandableRowContent } from './ExpandableRowContent';
-export { sortable, headerCol, cellWidth, ISortable, expandable, isRowExpanded, mapOpenedRows } from './utils';
+export { sortable, headerCol, cellWidth, ISortable, expandable, isRowExpanded } from './utils';
export { SortByDirection } from './SortColumn';
diff --git a/packages/patternfly-4/react-table/src/components/Table/index.js b/packages/patternfly-4/react-table/src/components/Table/index.js
index 46ef77ca470..0c044c99655 100644
--- a/packages/patternfly-4/react-table/src/components/Table/index.js
+++ b/packages/patternfly-4/react-table/src/components/Table/index.js
@@ -4,5 +4,5 @@ export { default as TableBody } from './Body';
export { default as BodyWrapper } from './BodyWrapper';
export { default as RowWrapper } from './RowWrapper';
export { default as ExpandableRowContent } from './ExpandableRowContent';
-export { sortable, headerCol, cellWidth, expandable, isRowExpanded, mapOpenedRows } from './utils';
+export { sortable, headerCol, cellWidth, expandable, isRowExpanded } from './utils';
export { SortByDirection } from './SortColumn';
diff --git a/packages/patternfly-4/react-virtualized-extension/README.md b/packages/patternfly-4/react-virtualized-extension/README.md
index b968a01f741..ba01cc213be 100644
--- a/packages/patternfly-4/react-virtualized-extension/README.md
+++ b/packages/patternfly-4/react-virtualized-extension/README.md
@@ -1,3 +1,5 @@
# react-virtualized-extension
This package contains virtualization extensions for tables and lists.
+
+This package is currently an extension. Extension components do not undergo the same rigorous design or coding review process as core PatternFly components. If enough members of the community find them useful, we will work to move them into our core PatternFly system by starting the design process for the idea.
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 3f4fc7ef0f8..a1846846767 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
@@ -8,60 +8,127 @@ import calculateRows from './utils/calculateRows';
const initialContext = {
amountOfRowsToRender: 3, // First few rows for initial measurement
startIndex: 0, // Index where to start rendering
-
- // Heights for extra rows to mimic scrolling
- startHeight: 0,
+ startHeight: 0, // Heights for extra rows to mimic scrolling
endHeight: 0,
-
- // Show extra row (even/odd issue)
- showExtraRow: false
+ showExtraRow: false // Show extra row (even/odd issue)
};
export const VirtualizedBodyContext = React.createContext(initialContext);
class Body extends React.Component {
- constructor(props) {
- super(props);
+ state = initialContext;
+ measuredRows = {}; // row key -> measurement
+ ref = React.createRef(); //tbody ref used for gathering scroll position
+ initialMeasurement = true;
+ scrollTop = 0;
+ timeoutId = 0;
+
+ scrollTo = index => {
+ const { rows, rowKey } = this.props;
+ const startIndex = parseInt(index, 10);
+
+ if (startIndex >= 0) {
+ const startHeight =
+ calculateAverageHeight({
+ measuredRows: this.measuredRows,
+ rows,
+ rowKey
+ }) * startIndex;
+
+ this.scrollTop = startHeight;
+ this.ref.current.scrollTop = startHeight;
+
+ this.setState(this.calculateRows());
+ }
+ };
- this.measuredRows = {}; // row key -> measurement
+ onScroll = e => {
+ const { onScroll, container } = this.props;
+ onScroll && onScroll(e);
- this.ref = React.createRef();
+ const {
+ target: { scrollTop }
+ } = e;
- this.scrollTo = index => {
- const startIndex = parseInt(index, 10);
+ // Y didn't change, bail to avoid rendering rows
+ if (this.scrollTop === scrollTop) {
+ return;
+ }
- if (startIndex >= 0) {
- const startHeight =
- calculateAverageHeight({
- measuredRows: this.measuredRows,
- rows: props.rows,
- rowKey: props.rowKey
- }) * startIndex;
+ this.scrollTop = container ? scrollTop - this.getBodyOffset() : scrollTop;
- this.scrollTop = startHeight;
- this.ref.current.scrollTop = startHeight;
+ this.setState(this.calculateRows());
+ };
- this.setState(this.calculateRows(this.props));
+ 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.scrollTop = 0;
- this.initialMeasurement = true;
- this.timeoutId = 0;
+ if (!rows) {
+ // Refresh the rows to trigger measurement.
+ this.forceUpdate();
- this.state = initialContext;
+ return;
+ }
- this.checkMeasurements = this.checkMeasurements.bind(this);
- this.onScroll = this.onScroll.bind(this);
- }
- componentDidMount() {
- this.checkMeasurements();
- this.props.container && this.registerContainer();
- }
- registerContainer() {
+ this.setState(rows, () => {
+ this.initialMeasurement = false;
+ });
+ }, 100);
+ }
+ };
+
+ getHeight = () => {
+ const { container, height, style } = this.props;
+ if (container) {
+ return container().clientHeight;
+ }
+ // If `props.height` is not defined, we use `props.style.maxHeight` instead.
+ return height || style.maxHeight;
+ };
+
+ // Attach information about measuring status. This way we can implement
+ // proper shouldComponentUpdate
+ rowsToRender = (rows, startIndex, amountOfRowsToRender, rowKey) => {
+ const renderedRows = rows.slice(startIndex, startIndex + amountOfRowsToRender).map((rowData, rowIndex) => ({
+ ...rowData,
+ _measured: !!this.measuredRows[resolveRowKey({ rowData, rowIndex, rowKey })]
+ }));
+ return renderedRows;
+ };
+
+ getBodyOffset = () => this.ref.current.parentElement.offsetTop + this.ref.current.offsetTop;
+
+ registerContainer = () => {
setTimeout(() => {
this.props.container().addEventListener('scroll', this.onScroll);
}, 0);
+ };
+
+ calculateRows = () => {
+ const { rows, rowKey } = this.props;
+ return calculateRows({
+ scrollTop: this.scrollTop,
+ measuredRows: this.measuredRows,
+ height: this.getHeight(),
+ rowKey,
+ rows
+ });
+ };
+
+ componentDidMount() {
+ this.checkMeasurements();
+ this.props.container && this.registerContainer();
}
+
componentDidUpdate(prevProps) {
this.checkMeasurements(prevProps);
}
@@ -70,32 +137,12 @@ class Body extends React.Component {
clearTimeout(this.timeoutId);
}
- getHeight(props) {
- if (this.props.container) {
- return this.props.container().clientHeight;
- }
- // If `props.height` is not defined, we use `props.style.maxHeight` instead.
- return props.height || props.style.maxHeight;
- }
-
render() {
- const { onRow, rows, onScroll, container, ...props } = this.props;
+ const { onRow, rows, onScroll, container, rowKey, ...props } = this.props;
const { startIndex, amountOfRowsToRender, startHeight, endHeight, showExtraRow } = this.state;
- const height = this.getHeight(this.props);
-
- // Attach information about measuring status. This way we can implement
- // proper shouldComponentUpdate
- const rowsToRender = rows.slice(startIndex, startIndex + amountOfRowsToRender).map((rowData, rowIndex) => ({
- ...rowData,
- _measured: !!this.measuredRows[
- resolveRowKey({
- rowData,
- rowIndex,
- rowKey: this.props.rowKey
- })
- ]
- }));
+ const height = this.getHeight();
+ const rowsToRender = this.rowsToRender(rows, startIndex, amountOfRowsToRender, rowKey);
if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' && window.LOG_VIRTUALIZED) {
console.log(
// eslint-disable-line no-console
@@ -115,15 +162,11 @@ class Body extends React.Component {
const tableBodyProps = {
...props,
style: { height, display: 'block', overflow: 'auto' },
- onRow: (row, extra) => {
- const rowProps = onRow ? onRow(row, extra) : {};
-
- return {
- // Pass index so that row heights can be tracked properly
- 'data-rowkey': extra.rowKey,
- ...rowProps
- };
- },
+ onRow: (row, extra) => ({
+ // Pass index so that row heights can be tracked properly
+ 'data-rowkey': extra.rowKey,
+ ...(onRow ? onRow(row, extra) : {})
+ }),
rowsToRender,
onScroll: this.onScroll
};
@@ -135,8 +178,8 @@ class Body extends React.Component {
startHeight,
endHeight,
showExtraRow,
- updateHeight: (rowKey, rowHeight) => {
- this.measuredRows[rowKey] = rowHeight;
+ updateHeight: (oneRowKey, rowHeight) => {
+ this.measuredRows[oneRowKey] = rowHeight;
},
// Capture height data only during the initial measurement
initialMeasurement: this.initialMeasurement
@@ -146,64 +189,6 @@ class Body extends React.Component {
);
}
-
- getBodyOffset() {
- return this.ref.current.parentElement.offsetTop + this.ref.current.offsetTop;
- }
-
- onScroll(e) {
- const { onScroll } = this.props;
- onScroll && onScroll(e);
-
- const {
- target: { scrollTop }
- } = e;
-
- // Y didn't change, bail to avoid rendering rows
- if (this.scrollTop === scrollTop) {
- return;
- }
-
- this.scrollTop = this.props.container ? scrollTop - this.getBodyOffset() : scrollTop;
-
- this.setState(this.calculateRows(this.props));
- }
-
- calculateRows(props) {
- return calculateRows({
- scrollTop: this.scrollTop,
- measuredRows: this.measuredRows,
- height: this.getHeight(props),
- rowKey: props.rowKey,
- rows: props.rows
- });
- }
- 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);
-
- if (!rows) {
- // Refresh the rows to trigger measurement.
- this.forceUpdate();
-
- return;
- }
-
- this.setState(rows, () => {
- this.initialMeasurement = false;
- });
- }, 100);
- }
- }
}
const VirtualizedBody = ({ tableBody, ...props }) => (
diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/BodyWrapper.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/BodyWrapper.js
index d2db7c688bc..c984b4fb6d3 100644
--- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/BodyWrapper.js
+++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/BodyWrapper.js
@@ -2,6 +2,10 @@ import React, { Component } from 'react';
import { VirtualizedBodyContext } from './Body';
import { bodyWrapperContextTypes, bodyWrapperTypes } from './types';
+import { virtualizedCss } from './css/virtualized-css';
+
+virtualizedCss.inject();
+
class BodyWrapper extends Component {
tr = props => React.createElement('tr', props);
render() {
diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/RowWrapper.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/RowWrapper.js
index 9062f00c390..d193d18ffb6 100644
--- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/RowWrapper.js
+++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/RowWrapper.js
@@ -6,31 +6,39 @@ import { RowWrapper } from '@patternfly/react-table';
import { VirtualizedBodyContext } from './Body';
class VirtualizedRowWrapper extends React.Component {
- constructor(props) {
- super(props);
-
- this.ref = React.createRef();
-
- this.updateHeight = this.updateHeight.bind(this);
+ ref = React.createRef();
+
+ updateRowHeight = () => {
+ const { updateHeight, rowProps } = this.props;
+ updateHeight(rowProps['data-rowkey'], this.ref.current.offsetHeight);
+ };
+
+ static shouldComponentUpdate(nextProps) {
+ const { columns, rowData } = this.props;
+ // Update only if a row has not been measured and either
+ // columns or rowData hasn't changed
+ if (nextProps.rowData._measured) {
+ return !(columnsAreEqual(columns, nextProps.columns) && isEqual(rowData, nextProps.rowData));
+ }
+ return true;
}
+
componentDidMount() {
- this.updateHeight();
+ this.updateRowHeight();
}
componentDidUpdate() {
// Capture height data only during initial measurement for performance.
// This loses some accuracy if row height changes, but it's good enough
// for most purposes.
if (this.props.initialMeasurement) {
- this.updateHeight();
+ this.updateRowHeight();
}
}
+
render() {
const { updateHeight, initialMeasurement, ...props } = this.props;
return ;
}
- updateHeight() {
- this.props.updateHeight(this.props.rowProps['data-rowkey'], this.ref.current.offsetHeight);
- }
}
VirtualizedRowWrapper.propTypes = {
rowProps: PropTypes.shape({
@@ -41,20 +49,6 @@ VirtualizedRowWrapper.propTypes = {
trRef: PropTypes.object
};
-VirtualizedRowWrapper.shouldComponentUpdate = function(nextProps) {
- const previousProps = this.props;
-
- // Update only if a row has not been measured and either
- // columns or rowData hasn't changed
- if (nextProps.rowData._measured) {
- return !(
- columnsAreEqual(previousProps.columns, nextProps.columns) && isEqual(previousProps.rowData, nextProps.rowData)
- );
- }
-
- return true;
-};
-
const VirtualizedRowWrapperWithContext = props => (
{({ updateHeight, initialMeasurement }) => (
diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/WindowScroller.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/WindowScroller.js
index 7b8868f44b4..6e46f098cc8 100644
--- a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/WindowScroller.js
+++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/WindowScroller.js
@@ -219,8 +219,8 @@ WindowScroller.propTypes = {
/** Callback to be invoked on-scroll: ({ scrollLeft, scrollTop }) */
onScroll: PropTypes.func,
- /** Element to attach scroll event listeners. Defaults to window. */
- scrollElement: PropTypes.oneOfType([PropTypes.instanceOf(window.constructor), PropTypes.instanceOf(Element)]),
+ /** Query string for element to attach scroll event listeners. Defaults to window if no element query string provided. */
+ scrollElement: PropTypes.string,
/**
* Wait this amount of time after the last scroll event before resetting child `pointer-events`.
*/
diff --git a/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/css/virtualized-css.js b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/css/virtualized-css.js
new file mode 100644
index 00000000000..97346157241
--- /dev/null
+++ b/packages/patternfly-4/react-virtualized-extension/src/components/Virtualized/css/virtualized-css.js
@@ -0,0 +1,40 @@
+import { StyleSheet } from '@patternfly/react-styles';
+
+export const virtualizedCss = 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%;
+ }
+`);