LeftPane: Measure content above list to calculate list size
This commit is contained in:
		
					parent
					
						
							
								5e3bbf1dc0
							
						
					
				
			
			
				commit
				
					
						d5925ac9d5
					
				
			
		
					 5 changed files with 67 additions and 74 deletions
				
			
		|  | @ -5206,8 +5206,8 @@ button.module-image__border-overlay:focus { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .module-left-pane__header { | .module-left-pane__header { | ||||||
|   flex-shrink: 0; |  | ||||||
|   flex-grow: 0; |   flex-grow: 0; | ||||||
|  |   flex-shrink: 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .module-left-pane__archive-header { | .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-grow: 1; | ||||||
|   flex-shrink: 1; |   flex-shrink: 1; | ||||||
| 
 |  | ||||||
|   outline: none; |   outline: none; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .module-left-pane__list--wrapper { | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .module-left-pane__list { | ||||||
|  |   position: absolute; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .module-left-pane__virtual-list { | .module-left-pane__virtual-list { | ||||||
|   outline: none; |   outline: none; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,12 +56,6 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
|   public containerRef = React.createRef<HTMLDivElement>(); |   public containerRef = React.createRef<HTMLDivElement>(); | ||||||
|   public setFocusToFirstNeeded = false; |   public setFocusToFirstNeeded = false; | ||||||
|   public setFocusToLastNeeded = false; |   public setFocusToLastNeeded = false; | ||||||
|   public state = { |  | ||||||
|     dimensions: { |  | ||||||
|       height: 0, |  | ||||||
|       width: 0, |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   public renderRow = ({ |   public renderRow = ({ | ||||||
|     index, |     index, | ||||||
|  | @ -165,10 +159,6 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   public handleResize = ({ bounds }: { bounds: BoundingRect }) => { |  | ||||||
|     this.setState({ dimensions: bounds }); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   public handleFocus = () => { |   public handleFocus = () => { | ||||||
|     const { selectedConversationId } = this.props; |     const { selectedConversationId } = this.props; | ||||||
|     const { current: container } = this.containerRef; |     const { current: container } = this.containerRef; | ||||||
|  | @ -295,7 +285,10 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
|       : conversations.length + (archivedConversations.length ? 1 : 0); |       : 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 { |     const { | ||||||
|       archivedConversations, |       archivedConversations, | ||||||
|       i18n, |       i18n, | ||||||
|  | @ -311,6 +304,8 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
|       return ( |       return ( | ||||||
|         <SearchResults |         <SearchResults | ||||||
|           {...searchResults} |           {...searchResults} | ||||||
|  |           height={height || 0} | ||||||
|  |           width={width || 0} | ||||||
|           openConversationInternal={openConversationInternal} |           openConversationInternal={openConversationInternal} | ||||||
|           startNewConversation={startNewConversation} |           startNewConversation={startNewConversation} | ||||||
|           renderMessageSearchResult={renderMessageSearchResult} |           renderMessageSearchResult={renderMessageSearchResult} | ||||||
|  | @ -327,24 +322,16 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
| 
 | 
 | ||||||
|     const length = this.getLength(); |     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
 |     // 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
 |     //   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.
 |     //   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.
 |     //   It also ensures that we scroll to the top when switching views.
 | ||||||
|     const listKey = showArchived ? 1 : 0; |     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
 |     // 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
 |     //   it re-renders when our conversation data changes. Otherwise it would just render
 | ||||||
|     //   on startup and scroll.
 |     //   on startup and scroll.
 | ||||||
|     const list = ( |     return ( | ||||||
|       <div |       <div | ||||||
|         aria-live="polite" |         aria-live="polite" | ||||||
|         className="module-left-pane__list" |         className="module-left-pane__list" | ||||||
|  | @ -358,19 +345,17 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
|         <List |         <List | ||||||
|           className="module-left-pane__virtual-list" |           className="module-left-pane__virtual-list" | ||||||
|           conversations={conversations} |           conversations={conversations} | ||||||
|           height={height} |           height={height || 0} | ||||||
|           onScroll={this.onScroll} |           onScroll={this.onScroll} | ||||||
|           ref={this.listRef} |           ref={this.listRef} | ||||||
|           rowCount={length} |           rowCount={length} | ||||||
|           rowHeight={68} |           rowHeight={68} | ||||||
|           rowRenderer={this.renderRow} |           rowRenderer={this.renderRow} | ||||||
|           tabIndex={-1} |           tabIndex={-1} | ||||||
|           width={width} |           width={width || 0} | ||||||
|         /> |         /> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
| 
 |  | ||||||
|     return [archived, list]; |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   public renderArchivedHeader = (): JSX.Element => { |   public renderArchivedHeader = (): JSX.Element => { | ||||||
|  | @ -392,6 +377,7 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
| 
 | 
 | ||||||
|   public render(): JSX.Element { |   public render(): JSX.Element { | ||||||
|     const { |     const { | ||||||
|  |       i18n, | ||||||
|       renderExpiredBuildDialog, |       renderExpiredBuildDialog, | ||||||
|       renderMainHeader, |       renderMainHeader, | ||||||
|       renderNetworkStatus, |       renderNetworkStatus, | ||||||
|  | @ -400,21 +386,31 @@ export class LeftPane extends React.Component<PropsType> { | ||||||
|       showArchived, |       showArchived, | ||||||
|     } = this.props; |     } = this.props; | ||||||
| 
 | 
 | ||||||
|  |     /* tslint:disable no-non-null-assertion */ | ||||||
|     return ( |     return ( | ||||||
|       <Measure bounds={true} onResize={this.handleResize}> |       <div className="module-left-pane"> | ||||||
|         {({ measureRef }: MeasuredComponentProps) => ( |         <div className="module-left-pane__header"> | ||||||
|           <div className="module-left-pane" ref={measureRef}> |           {showArchived ? this.renderArchivedHeader() : renderMainHeader()} | ||||||
|             <div className="module-left-pane__header"> |         </div> | ||||||
|               {showArchived ? this.renderArchivedHeader() : renderMainHeader()} |         {renderExpiredBuildDialog()} | ||||||
|             </div> |         {renderNetworkStatus()} | ||||||
|             {renderExpiredBuildDialog()} |         {renderUpdateDialog()} | ||||||
|             {renderNetworkStatus()} |         {renderRelinkDialog()} | ||||||
|             {renderUpdateDialog()} |         {showArchived && ( | ||||||
|             {renderRelinkDialog()} |           <div className="module-left-pane__archive-helper-text" key={0}> | ||||||
|             {this.renderList()} |             {i18n('archiveHelperText')} | ||||||
|           </div> |           </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> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -111,6 +111,7 @@ messageLookup.set('4-guid-guid-guid-guid-guid', { | ||||||
| 
 | 
 | ||||||
| const defaultProps = { | const defaultProps = { | ||||||
|   discussionsLoading: false, |   discussionsLoading: false, | ||||||
|  |   height: 700, | ||||||
|   items: [], |   items: [], | ||||||
|   i18n, |   i18n, | ||||||
|   messagesLoading: false, |   messagesLoading: false, | ||||||
|  | @ -135,6 +136,7 @@ const defaultProps = { | ||||||
|   selectedConversationId: undefined, |   selectedConversationId: undefined, | ||||||
|   selectedMessageId: undefined, |   selectedMessageId: undefined, | ||||||
|   startNewConversation: action('start-new-conversation'), |   startNewConversation: action('start-new-conversation'), | ||||||
|  |   width: 320, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const conversations = [ | const conversations = [ | ||||||
|  |  | ||||||
|  | @ -1,10 +1,5 @@ | ||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { | import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized'; | ||||||
|   AutoSizer, |  | ||||||
|   CellMeasurer, |  | ||||||
|   CellMeasurerCache, |  | ||||||
|   List, |  | ||||||
| } from 'react-virtualized'; |  | ||||||
| import { debounce, get, isNumber } from 'lodash'; | import { debounce, get, isNumber } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { Intl } from './Intl'; | import { Intl } from './Intl'; | ||||||
|  | @ -86,6 +81,8 @@ type PropsHousekeepingType = { | ||||||
|     query: string, |     query: string, | ||||||
|     options: { regionCode: string } |     options: { regionCode: string } | ||||||
|   ) => void; |   ) => void; | ||||||
|  |   height: number; | ||||||
|  |   width: number; | ||||||
| 
 | 
 | ||||||
|   renderMessageSearchResult: (id: string) => JSX.Element; |   renderMessageSearchResult: (id: string) => JSX.Element; | ||||||
| }; | }; | ||||||
|  | @ -121,8 +118,6 @@ type OnScrollParamsType = { | ||||||
| export class SearchResults extends React.Component<PropsType, StateType> { | export class SearchResults extends React.Component<PropsType, StateType> { | ||||||
|   public setFocusToFirstNeeded = false; |   public setFocusToFirstNeeded = false; | ||||||
|   public setFocusToLastNeeded = false; |   public setFocusToLastNeeded = false; | ||||||
|   public mostRecentWidth = 0; |  | ||||||
|   public mostRecentHeight = 0; |  | ||||||
|   public cellSizeCache = new CellMeasurerCache({ |   public cellSizeCache = new CellMeasurerCache({ | ||||||
|     defaultHeight: 80, |     defaultHeight: 80, | ||||||
|     fixedWidth: true, |     fixedWidth: true, | ||||||
|  | @ -454,7 +449,7 @@ export class SearchResults extends React.Component<PropsType, StateType> { | ||||||
|     parent, |     parent, | ||||||
|     style, |     style, | ||||||
|   }: RowRendererParamsType): JSX.Element => { |   }: RowRendererParamsType): JSX.Element => { | ||||||
|     const { items } = this.props; |     const { items, width } = this.props; | ||||||
| 
 | 
 | ||||||
|     const row = items[index]; |     const row = items[index]; | ||||||
| 
 | 
 | ||||||
|  | @ -466,7 +461,7 @@ export class SearchResults extends React.Component<PropsType, StateType> { | ||||||
|           key={key} |           key={key} | ||||||
|           parent={parent} |           parent={parent} | ||||||
|           rowIndex={index} |           rowIndex={index} | ||||||
|           width={this.mostRecentWidth} |           width={width} | ||||||
|         > |         > | ||||||
|           {this.renderRowContents(row)} |           {this.renderRowContents(row)} | ||||||
|         </CellMeasurer> |         </CellMeasurer> | ||||||
|  | @ -530,11 +525,13 @@ export class SearchResults extends React.Component<PropsType, StateType> { | ||||||
| 
 | 
 | ||||||
|   public render() { |   public render() { | ||||||
|     const { |     const { | ||||||
|  |       height, | ||||||
|       i18n, |       i18n, | ||||||
|       items, |       items, | ||||||
|       noResults, |       noResults, | ||||||
|       searchConversationName, |       searchConversationName, | ||||||
|       searchTerm, |       searchTerm, | ||||||
|  |       width, | ||||||
|     } = this.props; |     } = this.props; | ||||||
|     const { scrollToIndex } = this.state; |     const { scrollToIndex } = this.state; | ||||||
| 
 | 
 | ||||||
|  | @ -581,30 +578,21 @@ export class SearchResults extends React.Component<PropsType, StateType> { | ||||||
|         onKeyDown={this.handleKeyDown} |         onKeyDown={this.handleKeyDown} | ||||||
|         onFocus={this.handleFocus} |         onFocus={this.handleFocus} | ||||||
|       > |       > | ||||||
|         <AutoSizer> |         <List | ||||||
|           {({ height, width }) => { |           className="module-search-results__virtual-list" | ||||||
|             this.mostRecentWidth = width; |           deferredMeasurementCache={this.cellSizeCache} | ||||||
|             this.mostRecentHeight = height; |           height={height} | ||||||
| 
 |           items={items} | ||||||
|             return ( |           overscanRowCount={5} | ||||||
|               <List |           ref={this.listRef} | ||||||
|                 className="module-search-results__virtual-list" |           rowCount={this.getRowCount()} | ||||||
|                 deferredMeasurementCache={this.cellSizeCache} |           rowHeight={this.cellSizeCache.rowHeight} | ||||||
|                 height={height} |           rowRenderer={this.renderRow} | ||||||
|                 items={items} |           scrollToIndex={scrollToIndex} | ||||||
|                 overscanRowCount={5} |           tabIndex={-1} | ||||||
|                 ref={this.listRef} |           onScroll={this.onScroll as any} | ||||||
|                 rowCount={this.getRowCount()} |           width={width} | ||||||
|                 rowHeight={this.cellSizeCache.rowHeight} |         /> | ||||||
|                 rowRenderer={this.renderRow} |  | ||||||
|                 scrollToIndex={scrollToIndex} |  | ||||||
|                 tabIndex={-1} |  | ||||||
|                 onScroll={this.onScroll as any} |  | ||||||
|                 width={width} |  | ||||||
|               /> |  | ||||||
|             ); |  | ||||||
|           }} |  | ||||||
|         </AutoSizer> |  | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -11522,7 +11522,7 @@ | ||||||
|     "rule": "React-createRef", |     "rule": "React-createRef", | ||||||
|     "path": "ts/components/SearchResults.js", |     "path": "ts/components/SearchResults.js", | ||||||
|     "line": "        this.listRef = react_1.default.createRef();", |     "line": "        this.listRef = react_1.default.createRef();", | ||||||
|     "lineNumber": 26, |     "lineNumber": 24, | ||||||
|     "reasonCategory": "usageTrusted", |     "reasonCategory": "usageTrusted", | ||||||
|     "updated": "2019-08-09T00:44:31.008Z", |     "updated": "2019-08-09T00:44:31.008Z", | ||||||
|     "reasonDetail": "SearchResults needs to interact with its child List directly" |     "reasonDetail": "SearchResults needs to interact with its child List directly" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Josh Perez
				Josh Perez