diff --git a/packages/base/src/experiment-manager.ts b/packages/base/src/experiment-manager.ts index 3383d7fe6..c5e65e923 100644 --- a/packages/base/src/experiment-manager.ts +++ b/packages/base/src/experiment-manager.ts @@ -8,18 +8,16 @@ import { TspClientResponse } from 'tsp-typescript-client/lib/protocol/tsp-client import { signalManager, Signals } from './signals/signal-manager'; export class ExperimentManager { - private fOpenExperiments: Map = new Map(); private fTspClient: TspClient; private fTraceManager: TraceManager; - constructor( - tspClient: TspClient, - traceManager: TraceManager - ) { + constructor(tspClient: TspClient, traceManager: TraceManager) { this.fTspClient = tspClient; this.fTraceManager = traceManager; - signalManager().on(Signals.EXPERIMENT_DELETED, (experiment: Experiment) => this.onExperimentDeleted(experiment)); + signalManager().on(Signals.EXPERIMENT_DELETED, (experiment: Experiment) => + this.onExperimentDeleted(experiment) + ); } /** @@ -82,10 +80,12 @@ export class ExperimentManager { } const tryCreate = async function (tspClient: TspClient, retry: number): Promise> { - return tspClient.createExperiment(new Query({ - 'name': retry === 0 ? name : name + '(' + retry + ')', - 'traces': traceURIs - })); + return tspClient.createExperiment( + new Query({ + name: retry === 0 ? name : name + '(' + retry + ')', + traces: traceURIs + }) + ); }; let tryNb = 0; let experimentResponse: TspClientResponse | undefined; diff --git a/packages/base/src/lazy-tsp-client.ts b/packages/base/src/lazy-tsp-client.ts index c85b35457..6a96cb893 100644 --- a/packages/base/src/lazy-tsp-client.ts +++ b/packages/base/src/lazy-tsp-client.ts @@ -8,9 +8,9 @@ import { TspClient } from 'tsp-typescript-client'; * Only keep methods, discard properties. */ export type LazyTspClient = { - [K in keyof TspClient]: TspClient[K] extends (...args: infer A) => (infer R | Promise) + [K in keyof TspClient]: TspClient[K] extends (...args: infer A) => infer R | Promise ? (...args: A) => Promise - : never // Discard property. + : never; // Discard property. }; export type LazyTspClientFactory = typeof LazyTspClientFactory; @@ -25,7 +25,7 @@ export function LazyTspClientFactory(url: Promise): TspClient { let method = target[property]; if (!method) { target[property] = method = async (...args: any[]) => { - const tspClient = await tspClientPromise as any; + const tspClient = (await tspClientPromise) as any; return tspClient[property](...args); }; } diff --git a/packages/base/src/message-manager.ts b/packages/base/src/message-manager.ts index 7778b013b..6398c5664 100644 --- a/packages/base/src/message-manager.ts +++ b/packages/base/src/message-manager.ts @@ -1,7 +1,7 @@ export enum MessageCategory { TRACE_CONTEXT, SERVER_MESSAGE, - SERVER_STATUS, + SERVER_STATUS } export enum MessageSeverity { @@ -18,22 +18,19 @@ export interface StatusMessage { } export declare interface MessageManager { - addStatusMessage(messageKey: string, message: StatusMessage): void; removeStatusMessage(messageKey: string): void; - } export class MessageManager implements MessageManager { - - addStatusMessage(messageKey: string, {text, - category = MessageCategory.SERVER_MESSAGE, - severity = MessageSeverity.INFO }: StatusMessage): void { + addStatusMessage( + messageKey: string, + { text, category = MessageCategory.SERVER_MESSAGE, severity = MessageSeverity.INFO }: StatusMessage + ): void { console.log('New status message', messageKey, text, category, severity); } removeStatusMessage(messageKey: string): void { console.log('Removing status message status message', messageKey); } - } diff --git a/packages/base/src/signals/signal-manager.ts b/packages/base/src/signals/signal-manager.ts index dd7878bc7..7f5c24eef 100644 --- a/packages/base/src/signals/signal-manager.ts +++ b/packages/base/src/signals/signal-manager.ts @@ -26,7 +26,7 @@ export declare interface SignalManager { fireResetZoomSignal(): void; fireMarkerCategoriesFetchedSignal(): void; fireMarkerSetsFetchedSignal(): void; - fireMarkerCategoryClosedSignal(payload: { traceViewerId: string, markerCategory: string }): void; + fireMarkerCategoryClosedSignal(payload: { traceViewerId: string; markerCategory: string }): void; fireTraceServerStartedSignal(): void; fireUndoSignal(): void; fireRedoSignal(): void; @@ -35,8 +35,8 @@ export declare interface SignalManager { firePinView(output: OutputDescriptor, payload?: any): void; // eslint-disable-next-line @typescript-eslint/no-explicit-any fireUnPinView(output: OutputDescriptor, payload?: any): void; - fireOverviewOutputSelectedSignal(payload: { traceId: string, outputDescriptor: OutputDescriptor}): void; - fireSaveAsCsv(payload: {traceId: string, data: string}): void; + fireOverviewOutputSelectedSignal(payload: { traceId: string; outputDescriptor: OutputDescriptor }): void; + fireSaveAsCsv(payload: { traceId: string; data: string }): void; fireSelectionRangeUpdated(payload: TimeRangeUpdatePayload): void; fireViewRangeUpdated(payload: TimeRangeUpdatePayload): void; fireRequestSelectionRangeChange(payload: TimeRangeUpdatePayload): void; @@ -73,7 +73,7 @@ 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' }; export class SignalManager extends EventEmitter implements SignalManager { @@ -104,13 +104,13 @@ export class SignalManager extends EventEmitter implements SignalManager { fireOutputAddedSignal(payload: OutputAddedSignalPayload): void { this.emit(Signals.OUTPUT_ADDED, payload); } - fireItemPropertiesSignalUpdated(properties?: { [key: string]: string; }): void { + fireItemPropertiesSignalUpdated(properties?: { [key: string]: string }): void { this.emit(Signals.ITEM_PROPERTIES_UPDATED, properties); } fireThemeChangedSignal(theme: string): void { this.emit(Signals.THEME_CHANGED, theme); } - fireSelectionChangedSignal(payload: { [key: string]: string; }): void { + fireSelectionChangedSignal(payload: { [key: string]: string }): void { this.emit(Signals.SELECTION_CHANGED, payload); } fireCloseTraceViewerTabSignal(traceUUID: string): void { @@ -131,7 +131,7 @@ export class SignalManager extends EventEmitter implements SignalManager { fireMarkerSetsFetchedSignal(): void { this.emit(Signals.MARKERSETS_FETCHED); } - fireMarkerCategoryClosedSignal(payload: { traceViewerId: string, markerCategory: string }): void { + fireMarkerCategoryClosedSignal(payload: { traceViewerId: string; markerCategory: string }): void { this.emit(Signals.MARKER_CATEGORY_CLOSED, payload); } fireTraceServerStartedSignal(): void { @@ -154,10 +154,10 @@ export class SignalManager extends EventEmitter implements SignalManager { fireOpenOverviewOutputSignal(traceId: string): void { this.emit(Signals.OPEN_OVERVIEW_OUTPUT, traceId); } - fireOverviewOutputSelectedSignal(payload: { traceId: string, outputDescriptor: OutputDescriptor}): void { + fireOverviewOutputSelectedSignal(payload: { traceId: string; outputDescriptor: OutputDescriptor }): void { this.emit(Signals.OVERVIEW_OUTPUT_SELECTED, payload); } - fireSaveAsCsv(payload: {traceId: string, data: string}): void { + fireSaveAsCsv(payload: { traceId: string; data: string }): void { this.emit(Signals.SAVE_AS_CSV, payload); } fireViewRangeUpdated(payload: TimeRangeUpdatePayload): void { @@ -169,7 +169,6 @@ export class SignalManager extends EventEmitter implements SignalManager { fireRequestSelectionRangeChange(payload: TimeRangeUpdatePayload): void { this.emit(Signals.REQUEST_SELECTION_RANGE_CHANGE, payload); } - } let instance: SignalManager = new SignalManager(); diff --git a/packages/base/src/signals/time-range-data-signal-payloads.tsx b/packages/base/src/signals/time-range-data-signal-payloads.tsx index 789ae7e2f..fa331537d 100644 --- a/packages/base/src/signals/time-range-data-signal-payloads.tsx +++ b/packages/base/src/signals/time-range-data-signal-payloads.tsx @@ -1,6 +1,6 @@ import { TimeRange } from '../utils/time-range'; export interface TimeRangeUpdatePayload { - experimentUUID: string, - timeRange?: TimeRange, + experimentUUID: string; + timeRange?: TimeRange; } diff --git a/packages/base/src/trace-manager.ts b/packages/base/src/trace-manager.ts index d28fa4d48..a28ef0ed4 100644 --- a/packages/base/src/trace-manager.ts +++ b/packages/base/src/trace-manager.ts @@ -1,19 +1,15 @@ - import { Trace } from 'tsp-typescript-client/lib/models/trace'; import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; import { Query } from 'tsp-typescript-client/lib/models/query/query'; import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor'; import { TspClientResponse } from 'tsp-typescript-client/lib/protocol/tsp-client-response'; -import { signalManager} from './signals/signal-manager'; +import { signalManager } from './signals/signal-manager'; export class TraceManager { - private fOpenTraces: Map = new Map(); private fTspClient: TspClient; - constructor( - tspClient: TspClient - ) { + constructor(tspClient: TspClient) { this.fTspClient = tspClient; } @@ -74,10 +70,12 @@ export class TraceManager { const name = traceName ? traceName : traceURI.replace(/\/$/, '').replace(/(.*\/)?/, ''); const tryOpen = async function (tspClient: TspClient, retry: number): Promise> { - return tspClient.openTrace(new Query({ - 'name': retry === 0 ? name : name + '(' + retry + ')', - 'uri': traceURI - })); + return tspClient.openTrace( + new Query({ + name: retry === 0 ? name : name + '(' + retry + ')', + uri: traceURI + }) + ); }; let tryNb = 0; let traceResponse: TspClientResponse | undefined; diff --git a/packages/base/src/utils/convert-color-string-to-hex.ts b/packages/base/src/utils/convert-color-string-to-hex.ts index d91993e72..5cb3848f9 100644 --- a/packages/base/src/utils/convert-color-string-to-hex.ts +++ b/packages/base/src/utils/convert-color-string-to-hex.ts @@ -12,10 +12,14 @@ export function convertColorStringToHexNumber(rgb: string): number { // Working with RGB String const match = rgb.match(/\d+/g); if (match) { - string = '0x' + match.map(x => { - x = parseInt(x).toString(16); - return (x.length === 1) ? '0' + x : x; - }).join(''); + string = + '0x' + + match + .map(x => { + x = parseInt(x).toString(16); + return x.length === 1 ? '0' + x : x; + }) + .join(''); string = string.slice(0, 8); } } diff --git a/packages/base/src/utils/value-hash.ts b/packages/base/src/utils/value-hash.ts index 2d3936806..b4c28cc16 100644 --- a/packages/base/src/utils/value-hash.ts +++ b/packages/base/src/utils/value-hash.ts @@ -14,7 +14,7 @@ const hash = (str: string): number => { let hashCode = 0; for (let i = 0; i < str.length; i++) { const chr = str.charCodeAt(i); - hashCode = ((hashCode << 5) - hashCode) + chr; + hashCode = (hashCode << 5) - hashCode + chr; hashCode |= 0; // Convert to 32bit integer } return hashCode; diff --git a/packages/react-components/src/components/__tests__/table-renderer-components.test.tsx b/packages/react-components/src/components/__tests__/table-renderer-components.test.tsx index 596293736..c62a58234 100644 --- a/packages/react-components/src/components/__tests__/table-renderer-components.test.tsx +++ b/packages/react-components/src/components/__tests__/table-renderer-components.test.tsx @@ -10,7 +10,6 @@ import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; import { ColDef, Column, ColumnApi, GridApi, IRowModel, RowNode } from 'ag-grid-community'; describe('', () => { - let tableComponent: any; const ref = (el: TableOutputComponent | undefined | null): void => { tableComponent = el; @@ -50,7 +49,7 @@ describe('', () => { start: BigInt(0), end: BigInt(0), final: true, - compatibleProviders: [] + compatibleProviders: [] }, markerCategories: undefined, markerSetId: '0', @@ -59,7 +58,7 @@ describe('', () => { viewRange: new TimeRange(BigInt(0), BigInt(0)), selectionRange: undefined, onOutputRemove: () => 0, - unitController: new TimeGraphUnitController(BigInt(0), { start: BigInt(0), end: BigInt(0)}), + unitController: new TimeGraphUnitController(BigInt(0), { start: BigInt(0), end: BigInt(0) }), backgroundTheme: 'light', tooltipXYComponent: null, outputWidth: 0 @@ -67,19 +66,19 @@ describe('', () => { const tableOutputComponentState = [ { - "headerName":"Trace", - "field":"0", - "width":0, - "resizable":true, - "cellRenderer":"cellRenderer", - "cellRendererParams":{"filterModel":{},"searchResultsColor":"#cccc00"}, - "suppressMenu":true, - "filter":"agTextColumnFilter", - "floatingFilter":true, - "floatingFilterComponent":"searchFilterRenderer", - "floatingFilterComponentParams":{"suppressFilterButton":true,"colName":"0"}, - "icons":{"filter":""}, - "tooltipField":"0" + headerName: 'Trace', + field: '0', + width: 0, + resizable: true, + cellRenderer: 'cellRenderer', + cellRendererParams: { filterModel: {}, searchResultsColor: '#cccc00' }, + suppressMenu: true, + filter: 'agTextColumnFilter', + floatingFilter: true, + floatingFilterComponent: 'searchFilterRenderer', + floatingFilterComponentParams: { suppressFilterButton: true, colName: '0' }, + icons: { filter: '' }, + tooltipField: '0' } ]; @@ -89,10 +88,8 @@ describe('', () => { const table: TableOutputComponent = tableComponent; act(() => { // fire events that update state - table.setState( - {tableColumns: tableOutputComponentState} - ); - }); + table.setState({ tableColumns: tableOutputComponentState }); + }); // Renders with provided props expect(table.state.tableColumns).toEqual(tableOutputComponentState); @@ -124,14 +121,14 @@ describe('', () => { const mockShowParentFilter = jest.fn(); test('Empty search filter renderer', () => { - const searchFilter = create(', () => { filterModifiedCallback: mockFilterModifiedCallback, valueGetter: mockValueGetter, doesRowPassOtherFilter: mockDoesRowPassOtherFilter, - context: '', - } - } - currentParentModel={mockCurrentParentModel} - parentFilterInstance={mockParentilterInstance} - suppressFilterButton={false} - api={new GridApi()} - columnApi={new ColumnApi()} - showParentFilter={mockShowParentFilter} - context={''} - filterModel= {new Map()} - />).toJSON(); + context: '' + }} + currentParentModel={mockCurrentParentModel} + parentFilterInstance={mockParentilterInstance} + suppressFilterButton={false} + api={new GridApi()} + columnApi={new ColumnApi()} + showParentFilter={mockShowParentFilter} + context={''} + filterModel={new Map()} + /> + ).toJSON(); expect(searchFilter).toMatchSnapshot(); }); test('Search filter renderer key interactions', () => { - render(', () => { filterModifiedCallback: mockFilterModifiedCallback, valueGetter: mockValueGetter, doesRowPassOtherFilter: mockDoesRowPassOtherFilter, - context: '', - } - } - currentParentModel={mockCurrentParentModel} - parentFilterInstance={mockParentilterInstance} - suppressFilterButton={false} - api={new GridApi()} - columnApi={new ColumnApi()} - showParentFilter={mockShowParentFilter} - context={''} - filterModel={new Map()} - />); + context: '' + }} + currentParentModel={mockCurrentParentModel} + parentFilterInstance={mockParentilterInstance} + suppressFilterButton={false} + api={new GridApi()} + columnApi={new ColumnApi()} + showParentFilter={mockShowParentFilter} + context={''} + filterModel={new Map()} + /> + ); const parentDiv = screen.getByTestId('search-filter-element-parent'); fireEvent.click(parentDiv); const input = screen.getByTestId('search-filter-element-input'); - fireEvent.keyDown(input, {key: 'Enter', code: 'Enter', charCode: 13}); + fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', charCode: 13 }); expect(mockOnClickNext).toHaveBeenCalledTimes(1); - fireEvent.keyDown(input, {key: 'Escape', code: 'Escape', charCode: 27}); + fireEvent.keyDown(input, { key: 'Escape', code: 'Escape', charCode: 27 }); expect(mockOnFilterChange).toHaveBeenCalledTimes(1); }); @@ -224,20 +221,20 @@ describe('', () => { throw new Error('Function not implemented.'); }, searchResultsColor: '#FFFF00', - filterModel: new Map() - } + filterModel: new Map() + }; test('Cell renderer', () => { - const cellRenderer = create().toJSON(); + const cellRenderer = create().toJSON(); expect(cellRenderer).toMatchSnapshot(); }); test('Cell renderer with search selection', () => { cellRendererProps.colDef.field = 'testField'; cellRendererProps.filterModel.set('testField', 'test'); - cellRendererProps.data = {'isMatched' : true}; + cellRendererProps.data = { isMatched: true }; - const cellRenderer = create().toJSON(); + const cellRenderer = create().toJSON(); expect(cellRenderer).toMatchSnapshot(); }); @@ -245,15 +242,14 @@ describe('', () => { delete cellRendererProps.filterModel; delete cellRendererProps.searchResultsColor; - const loadingRenderer = create().toJSON(); + const loadingRenderer = create().toJSON(); expect(loadingRenderer).toMatchSnapshot(); }); test('Loading renderer in loading', () => { cellRendererProps.value = undefined; - const loadingRenderer = create().toJSON(); + const loadingRenderer = create().toJSON(); expect(loadingRenderer).toMatchSnapshot(); }); - -}) +}); diff --git a/packages/react-components/src/components/__tests__/tooltip-component.test.tsx b/packages/react-components/src/components/__tests__/tooltip-component.test.tsx index 7f347dc64..6108626bc 100644 --- a/packages/react-components/src/components/__tests__/tooltip-component.test.tsx +++ b/packages/react-components/src/components/__tests__/tooltip-component.test.tsx @@ -3,89 +3,88 @@ import { cleanup, fireEvent, render, screen } from '@testing-library/react'; import { TooltipComponent } from '../tooltip-component'; const model = { - id: '123', - range: { start: 1, end: 10 }, - label: 'model' -} + id: '123', + range: { start: 1, end: 10 }, + label: 'model' +}; const tooltip = new TooltipComponent(10); tooltip.setState = jest.fn(); describe('Tooltip component', () => { + let axisComponent: any; + const ref = (el: TooltipComponent | undefined | null): void => { + axisComponent = el; + }; - let axisComponent: any; - const ref = (el: TooltipComponent | undefined | null): void => { - axisComponent = el; - }; + beforeEach(() => { + axisComponent = null; + }); - beforeEach(() => { - axisComponent = null; - }); + afterEach(() => { + cleanup(); + jest.clearAllMocks(); + }); - afterEach(() => { - cleanup(); - jest.clearAllMocks(); - }); + /* + * Skip due to issues with TooltipComponent: + * + * react-tooltip v4.2.14 works in the application but causes an exception + * in tests (https://github.com/ReactTooltip/react-tooltip/issues/681), + * which is fixed in v4.2.17. However, version v4.2.17 breaks the tooltip + * when running in the application. + */ + it.skip('renders itself', () => { + render(); + expect(axisComponent).toBeTruthy(); + expect(axisComponent instanceof TooltipComponent).toBe(true); + }); - /* - * Skip due to issues with TooltipComponent: - * - * react-tooltip v4.2.14 works in the application but causes an exception - * in tests (https://github.com/ReactTooltip/react-tooltip/issues/681), - * which is fixed in v4.2.17. However, version v4.2.17 breaks the tooltip - * when running in the application. - */ - it.skip('renders itself', () => { - render(); - expect(axisComponent).toBeTruthy(); - expect(axisComponent instanceof TooltipComponent).toBe(true); - }); + // Skip due to issues with TooltipComponent (see above for details) + it.skip('resets timer on mouse enter', () => { + tooltip.state = { + element: model, + func: undefined, + content: 'Test' + }; + render(); + const component = screen.getByRole('tooltip-component-role'); + fireEvent.mouseEnter(component); + fireEvent.mouseLeave(component); - // Skip due to issues with TooltipComponent (see above for details) - it.skip('resets timer on mouse enter', () => { - tooltip.state = { - element: model, - func: undefined, - content: 'Test' - } - render(); - const component = screen.getByRole('tooltip-component-role'); - fireEvent.mouseEnter(component); - fireEvent.mouseLeave(component); + expect(tooltip.setState).toBeCalledWith({ content: undefined }); + }); - expect(tooltip.setState).toBeCalledWith({ content: undefined }); - }) + it('displays a tooltip for a time graph state component', () => { + tooltip.state = { + element: undefined, + func: undefined, + content: undefined + }; + tooltip.setElement(model); - it('displays a tooltip for a time graph state component', () => { - tooltip.state = { - element: undefined, - func: undefined, - content: undefined - } - tooltip.setElement(model); - - expect(tooltip.setState).toBeCalledWith({ element: model, func: undefined }); - }); + expect(tooltip.setState).toBeCalledWith({ element: model, func: undefined }); + }); - it('hides tooltip if mouse is not hovering over element', async () => { - tooltip.state = { - element: model, - func: undefined, - content: 'Test' - } - tooltip.setElement(undefined); - await new Promise((r) => setTimeout(r, 500)); - - expect(tooltip.setState).toBeCalledWith({ content: undefined }); - }) + it('hides tooltip if mouse is not hovering over element', async () => { + tooltip.state = { + element: model, + func: undefined, + content: 'Test' + }; + tooltip.setElement(undefined); + await new Promise(r => setTimeout(r, 500)); - it('hide tooltip because there is no content', () => { - tooltip.state = { - element: model, - func: undefined, - content: undefined - } - tooltip.setElement(undefined); - - expect(tooltip.setState).toBeCalledWith({ content: undefined }); - }) -}) + expect(tooltip.setState).toBeCalledWith({ content: undefined }); + }); + + it('hide tooltip because there is no content', () => { + tooltip.state = { + element: model, + func: undefined, + content: undefined + }; + tooltip.setElement(undefined); + + expect(tooltip.setState).toBeCalledWith({ content: undefined }); + }); +}); diff --git a/packages/react-components/src/components/abstract-dialog-component.tsx b/packages/react-components/src/components/abstract-dialog-component.tsx index 9f20bd114..4625d0eb4 100644 --- a/packages/react-components/src/components/abstract-dialog-component.tsx +++ b/packages/react-components/src/components/abstract-dialog-component.tsx @@ -1,37 +1,45 @@ -import React from 'react'; -import ReactModal from 'react-modal'; -import '../../style/dialog-component-style.css'; -import '../../style/output-components-style.css'; - -export interface DialogComponentProps { - title: string, - onCloseDialog: () => void, - isOpen: boolean -} - -export abstract class AbstractDialogComponent

extends React.Component { - constructor(props: P) { - super(props); - } - - render(): React.ReactNode { - return -

{e.preventDefault();}}> -
-
{this.props.title}
- -
-
{this.renderDialogBody()}
-
{this.renderFooter()}
-
- ; - } - - protected abstract renderDialogBody(): React.ReactElement; - protected abstract renderFooter(): React.ReactElement; -} +import React from 'react'; +import ReactModal from 'react-modal'; +import '../../style/dialog-component-style.css'; +import '../../style/output-components-style.css'; + +export interface DialogComponentProps { + title: string; + onCloseDialog: () => void; + isOpen: boolean; +} + +export abstract class AbstractDialogComponent

extends React.Component { + constructor(props: P) { + super(props); + } + + render(): React.ReactNode { + return ( + +

{ + e.preventDefault(); + }} + > +
+
{this.props.title}
+ +
+
{this.renderDialogBody()}
+
{this.renderFooter()}
+
+ + ); + } + + protected abstract renderDialogBody(): React.ReactElement; + protected abstract renderFooter(): React.ReactElement; +} diff --git a/packages/react-components/src/components/abstract-output-component.tsx b/packages/react-components/src/components/abstract-output-component.tsx index 1ff575933..d4e3f385c 100644 --- a/packages/react-components/src/components/abstract-output-component.tsx +++ b/packages/react-components/src/components/abstract-output-component.tsx @@ -41,7 +41,7 @@ export interface AbstractOutputProps { onTouchStart?: VoidFunction; onTouchEnd?: VoidFunction; setChartOffset?: (chartOffset: number) => void; - pinned?: boolean + pinned?: boolean; // eslint-disable-next-line @typescript-eslint/no-explicit-any persistChartState?: any; children?: string; @@ -53,8 +53,10 @@ export interface AbstractOutputState { dropDownOpen?: boolean; } -export abstract class AbstractOutputComponent

extends React.Component { - +export abstract class AbstractOutputComponent< + P extends AbstractOutputProps, + S extends AbstractOutputState +> extends React.Component { private readonly DEFAULT_HANDLE_WIDTH = 30; protected mainOutputContainer: React.RefObject; @@ -85,29 +87,36 @@ export abstract class AbstractOutputComponent

+ return (

- {this.renderTitleBar()} -
-
- {this.renderMainOutputContainer()} +
+ {this.renderTitleBar()} +
+
+ {this.renderMainOutputContainer()} +
+ {this.props.children}
- {this.props.children} - ; + ); } private renderTitleBar(): React.ReactNode { @@ -124,39 +133,50 @@ export abstract class AbstractOutputComponent

- - {this.showViewOptionsCondition() && -

- - + return ( + + + {this.showViewOptionsCondition() && ( +
+ + +
+ )} +
this.setFocus()} + > + {outputName} + ... + + {this.props.pinned === true && ( + + )}
- } -
this.setFocus()}> - {outputName} - ... - - {this.props.pinned === true && } -
-
; + + ); } private showViewOptionsCondition(): boolean { - const nonPinViewOptionExists = this.dropDownOptions.some(option => option.label !== this.PIN_VIEW_LABEL && option.label !== this.UNPIN_VIEW_LABEL); + const nonPinViewOptionExists = this.dropDownOptions.some( + option => option.label !== this.PIN_VIEW_LABEL && option.label !== this.UNPIN_VIEW_LABEL + ); return nonPinViewOptionExists || this.props.pinned !== false; } @@ -237,7 +257,7 @@ export abstract class AbstractOutputComponent

-

- Trace analysis failed. -
- ; + return ( + <> +
Trace analysis failed.
+ + ); } protected renderEmptyResults(): React.ReactElement { - return <> -
+ return ( + <> +
Trace analysis complete.
No results: Trace missing required events.
- ; + + ); } protected getOutputComponentDomId(): string { diff --git a/packages/react-components/src/components/abstract-tree-output-component.tsx b/packages/react-components/src/components/abstract-tree-output-component.tsx index 6e1e9d15e..6edff0562 100644 --- a/packages/react-components/src/components/abstract-tree-output-component.tsx +++ b/packages/react-components/src/components/abstract-tree-output-component.tsx @@ -3,11 +3,13 @@ import * as React from 'react'; import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/responses'; export type AbstractTreeOutputState = AbstractOutputState & { - showTree: boolean + showTree: boolean; }; -export abstract class AbstractTreeOutputComponent

extends AbstractOutputComponent { - +export abstract class AbstractTreeOutputComponent< + P extends AbstractOutputProps, + S extends AbstractTreeOutputState +> extends AbstractOutputComponent { private readonly DEFAULT_Y_AXIS_WIDTH = 40; private readonly DEFAULT_SASH_WIDTH = 4; @@ -22,28 +24,46 @@ export abstract class AbstractTreeOutputComponent

- {this.state.showTree &&

- {this.renderTree()} -
} -
- {this.renderYAxis()} -
-
this.onSashDown(event)} style={{ - width: this.getSashWidth(), height: this.props.style.height - }}/> -
- {this.renderChart()} -
- ; + return ( + + {this.state.showTree && ( +
+ {this.renderTree()} +
+ )} +
+ {this.renderYAxis()} +
+
this.onSashDown(event)} + style={{ + width: this.getSashWidth(), + height: this.props.style.height + }} + /> +
+ {this.renderChart()} +
+ + ); } private onSashDown(event: React.MouseEvent): void { @@ -55,8 +75,10 @@ export abstract class AbstractTreeOutputComponent

setTimeout(resolve, timeout)); - factor = factor > maxFactor ? factor: factor + 1; + factor = factor > maxFactor ? factor : factor + 1; } } componentWillUnmount(): void { // fix Warning: Can't perform a React state update on an unmounted component this.setState = (_state, _callback) => undefined; - } protected getYAxisWidth(): number { @@ -109,7 +130,7 @@ export abstract class AbstractTreeOutputComponent

extends AbstractTreeOutputComponent { - +export abstract class AbstractXYOutputComponent< + P extends AbstractOutputProps, + S extends AbstractXYOutputState +> extends AbstractTreeOutputComponent { // References private yAxisRef: any; protected divRef: any; @@ -100,7 +107,7 @@ export abstract class AbstractXYOutputComponent

{ if (this.clickedMouseButton === MouseButton.RIGHT) { - this.applySelectionZoom(); + this.applySelectionZoom(); } this.mouseIsDown = false; @@ -122,7 +129,9 @@ export abstract class AbstractXYOutputComponent

{ this.afterChartDraw(chartInstance.ctx, chartInstance.chartArea); } + afterDraw: (chartInstance: Chart, _easing: Chart.Easing, _options?: any) => { + this.afterChartDraw(chartInstance.ctx, chartInstance.chartArea); + } }; private _debouncedUpdateXY = debounce(() => this.updateXY(), 500); @@ -162,11 +171,11 @@ export abstract class AbstractXYOutputComponent

{ this.viewSpinner(true); const parameters = QueryHelper.timeRangeQuery(this.props.range.getStart(), this.props.range.getEnd()); - const tspClientResponse = await this.props.tspClient.fetchXYTree(this.props.traceId, this.props.outputDescriptor.id, parameters); + 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) { @@ -261,20 +281,23 @@ export abstract class AbstractXYOutputComponent

0) { headers.forEach(header => { - columns.push({title: header.name, sortable: true, resizable: true, tooltip: header.tooltip}); + columns.push({ title: header.name, sortable: true, resizable: true, tooltip: header.tooltip }); }); } else { - columns.push({title: 'Name', sortable: true}); + columns.push({ title: 'Name', sortable: true }); } const checkedSeries = this.getAllCheckedIds(treeResponse.model.entries); - this.setState({ - outputStatus: treeResponse.status, - xyTree: treeResponse.model.entries, - checkedSeries, - columns - }, () => { - this.updateXY(); - }); + this.setState( + { + outputStatus: treeResponse.status, + xyTree: treeResponse.model.entries, + checkedSeries, + columns + }, + () => { + this.updateXY(); + } + ); } else { this.setState({ outputStatus: treeResponse.status @@ -304,8 +327,8 @@ export abstract class AbstractXYOutputComponent

+ return this.state.xyTree.length ? ( +

- : undefined - ; + ) : undefined; } renderYAxis(): React.ReactNode { @@ -332,24 +354,34 @@ export abstract class AbstractXYOutputComponent

( - d >= 1000000000000 ? Math.round(d / 100000000000) / 10 + 'G' : - d >= 1000000000 ? Math.round(d / 100000000) / 10 + 'B': - d >= 1000000 ? Math.round(d / 100000) / 10 + 'M' : - d >= 1000 ? Math.round(d / 100) / 10 + 'K': - Math.round(d * 10) / 10 - ); + const scaleYLabel = (d: number) => + d >= 1000000000000 + ? Math.round(d / 100000000000) / 10 + 'G' + : d >= 1000000000 + ? Math.round(d / 100000000) / 10 + 'B' + : d >= 1000000 + ? Math.round(d / 100000) / 10 + 'M' + : d >= 1000 + ? Math.round(d / 100) / 10 + 'K' + : Math.round(d * 10) / 10; if (this.state.allMax > 0) { - select(this.yAxisRef.current).call(axisLeft(yScale).tickSizeOuter(0).ticks(4)).call(g => g.select('.domain').remove()); - select(this.yAxisRef.current).selectAll('.tick text').style('font-size', '11px').text((d: any) => scaleYLabel(d)); + select(this.yAxisRef.current) + .call(axisLeft(yScale).tickSizeOuter(0).ticks(4)) + .call(g => g.select('.domain').remove()); + select(this.yAxisRef.current) + .selectAll('.tick text') + .style('font-size', '11px') + .text((d: any) => scaleYLabel(d)); } - return - - - - ; + return ( + + + + + + ); } resultsAreEmpty(): boolean { @@ -406,9 +438,18 @@ export abstract class AbstractXYOutputComponent

{ - this.setState({ xyData: scatterData}); + this.setState({ xyData: scatterData }); }); this.calculateYRange(); @@ -502,8 +545,18 @@ export abstract class AbstractXYOutputComponent

{ if (!this.styleModel || forceUpdate) { - const tspClientResponse = await this.tspClient.fetchStyles(this.traceId, this.outputId, QueryHelper.query()); + const tspClientResponse = await this.tspClient.fetchStyles( + this.traceId, + this.outputId, + QueryHelper.query() + ); const styleResponse = tspClientResponse.getModel(); if (tspClientResponse.isOk() && styleResponse) { this.styleModel = styleResponse.model; @@ -154,7 +160,7 @@ export class StyleProvider { const value = styleValues[property]; if (typeof value === 'number') { const numberValue = value as number; - return (factor === undefined) ? numberValue : factor * numberValue; + return factor === undefined ? numberValue : factor * numberValue; } // Get the next style @@ -176,7 +182,10 @@ export class StyleProvider { * the style property * @return the style property color value, or undefined */ - public getColorStyle(elementStyle: OutputElementStyle, property: string): { color: number, alpha: number } | undefined { + public getColorStyle( + elementStyle: OutputElementStyle, + property: string + ): { color: number; alpha: number } | undefined { let color: string | undefined = undefined; let opacity: number | undefined = undefined; let blend = undefined; @@ -212,9 +221,14 @@ export class StyleProvider { // Get the next style style = this.popNextStyle(style, styleQueue); } - const alpha = (opacity === undefined) ? 1.0 : opacity; - const rgba = (color === undefined) ? (opacity === undefined ? undefined : this.rgbaToColor(0, 0, 0, alpha)) : this.rgbStringToColor(color, alpha); - return (rgba === undefined) ? undefined : (blend === undefined) ? rgba : this.blend(rgba, blend); + const alpha = opacity === undefined ? 1.0 : opacity; + const rgba = + color === undefined + ? opacity === undefined + ? undefined + : this.rgbaToColor(0, 0, 0, alpha) + : this.rgbStringToColor(color, alpha); + return rgba === undefined ? undefined : blend === undefined ? rgba : this.blend(rgba, blend); } private popNextStyle(style: OutputElementStyle, styleQueue: string[]): OutputElementStyle | undefined { @@ -235,7 +249,10 @@ export class StyleProvider { return nextStyle; } - private blend(color1: { color: number, alpha: number }, color2: { color: number, alpha: number }): { color: number, alpha: number } { + private blend( + color1: { color: number; alpha: number }, + color2: { color: number; alpha: number } + ): { color: number; alpha: number } { /** * If a color component 'c' with alpha 'a' is blended with color * component 'd' with alpha 'b', the blended color and alpha are: @@ -246,9 +263,21 @@ export class StyleProvider { * */ const alpha = color1.alpha + color2.alpha - color1.alpha * color2.alpha; - const r = this.blendComponent(color1.alpha, (color1.color >> 16) & 0xff, color2.alpha, (color2.color >> 16) & 0xff, alpha); - const g = this.blendComponent(color1.alpha, (color1.color >> 8) & 0xff, color2.alpha, (color2.color >> 8) & 0xff, alpha); - const b = this.blendComponent(color1.alpha, (color1.color) & 0xff, color2.alpha, (color2.color) & 0xff, alpha); + const r = this.blendComponent( + color1.alpha, + (color1.color >> 16) & 0xff, + color2.alpha, + (color2.color >> 16) & 0xff, + alpha + ); + const g = this.blendComponent( + color1.alpha, + (color1.color >> 8) & 0xff, + color2.alpha, + (color2.color >> 8) & 0xff, + alpha + ); + const b = this.blendComponent(color1.alpha, color1.color & 0xff, color2.alpha, color2.color & 0xff, alpha); return this.rgbaToColor(r, g, b, alpha); } @@ -256,19 +285,19 @@ export class StyleProvider { return Math.floor((alpha1 * (1.0 - alpha2) * color1 + alpha2 * color2) / alpha); } - private rgbStringToColor(rgbString: string, alpha: number): { color: number, alpha: number } { + private rgbStringToColor(rgbString: string, alpha: number): { color: number; alpha: number } { const color = parseInt(rgbString.replace(/^#/, ''), 16); return { color, alpha }; } - private rgbaStringToColor(rgbaString: string): { color: number, alpha: number } { + private rgbaStringToColor(rgbaString: string): { color: number; alpha: number } { const int = parseInt(rgbaString.replace(/^#/, ''), 16); const color = (int >> 8) & 0xffffff; const alpha = (int & 0xff) / 255; return { color, alpha }; } - private rgbaToColor(r: number, g: number, b: number, alpha: number): { color: number, alpha: number } { + private rgbaToColor(r: number, g: number, b: number, alpha: number): { color: number; alpha: number } { const color = (r << 16) + (g << 8) + b; return { color, alpha }; } diff --git a/packages/react-components/src/components/data-providers/tsp-data-provider.ts b/packages/react-components/src/components/data-providers/tsp-data-provider.ts index e836d038a..3c881adcc 100644 --- a/packages/react-components/src/components/data-providers/tsp-data-provider.ts +++ b/packages/react-components/src/components/data-providers/tsp-data-provider.ts @@ -1,6 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; -import { TimeGraphArrow, TimeGraphEntry, TimeGraphRow, TimeGraphState } from 'tsp-typescript-client/lib/models/timegraph'; +import { + TimeGraphArrow, + TimeGraphEntry, + TimeGraphRow, + TimeGraphState +} from 'tsp-typescript-client/lib/models/timegraph'; import { TimeGraphStateComponent } from 'timeline-chart/lib/components/time-graph-state'; import { TimeGraphAnnotationComponent } from 'timeline-chart/lib/components/time-graph-annotation'; import { TimelineChart } from 'timeline-chart/lib/time-graph-model'; @@ -42,8 +47,15 @@ export class TspDataProvider { * @param annotationMarkers requested annotation categories * @returns time graph model */ - async getData(ids: number[], entries: TimeGraphEntry[], totalTimeRange: TimeRange, worldRange?: TimelineChart.TimeGraphRange, - nbTimes?: number, annotationMarkers?: string[], markerSetId?: string): Promise { + async getData( + ids: number[], + entries: TimeGraphEntry[], + totalTimeRange: TimeRange, + worldRange?: TimelineChart.TimeGraphRange, + nbTimes?: number, + annotationMarkers?: string[], + markerSetId?: string + ): Promise { this.timeGraphEntries = [...entries]; if (!this.timeGraphEntries.length || !worldRange || !nbTimes) { return { @@ -80,7 +92,11 @@ export class TspDataProvider { const arrowsPromise = this.client.fetchTimeGraphArrows(this.traceUUID, this.outputId, fetchParameters); // Wait for responses - const [tspClientAnnotationsResponse, tspClientStatesResponse, tspClientArrowsResponse] = await Promise.all([annotationsPromise, statesPromise, arrowsPromise]); + const [tspClientAnnotationsResponse, tspClientStatesResponse, tspClientArrowsResponse] = await Promise.all([ + annotationsPromise, + statesPromise, + arrowsPromise + ]); // the start time which is normalized to logical 0 in timeline chart. const chartStart = totalTimeRange.getStart(); @@ -145,9 +161,11 @@ export class TspDataProvider { }; } - private getArrows(tspClientArrowsResponse: TspClientResponse>, - viewRange?: TimelineChart.TimeGraphRange, nbTimes?: number): TimelineChart.TimeGraphArrow[] { - + private getArrows( + tspClientArrowsResponse: TspClientResponse>, + viewRange?: TimelineChart.TimeGraphRange, + nbTimes?: number + ): TimelineChart.TimeGraphArrow[] { let timeGraphArrows: TimeGraphArrow[] = []; if (viewRange && nbTimes) { const stateResponseArrows = tspClientArrowsResponse.getModel(); @@ -156,15 +174,17 @@ export class TspDataProvider { } } const offset = this.timeGraphEntries[0].start; - const arrows = timeGraphArrows.map(arrow => ({ - sourceId: arrow.sourceId, - destinationId: arrow.targetId, - range: { - start: arrow.start - offset, - end: arrow.end - offset - } as TimelineChart.TimeGraphRange - } as TimelineChart.TimeGraphArrow - )); + const arrows = timeGraphArrows.map( + arrow => + ({ + sourceId: arrow.sourceId, + destinationId: arrow.targetId, + range: { + start: arrow.start - offset, + end: arrow.end - offset + } as TimelineChart.TimeGraphRange + } as TimelineChart.TimeGraphArrow) + ); return arrows; } @@ -175,7 +195,10 @@ export class TspDataProvider { if (timeGraphRow) { newTimeGraphRows.push(timeGraphRow); } else { - const emptyRow: TimeGraphRow = { states: [{ start: BigInt(0), end: BigInt(0), label: '', tags: 0 }], entryId: id }; + const emptyRow: TimeGraphRow = { + states: [{ start: BigInt(0), end: BigInt(0), label: '', tags: 0 }], + entryId: id + }; newTimeGraphRows.push(emptyRow); } }); @@ -256,7 +279,10 @@ export class TspDataProvider { }; } - async fetchStateTooltip(element: TimeGraphStateComponent, viewRange: TimeRange): Promise<{ [key: string]: string } | undefined> { + async fetchStateTooltip( + element: TimeGraphStateComponent, + viewRange: TimeRange + ): Promise<{ [key: string]: string } | undefined> { const offset = viewRange.getOffset() ? viewRange.getOffset() : BigInt(0); // use start of state for fetching tooltip since hover time is not available const time = element.model.range.start + (offset ? offset : BigInt(0)); @@ -266,12 +292,17 @@ export class TspDataProvider { duration: element.model.range.end - element.model.range.start }; const entryId = [element.row.model.id]; - const parameters = QueryHelper.selectionTimeQuery([time], entryId, { [QueryHelper.REQUESTED_ELEMENT_KEY]: requestedElement }); + const parameters = QueryHelper.selectionTimeQuery([time], entryId, { + [QueryHelper.REQUESTED_ELEMENT_KEY]: requestedElement + }); const tooltipResponse = await this.client.fetchTimeGraphTooltip(this.traceUUID, this.outputId, parameters); return tooltipResponse.getModel()?.model; } - async fetchAnnotationTooltip(element: TimeGraphAnnotationComponent, viewRange: TimeRange): Promise<{ [key: string]: string } | undefined> { + async fetchAnnotationTooltip( + element: TimeGraphAnnotationComponent, + viewRange: TimeRange + ): Promise<{ [key: string]: string } | undefined> { const elementRange = element.model.range; const offset = viewRange.getOffset() ? viewRange.getOffset() : BigInt(0); const time = elementRange.start + (offset ? offset : BigInt(0)); @@ -282,7 +313,9 @@ export class TspDataProvider { entryId: element.row.model.id }; const entryId = [element.row.model.id]; - const parameters = QueryHelper.selectionTimeQuery([time], entryId, { [QueryHelper.REQUESTED_ELEMENT_KEY]: requestedElement }); + const parameters = QueryHelper.selectionTimeQuery([time], entryId, { + [QueryHelper.REQUESTED_ELEMENT_KEY]: requestedElement + }); const tooltipResponse = await this.client.fetchTimeGraphTooltip(this.traceUUID, this.outputId, parameters); return tooltipResponse.getModel()?.model; } diff --git a/packages/react-components/src/components/datatree-output-component.tsx b/packages/react-components/src/components/datatree-output-component.tsx index 4c5e06e36..41819742e 100644 --- a/packages/react-components/src/components/datatree-output-component.tsx +++ b/packages/react-components/src/components/datatree-output-component.tsx @@ -16,8 +16,7 @@ import '../../style/react-contextify.css'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSpinner } from '@fortawesome/free-solid-svg-icons'; -type DataTreeOutputProps = AbstractOutputProps & { -}; +type DataTreeOutputProps = AbstractOutputProps & {}; type DataTreeOuputState = AbstractOutputState & { selectedSeriesId: number[]; @@ -42,7 +41,7 @@ export class DataTreeOutputComponent extends AbstractOutputComponent this.exportOutput()); @@ -63,7 +62,13 @@ export class DataTreeOutputComponent extends AbstractOutputComponent 0) { headers.forEach(header => { - columns.push({ title: header.name, sortable: true, resizable: true, tooltip: header.tooltip, dataType: header.dataType }); + columns.push({ + title: header.name, + sortable: true, + resizable: true, + tooltip: header.tooltip, + dataType: header.dataType + }); }); } else { columns.push({ title: 'Name', sortable: true }); @@ -93,11 +98,12 @@ export class DataTreeOutputComponent extends AbstractOutputComponent

- : undefined - ; + ) : undefined; } renderMainArea(): React.ReactNode { - return - {this.state.outputStatus === ResponseStatus.COMPLETED ? -
- {this.renderContextMenu()} -
+ {this.state.outputStatus === ResponseStatus.COMPLETED ? ( +
+ {this.renderContextMenu()} +
+ {this.renderTree()} +
+
+ ) : ( +
- {this.renderTree()} + + Analysis running
-
- : -
- - Analysis running -
- } - ; + )} + + ); } renderContextMenu(): React.ReactNode { @@ -145,16 +157,28 @@ export class DataTreeOutputComponent extends AbstractOutputComponent { - - {(timeRanges && timeRanges.length > 0) ? - timeRanges.map(key => Select {key}) - : - <> + return ( + + {' '} + { + + {timeRanges && timeRanges.length > 0 ? ( + timeRanges.map(key => ( + + Select {key} + + )) + ) : ( + <> + )} + } - - } - ; + + ); } protected handleItemClick = (args: ItemParams): void => { @@ -211,25 +235,25 @@ export class DataTreeOutputComponent extends AbstractOutputComponent 0) { const { show } = useContextMenu({ - id: MENU_ID + this.props.outputDescriptor.id, + id: MENU_ID + this.props.outputDescriptor.id }); show(event, { props: { - data: timeProperties, + data: timeProperties }, position: this.getMenuPosition(event) }); } } } - getMenuPosition(event: React.MouseEvent): { x: number, y: number } { + getMenuPosition(event: React.MouseEvent): { x: number; y: number } { const refNode = this.treeRef.current; if (refNode) { return { // Compute position relative to treeRef - x: (event.clientX - refNode.getBoundingClientRect().left), - y: (event.clientY - refNode.getBoundingClientRect().top) + x: event.clientX - refNode.getBoundingClientRect().left, + y: event.clientY - refNode.getBoundingClientRect().top }; } return { @@ -274,9 +298,15 @@ export class DataTreeOutputComponent extends AbstractOutputComponent { - const focusContainer = document.getElementById(this.props.traceId + this.props.outputDescriptor.id + 'focusContainer'); + const focusContainer = document.getElementById( + this.props.traceId + this.props.outputDescriptor.id + 'focusContainer' + ); if (focusContainer) { const table = focusContainer.querySelector('div:nth-child(2) > table'); if (table) { @@ -334,10 +366,18 @@ export class DataTreeOutputComponent extends AbstractOutputComponent { label: props.label, onClick: props.onClick ?? undefined, arg: props.arg ?? undefined, - condition: props.condition ?? (() => true), + condition: props.condition ?? (() => true) }; } render(): JSX.Element { return ( - {this.state.condition?.() && -
  • this.state.onClick?.(this.state.arg?.())} - > + {this.state.condition?.() && ( +
  • this.state.onClick?.(this.state.arg?.())}>
    {this.state.label}
  • - } + )}
    ); } @@ -45,7 +42,7 @@ export class DropDownOption extends React.Component { type DropDownProps = AbstractOutputProps & { dropDownOptions: OptionState[]; - dropDownOpen: boolean + dropDownOpen: boolean; }; export class DropDownComponent extends React.Component { @@ -57,20 +54,18 @@ export class DropDownComponent extends React.Component { } render(): JSX.Element { - return - {this.props.dropDownOpen && ( -
    -
      - {this.props.dropDownOptions?.map((option, index) => ( - - ))} -
    -
    - )} -
    ; + return ( + + {this.props.dropDownOpen && ( +
    +
      + {this.props.dropDownOptions?.map((option, index) => ( + + ))} +
    +
    + )} +
    + ); } } diff --git a/packages/react-components/src/components/null-output-component.tsx b/packages/react-components/src/components/null-output-component.tsx index 6f8a71c5d..eefa57dcc 100644 --- a/packages/react-components/src/components/null-output-component.tsx +++ b/packages/react-components/src/components/null-output-component.tsx @@ -2,11 +2,9 @@ import * as React from 'react'; import { ResponseStatus } from 'tsp-typescript-client'; import { AbstractOutputComponent, AbstractOutputProps, AbstractOutputState } from './abstract-output-component'; -type NullOutputState = AbstractOutputState & { -}; +type NullOutputState = AbstractOutputState & {}; -type NullOutputProps = AbstractOutputProps & { -}; +type NullOutputProps = AbstractOutputProps & {}; export class NullOutputComponent extends AbstractOutputComponent { constructor(props: NullOutputProps) { @@ -17,12 +15,14 @@ export class NullOutputComponent extends AbstractOutputComponent -
    - Not implemented yet! -
    -
    - ; + return ( + +
    + Not implemented yet! +
    +
    +
    + ); } resultsAreEmpty(): boolean { @@ -32,4 +32,4 @@ export class NullOutputComponent extends AbstractOutputComponent 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, - display: 'flex', - flexDirection: 'column' - }}> - 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, + display: 'flex', + flexDirection: 'column' + }} > - - {this.pagination && - - } -
    ; + suppressPaginationPanel={true} + debug={this.debugMode} + onGridReady={this.onGridReady} + onCellClicked={this.onEventClick} + rowSelection="multiple" + onModelUpdated={this.onModelUpdated} + onCellKeyDown={this.onKeyDown} + components={this.frameworkComponents} + enableBrowserTooltips={true} + > + {this.pagination && ( + + )} +
    + ); } resultsAreEmpty(): boolean { @@ -158,7 +170,9 @@ export class TableOutputComponent extends AbstractOutputComponent { this.handleTimeSelectionChange(range); }); + this.props.unitController.onSelectionRangeChange(range => { + this.handleTimeSelectionChange(range); + }); } private checkFocus(event: React.FocusEvent): void { @@ -202,7 +216,7 @@ export class TableOutputComponent extends AbstractOutputComponent this.endTimestamp ? this.startTimestamp + BigInt(1) : this.startTimestamp); + const index = await this.fetchTableIndex( + this.startTimestamp > this.endTimestamp ? this.startTimestamp + BigInt(1) : this.startTimestamp + ); if (index) { 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.selectEndIndex = + this.enableIndexSelection && this.selectEndIndex === -1 ? startIndex : this.selectEndIndex; this.updatePageIndex(index); this.selectRows(); } @@ -530,8 +550,11 @@ export class TableOutputComponent extends AbstractOutputComponent(); @@ -594,7 +621,7 @@ export class TableOutputComponent extends AbstractOutputComponent 0); + obj['isMatched'] = line.tags !== undefined && line.tags > 0; linesArray.push(obj); }); return linesArray; @@ -629,7 +656,7 @@ export class TableOutputComponent extends AbstractOutputComponent { - if ((rowNode.data[key].search(new RegExp(value)) === -1)) { + if (rowNode.data[key].search(new RegExp(value)) === -1) { isMatched = false; } }); @@ -646,7 +673,11 @@ export class TableOutputComponent extends AbstractOutputComponent= Math.min(this.selectStartIndex, this.selectEndIndex) - && rowNode.rowIndex <= Math.max(this.selectStartIndex, this.selectEndIndex)) || (!this.enableIndexSelection - && this.timestampCol && BigInt(rowNode.data[this.timestampCol]) >= (this.startTimestamp <= this.endTimestamp ? this.startTimestamp : this.endTimestamp) - && BigInt(rowNode.data[this.timestampCol]) <= (this.startTimestamp <= this.endTimestamp ? this.endTimestamp : this.startTimestamp))) { + if ( + (this.enableIndexSelection && + this.selectStartIndex !== -1 && + this.selectEndIndex !== -1 && + rowNode.rowIndex && + rowNode.rowIndex >= Math.min(this.selectStartIndex, this.selectEndIndex) && + rowNode.rowIndex <= Math.max(this.selectStartIndex, this.selectEndIndex)) || + (!this.enableIndexSelection && + this.timestampCol && + BigInt(rowNode.data[this.timestampCol]) >= + (this.startTimestamp <= this.endTimestamp ? this.startTimestamp : this.endTimestamp) && + BigInt(rowNode.data[this.timestampCol]) <= + (this.startTimestamp <= this.endTimestamp ? this.endTimestamp : this.startTimestamp)) + ) { return true; } return false; @@ -834,24 +880,28 @@ export class TableOutputComponent extends AbstractOutputComponent - - - {columns.map((column, key) => - - - - - )} - -
    - this.toggleColumnVisibility(this.columnApi!, column.getColDef())} - /> - {column.getColDef().headerName}
    - ; + return ( + + + + {columns.map((column, key) => ( + + + + + ))} + +
    + + this.toggleColumnVisibility(this.columnApi!, column.getColDef()) + } + /> + {column.getColDef().headerName}
    +
    + ); } protected closeOptionsDropDown(): void { @@ -861,16 +911,23 @@ export class TableOutputComponent extends AbstractOutputComponent -
      -
    • { - this.setState({showToggleColumns: !this.state.showToggleColumns}); - }}> -
      Toggle Columns
      -
    • -
    - {this.state.showToggleColumns &&
    {this.renderToggleColumnsTable()}
    } - ; + return ( + +
      +
    • { + this.setState({ showToggleColumns: !this.state.showToggleColumns }); + }} + > +
      Toggle Columns
      +
    • +
    + {this.state.showToggleColumns && ( +
    {this.renderToggleColumnsTable()}
    + )} +
    + ); } private updatePageIndex(rowIndex: number): void { diff --git a/packages/react-components/src/components/table-renderer-components.tsx b/packages/react-components/src/components/table-renderer-components.tsx index aefc06e1b..1ce8d203d 100644 --- a/packages/react-components/src/components/table-renderer-components.tsx +++ b/packages/react-components/src/components/table-renderer-components.tsx @@ -24,16 +24,22 @@ interface SearchFilterRendererState { export class LoadingRenderer extends React.Component { render(): JSX.Element { - return - {this.props.value ? this.props.value : } - ; + return ( + + {this.props.value ? this.props.value : } + + ); } } export class CellRenderer extends React.Component { render(): JSX.Element { if (this.props.value === undefined) { - return ; + return ( + + + + ); } const currField = this.props.colDef?.field; const currFieldVal = this.props.value || ''; @@ -42,38 +48,38 @@ export class CellRenderer extends React.Component { const searchTerm = (currField && this.props.filterModel.get(currField)) || ''; if (this.props.filterModel.size > 0) { if (isMatched) { - cellElement = <> - {currFieldVal.split(new RegExp(searchTerm)).map((tag: string, index: number, array: string[]) => - - <> - {tag} - - <> - { - index < array.length - 1 ? - {currFieldVal.match(new RegExp(searchTerm))[0]} - : <> - } - - - )} - ; - } - else { + cellElement = ( + <> + {currFieldVal + .split(new RegExp(searchTerm)) + .map((tag: string, index: number, array: string[]) => ( + + <>{tag} + <> + {index < array.length - 1 ? ( + + {currFieldVal.match(new RegExp(searchTerm))[0]} + + ) : ( + <> + )} + + + ))} + + ); + } else { cellElement = {currFieldVal} ; } } else { cellElement = <>{currFieldVal || ''}; } - return - {cellElement} - ; + return {cellElement}; } } export class SearchFilterRenderer extends React.Component { - private debouncedChangeHandler: (colName: string, inputVal: string) => void; constructor(props: SearchFilterRendererProps) { @@ -95,20 +101,36 @@ export class SearchFilterRenderer extends React.Component - {!this.state.hasClicked && !this.state.hasHovered && - } - {!this.state.hasClicked && this.state.hasHovered && +
    + {!this.state.hasClicked && !this.state.hasHovered && ( + + )} + {!this.state.hasClicked && this.state.hasHovered && (
    - Search -
    } - {this.state.hasClicked && + + Search + +
    + )} + {this.state.hasClicked && (
    - - - -
    } + + + +
    + )}
    ); } diff --git a/packages/react-components/src/components/timegraph-output-component.tsx b/packages/react-components/src/components/timegraph-output-component.tsx index 94149c703..0a76d8121 100644 --- a/packages/react-components/src/components/timegraph-output-component.tsx +++ b/packages/react-components/src/components/timegraph-output-component.tsx @@ -41,7 +41,9 @@ type TimegraphOutputProps = AbstractOutputProps & { type TimegraphOutputState = AbstractTreeOutputState & { timegraphTree: TimeGraphEntry[]; markerCategoryEntries: Entry[]; - markerLayerData: { rows: TimelineChart.TimeGraphRowModel[], range: TimelineChart.TimeGraphRange, resolution: number } | undefined; + markerLayerData: + | { rows: TimelineChart.TimeGraphRowModel[]; range: TimelineChart.TimeGraphRange; resolution: number } + | undefined; selectedRow?: number; selectedMarkerRow?: number; collapsedNodes: number[]; @@ -74,7 +76,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent this.doHandleSelectionChangedSignal(payload); + private onSelectionChanged = (payload: { [key: string]: string }) => this.doHandleSelectionChangedSignal(payload); private pendingSelection: TimeGraphEntry | undefined; constructor(props: TimegraphOutputProps) { @@ -84,11 +86,15 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent this.fetchTimegraphData(range, resolution, rowIds), + dataProvider: async (range: TimelineChart.TimeGraphRange, resolution: number, rowIds?: number[]) => + this.fetchTimegraphData(range, resolution, rowIds), stateStyleProvider: (state: TimelineChart.TimeGraphState) => this.getStateStyle(state), - rowAnnotationStyleProvider: (annotation: TimelineChart.TimeGraphAnnotation) => this.getAnnotationStyle(annotation), + rowAnnotationStyleProvider: (annotation: TimelineChart.TimeGraphAnnotation) => + this.getAnnotationStyle(annotation), rowStyleProvider: (row?: TimelineChart.TimeGraphRowModel) => this.getRowStyle(row) }; const markersProvider: TimeGraphChartProviders = { rowProvider: () => this.getMarkersRowIds(), - dataProvider: async (range: TimelineChart.TimeGraphRange, resolution: number) => this.fetchMarkersData(range, resolution), + dataProvider: async (range: TimelineChart.TimeGraphRange, resolution: number) => + this.fetchMarkersData(range, resolution), stateStyleProvider: (state: TimelineChart.TimeGraphState) => this.getMarkerStateStyle(state), rowStyleProvider: (row?: TimelineChart.TimeGraphRowModel) => this.getRowStyle(row) }; @@ -127,7 +144,9 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { @@ -137,7 +156,12 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { this.pendingSelection = undefined; if (model) { @@ -181,7 +205,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent ({ collapsedNodes: this.state.collapsedNodes, - collapsedMarkerNodes: this.state.collapsedMarkerNodes, + collapsedMarkerNodes: this.state.collapsedMarkerNodes })); } @@ -216,7 +240,11 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { const parameters = QueryHelper.timeRangeQuery(this.props.range.getStart(), this.props.range.getEnd()); - const tspClientResponse = await this.props.tspClient.fetchTimeGraphTree(this.props.traceId, this.props.outputDescriptor.id, parameters); + const tspClientResponse = await this.props.tspClient.fetchTimeGraphTree( + this.props.traceId, + this.props.outputDescriptor.id, + parameters + ); const treeResponse = tspClientResponse.getModel(); if (tspClientResponse.isOk() && treeResponse) { if (treeResponse.model) { @@ -229,11 +257,14 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { - if (prevState.outputStatus === ResponseStatus.RUNNING || + if ( + prevState.outputStatus === ResponseStatus.RUNNING || !isEqual(this.state.collapsedNodes, prevState.collapsedNodes) || !isEqual(prevProps.markerCategories, this.props.markerCategories) || - prevProps.markerSetId !== this.props.markerSetId) { + prevProps.markerSetId !== this.props.markerSetId + ) { this.selectedMarkerCategories = this.props.markerCategories; this.chartLayer.updateChart(); this.markersChartLayer.updateChart(); this.arrowLayer.update(); this.rangeEventsLayer.update(); } - if (!isEqual(this.state.markerCategoryEntries, prevState.markerCategoryEntries) || + if ( + !isEqual(this.state.markerCategoryEntries, prevState.markerCategoryEntries) || !isEqual(this.state.collapsedMarkerNodes, prevState.collapsedMarkerNodes) || - !isEqual(this.state.markerLayerData, prevState.markerLayerData)) { + !isEqual(this.state.markerLayerData, prevState.markerLayerData) + ) { this.markersChartLayer.updateChart(); } } @@ -300,7 +335,10 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { - if (typeof (payload.load) !== 'string') { + if (typeof payload.load !== 'string') { const val = payload.load[key]; if (val !== undefined) { const num = Number(val); // at least one value in array needs to match - const result = values.find((value: string | number) => (num !== undefined && num === value) || (val === value)); + const result = values.find( + (value: string | number) => (num !== undefined && num === value) || val === value + ); if (result !== undefined) { cnt++; } @@ -377,7 +417,6 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent -
    this.synchronizeTreeScroll()} - style={{ height: parseInt(this.props.style.height.toString()) - this.getMarkersLayerHeight() }} - tabIndex={0} + return ( + <> +
    this.synchronizeTreeScroll()} + style={{ height: parseInt(this.props.style.height.toString()) - this.getMarkersLayerHeight() }} + tabIndex={0} > - -
    -
    - -
    - ; + +
    +
    + +
    + + ); } renderYAxis(): React.ReactNode { @@ -433,25 +478,28 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent - {this.state.outputStatus === ResponseStatus.COMPLETED ? -
    { ev.preventDefault(); ev.stopPropagation(); }} style={{ height: this.props.style.height }} > - {this.renderTimeGraphContent()} -
    : -
    - {( - - )} - { - 'Analysis running' - } -
    - } - ; + return ( + + {this.state.outputStatus === ResponseStatus.COMPLETED ? ( +
    { + ev.preventDefault(); + ev.stopPropagation(); + }} + style={{ height: this.props.style.height }} + > + {this.renderTimeGraphContent()} +
    + ) : ( +
    + {} + {'Analysis running'} +
    + )} +
    + ); } resultsAreEmpty(): boolean { @@ -475,11 +523,11 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { // eslint-disable-next-line no-null/no-null if (fullTooltip[key as keyof typeof fullTooltip] === null) { @@ -526,55 +574,67 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent - {this.getChartContainer()} - {this.getMarkersContainer()} - ; + return ( +
    + {this.getChartContainer()} + {this.getMarkersContainer()} +
    + ); } private getMarkersContainer() { - return ; - + }} + addWidgetResizeHandler={this.props.addWidgetResizeHandler} + removeWidgetResizeHandler={this.props.removeWidgetResizeHandler} + unitController={this.props.unitController} + id="timegraph-chart-1" + layers={[this.markersChartLayer, this.markerChartCursors]} + /> + ); } private getChartContainer() { - const grid = new TimeGraphChartGrid('timeGraphGrid', this.props.style.rowHeight, this.props.backgroundTheme === 'light' ? 0xdddddd : 0x34383C); - const selectionRange = new TimeGraphChartSelectionRange('chart-selection-range', { color: this.props.style.cursorColor }); - return ; + }} + addWidgetResizeHandler={this.props.addWidgetResizeHandler} + removeWidgetResizeHandler={this.props.removeWidgetResizeHandler} + unitController={this.props.unitController} + id={this.props.traceId + this.props.outputDescriptor.id + 'focusContainer'} + layers={[ + grid, + this.chartLayer, + selectionRange, + this.chartCursors, + this.arrowLayer, + this.rangeEventsLayer + ]} + /> + ); } setFocus(): void { @@ -586,19 +646,21 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent; + return ( + + ); } private async onElementSelected(element: TimeGraphStateComponent | undefined) { @@ -611,29 +673,43 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent = new Map(); const categories: string[] = []; @@ -736,7 +816,10 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { rowIds.push(row.id); @@ -748,7 +831,7 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { const exist = this.state.collapsedNodes.find(expandId => expandId === parentIds); if (exist !== undefined) { - newList = newList.filter(collapsed => parentIds !== collapsed); + newList = newList.filter(collapsed => parentIds !== collapsed); } }); const retVal = newList.length === this.state.collapsedNodes.length; @@ -972,7 +1067,11 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { - const rowIndex = getIndexOfNode(id, listToTree(this.state.timegraphTree, this.state.columns), this.state.collapsedNodes); + const rowIndex = getIndexOfNode( + id, + listToTree(this.state.timegraphTree, this.state.columns), + this.state.collapsedNodes + ); this.chartLayer.selectAndReveal(rowIndex); if (this.rowController.selectedRow?.id !== id) { // This highlights the left side if the row is loading. @@ -981,15 +1080,24 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent { - const rowIndex = getIndexOfNode(id, listToTree(this.state.markerCategoryEntries, this.state.columns), this.state.collapsedNodes); + const rowIndex = getIndexOfNode( + id, + listToTree(this.state.markerCategoryEntries, this.state.columns), + this.state.collapsedNodes + ); this.markersChartLayer.selectAndReveal(rowIndex); }; public onSelectionChange = (row: TimelineChart.TimeGraphRowModel): void => this.setState({ selectedRow: row.id }); - public onMarkerSelectionChange = (row: TimelineChart.TimeGraphRowModel): void => this.setState({ selectedMarkerRow: row.id }); + public onMarkerSelectionChange = (row: TimelineChart.TimeGraphRowModel): void => + this.setState({ selectedMarkerRow: row.id }); private selectAndReveal(item: TimeGraphEntry) { - const rowIndex = getIndexOfNode(item.id, listToTree(this.state.timegraphTree, this.state.columns), this.state.collapsedNodes); + const rowIndex = getIndexOfNode( + item.id, + listToTree(this.state.timegraphTree, this.state.columns), + this.state.collapsedNodes + ); this.chartLayer.selectAndReveal(rowIndex); } } diff --git a/packages/react-components/src/components/tooltip-component.tsx b/packages/react-components/src/components/tooltip-component.tsx index 7ae21919b..08d04918c 100644 --- a/packages/react-components/src/components/tooltip-component.tsx +++ b/packages/react-components/src/components/tooltip-component.tsx @@ -6,12 +6,11 @@ type MaybePromise = T | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any export interface TooltipComponentState { element?: T; - func?: ((element: T) => MaybePromise<{ [key: string]: string } | undefined>); + func?: (element: T) => MaybePromise<{ [key: string]: string } | undefined>; content?: string; } export class TooltipComponent extends React.Component { - private static readonly HOURGLASS_NOT_DONE = '⏳'; timerId?: NodeJS.Timeout; @@ -26,50 +25,53 @@ export class TooltipComponent extends React.Component { - if (this.timerId) { - clearTimeout(this.timerId); - this.timerId = undefined; - } - }} - onMouseLeave={() => { - ReactTooltip.hide(); - this.setState({ content: undefined }); - }} - > - { + return ( +
    { if (this.timerId) { clearTimeout(this.timerId); this.timerId = undefined; } - if (this.state.content === undefined) { - this.fetchContent(this.state.element); - } }} - clickable={true} - scrollHide={true} - arrowColor='transparent' - overridePosition={({ left, top }, currentEvent, currentTarget, refNode, place) => { - left += (place === 'left') ? -10 : (place === 'right') ? 10 : 0; - top += (place === 'top') ? -10 : 0; - return { left, top }; + onMouseLeave={() => { + ReactTooltip.hide(); + this.setState({ content: undefined }); }} - getContent={() => this.getContent()} - /> -
    ; + > + { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = undefined; + } + if (this.state.content === undefined) { + this.fetchContent(this.state.element); + } + }} + clickable={true} + scrollHide={true} + arrowColor="transparent" + overridePosition={({ left, top }, currentEvent, currentTarget, refNode, place) => { + left += place === 'left' ? -10 : place === 'right' ? 10 : 0; + top += place === 'top' ? -10 : 0; + return { left, top }; + }} + getContent={() => this.getContent()} + /> + + ); } - setElement(element: T, func?: ((element: T) => MaybePromise<{ [key: string]: string } | undefined>)): void { + setElement(element: T, func?: (element: T) => MaybePromise<{ [key: string]: string } | undefined>): void { if (element !== this.state.element && this.state.element) { if (this.state.content) { if (this.timerId === undefined) { @@ -104,7 +106,7 @@ export class TooltipComponent extends React.Component content += this.tooltipRow(k, v)); + Object.entries(tooltipInfo).forEach(([k, v]) => (content += this.tooltipRow(k, v))); } content += ''; if (this.state.element === element) { diff --git a/packages/react-components/src/components/tooltip-xy-component.tsx b/packages/react-components/src/components/tooltip-xy-component.tsx index 15417b643..c4820756a 100644 --- a/packages/react-components/src/components/tooltip-xy-component.tsx +++ b/packages/react-components/src/components/tooltip-xy-component.tsx @@ -4,21 +4,21 @@ type MaybePromise = T | Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any export interface TooltipXYComponentState { - tooltipData?: TooltipData, - onDisplay: boolean, - inTooltip: boolean + tooltipData?: TooltipData; + onDisplay: boolean; + inTooltip: boolean; } interface TooltipData { - title?: string, + title?: string; dataPoints?: []; - top?: number, - bottom?: number, - right?: number, - left?: number, - opacity?: number, - transition?: string, - zeros?: number + top?: number; + bottom?: number; + right?: number; + left?: number; + opacity?: number; + transition?: string; + zeros?: number; } export class TooltipXYComponent extends React.Component { @@ -32,14 +32,16 @@ export class TooltipXYComponent extends React.Component 0) { @@ -79,33 +81,43 @@ export class TooltipXYComponent extends React.Component -

    {this.state.tooltipData?.title}

    -
      - {this.state.tooltipData?.dataPoints?.map((point: { color: string, background: string, label: string, value: string }, i: number) => -
    • -
      - {point.label} {point.value} -
    • +

      {this.state.tooltipData?.title}

      +
        + {this.state.tooltipData?.dataPoints?.map( + (point: { color: string; background: string; label: string; value: string }, i: number) => ( +
      • +
        + + {point.label} {point.value} + +
      • + ) )}
      - {allZeros ? -

      All values: 0

      - : - zeros > 0 && -

      {this.state.tooltipData?.zeros} other{zeros > 1 ? 's' : ''}: 0

      - } + {allZeros ? ( +

      All values: 0

      + ) : ( + zeros > 0 && ( +

      + {this.state.tooltipData?.zeros} other{zeros > 1 ? 's' : ''}: 0 +

      + ) + )} ); } @@ -122,7 +134,7 @@ export class TooltipXYComponent extends React.Component { if (!this.state.inTooltip) { - this.setState({tooltipData: { opacity: 0, transition: '0s' }, inTooltip: false }); + this.setState({ tooltipData: { opacity: 0, transition: '0s' }, inTooltip: false }); } }, 500); } diff --git a/packages/react-components/src/components/trace-context-component.tsx b/packages/react-components/src/components/trace-context-component.tsx index 594dc1646..88953696b 100644 --- a/packages/react-components/src/components/trace-context-component.tsx +++ b/packages/react-components/src/components/trace-context-component.tsx @@ -55,7 +55,7 @@ export interface TraceContextState { currentRange: TimeRange; currentViewRange: TimeRange; currentTimeSelection: TimeRange | undefined; - experiment: Experiment + experiment: Experiment; traceIndexing: boolean; style: OutputComponentStyle; backgroundTheme: string; @@ -67,7 +67,7 @@ export interface PersistedState { currentRange: TimeRangeString; currentViewRange: TimeRangeString; currentTimeSelection?: TimeRangeString; - unitControllerViewRange: { start: string, end: string }; + unitControllerViewRange: { start: string; end: string }; storedTimescaleLayout: Layout[]; storedNonTimescaleLayout: Layout[]; pinnedView: OutputDescriptor | undefined; @@ -91,7 +91,7 @@ export class TraceContextComponent extends React.Component { this.handleTimeSelectionChange(range); }); - this.unitController.onViewRangeChanged(viewRangeParam => { this.handleViewRangeChange(viewRangeParam); }); + this.unitController.onSelectionRangeChange(range => { + this.handleTimeSelectionChange(range); + }); + this.unitController.onViewRangeChanged(viewRangeParam => { + this.handleViewRangeChange(viewRangeParam); + }); this.tooltipComponent = React.createRef(); this.tooltipXYComponent = React.createRef(); this.traceContextContainer = React.createRef(); @@ -228,7 +242,10 @@ export class TraceContextComponent extends React.Component= this.unitController.viewRange.start && positionMiddleSelection <= this.unitController.viewRange.end) { + const positionMiddleSelection = + (this.unitController.selectionRange.end + this.unitController.selectionRange.start) / BigInt(2); + if ( + positionMiddleSelection >= this.unitController.viewRange.start && + positionMiddleSelection <= this.unitController.viewRange.end + ) { position = positionMiddleSelection; } } const startDistance = position - this.unitController.viewRange.start; const zoomFactor = zoomIn ? 0.8 : 1.25; - const newDuration = BIMath.clamp(Number(this.unitController.viewRangeLength) * zoomFactor, - BigInt(2), this.unitController.absoluteRange); + const newDuration = BIMath.clamp( + Number(this.unitController.viewRangeLength) * zoomFactor, + BigInt(2), + this.unitController.absoluteRange + ); const newStartRange = BIMath.max(0, position - BIMath.round(Number(startDistance) * zoomFactor)); const newEndRange = newStartRange + newDuration; this.unitController.viewRange = { start: newStartRange, end: newEndRange }; } private onResize() { - const newWidth = this.traceContextContainer.current ? this.traceContextContainer.current.clientWidth - this.SCROLLBAR_PADDING : 0; - const bounds = this.traceContextContainer.current ? this.traceContextContainer.current.getBoundingClientRect() : { left: this.DEFAULT_COMPONENT_LEFT }; + const newWidth = this.traceContextContainer.current + ? this.traceContextContainer.current.clientWidth - this.SCROLLBAR_PADDING + : 0; + const bounds = this.traceContextContainer.current + ? this.traceContextContainer.current.getBoundingClientRect() + : { left: this.DEFAULT_COMPONENT_LEFT }; this.setState(prevState => ({ style: { ...prevState.style, width: newWidth, componentLeft: bounds.left } })); this.widgetResizeHandlers.forEach(h => h()); } @@ -379,7 +417,6 @@ export class TraceContextComponent extends React.Component ({ - currentTimeSelection: new TimeRange(range.start, range.end, prevState.timeOffset) - }), () => this.updateHistory()); + this.setState( + prevState => ({ + currentTimeSelection: new TimeRange(range.start, range.end, prevState.timeOffset) + }), + () => this.updateHistory() + ); } } @@ -410,9 +450,12 @@ export class TraceContextComponent extends React.Component ({ - currentViewRange: new TimeRange(viewRange.start, viewRange.end, prevState.timeOffset) - }), () => this.updateHistory()); + this.setState( + prevState => ({ + currentViewRange: new TimeRange(viewRange.start, viewRange.end, prevState.timeOffset) + }), + () => this.updateHistory() + ); } private emitTimeRangeData = (): void => { @@ -448,16 +491,21 @@ export class TraceContextComponent extends React.Component 0 && (this.props.outputs.length || this.props.overviewDescriptor)); - return
      this.onContextMenu(event)} - onKeyDown={event => this.onKeyDown(event)} - onKeyUp={event => this.onKeyUp(event)} - ref={this.traceContextContainer}> - - - {shouldRenderOutputs ? this.renderOutputs() : this.renderPlaceHolder()} -
      ; + const shouldRenderOutputs = + this.state.style.width > 0 && (this.props.outputs.length || this.props.overviewDescriptor); + return ( +
      this.onContextMenu(event)} + onKeyDown={event => this.onKeyDown(event)} + onKeyUp={event => this.onKeyUp(event)} + ref={this.traceContextContainer} + > + + + {shouldRenderOutputs ? this.renderOutputs() : this.renderPlaceHolder()} +
      + ); } private onKeyDown(event: React.KeyboardEvent) { @@ -524,141 +572,213 @@ export class TraceContextComponent extends React.Component 0 || pinnedViewTimeScale; - return - { - // Syntax to use ReactGridLayout with Custom Components, while passing resized dimensions to children: - // https://github.com/STRML/react-grid-layout/issues/299#issuecomment-524959229 - } - { - this.props.overviewDescriptor && this._storedOverviewLayout && - // Margin required to have the close button clickable, else overlapped by the tooltip container -
      - {this.renderGridLayout([this.props.overviewDescriptor] , [this._storedOverviewLayout], true)} -
      - } - {this.state.pinnedView !== undefined && this._storedPinnedViewLayout !== undefined && - !pinnedViewTimeScale && -
      - {this.renderGridLayout([this.state.pinnedView], [this._storedPinnedViewLayout])} -
      - } - {timeScaleChartExists && - <> -
      - -
      - - } - {this.state.pinnedView !== undefined && this._storedPinnedViewLayout !== undefined && - pinnedViewTimeScale && -
      - {this.renderGridLayout([this.state.pinnedView], [this._storedPinnedViewLayout])} -
      - } -
      - {timeScaleCharts.length > 0 && - <> - {this.renderGridLayout(timeScaleCharts, this._storedTimescaleLayout)} - - } - {timeScaleChartExists && - <> -
      - + return ( + + { + // Syntax to use ReactGridLayout with Custom Components, while passing resized dimensions to children: + // https://github.com/STRML/react-grid-layout/issues/299#issuecomment-524959229 + } + {this.props.overviewDescriptor && this._storedOverviewLayout && ( + // Margin required to have the close button clickable, else overlapped by the tooltip container +
      + {this.renderGridLayout([this.props.overviewDescriptor], [this._storedOverviewLayout], true)}
      - - } - - {nonTimeScaleCharts.length > 0 && -
      - {this.renderGridLayout(nonTimeScaleCharts, this._storedNonTimescaleLayout)} + )} + {this.state.pinnedView !== undefined && + this._storedPinnedViewLayout !== undefined && + !pinnedViewTimeScale && ( +
      + {this.renderGridLayout([this.state.pinnedView], [this._storedPinnedViewLayout])} +
      + )} + {timeScaleChartExists && ( + <> +
      + +
      + + )} + {this.state.pinnedView !== undefined && + this._storedPinnedViewLayout !== undefined && + pinnedViewTimeScale && ( +
      + {this.renderGridLayout([this.state.pinnedView], [this._storedPinnedViewLayout])} +
      + )} +
      + {timeScaleCharts.length > 0 && ( + <>{this.renderGridLayout(timeScaleCharts, this._storedTimescaleLayout)} + )} + {timeScaleChartExists && ( + <> +
      + +
      + + )} + + {nonTimeScaleCharts.length > 0 && ( +
      + {this.renderGridLayout(nonTimeScaleCharts, this._storedNonTimescaleLayout)} +
      + )}
      - } -
      -
      ; + + ); } private renderGridLayout(outputs: OutputDescriptor[], layout: Layout[], isOverview?: boolean) { const rowHeight = isOverview ? this.DEFAULT_OVERVIEW_ROWHEIGHT : this.DEFAULT_COMPONENT_ROWHEIGHT; - return - {outputs.map(output => { - let onOutputRemove; - let responseType; - if (isOverview) { - onOutputRemove = this.props.onOverviewRemove; - responseType = 'OVERVIEW'; - } - else { - onOutputRemove = this.props.onOutputRemove; - responseType = output.type; - } + return ( + + {outputs.map(output => { + let onOutputRemove; + let responseType; + if (isOverview) { + onOutputRemove = this.props.onOverviewRemove; + responseType = 'OVERVIEW'; + } else { + onOutputRemove = this.props.onOutputRemove; + responseType = output.type; + } - const outputProps: AbstractOutputProps = { - tspClient: this.props.tspClient, - tooltipComponent: this.tooltipComponent.current, - tooltipXYComponent: this.tooltipXYComponent.current, - traceId: this.state.experiment.UUID, - traceName: this.props.experiment.name, - outputDescriptor: output, - markerCategories: this.props.markerCategoriesMap.get(output.id), - markerSetId: this.props.markerSetId, - range: this.state.currentRange, - nbEvents: this.state.experiment.nbEvents, - viewRange: this.state.currentViewRange, - selectionRange: this.state.currentTimeSelection, - style: this.state.style, - onOutputRemove: onOutputRemove, - unitController: this.unitController, - outputWidth: this.state.style.width, - backgroundTheme: this.state.backgroundTheme, - setChartOffset: this.setChartOffset, - pinned: this.state.pinnedView ? (this.state.pinnedView === output) : undefined - }; - switch (responseType) { - case 'OVERVIEW': - return ; - case 'TIME_GRAPH': - if (this.chartPersistedState && this.chartPersistedState.output.id === output.id) { - outputProps.persistChartState = this.chartPersistedState.payload; - this.chartPersistedState = undefined; - } - return ; - case 'TREE_TIME_XY': - if (this.chartPersistedState && this.chartPersistedState.output.id === output.id) { - outputProps.persistChartState = this.chartPersistedState.payload; - this.chartPersistedState = undefined; - } - return ; - case 'TABLE': - return ; - case 'DATA_TREE': - return ; - default: - return ; - } - })} - ; + const outputProps: AbstractOutputProps = { + tspClient: this.props.tspClient, + tooltipComponent: this.tooltipComponent.current, + tooltipXYComponent: this.tooltipXYComponent.current, + traceId: this.state.experiment.UUID, + traceName: this.props.experiment.name, + outputDescriptor: output, + markerCategories: this.props.markerCategoriesMap.get(output.id), + markerSetId: this.props.markerSetId, + range: this.state.currentRange, + nbEvents: this.state.experiment.nbEvents, + viewRange: this.state.currentViewRange, + selectionRange: this.state.currentTimeSelection, + style: this.state.style, + onOutputRemove: onOutputRemove, + unitController: this.unitController, + outputWidth: this.state.style.width, + backgroundTheme: this.state.backgroundTheme, + setChartOffset: this.setChartOffset, + pinned: this.state.pinnedView ? this.state.pinnedView === output : undefined + }; + switch (responseType) { + case 'OVERVIEW': + return ( + + ); + case 'TIME_GRAPH': + if (this.chartPersistedState && this.chartPersistedState.output.id === output.id) { + outputProps.persistChartState = this.chartPersistedState.payload; + this.chartPersistedState = undefined; + } + return ( + + ); + case 'TREE_TIME_XY': + if (this.chartPersistedState && this.chartPersistedState.output.id === output.id) { + outputProps.persistChartState = this.chartPersistedState.payload; + this.chartPersistedState = undefined; + } + return ( + + ); + case 'TABLE': + return ( + + ); + case 'DATA_TREE': + return ( + + ); + default: + return ( + + ); + } + })} + + ); } get persistedState(): PersistedState { const { currentRange, currentViewRange, currentTimeSelection, pinnedView } = this.state; - const { _storedNonTimescaleLayout: storedNonTimescaleLayout, + const { + _storedNonTimescaleLayout: storedNonTimescaleLayout, _storedTimescaleLayout: storedTimescaleLayout, _storedPinnedViewLayout: storedPinnedViewLayout, - _storedOverviewLayout: storedOverviewLayout } = this; + _storedOverviewLayout: storedOverviewLayout + } = this; const { start: ucStart, end: ucEnd } = this.unitController.viewRange; return { outputs: this.props.outputs, @@ -680,11 +800,13 @@ export class TraceContextComponent extends React.Component - {'Trace loaded successfully.'} -
      - {'To see available views, open the Trace Viewer.'} -
      ; + return ( +
      + {'Trace loaded successfully.'} +
      + {'To see available views, open the Trace Viewer.'} +
      + ); } private undoHistory = (): void => { @@ -704,7 +826,7 @@ export class TraceContextComponent extends React.Component layout.i === output.id); @@ -759,30 +881,32 @@ export class TraceContextComponent extends React.Component (a.y > b.y) ? 1 : -1); - existingNonTimeScaleLayouts.sort((a,b) => (a.y > b.y) ? 1 : -1); + existingTimeScaleLayouts.sort((a, b) => (a.y > b.y ? 1 : -1)); + existingNonTimeScaleLayouts.sort((a, b) => (a.y > b.y ? 1 : -1)); existingTimeScaleLayouts = existingTimeScaleLayouts.concat(newTimeScaleLayouts); existingNonTimeScaleLayouts = existingNonTimeScaleLayouts.concat(newNonTimeScaleLayouts); diff --git a/packages/react-components/src/components/trace-overview-component.tsx b/packages/react-components/src/components/trace-overview-component.tsx index 7ca3feba7..2f4061dcf 100644 --- a/packages/react-components/src/components/trace-overview-component.tsx +++ b/packages/react-components/src/components/trace-overview-component.tsx @@ -8,11 +8,13 @@ type XYoutputOverviewProps = AbstractOutputProps & { }; export class TraceOverviewComponent extends React.Component { - constructor(props: XYoutputOverviewProps){ + constructor(props: XYoutputOverviewProps) { super(props); } render(): JSX.Element { - return ; + return ( + + ); } } diff --git a/packages/react-components/src/components/trace-overview-selection-dialog-component.tsx b/packages/react-components/src/components/trace-overview-selection-dialog-component.tsx index 58991cac5..133eecf25 100644 --- a/packages/react-components/src/components/trace-overview-selection-dialog-component.tsx +++ b/packages/react-components/src/components/trace-overview-selection-dialog-component.tsx @@ -5,17 +5,21 @@ import { signalManager } from 'traceviewer-base/lib/signals/signal-manager'; import { AvailableViewsComponent } from './utils/available-views-component'; export interface TraceOverviewSelectionComponentProps extends DialogComponentProps { - tspClient: TspClient, - traceID: string + tspClient: TspClient; + traceID: string; } export interface TraceOverviewSelectionComponentState { - outputDescriptors: OutputDescriptor[] + outputDescriptors: OutputDescriptor[]; } -export class TraceOverviewSelectionDialogComponent extends AbstractDialogComponent { +export class TraceOverviewSelectionDialogComponent extends AbstractDialogComponent< + TraceOverviewSelectionComponentProps, + TraceOverviewSelectionComponentState +> { private selectedOutput: OutputDescriptor | undefined; - protected handleOutputClicked = (selectedOutput: OutputDescriptor): void => this.doHandleOutputClicked(selectedOutput); + protected handleOutputClicked = (selectedOutput: OutputDescriptor): void => + this.doHandleOutputClicked(selectedOutput); constructor(props: TraceOverviewSelectionComponentProps) { super(props); @@ -28,18 +32,18 @@ export class TraceOverviewSelectionDialogComponent extends AbstractDialogCompone protected renderDialogBody(): React.ReactElement { if (!this.state.outputDescriptors) { - return (
      - Loading available outputs... -
      ); + return
      Loading available outputs...
      ; } return (
      {this.doHandleOutputClicked(e);}} + onOutputClicked={e => { + this.doHandleOutputClicked(e); + }} outputDescriptors={this.state.outputDescriptors} - listRowWidth='95%' - listRowPadding='10px' + listRowWidth="95%" + listRowPadding="10px" >
      ); @@ -47,12 +51,14 @@ export class TraceOverviewSelectionDialogComponent extends AbstractDialogCompone protected renderFooter(): React.ReactElement { return ( - + ); } private async getAvailableOutputDescriptors() { - if (this.props.traceID){ + if (this.props.traceID) { const result = await this.props.tspClient.experimentOutputs(this.props.traceID); const descriptors = result.getModel(); const overviewOutputDescriptors = descriptors?.filter(output => output.type === 'TREE_TIME_XY'); diff --git a/packages/react-components/src/components/utils/__tests__/time-axis-component.test.tsx b/packages/react-components/src/components/utils/__tests__/time-axis-component.test.tsx index a4f49aa5d..b74c2d7f6 100644 --- a/packages/react-components/src/components/utils/__tests__/time-axis-component.test.tsx +++ b/packages/react-components/src/components/utils/__tests__/time-axis-component.test.tsx @@ -5,54 +5,78 @@ import { TimeGraphUnitController } from 'timeline-chart/lib/time-graph-unit-cont import { OutputComponentStyle } from '../output-component-style'; describe('Time axis component', () => { - - let axisComponent: any; + let axisComponent: any; const ref = (el: TimeAxisComponent | undefined | null): void => { - axisComponent = el; + axisComponent = el; }; - beforeEach(() => { - axisComponent = null; - }); + beforeEach(() => { + axisComponent = null; + }); - afterEach(() => { - cleanup(); - jest.clearAllMocks(); - }); + afterEach(() => { + cleanup(); + jest.clearAllMocks(); + }); - it('renders with provided style', () => { - const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { start: BigInt(0), end: BigInt(10)}); - const style: OutputComponentStyle = { - width: 600, - chartOffset: 200, - componentLeft: 0, - height: 100, - rowHeight: 100, - naviBackgroundColor: 0xf4f7fb, - chartBackgroundColor: 0xf4f7fb, - cursorColor: 0x259fd8, - lineColor: 0x757575, - } - render(
      null} removeWidgetResizeHandler={() => null} ref={ref}/>
      ); - expect(axisComponent).toBeTruthy(); - expect(axisComponent instanceof TimeAxisComponent).toBe(true); - }); + it('renders with provided style', () => { + const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { + start: BigInt(0), + end: BigInt(10) + }); + const style: OutputComponentStyle = { + width: 600, + chartOffset: 200, + componentLeft: 0, + height: 100, + rowHeight: 100, + naviBackgroundColor: 0xf4f7fb, + chartBackgroundColor: 0xf4f7fb, + cursorColor: 0x259fd8, + lineColor: 0x757575 + }; + render( +
      + null} + removeWidgetResizeHandler={() => null} + ref={ref} + /> +
      + ); + expect(axisComponent).toBeTruthy(); + expect(axisComponent instanceof TimeAxisComponent).toBe(true); + }); - it('creates canvas', () => { - const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { start: BigInt(0), end: BigInt(10)}); - const style: OutputComponentStyle = { - width: 600, - chartOffset: 200, - componentLeft: 0, - height: 100, - rowHeight: 100, - naviBackgroundColor: 0xf4f7fb, - chartBackgroundColor: 0xf4f7fb, - cursorColor: 0x259fd8, - lineColor: 0x757575 - } + it('creates canvas', () => { + const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { + start: BigInt(0), + end: BigInt(10) + }); + const style: OutputComponentStyle = { + width: 600, + chartOffset: 200, + componentLeft: 0, + height: 100, + rowHeight: 100, + naviBackgroundColor: 0xf4f7fb, + chartBackgroundColor: 0xf4f7fb, + cursorColor: 0x259fd8, + lineColor: 0x757575 + }; - const { container } = render(
      null} removeWidgetResizeHandler={() => null}/>
      ); - expect(container).toMatchSnapshot(); - }); + const { container } = render( +
      + null} + removeWidgetResizeHandler={() => null} + /> +
      + ); + expect(container).toMatchSnapshot(); + }); }); diff --git a/packages/react-components/src/components/utils/__tests__/time-navigator-component.test.tsx b/packages/react-components/src/components/utils/__tests__/time-navigator-component.test.tsx index a4f540548..daef20af9 100644 --- a/packages/react-components/src/components/utils/__tests__/time-navigator-component.test.tsx +++ b/packages/react-components/src/components/utils/__tests__/time-navigator-component.test.tsx @@ -4,47 +4,72 @@ import { TimeNavigatorComponent } from '../time-navigator-component'; import { TimeGraphUnitController } from 'timeline-chart/lib/time-graph-unit-controller'; describe('Time axis component', () => { + let axisComponent: any; + const ref = (el: TimeNavigatorComponent | undefined | null): void => { + axisComponent = el; + }; - let axisComponent: any; - const ref = (el: TimeNavigatorComponent | undefined | null): void => { - axisComponent = el; - }; + beforeEach(() => { + axisComponent = null; + }); - beforeEach(() => { - axisComponent = null; - }); + afterEach(() => { + cleanup(); + jest.clearAllMocks(); + }); - afterEach(() => { - cleanup(); - jest.clearAllMocks(); - }); + it('renders with provided style', () => { + const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { + start: BigInt(0), + end: BigInt(10) + }); + const style = { + width: 600, + chartOffset: 200, + naviBackgroundColor: 0xf4f7fb, + cursorColor: 0x259fd8, + lineColor: 0x757575 + }; - it('renders with provided style', () => { - const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { start: BigInt(0), end: BigInt(10)}); - const style = { - width: 600, - chartOffset: 200, - naviBackgroundColor: 0xf4f7fb, - cursorColor: 0x259fd8, - lineColor: 0x757575 - } + render( +
      + null} + removeWidgetResizeHandler={() => null} + ref={ref} + /> +
      + ); + expect(axisComponent).toBeTruthy(); + expect(axisComponent instanceof TimeNavigatorComponent).toBe(true); + }); - render(
      null} removeWidgetResizeHandler={() => null} ref={ref}/>
      ); - expect(axisComponent).toBeTruthy(); - expect(axisComponent instanceof TimeNavigatorComponent).toBe(true); - }); + it('creates canvas', () => { + const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { + start: BigInt(0), + end: BigInt(10) + }); + const style = { + width: 600, + chartOffset: 200, + naviBackgroundColor: 0xf4f7fb, + cursorColor: 0x259fd8, + lineColor: 0x757575 + }; - it('creates canvas', () => { - const unitController: TimeGraphUnitController = new TimeGraphUnitController(BigInt(10), { start: BigInt(0), end: BigInt(10)}); - const style = { - width: 600, - chartOffset: 200, - naviBackgroundColor: 0xf4f7fb, - cursorColor: 0x259fd8, - lineColor: 0x757575 - } - - const { container} = render(
      null} removeWidgetResizeHandler={() => null} ref={ref}/>
      ); - expect(container).toMatchSnapshot(); - }); + const { container } = render( +
      + null} + removeWidgetResizeHandler={() => null} + ref={ref} + /> +
      + ); + expect(container).toMatchSnapshot(); + }); }); diff --git a/packages/react-components/src/components/utils/available-views-component.tsx b/packages/react-components/src/components/utils/available-views-component.tsx index 3d633b3ec..149fc1882 100644 --- a/packages/react-components/src/components/utils/available-views-component.tsx +++ b/packages/react-components/src/components/utils/available-views-component.tsx @@ -5,29 +5,35 @@ import { Experiment } from 'tsp-typescript-client/lib/models/experiment'; import { signalManager, Signals } from 'traceviewer-base/lib/signals/signal-manager'; export interface AvailableViewsComponentProps { - traceID: string | undefined, - outputDescriptors: OutputDescriptor[], - onContextMenuEvent?: (event: React.MouseEvent, output: OutputDescriptor | undefined) => void, - onOutputClicked: (selectedOutput: OutputDescriptor) => void, - listRowWidth?: string, - listRowPadding?: string, - highlightAfterSelection?: boolean + traceID: string | undefined; + outputDescriptors: OutputDescriptor[]; + onContextMenuEvent?: ( + event: React.MouseEvent, + output: OutputDescriptor | undefined + ) => void; + onOutputClicked: (selectedOutput: OutputDescriptor) => void; + listRowWidth?: string; + listRowPadding?: string; + highlightAfterSelection?: boolean; } export interface AvailableViewsComponentState { lastSelectedOutputIndex: number; } -export class AvailableViewsComponent extends React.Component{ +export class AvailableViewsComponent extends React.Component< + AvailableViewsComponentProps, + AvailableViewsComponentState +> { static LIST_MARGIN = 2; static LINE_HEIGHT = 16; - static ROW_HEIGHT = (2 * AvailableViewsComponent.LINE_HEIGHT) + AvailableViewsComponent.LIST_MARGIN; + static ROW_HEIGHT = 2 * AvailableViewsComponent.LINE_HEIGHT + AvailableViewsComponent.LIST_MARGIN; private _forceUpdateKey = false; protected handleOutputClicked = (e: React.MouseEvent): void => this.doHandleOutputClicked(e); private _onExperimentSelected = (experiment: Experiment): void => this.doHandleExperimentSelectedSignal(experiment); - constructor(props: AvailableViewsComponentProps){ + constructor(props: AvailableViewsComponentProps) { super(props); signalManager().on(Signals.EXPERIMENT_SELECTED, this._onExperimentSelected); this.state = { lastSelectedOutputIndex: -1 }; @@ -47,9 +53,9 @@ export class AvailableViewsComponent extends React.Component +
      - {({ width }) => + {({ width }) => ( - } + )}
      ); @@ -90,40 +96,45 @@ export class AvailableViewsComponent extends React.Component { this.doHandleContextMenuEvent(event, output); }} - data-id={`${props.index}`} - > -
      -

      - {outputName} -

      -
      - {outputDescription} + return ( +
      { + this.doHandleContextMenuEvent(event, output); + }} + data-id={`${props.index}`} + > +
      +

      {outputName}

      +
      {outputDescription}
      -
      ; + ); } - private doHandleContextMenuEvent(event: React.MouseEvent, output: OutputDescriptor | undefined) { - if (this.props.onContextMenuEvent) - {this.props.onContextMenuEvent(event, output);} + private doHandleContextMenuEvent( + event: React.MouseEvent, + output: OutputDescriptor | undefined + ) { + if (this.props.onContextMenuEvent) { + this.props.onContextMenuEvent(event, output); + } } protected doHandleExperimentSelectedSignal(experiment: Experiment | undefined): void { - if ((this.props.traceID !== experiment?.UUID) || this.props.outputDescriptors.length === 0) { + if (this.props.traceID !== experiment?.UUID || this.props.outputDescriptors.length === 0) { this.setState({ lastSelectedOutputIndex: -1 }); } } private doHandleOutputClicked(e: React.MouseEvent) { const index = Number(e.currentTarget.getAttribute('data-id')); - this.setState({lastSelectedOutputIndex: index}); + this.setState({ lastSelectedOutputIndex: index }); const selectedOutput = this.props.outputDescriptors[index]; this.props.onOutputClicked(selectedOutput); @@ -132,7 +143,7 @@ export class AvailableViewsComponent extends React.Component totalHeight += AvailableViewsComponent.ROW_HEIGHT); + outputDescriptors?.forEach(() => (totalHeight += AvailableViewsComponent.ROW_HEIGHT)); return totalHeight; } } diff --git a/packages/react-components/src/components/utils/filter-tree/__tests__/entry-tree.test.tsx b/packages/react-components/src/components/utils/filter-tree/__tests__/entry-tree.test.tsx index 07c068e37..cce782be8 100644 --- a/packages/react-components/src/components/utils/filter-tree/__tests__/entry-tree.test.tsx +++ b/packages/react-components/src/components/utils/filter-tree/__tests__/entry-tree.test.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { EntryTree } from '../entry-tree'; import { create } from 'react-test-renderer'; -import { render, fireEvent } from "@testing-library/react"; -import { within } from "@testing-library/dom"; +import { render, fireEvent } from '@testing-library/react'; +import { within } from '@testing-library/dom'; const mockOnChecked = jest.fn(); const mockOnCollapse = jest.fn(); @@ -10,19 +10,20 @@ const mockOnClose = jest.fn(); const mockOnClick = jest.fn(); test('Empty tree', () => { - const tree = create() - .toJSON(); + const tree = create( + + ).toJSON(); expect(tree).toMatchSnapshot(); }); @@ -31,69 +32,83 @@ test('one level of entries', () => { id: 0, parentId: -1, labels: ['entry1'] - }; const entry2 = { id: 1, parentId: -1, labels: ['entry2', 'entry2 column2'] - }; - const treeWithoutCheckboxes = create() - .toJSON(); + const treeWithoutCheckboxes = create( + + ).toJSON(); expect(treeWithoutCheckboxes).toMatchSnapshot(); - const treeWithCheckboxes = create() + const treeWithCheckboxes = create( + + ); expect(treeWithCheckboxes).toMatchSnapshot(); - const treeWithoutHeaders = create() + const treeWithoutHeaders = create( + + ); expect(treeWithoutHeaders).toMatchSnapshot(); }); -window.ResizeObserver = window.ResizeObserver || jest.fn().mockImplementation(() => ({ - disconnect: jest.fn(), - observe: jest.fn(), - unobserve: jest.fn(), -})); +window.ResizeObserver = + window.ResizeObserver || + jest.fn().mockImplementation(() => ({ + disconnect: jest.fn(), + observe: jest.fn(), + unobserve: jest.fn() + })); describe('Entry with children', () => { const parent = { @@ -123,81 +138,98 @@ describe('Entry with children', () => { }; test('All unchecked', () => { - const tree = create() - .toJSON(); + const tree = create( + + ).toJSON(); expect(tree).toMatchSnapshot(); }); test('Check one grandchild', () => { - const tree = create() - .toJSON(); + const tree = create( + + ).toJSON(); expect(tree).toMatchSnapshot(); }); test('Collapse one child', () => { - const tree = create() - .toJSON(); + const tree = create( + + ).toJSON(); expect(tree).toMatchSnapshot(); }); test('Check series', () => { mockOnChecked.mockClear(); - let { getByText } = render() + let { getByText } = render( + + ); // Check a parent and make sure all its children were selected too const element = getByText(child2.labels[0]); - const image = within(element).getAllByRole("img", {hidden: true})[1]; + const image = within(element).getAllByRole('img', { hidden: true })[1]; fireEvent.click(image as HTMLElement); expect(mockOnChecked).toHaveBeenCalledTimes(1); expect(mockOnChecked).toHaveBeenCalledWith([child2.id, grandchild1.id, grandchild2.id]); @@ -205,7 +237,7 @@ describe('Entry with children', () => { // Check a child and make sure all its children were selected too const childElement = getByText(grandchild1.labels[0]); - const image2 = within(childElement).getByRole("img", {hidden: true}); + const image2 = within(childElement).getByRole('img', { hidden: true }); fireEvent.click(image2 as HTMLElement); expect(mockOnChecked).toHaveBeenCalledTimes(1); expect(mockOnChecked).toHaveBeenCalledWith([grandchild1.id]); @@ -215,94 +247,112 @@ describe('Entry with children', () => { fireEvent.click(image as HTMLElement); expect(mockOnChecked).toHaveBeenCalledTimes(1); expect(mockOnChecked).toHaveBeenCalledWith([child2.id, grandchild1.id, grandchild2.id]); - }); test('Check series with previous selected', () => { mockOnChecked.mockClear(); - let { getByText } = render() + let { getByText } = render( + + ); // Check a parent and make sure all its children were selected too const element = getByText(child2.labels[0]); - const image = within(element).getAllByRole("img", {hidden: true})[1]; + const image = within(element).getAllByRole('img', { hidden: true })[1]; fireEvent.click(image as HTMLElement); - expect(mockOnChecked).toHaveBeenCalledTimes(1) - expect(mockOnChecked).toHaveBeenCalledWith([child2.id, grandchild1.id]) + expect(mockOnChecked).toHaveBeenCalledTimes(1); + expect(mockOnChecked).toHaveBeenCalledWith([child2.id, grandchild1.id]); }); test('Collapse items', () => { - mockOnCollapse.mockClear() - const { getByText } = render() + mockOnCollapse.mockClear(); + const { getByText } = render( + + ); // Click on the collapse icon of the element and make sure the function is called const element = getByText(child2.labels[0]); - const collapseImage = within(element).getAllByRole("img", {hidden: true})[0]; + const collapseImage = within(element).getAllByRole('img', { hidden: true })[0]; fireEvent.click(collapseImage as HTMLElement); - expect(mockOnCollapse).toHaveBeenCalledTimes(1) - expect(mockOnCollapse).toHaveBeenCalledWith(child2.id, expect.anything()) + expect(mockOnCollapse).toHaveBeenCalledTimes(1); + expect(mockOnCollapse).toHaveBeenCalledWith(child2.id, expect.anything()); }); test('With filter element', () => { - const tree = create() - .toJSON(); + const tree = create( + + ).toJSON(); expect(tree).toMatchSnapshot(); }); test('Filter items behavior', () => { - const { getByPlaceholderText, queryByText } = render() + const { getByPlaceholderText, queryByText } = render( + + ); // Enter a filter and make sure only visible elements are present - const filterEl = getByPlaceholderText("Filter"); - fireEvent.change(filterEl, {target: { value: grandchild2.labels[0]}}); + const filterEl = getByPlaceholderText('Filter'); + fireEvent.change(filterEl, { target: { value: grandchild2.labels[0] } }); let treeEl = queryByText(parent.labels[0]); expect(treeEl).toBeTruthy(); @@ -320,8 +370,8 @@ describe('Entry with children', () => { expect(treeEl).toBeTruthy(); // Remove the filter - const filterEl2 = getByPlaceholderText("Filter"); - fireEvent.change(filterEl2, {target: { value: ""}}); + const filterEl2 = getByPlaceholderText('Filter'); + fireEvent.change(filterEl2, { target: { value: '' } }); treeEl = queryByText(parent.labels[0]); expect(treeEl).toBeTruthy(); diff --git a/packages/react-components/src/components/utils/filter-tree/__tests__/table-row.test.tsx b/packages/react-components/src/components/utils/filter-tree/__tests__/table-row.test.tsx index 176afb847..cac1da643 100644 --- a/packages/react-components/src/components/utils/filter-tree/__tests__/table-row.test.tsx +++ b/packages/react-components/src/components/utils/filter-tree/__tests__/table-row.test.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import { TreeNode } from '../tree-node'; import { TableRow } from '../table-row'; import { create } from 'react-test-renderer'; -import { render, fireEvent } from "@testing-library/react"; -import { within } from "@testing-library/dom"; +import { render, fireEvent } from '@testing-library/react'; +import { within } from '@testing-library/dom'; const mockOnChecked = jest.fn(); const mockOnCollapse = jest.fn(); @@ -11,7 +11,7 @@ const mockOnClose = jest.fn(); const mockOnClick = jest.fn(); const mockOnContext = jest.fn(); -const cell1Text = "cell1 - text"; +const cell1Text = 'cell1 - text'; const testTreeNode = { id: 5, parentId: -1, @@ -21,104 +21,133 @@ const testTreeNode = { } as TreeNode; test('Checked status', () => { - const treeNodeUnchecked = create( 0} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + const treeNodeUnchecked = create( + 0} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(treeNodeUnchecked).toMatchSnapshot(); - const treeNodeChecked = create( 1} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + const treeNodeChecked = create( + 1} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(treeNodeChecked).toMatchSnapshot(); - const treeNodePartialCheck = create( 2} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + const treeNodePartialCheck = create( + 2} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(treeNodePartialCheck).toMatchSnapshot(); }); test('Uncheckable', () => { - const uncheckableTree = create( 0} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + const uncheckableTree = create( + 0} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(uncheckableTree).toMatchSnapshot(); }); test('Levels', () => { - const lessPadding = create( 0} - level={1} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + const lessPadding = create( + 0} + level={1} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(lessPadding).toMatchSnapshot(); - const morePadding = create( 0} - level={10} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + const morePadding = create( + 0} + level={10} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(morePadding).toMatchSnapshot(); }); test('Toggle check', async () => { mockOnChecked.mockClear(); - let { getByText } = render( 0} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) + let { getByText } = render( + 0} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ); const element = getByText(cell1Text); - let {getByRole} = within(element); - const image = getByRole("img", {hidden: true}); + let { getByRole } = within(element); + const image = getByRole('img', { hidden: true }); fireEvent.click(image as HTMLElement); expect(mockOnChecked).toHaveBeenCalledTimes(1); expect(mockOnChecked).toHaveBeenCalledWith(testTreeNode.id); @@ -129,8 +158,7 @@ test('Toggle check', async () => { }); describe('with children', () => { - - const parentText = "parent text"; + const parentText = 'parent text'; const child1 = { id: 2, parentId: 1, @@ -152,65 +180,74 @@ describe('with children', () => { children: [child1, child2], isRoot: true } as TreeNode; - + test('With children', () => { - - let nodeWithChildren = create( 0} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + let nodeWithChildren = create( + 0} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(nodeWithChildren).toMatchSnapshot(); - }) + }); test('With children collapsed', () => { - - let nodeWithChildren = create( 0} - level={0} - isCheckable={true} - collapsedNodes={[parentNode.id]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + let nodeWithChildren = create( + 0} + level={0} + isCheckable={true} + collapsedNodes={[parentNode.id]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(nodeWithChildren).toMatchSnapshot(); - }) + }); test('Toggle collapse', () => { mockOnCollapse.mockClear(); - - let { getByText } = render( 0} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) + + let { getByText } = render( + 0} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ); const element = getByText(parentText); - const collapsedEl = within(element).getAllByRole("img", {hidden: true})[0]; + const collapsedEl = within(element).getAllByRole('img', { hidden: true })[0]; fireEvent.click(collapsedEl as HTMLElement); - expect(mockOnCollapse).toHaveBeenCalledTimes(1) - expect(mockOnCollapse).toHaveBeenCalledWith(parentNode.id) + expect(mockOnCollapse).toHaveBeenCalledTimes(1); + expect(mockOnCollapse).toHaveBeenCalledWith(parentNode.id); }); - }); describe('Multiple labels', () => { - - const parentText = "parent 1 text"; - const parentText2 = "parent 2 text"; + const parentText = 'parent 1 text'; + const parentText2 = 'parent 2 text'; const child1 = { id: 2, parentId: 1, @@ -232,21 +269,23 @@ describe('Multiple labels', () => { children: [child1, child2], isRoot: true } as TreeNode; - + test('With children and labels', () => { - - let nodeWithChildren = create( 0} - level={0} - isCheckable={true} - collapsedNodes={[]} - onToggleCollapse={mockOnCollapse} - onRowClick={mockOnClick} - onContextMenu={mockOnContext} - onToggleCheck={mockOnChecked} isClosable={false} onClose={mockOnClose} />) - .toJSON(); + let nodeWithChildren = create( + 0} + level={0} + isCheckable={true} + collapsedNodes={[]} + onToggleCollapse={mockOnCollapse} + onRowClick={mockOnClick} + onContextMenu={mockOnContext} + onToggleCheck={mockOnChecked} + isClosable={false} + onClose={mockOnClose} + /> + ).toJSON(); expect(nodeWithChildren).toMatchSnapshot(); - }) - + }); }); diff --git a/packages/react-components/src/components/utils/filter-tree/__tests__/utils.test.tsx b/packages/react-components/src/components/utils/filter-tree/__tests__/utils.test.tsx index 65d80c5e8..956c2ba14 100644 --- a/packages/react-components/src/components/utils/filter-tree/__tests__/utils.test.tsx +++ b/packages/react-components/src/components/utils/filter-tree/__tests__/utils.test.tsx @@ -1,4 +1,4 @@ -import { listToTree } from "../utils"; +import { listToTree } from '../utils'; describe('listToTree', () => { const parent = { @@ -28,39 +28,44 @@ describe('listToTree', () => { }; test('Basic case, empty headers', () => { - const expectedTree = ({ + const expectedTree = { labels: parent.labels, isRoot: true, id: parent.id, parentId: parent.parentId, - children: [{ - labels: child1.labels, - isRoot: false, - id: child1.id, - parentId: child1.parentId, - children: [] - }, { - labels: child2.labels, - isRoot: false, - id: child2.id, - parentId: child2.parentId, - children: [{ - labels: grandchild1.labels, + children: [ + { + labels: child1.labels, isRoot: false, - id: grandchild1.id, - parentId: grandchild1.parentId, + id: child1.id, + parentId: child1.parentId, children: [] - }, { - labels: grandchild2.labels, + }, + { + labels: child2.labels, isRoot: false, - id: grandchild2.id, - parentId: grandchild2.parentId, - children: [] - }] - }] - }); + id: child2.id, + parentId: child2.parentId, + children: [ + { + labels: grandchild1.labels, + isRoot: false, + id: grandchild1.id, + parentId: grandchild1.parentId, + children: [] + }, + { + labels: grandchild2.labels, + isRoot: false, + id: grandchild2.id, + parentId: grandchild2.parentId, + children: [] + } + ] + } + ] + }; const tree = listToTree([child1, parent, child2, grandchild1, grandchild2], []); - expect(tree).toMatchObject([expectedTree]); + expect(tree).toMatchObject([expectedTree]); }); - -}); \ No newline at end of file +}); diff --git a/packages/react-components/src/components/utils/filter-tree/checkbox-component.tsx b/packages/react-components/src/components/utils/filter-tree/checkbox-component.tsx index 7835316f1..a75103b0a 100644 --- a/packages/react-components/src/components/utils/filter-tree/checkbox-component.tsx +++ b/packages/react-components/src/components/utils/filter-tree/checkbox-component.tsx @@ -30,8 +30,10 @@ export class CheckboxComponent extends React.Component { }; render(): JSX.Element { - return
      + return ( +
      {this.renderCheckbox(this.props.checkedStatus)} -
      ; +
      + ); } } diff --git a/packages/react-components/src/components/utils/filter-tree/column-header.tsx b/packages/react-components/src/components/utils/filter-tree/column-header.tsx index 0c4bfa572..7317d1bbf 100644 --- a/packages/react-components/src/components/utils/filter-tree/column-header.tsx +++ b/packages/react-components/src/components/utils/filter-tree/column-header.tsx @@ -1,9 +1,9 @@ import { DataType } from 'tsp-typescript-client/lib/models/data-type'; export default interface ColumnHeader { - title: string, - tooltip?: string, - sortable?: boolean, - resizable?: boolean, - dataType?: DataType + title: string; + tooltip?: string; + sortable?: boolean; + resizable?: boolean; + dataType?: DataType; } diff --git a/packages/react-components/src/components/utils/filter-tree/entry-tree.tsx b/packages/react-components/src/components/utils/filter-tree/entry-tree.tsx index 917118726..9093ff114 100644 --- a/packages/react-components/src/components/utils/filter-tree/entry-tree.tsx +++ b/packages/react-components/src/components/utils/filter-tree/entry-tree.tsx @@ -27,10 +27,12 @@ interface EntryTreeProps { export class EntryTree extends React.Component { static defaultProps: Partial = { showFilter: true, - onOrderChange: () => { /* Nothing to do */ }, + onOrderChange: () => { + /* Nothing to do */ + }, showHeader: true, className: 'table-tree', - headers: [{title: 'Name', sortable: true}] + headers: [{ title: 'Name', sortable: true }] }; constructor(props: EntryTreeProps) { @@ -38,13 +40,12 @@ export class EntryTree extends React.Component { } shouldComponentUpdate = (nextProps: EntryTreeProps): boolean => - (this.props.checkedSeries !== nextProps.checkedSeries || this.props.entries !== nextProps.entries - || this.props.collapsedNodes !== nextProps.collapsedNodes || this.props.selectedRow !== nextProps.selectedRow); + this.props.checkedSeries !== nextProps.checkedSeries || + this.props.entries !== nextProps.entries || + this.props.collapsedNodes !== nextProps.collapsedNodes || + this.props.selectedRow !== nextProps.selectedRow; render(): JSX.Element { - return ; + return ; } } diff --git a/packages/react-components/src/components/utils/filter-tree/filter.tsx b/packages/react-components/src/components/utils/filter-tree/filter.tsx index 28df5decd..6225730da 100644 --- a/packages/react-components/src/components/utils/filter-tree/filter.tsx +++ b/packages/react-components/src/components/utils/filter-tree/filter.tsx @@ -5,10 +5,9 @@ interface FilterProps { } interface FilterState { - width: number + width: number; } export class Filter extends React.Component { - private ref: React.RefObject; private resizeObserver: ResizeObserver; @@ -37,14 +36,11 @@ export class Filter extends React.Component { } render(): JSX.Element { - return
      - - -
      ; + return ( +
      + + +
      + ); } } diff --git a/packages/react-components/src/components/utils/filter-tree/icons.tsx b/packages/react-components/src/components/utils/filter-tree/icons.tsx index 3e422a85c..64195cdc5 100644 --- a/packages/react-components/src/components/utils/filter-tree/icons.tsx +++ b/packages/react-components/src/components/utils/filter-tree/icons.tsx @@ -1,29 +1,39 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faChevronDown, faChevronRight, faCheckSquare, faSquare, faMinusSquare, faSort, faSortDown, faSortUp, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { + faChevronDown, + faChevronRight, + faCheckSquare, + faSquare, + faMinusSquare, + faSort, + faSortDown, + faSortUp, + faTimes +} from '@fortawesome/free-solid-svg-icons'; interface iconsShape { - expand: React.ReactNode, - collapse: React.ReactNode, - unchecked: React.ReactNode, - checked: React.ReactNode, - halfChecked: React.ReactNode, - sort: React.ReactNode, - sortDown: React.ReactNode, - sortUp: React.ReactNode, - close: React.ReactNode + expand: React.ReactNode; + collapse: React.ReactNode; + unchecked: React.ReactNode; + checked: React.ReactNode; + halfChecked: React.ReactNode; + sort: React.ReactNode; + sortDown: React.ReactNode; + sortUp: React.ReactNode; + close: React.ReactNode; } const icons: iconsShape = { - expand: , - collapse: , - unchecked: , - checked: , - halfChecked: , - sort: , - sortDown: , - sortUp: , - close: + expand: , + collapse: , + unchecked: , + checked: , + halfChecked: , + sort: , + sortDown: , + sortUp: , + close: }; export default icons; diff --git a/packages/react-components/src/components/utils/filter-tree/message.tsx b/packages/react-components/src/components/utils/filter-tree/message.tsx index a7a5f9c9d..120790186 100644 --- a/packages/react-components/src/components/utils/filter-tree/message.tsx +++ b/packages/react-components/src/components/utils/filter-tree/message.tsx @@ -14,8 +14,10 @@ export class Message extends React.Component { }; render(): JSX.Element { - return - {this.props.error} - ; + return ( + + {this.props.error} + + ); } } diff --git a/packages/react-components/src/components/utils/filter-tree/sort.tsx b/packages/react-components/src/components/utils/filter-tree/sort.tsx index 5065d35bd..9bf85ea84 100644 --- a/packages/react-components/src/components/utils/filter-tree/sort.tsx +++ b/packages/react-components/src/components/utils/filter-tree/sort.tsx @@ -2,8 +2,8 @@ import icons from './icons'; import { TreeNode } from './tree-node'; interface SortState { - asc: React.ReactNode, - desc: React.ReactNode, + asc: React.ReactNode; + desc: React.ReactNode; default: React.ReactNode; } @@ -59,7 +59,7 @@ export const sortNodes = (nodes: TreeNode[], sortConfig: SortConfig[]): TreeNode comp = value1.localeCompare(value2); } } - return (orderToSort.sortState === sortState.asc) ? comp : -comp; + return orderToSort.sortState === sortState.asc ? comp : -comp; }); sortedNodes.forEach((node: TreeNode) => { if (node.children.length) { @@ -82,7 +82,7 @@ const parseValue = (valueString: string): number | undefined => { const unit = valueArray[1]; if (unit === 'ns' || unit === '%') { factor = 1; - } else if (unit === 'us' || unit === ('\u00B5' + 's')) { + } else if (unit === 'us' || unit === '\u00B5' + 's') { factor = 1000; } else if (unit === 'ms') { factor = 1000 * 1000; @@ -98,4 +98,3 @@ const parseValue = (valueString: string): number | undefined => { } return undefined; }; - diff --git a/packages/react-components/src/components/utils/filter-tree/table-body.tsx b/packages/react-components/src/components/utils/filter-tree/table-body.tsx index f2e5bcd2b..2192f1796 100644 --- a/packages/react-components/src/components/utils/filter-tree/table-body.tsx +++ b/packages/react-components/src/components/utils/filter-tree/table-body.tsx @@ -21,23 +21,17 @@ export class TableBody extends React.Component { super(props); } - createRow = (node: TreeNode): React.ReactNode => - ; + 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; } + if (!this.props.nodes) { + return undefined; + } - return ( - - {this.renderRows()} - - ); + return {this.renderRows()}; } } diff --git a/packages/react-components/src/components/utils/filter-tree/table-cell.tsx b/packages/react-components/src/components/utils/filter-tree/table-cell.tsx index c7162a4ff..84dbb8cb1 100644 --- a/packages/react-components/src/components/utils/filter-tree/table-cell.tsx +++ b/packages/react-components/src/components/utils/filter-tree/table-cell.tsx @@ -16,7 +16,7 @@ export class TableCell extends React.Component { const content = node.labels[index]; return ( - + {this.props.children} {content} diff --git a/packages/react-components/src/components/utils/filter-tree/table-header.tsx b/packages/react-components/src/components/utils/filter-tree/table-header.tsx index 58dab3be1..eb25bb9da 100644 --- a/packages/react-components/src/components/utils/filter-tree/table-header.tsx +++ b/packages/react-components/src/components/utils/filter-tree/table-header.tsx @@ -75,42 +75,46 @@ export class TableHeader extends React.Component { }; /* Capitalize first character and add non-breaking spaces to make room for icons in default width calcluation */ - toHeaderTitle = (name: string): string => (name.charAt(0).toUpperCase() + name.slice(1) + '\xa0\xa0\xa0\xa0\xa0'); + toHeaderTitle = (name: string): string => name.charAt(0).toUpperCase() + name.slice(1) + '\xa0\xa0\xa0\xa0\xa0'; 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 state ? {state.sortState} : undefined; } return undefined; }; renderResizeIcon = (index: number): React.ReactNode => - (this.props.columns[index].resizable) - ? this.handleResizeMouseDown(ev, index)} onDoubleClick={ev => this.handleResizeDoubleClick(ev, index)}>| - : undefined; + this.props.columns[index].resizable ? ( + this.handleResizeMouseDown(ev, index)} + onDoubleClick={ev => this.handleResizeDoubleClick(ev, index)} + > + | + + ) : undefined; renderHeader = (): React.ReactNode => { - const header = this.props.columns.map((column: ColumnHeader, index) => - this.handleSortChange(column.title, ev)}> + const header = this.props.columns.map((column: ColumnHeader, index) => ( + this.handleSortChange(column.title, ev)}> {this.toHeaderTitle(column.title)} {this.renderResizeIcon(index)} {this.renderSortIcon(column.title)} - ); - header.push(); + )); + header.push(); return header; }; render(): React.ReactNode { - return - - {this.renderHeader()} - - ; + return ( + + {this.renderHeader()} + + ); } } diff --git a/packages/react-components/src/components/utils/filter-tree/table-row.tsx b/packages/react-components/src/components/utils/filter-tree/table-row.tsx index 50a503a9f..624490dff 100644 --- a/packages/react-components/src/components/utils/filter-tree/table-row.tsx +++ b/packages/react-components/src/components/utils/filter-tree/table-row.tsx @@ -36,58 +36,55 @@ export class TableRow extends React.Component { renderToggleCollapse = (): React.ReactNode => { const width = (this.props.level + 1) * 12; - return ( - (this.props.node.children.length === 0) - ?
      - :
      - {(this.isCollapsed() ? icons.expand : icons.collapse)}
      + 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 - ? - : undefined; + ) : undefined; }; renderCloseButton = (): React.ReactNode => - (this.props.isClosable && this.props.node.id) - ?
      {icons.close}
      - : undefined; + this.props.isClosable && this.props.node.id ? ( +
      + {icons.close} +
      + ) : undefined; renderRow = (): React.ReactNode => { - const { node} = this.props; - const row = node.labels.map((_label: string, index) => - - { (index === 0) ? this.renderToggleCollapse() : undefined } - { (index === 0) ? this.renderCheckbox() : undefined } - { (index === 0) ? this.renderCloseButton() : undefined } + const { node } = this.props; + const row = node.labels.map((_label: string, index) => ( + + {index === 0 ? this.renderToggleCollapse() : undefined} + {index === 0 ? this.renderCheckbox() : undefined} + {index === 0 ? this.renderCloseButton() : undefined} - ); - row.push(); + )); + row.push(); return row; }; renderChildren = (): React.ReactNode | undefined => { if (this.props.node.children.length && !this.isCollapsed()) { - return this.props.node.children.map((child: TreeNode) => - - ); + return this.props.node.children.map((child: TreeNode) => ( + + )); } return undefined; }; @@ -107,18 +104,18 @@ export class TableRow extends React.Component { }; render(): React.ReactNode | undefined { - if (!this.props.node) { return undefined; } + if (!this.props.node) { + return undefined; + } const children = this.renderChildren(); const { node, selectedRow } = this.props; const className = selectedRow === node.id ? 'selected' : ''; return ( - {this.renderRow()} + + {this.renderRow()} + {children} ); diff --git a/packages/react-components/src/components/utils/filter-tree/table.tsx b/packages/react-components/src/components/utils/filter-tree/table.tsx index 578f8f6c6..436cca4f9 100644 --- a/packages/react-components/src/components/utils/filter-tree/table.tsx +++ b/packages/react-components/src/components/utils/filter-tree/table.tsx @@ -26,7 +26,6 @@ interface TableProps { } export class Table extends React.Component { - private sortableColumns: string[]; constructor(props: TableProps) { @@ -58,20 +57,22 @@ export class Table extends React.Component { }; render(): JSX.Element { - const gridTemplateColumns = this.props.headers.map(() => 'max-content').join(' ').concat(' minmax(0px, 1fr)'); + const gridTemplateColumns = this.props.headers + .map(() => 'max-content') + .join(' ') + .concat(' minmax(0px, 1fr)'); return (
      - {this.props.showHeader && } - + {this.props.showHeader && ( + + )} +
      ); diff --git a/packages/react-components/src/components/utils/filter-tree/tree.tsx b/packages/react-components/src/components/utils/filter-tree/tree.tsx index 7d3f8fee5..a1f5bd6a4 100644 --- a/packages/react-components/src/components/utils/filter-tree/tree.tsx +++ b/packages/react-components/src/components/utils/filter-tree/tree.tsx @@ -12,11 +12,11 @@ interface FilterTreeProps { nodes: TreeNode[]; showCheckboxes: boolean; showCloseIcons: boolean; - showFilter: boolean; // Optional - checkedSeries: number[]; // Optional + showFilter: boolean; // Optional + checkedSeries: number[]; // Optional collapsedNodes: number[]; selectedRow?: number; - onToggleCheck: (ids: number[]) => void; // Optional + onToggleCheck: (ids: number[]) => void; // Optional onClose: (id: number) => void; onRowClick: (id: number) => void; onContextMenu: (event: React.MouseEvent, id: number) => void; @@ -36,8 +36,12 @@ export class FilterTree extends React.Component = { checkedSeries: [], showFilter: true, - onToggleCheck: () => { /* Nothing to do */ }, - onOrderChange: () => { /* Nothing to do */ }, + onToggleCheck: () => { + /* Nothing to do */ + }, + onOrderChange: () => { + /* Nothing to do */ + } }; constructor(props: FilterTreeProps) { @@ -133,7 +137,9 @@ export class FilterTree extends React.Component this.getNode(this.state.filteredNodes, childId) !== undefined); + const visibleChildrenIds = childrenIds.filter( + (childId: number) => this.getNode(this.state.filteredNodes, childId) !== undefined + ); if (!this.isNodeChecked(id)) { if (checkedNode.children.length) { const childIdsToCheck = visibleChildrenIds.filter(childId => !this.isNodeChecked(childId)); @@ -179,7 +185,9 @@ export class FilterTree extends React.Component { - const visibleNodes = node.children.filter((child: TreeNode) => this.getNode(this.state.filteredNodes, child.id) !== undefined); + const visibleNodes = node.children.filter( + (child: TreeNode) => this.getNode(this.state.filteredNodes, child.id) !== undefined + ); let allChildrenChecked = false; if (visibleNodes.length) { allChildrenChecked = visibleNodes.every((child: TreeNode) => { @@ -206,13 +214,14 @@ export class FilterTree extends React.Component node.children.some((child: TreeNode) => { - let isChecked = this.isNodeChecked(child.id); - if (child.children.length) { - isChecked = isChecked || this.isSomeChildChecked(child); - } - return isChecked; - }); + isSomeChildChecked = (node: TreeNode): boolean => + node.children.some((child: TreeNode) => { + let isChecked = this.isNodeChecked(child.id); + if (child.children.length) { + isChecked = isChecked || this.isSomeChildChecked(child); + } + return isChecked; + }); isCollapsed = (id: number): boolean => this.props.collapsedNodes.includes(id); @@ -242,18 +251,21 @@ export class FilterTree extends React.Component - nodes.filter((node: TreeNode) => matchedIds.indexOf(node.id) > -1) + nodes + .filter((node: TreeNode) => matchedIds.indexOf(node.id) > -1) .map((node: TreeNode) => ({ ...node, children: node.children ? this.filterTree(node.children, matchedIds) : [] })); - renderFilterTree = (): JSX.Element => - ) => this.handleFilterChanged(e.target.value)} /> - {this.renderTable(this.state.filteredNodes)} - ; + renderFilterTree = (): JSX.Element => ( + + ) => this.handleFilterChanged(e.target.value)} /> + {this.renderTable(this.state.filteredNodes)} + + ); - renderTable = (nodes: TreeNode[]): JSX.Element => + renderTable = (nodes: TreeNode[]): JSX.Element => ( ; + /> + ); render(): JSX.Element | undefined { - if (!this.props.nodes) { return undefined; } + if (!this.props.nodes) { + return undefined; + } const rootNodes = this.getRootNodes(); if (rootNodes && rootNodes.length) { - return - {this.props.showFilter - ? this.renderFilterTree() - : this.renderTable(rootNodes) - } - ; + return ( + + {this.props.showFilter ? this.renderFilterTree() : this.renderTable(rootNodes)} + + ); } else { return ; } diff --git a/packages/react-components/src/components/utils/filter-tree/utils.tsx b/packages/react-components/src/components/utils/filter-tree/utils.tsx index 3980b2580..919dcc51f 100644 --- a/packages/react-components/src/components/utils/filter-tree/utils.tsx +++ b/packages/react-components/src/components/utils/filter-tree/utils.tsx @@ -4,18 +4,18 @@ import ColumnHeader from './column-header'; const entryToTreeNode = (entry: Entry, headers: ColumnHeader[]) => { // TODO Instead of padding the labels, ColumnHeader should use a getter function instead of just assuming strings, this will allow to get the legend for XY charts - const labels = ((entry.labels) && (entry.labels.length > 0)) ? entry.labels : ['']; + const labels = entry.labels && entry.labels.length > 0 ? entry.labels : ['']; // Pad the labels to match the header count for (let i = labels.length; i <= headers.length - 1; i++) { labels[i] = ''; } - return ({ + return { labels: labels, isRoot: false, id: entry.id, parentId: entry.parentId, children: [] - } as TreeNode); + } as TreeNode; }; export const listToTree = (list: Entry[], headers: ColumnHeader[]): TreeNode[] => { @@ -28,7 +28,7 @@ export const listToTree = (list: Entry[], headers: ColumnHeader[]): TreeNode[] = // Create the tree in the order it has been received list.forEach(entry => { const node = lookup[entry.id]; - if ((entry.parentId !== undefined) && (entry.parentId !== -1)) { + if (entry.parentId !== undefined && entry.parentId !== -1) { const parent: TreeNode = lookup[entry.parentId]; if (parent) { if (parent.id !== node.id) { @@ -47,7 +47,7 @@ export const listToTree = (list: Entry[], headers: ColumnHeader[]): TreeNode[] = return rootNodes; }; -export const getAllExpandedNodeIds = (nodes: TreeNode[],collapsedNodes: number[]): number[] => { +export const getAllExpandedNodeIds = (nodes: TreeNode[], collapsedNodes: number[]): number[] => { const visibleIds: number[] = []; nodes.forEach((node: TreeNode) => { visibleIds.push(node.id); @@ -66,9 +66,7 @@ export const getIndexOfNode = (id: number, nodes: TreeNode[], collapsedNodes: nu // eslint-disable-next-line @typescript-eslint/no-explicit-any export const validateNumArray = (arr: any | undefined): boolean => { if (arr && Array.isArray(arr)) { - return (arr.length > 0 && - arr.every( value => typeof value === 'number') - ); + return arr.length > 0 && arr.every(value => typeof value === 'number'); } return false; }; diff --git a/packages/react-components/src/components/utils/pagination-bar-component.tsx b/packages/react-components/src/components/utils/pagination-bar-component.tsx index 07df497c4..3f6a1828d 100644 --- a/packages/react-components/src/components/utils/pagination-bar-component.tsx +++ b/packages/react-components/src/components/utils/pagination-bar-component.tsx @@ -21,40 +21,49 @@ enum Direction { export class PaginationBarComponent extends React.Component { render(): JSX.Element { - const currentPage = this.props.gridApi?.paginationGetCurrentPage() ? this.props.gridApi?.paginationGetCurrentPage() + 1 : 1; + 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); + const lastRow = + currentPage === this.props.paginationTotalPages + 1 + ? numberFormat(this.props.nbEvents) + : numberFormat(firstRowRaw + this.props.paginationPageSize - 1); - return
      - - {firstRow} to {lastRow} of {numberFormat(this.props.nbEvents)} - + return ( +
      + + {firstRow} to {lastRow} of {numberFormat(this.props.nbEvents)} + - + - + - - Page {currentPage} of {this.props.paginationTotalPages + 1} - + + Page {currentPage} of {this.props.paginationTotalPages + 1} + - + - -
      ; + +
      + ); } private paginationJumpTo(direction: Direction): void { diff --git a/packages/react-components/src/components/utils/time-axis-component.tsx b/packages/react-components/src/components/utils/time-axis-component.tsx index 1e8af72f8..8879026ce 100644 --- a/packages/react-components/src/components/utils/time-axis-component.tsx +++ b/packages/react-components/src/components/utils/time-axis-component.tsx @@ -7,11 +7,11 @@ import { TimeGraphUnitController } from 'timeline-chart/lib/time-graph-unit-cont interface TimeAxisProps { unitController: TimeGraphUnitController; style: { - width: number, - chartBackgroundColor: number, - cursorColor: number, - lineColor: number, - verticalAlign: string + width: number; + chartBackgroundColor: number; + cursorColor: number; + lineColor: number; + verticalAlign: string; }; addWidgetResizeHandler: (handler: () => void) => void; removeWidgetResizeHandler: (handler: () => void) => void; @@ -19,20 +19,23 @@ interface TimeAxisProps { export class TimeAxisComponent extends React.Component { render(): JSX.Element { - return ; + return ( + + ); } protected getAxisLayer(): TimeGraphAxis { diff --git a/packages/react-components/src/components/utils/time-navigator-component.tsx b/packages/react-components/src/components/utils/time-navigator-component.tsx index 61e4b0e5b..9380fe833 100644 --- a/packages/react-components/src/components/utils/time-navigator-component.tsx +++ b/packages/react-components/src/components/utils/time-navigator-component.tsx @@ -6,10 +6,10 @@ import { TimeGraphNavigator } from 'timeline-chart/lib/layer/time-graph-navigato interface TimeNavigatorProps { unitController: TimeGraphUnitController; style: { - width: number, - naviBackgroundColor: number, - cursorColor: number, - lineColor: number + width: number; + naviBackgroundColor: number; + cursorColor: number; + lineColor: number; }; addWidgetResizeHandler: (handler: () => void) => void; removeWidgetResizeHandler: (handler: () => void) => void; @@ -18,18 +18,21 @@ interface TimeNavigatorProps { export class TimeNavigatorComponent extends React.Component { render(): JSX.Element { const navi = new TimeGraphNavigator('timeGraphNavigator'); - return ; + return ( + + ); } } diff --git a/packages/react-components/src/components/utils/timegraph-container-component.tsx b/packages/react-components/src/components/utils/timegraph-container-component.tsx index 257051c6f..834dde280 100644 --- a/packages/react-components/src/components/utils/timegraph-container-component.tsx +++ b/packages/react-components/src/components/utils/timegraph-container-component.tsx @@ -6,13 +6,13 @@ import { debounce } from 'lodash'; export namespace ReactTimeGraphContainer { export interface Props { - id: string, - options: TimeGraphContainerOptions, - unitController: TimeGraphUnitController, - layers: TimeGraphLayer[], - children?: never[], - addWidgetResizeHandler: (handler: () => void) => void - removeWidgetResizeHandler: (handler: () => void) => void + id: string; + options: TimeGraphContainerOptions; + unitController: TimeGraphUnitController; + layers: TimeGraphLayer[]; + children?: never[]; + addWidgetResizeHandler: (handler: () => void) => void; + removeWidgetResizeHandler: (handler: () => void) => void; } } @@ -20,7 +20,7 @@ export class ReactTimeGraphContainer extends React.Component this.ref = ref || undefined } onWheel={ e => e.preventDefault() } tabIndex={ 0 }>; + return ( + (this.ref = ref || undefined)} onWheel={e => e.preventDefault()} tabIndex={0}> + ); } private resize(): void { if (this.container) { - this.container.updateCanvas(this.props.options.width, this.props.options.height, this.props.options.backgroundColor, this.props.options.lineColor); + this.container.updateCanvas( + this.props.options.width, + this.props.options.height, + this.props.options.backgroundColor, + this.props.options.lineColor + ); } } } diff --git a/packages/react-components/src/components/utils/unit-controller-history-handler.ts b/packages/react-components/src/components/utils/unit-controller-history-handler.ts index 3e43e8ea6..5a8c8220d 100644 --- a/packages/react-components/src/components/utils/unit-controller-history-handler.ts +++ b/packages/react-components/src/components/utils/unit-controller-history-handler.ts @@ -73,7 +73,7 @@ export class UnitControllerHistoryHandler { const { selectionRange, viewRange } = this.history[this.index]; this.unitController.selectionRange = selectionRange; this.unitController.viewRange = viewRange; - setTimeout(() => this.restoring = false, 500); + setTimeout(() => (this.restoring = false), 500); } private isEntryDuplicate(item: HistoryItem): boolean { @@ -88,7 +88,7 @@ export class UnitControllerHistoryHandler { if (oneIsDifferent) { return; } - oneIsDifferent = (value1 !== value2); + oneIsDifferent = value1 !== value2; }; check(itemSR?.start, prevSR?.start); check(itemSR?.end, prevSR?.end); @@ -104,5 +104,4 @@ export class UnitControllerHistoryHandler { private get canUndo(): boolean { return this.index > 1; } - } diff --git a/packages/react-components/src/components/utils/xy-output-component-utils.tsx b/packages/react-components/src/components/utils/xy-output-component-utils.tsx index 41ce669a8..b4f0621fa 100644 --- a/packages/react-components/src/components/utils/xy-output-component-utils.tsx +++ b/packages/react-components/src/components/utils/xy-output-component-utils.tsx @@ -2,46 +2,46 @@ import { TimeRange } from 'traceviewer-base/src/utils/time-range'; import { AbstractOutputProps } from '../abstract-output-component'; export interface XYChartFactoryParams { - viewRange: TimeRange, - allMax: number, - allMin: number, - isScatterPlot: boolean + viewRange: TimeRange; + allMax: number; + allMin: number; + isScatterPlot: boolean; } export interface XYOutputMargin { - top: number, - right: number, - bottom: number, - left: number + top: number; + right: number; + bottom: number; + left: number; } export interface GetClosestPointParam { - dataPoints: XYPoint[], - mousePosition: XYPoint, - chartWidth: number, - chartHeight: number, - range: TimeRange, - margin: XYOutputMargin, - allMax: number, - allMin: number, + dataPoints: XYPoint[]; + mousePosition: XYPoint; + chartWidth: number; + chartHeight: number; + range: TimeRange; + margin: XYOutputMargin; + allMax: number; + allMin: number; } export interface DrawSelectionParams { - ctx: CanvasRenderingContext2D | null, - chartArea: Chart.ChartArea | undefined | null, - startPixel: number, - endPixel: number, - isBarPlot: boolean, - props: AbstractOutputProps, - invertSelection: boolean + ctx: CanvasRenderingContext2D | null; + chartArea: Chart.ChartArea | undefined | null; + startPixel: number; + endPixel: number; + isBarPlot: boolean; + props: AbstractOutputProps; + invertSelection: boolean; } export interface XYPoint { - x: number, - y: number + x: number; + y: number; } -export function xyChartFactory(params: XYChartFactoryParams): Chart.ChartOptions { +export function xyChartFactory(params: XYChartFactoryParams): Chart.ChartOptions { const baseOptions: Chart.ChartOptions = getDefaultChartOptions(params); // If the chart is a line chart @@ -52,7 +52,7 @@ export function xyChartFactory(params: XYChartFactoryParams): Chart.ChartOption return getScatterChartOptions(baseOptions); } -function getLineChartOptions(lineOptions: Chart.ChartOptions): Chart.ChartOptions{ +function getLineChartOptions(lineOptions: Chart.ChartOptions): Chart.ChartOptions { if (lineOptions.elements && lineOptions.elements.point && lineOptions.elements.line && lineOptions.scales) { lineOptions.elements.point.radius = 0; lineOptions.elements.line.borderWidth = 0; @@ -62,7 +62,7 @@ function getLineChartOptions(lineOptions: Chart.ChartOptions): Chart.ChartOption return lineOptions; } -function getScatterChartOptions(scatterOptions: Chart.ChartOptions): Chart.ChartOptions{ +function getScatterChartOptions(scatterOptions: Chart.ChartOptions): Chart.ChartOptions { if (scatterOptions.elements && scatterOptions.elements.point) { scatterOptions.elements.point.radius = 2; } @@ -87,7 +87,7 @@ function getDefaultChartOptions(params: XYChartFactoryParams): Chart.ChartOption tooltips: { intersect: false, mode: 'index', - enabled: false, + enabled: false }, layout: { padding: { @@ -98,32 +98,36 @@ function getDefaultChartOptions(params: XYChartFactoryParams): Chart.ChartOption } }, scales: { - xAxes: [{ - id: 'time-axis', - display: false, - ticks: { - min: Number(params.viewRange?.getStart() - offset), - max: Number(params.viewRange?.getEnd() - offset) + xAxes: [ + { + id: 'time-axis', + display: false, + ticks: { + min: Number(params.viewRange?.getStart() - offset), + max: Number(params.viewRange?.getEnd() - offset) + } } - }], - yAxes: [{ - display: false, - stacked: false, - ticks: { - max: params.allMax > 0 ? params.allMax : 100, - min: params.allMin + ], + yAxes: [ + { + display: false, + stacked: false, + ticks: { + max: params.allMax > 0 ? params.allMax : 100, + min: params.allMin + } } - }] + ] }, animation: { duration: 0 }, - events: ['mousedown'], + events: ['mousedown'] }; return lineOptions; } export function drawSelection(params: DrawSelectionParams): void { - const { startPixel, endPixel, isBarPlot, chartArea, props, ctx, invertSelection} = params; + const { startPixel, endPixel, isBarPlot, chartArea, props, ctx, invertSelection } = params; const minPixel = Math.min(startPixel, endPixel); const maxPixel = Math.max(startPixel, endPixel); const initialPoint = isBarPlot ? 0 : chartArea?.left ?? 0; @@ -152,9 +156,8 @@ export function drawSelection(params: DrawSelectionParams): void { ctx.globalAlpha = 0.2; if (!invertSelection) { ctx.fillRect(minPixel, 0, maxPixel - minPixel, finalPoint); - } - else { - const leftSideWidth = - minPixel; + } else { + const leftSideWidth = -minPixel; const rightSideWidth = (chartArea?.right ? chartArea.right : 0) - maxPixel; ctx.fillRect(minPixel, 0, leftSideWidth, finalPoint); @@ -181,7 +184,7 @@ export function getClosestPointForScatterPlot(params: GetClosestPointParam): XYP const distY = params.chartHeight - params.mousePosition.y - y; const hypotenuse = distX * distX + distY * distY; - if (min_hypotenuse > hypotenuse){ + if (min_hypotenuse > hypotenuse) { closestPoint = point; min_hypotenuse = hypotenuse; } @@ -195,6 +198,6 @@ export function getClosestPointForScatterPlot(params: GetClosestPointParam): XYP return undefined; } -export function numberFormat(rawNumber: number): string{ +export function numberFormat(rawNumber: number): string { return new Intl.NumberFormat().format(rawNumber); } diff --git a/packages/react-components/src/components/xy-output-component.tsx b/packages/react-components/src/components/xy-output-component.tsx index f001145a1..21c9ee8c4 100644 --- a/packages/react-components/src/components/xy-output-component.tsx +++ b/packages/react-components/src/components/xy-output-component.tsx @@ -5,7 +5,15 @@ import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/respon import Chart = require('chart.js'); import { BIMath } from 'timeline-chart/lib/bigint-utils'; import { scaleLinear } from 'd3-scale'; -import { AbstractXYOutputComponent, AbstractXYOutputState, FLAG_PAN_LEFT, FLAG_PAN_RIGHT, FLAG_ZOOM_IN, FLAG_ZOOM_OUT, MouseButton } from './abstract-xy-output-component'; +import { + AbstractXYOutputComponent, + AbstractXYOutputState, + FLAG_PAN_LEFT, + FLAG_PAN_RIGHT, + FLAG_ZOOM_IN, + FLAG_ZOOM_OUT, + MouseButton +} from './abstract-xy-output-component'; import { TimeRange } from 'traceviewer-base/src/utils/time-range'; import { validateNumArray } from './utils/filter-tree/utils'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -22,8 +30,12 @@ export class XYOutputComponent extends AbstractXYOutputComponent ({ checkedSeries: this.state.checkedSeries, - collapsedNodes: this.state.collapsedNodes, + collapsedNodes: this.state.collapsedNodes })); this.addOptions('Export table to CSV...', () => this.exportOutput()); } renderChart(): React.ReactNode { if (this.state.outputStatus === ResponseStatus.COMPLETED && this.state.xyData?.datasets?.length === 0) { - return -
      - Select a checkbox to see analysis results -
      -
      ; + return ( + +
      Select a checkbox to see analysis results
      +
      + ); } - return -
      this.onKeyDown(event)} - onKeyUp={event => this.onKeyUp(event)} - onWheel={event => this.onWheel(event)} - onMouseMove={event => this.onMouseMove(event)} - onContextMenu={event => event.preventDefault()} - onMouseLeave={event => this.onMouseLeave(event)} - onMouseDown={event => this.onMouseDown(event)} - style={{ height: this.props.style.height, position: 'relative', cursor: this.state.cursor }} - ref={this.divRef} - > - {this.isBarPlot ? this.drawD3Chart() : this.chooseChart()} -
      - {(this.state.outputStatus === ResponseStatus.RUNNING) && + return ( +
      this.onKeyDown(event)} + onKeyUp={event => this.onKeyUp(event)} + onWheel={event => this.onWheel(event)} + onMouseMove={event => this.onMouseMove(event)} + onContextMenu={event => event.preventDefault()} + onMouseLeave={event => this.onMouseLeave(event)} + onMouseDown={event => this.onMouseDown(event)} + style={{ height: this.props.style.height, position: 'relative', cursor: this.state.cursor }} + ref={this.divRef} > -
      - - Analysis running -
      + {this.isBarPlot ? this.drawD3Chart() : this.chooseChart()}
      - } -
      ; + {this.state.outputStatus === ResponseStatus.RUNNING && ( +
      +
      + + Analysis running +
      +
      + )} +
      + ); } private drawD3Chart(): JSX.Element { @@ -112,9 +122,7 @@ export class XYOutputComponent extends AbstractXYOutputComponent { ctx.beginPath(); const xPos = this.getXForTime(tupple.xValue); - ctx.fillRect(xScale(xPos), chartHeight, 2, - chartHeight + yScale(+tupple.yValue)); + ctx.fillRect(xScale(xPos), chartHeight, 2, -chartHeight + yScale(+tupple.yValue)); ctx.closePath(); }); }); @@ -146,13 +154,7 @@ export class XYOutputComponent extends AbstractXYOutputComponent - ); + return ; } protected afterChartDraw(ctx: CanvasRenderingContext2D | null, chartArea?: Chart.ChartArea | null): void { @@ -175,7 +177,12 @@ export class XYOutputComponent extends AbstractXYOutputComponent ); if (this.state.outputStatus === ResponseStatus.COMPLETED && this.state.xyData?.datasets?.length === 0) { - return - {selectionDialog} -
      - Select a checkbox to see analysis results -
      -
      ; + return ( + + {selectionDialog} +
      Select a checkbox to see analysis results
      +
      + ); } - return - {selectionDialog} -
      this.onKeyDown(event)} - onKeyUp={event => this.onKeyUp(event)} - onWheel={event => this.onWheel(event)} - onMouseMove={event => this.onMouseMove(event)} - onContextMenu={event => event.preventDefault()} - onMouseLeave={event => this.onMouseLeave(event)} - onMouseDown={event => this.onMouseDown(event)} - style={{ height: this.props.style.height, position: 'relative', cursor: this.state.cursor }} - ref={this.divRef} - > - {this.chooseChart()} -
      - {(this.state.outputStatus === ResponseStatus.RUNNING) && + return ( + + {selectionDialog}
      this.onKeyDown(event)} + onKeyUp={event => this.onKeyUp(event)} + onWheel={event => this.onWheel(event)} + onMouseMove={event => this.onMouseMove(event)} + onContextMenu={event => event.preventDefault()} + onMouseLeave={event => this.onMouseLeave(event)} + onMouseDown={event => this.onMouseDown(event)} + style={{ height: this.props.style.height, position: 'relative', cursor: this.state.cursor }} + ref={this.divRef} > -
      - - Analysis running -
      + {this.chooseChart()}
      - } -
      ; + {this.state.outputStatus === ResponseStatus.RUNNING && ( +
      +
      + + Analysis running +
      +
      + )} +
      + ); } protected getDisplayedRange(): TimeRange { @@ -156,7 +158,7 @@ export class XYOutputOverviewComponent extends AbstractXYOutputComponent rightLimit) { @@ -346,7 +348,7 @@ export class XYOutputOverviewComponent extends AbstractXYOutputComponent {this.closeDropDown();}); + this.setState( + { + showTree: !this.state.showTree + }, + () => { + this.closeDropDown(); + } + ); } } diff --git a/packages/react-components/src/trace-explorer/trace-explorer-opened-traces-widget.tsx b/packages/react-components/src/trace-explorer/trace-explorer-opened-traces-widget.tsx index 8440968ea..07f89dc34 100644 --- a/packages/react-components/src/trace-explorer/trace-explorer-opened-traces-widget.tsx +++ b/packages/react-components/src/trace-explorer/trace-explorer-opened-traces-widget.tsx @@ -12,15 +12,15 @@ import { ITspClientProvider } from 'traceviewer-base/lib/tsp-client-provider'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; export interface ReactOpenTracesWidgetProps { - id: string, - title: string, - tspClientProvider: ITspClientProvider, - contextMenuRenderer?: (event: React.MouseEvent, experiment: Experiment) => void, - onClick?: (event: React.MouseEvent, experiment: Experiment) => void + id: string; + title: string; + tspClientProvider: ITspClientProvider; + contextMenuRenderer?: (event: React.MouseEvent, experiment: Experiment) => void; + onClick?: (event: React.MouseEvent, experiment: Experiment) => void; } export interface ReactOpenTracesWidgetState { - openedExperiments: Array, + openedExperiments: Array; selectedExperimentIndex: number; } @@ -36,10 +36,13 @@ export class ReactOpenTracesWidget extends React.Component => this.doHandleExperimentOpenedSignal(experiment); + private _onExperimentOpened = (experiment: Experiment): Promise => + this.doHandleExperimentOpenedSignal(experiment); private _onExperimentClosed = (experiment: Experiment): void => this.doHandleExperimentClosed(experiment); - private _onExperimentDeleted = (experiment: Experiment): Promise => this.doHandleExperimentDeletedSignal(experiment); - private _onOpenedTracesWidgetActivated = (experiment: Experiment): void => this.doHandleTracesWidgetActivatedSignal(experiment); + private _onExperimentDeleted = (experiment: Experiment): Promise => + this.doHandleExperimentDeletedSignal(experiment); + private _onOpenedTracesWidgetActivated = (experiment: Experiment): void => + this.doHandleTracesWidgetActivatedSignal(experiment); private _onTraceServerStarted = (): Promise => this.doHandleTraceServerStartedSignal(); constructor(props: ReactOpenTracesWidgetProps) { @@ -96,7 +99,9 @@ export class ReactOpenTracesWidget extends React.Component openedExperiment.UUID === experiment.UUID); + const selectedIndex = this.state.openedExperiments.findIndex( + openedExperiment => openedExperiment.UUID === experiment.UUID + ); this.selectExperiment(selectedIndex); } } @@ -131,15 +136,19 @@ export class ReactOpenTracesWidget extends React.Component - + {this.renderSharingModal()} -
      -
      +
      +
      - {({ width }) => + {({ width }) => ( } + /> + )}
      @@ -162,40 +172,57 @@ export class ReactOpenTracesWidget extends React.Component= 0) { traceContainerClassName = traceContainerClassName + ' theia-mod-selected'; } - return
      { this.handleClickEvent(event, traceUUID); }} - onContextMenu={event => { this.handleContextMenuEvent(event, traceUUID); }} - data-id={`${props.index}`}> -
      -
      -

      {traceName}

      - {this.renderTracesForExperiment(props.index)} -
      -
      - -
      - {/*
      + return ( +
      { + this.handleClickEvent(event, traceUUID); + }} + onContextMenu={event => { + this.handleContextMenuEvent(event, traceUUID); + }} + data-id={`${props.index}`} + > +
      +
      +

      {traceName}

      + {this.renderTracesForExperiment(props.index)} +
      +
      + +
      + {/*
      */} +
      -
      ; + ); } protected doHandleOnExperimentDeleted(e: React.MouseEvent, traceUUID: string): void { @@ -208,9 +235,14 @@ export class ReactOpenTracesWidget extends React.Component +
      {tracePaths.map(trace => ( -
      +
      {` > ${trace.name}`}
      ))} @@ -243,25 +275,28 @@ export class ReactOpenTracesWidget extends React.Component -
      - {'Copy URL to share your trace context'} -
      -
      -
      -