Skip to content

Commit

Permalink
Wrap render table function in useCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
rawagner committed May 5, 2021
1 parent 0255bc2 commit 29f2299
Show file tree
Hide file tree
Showing 3 changed files with 411 additions and 52 deletions.
325 changes: 325 additions & 0 deletions frontend/public/components/factory/Table/PFBody.tsx
@@ -0,0 +1,325 @@
/* eslint-disable */

import * as React from 'react';
import {
NoContentRenderer,
Alignment,
CellSize,
CellPosition,
OverscanIndicesGetter,
RenderedSection,
CellRendererParams,
Scroll as VirtualGridScroll,
Scroll,
} from '@patternfly/react-virtualized-extension/dist/js/components/Virtualized/types';

import accessibilityOverscanIndicesGetter from '@patternfly/react-virtualized-extension/dist/js/components/Virtualized/accessibilityOverscanIndicesGetter';
import {
VirtualGrid,
VirtualGridProps,
} from '@patternfly/react-virtualized-extension/dist/js/components/Virtualized/VirtualGrid';
import { css } from '@patternfly/react-styles';

/**
* It is inefficient to create and manage a large list of DOM elements within a scrolling container
* if only a few of those elements are visible. The primary purpose of this component is to improve
* performance by only rendering the DOM nodes that a user is able to see based on their current
* scroll position.
*
* This component renders a virtualized list of elements with either fixed or dynamic heights.
*/

interface VirtualTableBodyProps
extends Omit<
VirtualGridProps,
| 'style'
| 'containerStyle'
| 'aria-label'
| 'aria-readonly'
| 'tabIndex'
| 'role'
| 'autoContainerWidth'
| 'cellRenderer'
| 'className'
| 'columnWidth'
| 'columnCount'
| 'noContentRenderer'
| 'onScroll'
| 'onSectionRendered'
| 'ref'
| 'scrollToRow'
> {
'aria-label'?: string;

/**
* Removes fixed height from the scrollingContainer so that the total height
* of rows can stretch the window. Intended for use with WindowScroller
*/
autoHeight?: boolean;

/** Optional CSS class name */
className?: string;

/**
* Used to estimate the total height of a List before all of its rows have actually been measured.
* The estimated total height is adjusted as rows are rendered.
*/
estimatedRowSize?: number;

/** Height constraint for list (determines how many actual rows are rendered) */
height: number;

/** Optional renderer to be used in place of rows when rowCount is 0 */
noRowsRenderer?: NoContentRenderer;

/** Callback invoked with information about the slice of rows that were just rendered. */

onRowsRendered?: (params: any) => void;

/**
* Callback invoked whenever the scroll offset changes within the inner scrollable region.
* This callback can be used to sync scrolling between lists, tables, or grids.
*/
onScroll?: (params: Scroll) => void;

/** See VirtualGrid#overscanIndicesGetter */
overscanIndicesGetter?: OverscanIndicesGetter;

/**
* Number of rows to render above/below the visible bounds of the list.
* These rows can help for smoother scrolling on touch devices.
*/
overscanRowCount?: number;

/** Either a fixed row height (number) or a function that returns the height of a row given its index. */
rowHeight: CellSize;

/** Responsible for rendering a row given an index; ({ index: number }): node */
rowRenderer: any;

/** Number of rows in list. */
rowCount: number;

/** See VirtualGrid#scrollToAlignment */
scrollToAlignment?: Alignment;

/** Row index to ensure visible (by forcefully scrolling if necessary) */
scrollToIndex?: number;

/** Vertical offset. */
scrollTop?: number;

style?: Object;

/** Tab index for focus */
tabIndex?: number;

/** Width of list */
width: number;

columns?: any[];

columnCount?: number;

rows: any[];
}

export class VirtualTableBody extends React.Component<VirtualTableBodyProps> {
static defaultProps = {
autoHeight: false,
estimatedRowSize: 30,
onScroll: () => {},
noRowsRenderer: () => null as any,
onRowsRendered: () => {},
overscanIndicesGetter: accessibilityOverscanIndicesGetter,
overscanRowCount: 10,
scrollToAlignment: 'auto',
scrollToIndex: -1,
style: {},
scrollContainerComponent: 'table',
innerScrollContainerComponent: 'tbody',
};

VirtualGrid: any;

forceUpdateVirtualGrid() {
if (this.VirtualGrid) {
this.VirtualGrid.forceUpdate();
}
}

/** See VirtualGrid#getOffsetForCell */
getOffsetForRow({ alignment, index }: { alignment: Alignment; index: number }) {
if (this.VirtualGrid) {
const { scrollTop } = this.VirtualGrid.getOffsetForCell({
alignment,
rowIndex: index,
columnIndex: 0,
});

return scrollTop;
}
return 0;
}

/** CellMeasurer compatibility */
invalidateCellSizeAfterRender({ columnIndex, rowIndex }: CellPosition) {
if (this.VirtualGrid) {
this.VirtualGrid.invalidateCellSizeAfterRender({
rowIndex,
columnIndex,
});
}
}

/** See VirtualGrid#measureAllCells */
measureAllRows() {
if (this.VirtualGrid) {
this.VirtualGrid.measureAllCells();
}
}

/** CellMeasurer compatibility */
recomputeVirtualGridSize({ columnIndex = 0, rowIndex = 0 }: CellPosition = {}) {
if (this.VirtualGrid) {
this.VirtualGrid.recomputeVirtualGridSize({
rowIndex,
columnIndex,
});
}
}

/** See VirtualGrid#recomputeVirtualGridSize */
recomputeRowHeights(index: number = 0) {
if (this.VirtualGrid) {
this.VirtualGrid.recomputeVirtualGridSize({
rowIndex: index,
columnIndex: 0,
});
}
}

/** See VirtualGrid#scrollToPosition */
scrollToPosition(scrollTop: number = 0) {
if (this.VirtualGrid) {
this.VirtualGrid.scrollToPosition({ scrollTop });
}
}

/** See VirtualGrid#scrollToCell */
scrollToRow(index: number = 0) {
if (this.VirtualGrid) {
this.VirtualGrid.scrollToCell({
columnIndex: 0,
rowIndex: index,
});
}
}

render() {
const {
className,
noRowsRenderer,
scrollToIndex,
width,
columns,
columnCount,
style,
scrollContainerComponent,
innerScrollContainerComponent,
} = this.props;

const classNames = css('ReactVirtualized__List', className);

return (
// note: these aria props if rendered will break a11y for role="presentation"
// this approach attempts to fix non standard table grids
// see: https://www.html5accessibility.com/tests/aria-table-fix.html
<VirtualGrid
{...this.props}
style={{
tableLayout: 'fixed',
display: 'block',
...style,
}}
containerStyle={{
display: 'block',
}}
aria-label={null}
aria-readonly={null}
tabIndex={null}
autoContainerWidth
cellRenderer={this._cellRenderer}
className={classNames}
columnWidth={width}
columnCount={columns ? columns.length : columnCount}
noContentRenderer={noRowsRenderer}
onScroll={this._onScroll}
onSectionRendered={this._onSectionRendered}
ref={this._setRef}
scrollToRow={scrollToIndex}
scrollContainerComponent={scrollContainerComponent}
innerScrollContainerComponent={innerScrollContainerComponent}
/>
);
}

_cellRenderer = ({
parent,
rowIndex,
style,
isScrolling,
isVisible,
key,
}: CellRendererParams) => {
const { rowRenderer, rows, columns } = this.props;

// TRICKY The style object is sometimes cached by VirtualGrid.
// This prevents new style objects from bypassing shallowCompare().
// However as of React 16, style props are auto-frozen (at least in dev mode)
// Check to make sure we can still modify the style before proceeding.
// https://github.com/facebook/react/commit/977357765b44af8ff0cfea327866861073095c12#commitcomment-20648713
const { writable } = Object.getOwnPropertyDescriptor(style, 'width');
if (writable) {
// By default, List cells should be 100% width.
// This prevents them from flowing under a scrollbar (if present).
style.width = '100%';
}

return rowRenderer({
index: rowIndex,
style,
isVisible,
key,
parent,
datum: rows[rowIndex],
columns,
});
};

_setRef = (ref: any) => {
this.VirtualGrid = ref;
};

_onScroll = ({ clientHeight, scrollHeight, scrollTop }: VirtualGridScroll) => {
const { onScroll } = this.props;

onScroll({ clientHeight, scrollHeight, scrollTop } as VirtualGridScroll);
};

_onSectionRendered = ({
rowOverscanStartIndex,
rowOverscanStopIndex,
rowStartIndex,
rowStopIndex,
}: RenderedSection) => {
const { onRowsRendered } = this.props;

onRowsRendered({
overscanStartIndex: rowOverscanStartIndex,
overscanStopIndex: rowOverscanStopIndex,
startIndex: rowStartIndex,
stopIndex: rowStopIndex,
});
};
}

0 comments on commit 29f2299

Please sign in to comment.