Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revises Markdown Pages and Reports #19226

Draft
wants to merge 14 commits into
base: dev
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions client/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const INSTALL_PLUGIN_BUILD_IDS = [
"phylocanvas",
"plotly",
"venn",
"vitessce",
"vizarr",
]; // todo: derive from XML
const DIST_PLUGIN_BUILD_IDS = ["new_user"];
Expand Down
270 changes: 120 additions & 150 deletions client/src/components/Markdown/Markdown.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,116 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted } from "vue";

Check failure on line 2 in client/src/components/Markdown/Markdown.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Run autofix to sort these imports!

import { useWorkflowStore } from "@/stores/workflowStore";
import { parseMarkdown } from "./parse";

import MarkdownGalaxy from "./Sections/MarkdownGalaxy.vue";
import MarkdownDefault from "./Sections/MarkdownDefault.vue";
import MarkdownVega from "./Sections/MarkdownVega.vue";
import MarkdownVitessce from "./Sections/MarkdownVitessce.vue";

import LoadingSpan from "@/components/LoadingSpan.vue";
import StsDownloadButton from "@/components/StsDownloadButton.vue";

import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faDownload, faEdit } from "@fortawesome/free-solid-svg-icons";

library.add(faDownload, faEdit);

// Props
interface MarkdownConfig {
generate_time?: string;
generate_version?: string;
content?: string;
markdown?: string;
errors?: Array<{ error?: string; line?: string }>;
history_datasets?: Record<string, unknown>;
histories?: Record<string, unknown>;
history_dataset_collections?: Record<string, unknown>;
workflows?: Record<string, unknown>;
invocations?: Record<string, unknown>;
id?: string;
title?: string;
model_class?: string;
}

const props = defineProps<{
markdownConfig: MarkdownConfig;
enable_beta_markdown_export?: boolean;
downloadEndpoint: string;
readOnly?: boolean;
exportLink?: string;
}>();

// Refs and data
const markdownObjects = ref<any[]>([]);
const markdownErrors = ref<any[]>([]);
const datasets = ref<Record<string, unknown>>({});
const histories = ref<Record<string, unknown>>({});
const collections = ref<Record<string, unknown>>({});
const workflows = ref<Record<string, unknown>>({});
const invocations = ref<Record<string, unknown>>({});
const loading = ref(true);
const workflowID = ref("");

// Workflow Store
const workflowStore = useWorkflowStore();
const fetchWorkflowForInstanceId = workflowStore.fetchWorkflowForInstanceId;
const getStoredWorkflowByInstanceId = workflowStore.getStoredWorkflowByInstanceId;

// Computed properties
const effectiveExportLink = computed(() => (props.enable_beta_markdown_export ? props.exportLink : null));

const time = computed(() => {
const generateTime = props.markdownConfig.generate_time;
if (generateTime) {
let formattedTime = generateTime.endsWith("Z") ? generateTime : `${generateTime}Z`;

Check failure on line 68 in client/src/components/Markdown/Markdown.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

'formattedTime' is never reassigned. Use 'const' instead
const date = new Date(formattedTime);
return date.toLocaleString("default", {
day: "numeric",
month: "long",
year: "numeric",
hour: "numeric",
minute: "numeric",
timeZone: "UTC",
timeZoneName: "short",
});
}
return "unavailable";
});

const version = computed(() => props.markdownConfig.generate_version || "Unknown Galaxy Version");

const workflowVersions = computed(() => getStoredWorkflowByInstanceId(workflowID.value));

// Methods
function initConfig() {
const config = props.markdownConfig;
if (Object.keys(config).length) {
const markdown = config.content || config.markdown || "";
markdownErrors.value = config.errors || [];
markdownObjects.value = parseMarkdown(markdown);
datasets.value = config.history_datasets || {};
histories.value = config.histories || {};
collections.value = config.history_dataset_collections || {};
workflows.value = config.workflows || {};
invocations.value = config.invocations || {};
workflowID.value = Object.keys(config.workflows || {})[0] || "";
loading.value = false;
}
}

// Watchers
watch(() => props.markdownConfig, initConfig);

// Lifecycle hooks
onMounted(() => {
initConfig();
fetchWorkflowForInstanceId(workflowID.value);
});
</script>

<template>
<div class="markdown-wrapper">
<LoadingSpan v-if="loading" />
Expand All @@ -9,8 +122,7 @@
:fallback-url="exportLink"
:download-endpoint="downloadEndpoint"
size="sm"
title="Generate PDF">
</StsDownloadButton>
title="Generate PDF" />
<b-button
v-if="!readOnly"
v-b-tooltip.hover
Expand Down Expand Up @@ -45,11 +157,12 @@
</b-alert>
</div>
<div v-for="(obj, index) in markdownObjects" :key="index" class="markdown-components">
<p v-if="obj.name == 'default'" class="text-justify m-2" v-html="obj.content" />
<MarkdownContainer
v-else
:name="obj.name"
:args="obj.args"
<MarkdownDefault v-if="obj.name === 'default'" :content="obj.content" />
<MarkdownVega v-else-if="obj.name === 'vega'" :content="obj.content" />
<MarkdownVitessce v-else-if="obj.name === 'vitessce'" :content="obj.content" />
<MarkdownGalaxy
v-else-if="obj.name === 'galaxy'"
:content="obj.content"
:datasets="datasets"
:collections="collections"
:histories="histories"
Expand All @@ -61,146 +174,3 @@
</div>
</div>
</template>

<script>
import { library } from "@fortawesome/fontawesome-svg-core";
import { faDownload, faEdit } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import BootstrapVue from "bootstrap-vue";
import MarkdownIt from "markdown-it";
import markdownItRegexp from "markdown-it-regexp";
import { mapActions } from "pinia";
import Vue from "vue";

import { useWorkflowStore } from "@/stores/workflowStore";

import { splitMarkdown as splitMarkdownUnrendered } from "./parse";

import MarkdownContainer from "./MarkdownContainer.vue";
import LoadingSpan from "components/LoadingSpan.vue";
import StsDownloadButton from "components/StsDownloadButton.vue";

const mdNewline = markdownItRegexp(/<br>/, () => {
return "<div style='clear:both;'/><br>";
});

const md = MarkdownIt();
md.use(mdNewline);

Vue.use(BootstrapVue);

library.add(faDownload, faEdit);

export default {
components: {
MarkdownContainer,
FontAwesomeIcon,
LoadingSpan,
StsDownloadButton,
},
props: {
markdownConfig: {
type: Object,
default: null,
},
enable_beta_markdown_export: {
type: Boolean,
default: false,
},
downloadEndpoint: {
type: String,
default: null,
},
readOnly: {
type: Boolean,
default: false,
},
exportLink: {
type: String,
default: null,
},
},
data() {
return {
markdownObjects: [],
markdownErrors: [],
datasets: {},
histories: {},
collections: {},
workflows: {},
invocations: {},
loading: true,
workflowID: "",
};
},
computed: {
effectiveExportLink() {
return this.enable_beta_markdown_export ? this.exportLink : null;
},
time() {
let generateTime = this.markdownConfig.generate_time;
if (generateTime) {
if (!generateTime.endsWith("Z")) {
// We don't have tzinfo, but this will always be UTC coming
// from Galaxy so append Z to assert that prior to parsing
generateTime += "Z";
}
const date = new Date(generateTime);
return date.toLocaleString("default", {
day: "numeric",
month: "long",
year: "numeric",
minute: "numeric",
hour: "numeric",
timeZone: "UTC",
timeZoneName: "short",
});
}
return "unavailable";
},
workflowVersions() {
return this.getStoredWorkflowByInstanceId(this.workflowID);
},
version() {
return this.markdownConfig.generate_version || "Unknown Galaxy Version";
},
},
watch: {
markdownConfig() {
this.initConfig();
},
},
created() {
this.initConfig();
this.fetchWorkflowForInstanceId(this.workflowID);
},
methods: {
...mapActions(useWorkflowStore, ["getStoredWorkflowByInstanceId", "fetchWorkflowForInstanceId"]),
initConfig() {
if (Object.keys(this.markdownConfig).length) {
const config = this.markdownConfig;
const markdown = config.content || config.markdown || "";
this.markdownErrors = config.errors || [];
this.markdownObjects = this.splitMarkdown(markdown);
this.datasets = config.history_datasets || {};
this.histories = config.histories || {};
this.collections = config.history_dataset_collections || {};
this.workflows = config.workflows || {};
this.invocations = config.invocations || {};
this.loading = false;
this.workflowID = Object.keys(this.markdownConfig.workflows)[0];
}
},
splitMarkdown(markdown) {
const { sections, markdownErrors } = splitMarkdownUnrendered(markdown);
markdownErrors.forEach((error) => markdownErrors.push(error));
sections.forEach((section) => {
if (section.name == "default") {
section.content = md.render(section.content);
}
});
return sections;
},
},
};
</script>
23 changes: 23 additions & 0 deletions client/src/components/Markdown/Sections/MarkdownDefault.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import { computed } from "vue";

Check failure on line 2 in client/src/components/Markdown/Sections/MarkdownDefault.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Run autofix to sort these imports!
import MarkdownIt from "markdown-it";
//@ts-ignore
import markdownItRegexp from "markdown-it-regexp";

const mdNewline = markdownItRegexp(/<br>/, () => {
return "<div style='clear:both;'/><br>";
});

const md = MarkdownIt();
md.use(mdNewline);

const props = defineProps<{
content: string;
}>();

const renderedContent = computed(() => md.render(props.content));
</script>

<template>
<p class="text-justify m-2" v-html="renderedContent" />
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import flushPromises from "flush-promises";
import { getLocalVue } from "tests/jest/helpers";
import { withPrefix } from "utils/redirect";

import MountTarget from "./MarkdownContainer.vue";
import MountTarget from "./MarkdownGalaxy.vue";

// mock routes
jest.mock("utils/redirect");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script setup>
import { computed, ref } from "vue";

Check failure on line 2 in client/src/components/Markdown/Sections/MarkdownGalaxy.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Run autofix to sort these imports!

import { useConfig } from "@/composables/config";
import { getArgs } from "@/components/Markdown/parse";

import HistoryDatasetAsImage from "./Elements/HistoryDatasetAsImage.vue";
import HistoryDatasetAsTable from "./Elements/HistoryDatasetAsTable.vue";
Expand All @@ -25,14 +26,10 @@

const toggle = ref(false);
const props = defineProps({
name: {
content: {
type: String,
required: true,
},
args: {
type: Object,
required: true,
},
datasets: {
type: Object,
default: null,
Expand Down Expand Up @@ -63,7 +60,11 @@
},
});

const isCollapsible = computed(() => props.args.collapse !== undefined);
const parsedArgs = computed(() => getArgs(props.content));
const args = computed(() => parsedArgs.value.args);
const name = computed(() => parsedArgs.value.name);

const isCollapsible = computed(() => args.value.collapse !== undefined);
const isVisible = computed(() => !isCollapsible.value || toggle.value);

function argToBoolean(args, name, booleanDefault) {
Expand Down
17 changes: 17 additions & 0 deletions client/src/components/Markdown/Sections/MarkdownVega.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
const VegaWrapper = () => import("@/components/Common/VegaWrapper.vue");
import { computed } from "vue";

Check failure on line 3 in client/src/components/Markdown/Sections/MarkdownVega.vue

View workflow job for this annotation

GitHub Actions / client-unit-test (18)

Import in body of module; reorder to top

const props = defineProps<{
content: string;
}>();

const spec = computed(() => ({
...JSON.parse(props.content),
width: "container",
}));
</script>

<template>
<VegaWrapper :spec="spec" />
</template>
Loading
Loading