Maintain expanded state of JobStep during polling

The InvocationStepProvider updates the invocation state in 3 second
intervals, and with it the jobs array. The new jobs in the  array don't
have the _showDetails attribute, so all expanded view will be reset.
This fixes that by keeping a component local state for the _showDetails
attribute.
This commit is contained in:
mvdbeek
2021-01-15 13:08:41 +01:00
parent 5d1d29d838
commit fff4fd5b04
3 changed files with 122 additions and 8 deletions
@@ -0,0 +1,71 @@
import BootstrapVue from "bootstrap-vue";
import JobStep from "./JobStep";
import { mount } from "@vue/test-utils";
import jobs from "./test/json/jobs.json";
jest.mock("../History/caching");
import { createLocalVue } from "@vue/test-utils";
// create an extended `Vue` constructor
const localVue = createLocalVue();
// install plugins as normal
localVue.use(BootstrapVue);
describe("DatasetUIWrapper.vue with Dataset", () => {
let wrapper;
let propsData;
beforeEach(async () => {
propsData = {
jobs: jobs,
};
wrapper = mount(JobStep, {
localVue,
propsData,
stubs: {
JobProvider: { template: "<div class='expanded'>I am expanded</div>" },
},
});
});
test("it renders a table with 2 jobs", async () => {
expect(wrapper.find("tbody").findAll("tr").length).toBe(2);
});
test("it expands on row click", async () => {
// verify no item is expanded
expect(wrapper.vm.toggledItems["1"]).toBe(undefined);
expect(wrapper.find(".expanded").exists()).toBeFalsy();
// expand
wrapper.find("tbody").find("tbody").find("tr").trigger("click");
await localVue.nextTick();
expect(wrapper.vm.toggledItems["1"]).toBeTruthy();
expect(wrapper.find(".expanded").exists()).toBeTruthy();
wrapper.find("tbody").find("tbody").find("tr").trigger("click");
// close again
await localVue.nextTick();
expect(wrapper.vm.toggledItems["1"]).toBeFalsy();
expect(wrapper.find(".expanded").exists()).toBeFalsy();
});
test("it sustains expanded on prop update", async () => {
// verify no item is expanded
expect(wrapper.vm.toggledItems["1"]).toBe(undefined);
expect(wrapper.find(".expanded").exists()).toBeFalsy();
// expand
wrapper.find("tbody").find("tbody").find("tr").trigger("click");
await localVue.nextTick();
expect(wrapper.vm.toggledItems["1"]).toBeTruthy();
expect(wrapper.find(".expanded").exists()).toBeTruthy();
// 2 collapsed rows, plus 1 expanded row
expect(wrapper.find("tbody").findAll("tr").length).toBe(3);
// update data
const additionalJob = { ...jobs[0], id: 3 };
wrapper.setProps({ jobs: [...jobs, additionalJob] });
await localVue.nextTick();
// verify new data is displayed
expect(wrapper.find("tbody").findAll("tr").length).toBe(4);
// verify first row is still expanded
expect(wrapper.vm.toggledItems["1"]).toBeTruthy();
expect(wrapper.find(".expanded").exists()).toBeTruthy();
});
});
@@ -1,13 +1,6 @@
<template>
<b-card v-if="jobs">
<b-table
small
caption-top
:items="jobs"
:fields="fields"
v-if="jobs"
@row-clicked="(item) => $set(item, '_showDetails', !item._showDetails)"
>
<b-table small caption-top :items="jobsProvider" :fields="fields" primary-key="id" @row-clicked="toggleDetails">
<template v-slot:row-details="row">
<job-provider
:id="row.item.id"
@@ -53,6 +46,31 @@ export default {
props: {
jobs: { type: Array, required: true },
},
methods: {
jobsProvider(ctx, callback) {
// It may seem unnecessary to use a provider here, since the jobs prop
// is being updated externally. However we need to keep track of the
// _showDetails attribute which determines whether the row is shown as expanded
this.$watch(
"jobs",
function () {
// update new jobs array with current state
const toggledJobs = this.jobs.map((e) => {
return { ...e, _showDetails: !!this.toggledItems[e.id] };
});
callback(toggledJobs);
},
{ immediate: true }
);
return null;
},
toggleDetails(item) {
// toggle item
item._showDetails = !item._showDetails;
// update state
this.toggledItems[item.id] = item._showDetails;
},
},
data() {
return {
fields: [
@@ -60,6 +78,7 @@ export default {
{ key: "update_time", label: "Updated", sortable: true },
{ key: "create_time", label: "Created", sortable: true },
],
toggledItems: {},
};
},
};
@@ -0,0 +1,24 @@
[
{
"model_class": "Job",
"id": "1",
"state": "new",
"exit_code": 0,
"update_time": "2021-01-15T10:38:36.015269",
"create_time": "2021-01-15T10:38:21.462106",
"galaxy_version": "21.01",
"tool_id": "cat_data_and_sleep",
"history_id": "b6aaf20167e56df2"
},
{
"model_class": "Job",
"id": "2",
"state": "new",
"exit_code": 0,
"update_time": "2021-01-15T10:38:36.015269",
"create_time": "2021-01-15T10:38:21.462106",
"galaxy_version": "21.01",
"tool_id": "cat_data_and_sleep",
"history_id": "b6aaf20167e56df2"
}
]