@cdc/chart 4.23.10 → 4.24.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 (125) hide show
  1. package/dist/cdcchart.js +34606 -32218
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/example-bar-chart.json +1 -46
  4. package/examples/feature/bar/lollipop.json +156 -0
  5. package/examples/feature/bar/tall-data.json +98 -0
  6. package/examples/feature/combo/planet-combo-example-config.json +99 -9
  7. package/examples/feature/dev-4261.json +399 -0
  8. package/examples/feature/forest-plot/forest-plot.json +63 -19
  9. package/examples/feature/forest-plot/{broken.json → linear.json} +77 -23
  10. package/examples/feature/forest-plot/log.json +26 -0
  11. package/examples/feature/forest-plot/logarithmic.json +271 -0
  12. package/examples/feature/line/line-chart-preliminary.json +346 -0
  13. package/examples/feature/line/line-points.json +340 -0
  14. package/examples/feature/regions/index.json +462 -0
  15. package/examples/feature/scatterplot/scatterplot.json +272 -33
  16. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
  17. package/examples/private/chart-t.json +3740 -0
  18. package/examples/private/combo.json +369 -0
  19. package/examples/private/epi-data.csv +13 -0
  20. package/examples/private/epi-data.json +62 -0
  21. package/examples/private/epi.json +403 -0
  22. package/examples/private/occupancy.json +109283 -0
  23. package/examples/private/prod-line-config.json +401 -0
  24. package/examples/private/region-data.json +822 -0
  25. package/examples/private/region-testing.json +312 -0
  26. package/examples/private/scaling.json +45325 -0
  27. package/examples/private/testing-data.json +1739 -0
  28. package/examples/private/testing.json +816 -0
  29. package/examples/sparkline-multilple.json +846 -0
  30. package/index.html +12 -8
  31. package/package.json +3 -3
  32. package/src/CdcChart.tsx +42 -211
  33. package/src/ConfigContext.tsx +6 -0
  34. package/src/_stories/Chart.stories.tsx +188 -0
  35. package/src/_stories/Chart.tooltip.stories.tsx +305 -0
  36. package/src/_stories/ChartBrush.stories.tsx +19 -0
  37. package/src/_stories/ChartEditor.stories.tsx +22 -0
  38. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  39. package/src/_stories/ChartSuppress.stories.tsx +19 -0
  40. package/src/_stories/_mock/brush_mock.json +393 -0
  41. package/src/_stories/_mock/pie_config.json +191 -0
  42. package/src/_stories/_mock/pie_data.json +218 -0
  43. package/src/_stories/_mock/preliminary_mock.json +346 -0
  44. package/src/_stories/_mock/suppress_mock.json +911 -0
  45. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +6 -7
  46. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +7 -36
  47. package/src/components/AreaChart/index.tsx +4 -0
  48. package/src/components/{BarChart.Horizontal.jsx → BarChart/components/BarChart.Horizontal.tsx} +111 -34
  49. package/src/components/{BarChart.StackedHorizontal.jsx → BarChart/components/BarChart.StackedHorizontal.tsx} +55 -20
  50. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
  51. package/src/components/{BarChart.Vertical.jsx → BarChart/components/BarChart.Vertical.tsx} +162 -34
  52. package/src/components/BarChart/components/BarChart.jsx +39 -0
  53. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  54. package/src/components/BarChart/components/context.tsx +13 -0
  55. package/src/components/BarChart/index.tsx +3 -0
  56. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
  57. package/src/components/BoxPlot/index.tsx +3 -0
  58. package/src/components/DeviationBar.jsx +4 -3
  59. package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +807 -865
  60. package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
  61. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +190 -220
  62. package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
  63. package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +23 -4
  64. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  65. package/src/components/EditorPanel/components/Panels.tsx +13 -0
  66. package/src/components/EditorPanel/components/panels.scss +72 -0
  67. package/src/components/EditorPanel/editor-panel.scss +751 -0
  68. package/src/components/EditorPanel/index.tsx +3 -0
  69. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +50 -5
  70. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  71. package/src/components/Forecasting/index.tsx +3 -0
  72. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  73. package/src/components/ForestPlot/ForestPlotProps.ts +18 -0
  74. package/src/components/ForestPlot/index.scss +1 -0
  75. package/src/components/ForestPlot/index.tsx +3 -0
  76. package/src/components/Legend/Legend.tsx +347 -0
  77. package/src/components/Legend/index.tsx +3 -0
  78. package/src/components/LineChart/LineChartProps.ts +46 -0
  79. package/src/components/{LineChart.Circle.tsx → LineChart/components/LineChart.Circle.tsx} +36 -30
  80. package/src/components/LineChart/helpers.ts +45 -0
  81. package/src/components/LineChart/index.scss +1 -0
  82. package/src/components/{LineChart.tsx → LineChart/index.tsx} +83 -42
  83. package/src/components/LinearChart.jsx +125 -82
  84. package/src/components/PairedBarChart.jsx +2 -2
  85. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
  86. package/src/components/PieChart/index.tsx +3 -0
  87. package/src/components/Regions/components/Regions.tsx +135 -0
  88. package/src/components/Regions/index.tsx +3 -0
  89. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  90. package/src/components/ScatterPlot/index.tsx +3 -0
  91. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  92. package/src/components/Sparkline/index.tsx +3 -0
  93. package/src/components/ZoomBrush.tsx +168 -0
  94. package/src/data/initial-state.js +30 -16
  95. package/src/helpers/abbreviateNumber.ts +17 -0
  96. package/src/helpers/computeMarginBottom.ts +55 -0
  97. package/src/helpers/filterData.ts +18 -0
  98. package/src/helpers/generateColorsArray.ts +8 -0
  99. package/src/helpers/getQuartiles.ts +30 -0
  100. package/src/helpers/handleChartAriaLabels.ts +19 -0
  101. package/src/helpers/handleLineType.ts +18 -0
  102. package/src/helpers/lineOptions.ts +18 -0
  103. package/src/helpers/sort.ts +7 -0
  104. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  105. package/src/hooks/useBarChart.js +72 -7
  106. package/src/hooks/useColorScale.ts +50 -0
  107. package/src/hooks/{useMinMax.js → useMinMax.ts} +75 -23
  108. package/src/hooks/{useRightAxis.js → useRightAxis.ts} +10 -2
  109. package/src/hooks/{useScales.js → useScales.ts} +64 -17
  110. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +84 -55
  111. package/src/scss/main.scss +70 -38
  112. package/src/types/ChartConfig.ts +178 -0
  113. package/src/types/ChartContext.ts +54 -0
  114. package/src/types/ForestPlot.ts +53 -0
  115. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  116. package/src/ConfigContext.jsx +0 -5
  117. package/src/components/BarChart.StackedVertical.jsx +0 -95
  118. package/src/components/BarChart.jsx +0 -30
  119. package/src/components/ForestPlot.jsx +0 -191
  120. package/src/components/Legend.jsx +0 -277
  121. package/src/scss/LinearChart.scss +0 -0
  122. package/src/scss/editor-panel.scss +0 -745
  123. package/src/scss/legend.scss +0 -206
  124. package/src/scss/mixins.scss +0 -0
  125. package/src/scss/variables.scss +0 -1
@@ -1,17 +0,0 @@
1
- x,y1,y2,y3,y4
2
- 1000,8,37,72,82
3
- 2000,2,30,75,83
4
- 3000,15,42,51,95
5
- 4,10,38,61,96
6
- 5,1,38,66,86
7
- 60,6,37,70,85
8
- 10,19,47,59,91
9
- 24,18,32,68,89
10
- 3,7,38,74,89
11
- 26,10,39,56,91
12
- 42,16,38,55,76
13
- 32,20,46,52,94
14
- 11,9,41,57,86
15
- 22,10,47,56,80
16
- 73,1,36,71,94
17
- 23,13,30,66,78
@@ -1,5 +0,0 @@
1
- import { createContext } from 'react'
2
-
3
- const ConfigContext = createContext({})
4
-
5
- export default ConfigContext
@@ -1,95 +0,0 @@
1
- import React, { useContext } from 'react'
2
- import ConfigContext from '../ConfigContext'
3
- import { useBarChart } from '../hooks/useBarChart'
4
- import { BarStack } from '@visx/shape'
5
- import { Group } from '@visx/group'
6
- import { Text } from '@visx/text'
7
-
8
- const BarChartStackedVertical = props => {
9
- const { xScale, yScale, xMax, yMax } = props
10
- const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter } = useContext(ConfigContext)
11
- const { isHorizontal, barBorderWidth, hasMultipleSeries, applyRadius } = useBarChart()
12
- const { orientation } = config
13
-
14
- return (
15
- config.visualizationSubType === 'stacked' &&
16
- !isHorizontal && (
17
- <BarStack data={data} keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys} x={d => d[config.runtime.xAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale}>
18
- {barStacks =>
19
- barStacks.reverse().map(barStack =>
20
- barStack.bars.map(bar => {
21
- let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
22
- let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
23
- let barThickness = xMax / barStack.bars.length
24
- let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
25
- let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
26
- // tooltips
27
- const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
28
- const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0, 'left')
29
-
30
- if(!yAxisValue) return <></>
31
-
32
- const style = applyRadius(barStack.index)
33
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
34
- const xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
35
- if (!hasMultipleSeries) {
36
- yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
37
- }
38
-
39
- const {
40
- legend: { showLegendValuesTooltip },
41
- runtime: { seriesLabels }
42
- } = config
43
-
44
- const barStackTooltip = `<div>
45
- <p class="tooltip-heading"><strong>${xAxisTooltip}</strong></p>
46
- ${showLegendValuesTooltip && seriesLabels && hasMultipleSeries ? `${seriesLabels[bar.key] || ''}<br/>` : ''}
47
- ${yAxisTooltip}<br />
48
- </div>`
49
-
50
- return (
51
- <Group key={`${barStack.index}--${bar.index}--${orientation}`}>
52
- <style>
53
- {`
54
- #barStack${barStack.index}-${bar.index} rect,
55
- #barStack${barStack.index}-${bar.index} foreignObject{
56
- animation-delay: ${barStack.index * 0.5}s;
57
- transform-origin: ${barThicknessAdjusted / 2}px ${bar.y + bar.height}px
58
- }
59
- `}
60
- </style>
61
- <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
62
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barThickness * bar.index + offset} y={bar.y - 5} fill={'#000'} textAnchor='middle'>
63
- {yAxisValue}
64
- </Text>
65
- <foreignObject
66
- key={`bar-stack-${barStack.index}-${bar.index}`}
67
- x={barThickness * bar.index + offset}
68
- y={bar.y}
69
- width={barThicknessAdjusted}
70
- height={bar.height}
71
- style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
72
- opacity={transparentBar ? 0.5 : 1}
73
- display={displayBar ? 'block' : 'none'}
74
- data-tooltip-html={barStackTooltip}
75
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
76
- onClick={e => {
77
- e.preventDefault()
78
- if (setSharedFilter) {
79
- bar[config.xAxis.dataKey] = xAxisValue
80
- setSharedFilter(config.uid, bar)
81
- }
82
- }}
83
- ></foreignObject>
84
- </Group>
85
- </Group>
86
- )
87
- })
88
- )
89
- }
90
- </BarStack>
91
- )
92
- )
93
- }
94
-
95
- export default BarChartStackedVertical
@@ -1,30 +0,0 @@
1
- import React, { useContext } from 'react'
2
-
3
- // visx
4
- import { Group } from '@visx/group'
5
- import { Bar } from '@visx/shape'
6
-
7
- // cdc
8
- import BarChartType from './BarChartType'
9
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
10
- import ConfigContext from '../ConfigContext'
11
-
12
- const BarChart = ({ xScale, yScale, seriesScale, xMax, yMax, handleTooltipMouseOver, handleTooltipMouseOff, handleTooltipClick }) => {
13
- const { transformedData: data, config } = useContext(ConfigContext)
14
-
15
- return (
16
- <ErrorBoundary component='BarChart'>
17
- <Group left={parseFloat(config.runtime.yAxis.size)}>
18
- <BarChartType.StackedVertical xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} />
19
- <BarChartType.StackedHorizontal xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} />
20
- <BarChartType.Vertical xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} seriesScale={seriesScale} />
21
- <BarChartType.Horizontal xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} seriesScale={seriesScale} />
22
-
23
- {/* tooltips */}
24
- <Bar key={'bars'} width={Number(xMax)} height={Number(yMax)} fill={false ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} onClick={e => handleTooltipClick(e, data)} />
25
- </Group>
26
- </ErrorBoundary>
27
- )
28
- }
29
-
30
- export default BarChart
@@ -1,191 +0,0 @@
1
- import React, { useContext, useEffect } from 'react'
2
-
3
- // visx
4
- import { Group } from '@visx/group'
5
- import { Line, Bar, Circle, LinePath } from '@visx/shape'
6
- import { GlyphDiamond } from '@visx/glyph'
7
- import { Text } from '@visx/text'
8
- import { scaleLinear } from '@visx/scale'
9
- import { curveLinearClosed } from '@visx/curve'
10
-
11
- // cdc
12
- import ConfigContext from '../ConfigContext'
13
- import { getFontSize } from '@cdc/core/helpers/cove/number'
14
-
15
- const ForestPlot = props => {
16
- const { transformedData: data, updateConfig, dimensions, rawData } = useContext(ConfigContext)
17
- const { xScale, yScale, config, height, width, handleTooltipMouseOff, handleTooltipMouseOver, maxWidth, maxHeight } = props
18
- const { forestPlot, runtime, dataFormat } = config
19
- const [screenWidth, screenHeight] = dimensions
20
-
21
- // Requirements for forest plot
22
- // - force legend to be hidden for this chart type
23
- // - reset the date category axis to zero
24
- useEffect(() => {
25
- if (!config.legend.hide) {
26
- updateConfig({
27
- ...config,
28
- legend: {
29
- ...config.legend,
30
- hide: true
31
- },
32
- xAxis: {
33
- ...config.xAxis,
34
- size: 0
35
- }
36
- })
37
- }
38
- }, [])
39
-
40
- const diamondHeight = 5
41
-
42
- // diamond path
43
- const regressionPoints = [
44
- { x: xScale(forestPlot.regression.lower), y: height - Number(config.forestPlot.rowHeight) },
45
- { x: xScale(forestPlot.regression.estimateField), y: height - diamondHeight - Number(config.forestPlot.rowHeight) },
46
- { x: xScale(forestPlot.regression.upper), y: height - Number(config.forestPlot.rowHeight) },
47
- { x: xScale(forestPlot.regression.estimateField), y: height + diamondHeight - Number(config.forestPlot.rowHeight) },
48
- { x: xScale(forestPlot.regression.lower), y: height - Number(config.forestPlot.rowHeight) }
49
- ]
50
-
51
- const topMarginOffset = config.forestPlot.rowHeight
52
-
53
- const topLine = [
54
- { x: 0, y: topMarginOffset },
55
- { x: width, y: topMarginOffset }
56
- ]
57
-
58
- const bottomLine = [
59
- { x: 0, y: height },
60
- { x: width, y: height }
61
- ]
62
-
63
- const columnsOnChart = Object.entries(config.columns)
64
- .map(entry => entry[1])
65
- .filter(entry => entry.forestPlot === true)
66
-
67
- const rightOffset = forestPlot.rightWidthOffset !== 0 ? (Number(forestPlot.rightWidthOffset) / 100) * width : width
68
- const leftOffset = forestPlot.leftWidthOffset !== 0 ? (Number(forestPlot.leftWidthOffset) / 100) * width : width
69
- const chartWidth = width - rightOffset - leftOffset
70
-
71
- return (
72
- <>
73
- <Group>
74
- {forestPlot.title !== '' && (
75
- <Text className={`forest-plot--title`} x={xScale(0)} y={0} textAnchor='middle' verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
76
- {forestPlot.title}
77
- </Text>
78
- )}
79
- {forestPlot.regression.showBaseLine && <Line from={{ x: xScale(forestPlot.regression.estimateField), y: 0 + topMarginOffset }} to={{ x: xScale(forestPlot.regression.estimateField), y: height }} className='forestplot__baseline' stroke={forestPlot.regression.baseLineColor || 'black'} />}
80
- {forestPlot.showZeroLine && <Line from={{ x: xScale(0), y: 0 + topMarginOffset }} to={{ x: xScale(0), y: height }} className='forestplot__zero-line' stroke='gray' strokeDasharray={'5 5'} />}
81
-
82
- {data.map((d, i) => {
83
- // calculate both square and circle size based on radius.min and radius.max
84
- const scaleRadius = scaleLinear({
85
- domain: xScale.domain(),
86
- range: [forestPlot.radius.min, forestPlot.radius.max]
87
- })
88
-
89
- // glyph settings
90
- const diamondSize = forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.estimateField]) * 5 : 4
91
- const rectSize = forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.estimateField]) : 4
92
- const shapeColor = forestPlot.colors.shape ? forestPlot.colors.shape : 'black'
93
- const lineColor = forestPlot.colors.line ? forestPlot.colors.line : 'black'
94
-
95
- // ci size
96
- const ciEndSize = 4
97
-
98
- return (
99
- <Group>
100
- {/* Confidence Interval Paths */}
101
- <path
102
- stroke={lineColor}
103
- strokeWidth={1}
104
- className='lower-ci'
105
- d={`
106
- M${xScale(d[forestPlot.lower])} ${yScale(i) - Number(ciEndSize)}
107
- L${xScale(d[forestPlot.lower])} ${yScale(i) + Number(ciEndSize)}
108
- `}
109
- />
110
-
111
- <path
112
- stroke={lineColor}
113
- strokeWidth={1}
114
- className='upper-ci'
115
- d={`
116
- M${xScale(d[forestPlot.upper])} ${yScale(i) - Number(ciEndSize)}
117
- L${xScale(d[forestPlot.upper])} ${yScale(i) + Number(ciEndSize)}
118
- `}
119
- />
120
-
121
- {/* main line */}
122
- <line stroke={lineColor} className={`line-${d[config.yAxis.dataKey]}`} key={i} x1={xScale(d[forestPlot.lower])} x2={xScale(d[forestPlot.upper])} y1={yScale(i)} y2={yScale(i)} />
123
- {forestPlot.shape === 'circle' && (
124
- <Circle className='forest-plot--circle' cx={xScale(Number(d[forestPlot.estimateField]))} cy={yScale(i)} r={forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.estimateField]) : 4} fill={shapeColor} style={{ opacity: 1, filter: 'unset' }} />
125
- )}
126
- {forestPlot.shape === 'square' && <rect className='forest-plot--square' x={xScale(Number(d[forestPlot.estimateField]))} y={yScale(i) - rectSize / 2} width={rectSize} height={rectSize} fill={shapeColor} style={{ opacity: 1, filter: 'unset' }} />}
127
- {forestPlot.shape === 'diamond' && <GlyphDiamond className='forest-plot--diamond' size={diamondSize} top={yScale(i)} left={xScale(Number(d[forestPlot.estimateField]))} fill={shapeColor} />}
128
- {forestPlot.shape === 'text' && (
129
- <Text className='forest-plot--text' x={xScale(Number(d[forestPlot.estimateField]))} y={yScale(i)} textAnchor='middle' verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={shapeColor}>
130
- {d[forestPlot.estimateField]}
131
- </Text>
132
- )}
133
- </Group>
134
- )
135
- })}
136
-
137
- {/* regression diamond */}
138
- {regressionPoints && forestPlot.regression.showDiamond && <LinePath data={regressionPoints} x={d => d.x} y={d => d.y} stroke='black' strokeWidth={2} fill={forestPlot.regression.baseLineColor} curve={curveLinearClosed} />}
139
- {/* regression text */}
140
- {forestPlot.regression.description && (
141
- <Text x={0 - Number(config.xAxis.size)} width={width} y={height - config.forestPlot.rowHeight - Number(forestPlot.rowHeight) / 3} verticalAnchor='start' textAnchor='start' style={{ fontWeight: 'bold', fontSize: 12 }}>
142
- {forestPlot.regression.description}
143
- </Text>
144
- )}
145
-
146
- <Bar key='forest-plot-tooltip-area' className='forest-plot-tooltip-area' width={width} height={height} fill={false ? 'red' : 'transparent'} fillOpacity={0.5} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} />
147
- </Group>
148
- <Line from={topLine[0]} to={topLine[1]} style={{ stroke: 'black', strokeWidth: 2 }} className='forestplot__top-line' />
149
- <Line from={bottomLine[0]} to={bottomLine[1]} style={{ stroke: 'black', strokeWidth: 2 }} className='forestplot__bottom-line' />
150
-
151
- {/* column data */}
152
- {columnsOnChart.map(column => {
153
- return rawData.map((d, i) => {
154
- return (
155
- <Text className={`${d[column.name]}`} x={column.forestPlotAlignRight ? width : column.forestPlotStartingPoint} y={yScale(i)} textAnchor={column.forestPlotAlignRight ? 'end' : 'start'} verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={'black'}>
156
- {d[column.name]}
157
- </Text>
158
- )
159
- })
160
- })}
161
-
162
- {/* X Axis DataKey Cols*/}
163
- {!forestPlot.hideDateCategoryCol &&
164
- data.map((d, i) => {
165
- return (
166
- <Text className={`${d[config.xAxis.dataKey]}`} x={0} y={yScale(i)} textAnchor={'start'} verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={'black'}>
167
- {d[config.xAxis.dataKey]}
168
- </Text>
169
- )
170
- })}
171
-
172
- {/* X Axis Datakey Header */}
173
- {!forestPlot.hideDateCategoryCol && config.xAxis.dataKey && (
174
- <Text className={config.xAxis.dataKey} x={0} y={0} textAnchor={'start'} verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
175
- {config.xAxis.dataKey}
176
- </Text>
177
- )}
178
-
179
- {/* column headers */}
180
- {columnsOnChart.map(column => {
181
- return (
182
- <Text className={`${column.label}`} x={column.forestPlotAlignRight ? width : column.forestPlotStartingPoint} y={0} textAnchor={column.forestPlotAlignRight ? 'end' : 'start'} verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
183
- {column.label}
184
- </Text>
185
- )
186
- })}
187
- </>
188
- )
189
- }
190
-
191
- export default ForestPlot
@@ -1,277 +0,0 @@
1
- import React, { useContext, useEffect } from 'react'
2
- import ConfigContext from '../ConfigContext'
3
- import parse from 'html-react-parser'
4
- import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
5
- import LegendCircle from '@cdc/core/components/LegendCircle'
6
-
7
- import useLegendClasses from './../hooks/useLegendClasses'
8
- import { useHighlightedBars } from '../hooks/useHighlightedBars'
9
- import { Line } from '@visx/shape'
10
- import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
11
-
12
- // * FILE REVIEW *
13
- // TODO: fix eslint-disable jsxa11y issues
14
-
15
- // * ADDITIONAL NOTES *
16
- // > recently removed dynamic legend items as they weren't used
17
- // > recently removed boxplots, they don't provide any legend settings
18
-
19
- /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
20
- const Legend = () => {
21
- // prettier-ignore
22
- const {
23
- config,
24
- legend,
25
- colorScale,
26
- seriesHighlight,
27
- highlight,
28
- twoColorPalette,
29
- tableData,
30
- highlightReset,
31
- transformedData: data,
32
- colorPalettes,
33
- currentViewport,
34
- handleLineType
35
- } = useContext(ConfigContext)
36
-
37
- const { innerClasses, containerClasses } = useLegendClasses(config)
38
- const { visualizationType, visualizationSubType, series, runtime, orientation } = config
39
- // create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
40
- const reverseLabels = labels => (config.legend.reverseLabelOrder && config.legend.position === 'bottom' ? labels.reverse() : labels)
41
-
42
- const createLegendLabels = defaultLabels => {
43
- const colorCode = config.legend?.colorCode
44
- if (visualizationType === 'Deviation Bar') {
45
- const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
46
- const labelBelow = {
47
- datum: 'X',
48
- index: 0,
49
- text: `Below ${config.xAxis.targetLabel}`,
50
- value: belowColor
51
- }
52
- const labelAbove = {
53
- datum: 'X',
54
- index: 1,
55
- text: `Above ${config.xAxis.targetLabel}`,
56
- value: aboveColor
57
- }
58
-
59
- return reverseLabels([labelBelow, labelAbove])
60
- }
61
- if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
62
- let palette = colorPalettes[config.palette]
63
-
64
- while (tableData.length > palette.length) {
65
- palette = palette.concat(palette)
66
- }
67
- palette = palette.slice(0, data.length)
68
- //store unique values to Set by colorCode
69
- const set = new Set()
70
-
71
- tableData.forEach(d => set.add(d[colorCode]))
72
-
73
- // create labels with unique values
74
- const uniqueLabels = Array.from(set).map((val, i) => {
75
- const newLabel = {
76
- datum: val,
77
- index: i,
78
- text: val,
79
- value: palette[i]
80
- }
81
- return newLabel
82
- })
83
-
84
- return reverseLabels(uniqueLabels)
85
- }
86
-
87
- // get forecasting items inside of combo
88
- if (runtime?.forecastingSeriesKeys?.length > 0) {
89
- let seriesLabels = []
90
-
91
- //store unique values to Set by colorCode
92
-
93
- // loop through each stage/group/area on the chart and create a label
94
- config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
95
- return outerGroup?.stages?.map((stage, index) => {
96
- let colorValue = sequentialPalettes[stage.color]?.[2] ? sequentialPalettes[stage.color]?.[2] : colorPalettes[stage.color]?.[2] ? colorPalettes[stage.color]?.[2] : '#ccc'
97
-
98
- const newLabel = {
99
- datum: stage.key,
100
- index: index,
101
- text: stage.key,
102
- value: colorValue
103
- }
104
-
105
- seriesLabels.push(newLabel)
106
- })
107
- })
108
-
109
- // loop through bars for now to meet requirements.
110
- config.runtime.barSeriesKeys &&
111
- config.runtime.barSeriesKeys.forEach((bar, index) => {
112
- let colorValue = colorPalettes[config.palette][index] ? colorPalettes[config.palette][index] : '#ccc'
113
-
114
- const newLabel = {
115
- datum: bar,
116
- index: index,
117
- text: bar,
118
- value: colorValue
119
- }
120
-
121
- seriesLabels.push(newLabel)
122
- })
123
-
124
- return reverseLabels(seriesLabels)
125
- }
126
-
127
- // DEV-4161: replaceable series name in the legend
128
- const hasNewSeriesName = config.series.map(s => s.name).filter(item => item).length > 0
129
- if (hasNewSeriesName) {
130
- let palette = colorPalettes[config.palette]
131
-
132
- while (tableData.length > palette.length) {
133
- palette = palette.concat(palette)
134
- }
135
-
136
- palette = palette.slice(0, data.length)
137
- //store unique values to Set by colorCode
138
- const set = new Set()
139
-
140
- config.series.forEach(d => {
141
- set.add(d['name'] ? d['name'] : d['dataKey'])
142
- })
143
-
144
- // create labels with unique values
145
- const uniqueLabels = Array.from(set).map((val, i) => {
146
- const newLabel = {
147
- datum: val,
148
- index: i,
149
- text: val,
150
- value: palette[i]
151
- }
152
- return newLabel
153
- })
154
-
155
- return reverseLabels(uniqueLabels)
156
- }
157
-
158
- return reverseLabels(defaultLabels)
159
- }
160
-
161
- const isBottomOrSmallViewport = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
162
-
163
- const legendClasses = {
164
- marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
165
- marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `${config.dynamicMarginTop + 15}px`
166
- }
167
-
168
- const { HighLightedBarUtils } = useHighlightedBars(config)
169
-
170
- let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
171
- if (!legend) return null
172
-
173
- return (
174
- config.visualizationType !== 'Box Plot' && (
175
- <aside style={legendClasses} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
176
- {legend.label && <h2>{parse(legend.label)}</h2>}
177
- {legend.description && <p>{parse(legend.description)}</p>}
178
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
179
- {labels => {
180
- return (
181
- <div className={innerClasses.join(' ')}>
182
- {createLegendLabels(labels).map((label, i) => {
183
- let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
184
- let itemName = label.datum
185
-
186
- // Filter excluded data keys from legend
187
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
188
- return null
189
- }
190
-
191
- if (runtime.seriesLabels) {
192
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
193
- itemName = config.runtime.seriesKeys[index]
194
-
195
- if (runtime?.forecastingSeriesKeys?.length > 0) {
196
- itemName = label.text
197
- }
198
- }
199
-
200
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
201
- className.push('inactive')
202
- }
203
-
204
- return (
205
- <LegendItem
206
- className={className.join(' ')}
207
- tabIndex={0}
208
- key={`legend-quantile-${i}`}
209
- onKeyPress={e => {
210
- if (e.key === 'Enter') {
211
- highlight(label)
212
- }
213
- }}
214
- onClick={() => {
215
- highlight(label)
216
- }}
217
- >
218
- {config.visualizationType === 'Line' && config.legend.lineMode ? (
219
- <svg width={40} height={20}>
220
- <Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={label.value} strokeWidth={2} strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')} />
221
- </svg>
222
- ) : (
223
- <LegendCircle fill={label.value} />
224
- )}
225
-
226
- <LegendLabel align='left' margin='0 0 0 4px'>
227
- {label.text}
228
- </LegendLabel>
229
- </LegendItem>
230
- )
231
- })}
232
-
233
- {highLightedLegendItems.map((bar, i) => {
234
- // if duplicates only return first item
235
- let className = 'legend-item'
236
- let itemName = bar.legendLabel
237
-
238
- if (!itemName) return false
239
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
240
- className += ' inactive'
241
- }
242
- return (
243
- <LegendItem
244
- className={className}
245
- tabIndex={0}
246
- key={`legend-quantile-${i}`}
247
- onKeyPress={e => {
248
- if (e.key === 'Enter') {
249
- highlight(bar.legendLabel)
250
- }
251
- }}
252
- onClick={() => {
253
- highlight(bar.legendLabel)
254
- }}
255
- >
256
- <LegendCircle fill='transparent' borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`} />{' '}
257
- <LegendLabel align='left' margin='0 0 0 4px'>
258
- {bar.legendLabel ? bar.legendLabel : bar.value}
259
- </LegendLabel>
260
- </LegendItem>
261
- )
262
- })}
263
- {seriesHighlight.length > 0 && (
264
- <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
265
- Reset
266
- </button>
267
- )}
268
- </div>
269
- )
270
- }}
271
- </LegendOrdinal>
272
- </aside>
273
- )
274
- )
275
- }
276
-
277
- export default Legend
File without changes