fix(trace viewer): make red dot to the center of the target element (#26825)

Also make sure red dot is visible in the popout tab.

Fixes #24532.
This commit is contained in:
Dmitry Gozman 2023-08-31 16:52:54 -07:00 committed by GitHub
parent fa286de0b3
commit f3c02a5b4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 30 deletions

View File

@ -197,6 +197,7 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
function applyPlaywrightAttributes(unwrapPopoutUrl: (url: string) => string, ...targetIds: (string | undefined)[]) {
const scrollTops: Element[] = [];
const scrollLefts: Element[] = [];
const targetElements: Element[] = [];
const visit = (root: Document | ShadowRoot) => {
// Collect all scrolled elements for later use.
@ -223,6 +224,7 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
const style = (target as HTMLElement).style;
style.outline = '2px solid #006ab1';
style.backgroundColor = '#6fa8dc7f';
targetElements.push(target);
}
}
@ -231,10 +233,8 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
if (!src) {
iframe.setAttribute('src', 'data:text/html,<body style="background: #ddd"></body>');
} else {
// Append query parameters to inherit ?name= or ?time= values from parent.
// Retain query parameters to inherit name=, time=, showPoint= and other values from parent.
const url = new URL(unwrapPopoutUrl(window.location.href));
url.searchParams.delete('pointX');
url.searchParams.delete('pointY');
// We can be loading iframe from within iframe, reset base to be absolute.
const index = url.pathname.lastIndexOf('/snapshot/');
if (index !== -1)
@ -284,23 +284,25 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
element.removeAttribute('__playwright_scroll_left_');
}
const search = new URL(window.location.href).searchParams;
const pointX = search.get('pointX');
const pointY = search.get('pointY');
if (pointX) {
const pointElement = document.createElement('x-pw-pointer');
pointElement.style.position = 'fixed';
pointElement.style.backgroundColor = '#f44336';
pointElement.style.width = '20px';
pointElement.style.height = '20px';
pointElement.style.borderRadius = '10px';
pointElement.style.margin = '-10px 0 0 -10px';
pointElement.style.zIndex = '2147483647';
pointElement.style.left = pointX + 'px';
pointElement.style.top = pointY + 'px';
document.documentElement.appendChild(pointElement);
}
document.styleSheets[0].disabled = true;
const search = new URL(window.location.href).searchParams;
if (search.get('showPoint')) {
for (const target of targetElements) {
const pointElement = document.createElement('x-pw-pointer');
pointElement.style.position = 'fixed';
pointElement.style.backgroundColor = '#f44336';
pointElement.style.width = '20px';
pointElement.style.height = '20px';
pointElement.style.borderRadius = '10px';
pointElement.style.margin = '-10px 0 0 -10px';
pointElement.style.zIndex = '2147483647';
const box = target.getBoundingClientRect();
pointElement.style.left = (box.left + box.width / 2) + 'px';
pointElement.style.top = (box.top + box.height / 2) + 'px';
document.documentElement.appendChild(pointElement);
}
}
};
const onDOMContentLoaded = () => visit(document);

View File

@ -42,23 +42,24 @@ export const SnapshotTab: React.FunctionComponent<{
const [measure, ref] = useMeasure<HTMLDivElement>();
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
type Snapshot = { action: ActionTraceEvent, snapshotName: string, showPoint?: boolean };
const { snapshots } = React.useMemo(() => {
if (!action)
return { snapshots: {} };
// if the action has no beforeSnapshot, use the last available afterSnapshot.
let beforeSnapshot = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined;
let beforeSnapshot: Snapshot | undefined = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined;
let a = action;
while (!beforeSnapshot && a) {
a = prevInList(a);
beforeSnapshot = a?.afterSnapshot ? { action: a, snapshotName: a?.afterSnapshot } : undefined;
}
const afterSnapshot = action.afterSnapshot ? { action, snapshotName: action.afterSnapshot } : beforeSnapshot;
const actionSnapshot = action.inputSnapshot ? { action, snapshotName: action.inputSnapshot } : afterSnapshot;
const afterSnapshot: Snapshot | undefined = action.afterSnapshot ? { action, snapshotName: action.afterSnapshot } : beforeSnapshot;
const actionSnapshot: Snapshot | undefined = action.inputSnapshot ? { action, snapshotName: action.inputSnapshot, showPoint: !!action.point } : afterSnapshot;
return { snapshots: { action: actionSnapshot, before: beforeSnapshot, after: afterSnapshot } };
}, [action]);
const { snapshotInfoUrl, snapshotUrl, pointX, pointY, popoutUrl } = React.useMemo(() => {
const { snapshotInfoUrl, snapshotUrl, popoutUrl } = React.useMemo(() => {
const snapshot = snapshots[snapshotTab];
if (!snapshot)
return { snapshotUrl: kBlankSnapshotUrl };
@ -66,16 +67,18 @@ export const SnapshotTab: React.FunctionComponent<{
const params = new URLSearchParams();
params.set('trace', context(snapshot.action).traceUrl);
params.set('name', snapshot.snapshotName);
if (snapshot.showPoint)
params.set('showPoint', '1');
const snapshotUrl = new URL(`snapshot/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString();
const snapshotInfoUrl = new URL(`snapshotInfo/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString();
const pointX = snapshotTab === 'action' ? snapshot.action.point?.x : undefined;
const pointY = snapshotTab === 'action' ? snapshot.action.point?.y : undefined;
const popoutParams = new URLSearchParams();
popoutParams.set('r', snapshotUrl);
popoutParams.set('trace', context(snapshot.action).traceUrl);
if (snapshot.showPoint)
popoutParams.set('showPoint', '1');
const popoutUrl = new URL(`snapshot.html?${popoutParams.toString()}`, window.location.href).toString();
return { snapshots, snapshotInfoUrl, snapshotUrl, pointX, pointY, popoutUrl };
return { snapshots, snapshotInfoUrl, snapshotUrl, popoutUrl };
}, [snapshots, snapshotTab]);
const iframeRef0 = React.useRef<HTMLIFrameElement>(null);
@ -111,12 +114,11 @@ export const SnapshotTab: React.FunctionComponent<{
iframe.addEventListener('load', loadedCallback);
iframe.addEventListener('error', loadedCallback);
const newUrl = snapshotUrl + (pointX === undefined ? '' : `&pointX=${pointX}&pointY=${pointY}`);
// Try preventing history entry from being created.
if (iframe.contentWindow)
iframe.contentWindow.location.replace(newUrl);
iframe.contentWindow.location.replace(snapshotUrl);
else
iframe.src = newUrl;
iframe.src = snapshotUrl;
await loadedPromise;
} catch {
@ -132,7 +134,7 @@ export const SnapshotTab: React.FunctionComponent<{
loadingRef.current.visibleIframe = newVisibleIframe;
setSnapshotInfo(newSnapshotInfo);
})();
}, [snapshotUrl, snapshotInfoUrl, pointX, pointY]);
}, [snapshotUrl, snapshotInfoUrl]);
const windowHeaderHeight = 40;
const snapshotContainerSize = {