LeftPane: Measure content above list to calculate list size

This commit is contained in:
Josh Perez 2020-04-23 15:20:47 -04:00 committed by Scott Nonnenberg
parent 5e3bbf1dc0
commit d5925ac9d5
5 changed files with 67 additions and 74 deletions

View file

@ -5206,8 +5206,8 @@ button.module-image__border-overlay:focus {
}
.module-left-pane__header {
flex-shrink: 0;
flex-grow: 0;
flex-shrink: 0;
}
.module-left-pane__archive-header {
@ -5295,13 +5295,20 @@ button.module-image__border-overlay:focus {
}
}
.module-left-pane__list {
.module-left-pane__list--measure {
flex-grow: 1;
flex-shrink: 1;
outline: none;
}
.module-left-pane__list--wrapper {
position: relative;
}
.module-left-pane__list {
position: absolute;
}
.module-left-pane__virtual-list {
outline: none;
}

View file

@ -56,12 +56,6 @@ export class LeftPane extends React.Component<PropsType> {
public containerRef = React.createRef<HTMLDivElement>();
public setFocusToFirstNeeded = false;
public setFocusToLastNeeded = false;
public state = {
dimensions: {
height: 0,
width: 0,
},
};
public renderRow = ({
index,
@ -165,10 +159,6 @@ export class LeftPane extends React.Component<PropsType> {
}
};
public handleResize = ({ bounds }: { bounds: BoundingRect }) => {
this.setState({ dimensions: bounds });
};
public handleFocus = () => {
const { selectedConversationId } = this.props;
const { current: container } = this.containerRef;
@ -295,7 +285,10 @@ export class LeftPane extends React.Component<PropsType> {
: conversations.length + (archivedConversations.length ? 1 : 0);
};
public renderList = (): JSX.Element | Array<JSX.Element | null> => {
public renderList = ({
height,
width,
}: BoundingRect): JSX.Element | Array<JSX.Element | null> => {
const {
archivedConversations,
i18n,
@ -311,6 +304,8 @@ export class LeftPane extends React.Component<PropsType> {
return (
<SearchResults
{...searchResults}
height={height || 0}
width={width || 0}
openConversationInternal={openConversationInternal}
startNewConversation={startNewConversation}
renderMessageSearchResult={renderMessageSearchResult}
@ -327,24 +322,16 @@ export class LeftPane extends React.Component<PropsType> {
const length = this.getLength();
const archived = showArchived ? (
<div className="module-left-pane__archive-helper-text" key={0}>
{i18n('archiveHelperText')}
</div>
) : null;
// We ensure that the listKey differs between inbox and archive views, which ensures
// that AutoSizer properly detects the new size of its slot in the flexbox. The
// archive explainer text at the top of the archive view causes problems otherwise.
// It also ensures that we scroll to the top when switching views.
const listKey = showArchived ? 1 : 0;
const { height, width } = this.state.dimensions;
// Note: conversations is not a known prop for List, but it is required to ensure that
// it re-renders when our conversation data changes. Otherwise it would just render
// on startup and scroll.
const list = (
return (
<div
aria-live="polite"
className="module-left-pane__list"
@ -358,19 +345,17 @@ export class LeftPane extends React.Component<PropsType> {
<List
className="module-left-pane__virtual-list"
conversations={conversations}
height={height}
height={height || 0}
onScroll={this.onScroll}
ref={this.listRef}
rowCount={length}
rowHeight={68}
rowRenderer={this.renderRow}
tabIndex={-1}
width={width}
width={width || 0}
/>
</div>
);
return [archived, list];
};
public renderArchivedHeader = (): JSX.Element => {
@ -392,6 +377,7 @@ export class LeftPane extends React.Component<PropsType> {
public render(): JSX.Element {
const {
i18n,
renderExpiredBuildDialog,
renderMainHeader,
renderNetworkStatus,
@ -400,21 +386,31 @@ export class LeftPane extends React.Component<PropsType> {
showArchived,
} = this.props;
/* tslint:disable no-non-null-assertion */
return (
<Measure bounds={true} onResize={this.handleResize}>
{({ measureRef }: MeasuredComponentProps) => (
<div className="module-left-pane" ref={measureRef}>
<div className="module-left-pane__header">
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
</div>
{renderExpiredBuildDialog()}
{renderNetworkStatus()}
{renderUpdateDialog()}
{renderRelinkDialog()}
{this.renderList()}
<div className="module-left-pane">
<div className="module-left-pane__header">
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
</div>
{renderExpiredBuildDialog()}
{renderNetworkStatus()}
{renderUpdateDialog()}
{renderRelinkDialog()}
{showArchived && (
<div className="module-left-pane__archive-helper-text" key={0}>
{i18n('archiveHelperText')}
</div>
)}
</Measure>
<Measure bounds={true}>
{({ contentRect, measureRef }: MeasuredComponentProps) => (
<div className="module-left-pane__list--measure" ref={measureRef}>
<div className="module-left-pane__list--wrapper">
{this.renderList(contentRect.bounds!)}
</div>
</div>
)}
</Measure>
</div>
);
}
}

View file

@ -111,6 +111,7 @@ messageLookup.set('4-guid-guid-guid-guid-guid', {
const defaultProps = {
discussionsLoading: false,
height: 700,
items: [],
i18n,
messagesLoading: false,
@ -135,6 +136,7 @@ const defaultProps = {
selectedConversationId: undefined,
selectedMessageId: undefined,
startNewConversation: action('start-new-conversation'),
width: 320,
};
const conversations = [

View file

@ -1,10 +1,5 @@
import React from 'react';
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from 'react-virtualized';
import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
import { debounce, get, isNumber } from 'lodash';
import { Intl } from './Intl';
@ -86,6 +81,8 @@ type PropsHousekeepingType = {
query: string,
options: { regionCode: string }
) => void;
height: number;
width: number;
renderMessageSearchResult: (id: string) => JSX.Element;
};
@ -121,8 +118,6 @@ type OnScrollParamsType = {
export class SearchResults extends React.Component<PropsType, StateType> {
public setFocusToFirstNeeded = false;
public setFocusToLastNeeded = false;
public mostRecentWidth = 0;
public mostRecentHeight = 0;
public cellSizeCache = new CellMeasurerCache({
defaultHeight: 80,
fixedWidth: true,
@ -454,7 +449,7 @@ export class SearchResults extends React.Component<PropsType, StateType> {
parent,
style,
}: RowRendererParamsType): JSX.Element => {
const { items } = this.props;
const { items, width } = this.props;
const row = items[index];
@ -466,7 +461,7 @@ export class SearchResults extends React.Component<PropsType, StateType> {
key={key}
parent={parent}
rowIndex={index}
width={this.mostRecentWidth}
width={width}
>
{this.renderRowContents(row)}
</CellMeasurer>
@ -530,11 +525,13 @@ export class SearchResults extends React.Component<PropsType, StateType> {
public render() {
const {
height,
i18n,
items,
noResults,
searchConversationName,
searchTerm,
width,
} = this.props;
const { scrollToIndex } = this.state;
@ -581,30 +578,21 @@ export class SearchResults extends React.Component<PropsType, StateType> {
onKeyDown={this.handleKeyDown}
onFocus={this.handleFocus}
>
<AutoSizer>
{({ height, width }) => {
this.mostRecentWidth = width;
this.mostRecentHeight = height;
return (
<List
className="module-search-results__virtual-list"
deferredMeasurementCache={this.cellSizeCache}
height={height}
items={items}
overscanRowCount={5}
ref={this.listRef}
rowCount={this.getRowCount()}
rowHeight={this.cellSizeCache.rowHeight}
rowRenderer={this.renderRow}
scrollToIndex={scrollToIndex}
tabIndex={-1}
onScroll={this.onScroll as any}
width={width}
/>
);
}}
</AutoSizer>
<List
className="module-search-results__virtual-list"
deferredMeasurementCache={this.cellSizeCache}
height={height}
items={items}
overscanRowCount={5}
ref={this.listRef}
rowCount={this.getRowCount()}
rowHeight={this.cellSizeCache.rowHeight}
rowRenderer={this.renderRow}
scrollToIndex={scrollToIndex}
tabIndex={-1}
onScroll={this.onScroll as any}
width={width}
/>
</div>
);
}

View file

@ -11522,7 +11522,7 @@
"rule": "React-createRef",
"path": "ts/components/SearchResults.js",
"line": " this.listRef = react_1.default.createRef();",
"lineNumber": 26,
"lineNumber": 24,
"reasonCategory": "usageTrusted",
"updated": "2019-08-09T00:44:31.008Z",
"reasonDetail": "SearchResults needs to interact with its child List directly"