Skip to content

Commit

Permalink
WIP (parameter form has component test but cannot yet be submitted)
Browse files Browse the repository at this point in the history
  • Loading branch information
david-mears-2 committed Sep 2, 2024
1 parent 9e0e8b8 commit dda7c27
Show file tree
Hide file tree
Showing 9 changed files with 459 additions and 174 deletions.
4 changes: 4 additions & 0 deletions assets/scss/_theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ body {
.button-group-container .btn-outline-primary {
background-color: var(--cui-tertiary-bg);
}

.btn:disabled, .btn.disabled, fieldset:disabled .btn {
opacity: 0.5;
}
2 changes: 2 additions & 0 deletions assets/scss/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ $app-header-height: 70px;
$app-header-margin-bottom: 1.5rem;
$min-wrapper-height: calc(100dvh - $app-header-height - $app-header-margin-bottom);
$container-padding: 1.5rem;
$sidebar-narrow-width: 4rem;

// Imperial Brand
// ==============
Expand Down Expand Up @@ -44,3 +45,4 @@ $grid-breakpoints: (
xl: 1200px,
xxl: 1400px
);
$cui-tertiary-bg: rgb(243, 244, 247)
1 change: 0 additions & 1 deletion components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ onBeforeUnmount(() => {
.header-toggler {
margin-inline-start: -14px;
}
$sidebar-narrow-width: 4rem;
.full-breadcrumb-container {
min-height: 2.5rem !important;
background-color: rgb(250, 250, 250);
Expand Down
240 changes: 240 additions & 0 deletions components/ParameterForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
<template>
<div>
<CForm
v-if="props.metaData"
class="inputs"
:data-test="JSON.stringify(formData)"
@submit.prevent="submitForm"
>
<div
v-for="(parameter) in parametersOfTypeSelect"
:key="parameter.id"
class="field-container"
>
<CIcon
v-if="parameterIcons[parameter.id] && !optionsAreTerse(parameter)"
:icon="parameterIcons[parameter.id]"
class="parameter-icon"
/>
<CFormSelect
v-if="!optionsAreTerse(parameter)"
:ref="parameter.id"
v-model="formData[parameter.id]"
:label="parameter.label"
:aria-label="parameter.label"
:options="parameter.options.map((option: ParameterOption) => {
return { label: option.label, value: option.id };
})"
:feedback="true ? '' : 'Please select a parameter'"
:invalid="true ? false : true"
:size="largeScreen ? 'lg' : ''"
/>
<CCol v-else class="button-group-container">
<CRow>
<CIcon
v-if="parameterIcons[parameter.id]"
:icon="parameterIcons[parameter.id]"
class="parameter-icon"
/>
<CFormLabel :for="parameter.id">
{{ parameter.label }}
</CFormLabel>
</CRow>
<CRow>
<CButtonGroup
role="group"
:aria-label="parameter.label"
:size="largeScreen ? 'lg' : ''"
@click="disableRunButton = false"
>
<CFormCheck
v-for="(option) in parameter.options"
:id="option.id"
:key="option.id"
v-model="formData[parameter.id]"
type="radio"
:button="{ color: 'primary', variant: 'outline' }"
:name="parameter.id"
autocomplete="off"
:label="option.label"
:value="option.id"
/>
</CButtonGroup>
</CRow>
</CCol>
</div>
<div
v-if="globeParameter"
class="field-container"
>
<CIcon
v-if="parameterIcons[globeParameter.id]"
:icon="parameterIcons[globeParameter.id]"
class="parameter-icon"
/>
<CFormSelect
v-model="formData[globeParameter.id]"
:data-test="`${globeParameter.id}-select`"
:label="globeParameter.label"
:aria-label="globeParameter.label"
:options="selectOptions(globeParameter)"
:size="largeScreen ? 'lg' : ''"
/>
</div>
<CButton
id="run-button"
color="primary"
:size="largeScreen ? 'lg' : ''"
type="submit"
@click="console.log('not implemetned yet')"
>
Run
<CIcon
icon="cilArrowRight"
/>
</CButton>
</CForm>
<CAlert v-else-if="props.metadataFetchStatus === 'error'" color="warning">
Failed to retrieve metadata from R API. {{ metadataFetchError }}
</CAlert>
<CSpinner v-else-if="props.metadataFetchStatus === 'pending'" />
</div>
</template>

<script lang="ts" setup>
import type { FetchError } from "ofetch";
import { CIcon } from "@coreui/icons-vue";
import type { MetaData, Parameter, ParameterOption } from "@/types/daedalusApiResponseTypes";
import type { AsyncDataRequestStatus } from "#app";
const props = defineProps<{
globeParameter: Parameter | undefined
metaData: MetaData | undefined
metadataFetchStatus: AsyncDataRequestStatus
metadataFetchError: FetchError | null
}>();
const formData = ref(
// Create a new object with keys set to the id values of the metaData.parameters array of objects, and all values set to empty refs.
props.metaData?.parameters.reduce((accumulator, parameter) => {
if (parameter.parameterType !== "select" && parameter.parameterType !== "globeSelect") {
accumulator[parameter.id] = ref("");
return accumulator;
}
// TODO: Make default country UK after November 2024 workshop
if (parameter.id === "country") {
accumulator[parameter.id] = ref("Thailand");
} else {
// Don't set an empty value or there will be a disjoint between the select component and the formData object,
// since the select component will visually appear to have an option selected (the first), but the formData object will have
// an empty string.
const defaultOption = parameter.defaultOption || parameter.options[0].id;
accumulator[parameter.id] = ref(defaultOption);
}
return accumulator;
}, {} as { [key: string]: any }),
);
const parameterIcons = {
country: "cilGlobeAlt",
response: "cilShieldAlt",
vaccine: "cilIndustry",
pathogen: "cilBug",
};
const parametersOfTypeSelect = computed(() => {
if (props.metaData) {
return props.metaData.parameters.filter(parameter => parameter.parameterType === "select");
} else {
return [];
}
});
const globeParameter = computed(() => {
if (props.metaData) {
return props.metaData.parameters.filter(parameter => parameter.parameterType === "globeSelect")[0];
} else {
return undefined;
}
});
const selectOptions = (parameter: Parameter) => {
return parameter.options.map((option: ParameterOption) => {
// Because the select component does not seem to honour the initial v-model value, we had to manually
// set the 'selected' attribute below.
// As a result, this list of options is recalculated each time an option is selected.
return { label: option.label, value: option.id, selected: option.id === formData.value![parameter.id] };
});
};
const optionsAreTerse = (parameter: Parameter) => {
const eachOptionIsASingleWord = parameter.options.filter((option) => {
return option.label.includes(" ");
}).length === 0;
return parameter.options.length <= 5 && eachOptionIsASingleWord;
};
const submitForm = () => {
// console.log(formData.value);
};
const largeScreen = ref(true);
const breakpoint = 992; // CoreUI's "lg" breakpoint
const setFieldSizes = () => {
if (window.innerWidth < breakpoint) {
largeScreen.value = false;
} else {
largeScreen.value = true;
}
};
onMounted(() => {
setFieldSizes();
window.addEventListener("resize", setFieldSizes);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", setFieldSizes);
});
watchEffect(() => {
// if (formData.value) {
// console.log(formData.value)
// }
});
</script>

<style lang="scss">
.inputs {
display: flex;
flex-wrap: wrap;
row-gap: 1rem;
column-gap: 1rem;
// background-color: rgba(100, 100, 100, 0.5);
// background-color: rgba(255, 255, 255, 0.5); /* Adjust opacity as needed */
// mix-blend-mode: overlay;
// backdrop-filter: blur(5px);
}
.field-container {
min-width: 15rem;
flex-grow: 1;
.parameter-icon {
margin-left: 0.75rem;
margin-right: 0.5rem;
padding: 0;
}
.button-group-container .parameter-icon {
margin-left: 1.5rem;
}
}
#run-button {
align-self: flex-end;
min-width: 7rem;
}
</style>
2 changes: 0 additions & 2 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ function handleToggleSidebarVisibility() {

<style lang="scss">
@use "sass:map";
$sidebar-narrow-width: 4rem;
.body {
@media (min-width: map.get($grid-breakpoints, 'lg')) {
padding-left: $sidebar-narrow-width;
Expand Down
10 changes: 10 additions & 0 deletions modules/coreui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import { addComponent, defineNuxtModule } from "@nuxt/kit";
// https://nuxt.com/docs/guide/directory-structure/components#npm-packages
export default defineNuxtModule({
setup() {
addComponent({
name: "CAlert",
export: "CAlert",
filePath: "@coreui/vue/src/components/alert/CAlert",
});
addComponent({
name: "CButtonGroup",
export: "CButtonGroup",
Expand All @@ -21,6 +26,11 @@ export default defineNuxtModule({
export: "CCol",
filePath: "@coreui/vue/src/components/grid/CCol",
});
addComponent({
name: "CForm",
export: "CForm",
filePath: "@coreui/vue/src/components/form/CForm",
});
addComponent({
name: "CFormCheck",
export: "CFormCheck",
Expand Down
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit dda7c27

Please sign in to comment.