Skip to content

Commit c6ae6ae

Browse files
committed
Simplify TreeItem and fix tests
1 parent b6c2d08 commit c6ae6ae

File tree

6 files changed

+156
-37
lines changed

6 files changed

+156
-37
lines changed

cypress/component/treeItem.cy.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import TreeItem from '@/components/cylc/tree/TreeItem.vue'
19+
import {
20+
simpleCyclepointNode,
21+
} from '$tests/unit/components/cylc/tree/tree.data'
22+
23+
// Get lists of: a) all tree item nodes; b) only visible ones.
24+
Cypress.Commands.add('getNodeTypes', () => {
25+
cy.get('.c-treeitem')
26+
.then(($els) => {
27+
const all = Array.from($els, ({ dataset }) => dataset.nodeType)
28+
const visible = all.filter((val, i) => $els[i].checkVisibility())
29+
return { all, visible }
30+
})
31+
})
32+
33+
// Expand/collapse the first node of this type.
34+
Cypress.Commands.add('toggleNode', (nodeType) => {
35+
cy.get(`[data-node-type=${nodeType}] .node-expand-collapse-button:first`).click()
36+
})
37+
38+
describe('TreeItem component', () => {
39+
it('expands/collapses children', () => {
40+
cy.vmount(TreeItem, {
41+
props: {
42+
node: simpleCyclepointNode,
43+
filteredOutNodesCache: new WeakMap(),
44+
},
45+
})
46+
cy.addVuetifyStyles(cy)
47+
48+
cy.getNodeTypes()
49+
.should('deep.equal', {
50+
// Auto expand everything down to task nodes by default
51+
all: ['cycle', 'task'],
52+
visible: ['cycle', 'task']
53+
})
54+
55+
cy.toggleNode('task')
56+
cy.getNodeTypes()
57+
.should('deep.equal', {
58+
all: ['cycle', 'task', 'job'],
59+
visible: ['cycle', 'task', 'job']
60+
})
61+
62+
cy.toggleNode('cycle')
63+
cy.getNodeTypes()
64+
.should('deep.equal', {
65+
// All previously expanded nodes under cycle should be hidden but remain rendered
66+
all: ['cycle', 'task', 'job'],
67+
visible: ['cycle']
68+
})
69+
70+
cy.toggleNode('cycle')
71+
cy.toggleNode('job')
72+
cy.getNodeTypes()
73+
.should('deep.equal', {
74+
// Job node does not use a child TreeItem
75+
all: ['cycle', 'task', 'job'],
76+
visible: ['cycle', 'task', 'job']
77+
})
78+
})
79+
})

src/components/cylc/tree/TreeItem.vue

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
<div
2020
v-show="!filteredOutNodesCache.get(node)"
2121
class="c-treeitem"
22+
:data-node-type="node.type"
2223
>
2324
<div
2425
class="node d-flex align-center"
@@ -30,7 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
3031
v-if="renderExpandCollapseBtn"
3132
aria-label="Expand/collapse"
3233
class="node-expand-collapse-button flex-shrink-0"
33-
@click="toggleExpandCollapse"
34+
@click="toggleExpandCollapse()"
3435
:style="expandCollapseBtnStyle"
3536
xmlns="http://www.w3.org/2000/svg"
3637
viewBox="0 0 24 24"
@@ -134,7 +135,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
134135
class="ml-2 bg-grey text-white"
135136
size="small"
136137
link
137-
@click="toggleExpandCollapse"
138+
@click="toggleExpandCollapse()"
138139
>
139140
+{{ jobMessageOutputs.length - 5 }}
140141
</v-chip>
@@ -184,6 +185,8 @@ import {
184185
jobMessageOutputs
185186
} from '@/utils/tasks'
186187
import { getIndent, getNodeChildren } from '@/components/cylc/tree/util'
188+
import { once } from '@/utils'
189+
import { useToggle } from '@vueuse/core'
187190
188191
export default {
189192
name: 'TreeItem',
@@ -240,27 +243,22 @@ export default {
240243
},
241244
},
242245
243-
data () {
246+
setup (props) {
247+
const [isExpanded, toggleExpandCollapse] = useToggle(
248+
props.autoExpandTypes.includes(props.node.type)
249+
)
250+
// Toggles to true when this.isExpanded first becomes true and doesn't get recomputed afterwards
251+
const renderChildren = once(isExpanded)
252+
244253
return {
245-
manuallyExpanded: null,
254+
isExpanded,
255+
latestJob,
256+
renderChildren,
257+
toggleExpandCollapse,
246258
}
247259
},
248260
249261
computed: {
250-
isExpanded: {
251-
get () {
252-
return this.manuallyExpanded ?? this.autoExpandTypes.includes(this.node.type)
253-
},
254-
set (value) {
255-
this.manuallyExpanded = value
256-
}
257-
},
258-
259-
renderChildren () {
260-
// Toggles to true when this.isExpanded first becomes true and doesn't get recomputed afterwards
261-
return this.renderChildren || this.isExpanded
262-
},
263-
264262
hasChildren () {
265263
return (
266264
// "job" nodes have auto-generated "job-detail" nodes
@@ -310,13 +308,6 @@ export default {
310308
}
311309
},
312310
313-
methods: {
314-
toggleExpandCollapse () {
315-
this.isExpanded = !this.isExpanded
316-
},
317-
latestJob
318-
},
319-
320311
icons: {
321312
mdiChevronRight,
322313
},

src/utils/index.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
import { watch } from 'vue'
18+
import { ref, watch } from 'vue'
1919

2020
/**
2121
* Watch source until it is truthy, then call the callback (and stop watching).
@@ -27,6 +27,10 @@ import { watch } from 'vue'
2727
* @param {import('vue').WatchOptions?} options
2828
*/
2929
export function when (source, callback, options = {}) {
30+
if (source.value) {
31+
callback()
32+
return
33+
}
3034
const unwatch = watch(
3135
source,
3236
(ready) => {
@@ -35,7 +39,7 @@ export function when (source, callback, options = {}) {
3539
callback()
3640
}
3741
},
38-
{ immediate: true, ...options }
42+
options
3943
)
4044
}
4145

@@ -53,3 +57,20 @@ export function until (source, options = {}) {
5357
when(source, resolve, options)
5458
})
5559
}
60+
61+
/**
62+
* Return a ref that is permanently set to true when the source becomes truthy.
63+
*
64+
* @param {import('vue').WatchSource} source
65+
* @param {import('vue').WatchOptions?} options
66+
* @returns {import('vue').Ref<boolean>}
67+
*/
68+
export function once (source, options = {}) {
69+
const _ref = ref(false)
70+
when(
71+
source,
72+
() => { _ref.value = true },
73+
options
74+
)
75+
return _ref
76+
}

tests/unit/components/cylc/tree/tree.data.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ const simpleWorkflowTree4Nodes = [
237237
__typename: 'CyclePoint',
238238
state: 'failed'
239239
},
240-
children: [],
240+
children: ['stub'],
241241
familyTree: [
242242
{
243243
id: '~user/workflow1//20100101T0000Z/root',

tests/unit/components/cylc/tree/treeitem.vue.spec.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/*
22
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
33
*
44
* This program is free software: you can redistribute it and/or modify
@@ -116,18 +116,17 @@ describe('TreeItem component', () => {
116116

117117
describe('children', () => {
118118
it.each([
119-
{ manuallyExpanded: null, expected: ['CyclePoint', 'TaskProxy'] },
120-
{ manuallyExpanded: true, expected: ['CyclePoint', 'TaskProxy', 'Job'] },
121-
{ manuallyExpanded: false, expected: [] },
122-
])('recursively mounts child TreeItems if expanded ($manuallyExpanded)', ({ manuallyExpanded, expected }) => {
119+
{ autoExpandTypes: undefined, expected: ['CyclePoint', 'TaskProxy'] },
120+
{ autoExpandTypes: ['workflow', 'cycle', 'family', 'task'], expected: ['CyclePoint', 'TaskProxy', 'Job'] },
121+
{ autoExpandTypes: ['workflow'], expected: ['CyclePoint'] },
122+
{ autoExpandTypes: [], expected: [] },
123+
])('recursively mounts child TreeItems ($autoExpandTypes)', ({ autoExpandTypes, expected }) => {
123124
const wrapper = mountFunction({
124125
props: {
125126
node: simpleWorkflowNode,
126127
filteredOutNodesCache: new WeakMap(),
128+
autoExpandTypes,
127129
},
128-
data: () => ({
129-
manuallyExpanded,
130-
}),
131130
})
132131
expect(
133132
wrapper.findAllComponents({ name: 'TreeItem' })

tests/unit/utils/index.spec.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import { nextTick, ref } from 'vue'
19-
import { when, until } from '@/utils/index'
19+
import { once, when, until } from '@/utils/index'
2020

2121
describe.each([
2222
{ func: when, description: 'watches source until true and then stops watching' },
@@ -42,3 +42,32 @@ describe.each([
4242
expect(counter).toEqual(1)
4343
})
4444
})
45+
46+
describe('when()', () => {
47+
it('works for a source that is already truthy', () => {
48+
const source = ref(true)
49+
let counter = 0
50+
when(source, () => counter++)
51+
expect(counter).toEqual(1)
52+
})
53+
})
54+
55+
describe('once()', () => {
56+
it('returns a ref that permanently toggles to true when the source bevomes truthy', async () => {
57+
const source = ref(false)
58+
const myRef = once(source)
59+
expect(myRef.value).toEqual(false)
60+
source.value = true
61+
await nextTick()
62+
expect(myRef.value).toEqual(true)
63+
source.value = false
64+
await nextTick()
65+
expect(myRef.value).toEqual(true)
66+
})
67+
68+
it('works for a source that is already truthy', () => {
69+
const source = ref(true)
70+
const myRef = once(source)
71+
expect(myRef.value).toEqual(true)
72+
})
73+
})

0 commit comments

Comments
 (0)