@cdc/chart 4.26.3 → 4.26.5
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/CONFIG.md +489 -0
- package/README.md +42 -17
- package/dist/cdcchart.js +39024 -36587
- package/examples/feature/bar/additional-column-tooltip.json +3 -3
- package/examples/feature/deviation/planet-deviation-config.json +2 -2
- package/examples/feature/heatmap/average-age-categorical.json +313 -0
- package/examples/feature/heatmap/calendar-heatmap.json +134 -0
- package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
- package/examples/feature/heatmap/sparse-aggregation.json +158 -0
- package/examples/feature/paired-bar/paired-bar-example.json +2 -2
- package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
- package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
- package/examples/feature/tests-covid/covid-example-config.json +2 -2
- package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
- package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
- package/examples/metadata-variables.json +1 -1
- package/examples/minimal-example.json +24 -0
- package/examples/private/bump-chart.json +1116 -0
- package/examples/private/test-123.json +624 -0
- package/examples/private/test2.json +35012 -0
- package/package.json +5 -4
- package/src/CdcChartComponent.tsx +144 -60
- package/src/_stories/Chart.AltText.stories.tsx +136 -0
- package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
- package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
- package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
- package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
- package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +108 -0
- package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +12 -0
- package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
- package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +13 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +151 -85
- package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
- package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +13 -4
- package/src/_stories/ChartEditor.Editor.stories.tsx +23 -0
- package/src/_stories/ChartLine.DashedDates.smoke.stories.tsx +23 -0
- package/src/_stories/_mock/alt_text_metadata.json +38 -0
- package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
- package/src/_stories/_mock/heatmap.json +156 -0
- package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
- package/src/_stories/_mock/paired-bar-abbr.json +1 -1
- package/src/_stories/_mock/paired-bar.json +1 -1
- package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
- package/src/_stories/_mock/sankey_mock.json +78 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
- package/src/components/Annotations/components/AnnotationDraggable.tsx +2 -2
- package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
- package/src/components/Axis/BottomAxis.tsx +1 -1
- package/src/components/Axis/LeftAxis.tsx +15 -14
- package/src/components/Axis/LeftAxisGridlines.tsx +14 -13
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +9 -5
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +9 -5
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +9 -5
- package/src/components/BarChart/components/BarChart.Vertical.tsx +9 -6
- package/src/components/BarChart/components/BarChart.tsx +2 -6
- package/src/components/BarChart/helpers/useBarChart.ts +1 -1
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
- package/src/components/Brush/BrushSelector.tsx +88 -187
- package/src/components/Brush/brushInteraction.ts +22 -0
- package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
- package/src/components/EditorPanel/EditorPanel.tsx +946 -960
- package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +229 -165
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -116
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +159 -109
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +14 -28
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -1
- package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +57 -0
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +56 -0
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +70 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +32 -21
- package/src/components/Forecasting/Forecasting.tsx +2 -2
- package/src/components/HeatMap/components/HeatMap.tsx +762 -0
- package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +114 -0
- package/src/components/HeatMap/heatmap.constants.ts +13 -0
- package/src/components/HeatMap/heatmap.css +71 -0
- package/src/components/HeatMap/helpers/index.ts +261 -0
- package/src/components/HeatMap/index.tsx +5 -0
- package/src/components/HeatMap/tests/HeatMap.test.tsx +1040 -0
- package/src/components/HeatMap/tests/helpers.test.ts +167 -0
- package/src/components/HorizonChart/HorizonChart.tsx +10 -2
- package/src/components/HoverLine/HoverLine.tsx +6 -8
- package/src/components/Legend/Legend.Component.tsx +4 -4
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +5 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
- package/src/components/Legend/helpers/index.ts +0 -2
- package/src/components/LineChart/LineChartProps.ts +1 -0
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +22 -24
- package/src/components/LineChart/helpers.test.ts +345 -0
- package/src/components/LineChart/helpers.ts +15 -5
- package/src/components/LineChart/index.tsx +18 -5
- package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
- package/src/components/LinearChart/tests/LinearChart.test.tsx +228 -7
- package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
- package/src/components/LinearChart.tsx +155 -77
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/PieChart/PieChart.tsx +15 -10
- package/src/components/RadarChart/RadarChart.tsx +3 -1
- package/src/components/Regions/components/Regions.tsx +199 -35
- package/src/components/Sankey/components/Sankey.tsx +7 -3
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
- package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +31 -3
- package/src/components/SmallMultiples/SmallMultiples.css +12 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +15 -9
- package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
- package/src/components/Sparkline/components/SparkLine.tsx +5 -10
- package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
- package/src/data/initial-state.js +12 -0
- package/src/helpers/axisLabelFontSize.ts +8 -0
- package/src/helpers/bumpChartTooltip.ts +54 -0
- package/src/helpers/calculateLeftYAxisWidth.ts +197 -0
- package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
- package/src/helpers/getMinMax.ts +2 -3
- package/src/helpers/getYAxisAutoPadding.ts +8 -6
- package/src/helpers/getYAxisAutoPaddingMode.ts +17 -0
- package/src/helpers/handleChartAriaLabels.ts +23 -10
- package/src/helpers/seriesHighlight.ts +26 -0
- package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
- package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
- package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -0
- package/src/helpers/tests/getYAxisAutoPadding.test.ts +37 -0
- package/src/helpers/tests/getYAxisAutoPaddingMode.test.ts +90 -0
- package/src/helpers/tests/handleChartAriaLabels.test.ts +71 -0
- package/src/helpers/tests/seriesHighlight.test.ts +61 -0
- package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
- package/src/helpers/tooltipHelpers.ts +176 -0
- package/src/hooks/tests/useScales.test.tsx +105 -0
- package/src/hooks/useHighlightedBars.ts +9 -16
- package/src/hooks/useProgrammaticTooltip.ts +16 -2
- package/src/hooks/useScales.ts +19 -9
- package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
- package/src/hooks/useTooltip.tsx +167 -38
- package/src/index.jsx +5 -2
- package/src/scss/main.scss +32 -49
- package/src/test/CdcChart.test.jsx +24 -0
- package/src/types/ChartConfig.ts +19 -0
- package/src/types/ChartContext.ts +2 -0
- package/examples/chart-regression-2.json +0 -2360
- package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
- package/examples/feature/__data__/city-temperature.json +0 -2198
- package/examples/feature/bar/Barchart_with_negative.json +0 -34
- package/examples/feature/boxplot/box-plot-data.json +0 -71
- package/examples/feature/boxplot/boxplot-data.json +0 -156
- package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
- package/examples/feature/forest-plot/log.json +0 -26
- package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
- package/examples/region-issue.json +0 -2065
- package/examples/xaxis.json +0 -493
- package/src/_stories/Chart.SmallMultiples.stories.tsx +0 -63
- /package/examples/{data → __data__}/data-with-metadata.json +0 -0
- /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
- /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
- /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
- /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
- /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
- /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
- /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
- /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
- /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
- /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.CustomColors.stories.tsx → Chart.CustomColors.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Defaults.stories.tsx → Chart.Defaults.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.SmallestLeftAxisMax.stories.tsx → Chart.SmallestLeftAxisMax.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
- /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
package/src/hooks/useTooltip.tsx
CHANGED
|
@@ -8,13 +8,21 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
|
8
8
|
// Third-party library imports
|
|
9
9
|
import { localPoint } from '@visx/event'
|
|
10
10
|
import { bisector } from 'd3-array'
|
|
11
|
-
import _
|
|
11
|
+
import _ from 'lodash'
|
|
12
12
|
import { getHorizontalBarHeights } from '../components/BarChart/helpers/getBarHeights'
|
|
13
13
|
import {
|
|
14
14
|
findColumnConfigByName,
|
|
15
15
|
getSeriesColumnFormattingParams,
|
|
16
16
|
getSeriesOwnedColumnNames
|
|
17
17
|
} from '../helpers/seriesColumnSettings'
|
|
18
|
+
import {
|
|
19
|
+
buildTooltipRow,
|
|
20
|
+
getTooltipSeriesMarker,
|
|
21
|
+
tooltipHasMarkerColumn,
|
|
22
|
+
type TooltipRow
|
|
23
|
+
} from '../helpers/tooltipHelpers'
|
|
24
|
+
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
25
|
+
import { getVizSubType, getVizTitle } from '@cdc/core/helpers/metrics/utils'
|
|
18
26
|
|
|
19
27
|
export const useTooltip = props => {
|
|
20
28
|
// Track the last X-axis value to prevent duplicate analytics events
|
|
@@ -22,6 +30,7 @@ export const useTooltip = props => {
|
|
|
22
30
|
const {
|
|
23
31
|
tableData: data,
|
|
24
32
|
config,
|
|
33
|
+
colorScale,
|
|
25
34
|
formatNumber,
|
|
26
35
|
capitalize,
|
|
27
36
|
formatDate,
|
|
@@ -33,7 +42,6 @@ export const useTooltip = props => {
|
|
|
33
42
|
const { xScale, yScale, seriesScale, showTooltip, hideTooltip, interactionLabel = '' } = props
|
|
34
43
|
const { xAxis, visualizationType, orientation, yAxis, runtime } = config
|
|
35
44
|
const seriesOwnedColumnNames = getSeriesOwnedColumnNames(config.series)
|
|
36
|
-
|
|
37
45
|
// Track the latest xScale in a ref to prevent stale closures
|
|
38
46
|
const xScaleRef = useRef(xScale)
|
|
39
47
|
|
|
@@ -42,7 +50,7 @@ export const useTooltip = props => {
|
|
|
42
50
|
xScaleRef.current = xScale
|
|
43
51
|
}, [xScale])
|
|
44
52
|
|
|
45
|
-
const Y_AXIS_SIZE = Number(
|
|
53
|
+
const Y_AXIS_SIZE = Number(props.yAxisWidth || 0)
|
|
46
54
|
|
|
47
55
|
// function handles only Single series hovered data tooltips
|
|
48
56
|
const findDataKeyByThreshold = (mouseY, datapoint) => {
|
|
@@ -85,11 +93,38 @@ export const useTooltip = props => {
|
|
|
85
93
|
const formattedValue =
|
|
86
94
|
seriesKey === config.xAxis.dataKey
|
|
87
95
|
? value
|
|
88
|
-
: formatColNumber(value, getAxisPosition(seriesKey),
|
|
96
|
+
: formatColNumber(value, getAxisPosition(seriesKey), false, config, formattingParams)
|
|
89
97
|
|
|
90
98
|
return showMissingDataValue ? 'N/A' : formattedValue
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
const createTooltipRow = ({
|
|
102
|
+
key,
|
|
103
|
+
value,
|
|
104
|
+
kind,
|
|
105
|
+
axisPosition,
|
|
106
|
+
seriesKey
|
|
107
|
+
}: {
|
|
108
|
+
key: string
|
|
109
|
+
value: string | number
|
|
110
|
+
kind: TooltipRow['kind']
|
|
111
|
+
axisPosition?: string
|
|
112
|
+
seriesKey?: string
|
|
113
|
+
}): TooltipRow =>
|
|
114
|
+
(() => {
|
|
115
|
+
const marker = kind === 'series' ? getTooltipSeriesMarker(config, colorScale, seriesKey || key) : null
|
|
116
|
+
|
|
117
|
+
return buildTooltipRow({
|
|
118
|
+
key,
|
|
119
|
+
value,
|
|
120
|
+
axisPosition,
|
|
121
|
+
kind,
|
|
122
|
+
seriesKey,
|
|
123
|
+
markerColor: marker?.markerColor ?? null,
|
|
124
|
+
markerShape: marker?.markerShape
|
|
125
|
+
})
|
|
126
|
+
})()
|
|
127
|
+
|
|
93
128
|
/**
|
|
94
129
|
* Handles the mouse over event for the tooltip.
|
|
95
130
|
* @function handleTooltipMouseOver
|
|
@@ -108,7 +143,7 @@ export const useTooltip = props => {
|
|
|
108
143
|
const singleSeriesValue = getYValueFromCoordinate(y, resolvedScaleValues)
|
|
109
144
|
|
|
110
145
|
const columnsWithTooltips = []
|
|
111
|
-
const tooltipItems
|
|
146
|
+
const tooltipItems: TooltipRow[] = []
|
|
112
147
|
for (const [colKey, column] of Object.entries(config.columns)) {
|
|
113
148
|
const columnName = column.name || colKey
|
|
114
149
|
if (seriesOwnedColumnNames.includes(columnName)) continue
|
|
@@ -129,16 +164,22 @@ export const useTooltip = props => {
|
|
|
129
164
|
: resolvedScaleValues[0]?.[colKey]
|
|
130
165
|
const closestValue = config.visualizationType === 'Pie' ? pieColumnData : columnData
|
|
131
166
|
|
|
132
|
-
const formattedValue = formatColNumber(closestValue, 'left',
|
|
167
|
+
const formattedValue = formatColNumber(closestValue, 'left', false, config, formattingParams)
|
|
133
168
|
|
|
134
169
|
if (column.tooltips) {
|
|
135
170
|
columnsWithTooltips.push([column.label, formattedValue])
|
|
136
171
|
}
|
|
137
172
|
}
|
|
138
|
-
const additionalTooltipItems
|
|
173
|
+
const additionalTooltipItems: TooltipRow[] = []
|
|
139
174
|
|
|
140
175
|
columnsWithTooltips.forEach(columnData => {
|
|
141
|
-
additionalTooltipItems.push(
|
|
176
|
+
additionalTooltipItems.push(
|
|
177
|
+
createTooltipRow({
|
|
178
|
+
key: columnData[0],
|
|
179
|
+
value: columnData[1],
|
|
180
|
+
kind: 'extra'
|
|
181
|
+
})
|
|
182
|
+
)
|
|
142
183
|
})
|
|
143
184
|
|
|
144
185
|
if (visualizationType === 'Pie') {
|
|
@@ -155,21 +196,46 @@ export const useTooltip = props => {
|
|
|
155
196
|
const showPiePercent = config.dataFormat.showPiePercent || false
|
|
156
197
|
|
|
157
198
|
if (showPiePercent && pieData[config.xAxis.dataKey] === 'Calculated Area') {
|
|
158
|
-
tooltipItems.push(
|
|
199
|
+
tooltipItems.push(
|
|
200
|
+
createTooltipRow({
|
|
201
|
+
key: '',
|
|
202
|
+
value: 'Calculated Area',
|
|
203
|
+
kind: 'heading'
|
|
204
|
+
})
|
|
205
|
+
)
|
|
159
206
|
} else {
|
|
160
207
|
tooltipItems.push(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
208
|
+
createTooltipRow({
|
|
209
|
+
key: config.xAxis.dataKey,
|
|
210
|
+
value: pieData[config.xAxis.dataKey],
|
|
211
|
+
kind: 'heading'
|
|
212
|
+
}),
|
|
213
|
+
createTooltipRow({
|
|
214
|
+
key: config.runtime.yAxis.label || config.runtime.yAxis.dataKey,
|
|
215
|
+
value: showPiePercent ? pctString(actualPieValue) : formatNumber(pieData[config.runtime.yAxis.dataKey]),
|
|
216
|
+
kind: 'extra'
|
|
217
|
+
})
|
|
167
218
|
)
|
|
219
|
+
if (!showPiePercent) {
|
|
220
|
+
tooltipItems.push(
|
|
221
|
+
createTooltipRow({
|
|
222
|
+
key: 'Percent',
|
|
223
|
+
value: pctString(pctOf360),
|
|
224
|
+
kind: 'extra'
|
|
225
|
+
})
|
|
226
|
+
)
|
|
227
|
+
}
|
|
168
228
|
}
|
|
169
229
|
}
|
|
170
230
|
|
|
171
231
|
if (visualizationType === 'Forest Plot') {
|
|
172
|
-
tooltipItems.push(
|
|
232
|
+
tooltipItems.push(
|
|
233
|
+
createTooltipRow({
|
|
234
|
+
key: config.xAxis.dataKey,
|
|
235
|
+
value: getClosestYValue(y),
|
|
236
|
+
kind: 'heading'
|
|
237
|
+
})
|
|
238
|
+
)
|
|
173
239
|
}
|
|
174
240
|
|
|
175
241
|
const isLinearChart = !['Pie', 'Forest Plot'].includes(visualizationType)
|
|
@@ -205,9 +271,34 @@ export const useTooltip = props => {
|
|
|
205
271
|
) {
|
|
206
272
|
return []
|
|
207
273
|
} else if (seriesObjWithName && seriesObjWithName.name === '') {
|
|
208
|
-
return [
|
|
274
|
+
return [
|
|
275
|
+
createTooltipRow({
|
|
276
|
+
key: '',
|
|
277
|
+
value: formattedValue,
|
|
278
|
+
axisPosition: getAxisPosition(seriesKey),
|
|
279
|
+
kind: 'series',
|
|
280
|
+
seriesKey
|
|
281
|
+
})
|
|
282
|
+
]
|
|
283
|
+
} else if (config.xAxis?.dataKey == seriesKey) {
|
|
284
|
+
return [
|
|
285
|
+
createTooltipRow({
|
|
286
|
+
key: seriesKey,
|
|
287
|
+
value: formattedValue,
|
|
288
|
+
axisPosition: getAxisPosition(seriesKey),
|
|
289
|
+
kind: 'heading'
|
|
290
|
+
})
|
|
291
|
+
]
|
|
209
292
|
} else {
|
|
210
|
-
return [
|
|
293
|
+
return [
|
|
294
|
+
createTooltipRow({
|
|
295
|
+
key: seriesKey,
|
|
296
|
+
value: formattedValue,
|
|
297
|
+
axisPosition: getAxisPosition(seriesKey),
|
|
298
|
+
kind: 'series',
|
|
299
|
+
seriesKey
|
|
300
|
+
})
|
|
301
|
+
]
|
|
211
302
|
}
|
|
212
303
|
})
|
|
213
304
|
)
|
|
@@ -247,7 +338,15 @@ export const useTooltip = props => {
|
|
|
247
338
|
if (resolvedScaleValue) {
|
|
248
339
|
const value = resolvedScaleValue[series.originalDataKey]
|
|
249
340
|
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
250
|
-
tooltipItems.push(
|
|
341
|
+
tooltipItems.push(
|
|
342
|
+
createTooltipRow({
|
|
343
|
+
key: seriesKey,
|
|
344
|
+
value: formattedValue,
|
|
345
|
+
axisPosition: getAxisPosition(seriesKey),
|
|
346
|
+
kind: 'series',
|
|
347
|
+
seriesKey
|
|
348
|
+
})
|
|
349
|
+
)
|
|
251
350
|
}
|
|
252
351
|
}
|
|
253
352
|
})
|
|
@@ -261,12 +360,31 @@ export const useTooltip = props => {
|
|
|
261
360
|
const xVal = dataColumn[config.xAxis.dataKey]
|
|
262
361
|
const closestXScaleValue = getXValueFromCoordinate(x - Y_AXIS_SIZE)
|
|
263
362
|
|
|
264
|
-
tooltipItems.push(
|
|
363
|
+
tooltipItems.push(
|
|
364
|
+
createTooltipRow({
|
|
365
|
+
key: config.xAxis.dataKey,
|
|
366
|
+
value: closestXScaleValue || xVal,
|
|
367
|
+
kind: 'heading'
|
|
368
|
+
})
|
|
369
|
+
)
|
|
265
370
|
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
266
|
-
tooltipItems.push(
|
|
371
|
+
tooltipItems.push(
|
|
372
|
+
createTooltipRow({
|
|
373
|
+
key: seriesKey,
|
|
374
|
+
value: formattedValue,
|
|
375
|
+
kind: 'series',
|
|
376
|
+
seriesKey
|
|
377
|
+
})
|
|
378
|
+
)
|
|
267
379
|
} else if (dynamicSeries) {
|
|
268
380
|
Object.keys(dataColumn).forEach(key => {
|
|
269
|
-
tooltipItems.push(
|
|
381
|
+
tooltipItems.push(
|
|
382
|
+
createTooltipRow({
|
|
383
|
+
key,
|
|
384
|
+
value: dataColumn[key],
|
|
385
|
+
kind: key === config.xAxis.dataKey ? 'heading' : 'extra'
|
|
386
|
+
})
|
|
387
|
+
)
|
|
270
388
|
})
|
|
271
389
|
}
|
|
272
390
|
}
|
|
@@ -283,16 +401,11 @@ export const useTooltip = props => {
|
|
|
283
401
|
}
|
|
284
402
|
|
|
285
403
|
// Strip link tags from all tooltip values
|
|
286
|
-
const cleanTooltipItems = [...tooltipItems, ...additionalTooltipItems].map(item => {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
newItem[1] = stripLinkTags(newItem[1])
|
|
292
|
-
return newItem
|
|
293
|
-
}
|
|
294
|
-
return item
|
|
295
|
-
})
|
|
404
|
+
const cleanTooltipItems = [...tooltipItems, ...additionalTooltipItems].map(item => ({
|
|
405
|
+
...item,
|
|
406
|
+
value: stripLinkTags(item.value)
|
|
407
|
+
}))
|
|
408
|
+
const useMarkerColumn = tooltipHasMarkerColumn(cleanTooltipItems)
|
|
296
409
|
|
|
297
410
|
const tooltipInformation = {
|
|
298
411
|
tooltipLeft: dataXPosition,
|
|
@@ -300,7 +413,8 @@ export const useTooltip = props => {
|
|
|
300
413
|
tooltipData: {
|
|
301
414
|
data: cleanTooltipItems,
|
|
302
415
|
dataXPosition,
|
|
303
|
-
dataYPosition
|
|
416
|
+
dataYPosition,
|
|
417
|
+
useMarkerColumn
|
|
304
418
|
}
|
|
305
419
|
}
|
|
306
420
|
showTooltip(tooltipInformation)
|
|
@@ -654,9 +768,8 @@ export const useTooltip = props => {
|
|
|
654
768
|
return originalColumnName
|
|
655
769
|
}
|
|
656
770
|
|
|
657
|
-
const TooltipListItem = ({
|
|
658
|
-
const
|
|
659
|
-
const [key, value, axisPosition] = additionalData
|
|
771
|
+
const TooltipListItem = ({ row, index, useMarkerColumn = false }) => {
|
|
772
|
+
const { key, value, axisPosition, kind, markerColor, markerShape = 'circle' } = row
|
|
660
773
|
|
|
661
774
|
if (visualizationType === 'Forest Plot') {
|
|
662
775
|
if (key === config.xAxis.dataKey)
|
|
@@ -718,10 +831,26 @@ export const useTooltip = props => {
|
|
|
718
831
|
}
|
|
719
832
|
const activeLabel = getSeriesNameFromLabel(key)
|
|
720
833
|
const displayText = activeLabel ? `${activeLabel}: ${newValue}` : newValue
|
|
834
|
+
const shouldRenderMarkerSlot = useMarkerColumn && kind !== 'heading'
|
|
835
|
+
const markerSlot = shouldRenderMarkerSlot ? (
|
|
836
|
+
<span className='tooltip-marker-slot' aria-hidden='true'>
|
|
837
|
+
{markerColor ? (
|
|
838
|
+
<span
|
|
839
|
+
className={`tooltip-marker-swatch tooltip-marker-swatch--${markerShape}`}
|
|
840
|
+
style={{ backgroundColor: markerColor }}
|
|
841
|
+
/>
|
|
842
|
+
) : null}
|
|
843
|
+
</span>
|
|
844
|
+
) : null
|
|
845
|
+
const content = displayText !== undefined ? parse(String(displayText)) : displayText
|
|
721
846
|
|
|
722
847
|
return (
|
|
723
|
-
<li
|
|
724
|
-
{
|
|
848
|
+
<li
|
|
849
|
+
style={style}
|
|
850
|
+
className={`tooltip-body mb-1 cove-prose ${shouldRenderMarkerSlot ? 'tooltip-body--marker-layout' : ''}`}
|
|
851
|
+
>
|
|
852
|
+
{markerSlot}
|
|
853
|
+
{shouldRenderMarkerSlot ? <span className='tooltip-body-content'>{content}</span> : content}
|
|
725
854
|
</li>
|
|
726
855
|
)
|
|
727
856
|
}
|
package/src/index.jsx
CHANGED
|
@@ -9,12 +9,15 @@ let isEditor = window.location.href.includes('editor=true')
|
|
|
9
9
|
let isDebug = window.location.href.includes('debug=true')
|
|
10
10
|
|
|
11
11
|
let domContainer = document.getElementsByClassName('react-container')[0]
|
|
12
|
+
let configUrl = domContainer.dataset.configUrl
|
|
13
|
+
let injectedConfig = domContainer.coveConfig
|
|
12
14
|
|
|
13
15
|
ReactDOM.createRoot(domContainer).render(
|
|
14
16
|
<React.StrictMode>
|
|
15
17
|
<CdcChart
|
|
16
|
-
interactionLabel={
|
|
17
|
-
|
|
18
|
+
interactionLabel={configUrl}
|
|
19
|
+
config={injectedConfig}
|
|
20
|
+
configUrl={injectedConfig ? undefined : configUrl}
|
|
18
21
|
isEditor={isEditor}
|
|
19
22
|
isDebug={isDebug}
|
|
20
23
|
/>
|
package/src/scss/main.scss
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@import '@cdc/core/styles/utils/breakpoints';
|
|
2
2
|
@import '@cdc/core/styles/layout/wrapper-padding';
|
|
3
|
+
@import '@cdc/core/styles/layout/callout';
|
|
3
4
|
|
|
4
5
|
.form-container {
|
|
5
6
|
overflow-y: auto;
|
|
@@ -24,33 +25,17 @@
|
|
|
24
25
|
background: transparent;
|
|
25
26
|
|
|
26
27
|
.cdc-callout {
|
|
27
|
-
|
|
28
|
-
border: 1px solid #dff2f6;
|
|
29
|
-
margin: 0;
|
|
30
|
-
padding: 1.25rem;
|
|
31
|
-
border-radius: 0.25rem;
|
|
32
|
-
position: relative;
|
|
33
|
-
background: transparent;
|
|
34
|
-
|
|
35
|
-
.cdc-callout__flag {
|
|
36
|
-
position: absolute;
|
|
37
|
-
top: -0.36rem;
|
|
38
|
-
right: 1.08rem;
|
|
39
|
-
width: 1.84rem;
|
|
40
|
-
height: auto;
|
|
41
|
-
}
|
|
28
|
+
--cdc-callout-background: transparent;
|
|
42
29
|
|
|
43
30
|
.cove-visualization__title,
|
|
44
31
|
.cove-visualization__header {
|
|
45
32
|
background: transparent;
|
|
46
33
|
border: 0;
|
|
47
|
-
color: var(--cool-gray-90, #1f2937);
|
|
48
34
|
margin: 0 0 1rem;
|
|
49
35
|
padding: 0;
|
|
50
36
|
border-radius: 0;
|
|
51
37
|
|
|
52
38
|
h2 {
|
|
53
|
-
color: var(--cool-gray-90, #1f2937);
|
|
54
39
|
font-family: var(--fonts-nunito, var(--app-font-secondary));
|
|
55
40
|
font-size: 1.1rem;
|
|
56
41
|
font-weight: 700;
|
|
@@ -64,11 +49,17 @@
|
|
|
64
49
|
margin-bottom: 0 !important;
|
|
65
50
|
}
|
|
66
51
|
|
|
52
|
+
.y-axis-top-title {
|
|
53
|
+
font-family: var(--fonts-nunito, var(--app-font-secondary));
|
|
54
|
+
font-weight: 700;
|
|
55
|
+
line-height: 1.4;
|
|
56
|
+
margin: 0 0 0.5rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
67
59
|
.legend-top {
|
|
68
60
|
.legend-wrapper .tooltip-boundary {
|
|
69
61
|
display: flex;
|
|
70
62
|
flex-wrap: wrap;
|
|
71
|
-
row-gap: var(--cove-visualization-section-gap, 1.5rem);
|
|
72
63
|
order: 2;
|
|
73
64
|
|
|
74
65
|
aside {
|
|
@@ -188,7 +179,7 @@
|
|
|
188
179
|
white-space: nowrap;
|
|
189
180
|
}
|
|
190
181
|
|
|
191
|
-
.legend-item
|
|
182
|
+
.legend-item > .legend-item {
|
|
192
183
|
display: inline-block;
|
|
193
184
|
flex: 0 0 auto;
|
|
194
185
|
}
|
|
@@ -222,11 +213,11 @@
|
|
|
222
213
|
left: 0%;
|
|
223
214
|
top: 108%;
|
|
224
215
|
|
|
225
|
-
|
|
216
|
+
& > * {
|
|
226
217
|
margin: 0;
|
|
227
218
|
}
|
|
228
219
|
|
|
229
|
-
|
|
220
|
+
& > p,
|
|
230
221
|
a {
|
|
231
222
|
margin-left: 5px;
|
|
232
223
|
}
|
|
@@ -235,7 +226,6 @@
|
|
|
235
226
|
}
|
|
236
227
|
|
|
237
228
|
.dynamic-legend-list {
|
|
238
|
-
|
|
239
229
|
// overide traditional legend item that uses !important
|
|
240
230
|
.legend-item {
|
|
241
231
|
align-items: flex-end !important;
|
|
@@ -268,7 +258,7 @@
|
|
|
268
258
|
font-size: 1em;
|
|
269
259
|
vertical-align: middle;
|
|
270
260
|
|
|
271
|
-
|
|
261
|
+
& > span {
|
|
272
262
|
display: flex;
|
|
273
263
|
justify-items: center;
|
|
274
264
|
align-items: center;
|
|
@@ -278,25 +268,25 @@
|
|
|
278
268
|
max-height: 1px;
|
|
279
269
|
}
|
|
280
270
|
|
|
281
|
-
|
|
271
|
+
& > span[class*='Asterisk'] {
|
|
282
272
|
transform: scale(1.8);
|
|
283
273
|
margin-top: 15px;
|
|
284
274
|
}
|
|
285
275
|
|
|
286
|
-
|
|
276
|
+
& > span[class*='Dagger'] {
|
|
287
277
|
margin-top: 2px;
|
|
288
278
|
}
|
|
289
279
|
|
|
290
|
-
|
|
280
|
+
& > span[class*='Sign'] {
|
|
291
281
|
margin-top: 1px;
|
|
292
282
|
}
|
|
293
283
|
|
|
294
|
-
|
|
284
|
+
& > svg {
|
|
295
285
|
width: 50px;
|
|
296
286
|
height: 23px;
|
|
297
287
|
}
|
|
298
288
|
|
|
299
|
-
|
|
289
|
+
& > p {
|
|
300
290
|
margin: 0;
|
|
301
291
|
}
|
|
302
292
|
}
|
|
@@ -325,8 +315,8 @@
|
|
|
325
315
|
min-width: 0;
|
|
326
316
|
max-width: 100%;
|
|
327
317
|
|
|
328
|
-
>div,
|
|
329
|
-
>aside {
|
|
318
|
+
> div,
|
|
319
|
+
> aside {
|
|
330
320
|
min-width: 0;
|
|
331
321
|
max-width: 100%;
|
|
332
322
|
}
|
|
@@ -339,7 +329,7 @@
|
|
|
339
329
|
|
|
340
330
|
&.legend-top:not(.legend-hidden) {
|
|
341
331
|
.legend-wrapper {
|
|
342
|
-
|
|
332
|
+
& > div {
|
|
343
333
|
order: 2;
|
|
344
334
|
width: 100%;
|
|
345
335
|
}
|
|
@@ -354,7 +344,7 @@
|
|
|
354
344
|
|
|
355
345
|
&.legend-left:not(.legend-hidden) {
|
|
356
346
|
.legend-wrapper {
|
|
357
|
-
|
|
347
|
+
& > div {
|
|
358
348
|
order: 2;
|
|
359
349
|
width: 75%;
|
|
360
350
|
}
|
|
@@ -369,7 +359,7 @@
|
|
|
369
359
|
|
|
370
360
|
&.legend-right:not(.legend-hidden) {
|
|
371
361
|
.legend-wrapper {
|
|
372
|
-
|
|
362
|
+
& > div {
|
|
373
363
|
order: 1;
|
|
374
364
|
width: 75%;
|
|
375
365
|
}
|
|
@@ -391,7 +381,7 @@
|
|
|
391
381
|
flex-wrap: nowrap;
|
|
392
382
|
flex-direction: column-reverse;
|
|
393
383
|
|
|
394
|
-
|
|
384
|
+
& > div.tooltip-boundary {
|
|
395
385
|
margin-top: 1.5em;
|
|
396
386
|
}
|
|
397
387
|
}
|
|
@@ -401,14 +391,10 @@
|
|
|
401
391
|
flex-direction: column;
|
|
402
392
|
}
|
|
403
393
|
|
|
404
|
-
&.legend-hidden>svg {
|
|
394
|
+
&.legend-hidden > svg {
|
|
405
395
|
width: 100% !important;
|
|
406
396
|
}
|
|
407
397
|
|
|
408
|
-
&.dashboard-brush {
|
|
409
|
-
margin-bottom: 2.5em;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
398
|
// Brush touch support
|
|
413
399
|
.brush-overlay {
|
|
414
400
|
touch-action: none;
|
|
@@ -434,7 +420,7 @@
|
|
|
434
420
|
pointer-events: none;
|
|
435
421
|
}
|
|
436
422
|
|
|
437
|
-
>svg {
|
|
423
|
+
> svg {
|
|
438
424
|
overflow: visible;
|
|
439
425
|
font-size: 14px;
|
|
440
426
|
margin: 1rem 0 2rem;
|
|
@@ -449,7 +435,7 @@
|
|
|
449
435
|
}
|
|
450
436
|
|
|
451
437
|
&.chart-line--hover {
|
|
452
|
-
>svg circle {
|
|
438
|
+
> svg circle {
|
|
453
439
|
opacity: 0;
|
|
454
440
|
|
|
455
441
|
&:hover {
|
|
@@ -459,12 +445,12 @@
|
|
|
459
445
|
}
|
|
460
446
|
|
|
461
447
|
&.chart-line--always {
|
|
462
|
-
>svg circle {
|
|
448
|
+
> svg circle {
|
|
463
449
|
opacity: 1;
|
|
464
450
|
}
|
|
465
451
|
|
|
466
452
|
// Animations for line chart and data points
|
|
467
|
-
>svg.animated {
|
|
453
|
+
> svg.animated {
|
|
468
454
|
circle {
|
|
469
455
|
opacity: 0;
|
|
470
456
|
animation: revealLolly 0.25s linear forwards;
|
|
@@ -525,7 +511,7 @@
|
|
|
525
511
|
|
|
526
512
|
@include breakpointClass(md) {
|
|
527
513
|
.visualization-container {
|
|
528
|
-
>svg {
|
|
514
|
+
> svg {
|
|
529
515
|
font-size: 16px;
|
|
530
516
|
width: 75%;
|
|
531
517
|
order: 1;
|
|
@@ -608,7 +594,6 @@
|
|
|
608
594
|
}
|
|
609
595
|
|
|
610
596
|
&.animated {
|
|
611
|
-
|
|
612
597
|
.vertical path,
|
|
613
598
|
.vertical rect,
|
|
614
599
|
.vertical foreignObject div {
|
|
@@ -626,7 +611,6 @@
|
|
|
626
611
|
}
|
|
627
612
|
|
|
628
613
|
&.animate {
|
|
629
|
-
|
|
630
614
|
path,
|
|
631
615
|
rect,
|
|
632
616
|
foreignObject div {
|
|
@@ -685,7 +669,7 @@
|
|
|
685
669
|
text-align: center;
|
|
686
670
|
transform: scale(1);
|
|
687
671
|
|
|
688
|
-
|
|
672
|
+
> .visx-group[style] {
|
|
689
673
|
transform: scale(1);
|
|
690
674
|
}
|
|
691
675
|
}
|
|
@@ -707,7 +691,7 @@
|
|
|
707
691
|
}
|
|
708
692
|
|
|
709
693
|
.cove-visualization.type-chart.type-sparkline {
|
|
710
|
-
&.lg .visualization-container>svg {
|
|
694
|
+
&.lg .visualization-container > svg {
|
|
711
695
|
width: 100%;
|
|
712
696
|
}
|
|
713
697
|
|
|
@@ -738,7 +722,6 @@
|
|
|
738
722
|
transform: translate(-20px, 0);
|
|
739
723
|
}
|
|
740
724
|
}
|
|
741
|
-
|
|
742
725
|
}
|
|
743
726
|
|
|
744
727
|
.subtext,
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import vm from 'node:vm'
|
|
2
4
|
import { testStandaloneBuild } from '@cdc/core/helpers/tests/testStandaloneBuild.ts'
|
|
3
5
|
import { describe, it, expect } from 'vitest'
|
|
4
6
|
|
|
7
|
+
const extractMarkedExampleConfig = (content, label) => {
|
|
8
|
+
const match = content.match(
|
|
9
|
+
/<!-- README_EXAMPLE_CONFIG_START -->\s*```jsx\s*([\s\S]*?)\s*```\s*<!-- README_EXAMPLE_CONFIG_END -->/
|
|
10
|
+
)
|
|
11
|
+
expect(match, `${label} should contain a marked README example block`).toBeTruthy()
|
|
12
|
+
const configMatch = match[1].match(/const config = (\{[\s\S]*?\})\n\nfunction App\(\)/)
|
|
13
|
+
expect(configMatch, `${label} should define const config before function App()`).toBeTruthy()
|
|
14
|
+
return vm.runInNewContext(`(${configMatch[1]})`)
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
describe('Chart', () => {
|
|
6
18
|
it('Can be built in isolation', async () => {
|
|
7
19
|
const pkgDir = path.join(__dirname, '..')
|
|
8
20
|
const result = await testStandaloneBuild(pkgDir)
|
|
9
21
|
expect(result).toBe(true)
|
|
10
22
|
}, 300000)
|
|
23
|
+
|
|
24
|
+
it('keeps the minimal example in sync with the README docs', () => {
|
|
25
|
+
const pkgRoot = path.join(__dirname, '..', '..')
|
|
26
|
+
const minimalExamplePath = path.join(pkgRoot, 'examples', 'minimal-example.json')
|
|
27
|
+
const readmePath = path.join(pkgRoot, 'README.md')
|
|
28
|
+
|
|
29
|
+
const minimalExample = JSON.parse(fs.readFileSync(minimalExamplePath, 'utf8'))
|
|
30
|
+
const readmeBlock = extractMarkedExampleConfig(fs.readFileSync(readmePath, 'utf8'), 'README.md')
|
|
31
|
+
|
|
32
|
+
expect(readmeBlock).toEqual(minimalExample)
|
|
33
|
+
expect(minimalExample.version).toBeTruthy()
|
|
34
|
+
})
|
|
11
35
|
})
|