fix(trace-viewer): trap focus inside drop-target popup (#29845)

https://github.com/microsoft/playwright/issues/29099
This commit is contained in:
Max Schmitt 2024-03-07 22:24:21 +01:00 committed by GitHub
parent 40b8bcb6ad
commit 9ca895dea0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 10 additions and 7 deletions

View File

@ -50,7 +50,8 @@ export const Workbench: React.FunctionComponent<{
onSelectionChanged?: (action: ActionTraceEventInContext) => void, onSelectionChanged?: (action: ActionTraceEventInContext) => void,
isLive?: boolean, isLive?: boolean,
status?: UITestStatus, status?: UITestStatus,
}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status }) => { inert?: boolean,
}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status, inert }) => {
const [selectedAction, setSelectedActionImpl] = React.useState<ActionTraceEventInContext | undefined>(undefined); const [selectedAction, setSelectedActionImpl] = React.useState<ActionTraceEventInContext | undefined>(undefined);
const [revealedStack, setRevealedStack] = React.useState<StackFrame[] | undefined>(undefined); const [revealedStack, setRevealedStack] = React.useState<StackFrame[] | undefined>(undefined);
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>(); const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>();
@ -213,7 +214,7 @@ export const Workbench: React.FunctionComponent<{
else if (model && model.wallTime) else if (model && model.wallTime)
time = Date.now() - model.wallTime; time = Date.now() - model.wallTime;
return <div className='vbox workbench'> return <div className='vbox workbench' {...(inert ? { inert: 'true' } : {})}>
<Timeline <Timeline
model={model} model={model}
boundaries={boundaries} boundaries={boundaries}

View File

@ -137,8 +137,10 @@ export const WorkbenchLoader: React.FunctionComponent<{
})(); })();
}, [isServer, traceURLs, uploadedTraceNames]); }, [isServer, traceURLs, uploadedTraceNames]);
const showFileUploadDropArea = !!(!isServer && !dragOver && !fileForLocalModeError && (!traceURLs.length || processingErrorMessage));
return <div className='vbox workbench-loader' onDragOver={event => { event.preventDefault(); setDragOver(true); }}> return <div className='vbox workbench-loader' onDragOver={event => { event.preventDefault(); setDragOver(true); }}>
<div className='hbox header'> <div className='hbox header' {...(showFileUploadDropArea ? { inert: 'true' } : {})}>
<div className='logo'> <div className='logo'>
<img src='playwright-logo.svg' alt='Playwright logo' /> <img src='playwright-logo.svg' alt='Playwright logo' />
</div> </div>
@ -150,7 +152,7 @@ export const WorkbenchLoader: React.FunctionComponent<{
<div className='progress'> <div className='progress'>
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div> <div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
</div> </div>
<Workbench model={model} /> <Workbench model={model} inert={showFileUploadDropArea} />
{fileForLocalModeError && <div className='drop-target'> {fileForLocalModeError && <div className='drop-target'>
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div> <div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
<div style={{ paddingTop: 20 }}> <div style={{ paddingTop: 20 }}>
@ -159,9 +161,9 @@ export const WorkbenchLoader: React.FunctionComponent<{
<div>3. Drop the trace from the download shelf into the page</div> <div>3. Drop the trace from the download shelf into the page</div>
</div> </div>
</div>} </div>}
{!isServer && !dragOver && !fileForLocalModeError && (!traceURLs.length || processingErrorMessage) && <div className='drop-target'> {showFileUploadDropArea && <div className='drop-target'>
<div className='processing-error' aria-live='assertive'>{processingErrorMessage}</div> <div className='processing-error' aria-live='assertive'>{processingErrorMessage}</div>
<div className='title' role='heading'>Drop Playwright Trace to load</div> <div className='title' role='heading' aria-level={1}>Drop Playwright Trace to load</div>
<div>or</div> <div>or</div>
<button onClick={() => { <button onClick={() => {
const input = document.createElement('input'); const input = document.createElement('input');
@ -169,7 +171,7 @@ export const WorkbenchLoader: React.FunctionComponent<{
input.multiple = true; input.multiple = true;
input.click(); input.click();
input.addEventListener('change', e => handleFileInputChange(e)); input.addEventListener('change', e => handleFileInputChange(e));
}}>Select file(s)</button> }} type='button'>Select file(s)</button>
<div style={{ maxWidth: 400 }}>Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere, <div style={{ maxWidth: 400 }}>Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere,
it opens it locally.</div> it opens it locally.</div>
</div>} </div>}