Skip to content

Commit 600e3ec

Browse files
graph: fix graph rendering (cylc#1780)
* There appears to have been a recent change in how multiple browsers measure the "bounding box" of SVG elements. * This caused graph nodes to be measured much larger than the area they actually render to. * This messed with the graph because we feed these dimensions into GraphViz when creating the virtual graph causing the nodes in the virtual graph to be given too much space. An error which is subsequently translated to the SVG graph. * The changes in "bounding box" measurement appear to affect SVG Symbols and rotations. * This commit removes the use of SVG symbols (and their related "use" elements) and moves the rotation from the circle element to a group. * The use of SVG symbols made it easier for us to capture click events from behind the svgPanZoom layer. * Removing SVG symbols required changes to the way click events are captured in the graph. Note in SVG the click events land on graphical elements (i.e. children), not their containing groups (i.e. parents).
1 parent 3d64d28 commit 600e3ec

File tree

7 files changed

+51
-78
lines changed

7 files changed

+51
-78
lines changed

cypress/component/cylc-graph-node.cy.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,14 @@ const GraphNodeSVG = defineComponent({
6767
render () {
6868
return h(
6969
'svg',
70-
{ id: 'app', class: 'job_theme--default', width: '100%', height: '100%' },
70+
{
71+
id: 'app',
72+
class: 'job_theme--default',
73+
width: '100%',
74+
height: '100%',
75+
// the "-40" bit is to account for the task modifiers
76+
viewBox: '-40,-40,450,150'
77+
},
7178
[
7279
h(GraphNode, this.$attrs)
7380
]
@@ -88,10 +95,10 @@ describe('graph node component', () => {
8895
props: { task, jobs }
8996
}
9097
)
91-
// there should be 4 jobs (8 svg nodes)
98+
// there should be 4 jobs
9299
cy.get('.c-graph-node:last .jobs')
93100
.children()
94-
.should('have.length', 8)
101+
.should('have.length', 4)
95102
// there shouldn't be a job overflow indicator
96103
cy.get('.c-graph-node:last .job-overflow').should('not.exist')
97104

@@ -113,10 +120,10 @@ describe('graph node component', () => {
113120
props: { task, jobs, maxJobs: 4 }
114121
}
115122
)
116-
// there should be <maxJobs> jobs (<maxJobs * 2 svg nodes)
123+
// there should be <maxJobs> jobs
117124
cy.get('.c-graph-node:last .jobs')
118125
.children()
119-
.should('have.length', 8)
126+
.should('have.length', 4)
120127
// there should be a job overflow indicator with the number of overflow jobs
121128
cy.get('.c-graph-node:last .job-overflow')
122129
.should('exist')
@@ -189,7 +196,7 @@ describe('graph node component', () => {
189196
{ overwrite: true, disableTimersAndAnimations: false }
190197
)
191198
// check the progress animation
192-
cy.get('.c8-task:last .status > .progress')
199+
cy.get('.c8-task:last .status .progress')
193200
// the animation duration should be equal to the expected job duration
194201
.should('have.css', 'animation-duration', `${MEAN_ELAPSED_TIME}s`)
195202
// the offset should be set to the "percent" of the expected job duration

cypress/component/cylc-icons.cy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('Task component', () => {
9898
{ overwrite: true, disableTimersAndAnimations: false }
9999
)
100100
// check the progress animation
101-
.get('.c8-task:last .status > .progress')
101+
.get('.c8-task:last .status .progress')
102102
// the animation duration should be equal to the expected job duration
103103
.should('have.css', 'animation-duration', `${MEAN_ELAPSED_TIME}s`)
104104
// the offset should be set to the "percent" of the expected job duration

src/components/cylc/GraphNode.vue

Lines changed: 14 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
<template>
1919
<g class="c-graph-node">
2020
<!-- the task icon -->
21-
<symbol :id="nodeID" viewBox="-40 -40 140 140">
22-
<!--
23-
Use a "symbol" for the task node in order to apply a viewBox to it.
24-
This both contains it and makes it clickable.
25-
26-
NOTE: Due to the viewBox we use here the coordinate system ends up
27-
offset by -20px. This doesn't impact most things, however, rotations
28-
can be sensitive to this change causing the rotated elements to end up
29-
in the wrong places. To counteract this we provide the coordinate offset
30-
to the task component.
31-
-->
32-
<SVGTask
33-
:task="task.node"
34-
:modifierSize="0.5"
35-
:startTime="startTime"
36-
:coordinateOffset="-20"
37-
/>
38-
</symbol>
39-
<use
40-
:href="`#${nodeID}`"
41-
x="0" y="0"
42-
width="150" height="150"
21+
<SVGTask
22+
:task="task.node"
23+
:modifierSize="0.5"
24+
:startTime="startTime"
25+
viewBox="-40 -40 140 140"
4326
v-cylc-object="task"
27+
x="0" y="0"
4428
/>
4529

30+
<!-- the label -->
4631
<g :transform="labelTransform">
47-
<!-- the task name -->
4832
<text
49-
x="180" y="70"
33+
x="130" y="25"
5034
font-size="45"
5135
>
5236
{{ task.name }}
5337
</text>
5438

55-
<!-- the cycle point -->
5639
<text
57-
x="180" y="115"
40+
x="130" y="65"
5841
font-size="30"
5942
>
6043
{{ task.tokens.cycle }}
@@ -64,7 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6447
<!-- the job(s) -->
6548
<g
6649
transform="
67-
translate(180, 125)
50+
translate(130, 75)
6851
scale(0.3, 0.3)
6952
"
7053
>
@@ -77,23 +60,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
7760
scale(${ (index === 0) ? mostRecentJobScale : '1' })
7861
`"
7962
>
80-
<symbol
81-
:id="`${nodeID}-${index}`"
63+
<Job
64+
:svg="true"
65+
:status="job.node.state"
8266
viewBox="0 0 100 100"
83-
:class="`job_theme--${jobTheme}`"
84-
>
85-
<!--
86-
Use a "symbol" for job nodes in order to make them clickable.
87-
The job theme must be set on the "symbol" for styling to work.
88-
-->
89-
<job
90-
:svg="true"
91-
:status="job.node.state"
92-
/>
93-
</symbol>
94-
<use
95-
:href="`#${nodeID}-${index}`"
96-
width="100" height="100"
9767
v-cylc-object="job"
9868
/>
9969
</g>
@@ -174,7 +144,7 @@ export default {
174144
if (this.jobs.length) {
175145
return ''
176146
}
177-
return 'translate(0, 14)'
147+
return 'translate(0, 20)'
178148
},
179149
previousJobOffset () {
180150
// the most recent job is larger so all subsequent jobs need to be bumped

src/components/cylc/SVGTask.vue

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6868
the "stroke-dashoffset" to find the new values for 0% and 100%.
6969
Then copy these values to the corresponding CSS animation keyframes.
7070
-->
71-
<circle
72-
class="progress"
73-
cx="50"
74-
cy="50"
75-
r="16"
76-
stroke-width="50"
77-
stroke-dasharray="157"
78-
:transform="progressTransform()"
79-
:style="getRunningStyle()"
80-
/>
71+
<g transform="rotate(-90, 50, 50)">
72+
<circle
73+
class="progress"
74+
cx="50"
75+
cy="50"
76+
r="16"
77+
stroke-width="50"
78+
stroke-dasharray="157"
79+
:style="getRunningStyle()"
80+
/>
81+
</g>
8182
<!-- dot
8283
8384
A small dot at the centre of the outline used to represent the preparing
@@ -274,12 +275,6 @@ export default {
274275
type: Number,
275276
default: 0.7
276277
},
277-
coordinateOffset: {
278-
// You may need to provide this if encorporating this icon into a viewBox
279-
// otherwise the progress indicator may end up in the wrong place.
280-
type: Number,
281-
default: 0
282-
}
283278
},
284279
methods: {
285280
getRunningStyle () {
@@ -334,9 +329,6 @@ export default {
334329
translate(${translation}, ${translation})
335330
`
336331
},
337-
progressTransform () {
338-
return `rotate(-90, ${this.coordinateOffset}, ${this.coordinateOffset})`
339-
}
340332
}
341333
}
342334
</script>

src/components/cylc/cylcObject/Menu.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,17 @@ export default {
283283
*/
284284
onClickOutside (e) {
285285
this.closeMenu()
286-
if (e.target?.getAttribute('data-c-interactive')) {
287-
this.showMenu = true
286+
287+
// check if the thing being clicked on is a child of the thing that the
288+
// menu is open for
289+
let target = e.target
290+
while (target) {
291+
if (target?.getAttribute('data-c-interactive')) {
292+
// keep the menu open
293+
this.showMenu = true
294+
break
295+
}
296+
target = target.parentElement
288297
}
289298
},
290299

src/styles/index.scss

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ html {
5858
&:hover {
5959
cursor: pointer;
6060
}
61-
62-
> * {
63-
// Prevent click handler from thinking any child elements are the target
64-
pointer-events: none;
65-
}
6661
}
6762

6863
.position-relative {

src/views/Graph.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6969
<g
7070
class="edges"
7171
:transform="
72-
(transpose) ? 'translate(15, 30)' : 'translate(45, 5)'
72+
(transpose) ? 'translate(-25, -8)' : 'translate(0, -25)'
7373
"
7474
>
7575
<g

0 commit comments

Comments
 (0)