mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 02:04:04 +08:00
Merge pull request #20290 from guerler/adjust_dataset_view
[25.0] Touch up Dataset View
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { computed } from "vue";
|
||||
|
||||
import { STATES } from "@/components/History/Content/model/states";
|
||||
import { useDatasetStore } from "@/stores/datasetStore";
|
||||
|
||||
const datasetStore = useDatasetStore();
|
||||
|
||||
const props = defineProps<{
|
||||
datasetId: string;
|
||||
}>();
|
||||
|
||||
const dataset = computed(() => datasetStore.getDataset(props.datasetId));
|
||||
const contentState = computed(() => {
|
||||
const state = dataset.value && dataset.value.state;
|
||||
return state && STATES[state] ? STATES[state] : null;
|
||||
});
|
||||
const contentCls = computed(() => {
|
||||
const status = contentState.value && contentState.value.status;
|
||||
if (!status) {
|
||||
return `alert-success`;
|
||||
} else {
|
||||
return `alert-${status}`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="dataset && contentState" class="rounded px-2 py-1 ml-2" :class="contentCls">
|
||||
<span v-if="contentState.icon" class="mr-1">
|
||||
<FontAwesomeIcon fixed-width :icon="contentState.icon" :spin="contentState.spin" />
|
||||
</span>
|
||||
{{ contentState.text || dataset.state || "n/a" }}
|
||||
</span>
|
||||
</template>
|
||||
@@ -226,13 +226,6 @@ describe("DatasetView", () => {
|
||||
expect(wrapper.props().tab).toBe("edit");
|
||||
});
|
||||
|
||||
it("redirects invalid tabs to preview", async () => {
|
||||
const wrapper = await mountDatasetView("invalid_tab");
|
||||
|
||||
const router = wrapper.vm.$router;
|
||||
expect(router.replace).toHaveBeenCalledWith(`/datasets/${DATASET_ID}`);
|
||||
});
|
||||
|
||||
it("includes preview tab for dataset viewing", async () => {
|
||||
const wrapper = await mountDatasetView("preview");
|
||||
|
||||
@@ -245,13 +238,6 @@ describe("DatasetView", () => {
|
||||
});
|
||||
|
||||
describe("Error state handling", () => {
|
||||
it("redirects error tab for normal datasets", async () => {
|
||||
const wrapper = await mountDatasetView("error");
|
||||
|
||||
const router = wrapper.vm.$router;
|
||||
expect(router.replace).toHaveBeenCalledWith(`/datasets/${DATASET_ID}`);
|
||||
});
|
||||
|
||||
it("shows error tab for datasets with error state", async () => {
|
||||
const wrapper = await mountDatasetView("error", { dataset: errorDataset });
|
||||
|
||||
|
||||
@@ -1,199 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { BLink, BTab, BTabs } from "bootstrap-vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router/composables";
|
||||
import { BNav, BNavItem } from "bootstrap-vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
|
||||
import { STATES } from "@/components/History/Content/model/states";
|
||||
import { usePersistentToggle } from "@/composables/persistentToggle";
|
||||
import { useDatasetStore } from "@/stores/datasetStore";
|
||||
import { useDatatypeVisualizationsStore } from "@/stores/datatypeVisualizationsStore";
|
||||
import { withPrefix } from "@/utils/redirect";
|
||||
|
||||
import Heading from "../Common/Heading.vue";
|
||||
import DatasetError from "../DatasetInformation/DatasetError.vue";
|
||||
import LoadingSpan from "../LoadingSpan.vue";
|
||||
import DatasetState from "./DatasetState.vue";
|
||||
import Heading from "@/components/Common/Heading.vue";
|
||||
import DatasetAttributes from "@/components/DatasetInformation/DatasetAttributes.vue";
|
||||
import DatasetDetails from "@/components/DatasetInformation/DatasetDetails.vue";
|
||||
import VisualizationsList from "@/components/Visualizations/Index.vue";
|
||||
import VisualizationFrame from "@/components/Visualizations/VisualizationFrame.vue";
|
||||
|
||||
library.add(faChevronUp, faChevronDown);
|
||||
import CenterFrame from "@/entry/analysis/modules/CenterFrame.vue";
|
||||
|
||||
const datasetStore = useDatasetStore();
|
||||
const datatypeVisualizationsStore = useDatatypeVisualizationsStore();
|
||||
const router = useRouter();
|
||||
const iframeLoading = ref(true);
|
||||
|
||||
// Use persistent toggle for header state
|
||||
const { toggled: headerCollapsed, toggle: toggleHeaderCollapse } = usePersistentToggle("dataset-header-collapsed");
|
||||
const headerState = computed(() => (headerCollapsed.value ? "closed" : "open"));
|
||||
|
||||
const props = defineProps({
|
||||
datasetId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
tab: {
|
||||
type: String,
|
||||
default: "preview",
|
||||
},
|
||||
interface Props {
|
||||
datasetId: string;
|
||||
tab?: "details" | "edit" | "error" | "preview" | "visualize";
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
tab: "preview",
|
||||
});
|
||||
|
||||
const TABS = {
|
||||
PREVIEW: "preview",
|
||||
VISUALIZE: "visualize",
|
||||
DETAILS: "details",
|
||||
EDIT: "edit",
|
||||
ERROR: "error",
|
||||
} as const;
|
||||
|
||||
type TabName = (typeof TABS)[keyof typeof TABS];
|
||||
const TAB_VALUES = Object.values(TABS) as TabName[];
|
||||
|
||||
const activeTab = ref<TabName>(TABS.PREVIEW);
|
||||
const preferredVisualization = ref<string | null>(null);
|
||||
const iframeLoading = ref(true);
|
||||
const preferredVisualization = ref<string>();
|
||||
|
||||
const dataset = computed(() => datasetStore.getDataset(props.datasetId));
|
||||
const headerState = computed(() => (headerCollapsed.value ? "closed" : "open"));
|
||||
const isLoading = computed(() => datasetStore.isLoadingDataset(props.datasetId));
|
||||
|
||||
// Compute display URL based on whether we have a preferred visualization
|
||||
const displayUrl = computed(() => {
|
||||
// Standard preview display URL
|
||||
if (!preferredVisualization.value) {
|
||||
return withPrefix(`/datasets/${props.datasetId}/display/?preview=true`);
|
||||
}
|
||||
|
||||
// Use preferred visualization
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const showError = computed(
|
||||
() => dataset.value && (dataset.value.state === "error" || dataset.value.state === "failed_metadata")
|
||||
);
|
||||
|
||||
const contentState = computed(() => {
|
||||
return dataset.value && STATES[dataset.value.state] ? STATES[dataset.value.state] : null;
|
||||
});
|
||||
|
||||
const stateText = computed(() => (dataset.value && dataset.value.state) || "");
|
||||
|
||||
const hasStateIcon = computed(() => {
|
||||
return contentState.value && contentState.value.icon;
|
||||
});
|
||||
|
||||
function toggleHeader() {
|
||||
toggleHeaderCollapse();
|
||||
}
|
||||
|
||||
// Check if the dataset has a preferred visualization by datatype
|
||||
async function checkPreferredVisualization() {
|
||||
if (!dataset.value || !dataset.value.file_ext) {
|
||||
preferredVisualization.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const mapping = await datatypeVisualizationsStore.getPreferredVisualizationForDatatype(dataset.value.file_ext);
|
||||
if (mapping) {
|
||||
preferredVisualization.value = mapping.visualization;
|
||||
} else {
|
||||
preferredVisualization.value = null;
|
||||
if (dataset.value && dataset.value.file_ext) {
|
||||
try {
|
||||
const mapping = await datatypeVisualizationsStore.getPreferredVisualizationForDatatype(
|
||||
dataset.value.file_ext
|
||||
);
|
||||
if (mapping) {
|
||||
preferredVisualization.value = mapping.visualization;
|
||||
} else {
|
||||
preferredVisualization.value = undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
preferredVisualization.value = undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
preferredVisualization.value = null;
|
||||
} else {
|
||||
preferredVisualization.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function onTabChange(tabName: TabName) {
|
||||
if (!TAB_VALUES.includes(tabName)) {
|
||||
console.error("Invalid tab name received:", tabName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tabName === TABS.ERROR && !showError.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tabName === TABS.PREVIEW) {
|
||||
iframeLoading.value = true;
|
||||
}
|
||||
|
||||
const basePath = `/datasets/${props.datasetId}`;
|
||||
const targetPath = tabName === TABS.PREVIEW ? basePath : `${basePath}/${tabName}`;
|
||||
|
||||
router.push(targetPath);
|
||||
}
|
||||
|
||||
function setActiveTabFromProp() {
|
||||
const currentTabName = props.tab as string;
|
||||
|
||||
if (!TAB_VALUES.includes(currentTabName as TabName)) {
|
||||
if (currentTabName !== TABS.PREVIEW) {
|
||||
const previewPath = `/datasets/${props.datasetId}`;
|
||||
router.replace(previewPath);
|
||||
}
|
||||
activeTab.value = TABS.PREVIEW;
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTabName === TABS.ERROR && !showError.value) {
|
||||
if (currentTabName === TABS.ERROR) {
|
||||
const previewPath = `/datasets/${props.datasetId}`;
|
||||
router.replace(previewPath);
|
||||
}
|
||||
activeTab.value = TABS.PREVIEW;
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeTab.value !== currentTabName) {
|
||||
activeTab.value = currentTabName as TabName;
|
||||
}
|
||||
}
|
||||
|
||||
// Watch the 'tab' prop instead of the route path
|
||||
watch(() => props.tab, setActiveTabFromProp, { immediate: true });
|
||||
|
||||
// Reset iframe loading when datasetId changes
|
||||
watch(
|
||||
() => props.datasetId,
|
||||
() => {
|
||||
if (activeTab.value === TABS.PREVIEW) {
|
||||
iframeLoading.value = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Watch for changes to the dataset to check for preferred visualizations
|
||||
watch(() => dataset.value?.file_ext, checkPreferredVisualization);
|
||||
|
||||
onMounted(() => {
|
||||
// Check for preferred visualization when component is mounted
|
||||
if (dataset.value) {
|
||||
checkPreferredVisualization();
|
||||
}
|
||||
});
|
||||
watch(() => dataset.value?.file_ext, checkPreferredVisualization, { immediate: true });
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="dataset && !isLoading" class="dataset-view d-flex flex-column">
|
||||
<header :key="`dataset-header-${dataset.id}`" class="dataset-header flex-shrink-0">
|
||||
<Heading h1 separator :collapse="headerState" class="dataset-name" @click="toggleHeader">
|
||||
<div class="item-identifier">
|
||||
<span class="hid-box">{{ dataset.hid }}:</span>
|
||||
<span class="dataset-title">{{ dataset.name }}</span>
|
||||
<span v-if="contentState" class="state-pill ml-2" :class="`state-${dataset.state}`">
|
||||
<span v-if="hasStateIcon" class="state-icon mr-1">
|
||||
<FontAwesomeIcon fixed-width :icon="contentState.icon" :spin="contentState.spin" />
|
||||
</span>
|
||||
{{ stateText }}
|
||||
</span>
|
||||
</div>
|
||||
</Heading>
|
||||
|
||||
<transition name="header">
|
||||
<div v-if="headerState === 'open'" class="header-details">
|
||||
<template>
|
||||
<LoadingSpan v-if="isLoading || !dataset" message="Loading dataset details" />
|
||||
<div v-else class="dataset-view d-flex flex-column h-100">
|
||||
<header :key="`dataset-header-${dataset.id}`" class="dataset-header flex-shrink-0">
|
||||
<div class="d-flex">
|
||||
<Heading
|
||||
h1
|
||||
separator
|
||||
inline
|
||||
size="lg"
|
||||
class="flex-grow-1"
|
||||
:collapse="headerState"
|
||||
@click="toggleHeaderCollapse">
|
||||
{{ dataset?.hid }}: <span class="font-weight-bold">{{ dataset?.name }}</span>
|
||||
<span class="dataset-state-header">
|
||||
<DatasetState :dataset-id="datasetId" />
|
||||
</span>
|
||||
</Heading>
|
||||
</div>
|
||||
<transition v-if="dataset" name="header">
|
||||
<div v-show="headerState === 'open'" class="header-details">
|
||||
<div v-if="dataset.misc_blurb" class="blurb">
|
||||
<span class="value">{{ dataset.misc_blurb }}</span>
|
||||
</div>
|
||||
@@ -206,9 +96,9 @@ onMounted(() => {
|
||||
<BLink
|
||||
class="value font-weight-bold"
|
||||
data-label="Database/Build"
|
||||
@click="onTabChange(TABS.EDIT)"
|
||||
>{{ dataset.genome_build }}</BLink
|
||||
>
|
||||
:to="`/datasets/${datasetId}/edit`">
|
||||
{{ dataset.genome_build }}
|
||||
</BLink>
|
||||
</span>
|
||||
<div v-if="dataset.misc_info" class="info">
|
||||
<span class="value">{{ dataset.misc_info }}</span>
|
||||
@@ -216,153 +106,56 @@ onMounted(() => {
|
||||
</div>
|
||||
</transition>
|
||||
</header>
|
||||
|
||||
<!-- Tab container - make it grow to fill remaining space and handle overflow -->
|
||||
<div class="dataset-tabs-container flex-grow-1 overflow-hidden">
|
||||
<BTabs
|
||||
:key="`tabs-${datasetId}`"
|
||||
pills
|
||||
card
|
||||
lazy
|
||||
class="h-100 d-flex flex-column"
|
||||
:value="TAB_VALUES.indexOf(activeTab)"
|
||||
@input="
|
||||
(tabIndex) => {
|
||||
const newTab = TAB_VALUES[tabIndex];
|
||||
if (newTab) onTabChange(newTab);
|
||||
}
|
||||
">
|
||||
<BTab title="Preview" class="iframe-card" data-test-id="dataset-preview-tab">
|
||||
<div class="preview-container position-relative h-100">
|
||||
<!-- Loading indicator for iframe -->
|
||||
<div v-if="iframeLoading" class="iframe-loading">
|
||||
<LoadingSpan message="Loading preview" />
|
||||
</div>
|
||||
|
||||
<!-- Show preferred visualization if available -->
|
||||
<template v-if="preferredVisualization">
|
||||
<VisualizationFrame
|
||||
:dataset-id="datasetId"
|
||||
:visualization="preferredVisualization"
|
||||
@loaded="iframeLoading = false" />
|
||||
</template>
|
||||
<!-- Default iframe preview otherwise -->
|
||||
<iframe
|
||||
v-else
|
||||
:src="displayUrl"
|
||||
title="galaxy dataset display frame"
|
||||
class="dataset-preview-iframe"
|
||||
frameborder="0"
|
||||
@load="iframeLoading = false"></iframe>
|
||||
</div>
|
||||
</BTab>
|
||||
<BTab title="Visualize" :title-link-attributes="{ title: 'Visualize' }">
|
||||
<VisualizationsList :dataset-id="datasetId" />
|
||||
</BTab>
|
||||
<BTab title="Details" :title-link-attributes="{ title: 'Details' }">
|
||||
<DatasetDetails :dataset-id="datasetId" />
|
||||
</BTab>
|
||||
<BTab title="Edit" :title-link-attributes="{ title: 'Edit' }">
|
||||
<DatasetAttributes :dataset-id="datasetId" />
|
||||
</BTab>
|
||||
<BTab v-if="showError" title="Error Report" :title-link-attributes="{ title: 'Error' }">
|
||||
<DatasetError :dataset-id="datasetId" />
|
||||
</BTab>
|
||||
</BTabs>
|
||||
<BNav pills class="my-2 p-2 bg-light border-bottom">
|
||||
<BNavItem title="Preview" :active="tab === 'preview'" :to="`/datasets/${datasetId}/preview`">
|
||||
Preview
|
||||
</BNavItem>
|
||||
<BNavItem
|
||||
v-if="!showError"
|
||||
title="Visualize"
|
||||
:active="tab === 'visualize'"
|
||||
:to="`/datasets/${datasetId}/visualize`">
|
||||
Visualize
|
||||
</BNavItem>
|
||||
<BNavItem title="Details" :active="tab === 'details'" :to="`/datasets/${datasetId}/details`">
|
||||
Details
|
||||
</BNavItem>
|
||||
<BNavItem title="Edit" :active="tab === 'edit'" :to="`/datasets/${datasetId}/edit`">Edit</BNavItem>
|
||||
<BNavItem v-if="showError" title="Error" :active="tab === 'error'" :to="`/datasets/${datasetId}/error`">
|
||||
Error
|
||||
</BNavItem>
|
||||
</BNav>
|
||||
<div v-if="tab === 'preview'" class="h-100">
|
||||
<VisualizationFrame
|
||||
v-if="preferredVisualization"
|
||||
:dataset-id="datasetId"
|
||||
:visualization="preferredVisualization"
|
||||
@load="iframeLoading = false" />
|
||||
<CenterFrame
|
||||
v-else
|
||||
:src="`/datasets/${datasetId}/display/?preview=True`"
|
||||
:is_preview="true"
|
||||
@load="iframeLoading = false" />
|
||||
</div>
|
||||
<div v-else-if="tab === 'visualize'" class="d-flex flex-column overflow-hidden overflow-y">
|
||||
<VisualizationsList :dataset-id="datasetId" />
|
||||
</div>
|
||||
<div v-else-if="tab === 'edit'" class="d-flex flex-column overflow-hidden overflow-y mt-2">
|
||||
<DatasetAttributes :dataset-id="datasetId" />
|
||||
</div>
|
||||
<div v-else-if="tab === 'details'" class="d-flex flex-column overflow-hidden overflow-y mt-2">
|
||||
<DatasetDetails :dataset-id="datasetId" />
|
||||
</div>
|
||||
<div v-else-if="tab === 'error'" class="d-flex flex-column overflow-hidden overflow-y mt-2">
|
||||
<DatasetError :dataset-id="datasetId" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Loading state indicator -->
|
||||
<div v-else class="loading-message">
|
||||
<LoadingSpan message="Loading dataset details" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~bootstrap/scss/_functions.scss";
|
||||
@import "theme/blue.scss";
|
||||
|
||||
.dataset-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dataset-header {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.item-identifier {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dataset-name {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.hid-box {
|
||||
margin-right: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dataset-title {
|
||||
font-weight: bold;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.state-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.state-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.85rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.state-running,
|
||||
.state-upload,
|
||||
.state-setting_metadata,
|
||||
.state-new,
|
||||
.state-new_populated_state,
|
||||
.state-inaccessible {
|
||||
background-color: $state-running-bg;
|
||||
color: $state-warning-text;
|
||||
}
|
||||
|
||||
.state-paused,
|
||||
.state-deferred {
|
||||
background-color: $state-info-bg;
|
||||
color: $state-info-text;
|
||||
}
|
||||
|
||||
.state-queued,
|
||||
.state-placeholder {
|
||||
background-color: $state-default-bg;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.state-ok,
|
||||
.state-empty {
|
||||
background-color: $state-success-bg;
|
||||
color: $state-success-text;
|
||||
}
|
||||
|
||||
.state-error,
|
||||
.state-failed_metadata,
|
||||
.state-discarded,
|
||||
.state-failed_populated_state {
|
||||
background-color: $state-danger-bg;
|
||||
color: $state-danger-text;
|
||||
}
|
||||
|
||||
.header-details {
|
||||
margin-top: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
max-height: 500px;
|
||||
opacity: 1;
|
||||
@@ -379,64 +172,8 @@ onMounted(() => {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dataset-tabs-container {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.dataset-tabs-container :deep(.nav-tabs) {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dataset-tabs-container :deep(.tab-content) {
|
||||
flex-grow: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dataset-tabs-container :deep(.tab-pane) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dataset-tabs-container :deep(.iframe-card) {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.dataset-preview-iframe {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iframe-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $brand-light;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
.dataset-state-header {
|
||||
font-size: $h5-font-size;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -92,7 +92,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div aria-labelledby="dataset-attributes-heading">
|
||||
<Heading id="dataset-attributes-heading" h1 separator inline size="lg">
|
||||
<Heading id="dataset-attributes-heading" h1 separator inline size="md">
|
||||
{{ localize("Edit Dataset Attributes") }}
|
||||
</Heading>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { type HDADetailed } from "@/api";
|
||||
import { withPrefix } from "@/utils/redirect";
|
||||
import { bytesToString } from "@/utils/utils";
|
||||
|
||||
import Heading from "../Common/Heading.vue";
|
||||
import HelpText from "../Help/HelpText.vue";
|
||||
import DatasetHashes from "@/components/DatasetInformation/DatasetHashes.vue";
|
||||
import DatasetSources from "@/components/DatasetInformation/DatasetSources.vue";
|
||||
@@ -20,8 +21,9 @@ defineProps<Props>();
|
||||
|
||||
<template>
|
||||
<div v-if="dataset">
|
||||
<h2 class="h-md">Dataset Information</h2>
|
||||
|
||||
<Heading id="dataset-information-heading" v-localize h1 separator inline size="md">
|
||||
Dataset Information
|
||||
</Heading>
|
||||
<table id="dataset-details" class="tabletip info_data_table">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
@@ -91,7 +91,7 @@ export const STATES: StateMap = {
|
||||
/** the tool producing this dataset has errored */
|
||||
error: {
|
||||
status: "danger",
|
||||
text: "An error occurred with this dataset:",
|
||||
text: "An error occurred with this dataset.",
|
||||
icon: "exclamation-triangle",
|
||||
},
|
||||
/** metadata discovery/setting failed or errored (but otherwise ok) */
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
import { withPrefix } from "@/utils/redirect";
|
||||
|
||||
const emit = defineEmits(["loaded"]);
|
||||
import LoadingSpan from "@/components/LoadingSpan.vue";
|
||||
|
||||
const emit = defineEmits(["load"]);
|
||||
|
||||
export interface Props {
|
||||
datasetId?: string;
|
||||
@@ -13,6 +15,8 @@ export interface Props {
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const isLoading = ref(true);
|
||||
|
||||
const srcWithRoot = computed(() => {
|
||||
let url = "";
|
||||
if (props.visualization === "trackster") {
|
||||
@@ -31,16 +35,43 @@ const srcWithRoot = computed(() => {
|
||||
|
||||
return withPrefix(url);
|
||||
});
|
||||
|
||||
function handleLoad() {
|
||||
isLoading.value = false;
|
||||
emit("load");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<iframe
|
||||
id="galaxy_visualization"
|
||||
:src="srcWithRoot"
|
||||
class="center-frame"
|
||||
frameborder="0"
|
||||
title="galaxy visualization frame"
|
||||
width="100%"
|
||||
height="100%"
|
||||
@load="emit('loaded')" />
|
||||
<div class="position-relative h-100">
|
||||
<div v-if="isLoading" class="iframe-loading bg-light">
|
||||
<LoadingSpan message="Loading preview" />
|
||||
</div>
|
||||
<iframe
|
||||
id="galaxy_visualization"
|
||||
:src="srcWithRoot"
|
||||
class="center-frame"
|
||||
frameborder="0"
|
||||
title="galaxy visualization frame"
|
||||
width="100%"
|
||||
height="100%"
|
||||
@load="handleLoad" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.iframe-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
opacity: 0.9;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,6 @@ import CollectionEditView from "components/Collections/common/CollectionEditView
|
||||
import DatasetList from "components/Dataset/DatasetList";
|
||||
import DatasetView from "components/Dataset/DatasetView";
|
||||
import DatasetDetails from "components/DatasetInformation/DatasetDetails";
|
||||
import DatasetError from "components/DatasetInformation/DatasetError";
|
||||
import FormGeneric from "components/Form/FormGeneric";
|
||||
import GalaxyWizard from "components/GalaxyWizard";
|
||||
import HelpTerm from "components/Help/HelpTerm";
|
||||
@@ -50,7 +49,6 @@ import WorkflowCreate from "components/Workflow/WorkflowCreate";
|
||||
import WorkflowExport from "components/Workflow/WorkflowExport";
|
||||
import WorkflowImport from "components/Workflow/WorkflowImport";
|
||||
import Analysis from "entry/analysis/modules/Analysis";
|
||||
import CenterFrame from "entry/analysis/modules/CenterFrame";
|
||||
import Home from "entry/analysis/modules/Home";
|
||||
import Login from "entry/analysis/modules/Login";
|
||||
import WorkflowEditorModule from "entry/analysis/modules/WorkflowEditor";
|
||||
@@ -234,14 +232,6 @@ export function getRouter(Galaxy) {
|
||||
path: "datasets/list",
|
||||
component: DatasetList,
|
||||
},
|
||||
{
|
||||
path: "datasets/:datasetId/preview",
|
||||
component: CenterFrame,
|
||||
props: (route) => ({
|
||||
src: `/datasets/${route.params.datasetId}/display/?preview=True`,
|
||||
isPreview: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: "datasets/:datasetId/report",
|
||||
component: ToolReport,
|
||||
@@ -253,11 +243,6 @@ export function getRouter(Galaxy) {
|
||||
component: DatasetDetails,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "datasets/:datasetId/error",
|
||||
component: DatasetError,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
// Consolidated route for dataset view with optional tab
|
||||
// Handles /datasets/{id}, /datasets/{id}/details, /datasets/{id}/visualize, etc.
|
||||
|
||||
@@ -157,9 +157,9 @@ object_store_details:
|
||||
dataset_view:
|
||||
selectors:
|
||||
_: .dataset-view
|
||||
edit_tab: "a.nav-link[title='Edit']"
|
||||
details_tab: "a.nav-link[title='Details']"
|
||||
visualize_tab: "a.nav-link[title='Visualize']"
|
||||
edit_tab: ".nav-item[title='Edit'] > a.nav-link"
|
||||
details_tab: ".nav-item[title='Details'] > a.nav-link"
|
||||
visualize_tab: ".nav-item[title='Visualize'] > a.nav-link"
|
||||
|
||||
history_panel:
|
||||
menu:
|
||||
|
||||
@@ -1991,7 +1991,7 @@ class NavigatesGalaxy(HasDriver):
|
||||
|
||||
# Find and click the Edit tab - using a more reliable selector
|
||||
# BVue generates '.nav-item a' elements with a title attribute matching the tab title
|
||||
edit_tab_button = self.wait_for_selector_clickable("a.nav-link[title='Edit']")
|
||||
edit_tab_button = self.wait_for_selector_clickable(".nav-item[title='Edit'] > a.nav-link")
|
||||
edit_tab_button.click()
|
||||
|
||||
# Wait for the edit attributes panel to be visible
|
||||
@@ -2006,14 +2006,14 @@ class NavigatesGalaxy(HasDriver):
|
||||
def show_dataset_details(self, hid):
|
||||
self.display_dataset(hid)
|
||||
# Find and click the Details tab
|
||||
details_tab_button = self.wait_for_selector_clickable("a.nav-link[title='Details']")
|
||||
details_tab_button = self.wait_for_selector_clickable(".nav-item[title='Details'] > a.nav-link")
|
||||
details_tab_button.click()
|
||||
self.components.dataset_details._.wait_for_visible()
|
||||
|
||||
def show_dataset_visualizations(self, hid):
|
||||
self.display_dataset(hid)
|
||||
# Find and click the Visualize tab
|
||||
visualize_tab_button = self.wait_for_selector_clickable("a.nav-link[title='Visualize']")
|
||||
visualize_tab_button = self.wait_for_selector_clickable(".nav-item[title='Visualize'] > a.nav-link")
|
||||
visualize_tab_button.click()
|
||||
|
||||
def history_panel_item_view_dataset_details(self, hid):
|
||||
|
||||
@@ -286,6 +286,7 @@ def app_pair(global_conf, load_app_kwds=None, wsgi_preflight=True, **kwargs):
|
||||
webapp.add_client_route("/datasets/{dataset_id}/preview")
|
||||
webapp.add_client_route("/datasets/{dataset_id}/report")
|
||||
webapp.add_client_route("/datasets/{dataset_id}/show_params")
|
||||
webapp.add_client_route("/datasets/{dataset_id}/visualize")
|
||||
webapp.add_client_route("/datasets/{dataset_id}")
|
||||
webapp.add_client_route("/collection/{collection_id}/edit")
|
||||
webapp.add_client_route("/collection/new_list")
|
||||
|
||||
Reference in New Issue
Block a user