@cdc/chart 4.26.2 → 4.26.4
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 +169 -0
- package/README.md +42 -17
- package/dist/cdcchart.js +39419 -35180
- package/examples/__data__/data-with-metadata.json +10 -0
- package/examples/feature/bar/additional-column-tooltip.json +3 -3
- package/examples/feature/deviation/planet-deviation-config.json +2 -2
- package/examples/feature/paired-bar/paired-bar-example.json +2 -2
- package/examples/feature/pie/planet-pie-example-config.json +2 -1
- 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 +58 -0
- package/examples/minimal-example.json +24 -0
- package/examples/private/test2.json +35012 -0
- package/package.json +4 -4
- package/src/CdcChart.tsx +8 -4
- package/src/CdcChartComponent.tsx +326 -290
- package/src/_stories/Chart.CustomColors.smoke.stories.tsx +97 -0
- package/src/_stories/Chart.Defaults.smoke.stories.tsx +95 -0
- package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
- package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
- package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
- package/src/_stories/Chart.SmallestLeftAxisMax.smoke.stories.tsx +64 -0
- package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +48 -2
- package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +177 -115
- package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
- package/src/_stories/ChartEditor.Editor.stories.tsx +24 -1
- package/src/_stories/{Chart.CustomColors.stories.tsx → ChartLine.DashedDates.smoke.stories.tsx} +4 -4
- package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
- package/src/_stories/_mock/paired-bar-abbr.json +421 -0
- package/src/_stories/_mock/paired-bar.json +1 -1
- package/src/_stories/_mock/pie_custom_colors.json +268 -0
- package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
- package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
- package/src/components/Axis/BottomAxis.tsx +10 -3
- package/src/components/Axis/LeftAxis.tsx +2 -4
- package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
- package/src/components/Axis/PairedBarAxis.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +21 -33
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +21 -35
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +21 -36
- package/src/components/BarChart/components/BarChart.Vertical.tsx +21 -34
- package/src/components/BarChart/components/BarChart.tsx +2 -6
- package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
- package/src/components/BarChart/helpers/useBarChart.ts +4 -1
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
- package/src/components/Brush/BrushSelector.tsx +2 -1
- package/src/components/Brush/MiniChartPreview.tsx +21 -26
- package/src/components/EditorPanel/EditorPanel.tsx +692 -954
- package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +218 -167
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -135
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +95 -28
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
- package/src/components/EditorPanel/editor-panel.scss +1 -1
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
- package/src/components/Forecasting/Forecasting.tsx +2 -2
- package/src/components/ForestPlot/ForestPlot.tsx +26 -22
- 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 +9 -4
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
- package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
- 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 +3 -2
- package/src/components/LineChart/index.tsx +11 -4
- package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
- package/src/components/LinearChart/tests/LinearChart.test.tsx +224 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
- package/src/components/LinearChart.tsx +124 -47
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/PieChart/PieChart.tsx +30 -13
- package/src/components/RadarChart/RadarChart.tsx +1 -1
- package/src/components/Regions/components/Regions.tsx +36 -26
- package/src/components/Sankey/components/Sankey.tsx +3 -3
- package/src/components/Sankey/sankey.scss +1 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
- package/src/components/SmallMultiples/SmallMultiples.css +5 -5
- package/src/components/Sparkline/components/SparkLine.tsx +1 -1
- package/src/components/Sparkline/index.scss +4 -2
- package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
- package/src/data/initial-state.js +24 -14
- package/src/data/legacy-defaults.ts +18 -0
- package/src/helpers/abbreviateNumber.ts +24 -17
- package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
- package/src/helpers/getChartPatternId.ts +17 -0
- package/src/helpers/getMinMax.ts +16 -2
- package/src/helpers/seriesColumnSettings.ts +114 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
- package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
- package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
- package/src/helpers/tooltipHelpers.ts +176 -0
- package/src/hooks/useHighlightedBars.ts +9 -16
- package/src/hooks/useProgrammaticTooltip.ts +16 -2
- package/src/hooks/useRightAxis.ts +14 -0
- package/src/hooks/useScales.ts +92 -56
- package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
- package/src/hooks/useTooltip.tsx +186 -40
- package/src/scss/main.scss +152 -79
- package/src/test/CdcChart.test.jsx +26 -2
- package/src/types/ChartConfig.ts +5 -0
- package/tests/fixtures/chart-config-with-metadata.json +29 -0
- package/tests/fixtures/data-with-metadata.json +10 -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/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.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/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.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/{ChartBrush.stories.tsx → ChartBrush.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
|
@@ -5,7 +5,7 @@ import ResizeObserver from 'resize-observer-polyfill'
|
|
|
5
5
|
import 'whatwg-fetch'
|
|
6
6
|
// Core components
|
|
7
7
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
8
|
-
import
|
|
8
|
+
import { VisualizationContainer, VisualizationContent } from '@cdc/core/components/Layout'
|
|
9
9
|
import Confirm from '@cdc/core/components/elements/Confirm'
|
|
10
10
|
import Error from '@cdc/core/components/elements/Error'
|
|
11
11
|
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
@@ -24,7 +24,21 @@ import { Label } from './types/Label'
|
|
|
24
24
|
import ParentSize from '@visx/responsive/lib/components/ParentSize'
|
|
25
25
|
import { timeParse } from 'd3-time-format'
|
|
26
26
|
import parse from 'html-react-parser'
|
|
27
|
-
import
|
|
27
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
28
|
+
import defaultsDeep from 'lodash/defaultsDeep'
|
|
29
|
+
import lodashDefaults from 'lodash/defaults'
|
|
30
|
+
import findKey from 'lodash/findKey'
|
|
31
|
+
import forEach from 'lodash/forEach'
|
|
32
|
+
import get from 'lodash/get'
|
|
33
|
+
import isEmpty from 'lodash/isEmpty'
|
|
34
|
+
import isEqual from 'lodash/isEqual'
|
|
35
|
+
import isString from 'lodash/isString'
|
|
36
|
+
import kebabCase from 'lodash/kebabCase'
|
|
37
|
+
import pick from 'lodash/pick'
|
|
38
|
+
import remove from 'lodash/remove'
|
|
39
|
+
import set from 'lodash/set'
|
|
40
|
+
import uniq from 'lodash/uniq'
|
|
41
|
+
import xor from 'lodash/xor'
|
|
28
42
|
// Primary Components
|
|
29
43
|
import ConfigContext, { ChartDispatchContext } from './ConfigContext'
|
|
30
44
|
import PieChart from './components/PieChart'
|
|
@@ -40,6 +54,7 @@ import SparkLine from './components/Sparkline'
|
|
|
40
54
|
import Legend from './components/Legend'
|
|
41
55
|
import WarmingStripesGradientLegend from './components/WarmingStripes/WarmingStripesGradientLegend'
|
|
42
56
|
import defaults from './data/initial-state'
|
|
57
|
+
import { LEGACY_CHART_DEFAULTS } from './data/legacy-defaults'
|
|
43
58
|
import EditorPanel from './components/EditorPanel'
|
|
44
59
|
import { abbreviateNumber } from './helpers/abbreviateNumber'
|
|
45
60
|
import { handleChartTabbing } from './helpers/handleChartTabbing'
|
|
@@ -57,6 +72,7 @@ import Annotation from './components/Annotations'
|
|
|
57
72
|
import { getVisibleAnnotations } from './components/Annotations/helpers/getVisibleAnnotations'
|
|
58
73
|
// Core Helpers
|
|
59
74
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
75
|
+
import { backfillDefaults } from '@cdc/core/helpers/backfillDefaults'
|
|
60
76
|
import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
|
|
61
77
|
import { missingRequiredSections } from '@cdc/core/helpers/missingRequiredSections'
|
|
62
78
|
import { filterVizData } from '@cdc/core/helpers/filterVizData'
|
|
@@ -90,6 +106,8 @@ import { Datasets } from '@cdc/core/types/DataSet'
|
|
|
90
106
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
91
107
|
import cloneConfig from '@cdc/core/helpers/cloneConfig'
|
|
92
108
|
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
109
|
+
import { ENABLE_CHART_MAP_TP5_TREATMENT, ENABLE_CHART_VISUAL_SETTINGS } from '@cdc/core/helpers/constants'
|
|
110
|
+
import CalloutFlag from '@cdc/core/assets/callout-flag.svg?url'
|
|
93
111
|
|
|
94
112
|
interface CdcChartProps {
|
|
95
113
|
config?: ChartConfig
|
|
@@ -174,36 +192,32 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
174
192
|
}
|
|
175
193
|
}
|
|
176
194
|
|
|
195
|
+
const markupOptions = {
|
|
196
|
+
isEditor,
|
|
197
|
+
filters: config.filters || [],
|
|
198
|
+
locale: config.locale,
|
|
199
|
+
dataMetadata: config.dataMetadata
|
|
200
|
+
}
|
|
201
|
+
|
|
177
202
|
return {
|
|
178
203
|
title: title
|
|
179
|
-
? processMarkupVariables(title, config.data || [], config.markupVariables,
|
|
180
|
-
isEditor,
|
|
181
|
-
filters: config.filters || []
|
|
182
|
-
}).processedContent
|
|
204
|
+
? processMarkupVariables(title, config.data || [], config.markupVariables, markupOptions).processedContent
|
|
183
205
|
: title,
|
|
184
206
|
superTitle: config.superTitle
|
|
185
|
-
? processMarkupVariables(config.superTitle, config.data || [], config.markupVariables,
|
|
186
|
-
|
|
187
|
-
filters: config.filters || []
|
|
188
|
-
}).processedContent
|
|
207
|
+
? processMarkupVariables(config.superTitle, config.data || [], config.markupVariables, markupOptions)
|
|
208
|
+
.processedContent
|
|
189
209
|
: config.superTitle,
|
|
190
210
|
introText: config.introText
|
|
191
|
-
? processMarkupVariables(config.introText, config.data || [], config.markupVariables,
|
|
192
|
-
|
|
193
|
-
filters: config.filters || []
|
|
194
|
-
}).processedContent
|
|
211
|
+
? processMarkupVariables(config.introText, config.data || [], config.markupVariables, markupOptions)
|
|
212
|
+
.processedContent
|
|
195
213
|
: config.introText,
|
|
196
214
|
legacyFootnotes: config.legacyFootnotes
|
|
197
|
-
? processMarkupVariables(config.legacyFootnotes, config.data || [], config.markupVariables,
|
|
198
|
-
|
|
199
|
-
filters: config.filters || []
|
|
200
|
-
}).processedContent
|
|
215
|
+
? processMarkupVariables(config.legacyFootnotes, config.data || [], config.markupVariables, markupOptions)
|
|
216
|
+
.processedContent
|
|
201
217
|
: config.legacyFootnotes,
|
|
202
218
|
description: config.description
|
|
203
|
-
? processMarkupVariables(config.description, config.data || [], config.markupVariables,
|
|
204
|
-
|
|
205
|
-
filters: config.filters || []
|
|
206
|
-
}).processedContent
|
|
219
|
+
? processMarkupVariables(config.description, config.data || [], config.markupVariables, markupOptions)
|
|
220
|
+
.processedContent
|
|
207
221
|
: config.description
|
|
208
222
|
}
|
|
209
223
|
}, [
|
|
@@ -237,10 +251,6 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
237
251
|
const { lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
|
|
238
252
|
const legendId = useId()
|
|
239
253
|
|
|
240
|
-
const hasDateAxis =
|
|
241
|
-
(config.xAxis || config.yAxis) && ['date-time', 'date'].includes((config.xAxis || config.yAxis).type)
|
|
242
|
-
const dataTableDefaultSortBy = hasDateAxis && config.xAxis.dataKey
|
|
243
|
-
|
|
244
254
|
const convertLineToBarGraph = isConvertLineToBarGraph(config, filteredData)
|
|
245
255
|
|
|
246
256
|
// Declaratively calculate series keys for pie charts based on filtered data
|
|
@@ -248,7 +258,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
248
258
|
if (config.visualizationType !== 'Pie' || !config.xAxis?.dataKey) return null
|
|
249
259
|
const data = filteredData?.length > 0 ? filteredData : excludedData
|
|
250
260
|
if (!data) return null
|
|
251
|
-
return
|
|
261
|
+
return uniq(data.map(d => d[config.xAxis.dataKey]))
|
|
252
262
|
}, [config.visualizationType, config.xAxis?.dataKey, filteredData, excludedData])
|
|
253
263
|
|
|
254
264
|
const prepareConfig = (loadedConfig: ChartConfig) => {
|
|
@@ -295,18 +305,18 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
295
305
|
// Ensure Horizon Chart has enough palette colors for all layers
|
|
296
306
|
if (newConfig.visualizationType === 'Horizon Chart') {
|
|
297
307
|
const numLayers = newConfig.horizon?.numLayers ?? 4
|
|
298
|
-
const currentCount =
|
|
299
|
-
|
|
308
|
+
const currentCount = get(newConfig, 'general.paletteColorCount', 4)
|
|
309
|
+
set(newConfig, 'general.paletteColorCount', Math.max(currentCount, numLayers))
|
|
300
310
|
}
|
|
301
311
|
|
|
302
|
-
|
|
312
|
+
defaultsDeep(newConfig, {
|
|
303
313
|
table: { showVertical: false }
|
|
304
314
|
})
|
|
305
315
|
|
|
306
|
-
|
|
316
|
+
set(newConfig, 'table.show', get(newConfig, 'table.show', !isDashboard))
|
|
307
317
|
|
|
308
|
-
|
|
309
|
-
|
|
318
|
+
forEach(newConfig.series, series => {
|
|
319
|
+
lodashDefaults(series, {
|
|
310
320
|
tooltip: true,
|
|
311
321
|
axis: 'Left'
|
|
312
322
|
})
|
|
@@ -325,15 +335,18 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
325
335
|
let processedYAxis = targetConfig.yAxis?.label
|
|
326
336
|
|
|
327
337
|
if (targetConfig.enableMarkupVariables && targetConfig.markupVariables?.length) {
|
|
338
|
+
const axisMarkupOptions = {
|
|
339
|
+
isEditor,
|
|
340
|
+
filters: targetConfig.filters || [],
|
|
341
|
+
locale: targetConfig.locale,
|
|
342
|
+
dataMetadata: targetConfig.dataMetadata
|
|
343
|
+
}
|
|
328
344
|
if (targetConfig.xAxis?.label) {
|
|
329
345
|
processedXAxis = processMarkupVariables(
|
|
330
346
|
targetConfig.xAxis.label,
|
|
331
347
|
dataSource || [],
|
|
332
348
|
targetConfig.markupVariables,
|
|
333
|
-
|
|
334
|
-
isEditor,
|
|
335
|
-
filters: targetConfig.filters || []
|
|
336
|
-
}
|
|
349
|
+
axisMarkupOptions
|
|
337
350
|
).processedContent
|
|
338
351
|
}
|
|
339
352
|
if (targetConfig.yAxis?.label) {
|
|
@@ -341,10 +354,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
341
354
|
targetConfig.yAxis.label,
|
|
342
355
|
dataSource || [],
|
|
343
356
|
targetConfig.markupVariables,
|
|
344
|
-
|
|
345
|
-
isEditor,
|
|
346
|
-
filters: targetConfig.filters || []
|
|
347
|
-
}
|
|
357
|
+
axisMarkupOptions
|
|
348
358
|
).processedContent
|
|
349
359
|
}
|
|
350
360
|
}
|
|
@@ -376,12 +386,17 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
376
386
|
const { processedXAxis, processedYAxis, runtimeXAxisLabel, runtimeYAxisLabel, isHorizontalVariant } =
|
|
377
387
|
getProcessedAxisLabels(newConfig, data || [])
|
|
378
388
|
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
389
|
+
// Backfill missing properties from defaults, respecting legacy values
|
|
390
|
+
backfillDefaults(newConfig, defaults, LEGACY_CHART_DEFAULTS)
|
|
391
|
+
|
|
392
|
+
// Auto-populate table.defaultSort for date-axis charts if not already set by user
|
|
393
|
+
const hasDateAxisType = ['date-time', 'date'].includes(newConfig.xAxis?.type)
|
|
394
|
+
if (hasDateAxisType && newConfig.xAxis?.dataKey && !newConfig.table?.defaultSort?.column) {
|
|
395
|
+
newConfig.table = {
|
|
396
|
+
...newConfig.table,
|
|
397
|
+
defaultSort: { column: newConfig.xAxis.dataKey, sortDirection: 'desc' }
|
|
383
398
|
}
|
|
384
|
-
}
|
|
399
|
+
}
|
|
385
400
|
|
|
386
401
|
const newExcludedData: any[] = getExcludedData(newConfig, dataOverride || stateData)
|
|
387
402
|
dispatch({ type: 'SET_EXCLUDED_DATA', payload: newExcludedData })
|
|
@@ -408,7 +423,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
408
423
|
const shouldPreserveError = existingErrorMessage && !isPieChartValidationError
|
|
409
424
|
|
|
410
425
|
newConfig.runtime = {} as Runtime
|
|
411
|
-
newConfig.runtime.series =
|
|
426
|
+
newConfig.runtime.series = cloneDeep(newConfig.series)
|
|
412
427
|
newConfig.runtime.seriesLabels = {}
|
|
413
428
|
newConfig.runtime.seriesLabelsAll = []
|
|
414
429
|
newConfig.runtime.originalXAxis = newConfig.xAxis
|
|
@@ -416,22 +431,22 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
416
431
|
if (newConfig.visualizationType === 'Pie') {
|
|
417
432
|
// Use the same data that will be passed to PieChart (after exclusions and filters)
|
|
418
433
|
const pieData = currentData.length > 0 ? currentData : newExcludedData
|
|
419
|
-
newConfig.runtime.seriesKeys =
|
|
434
|
+
newConfig.runtime.seriesKeys = uniq(pieData.map(d => d[newConfig.xAxis.dataKey]))
|
|
420
435
|
newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
|
|
421
436
|
newConfig.runtime.isPieChart = true // Flag to know when to use derived keys
|
|
422
437
|
} else if (newConfig.visualizationType === 'Radar') {
|
|
423
438
|
// Radar chart: seriesKeys are the entity names from xAxis.dataKey
|
|
424
439
|
const radarData = currentData.length > 0 ? currentData : newExcludedData
|
|
425
|
-
newConfig.runtime.seriesKeys =
|
|
440
|
+
newConfig.runtime.seriesKeys = uniq(radarData.map(d => d[newConfig.xAxis.dataKey]))
|
|
426
441
|
newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
|
|
427
442
|
} else {
|
|
428
443
|
const finalData = dataOverride || newConfig.formattedData || newConfig.data
|
|
429
444
|
newConfig.runtime.seriesKeys = (newConfig.runtime.series || []).flatMap(series => {
|
|
430
445
|
if (series.dynamicCategory) {
|
|
431
|
-
|
|
432
|
-
|
|
446
|
+
remove(newConfig.runtime.seriesLabelsAll, label => label === series.dataKey)
|
|
447
|
+
remove(newConfig.runtime.series, s => s.dataKey === series.dataKey)
|
|
433
448
|
// grab the dynamic series keys from the data
|
|
434
|
-
const seriesKeys: string[] =
|
|
449
|
+
const seriesKeys: string[] = uniq(finalData.map(d => d[series.dynamicCategory]))
|
|
435
450
|
// for each of those keys perform side effects
|
|
436
451
|
seriesKeys.forEach(dataKey => {
|
|
437
452
|
newConfig.runtime.seriesLabels[dataKey] = dataKey
|
|
@@ -515,8 +530,8 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
515
530
|
|
|
516
531
|
if (isHorizontalVariant) {
|
|
517
532
|
// For horizontal charts, axes are swapped, so processedYAxis goes to runtime.xAxis and vice versa
|
|
518
|
-
const horizontalXAxisSource =
|
|
519
|
-
const horizontalYAxisSource =
|
|
533
|
+
const horizontalXAxisSource = cloneDeep((newConfig.yAxis as any)?.yAxis || newConfig.yAxis)
|
|
534
|
+
const horizontalYAxisSource = cloneDeep((newConfig.xAxis as any)?.xAxis || newConfig.xAxis)
|
|
520
535
|
newConfig.runtime.xAxis = {
|
|
521
536
|
...horizontalXAxisSource,
|
|
522
537
|
label: runtimeXAxisLabel ?? horizontalXAxisSource?.label
|
|
@@ -525,7 +540,6 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
525
540
|
...horizontalYAxisSource,
|
|
526
541
|
label: runtimeYAxisLabel ?? horizontalYAxisSource?.label
|
|
527
542
|
}
|
|
528
|
-
newConfig.runtime.yAxis.labelOffset *= -1
|
|
529
543
|
|
|
530
544
|
newConfig.runtime.horizontal = false
|
|
531
545
|
newConfig.orientation = 'horizontal'
|
|
@@ -659,7 +673,8 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
659
673
|
if (newConfig.dataUrl && !urlFilters) {
|
|
660
674
|
// handle urls with spaces in the name.
|
|
661
675
|
if (newConfig.dataUrl) newConfig.dataUrl = `${newConfig.dataUrl}`
|
|
662
|
-
let newData = await fetchRemoteData(newConfig.dataUrl)
|
|
676
|
+
let { data: newData, dataMetadata } = await fetchRemoteData(newConfig.dataUrl)
|
|
677
|
+
newConfig.dataMetadata = dataMetadata
|
|
663
678
|
|
|
664
679
|
if (newConfig.vegaConfig) {
|
|
665
680
|
newData = extractCoveData(updateVegaData(newConfig.vegaConfig, newData))
|
|
@@ -720,7 +735,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
720
735
|
* When cove has a config and container ref publish the cove_loaded event.
|
|
721
736
|
*/
|
|
722
737
|
useEffect(() => {
|
|
723
|
-
if (container && !isLoading && !
|
|
738
|
+
if (container && !isLoading && !isEmpty(config) && !coveLoadedEventRan) {
|
|
724
739
|
publish('cove_loaded', { config: config })
|
|
725
740
|
dispatch({ type: 'SET_LOADED_EVENT', payload: true })
|
|
726
741
|
}
|
|
@@ -775,7 +790,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
775
790
|
}, [externalFilters]) // eslint-disable-line
|
|
776
791
|
|
|
777
792
|
// Declaratively update runtime series keys for pie charts when derived value changes
|
|
778
|
-
if (config.runtime?.isPieChart && pieSeriesKeys && !
|
|
793
|
+
if (config.runtime?.isPieChart && pieSeriesKeys && !isEqual(pieSeriesKeys, config.runtime?.seriesKeys)) {
|
|
779
794
|
const newConfig = {
|
|
780
795
|
...config,
|
|
781
796
|
runtime: {
|
|
@@ -815,7 +830,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
815
830
|
useEffect(() => {
|
|
816
831
|
if (
|
|
817
832
|
!config?.runtime ||
|
|
818
|
-
|
|
833
|
+
isEmpty(config.runtime) ||
|
|
819
834
|
(!config.runtime.xAxis && !config.runtime.yAxis) ||
|
|
820
835
|
!config.markupVariables?.length
|
|
821
836
|
) {
|
|
@@ -825,7 +840,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
825
840
|
const dataSource = (stateData && stateData.length ? stateData : config.data) || []
|
|
826
841
|
const { runtimeXAxisLabel, runtimeYAxisLabel, isHorizontalVariant } = getProcessedAxisLabels(config, dataSource)
|
|
827
842
|
|
|
828
|
-
const runtimeClone =
|
|
843
|
+
const runtimeClone = cloneDeep(config.runtime)
|
|
829
844
|
|
|
830
845
|
if (!runtimeClone?.xAxis || !runtimeClone?.yAxis) {
|
|
831
846
|
return
|
|
@@ -864,9 +879,9 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
864
879
|
return handleShowAll()
|
|
865
880
|
}
|
|
866
881
|
|
|
867
|
-
const newHighlight =
|
|
882
|
+
const newHighlight = findKey(config.runtime.seriesLabels, v => v === label.datum) || label.datum
|
|
868
883
|
|
|
869
|
-
const newSeriesHighlight =
|
|
884
|
+
const newSeriesHighlight = xor(seriesHighlight, [newHighlight])
|
|
870
885
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: newSeriesHighlight })
|
|
871
886
|
}
|
|
872
887
|
// Called on reset button click, unhighlights all data series
|
|
@@ -909,11 +924,11 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
909
924
|
const formatDate = (date, i, ticks) => {
|
|
910
925
|
const displayFormat =
|
|
911
926
|
config.runtime[section].dateDisplayFormat || config.runtime[section].dateParseFormat || '%Y-%m-%d'
|
|
912
|
-
let formattedDate = coreFormatDate(displayFormat, date)
|
|
927
|
+
let formattedDate = coreFormatDate(displayFormat, date, config.locale)
|
|
913
928
|
// Show years only once
|
|
914
929
|
if (config.xAxis.showYearsOnce && displayFormat?.includes('%Y') && ticks) {
|
|
915
930
|
const prevDate = ticks[i - 1] ? ticks[i - 1].value : null
|
|
916
|
-
const prevFormattedDate = coreFormatDate(displayFormat, prevDate)
|
|
931
|
+
const prevFormattedDate = coreFormatDate(displayFormat, prevDate, config.locale)
|
|
917
932
|
const year = formattedDate.match(/\d{4}/)
|
|
918
933
|
const prevYear = prevFormattedDate.match(/\d{4}/)
|
|
919
934
|
if (year && prevYear && year[0] === prevYear[0]) {
|
|
@@ -924,7 +939,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
924
939
|
}
|
|
925
940
|
|
|
926
941
|
const formatTooltipsDate = date => {
|
|
927
|
-
return coreFormatDate(config.tooltips.dateDisplayFormat, date)
|
|
942
|
+
return coreFormatDate(config.tooltips.dateDisplayFormat, date, config.locale)
|
|
928
943
|
}
|
|
929
944
|
|
|
930
945
|
// Format numeric data based on settings in config OR from passed in settings for Additional Columns
|
|
@@ -1066,16 +1081,16 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1066
1081
|
) {
|
|
1067
1082
|
num = num // eslint-disable-line
|
|
1068
1083
|
} else {
|
|
1069
|
-
num = num.toLocaleString(
|
|
1084
|
+
num = num.toLocaleString(config.locale, stringFormattingOptions)
|
|
1070
1085
|
}
|
|
1071
1086
|
let result = ''
|
|
1072
1087
|
|
|
1073
1088
|
if (abbreviated && axis === 'left' && shouldAbbreviate) {
|
|
1074
|
-
num = abbreviateNumber(parseFloat(num))
|
|
1089
|
+
num = abbreviateNumber(parseFloat(num), config.locale)
|
|
1075
1090
|
}
|
|
1076
1091
|
|
|
1077
1092
|
if (bottomAbbreviated && axis === 'bottom' && shouldAbbreviate) {
|
|
1078
|
-
num = abbreviateNumber(parseFloat(num))
|
|
1093
|
+
num = abbreviateNumber(parseFloat(num), config.locale)
|
|
1079
1094
|
}
|
|
1080
1095
|
|
|
1081
1096
|
if (addColPrefix && axis === 'left') {
|
|
@@ -1143,8 +1158,8 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1143
1158
|
if (!Array.isArray(data)) return []
|
|
1144
1159
|
if (config.visualizationType === 'Forecasting') return data
|
|
1145
1160
|
// specify keys that needs to be cleaned to render chart and skip rest
|
|
1146
|
-
const CIkeys: string[] = Object.values(
|
|
1147
|
-
const seriesKeys: string[] =
|
|
1161
|
+
const CIkeys: string[] = Object.values(get(config, 'confidenceKeys', {})) as string[]
|
|
1162
|
+
const seriesKeys: string[] = get(config, 'series', []).map((s: any) => s.dataKey)
|
|
1148
1163
|
const keysToClean: string[] = [...(seriesKeys ?? []), ...(CIkeys ?? [])]
|
|
1149
1164
|
|
|
1150
1165
|
// key that does not need to be cleaned
|
|
@@ -1166,7 +1181,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1166
1181
|
.map(col => col.name)
|
|
1167
1182
|
.concat([dynamicSeries.dynamicCategory, dynamicSeries.dataKey])
|
|
1168
1183
|
if (config.xAxis?.dataKey) usedColumns.push(config.xAxis.dataKey)
|
|
1169
|
-
return data.map(d =>
|
|
1184
|
+
return data.map(d => pick(d, usedColumns))
|
|
1170
1185
|
}
|
|
1171
1186
|
|
|
1172
1187
|
const pivotDynamicSeries = (config: ChartConfig): TableConfig => {
|
|
@@ -1184,18 +1199,51 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1184
1199
|
|
|
1185
1200
|
// Filter annotations to only those visible in current data view
|
|
1186
1201
|
const visibleAnnotations = getVisibleAnnotations(config.annotations, transformedData, config.xAxis?.dataKey)
|
|
1202
|
+
const isTp5Treatment = ENABLE_CHART_MAP_TP5_TREATMENT && config.visual?.tp5Treatment
|
|
1203
|
+
const visualSettingClasses = new Set([
|
|
1204
|
+
'component--has-border-color-theme',
|
|
1205
|
+
'component--has-accent',
|
|
1206
|
+
'component--has-background',
|
|
1207
|
+
'component--hide-background-color'
|
|
1208
|
+
])
|
|
1209
|
+
const tp5Classes = new Set(['component--tp5-treatment', 'component--tp5-treatment-background'])
|
|
1210
|
+
const bodyClasses = contentClasses.filter(className => {
|
|
1211
|
+
if (!ENABLE_CHART_VISUAL_SETTINGS && visualSettingClasses.has(className)) return false
|
|
1212
|
+
if (!ENABLE_CHART_MAP_TP5_TREATMENT && tp5Classes.has(className)) return false
|
|
1213
|
+
return true
|
|
1214
|
+
})
|
|
1215
|
+
if (config.visualizationType === 'Spark Line' && config.visual?.background) {
|
|
1216
|
+
bodyClasses.push('component--has-background')
|
|
1217
|
+
}
|
|
1218
|
+
if (config.visualizationType === 'Spark Line' && config.visual?.hideBackgroundColor) {
|
|
1219
|
+
bodyClasses.push('component--hide-background-color')
|
|
1220
|
+
}
|
|
1221
|
+
if (isTp5Treatment && !bodyClasses.includes('no-borders')) bodyClasses.push('no-borders')
|
|
1222
|
+
const chartTitle = (
|
|
1223
|
+
<Title
|
|
1224
|
+
showTitle={config.showTitle}
|
|
1225
|
+
isDashboard={isDashboard}
|
|
1226
|
+
title={title}
|
|
1227
|
+
superTitle={processedSuperTitle}
|
|
1228
|
+
titleStyle={isTp5Treatment ? 'small' : config.titleStyle}
|
|
1229
|
+
classes={['chart-title', `${config.theme}`, 'cove-visualization__title', isTp5Treatment ? '' : 'mb-3']}
|
|
1230
|
+
style={undefined}
|
|
1231
|
+
config={config}
|
|
1232
|
+
/>
|
|
1233
|
+
)
|
|
1187
1234
|
|
|
1188
1235
|
// Prevent render if loading
|
|
1189
1236
|
let body = <Loading />
|
|
1190
1237
|
|
|
1191
1238
|
const makeClassName = string => {
|
|
1192
|
-
if (!
|
|
1239
|
+
if (!isString(string)) return undefined
|
|
1193
1240
|
|
|
1194
|
-
return
|
|
1241
|
+
return kebabCase(string)
|
|
1195
1242
|
}
|
|
1196
1243
|
const getChartWrapperClasses = () => {
|
|
1197
1244
|
const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
|
|
1198
|
-
const classes = ['chart-container', 'p-relative']
|
|
1245
|
+
const classes = ['chart-container', 'visualization-container', 'p-relative']
|
|
1246
|
+
const visualSettingClasses = ['component--has-border-color-theme', 'component--has-accent']
|
|
1199
1247
|
if (legend?.position) {
|
|
1200
1248
|
if (isLegendWrapViewport(currentViewport) && legend?.position !== 'top') {
|
|
1201
1249
|
classes.push('legend-bottom')
|
|
@@ -1204,16 +1252,23 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1204
1252
|
}
|
|
1205
1253
|
}
|
|
1206
1254
|
if (legend?.hide) classes.push('legend-hidden')
|
|
1255
|
+
if (contentClasses.includes('sparkline')) classes.push('sparkline')
|
|
1207
1256
|
if (lineDatapointClass) classes.push(lineDatapointClass)
|
|
1208
1257
|
if (!config.barHasBorder) classes.push('chart-bar--no-border')
|
|
1209
1258
|
if (config.xAxis.brushActive && dashboardConfig?.type === 'dashboard' && (!isLegendOnBottom || legend.hide))
|
|
1210
1259
|
classes.push('dashboard-brush')
|
|
1211
|
-
|
|
1260
|
+
|
|
1261
|
+
if (!ENABLE_CHART_VISUAL_SETTINGS) {
|
|
1262
|
+
const filtered = classes.filter(className => !visualSettingClasses.includes(className))
|
|
1263
|
+
if (!filtered.includes('no-borders')) filtered.push('no-borders')
|
|
1264
|
+
return filtered
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1212
1267
|
return classes
|
|
1213
1268
|
}
|
|
1214
1269
|
|
|
1215
1270
|
const getChartSubTextClasses = () => {
|
|
1216
|
-
const classes = ['subtext
|
|
1271
|
+
const classes = ['subtext']
|
|
1217
1272
|
const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
|
|
1218
1273
|
|
|
1219
1274
|
if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
|
|
@@ -1228,211 +1283,34 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1228
1283
|
)
|
|
1229
1284
|
body = (
|
|
1230
1285
|
<>
|
|
1231
|
-
{
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
const hasError = errorMessage && typeof errorMessage === 'string' && errorMessage.trim() !== ''
|
|
1257
|
-
const shouldShow = undefined === config.newViz && isEditor && config.runtime && hasError
|
|
1258
|
-
return shouldShow ? <Error errorMessage={errorMessage} /> : null
|
|
1259
|
-
})()} */}
|
|
1260
|
-
|
|
1261
|
-
{/* Visualization Wrapper */}
|
|
1262
|
-
<div className={getChartWrapperClasses().join(' ')}>
|
|
1263
|
-
{/* Intro Text/Message */}
|
|
1264
|
-
{processedIntroText && config.visualizationType !== 'Spark Line' && (
|
|
1265
|
-
<section className={`introText mb-4`}>{parse(processedIntroText)}</section>
|
|
1266
|
-
)}
|
|
1267
|
-
|
|
1268
|
-
{/* Filters */}
|
|
1269
|
-
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
|
|
1270
|
-
<Filters
|
|
1271
|
-
config={config}
|
|
1272
|
-
setFilters={setFilters}
|
|
1273
|
-
excludedData={excludedData}
|
|
1274
|
-
dimensions={dimensions}
|
|
1275
|
-
interactionLabel={interactionLabel}
|
|
1276
|
-
/>
|
|
1277
|
-
)}
|
|
1278
|
-
<SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
|
|
1279
|
-
{visibleAnnotations.length > 0 && (
|
|
1280
|
-
<SkipTo
|
|
1281
|
-
skipId={handleChartTabbing(config, legendId)}
|
|
1282
|
-
skipMessage={`Skip over annotations`}
|
|
1283
|
-
key={`skip-annotations`}
|
|
1284
|
-
/>
|
|
1285
|
-
)}
|
|
1286
|
-
<LegendWrapper>
|
|
1287
|
-
<div
|
|
1288
|
-
className={
|
|
1289
|
-
legend.hide || isLegendWrapViewport(currentViewport)
|
|
1290
|
-
? 'w-100'
|
|
1291
|
-
: legend.position === 'bottom' ||
|
|
1292
|
-
legend.position === 'top' ||
|
|
1293
|
-
visualizationType === 'Sankey' ||
|
|
1294
|
-
visualizationType === 'Spark Line'
|
|
1295
|
-
? 'w-100'
|
|
1296
|
-
: 'w-75'
|
|
1297
|
-
}
|
|
1298
|
-
>
|
|
1299
|
-
{/* Check if there is data to display */}
|
|
1300
|
-
{(!filteredData || filteredData.length === 0) && (
|
|
1301
|
-
<div className='no-data-message' style={{ padding: '2rem', textAlign: 'center', color: '#666' }}>
|
|
1302
|
-
{config.chartMessage?.noData || 'No Data Available'}
|
|
1303
|
-
</div>
|
|
1304
|
-
)}
|
|
1305
|
-
|
|
1306
|
-
{/* All charts with LinearChart */}
|
|
1307
|
-
{filteredData &&
|
|
1308
|
-
filteredData.length > 0 &&
|
|
1309
|
-
!['Spark Line', 'Line', 'Sankey', 'Pie', 'Radar'].includes(config.visualizationType) && (
|
|
1310
|
-
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1311
|
-
<ParentSize>
|
|
1312
|
-
{parent => (
|
|
1313
|
-
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1314
|
-
)}
|
|
1315
|
-
</ParentSize>
|
|
1316
|
-
</div>
|
|
1317
|
-
)}
|
|
1318
|
-
|
|
1319
|
-
{filteredData && filteredData.length > 0 && config.visualizationType === 'Pie' && (
|
|
1320
|
-
<ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
|
|
1321
|
-
{parent => (
|
|
1322
|
-
<PieChart
|
|
1323
|
-
ref={svgRef}
|
|
1324
|
-
parentWidth={parent.width}
|
|
1325
|
-
parentHeight={parent.height}
|
|
1326
|
-
interactionLabel={interactionLabel}
|
|
1327
|
-
/>
|
|
1328
|
-
)}
|
|
1329
|
-
</ParentSize>
|
|
1330
|
-
)}
|
|
1331
|
-
{/* Radar Chart */}
|
|
1332
|
-
{filteredData && filteredData.length > 0 && config.visualizationType === 'Radar' && (
|
|
1333
|
-
<ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
|
|
1334
|
-
{parent => (
|
|
1335
|
-
<RadarChart
|
|
1336
|
-
ref={svgRef}
|
|
1337
|
-
parentWidth={parent.width}
|
|
1338
|
-
parentHeight={parent.height}
|
|
1339
|
-
interactionLabel={interactionLabel}
|
|
1340
|
-
/>
|
|
1341
|
-
)}
|
|
1342
|
-
</ParentSize>
|
|
1343
|
-
)}
|
|
1344
|
-
{/* Line Chart */}
|
|
1345
|
-
{filteredData &&
|
|
1346
|
-
filteredData.length > 0 &&
|
|
1347
|
-
config.visualizationType === 'Line' &&
|
|
1348
|
-
(convertLineToBarGraph ? (
|
|
1349
|
-
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1350
|
-
<ParentSize>
|
|
1351
|
-
{parent => (
|
|
1352
|
-
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1353
|
-
)}
|
|
1354
|
-
</ParentSize>
|
|
1355
|
-
</div>
|
|
1356
|
-
) : (
|
|
1357
|
-
<div ref={parentRef} style={{ width: '100%' }}>
|
|
1358
|
-
<ParentSize>
|
|
1359
|
-
{parent => {
|
|
1360
|
-
const labelMargin = 120
|
|
1361
|
-
const widthReduction =
|
|
1362
|
-
config.showLineSeriesLabels &&
|
|
1363
|
-
(config.legend.position !== 'right' || config.legend.hide)
|
|
1364
|
-
? labelMargin
|
|
1365
|
-
: 0
|
|
1366
|
-
return (
|
|
1367
|
-
<LinearChart
|
|
1368
|
-
ref={svgRef}
|
|
1369
|
-
parentWidth={parent.width - widthReduction}
|
|
1370
|
-
parentHeight={parent.height}
|
|
1371
|
-
/>
|
|
1372
|
-
)
|
|
1373
|
-
}}
|
|
1374
|
-
</ParentSize>
|
|
1375
|
-
</div>
|
|
1376
|
-
))}
|
|
1377
|
-
{/* Sparkline */}
|
|
1378
|
-
{config.visualizationType === 'Spark Line' && (
|
|
1379
|
-
<>
|
|
1380
|
-
<Filters
|
|
1381
|
-
config={config}
|
|
1382
|
-
setFilters={setFilters}
|
|
1383
|
-
excludedData={excludedData}
|
|
1384
|
-
dimensions={dimensions}
|
|
1385
|
-
interactionLabel={interactionLabel}
|
|
1386
|
-
/>
|
|
1387
|
-
{processedIntroText && (
|
|
1388
|
-
<section className='introText mb-4' style={{ padding: '0px 0 35px' }}>
|
|
1389
|
-
{parse(processedIntroText)}
|
|
1390
|
-
</section>
|
|
1391
|
-
)}
|
|
1392
|
-
<div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
|
|
1393
|
-
<ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
|
|
1394
|
-
</div>
|
|
1395
|
-
{description && (
|
|
1396
|
-
<div className='subtext' style={{ padding: '35px 0 15px' }}>
|
|
1397
|
-
{parse(description)}
|
|
1398
|
-
</div>
|
|
1399
|
-
)}
|
|
1400
|
-
</>
|
|
1401
|
-
)}
|
|
1402
|
-
{/* Sankey */}
|
|
1403
|
-
{config.visualizationType === 'Sankey' && (
|
|
1404
|
-
<ParentSize aria-hidden='true'>
|
|
1405
|
-
{parent => <SankeyChart runtime={config.runtime} width={parent.width} height={parent.height} />}
|
|
1406
|
-
</ParentSize>
|
|
1407
|
-
)}
|
|
1408
|
-
</div>
|
|
1409
|
-
{/* Legend */}
|
|
1410
|
-
{!config.legend.hide &&
|
|
1411
|
-
config.visualizationType !== 'Spark Line' &&
|
|
1412
|
-
config.visualizationType !== 'Sankey' &&
|
|
1413
|
-
!(config.visualizationType === 'Warming Stripes' && config.legend?.style === 'gradient') &&
|
|
1414
|
-
!(config.visualizationType === 'Warming Stripes' && config.smallMultiples?.mode) && (
|
|
1415
|
-
<Legend
|
|
1416
|
-
ref={legendRef}
|
|
1417
|
-
skipId={handleChartTabbing(config, legendId)}
|
|
1418
|
-
interactionLabel={interactionLabel}
|
|
1419
|
-
/>
|
|
1420
|
-
)}
|
|
1421
|
-
{config.visualizationType === 'Warming Stripes' &&
|
|
1422
|
-
config.legend?.style === 'gradient' &&
|
|
1423
|
-
!config.smallMultiples?.mode && <WarmingStripesGradientLegend />}
|
|
1424
|
-
</LegendWrapper>
|
|
1425
|
-
{/* Link */}
|
|
1286
|
+
{config.newViz && <Confirm updateConfig={updateConfig} config={config} />}
|
|
1287
|
+
{!missingRequiredSections(config) && !config.newViz && (
|
|
1288
|
+
<VisualizationContent
|
|
1289
|
+
innerClassName={`type-${makeClassName(config.visualizationType)}`}
|
|
1290
|
+
innerProps={{ 'aria-label': handleChartAriaLabels(config), tabIndex: 0 }}
|
|
1291
|
+
bodyClassName={bodyClasses.join(' ')}
|
|
1292
|
+
bodyWrapClassName={isTp5Treatment ? 'cdc-callout d-flex flex-column tp5-chart-callout' : ''}
|
|
1293
|
+
filters={
|
|
1294
|
+
config.filters?.length > 0 && !externalFilters && config.visualizationType !== 'Spark Line' ? (
|
|
1295
|
+
<Filters
|
|
1296
|
+
config={config}
|
|
1297
|
+
setFilters={setFilters}
|
|
1298
|
+
excludedData={excludedData}
|
|
1299
|
+
dimensions={dimensions}
|
|
1300
|
+
interactionLabel={interactionLabel}
|
|
1301
|
+
/>
|
|
1302
|
+
) : undefined
|
|
1303
|
+
}
|
|
1304
|
+
bodySubtext={
|
|
1305
|
+
processedDescription && config.visualizationType !== 'Spark Line' ? (
|
|
1306
|
+
<div className={`cove-prose ${getChartSubTextClasses().join(' ')}`}>{parse(processedDescription)}</div>
|
|
1307
|
+
) : null
|
|
1308
|
+
}
|
|
1309
|
+
bodyFooter={
|
|
1310
|
+
<>
|
|
1426
1311
|
{isDashboard && config.table && config.table.show && config.table.showDataTableLink
|
|
1427
1312
|
? tableLink
|
|
1428
1313
|
: link && link}
|
|
1429
|
-
{/* Description */}
|
|
1430
|
-
|
|
1431
|
-
{processedDescription && config.visualizationType !== 'Spark Line' && (
|
|
1432
|
-
<div className={getChartSubTextClasses().join(' ')}>{parse(processedDescription)}</div>
|
|
1433
|
-
)}
|
|
1434
|
-
|
|
1435
|
-
{/* Data Table */}
|
|
1436
1314
|
{(config.xAxis.dataKey &&
|
|
1437
1315
|
config.table.show &&
|
|
1438
1316
|
config.visualizationType !== 'Spark Line' &&
|
|
@@ -1461,15 +1339,12 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1461
1339
|
|
|
1462
1340
|
return (
|
|
1463
1341
|
<DataTable
|
|
1464
|
-
|
|
1465
|
-
when the default sort changes while editing */
|
|
1466
|
-
key={dataTableDefaultSortBy}
|
|
1342
|
+
key={config.table?.defaultSort?.column || ''}
|
|
1467
1343
|
config={dataTableConfig}
|
|
1468
1344
|
rawData={dataTableRawData}
|
|
1469
1345
|
runtimeData={dataTableRuntimeData}
|
|
1470
1346
|
expandDataTable={config.table.expanded}
|
|
1471
1347
|
columns={dataTableColumns}
|
|
1472
|
-
defaultSortBy={dataTableDefaultSortBy}
|
|
1473
1348
|
displayGeoName={name => name}
|
|
1474
1349
|
applyLegendToRow={applyLegendToRow}
|
|
1475
1350
|
tableTitle={config.table.label}
|
|
@@ -1482,6 +1357,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1482
1357
|
showDownloadImgButton={config.table.showDownloadImgButton}
|
|
1483
1358
|
showDownloadPdfButton={config.table.showDownloadPdfButton}
|
|
1484
1359
|
includeContextInDownload={config.table?.includeContextInDownload}
|
|
1360
|
+
hasSubtextAbove={Boolean(processedDescription && config.visualizationType !== 'Spark Line')}
|
|
1485
1361
|
interactionLabel={interactionLabel}
|
|
1486
1362
|
/>
|
|
1487
1363
|
)
|
|
@@ -1513,21 +1389,181 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1513
1389
|
</div>
|
|
1514
1390
|
)}
|
|
1515
1391
|
{visibleAnnotations.length > 0 && <Annotation.Dropdown />}
|
|
1516
|
-
{/* show pdf or image button */}
|
|
1517
1392
|
{processedLegacyFootnotes && (
|
|
1518
|
-
<section className='footnotes pt-2 mt-4'>{parse(processedLegacyFootnotes)}</section>
|
|
1393
|
+
<section className='footnotes cove-prose pt-2 mt-4'>{parse(processedLegacyFootnotes)}</section>
|
|
1519
1394
|
)}
|
|
1520
|
-
|
|
1395
|
+
</>
|
|
1396
|
+
}
|
|
1397
|
+
header={isTp5Treatment ? null : chartTitle}
|
|
1398
|
+
messageIsIntroText={config.visualizationType !== 'Spark Line' && !!processedIntroText}
|
|
1399
|
+
message={
|
|
1400
|
+
config.visualizationType !== 'Spark Line' && processedIntroText ? (
|
|
1401
|
+
<div className='cove-prose'>{parse(processedIntroText)}</div>
|
|
1402
|
+
) : null
|
|
1403
|
+
}
|
|
1404
|
+
footer={
|
|
1521
1405
|
<FootnotesStandAlone
|
|
1522
1406
|
config={configObj.footnotes}
|
|
1523
1407
|
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
1524
1408
|
markupVariables={config.markupVariables}
|
|
1525
1409
|
enableMarkupVariables={config.enableMarkupVariables}
|
|
1526
1410
|
data={config.data}
|
|
1411
|
+
dataMetadata={config.dataMetadata}
|
|
1527
1412
|
/>
|
|
1413
|
+
}
|
|
1414
|
+
>
|
|
1415
|
+
{isTp5Treatment && <img src={CalloutFlag} alt='' className='cdc-callout__flag' aria-hidden='true' />}
|
|
1416
|
+
{isTp5Treatment && chartTitle}
|
|
1417
|
+
<div className={getChartWrapperClasses().join(' ')}>
|
|
1418
|
+
<SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
|
|
1419
|
+
{visibleAnnotations.length > 0 && (
|
|
1420
|
+
<SkipTo
|
|
1421
|
+
skipId={handleChartTabbing(config, legendId)}
|
|
1422
|
+
skipMessage={`Skip over annotations`}
|
|
1423
|
+
key={`skip-annotations`}
|
|
1424
|
+
/>
|
|
1425
|
+
)}
|
|
1426
|
+
<LegendWrapper>
|
|
1427
|
+
<div
|
|
1428
|
+
className={
|
|
1429
|
+
legend.hide || isLegendWrapViewport(currentViewport)
|
|
1430
|
+
? 'w-100'
|
|
1431
|
+
: legend.position === 'bottom' ||
|
|
1432
|
+
legend.position === 'top' ||
|
|
1433
|
+
visualizationType === 'Sankey' ||
|
|
1434
|
+
visualizationType === 'Spark Line'
|
|
1435
|
+
? 'w-100'
|
|
1436
|
+
: 'w-75'
|
|
1437
|
+
}
|
|
1438
|
+
>
|
|
1439
|
+
{/* Check if there is data to display */}
|
|
1440
|
+
{(!filteredData || filteredData.length === 0) && (
|
|
1441
|
+
<div className='no-data-message' style={{ padding: '2rem', textAlign: 'center', color: '#666' }}>
|
|
1442
|
+
{config.chartMessage?.noData || 'No Data Available'}
|
|
1443
|
+
</div>
|
|
1444
|
+
)}
|
|
1445
|
+
|
|
1446
|
+
{/* All charts with LinearChart */}
|
|
1447
|
+
{filteredData &&
|
|
1448
|
+
filteredData.length > 0 &&
|
|
1449
|
+
!['Spark Line', 'Line', 'Sankey', 'Pie', 'Radar'].includes(config.visualizationType) && (
|
|
1450
|
+
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1451
|
+
<ParentSize>
|
|
1452
|
+
{parent => (
|
|
1453
|
+
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1454
|
+
)}
|
|
1455
|
+
</ParentSize>
|
|
1456
|
+
</div>
|
|
1457
|
+
)}
|
|
1458
|
+
|
|
1459
|
+
{filteredData && filteredData.length > 0 && config.visualizationType === 'Pie' && (
|
|
1460
|
+
<ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
|
|
1461
|
+
{parent => (
|
|
1462
|
+
<PieChart
|
|
1463
|
+
ref={svgRef}
|
|
1464
|
+
parentWidth={parent.width}
|
|
1465
|
+
parentHeight={parent.height}
|
|
1466
|
+
interactionLabel={interactionLabel}
|
|
1467
|
+
/>
|
|
1468
|
+
)}
|
|
1469
|
+
</ParentSize>
|
|
1470
|
+
)}
|
|
1471
|
+
{/* Radar Chart */}
|
|
1472
|
+
{filteredData && filteredData.length > 0 && config.visualizationType === 'Radar' && (
|
|
1473
|
+
<ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
|
|
1474
|
+
{parent => (
|
|
1475
|
+
<RadarChart
|
|
1476
|
+
ref={svgRef}
|
|
1477
|
+
parentWidth={parent.width}
|
|
1478
|
+
parentHeight={parent.height}
|
|
1479
|
+
interactionLabel={interactionLabel}
|
|
1480
|
+
/>
|
|
1481
|
+
)}
|
|
1482
|
+
</ParentSize>
|
|
1483
|
+
)}
|
|
1484
|
+
{/* Line Chart */}
|
|
1485
|
+
{filteredData &&
|
|
1486
|
+
filteredData.length > 0 &&
|
|
1487
|
+
config.visualizationType === 'Line' &&
|
|
1488
|
+
(convertLineToBarGraph ? (
|
|
1489
|
+
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1490
|
+
<ParentSize>
|
|
1491
|
+
{parent => (
|
|
1492
|
+
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1493
|
+
)}
|
|
1494
|
+
</ParentSize>
|
|
1495
|
+
</div>
|
|
1496
|
+
) : (
|
|
1497
|
+
<div ref={parentRef} style={{ width: '100%' }}>
|
|
1498
|
+
<ParentSize>
|
|
1499
|
+
{parent => {
|
|
1500
|
+
const labelMargin = 120
|
|
1501
|
+
const widthReduction =
|
|
1502
|
+
config.showLineSeriesLabels && (config.legend.position !== 'right' || config.legend.hide)
|
|
1503
|
+
? labelMargin
|
|
1504
|
+
: 0
|
|
1505
|
+
return (
|
|
1506
|
+
<LinearChart
|
|
1507
|
+
ref={svgRef}
|
|
1508
|
+
parentWidth={parent.width - widthReduction}
|
|
1509
|
+
parentHeight={parent.height}
|
|
1510
|
+
/>
|
|
1511
|
+
)
|
|
1512
|
+
}}
|
|
1513
|
+
</ParentSize>
|
|
1514
|
+
</div>
|
|
1515
|
+
))}
|
|
1516
|
+
{/* Sparkline */}
|
|
1517
|
+
{config.visualizationType === 'Spark Line' && (
|
|
1518
|
+
<>
|
|
1519
|
+
<Filters
|
|
1520
|
+
config={config}
|
|
1521
|
+
setFilters={setFilters}
|
|
1522
|
+
excludedData={excludedData}
|
|
1523
|
+
dimensions={dimensions}
|
|
1524
|
+
interactionLabel={interactionLabel}
|
|
1525
|
+
/>
|
|
1526
|
+
{processedIntroText && (
|
|
1527
|
+
<section className='introText cove-prose' style={{ padding: '0px 0 35px' }}>
|
|
1528
|
+
{parse(processedIntroText)}
|
|
1529
|
+
</section>
|
|
1530
|
+
)}
|
|
1531
|
+
<div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
|
|
1532
|
+
<ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
|
|
1533
|
+
</div>
|
|
1534
|
+
{processedDescription && (
|
|
1535
|
+
<div className='subtext cove-prose' style={{ padding: '35px 0 1.5rem' }}>
|
|
1536
|
+
{parse(processedDescription)}
|
|
1537
|
+
</div>
|
|
1538
|
+
)}
|
|
1539
|
+
</>
|
|
1540
|
+
)}
|
|
1541
|
+
{/* Sankey */}
|
|
1542
|
+
{config.visualizationType === 'Sankey' && (
|
|
1543
|
+
<ParentSize aria-hidden='true'>
|
|
1544
|
+
{parent => <SankeyChart runtime={config.runtime} width={parent.width} height={parent.height} />}
|
|
1545
|
+
</ParentSize>
|
|
1546
|
+
)}
|
|
1547
|
+
</div>
|
|
1548
|
+
{/* Legend */}
|
|
1549
|
+
{!config.legend.hide &&
|
|
1550
|
+
config.visualizationType !== 'Spark Line' &&
|
|
1551
|
+
config.visualizationType !== 'Sankey' &&
|
|
1552
|
+
!(config.visualizationType === 'Warming Stripes' && config.legend?.style === 'gradient') &&
|
|
1553
|
+
!(config.visualizationType === 'Warming Stripes' && config.smallMultiples?.mode) && (
|
|
1554
|
+
<Legend
|
|
1555
|
+
ref={legendRef}
|
|
1556
|
+
skipId={handleChartTabbing(config, legendId)}
|
|
1557
|
+
interactionLabel={interactionLabel}
|
|
1558
|
+
/>
|
|
1559
|
+
)}
|
|
1560
|
+
{config.visualizationType === 'Warming Stripes' &&
|
|
1561
|
+
config.legend?.style === 'gradient' &&
|
|
1562
|
+
!config.smallMultiples?.mode && <WarmingStripesGradientLegend />}
|
|
1563
|
+
</LegendWrapper>
|
|
1528
1564
|
</div>
|
|
1529
|
-
|
|
1530
|
-
|
|
1565
|
+
</VisualizationContent>
|
|
1566
|
+
)}
|
|
1531
1567
|
</>
|
|
1532
1568
|
)
|
|
1533
1569
|
}
|
|
@@ -1595,16 +1631,16 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1595
1631
|
return (
|
|
1596
1632
|
<ConfigContext.Provider value={contextValues}>
|
|
1597
1633
|
<ChartDispatchContext.Provider value={dispatch}>
|
|
1598
|
-
<
|
|
1634
|
+
<VisualizationContainer
|
|
1599
1635
|
config={config}
|
|
1600
1636
|
isEditor={isEditor}
|
|
1601
1637
|
currentViewport={currentViewport}
|
|
1602
1638
|
ref={outerContainerRef}
|
|
1603
1639
|
imageId={imageId}
|
|
1604
|
-
|
|
1640
|
+
editorPanel={!isLoading ? <EditorPanel datasets={datasets} /> : null}
|
|
1605
1641
|
>
|
|
1606
1642
|
{body}
|
|
1607
|
-
</
|
|
1643
|
+
</VisualizationContainer>
|
|
1608
1644
|
</ChartDispatchContext.Provider>
|
|
1609
1645
|
</ConfigContext.Provider>
|
|
1610
1646
|
)
|