{
};
render(): JSX.Element {
- return
-
+ return
{this.renderCheckbox(this.props.checkedStatus)}
-
- {this.props.name}
-
;
+ ;
}
}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/entry-tree.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/entry-tree.tsx
index 0b7feb874..94cee403b 100644
--- a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/entry-tree.tsx
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/entry-tree.tsx
@@ -2,6 +2,7 @@ import * as React from 'react';
import { Entry } from 'tsp-typescript-client/lib/models/entry';
import { listToTree } from './utils';
import { FilterTree } from './tree';
+import { TreeNode } from './tree-node';
interface EntryTreeProps {
entries: Entry[];
@@ -10,12 +11,18 @@ interface EntryTreeProps {
collapsedNodes: number[];
showFilter: boolean;
onToggleCheck: (ids: number[]) => void;
- onToggleCollapse: (id: number) => void;
+ onToggleCollapse: (id: number, nodes: TreeNode[]) => void;
+ onOrderChange: (ids: number[]) => void;
+ showHeader: boolean;
+ className: string;
}
export class EntryTree extends React.Component {
static defaultProps: Partial = {
- showFilter: true
+ showFilter: true,
+ onOrderChange: () => { /* Nothing to do */ },
+ showHeader: true,
+ className: 'table-tree'
};
constructor(props: EntryTreeProps) {
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/icons.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/icons.tsx
index 2b634599f..5ee0d991c 100644
--- a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/icons.tsx
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/icons.tsx
@@ -1,13 +1,16 @@
import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faChevronDown, faChevronRight, faCheckSquare, faSquare, faMinusSquare } from '@fortawesome/free-solid-svg-icons';
+import { faChevronDown, faChevronRight, faCheckSquare, faSquare, faMinusSquare, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons';
interface iconsShape {
expand: React.ReactNode,
collapse: React.ReactNode,
unchecked: React.ReactNode,
checked: React.ReactNode,
- halfChecked: React.ReactNode
+ halfChecked: React.ReactNode,
+ sort: React.ReactNode,
+ sortDown: React.ReactNode,
+ sortUp: React.ReactNode
}
const icons: iconsShape = {
@@ -15,7 +18,10 @@ const icons: iconsShape = {
collapse: ,
unchecked: ,
checked: ,
- halfChecked:
+ halfChecked: ,
+ sort: ,
+ sortDown: ,
+ sortUp:
};
export default icons;
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/sort.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/sort.tsx
new file mode 100644
index 000000000..2feedd13e
--- /dev/null
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/sort.tsx
@@ -0,0 +1,70 @@
+import icons from './icons';
+import { TreeNode } from './tree-node';
+
+interface SortState {
+ asc: React.ReactNode,
+ desc: React.ReactNode,
+ default: React.ReactNode;
+}
+
+export const sortState: SortState = {
+ asc: icons.sortUp,
+ desc: icons.sortDown,
+ default: icons.sort
+};
+
+export interface SortConfig {
+ column: string;
+ sortState: React.ReactNode;
+}
+
+export const nextSortState = (currentState: React.ReactNode): React.ReactNode => {
+ if (currentState === sortState.default || currentState === sortState.asc) {
+ return sortState.desc;
+ } else if (currentState === sortState.desc) {
+ return sortState.asc;
+ } else {
+ return sortState.default;
+ }
+};
+
+export const sortNodes = (nodes: TreeNode[], sortConfig: SortConfig[]): TreeNode[] => {
+ const sortedNodes = [...nodes];
+ const orderToSort = sortConfig.find((config: SortConfig) => config.sortState !== sortState.default);
+ if (orderToSort) {
+ sortedNodes.sort((node1: TreeNode, node2: TreeNode) => {
+ const key = orderToSort.column;
+ const order = (orderToSort.sortState === sortState.asc) ? 'asc' : 'desc';
+ const value1 = node1[key as keyof TreeNode];
+ const value2 = node2[key as keyof TreeNode];
+ let result = 0;
+ if (!value1 && value2) {
+ result = -1;
+ } else if (value1 && !value2) {
+ result = 1;
+ } else if (!value1 && !value2) {
+ result = 0;
+ } else {
+ if (typeof value1 === 'string' && typeof value2 === 'string') {
+ const comp = value1.localeCompare(value2);
+ result = (order === 'asc') ? -comp : comp;
+ } else {
+ if (value1 < value2) {
+ result = (order === 'asc') ? -1 : 1;
+ } else if (value1 > value2) {
+ result = (order === 'asc') ? 1 : -1;
+ } else {
+ result = 0;
+ }
+ }
+ }
+ return result;
+ });
+ sortedNodes.forEach((node: TreeNode) => {
+ if (node.children.length) {
+ node.children = sortNodes(node.children, sortConfig);
+ }
+ });
+ }
+ return sortedNodes;
+};
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-body.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-body.tsx
new file mode 100644
index 000000000..9f86f57c3
--- /dev/null
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-body.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react';
+import { TreeNode } from './tree-node';
+import { TableRow } from './table-row';
+
+interface TableBodyProps {
+ nodes: TreeNode[];
+ keys: string[];
+ collapsedNodes: number[];
+ isCheckable: boolean;
+ getCheckedStatus: (id: number) => number;
+ onToggleCollapse: (id: number) => void;
+ onToggleCheck: (id: number) => void;
+}
+
+export class TableBody extends React.Component {
+ constructor(props: TableBodyProps) {
+ super(props);
+ }
+
+ createRow = (node: TreeNode): React.ReactNode =>
+ ;
+
+ renderRows = (): React.ReactNode => this.props.nodes.map((node: TreeNode) => this.createRow(node));
+
+ render(): React.ReactNode | undefined {
+ if (!this.props.nodes) {return undefined;}
+
+ return (
+
+ {this.renderRows()}
+
+ );
+ }
+}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-cell.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-cell.tsx
new file mode 100644
index 000000000..44e931f74
--- /dev/null
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-cell.tsx
@@ -0,0 +1,25 @@
+import * as React from 'react';
+import { TreeNode } from './tree-node';
+
+interface TableCellProps {
+ nodeKey: string;
+ node: TreeNode;
+}
+
+export class TableCell extends React.Component {
+ constructor(props: TableCellProps) {
+ super(props);
+ }
+
+ render(): React.ReactNode {
+ const content: React.ReactNode = (this.props.nodeKey !== 'Legend')
+ ? this.props.node[this.props.nodeKey as keyof TreeNode]
+ : undefined;
+ return (
+
+ {this.props.children}
+ {content}
+ |
+ );
+ }
+}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-header.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-header.tsx
new file mode 100644
index 000000000..c62bc61a1
--- /dev/null
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-header.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react';
+import { SortConfig } from './sort';
+
+interface TableHeaderProps {
+ columns: string[];
+ sortableColumns: string[];
+ sortConfig: SortConfig[];
+ onSort: (sortColumn: string) => void;
+}
+
+export class TableHeader extends React.Component {
+ constructor(props: TableHeaderProps) {
+ super(props);
+ }
+
+ handleSortChange = (sortColumn: string): void => {
+ this.props.onSort(sortColumn);
+ };
+
+ toCapitalCase = (name: string): string => (name.charAt(0).toUpperCase() + name.slice(1));
+
+ renderSortIcon = (column: string): React.ReactNode | undefined => {
+ if (this.props.sortableColumns.includes(column)) {
+ const state = this.props.sortConfig.find((config: SortConfig) => config.column === column);
+ return state
+ ? {state.sortState}
+ : undefined;
+ }
+ return undefined;
+ };
+
+ renderHeader = (): React.ReactNode => this.props.columns.map((column: string, index) =>
+ this.handleSortChange(column)}>
+ {this.toCapitalCase(column)}
+ {this.renderSortIcon(column)}
+ |
+ );
+
+ render(): React.ReactNode {
+ return
+
+ {this.renderHeader()}
+
+ ;
+ }
+}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-row.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-row.tsx
new file mode 100644
index 000000000..2c97f6057
--- /dev/null
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table-row.tsx
@@ -0,0 +1,91 @@
+import * as React from 'react';
+import { TreeNode } from './tree-node';
+import { TableCell } from './table-cell';
+import { CheckboxComponent } from './checkbox-component';
+import icons from './icons';
+
+interface TableRowProps {
+ node: TreeNode;
+ keys: string[];
+ level: number;
+ collapsedNodes: number[];
+ isCheckable: boolean;
+ getCheckedStatus: (id: number) => number;
+ onToggleCollapse: (id: number) => void;
+ onToggleCheck: (id: number) => void;
+}
+
+export class TableRow extends React.Component {
+ constructor(props: TableRowProps) {
+ super(props);
+ }
+
+ isCollapsed = (): boolean => this.props.collapsedNodes.includes(this.props.node.id);
+
+ private handleCollapse = (): void => {
+ this.props.onToggleCollapse(this.props.node.id);
+ };
+
+ renderToggleCollapse = (): React.ReactNode => {
+ const marginLeft = this.props.level * 15 + 'px';
+ return (
+ (this.props.node.children.length === 0)
+ ?
+ :
+ {(this.isCollapsed() ? icons.expand : icons.collapse)}
+
+ );
+ };
+
+ renderCheckbox = (): React.ReactNode => {
+ const checkedStatus = this.props.getCheckedStatus(this.props.node.id);
+ return this.props.isCheckable
+ ?
+ : ;
+ };
+
+ renderRow = (): React.ReactNode => this.props.keys.map((nodeKey: string, index) => {
+ let toggleCollapse: React.ReactNode;
+ let toggleCheck: React.ReactNode;
+ if (index === 0) {
+ toggleCollapse = this.renderToggleCollapse();
+ toggleCheck = this.renderCheckbox();
+ }
+
+ return
+ {toggleCollapse}
+ {toggleCheck}
+ ;
+ });
+
+ renderChildren = (): React.ReactNode | undefined => {
+ if (this.props.node.children.length && !this.isCollapsed()) {
+ return this.props.node.children.map((child: TreeNode) =>
+
+ );
+ }
+ return undefined;
+ };
+
+ render(): React.ReactNode | undefined {
+ if (!this.props.node) {return undefined;}
+ const children = this.renderChildren();
+
+ return (
+
+ {this.renderRow()}
+ {children}
+
+ );
+ }
+}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table.tsx
new file mode 100644
index 000000000..f42b956db
--- /dev/null
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/table.tsx
@@ -0,0 +1,91 @@
+import * as React from 'react';
+import { TreeNode } from './tree-node';
+import { TableHeader } from './table-header';
+import { TableBody } from './table-body';
+import { SortConfig, sortState, nextSortState, sortNodes } from './sort';
+
+interface TableProps {
+ nodes: TreeNode[];
+ collapsedNodes: number[];
+ isCheckable: boolean;
+ sortConfig: SortConfig[];
+ getCheckedStatus: (id: number) => number;
+ onToggleCollapse: (id: number) => void;
+ onToggleCheck: (id: number) => void;
+ onSort: (sortedNodes: TreeNode[]) => void;
+ onSortConfigChange: (sortConfig: SortConfig[]) => void;
+ showHeader: boolean;
+ className: string;
+}
+
+export class Table extends React.Component {
+
+ private keys: string[] = [];
+ private sortableColumns: string[] = [];
+
+ constructor(props: TableProps) {
+ super(props);
+ this.keys = this.getTableHeaderKeys();
+ this.sortableColumns = this.getSortableColumns();
+ this.initializeSortConfig();
+ }
+
+ initializeSortConfig = (): void => {
+ const config: SortConfig[] = [];
+ this.sortableColumns.forEach((column: string) => {
+ config.push({column: column, sortState: sortState.default});
+ });
+ this.props.onSortConfigChange(config);
+ };
+
+ getTableHeaderKeys = (): string[] => {
+ // TODO Re-factor this once the server can properly send column headers
+ const keys = Object.keys(this.props.nodes[0]);
+ const excludeKeys = ['isRoot', 'children', 'style', 'metadata', 'labels', 'id', 'parentId'];
+ return keys.filter(key => !excludeKeys.includes(key)).concat('Legend');
+ };
+
+ getSortableColumns = (): string[] => {
+ const sortableColumns: string[] = [];
+ this.keys.forEach((key: string) => {
+ if (typeof this.props.nodes[0][key as keyof TreeNode] === 'string' || typeof this.props.nodes[0][key as keyof TreeNode] === 'number') {
+ sortableColumns.push(key);
+ }
+ });
+ return sortableColumns;
+ };
+
+ onSortChange = (sortColumn: string): void => {
+ let newSortConfigs: SortConfig[] = [...this.props.sortConfig];
+ newSortConfigs = newSortConfigs.map((config: SortConfig) => {
+ if (config.column === sortColumn) {
+ return {...config, sortState: nextSortState(config.sortState)};
+ } else {
+ return {...config, sortState: sortState.default};
+ }
+ });
+ const newSortedNodes = sortNodes(this.props.nodes, newSortConfigs);
+ this.props.onSortConfigChange(newSortConfigs);
+ this.props.onSort(newSortedNodes);
+ };
+
+ render(): JSX.Element {
+ return (
+
+
+ {this.props.showHeader && }
+
+
+
+ );
+ }
+}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree-node.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree-node.tsx
index aea4a5f81..d01e54fa7 100644
--- a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree-node.tsx
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree-node.tsx
@@ -1,7 +1,3 @@
-import * as React from 'react';
-import { CheckboxComponent } from './checkbox-component';
-import icons from './icons';
-
export interface TreeNode {
id: number;
parentId: number;
@@ -9,77 +5,3 @@ export interface TreeNode {
children: Array;
isRoot: boolean;
}
-
-export const defaultTreeNode: TreeNode = {
- id: -1,
- parentId: -1,
- name: '',
- children: [],
- isRoot: false
-};
-
-interface TreeNodeComponentProps {
- node: TreeNode;
- checkedStatus: number;
- level: number;
- padding: number;
- isCheckable: boolean;
- collapsed: boolean;
- children: JSX.Element | undefined;
- onToggleCollapse: (id: number) => void;
- onToggleCheck: (id: number) => void;
-}
-
-export class TreeNodeComponent extends React.Component {
- constructor(props: TreeNodeComponentProps) {
- super(props);
- }
-
- private isLeaf = (): boolean => this.props.node.children.length === 0;
-
- private handleCollapse = (): void => {
- this.props.onToggleCollapse(this.props.node.id);
- };
-
- renderChildren = (): JSX.Element | undefined => {
- if (this.props.collapsed) {
- return undefined;
- }
- return this.props.children;
- };
-
- render(): JSX.Element {
- return (
-
-
- { this.isLeaf()
- ?
- :
- {(this.props.collapsed ? icons.expand : icons.collapse)}
-
- }
-
- { this.props.isCheckable
- ?
- : {this.props.node.name}
- }
-
- {this.renderChildren()}
-
- );
- }
-}
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree.tsx
index 8f88aa2e9..e477c6eb0 100644
--- a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree.tsx
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/tree.tsx
@@ -1,7 +1,10 @@
import * as React from 'react';
-import { TreeNode, TreeNodeComponent } from './tree-node';
+import { TreeNode } from './tree-node';
import { Message } from './message';
import { Filter } from './filter';
+import { Table } from './table';
+import { getAllExpandedNodeIds } from './utils';
+import { SortConfig, sortNodes } from './sort';
interface FilterTreeProps {
nodes: TreeNode[];
@@ -10,11 +13,15 @@ interface FilterTreeProps {
checkedSeries: number[]; // Optional
collapsedNodes: number[];
onToggleCheck: (ids: number[]) => void; // Optional
- onToggleCollapse: (id: number) => void;
+ onToggleCollapse: (id: number, nodes: TreeNode[]) => void;
+ onOrderChange: (ids: number[]) => void;
+ showHeader: boolean;
+ className: string;
}
interface FilterTreeState {
filteredNodes: TreeNode[];
+ sortConfig: SortConfig[];
}
export class FilterTree extends React.Component {
@@ -22,12 +29,14 @@ export class FilterTree extends React.Component { /* Nothing to do */ },
+ onOrderChange: () => { /* Nothing to do */ },
};
constructor(props: FilterTreeProps) {
super(props);
this.state = {
- filteredNodes: this.props.nodes
+ filteredNodes: this.props.nodes,
+ sortConfig: []
};
}
@@ -59,7 +68,17 @@ export class FilterTree extends React.Component {
- this.props.onToggleCollapse(id);
+ const nodes = sortNodes(this.state.filteredNodes, this.state.sortConfig);
+ this.props.onToggleCollapse(id, nodes);
+ };
+
+ handleOrderChange = (nodes: TreeNode[]): void => {
+ const ids = getAllExpandedNodeIds(nodes, this.props.collapsedNodes);
+ this.props.onOrderChange(ids);
+ };
+
+ handleSortConfigChange = (sortConfig: SortConfig[]): void => {
+ this.setState({sortConfig: sortConfig});
};
getAllChildrenIds = (node: TreeNode, ids: number[]): number[] => {
@@ -147,13 +166,16 @@ export class FilterTree extends React.Component {
const visibleNodes = node.children.filter((child: TreeNode) =>this.getNode(this.state.filteredNodes, child.id) !== undefined);
- const allChildrenChecked = visibleNodes.every((child: TreeNode) => {
- let isChecked = this.isNodeChecked(child.id);
- if (child.children.length) {
- isChecked = isChecked && this.isEveryChildChecked(child);
- }
- return isChecked;
- });
+ let allChildrenChecked = false;
+ if (visibleNodes.length) {
+ allChildrenChecked = visibleNodes.every((child: TreeNode) => {
+ let isChecked = this.isNodeChecked(child.id);
+ if (child.children.length) {
+ isChecked = isChecked && this.isEveryChildChecked(child);
+ }
+ return isChecked;
+ });
+ }
const leaves = this.getAllLeavesId(this.state.filteredNodes, []);
const allLeavesChecked = leaves.every((id: number) => this.isNodeChecked(id));
return allChildrenChecked || allLeavesChecked;
@@ -187,6 +209,7 @@ export class FilterTree extends React.Component this.getMatchingIds(node, filter, matchedIds));
filteredTree = this.filterTree(this.props.nodes, matchedIds);
this.setState({filteredNodes: filteredTree});
+ this.handleOrderChange(filteredTree);
};
getMatchingIds = (node: TreeNode, filter: string, foundIds: number[]): boolean => {
@@ -213,41 +236,25 @@ export class FilterTree extends React.Component
)=> this.handleFilterChanged(e.target.value)}/>
{this.state.filteredNodes.length
- ? this.renderTreeNodes(this.state.filteredNodes)
+ ? this.renderTable(this.state.filteredNodes)
: No entries found
}
;
- renderTreeNodes = (nodes: TreeNode[], level = 0): JSX.Element | undefined => {
- const treeNodes = nodes.map((node: TreeNode) => {
- const children = node.children.length > 0 ? this.renderTreeNodes(node.children, level+1) : undefined;
- const checkedStatus = this.getCheckedStatus(node.id);
-
- if (!node.isRoot && this.isCollapsed(node.parentId)) {
- return undefined;
- }
- return (
-
- {children}
-
- );
- });
- return (
-
- );
- };
+ renderTable = (nodes: TreeNode[]): JSX.Element =>
+ ;
render(): JSX.Element | undefined {
if (!this.props.nodes) {return undefined;}
@@ -256,7 +263,7 @@ export class FilterTree extends React.Component
{ this.props.showFilter
? this.renderFilterTree()
- : this.renderTreeNodes(rootNodes)
+ : this.renderTable(rootNodes)
}
;
diff --git a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/utils.tsx b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/utils.tsx
index 744d254cd..2a64d0565 100644
--- a/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/utils.tsx
+++ b/viewer-prototype/src/browser/trace-viewer/components/utils/filtrer-tree/utils.tsx
@@ -2,11 +2,10 @@ import { Entry } from 'tsp-typescript-client/lib/models/entry';
import { TreeNode } from './tree-node';
const entryToTreeNode = (entry: Entry) => ({
- id: entry.id,
- parentId: entry.parentId,
name: ((entry.labels) && (entry.labels.length > 0)) ? entry.labels[0] : '',
isRoot: false,
- children: []
+ children: [],
+ ...entry
} as TreeNode);
export const listToTree = (list: Entry[]): TreeNode[] => {
diff --git a/viewer-prototype/src/browser/trace-viewer/components/xy-output-component.tsx b/viewer-prototype/src/browser/trace-viewer/components/xy-output-component.tsx
index 3c722f21e..01b373e34 100644
--- a/viewer-prototype/src/browser/trace-viewer/components/xy-output-component.tsx
+++ b/viewer-prototype/src/browser/trace-viewer/components/xy-output-component.tsx
@@ -9,12 +9,15 @@ import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/respon
import { XYSeries } from 'tsp-typescript-client/lib/models/xy';
import Chart = require('chart.js');
import { EntryTree } from './utils/filtrer-tree/entry-tree';
+import { getAllExpandedNodeIds } from './utils/filtrer-tree/utils';
+import { TreeNode } from './utils/filtrer-tree/tree-node';
type XYOuputState = AbstractOutputState & {
selectedSeriesId: number[];
XYTree: Entry[];
checkedSeries: number[];
collapsedNodes: number[];
+ orderedNodes: number[];
XYData: any;
};
@@ -32,6 +35,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent
: undefined
;
@@ -172,7 +178,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent expandId === id);
@@ -182,7 +188,12 @@ export class XYOutputComponent extends AbstractTreeOutputComponent(this.props.traceId, this.props.outputDescriptor.id, xyTreeParameters)).getModel();
const treeModel = xyTreeResponse.model;
- this.buildTreeNodes(treeModel.entries);
+ if (treeModel) {
+ this.buildTreeNodes(treeModel.entries);
+ }
}
private async updateXY() {