diff --git a/packages/trace-viewer/src/ui/callTab.css b/packages/trace-viewer/src/ui/callTab.css
index d515896d51..23e8ae0bbb 100644
--- a/packages/trace-viewer/src/ui/callTab.css
+++ b/packages/trace-viewer/src/ui/callTab.css
@@ -49,7 +49,7 @@
.call-line {
padding: 4px 0 4px 6px;
- flex: none;
+ display: flex;
align-items: center;
text-overflow: ellipsis;
overflow: hidden;
@@ -57,6 +57,25 @@
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 .string {
color: var(--orange);
diff --git a/packages/trace-viewer/src/ui/callTab.tsx b/packages/trace-viewer/src/ui/callTab.tsx
index 2bc669dcfa..841d1a5da4 100644
--- a/packages/trace-viewer/src/ui/callTab.tsx
+++ b/packages/trace-viewer/src/ui/callTab.tsx
@@ -20,6 +20,7 @@ import type { ActionTraceEvent } from '@trace/trace';
import { msToString } from '@web/uiUtils';
import * as React from 'react';
import './callTab.css';
+import { CopyToClipboard } from './copyToClipboard';
export const CallTab: React.FunctionComponent<{
action: ActionTraceEvent | undefined,
@@ -42,8 +43,8 @@ export const CallTab: React.FunctionComponent<{
{action.metadata.apiName}
{<>
Time
- {action.metadata.wallTime && wall time: {wallTime}
}
- duration: {duration}
+ {action.metadata.wallTime && wall time: {wallTime}
}
+ duration: {duration}
>}
{ !!paramKeys.length && Parameters
}
{
@@ -66,12 +67,29 @@ export const CallTab: React.FunctionComponent<{
;
};
+function shouldCopy(type: string): boolean {
+ return !!({
+ 'string': true,
+ 'number': true,
+ 'object': true,
+ }[type]);
+}
+
function renderLine(metadata: CallMetadata, name: string, value: any, key: string) {
const { title, type } = toString(metadata, name, value);
let text = title.replace(/\n/g, '↵');
if (type === 'string')
text = `"${text}"`;
- return {name}: {text}
;
+ return (
+
+ {name}: {text}
+ { shouldCopy(type) && (
+
+
+
+ )}
+
+ );
}
function toString(metadata: CallMetadata, name: string, value: any): { title: string, type: string } {
diff --git a/packages/trace-viewer/src/ui/copyToClipboard.css b/packages/trace-viewer/src/ui/copyToClipboard.css
new file mode 100644
index 0000000000..2234a4bfa6
--- /dev/null
+++ b/packages/trace-viewer/src/ui/copyToClipboard.css
@@ -0,0 +1,7 @@
+.codicon-check {
+ color: var(--green);
+}
+
+.codicon-close {
+ color: var(--red);
+}
diff --git a/packages/trace-viewer/src/ui/copyToClipboard.tsx b/packages/trace-viewer/src/ui/copyToClipboard.tsx
new file mode 100644
index 0000000000..b8dc6d59ee
--- /dev/null
+++ b/packages/trace-viewer/src/ui/copyToClipboard.tsx
@@ -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 ;
+};