mirror of
https://github.com/microsoft/playwright.git
synced 2024-10-27 13:50:25 +03:00
feat(html): "copy to clipboard" for text attachments (#27556)
Mostly copied from trace viewer. Not reused due to different colors/icons. Screenshot: <img width="999" alt="copy-to-clipboard" src="https://github.com/microsoft/playwright/assets/9881434/2bb38442-3b8d-42ba-a3ed-4b9052b22854">
This commit is contained in:
parent
393bd36e0a
commit
823b104a9e
34
packages/html-reporter/src/copyToClipboard.css
Normal file
34
packages/html-reporter/src/copyToClipboard.css
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
.copy-icon {
|
||||
flex: none;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--color-fg-default);
|
||||
background: transparent;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.copy-icon:not(:disabled):hover {
|
||||
background-color: var(--color-border-default);
|
||||
}
|
38
packages/html-reporter/src/copyToClipboard.tsx
Normal file
38
packages/html-reporter/src/copyToClipboard.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 * as icons from './icons';
|
||||
import './copyToClipboard.css';
|
||||
|
||||
export const CopyToClipboard: React.FunctionComponent<{
|
||||
value: string,
|
||||
}> = ({ value }) => {
|
||||
type IconType = 'copy' | 'check' | 'cross';
|
||||
const [icon, setIcon] = React.useState<IconType>('copy');
|
||||
const handleCopy = React.useCallback(() => {
|
||||
navigator.clipboard.writeText(value).then(() => {
|
||||
setIcon('check');
|
||||
setTimeout(() => {
|
||||
setIcon('copy');
|
||||
}, 3000);
|
||||
}, () => {
|
||||
setIcon('cross');
|
||||
});
|
||||
}, [value]);
|
||||
const iconElement = icon === 'check' ? icons.check() : icon === 'cross' ? icons.cross() : icons.copy();
|
||||
return <button className='copy-icon' onClick={handleCopy}>{iconElement}</button>;
|
||||
};
|
@ -106,3 +106,10 @@ export const trace = () => {
|
||||
export const empty = () => {
|
||||
return <svg className='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'></svg>;
|
||||
};
|
||||
|
||||
export const copy = () => {
|
||||
return <svg className='octicon' viewBox='0 0 16 16' width='16' height='16' aria-hidden='true'>
|
||||
<path d='M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z'></path>
|
||||
<path d='M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z'></path>
|
||||
</svg>;
|
||||
};
|
||||
|
@ -102,4 +102,11 @@
|
||||
line-height: normal;
|
||||
padding: 8px;
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.attachment-body .copy-icon {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import type { TestAttachment } from './types';
|
||||
import * as React from 'react';
|
||||
import * as icons from './icons';
|
||||
import { TreeItem } from './treeItem';
|
||||
import { CopyToClipboard } from './copyToClipboard';
|
||||
import './links.css';
|
||||
|
||||
export function navigate(href: string) {
|
||||
@ -71,7 +72,7 @@ export const AttachmentLink: React.FunctionComponent<{
|
||||
{attachment.path && <a href={href || attachment.path} download={downloadFileNameForAttachment(attachment)}>{linkName || attachment.name}</a>}
|
||||
{attachment.body && <span>{attachment.name}</span>}
|
||||
</span>} loadChildren={attachment.body ? () => {
|
||||
return [<div className='attachment-body'>{attachment.body}</div>];
|
||||
return [<div className='attachment-body'><CopyToClipboard value={attachment.body!}/>{attachment.body}</div>];
|
||||
} : undefined} depth={0} style={{ lineHeight: '32px' }}></TreeItem>;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user