From 26e7b1e77a757f7c1a0c6b986b501885aacc4af6 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Mon, 4 Nov 2024 10:48:22 +0100 Subject: [PATCH] split up highlight and reveal --- packages/trace-viewer/src/ui/actionList.tsx | 18 +++++++-------- .../trace-viewer/src/ui/attachmentsTab.tsx | 22 ++++++++++++------- packages/trace-viewer/src/ui/workbench.tsx | 9 ++++++-- .../ui-mode-test-attachments.spec.ts | 4 ++-- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index bcefc81438..101c532aea 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -14,7 +14,7 @@ limitations under the License. */ -import type { ActionTraceEvent } from '@trace/trace'; +import type { ActionTraceEvent, AfterActionTraceEventAttachment } from '@trace/trace'; import { msToString } from '@web/uiUtils'; import * as React from 'react'; import './actionList.css'; @@ -36,7 +36,7 @@ export interface ActionListProps { onSelected?: (action: ActionTraceEventInContext) => void, onHighlighted?: (action: ActionTraceEventInContext | undefined) => void, revealConsole?: () => void, - revealAttachments(): void, + revealAttachment(attachment: AfterActionTraceEventAttachment): void, isLive?: boolean, } @@ -51,7 +51,7 @@ export const ActionList: React.FC = ({ onSelected, onHighlighted, revealConsole, - revealAttachments, + revealAttachment, isLive, }) => { const [treeState, setTreeState] = React.useState({ expandedItems: new Map() }); @@ -71,8 +71,8 @@ export const ActionList: React.FC = ({ }, [setSelectedTime]); const render = React.useCallback((item: ActionTreeItem) => { - return renderAction(item.action!, { sdkLanguage, revealConsole, revealAttachments, isLive, showDuration: true, showBadges: true }); - }, [isLive, revealConsole, revealAttachments, sdkLanguage]); + return renderAction(item.action!, { sdkLanguage, revealConsole, revealAttachment, isLive, showDuration: true, showBadges: true }); + }, [isLive, revealConsole, revealAttachment, sdkLanguage]); const isVisible = React.useCallback((item: ActionTreeItem) => { return !selectedTime || !item.action || (item.action!.startTime <= selectedTime.maximum && item.action!.endTime >= selectedTime.minimum); @@ -109,15 +109,15 @@ export const renderAction = ( options: { sdkLanguage?: Language, revealConsole?: () => void, - revealAttachments?(): void, + revealAttachment?(attachment: AfterActionTraceEventAttachment): void, isLive?: boolean, showDuration?: boolean, showBadges?: boolean, }) => { - const { sdkLanguage, revealConsole, revealAttachments, isLive, showDuration, showBadges } = options; + const { sdkLanguage, revealConsole, revealAttachment, isLive, showDuration, showBadges } = options; const { errors, warnings } = modelUtil.stats(action); const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector) : undefined; - const showAttachments = !!action.attachments?.length && !!revealAttachments; + const showAttachments = !!action.attachments?.length && !!revealAttachment; let time: string = ''; if (action.endTime) @@ -134,7 +134,7 @@ export const renderAction = ( {action.class === 'APIRequestContext' && action.params.url &&
{excludeOrigin(action.params.url)}
} {(showDuration || showBadges || showAttachments) &&
} - {showAttachments && } + {showAttachments && revealAttachment(action.attachments![0])} />} {showDuration &&
{time || }
} {showBadges &&
revealConsole?.()}> {!!errors &&
{errors}
} diff --git a/packages/trace-viewer/src/ui/attachmentsTab.tsx b/packages/trace-viewer/src/ui/attachmentsTab.tsx index d26089265e..cf9ed2e681 100644 --- a/packages/trace-viewer/src/ui/attachmentsTab.tsx +++ b/packages/trace-viewer/src/ui/attachmentsTab.tsx @@ -30,10 +30,11 @@ type Attachment = AfterActionTraceEventAttachment & { traceUrl: string }; type ExpandableAttachmentProps = { attachment: Attachment; - highlight?: boolean; + reveal: boolean; + highlight: boolean; }; -const ExpandableAttachment: React.FunctionComponent = ({ attachment, highlight }) => { +const ExpandableAttachment: React.FunctionComponent = ({ attachment, reveal, highlight }) => { const [expanded, setExpanded] = React.useState(false); const [attachmentText, setAttachmentText] = React.useState(null); const [placeholder, setPlaceholder] = React.useState(null); @@ -43,9 +44,9 @@ const ExpandableAttachment: React.FunctionComponent = const hasContent = !!attachment.sha1 || !!attachment.path; React.useEffect(() => { - if (highlight) + if (reveal) ref.current?.scrollIntoView({ behavior: 'smooth' }); - }, [highlight]); + }, [reveal]); React.useEffect(() => { if (expanded && attachmentText === null && placeholder === null) { @@ -92,7 +93,8 @@ const ExpandableAttachment: React.FunctionComponent = export const AttachmentsTab: React.FunctionComponent<{ model: MultiTraceModel | undefined, selectedAction: ActionTraceEventInContext | undefined, -}> = ({ model, selectedAction }) => { + revealedAttachment?: AfterActionTraceEventAttachment, +}> = ({ model, selectedAction, revealedAttachment }) => { const { diffMap, screenshots, attachments } = React.useMemo(() => { const attachments = new Set(); const screenshots = new Set(); @@ -149,14 +151,18 @@ export const AttachmentsTab: React.FunctionComponent<{ {attachments.size ?
Attachments
: undefined} {[...attachments.values()].map((a, i) => { return
- + isEqualAttachment(a, selected)) ?? false} + reveal={!!revealedAttachment && isEqualAttachment(a, revealedAttachment)} + />
; })}
; }; -function isActiveAttachment(attachment: Attachment, activeAction: ActionTraceEventInContext | undefined): boolean { - return activeAction?.attachments?.some(a => a.name === attachment.name && a.path === attachment.path && a.sha1 === attachment.sha1) ?? false; +function isEqualAttachment(a: Attachment, b: AfterActionTraceEventAttachment): boolean { + return a.name === b.name && a.path === b.path && a.sha1 === b.sha1; } function attachmentURL(attachment: Attachment, queryParams: Record = {}) { diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index b97de5b4ce..847cc094d4 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -41,6 +41,7 @@ import type { Entry } from '@trace/har'; import './workbench.css'; import { testStatusIcon, testStatusText } from './testUtils'; import type { UITestStatus } from './testUtils'; +import type { AfterActionTraceEventAttachment } from '@trace/trace'; export const Workbench: React.FunctionComponent<{ model?: modelUtil.MultiTraceModel, @@ -58,6 +59,7 @@ export const Workbench: React.FunctionComponent<{ }> = ({ model, showSourcesFirst, rootDir, fallbackLocation, isLive, hideTimeline, status, annotations, inert, openPage, onOpenExternally, revealSource }) => { const [selectedCallId, setSelectedCallId] = React.useState(undefined); const [revealedError, setRevealedError] = React.useState(undefined); + const [revealedAttachment, setRevealedAttachment] = React.useState(undefined); const [highlightedCallId, setHighlightedCallId] = React.useState(); const [highlightedEntry, setHighlightedEntry] = React.useState(); const [highlightedConsoleMessage, setHighlightedConsoleMessage] = React.useState(); @@ -231,7 +233,7 @@ export const Workbench: React.FunctionComponent<{ id: 'attachments', title: 'Attachments', count: attachments.length, - render: () => + render: () => }; const tabs: TabbedPaneTabModel[] = [ @@ -296,7 +298,10 @@ export const Workbench: React.FunctionComponent<{ setSelectedTime={setSelectedTime} onSelected={onActionSelected} onHighlighted={setHighlightedAction} - revealAttachments={() => selectPropertiesTab('attachments')} + revealAttachment={attachment => { + selectPropertiesTab('attachments'); + setRevealedAttachment(attachment); + }} revealConsole={() => selectPropertiesTab('console')} isLive={isLive} /> diff --git a/tests/playwright-test/ui-mode-test-attachments.spec.ts b/tests/playwright-test/ui-mode-test-attachments.spec.ts index 0e3b91f87d..1a9e5b56c2 100644 --- a/tests/playwright-test/ui-mode-test-attachments.spec.ts +++ b/tests/playwright-test/ui-mode-test-attachments.spec.ts @@ -167,9 +167,9 @@ test('should link from attachment step to attachments view', async ({ runUITest const panel = page.getByRole('tabpanel', { name: 'Attachments' }); const attachment = panel.getByLabel('my-attachment'); - await page.getByText('attach "spacer-1"').click(); + await page.getByRole('treeitem', { name: 'attach "spacer-1"' }).getByLabel('Open Attachment').click(); await expect(attachment).not.toBeInViewport(); - await page.getByText('attach "my-attachment"').click(); + await page.getByRole('treeitem', { name: 'attach "my-attachment"' }).getByLabel('Open Attachment').click(); await expect(attachment).toBeInViewport(); });