Skip to content

Commit fc58b4e

Browse files
Analysis view: use initialOptions to save & restore view state on navigation (cylc#1744)
1 parent affdd8a commit fc58b4e

File tree

6 files changed

+292
-29
lines changed

6 files changed

+292
-29
lines changed

changes.d/1744.feat.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
More view options are now remembered & restored when navigating between workflows.

src/components/cylc/analysis/AnalysisTable.vue

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
3131
<v-data-table
3232
:headers="shownHeaders"
3333
:items="tasks"
34-
:sort-by="sortBy"
34+
v-model:sort-by="sortBy"
3535
density="compact"
36+
v-model:page="page"
3637
v-model:items-per-page="itemsPerPage"
3738
>
3839
<!-- Use custom format for values in columns that have a specified formatter: -->
@@ -56,10 +57,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
5657
<script>
5758
import { upperFirst } from 'lodash'
5859
import { formatDuration } from '@/utils/tasks'
60+
import {
61+
initialOptions,
62+
updateInitialOptionsEvent,
63+
useInitialOptions
64+
} from '@/utils/initialOptions'
5965
6066
export default {
6167
name: 'AnalysisTableComponent',
6268
69+
emits: [updateInitialOptionsEvent],
70+
6371
props: {
6472
tasks: {
6573
type: Array,
@@ -68,15 +76,38 @@ export default {
6876
timingOption: {
6977
type: String,
7078
required: true
79+
},
80+
initialOptions,
81+
},
82+
83+
setup (props, { emit }) {
84+
/**
85+
* The number of tasks displayed per page.
86+
* @type {import('vue').Ref<number>}
87+
*/
88+
const itemsPerPage = useInitialOptions('tasksFilter', { props, emit }, 50)
89+
90+
/**
91+
* The 'sort by' state.
92+
* @type {import('vue').Ref<array>}
93+
*/
94+
const sortBy = useInitialOptions('sortBy', { props, emit }, [{ key: 'name', order: 'asc' }])
95+
96+
/**
97+
* The page number state.
98+
* @type {import('vue').Ref<number>}
99+
*/
100+
const page = useInitialOptions('page', { props, emit }, 1)
101+
102+
return {
103+
itemsPerPage,
104+
sortBy,
105+
page
71106
}
72107
},
73108
74109
data () {
75110
return {
76-
itemsPerPage: 50,
77-
sortBy: [
78-
{ key: 'name', order: 'asc' }
79-
],
80111
headers: [
81112
{
82113
title: 'Task',

src/components/cylc/analysis/BoxPlot.vue

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
<template>
1919
<Teleport
2020
v-if="sortInputTeleportTarget"
21-
:to="sortInputTeleportTarget"
21+
:to="`#${sortInputTeleportTarget}`"
2222
>
2323
<div class="d-flex flex-grow-1 col-gap-1">
2424
<v-select
25+
data-cy="box-plot-sort-select"
2526
:items="sortChoices"
2627
v-model="sortBy"
2728
label="Sort by"
@@ -66,6 +67,11 @@ import {
6667
import { upperFirst } from 'lodash'
6768
import { formatDuration } from '@/utils/tasks'
6869
import { useReducedAnimation } from '@/composables/localStorage'
70+
import {
71+
initialOptions,
72+
updateInitialOptionsEvent,
73+
useInitialOptions
74+
} from '@/utils/initialOptions'
6975
7076
export default {
7177
name: 'BoxPlot',
@@ -74,6 +80,8 @@ export default {
7480
VueApexCharts,
7581
},
7682
83+
emits: [updateInitialOptionsEvent],
84+
7785
props: {
7886
tasks: {
7987
type: Array,
@@ -83,6 +91,7 @@ export default {
8391
type: String,
8492
required: true,
8593
},
94+
initialOptions,
8695
itemsPerPage: {
8796
type: Number,
8897
default: 20,
@@ -91,14 +100,32 @@ export default {
91100
type: Boolean,
92101
default: true,
93102
},
94-
/** Where to teleport the sorting input (or don't render if null) */
103+
/** ID of element to teleport the sorting input (or don't render if null) */
95104
sortInputTeleportTarget: {
96105
type: String,
97106
default: null,
98107
},
99108
},
100109
101-
setup (props) {
110+
setup (props, { emit }) {
111+
/**
112+
* The 'sort by' state.
113+
* @type {import('vue').Ref<string>}
114+
*/
115+
const sortBy = useInitialOptions('sortBy', { props, emit }, 'name')
116+
117+
/**
118+
* The page number state.
119+
* @type {import('vue').Ref<number>}
120+
*/
121+
const page = useInitialOptions('page', { props, emit }, 1)
122+
123+
/**
124+
* The sort descending/sscending state.
125+
* @type {import('vue').Ref<boolean>}
126+
*/
127+
const sortDesc = useInitialOptions('sortDesc', { props, emit }, false)
128+
102129
const reducedAnimation = useReducedAnimation()
103130
104131
const chartOptions = computed(() => ({
@@ -163,18 +190,13 @@ export default {
163190
}))
164191
165192
return {
193+
sortBy,
194+
page,
195+
sortDesc,
166196
chartOptions,
167197
}
168198
},
169199
170-
data () {
171-
return {
172-
page: 1,
173-
sortBy: 'name',
174-
sortDesc: false,
175-
}
176-
},
177-
178200
errorCaptured (err, instance, info) {
179201
if (err.name === 'TypeError' && instance.type === 'boxPlot') {
180202
// Suppress bogus error https://github.com/apexcharts/vue3-apexcharts/issues/79

src/components/cylc/analysis/TimeSeries.vue

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
<template>
1919
<Teleport
2020
v-if="sortInputTeleportTarget"
21-
:to="sortInputTeleportTarget"
21+
:to="`#${sortInputTeleportTarget}`"
2222
>
2323
<div class="d-flex flex-grow-1 col-gap-1">
2424
<v-autocomplete
@@ -104,6 +104,11 @@ import {
104104
} from '@mdi/js'
105105
import { useReducedAnimation } from '@/composables/localStorage'
106106
import DeltasCallback from '@/services/callbacks'
107+
import {
108+
initialOptions,
109+
updateInitialOptionsEvent,
110+
useInitialOptions
111+
} from '@/utils/initialOptions'
107112
108113
/** List of fields to request for task for each task */
109114
const jobFields = [
@@ -173,6 +178,8 @@ export default {
173178
VueApexCharts,
174179
},
175180
181+
emits: [updateInitialOptionsEvent],
182+
176183
props: {
177184
workflowIDs: {
178185
type: Array,
@@ -182,6 +189,7 @@ export default {
182189
type: String,
183190
required: true,
184191
},
192+
initialOptions,
185193
platformOption: {
186194
type: [String, Number],
187195
required: true,
@@ -197,24 +205,43 @@ export default {
197205
},
198206
},
199207
200-
setup () {
208+
setup (props, { emit }) {
201209
const reducedAnimation = useReducedAnimation()
202-
return { reducedAnimation }
210+
211+
/**
212+
* The state for storing displayed tasks
213+
* @type {import('vue').Ref<array>}
214+
*/
215+
const displayedTasks = useInitialOptions('displayedTasks', { props, emit }, [])
216+
217+
/**
218+
* The show origin option toggle state
219+
* @type {import('vue').Ref<boolean>}
220+
*/
221+
const showOrigin = useInitialOptions('showOrigin', { props, emit }, false)
222+
223+
return {
224+
reducedAnimation,
225+
displayedTasks,
226+
showOrigin
227+
}
203228
},
204229
205230
beforeMount () {
206231
this.taskNamesQuery()
207232
},
208233
234+
mounted () {
235+
this.refreshData()
236+
},
237+
209238
data () {
210239
const jobs = []
211240
return {
212241
jobCallback: new AnalysisJobCallback(jobs),
213242
/** Object containing all of the jobs added by the callback */
214243
jobs,
215244
taskNames: [],
216-
displayedTasks: [],
217-
showOrigin: false,
218245
xRange: [undefined, undefined],
219246
}
220247
},

src/views/Analysis.vue

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6363
</v-col>
6464
</v-row>
6565
<div
66+
ref="toolbar"
6667
id="analysis-toolbar"
6768
class="d-flex align-center flex-wrap my-2 col-gap-2 row-gap-4"
6869
>
@@ -115,32 +116,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
115116
v-if="chartType === 'table'"
116117
:tasks="filteredTasks"
117118
:timing-option="timingOption"
119+
v-model:initial-options="dataTableOptions"
118120
/>
119121
<BoxPlot
120122
v-else-if="chartType === 'box'"
121123
:tasks="filteredTasks"
122124
:timing-option="timingOption"
123-
sort-input-teleport-target="#analysis-toolbar"
125+
:sort-input-teleport-target="toolbar?.id"
126+
v-model:initial-options="boxPlotOptions"
124127
/>
125128
<TimeSeries
126129
v-else-if="chartType === 'timeSeries'"
127130
:workflowIDs="workflowIDs"
128131
:platform-option="tasksFilter.platformOption"
129132
:timing-option="timingOption"
130-
sort-input-teleport-target="#analysis-toolbar"
133+
:sort-input-teleport-target="toolbar?.id"
134+
v-model:initial-options="timeseriesPlotOptions"
131135
/>
132136
</v-container>
133137
</div>
134138
</template>
135139

136140
<script>
141+
import { ref } from 'vue'
137142
import {
138143
debounce,
139144
pick,
140145
} from 'lodash'
141146
import gql from 'graphql-tag'
142147
import { getPageTitle } from '@/utils/index'
143148
import graphqlMixin from '@/mixins/graphql'
149+
import {
150+
initialOptions,
151+
updateInitialOptionsEvent,
152+
useInitialOptions
153+
} from '@/utils/initialOptions'
144154
import DeltasCallback from '@/services/callbacks'
145155
import AnalysisTable from '@/components/cylc/analysis/AnalysisTable.vue'
146156
import BoxPlot from '@/components/cylc/analysis/BoxPlot.vue'
@@ -248,18 +258,60 @@ export default {
248258
this.tasksQuery()
249259
},
250260
261+
emits: [updateInitialOptionsEvent],
262+
263+
props: { initialOptions },
264+
265+
setup (props, { emit }) {
266+
/**
267+
* The task name, timing option and platform filter state.
268+
* @type {import('vue').Ref<object>}
269+
*/
270+
const tasksFilter = useInitialOptions('tasksFilter', { props, emit }, { name: '', timingOption: 'totalTimes', platformOption: -1 })
271+
272+
/**
273+
* Determines the Analysis type ('table' | 'box' | 'timeSeries')
274+
* @type {import('vue').Ref<string>}
275+
*/
276+
const chartType = useInitialOptions('chartType', { props, emit }, 'table')
277+
278+
/** @type {import('vue').Ref<HTMLElement>} template ref */
279+
const toolbar = ref(null)
280+
281+
/**
282+
* The Vuetify data table options (sortBy, page etc).
283+
* @type {import('vue').Ref<object>}
284+
*/
285+
const dataTableOptions = useInitialOptions('dataTableOptions', { props, emit })
286+
287+
/**
288+
* The Vuetify box and whisker plot options (sortBy, page etc).
289+
* @type {import('vue').Ref<object>}
290+
*/
291+
const boxPlotOptions = useInitialOptions('boxPlotOptions', { props, emit })
292+
293+
/**
294+
* The Vuetify box and whisker plot options (displayedTasks, showOrigin).
295+
* @type {import('vue').Ref<object>}
296+
*/
297+
const timeseriesPlotOptions = useInitialOptions('timeseriesPlotOptions', { props, emit })
298+
299+
return {
300+
tasksFilter,
301+
chartType,
302+
toolbar,
303+
dataTableOptions,
304+
boxPlotOptions,
305+
timeseriesPlotOptions
306+
}
307+
},
308+
251309
data () {
252310
const tasks = []
253311
return {
254312
callback: new AnalysisTaskCallback(tasks),
255313
/** Object containing all of the tasks added by the callback */
256314
tasks,
257-
tasksFilter: {
258-
name: '',
259-
timingOption: 'totalTimes',
260-
platformOption: -1,
261-
},
262-
chartType: 'table',
263315
}
264316
},
265317

0 commit comments

Comments
 (0)