From 7d1b3e7e149a8d68c4ec24116f7b48b33e95d0ef Mon Sep 17 00:00:00 2001
From: Yury Molodov <yurymolodov@gmail.com>
Date: Mon, 21 Nov 2022 23:31:33 +0100
Subject: [PATCH] vmui: add copy button to row on Table view (#3363)

* feat: add copy button to row on Table view

* vmui: add copy button to row on Table view

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
---
 .../components/Views/TableView/TableView.tsx  | 50 +++++++++++++++++--
 app/vmui/packages/vmui/src/types/index.ts     |  1 +
 docs/CHANGELOG.md                             |  1 +
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/app/vmui/packages/vmui/src/components/Views/TableView/TableView.tsx b/app/vmui/packages/vmui/src/components/Views/TableView/TableView.tsx
index 32126a22c2..b11c2e9c67 100644
--- a/app/vmui/packages/vmui/src/components/Views/TableView/TableView.tsx
+++ b/app/vmui/packages/vmui/src/components/Views/TableView/TableView.tsx
@@ -4,7 +4,10 @@ import { InstantDataSeries } from "../../../types";
 import { useSortedCategories } from "../../../hooks/useSortedCategories";
 import Alert from "../../Main/Alert/Alert";
 import classNames from "classnames";
-import { ArrowDropDownIcon } from "../../Main/Icons";
+import { ArrowDropDownIcon, CopyIcon } from "../../Main/Icons";
+import Tooltip from "../../Main/Tooltip/Tooltip";
+import Button from "../../Main/Button/Button";
+import { useSnack } from "../../../contexts/Snackbar";
 
 export interface GraphViewProps {
   data: InstantMetricResult[];
@@ -12,16 +15,24 @@ export interface GraphViewProps {
 }
 
 const TableView: FC<GraphViewProps> = ({ data, displayColumns }) => {
+  const { showInfoMessage } = useSnack();
 
   const sortedColumns = useSortedCategories(data, displayColumns);
 
   const [orderBy, setOrderBy] = useState("");
   const [orderDir, setOrderDir] = useState<"asc" | "desc">("asc");
 
+  const getCopyValue = (metric: {[p: string]: string}) => {
+    const { __name__, ...fields } = metric;
+    if (!__name__ && !Object.keys(fields).length) return "";
+    return `${__name__} ${JSON.stringify(fields)}`;
+  };
+
   const rows: InstantDataSeries[] = useMemo(() => {
     const rows = data?.map(d => ({
       metadata: sortedColumns.map(c => d.metric[c.key] || "-"),
-      value: d.value ? d.value[1] : "-"
+      value: d.value ? d.value[1] : "-",
+      copyValue: getCopyValue(d.metric)
     }));
     const orderByValue = orderBy === "Value";
     const rowIndex = sortedColumns.findIndex(c => c.key === orderBy);
@@ -34,15 +45,26 @@ const TableView: FC<GraphViewProps> = ({ data, displayColumns }) => {
     });
   }, [sortedColumns, data, orderBy, orderDir]);
 
-  const createSortHandler = (key: string) => () => {
-    sortHandler(key);
-  };
+  const hasCopyValue = useMemo(() => rows.some(r => r.copyValue), [rows]);
 
   const sortHandler = (key: string) => {
     setOrderDir((prev) => prev === "asc" && orderBy === key ? "desc" : "asc");
     setOrderBy(key);
   };
 
+  const copyHandler = async (copyValue: string) => {
+    await navigator.clipboard.writeText(copyValue);
+    showInfoMessage({ text: "Row has been copied", type: "success" });
+  };
+
+  const createSortHandler = (key: string) => () => {
+    sortHandler(key);
+  };
+
+  const createCopyHandler = (copyValue: string) => () => {
+    copyHandler(copyValue);
+  };
+
   if (!rows.length) return <Alert variant="warning">No data to show</Alert>;
 
   return (
@@ -86,6 +108,7 @@ const TableView: FC<GraphViewProps> = ({ data, displayColumns }) => {
               Value
             </div>
           </td>
+          {hasCopyValue && <td className="vm-table-cell vm-table-cell_header"/>}
         </tr>
       </thead>
       <tbody className="vm-table-body">
@@ -108,6 +131,23 @@ const TableView: FC<GraphViewProps> = ({ data, displayColumns }) => {
             <td className="vm-table-cell vm-table-cell_right">
               {row.value}
             </td>
+            {hasCopyValue && (
+              <td className="vm-table-cell vm-table-cell_right">
+                {row.copyValue && (
+                  <div className="vm-table-cell__content">
+                    <Tooltip title="Copy row">
+                      <Button
+                        variant="text"
+                        color="gray"
+                        size="small"
+                        startIcon={<CopyIcon/>}
+                        onClick={createCopyHandler(row.copyValue)}
+                      />
+                    </Tooltip>
+                  </div>
+                )}
+              </td>
+            )}
           </tr>
         ))}
       </tbody>
diff --git a/app/vmui/packages/vmui/src/types/index.ts b/app/vmui/packages/vmui/src/types/index.ts
index 3e711e82e0..0bbbdb4d22 100644
--- a/app/vmui/packages/vmui/src/types/index.ts
+++ b/app/vmui/packages/vmui/src/types/index.ts
@@ -35,6 +35,7 @@ export interface DataSeries extends MetricBase{
 export interface InstantDataSeries {
   metadata: string[]; // just ordered columns
   value: string;
+  copyValue: string;
 }
 
 export enum ErrorTypes {
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 8f922c3a1d..a9d8c2ab16 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -24,6 +24,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
 * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to upload/paste JSON to investigate the trace. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3308) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3310).
 * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): reduce JS bundle size from 200Kb to 100Kb. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3298).
 * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to hide results of a particular query by clicking the `eye` icon. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3359).
+* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add copy button to row on Table view. The button copies row in MetricQL format. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2815).
 * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to "stick" a tooltip on the chart by clicking on a data point. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3321) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3376)
 * FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): add default alert list for vmalert's metrics. See [alerts-vmalert.yml](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-vmalert.yml).