Skip to content

Commit

Permalink
Analysis view: use initialOptions to save & restore view state on n…
Browse files Browse the repository at this point in the history
…avigation (#1744)
  • Loading branch information
markgrahamdawson authored May 10, 2024
1 parent affdd8a commit fc58b4e
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 29 deletions.
1 change: 1 addition & 0 deletions changes.d/1744.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
More view options are now remembered & restored when navigating between workflows.
41 changes: 36 additions & 5 deletions src/components/cylc/analysis/AnalysisTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<v-data-table
:headers="shownHeaders"
:items="tasks"
:sort-by="sortBy"
v-model:sort-by="sortBy"
density="compact"
v-model:page="page"
v-model:items-per-page="itemsPerPage"
>
<!-- Use custom format for values in columns that have a specified formatter: -->
Expand All @@ -56,10 +57,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<script>
import { upperFirst } from 'lodash'
import { formatDuration } from '@/utils/tasks'
import {
initialOptions,
updateInitialOptionsEvent,
useInitialOptions
} from '@/utils/initialOptions'

export default {
name: 'AnalysisTableComponent',

emits: [updateInitialOptionsEvent],

props: {
tasks: {
type: Array,
Expand All @@ -68,15 +76,38 @@ export default {
timingOption: {
type: String,
required: true
},
initialOptions,
},

setup (props, { emit }) {
/**
* The number of tasks displayed per page.
* @type {import('vue').Ref<number>}
*/
const itemsPerPage = useInitialOptions('tasksFilter', { props, emit }, 50)

/**
* The 'sort by' state.
* @type {import('vue').Ref<array>}
*/
const sortBy = useInitialOptions('sortBy', { props, emit }, [{ key: 'name', order: 'asc' }])

/**
* The page number state.
* @type {import('vue').Ref<number>}
*/
const page = useInitialOptions('page', { props, emit }, 1)

return {
itemsPerPage,
sortBy,
page
}
},

data () {
return {
itemsPerPage: 50,
sortBy: [
{ key: 'name', order: 'asc' }
],
headers: [
{
title: 'Task',
Expand Down
44 changes: 33 additions & 11 deletions src/components/cylc/analysis/BoxPlot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<Teleport
v-if="sortInputTeleportTarget"
:to="sortInputTeleportTarget"
:to="`#${sortInputTeleportTarget}`"
>
<div class="d-flex flex-grow-1 col-gap-1">
<v-select
data-cy="box-plot-sort-select"
:items="sortChoices"
v-model="sortBy"
label="Sort by"
Expand Down Expand Up @@ -66,6 +67,11 @@ import {
import { upperFirst } from 'lodash'
import { formatDuration } from '@/utils/tasks'
import { useReducedAnimation } from '@/composables/localStorage'
import {
initialOptions,
updateInitialOptionsEvent,
useInitialOptions
} from '@/utils/initialOptions'

export default {
name: 'BoxPlot',
Expand All @@ -74,6 +80,8 @@ export default {
VueApexCharts,
},

emits: [updateInitialOptionsEvent],

props: {
tasks: {
type: Array,
Expand All @@ -83,6 +91,7 @@ export default {
type: String,
required: true,
},
initialOptions,
itemsPerPage: {
type: Number,
default: 20,
Expand All @@ -91,14 +100,32 @@ export default {
type: Boolean,
default: true,
},
/** Where to teleport the sorting input (or don't render if null) */
/** ID of element to teleport the sorting input (or don't render if null) */
sortInputTeleportTarget: {
type: String,
default: null,
},
},

setup (props) {
setup (props, { emit }) {
/**
* The 'sort by' state.
* @type {import('vue').Ref<string>}
*/
const sortBy = useInitialOptions('sortBy', { props, emit }, 'name')

/**
* The page number state.
* @type {import('vue').Ref<number>}
*/
const page = useInitialOptions('page', { props, emit }, 1)

/**
* The sort descending/sscending state.
* @type {import('vue').Ref<boolean>}
*/
const sortDesc = useInitialOptions('sortDesc', { props, emit }, false)

const reducedAnimation = useReducedAnimation()

const chartOptions = computed(() => ({
Expand Down Expand Up @@ -163,18 +190,13 @@ export default {
}))

return {
sortBy,
page,
sortDesc,
chartOptions,
}
},

data () {
return {
page: 1,
sortBy: 'name',
sortDesc: false,
}
},

errorCaptured (err, instance, info) {
if (err.name === 'TypeError' && instance.type === 'boxPlot') {
// Suppress bogus error https://github.com/apexcharts/vue3-apexcharts/issues/79
Expand Down
37 changes: 32 additions & 5 deletions src/components/cylc/analysis/TimeSeries.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<Teleport
v-if="sortInputTeleportTarget"
:to="sortInputTeleportTarget"
:to="`#${sortInputTeleportTarget}`"
>
<div class="d-flex flex-grow-1 col-gap-1">
<v-autocomplete
Expand Down Expand Up @@ -104,6 +104,11 @@ import {
} from '@mdi/js'
import { useReducedAnimation } from '@/composables/localStorage'
import DeltasCallback from '@/services/callbacks'
import {
initialOptions,
updateInitialOptionsEvent,
useInitialOptions
} from '@/utils/initialOptions'

/** List of fields to request for task for each task */
const jobFields = [
Expand Down Expand Up @@ -173,6 +178,8 @@ export default {
VueApexCharts,
},

emits: [updateInitialOptionsEvent],

props: {
workflowIDs: {
type: Array,
Expand All @@ -182,6 +189,7 @@ export default {
type: String,
required: true,
},
initialOptions,
platformOption: {
type: [String, Number],
required: true,
Expand All @@ -197,24 +205,43 @@ export default {
},
},

setup () {
setup (props, { emit }) {
const reducedAnimation = useReducedAnimation()
return { reducedAnimation }

/**
* The state for storing displayed tasks
* @type {import('vue').Ref<array>}
*/
const displayedTasks = useInitialOptions('displayedTasks', { props, emit }, [])

/**
* The show origin option toggle state
* @type {import('vue').Ref<boolean>}
*/
const showOrigin = useInitialOptions('showOrigin', { props, emit }, false)

return {
reducedAnimation,
displayedTasks,
showOrigin
}
},

beforeMount () {
this.taskNamesQuery()
},

mounted () {
this.refreshData()
},

data () {
const jobs = []
return {
jobCallback: new AnalysisJobCallback(jobs),
/** Object containing all of the jobs added by the callback */
jobs,
taskNames: [],
displayedTasks: [],
showOrigin: false,
xRange: [undefined, undefined],
}
},
Expand Down
68 changes: 60 additions & 8 deletions src/views/Analysis.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</v-col>
</v-row>
<div
ref="toolbar"
id="analysis-toolbar"
class="d-flex align-center flex-wrap my-2 col-gap-2 row-gap-4"
>
Expand Down Expand Up @@ -115,32 +116,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-if="chartType === 'table'"
:tasks="filteredTasks"
:timing-option="timingOption"
v-model:initial-options="dataTableOptions"
/>
<BoxPlot
v-else-if="chartType === 'box'"
:tasks="filteredTasks"
:timing-option="timingOption"
sort-input-teleport-target="#analysis-toolbar"
:sort-input-teleport-target="toolbar?.id"
v-model:initial-options="boxPlotOptions"
/>
<TimeSeries
v-else-if="chartType === 'timeSeries'"
:workflowIDs="workflowIDs"
:platform-option="tasksFilter.platformOption"
:timing-option="timingOption"
sort-input-teleport-target="#analysis-toolbar"
:sort-input-teleport-target="toolbar?.id"
v-model:initial-options="timeseriesPlotOptions"
/>
</v-container>
</div>
</template>

<script>
import { ref } from 'vue'
import {
debounce,
pick,
} from 'lodash'
import gql from 'graphql-tag'
import { getPageTitle } from '@/utils/index'
import graphqlMixin from '@/mixins/graphql'
import {
initialOptions,
updateInitialOptionsEvent,
useInitialOptions
} from '@/utils/initialOptions'
import DeltasCallback from '@/services/callbacks'
import AnalysisTable from '@/components/cylc/analysis/AnalysisTable.vue'
import BoxPlot from '@/components/cylc/analysis/BoxPlot.vue'
Expand Down Expand Up @@ -248,18 +258,60 @@ export default {
this.tasksQuery()
},

emits: [updateInitialOptionsEvent],

props: { initialOptions },

setup (props, { emit }) {
/**
* The task name, timing option and platform filter state.
* @type {import('vue').Ref<object>}
*/
const tasksFilter = useInitialOptions('tasksFilter', { props, emit }, { name: '', timingOption: 'totalTimes', platformOption: -1 })

/**
* Determines the Analysis type ('table' | 'box' | 'timeSeries')
* @type {import('vue').Ref<string>}
*/
const chartType = useInitialOptions('chartType', { props, emit }, 'table')

/** @type {import('vue').Ref<HTMLElement>} template ref */
const toolbar = ref(null)

/**
* The Vuetify data table options (sortBy, page etc).
* @type {import('vue').Ref<object>}
*/
const dataTableOptions = useInitialOptions('dataTableOptions', { props, emit })

/**
* The Vuetify box and whisker plot options (sortBy, page etc).
* @type {import('vue').Ref<object>}
*/
const boxPlotOptions = useInitialOptions('boxPlotOptions', { props, emit })

/**
* The Vuetify box and whisker plot options (displayedTasks, showOrigin).
* @type {import('vue').Ref<object>}
*/
const timeseriesPlotOptions = useInitialOptions('timeseriesPlotOptions', { props, emit })

return {
tasksFilter,
chartType,
toolbar,
dataTableOptions,
boxPlotOptions,
timeseriesPlotOptions
}
},

data () {
const tasks = []
return {
callback: new AnalysisTaskCallback(tasks),
/** Object containing all of the tasks added by the callback */
tasks,
tasksFilter: {
name: '',
timingOption: 'totalTimes',
platformOption: -1,
},
chartType: 'table',
}
},

Expand Down
Loading

0 comments on commit fc58b4e

Please sign in to comment.