@cdc/chart 4.23.11 → 4.24.2

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 (104) hide show
  1. package/dist/cdcchart.js +35740 -35027
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/tall-data.json +98 -0
  4. package/examples/feature/forest-plot/forest-plot.json +63 -19
  5. package/examples/feature/forest-plot/linear.json +52 -3
  6. package/examples/feature/forest-plot/log.json +26 -0
  7. package/examples/feature/forest-plot/logarithmic.json +0 -35
  8. package/examples/feature/line/line-chart-preliminary.json +393 -0
  9. package/examples/feature/regions/index.json +9 -5
  10. package/examples/feature/scatterplot/scatterplot.json +272 -33
  11. package/index.html +10 -8
  12. package/package.json +2 -2
  13. package/src/CdcChart.tsx +70 -234
  14. package/src/ConfigContext.tsx +6 -0
  15. package/src/_stories/ChartEditor.stories.tsx +22 -0
  16. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  17. package/src/_stories/_mock/pie_config.json +192 -0
  18. package/src/_stories/_mock/pie_data.json +218 -0
  19. package/src/_stories/_mock/preliminary_mock.json +346 -0
  20. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
  21. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +2 -26
  22. package/src/components/AreaChart/index.tsx +4 -0
  23. package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
  24. package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
  25. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +108 -0
  26. package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
  27. package/src/components/BarChart/components/BarChart.jsx +39 -0
  28. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  29. package/src/components/BarChart/components/context.tsx +13 -0
  30. package/src/components/BarChart/index.tsx +3 -0
  31. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +10 -9
  32. package/src/components/BoxPlot/index.tsx +3 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
  34. package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
  35. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  36. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
  37. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
  38. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
  39. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
  40. package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
  41. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
  42. package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
  43. package/src/components/EditorPanel/components/panels.scss +72 -0
  44. package/src/components/EditorPanel/editor-panel.scss +739 -0
  45. package/src/components/EditorPanel/index.tsx +3 -0
  46. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -2
  47. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  48. package/src/components/Forecasting/index.tsx +3 -0
  49. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  50. package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
  51. package/src/components/ForestPlot/index.tsx +1 -209
  52. package/src/components/Legend/Legend.Component.tsx +199 -0
  53. package/src/components/Legend/Legend.tsx +28 -0
  54. package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
  55. package/src/components/Legend/index.tsx +3 -0
  56. package/src/components/LineChart/LineChartProps.ts +29 -0
  57. package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
  58. package/src/components/LineChart/helpers.ts +45 -0
  59. package/src/components/LineChart/index.tsx +111 -23
  60. package/src/components/LinearChart.jsx +55 -72
  61. package/src/components/PairedBarChart.jsx +4 -2
  62. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
  63. package/src/components/PieChart/index.tsx +3 -0
  64. package/src/components/Regions/components/Regions.tsx +144 -0
  65. package/src/components/Regions/index.tsx +3 -0
  66. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  67. package/src/components/ScatterPlot/index.tsx +3 -0
  68. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  69. package/src/components/Sparkline/index.tsx +3 -0
  70. package/src/data/initial-state.js +10 -8
  71. package/src/helpers/abbreviateNumber.ts +17 -0
  72. package/src/helpers/computeMarginBottom.ts +55 -0
  73. package/src/helpers/filterData.ts +18 -0
  74. package/src/helpers/generateColorsArray.ts +8 -0
  75. package/src/helpers/getQuartiles.ts +30 -0
  76. package/src/helpers/handleChartAriaLabels.ts +19 -0
  77. package/src/helpers/handleLineType.ts +18 -0
  78. package/src/helpers/lineOptions.ts +18 -0
  79. package/src/helpers/sort.ts +7 -0
  80. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  81. package/src/hooks/useBarChart.js +7 -6
  82. package/src/hooks/useHighlightedBars.js +1 -1
  83. package/src/hooks/useMinMax.ts +3 -3
  84. package/src/hooks/useScales.ts +19 -6
  85. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
  86. package/src/scss/main.scss +0 -3
  87. package/src/types/ChartConfig.ts +167 -23
  88. package/src/types/ChartContext.ts +34 -12
  89. package/src/types/ForestPlot.ts +7 -14
  90. package/src/types/Label.ts +7 -0
  91. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  92. package/src/ConfigContext.jsx +0 -5
  93. package/src/components/BarChart.StackedVertical.tsx +0 -91
  94. package/src/components/BarChart.jsx +0 -30
  95. package/src/components/EditorPanel.jsx +0 -3356
  96. package/src/components/ForestPlot/Readme.md +0 -0
  97. package/src/components/Legend.jsx +0 -310
  98. package/src/components/LineChart/LineChart.Circle.tsx +0 -105
  99. package/src/scss/LinearChart.scss +0 -0
  100. package/src/scss/editor-panel.scss +0 -745
  101. package/src/scss/legend.scss +0 -206
  102. package/src/scss/mixins.scss +0 -0
  103. package/src/scss/variables.scss +0 -1
  104. package/src/types/ChartProps.ts +0 -7
@@ -1,5 +1,5 @@
1
1
  import React, { useContext } from 'react'
2
- import ConfigContext from '../ConfigContext'
2
+ import ConfigContext from '../../ConfigContext'
3
3
 
4
4
  export const useEditorPermissions = () => {
5
5
  const { config } = useContext(ConfigContext)
@@ -24,6 +24,12 @@ export const useEditorPermissions = () => {
24
24
 
25
25
  const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
26
26
 
27
+ const visSupportsDateCategoryAxis = () => {
28
+ const disabledCharts = ['Forest Plot']
29
+ if (disabledCharts.includes(visualizationType)) return false
30
+ return true
31
+ }
32
+
27
33
  const visSupportsSuperTitle = () => {
28
34
  const disabledCharts = ['Spark Line']
29
35
  if (disabledCharts.includes(visualizationType)) return false
@@ -186,6 +192,18 @@ export const useEditorPermissions = () => {
186
192
  return true
187
193
  }
188
194
 
195
+ const visSupportsValueAxisMax = () => {
196
+ const disabledCharts = ['Forest Plot']
197
+ if (disabledCharts.includes(visualizationType)) return false
198
+ return true
199
+ }
200
+
201
+ const visSupportsValueAxisMin = () => {
202
+ const disabledCharts = ['Forest Plot']
203
+ if (disabledCharts.includes(visualizationType)) return false
204
+ return true
205
+ }
206
+
189
207
  const visSupportsFilters = () => {
190
208
  const disabledCharts = ['Forest Plot']
191
209
  if (disabledCharts.includes(visualizationType)) return false
@@ -201,16 +219,22 @@ export const useEditorPermissions = () => {
201
219
 
202
220
  // implement later
203
221
  const visSupportsValueAxisTicks = () => {
222
+ const disabledCharts = ['Forest Plot']
223
+ if (disabledCharts.includes(visualizationType)) return false
204
224
  return true
205
225
  }
206
226
 
207
227
  // implement later
208
228
  const visSupportsValueAxisLine = () => {
229
+ const disabledCharts = ['Forest Plot']
230
+ if (disabledCharts.includes(visualizationType)) return false
209
231
  return true
210
232
  }
211
233
 
212
234
  // implement later
213
235
  const visSupportsValueAxisLabels = () => {
236
+ const disabledCharts = ['Forest Plot']
237
+ if (disabledCharts.includes(visualizationType)) return false
214
238
  return true
215
239
  }
216
240
 
@@ -251,6 +275,10 @@ export const useEditorPermissions = () => {
251
275
  return true
252
276
  }
253
277
 
278
+ const visSupportsDateCategoryAxisPadding = () => {
279
+ return config.xAxis.type === 'date' && config.xAxis.sortDates
280
+ }
281
+
254
282
  const visSupportsReactTooltip = () => {
255
283
  if (['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) || (visualizationType === 'Bar' && config.tooltips.singleSeries)) {
256
284
  return true
@@ -272,12 +300,14 @@ export const useEditorPermissions = () => {
272
300
  visSupportsBarSpace,
273
301
  visSupportsBarThickness,
274
302
  visSupportsChartHeight,
303
+ visSupportsDateCategoryAxis,
275
304
  visSupportsDateCategoryAxisLabel,
276
305
  visSupportsDateCategoryAxisLine,
277
306
  visSupportsDateCategoryAxisTicks,
278
307
  visSupportsDateCategoryHeight,
279
308
  visSupportsDateCategoryNumTicks,
280
309
  visSupportsDateCategoryTickRotation,
310
+ visSupportsDateCategoryAxisPadding,
281
311
  visSupportsFilters,
282
312
  visSupportsFootnotes,
283
313
  visSupportsLeftValueAxis,
@@ -294,6 +324,8 @@ export const useEditorPermissions = () => {
294
324
  visSupportsValueAxisLabels,
295
325
  visSupportsValueAxisLine,
296
326
  visSupportsValueAxisTicks,
297
- visSupportsReactTooltip
327
+ visSupportsReactTooltip,
328
+ visSupportsValueAxisMax,
329
+ visSupportsValueAxisMin
298
330
  }
299
331
  }
@@ -1,7 +1,7 @@
1
1
  import React, { useContext } from 'react'
2
2
 
3
3
  // cdc
4
- import ConfigContext from '../ConfigContext'
4
+ import ConfigContext from '../../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
6
  import { colorPalettesChart, sequentialPalettes } from '@cdc/core/data/colorPalettes'
7
7
 
@@ -0,0 +1,3 @@
1
+ import Forecasting from './Forecasting'
2
+
3
+ export default Forecasting
@@ -0,0 +1,254 @@
1
+ import { useContext, useEffect, useState } from 'react'
2
+
3
+ // visx
4
+ import { Group } from '@visx/group'
5
+ import { Line, Bar, Circle, LinePath } from '@visx/shape'
6
+ import { Text } from '@visx/text'
7
+ import { scaleLinear } from '@visx/scale'
8
+ import { curveLinearClosed } from '@visx/curve'
9
+
10
+ // types
11
+ import { type ForestPlotProps } from '@cdc/chart/src/components/ForestPlot/ForestPlotProps'
12
+ import { type ChartConfig } from '@cdc/chart/src/types/ChartConfig'
13
+ import { type ChartContext } from '@cdc/chart/src/types/ChartContext'
14
+
15
+ // cdc
16
+ import ConfigContext from '../../ConfigContext'
17
+ import { getFontSize } from '@cdc/core/helpers/cove/number'
18
+
19
+ const ForestPlot = (props: ForestPlotProps) => {
20
+ const { rawData: data, updateConfig } = useContext<ChartContext>(ConfigContext)
21
+ const { xScale, yScale, config, height, width, handleTooltipMouseOff, handleTooltipMouseOver } = props
22
+ const { forestPlot } = config as ChartConfig
23
+ const labelPosition = config.xAxis.tickWidthMax + 10
24
+ const [initialLogTicksSet, setInitialLogTicks] = useState(false)
25
+
26
+ useEffect(() => {
27
+ try {
28
+ const defaultColumns = ['estimateField', 'lower', 'upper', 'estimateRadius']
29
+ const newConfig = config
30
+
31
+ // Setting a default number of colums.additionalColumn1, columns.additionalColumn2, etc.
32
+ // to loop through to clean up which columns are being displayed by default for forest plots
33
+ // Set tooltip and data table properties according to the default forestPlot column names above.
34
+ const colsToCheck = 10
35
+ for (let i = 0; i < colsToCheck; i++) {
36
+ defaultColumns.forEach(col => {
37
+ if (config.forestPlot[col] && config.forestPlot[col] !== newConfig.columns[config.forestPlot[`additionalColumn${i}`]]?.name) {
38
+ delete newConfig.columns[`additionalColumn${i}`] // Remove old value if found to prevent duplicates
39
+ newConfig.columns[config.forestPlot[col]] = {}
40
+ newConfig.columns[config.forestPlot[col]].dataKey = newConfig.forestPlot[col]
41
+ newConfig.columns[config.forestPlot[col]].name = newConfig.forestPlot[col]
42
+ newConfig.columns[config.forestPlot[col]].dataTable = true
43
+ newConfig.columns[config.forestPlot[col]].tooltips = true
44
+ newConfig.columns[config.forestPlot[col]].label = newConfig.forestPlot[col]
45
+ }
46
+ })
47
+ }
48
+
49
+ // Add weight column into tooltip and data table.
50
+ if (config.forestPlot.radius.scalingColumn) {
51
+ newConfig.columns[config.forestPlot.radius.scalingColumn] = {}
52
+ newConfig.columns[config.forestPlot.radius.scalingColumn].dataKey = newConfig.forestPlot.radius.scalingColumn
53
+ newConfig.columns[config.forestPlot.radius.scalingColumn].name = newConfig.forestPlot.radius.scalingColumn
54
+ newConfig.columns[config.forestPlot.radius.scalingColumn].label = newConfig.forestPlot.radius.scalingColumn
55
+ newConfig.columns[config.forestPlot.radius.scalingColumn].dataTable = true
56
+ newConfig.columns[config.forestPlot.radius.scalingColumn].tooltips = true
57
+ }
58
+
59
+ if (newConfig.table.showVertical) {
60
+ newConfig.table.indexLabel = config.xAxis.dataKey
61
+ }
62
+
63
+ updateConfig(newConfig)
64
+ } catch (e) {
65
+ console.log(e.message)
66
+ }
67
+ }, [])
68
+
69
+ useEffect(() => {
70
+ if (!initialLogTicksSet && config.forestPlot.type === 'Logarithmic') {
71
+ updateConfig({
72
+ ...config,
73
+ dataFormat: {
74
+ ...config.dataFormat,
75
+ roundTo: 2
76
+ }
77
+ })
78
+ setInitialLogTicks(true)
79
+ }
80
+ }, [config.forestPlot.type])
81
+
82
+ const pooledData = config.data.find(d => d[config.xAxis.dataKey] === config.forestPlot.pooledResult.column)
83
+
84
+ const regressionPoints = pooledData
85
+ ? [
86
+ { x: xScale(pooledData[config.forestPlot.lower]), y: height - Number(config.forestPlot.rowHeight) },
87
+ { x: xScale(pooledData[config.forestPlot.estimateField]), y: height - forestPlot.pooledResult.diamondHeight - Number(config.forestPlot.rowHeight) },
88
+ { x: xScale(pooledData[config.forestPlot.upper]), y: height - Number(config.forestPlot.rowHeight) },
89
+ { x: xScale(pooledData[config.forestPlot.estimateField]), y: height + forestPlot.pooledResult.diamondHeight - Number(config.forestPlot.rowHeight) },
90
+ { x: xScale(pooledData[config.forestPlot.lower]), y: height - Number(config.forestPlot.rowHeight) }
91
+ ]
92
+ : []
93
+
94
+ const topMarginOffset = config.forestPlot.rowHeight
95
+
96
+ const topLine = [
97
+ { x: 0, y: topMarginOffset },
98
+ { x: width, y: topMarginOffset }
99
+ ]
100
+
101
+ const bottomLine = [
102
+ { x: 0, y: height },
103
+ { x: width, y: height }
104
+ ]
105
+
106
+ type Columns = {
107
+ forestPlot?: boolean
108
+ }
109
+
110
+ const columnsOnChart: Columns[] = Object.entries(config.columns)
111
+ .map(entry => entry[1])
112
+ .filter(entry => entry.forestPlot === true)
113
+
114
+ return (
115
+ <>
116
+ <Group width={width}>
117
+ {forestPlot.title && (
118
+ <Text className={`forest-plot--title`} x={forestPlot.type === 'Linear' ? xScale(0) : xScale(1)} y={0} textAnchor='middle' verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
119
+ {forestPlot.title}
120
+ </Text>
121
+ )}
122
+
123
+ {/* Line of no effect on Continuous Scale. */}
124
+ {forestPlot.lineOfNoEffect.show && forestPlot.type === 'Linear' && <Line from={{ x: xScale(0), y: 0 + topMarginOffset }} to={{ x: xScale(0), y: height }} className='forestplot__line-of-no-effect' stroke={forestPlot.regression.baseLineColor || 'black'} />}
125
+
126
+ {/* Line of no effect on Logarithmic Scale. */}
127
+ {forestPlot.lineOfNoEffect.show && forestPlot.type === 'Logarithmic' && <Line from={{ x: xScale(1), y: 0 + topMarginOffset }} to={{ x: xScale(1), y: height }} className='forestplot__line-of-no-effect' stroke={forestPlot.regression.baseLineColor || 'black'} />}
128
+
129
+ {data.map((d, i) => {
130
+ // calculate both square and circle size based on radius.min and radius.max
131
+ const scaleRadius = scaleLinear({
132
+ domain: data.map(d => d[forestPlot.radius.scalingColumn]),
133
+ range: [forestPlot.radius.min, forestPlot.radius.max]
134
+ })
135
+
136
+ // glyph settings
137
+ const rectSize = forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.radius.scalingColumn]) : 4
138
+ const shapeColor = forestPlot.colors.shape ? forestPlot.colors.shape : 'black'
139
+ const lineColor = forestPlot.colors.line ? forestPlot.colors.line : 'black'
140
+
141
+ // ci size
142
+ const ciEndSize = 4
143
+
144
+ // Don't run calculations on the pooled column
145
+ const isTotalColumn = d[config.xAxis.dataKey] === forestPlot.pooledResult.column
146
+
147
+ return !isTotalColumn ? (
148
+ <Group>
149
+ {/* Confidence Interval Paths */}
150
+ <path
151
+ stroke={lineColor}
152
+ strokeWidth={1}
153
+ className='lower-ci'
154
+ d={`
155
+ M${xScale(d[forestPlot.lower])} ${yScale(i) - Number(ciEndSize)}
156
+ L${xScale(d[forestPlot.lower])} ${yScale(i) + Number(ciEndSize)}
157
+ `}
158
+ />
159
+
160
+ <path
161
+ stroke={lineColor}
162
+ strokeWidth={1}
163
+ className='upper-ci'
164
+ d={`
165
+ M${xScale(d[forestPlot.upper])} ${yScale(i) - Number(ciEndSize)}
166
+ L${xScale(d[forestPlot.upper])} ${yScale(i) + Number(ciEndSize)}
167
+ `}
168
+ />
169
+
170
+ {/* main line */}
171
+ <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)} />
172
+ {forestPlot.shape === 'circle' && (
173
+ <Circle className='forest-plot--circle' cx={xScale(Number(d[forestPlot.estimateField]))} cy={yScale(i)} r={forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.radius.scalingColumn]) : 4} fill={shapeColor} style={{ opacity: 1, filter: 'unset' }} />
174
+ )}
175
+ {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' }} />}
176
+ {forestPlot.shape === 'text' && (
177
+ <Text className='forest-plot--text' x={xScale(Number(d[forestPlot.estimateField]))} y={yScale(i)} textAnchor='middle' verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={shapeColor}>
178
+ {d[forestPlot.estimateField]}
179
+ </Text>
180
+ )}
181
+ </Group>
182
+ ) : (
183
+ <LinePath data={regressionPoints} x={d => d.x} y={d => d.y - getFontSize(config.fontSize) / 2} stroke='black' strokeWidth={2} fill={'black'} curve={curveLinearClosed} />
184
+ )
185
+ })}
186
+
187
+ {/* regression diamond */}
188
+ {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} />}
189
+ {/* regression text */}
190
+ {forestPlot.regression.description && (
191
+ <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 }}>
192
+ {forestPlot.regression.description}
193
+ </Text>
194
+ )}
195
+
196
+ <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} />
197
+ </Group>
198
+ <Line from={topLine[0]} to={topLine[1]} style={{ stroke: 'black', strokeWidth: 2 }} className='forestplot__top-line' />
199
+ <Line from={bottomLine[0]} to={bottomLine[1]} style={{ stroke: 'black', strokeWidth: 2 }} className='forestplot__bottom-line' />
200
+
201
+ {/* column data */}
202
+ {columnsOnChart.map(column => {
203
+ return data.map((d, i) => {
204
+ return (
205
+ <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'}>
206
+ {d[column.name]}
207
+ </Text>
208
+ )
209
+ })
210
+ })}
211
+
212
+ {/* X Axis DataKey Cols*/}
213
+ {!forestPlot.hideDateCategoryCol &&
214
+ data.map((d, i) => {
215
+ return (
216
+ <Text className={`${d[config.xAxis.dataKey]}`} x={0} y={yScale(i)} textAnchor={'start'} verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={'black'}>
217
+ {d[config.xAxis.dataKey]}
218
+ </Text>
219
+ )
220
+ })}
221
+
222
+ {/* X Axis Datakey Header */}
223
+ {!forestPlot.hideDateCategoryCol && config.xAxis.dataKey && (
224
+ <Text className={config.xAxis.dataKey} x={0} y={0} textAnchor={'start'} verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
225
+ {config.xAxis.dataKey}
226
+ </Text>
227
+ )}
228
+
229
+ {/* column headers */}
230
+ {columnsOnChart.map(column => {
231
+ return (
232
+ <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'}>
233
+ {column.label}
234
+ </Text>
235
+ )
236
+ })}
237
+
238
+ {/* left bottom label */}
239
+ {forestPlot.leftLabel && (
240
+ <Text className='forest-plot__left-label' x={forestPlot.type === 'Linear' ? xScale(0) - 25 : xScale(1) - 25} y={height + labelPosition} textAnchor='end' verticalAnchor='start'>
241
+ {forestPlot.leftLabel}
242
+ </Text>
243
+ )}
244
+
245
+ {forestPlot.rightLabel && (
246
+ <Text className='forest-plot__right-label' x={forestPlot.type === 'Linear' ? xScale(0) + 25 : xScale(1) + 25} y={height + labelPosition} textAnchor='start' verticalAnchor='start'>
247
+ {forestPlot.rightLabel}
248
+ </Text>
249
+ )}
250
+ </>
251
+ )
252
+ }
253
+
254
+ export default ForestPlot
@@ -1,11 +1,18 @@
1
1
  import { type ChartConfig } from '@cdc/chart/src/types/ChartConfig'
2
2
 
3
3
  export type ForestPlotProps = {
4
+ /** xScale - scaling function for bottom axis */
4
5
  xScale: Function
6
+ /** yScale - scaling function for left axis */
5
7
  yScale: Function
8
+ /** config - standard chart config */
6
9
  config: ChartConfig
10
+ /** height - height of chart */
7
11
  height: number
12
+ /** width - width of chart */
8
13
  width: number
14
+ /** handleTooltipMouseOff - helper function for hiding tooltip */
9
15
  handleTooltipMouseOff: Function
16
+ /** handleTooltipMousOver - helper function for showing tooltip */
10
17
  handleTooltipMouseOver: Function
11
18
  }
@@ -1,211 +1,3 @@
1
- import { useContext, useEffect } from 'react'
2
-
3
- // visx
4
- import { Group } from '@visx/group'
5
- import { Line, Bar, Circle, LinePath } from '@visx/shape'
6
- import { Text } from '@visx/text'
7
- import { scaleLinear } from '@visx/scale'
8
- import { curveLinearClosed } from '@visx/curve'
9
-
10
- // types
11
- import { type ForestPlotProps } from '@cdc/chart/src/components/ForestPlot/ForestPlotProps'
12
- import { type ChartConfig } from '@cdc/chart/src/types/ChartConfig'
13
- import { type ChartContext } from '@cdc/chart/src/types/ChartContext'
14
-
15
- // cdc
16
- import ConfigContext from '../../ConfigContext'
17
- import { getFontSize } from '@cdc/core/helpers/cove/number'
18
-
19
- const ForestPlot = (props: ForestPlotProps) => {
20
- const { rawData: data, updateConfig } = useContext<ChartContext>(ConfigContext)
21
- const { xScale, yScale, config, height, width, handleTooltipMouseOff, handleTooltipMouseOver } = props
22
- const { forestPlot } = config as ChartConfig
23
-
24
- // Requirements for forest plot
25
- // - force legend to be hidden for this chart type
26
- // - reset the date category axis to zero
27
- useEffect(() => {
28
- if (!config.legend.hide) {
29
- updateConfig({
30
- ...config,
31
- legend: {
32
- ...config.legend,
33
- hide: true
34
- },
35
- xAxis: {
36
- ...config.xAxis,
37
- size: 0
38
- }
39
- })
40
- }
41
- }, [])
42
-
43
- const pooledData = config.data.find(d => d[config.xAxis.dataKey] === config.forestPlot.pooledResult.column)
44
-
45
- const regressionPoints = pooledData
46
- ? [
47
- { x: xScale(pooledData[config.forestPlot.lower]), y: height - Number(config.forestPlot.rowHeight) },
48
- { x: xScale(pooledData[config.forestPlot.estimateField]), y: height - forestPlot.pooledResult.diamondHeight - Number(config.forestPlot.rowHeight) },
49
- { x: xScale(pooledData[config.forestPlot.upper]), y: height - Number(config.forestPlot.rowHeight) },
50
- { x: xScale(pooledData[config.forestPlot.estimateField]), y: height + forestPlot.pooledResult.diamondHeight - Number(config.forestPlot.rowHeight) },
51
- { x: xScale(pooledData[config.forestPlot.lower]), y: height - Number(config.forestPlot.rowHeight) }
52
- ]
53
- : []
54
-
55
- const topMarginOffset = config.forestPlot.rowHeight
56
-
57
- const topLine = [
58
- { x: 0, y: topMarginOffset },
59
- { x: width, y: topMarginOffset }
60
- ]
61
-
62
- const bottomLine = [
63
- { x: 0, y: height },
64
- { x: width, y: height }
65
- ]
66
-
67
- const columnsOnChart = Object.entries(config.columns)
68
- .map(entry => entry[1])
69
- .filter(entry => entry.forestPlot === true)
70
-
71
- return (
72
- <>
73
- <Group>
74
- {forestPlot.title && (
75
- <Text className={`forest-plot--title`} x={forestPlot.type === 'Linear' ? xScale(0) : xScale(1)} y={0} textAnchor='middle' verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
76
- {forestPlot.title}
77
- </Text>
78
- )}
79
-
80
- {/* Line of no effect on Continuous Scale. */}
81
- {forestPlot.lineOfNoEffect.show && forestPlot.type === 'Linear' && <Line from={{ x: xScale(0), y: 0 + topMarginOffset }} to={{ x: xScale(0), y: height }} className='forestplot__line-of-no-effect' stroke={forestPlot.regression.baseLineColor || 'black'} />}
82
-
83
- {/* Line of no effect on Logarithmic Scale. */}
84
- {forestPlot.lineOfNoEffect.show && forestPlot.type === 'Logarithmic' && <Line from={{ x: xScale(1), y: 0 + topMarginOffset }} to={{ x: xScale(1), y: height }} className='forestplot__line-of-no-effect' stroke={forestPlot.regression.baseLineColor || 'black'} />}
85
-
86
- {data.map((d, i) => {
87
- // calculate both square and circle size based on radius.min and radius.max
88
- const scaleRadius = scaleLinear({
89
- domain: data.map(d => d[forestPlot.radius.scalingColumn]),
90
- range: [forestPlot.radius.min, forestPlot.radius.max]
91
- })
92
-
93
- // glyph settings
94
- const rectSize = forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.radius.scalingColumn]) : 4
95
- const shapeColor = forestPlot.colors.shape ? forestPlot.colors.shape : 'black'
96
- const lineColor = forestPlot.colors.line ? forestPlot.colors.line : 'black'
97
-
98
- // ci size
99
- const ciEndSize = 4
100
-
101
- // Don't run calculations on the pooled column
102
- const isTotalColumn = d[config.xAxis.dataKey] === forestPlot.pooledResult.column
103
-
104
- return !isTotalColumn ? (
105
- <Group>
106
- {/* Confidence Interval Paths */}
107
- <path
108
- stroke={lineColor}
109
- strokeWidth={1}
110
- className='lower-ci'
111
- d={`
112
- M${xScale(d[forestPlot.lower])} ${yScale(i) - Number(ciEndSize)}
113
- L${xScale(d[forestPlot.lower])} ${yScale(i) + Number(ciEndSize)}
114
- `}
115
- />
116
-
117
- <path
118
- stroke={lineColor}
119
- strokeWidth={1}
120
- className='upper-ci'
121
- d={`
122
- M${xScale(d[forestPlot.upper])} ${yScale(i) - Number(ciEndSize)}
123
- L${xScale(d[forestPlot.upper])} ${yScale(i) + Number(ciEndSize)}
124
- `}
125
- />
126
-
127
- {/* main line */}
128
- <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)} />
129
- {forestPlot.shape === 'circle' && (
130
- <Circle className='forest-plot--circle' cx={xScale(Number(d[forestPlot.estimateField]))} cy={yScale(i)} r={forestPlot.radius.scalingColumn !== '' ? scaleRadius(data[i][forestPlot.radius.scalingColumn]) : 4} fill={shapeColor} style={{ opacity: 1, filter: 'unset' }} />
131
- )}
132
- {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' }} />}
133
- {forestPlot.shape === 'text' && (
134
- <Text className='forest-plot--text' x={xScale(Number(d[forestPlot.estimateField]))} y={yScale(i)} textAnchor='middle' verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={shapeColor}>
135
- {d[forestPlot.estimateField]}
136
- </Text>
137
- )}
138
- </Group>
139
- ) : (
140
- <LinePath data={regressionPoints} x={d => d.x} y={d => d.y - getFontSize(config.fontSize) / 2} stroke='black' strokeWidth={2} fill={'black'} curve={curveLinearClosed} />
141
- )
142
- })}
143
-
144
- {/* regression diamond */}
145
- {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} />}
146
- {/* regression text */}
147
- {forestPlot.regression.description && (
148
- <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 }}>
149
- {forestPlot.regression.description}
150
- </Text>
151
- )}
152
-
153
- <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} />
154
- </Group>
155
- <Line from={topLine[0]} to={topLine[1]} style={{ stroke: 'black', strokeWidth: 2 }} className='forestplot__top-line' />
156
- <Line from={bottomLine[0]} to={bottomLine[1]} style={{ stroke: 'black', strokeWidth: 2 }} className='forestplot__bottom-line' />
157
-
158
- {/* column data */}
159
- {columnsOnChart.map(column => {
160
- return rawData.map((d, i) => {
161
- return (
162
- <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'}>
163
- {d[column.name]}
164
- </Text>
165
- )
166
- })
167
- })}
168
-
169
- {/* X Axis DataKey Cols*/}
170
- {!forestPlot.hideDateCategoryCol &&
171
- data.map((d, i) => {
172
- return (
173
- <Text className={`${d[config.xAxis.dataKey]}`} x={0} y={yScale(i)} textAnchor={'start'} verticalAnchor='middle' fontSize={getFontSize(config.fontSize)} fill={'black'}>
174
- {d[config.xAxis.dataKey]}
175
- </Text>
176
- )
177
- })}
178
-
179
- {/* X Axis Datakey Header */}
180
- {!forestPlot.hideDateCategoryCol && config.xAxis.dataKey && (
181
- <Text className={config.xAxis.dataKey} x={0} y={0} textAnchor={'start'} verticalAnchor='start' fontSize={getFontSize(config.fontSize)} fill={'black'}>
182
- {config.xAxis.dataKey}
183
- </Text>
184
- )}
185
-
186
- {/* column headers */}
187
- {columnsOnChart.map(column => {
188
- return (
189
- <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'}>
190
- {column.label}
191
- </Text>
192
- )
193
- })}
194
-
195
- {/* left bottom label */}
196
- {forestPlot.leftLabel && (
197
- <Text className='forest-plot__left-label' x={forestPlot.type === 'Linear' ? xScale(0) - 25 : xScale(1) - 25} y={height + 50} textAnchor='end'>
198
- {forestPlot.leftLabel}
199
- </Text>
200
- )}
201
-
202
- {forestPlot.rightLabel && (
203
- <Text className='forest-plot__right-label' x={forestPlot.type === 'Linear' ? xScale(0) + 25 : xScale(1) + 25} y={height + 50} textAnchor='start'>
204
- {forestPlot.rightLabel}
205
- </Text>
206
- )}
207
- </>
208
- )
209
- }
1
+ import ForestPlot from './ForestPlot'
210
2
 
211
3
  export default ForestPlot