mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(trace-viewer): add parameters copy to clip (#19662)
This commit is contained in:
parent
b363902e1b
commit
166a729386
@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
.call-line {
|
.call-line {
|
||||||
padding: 4px 0 4px 6px;
|
padding: 4px 0 4px 6px;
|
||||||
flex: none;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -57,6 +57,25 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.call-line__copy-icon {
|
||||||
|
visibility: hidden;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.call-line:hover .call-line__copy-icon {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.call-value {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.call-value::before {
|
||||||
|
content: '\00a0';
|
||||||
|
}
|
||||||
|
|
||||||
.call-line .datetime,
|
.call-line .datetime,
|
||||||
.call-line .string {
|
.call-line .string {
|
||||||
color: var(--orange);
|
color: var(--orange);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import type { ActionTraceEvent } from '@trace/trace';
|
|||||||
import { msToString } from '@web/uiUtils';
|
import { msToString } from '@web/uiUtils';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './callTab.css';
|
import './callTab.css';
|
||||||
|
import { CopyToClipboard } from './copyToClipboard';
|
||||||
|
|
||||||
export const CallTab: React.FunctionComponent<{
|
export const CallTab: React.FunctionComponent<{
|
||||||
action: ActionTraceEvent | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
@ -42,8 +43,8 @@ export const CallTab: React.FunctionComponent<{
|
|||||||
<div className='call-line'>{action.metadata.apiName}</div>
|
<div className='call-line'>{action.metadata.apiName}</div>
|
||||||
{<>
|
{<>
|
||||||
<div className='call-section'>Time</div>
|
<div className='call-section'>Time</div>
|
||||||
{action.metadata.wallTime && <div className='call-line'>wall time: <span className='datetime' title={wallTime}>{wallTime}</span></div>}
|
{action.metadata.wallTime && <div className='call-line'>wall time: <span className='call-value datetime' title={wallTime}>{wallTime}</span></div>}
|
||||||
<div className='call-line'>duration: <span className='datetime' title={duration}>{duration}</span></div>
|
<div className='call-line'>duration: <span className='call-value datetime' title={duration}>{duration}</span></div>
|
||||||
</>}
|
</>}
|
||||||
{ !!paramKeys.length && <div className='call-section'>Parameters</div> }
|
{ !!paramKeys.length && <div className='call-section'>Parameters</div> }
|
||||||
{
|
{
|
||||||
@ -66,12 +67,29 @@ export const CallTab: React.FunctionComponent<{
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function shouldCopy(type: string): boolean {
|
||||||
|
return !!({
|
||||||
|
'string': true,
|
||||||
|
'number': true,
|
||||||
|
'object': true,
|
||||||
|
}[type]);
|
||||||
|
}
|
||||||
|
|
||||||
function renderLine(metadata: CallMetadata, name: string, value: any, key: string) {
|
function renderLine(metadata: CallMetadata, name: string, value: any, key: string) {
|
||||||
const { title, type } = toString(metadata, name, value);
|
const { title, type } = toString(metadata, name, value);
|
||||||
let text = title.replace(/\n/g, '↵');
|
let text = title.replace(/\n/g, '↵');
|
||||||
if (type === 'string')
|
if (type === 'string')
|
||||||
text = `"${text}"`;
|
text = `"${text}"`;
|
||||||
return <div key={key} className='call-line'>{name}: <span className={type} title={title}>{text}</span></div>;
|
return (
|
||||||
|
<div key={key} className='call-line'>
|
||||||
|
{name}: <span className={`call-value ${type}`} title={title}>{text}</span>
|
||||||
|
{ shouldCopy(type) && (
|
||||||
|
<span className='call-line__copy-icon'>
|
||||||
|
<CopyToClipboard value={title} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toString(metadata: CallMetadata, name: string, value: any): { title: string, type: string } {
|
function toString(metadata: CallMetadata, name: string, value: any): { title: string, type: string } {
|
||||||
|
|||||||
7
packages/trace-viewer/src/ui/copyToClipboard.css
Normal file
7
packages/trace-viewer/src/ui/copyToClipboard.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.codicon-check {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.codicon-close {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
43
packages/trace-viewer/src/ui/copyToClipboard.tsx
Normal file
43
packages/trace-viewer/src/ui/copyToClipboard.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import './copyToClipboard.css';
|
||||||
|
|
||||||
|
const TIMEOUT = 3000;
|
||||||
|
const DEFAULT_ICON = 'codicon-clippy';
|
||||||
|
const COPIED_ICON = 'codicon-check';
|
||||||
|
const FAILED_ICON = 'codicon-close';
|
||||||
|
|
||||||
|
export const CopyToClipboard: React.FunctionComponent<{
|
||||||
|
value: string,
|
||||||
|
}> = ({ value }) => {
|
||||||
|
const [iconClassName, setIconClassName] = React.useState(DEFAULT_ICON);
|
||||||
|
|
||||||
|
const handleCopy = React.useCallback(() => {
|
||||||
|
navigator.clipboard.writeText(value).then(() => {
|
||||||
|
setIconClassName(COPIED_ICON);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIconClassName(DEFAULT_ICON);
|
||||||
|
}, TIMEOUT);
|
||||||
|
}, () => {
|
||||||
|
setIconClassName(FAILED_ICON);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return <span className={`codicon ${iconClassName}`} onClick={handleCopy}/>;
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user