From 5e86d9778ee91792b8f6890d55c7042888b7becb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Wed, 15 Mar 2023 20:16:19 +0100 Subject: [PATCH] Add renderer to show help page for parameter schema #38 --- package-lock.json | 144 ++++++++++++++---- package.json | 1 + src/App.vue | 2 +- src/assets/main.css | 10 ++ .../ParameterSchemaDescriptionComponent.vue | 80 ++++++++++ .../description-mode/ParameterDescription.vue | 136 +++++++++++++++++ .../ParameterGroupDescription.vue | 58 +++++++ src/views/workflows/WorkflowVersionView.vue | 10 +- src/views/workflows/WorkflowView.vue | 1 + 9 files changed, 408 insertions(+), 34 deletions(-) create mode 100644 src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue create mode 100644 src/components/parameter-schema/description-mode/ParameterDescription.vue create mode 100644 src/components/parameter-schema/description-mode/ParameterGroupDescription.vue diff --git a/package-lock.json b/package-lock.json index 38ed882..4a8df54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@aws-sdk/s3-request-presigner": "^3.290.0", "@fortawesome/fontawesome-free": "^6.3.0", "@popperjs/core": "^2.11.6", + "ajv": "^8.12.0", "bootstrap": "^5.2.3", "dayjs": "^1.11.7", "dompurify": "^3.0.1", @@ -2237,6 +2238,28 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/@fortawesome/fontawesome-free": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz", @@ -2837,14 +2860,13 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -3901,6 +3923,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -3923,6 +3961,12 @@ "node": ">=4.0" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/espree": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", @@ -4016,8 +4060,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.2.0", @@ -4914,10 +4957,9 @@ } }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5672,7 +5714,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, "engines": { "node": ">=6" } @@ -5777,6 +5818,14 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -6329,7 +6378,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -8596,6 +8644,26 @@ "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } } }, "@fortawesome/fontawesome-free": { @@ -9040,14 +9108,13 @@ "requires": {} }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, @@ -9656,6 +9723,18 @@ "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -9671,6 +9750,12 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true } } }, @@ -9808,8 +9893,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-diff": { "version": "1.2.0", @@ -10438,10 +10522,9 @@ } }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -10981,8 +11064,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "queue-microtask": { "version": "1.2.3", @@ -11048,6 +11130,11 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -11443,7 +11530,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "requires": { "punycode": "^2.1.0" } diff --git a/package.json b/package.json index 1460ccd..9480d9a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@aws-sdk/s3-request-presigner": "^3.290.0", "@fortawesome/fontawesome-free": "^6.3.0", "@popperjs/core": "^2.11.6", + "ajv": "^8.12.0", "bootstrap": "^5.2.3", "dayjs": "^1.11.7", "dompurify": "^3.0.1", diff --git a/src/App.vue b/src/App.vue index 359fecb..5a48cd8 100644 --- a/src/App.vue +++ b/src/App.vue @@ -44,7 +44,7 @@ onBeforeMount(() => { <template> <NavbarTop /> - <div class="container mt-4"> + <div class="container-xxl mt-4"> <router-view></router-view> </div> </template> diff --git a/src/assets/main.css b/src/assets/main.css index 7024e78..8237a72 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -14,3 +14,13 @@ body { .cursor-pointer { cursor: pointer; } + +.helpTextCode > pre { + border: thin solid var(--bs-secondary); + border-radius: var(--bs-border-radius); + background: var(--bs-dark); + filter: brightness(0.9); + padding: 1rem; + margin: 1em 2em; + color: var(--bs-code-color); +} diff --git a/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue b/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue new file mode 100644 index 0000000..43ce261 --- /dev/null +++ b/src/components/parameter-schema/ParameterSchemaDescriptionComponent.vue @@ -0,0 +1,80 @@ +<script setup lang="ts"> +import { computed } from "vue"; +import ParameterGroupDescription from "@/components/parameter-schema/description-mode/ParameterGroupDescription.vue"; +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; + +const props = defineProps({ + schema: { + type: Object, + required: true, + }, +}); + +type ParameterGroup = { + group: string; + title: string; + icon?: string; +}; + +const navParameterGroups = computed<ParameterGroup[]>(() => + Object.keys(parameterGroups.value) + .map((group) => { + return { + group: group, + title: parameterGroups.value[group]["title"], + icon: parameterGroups.value[group]["fa_icon"], + }; + }) + .filter( + // filter all groups that have only hidden parameters + (group) => + Object.keys(parameterGroups.value[group.group]["properties"]).filter( + (key) => + !parameterGroups.value[group.group]["properties"][key]["hidden"] + ).length > 0 + ) +); + +const parameterGroups = computed<Record<string, never>>( + () => props.schema["definitions"] +); +</script> + +<template> + <div class="row mb-5 align-items-start"> + <div class="col-9"> + <div v-for="(group, groupName) in parameterGroups" :key="groupName"> + <parameter-group-description + :parameter-group="group" + :parameter-group-name="groupName" + /> + </div> + </div> + <div + class="col-3 sticky-top" + style="top: 70px !important; max-height: calc(100vh - 150px)" + > + <nav class="h-100 bg-dark rounded-1"> + <nav class="nav"> + <ul class="ps-0"> + <li + class="nav-link" + v-for="group in navParameterGroups" + :key="group.group" + > + <a :href="'#' + group.group" + ><font-awesome-icon + :icon="group.icon" + v-if="group.icon" + class="me-2" + />{{ group.title }}</a + > + </li> + </ul> + </nav> + </nav> + </div> + </div> +</template> + +<style scoped></style> diff --git a/src/components/parameter-schema/description-mode/ParameterDescription.vue b/src/components/parameter-schema/description-mode/ParameterDescription.vue new file mode 100644 index 0000000..0c2371d --- /dev/null +++ b/src/components/parameter-schema/description-mode/ParameterDescription.vue @@ -0,0 +1,136 @@ +<script setup lang="ts"> +import { computed } from "vue"; +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; +import MarkdownRenderer from "@/components/MarkdownRenderer.vue"; + +const props = defineProps({ + parameter: { + type: Object, + required: true, + validator(value: Record<string, never>) { + return ["boolean", "array", "number", "string"].includes(value["type"]); + }, + }, + required: Boolean, + parameterName: { + type: String, + required: true, + }, +}); + +const randomIDSuffix = Math.random().toString(16).substr(2, 8); + +const helpText = computed<string | undefined>( + () => props.parameter["help_text"] +); +const parameterType = computed<string>(() => props.parameter["type"]); +const icon = computed<string | undefined>(() => props.parameter["fa_icon"]); +const description = computed<string>(() => props.parameter["description"]); +const defaultValue = computed<string | undefined>(() => + props.parameter["default"]?.toString() +); +const enumValues = computed<string[] | undefined>(() => + props.parameter["enum"]?.map((val: string) => val.toString()) +); +const hidden = computed<boolean>(() => props.parameter["hidden"] ?? false); +const parameterPattern = computed<string | undefined>( + () => props.parameter["pattern"] +); + +const showRightColum = computed<boolean>( + () => + helpText.value != undefined || + props.required || + defaultValue.value != undefined +); +</script> + +<template> + <div + class="row border-top border-bottom border-secondary align-items-start py-2" + v-if="!hidden" + > + <div class="fs-6 col-3"> + <font-awesome-icon :icon="icon" v-if="icon" class="me-2" /> + <code class="bg-dark p-1" :id="props.parameterName" + >--{{ props.parameterName }}</code + > + <br /> + <span>type: '{{ parameterType }}'</span> + </div> + <div + :class="{ 'col-7': showRightColum, 'col-9': !showRightColum }" + class="flex-fill" + > + <markdown-renderer :markdown="description" /> + </div> + <div + class="col-auto d-flex flex-column align-items-end flex-fill" + v-if="showRightColum" + > + <button + class="btn btn-outline-info btn-sm my-1" + type="button" + data-bs-toggle="collapse" + :data-bs-target="'#helpCollapse' + randomIDSuffix" + aria-expanded="false" + aria-controls="collapseExample" + v-if="helpText" + > + <font-awesome-icon icon="fa-solid fa-circle-info" /> + Help + </button> + <div v-if="enumValues" class="dropdown w-fit my-1"> + <a + class="bg-dark rounded-1 p-1 dropdown-toggle text-reset text-decoration-none" + href="#" + role="button" + data-bs-toggle="dropdown" + aria-expanded="false" + > + Options: + <span v-if="defaultValue" + ><code>{{ defaultValue }}</code> (default)</span + > + </a> + <ul class="dropdown-menu dropdown-menu-dark" v-if="enumValues"> + <li v-for="val in enumValues" :key="val" class="px-2 w-100"> + {{ val }} <span v-if="val === defaultValue">(default)</span> + </li> + </ul> + </div> + <span v-else-if="defaultValue" class="bg-dark rounded-1 py-0 px-1 my-1" + >default: <code>{{ defaultValue }}</code></span + > + + <span + v-if="props.required" + class="bg-warning rounded-1 px-1 py-0 text-white" + >required</span + > + </div> + <div + class="collapse p-2 pb-0 bg-dark m-2 flex-shrink-1" + :id="'helpCollapse' + randomIDSuffix" + v-if="helpText" + > + <markdown-renderer class="helpTextCode" :markdown="helpText" /> + <span v-if="parameterPattern" class="mb-2" + >Pattern: <code>{{ parameterPattern }}</code></span + > + </div> + </div> +</template> + +<style scoped> +code { + color: var(--bs-code-color) !important; +} + +li:hover { + background: var(--bs-secondary); +} +a:hover { + filter: brightness(0.8); +} +</style> diff --git a/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue b/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue new file mode 100644 index 0000000..f8cc45c --- /dev/null +++ b/src/components/parameter-schema/description-mode/ParameterGroupDescription.vue @@ -0,0 +1,58 @@ +<script setup lang="ts"> +import { computed } from "vue"; +import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; +import ParameterDescription from "@/components/parameter-schema/description-mode/ParameterDescription.vue"; + +const props = defineProps({ + parameterGroup: { + type: Object, + required: true, + validator(value: Record<string, never>) { + return "object" === value["type"]; + }, + }, + parameterGroupName: { + type: String, + required: true, + }, +}); + +const title = computed<string>(() => props.parameterGroup["title"]); +const icon = computed<string>(() => props.parameterGroup["fa_icon"]); +const description = computed<string>(() => props.parameterGroup["description"]); +const groupHidden = computed<boolean>(() => + Object.keys(parameters.value).reduce( + (acc: boolean, val: string) => acc && parameters.value[val]["hidden"], + true + ) +); +const parameters = computed<Record<string, never>>( + () => props.parameterGroup["properties"] +); +</script> + +<template> + <div class="mb-5" v-if="!groupHidden"> + <div class="row"> + <h2 :id="props.parameterGroupName"> + <font-awesome-icon :icon="icon" class="me-3" v-if="icon" />{{ title }} + </h2> + <h4>{{ description }}</h4> + </div> + <template + v-for="(parameter, parameterName) in parameters" + :key="parameterName" + > + <parameter-description + v-if="parameter['type'] !== 'object'" + :parameter-name="parameterName" + :parameter="parameter" + :required=" + props.parameterGroup['required']?.includes(parameterName) ?? false + " + /> + </template> + </div> +</template> + +<style scoped></style> diff --git a/src/views/workflows/WorkflowVersionView.vue b/src/views/workflows/WorkflowVersionView.vue index 9ffb18d..c450386 100644 --- a/src/views/workflows/WorkflowVersionView.vue +++ b/src/views/workflows/WorkflowVersionView.vue @@ -4,6 +4,7 @@ import { WorkflowVersionService } from "@/client/workflow"; import type { WorkflowVersionFull } from "@/client/workflow"; import axios from "axios"; import MarkdownRenderer from "@/components/MarkdownRenderer.vue"; +import ParameterSchemaDescriptionComponent from "@/components/parameter-schema/ParameterSchemaDescriptionComponent.vue"; const props = defineProps<{ versionId: string; @@ -127,13 +128,14 @@ onMounted(() => { ></span> </p> </div> - <div v-else> + <div v-else class="px-2"> <p v-if="props.activeTab === 'description'"> <markdown-renderer :markdown="versionState.descriptionMarkdown" /> </p> - <pre v-else-if="props.activeTab === 'parameters'" - >{{ JSON.stringify(versionState.parameterSchema, null, 2) }} - </pre> + <parameter-schema-description-component + v-else-if="props.activeTab === 'parameters'" + :schema="versionState.parameterSchema" + /> <p v-else-if="props.activeTab === 'changes'"> <markdown-renderer :markdown="versionState.changelogMarkdown" /> </p> diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue index f8fd224..9a5b54f 100644 --- a/src/views/workflows/WorkflowView.vue +++ b/src/views/workflows/WorkflowView.vue @@ -169,6 +169,7 @@ onMounted(() => { params: { versionId: latestVersion.git_commit_hash, }, + query: { tab: route.query.tab }, }" >Try the latest version {{ latestVersion?.version }}.</router-link > -- GitLab