horizontal scrolling

Summary:
Adding a property `horizontallyScrollable` the `ManagedTable` that enables horizontal scrolling, if that is required by the table's contents.
By default this behaviour is disabled to not break existing tables.

Reviewed By: jknoxville

Differential Revision: D15166144

fbshipit-source-id: 7a9b76facdbe717deb3d097e0b8883d4b0732d51
This commit is contained in:
Daniel Büchele
2019-05-01 09:28:18 -07:00
committed by Facebook Github Bot
parent da44a02cad
commit fca7bc93ee
3 changed files with 67 additions and 19 deletions

View File

@@ -116,6 +116,10 @@ export type ManagedTableProps = {|
* Callback when sorting changes
*/
onSort?: (order: TableRowSortOrder) => void,
/**
* Table scroll horizontally, if needed
*/
horizontallyScrollable?: boolean,
|};
type ManagedTableState = {|
@@ -126,9 +130,10 @@ type ManagedTableState = {|
shouldScrollToBottom: boolean,
|};
const Container = styled(FlexColumn)({
const Container = styled(FlexColumn)(props => ({
overflow: props.overflow ? 'scroll' : 'visible',
flexGrow: 1,
});
}));
class ManagedTable extends React.Component<
ManagedTableProps,
@@ -318,8 +323,13 @@ class ManagedTable extends React.Component<
);
};
onColumnResize = (columnSizes: TableColumnSizes) => {
this.setState({columnSizes});
onColumnResize = (id: string, width: number | string) => {
this.setState(({columnSizes}) => ({
columnSizes: {
...columnSizes,
[id]: width,
},
}));
};
scrollToBottom() {
@@ -499,7 +509,13 @@ class ManagedTable extends React.Component<
);
getRow = ({index, style}) => {
const {onAddFilter, multiline, zebra, rows} = this.props;
const {
onAddFilter,
multiline,
zebra,
rows,
horizontallyScrollable,
} = this.props;
const {columnOrder, columnSizes, highlightedRows} = this.state;
const columnKeys = columnOrder
.map(k => (k.visible ? k.key : null))
@@ -520,16 +536,37 @@ class ManagedTable extends React.Component<
style={style}
onAddFilter={onAddFilter}
zebra={zebra}
horizontallyScrollable={horizontallyScrollable}
/>
);
};
render() {
const {columns, rows, rowLineHeight, hideHeader} = this.props;
const {
columns,
rows,
rowLineHeight,
hideHeader,
horizontallyScrollable,
} = this.props;
const {columnOrder, columnSizes} = this.state;
let computedWidth = 0;
if (horizontallyScrollable) {
for (const col in columnSizes) {
const width = columnSizes[col];
if (isNaN(width)) {
// non-numeric columns with, can't caluclate
computedWidth = 0;
break;
} else {
computedWidth += parseInt(width, 10);
}
}
}
return (
<Container>
<Container overflow={horizontallyScrollable}>
{hideHeader !== true && (
<TableHead
columnOrder={columnOrder}
@@ -539,6 +576,7 @@ class ManagedTable extends React.Component<
sortOrder={this.state.sortOrder}
columnSizes={columnSizes}
onSort={this.onSort}
horizontallyScrollable={horizontallyScrollable}
/>
)}
<Container>
@@ -560,7 +598,7 @@ class ManagedTable extends React.Component<
DEFAULT_ROW_HEIGHT
}
ref={this.tableRef}
width={width}
width={Math.max(width, computedWidth)}
estimatedItemSize={rowLineHeight || DEFAULT_ROW_HEIGHT}
overscanCount={5}
innerRef={this.scrollRef}

View File

@@ -43,7 +43,7 @@ const TableHeaderColumnContainer = styled('div')({
padding: '0 8px',
});
const TableHeadContainer = styled(FlexRow)({
const TableHeadContainer = styled(FlexRow)(props => ({
borderBottom: `1px solid ${colors.sectionHeaderBorder}`,
color: colors.light50,
flexShrink: 0,
@@ -53,7 +53,8 @@ const TableHeadContainer = styled(FlexRow)({
textAlign: 'left',
top: 0,
zIndex: 2,
});
minWidth: props.horizontallyScrollable ? 'min-content' : 0,
}));
const TableHeadColumnContainer = styled('div')(props => ({
position: 'relative',
@@ -97,9 +98,17 @@ class TableHeadColumn extends PureComponent<{
onColumnResize: ?TableOnColumnResize,
children?: React$Node,
title?: string,
horizontallyScrollable?: boolean,
}> {
ref: HTMLElement;
componentDidMount() {
if (this.props.horizontallyScrollable) {
// measure initial width
this.onResize(this.ref.offsetWidth);
}
}
onClick = () => {
const {id, onSort, sortOrder} = this.props;
@@ -117,7 +126,7 @@ class TableHeadColumn extends PureComponent<{
};
onResize = (newWidth: number) => {
const {id, columnSizes, onColumnResize, width} = this.props;
const {id, onColumnResize, width} = this.props;
if (!onColumnResize) {
return;
}
@@ -143,10 +152,7 @@ class TableHeadColumn extends PureComponent<{
}
}
onColumnResize({
...columnSizes,
[id]: normalizedWidth,
});
onColumnResize(id, normalizedWidth);
};
setRef = (ref: HTMLElement) => {
@@ -191,6 +197,7 @@ export default class TableHead extends PureComponent<{
onSort: ?TableOnSort,
columnSizes: TableColumnSizes,
onColumnResize: ?TableOnColumnResize,
horizontallyScrollable?: boolean,
}> {
buildContextMenu = (): MenuTemplate => {
const visibles = this.props.columnOrder
@@ -237,6 +244,7 @@ export default class TableHead extends PureComponent<{
onColumnResize,
onSort,
sortOrder,
horizontallyScrollable,
} = this.props;
const elems = [];
@@ -284,7 +292,8 @@ export default class TableHead extends PureComponent<{
onSort={onSort}
columnSizes={columnSizes}
onColumnResize={onColumnResize}
title={key}>
title={key}
horizontallyScrollable={horizontallyScrollable}>
{col.value}
{arrow}
</TableHeadColumn>
@@ -296,10 +305,11 @@ export default class TableHead extends PureComponent<{
lastResizable = isResizable;
}
return (
<ContextMenu buildItems={this.buildContextMenu}>
<TableHeadContainer>{elems}</TableHeadContainer>
<TableHeadContainer horizontallyScrollable={horizontallyScrollable}>
{elems}
</TableHeadContainer>
</ContextMenu>
);
}

View File

@@ -26,7 +26,7 @@ export type TableHighlightedRows = Array<string>;
export type TableColumnKeys = Array<string>;
export type TableOnColumnResize = (sizes: TableColumnSizes) => void;
export type TableOnColumnResize = (id: string, size: number | string) => void;
export type TableOnColumnOrder = (order: TableColumnOrder) => void;
export type TableOnSort = (order: TableRowSortOrder) => void;
export type TableOnHighlight = (