feat(inspector): collapse completed items (#5484)

This commit is contained in:
Pavel Feldman 2021-02-17 17:28:02 -08:00 committed by GitHub
parent dc51536bca
commit 3248c2449c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 55 additions and 67 deletions

View file

@ -35,6 +35,7 @@ export type CallLog = {
messages: string[];
status: 'in-progress' | 'done' | 'error' | 'paused';
error?: string;
reveal?: boolean;
};
export type SourceHighlight = {

View file

@ -81,10 +81,6 @@ body {
display: none !important;
}
.codicon {
color: var(--toolbar-color);
}
svg {
fill: currentColor;
}

View file

@ -25,9 +25,9 @@ export default {
} as Meta;
const Template: Story<ToolbarProps> = () => <Toolbar>
<ToolbarButton icon="clone" title="Copy" onClick={() => {}}></ToolbarButton>
<ToolbarButton icon="trashcan" title="Erase" onClick={() => {}}></ToolbarButton>
<ToolbarButton icon="close" title="Close" onClick={() => {}}></ToolbarButton>
<ToolbarButton icon='record' title='Record' onClick={() => {}}>Record</ToolbarButton>
<ToolbarButton icon='question' title='Inspect' onClick={() => {}}>Explore</ToolbarButton>
<ToolbarButton icon='files' title='Copy' onClick={() => {}}></ToolbarButton>
</Toolbar>;
export const Primary = Template.bind({});

View file

@ -22,6 +22,8 @@
padding: 0;
margin-left: 10px;
cursor: pointer;
display: flex;
align-items: center;
}
.toolbar-button:disabled {
@ -29,24 +31,10 @@
cursor: default;
}
.toolbar-button:not(.disabled):hover {
.toolbar-button:not(.disabled):not(.toggled):hover {
color: #555;
}
.toolbar-button.toggled {
color: #1ea7fd;
}
.toolbar-button.codicon-record.toggled {
color: #fd1e1e;
}
.toolbar-button.codicon-debug-continue,
.toolbar-button.codicon-debug-step-over {
color: #01bb01;
}
.toolbar-button.codicon-debug-continue:hover,
.toolbar-button.codicon-debug-step-over:hover {
color: #41ca1e;
.toolbar-button .codicon {
margin-right: 4px;
}

View file

@ -27,14 +27,15 @@ export interface ToolbarButtonProps {
}
export const ToolbarButton: React.FC<ToolbarButtonProps> = ({
children,
title = '',
icon = '',
disabled = false,
toggled = false,
onClick = () => {},
}) => {
let className = `toolbar-button codicon codicon-${icon}`;
let className = `toolbar-button ${icon}`;
if (toggled)
className += ' toggled';
return <button className={className} onClick={onClick} title={title} disabled={!!disabled}></button>;
return <button className={className} onClick={onClick} title={title} disabled={!!disabled}><span className={`codicon codicon-${icon}`}></span>{ children }</button>;
};

View file

@ -50,7 +50,7 @@ export function exampleCallLog(): CallLog[] {
'status': 'paused'
},
{
'id': 5,
'id': 6,
'messages': [
'navigating to "https://github.com/microsoft", waiting until "load"',
],

View file

@ -23,27 +23,37 @@ export interface CallLogProps {
}
export const CallLogView: React.FC<CallLogProps> = ({
log
log,
}) => {
const messagesEndRef = React.createRef<HTMLDivElement>();
const [expandOverrides, setExpandOverrides] = React.useState<Map<number, boolean>>(new Map());
React.useLayoutEffect(() => {
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
if (log.find(callLog => callLog.reveal))
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
}, [messagesEndRef]);
return <div className='vbox'>
<div className='call-log-header' style={{flex: 'none'}}>Log</div>
<div className='call-log' style={{flex: 'auto'}}>
{log.map(callLog => {
const expandOverride = expandOverrides.get(callLog.id);
const isExpanded = typeof expandOverride === 'boolean' ? expandOverride : callLog.status !== 'done';
return <div className={`call-log-call ${callLog.status}`} key={callLog.id}>
<div className='call-log-call-header'>
<span className={'codicon ' + iconClass(callLog)}></span>{ callLog.title }
<span className={`codicon codicon-chevron-${isExpanded ? 'down' : 'right'}`} style={{ cursor: 'pointer' }}onClick={() => {
const newOverrides = new Map(expandOverrides);
newOverrides.set(callLog.id, !isExpanded);
setExpandOverrides(newOverrides);
}}></span>
{ callLog.title }
<span className={'codicon ' + iconClass(callLog)}></span>
</div>
{ callLog.messages.map((message, i) => {
{ (isExpanded ? callLog.messages : []).map((message, i) => {
return <div className='call-log-message' key={i}>
{ message.trim() }
</div>;
})}
{ callLog.error ? <div className='call-log-message error'>
{ callLog.error ? <div className='call-log-message error' hidden={!isExpanded}>
{ callLog.error }
</div> : undefined }
</div>

View file

@ -42,8 +42,10 @@ export const Main: React.FC = ({
window.playwrightSetPaused = setPaused;
window.playwrightUpdateLogs = callLogs => {
const newLog = new Map<number, CallLog>(log);
for (const callLog of callLogs)
for (const callLog of callLogs) {
callLog.reveal = !log.has(callLog.id);
newLog.set(callLog.id, callLog);
}
setLog(newLog);
};

View file

@ -37,3 +37,21 @@
color: var(--toolbar-color);
margin-left: 16px;
}
.recorder .toolbar-button.toggled.question {
color: #12a3ff;
}
.recorder .toolbar-button.toggled.record {
color: #fd1e1e;
}
.recorder .toolbar-button:not([disabled]) .codicon-debug-continue,
.recorder .toolbar-button:not([disabled]) .codicon-debug-step-over {
color: #01bb01;
}
.recorder .toolbar-button:not([disabled]):hover .codicon-debug-continue,
.recorder .toolbar-button:not([disabled]):hover .codicon-debug-step-over {
color: #41ca1e;
}

View file

@ -57,15 +57,14 @@ export const Recorder: React.FC<RecorderProps> = ({
React.useLayoutEffect(() => {
messagesEndRef.current?.scrollIntoView({ block: 'center', inline: 'nearest' });
}, [messagesEndRef]);
return <div className='recorder'>
<Toolbar>
<ToolbarButton icon='record' title='Record' toggled={mode == 'recording'} onClick={() => {
window.dispatch({ event: 'setMode', params: { mode: mode === 'recording' ? 'none' : 'recording' }}).catch(() => { });
}}></ToolbarButton>
<ToolbarButton icon='question' title='Inspect' toggled={mode == 'inspecting'} onClick={() => {
}}>Record</ToolbarButton>
<ToolbarButton icon='question' title='Explore' toggled={mode == 'inspecting'} onClick={() => {
window.dispatch({ event: 'setMode', params: { mode: mode === 'inspecting' ? 'none' : 'inspecting' }}).catch(() => { });
}}></ToolbarButton>
}}>Explore</ToolbarButton>
<ToolbarButton icon='files' title='Copy' disabled={!source || !source.text} onClick={() => {
copy(source.text);
}}></ToolbarButton>
@ -78,12 +77,12 @@ export const Recorder: React.FC<RecorderProps> = ({
<ToolbarButton icon='debug-step-over' title='Step over' disabled={!paused} onClick={() => {
window.dispatch({ event: 'step' }).catch(() => {});
}}></ToolbarButton>
<select className='recorder-chooser' hidden={!sources.length} onChange={event => {
<select className='recorder-chooser' hidden={!sources.length} value={file} onChange={event => {
setFile(event.target.selectedOptions[0].value);
}}>{
sources.map(s => {
const title = s.file.replace(/.*[/\\]([^/\\]+)/, '$1');
return <option key={s.file} value={s.file} selected={s.file === file}>{title}</option>;
return <option key={s.file} value={s.file}>{title}</option>;
})
}
</select>

View file

@ -148,19 +148,6 @@ describe('pause', (suite, { mode }) => {
expect(await sanitizeLog(recorderPage)).toEqual([
'pause',
'click',
'waiting for selector "button"',
'selector resolved to visible <button>Submit</button>',
'attempting click action',
'waiting for element to be visible, enabled and stable',
'element is visible, enabled and stable',
'scrolling into view if needed',
'done scrolling',
'checking that element receives pointer events at ()',
'element does receive pointer events',
'performing click action',
'click action done',
'waiting for scheduled navigations to finish',
'navigations have finished',
'pause',
]);
await recorderPage.click('[title="Resume"]');
@ -183,21 +170,7 @@ describe('pause', (suite, { mode }) => {
expect(await sanitizeLog(recorderPage)).toEqual([
'pause',
'waitForEvent()',
'waiting for event \"console\"',
'click',
'waiting for selector "button"',
'selector resolved to visible <button onclick=\"console.log()\">Submit</button>',
'attempting click action',
'waiting for element to be visible, enabled and stable',
'element is visible, enabled and stable',
'scrolling into view if needed',
'done scrolling',
'checking that element receives pointer events at ()',
'element does receive pointer events',
'performing click action',
'click action done',
'waiting for scheduled navigations to finish',
'navigations have finished',
'pause',
]);
await recorderPage.click('[title="Resume"]');