feat(trace-viewer): Move copy request buttons to toolbar (#35366)
This commit is contained in:
parent
3d603d1e5c
commit
aa278d3aed
|
@ -49,12 +49,6 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.network-request-details-copy {
|
||||
display: flex;
|
||||
margin: 8px 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.network-font-preview {
|
||||
font-family: font-preview;
|
||||
font-size: 30px;
|
||||
|
@ -85,6 +79,31 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.copy-request-dropdown {
|
||||
.copy-request-dropdown-toggle {
|
||||
margin-right: 14px;
|
||||
width: 135px;
|
||||
}
|
||||
|
||||
&:not(:hover) .copy-request-dropdown-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.copy-request-dropdown-menu {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 135px;
|
||||
z-index: 10;
|
||||
background-color: var(--vscode-list-dropBackground);
|
||||
|
||||
button {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.green-circle::before,
|
||||
.red-circle::before,
|
||||
.yellow-circle::before {
|
||||
|
|
|
@ -24,7 +24,11 @@ import { generateCurlCommand, generateFetchCall } from '../third_party/devtools'
|
|||
import { CopyToClipboardTextButton } from './copyToClipboard';
|
||||
import { getAPIRequestCodeGen } from './codegen';
|
||||
import type { Language } from '@isomorphic/locatorGenerators';
|
||||
import { msToString } from '@web/uiUtils';
|
||||
import { msToString, useAsyncMemo } from '@web/uiUtils';
|
||||
import type { Entry } from '@trace/har';
|
||||
|
||||
type RequestBody = { text: string, mimeType?: string } | null;
|
||||
|
||||
|
||||
export const NetworkResourceDetails: React.FunctionComponent<{
|
||||
resource: ResourceSnapshot;
|
||||
|
@ -34,14 +38,30 @@ export const NetworkResourceDetails: React.FunctionComponent<{
|
|||
}> = ({ resource, sdkLanguage, startTimeOffset, onClose }) => {
|
||||
const [selectedTab, setSelectedTab] = React.useState('request');
|
||||
|
||||
const requestBody = useAsyncMemo<RequestBody>(async () => {
|
||||
if (resource.request.postData) {
|
||||
const requestContentTypeHeader = resource.request.headers.find(q => q.name.toLowerCase() === 'content-type');
|
||||
const requestContentType = requestContentTypeHeader ? requestContentTypeHeader.value : '';
|
||||
if (resource.request.postData._sha1) {
|
||||
const response = await fetch(`sha1/${resource.request.postData._sha1}`);
|
||||
return { text: formatBody(await response.text(), requestContentType), mimeType: requestContentType };
|
||||
} else {
|
||||
return { text: formatBody(resource.request.postData.text, requestContentType), mimeType: requestContentType };
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, [resource], null);
|
||||
|
||||
return <TabbedPane
|
||||
dataTestId='network-request-details'
|
||||
leftToolbar={[<ToolbarButton key='close' icon='close' title='Close' onClick={onClose}></ToolbarButton>]}
|
||||
leftToolbar={[<ToolbarButton key='close' icon='close' title='Close' onClick={onClose} />]}
|
||||
rightToolbar={[<CopyDropdown key='dropdown' requestBody={requestBody} resource={resource} sdkLanguage={sdkLanguage} />]}
|
||||
tabs={[
|
||||
{
|
||||
id: 'request',
|
||||
title: 'Request',
|
||||
render: () => <RequestTab resource={resource} sdkLanguage={sdkLanguage} startTimeOffset={startTimeOffset} />,
|
||||
render: () => <RequestTab resource={resource} startTimeOffset={startTimeOffset} requestBody={requestBody} />,
|
||||
},
|
||||
{
|
||||
id: 'response',
|
||||
|
@ -58,31 +78,36 @@ export const NetworkResourceDetails: React.FunctionComponent<{
|
|||
setSelectedTab={setSelectedTab} />;
|
||||
};
|
||||
|
||||
|
||||
const CopyDropdown: React.FC<{
|
||||
resource: Entry,
|
||||
sdkLanguage: Language,
|
||||
requestBody: RequestBody,
|
||||
}> = ({ resource, sdkLanguage, requestBody }) => {
|
||||
const copiedDescription = <><span className='codicon codicon-check' style={{ marginRight: '5px' }} /> Copied </>;
|
||||
const copyAsPlaywright = async () => getAPIRequestCodeGen(sdkLanguage).generatePlaywrightRequestCall(resource.request, requestBody?.text);
|
||||
return (
|
||||
<div className='copy-request-dropdown'>
|
||||
<ToolbarButton className='copy-request-dropdown-toggle'>
|
||||
<span className='codicon codicon-copy' style={{ marginRight: '5px' }}/>
|
||||
Copy request
|
||||
<span className='codicon codicon-chevron-down' style={{ marginLeft: '5px' }}/>
|
||||
</ToolbarButton>
|
||||
|
||||
<div className='copy-request-dropdown-menu'>
|
||||
<CopyToClipboardTextButton description='Copy as cURL' copiedDescription={copiedDescription} value={() => generateCurlCommand(resource)}/>
|
||||
<CopyToClipboardTextButton description='Copy as Fetch' copiedDescription={copiedDescription} value={() => generateFetchCall(resource)}/>
|
||||
<CopyToClipboardTextButton description='Copy as Playwright' copiedDescription={copiedDescription} value={copyAsPlaywright}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RequestTab: React.FunctionComponent<{
|
||||
resource: ResourceSnapshot;
|
||||
sdkLanguage: Language;
|
||||
startTimeOffset: number;
|
||||
}> = ({ resource, sdkLanguage, startTimeOffset }) => {
|
||||
const [requestBody, setRequestBody] = React.useState<{ text: string, mimeType?: string } | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const readResources = async () => {
|
||||
if (resource.request.postData) {
|
||||
const requestContentTypeHeader = resource.request.headers.find(q => q.name.toLowerCase() === 'content-type');
|
||||
const requestContentType = requestContentTypeHeader ? requestContentTypeHeader.value : '';
|
||||
if (resource.request.postData._sha1) {
|
||||
const response = await fetch(`sha1/${resource.request.postData._sha1}`);
|
||||
setRequestBody({ text: formatBody(await response.text(), requestContentType), mimeType: requestContentType });
|
||||
} else {
|
||||
setRequestBody({ text: formatBody(resource.request.postData.text, requestContentType), mimeType: requestContentType });
|
||||
}
|
||||
} else {
|
||||
setRequestBody(null);
|
||||
}
|
||||
};
|
||||
readResources();
|
||||
}, [resource]);
|
||||
|
||||
requestBody: RequestBody,
|
||||
}> = ({ resource, startTimeOffset, requestBody }) => {
|
||||
return <div className='network-request-details-tab'>
|
||||
<div className='network-request-details-header'>General</div>
|
||||
<div className='network-request-details-url'>{`URL: ${resource.request.url}`}</div>
|
||||
|
@ -103,12 +128,6 @@ const RequestTab: React.FunctionComponent<{
|
|||
<div className='network-request-details-general'>{`Start: ${msToString(startTimeOffset)}`}</div>
|
||||
<div className='network-request-details-general'>{`Duration: ${msToString(resource.time)}`}</div>
|
||||
|
||||
<div className='network-request-details-copy'>
|
||||
<CopyToClipboardTextButton description='Copy as cURL' value={() => generateCurlCommand(resource)} />
|
||||
<CopyToClipboardTextButton description='Copy as Fetch' value={() => generateFetchCall(resource)} />
|
||||
<CopyToClipboardTextButton description='Copy as Playwright' value={async () => getAPIRequestCodeGen(sdkLanguage).generatePlaywrightRequestCall(resource.request, requestBody?.text)} />
|
||||
</div>
|
||||
|
||||
{requestBody && <div className='network-request-details-header'>Request Body</div>}
|
||||
{requestBody && <CodeMirrorWrapper text={requestBody.text} mimeType={requestBody.mimeType} readOnly lineNumbers={true}/>}
|
||||
</div>;
|
||||
|
|
|
@ -20,11 +20,11 @@ import * as React from 'react';
|
|||
import { clsx } from '../uiUtils';
|
||||
|
||||
export interface ToolbarButtonProps {
|
||||
title: string,
|
||||
title?: string,
|
||||
icon?: string,
|
||||
disabled?: boolean,
|
||||
toggled?: boolean,
|
||||
onClick: (e: React.MouseEvent) => void,
|
||||
onClick?: (e: React.MouseEvent) => void,
|
||||
style?: React.CSSProperties,
|
||||
testId?: string,
|
||||
className?: string,
|
||||
|
|
Loading…
Reference in New Issue