chore(trace-viewer): preserve column widths after showing resource details

This commit is contained in:
Yury Semikhatsky 2024-05-29 19:02:38 -07:00
parent 6e9c31f93b
commit 684159728f
3 changed files with 56 additions and 15 deletions

View file

@ -76,6 +76,10 @@ export const NetworkTab: React.FunctionComponent<{
return { renderedEntries }; return { renderedEntries };
}, [networkModel.resources, networkModel.contextIdMap, sorting, boundaries]); }, [networkModel.resources, networkModel.contextIdMap, sorting, boundaries]);
const [widths, setWidths] = React.useState<Map<ColumnName, number>>(() => {
return new Map(allColumns().map(column => [column, columnWidth(column)]));
});
if (!networkModel.resources.length) if (!networkModel.resources.length)
return <PlaceholderPanel text='No network calls' />; return <PlaceholderPanel text='No network calls' />;
@ -87,7 +91,8 @@ export const NetworkTab: React.FunctionComponent<{
onHighlighted={item => onEntryHovered(item?.resource)} onHighlighted={item => onEntryHovered(item?.resource)}
columns={visibleColumns(!!selectedEntry, renderedEntries)} columns={visibleColumns(!!selectedEntry, renderedEntries)}
columnTitle={columnTitle} columnTitle={columnTitle}
columnWidth={columnWidth} columnWidths={widths}
setColumnWidths={setWidths}
isError={item => item.status.code >= 400} isError={item => item.status.code >= 400}
isInfo={item => !!item.route} isInfo={item => !!item.route}
render={(item, column) => renderCell(item, column)} render={(item, column) => renderCell(item, column)}
@ -96,7 +101,7 @@ export const NetworkTab: React.FunctionComponent<{
/>; />;
return <> return <>
{!selectedEntry && grid} {!selectedEntry && grid}
{selectedEntry && <SplitView sidebarSize={200} sidebarIsFirst={true} orientation='horizontal'> {selectedEntry && <SplitView sidebarSize={widths.get('name')!} sidebarIsFirst={true} orientation='horizontal' settingName='networkResourceDetails'>
<NetworkResourceDetails resource={selectedEntry.resource} onClose={() => setSelectedEntry(undefined)} /> <NetworkResourceDetails resource={selectedEntry.resource} onClose={() => setSelectedEntry(undefined)} />
{grid} {grid}
</SplitView>} </SplitView>}
@ -142,13 +147,16 @@ const columnWidth = (column: ColumnName) => {
function visibleColumns(entrySelected: boolean, renderedEntries: RenderedEntry[]): (keyof RenderedEntry)[] { function visibleColumns(entrySelected: boolean, renderedEntries: RenderedEntry[]): (keyof RenderedEntry)[] {
if (entrySelected) if (entrySelected)
return ['name']; return ['name'];
const columns: (keyof RenderedEntry)[] = []; let columns: (keyof RenderedEntry)[] = allColumns();
if (hasMultipleContexts(renderedEntries)) if (!hasMultipleContexts(renderedEntries))
columns.push('contextId'); columns = columns.filter(name => name !== 'contextId');
columns.push('name', 'method', 'status', 'contentType', 'duration', 'size', 'start', 'route');
return columns; return columns;
} }
function allColumns(): (keyof RenderedEntry)[] {
return ['contextId', 'name', 'method', 'status', 'contentType', 'duration', 'size', 'start', 'route'];
}
const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell => { const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell => {
if (column === 'contextId') { if (column === 'contextId') {
return { return {

View file

@ -30,19 +30,33 @@ export type RenderedGridCell = {
export type GridViewProps<T> = Omit<ListViewProps<T>, 'render'> & { export type GridViewProps<T> = Omit<ListViewProps<T>, 'render'> & {
columns: (keyof T)[], columns: (keyof T)[],
columnTitle: (column: keyof T) => string, columnTitle: (column: keyof T) => string,
columnWidth: (column: keyof T) => number, columnWidths: Map<keyof T, number>,
setColumnWidths: (widths: Map<keyof T, number>) => void,
render: (item: T, column: keyof T, index: number) => RenderedGridCell, render: (item: T, column: keyof T, index: number) => RenderedGridCell,
sorting?: Sorting<T>, sorting?: Sorting<T>,
setSorting?: (sorting: Sorting<T> | undefined) => void, setSorting?: (sorting: Sorting<T> | undefined) => void,
}; };
export function GridView<T>(model: GridViewProps<T>) { export function GridView<T>(model: GridViewProps<T>) {
const initialOffsets: number[] = []; function offsets() {
for (let i = 0; i < model.columns.length - 1; ++i) { const offsets: number[] = [];
const column = model.columns[i]; for (let i = 0; i < model.columns.length - 1; ++i) {
initialOffsets[i] = (initialOffsets[i - 1] || 0) + model.columnWidth(column); const column = model.columns[i];
offsets[i] = (offsets[i - 1] || 0) + model.columnWidths.get(column)!;
}
console.log('offsets', offsets, model.columns);
return offsets;
}
function setOffsets(offsets: number[]) {
const widths = new Map<keyof T, number>(model.columnWidths.entries());
for (let i = 0; i < offsets.length; ++i) {
const width = offsets[i] - (offsets[i - 1] || 0);
const column = model.columns[i];
widths.set(column, width);
}
console.log('setOffsets', offsets);
model.setColumnWidths(widths);
} }
const [offsets, setOffsets] = React.useState<number[]>(initialOffsets);
const toggleSorting = React.useCallback((f: keyof T) => { const toggleSorting = React.useCallback((f: keyof T) => {
model.setSorting?.({ by: f, negate: model.sorting?.by === f ? !model.sorting.negate : false }); model.setSorting?.({ by: f, negate: model.sorting?.by === f ? !model.sorting.negate : false });
@ -51,7 +65,7 @@ export function GridView<T>(model: GridViewProps<T>) {
return <div className={`grid-view ${model.name}-grid-view`}> return <div className={`grid-view ${model.name}-grid-view`}>
<ResizeView <ResizeView
orientation={'horizontal'} orientation={'horizontal'}
offsets={offsets} offsets={offsets()}
setOffsets={setOffsets} setOffsets={setOffsets}
resizerColor='var(--vscode-panel-border)' resizerColor='var(--vscode-panel-border)'
resizerWidth={1} resizerWidth={1}
@ -63,7 +77,7 @@ export function GridView<T>(model: GridViewProps<T>) {
return <div return <div
className={'grid-view-header-cell ' + sortingHeader(column, model.sorting)} className={'grid-view-header-cell ' + sortingHeader(column, model.sorting)}
style={{ style={{
width: offsets[i] - (offsets[i - 1] || 0), width: i < model.columns.length - 1 ? model.columnWidths.get(column) : undefined,
}} }}
onClick={() => model.setSorting && toggleSorting(column)} onClick={() => model.setSorting && toggleSorting(column)}
> >
@ -84,7 +98,9 @@ export function GridView<T>(model: GridViewProps<T>) {
return <div return <div
className={`grid-view-cell grid-view-column-${String(column)}`} className={`grid-view-cell grid-view-column-${String(column)}`}
title={title} title={title}
style={{ width: offsets[i] - (offsets[i - 1] || 0) }}> style={{
width: i < model.columns.length - 1 ? model.columnWidths.get(column) : undefined,
}}>
{body} {body}
</div>; </div>;
})} })}

View file

@ -19,6 +19,7 @@ import { kTargetClosedErrorMessage } from '../config/errors';
import { browserTest as it, expect } from '../config/browserTest'; import { browserTest as it, expect } from '../config/browserTest';
import { attachFrame, verifyViewport } from '../config/utils'; import { attachFrame, verifyViewport } from '../config/utils';
import type { Page } from '@playwright/test'; import type { Page } from '@playwright/test';
import { request } from '@playwright/test';
it('should create new context @smoke', async function({ browser }) { it('should create new context @smoke', async function({ browser }) {
expect(browser.contexts().length).toBe(0); expect(browser.contexts().length).toBe(0);
@ -31,6 +32,22 @@ it('should create new context @smoke', async function({ browser }) {
expect(browser).toBe(context.browser()); expect(browser).toBe(context.browser());
}); });
it('log two contexts', async function({ browser }) {
const context1 = await browser.newContext();
const context2 = await browser.newContext();
const api1 = await request.newContext();
const api2 = await request.newContext();
const page1 = await context1.newPage();
const page2 = await context2.newPage();
await page1.evaluate(() => { window.open('about:blank'); });
const promises = [];
promises.push(page1.goto('https://playwright.dev/'));
promises.push(page2.goto('https://theverge.com/'));
promises.push(api1.get('https://playwright.dev/'));
promises.push(api2.get('https://theverge.com/'));
await Promise.all(promises);
});
it('should be able to click across browser contexts', async function({ browser }) { it('should be able to click across browser contexts', async function({ browser }) {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29096' }); it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29096' });
expect(browser.contexts().length).toBe(0); expect(browser.contexts().length).toBe(0);