@cdc/chart 4.23.11 → 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 +30220 -29764
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/tall-data.json +98 -0
- package/examples/feature/forest-plot/forest-plot.json +63 -19
- package/examples/feature/forest-plot/linear.json +52 -3
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +0 -35
- package/examples/feature/line/line-chart-preliminary.json +346 -0
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- 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/index.html +7 -7
- package/package.json +2 -2
- package/src/CdcChart.tsx +29 -210
- package/src/ConfigContext.tsx +6 -0
- package/src/_stories/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -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/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +1 -1
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
- package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
- package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +41 -57
- 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/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +667 -851
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +87 -166
- package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +1 -1
- 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 +29 -2
- 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 +7 -0
- package/src/components/ForestPlot/index.tsx +1 -209
- package/src/components/{Legend.jsx → Legend/Legend.tsx} +150 -113
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +29 -0
- package/src/components/LineChart/{LineChart.Circle.tsx → components/LineChart.Circle.tsx} +12 -3
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.tsx +20 -8
- package/src/components/LinearChart.jsx +52 -69
- 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/data/initial-state.js +5 -6
- 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 +7 -6
- package/src/hooks/useScales.ts +1 -1
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +23 -21
- package/src/scss/main.scss +67 -3
- package/src/types/ChartConfig.ts +158 -23
- package/src/types/ChartContext.ts +26 -10
- package/src/types/ForestPlot.ts +7 -14
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.tsx +0 -91
- package/src/components/BarChart.jsx +0 -30
- package/src/components/ForestPlot/Readme.md +0 -0
- 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
- package/src/types/ChartProps.ts +0 -7
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the first quartile (q1) and third quartile (q3) from an array of integers or decimals.
|
|
3
|
+
*
|
|
4
|
+
* @param {Array} arr - The array of integers or decimals.
|
|
5
|
+
* @returns {Object} An object containing the q1 and q3 values.
|
|
6
|
+
*/
|
|
7
|
+
export const getQuartiles = arr => {
|
|
8
|
+
arr.sort((a, b) => a - b)
|
|
9
|
+
|
|
10
|
+
// Calculate the index of the median value of the array
|
|
11
|
+
const medianIndex = Math.floor(arr.length / 2)
|
|
12
|
+
|
|
13
|
+
// Check if the length of the array is even or odd
|
|
14
|
+
const isEvenLength = arr.length % 2 === 0
|
|
15
|
+
|
|
16
|
+
// Split the array into two subarrays based on the median index
|
|
17
|
+
const q1Array = isEvenLength ? arr.slice(0, medianIndex) : arr.slice(0, medianIndex + 1)
|
|
18
|
+
const q3Array = isEvenLength ? arr.slice(medianIndex) : arr.slice(medianIndex + 1)
|
|
19
|
+
|
|
20
|
+
// Calculate the median of the first subarray to get the q1 value
|
|
21
|
+
const q1Index = Math.floor(q1Array.length / 2)
|
|
22
|
+
const q1 = isEvenLength ? (q1Array[q1Index - 1] + q1Array[q1Index]) / 2 : q1Array[q1Index]
|
|
23
|
+
|
|
24
|
+
// Calculate the median of the second subarray to get the q3 value
|
|
25
|
+
const q3Index = Math.floor(q3Array.length / 2)
|
|
26
|
+
const q3 = isEvenLength ? (q3Array[q3Index - 1] + q3Array[q3Index]) / 2 : q3Array[q3Index]
|
|
27
|
+
|
|
28
|
+
// Return an object containing the q1 and q3 values
|
|
29
|
+
return { q1, q3 }
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const handleChartAriaLabels = (state, testing = false) => {
|
|
2
|
+
if (testing) console.log(`handleChartAriaLabels Testing On:`, state) // eslint-disable-line
|
|
3
|
+
try {
|
|
4
|
+
if (!state.visualizationType) throw Error('handleChartAriaLabels: no visualization type found in state')
|
|
5
|
+
let ariaLabel = ''
|
|
6
|
+
|
|
7
|
+
if (state.visualizationType) {
|
|
8
|
+
ariaLabel += `${state.visualizationType} chart`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (state.title && state.visualizationType) {
|
|
12
|
+
ariaLabel += ` with the title: ${state.title}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return ariaLabel
|
|
16
|
+
} catch (e) {
|
|
17
|
+
console.error('COVE: ', e.message) // eslint-disable-line
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const handleLineType = lineType => {
|
|
2
|
+
switch (lineType) {
|
|
3
|
+
case 'dashed-sm':
|
|
4
|
+
return '5 5'
|
|
5
|
+
case 'Dashed Small':
|
|
6
|
+
return '5 5'
|
|
7
|
+
case 'dashed-md':
|
|
8
|
+
return '10 5'
|
|
9
|
+
case 'Dashed Medium':
|
|
10
|
+
return '10 5'
|
|
11
|
+
case 'dashed-lg':
|
|
12
|
+
return '15 5'
|
|
13
|
+
case 'Dashed Large':
|
|
14
|
+
return '15 5'
|
|
15
|
+
default:
|
|
16
|
+
return 0
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const lineOptions = [
|
|
2
|
+
{
|
|
3
|
+
value: 'Dashed Small',
|
|
4
|
+
key: 'dashed-sm'
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
value: 'Dashed Medium',
|
|
8
|
+
key: 'dashed-md'
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
value: 'Dashed Large',
|
|
12
|
+
key: 'dashed-lg'
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
value: 'Solid Line',
|
|
16
|
+
key: 'solid-line'
|
|
17
|
+
}
|
|
18
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ChartConfig, Legend } from '../../types/ChartConfig'
|
|
2
|
+
import { computeMarginBottom } from '../computeMarginBottom'
|
|
3
|
+
|
|
4
|
+
describe('computeMarginBottom', () => {
|
|
5
|
+
it('should return correct value', () => {
|
|
6
|
+
const config = {
|
|
7
|
+
orientation: 'horizontal',
|
|
8
|
+
xAxis: { labelOffset: '10' },
|
|
9
|
+
yAxis: { label: null },
|
|
10
|
+
brush: { active: false },
|
|
11
|
+
isResponsiveTicks: true,
|
|
12
|
+
dynamicMarginTop: 20
|
|
13
|
+
}
|
|
14
|
+
const legend = { position: 'top' }
|
|
15
|
+
const currentViewport = 'md'
|
|
16
|
+
expect(computeMarginBottom(config as unknown as ChartConfig, legend as Legend, currentViewport)).toBe('0px')
|
|
17
|
+
config.yAxis.label = 'label'
|
|
18
|
+
expect(computeMarginBottom(config as unknown as ChartConfig, legend as Legend, currentViewport)).toBe('40px')
|
|
19
|
+
})
|
|
20
|
+
})
|
package/src/hooks/useBarChart.js
CHANGED
|
@@ -184,7 +184,7 @@ export const useBarChart = () => {
|
|
|
184
184
|
return 0
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
const getAdditionalColumn = xAxisDataValue => {
|
|
187
|
+
const getAdditionalColumn = (series, xAxisDataValue) => {
|
|
188
188
|
if (!xAxisDataValue) return ''
|
|
189
189
|
const columns = config.columns
|
|
190
190
|
const columnsWithTooltips = []
|
|
@@ -193,7 +193,8 @@ export const useBarChart = () => {
|
|
|
193
193
|
tableData.find(d => {
|
|
194
194
|
return d[config.xAxis.dataKey] === xAxisDataValue
|
|
195
195
|
}) || {}
|
|
196
|
-
|
|
196
|
+
Object.keys(columns).forEach(colKeys => {
|
|
197
|
+
if(series && config.columns[colKeys].series && config.columns[colKeys].series !== series) return
|
|
197
198
|
const formattingParams = {
|
|
198
199
|
addColPrefix: config.columns[colKeys].prefix,
|
|
199
200
|
addColSuffix: config.columns[colKeys].suffix,
|
|
@@ -201,11 +202,11 @@ export const useBarChart = () => {
|
|
|
201
202
|
addColCommas: config.columns[colKeys].commas
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
const formattedValue = formatColNumber(closestVal[
|
|
205
|
-
if (
|
|
206
|
-
columnsWithTooltips.push([
|
|
205
|
+
const formattedValue = formatColNumber(closestVal[config.columns[colKeys].name], 'left', true, config, formattingParams)
|
|
206
|
+
if (config.columns[colKeys].tooltips) {
|
|
207
|
+
columnsWithTooltips.push([config.columns[colKeys].label, formattedValue])
|
|
207
208
|
}
|
|
208
|
-
}
|
|
209
|
+
})
|
|
209
210
|
columnsWithTooltips.forEach(columnData => {
|
|
210
211
|
additionalTooltipItems += `${columnData[0]} : ${columnData[1]} <br/>`
|
|
211
212
|
})
|
package/src/hooks/useScales.ts
CHANGED
|
@@ -190,7 +190,7 @@ const useScales = (properties: useScaleProps) => {
|
|
|
190
190
|
if (config.forestPlot.type === 'Linear') {
|
|
191
191
|
xScale = scaleLinear({
|
|
192
192
|
domain: [Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding],
|
|
193
|
-
range: [leftWidthOffset,
|
|
193
|
+
range: [leftWidthOffset, dimensions[0] - rightWidthOffset]
|
|
194
194
|
})
|
|
195
195
|
xScale.type = scaleTypes.LINEAR
|
|
196
196
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
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'
|
|
@@ -11,9 +11,9 @@ const transform = new DataTransform()
|
|
|
11
11
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
12
12
|
|
|
13
13
|
export const useTooltip = props => {
|
|
14
|
-
const { tableData, config, formatNumber, capitalize, formatDate, parseDate, setSharedFilter } = useContext(ConfigContext)
|
|
14
|
+
const { tableData, config, formatNumber, capitalize, formatDate, parseDate, setSharedFilter } = useContext<ChartContext>(ConfigContext)
|
|
15
15
|
const { xScale, yScale, showTooltip, hideTooltip } = props
|
|
16
|
-
const { xAxis, visualizationType, orientation, yAxis, runtime } = config
|
|
16
|
+
const { xAxis, visualizationType, orientation, yAxis, runtime, barWidth } = config
|
|
17
17
|
const data = transform.applySuppression(tableData, config.suppressedData)
|
|
18
18
|
/**
|
|
19
19
|
* Provides the tooltip information based on the tooltip data array and svg cursor coordinates
|
|
@@ -82,13 +82,10 @@ export const useTooltip = props => {
|
|
|
82
82
|
includedSeries.push(...getColumnNames(config.columns))
|
|
83
83
|
|
|
84
84
|
const yScaleValues = getYScaleValues(closestXScaleValue, includedSeries)
|
|
85
|
-
|
|
86
85
|
const xScaleValues = data.filter(d => d[xAxis.dataKey] === getClosestYValue(y))
|
|
87
86
|
|
|
88
87
|
const resolvedScaleValues = orientation === 'vertical' ? yScaleValues : xScaleValues
|
|
89
88
|
|
|
90
|
-
// const forestPlotXValue = visualizationType === 'Forest Plot' ? data?.filter(d => d[xAxis.dataKey] === getClosestYValue(y))?.[0]?.[config.forestPlot.estimateField] : null
|
|
91
|
-
|
|
92
89
|
const getAxisPosition = seriesKey => {
|
|
93
90
|
const seriesObj = config.series.filter(s => s.dataKey === seriesKey)[0]
|
|
94
91
|
const position = seriesObj?.axis ? String(seriesObj.axis).toLowerCase() : 'left'
|
|
@@ -112,7 +109,7 @@ export const useTooltip = props => {
|
|
|
112
109
|
if (config.visualizationType === 'Pie') {
|
|
113
110
|
closestValue = arc?.data[colVals.name]
|
|
114
111
|
} else {
|
|
115
|
-
closestValue = resolvedScaleValues[0][colVals.name]
|
|
112
|
+
closestValue = resolvedScaleValues[0]?.[colVals.name]
|
|
116
113
|
}
|
|
117
114
|
|
|
118
115
|
const formattedValue = formatColNumber(closestValue, 'left', true, config, formattingParams)
|
|
@@ -135,6 +132,7 @@ export const useTooltip = props => {
|
|
|
135
132
|
['Percent', `${Math.round((((arc?.endAngle - arc?.startAngle) * 180) / Math.PI / 360) * 100) + '%'}`]
|
|
136
133
|
)
|
|
137
134
|
}
|
|
135
|
+
|
|
138
136
|
if (visualizationType === 'Forest Plot') {
|
|
139
137
|
tooltipItems.push([config.xAxis.dataKey, getClosestYValue(y)])
|
|
140
138
|
}
|
|
@@ -143,9 +141,9 @@ export const useTooltip = props => {
|
|
|
143
141
|
tooltipItems.push(
|
|
144
142
|
...getIncludedTooltipSeries()
|
|
145
143
|
?.filter(Boolean)
|
|
146
|
-
|
|
147
|
-
const formattedValue = seriesKey === config.xAxis.dataKey ? resolvedScaleValues[0][seriesKey] : formatNumber(resolvedScaleValues[0][seriesKey], getAxisPosition(seriesKey))
|
|
148
|
-
return resolvedScaleValues[0][seriesKey] ? [[seriesKey, formattedValue]] : []
|
|
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)]] : []
|
|
149
147
|
})
|
|
150
148
|
)
|
|
151
149
|
}
|
|
@@ -204,9 +202,11 @@ export const useTooltip = props => {
|
|
|
204
202
|
* @function getXValueFromCoordinate
|
|
205
203
|
* @returns {String} - the closest x value to the cursor position
|
|
206
204
|
*/
|
|
207
|
-
const getXValueFromCoordinate = x => {
|
|
205
|
+
const getXValueFromCoordinate = (x, isClick = false) => {
|
|
208
206
|
if (visualizationType === 'Pie') return
|
|
209
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
|
|
210
210
|
if (xScale.type === 'point' || xAxis.type === 'continuous' || xAxis.type === 'date') {
|
|
211
211
|
// Find the closest x value by calculating the minimum distance
|
|
212
212
|
let closestX = null
|
|
@@ -215,11 +215,12 @@ export const useTooltip = props => {
|
|
|
215
215
|
|
|
216
216
|
data.forEach(d => {
|
|
217
217
|
const xPosition = xAxis.type === 'date' ? xScale(parseDate(d[xAxis.dataKey])) : xScale(d[xAxis.dataKey])
|
|
218
|
-
|
|
218
|
+
let bwOffset = config.barHeight
|
|
219
|
+
const distance = Math.abs(Number(xPosition - offset + (isClick ? bwOffset * 2 : 0)))
|
|
219
220
|
|
|
220
|
-
if (distance
|
|
221
|
+
if (distance <= minDistance) {
|
|
221
222
|
minDistance = distance
|
|
222
|
-
closestX = xAxis.type === 'date' ?
|
|
223
|
+
closestX = xAxis.type === 'date' ? d[xAxis.dataKey] : d[xAxis.dataKey]
|
|
223
224
|
}
|
|
224
225
|
})
|
|
225
226
|
return closestX
|
|
@@ -250,7 +251,6 @@ export const useTooltip = props => {
|
|
|
250
251
|
const yPositionOnPlot = visualizationType !== 'Forest Plot' ? yScale(d[config.xAxis.dataKey]) : yScale(index)
|
|
251
252
|
|
|
252
253
|
const distance = Math.abs(yPositionOnPlot - yPosition)
|
|
253
|
-
|
|
254
254
|
if (distance < minDistance) {
|
|
255
255
|
minDistance = distance
|
|
256
256
|
closestYValue = key ? d[key] : d[config.xAxis.dataKey]
|
|
@@ -275,9 +275,14 @@ export const useTooltip = props => {
|
|
|
275
275
|
const eventSvgCoords = localPoint(e)
|
|
276
276
|
const { x } = eventSvgCoords
|
|
277
277
|
if (!x) throw new Error('COVE: no x value in handleTooltipClick.')
|
|
278
|
-
let closestXScaleValue = getXValueFromCoordinate(x)
|
|
279
|
-
if (!closestXScaleValue) throw new Error('COVE: no closest x scale value in handleTooltipClick')
|
|
278
|
+
let closestXScaleValue = getXValueFromCoordinate(x, true)
|
|
280
279
|
let datum = config.data?.filter(item => item[config.xAxis.dataKey] === closestXScaleValue)
|
|
280
|
+
if (!closestXScaleValue) throw new Error('COVE: no closest x scale value in handleTooltipClick')
|
|
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
|
+
}
|
|
281
286
|
|
|
282
287
|
if (!datum[0]) {
|
|
283
288
|
throw new Error(`COVE: no data found matching the closest xScale value: ${closestXScaleValue}`)
|
|
@@ -300,16 +305,13 @@ export const useTooltip = props => {
|
|
|
300
305
|
*/
|
|
301
306
|
const getYScaleValues = (closestXScaleValue, includedSeries) => {
|
|
302
307
|
try {
|
|
303
|
-
const formattedDate = formatDate(closestXScaleValue)
|
|
304
|
-
|
|
305
308
|
let dataToSearch
|
|
306
309
|
|
|
307
310
|
if (xAxis.type === 'categorical') {
|
|
308
311
|
dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
309
312
|
} else {
|
|
310
|
-
dataToSearch = data.filter(d =>
|
|
313
|
+
dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
311
314
|
}
|
|
312
|
-
|
|
313
315
|
// Return an empty array if no matching data is found.
|
|
314
316
|
if (!dataToSearch || dataToSearch.length === 0) {
|
|
315
317
|
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
|
|
package/src/types/ChartConfig.ts
CHANGED
|
@@ -1,43 +1,178 @@
|
|
|
1
|
+
import { Axis } from '@cdc/core/types/Axis'
|
|
1
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'
|
|
2
8
|
|
|
3
|
-
export type ChartColumns =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
forestPlotAlignRight: boolean
|
|
13
|
-
name?: string
|
|
14
|
-
}
|
|
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
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
type
|
|
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
|
|
20
85
|
}
|
|
21
86
|
|
|
22
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
|
|
23
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
|
|
24
117
|
forestPlot: ForestPlotConfigSettings
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
behavior: 'highlight' | unknown
|
|
118
|
+
heights: {
|
|
119
|
+
vertical: number
|
|
28
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
|
|
29
135
|
orientation: 'vertical' | 'horizontal'
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
32
157
|
useLogScale: boolean
|
|
33
|
-
|
|
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
|
|
34
163
|
xScale: Function
|
|
35
164
|
yScale: Function
|
|
165
|
+
regions: Region[]
|
|
36
166
|
}
|
|
37
167
|
|
|
168
|
+
export type ForestPlotConfig = {
|
|
169
|
+
visualizationType: 'Forest Plot'
|
|
170
|
+
forestPlot: ForestPlotConfigSettings
|
|
171
|
+
} & AllChartsConfig
|
|
172
|
+
|
|
38
173
|
export type LineChartConfig = {
|
|
39
174
|
visualizationType: 'Line'
|
|
40
175
|
lineDatapointStyle: 'hidden' | 'always show' | 'hover'
|
|
41
|
-
} &
|
|
176
|
+
} & AllChartsConfig
|
|
42
177
|
|
|
43
|
-
export type ChartConfig = LineChartConfig | AllChartsConfig
|
|
178
|
+
export type ChartConfig = LineChartConfig | ForestPlotConfig | AllChartsConfig
|
|
@@ -1,38 +1,54 @@
|
|
|
1
1
|
import { type ChartConfig } from './ChartConfig'
|
|
2
|
+
import { PickD3Scale } from '@visx/scale'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
6
13
|
config: ChartConfig
|
|
14
|
+
currentViewport?: string
|
|
15
|
+
highlight?: Function
|
|
16
|
+
highlightReset?: Function
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Line Chart Specific Context
|
|
20
|
+
type LineChartContext = SharedChartContext & {
|
|
7
21
|
dimensions: [screenWidth: number, screenHeight: number]
|
|
8
22
|
formatDate: Function
|
|
9
23
|
formatNumber: Function
|
|
10
24
|
handleLineType: Function
|
|
11
25
|
isNumber: unknown
|
|
26
|
+
isDebug?: boolean
|
|
12
27
|
parseDate: Function
|
|
13
28
|
rawData: Object[]
|
|
14
29
|
seriesHighlight: String[]
|
|
15
30
|
tableData: Object[]
|
|
16
|
-
transformedData:
|
|
31
|
+
transformedData: TransformedData[]
|
|
17
32
|
updateConfig: Function
|
|
18
33
|
visualizationType: 'Line'
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
export type ChartContext =
|
|
22
37
|
| LineChartContext
|
|
23
|
-
| {
|
|
24
|
-
colorScale?: Function
|
|
25
|
-
config?: ChartConfig
|
|
38
|
+
| (SharedChartContext & {
|
|
26
39
|
dimensions: [screenWidth: number, screenHeight: number]
|
|
27
|
-
|
|
28
40
|
formatDate?: Function
|
|
29
41
|
formatNumber?: Function
|
|
30
42
|
handleLineType?: Function
|
|
31
43
|
isNumber?: boolean
|
|
44
|
+
// url param added to allow various console logs and chart helpers
|
|
45
|
+
isDebug?: boolean
|
|
32
46
|
parseDate?: Function
|
|
33
47
|
rawData?: Object[]
|
|
34
48
|
seriesHighlight?: String[]
|
|
35
49
|
tableData?: Object[]
|
|
36
|
-
transformedData?:
|
|
50
|
+
transformedData?: TransformedData[]
|
|
51
|
+
setSharedFilter?: Function
|
|
52
|
+
sharedFilterValue?: string
|
|
37
53
|
updateConfig?: Function
|
|
38
|
-
}
|
|
54
|
+
})
|