1
- import React from " react" ;
2
- import { BarStack } from " @visx/shape" ;
3
- import { SeriesPoint } from " @visx/shape/lib/types" ;
4
- import { Group } from " @visx/group" ;
5
- import { Grid } from " @visx/grid" ;
6
- import { AxisBottom , AxisLeft } from " @visx/axis" ;
7
- import { scaleBand , scaleLinear , scaleOrdinal } from " @visx/scale" ;
8
- import { useTooltip , useTooltipInPortal , defaultStyles } from " @visx/tooltip" ;
1
+ import React from ' react' ;
2
+ import { BarStack } from ' @visx/shape' ;
3
+ import { SeriesPoint } from ' @visx/shape/lib/types' ;
4
+ import { Group } from ' @visx/group' ;
5
+ import { Grid } from ' @visx/grid' ;
6
+ import { AxisBottom , AxisLeft } from ' @visx/axis' ;
7
+ import { scaleBand , scaleLinear , scaleOrdinal } from ' @visx/scale' ;
8
+ import { useTooltip , useTooltipInPortal , defaultStyles } from ' @visx/tooltip' ;
9
9
import { Text } from '@visx/text' ;
10
- import { schemeSet3 } from "d3-scale-chromatic" ;
11
-
10
+ import { schemeSet3 } from 'd3-scale-chromatic' ;
12
11
13
12
/* NOTES
14
13
Current issues
15
- 1. Not fully compatible with recoil apps. Reference the recoil-todo-test.
16
- Barstacks display inconsistently...however, almost always displays upon initial test app load or
14
+ 1. Not fully compatible with recoil apps. Reference the recoil-todo-test.
15
+ Barstacks display inconsistently...however, almost always displays upon initial test app load or
17
16
when empty button is clicked. Updating state after initial app load typically makes bars disappear.
18
- However, cycling between updating state and then emptying sometimes fixes the stack bars. Important to note - all snapshots
17
+ However, cycling between updating state and then emptying sometimes fixes the stack bars. Important to note - all snapshots
19
18
do render (check HTML doc) within the chrome extension but they do not display because height is not consistently passed to each bar.
20
19
This side effect is only seen in recoil apps...
21
20
*/
22
21
23
-
24
22
/* TYPESCRIPT */
25
23
type snapshot = any ;
24
+
26
25
type TooltipData = {
27
26
bar : SeriesPoint < snapshot > ;
28
27
key : CityName ;
@@ -34,7 +33,8 @@ type TooltipData = {
34
33
color : string ;
35
34
} ;
36
35
37
- export type BarStackProps = {
36
+ // typescript for PROPS from StateRoute.tsx
37
+ type BarStackProps = {
38
38
width : number ;
39
39
height : number ;
40
40
margin ?: { top : number ; right : number ; bottom : number ; left : number } ;
@@ -44,55 +44,53 @@ export type BarStackProps = {
44
44
} ;
45
45
46
46
/* DEFAULTS */
47
- const defaultMargin = { top : 60 , right : 30 , bottom : 0 , left : 50 } ;
47
+ const defaultMargin = {
48
+ top : 60 , right : 30 , bottom : 0 , left : 50 ,
49
+ } ;
48
50
const axisColor = '#679DCA' ;
49
- const background = " #242529" ;
51
+ const background = ' #242529' ;
50
52
const tooltipStyles = {
51
53
...defaultStyles ,
52
54
minWidth : 60 ,
53
- backgroundColor : " rgba(0,0,0,0.9)" ,
54
- color : " white"
55
+ backgroundColor : ' rgba(0,0,0,0.9)' ,
56
+ color : ' white' ,
55
57
} ;
56
58
57
59
/* DATA HANDLING HELPER FUNCTIONS */
58
- const getPerfMetrics = ( snapshots , snapshotsIds ) => {
59
- return snapshots . reduce ( ( perfSnapshots , curSnapshot , idx ) => {
60
- return perfSnapshots . concat ( traverse ( curSnapshot , { snapshotId :snapshotsIds [ idx ] } ) )
61
- } , [ ] )
62
- }
63
60
64
- // traverses a single snapshot either returning all component rendering times OR all component state types. Depends on 2nd arg
61
+ // Returns an array of objects (snapshots) with key-value pairs of each component and corresponding render time
62
+ const getPerfMetrics = ( snapshots , snapshotsIds ) => snapshots . reduce ( ( perfSnapshots , curSnapshot , idx ) => perfSnapshots . concat ( traverse ( curSnapshot , { snapshotId : snapshotsIds [ idx ] } ) ) , [ ] ) ;
63
+
64
+ // traverses a single snapshot - returns either all component rendering times OR all component state types. Depends on 2nd arg
65
65
const traverse = ( snapshot , data = { } ) => {
66
- if ( ! snapshot . children [ 0 ] ) return
67
- for ( let i = 0 ; i < snapshot . children . length ; i ++ ) {
68
- const componentName = snapshot . children [ i ] . name + - [ i + 1 ]
66
+ if ( ! snapshot . children [ 0 ] ) return ;
67
+ for ( let i = 0 ; i < snapshot . children . length ; i ++ ) {
68
+ const componentName = snapshot . children [ i ] . name + - [ i + 1 ] ;
69
69
// Get component Type
70
- if ( ! data . snapshotId ) {
71
- if ( snapshot . children [ i ] . state !== 'stateless' ) data [ componentName ] = 'STATEFUL'
70
+ if ( ! data . snapshotId ) {
71
+ if ( snapshot . children [ i ] . state !== 'stateless' ) data [ componentName ] = 'STATEFUL' ;
72
72
else data [ componentName ] = snapshot . children [ i ] . state ;
73
- }
73
+ }
74
74
// Get component Rendering Time
75
- else if ( snapshot . children [ i ] . componentData . actualDuration ) {
75
+ else if ( snapshot . children [ i ] . componentData . actualDuration ) {
76
76
const renderTime = Number ( Number . parseFloat ( snapshot . children [ i ] . componentData . actualDuration ) . toPrecision ( 5 ) ) ;
77
77
data [ componentName ] = renderTime ;
78
78
}
79
- traverse ( snapshot . children [ i ] , data )
79
+ traverse ( snapshot . children [ i ] , data ) ;
80
80
}
81
- return data
82
- }
81
+ return data ;
82
+ } ;
83
83
84
84
const getSnapshotIds = ( obj , snapshotIds = [ ] ) => {
85
85
snapshotIds . push ( `${ obj . name } .${ obj . branch } ` ) ;
86
- if ( obj . children ) {
87
- obj . children . forEach ( child => {
88
- getSnapshotIds ( child , snapshotIds ) ;
89
- } ) ;
90
- }
91
- return snapshotIds
86
+ if ( obj . children ) {
87
+ obj . children . forEach ( child => {
88
+ getSnapshotIds ( child , snapshotIds ) ;
89
+ } ) ;
90
+ }
91
+ return snapshotIds ;
92
92
} ;
93
93
94
-
95
-
96
94
/* EXPORT COMPONENT */
97
95
export default function PerformanceVisx ( {
98
96
width,
@@ -101,69 +99,67 @@ export default function PerformanceVisx({
101
99
margin = defaultMargin ,
102
100
snapshots,
103
101
hierarchy,
104
- } : BarStackProps )
105
-
106
- {
102
+ } : BarStackProps ) {
107
103
const {
108
104
tooltipOpen,
109
105
tooltipLeft,
110
106
tooltipTop,
111
107
tooltipData,
112
- hideTooltip, ƒ
113
- showTooltip
108
+ hideTooltip,
109
+ showTooltip,
114
110
} = useTooltip < TooltipData > ( ) ;
115
111
116
- let tooltipTimeout : number ;
112
+ let tooltipTimeout : number ;
117
113
118
- // filter and structure incoming data for VISX
119
- const data = getPerfMetrics ( snapshots , getSnapshotIds ( hierarchy ) )
120
- const keys = Object . keys ( data [ 0 ] ) . filter ( ( d ) => d !== "snapshotId" ) as CityName [ ] ;
121
- const allComponentStates = traverse ( snapshots [ 0 ] )
114
+ const { containerRef, TooltipInPortal } = useTooltipInPortal ( ) ;
122
115
123
- // create array of total render times for each snapshot
124
- const totalRenderArr = data . reduce ( ( totalRender , curSnapshot ) => {
125
- const curRenderTotal = keys . reduce ( ( acc , cur ) => {
126
- acc += Number ( curSnapshot [ cur ] ) ;
127
- return acc ;
128
- } , 0 ) ;
129
- totalRender . push ( curRenderTotal ) ;
130
- return totalRender ;
131
- } , [ ] as number [ ] ) ;
116
+ // filter and structure incoming data for VISX
117
+ const data = getPerfMetrics ( snapshots , getSnapshotIds ( hierarchy ) ) ;
118
+ const keys = Object . keys ( data [ 0 ] ) . filter ( d => d !== 'snapshotId' ) as CityName [ ] ;
119
+ const allComponentStates = traverse ( snapshots [ 0 ] ) ;
132
120
133
- // data accessor (used to generate scales) and formatter (add units for on hover box)
134
- const getSnapshotId = ( d : snapshot ) => d . snapshotId ;
135
- const formatSnapshotId = id => 'Snapshot ID: ' + id ;
136
- const formatRenderTime = time => time + ' ms ' ;
121
+ // create array of total render times for each snapshot
122
+ const totalRenderArr = data . reduce ( ( totalRender , curSnapshot ) => {
123
+ const curRenderTotal = keys . reduce ( ( acc , cur ) => {
124
+ acc += Number ( curSnapshot [ cur ] ) ;
125
+ return acc ;
126
+ } , 0 ) ;
127
+ totalRender . push ( curRenderTotal ) ;
128
+ return totalRender ;
129
+ } , [ ] as number [ ] ) ;
137
130
138
- // create visualization SCALES with cleaned data
139
- const snapshotIdScale = scaleBand < string > ( {
140
- domain : data . map ( getSnapshotId ) ,
141
- padding : 0.2
142
- } ) ;
131
+ // data accessor (used to generate scales) and formatter (add units for on hover box)
132
+ const getSnapshotId = ( d : snapshot ) => d . snapshotId ;
133
+ const formatSnapshotId = id => `Snapshot ID: ${ id } ` ;
134
+ const formatRenderTime = time => `${ time } ms ` ;
143
135
144
- const renderingScale = scaleLinear < number > ( {
145
- domain : [ 0 , Math . max ( ...totalRenderArr ) ] ,
146
- nice : true
147
- } ) ;
136
+ // create visualization SCALES with cleaned data
137
+ const snapshotIdScale = scaleBand < string > ( {
138
+ domain : data . map ( getSnapshotId ) ,
139
+ padding : 0.2 ,
140
+ } ) ;
148
141
149
- const colorScale = scaleOrdinal < CityName , string > ( {
150
- domain : keys ,
151
- range : schemeSet3
152
- } ) ;
142
+ const renderingScale = scaleLinear < number > ( {
143
+ domain : [ 0 , Math . max ( ... totalRenderArr ) ] ,
144
+ nice : true ,
145
+ } ) ;
153
146
154
- const { containerRef, TooltipInPortal } = useTooltipInPortal ( ) ;
147
+ const colorScale = scaleOrdinal < CityName , string > ( {
148
+ domain : keys ,
149
+ range : schemeSet3 ,
150
+ } ) ;
155
151
156
- // setting max dimensions and scale ranges
157
- if ( width < 10 ) return null ;
158
- const xMax = width - margin . left - margin . right
159
- const yMax = height - margin . top - 150 ;
160
- snapshotIdScale . rangeRound ( [ 0 , xMax ] ) ;
161
- renderingScale . range ( [ yMax , 0 ] ) ;
152
+ // setting max dimensions and scale ranges
153
+ if ( width < 10 ) return null ;
154
+ const xMax = width - margin . left - margin . right ;
155
+ const yMax = height - margin . top - 150 ;
156
+ snapshotIdScale . rangeRound ( [ 0 , xMax ] ) ;
157
+ renderingScale . range ( [ yMax , 0 ] ) ;
162
158
163
159
return width < 10 ? null : (
164
160
// relative position is needed for correct tooltip positioning
165
161
166
- < div style = { { position : " relative" } } >
162
+ < div style = { { position : ' relative' } } >
167
163
< svg ref = { containerRef } width = { width } height = { height } >
168
164
< rect
169
165
x = { 0 }
@@ -193,52 +189,50 @@ renderingScale.range([yMax, 0]);
193
189
yScale = { renderingScale }
194
190
color = { colorScale }
195
191
>
196
- { ( barStacks ) => barStacks . map ( barStack => barStack . bars . map ( ( ( bar , idx ) => (
197
- < rect
198
- key = { `bar-stack-${ barStack . index } -${ bar . index } ` }
199
- x = { bar . x }
200
- y = { bar . y }
201
- height = { bar . height === 0 ? idx + 1 : bar . height }
202
- width = { bar . width }
203
- fill = { bar . color }
192
+ { barStacks => barStacks . map ( barStack => barStack . bars . map ( ( ( bar , idx ) => (
193
+ < rect
194
+ key = { `bar-stack-${ barStack . index } -${ bar . index } ` }
195
+ x = { bar . x }
196
+ y = { bar . y }
197
+ height = { bar . height === 0 ? idx + 1 : bar . height }
198
+ width = { bar . width }
199
+ fill = { bar . color }
204
200
/* TIP TOOL EVENT HANDLERS */
205
201
// Hides tool tip once cursor moves off the current rect
206
- onMouseLeave = { ( ) => {
207
- tooltipTimeout = window . setTimeout ( ( ) => {
208
- hideTooltip ( ) ;
209
- } , 300 ) ;
210
- } }
211
- // Cursor position in window updates position of the tool tip
212
- onMouseMove = { event => {
213
- if ( tooltipTimeout ) clearTimeout ( tooltipTimeout ) ;
214
- const top = event . clientY - margin . top - bar . height ;
215
- const left = bar . x + bar . width / 2 ;
216
- showTooltip ( {
217
- tooltipData : bar ,
218
- tooltipTop : top ,
219
- tooltipLeft : left ,
220
- } ) ;
221
- } }
222
- />
223
- ) ) ,
224
- )
225
- }
202
+ onMouseLeave = { ( ) => {
203
+ tooltipTimeout = window . setTimeout ( ( ) => {
204
+ hideTooltip ( ) ;
205
+ } , 300 ) ;
206
+ } }
207
+ // Cursor position in window updates position of the tool tip
208
+ onMouseMove = { event => {
209
+ if ( tooltipTimeout ) clearTimeout ( tooltipTimeout ) ;
210
+ const top = event . clientY - margin . top - bar . height ;
211
+ const left = bar . x + bar . width / 2 ;
212
+ showTooltip ( {
213
+ tooltipData : bar ,
214
+ tooltipTop : top ,
215
+ tooltipLeft : left ,
216
+ } ) ;
217
+ } }
218
+ />
219
+ ) ) ) ) }
226
220
</ BarStack >
227
221
</ Group >
228
222
< AxisLeft
229
- top = { margin . top }
230
- left = { margin . left }
231
- scale = { renderingScale }
232
- stroke = { axisColor }
233
- tickStroke = { axisColor }
234
- strokeWidth = { 2 }
235
- tickLabelProps = { ( ) => ( {
236
- fill : axisColor ,
237
- fontSize : 11 ,
238
- verticalAnchor : 'middle' ,
239
- textAnchor : 'end'
240
- } ) }
241
- />
223
+ top = { margin . top }
224
+ left = { margin . left }
225
+ scale = { renderingScale }
226
+ stroke = { axisColor }
227
+ tickStroke = { axisColor }
228
+ strokeWidth = { 2 }
229
+ tickLabelProps = { ( ) => ( {
230
+ fill : axisColor ,
231
+ fontSize : 11 ,
232
+ verticalAnchor : 'middle' ,
233
+ textAnchor : 'end' ,
234
+ } ) }
235
+ />
242
236
< AxisBottom
243
237
top = { yMax + margin . top }
244
238
left = { margin . left }
@@ -252,10 +246,9 @@ renderingScale.range([yMax, 0]);
252
246
textAnchor : 'middle' ,
253
247
} ) }
254
248
/>
255
- < Text x = { - xMax / 2 } y = "15" transform = "rotate(-90)" fontSize = { 10 } fill = "#FFFFFF" > Rendering Time (ms) </ Text >
256
- < Text x = { xMax / 2 } y = { yMax + 100 } fontSize = { 10 } fill = "#FFFFFF" > Snapshot Id </ Text >
249
+ < Text x = { - xMax / 2 } y = "15" transform = "rotate(-90)" fontSize = { 10 } fill = "#FFFFFF" > Rendering Time (ms) </ Text >
250
+ < Text x = { xMax / 2 } y = { yMax + 100 } fontSize = { 10 } fill = "#FFFFFF" > Snapshot Id </ Text >
257
251
</ svg >
258
-
259
252
260
253
{ /* FOR HOVER OVER DISPLAY */ }
261
254
{ tooltipOpen && tooltipData && (
@@ -265,11 +258,21 @@ renderingScale.range([yMax, 0]);
265
258
left = { tooltipLeft }
266
259
style = { tooltipStyles }
267
260
>
268
- // Displays: component name, component state type, render time, snapshotID
269
- < div style = { { color : colorScale ( tooltipData . key ) } } > < strong > { tooltipData . key } </ strong > </ div >
261
+ < div style = { { color : colorScale ( tooltipData . key ) } } >
262
+ { ' ' }
263
+ < strong > { tooltipData . key } </ strong >
264
+ { ' ' }
265
+ </ div >
270
266
< div > { allComponentStates [ tooltipData . key ] } </ div >
271
- < div > { formatRenderTime ( tooltipData . bar . data [ tooltipData . key ] ) } </ div >
272
- < div > < small > { formatSnapshotId ( getSnapshotId ( tooltipData . bar . data ) ) } </ small > </ div >
267
+ < div >
268
+ { ' ' }
269
+ { formatRenderTime ( tooltipData . bar . data [ tooltipData . key ] ) }
270
+ { ' ' }
271
+ </ div >
272
+ < div >
273
+ { ' ' }
274
+ < small > { formatSnapshotId ( getSnapshotId ( tooltipData . bar . data ) ) } </ small >
275
+ </ div >
273
276
</ TooltipInPortal >
274
277
) }
275
278
</ div >
0 commit comments