@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.
- package/dist/cdcchart.js +34606 -32218
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/example-bar-chart.json +1 -46
- package/examples/feature/bar/lollipop.json +156 -0
- package/examples/feature/bar/tall-data.json +98 -0
- package/examples/feature/combo/planet-combo-example-config.json +99 -9
- package/examples/feature/dev-4261.json +399 -0
- package/examples/feature/forest-plot/forest-plot.json +63 -19
- package/examples/feature/forest-plot/{broken.json → linear.json} +77 -23
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +271 -0
- package/examples/feature/line/line-chart-preliminary.json +346 -0
- package/examples/feature/line/line-points.json +340 -0
- package/examples/feature/regions/index.json +462 -0
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
- package/examples/private/chart-t.json +3740 -0
- package/examples/private/combo.json +369 -0
- package/examples/private/epi-data.csv +13 -0
- package/examples/private/epi-data.json +62 -0
- package/examples/private/epi.json +403 -0
- package/examples/private/occupancy.json +109283 -0
- package/examples/private/prod-line-config.json +401 -0
- package/examples/private/region-data.json +822 -0
- package/examples/private/region-testing.json +312 -0
- package/examples/private/scaling.json +45325 -0
- package/examples/private/testing-data.json +1739 -0
- package/examples/private/testing.json +816 -0
- package/examples/sparkline-multilple.json +846 -0
- package/index.html +12 -8
- package/package.json +3 -3
- package/src/CdcChart.tsx +42 -211
- package/src/ConfigContext.tsx +6 -0
- package/src/_stories/Chart.stories.tsx +188 -0
- package/src/_stories/Chart.tooltip.stories.tsx +305 -0
- package/src/_stories/ChartBrush.stories.tsx +19 -0
- package/src/_stories/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -0
- package/src/_stories/ChartSuppress.stories.tsx +19 -0
- package/src/_stories/_mock/brush_mock.json +393 -0
- package/src/_stories/_mock/pie_config.json +191 -0
- package/src/_stories/_mock/pie_data.json +218 -0
- package/src/_stories/_mock/preliminary_mock.json +346 -0
- package/src/_stories/_mock/suppress_mock.json +911 -0
- package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +6 -7
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +7 -36
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.jsx → BarChart/components/BarChart.Horizontal.tsx} +111 -34
- package/src/components/{BarChart.StackedHorizontal.jsx → BarChart/components/BarChart.StackedHorizontal.tsx} +55 -20
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
- package/src/components/{BarChart.Vertical.jsx → BarChart/components/BarChart.Vertical.tsx} +162 -34
- package/src/components/BarChart/components/BarChart.jsx +39 -0
- package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
- package/src/components/BarChart/components/context.tsx +13 -0
- package/src/components/BarChart/index.tsx +3 -0
- package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/DeviationBar.jsx +4 -3
- package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +807 -865
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +190 -220
- package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +23 -4
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels.tsx +13 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +751 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +50 -5
- package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
- package/src/components/Forecasting/index.tsx +3 -0
- package/src/components/ForestPlot/ForestPlot.tsx +254 -0
- package/src/components/ForestPlot/ForestPlotProps.ts +18 -0
- package/src/components/ForestPlot/index.scss +1 -0
- package/src/components/ForestPlot/index.tsx +3 -0
- package/src/components/Legend/Legend.tsx +347 -0
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +46 -0
- package/src/components/{LineChart.Circle.tsx → LineChart/components/LineChart.Circle.tsx} +36 -30
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.scss +1 -0
- package/src/components/{LineChart.tsx → LineChart/index.tsx} +83 -42
- package/src/components/LinearChart.jsx +125 -82
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +135 -0
- package/src/components/Regions/index.tsx +3 -0
- package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
- package/src/components/ScatterPlot/index.tsx +3 -0
- package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
- package/src/components/Sparkline/index.tsx +3 -0
- package/src/components/ZoomBrush.tsx +168 -0
- package/src/data/initial-state.js +30 -16
- package/src/helpers/abbreviateNumber.ts +17 -0
- package/src/helpers/computeMarginBottom.ts +55 -0
- package/src/helpers/filterData.ts +18 -0
- package/src/helpers/generateColorsArray.ts +8 -0
- package/src/helpers/getQuartiles.ts +30 -0
- package/src/helpers/handleChartAriaLabels.ts +19 -0
- package/src/helpers/handleLineType.ts +18 -0
- package/src/helpers/lineOptions.ts +18 -0
- package/src/helpers/sort.ts +7 -0
- package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
- package/src/hooks/useBarChart.js +72 -7
- package/src/hooks/useColorScale.ts +50 -0
- package/src/hooks/{useMinMax.js → useMinMax.ts} +75 -23
- package/src/hooks/{useRightAxis.js → useRightAxis.ts} +10 -2
- package/src/hooks/{useScales.js → useScales.ts} +64 -17
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +84 -55
- package/src/scss/main.scss +70 -38
- package/src/types/ChartConfig.ts +178 -0
- package/src/types/ChartContext.ts +54 -0
- package/src/types/ForestPlot.ts +53 -0
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.jsx +0 -95
- package/src/components/BarChart.jsx +0 -30
- package/src/components/ForestPlot.jsx +0 -191
- package/src/components/Legend.jsx +0 -277
- package/src/scss/LinearChart.scss +0 -0
- package/src/scss/editor-panel.scss +0 -745
- package/src/scss/legend.scss +0 -206
- package/src/scss/mixins.scss +0 -0
- package/src/scss/variables.scss +0 -1
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../ConfigContext'
|
|
3
|
-
import {
|
|
3
|
+
import { type ChartContext } from '../types/ChartContext'
|
|
4
4
|
|
|
5
5
|
// third party
|
|
6
6
|
import { localPoint } from '@visx/event'
|
|
7
7
|
import { bisector } from 'd3-array'
|
|
8
|
+
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
9
|
+
const transform = new DataTransform()
|
|
8
10
|
|
|
9
11
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
10
12
|
|
|
11
13
|
export const useTooltip = props => {
|
|
12
|
-
const { tableData
|
|
14
|
+
const { tableData, config, formatNumber, capitalize, formatDate, parseDate, setSharedFilter } = useContext<ChartContext>(ConfigContext)
|
|
13
15
|
const { xScale, yScale, showTooltip, hideTooltip } = props
|
|
14
|
-
const { xAxis, visualizationType, orientation, yAxis, runtime } = config
|
|
15
|
-
|
|
16
|
+
const { xAxis, visualizationType, orientation, yAxis, runtime, barWidth } = config
|
|
17
|
+
const data = transform.applySuppression(tableData, config.suppressedData)
|
|
16
18
|
/**
|
|
17
19
|
* Provides the tooltip information based on the tooltip data array and svg cursor coordinates
|
|
18
20
|
* @function getTooltipInformation
|
|
@@ -51,13 +53,12 @@ export const useTooltip = props => {
|
|
|
51
53
|
const { x, y } = eventSvgCoords
|
|
52
54
|
|
|
53
55
|
// Additional data for pie charts
|
|
54
|
-
const { data: pieChartData, arc } = additionalChartData
|
|
56
|
+
const { data: pieChartData, arc } = additionalChartData ?? {}
|
|
55
57
|
|
|
56
58
|
const closestXScaleValue = getXValueFromCoordinate(x - Number(config.yAxis.size || 0))
|
|
57
59
|
|
|
58
60
|
const includedSeries = visualizationType !== 'Pie' ? config.series.filter(series => series.tooltip === true).map(item => item.dataKey) : config.series.map(item => item.dataKey)
|
|
59
61
|
includedSeries.push(config.xAxis.dataKey)
|
|
60
|
-
|
|
61
62
|
if (config.visualizationType === 'Forecasting') {
|
|
62
63
|
config.series.map(s => {
|
|
63
64
|
s.confidenceIntervals.map(c => {
|
|
@@ -68,15 +69,23 @@ export const useTooltip = props => {
|
|
|
68
69
|
})
|
|
69
70
|
})
|
|
70
71
|
}
|
|
72
|
+
function getColumnNames(columns) {
|
|
73
|
+
let names = []
|
|
74
|
+
for (let key in columns) {
|
|
75
|
+
if (columns.hasOwnProperty(key)) {
|
|
76
|
+
names.push(columns[key].name)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return names
|
|
80
|
+
}
|
|
81
|
+
includedSeries.push(...getColumnNames(config.columns))
|
|
82
|
+
includedSeries.push(...getColumnNames(config.columns))
|
|
71
83
|
|
|
72
84
|
const yScaleValues = getYScaleValues(closestXScaleValue, includedSeries)
|
|
73
|
-
|
|
74
85
|
const xScaleValues = data.filter(d => d[xAxis.dataKey] === getClosestYValue(y))
|
|
75
86
|
|
|
76
87
|
const resolvedScaleValues = orientation === 'vertical' ? yScaleValues : xScaleValues
|
|
77
88
|
|
|
78
|
-
const forestPlotXValue = visualizationType === 'Forest Plot' ? data?.filter(d => d[xAxis.dataKey] === getClosestYValue(y))[0][config.forestPlot.estimateField] : null
|
|
79
|
-
|
|
80
89
|
const getAxisPosition = seriesKey => {
|
|
81
90
|
const seriesObj = config.series.filter(s => s.dataKey === seriesKey)[0]
|
|
82
91
|
const position = seriesObj?.axis ? String(seriesObj.axis).toLowerCase() : 'left'
|
|
@@ -84,49 +93,62 @@ export const useTooltip = props => {
|
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
const getTooltipDataArray = () => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
let closestValue = getClosestYValue(y, colVals.name)
|
|
100
|
-
|
|
101
|
-
const formattedValue = formatColNumber(closestValue, 'left', true, config, formattingParams)
|
|
96
|
+
const columns = config.columns
|
|
97
|
+
const columnsWithTooltips = []
|
|
98
|
+
const tooltipItems = []
|
|
99
|
+
|
|
100
|
+
for (const [colKeys, colVals] of Object.entries(columns)) {
|
|
101
|
+
const formattingParams = {
|
|
102
|
+
addColPrefix: config.columns[colKeys].prefix,
|
|
103
|
+
addColSuffix: config.columns[colKeys].suffix,
|
|
104
|
+
addColRoundTo: config.columns[colKeys].roundToPlace ? config.columns[colKeys].roundToPlace : '',
|
|
105
|
+
addColCommas: config.columns[colKeys].commas
|
|
106
|
+
}
|
|
107
|
+
let closestValue = null
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
if (config.visualizationType === 'Pie') {
|
|
110
|
+
closestValue = arc?.data[colVals.name]
|
|
111
|
+
} else {
|
|
112
|
+
closestValue = resolvedScaleValues[0]?.[colVals.name]
|
|
106
113
|
}
|
|
107
114
|
|
|
108
|
-
const
|
|
109
|
-
tooltipItems.push([config.xAxis.dataKey, getClosestYValue(y)])
|
|
115
|
+
const formattedValue = formatColNumber(closestValue, 'left', true, config, formattingParams)
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
return tooltipItems
|
|
117
|
+
if (colVals.tooltips) {
|
|
118
|
+
columnsWithTooltips.push([colVals.label, formattedValue])
|
|
119
|
+
}
|
|
115
120
|
}
|
|
121
|
+
const additionalTooltipItems = []
|
|
122
|
+
|
|
123
|
+
columnsWithTooltips.forEach(columnData => {
|
|
124
|
+
additionalTooltipItems.push([columnData[0], columnData[1]])
|
|
125
|
+
})
|
|
116
126
|
|
|
117
127
|
if (visualizationType === 'Pie') {
|
|
118
|
-
|
|
128
|
+
tooltipItems.push(
|
|
129
|
+
// ignore
|
|
119
130
|
[config.xAxis.dataKey, pieChartData],
|
|
120
131
|
[config.runtime.yAxis.dataKey, formatNumber(arc?.data[config.runtime.yAxis.dataKey])],
|
|
121
132
|
['Percent', `${Math.round((((arc?.endAngle - arc?.startAngle) * 180) / Math.PI / 360) * 100) + '%'}`]
|
|
122
|
-
|
|
133
|
+
)
|
|
123
134
|
}
|
|
124
135
|
|
|
125
|
-
|
|
126
|
-
.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
if (visualizationType === 'Forest Plot') {
|
|
137
|
+
tooltipItems.push([config.xAxis.dataKey, getClosestYValue(y)])
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot') {
|
|
141
|
+
tooltipItems.push(
|
|
142
|
+
...getIncludedTooltipSeries()
|
|
143
|
+
?.filter(Boolean)
|
|
144
|
+
?.flatMap(seriesKey => {
|
|
145
|
+
const formattedValue = seriesKey === config.xAxis.dataKey ? resolvedScaleValues[0]?.[seriesKey] : formatNumber(resolvedScaleValues[0]?.[seriesKey], getAxisPosition(seriesKey))
|
|
146
|
+
return resolvedScaleValues?.[0]?.[seriesKey] ? [[seriesKey, formattedValue, getAxisPosition(seriesKey)]] : []
|
|
147
|
+
})
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return [...tooltipItems, ...additionalTooltipItems]
|
|
130
152
|
}
|
|
131
153
|
|
|
132
154
|
// Returns an array of arrays.
|
|
@@ -180,9 +202,11 @@ export const useTooltip = props => {
|
|
|
180
202
|
* @function getXValueFromCoordinate
|
|
181
203
|
* @returns {String} - the closest x value to the cursor position
|
|
182
204
|
*/
|
|
183
|
-
const getXValueFromCoordinate = x => {
|
|
205
|
+
const getXValueFromCoordinate = (x, isClick = false) => {
|
|
184
206
|
if (visualizationType === 'Pie') return
|
|
185
207
|
if (orientation === 'horizontal') return
|
|
208
|
+
|
|
209
|
+
// Check the type of x equal to point or if the type of xAxis is equal to continuous or date
|
|
186
210
|
if (xScale.type === 'point' || xAxis.type === 'continuous' || xAxis.type === 'date') {
|
|
187
211
|
// Find the closest x value by calculating the minimum distance
|
|
188
212
|
let closestX = null
|
|
@@ -191,11 +215,12 @@ export const useTooltip = props => {
|
|
|
191
215
|
|
|
192
216
|
data.forEach(d => {
|
|
193
217
|
const xPosition = xAxis.type === 'date' ? xScale(parseDate(d[xAxis.dataKey])) : xScale(d[xAxis.dataKey])
|
|
194
|
-
|
|
218
|
+
let bwOffset = config.barHeight
|
|
219
|
+
const distance = Math.abs(Number(xPosition - offset + (isClick ? bwOffset * 2 : 0)))
|
|
195
220
|
|
|
196
|
-
if (distance
|
|
221
|
+
if (distance <= minDistance) {
|
|
197
222
|
minDistance = distance
|
|
198
|
-
closestX = xAxis.type === 'date' ?
|
|
223
|
+
closestX = xAxis.type === 'date' ? d[xAxis.dataKey] : d[xAxis.dataKey]
|
|
199
224
|
}
|
|
200
225
|
})
|
|
201
226
|
return closestX
|
|
@@ -226,7 +251,6 @@ export const useTooltip = props => {
|
|
|
226
251
|
const yPositionOnPlot = visualizationType !== 'Forest Plot' ? yScale(d[config.xAxis.dataKey]) : yScale(index)
|
|
227
252
|
|
|
228
253
|
const distance = Math.abs(yPositionOnPlot - yPosition)
|
|
229
|
-
|
|
230
254
|
if (distance < minDistance) {
|
|
231
255
|
minDistance = distance
|
|
232
256
|
closestYValue = key ? d[key] : d[config.xAxis.dataKey]
|
|
@@ -251,11 +275,20 @@ export const useTooltip = props => {
|
|
|
251
275
|
const eventSvgCoords = localPoint(e)
|
|
252
276
|
const { x } = eventSvgCoords
|
|
253
277
|
if (!x) throw new Error('COVE: no x value in handleTooltipClick.')
|
|
254
|
-
let closestXScaleValue = getXValueFromCoordinate(x)
|
|
278
|
+
let closestXScaleValue = getXValueFromCoordinate(x, true)
|
|
279
|
+
let datum = config.data?.filter(item => item[config.xAxis.dataKey] === closestXScaleValue)
|
|
255
280
|
if (!closestXScaleValue) throw new Error('COVE: no closest x scale value in handleTooltipClick')
|
|
256
|
-
|
|
281
|
+
if (xAxis.type === 'date' && closestXScaleValue) {
|
|
282
|
+
closestXScaleValue = new Date(closestXScaleValue)
|
|
283
|
+
closestXScaleValue = formatDate(closestXScaleValue)
|
|
284
|
+
datum = config.data?.filter(item => formatDate(new Date(item[config.xAxis.dataKey])) === closestXScaleValue)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!datum[0]) {
|
|
288
|
+
throw new Error(`COVE: no data found matching the closest xScale value: ${closestXScaleValue}`)
|
|
289
|
+
}
|
|
257
290
|
|
|
258
|
-
if (setSharedFilter) {
|
|
291
|
+
if (setSharedFilter && config?.uid && datum?.[0]) {
|
|
259
292
|
setSharedFilter(config.uid, datum[0])
|
|
260
293
|
}
|
|
261
294
|
} catch (e) {
|
|
@@ -272,16 +305,13 @@ export const useTooltip = props => {
|
|
|
272
305
|
*/
|
|
273
306
|
const getYScaleValues = (closestXScaleValue, includedSeries) => {
|
|
274
307
|
try {
|
|
275
|
-
const formattedDate = formatDate(closestXScaleValue)
|
|
276
|
-
|
|
277
308
|
let dataToSearch
|
|
278
309
|
|
|
279
310
|
if (xAxis.type === 'categorical') {
|
|
280
311
|
dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
281
312
|
} else {
|
|
282
|
-
dataToSearch = data.filter(d =>
|
|
313
|
+
dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
283
314
|
}
|
|
284
|
-
|
|
285
315
|
// Return an empty array if no matching data is found.
|
|
286
316
|
if (!dataToSearch || dataToSearch.length === 0) {
|
|
287
317
|
return []
|
|
@@ -328,7 +358,7 @@ export const useTooltip = props => {
|
|
|
328
358
|
if (!config.dashboard) {
|
|
329
359
|
switch (visualizationType) {
|
|
330
360
|
case 'Combo':
|
|
331
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys, ...
|
|
361
|
+
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys, ...ciItems]
|
|
332
362
|
break
|
|
333
363
|
case 'Forecasting':
|
|
334
364
|
standardLoopItems = [runtime.xAxis.dataKey, ...stageColumns, ...ciItems]
|
|
@@ -346,7 +376,6 @@ export const useTooltip = props => {
|
|
|
346
376
|
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys]
|
|
347
377
|
default:
|
|
348
378
|
throw new Error('No visualization type found in handleTooltipMouseOver')
|
|
349
|
-
break
|
|
350
379
|
}
|
|
351
380
|
}
|
|
352
381
|
|
|
@@ -403,7 +432,7 @@ export const useTooltip = props => {
|
|
|
403
432
|
if (key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ` : '')} ${config.xAxis.type === 'date' ? value : value}`}</li>
|
|
404
433
|
|
|
405
434
|
// TOOLTIP BODY
|
|
406
|
-
return <li className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${
|
|
435
|
+
return <li className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${value}`}</li>
|
|
407
436
|
}
|
|
408
437
|
|
|
409
438
|
return {
|
package/src/scss/main.scss
CHANGED
|
@@ -1,8 +1,73 @@
|
|
|
1
1
|
@import '@cdc/core/styles/base';
|
|
2
2
|
@import '@cdc/core/styles/heading-colors';
|
|
3
|
-
@import 'mixins';
|
|
4
|
-
@import 'variables';
|
|
5
3
|
@import '@cdc/core/styles/v2/themes/color-definitions';
|
|
4
|
+
.dash-container {
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
flex-direction: row;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.legend-dash-left {
|
|
11
|
+
margin-left: 8px !important;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.dash-inner {
|
|
17
|
+
width: 20px;
|
|
18
|
+
margin-left: 0px !important;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dashes {
|
|
25
|
+
display: inline-block;
|
|
26
|
+
|
|
27
|
+
&.open-circles {
|
|
28
|
+
width: 12px;
|
|
29
|
+
height: 12px;
|
|
30
|
+
border: 2px solid currentColor;
|
|
31
|
+
border-radius: 50%;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&.dashed-small {
|
|
35
|
+
margin: 0 0px;
|
|
36
|
+
font-size: 20px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&.dashed-medium,
|
|
40
|
+
&.dashed-large {
|
|
41
|
+
span {
|
|
42
|
+
display: inline-block;
|
|
43
|
+
position: relative;
|
|
44
|
+
margin-right: 12px;
|
|
45
|
+
margin-left: 0 !important;
|
|
46
|
+
|
|
47
|
+
&::before {
|
|
48
|
+
content: '';
|
|
49
|
+
display: block;
|
|
50
|
+
height: 2px;
|
|
51
|
+
background-color: currentColor;
|
|
52
|
+
position: absolute;
|
|
53
|
+
top: 50%;
|
|
54
|
+
transform: translateY(-20%);
|
|
55
|
+
width: 10px;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&.dashed-large {
|
|
61
|
+
span {
|
|
62
|
+
margin-right: 12px;
|
|
63
|
+
margin-left: 0 !important;
|
|
64
|
+
|
|
65
|
+
&::before {
|
|
66
|
+
width: 13px;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
6
71
|
|
|
7
72
|
.form-container {
|
|
8
73
|
overflow-y: auto;
|
|
@@ -17,7 +82,6 @@
|
|
|
17
82
|
|
|
18
83
|
.cdc-open-viz-module.type-chart {
|
|
19
84
|
@import 'DataTable';
|
|
20
|
-
@import 'editor-panel';
|
|
21
85
|
|
|
22
86
|
border-radius: 3px;
|
|
23
87
|
|
|
@@ -74,34 +138,6 @@
|
|
|
74
138
|
}
|
|
75
139
|
}
|
|
76
140
|
|
|
77
|
-
.chart-title {
|
|
78
|
-
position: relative;
|
|
79
|
-
padding: 0.6em 0.8em;
|
|
80
|
-
margin: 0;
|
|
81
|
-
color: #fff;
|
|
82
|
-
font-size: 1.1em;
|
|
83
|
-
border-bottom-width: 3px;
|
|
84
|
-
border-bottom-style: solid;
|
|
85
|
-
|
|
86
|
-
border-top-left-radius: 3px;
|
|
87
|
-
border-top-right-radius: 3px;
|
|
88
|
-
|
|
89
|
-
em {
|
|
90
|
-
font-style: italic;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
strong {
|
|
94
|
-
font-weight: bold;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
&:not(:empty) {
|
|
98
|
-
margin: 0 0 1rem 0 !important;
|
|
99
|
-
padding: 0.6em 0.8em;
|
|
100
|
-
border-bottom-width: 3px;
|
|
101
|
-
border-bottom-style: solid;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
141
|
.chart-description {
|
|
106
142
|
margin-bottom: 20px;
|
|
107
143
|
}
|
|
@@ -560,14 +596,14 @@
|
|
|
560
596
|
|
|
561
597
|
&.animated {
|
|
562
598
|
.vertical rect,
|
|
563
|
-
.vertical foreignObject {
|
|
599
|
+
.vertical foreignObject div {
|
|
564
600
|
opacity: 0;
|
|
565
601
|
animation: growBar 0.5s linear forwards;
|
|
566
602
|
animation-play-state: paused;
|
|
567
603
|
}
|
|
568
604
|
|
|
569
605
|
.horizontal rect,
|
|
570
|
-
.horizontal foreignObject {
|
|
606
|
+
.horizontal foreignObject div {
|
|
571
607
|
opacity: 0;
|
|
572
608
|
animation: growBarH 0.5s linear forwards;
|
|
573
609
|
animation-play-state: paused;
|
|
@@ -575,7 +611,7 @@
|
|
|
575
611
|
|
|
576
612
|
&.animate {
|
|
577
613
|
rect,
|
|
578
|
-
foreignObject {
|
|
614
|
+
foreignObject div {
|
|
579
615
|
animation-play-state: running;
|
|
580
616
|
}
|
|
581
617
|
}
|
|
@@ -674,10 +710,6 @@
|
|
|
674
710
|
background: transparent;
|
|
675
711
|
}
|
|
676
712
|
|
|
677
|
-
.chart-title:not(:empty) {
|
|
678
|
-
margin-bottom: 0 !important;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
713
|
.cove-component__content .chart-container {
|
|
682
714
|
padding: 1em;
|
|
683
715
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Axis } from '@cdc/core/types/Axis'
|
|
2
|
+
import { type ForestPlotConfigSettings } from './ForestPlot'
|
|
3
|
+
import { type Column } from '@cdc/core/types/Column'
|
|
4
|
+
import { type Series } from '@cdc/core/types/Series'
|
|
5
|
+
import { Runtime } from '@cdc/core/types/Runtime'
|
|
6
|
+
import { FilterBehavior } from '@cdc/core/types/FilterBehavior'
|
|
7
|
+
import { Table } from '@cdc/core/types/Table'
|
|
8
|
+
|
|
9
|
+
export type ChartColumns = Record<string, Column>
|
|
10
|
+
|
|
11
|
+
type Region = {
|
|
12
|
+
from: string
|
|
13
|
+
to: string
|
|
14
|
+
label: string
|
|
15
|
+
color: string
|
|
16
|
+
background: string
|
|
17
|
+
range: 'Custom' | string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type BoxPlot = {
|
|
21
|
+
firstQuartilePercentage: number
|
|
22
|
+
[key: string]: any
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type DataFormat = {
|
|
26
|
+
abbreviated: boolean
|
|
27
|
+
bottomAbbreviated: boolean
|
|
28
|
+
bottomCommas: boolean
|
|
29
|
+
bottomPrefix: string
|
|
30
|
+
bottomRoundTo: number
|
|
31
|
+
bottomSuffix: string
|
|
32
|
+
commas: boolean
|
|
33
|
+
prefix: string
|
|
34
|
+
rightCommas: boolean
|
|
35
|
+
rightPrefix: string
|
|
36
|
+
rightRoundTo: number
|
|
37
|
+
rightSuffix: string
|
|
38
|
+
roundTo: number
|
|
39
|
+
suffix: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type Exclusions = {
|
|
43
|
+
keys: string[]
|
|
44
|
+
active: boolean
|
|
45
|
+
dateStart: string
|
|
46
|
+
dateEnd: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type Filter = {
|
|
50
|
+
type: 'url'
|
|
51
|
+
columnName: string
|
|
52
|
+
showDropdown: boolean
|
|
53
|
+
filterStyle: string
|
|
54
|
+
label: string
|
|
55
|
+
order: 'asc' | 'desc' | 'cust'
|
|
56
|
+
values: string[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type Legend = {
|
|
60
|
+
additionalCategories: string[]
|
|
61
|
+
// general legend onClick behavior
|
|
62
|
+
behavior: 'highlight' | 'isolate' | string
|
|
63
|
+
colorCode: string
|
|
64
|
+
description: string
|
|
65
|
+
// show or hide the legend
|
|
66
|
+
hide: boolean
|
|
67
|
+
highlightOnHover: boolean
|
|
68
|
+
label: string
|
|
69
|
+
lineMode: boolean
|
|
70
|
+
position: string
|
|
71
|
+
reverseLabelOrder: boolean
|
|
72
|
+
singleRow: boolean
|
|
73
|
+
type: string
|
|
74
|
+
verticalSorted: boolean
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type Visual = {
|
|
78
|
+
border?: boolean
|
|
79
|
+
borderColorTheme?: boolean
|
|
80
|
+
accent?: boolean
|
|
81
|
+
background?: boolean
|
|
82
|
+
hideBackgroundColor?: boolean
|
|
83
|
+
verticalHoverLine?: boolean
|
|
84
|
+
horizontalHoverLine?: boolean
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type AllChartsConfig = {
|
|
88
|
+
animate: boolean
|
|
89
|
+
general: {
|
|
90
|
+
boxplot: BoxPlot
|
|
91
|
+
}
|
|
92
|
+
barHasBorder: 'true' | 'false'
|
|
93
|
+
barHeight: number
|
|
94
|
+
barSpace: number
|
|
95
|
+
barStyle: string
|
|
96
|
+
barThickness: number
|
|
97
|
+
boxplot: BoxPlot
|
|
98
|
+
brush: {
|
|
99
|
+
active: boolean
|
|
100
|
+
height: number
|
|
101
|
+
}
|
|
102
|
+
chartMessage: { noData?: string }
|
|
103
|
+
colorMatchLineSeriesLabels: boolean
|
|
104
|
+
columns: ChartColumns
|
|
105
|
+
confidenceKeys: Record<string, any>
|
|
106
|
+
data: Object[]
|
|
107
|
+
dataCutoff: number
|
|
108
|
+
dataFormat: DataFormat
|
|
109
|
+
dataKey: string
|
|
110
|
+
description: string
|
|
111
|
+
dynamicMarginTop: number
|
|
112
|
+
exclusions: Exclusions
|
|
113
|
+
filters: Filter[]
|
|
114
|
+
filterBehavior: FilterBehavior
|
|
115
|
+
fontSize: 'small' | 'medium' | 'large'
|
|
116
|
+
footnotes: string
|
|
117
|
+
forestPlot: ForestPlotConfigSettings
|
|
118
|
+
heights: {
|
|
119
|
+
vertical: number
|
|
120
|
+
}
|
|
121
|
+
highlightedBarValues: { value: any; color: string; borderWidth: number; legendLabel: string }[]
|
|
122
|
+
introText: string
|
|
123
|
+
isLollipopChart: boolean
|
|
124
|
+
isLegendValue: boolean
|
|
125
|
+
isResponsiveTicks: boolean
|
|
126
|
+
isPaletteReversed: boolean
|
|
127
|
+
labels: boolean
|
|
128
|
+
legend: Legend
|
|
129
|
+
lineDatapointColor: 'Same as Line' | 'Lighter than Line'
|
|
130
|
+
lineDatapointStyle: 'hidden' | 'always show' | 'hover'
|
|
131
|
+
lollipopColorStyle: 'regular' | 'two-tone'
|
|
132
|
+
lollipopShape: string
|
|
133
|
+
lollipopSize: 'small' | 'medium' | 'large'
|
|
134
|
+
newViz: Object
|
|
135
|
+
orientation: 'vertical' | 'horizontal'
|
|
136
|
+
palette: string
|
|
137
|
+
pieType?: string
|
|
138
|
+
roundingStyle: string
|
|
139
|
+
runtime: Runtime
|
|
140
|
+
series: Series
|
|
141
|
+
showLineSeriesLabels: boolean
|
|
142
|
+
showTitle: boolean
|
|
143
|
+
stackedAreaChartLineType: string
|
|
144
|
+
suppressedData?: { label: string; icon: string; value: string }[]
|
|
145
|
+
superTitle: string
|
|
146
|
+
theme: string
|
|
147
|
+
table: Table
|
|
148
|
+
tipRounding: string
|
|
149
|
+
title: string
|
|
150
|
+
tooltips: {
|
|
151
|
+
singleSeries: boolean
|
|
152
|
+
opacity: number
|
|
153
|
+
}
|
|
154
|
+
topAxis: { hasLine: boolean }
|
|
155
|
+
twoColor: { palette: string }
|
|
156
|
+
type: string
|
|
157
|
+
useLogScale: boolean
|
|
158
|
+
visual: Visual
|
|
159
|
+
visualizationType: 'Area Chart' | 'Bar' | 'Box Plot' | 'Deviation Bar' | 'Forest Plot' | 'Line' | 'Paired Bar' | 'Pie' | 'Scatter Plot' | 'Spark Line' | 'Combo' | 'Forecasting'
|
|
160
|
+
visualizationSubType: string
|
|
161
|
+
xAxis: Axis
|
|
162
|
+
yAxis: Axis
|
|
163
|
+
xScale: Function
|
|
164
|
+
yScale: Function
|
|
165
|
+
regions: Region[]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export type ForestPlotConfig = {
|
|
169
|
+
visualizationType: 'Forest Plot'
|
|
170
|
+
forestPlot: ForestPlotConfigSettings
|
|
171
|
+
} & AllChartsConfig
|
|
172
|
+
|
|
173
|
+
export type LineChartConfig = {
|
|
174
|
+
visualizationType: 'Line'
|
|
175
|
+
lineDatapointStyle: 'hidden' | 'always show' | 'hover'
|
|
176
|
+
} & AllChartsConfig
|
|
177
|
+
|
|
178
|
+
export type ChartConfig = LineChartConfig | ForestPlotConfig | AllChartsConfig
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ChartConfig } from './ChartConfig'
|
|
2
|
+
import { PickD3Scale } from '@visx/scale'
|
|
3
|
+
|
|
4
|
+
type ColorScale = PickD3Scale<'ordinal', any, any>
|
|
5
|
+
|
|
6
|
+
type TransformedData = {
|
|
7
|
+
dataKey?: string
|
|
8
|
+
[key: string]: any
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type SharedChartContext = {
|
|
12
|
+
colorScale?: ColorScale
|
|
13
|
+
config: ChartConfig
|
|
14
|
+
currentViewport?: string
|
|
15
|
+
highlight?: Function
|
|
16
|
+
highlightReset?: Function
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Line Chart Specific Context
|
|
20
|
+
type LineChartContext = SharedChartContext & {
|
|
21
|
+
dimensions: [screenWidth: number, screenHeight: number]
|
|
22
|
+
formatDate: Function
|
|
23
|
+
formatNumber: Function
|
|
24
|
+
handleLineType: Function
|
|
25
|
+
isNumber: unknown
|
|
26
|
+
isDebug?: boolean
|
|
27
|
+
parseDate: Function
|
|
28
|
+
rawData: Object[]
|
|
29
|
+
seriesHighlight: String[]
|
|
30
|
+
tableData: Object[]
|
|
31
|
+
transformedData: TransformedData[]
|
|
32
|
+
updateConfig: Function
|
|
33
|
+
visualizationType: 'Line'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ChartContext =
|
|
37
|
+
| LineChartContext
|
|
38
|
+
| (SharedChartContext & {
|
|
39
|
+
dimensions: [screenWidth: number, screenHeight: number]
|
|
40
|
+
formatDate?: Function
|
|
41
|
+
formatNumber?: Function
|
|
42
|
+
handleLineType?: Function
|
|
43
|
+
isNumber?: boolean
|
|
44
|
+
// url param added to allow various console logs and chart helpers
|
|
45
|
+
isDebug?: boolean
|
|
46
|
+
parseDate?: Function
|
|
47
|
+
rawData?: Object[]
|
|
48
|
+
seriesHighlight?: String[]
|
|
49
|
+
tableData?: Object[]
|
|
50
|
+
transformedData?: TransformedData[]
|
|
51
|
+
setSharedFilter?: Function
|
|
52
|
+
sharedFilterValue?: string
|
|
53
|
+
updateConfig?: Function
|
|
54
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Color } from '@cdc/core/types/Color'
|
|
2
|
+
|
|
3
|
+
export type ForestPlotConfigSettings = {
|
|
4
|
+
colors: {
|
|
5
|
+
// color of line of no effect
|
|
6
|
+
line: Color
|
|
7
|
+
// color for effect estimate shape
|
|
8
|
+
shape: Color
|
|
9
|
+
}
|
|
10
|
+
// description - appears below the study column.
|
|
11
|
+
description: {
|
|
12
|
+
show: boolean
|
|
13
|
+
text: string
|
|
14
|
+
location: number
|
|
15
|
+
}
|
|
16
|
+
// column chose for the effect estimate
|
|
17
|
+
estimateField: string
|
|
18
|
+
// column chose for the esimate radius
|
|
19
|
+
estimateRadius: string
|
|
20
|
+
hideDateCategoryCol: boolean
|
|
21
|
+
leftWidthOffset: number
|
|
22
|
+
lineOfNoEffect: {
|
|
23
|
+
show: boolean
|
|
24
|
+
}
|
|
25
|
+
lower: string
|
|
26
|
+
upper: string
|
|
27
|
+
// type - determines the type of scale we're using, including the position of line of effect (0 or 1)
|
|
28
|
+
type: 'Linear' | 'Logarithmic'
|
|
29
|
+
pooledResult: {
|
|
30
|
+
diamondHeight: number
|
|
31
|
+
column: string
|
|
32
|
+
}
|
|
33
|
+
// rowHeight - height of study
|
|
34
|
+
rowHeight: number
|
|
35
|
+
// radius - scaling for effect estimates
|
|
36
|
+
radius: {
|
|
37
|
+
min: number
|
|
38
|
+
max: number
|
|
39
|
+
scalingColumn: string
|
|
40
|
+
}
|
|
41
|
+
result: {
|
|
42
|
+
show: boolean
|
|
43
|
+
text: string
|
|
44
|
+
location: number
|
|
45
|
+
}
|
|
46
|
+
rightWidthOffset: number
|
|
47
|
+
shape: string
|
|
48
|
+
startAt: number
|
|
49
|
+
title: string // centered title above the chart
|
|
50
|
+
// labels under chart
|
|
51
|
+
leftLabel: string
|
|
52
|
+
rightLabel: string
|
|
53
|
+
}
|