@cdc/chart 4.22.11 → 4.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/495.js +3 -0
  2. package/dist/703.js +1 -0
  3. package/dist/cdcchart.js +723 -6
  4. package/examples/box-plot-data.json +71 -0
  5. package/examples/box-plot.csv +5 -0
  6. package/examples/{private/yaxis-test.json → box-plot.json} +47 -56
  7. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +3 -1
  8. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +85 -16
  9. package/examples/new-data.csv +17 -0
  10. package/examples/newdata.json +90 -0
  11. package/package.json +3 -2
  12. package/src/CdcChart.tsx +150 -94
  13. package/src/components/BarChart.tsx +156 -226
  14. package/src/components/BoxPlot.js +92 -0
  15. package/src/components/DataTable.tsx +28 -12
  16. package/src/components/EditorPanel.js +151 -104
  17. package/src/components/Filters.js +131 -0
  18. package/src/components/Legend.js +8 -1
  19. package/src/components/LineChart.tsx +64 -13
  20. package/src/components/LinearChart.tsx +120 -81
  21. package/src/components/PairedBarChart.tsx +1 -1
  22. package/src/components/PieChart.tsx +12 -2
  23. package/src/components/useIntersectionObserver.tsx +9 -7
  24. package/src/data/initial-state.js +14 -8
  25. package/src/hooks/useReduceData.ts +8 -5
  26. package/src/index.html +51 -51
  27. package/src/scss/DataTable.scss +1 -1
  28. package/src/scss/main.scss +53 -22
  29. package/examples/private/filters.json +0 -170
  30. package/examples/private/line-test-data.json +0 -22
  31. package/examples/private/line-test-two.json +0 -210
  32. package/examples/private/line-test.json +0 -102
  33. package/examples/private/new.json +0 -48800
  34. package/examples/private/newtest.csv +0 -101
  35. package/examples/private/shawn.json +0 -1106
  36. package/examples/private/test.json +0 -10124
  37. package/examples/private/yaxis-testing.csv +0 -27
  38. package/examples/private/yaxis.json +0 -28
@@ -5,11 +5,10 @@ import { Text } from '@visx/text'
5
5
  import chroma from 'chroma-js'
6
6
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
7
  import Context from '../context'
8
- import ReactTooltip from 'react-tooltip'
9
8
  import { BarStackHorizontal } from '@visx/shape'
10
9
 
11
10
  export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible }) {
12
- const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, setParentConfig, colorPalettes, formatDate, parseDate } = useContext<any>(Context)
11
+ const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, formatDate, parseDate } = useContext<any>(Context)
13
12
  const { orientation, visualizationSubType } = config
14
13
  const isHorizontal = orientation === 'horizontal'
15
14
 
@@ -17,9 +16,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
17
16
  const lollipopShapeSize = config.lollipopSize === 'large' ? 14 : config.lollipopSize === 'medium' ? 12 : 10
18
17
 
19
18
  const isLabelBelowBar = config.yAxis.labelPlacement === 'Below Bar'
20
- const isLabelOnYAxis = config.yAxis.labelPlacement === 'On Date/Category Axis'
21
- const isLabelOnBar = config.yAxis.labelPlacement === 'On Bar'
22
- const isLabelMissing = !config.yAxis.labelPlacement
23
19
  const displayNumbersOnBar = config.yAxis.displayNumbersOnBar
24
20
  const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
25
21
 
@@ -29,6 +25,8 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
29
25
  const radius = config.roundingStyle === 'standard' ? '8px' : config.roundingStyle === 'shallow' ? '5px' : config.roundingStyle === 'finger' ? '15px' : '0px'
30
26
  const stackCount = config.runtime.seriesKeys.length
31
27
  const barBorderWidth = 1
28
+ const fontSize = { small: 14, medium: 16, large: 18 }
29
+ const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
32
30
 
33
31
  const applyRadius = (index: number) => {
34
32
  if (index === undefined || index === null || !isRounded) return
@@ -46,9 +44,57 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
46
44
 
47
45
  return style
48
46
  }
47
+ // }
48
+
49
+ const updateBars = defaultBars => {
50
+ // function updates stacked && regular && lollipop horizontal bars
51
+ if (config.visualizationType !== 'Bar' && !isHorizontal) return defaultBars
52
+
53
+ const barsArr = [...defaultBars]
54
+ let barHeight
55
+
56
+ const heights = {
57
+ stacked: config.barHeight,
58
+ lollipop: lollipopBarWidth
59
+ }
60
+
61
+ if (!isStacked) {
62
+ barHeight = heights[config.isLollipopChart ? 'lollipop' : 'stacked'] * stackCount
63
+ } else {
64
+ barHeight = heights.stacked
65
+ }
66
+
67
+ const labelHeight = isLabelBelowBar ? fontSize[config.fontSize] * 1.2 : 0
68
+ let barSpace = Number(config.barSpace)
69
+
70
+ // calculate height of container based height, space and fontSize of labels
71
+ let totalHeight = barsArr.length * (barHeight + labelHeight + barSpace)
72
+
73
+ if (isHorizontal) {
74
+ config.heights.horizontal = totalHeight
75
+ }
76
+
77
+ // return new updated bars/groupes
78
+ return barsArr.map((bar, i) => {
79
+ // set bars Y dynamycly to handle space between bars
80
+ let y = 0
81
+ bar.index !== 0 && (y = (barHeight + barSpace + labelHeight) * i)
82
+
83
+ return { ...bar, y: y, height: barHeight }
84
+ })
85
+ }
86
+
87
+ function getTextWidth(text, font) {
88
+ // function calculates the width of given text and its font-size
89
+ const canvas = document.createElement('canvas')
90
+ const context = canvas.getContext('2d')
91
+
92
+ context.font = font || getComputedStyle(document.body).font
93
+
94
+ return Math.ceil(context.measureText(text).width)
95
+ }
49
96
 
50
97
  // Using State
51
- const [horizBarHeight, setHorizBarHeight] = useState(null)
52
98
  const [textWidth, setTextWidth] = useState(null)
53
99
 
54
100
  useEffect(() => {
@@ -87,8 +133,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
87
133
  }
88
134
  }, [config.barStyle])
89
135
 
90
- // config.runtime.seriesKeys.sort().reverse();
91
-
92
136
  return (
93
137
  <ErrorBoundary component='BarChart'>
94
138
  <Group left={config.runtime.yAxis.size}>
@@ -98,22 +142,26 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
98
142
  {barStacks =>
99
143
  barStacks.reverse().map(barStack =>
100
144
  barStack.bars.map(bar => {
101
- const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
102
- const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0)
103
- let yAxisTooltip =config.runtime.yAxis.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue;
104
- let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
105
-
106
- const tooltip = `<div>
107
- ${yAxisTooltip}<br />
108
- ${xAxisTooltip}<br />
109
- ${config.seriesLabel ? `${config.seriesLabel}: ${bar.key}` : ''}`
110
-
111
145
  let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
112
146
  let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
113
147
  let barThickness = xMax / barStack.bars.length
114
148
  let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
115
149
  let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
116
150
  const style = applyRadius(barStack.index)
151
+ // tooltips
152
+ const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
153
+ const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0)
154
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
155
+ const xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
156
+ if (!hasMultipleSeries) {
157
+ yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
158
+ }
159
+
160
+ const tooltip = `<div>
161
+ ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
162
+ ${yAxisTooltip}<br />
163
+ ${xAxisTooltip}
164
+ </div>`
117
165
 
118
166
  return (
119
167
  <>
@@ -127,7 +175,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
127
175
  `}
128
176
  </style>
129
177
  <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
130
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barThickness * (bar.index + 0.5) + offset} y={bar.y - 5} fill={bar.color} textAnchor='middle'>
178
+ <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barThickness * bar.index + offset} y={bar.y - 5} fill={bar.color} textAnchor='middle'>
131
179
  {formatNumber(bar.bar ? bar.bar.data[bar.key] : 0)}
132
180
  </Text>
133
181
  <foreignObject
@@ -141,9 +189,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
141
189
  display={displayBar ? 'block' : 'none'}
142
190
  data-tip={tooltip}
143
191
  data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
144
- >
145
- {' '}
146
- </foreignObject>
192
+ ></foreignObject>
147
193
  </Group>
148
194
  </>
149
195
  )
@@ -159,44 +205,25 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
159
205
  <BarStackHorizontal data={data} keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys} height={yMax} y={(d: any) => d[config.runtime.yAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale} offset='none'>
160
206
  {barStacks =>
161
207
  barStacks.map(barStack =>
162
- barStack.bars.map((bar, index) => {
163
- const yAxisValue = formatNumber(data[bar.index][bar.key]);
164
- const xAxisValue = config.runtime.yAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey])) : data[bar.index][config.runtime.originalXAxis.dataKey]
165
- let yAxisTooltip = config.yAxis.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.yAxis.label ? `${config.yAxis.label}: ${yAxisValue}` :`${yAxisValue}`
166
- let xAxisTooltip = config.xAxis.label ? `${config.xAxis.label}: ${xAxisValue}` : xAxisValue
167
- // let yAxisTooltip = config.yAxis.label ? `${config.yAxis.label}: ${data[bar.index][bar.key]}` : `${bar.key}: ${data[bar.index][bar.key]}`
168
- // let xAxisTooltip = config.xAxis.label ? `${config.xAxis.label}: ${data[bar.index][config.runtime.originalXAxis.dataKey]}` :`${data[bar.index].name}`
169
-
170
- const tooltip = `<div>
171
- ${yAxisTooltip}<br />
172
- ${xAxisTooltip}<br />
173
- ${config.seriesLabel ? `${config.seriesLabel}: ${bar.key}` : ''}`
208
+ updateBars(barStack.bars).map((bar, index) => {
174
209
  let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
175
210
  let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
176
- const barsPerGroup = config.series.length
177
- let barHeight = config.barHeight ? config.barHeight : 25
178
- let barPadding = barHeight
179
-
180
211
  config.barHeight = Number(config.barHeight)
181
212
  const style = applyRadius(barStack.index)
182
-
183
- if (orientation === 'horizontal') {
184
- if (isLabelBelowBar || isLabelMissing || isLabelOnYAxis) {
185
- if (barHeight < 40) {
186
- config.barPadding = 40
187
- } else {
188
- config.barPadding = Number(barPadding)
189
- }
190
- } else {
191
- config.barPadding = Number(barPadding) / 2
192
- }
193
- }
194
-
195
- config.height = Number(barHeight) * data.length + Number(config.barPadding) * data.length
196
-
197
213
  let labelColor = '#000000'
198
-
199
- let textPosition = bar.y - config.barPadding / 2 - Number(config.barHeight / 2) + Number(config.barHeight) + 5
214
+ // tooltips
215
+ const xAxisValue = formatNumber(data[bar.index][bar.key])
216
+ const yAxisValue = config.runtime.yAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey])) : data[bar.index][config.runtime.originalXAxis.dataKey]
217
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
218
+ let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
219
+ if (!hasMultipleSeries) {
220
+ xAxisTooltip = config.isLegendValue ? `${bar.key}: ${xAxisValue}` : config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisTooltip
221
+ }
222
+ const tooltip = `<div>
223
+ ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
224
+ ${yAxisTooltip}<br />
225
+ ${xAxisTooltip}
226
+ </div>`
200
227
 
201
228
  if (chroma.contrast(labelColor, bar.color) < 4.9) {
202
229
  labelColor = '#FFFFFF'
@@ -208,9 +235,9 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
208
235
  key={`barstack-horizontal-${barStack.index}-${bar.index}-${index}`}
209
236
  className={`animated-chart group ${animatedChart ? 'animated' : ''}`}
210
237
  x={bar.x}
211
- y={bar.y - config.barPadding / 2 - config.barHeight / 2}
238
+ y={bar.y}
212
239
  width={bar.width}
213
- height={config.barHeight}
240
+ height={bar.height}
214
241
  style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
215
242
  opacity={transparentBar ? 0.5 : 1}
216
243
  display={displayBar ? 'block' : 'none'}
@@ -221,12 +248,12 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
221
248
  {orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
222
249
  <Text
223
250
  x={`${bar.x + (config.isLollipopChart ? 15 : 5)}`} // padding
224
- y={textPosition}
251
+ y={bar.y + bar.height * 1.2}
225
252
  fill={'#000000'}
226
253
  textAnchor='start'
227
254
  verticalAnchor='start'
228
255
  >
229
- {isHorizontal ? xAxisValue : formatNumber(xAxisValue)}
256
+ {yAxisValue}
230
257
  </Text>
231
258
  )}
232
259
 
@@ -234,7 +261,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
234
261
  <Text
235
262
  display={displayBar ? 'block' : 'none'}
236
263
  x={bar.x + barStack.bars[bar.index].width / 2} // padding
237
- y={textPosition - 5 - config.barHeight / 2}
264
+ y={bar.y + bar.height / 2}
238
265
  fill={labelColor}
239
266
  textAnchor='middle'
240
267
  verticalAnchor='middle'
@@ -274,35 +301,11 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
274
301
  }}
275
302
  >
276
303
  {barGroups => {
277
- let barType = 'vertical'
278
- if (orientation === 'horizontal') {
279
- const barsPerGroup = config.series.length
280
- let barHeight = config.barHeight ? config.barHeight : 25
281
- let barPadding = barHeight
282
- barType = 'horizontal'
283
-
284
- if (isLabelBelowBar || isLabelMissing || isLabelOnYAxis) {
285
- if (barHeight < 40) {
286
- config.barPadding = 40
287
- } else {
288
- config.barPadding = barPadding
289
- }
290
- } else {
291
- config.barPadding = barPadding / 2
292
- }
293
-
294
- if (config.isLollipopChart && config.yAxis.labelPlacement === 'Below Bar') {
295
- config.barPadding = config.barPadding + 7
296
- }
297
- config.barHeight = config.isLollipopChart ? lollipopBarWidth : barHeight
298
- config.height = barsPerGroup * barHeight * barGroups.length + config.barPadding * barGroups.length
299
- }
300
-
301
- return barGroups.map((barGroup, index) => (
304
+ return updateBars(barGroups).map((barGroup, index) => (
302
305
  <Group
303
- className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${barType}`}
306
+ className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${config.orientation}`}
304
307
  key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
305
- top={config.runtime.horizontal ? (yMax / barGroups.length) * barGroup.index : 0}
308
+ top={config.runtime.horizontal ? barGroup.y : 0}
306
309
  left={config.runtime.horizontal ? 0 : (xMax / barGroups.length) * barGroup.index}
307
310
  >
308
311
  {barGroup.bars.map((bar, index) => {
@@ -317,7 +320,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
317
320
  if (config.isLollipopChart) {
318
321
  offset = (config.runtime.horizontal ? yMax : xMax) / barGroups.length / 2 - lollipopBarWidth / 2
319
322
  }
320
-
321
323
  const set = new Set()
322
324
  data.forEach(d => set.add(d[config.legend.colorCode]))
323
325
  const uniqValues = Array.from(set)
@@ -340,10 +342,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
340
342
  xAxisValue = tempValue
341
343
  barWidth = config.barHeight
342
344
  }
343
-
344
- let yAxisTooltip = config.runtime.yAxis.isLegendValue ? `${bar.key} : ${yAxisValue}`: config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` :yAxisValue;
345
- let xAxisTooltip =config.runtime.xAxis.isLegendValue ? ` ${bar.key} :${xAxisValue}` : config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
346
- let horizBarLabelPadding = null
347
345
  let labelColor = '#000000'
348
346
 
349
347
  // Set label color
@@ -351,20 +349,26 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
351
349
  labelColor = '#FFFFFF'
352
350
  }
353
351
 
354
- // font size and text spacing used for centering text on bar
355
- if (config.fontSize === 'small') {
356
- horizBarLabelPadding = 16
357
- } else if (config.fontSize === 'medium') {
358
- horizBarLabelPadding = 18
359
- } else {
360
- horizBarLabelPadding = 20
352
+ const style = applyRadius(index)
353
+
354
+ // check if bar text/value string fits into each bars.
355
+ let textWidth = getTextWidth(xAxisValue, config.fontSize)
356
+ let doesTextFit = (textWidth / bar.y) * 100 < 48
357
+
358
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
359
+ let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
360
+ if (!hasMultipleSeries && config.runtime.horizontal) {
361
+ xAxisTooltip = config.isLegendValue ? `${bar.key}: ${xAxisValue}` : config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
362
+ }
363
+ if (!hasMultipleSeries && !config.runtime.horizontal) {
364
+ yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
361
365
  }
362
- const onBarTextSpacing = 25
366
+
363
367
  const tooltip = `<div>
364
- ${yAxisTooltip}<br />
365
- ${xAxisTooltip}<br />
366
- ${config.seriesLabel ? `${config.seriesLabel}: ${bar.key}` : ''}`
367
- const style = applyRadius(index)
368
+ ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
369
+ ${yAxisTooltip}<br />
370
+ ${xAxisTooltip}
371
+ </div>`
368
372
 
369
373
  return (
370
374
  <>
@@ -378,16 +382,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
378
382
  `}
379
383
  </style>
380
384
  <Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
381
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barWidth * (barGroup.bars.length - bar.index - 0.5) + offset} y={barY - 5} fill={barColor} textAnchor='middle'>
382
- {formatNumber(bar.value)}
383
- </Text>
384
385
  <foreignObject
385
386
  id={`barGroup${barGroup.index}`}
386
387
  key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
387
- x={config.runtime.horizontal ? 0 : barWidth * (barGroup.bars.length - bar.index - 1) + offset}
388
- y={config.runtime.horizontal ? barWidth * (barGroup.bars.length - bar.index - 1) + (config.isLollipopChart && isLabelOnYAxis ? offset : 0) : barY}
388
+ x={config.runtime.horizontal ? 0 : barWidth * bar.index + offset}
389
+ y={config.runtime.horizontal ? barWidth * bar.index : barY}
389
390
  width={config.runtime.horizontal ? bar.y : barWidth}
390
- height={config.runtime.horizontal ? barWidth : barHeight}
391
+ height={isHorizontal && !config.isLollipopChart ? barWidth : isHorizontal && config.isLollipopChart ? lollipopBarWidth : barHeight}
391
392
  style={{
392
393
  background: config.isLollipopChart && config.lollipopColorStyle === 'regular' ? barColor : config.isLollipopChart && config.lollipopColorStyle === 'two-tone' ? chroma(barColor).brighten(1) : barColor,
393
394
  border: `${config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`,
@@ -398,10 +399,53 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
398
399
  data-tip={tooltip}
399
400
  data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
400
401
  ></foreignObject>
402
+ {orientation === 'horizontal' && !config.isLollipopChart && displayNumbersOnBar && (
403
+ <Text
404
+ display={displayBar ? 'block' : 'none'}
405
+ x={bar.y}
406
+ y={config.barHeight / 2 + config.barHeight * bar.index}
407
+ fill={labelColor}
408
+ dx={doesTextFit ? -5 : 5} // X padding
409
+ verticalAnchor='middle'
410
+ textAnchor={doesTextFit ? 'end' : 'start'}
411
+ >
412
+ {xAxisValue}
413
+ </Text>
414
+ )}
415
+ ;
416
+ {orientation === 'horizontal' && config.isLollipopChart && displayNumbersOnBar && (
417
+ <Text
418
+ display={displayBar ? 'block' : 'none'}
419
+ x={`${bar.y + (config.isLollipopChart ? 15 : 5) + (config.isLollipopChart && barGroup.bars.length === bar.index ? offset : 0)}`} // padding
420
+ y={0}
421
+ fill={'#000000'}
422
+ textAnchor='start'
423
+ verticalAnchor='middle'
424
+ fontWeight={'normal'}
425
+ >
426
+ {xAxisValue}
427
+ </Text>
428
+ )}
429
+ {orientation === 'horizontal' && isLabelBelowBar && !config.yAxis.hideLabel && (
430
+ <Text x={config.yAxis.hideAxis ? 0 : 5} y={barGroup.height} dy={4} verticalAnchor={'start'} textAnchor={'start'}>
431
+ {config.runtime.yAxis.type === 'date'
432
+ ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
433
+ : isHorizontal
434
+ ? data[barGroup.index][config.runtime.originalXAxis.dataKey]
435
+ : formatNumber(data[barGroup.index][config.runtime.originalXAxis.dataKey])}
436
+ </Text>
437
+ )}
438
+ ;
439
+ {orientation === 'vertical' && (
440
+ <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barWidth * (bar.index + 0.5) + offset} y={barY - 5} fill={barColor} textAnchor='middle'>
441
+ {bar.value}
442
+ </Text>
443
+ )}
444
+ ;
401
445
  {config.isLollipopChart && config.lollipopShape === 'circle' && (
402
446
  <circle
403
447
  cx={orientation === 'horizontal' ? bar.y : barWidth * (barGroup.bars.length - bar.index - 1) + (isLabelBelowBar && orientation === 'horizontal' ? 0 : offset) + lollipopShapeSize / 3.5}
404
- cy={orientation === 'horizontal' ? lollipopShapeSize / 3.5 + (isLabelBelowBar && orientation === 'horizontal' ? 0 : offset) : bar.y}
448
+ cy={orientation === 'horizontal' ? 0 + lollipopBarWidth / 2 : bar.y}
405
449
  r={lollipopShapeSize / 2}
406
450
  fill={barColor}
407
451
  key={`circle--${bar.index}`}
@@ -413,7 +457,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
413
457
  {config.isLollipopChart && config.lollipopShape === 'square' && (
414
458
  <rect
415
459
  x={orientation === 'horizontal' && bar.y > 10 ? bar.y - lollipopShapeSize / 2 : orientation === 'horizontal' && bar.y < 10 ? 0 : orientation !== 'horizontal' ? offset - lollipopBarWidth / 2 : barWidth * (barGroup.bars.length - bar.index - 1) + offset - 5.25}
416
- y={orientation === 'horizontal' ? 0 - lollipopBarWidth / 2 + (isLabelBelowBar ? 0 : offset) : config.height - bar.y > 10 ? bar.y - lollipopShapeSize / 2 : 0}
460
+ y={orientation === 'horizontal' ? 0 - lollipopBarWidth / 2 : barY}
417
461
  width={lollipopShapeSize}
418
462
  height={lollipopShapeSize}
419
463
  fill={barColor}
@@ -425,120 +469,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
425
469
  <animate attributeName='height' values={`0, ${lollipopShapeSize}`} dur='2.5s' />
426
470
  </rect>
427
471
  )}
428
- {orientation === 'horizontal' && textWidth + 100 < bar.y
429
- ? config.yAxis.labelPlacement === 'On Bar' && (
430
- <Group>
431
- <Text
432
- innerRef={e => {
433
- if (e) {
434
- // use font sizes and padding to set the bar height
435
- let elem = e.getBBox()
436
- setTextWidth(elem.width)
437
- config.barHeight = elem.height * 2 + horizBarLabelPadding * 2 + onBarTextSpacing / 2
438
- config.barPadding = horizBarHeight / 2
439
- }
440
- }}
441
- x={bar.y - horizBarLabelPadding}
442
- y={barHeight * (barGroup.bars.length - bar.index - 1) + horizBarLabelPadding * 2}
443
- fill={labelColor}
444
- textAnchor='end'
445
- >
446
- {yAxisValue}
447
- </Text>
448
- <Text x={bar.y - horizBarLabelPadding} y={barWidth * (barGroup.bars.length - bar.index - 1) + horizBarLabelPadding * 2 + onBarTextSpacing} fill={labelColor} textAnchor='end'>
449
- {xAxisValue}
450
- </Text>
451
- </Group>
452
- )
453
- : isLabelOnBar && (
454
- <Group>
455
- {/* hide y label if we're only showing data on bar */}
456
- <Text x={bar.y + horizBarLabelPadding} y={barWidth * (barGroup.bars.length - bar.index - 1) + horizBarLabelPadding * 2} fill={'#000'} textAnchor='start' verticalAnchor='end'>
457
- {yAxisValue}
458
- </Text>
459
- <Text x={bar.y + horizBarLabelPadding} y={barWidth * (barGroup.bars.length - bar.index - 1) + horizBarLabelPadding * 2 + onBarTextSpacing} fill={'#000'} textAnchor='start' verticalAnchor='start'>
460
- {xAxisValue}
461
- </Text>
462
- </Group>
463
- )}
464
-
465
- {orientation === 'horizontal' && isLabelBelowBar && !config.yAxis.hideLabel && (
466
- <>
467
- <Text
468
- x={config.yAxis.hideAxis ? 0 : 5} // padding
469
- y={config.isLollipopChart ? lollipopShapeSize * config.series.length + 2 : barWidth * config.series.length + 7}
470
- verticalAnchor={'start'}
471
- textAnchor={'start'}
472
- >
473
- {config.runtime.yAxis.type === 'date'
474
- ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
475
- : isHorizontal
476
- ? data[barGroup.index][config.runtime.originalXAxis.dataKey]
477
- : formatNumber(data[barGroup.index][config.runtime.originalXAxis.dataKey])}
478
- </Text>
479
-
480
- {displayNumbersOnBar ? (
481
- textWidth + 100 < bar.y && !config.isLollipopChart ? (
482
- <Text
483
- display={displayBar ? 'block' : 'none'}
484
- x={bar.y - 5} // padding
485
- y={config.isLollipopChart ? offset : config.barHeight / 2 + config.barHeight * (barGroup.bars.length - bar.index - 1)}
486
- fill={labelColor}
487
- textAnchor='end'
488
- verticalAnchor='middle'
489
- >
490
- {xAxisValue}
491
- </Text>
492
- ) : (
493
- <Text
494
- display={displayBar ? 'block' : 'none'}
495
- x={`${bar.y + (config.isLollipopChart ? 15 : 5) + (config.isLollipopChart && barGroup.bars.length === bar.index ? offset : 0)}`} // padding
496
- y={config.isLollipopChart ? 0 : config.barHeight / 2 + config.barHeight * (barGroup.bars.length - bar.index - 1)}
497
- fill={'#000000'}
498
- textAnchor='start'
499
- verticalAnchor='middle'
500
- fontWeight={'normal'}
501
- >
502
- {xAxisValue}
503
- </Text>
504
- )
505
- ) : (
506
- ''
507
- )}
508
- </>
509
- )}
510
-
511
- {isLabelOnYAxis && orientation === 'horizontal' && (
512
- <>
513
- {displayNumbersOnBar ? (
514
- textWidth + 100 < bar.y && !config.isLollipopChart ? (
515
- <Text
516
- display={displayBar ? 'block' : 'none'}
517
- x={bar.y - 5} // padding
518
- y={config.isLollipopChart ? config.barHeight * (barGroup.bars.length - bar.index - 1) + offset : config.barHeight * (barGroup.bars.length - bar.index - 1) + config.barHeight / 2}
519
- fill={labelColor}
520
- textAnchor='end'
521
- verticalAnchor='middle'
522
- >
523
- {formatNumber(xAxisValue)}
524
- </Text>
525
- ) : (
526
- <Text
527
- display={displayBar ? 'block' : 'none'}
528
- x={`${bar.y + (config.isLollipopChart ? 15 : 5)}`} // padding
529
- y={config.isLollipopChart ? config.barHeight * (barGroup.bars.length - bar.index - 1) + offset : config.barHeight * (barGroup.bars.length - bar.index - 1) + config.barHeight / 2}
530
- fill={'#000000'}
531
- textAnchor='start'
532
- verticalAnchor='middle'
533
- >
534
- {formatNumber(xAxisValue)}
535
- </Text>
536
- )
537
- ) : (
538
- ''
539
- )}
540
- </>
541
- )}
542
472
  </Group>
543
473
  </>
544
474
  )
@@ -0,0 +1,92 @@
1
+ import React, { useContext, useEffect } from 'react'
2
+ import { BoxPlot } from '@visx/stats'
3
+ import { Group } from '@visx/group'
4
+ import { scaleBand, scaleLinear } from '@visx/scale'
5
+ import Context from '../context'
6
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
+ import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
8
+ import ReactTooltip from 'react-tooltip'
9
+
10
+ const CoveBoxPlot = ({ xScale, yScale }) => {
11
+ const { transformedData: data, config } = useContext(Context)
12
+
13
+ const {
14
+ boxplot: { columnFirstQuartile, columnThirdQuartile, columnMax, columnMin, columnMedian, columnOutliers },
15
+ width,
16
+ height
17
+ } = config
18
+
19
+ const yMax = config.height - config.runtime.xAxis.size
20
+ const xMax = config.width - config.runtime.yAxis.size - config.yAxis.rightAxisSize
21
+ const boxWidth = xScale.bandwidth()
22
+ const constrainedWidth = Math.min(20, boxWidth)
23
+ const color_0 = colorPalettesChart[config?.palette][0] ? colorPalettesChart[config?.palette][0] : '#000'
24
+
25
+ // tooltips
26
+ const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
27
+ const handleTooltip = d => {
28
+ return `
29
+ <strong>${d.columnCategory}</strong></br>
30
+ Q1: ${d.columnFirstQuartile}<br/>
31
+ Q3: ${d.columnThirdQuartile}<br/>
32
+ IQR: ${d.columnIqr}<br/>
33
+ Median: ${d.columnMedian}
34
+ `
35
+ }
36
+ return (
37
+ <ErrorBoundary component='BoxPlot'>
38
+ <Group className='boxplot' key='boxplot-wrapper'>
39
+ {config.boxplot.map((d, i) => {
40
+ const offset = boxWidth - constrainedWidth
41
+ return (
42
+ <BoxPlot
43
+ key={`box-plot-${i}`}
44
+ min={d.columnMin}
45
+ max={d.columnMax}
46
+ left={xScale(d.columnCategory) + config.yAxis.size + offset / 2 + 0.5}
47
+ firstQuartile={d.columnFirstQuartile}
48
+ thirdQuartile={d.columnThirdQuartile}
49
+ median={d.columnMedian}
50
+ boxWidth={constrainedWidth}
51
+ fill={color_0}
52
+ fillOpacity={0.5}
53
+ stroke='black'
54
+ strokeWidth={1}
55
+ valueScale={yScale}
56
+ outliers={d.columnOutliers}
57
+ outlierProps={{
58
+ style: {
59
+ fill: `${color_0}`,
60
+ opacity: 1
61
+ }
62
+ }}
63
+ medianProps={{
64
+ style: {
65
+ stroke: 'black'
66
+ }
67
+ }}
68
+ boxProps={{
69
+ style: {
70
+ stroke: 'black'
71
+ },
72
+ 'data-tip': 'cool'
73
+ }}
74
+ maxProps={{
75
+ style: {
76
+ stroke: 'black'
77
+ }
78
+ }}
79
+ container
80
+ containerProps={{
81
+ 'data-tip': handleTooltip(d),
82
+ 'data-for': tooltip_id
83
+ }}
84
+ />
85
+ )
86
+ })}
87
+ </Group>
88
+ </ErrorBoundary>
89
+ )
90
+ }
91
+
92
+ export default CoveBoxPlot