Skip to content

Commit

Permalink
Add data tree output component implementation
Browse files Browse the repository at this point in the history
The implementation is the first iteration. It introduces the component
class which uses the tree implementation also used in the time-graph and
XY components. The component is not enabled in this patch.

The tree implementation need some UI/UX improvement, for example for
styling or column resizing which will be done in a separated commit.

Once some improvements are available, the data tree component
will be shown as part of the available views list for a given trace.

Signed-off-by: Bernd Hufmann <Bernd.Hufmann@ericsson.com>
  • Loading branch information
bhufmann committed Dec 14, 2021
1 parent fd8654b commit c905240
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 2 deletions.
122 changes: 122 additions & 0 deletions packages/react-components/src/components/datatree-output-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { AbstractOutputComponent, AbstractOutputProps, AbstractOutputState } from './abstract-output-component';
import * as React from 'react';
import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper';
import { Entry } from 'tsp-typescript-client/lib/models/entry';
import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/responses';
import { EntryTree } from './utils/filtrer-tree/entry-tree';
import { getAllExpandedNodeIds } from './utils/filtrer-tree/utils';
import { TreeNode } from './utils/filtrer-tree/tree-node';
import ColumnHeader from './utils/filtrer-tree/column-header';

type DataTreeOutputProps = AbstractOutputProps & {
};

type DataTreeOuputState = AbstractOutputState & {
selectedSeriesId: number[];
xyTree: Entry[];
collapsedNodes: number[];
orderedNodes: number[];
columns: ColumnHeader[];
};

export class DataTreeOutputComponent extends AbstractOutputComponent<AbstractOutputProps, DataTreeOuputState> {
treeRef: React.RefObject<HTMLDivElement> = React.createRef();

constructor(props: AbstractOutputProps) {
super(props);
this.state = {
outputStatus: ResponseStatus.RUNNING,
selectedSeriesId: [],
xyTree: [],
collapsedNodes: [],
orderedNodes: [],
columns: [{title: 'Name', sortable: true}],
};
}

componentDidMount(): void {
this.waitAnalysisCompletion();
}

async fetchTree(): Promise<ResponseStatus> {
const parameters = QueryHelper.timeQuery([this.props.range.getStart(), this.props.range.getEnd()]);
// TODO: use the data tree endpoint instead of the xy tree endpoint
const tspClientResponse = await this.props.tspClient.fetchXYTree(this.props.traceId, this.props.outputDescriptor.id, parameters);
const treeResponse = tspClientResponse.getModel();
if (tspClientResponse.isOk() && treeResponse) {
if (treeResponse.model) {
const headers = treeResponse.model.headers;
const columns = [];
if (headers && headers.length > 0) {
headers.forEach(header => {
columns.push({title: header.name, sortable: true, tooltip: header.tooltip});
});
} else {
columns.push({title: 'Name', sortable: true});
}
this.setState({
outputStatus: treeResponse.status,
xyTree: treeResponse.model.entries,
columns
});
}
return treeResponse.status;
}
return ResponseStatus.FAILED;
}

renderTree(): React.ReactNode | undefined {
this.onToggleCollapse = this.onToggleCollapse.bind(this);
this.onOrderChange = this.onOrderChange.bind(this);
return this.state.xyTree.length
? <EntryTree
entries={this.state.xyTree}
showCheckboxes={false}
collapsedNodes={this.state.collapsedNodes}
onToggleCollapse={this.onToggleCollapse}
onOrderChange={this.onOrderChange}
headers={this.state.columns}
/>
: undefined
;
}
renderMainArea(): React.ReactNode {
return <React.Fragment>
<div ref={this.treeRef} className='output-component-tree'
style={{ height: this.props.style.height, width: this.props.widthWPBugWorkaround }}
>
{this.renderTree()}
</div>
</React.Fragment>;
}
private onToggleCollapse(id: number, nodes: TreeNode[]) {
let newList = [...this.state.collapsedNodes];

const exist = this.state.collapsedNodes.find(expandId => expandId === id);

if (exist !== undefined) {
newList = newList.filter(collapsed => id !== collapsed);
} else {
newList = newList.concat(id);
}
const orderedIds = getAllExpandedNodeIds(nodes, newList);
this.setState({collapsedNodes: newList, orderedNodes: orderedIds});
}
private onOrderChange(ids: number[]) {
this.setState({orderedNodes: ids});
}

protected async waitAnalysisCompletion(): Promise<void> {
let outputStatus = this.state.outputStatus;
const timeout = 500;
while (this.state && outputStatus === ResponseStatus.RUNNING) {
outputStatus = await this.fetchTree();
await new Promise(resolve => setTimeout(resolve, timeout));
}
}
componentWillUnmount(): void {
// fix Warning: Can't perform a React state update on an unmounted component
this.setState = (_state, _callback) => undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import ReactTooltip from 'react-tooltip';
import { TooltipComponent } from './tooltip-component';
import { TooltipXYComponent } from './tooltip-xy-component';
import { BIMath } from 'timeline-chart/lib/bigint-utils';
import { DataTreeOutputComponent } from './datatree-output-component';

const ResponsiveGridLayout = WidthProvider(Responsive);

Expand Down Expand Up @@ -315,8 +316,9 @@ export class TraceContextComponent extends React.Component<TraceContextProps, Tr
private renderOutputs() {
const layouts = this.generateGridLayout();
const outputs = this.props.outputs;
const showTimeScale = outputs.filter(output => output.type === 'TIME_GRAPH' || output.type === 'TREE_TIME_XY').length > 0;
return <React.Fragment>
{(outputs.length > 1 || outputs[0].type !== 'TABLE') &&
{showTimeScale &&
<div style={{ marginLeft: this.state.style.width - this.state.style.chartWidth }}>
<TimeAxisComponent unitController={this.unitController} style={this.state.style}
addWidgetResizeHandler={this.addWidgetResizeHandler} removeWidgetResizeHandler={this.removeWidgetResizeHandler} />
Expand Down Expand Up @@ -357,12 +359,14 @@ export class TraceContextComponent extends React.Component<TraceContextProps, Tr
return <XYOutputComponent key={output.id} {...outputProps} />;
case 'TABLE':
return <TableOutputComponent key={output.id} {...outputProps} />;
case 'DATA_TREE':
return <DataTreeOutputComponent key={output.id} {...outputProps} />;
default:
return <NullOutputComponent key={output.id} {...outputProps} />;
}
})}
</ResponsiveGridLayout>
{(outputs.length > 1 || outputs[0].type !== 'TABLE') &&
{showTimeScale &&
<div style={{ marginLeft: this.state.style.width - this.state.style.chartWidth }}>
<TimeNavigatorComponent unitController={this.unitController} style={this.state.style}
addWidgetResizeHandler={this.addWidgetResizeHandler} removeWidgetResizeHandler={this.removeWidgetResizeHandler} />
Expand Down

0 comments on commit c905240

Please sign in to comment.