Преглед изворни кода

HPCC-25473 Common up Copy and Download Selection

Signed-off-by: Gordon Smith <GordonJSmith@gmail.com>
Gordon Smith пре 4 година
родитељ
комит
0fd9948ec4

+ 3 - 15
esp/src/src-react/components/Activities.tsx

@@ -5,7 +5,7 @@ import * as ESPActivity from "src/ESPActivity";
 import * as Utility from "src/Utility";
 import nlsHPCC from "src/nlsHPCC";
 import { HolyGrail } from "../layouts/HolyGrail";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector, tree } from "./DojoGrid";
 
 class DelayedRefresh {
@@ -246,19 +246,7 @@ export const Activities: React.FunctionComponent<ActivitiesProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.wuSelected || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.wuSelected, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "activities.csv")
     ];
 
     //  Grid ---
@@ -408,7 +396,7 @@ export const Activities: React.FunctionComponent<ActivitiesProps> = ({
             }
         });
         setUIState(state);
-    // eslint-disable-next-line react-hooks/exhaustive-deps
+        // eslint-disable-next-line react-hooks/exhaustive-deps
     }, [selection]);
 
     return <HolyGrail

+ 20 - 0
esp/src/src-react/components/Common.tsx

@@ -1,4 +1,24 @@
 import * as React from "react";
 import { VerticalDivider } from "@fluentui/react";
+import * as Utility from "src/Utility";
+import nlsHPCC from "src/nlsHPCC";
 
 export const ShortVerticalDivider = () => <VerticalDivider styles={{ divider: { paddingTop: "20%", height: "60%" } }} />;
+
+export function createCopyDownloadSelection(grid, selection: any, filename: string) {
+    return [{
+        key: "copy", text: nlsHPCC.CopySelectionToClipboard, disabled: !selection.length || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
+        onClick: () => {
+            const tsv = Utility.formatAsDelim(grid, selection, "\t");
+            navigator?.clipboard?.writeText(tsv);
+        }
+    },
+    {
+        key: "download", text: nlsHPCC.DownloadSelectionAsCSV, disabled: !selection.length, iconOnly: true, iconProps: { iconName: "Download" },
+        onClick: () => {
+            const csv = Utility.formatAsDelim(grid, selection, ",");
+            Utility.downloadText(csv, filename);
+        }
+    }];
+}
+

+ 2 - 14
esp/src/src-react/components/DFUWorkunits.tsx

@@ -10,7 +10,7 @@ import { HolyGrail } from "../layouts/HolyGrail";
 import { pushParams } from "../util/history";
 import { Filter } from "./forms/Filter";
 import { Fields } from "./forms/Fields";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector } from "./DojoGrid";
 
 const FilterFields: Fields = {
@@ -116,19 +116,7 @@ export const DFUWorkunits: React.FunctionComponent<DFUWorkunitsProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "dfuworkunits.csv")
     ];
 
     //  Grid ---

+ 2 - 14
esp/src/src-react/components/Files.tsx

@@ -11,7 +11,7 @@ import { HolyGrail } from "../layouts/HolyGrail";
 import { pushParams } from "../util/history";
 import { Fields } from "./forms/Fields";
 import { Filter } from "./forms/Filter";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector, tree } from "./DojoGrid";
 
 const FilterFields: Fields = {
@@ -106,19 +106,7 @@ export const Files: React.FunctionComponent<FilesProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyLogicalFiles, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Name);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.IsProtected, row.IsCompressed, row.IsKeyFile, row.__hpcc_displayName, row.Owner, row.SuperOwners, row.Description, row.NodeGroup, row.RecordCount, row.IntSize, row.Parts, row.Modified])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "logicalfiles.csv")
     ];
 
     //  Grid ---

+ 3 - 16
esp/src/src-react/components/InfoGrid.tsx

@@ -9,7 +9,7 @@ import nlsHPCC from "src/nlsHPCC";
 import { useWorkunitExceptions } from "../hooks/Workunit";
 import { HolyGrail } from "../layouts/HolyGrail";
 import { DojoGrid } from "./DojoGrid";
-import { WUInfo } from "@hpcc-js/comms";
+import { createCopyDownloadSelection } from "./Common";
 
 function extractGraphInfo(msg) {
     const retVal: { graphID?: string, subgraphID?: string, activityID?: string, activityName?: string } = {};
@@ -48,9 +48,8 @@ export const InfoGrid: React.FunctionComponent<InfoGridProps> = ({
     const [otherChecked, setOtherChecked] = React.useState(true);
     const [filterCounts, setFilterCounts] = React.useState<any>({});
     const [grid, setGrid] = React.useState<any>(undefined);
-    const [visibleExceptions, setvisibleExceptions] = React.useState<WUInfo.ECLException[]>([]);
     const [exceptions] = useWorkunitExceptions(wuid);
-    const [, setSelection] = React.useState([]);
+    const [selection, setSelection] = React.useState([]);
 
     //  Command Bar  ---
     const buttons: ICommandBarItemProps[] = [
@@ -61,18 +60,7 @@ export const InfoGrid: React.FunctionComponent<InfoGridProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                navigator?.clipboard?.writeText(Utility.toCSV(visibleExceptions, "\t"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: false, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadText(Utility.toCSV(visibleExceptions, ","), "ErrWarn.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "errorwarnings.csv")
     ];
 
     //  Grid ---
@@ -181,7 +169,6 @@ export const InfoGrid: React.FunctionComponent<InfoGridProps> = ({
             }
             return l.Severity.localeCompare(r.Severity);
         });
-        setvisibleExceptions(filteredExceptions);
         gridStore.setData(filteredExceptions);
         refreshTable();
         setFilterCounts(filterCounts);

+ 2 - 14
esp/src/src-react/components/Queries.tsx

@@ -9,7 +9,7 @@ import { HolyGrail } from "../layouts/HolyGrail";
 import { pushParams } from "../util/history";
 import { Fields } from "./forms/Fields";
 import { Filter } from "./forms/Filter";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector } from "./DojoGrid";
 
 const FilterFields: Fields = {
@@ -129,19 +129,7 @@ export const Queries: React.FunctionComponent<QueriesProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "roxiequeries.csv")
     ];
 
     //  Grid ---

+ 2 - 15
esp/src/src-react/components/Results.tsx

@@ -3,11 +3,10 @@ import { CommandBar, ContextualMenuItemType, ICommandBarItemProps } from "@fluen
 import { useConst } from "@fluentui/react-hooks";
 import { AlphaNumSortMemory } from "src/Memory";
 import * as Observable from "dojo/store/Observable";
-import * as Utility from "src/Utility";
 import nlsHPCC from "src/nlsHPCC";
 import { useWorkunitResults } from "../hooks/Workunit";
 import { HolyGrail } from "../layouts/HolyGrail";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector } from "./DojoGrid";
 
 const defaultUIState = {
@@ -61,19 +60,7 @@ export const Results: React.FunctionComponent<ResultsProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "results.csv")
     ];
 
     //  Grid ---

+ 7 - 20
esp/src/src-react/components/Search.tsx

@@ -2,10 +2,9 @@ import * as React from "react";
 import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, Pivot, PivotItem, ProgressIndicator } from "@fluentui/react";
 import { useConst } from "@fluentui/react-hooks";
 import { ESPSearch } from "src/ESPSearch";
-import * as Utility from "src/Utility";
 import nlsHPCC from "src/nlsHPCC";
 import { HolyGrail } from "../layouts/HolyGrail";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector } from "./DojoGrid";
 import { Workunits } from "./Workunits";
 import { Files } from "./Files";
@@ -84,19 +83,7 @@ export const Search: React.FunctionComponent<SearchProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "search.csv")
     ];
 
     //  Grid ---
@@ -151,11 +138,11 @@ export const Search: React.FunctionComponent<SearchProps> = ({
             </>}
             main={<DojoGrid store={search.store} columns={gridColumns} setGrid={setGrid} setSelection={setSelection} />}
         /> : selectedKey === "ecl" ?
-                <Workunits store={search.eclStore} /> : selectedKey === "dfu" ?
-                    <DFUWorkunits store={search.dfuStore} /> : selectedKey === "file" ?
-                        <Files store={search.fileStore} /> : selectedKey === "query" ?
-                            <Queries store={search.queryStore} /> :
-                            undefined
+            <Workunits store={search.eclStore} /> : selectedKey === "dfu" ?
+                <DFUWorkunits store={search.dfuStore} /> : selectedKey === "file" ?
+                    <Files store={search.fileStore} /> : selectedKey === "query" ?
+                        <Queries store={search.queryStore} /> :
+                        undefined
         }
     />;
 };

+ 2 - 14
esp/src/src-react/components/SourceFiles.tsx

@@ -8,7 +8,7 @@ import * as Utility from "src/Utility";
 import nlsHPCC from "src/nlsHPCC";
 import { useWorkunitSourceFiles } from "../hooks/Workunit";
 import { HolyGrail } from "../layouts/HolyGrail";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector, tree } from "./DojoGrid";
 
 const defaultUIState = {
@@ -61,19 +61,7 @@ export const SourceFiles: React.FunctionComponent<SourceFilesProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "sourcefiles.csv")
     ];
 
     //  Grid ---

+ 2 - 31
esp/src/src-react/components/Variables.tsx

@@ -3,17 +3,12 @@ import { CommandBar, ContextualMenuItemType, ICommandBarItemProps } from "@fluen
 import { useConst } from "@fluentui/react-hooks";
 import * as Observable from "dojo/store/Observable";
 import { AlphaNumSortMemory } from "src/Memory";
-import * as Utility from "src/Utility";
 import nlsHPCC from "src/nlsHPCC";
 import { useWorkunitVariables } from "../hooks/Workunit";
 import { HolyGrail } from "../layouts/HolyGrail";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid } from "./DojoGrid";
 
-const defaultUIState = {
-    hasSelection: false
-};
-
 interface VariablesProps {
     wuid: string;
 }
@@ -24,7 +19,6 @@ export const Variables: React.FunctionComponent<VariablesProps> = ({
 
     const [grid, setGrid] = React.useState<any>(undefined);
     const [selection, setSelection] = React.useState([]);
-    const [uiState, setUIState] = React.useState({ ...defaultUIState });
     const [variables] = useWorkunitVariables(wuid);
 
     //  Command Bar  ---
@@ -37,19 +31,7 @@ export const Variables: React.FunctionComponent<VariablesProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "variables.csv")
     ];
 
     //  Grid ---
@@ -68,17 +50,6 @@ export const Variables: React.FunctionComponent<VariablesProps> = ({
         }
     };
 
-    //  Selection  ---
-    React.useEffect(() => {
-        const state = { ...defaultUIState };
-
-        for (let i = 0; i < selection.length; ++i) {
-            state.hasSelection = true;
-            break;
-        }
-        setUIState(state);
-    }, [selection]);
-
     React.useEffect(() => {
         gridStore.setData(variables.map((row, idx) => {
             return {

+ 3 - 14
esp/src/src-react/components/Workflows.tsx

@@ -6,7 +6,7 @@ import * as Observable from "dojo/store/Observable";
 import nlsHPCC from "src/nlsHPCC";
 import { useWorkunitWorkflows } from "../hooks/Workunit";
 import { HolyGrail } from "../layouts/HolyGrail";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid } from "./DojoGrid";
 
 interface WorkflowsProps {
@@ -18,7 +18,7 @@ export const Workflows: React.FunctionComponent<WorkflowsProps> = ({
 }) => {
 
     const [grid, setGrid] = React.useState<any>(undefined);
-    const [, setSelection] = React.useState([]);
+    const [selection, setSelection] = React.useState([]);
     const [workflows, , refreshWorkflow] = useWorkunitWorkflows(wuid);
 
     //  Command Bar  ---
@@ -33,18 +33,7 @@ export const Workflows: React.FunctionComponent<WorkflowsProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.Copy, disabled: true, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                //  TODO: See HPCC-25473 
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: true, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                //  TODO: See HPCC-25473 
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "workflows.csv")
     ];
 
     //  Grid ---

+ 2 - 14
esp/src/src-react/components/Workunits.tsx

@@ -10,7 +10,7 @@ import { HolyGrail } from "../layouts/HolyGrail";
 import { pushParams } from "../util/history";
 import { Fields } from "./forms/Fields";
 import { Filter } from "./forms/Filter";
-import { ShortVerticalDivider } from "./Common";
+import { createCopyDownloadSelection, ShortVerticalDivider } from "./Common";
 import { DojoGrid, selector } from "./DojoGrid";
 
 const FilterFields: Fields = {
@@ -138,19 +138,7 @@ export const Workunits: React.FunctionComponent<WorkunitsProps> = ({
     ];
 
     const rightButtons: ICommandBarItemProps[] = [
-        {
-            key: "copy", text: nlsHPCC.CopyWUIDs, disabled: !uiState.hasSelection || !navigator?.clipboard?.writeText, iconOnly: true, iconProps: { iconName: "Copy" },
-            onClick: () => {
-                const wuids = selection.map(s => s.Wuid);
-                navigator?.clipboard?.writeText(wuids.join("\n"));
-            }
-        },
-        {
-            key: "download", text: nlsHPCC.DownloadToCSV, disabled: !uiState.hasSelection, iconOnly: true, iconProps: { iconName: "Download" },
-            onClick: () => {
-                Utility.downloadToCSV(grid, selection.map(row => ([row.Protected, row.Wuid, row.Owner, row.Jobname, row.Cluster, row.RoxieCluster, row.State, row.TotalClusterTime])), "workunits.csv");
-            }
-        }
+        ...createCopyDownloadSelection(grid, selection, "workunits.csv")
     ];
 
     //  Grid ---

+ 33 - 0
esp/src/src/Utility.ts

@@ -45,6 +45,7 @@ export function parseXML(val) {
 
 export function csvEncode(cell) {
     if (!isNaN(cell)) return cell;
+    if (cell === undefined) return "";
     return '"' + String(cell).replace('"', '""') + '"';
 }
 
@@ -209,6 +210,38 @@ export function espSkew2NumberTests() {
     }, this);
 }
 
+export function formatAsDelim(grid, rows: any, delim = ",") {
+    const headers = grid.columns;
+    const container: string[] = [];
+    const headerNames: string[] = [];
+
+    for (const key in headers) {
+        if (key !== headers[key].id && headers[key].selectorType !== "checkbox") {
+            if (!headers[key].label) {
+                const str = csvEncode(headers[key].field);
+                headerNames.push(str);
+            } else {
+                const str = csvEncode(headers[key].label);
+                headerNames.push(str);
+            }
+        }
+    }
+    container.push(headerNames.join(delim));
+
+    rows.forEach(row => {
+        const cells: any[] = [];
+        for (const key in headers) {
+            if (key !== headers[key].id && headers[key].selectorType !== "checkbox") {
+                const cell = row[headers[key].field];
+                cells.push(csvEncode(cell));
+            }
+        }
+        container.push(cells.join(delim));
+    });
+
+    return container.join("\n");
+}
+
 export function downloadToCSV(grid, rows, fileName) {
     let csvContent = "";
     const headers = grid.columns;

+ 2 - 0
esp/src/src/nls/hpcc.ts

@@ -128,6 +128,7 @@ export = {
         CopyWUIDs: "Copy WUIDs to clipboard",
         CopyWUIDToClipboard: "Copy WUID to clipboard",
         CopyLogicalFiles: "Copy Logical Files to clipboard",
+        CopySelectionToClipboard: "Copy selection to clipboard",
         Copied: "Copied!",
         Count: "Count",
         CPULoad: "CPU Load",
@@ -211,6 +212,7 @@ export = {
         Download: "Download",
         Downloads: "Downloads",
         DownloadToCSV: "Download to CSV",
+        DownloadSelectionAsCSV: "Download selection as CSV",
         DropZone: "Drop Zone",
         DueToInctivity: "You will be logged out of all ECL Watch sessions in 3 minutes due to inactivity.",
         Duration: "Duration",