chore: use ContextManager for trace-viewer state handling

This commit is contained in:
Max Schmitt 2024-07-18 15:29:57 +02:00
parent 6491e5b415
commit 33e4b31dcc
9 changed files with 48 additions and 28 deletions

View file

@ -25,13 +25,13 @@ import type { TreeState } from '@web/components/treeView';
import { TreeView } from '@web/components/treeView';
import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil';
import type { Boundaries } from '../geometry';
import { WorkbenchContext } from './workbenchContext';
export interface ActionListProps {
actions: ActionTraceEventInContext[],
selectedAction: ActionTraceEventInContext | undefined,
selectedTime: Boundaries | undefined,
setSelectedTime: (time: Boundaries | undefined) => void,
sdkLanguage: Language | undefined;
onSelected: (action: ActionTraceEventInContext) => void,
onHighlighted: (action: ActionTraceEventInContext | undefined) => void,
revealConsole: () => void,
@ -45,12 +45,12 @@ export const ActionList: React.FC<ActionListProps> = ({
selectedAction,
selectedTime,
setSelectedTime,
sdkLanguage,
onSelected,
onHighlighted,
revealConsole,
isLive,
}) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
const { rootItem, itemMap } = React.useMemo(() => modelUtil.buildActionTree(actions), [actions]);

View file

@ -24,11 +24,12 @@ import { asLocator } from '@isomorphic/locatorGenerators';
import type { Language } from '@isomorphic/locatorGenerators';
import { PlaceholderPanel } from './placeholderPanel';
import type { ActionTraceEventInContext } from './modelUtil';
import { WorkbenchContext } from './workbenchContext';
export const CallTab: React.FunctionComponent<{
action: ActionTraceEventInContext | undefined,
sdkLanguage: Language | undefined,
}> = ({ action, sdkLanguage }) => {
}> = ({ action }) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
if (!action)
return <PlaceholderPanel text='No action selected' />;
const params = { ...action.params };

View file

@ -19,8 +19,8 @@ import * as React from 'react';
import type * as modelUtil from './modelUtil';
import { PlaceholderPanel } from './placeholderPanel';
import { renderAction } from './actionList';
import type { Language } from '@isomorphic/locatorGenerators';
import type { StackFrame } from '@protocol/channels';
import { WorkbenchContext } from './workbenchContext';
type ErrorDescription = {
action?: modelUtil.ActionTraceEventInContext;
@ -44,9 +44,9 @@ export function useErrorsTabModel(model: modelUtil.MultiTraceModel | undefined):
export const ErrorsTab: React.FunctionComponent<{
errorsModel: ErrorsTabModel,
sdkLanguage: Language,
revealInSource: (error: ErrorDescription) => void,
}> = ({ errorsModel, sdkLanguage, revealInSource }) => {
}> = ({ errorsModel, revealInSource }) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
if (!errorsModel.errors.size)
return <PlaceholderPanel text='No errors' />;

View file

@ -15,18 +15,18 @@
*/
import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
import type { Language } from '@web/components/codeMirrorWrapper';
import { ToolbarButton } from '@web/components/toolbarButton';
import { copy } from '@web/uiUtils';
import * as React from 'react';
import './sourceTab.css';
import { WorkbenchContext } from './workbenchContext';
export const InspectorTab: React.FunctionComponent<{
sdkLanguage: Language,
setIsInspecting: (isInspecting: boolean) => void,
highlightedLocator: string,
setHighlightedLocator: (locator: string) => void,
}> = ({ sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => {
}> = ({ setIsInspecting, highlightedLocator, setHighlightedLocator }) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
return <div className='vbox' style={{ backgroundColor: 'var(--vscode-sideBar-background)' }}>
<div style={{ margin: '10px 0px 10px 10px', color: 'var(--vscode-editorCodeLens-foreground)', flex: 'none' }}>Locator</div>
<div style={{ margin: '0 10px 10px', flex: 'auto' }}>

View file

@ -29,17 +29,18 @@ import type { Language } from '@isomorphic/locatorGenerators';
import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser';
import { TabbedPaneTab } from '@web/components/tabbedPane';
import { BrowserFrame } from './browserFrame';
import { WorkbenchContext } from './workbenchContext';
export const SnapshotTab: React.FunctionComponent<{
action: ActionTraceEvent | undefined,
sdkLanguage: Language,
testIdAttributeName: string,
isInspecting: boolean,
setIsInspecting: (isInspecting: boolean) => void,
highlightedLocator: string,
setHighlightedLocator: (locator: string) => void,
openPage?: (url: string, target?: string) => Window | any,
}> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => {
}> = ({ action, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
const [measure, ref] = useMeasure<HTMLDivElement>();
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
@ -166,7 +167,6 @@ export const SnapshotTab: React.FunctionComponent<{
>
<InspectModeController
isInspecting={isInspecting}
sdkLanguage={sdkLanguage}
testIdAttributeName={testIdAttributeName}
highlightedLocator={highlightedLocator}
setHighlightedLocator={setHighlightedLocator}
@ -174,7 +174,6 @@ export const SnapshotTab: React.FunctionComponent<{
iteration={loadingRef.current.iteration} />
<InspectModeController
isInspecting={isInspecting}
sdkLanguage={sdkLanguage}
testIdAttributeName={testIdAttributeName}
highlightedLocator={highlightedLocator}
setHighlightedLocator={setHighlightedLocator}
@ -229,12 +228,12 @@ function renderTitle(snapshotTitle: string): string {
export const InspectModeController: React.FunctionComponent<{
iframe: HTMLIFrameElement | null,
isInspecting: boolean,
sdkLanguage: Language,
testIdAttributeName: string,
highlightedLocator: string,
setHighlightedLocator: (locator: string) => void,
iteration: number,
}> = ({ iframe, isInspecting, sdkLanguage, testIdAttributeName, highlightedLocator, setHighlightedLocator, iteration }) => {
}> = ({ iframe, isInspecting, testIdAttributeName, highlightedLocator, setHighlightedLocator, iteration }) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
React.useEffect(() => {
const recorders: { recorder: Recorder, frameSelector: string }[] = [];
const isUnderTest = new URLSearchParams(window.location.search).get('isUnderTest') === 'true';

View file

@ -22,8 +22,8 @@ import { FilmStrip } from './filmStrip';
import type { FilmStripPreviewPoint } from './filmStrip';
import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil';
import './timeline.css';
import type { Language } from '@isomorphic/locatorGenerators';
import type { Entry } from '@trace/har';
import { WorkbenchContext } from './workbenchContext';
type TimelineBar = {
action?: ActionTraceEventInContext;
@ -44,8 +44,8 @@ export const Timeline: React.FunctionComponent<{
onSelected: (action: ActionTraceEventInContext) => void,
selectedTime: Boundaries | undefined,
setSelectedTime: (time: Boundaries | undefined) => void,
sdkLanguage: Language,
}> = ({ model, boundaries, onSelected, highlightedAction, highlightedEntry, selectedTime, setSelectedTime, sdkLanguage }) => {
}> = ({ model, boundaries, onSelected, highlightedAction, highlightedEntry, selectedTime, setSelectedTime }) => {
const { sdkLanguage } = React.useContext(WorkbenchContext);
const [measure, ref] = useMeasure<HTMLDivElement>();
const [dragWindow, setDragWindow] = React.useState<{ startX: number, endX: number, pivot?: number, type: 'resize' | 'move' } | undefined>();
const [previewPoint, setPreviewPoint] = React.useState<FilmStripPreviewPoint | undefined>();

View file

@ -120,13 +120,10 @@ export const Workbench: React.FunctionComponent<{
return model?.actions.map(a => a.attachments || []).flat() || [];
}, [model]);
const sdkLanguage = model?.sdkLanguage || 'javascript';
const inspectorTab: TabbedPaneTabModel = {
id: 'inspector',
title: 'Locator',
render: () => <InspectorTab
sdkLanguage={sdkLanguage}
setIsInspecting={setIsInspecting}
highlightedLocator={highlightedLocator}
setHighlightedLocator={setHighlightedLocator} />,
@ -134,7 +131,7 @@ export const Workbench: React.FunctionComponent<{
const callTab: TabbedPaneTabModel = {
id: 'call',
title: 'Call',
render: () => <CallTab action={activeAction} sdkLanguage={sdkLanguage} />
render: () => <CallTab action={activeAction} />
};
const logTab: TabbedPaneTabModel = {
id: 'log',
@ -145,7 +142,7 @@ export const Workbench: React.FunctionComponent<{
id: 'errors',
title: 'Errors',
errorCount: errorsModel.errors.size,
render: () => <ErrorsTab errorsModel={errorsModel} sdkLanguage={sdkLanguage} revealInSource={error => {
render: () => <ErrorsTab errorsModel={errorsModel} revealInSource={error => {
if (error.action)
setSelectedAction(error.action);
else
@ -222,7 +219,6 @@ export const Workbench: React.FunctionComponent<{
highlightedAction={highlightedAction}
highlightedEntry={highlightedEntry}
onSelected={onActionSelected}
sdkLanguage={sdkLanguage}
selectedTime={selectedTime}
setSelectedTime={setSelectedTime}
/>
@ -230,7 +226,6 @@ export const Workbench: React.FunctionComponent<{
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true} settingName='actionListSidebar'>
<SnapshotTab
action={activeAction}
sdkLanguage={sdkLanguage}
testIdAttributeName={model?.testIdAttributeName || 'data-testid'}
isInspecting={isInspecting}
setIsInspecting={setIsInspecting}
@ -250,7 +245,6 @@ export const Workbench: React.FunctionComponent<{
<div className='workbench-run-duration'>{time ? msToString(time) : ''}</div>
</div>}
<ActionList
sdkLanguage={sdkLanguage}
actions={model?.actions || []}
selectedAction={model ? selectedAction : undefined}
selectedTime={selectedTime}

View file

@ -0,0 +1,23 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Language } from '@isomorphic/locatorGenerators';
import { createContext } from 'react';
type Context = {
sdkLanguage: Language,
};
export const WorkbenchContext = createContext<Context>(null as any);

View file

@ -22,6 +22,7 @@ import './workbenchLoader.css';
import { toggleTheme } from '@web/theme';
import { Workbench } from './workbench';
import { TestServerConnection } from '@testIsomorphic/testServerConnection';
import { WorkbenchContext } from './workbenchContext';
export const WorkbenchLoader: React.FunctionComponent<{
}> = () => {
@ -165,7 +166,9 @@ export const WorkbenchLoader: React.FunctionComponent<{
<div className='progress'>
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
</div>
<Workbench model={model} inert={showFileUploadDropArea} />
<WorkbenchContext.Provider value={{ sdkLanguage: model?.sdkLanguage || 'javascript' }}>
<Workbench model={model} inert={showFileUploadDropArea} />
</WorkbenchContext.Provider>
{fileForLocalModeError && <div className='drop-target'>
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
<div style={{ paddingTop: 20 }}>