From 32540d294eb5359a9e1b4d1edef736dde3f4865d Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 5 Jan 2023 16:20:14 +0200 Subject: [PATCH] Split up workflow output control and output visible controls --- .../Editor/Forms/FormOutputLabel.test.js | 4 +- .../src/components/Workflow/Editor/Lint.vue | 8 +- .../src/components/Workflow/Editor/Node.vue | 9 +- .../components/Workflow/Editor/NodeOutput.vue | 90 +++++++++--- .../Workflow/Editor/modules/outputs.js | 136 ------------------ .../Workflow/Editor/modules/outputs.test.js | 69 --------- .../Editor/test-data/parameter_steps.json | 54 +++---- .../Editor/test-data/simple_steps.json | 3 +- client/src/stores/workflowStepStore.ts | 17 ++- client/src/style/scss/workflow.scss | 8 ++ 10 files changed, 120 insertions(+), 278 deletions(-) delete mode 100644 client/src/components/Workflow/Editor/modules/outputs.js delete mode 100644 client/src/components/Workflow/Editor/modules/outputs.test.js diff --git a/client/src/components/Workflow/Editor/Forms/FormOutputLabel.test.js b/client/src/components/Workflow/Editor/Forms/FormOutputLabel.test.js index 1e03aaa4832..b0f9c8fd882 100644 --- a/client/src/components/Workflow/Editor/Forms/FormOutputLabel.test.js +++ b/client/src/components/Workflow/Editor/Forms/FormOutputLabel.test.js @@ -18,7 +18,7 @@ describe("FormOutputLabel", () => { ]; beforeEach(() => { - const stepOne = { id: 0, workflow_outputs: outputs }; + const stepOne = { id: 0, outputs: [{ name: "output-name" }], workflow_outputs: outputs }; const pinia = createPinia(); setActivePinia(pinia); wrapper = mount(FormOutputLabel, { @@ -29,7 +29,7 @@ describe("FormOutputLabel", () => { localVue, pinia, }); - const stepTwo = { id: 1, workflow_outputs: outputs }; + const stepTwo = { id: 1, outputs: [{ name: "other-name" }], workflow_outputs: outputs }; wrapperOther = mount(FormOutputLabel, { propsData: { name: "other-name", diff --git a/client/src/components/Workflow/Editor/Lint.vue b/client/src/components/Workflow/Editor/Lint.vue index eeda4afcc1b..1a0a49ca3e7 100644 --- a/client/src/components/Workflow/Editor/Lint.vue +++ b/client/src/components/Workflow/Editor/Lint.vue @@ -75,7 +75,7 @@ diff --git a/client/src/components/Workflow/Editor/modules/outputs.js b/client/src/components/Workflow/Editor/modules/outputs.js deleted file mode 100644 index 86e140c4be6..00000000000 --- a/client/src/components/Workflow/Editor/modules/outputs.js +++ /dev/null @@ -1,136 +0,0 @@ -import Vue from "vue"; - -export const allLabels = {}; - -export class ActiveOutputs { - constructor() { - this.entries = {}; - } - - /** Initialize list of active outputs from server response */ - initialize(outputs, incoming) { - this.outputs = outputs; - this._refreshIndex(); - incoming && - incoming.forEach((entry) => { - if (entry.label && allLabels[entry.label]) { - entry.label = null; - } - this.add(entry.output_name, entry.label); - if (entry.label) { - allLabels[entry.label] = true; - } - }); - } - - /** Adds a new record to the value stack **/ - add(name, label) { - if (!this.exists(name)) { - this.update(name, label); - return true; - } - return false; - } - - /** Toggle an entry */ - toggle(name) { - if (this.exists(name)) { - this.remove(name); - } else { - this.add(name); - } - } - - /** Change label for an output */ - labelOutput(outputName, newLabel) { - const activeOutput = this.get(outputName); - const oldLabel = activeOutput && activeOutput.label; - if (newLabel == oldLabel) { - return true; - } - if (this.outputsIndex[outputName] && !allLabels[newLabel]) { - const oldLabel = this.update(outputName, newLabel); - if (oldLabel && allLabels[oldLabel]) { - delete allLabels[oldLabel]; - } - if (newLabel) { - allLabels[newLabel] = true; - } - return true; - } - return false; - } - - /** Returns the number of added records **/ - count() { - return Object.keys(this.entries).length; - } - - /** Return label */ - get(name) { - return this.entries[name]; - } - - /** Returns true if a record is available for a given key **/ - exists(name) { - return !!this.entries[name]; - } - - /** Remove an entry given its name */ - remove(name) { - const activeOutput = this.get(name); - const activeLabel = activeOutput && activeOutput.label; - if (activeLabel && allLabels[activeLabel]) { - delete allLabels[activeLabel]; - } - delete this.entries[name]; - this._updateOutput(name); - } - - /** Returns list of all values */ - getAll() { - return Object.values(this.entries); - } - - /** Update an active outputs label */ - update(name, label) { - const activeOutput = this.get(name); - const oldLabel = activeOutput && activeOutput.label; - this.entries[name] = { - output_name: name, - label: label || null, - }; - this._updateOutput(name); - return oldLabel; - } - - /** Removes all entries which are not in the parsed dictionary of names */ - filterOutputs(names) { - this.getAll().forEach((wf_output) => { - if (!names.includes(wf_output.output_name)) { - this.remove(wf_output.output_name); - } - }); - this._refreshIndex(); - } - - /** Update an output */ - _updateOutput(name) { - const output = this.outputsIndex[name]; - if (output) { - const activeOutput = this.get(output.name); - Vue.set(output, "activeOutput", !!activeOutput); - Vue.set(output, "activeLabel", activeOutput && activeOutput.label); - } - } - - /** Refreshes dictionary of outputs */ - _refreshIndex() { - this.outputsIndex = {}; - this.outputs && - this.outputs.forEach((o) => { - this.outputsIndex[o.name] = o; - this._updateOutput(o.name); - }); - } -} diff --git a/client/src/components/Workflow/Editor/modules/outputs.test.js b/client/src/components/Workflow/Editor/modules/outputs.test.js deleted file mode 100644 index d5ec5cc4ce7..00000000000 --- a/client/src/components/Workflow/Editor/modules/outputs.test.js +++ /dev/null @@ -1,69 +0,0 @@ -import { allLabels, ActiveOutputs } from "./outputs"; - -describe("Workflow Outputs", () => { - it("test output label handling", () => { - // Test adding initial node - const activeOutputs = new ActiveOutputs(); - const outputs = [{ name: "output_0" }, { name: "output_1" }, { name: "output_2" }]; - const incoming = [ - { output_name: "output_0", label: "label_0" }, - { output_name: "output_1", label: "label_0" }, - { output_name: "output_2", label: "label_1" }, - ]; - activeOutputs.initialize(outputs, incoming); - expect(Object.keys(allLabels).length).toBe(2); - expect(allLabels["label_0"]).toBe(true); - expect(allLabels["label_1"]).toBe(true); - expect(activeOutputs.count()).toBe(3); - expect(activeOutputs.entries["output_0"].label).toBe("label_0"); - expect(activeOutputs.entries["output_1"].label).toBe(null); - expect(activeOutputs.entries["output_2"].label).toBe("label_1"); - - // Test adding additional node - const activeOutputs_1 = new ActiveOutputs(); - const outputs_1 = [{ name: "output_0" }, { name: "output_1" }, { name: "output_2" }]; - const incoming_1 = [ - { output_name: "output_0", label: "label_0" }, - { output_name: "output_1", label: "label_0" }, - { output_name: "output_2", label: "label_2" }, - ]; - activeOutputs_1.initialize(outputs_1, incoming_1); - expect(Object.keys(allLabels).length).toBe(3); - expect(allLabels["label_0"]).toBe(true); - expect(allLabels["label_1"]).toBe(true); - expect(allLabels["label_2"]).toBe(true); - expect(activeOutputs_1.count()).toBe(3); - expect(activeOutputs_1.entries["output_0"].label).toBe(null); - expect(activeOutputs_1.entries["output_1"].label).toBe(null); - expect(activeOutputs_1.entries["output_2"].label).toBe("label_2"); - - // Test toggle / removal - activeOutputs_1.toggle("output_0"); - expect(activeOutputs_1.count()).toBe(2); - activeOutputs_1.toggle("output_1"); - expect(activeOutputs_1.count()).toBe(1); - activeOutputs_1.toggle("output_0"); - expect(activeOutputs_1.count()).toBe(2); - activeOutputs_1.toggle("output_1"); - expect(activeOutputs_1.count()).toBe(3); - - // Test output filtering - activeOutputs.filterOutputs(["output_0", "output_2"]); - expect(activeOutputs.count()).toBe(2); - expect(activeOutputs.entries["output_0"].label).toBe("label_0"); - expect(activeOutputs.entries["output_1"]).toBe(undefined); - expect(activeOutputs.entries["output_2"].label).toBe("label_1"); - - // Update output label - const response = activeOutputs.labelOutput("output_0", "label_3"); - expect(response).toBe(true); - expect(activeOutputs.entries["output_0"].label).toBe("label_3"); - const response_1 = activeOutputs.labelOutput("output_0", "label_1"); - expect(response_1).toBe(false); - expect(activeOutputs.entries["output_0"].label).toBe("label_3"); - - // Test output removal - activeOutputs.filterOutputs([]); - expect(Object.keys(allLabels).length).toBe(1); - }); -}); diff --git a/client/src/components/Workflow/Editor/test-data/parameter_steps.json b/client/src/components/Workflow/Editor/test-data/parameter_steps.json index 47441b648a5..fb9a9ed0f02 100644 --- a/client/src/components/Workflow/Editor/test-data/parameter_steps.json +++ b/client/src/components/Workflow/Editor/test-data/parameter_steps.json @@ -19,8 +19,7 @@ "extensions": [ "input" ], - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -66,8 +65,7 @@ "tabular" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -109,8 +107,7 @@ ], "collection": true, "collection_type": "list", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -146,8 +143,7 @@ ], "collection": true, "collection_type": "list:list", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -183,8 +179,7 @@ ], "collection": true, "collection_type": "paired", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -216,8 +211,7 @@ "label": "integer parameter input", "type": "integer", "optional": false, - "parameter": true, - "activeOutput": false + "parameter": true } ], "annotation": "", @@ -263,8 +257,7 @@ "tabular" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -319,8 +312,7 @@ "optional": false, "collection": true, "collection_type": null, - "collection_type_source": null, - "activeOutput": false + "collection_type_source": null } ], "annotation": "", @@ -376,8 +368,7 @@ ], "type": "data", "optional": false, - "label": "input dataset(s) (extracted element)", - "activeOutput": false + "label": "input dataset(s) (extracted element)" } ], "annotation": "", @@ -416,8 +407,7 @@ "extensions": [ "input" ], - "optional": true, - "activeOutput": false + "optional": true } ], "annotation": "", @@ -449,8 +439,7 @@ "label": "text parameter input", "type": "text", "optional": false, - "parameter": true, - "activeOutput": false + "parameter": true } ], "annotation": "", @@ -497,8 +486,7 @@ "txt" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -564,8 +552,7 @@ "txt" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -618,8 +605,7 @@ "txt" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -680,8 +666,7 @@ "input" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -761,8 +746,7 @@ "txt" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false }, { "name": "out2", @@ -770,8 +754,7 @@ "txt" ], "type": "data", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", @@ -818,8 +801,7 @@ ], "collection": true, "collection_type": "list:list:list", - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", diff --git a/client/src/components/Workflow/Editor/test-data/simple_steps.json b/client/src/components/Workflow/Editor/test-data/simple_steps.json index 6c65a8cd4be..a3cd12dbbfc 100644 --- a/client/src/components/Workflow/Editor/test-data/simple_steps.json +++ b/client/src/components/Workflow/Editor/test-data/simple_steps.json @@ -19,8 +19,7 @@ "extensions": [ "input" ], - "optional": false, - "activeOutput": false + "optional": false } ], "annotation": "", diff --git a/client/src/stores/workflowStepStore.ts b/client/src/stores/workflowStepStore.ts index 810f7ccdd77..36663da8434 100644 --- a/client/src/stores/workflowStepStore.ts +++ b/client/src/stores/workflowStepStore.ts @@ -41,7 +41,6 @@ export interface DataOutput { extensions: string[]; name: string; optional: boolean; - activeOutput?: boolean; } export interface CollectionOutput extends Omit { @@ -148,15 +147,20 @@ export const useWorkflowStepStore = defineStore("workflowStepStore", { getStepIndex(state: State) { return Math.max(...Object.values(state.steps).map((step) => step.id), state.stepIndex); }, + hasActiveOutputs(state: State) { + return Boolean(Object.values(state.steps).find((step) => step.workflow_outputs?.length)); + }, workflowOutputs(state: State) { const workflowOutputs: WorkflowOutputs = {}; Object.values(state.steps).forEach((step) => { if (step.workflow_outputs?.length) { step.workflow_outputs.forEach((workflowOutput) => { - workflowOutputs[workflowOutput.label || workflowOutput.output_name] = { - outputName: workflowOutput.output_name, - stepId: step.id, - }; + if (workflowOutput.label) { + workflowOutputs[workflowOutput.label] = { + outputName: workflowOutput.output_name, + stepId: step.id, + }; + } }); } }); @@ -174,6 +178,9 @@ export const useWorkflowStepStore = defineStore("workflowStepStore", { return step; }, updateStep(this: State, step: Step) { + step.workflow_outputs = step.workflow_outputs?.filter((workflowOutput) => + step.outputs.find((output) => workflowOutput.output_name == output.name) + ); this.steps[step.id.toString()] = step; }, changeStepMapOver(stepId: number, mapOver: CollectionTypeDescriptor) { diff --git a/client/src/style/scss/workflow.scss b/client/src/style/scss/workflow.scss index 8c8afda5cc8..4b6ba4ef683 100644 --- a/client/src/style/scss/workflow.scss +++ b/client/src/style/scss/workflow.scss @@ -163,6 +163,14 @@ @extend .fa; @extend .fa-check-square; } + &.mark-terminal-visible { + @extend .fa; + @extend .fa-eye; + } + &.mark-terminal-hidden { + @extend .fa; + @extend .fa-eye-slash; + } } .delete-terminal { @extend .btn;