From 020a39860dcc1522fdedf0a4ab4851ed6f89d282 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 1 Feb 2024 13:44:26 -0800 Subject: [PATCH] chore: polish network panel highlight (#29299) Fixes https://github.com/microsoft/playwright/issues/29287 --- packages/trace-viewer/src/ui/networkTab.css | 91 ++------------------- packages/trace-viewer/src/ui/networkTab.tsx | 57 ++++++++----- packages/web/src/components/gridView.css | 4 + packages/web/src/components/gridView.tsx | 16 +++- packages/web/src/components/listView.css | 6 ++ packages/web/src/components/listView.tsx | 5 +- packages/web/src/shared/resizeView.tsx | 2 +- tests/assets/frames/frame.html | 3 + tests/library/trace-viewer.spec.ts | 2 + 9 files changed, 77 insertions(+), 109 deletions(-) diff --git a/packages/trace-viewer/src/ui/networkTab.css b/packages/trace-viewer/src/ui/networkTab.css index 99c4d45d81..130e2d2d25 100644 --- a/packages/trace-viewer/src/ui/networkTab.css +++ b/packages/trace-viewer/src/ui/networkTab.css @@ -14,96 +14,23 @@ limitations under the License. */ -.network-request-status .status-failure { - color: var(--vscode-statusBar-foreground); - background-color: var(--vscode-statusBarItem-errorBackground); -} - -.network-request-status .status-route { +.network-request-status-route { color: var(--vscode-statusBar-foreground); background-color: var(--vscode-statusBar-background); } -.network-request-status .status-route.api { +.network-request-status-route.api { color: var(--vscode-statusBar-foreground); background-color: var(--vscode-statusBarItem-remoteBackground); } -.network-request-column { - overflow: hidden; - text-overflow: ellipsis; - flex: 0.5; - padding: 0 5px; - display: flex; +.network-grid-view .grid-view-column-method, +.network-grid-view .grid-view-column-status { + text-align: center; } -.network-request-start { - flex: 0 0 65px; - justify-content: right; - padding-right: 10px; -} - -.network-request-status { - flex: 0 0 75px; -} - -.network-request-method { - flex: 0 0 65px; -} - -.network-request-file { - display: flex; - flex: 1; -} - -.network-request-file-url { - overflow: hidden; - text-overflow: ellipsis; - flex: 1; -} - -.network-request-body .network-request-start, -.network-request-body .network-request-status, -.network-request-body .network-request-duration, -.network-request-body .network-request-size { - justify-content: end; -} - -.network-request-header { - margin: 3px 14px 0 5px; - height: 30px; - border-bottom: 1px solid var(--vscode-panel-border); - flex: none; - align-items: center; - cursor: pointer; - user-select: none; -} - -.network-request-header .codicon-triangle-up { - display: none; -} -.network-request-header .codicon-triangle-down { - display: none; -} - -.network-request-header > div { - display: flex; - align-items: center; - white-space: nowrap; - height: 100%; - overflow: hidden; - padding: 0 5px; - justify-content: space-between; -} - -.network-request-header > .filter-positive .codicon-triangle-down { - display: initial !important; -} - -.network-request-header > .filter-negative .codicon-triangle-up { - display: initial !important; -} - -.network-request-header .network-request-column { - border-right: 1px solid var(--vscode-panel-border); +.network-grid-view .grid-view-column-duration, +.network-grid-view .grid-view-column-size, +.network-grid-view .grid-view-column-start { + text-align: end; } diff --git a/packages/trace-viewer/src/ui/networkTab.tsx b/packages/trace-viewer/src/ui/networkTab.tsx index c2b27fb023..b3caa501b5 100644 --- a/packages/trace-viewer/src/ui/networkTab.tsx +++ b/packages/trace-viewer/src/ui/networkTab.tsx @@ -22,7 +22,7 @@ import { NetworkResourceDetails } from './networkResourceDetails'; import { bytesToString, msToString } from '@web/uiUtils'; import { PlaceholderPanel } from './placeholderPanel'; import type { MultiTraceModel } from './modelUtil'; -import { GridView } from '@web/components/gridView'; +import { GridView, type RenderedGridCell } from '@web/components/gridView'; import { SplitView } from '@web/components/splitView'; type NetworkTabModel = { @@ -32,7 +32,7 @@ type NetworkTabModel = { type RenderedEntry = { name: { name: string, url: string }, method: string, - status: { code: number, text: string, className: string }, + status: { code: number, text: string }, contentType: string, duration: number, size: number, @@ -83,7 +83,9 @@ export const NetworkTab: React.FunctionComponent<{ onHighlighted={item => onEntryHovered(item?.resource)} columns={selectedEntry ? ['name'] : ['name', 'method', 'status', 'contentType', 'duration', 'size', 'start', 'route']} columnTitle={columnTitle} - columnWidth={column => column === 'name' ? 200 : 100} + columnWidth={columnWidth} + isError={item => item.status.code >= 400} + isInfo={item => !!item.route} render={(item, column) => renderCell(item, column)} sorting={sorting} setSorting={setSorting} @@ -117,23 +119,44 @@ const columnTitle = (column: ColumnName) => { return ''; }; -const renderCell = (entry: RenderedEntry, column: ColumnName) => { +const columnWidth = (column: ColumnName) => { if (column === 'name') - return {entry.name.name}; + return 200; if (column === 'method') - return {entry.method}; + return 60; if (column === 'status') - return {entry.status.code > 0 ? entry.status.code : ''}; + return 60; if (column === 'contentType') - return {entry.contentType}; + return 200; + return 100; +}; + +const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell => { + if (column === 'name') { + return { + body: entry.name.name, + title: entry.name.url, + }; + } + if (column === 'method') + return { body: entry.method }; + if (column === 'status') { + return { + body: entry.status.code > 0 ? entry.status.code : '', + title: entry.status.text + }; + } + if (column === 'contentType') + return { body: entry.contentType }; if (column === 'duration') - return {msToString(entry.duration)}; + return { body: msToString(entry.duration) }; if (column === 'size') - return {bytesToString(entry.size)}; + return { body: bytesToString(entry.size) }; if (column === 'start') - return {msToString(entry.start)}; + return { body: msToString(entry.start) }; if (column === 'route') - return entry.route && {entry.route}; + return { body: entry.route }; + return { body: '' }; }; const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => { @@ -155,7 +178,7 @@ const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => return { name: { name: resourceName, url: resource.request.url }, method: resource.request.method, - status: { code: resource.response.status, text: resource.response.statusText, className: statusClassName(resource.response.status) }, + status: { code: resource.response.status, text: resource.response.statusText }, contentType: contentType, duration: resource.time, size: resource.response._transferSize! > 0 ? resource.response._transferSize! : resource.response.bodySize, @@ -165,14 +188,6 @@ const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => }; }; -function statusClassName(status: number): string { - if (status >= 200 && status < 400) - return 'status-success'; - if (status >= 400) - return 'status-failure'; - return ''; -} - function formatRouteStatus(request: Entry): string { if (request._wasAborted) return 'aborted'; diff --git a/packages/web/src/components/gridView.css b/packages/web/src/components/gridView.css index 6443ad4a38..65dcd56962 100644 --- a/packages/web/src/components/gridView.css +++ b/packages/web/src/components/gridView.css @@ -20,6 +20,10 @@ flex: auto; } +.grid-view .list-view-entry { + padding-left: 0; +} + .grid-view-cell { overflow: hidden; text-overflow: ellipsis; diff --git a/packages/web/src/components/gridView.tsx b/packages/web/src/components/gridView.tsx index 0e6e74ed5b..2a5257da2b 100644 --- a/packages/web/src/components/gridView.tsx +++ b/packages/web/src/components/gridView.tsx @@ -22,11 +22,16 @@ import { ResizeView } from '@web/shared/resizeView'; export type Sorting = { by: keyof T, negate: boolean }; +export type RenderedGridCell = { + body: React.ReactNode; + title?: string; +}; + export type GridViewProps = Omit, 'render'> & { columns: (keyof T)[], columnTitle: (column: keyof T) => string, columnWidth: (column: keyof T) => number, - render: (item: T, column: keyof T, index: number) => React.ReactNode, + render: (item: T, column: keyof T, index: number) => RenderedGridCell, sorting?: Sorting, setSorting?: (sorting: Sorting | undefined) => void, }; @@ -43,7 +48,7 @@ export function GridView(model: GridViewProps) { model.setSorting?.({ by: f, negate: model.sorting?.by === f ? !model.sorting.negate : false }); }, [model]); - return
+ return
(model: GridViewProps) { render={(item, index) => { return <> {model.columns.map((column, i) => { + const { body, title } = model.render(item, column, index); return
- {model.render(item, column, index)} + {body}
; })} ; @@ -87,6 +94,7 @@ export function GridView(model: GridViewProps) { indent={model.indent} isError={model.isError} isWarning={model.isWarning} + isInfo={model.isInfo} selectedItem={model.selectedItem} onAccepted={model.onAccepted} onSelected={model.onSelected} diff --git a/packages/web/src/components/listView.css b/packages/web/src/components/listView.css index e64e47dd80..cc8c769332 100644 --- a/packages/web/src/components/listView.css +++ b/packages/web/src/components/listView.css @@ -74,8 +74,14 @@ .list-view-entry.error { color: var(--vscode-list-errorForeground); + background-color: var(--vscode-inputValidation-errorBackground); } .list-view-entry.warning { color: var(--vscode-list-warningForeground); + background-color: var(--vscode-inputValidation-warningBackground); +} + +.list-view-entry.info { + background-color: var(--vscode-inputValidation-infoBackground); } diff --git a/packages/web/src/components/listView.tsx b/packages/web/src/components/listView.tsx index 5f9eb7bd0e..a9bd9f8fb3 100644 --- a/packages/web/src/components/listView.tsx +++ b/packages/web/src/components/listView.tsx @@ -26,6 +26,7 @@ export type ListViewProps = { indent?: (item: T, index: number) => number | undefined, isError?: (item: T, index: number) => boolean, isWarning?: (item: T, index: number) => boolean, + isInfo?: (item: T, index: number) => boolean, selectedItem?: T, onAccepted?: (item: T, index: number) => void, onSelected?: (item: T, index: number) => void, @@ -48,6 +49,7 @@ export function ListView({ icon, isError, isWarning, + isInfo, indent, selectedItem, onAccepted, @@ -136,12 +138,13 @@ export function ListView({ const highlightedSuffix = !noHighlightOnHover && highlightedItem === item ? ' highlighted' : ''; const errorSuffix = isError?.(item, index) ? ' error' : ''; const warningSuffix = isWarning?.(item, index) ? ' warning' : ''; + const infoSuffix = isInfo?.(item, index) ? ' info' : ''; const indentation = indent?.(item, index) || 0; const rendered = render(item, index); return
onSelected?.(item, index)} onMouseEnter={() => setHighlightedItem(item)} onMouseLeave={() => setHighlightedItem(undefined)} diff --git a/packages/web/src/shared/resizeView.tsx b/packages/web/src/shared/resizeView.tsx index b915ce95d4..e575866de0 100644 --- a/packages/web/src/shared/resizeView.tsx +++ b/packages/web/src/shared/resizeView.tsx @@ -57,7 +57,7 @@ export const ResizeView: React.FC<{ top: 0, right: 0, bottom: 0, - left: 0, + left: -(7 - resizerWidth) / 2, zIndex: 1000, pointerEvents: 'none', }} diff --git a/tests/assets/frames/frame.html b/tests/assets/frames/frame.html index 38360b0911..3d3b943431 100644 --- a/tests/assets/frames/frame.html +++ b/tests/assets/frames/frame.html @@ -1,6 +1,9 @@ +