Skip to content

Commit

Permalink
Fix pagination bar bug
Browse files Browse the repository at this point in the history
With traces that have very large tables, if the event table is opened
before the trace is fully loaded, the pagination bar does not appear.

The problem is that Ag-grid does not make the pagination bar reactive,
so it only responds to the initial values of the table. Updates of the
number of rows are ignored.

The solution is to create a custom pagination bar that can respond
reactively to data being loaded.

Fixes #849

Signed-off-by: Rodrigo Pinto <rodrigo.pinto@calian.ca>
  • Loading branch information
Rodrigoplp-work committed Nov 18, 2022
1 parent e34d2d6 commit d97e257
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 22 deletions.
65 changes: 43 additions & 22 deletions packages/react-components/src/components/table-output-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { CellKeyDownEvent } from 'ag-grid-community/dist/lib/events';
import { TableModel } from 'tsp-typescript-client/lib/models/table';
import { SearchFilterRenderer, CellRenderer, LoadingRenderer } from './table-renderer-components';
import { ResponseStatus } from 'tsp-typescript-client';
import { PaginationBarComponent } from './utils/pagination-bar-component';

type TableOuputState = AbstractOutputState & {
tableColumns: ColDef[];
Expand All @@ -29,7 +30,9 @@ type TableOutputProps = AbstractOutputProps & {

enum Direction {
NEXT,
PREVIOUS
PREVIOUS,
FIRST,
LAST
}

export class TableOutputComponent extends AbstractOutputComponent<TableOutputProps, TableOuputState> {
Expand All @@ -39,6 +42,7 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
private columnArray = new Array<any>();
private pagination = true;
private paginationPageSize = 500000;
private paginationTotalPages = 0;
private showIndexColumn = false;
private frameworkComponents: any;
private gridApi: GridApi | undefined = undefined;
Expand Down Expand Up @@ -100,6 +104,7 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
}
};
this.pagination = this.props.nbEvents >= this.paginationPageSize;
this.paginationTotalPages = Math.floor(this.props.nbEvents / this.paginationPageSize);
this.onEventClick = this.onEventClick.bind(this);
this.onModelUpdated = this.onModelUpdated.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
Expand All @@ -113,15 +118,21 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
tabIndex={-1}
onFocus={event => this.checkFocus(event)}
className={this.props.backgroundTheme === 'light' ? 'ag-theme-balham' : 'ag-theme-balham-dark'}
style={{ height: this.props.style.height, width: this.props.outputWidth }}>
style={{
height: this.props.style.height,
width: this.props.outputWidth,
display: 'flex',
flexDirection: 'column'
}}>
<AgGridReact
columnDefs={this.columnArray}
rowModelType='infinite'
cacheBlockSize={this.props.cacheBlockSize}
maxBlocksInCache={this.props.maxBlocksInCache}
blockLoadDebounceMillis={this.props.blockLoadDebounce}
pagination={this.pagination}
pagination={true}
paginationPageSize={this.paginationPageSize}
suppressPaginationPanel={true}
debug={this.debugMode}
onGridReady={this.onGridReady}
onCellClicked={this.onEventClick}
Expand All @@ -132,6 +143,14 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
enableBrowserTooltips={true}
>
</AgGridReact>
{this.pagination &&
<PaginationBarComponent
paginationPageSize={this.paginationPageSize}
paginationTotalPages={this.paginationTotalPages}
nbEvents={this.props.nbEvents}
gridApi={this?.gridApi}
/>
}
</div>;
}

Expand Down Expand Up @@ -166,7 +185,13 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro

async componentDidUpdate(prevProps: TableOutputProps, _prevState: TableOuputState): Promise<void> {
if (this.props.nbEvents !== prevProps.nbEvents) {
this.gridApi?.setInfiniteRowCount(this.props.nbEvents);
this.gridApi?.setRowCount(this.props.nbEvents);
const newPagination = this.props.nbEvents >= this.paginationPageSize;
if (newPagination !== this.pagination) {
this.pagination = newPagination;
}

this.paginationTotalPages = Math.floor(this.props.nbEvents / this.paginationPageSize);
}
}

Expand Down Expand Up @@ -489,12 +514,10 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro

const index = await this.fetchTableIndex(this.startTimestamp > this.endTimestamp ? this.startTimestamp + BigInt(1) : this.startTimestamp);
if (index) {
const pageNumber = Math.floor(index / this.paginationPageSize);
this.gridApi.paginationGoToPage(pageNumber);
const startIndex = this.startTimestamp > this.endTimestamp ? index - 1 : index;
this.selectStartIndex = this.selectStartIndex === -1 ? startIndex : this.selectStartIndex;
this.selectEndIndex = (this.enableIndexSelection && this.selectEndIndex === -1) ? startIndex : this.selectEndIndex;
this.gridApi.ensureIndexVisible(this.selectStartIndex);
this.updatePageIndex(index);
this.selectRows();
}
} else {
Expand Down Expand Up @@ -691,8 +714,6 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
}

this.gridApi.deselectAll();
let indexPage = 0;
let indexRow = 0;
// consider only rows starting from the current row index and contiguous rows after that
let currRowIndexFound = false;
rowNodes.forEach(rowNode => {
Expand All @@ -711,6 +732,8 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
// only checking 'rowNode.rowIndex' below makes its '=== 0' case false:
if (currRowIndexFound && !isFound && (rowNode.rowIndex || rowNode.rowIndex === 0) && rowNode.data && rowNode.data['isMatched']) {
this.gridApi?.ensureIndexVisible(rowNode.rowIndex);
this.updatePageIndex(rowNode.rowIndex);

this.selectStartIndex = this.selectEndIndex = rowNode.rowIndex;
if (this.timestampCol) {
this.startTimestamp = this.endTimestamp = BigInt(rowNode.data[this.timestampCol]);
Expand All @@ -724,19 +747,12 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
// Notify properties changed
signalManager().fireTooltipSignal(itemPropsObj);
isFound = true;
indexPage = Math.floor(rowNode.rowIndex / this.paginationPageSize);
indexRow = rowNode.rowIndex;
rowNode.setSelected(true);
}
});

if (isFound) {
// Match found in cache
// Change page if match is not on current page
if (indexPage !== this.gridApi.paginationGetCurrentPage()) {
this.gridApi.paginationGoToPage(indexPage);
this.gridApi.ensureIndexVisible(indexRow);
}
this.gridMatched = false;
return;
}
Expand All @@ -746,12 +762,7 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
if (currRowIndex >= 0) {
const data = await this.findMatchIndex(currRowIndex, direction);
if (data !== undefined) {
// Change page if match is not on current page
indexPage = Math.floor(data.index / this.paginationPageSize);
if (indexPage !== this.gridApi.paginationGetCurrentPage()) {
this.gridApi.paginationGoToPage(indexPage);
}
this.gridApi.ensureIndexVisible(data.index);
this.updatePageIndex(data.index);
this.selectStartIndex = this.selectEndIndex = data.index;
if (this.timestampCol) {
this.startTimestamp = this.endTimestamp = BigInt(data.row[this.timestampCol]);
Expand Down Expand Up @@ -861,4 +872,14 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
{this.state.showToggleColumns && <div className='toggle-columns-table'>{this.renderToggleColumnsTable()}</div>}
</React.Fragment>;
}

private updatePageIndex(rowIndex: number): void {
// Change page if match is not on current page
const indexPage = Math.floor(rowIndex / this.paginationPageSize);
if (indexPage !== this.gridApi?.paginationGetCurrentPage()) {
this.gridApi?.paginationGoToPage(indexPage);
this.forceUpdate();
}
this.gridApi?.ensureIndexVisible(rowIndex);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as React from 'react';
import { GridApi } from 'ag-grid-community';
import { numberFormat } from './xy-output-component-utils';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronRight } from '@fortawesome/free-solid-svg-icons';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';

interface PaginationBarProps {
paginationPageSize: number;
paginationTotalPages: number;
nbEvents: number;
gridApi: GridApi | undefined;
}

enum Direction {
NEXT,
PREVIOUS,
FIRST,
LAST
}

export class PaginationBarComponent extends React.Component<PaginationBarProps> {
render(): JSX.Element {
const currentPage = this.props.gridApi?.paginationGetCurrentPage() ? this.props.gridApi?.paginationGetCurrentPage() + 1 : 1;
const firstRowRaw = (currentPage - 1) * this.props.paginationPageSize + 1;
const firstRow = numberFormat(firstRowRaw);
const lastRow = currentPage === this.props.paginationTotalPages + 1 ?
numberFormat(this.props.nbEvents) :
numberFormat(firstRowRaw + this.props.paginationPageSize - 1);

return <div className='pagination-bar'>
<span style={{ margin: 'auto 10px auto auto'}}>
{firstRow} to {lastRow} of {numberFormat(this.props.nbEvents)}
</span>

<button className='pagination-button' onClick={() => this.paginationJumpTo(Direction.FIRST)}>
<FontAwesomeIcon icon={faChevronLeft} />
<FontAwesomeIcon icon={faChevronLeft} />
</button>

<button className='pagination-button' onClick={() => this.paginationJumpTo(Direction.PREVIOUS)}>
<FontAwesomeIcon icon={faChevronLeft} />
</button>

<span style={{ margin: 'auto 10px' }}>
Page {currentPage} of {this.props.paginationTotalPages + 1}
</span>

<button className='pagination-button' onClick={() => this.paginationJumpTo(Direction.NEXT)}>
<FontAwesomeIcon icon={faChevronRight} />
</button>

<button className='pagination-button' style={{ marginRight: '10px' }} onClick={() => this.paginationJumpTo(Direction.LAST)}>
<FontAwesomeIcon icon={faChevronRight} />
<FontAwesomeIcon icon={faChevronRight} />
</button>
</div>;
}

private paginationJumpTo(direction: Direction): void {
switch (direction) {
case Direction.FIRST:
this.props.gridApi?.paginationGoToFirstPage();
break;
case Direction.LAST:
this.props.gridApi?.paginationGoToLastPage();
break;
case Direction.NEXT:
this.props.gridApi?.paginationGoToNextPage();
break;
case Direction.PREVIOUS:
this.props.gridApi?.paginationGoToPreviousPage();
break;
}

this.forceUpdate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,7 @@ export function getClosestPointForScatterPlot(params: GetClosestPointParam): XYP

return undefined;
}

export function numberFormat(rawNumber: number): string{
return new Intl.NumberFormat().format(rawNumber);
}
15 changes: 15 additions & 0 deletions packages/react-components/style/output-components-style.css
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,18 @@ canvas {
margin-top: 5px;
transform: rotate(180deg);
}

.pagination-bar {
width: calc(100% - 2px);
height: 30px;
display: flex;
color: var(--theia-descriptionForeground);
border: thin solid var(--theia-disabledForeground);
border-top: none;
}

.pagination-button {
border: none;
background: transparent;
color: var(--theia-foreground);
}

0 comments on commit d97e257

Please sign in to comment.