Skip to content

Commit

Permalink
Add and handle data changed signal
Browse files Browse the repository at this point in the history
Introduced a new signal to indicate that the data on the server has
been updated and therefore the components should refetch the data and
rerender the components. This PR handles the signal in table-output-component
and timegraph-output-component. We have a use case where data is dynamically
modified on the server, and we want the components to rerender with the new data.
Currently, the user would have to close the active trace panel and reopen it to
see the changes. Added output descriptors as the signal payload.

Signed-off-by: Neel Gondalia <ngondalia@blackberry.com>
  • Loading branch information
ngondalia committed Sep 1, 2023
1 parent caaf230 commit 63c0789
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 29 deletions.
7 changes: 6 additions & 1 deletion packages/base/src/signals/signal-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export declare interface SignalManager {
fireTraceServerStartedSignal(): void;
fireUndoSignal(): void;
fireRedoSignal(): void;
fireOutputDataChanged(outputs: OutputDescriptor[]): void;
fireOpenOverviewOutputSignal(traceId: string): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
firePinView(output: OutputDescriptor, payload?: any): void;
Expand Down Expand Up @@ -73,7 +74,8 @@ export const Signals = {
SAVE_AS_CSV: 'save as csv',
VIEW_RANGE_UPDATED: 'view range updated',
SELECTION_RANGE_UPDATED: 'selection range updated',
REQUEST_SELECTION_RANGE_CHANGE: 'change selection range'
REQUEST_SELECTION_RANGE_CHANGE: 'change selection range',
OUTPUT_DATA_CHANGED: 'output data changed'
};

export class SignalManager extends EventEmitter implements SignalManager {
Expand Down Expand Up @@ -143,6 +145,9 @@ export class SignalManager extends EventEmitter implements SignalManager {
fireRedoSignal(): void {
this.emit(Signals.REDO);
}
fireOutputDataChanged(outputs: OutputDescriptor[]): void {
this.emit(Signals.OUTPUT_DATA_CHANGED, outputs);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
firePinView(output: OutputDescriptor, payload?: any): void {
this.emit(Signals.PIN_VIEW, output, payload);
Expand Down
67 changes: 43 additions & 24 deletions packages/react-components/src/components/table-output-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ import {
} from 'ag-grid-community';
import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper';
import { cloneDeep } from 'lodash';
import { signalManager } from 'traceviewer-base/lib/signals/signal-manager';
import { Signals, signalManager } from 'traceviewer-base/lib/signals/signal-manager';
import { TimelineChart } from 'timeline-chart/lib/time-graph-model';
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 { OutputDescriptor, ResponseStatus } from 'tsp-typescript-client';
import { PaginationBarComponent } from './utils/pagination-bar-component';
import { OptionCheckBoxState, OptionState, OptionType } from './drop-down-component';

type TableOuputState = AbstractOutputState & {
tableColumns: ColDef[];
showToggleColumns: boolean;
tableSize: number;
pagination: boolean;
paginationTotalPages: number;
};

type TableOutputProps = AbstractOutputProps & {
Expand All @@ -50,9 +53,7 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
private columnIds: Array<number> = [];
private fetchColumns = true;
private columnArray = new Array<any>();
private pagination = true;
private paginationPageSize = 250000;
private paginationTotalPages = 0;
private showIndexColumn = false;
private frameworkComponents: any;
private gridApi: GridApi | undefined = undefined;
Expand All @@ -71,7 +72,7 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
private selectEndIndex = -1;
private filterModel: Map<string, string> = new Map<string, string>();
private dataSource: IDatasource;
private tableSize = 0;
private onOutputDataChanged = (outputs: OutputDescriptor[]) => this.doHandleOutputDataChangedSignal(outputs);

static defaultProps: Partial<TableOutputProps> = {
cacheBlockSize: 200,
Expand All @@ -87,7 +88,10 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
this.state = {
outputStatus: ResponseStatus.RUNNING,
tableColumns: [],
showToggleColumns: false
showToggleColumns: false,
tableSize: this.props.nbEvents,
pagination: this.props.nbEvents >= this.paginationPageSize,
paginationTotalPages: Math.floor(this.props.nbEvents / this.paginationPageSize)
};

this.frameworkComponents = {
Expand All @@ -108,11 +112,9 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
const itemCopy = cloneDeep(item);
rowsThisPage[i] = itemCopy;
}
params.successCallback(rowsThisPage, this.tableSize);
params.successCallback(rowsThisPage, this.state.tableSize);
}
};
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 Down Expand Up @@ -189,11 +191,11 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
components={this.frameworkComponents}
enableBrowserTooltips={true}
></AgGridReact>
{this.pagination && (
{this.state.pagination && (
<PaginationBarComponent
paginationPageSize={this.paginationPageSize}
paginationTotalPages={this.paginationTotalPages}
nbEvents={this.props.nbEvents}
paginationTotalPages={this.state.paginationTotalPages}
nbEvents={this.state.tableSize}
gridApi={this?.gridApi}
/>
)}
Expand All @@ -209,6 +211,22 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
this.props.unitController.onSelectionRangeChange(range => {
this.handleTimeSelectionChange(range);
});
signalManager().on(Signals.OUTPUT_DATA_CHANGED, this.onOutputDataChanged);
}

componentWillUnmount(): void {
// TODO: replace with removing the handler from unit controller
// See timeline-chart issue #98
// In the meantime, replace the handler with a noop on unmount
this.handleTimeSelectionChange = () => Promise.resolve();
signalManager().off(Signals.OUTPUT_DATA_CHANGED, this.onOutputDataChanged);
}

doHandleOutputDataChangedSignal(outputs: OutputDescriptor[]): void {
const desc = outputs.find(descriptor => descriptor.id === this.props.outputDescriptor.id);
if (desc !== undefined) {
this.gridApi?.setDatasource(this.dataSource);
}
}

private checkFocus(event: React.FocusEvent<HTMLDivElement, Element>): void {
Expand All @@ -225,22 +243,23 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
}
}

componentWillUnmount(): void {
// TODO: replace with removing the handler from unit controller
// See timeline-chart issue #98
// In the meantime, replace the handler with a noop on unmount
this.handleTimeSelectionChange = () => Promise.resolve();
}

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

this.paginationTotalPages = Math.floor(this.props.nbEvents / this.paginationPageSize);
if (this.state.tableSize !== _prevState.tableSize) {
this.gridApi?.setRowCount(this.state.tableSize);
const newPagination = this.state.tableSize >= this.paginationPageSize;
this.setState({
pagination: newPagination,
paginationTotalPages: Math.floor(this.state.tableSize / this.paginationPageSize)
});
}
}

Expand Down Expand Up @@ -638,7 +657,7 @@ export class TableOutputComponent extends AbstractOutputComponent<TableOutputPro
if (!tspClientResponse.isOk() || !lineResponse) {
return new Array<any>();
}
this.tableSize = lineResponse.model.size;
this.setState({ tableSize: lineResponse.model.size });
return this.modelToRow(lineResponse.model);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { listToTree, getAllExpandedNodeIds, getIndexOfNode, validateNumArray } f
import hash from 'traceviewer-base/lib/utils/value-hash';
import ColumnHeader from './utils/filter-tree/column-header';
import { TimeGraphAnnotationComponent } from 'timeline-chart/lib/components/time-graph-annotation';
import { Entry } from 'tsp-typescript-client';
import { Entry, OutputDescriptor } from 'tsp-typescript-client';
import { isEqual } from 'lodash';
import { convertColorStringToHexNumber } from 'traceviewer-base/lib/utils/convert-color-string-to-hex';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
Expand Down Expand Up @@ -73,6 +73,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
private horizontalContainer: React.RefObject<HTMLDivElement>;
private timeGraphTreeRef: React.RefObject<HTMLDivElement>;
private markerTreeRef: React.RefObject<HTMLDivElement>;
private containerRef: React.RefObject<ReactTimeGraphContainer>;

private tspDataProvider: TspDataProvider;
private styleProvider: StyleProvider;
Expand All @@ -81,9 +82,13 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
private selectedElement: TimeGraphStateComponent | undefined;
private selectedMarkerCategories: string[] | undefined = undefined;
private onSelectionChanged = (payload: { [key: string]: string }) => this.doHandleSelectionChangedSignal(payload);
private onOutputDataChanged = (outputs: OutputDescriptor[]) => this.doHandleOutputDataChangedSignal(outputs);
private pendingSelection: TimeGraphEntry | undefined;

private _debouncedUpdateSearch = debounce(() => this.updateSearchFilter(), 500);
private _debouncedUpdateChart = debounce(() => {
this.chartLayer.updateChart();
}, 500);

constructor(props: TimegraphOutputProps) {
super(props);
Expand Down Expand Up @@ -124,6 +129,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
this.horizontalContainer = React.createRef();
this.timeGraphTreeRef = React.createRef();
this.markerTreeRef = React.createRef();
this.containerRef = React.createRef();
this.handleSearchChange = this.handleSearchChange.bind(this);
this.clearSearchBox = this.clearSearchBox.bind(this);

Expand Down Expand Up @@ -242,14 +248,17 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
componentWillUnmount(): void {
super.componentWillUnmount();
this.unsubscribeToEvents();
this.containerRef.current?.destroyContainer();
}

protected subscribeToEvents(): void {
signalManager().on(Signals.OUTPUT_DATA_CHANGED, this.onOutputDataChanged);
signalManager().on(Signals.THEME_CHANGED, this.onThemeChange);
signalManager().on(Signals.SELECTION_CHANGED, this.onSelectionChanged);
}

protected unsubscribeToEvents(): void {
signalManager().off(Signals.OUTPUT_DATA_CHANGED, this.onOutputDataChanged);
signalManager().off(Signals.THEME_CHANGED, this.onThemeChange);
signalManager().off(Signals.SELECTION_CHANGED, this.onSelectionChanged);
}
Expand Down Expand Up @@ -700,6 +709,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
});
return (
<ReactTimeGraphContainer
ref={this.containerRef}
options={{
id: this.props.traceId + this.props.outputDescriptor.id + 'focusContainer',
height:
Expand Down Expand Up @@ -959,6 +969,14 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
};
}

public doHandleOutputDataChangedSignal = async (outputs: OutputDescriptor[]): Promise<void> => {
const desc = outputs.find(descriptor => descriptor.id === this.props.outputDescriptor.id);
if (desc !== undefined) {
await this.fetchTree();
this._debouncedUpdateChart();
}
};

public onThemeChange = (): void => {
// Simulate a click on the selected row when theme changes.
// This changes the color of the selected row to new theme.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,17 @@ export class ReactTimeGraphContainer extends React.Component<ReactTimeGraphConta
}

componentWillUnmount(): void {
if (this.container) {
this.container.destroy();
}
if (this._resizeHandler) {
this.props.removeWidgetResizeHandler(this._resizeHandler);
}
}

destroyContainer(): void {
if (this.container) {
this.container.destroy();
}
}

shouldComponentUpdate(nextProps: ReactTimeGraphContainer.Props): boolean {
return (
nextProps.options.height !== this.props.options.height ||
Expand Down

0 comments on commit 63c0789

Please sign in to comment.