Loading client/src/components/Form/Elements/FormData/FormData.test.js +4 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ import { mount } from "@vue/test-utils"; import { PiniaVuePlugin } from "pinia"; import { dispatchEvent, getLocalVue } from "tests/jest/helpers"; import { testDatatypesMapper } from "@/components/Datatypes/test_fixtures"; import { useDatatypesMapperStore } from "@/stores/datatypesMapperStore"; import { useEventStore } from "@/stores/eventStore"; import MountTarget from "./FormData.vue"; Loading @@ -15,6 +17,8 @@ let eventStore; function createTarget(propsData) { const pinia = createTestingPinia({ stubActions: false }); eventStore = useEventStore(); const datatypesStore = useDatatypesMapperStore(); datatypesStore.datatypesMapper = testDatatypesMapper; return mount(MountTarget, { localVue, propsData, Loading client/src/components/Form/Elements/FormData/FormData.vue +45 −11 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import { BAlert, BButton, BButtonGroup, BCollapse, BFormCheckbox, BTooltip } fro import { computed, onMounted, type Ref, ref, watch } from "vue"; import { getGalaxyInstance } from "@/app"; import { useDatatypesMapper } from "@/composables/datatypesMapper"; import { useUid } from "@/composables/utils/uid"; import { type EventData, useEventStore } from "@/stores/eventStore"; import { orList } from "@/utils/strings"; Loading Loading @@ -51,8 +52,9 @@ const props = withDefaults( ); const eventStore = useEventStore(); const { datatypesMapper } = useDatatypesMapper(); const $emit = defineEmits(["input"]); const $emit = defineEmits(["input", "alert"]); // Determines wether values should be processed as linked or unlinked const currentLinked = ref(true); Loading Loading @@ -302,6 +304,10 @@ function getSourceType(val: DataOption) { function handleIncoming(incoming: Record<string, unknown>, partial = true) { if (incoming) { const values = Array.isArray(incoming) ? incoming : [incoming]; const extensions = values.map((v) => v.extension || v.elements_datatypes).filter((v) => (v ? true : false)); if (!canAcceptDatatype(extensions)) { return false; } if (values.length > 0) { const incomingValues: Array<DataOption> = []; values.forEach((v) => { Loading Loading @@ -349,6 +355,7 @@ function handleIncoming(incoming: Record<string, unknown>, partial = true) { } } } return true; } /** Loading @@ -372,10 +379,36 @@ function onBrowse() { } } function canAcceptDatatype(itemDatatypes: string | Array<string>) { if (!(props.extensions?.length > 0)) { return true; } let datatypes: Array<string>; if (!Array.isArray(itemDatatypes)) { datatypes = [itemDatatypes]; } else { datatypes = itemDatatypes; } const incompatibleItem = datatypes.find( (extension) => !datatypesMapper.value?.isSubTypeOfAny(extension, props.extensions) ); if (incompatibleItem) { return false; } return true; } // Drag/Drop event handlers function onDragEnter(evt: MouseEvent) { const eventData = eventStore.getDragData(); if (eventData) { const extensions = (eventData.extension as string) || (eventData.elements_datatypes as Array<string>); if (!canAcceptDatatype(extensions)) { currentHighlighting.value = "warning"; $emit("alert", `${extensions} is not an acceptable format for this parameter.`); } else { currentHighlighting.value = "success"; } dragTarget.value = evt.target; dragData.value = eventData; } Loading @@ -384,23 +417,24 @@ function onDragEnter(evt: MouseEvent) { function onDragLeave(evt: MouseEvent) { if (dragTarget.value === evt.target) { currentHighlighting.value = null; } } function onDragOver() { if (dragData.value !== null) { currentHighlighting.value = "warning"; $emit("alert", undefined); } } function onDrop() { if (dragData.value) { let accept = false; if (eventStore.multipleDragData) { handleIncoming(Object.values(dragData.value) as any, false); accept = handleIncoming(Object.values(dragData.value) as any, false); } else { handleIncoming(dragData.value); accept = handleIncoming(dragData.value); } if (accept) { currentHighlighting.value = "success"; } else { currentHighlighting.value = "warning"; } $emit("alert", undefined); dragData.value = null; clearHighlighting(); } Loading Loading @@ -468,7 +502,7 @@ const noOptionsWarningMessage = computed(() => { :class="currentHighlighting && `ui-dragover-${currentHighlighting}`" @dragenter.prevent="onDragEnter" @dragleave.prevent="onDragLeave" @dragover.prevent="onDragOver" @dragover.prevent @drop.prevent="onDrop"> <div class="d-flex flex-column"> <BButtonGroup v-if="variant && variant.length > 1" buttons class="align-self-start"> Loading client/src/components/Form/FormElement.test.js +1 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ describe("FormElement", () => { const error = wrapper.find(".ui-form-error-text"); expect(error.text()).toBe("error_text"); await wrapper.setProps({ error: "" }); await wrapper.setProps({ error: undefined }); const no_error = wrapper.findAll(".ui-form-error"); expect(no_error.length).toBe(0); Loading client/src/components/Form/FormElement.vue +15 −6 Original line number Diff line number Diff line Loading @@ -127,7 +127,7 @@ function onConnect() { const isHidden = computed(() => attrs.value["hidden"]); const elementId = computed(() => `form-element-${props.id}`); const hasAlert = computed(() => Boolean(props.error || props.warning)); const hasAlert = computed(() => alerts.value.length > 0); const showPreview = computed(() => (collapsed.value && attrs.value["collapsible_preview"]) || props.disabled); const showField = computed(() => !collapsed.value && !props.disabled); Loading Loading @@ -174,6 +174,16 @@ const isEmpty = computed(() => { const isRequired = computed(() => attrs.value["optional"] === false); const isRequiredType = computed(() => props.type !== "boolean"); const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !== undefined); const formAlert = ref<string>(); const alerts = computed(() => { return [formAlert.value, props.error, props.warning] .filter((v) => v !== undefined && v !== null) .map((v) => linkify(sanitize(v!, { USE_PROFILES: { html: true } }))); }); function onAlert(value: string | undefined) { formAlert.value = value; } </script> <template> Loading @@ -182,11 +192,9 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] ! :id="elementId" class="ui-form-element section-row" :class="{ alert: hasAlert, 'alert-info': hasAlert }"> <div v-if="hasAlert" class="ui-form-error"> <div v-for="(alert, index) in alerts" :key="index" class="ui-form-error"> <FontAwesomeIcon class="mr-1" icon="fa-exclamation" /> <span class="ui-form-error-text" v-html="linkify(sanitize(props.error || props.warning, { USE_PROFILES: { html: true } }))" /> <span class="ui-form-error-text" v-html="alert" /> </div> <div class="ui-form-title"> Loading Loading @@ -288,7 +296,8 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] ! :optional="attrs.optional" :options="attrs.options" :tag="attrs.tag" :type="props.type" /> :type="props.type" @alert="onAlert" /> <FormDrilldown v-else-if="props.type === 'drill_down'" :id="id" Loading Loading
client/src/components/Form/Elements/FormData/FormData.test.js +4 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ import { mount } from "@vue/test-utils"; import { PiniaVuePlugin } from "pinia"; import { dispatchEvent, getLocalVue } from "tests/jest/helpers"; import { testDatatypesMapper } from "@/components/Datatypes/test_fixtures"; import { useDatatypesMapperStore } from "@/stores/datatypesMapperStore"; import { useEventStore } from "@/stores/eventStore"; import MountTarget from "./FormData.vue"; Loading @@ -15,6 +17,8 @@ let eventStore; function createTarget(propsData) { const pinia = createTestingPinia({ stubActions: false }); eventStore = useEventStore(); const datatypesStore = useDatatypesMapperStore(); datatypesStore.datatypesMapper = testDatatypesMapper; return mount(MountTarget, { localVue, propsData, Loading
client/src/components/Form/Elements/FormData/FormData.vue +45 −11 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import { BAlert, BButton, BButtonGroup, BCollapse, BFormCheckbox, BTooltip } fro import { computed, onMounted, type Ref, ref, watch } from "vue"; import { getGalaxyInstance } from "@/app"; import { useDatatypesMapper } from "@/composables/datatypesMapper"; import { useUid } from "@/composables/utils/uid"; import { type EventData, useEventStore } from "@/stores/eventStore"; import { orList } from "@/utils/strings"; Loading Loading @@ -51,8 +52,9 @@ const props = withDefaults( ); const eventStore = useEventStore(); const { datatypesMapper } = useDatatypesMapper(); const $emit = defineEmits(["input"]); const $emit = defineEmits(["input", "alert"]); // Determines wether values should be processed as linked or unlinked const currentLinked = ref(true); Loading Loading @@ -302,6 +304,10 @@ function getSourceType(val: DataOption) { function handleIncoming(incoming: Record<string, unknown>, partial = true) { if (incoming) { const values = Array.isArray(incoming) ? incoming : [incoming]; const extensions = values.map((v) => v.extension || v.elements_datatypes).filter((v) => (v ? true : false)); if (!canAcceptDatatype(extensions)) { return false; } if (values.length > 0) { const incomingValues: Array<DataOption> = []; values.forEach((v) => { Loading Loading @@ -349,6 +355,7 @@ function handleIncoming(incoming: Record<string, unknown>, partial = true) { } } } return true; } /** Loading @@ -372,10 +379,36 @@ function onBrowse() { } } function canAcceptDatatype(itemDatatypes: string | Array<string>) { if (!(props.extensions?.length > 0)) { return true; } let datatypes: Array<string>; if (!Array.isArray(itemDatatypes)) { datatypes = [itemDatatypes]; } else { datatypes = itemDatatypes; } const incompatibleItem = datatypes.find( (extension) => !datatypesMapper.value?.isSubTypeOfAny(extension, props.extensions) ); if (incompatibleItem) { return false; } return true; } // Drag/Drop event handlers function onDragEnter(evt: MouseEvent) { const eventData = eventStore.getDragData(); if (eventData) { const extensions = (eventData.extension as string) || (eventData.elements_datatypes as Array<string>); if (!canAcceptDatatype(extensions)) { currentHighlighting.value = "warning"; $emit("alert", `${extensions} is not an acceptable format for this parameter.`); } else { currentHighlighting.value = "success"; } dragTarget.value = evt.target; dragData.value = eventData; } Loading @@ -384,23 +417,24 @@ function onDragEnter(evt: MouseEvent) { function onDragLeave(evt: MouseEvent) { if (dragTarget.value === evt.target) { currentHighlighting.value = null; } } function onDragOver() { if (dragData.value !== null) { currentHighlighting.value = "warning"; $emit("alert", undefined); } } function onDrop() { if (dragData.value) { let accept = false; if (eventStore.multipleDragData) { handleIncoming(Object.values(dragData.value) as any, false); accept = handleIncoming(Object.values(dragData.value) as any, false); } else { handleIncoming(dragData.value); accept = handleIncoming(dragData.value); } if (accept) { currentHighlighting.value = "success"; } else { currentHighlighting.value = "warning"; } $emit("alert", undefined); dragData.value = null; clearHighlighting(); } Loading Loading @@ -468,7 +502,7 @@ const noOptionsWarningMessage = computed(() => { :class="currentHighlighting && `ui-dragover-${currentHighlighting}`" @dragenter.prevent="onDragEnter" @dragleave.prevent="onDragLeave" @dragover.prevent="onDragOver" @dragover.prevent @drop.prevent="onDrop"> <div class="d-flex flex-column"> <BButtonGroup v-if="variant && variant.length > 1" buttons class="align-self-start"> Loading
client/src/components/Form/FormElement.test.js +1 −1 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ describe("FormElement", () => { const error = wrapper.find(".ui-form-error-text"); expect(error.text()).toBe("error_text"); await wrapper.setProps({ error: "" }); await wrapper.setProps({ error: undefined }); const no_error = wrapper.findAll(".ui-form-error"); expect(no_error.length).toBe(0); Loading
client/src/components/Form/FormElement.vue +15 −6 Original line number Diff line number Diff line Loading @@ -127,7 +127,7 @@ function onConnect() { const isHidden = computed(() => attrs.value["hidden"]); const elementId = computed(() => `form-element-${props.id}`); const hasAlert = computed(() => Boolean(props.error || props.warning)); const hasAlert = computed(() => alerts.value.length > 0); const showPreview = computed(() => (collapsed.value && attrs.value["collapsible_preview"]) || props.disabled); const showField = computed(() => !collapsed.value && !props.disabled); Loading Loading @@ -174,6 +174,16 @@ const isEmpty = computed(() => { const isRequired = computed(() => attrs.value["optional"] === false); const isRequiredType = computed(() => props.type !== "boolean"); const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !== undefined); const formAlert = ref<string>(); const alerts = computed(() => { return [formAlert.value, props.error, props.warning] .filter((v) => v !== undefined && v !== null) .map((v) => linkify(sanitize(v!, { USE_PROFILES: { html: true } }))); }); function onAlert(value: string | undefined) { formAlert.value = value; } </script> <template> Loading @@ -182,11 +192,9 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] ! :id="elementId" class="ui-form-element section-row" :class="{ alert: hasAlert, 'alert-info': hasAlert }"> <div v-if="hasAlert" class="ui-form-error"> <div v-for="(alert, index) in alerts" :key="index" class="ui-form-error"> <FontAwesomeIcon class="mr-1" icon="fa-exclamation" /> <span class="ui-form-error-text" v-html="linkify(sanitize(props.error || props.warning, { USE_PROFILES: { html: true } }))" /> <span class="ui-form-error-text" v-html="alert" /> </div> <div class="ui-form-title"> Loading Loading @@ -288,7 +296,8 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] ! :optional="attrs.optional" :options="attrs.options" :tag="attrs.tag" :type="props.type" /> :type="props.type" @alert="onAlert" /> <FormDrilldown v-else-if="props.type === 'drill_down'" :id="id" Loading