mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-13 08:51:38 +08:00
Merge pull request #16282 from ElectronicBlueberry/workflow-editor-tags
Display Tags in Workflow Editor
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: string): void;
|
||||
}>();
|
||||
|
||||
const valueArray = computed(() => props.value?.split(",").map((v) => v.trim()) ?? []);
|
||||
|
||||
function onInput(tags: string[]) {
|
||||
emit("input", tags.join(","));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<StatelessTags :value="valueArray" :placeholder="props.placeholder" @input="onInput"></StatelessTags>
|
||||
</template>
|
||||
@@ -12,6 +12,7 @@ import FormOptionalText from "./Elements/FormOptionalText.vue";
|
||||
import FormRulesEdit from "./Elements/FormRulesEdit.vue";
|
||||
import FormUpload from "./Elements/FormUpload.vue";
|
||||
import FormDrilldown from "./Elements/FormDrilldown/FormDrilldown.vue";
|
||||
import FormTags from "./Elements/FormTags.vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { ref, computed, useAttrs } from "vue";
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
@@ -285,6 +286,10 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !
|
||||
:data-label="props.title"
|
||||
:type="props.type ?? (attrs.options ? 'select' : 'text')"
|
||||
:attributes="attrs" />
|
||||
<FormTags
|
||||
v-else-if="props.type === 'tags'"
|
||||
v-model="currentValue"
|
||||
:placeholder="props.attributes?.placeholder" />
|
||||
<FormInput v-else :id="props.id" v-model="currentValue" :area="attrs['area']" />
|
||||
</div>
|
||||
|
||||
|
||||
+2
-1
@@ -27,4 +27,5 @@ export type FormParameterTypes =
|
||||
| "ftpfile"
|
||||
| "upload"
|
||||
| "rules"
|
||||
| "data_dialog";
|
||||
| "data_dialog"
|
||||
| "tags";
|
||||
|
||||
@@ -17,6 +17,8 @@ interface StatelessTagsProps {
|
||||
clickable?: boolean;
|
||||
useToggleLink?: boolean;
|
||||
maxVisibleTags?: number;
|
||||
placeholder?: string;
|
||||
noPadding?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<StatelessTagsProps>(), {
|
||||
@@ -25,6 +27,7 @@ const props = withDefaults(defineProps<StatelessTagsProps>(), {
|
||||
clickable: false,
|
||||
useToggleLink: true,
|
||||
maxVisibleTags: 5,
|
||||
placeholder: "Add Tags",
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -113,12 +116,12 @@ function onTagClicked(tag: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="stateless-tags px-1">
|
||||
<div class="stateless-tags" :class="{ 'px-1': !props.noPadding }">
|
||||
<Multiselect
|
||||
v-if="!disabled"
|
||||
ref="multiselectElement"
|
||||
placeholder="Add Tags"
|
||||
open-direction="bottom"
|
||||
:placeholder="props.placeholder"
|
||||
:value="tags"
|
||||
:options="userTags"
|
||||
:multiple="true"
|
||||
@@ -145,7 +148,7 @@ function onTagClicked(tag: string) {
|
||||
|
||||
<template v-slot:caret>
|
||||
<b-button v-if="!editing" class="toggle-button" variant="link" tabindex="-1" @click="openMultiselect">
|
||||
Add Tags
|
||||
{{ props.placeholder }}
|
||||
<FontAwesomeIcon icon="fa-tags" />
|
||||
</b-button>
|
||||
</template>
|
||||
@@ -173,7 +176,7 @@ function onTagClicked(tag: string) {
|
||||
</template>
|
||||
</Multiselect>
|
||||
|
||||
<div v-else class="pl-1 pb-2">
|
||||
<div v-else :class="{ 'pl-1': !props.noPadding, 'pb-2': !props.noPadding }">
|
||||
<div class="d-inline">
|
||||
<Tag
|
||||
v-for="tag in trimmedTags"
|
||||
@@ -327,6 +330,7 @@ function onTagClicked(tag: string) {
|
||||
.multiselect__option--selected {
|
||||
.multiselect-option {
|
||||
color: $brand-primary;
|
||||
background-color: $brand-secondary;
|
||||
|
||||
.info:not(.highlighted) {
|
||||
display: inline-block;
|
||||
|
||||
@@ -21,15 +21,17 @@
|
||||
<FormElement
|
||||
:id="actionNames.TagDatasetAction__tags"
|
||||
:value="formData[actionNames.TagDatasetAction__tags]"
|
||||
:attributes="{ placeholder: 'Enter Tags' }"
|
||||
title="Add Tags"
|
||||
type="text"
|
||||
type="tags"
|
||||
help="This action will set tags for the dataset."
|
||||
@input="onInput" />
|
||||
<FormElement
|
||||
:id="actionNames.RemoveTagDatasetAction__tags"
|
||||
:value="formData[actionNames.RemoveTagDatasetAction__tags]"
|
||||
:attributes="{ placeholder: 'Enter Tags' }"
|
||||
title="Remove Tags"
|
||||
type="text"
|
||||
type="tags"
|
||||
help="This action will remove tags for the dataset."
|
||||
@input="onInput" />
|
||||
<FormCard title="Assign columns" collapsible :expanded.sync="expandedColumn">
|
||||
|
||||
@@ -16,9 +16,15 @@ import { assertDefined, ensureDefined } from "@/utils/assertions";
|
||||
import type { UseElementBoundingReturn, UseScrollReturn } from "@vueuse/core";
|
||||
import { useRelativePosition } from "./composables/relativePosition";
|
||||
import { NULL_COLLECTION_TYPE_DESCRIPTION, type CollectionTypeDescriptor } from "./modules/collectionTypeDescription";
|
||||
import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue";
|
||||
import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
|
||||
type ElementBounding = UnwrapRef<UseElementBoundingReturn>;
|
||||
|
||||
library.add(faPlus, faMinus);
|
||||
|
||||
const props = defineProps<{
|
||||
output: OutputTerminalSource;
|
||||
workflowOutputs: NonNullable<Step["workflow_outputs"]>;
|
||||
@@ -266,28 +272,60 @@ const outputDetails = computed(() => {
|
||||
onBeforeUnmount(() => {
|
||||
stateStore.deleteOutputTerminalPosition(props.stepId, props.output.name);
|
||||
});
|
||||
|
||||
const addTagsAction = computed(() => {
|
||||
return props.postJobActions[`TagDatasetAction${props.output.name}`]?.action_arguments?.tags?.split(",") ?? [];
|
||||
});
|
||||
|
||||
const removeTagsAction = computed(() => {
|
||||
return props.postJobActions[`RemoveTagDatasetAction${props.output.name}`]?.action_arguments?.tags?.split(",") ?? [];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="rowClass" :data-output-name="output.name">
|
||||
<div
|
||||
v-if="showCalloutActiveOutput"
|
||||
v-b-tooltip
|
||||
class="callout-terminal"
|
||||
title="Checked outputs will become primary workflow outputs and are available as subworkflow outputs."
|
||||
@keyup="onToggleActive"
|
||||
@click="onToggleActive">
|
||||
<i :class="['mark-terminal', activeClass]" />
|
||||
<div class="d-flex" :class="rowClass" :data-output-name="output.name">
|
||||
<div class="d-flex flex-column w-100">
|
||||
<div class="d-flex flex-row">
|
||||
<div
|
||||
v-if="showCalloutActiveOutput"
|
||||
v-b-tooltip
|
||||
class="callout-terminal"
|
||||
title="Checked outputs will become primary workflow outputs and are available as subworkflow outputs."
|
||||
@keyup="onToggleActive"
|
||||
@click="onToggleActive">
|
||||
<i :class="['mark-terminal', activeClass]" />
|
||||
</div>
|
||||
<div
|
||||
v-if="showCalloutVisible"
|
||||
v-b-tooltip
|
||||
class="callout-terminal"
|
||||
:title="visibleHint"
|
||||
@keyup="onToggleVisible"
|
||||
@click="onToggleVisible">
|
||||
<i :class="['mark-terminal', visibleClass]" />
|
||||
</div>
|
||||
{{ label }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="addTagsAction.length > 0"
|
||||
v-b-tooltip.left
|
||||
class="d-flex align-items-center overflow-x-hidden"
|
||||
title="These tags will be added to the output dataset">
|
||||
<FontAwesomeIcon icon="fa-plus" class="mr-1" />
|
||||
<StatelessTags disabled no-padding :value="addTagsAction" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="removeTagsAction.length > 0"
|
||||
v-b-tooltip.left
|
||||
class="d-flex align-items-center overflow-x-hidden"
|
||||
title="These tags will be removed from the output dataset">
|
||||
<FontAwesomeIcon icon="fa-minus" class="mr-1" />
|
||||
<StatelessTags disabled no-padding :value="removeTagsAction" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showCalloutVisible"
|
||||
v-b-tooltip
|
||||
class="callout-terminal"
|
||||
:title="visibleHint"
|
||||
@keyup="onToggleVisible"
|
||||
@click="onToggleVisible">
|
||||
<i :class="['mark-terminal', visibleClass]" />
|
||||
</div>
|
||||
{{ label }}
|
||||
|
||||
<draggable-wrapper
|
||||
:id="id"
|
||||
ref="el"
|
||||
|
||||
@@ -436,3 +436,7 @@ $ui-margin-horizontal-large: $margin-v * 2;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-x-hidden {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
@@ -744,11 +744,19 @@ workflow_editor:
|
||||
type: xpath
|
||||
selector: >
|
||||
//li[@class="multiselect__element"]//span[text()="${datatype}"]
|
||||
add_tags:
|
||||
add_tags_button:
|
||||
type: xpath
|
||||
selector: >
|
||||
//div[@data-label='Add Tags']//button
|
||||
add_tags_input:
|
||||
type: xpath
|
||||
selector: >
|
||||
//div[@data-label='Add Tags']//input
|
||||
remove_tags:
|
||||
remove_tags_button:
|
||||
type: xpath
|
||||
selector: >
|
||||
//div[@data-label='Remove Tags']//button
|
||||
remove_tags_input:
|
||||
type: xpath
|
||||
selector: >
|
||||
//div[@data-label='Remove Tags']//input
|
||||
|
||||
@@ -688,8 +688,10 @@ steps:
|
||||
editor.change_datatype.wait_for_and_click()
|
||||
editor.select_datatype_text_search.wait_for_and_send_keys("bam")
|
||||
editor.select_datatype(datatype="bam").wait_for_and_click()
|
||||
self.set_text_element(editor.add_tags, "#crazynewtag")
|
||||
self.set_text_element(editor.remove_tags, "#oldboringtag")
|
||||
editor.add_tags_button.wait_for_and_click()
|
||||
editor.add_tags_input.wait_for_and_send_keys("#crazynewtag" + Keys.ENTER + Keys.ESCAPE)
|
||||
editor.remove_tags_button.wait_for_and_click()
|
||||
editor.remove_tags_input.wait_for_and_send_keys("#oldboringtag" + Keys.ENTER + Keys.ESCAPE)
|
||||
self.sleep_for(self.wait_types.UX_RENDER)
|
||||
cat_node.clone.wait_for_and_click()
|
||||
editor.label_input.wait_for_and_send_keys("cloned label")
|
||||
|
||||
Reference in New Issue
Block a user