@cdc/chart 4.25.8 → 4.25.11
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/.claude/settings.local.json +9 -0
- package/dist/{cdcchart-1a1724a1.es.js → cdcchart-dgT_1dIT.es.js} +136 -151
- package/dist/cdcchart.js +44236 -40355
- package/examples/feature/__data__/planet-example-data.json +0 -30
- package/examples/feature/boxplot/valid-boxplot.csv +38 -17
- package/examples/grouped-bar-test.json +400 -0
- package/examples/private/DEV-11825.json +573 -0
- package/examples/private/d.json +382 -0
- package/examples/private/example-2.json +49784 -0
- package/examples/private/f2.json +1 -0
- package/examples/private/f4.json +1577 -0
- package/examples/private/forecast.json +1180 -0
- package/examples/private/lollipop.json +468 -0
- package/examples/private/na.json +913 -0
- package/examples/private/new.json +48756 -0
- package/examples/private/pie-chart-legend.json +904 -0
- package/examples/private/test-data.csv +28 -0
- package/examples/suppressed_tooltip.json +480 -0
- package/index.html +2 -133
- package/package.json +25 -7
- package/src/CdcChart.tsx +9 -13
- package/src/CdcChartComponent.tsx +403 -92
- package/src/_stories/Chart.Anchors.stories.tsx +2 -2
- package/src/_stories/Chart.BoxPlot.stories.tsx +1 -1
- package/src/_stories/Chart.CI.stories.tsx +1 -1
- package/src/_stories/Chart.Combo.stories.tsx +18 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +2 -2
- package/src/_stories/Chart.Filters.stories.tsx +2 -2
- package/src/_stories/Chart.Forecast.stories.tsx +36 -0
- package/src/_stories/Chart.HTMLInDataTable.stories.tsx +520 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.Patterns.stories.tsx +20 -0
- package/src/_stories/Chart.PreserveDecimals.stories.tsx +220 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +1 -1
- package/src/_stories/Chart.SmallMultiples.stories.tsx +47 -0
- package/src/_stories/Chart.stories.tsx +8 -5
- package/src/_stories/Chart.tooltip.stories.tsx +1 -1
- package/src/_stories/ChartAnnotation.stories.tsx +7 -4
- package/src/_stories/ChartAxisLabels.stories.tsx +2 -2
- package/src/_stories/ChartAxisTitles.stories.tsx +2 -2
- package/src/_stories/ChartBar.Editor.stories.tsx +3580 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +658 -0
- package/src/_stories/ChartEditor.stories.tsx +59 -60
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +1 -1
- package/src/_stories/ChartPrefixSuffix.stories.tsx +2 -2
- package/src/_stories/_mock/combo.json +451 -0
- package/src/_stories/_mock/editor-test-configs.json +376 -0
- package/src/_stories/_mock/editor-test-datasets.json +477 -0
- package/src/_stories/_mock/editor-tests/bar-chart-editor-test.json +255 -0
- package/src/_stories/_mock/editor-tests/bar-chart-general-test.json +267 -0
- package/src/_stories/_mock/editor-tests/bar-chart-test.json +237 -0
- package/src/_stories/_mock/forecast_combo_with_gaps.json +913 -0
- package/src/_stories/_mock/pie_config.json +257 -62
- package/src/_stories/_mock/small_multiples/small_multiples_bars.json +1944 -0
- package/src/_stories/_mock/small_multiples/small_multiples_big_data_bars.json +1114 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines.json +2646 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines_colors.json +1305 -0
- package/src/_stories/_mock/small_multiples/small_multiples_stacked_bars.json +1936 -0
- package/src/_stories/_mock/stacked-pattern-test.json +520 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/Annotations/components/findNearestDatum.ts +6 -41
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -6
- package/src/components/AreaChart/index.tsx +1 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +161 -22
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +138 -5
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +215 -73
- package/src/components/BarChart/components/BarChart.Vertical.tsx +155 -22
- package/src/components/BarChart/helpers/index.ts +43 -4
- package/src/components/BarChart/helpers/lollipopColors.ts +27 -0
- package/src/components/BarChart/helpers/useBarChart.ts +25 -3
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +2 -1
- package/src/components/BoxPlot/helpers/index.ts +3 -3
- package/src/components/Brush/BrushChart.tsx +1 -1
- package/src/components/DeviationBar.jsx +9 -6
- package/src/components/EditorPanel/EditorPanel.tsx +563 -229
- package/src/components/EditorPanel/EditorPanelContext.ts +3 -0
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +96 -111
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +19 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +461 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +80 -67
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +422 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +188 -139
- package/src/components/EditorPanel/components/Panels/index.tsx +5 -1
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +0 -8
- package/src/components/EditorPanel/editor-panel.scss +0 -20
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +49 -48
- package/src/components/EditorPanel/useEditorPermissions.ts +7 -15
- package/src/components/Forecasting/Forecasting.tsx +175 -27
- package/src/components/ForestPlot/ForestPlot.tsx +11 -7
- package/src/components/ForestPlot/ForestPlotProps.ts +1 -1
- package/src/components/Legend/Legend.Component.tsx +114 -14
- package/src/components/Legend/helpers/createFormatLabels.tsx +230 -171
- package/src/components/Legend/helpers/getLegendClasses.ts +0 -1
- package/src/components/LegendWrapper.tsx +1 -1
- package/src/components/LineChart/LineChartProps.ts +0 -3
- package/src/components/LineChart/components/LineChart.Circle.tsx +2 -2
- package/src/components/LineChart/helpers.ts +1 -1
- package/src/components/LineChart/index.tsx +38 -15
- package/src/components/LinearChart.tsx +96 -84
- package/src/components/PairedBarChart.jsx +6 -4
- package/src/components/PieChart/PieChart.tsx +170 -54
- package/src/components/Regions/components/Regions.tsx +3 -24
- package/src/components/Sankey/components/Sankey.tsx +7 -1
- package/src/components/Sankey/types/index.ts +1 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +32 -4
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +198 -0
- package/src/components/SmallMultiples/SmallMultiples.css +32 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +271 -0
- package/src/components/SmallMultiples/index.ts +2 -0
- package/src/data/initial-state.js +327 -293
- package/src/helpers/buildForecastPaletteMappings.ts +112 -0
- package/src/helpers/buildForecastPaletteOptions.ts +71 -0
- package/src/helpers/getColorScale.ts +82 -8
- package/src/{hooks/useMinMax.ts → helpers/getMinMax.ts} +14 -7
- package/src/helpers/getNewRuntime.ts +1 -1
- package/src/helpers/getTransformedData.ts +1 -1
- package/src/helpers/getYAxisAutoPadding.ts +53 -0
- package/src/helpers/smallMultiplesHelpers.ts +529 -0
- package/src/hooks/useChartHoverAnalytics.tsx +44 -0
- package/src/hooks/useProgrammaticTooltip.ts +96 -0
- package/src/hooks/useReduceData.ts +105 -70
- package/src/hooks/useScales.ts +88 -34
- package/src/hooks/useSmallMultipleSynchronization.ts +59 -0
- package/src/hooks/useTooltip.tsx +116 -29
- package/src/index.jsx +0 -2
- package/src/scss/main.scss +13 -80
- package/src/store/chart.actions.ts +2 -0
- package/src/store/chart.reducer.ts +5 -1
- package/src/test/CdcChart.test.jsx +8 -3
- package/src/types/ChartConfig.ts +53 -11
- package/src/types/ChartContext.ts +4 -0
- package/vite.config.js +1 -1
- package/vitest.config.ts +16 -0
- package/src/_stories/_mock/pie_data.json +0 -218
- package/src/components/AreaChart/components/AreaChart.jsx +0 -109
- package/src/coreStyles_chart.scss +0 -3
- package/src/helpers/configHelpers.ts +0 -28
- package/src/helpers/generateColorsArray.ts +0 -8
- package/src/helpers/sort.ts +0 -7
- package/src/hooks/useActiveElement.js +0 -19
- package/src/hooks/useChartClasses.js +0 -41
- package/src/hooks/useColorPalette.js +0 -76
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback, useRef, useId, useContext, useReducer } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef, useId, useContext, useReducer, useMemo } from 'react'
|
|
2
2
|
|
|
3
3
|
// IE11
|
|
4
4
|
import ResizeObserver from 'resize-observer-polyfill'
|
|
@@ -24,7 +24,6 @@ import { Label } from './types/Label'
|
|
|
24
24
|
import ParentSize from '@visx/responsive/lib/components/ParentSize'
|
|
25
25
|
import { timeParse, timeFormat } from 'd3-time-format'
|
|
26
26
|
import parse from 'html-react-parser'
|
|
27
|
-
import 'react-tooltip/dist/react-tooltip.css'
|
|
28
27
|
import _ from 'lodash'
|
|
29
28
|
// Primary Components
|
|
30
29
|
import ConfigContext, { ChartDispatchContext } from './ConfigContext'
|
|
@@ -33,7 +32,8 @@ import SankeyChart from './components/Sankey'
|
|
|
33
32
|
import LinearChart from './components/LinearChart'
|
|
34
33
|
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
35
34
|
|
|
36
|
-
import {
|
|
35
|
+
import { twoColorPalette } from '@cdc/core/data/colorPalettes'
|
|
36
|
+
import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
|
|
37
37
|
|
|
38
38
|
import SparkLine from './components/Sparkline'
|
|
39
39
|
import Legend from './components/Legend'
|
|
@@ -46,7 +46,8 @@ import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
|
|
|
46
46
|
import { lineOptions } from './helpers/lineOptions'
|
|
47
47
|
import { handleLineType } from './helpers/handleLineType'
|
|
48
48
|
import { handleRankByValue } from './helpers/handleRankByValue'
|
|
49
|
-
import { generateColorsArray } from '
|
|
49
|
+
import { generateColorsArray } from '@cdc/core/helpers/generateColorsArray'
|
|
50
|
+
import { processMarkupVariables } from '@cdc/core/helpers/markupProcessor'
|
|
50
51
|
import Loading from '@cdc/core/components/Loading'
|
|
51
52
|
import Filters from '@cdc/core/components/Filters'
|
|
52
53
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
@@ -63,7 +64,7 @@ import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
|
63
64
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
64
65
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
65
66
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
66
|
-
import EditorContext from '
|
|
67
|
+
import EditorContext from '@cdc/core/contexts/EditorContext'
|
|
67
68
|
import { EDITOR_WIDTH } from '@cdc/core/helpers/constants'
|
|
68
69
|
import { extractCoveData, updateVegaData } from '@cdc/core/helpers/vegaConfig'
|
|
69
70
|
// Local helpers
|
|
@@ -74,6 +75,7 @@ import { getExcludedData } from './helpers/getExcludedData'
|
|
|
74
75
|
import { getColorScale } from './helpers/getColorScale'
|
|
75
76
|
import { getTransformedData } from './helpers/getTransformedData'
|
|
76
77
|
import { getPiePercent } from './helpers/getPiePercent'
|
|
78
|
+
import { prepareSmallMultiplesDataTable } from './helpers/smallMultiplesHelpers'
|
|
77
79
|
|
|
78
80
|
// styles
|
|
79
81
|
import './scss/main.scss'
|
|
@@ -83,6 +85,8 @@ import { getNewRuntime } from './helpers/getNewRuntime'
|
|
|
83
85
|
import FootnotesStandAlone from '@cdc/core/components/Footnotes/FootnotesStandAlone'
|
|
84
86
|
import { Datasets } from '@cdc/core/types/DataSet'
|
|
85
87
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
88
|
+
import cloneConfig from '@cdc/core/helpers/cloneConfig'
|
|
89
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
86
90
|
|
|
87
91
|
interface CdcChartProps {
|
|
88
92
|
config?: ChartConfig
|
|
@@ -152,6 +156,73 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
152
156
|
// Destructure items from config for more readable JSX
|
|
153
157
|
let { legend, title } = config
|
|
154
158
|
|
|
159
|
+
// Process markup variables for text fields (memoized to prevent re-processing on every render)
|
|
160
|
+
// Note: XSS Safety - The processed content is parsed using html-react-parser which sanitizes
|
|
161
|
+
// HTML input by default. The markup processor returns plain text with user data substituted.
|
|
162
|
+
const processedTextFields = useMemo(() => {
|
|
163
|
+
if (!config.enableMarkupVariables || !config.markupVariables?.length) {
|
|
164
|
+
return {
|
|
165
|
+
title,
|
|
166
|
+
superTitle: config.superTitle,
|
|
167
|
+
introText: config.introText,
|
|
168
|
+
legacyFootnotes: config.legacyFootnotes,
|
|
169
|
+
description: config.description
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
title: title
|
|
175
|
+
? processMarkupVariables(title, config.data || [], config.markupVariables, {
|
|
176
|
+
isEditor,
|
|
177
|
+
filters: config.filters || []
|
|
178
|
+
}).processedContent
|
|
179
|
+
: title,
|
|
180
|
+
superTitle: config.superTitle
|
|
181
|
+
? processMarkupVariables(config.superTitle, config.data || [], config.markupVariables, {
|
|
182
|
+
isEditor,
|
|
183
|
+
filters: config.filters || []
|
|
184
|
+
}).processedContent
|
|
185
|
+
: config.superTitle,
|
|
186
|
+
introText: config.introText
|
|
187
|
+
? processMarkupVariables(config.introText, config.data || [], config.markupVariables, {
|
|
188
|
+
isEditor,
|
|
189
|
+
filters: config.filters || []
|
|
190
|
+
}).processedContent
|
|
191
|
+
: config.introText,
|
|
192
|
+
legacyFootnotes: config.legacyFootnotes
|
|
193
|
+
? processMarkupVariables(config.legacyFootnotes, config.data || [], config.markupVariables, {
|
|
194
|
+
isEditor,
|
|
195
|
+
filters: config.filters || []
|
|
196
|
+
}).processedContent
|
|
197
|
+
: config.legacyFootnotes,
|
|
198
|
+
description: config.description
|
|
199
|
+
? processMarkupVariables(config.description, config.data || [], config.markupVariables, {
|
|
200
|
+
isEditor,
|
|
201
|
+
filters: config.filters || []
|
|
202
|
+
}).processedContent
|
|
203
|
+
: config.description
|
|
204
|
+
}
|
|
205
|
+
}, [
|
|
206
|
+
config.enableMarkupVariables,
|
|
207
|
+
config.markupVariables,
|
|
208
|
+
config.data,
|
|
209
|
+
config.filters,
|
|
210
|
+
title,
|
|
211
|
+
config.superTitle,
|
|
212
|
+
config.introText,
|
|
213
|
+
config.legacyFootnotes,
|
|
214
|
+
config.description,
|
|
215
|
+
isEditor
|
|
216
|
+
])
|
|
217
|
+
|
|
218
|
+
// Destructure processed values
|
|
219
|
+
title = processedTextFields.title
|
|
220
|
+
const processedSuperTitle = processedTextFields.superTitle
|
|
221
|
+
const processedIntroText = processedTextFields.introText
|
|
222
|
+
const processedLegacyFootnotes = processedTextFields.legacyFootnotes
|
|
223
|
+
const processedDescription = processedTextFields.description
|
|
224
|
+
// Note: Axis labels are processed within updateConfig to ensure they use the correct data
|
|
225
|
+
|
|
155
226
|
// set defaults on titles if blank AND only in editor
|
|
156
227
|
if (isEditor) {
|
|
157
228
|
if (!title || title === '') title = 'Chart Title'
|
|
@@ -169,7 +240,22 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
169
240
|
const convertLineToBarGraph = isConvertLineToBarGraph(config, filteredData)
|
|
170
241
|
|
|
171
242
|
const prepareConfig = (loadedConfig: ChartConfig) => {
|
|
172
|
-
|
|
243
|
+
// Create defaults without version to avoid overriding legacy configs
|
|
244
|
+
const defaultsWithoutPalette = { ...defaults }
|
|
245
|
+
|
|
246
|
+
// Only remove palette defaults for legacy (v1) configs
|
|
247
|
+
// New configs and v2 configs should get the v2 palette defaults
|
|
248
|
+
if (loadedConfig?.general?.palette || (!loadedConfig?.general && !loadedConfig?.color)) {
|
|
249
|
+
// Keep palette defaults for:
|
|
250
|
+
// 1. Configs that already have general.palette (v2 configs)
|
|
251
|
+
// 2. New configs (no general section and no legacy color property)
|
|
252
|
+
} else {
|
|
253
|
+
// Remove palette defaults for legacy configs that have color but no general.palette
|
|
254
|
+
delete defaultsWithoutPalette.general?.palette
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let newConfig = { ...defaultsWithoutPalette, ...loadedConfig }
|
|
258
|
+
|
|
173
259
|
_.defaultsDeep(newConfig, {
|
|
174
260
|
table: { showVertical: false }
|
|
175
261
|
})
|
|
@@ -190,12 +276,63 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
190
276
|
return newConfig
|
|
191
277
|
}
|
|
192
278
|
|
|
279
|
+
const getProcessedAxisLabels = useCallback(
|
|
280
|
+
(targetConfig: AllChartsConfig, dataSource: any[] = []) => {
|
|
281
|
+
let processedXAxis = targetConfig.xAxis?.label
|
|
282
|
+
let processedYAxis = targetConfig.yAxis?.label
|
|
283
|
+
|
|
284
|
+
if (targetConfig.enableMarkupVariables && targetConfig.markupVariables?.length) {
|
|
285
|
+
if (targetConfig.xAxis?.label) {
|
|
286
|
+
processedXAxis = processMarkupVariables(
|
|
287
|
+
targetConfig.xAxis.label,
|
|
288
|
+
dataSource || [],
|
|
289
|
+
targetConfig.markupVariables,
|
|
290
|
+
{
|
|
291
|
+
isEditor,
|
|
292
|
+
filters: targetConfig.filters || []
|
|
293
|
+
}
|
|
294
|
+
).processedContent
|
|
295
|
+
}
|
|
296
|
+
if (targetConfig.yAxis?.label) {
|
|
297
|
+
processedYAxis = processMarkupVariables(
|
|
298
|
+
targetConfig.yAxis.label,
|
|
299
|
+
dataSource || [],
|
|
300
|
+
targetConfig.markupVariables,
|
|
301
|
+
{
|
|
302
|
+
isEditor,
|
|
303
|
+
filters: targetConfig.filters || []
|
|
304
|
+
}
|
|
305
|
+
).processedContent
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const isHorizontalVariant =
|
|
310
|
+
((targetConfig.visualizationType === 'Bar' || targetConfig.visualizationType === 'Box Plot') &&
|
|
311
|
+
targetConfig.orientation === 'horizontal') ||
|
|
312
|
+
['Deviation Bar', 'Paired Bar', 'Forest Plot'].includes(targetConfig.visualizationType)
|
|
313
|
+
|
|
314
|
+
const runtimeXAxisLabel = isHorizontalVariant
|
|
315
|
+
? processedYAxis ?? (targetConfig.yAxis as any)?.yAxis?.label ?? targetConfig.yAxis?.label
|
|
316
|
+
: processedXAxis ?? targetConfig.xAxis?.label
|
|
317
|
+
|
|
318
|
+
const runtimeYAxisLabel = isHorizontalVariant
|
|
319
|
+
? processedXAxis ?? (targetConfig.xAxis as any)?.xAxis?.label ?? targetConfig.xAxis?.label
|
|
320
|
+
: processedYAxis ?? targetConfig.yAxis?.label
|
|
321
|
+
|
|
322
|
+
return { processedXAxis, processedYAxis, runtimeXAxisLabel, runtimeYAxisLabel, isHorizontalVariant }
|
|
323
|
+
},
|
|
324
|
+
[isEditor]
|
|
325
|
+
)
|
|
326
|
+
|
|
193
327
|
const updateConfig = (_config: AllChartsConfig, dataOverride?: any[]) => {
|
|
194
|
-
const newConfig =
|
|
328
|
+
const newConfig = cloneConfig(_config)
|
|
195
329
|
let data = dataOverride || stateData
|
|
196
330
|
|
|
197
331
|
data = handleRankByValue(data, newConfig)
|
|
198
332
|
|
|
333
|
+
const { processedXAxis, processedYAxis, runtimeXAxisLabel, runtimeYAxisLabel, isHorizontalVariant } =
|
|
334
|
+
getProcessedAxisLabels(newConfig, data || [])
|
|
335
|
+
|
|
199
336
|
// Deeper copy
|
|
200
337
|
Object.keys(defaults).forEach(key => {
|
|
201
338
|
if (newConfig[key] && 'object' === typeof newConfig[key] && !Array.isArray(newConfig[key])) {
|
|
@@ -219,6 +356,14 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
219
356
|
}
|
|
220
357
|
|
|
221
358
|
//Enforce default values that need to be calculated at runtime
|
|
359
|
+
// Preserve error messages that were set outside of updateConfig (e.g., from pattern settings)
|
|
360
|
+
const existingErrorMessage = _config.runtime?.editorErrorMessage || ''
|
|
361
|
+
const isPieChartValidationError =
|
|
362
|
+
existingErrorMessage === 'Data column section must be set for pie charts.' ||
|
|
363
|
+
existingErrorMessage === 'Segment labels section must be set for pie charts.' ||
|
|
364
|
+
existingErrorMessage === 'Data column and Segment labels sections must be set for pie charts.'
|
|
365
|
+
const shouldPreserveError = existingErrorMessage && !isPieChartValidationError
|
|
366
|
+
|
|
222
367
|
newConfig.runtime = {} as Runtime
|
|
223
368
|
newConfig.runtime.series = _.cloneDeep(newConfig.series)
|
|
224
369
|
newConfig.runtime.seriesLabels = {}
|
|
@@ -226,7 +371,9 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
226
371
|
newConfig.runtime.originalXAxis = newConfig.xAxis
|
|
227
372
|
|
|
228
373
|
if (newConfig.visualizationType === 'Pie') {
|
|
229
|
-
|
|
374
|
+
// Use the same data that will be passed to PieChart (after exclusions and filters)
|
|
375
|
+
const pieData = currentData.length > 0 ? currentData : newExcludedData
|
|
376
|
+
newConfig.runtime.seriesKeys = _.uniq(pieData.map(d => d[newConfig.xAxis.dataKey]))
|
|
230
377
|
newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
|
|
231
378
|
} else {
|
|
232
379
|
const finalData = dataOverride || newConfig.formattedData || newConfig.data
|
|
@@ -278,6 +425,18 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
278
425
|
newConfig.runtime.forecastingSeriesKeys.push(series)
|
|
279
426
|
}
|
|
280
427
|
})
|
|
428
|
+
|
|
429
|
+
// Default to date scaling type for Forecasting charts
|
|
430
|
+
if (newConfig.xAxis.type === 'categorical') {
|
|
431
|
+
newConfig.xAxis.type = 'date'
|
|
432
|
+
// Initialize date parsing formats if they don't exist
|
|
433
|
+
if (!newConfig.xAxis.dateParseFormat) {
|
|
434
|
+
newConfig.xAxis.dateParseFormat = '%Y-%m-%d'
|
|
435
|
+
}
|
|
436
|
+
if (!newConfig.xAxis.dateDisplayFormat) {
|
|
437
|
+
newConfig.xAxis.dateDisplayFormat = '%Y-%m-%d'
|
|
438
|
+
}
|
|
439
|
+
}
|
|
281
440
|
}
|
|
282
441
|
|
|
283
442
|
if (newConfig.visualizationType === 'Area Chart' && newConfig.series) {
|
|
@@ -289,13 +448,18 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
289
448
|
newConfig.visualizationSubType = 'stacked'
|
|
290
449
|
}
|
|
291
450
|
|
|
292
|
-
if (
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
451
|
+
if (isHorizontalVariant) {
|
|
452
|
+
// For horizontal charts, axes are swapped, so processedYAxis goes to runtime.xAxis and vice versa
|
|
453
|
+
const horizontalXAxisSource = _.cloneDeep((newConfig.yAxis as any)?.yAxis || newConfig.yAxis)
|
|
454
|
+
const horizontalYAxisSource = _.cloneDeep((newConfig.xAxis as any)?.xAxis || newConfig.xAxis)
|
|
455
|
+
newConfig.runtime.xAxis = {
|
|
456
|
+
...horizontalXAxisSource,
|
|
457
|
+
label: runtimeXAxisLabel ?? horizontalXAxisSource?.label
|
|
458
|
+
}
|
|
459
|
+
newConfig.runtime.yAxis = {
|
|
460
|
+
...horizontalYAxisSource,
|
|
461
|
+
label: runtimeYAxisLabel ?? horizontalYAxisSource?.label
|
|
462
|
+
}
|
|
299
463
|
newConfig.runtime.yAxis.labelOffset *= -1
|
|
300
464
|
|
|
301
465
|
newConfig.runtime.horizontal = false
|
|
@@ -306,24 +470,40 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
306
470
|
['Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType) &&
|
|
307
471
|
!convertLineToBarGraph
|
|
308
472
|
) {
|
|
309
|
-
newConfig.runtime.xAxis = newConfig.xAxis
|
|
310
|
-
newConfig.runtime.yAxis = newConfig.yAxis
|
|
473
|
+
newConfig.runtime.xAxis = { ...newConfig.xAxis, label: runtimeXAxisLabel ?? newConfig.xAxis.label }
|
|
474
|
+
newConfig.runtime.yAxis = { ...newConfig.yAxis, label: runtimeYAxisLabel ?? newConfig.yAxis.label }
|
|
311
475
|
newConfig.runtime.horizontal = false
|
|
312
476
|
newConfig.orientation = 'vertical'
|
|
313
477
|
} else {
|
|
314
|
-
newConfig.runtime.xAxis = newConfig.xAxis
|
|
315
|
-
newConfig.runtime.yAxis = newConfig.yAxis
|
|
478
|
+
newConfig.runtime.xAxis = { ...newConfig.xAxis, label: runtimeXAxisLabel ?? newConfig.xAxis.label }
|
|
479
|
+
newConfig.runtime.yAxis = { ...newConfig.yAxis, label: runtimeYAxisLabel ?? newConfig.yAxis.label }
|
|
316
480
|
newConfig.runtime.horizontal = false
|
|
317
481
|
}
|
|
318
482
|
|
|
319
483
|
newConfig.runtime.uniqueId = Date.now()
|
|
320
|
-
newConfig.runtime.editorErrorMessage =
|
|
321
|
-
newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey
|
|
322
|
-
? 'Data Key property in Y Axis section must be set for pie charts.'
|
|
323
|
-
: ''
|
|
324
484
|
|
|
325
|
-
//
|
|
326
|
-
|
|
485
|
+
// Set error messages: preserve external errors (from pattern settings, etc.) or set validation errors
|
|
486
|
+
if (shouldPreserveError) {
|
|
487
|
+
// Preserve error messages set by editor panels (e.g., pattern contrast errors)
|
|
488
|
+
newConfig.runtime.editorErrorMessage = existingErrorMessage
|
|
489
|
+
} else if (newConfig.visualizationType === 'Pie') {
|
|
490
|
+
// Check for Pie chart validation errors
|
|
491
|
+
const missingDataColumn = !newConfig.yAxis.dataKey || newConfig.yAxis.dataKey === ''
|
|
492
|
+
const missingSegmentLabels = !newConfig.xAxis.dataKey || newConfig.xAxis.dataKey === ''
|
|
493
|
+
|
|
494
|
+
if (missingDataColumn && missingSegmentLabels) {
|
|
495
|
+
newConfig.runtime.editorErrorMessage = 'Data column and Segment labels sections must be set for pie charts.'
|
|
496
|
+
} else if (missingDataColumn) {
|
|
497
|
+
newConfig.runtime.editorErrorMessage = 'Data column section must be set for pie charts.'
|
|
498
|
+
} else if (missingSegmentLabels) {
|
|
499
|
+
newConfig.runtime.editorErrorMessage = 'Segment labels section must be set for pie charts.'
|
|
500
|
+
} else {
|
|
501
|
+
newConfig.runtime.editorErrorMessage = ''
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
// No errors
|
|
505
|
+
newConfig.runtime.editorErrorMessage = ''
|
|
506
|
+
}
|
|
327
507
|
|
|
328
508
|
if (newConfig.legend.seriesHighlight?.length) {
|
|
329
509
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: newConfig.legend?.seriesHighlight })
|
|
@@ -379,11 +559,13 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
379
559
|
for (let entry of entries) {
|
|
380
560
|
let { width, height } = entry.contentRect
|
|
381
561
|
|
|
382
|
-
|
|
562
|
+
const editorIsOpen = isEditor && !!document.querySelector('.editor-panel:not(.hidden)')
|
|
563
|
+
width = editorIsOpen ? width - EDITOR_WIDTH : width
|
|
383
564
|
|
|
384
565
|
const newViewport = getViewport(width)
|
|
385
566
|
|
|
386
567
|
dispatch({ type: 'SET_VIEWPORT', payload: newViewport })
|
|
568
|
+
dispatch({ type: 'SET_VIZ_VIEWPORT', payload: newViewport })
|
|
387
569
|
|
|
388
570
|
if (entry.target.dataset.lollipop === 'true') {
|
|
389
571
|
width = width - 2.5
|
|
@@ -429,8 +611,16 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
429
611
|
} else if (newConfig.formattedData) {
|
|
430
612
|
newConfig.data = newConfig.formattedData
|
|
431
613
|
} else if (newConfig.dataDescription) {
|
|
432
|
-
|
|
433
|
-
|
|
614
|
+
// For dashboard contexts, get data from datasets if config.data is undefined
|
|
615
|
+
let dataToProcess = newConfig.data
|
|
616
|
+
if (!dataToProcess && isDashboard && datasets && newConfig.dataKey) {
|
|
617
|
+
dataToProcess = datasets[newConfig.dataKey]?.data
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (dataToProcess) {
|
|
621
|
+
newConfig.data = transform.autoStandardize(dataToProcess)
|
|
622
|
+
newConfig.data = transform.developerStandardize(newConfig.data, newConfig.dataDescription)
|
|
623
|
+
}
|
|
434
624
|
}
|
|
435
625
|
} catch (err) {
|
|
436
626
|
console.error('Error on prepareData function ', err)
|
|
@@ -468,7 +658,6 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
468
658
|
if (container && !isLoading && !_.isEmpty(config) && !coveLoadedEventRan) {
|
|
469
659
|
publish('cove_loaded', { config: config })
|
|
470
660
|
dispatch({ type: 'SET_LOADED_EVENT', payload: true })
|
|
471
|
-
publishAnalyticsEvent('chart_loaded', 'load', interactionLabel, 'chart')
|
|
472
661
|
}
|
|
473
662
|
}, [container, config, isLoading]) // eslint-disable-line
|
|
474
663
|
|
|
@@ -534,6 +723,49 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
534
723
|
}
|
|
535
724
|
}, [config, stateData]) // eslint-disable-line
|
|
536
725
|
|
|
726
|
+
// Updates runtime axis labels when config or data changes when using markup variables
|
|
727
|
+
useEffect(() => {
|
|
728
|
+
if (
|
|
729
|
+
!config?.runtime ||
|
|
730
|
+
_.isEmpty(config.runtime) ||
|
|
731
|
+
(!config.runtime.xAxis && !config.runtime.yAxis) ||
|
|
732
|
+
!config.markupVariables?.length
|
|
733
|
+
) {
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const dataSource = (stateData && stateData.length ? stateData : config.data) || []
|
|
738
|
+
const { runtimeXAxisLabel, runtimeYAxisLabel, isHorizontalVariant } = getProcessedAxisLabels(config, dataSource)
|
|
739
|
+
|
|
740
|
+
const runtimeClone = _.cloneDeep(config.runtime)
|
|
741
|
+
|
|
742
|
+
if (!runtimeClone?.xAxis || !runtimeClone?.yAxis) {
|
|
743
|
+
return
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
let shouldUpdateLabels = false
|
|
747
|
+
|
|
748
|
+
if (typeof runtimeXAxisLabel !== 'undefined' && runtimeClone.xAxis.label !== runtimeXAxisLabel) {
|
|
749
|
+
runtimeClone.xAxis = { ...runtimeClone.xAxis, label: runtimeXAxisLabel }
|
|
750
|
+
shouldUpdateLabels = true
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (typeof runtimeYAxisLabel !== 'undefined' && runtimeClone.yAxis.label !== runtimeYAxisLabel) {
|
|
754
|
+
runtimeClone.yAxis = { ...runtimeClone.yAxis, label: runtimeYAxisLabel }
|
|
755
|
+
shouldUpdateLabels = true
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (shouldUpdateLabels) {
|
|
759
|
+
runtimeClone.uniqueId = Date.now()
|
|
760
|
+
const updatedConfig = { ...config, runtime: runtimeClone } as ChartConfig
|
|
761
|
+
dispatch({ type: 'SET_CONFIG', payload: updatedConfig })
|
|
762
|
+
|
|
763
|
+
if (isEditor && !isDashboard) {
|
|
764
|
+
editorContext.setTempConfig(updatedConfig)
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}, [config, stateData, getProcessedAxisLabels, dispatch, editorContext, isEditor, isDashboard])
|
|
768
|
+
|
|
537
769
|
// Called on legend click, highlights/unhighlights the data series with the given label
|
|
538
770
|
const highlight = (label: Label): void => {
|
|
539
771
|
if (
|
|
@@ -558,7 +790,17 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
558
790
|
} catch (e) {
|
|
559
791
|
console.error('COVE:', e.message)
|
|
560
792
|
}
|
|
561
|
-
publishAnalyticsEvent(
|
|
793
|
+
publishAnalyticsEvent({
|
|
794
|
+
vizType: config?.type,
|
|
795
|
+
vizSubType: getVizSubType(config),
|
|
796
|
+
eventType: 'chart_legend_reset',
|
|
797
|
+
eventAction: 'click',
|
|
798
|
+
eventLabel: interactionLabel,
|
|
799
|
+
vizTitle: getVizTitle(config),
|
|
800
|
+
...(config.visualizationType === 'Bar' && {
|
|
801
|
+
specifics: `orientation: ${config.orientation === 'horizontal' ? 'horizontal' : 'vertical'}`
|
|
802
|
+
})
|
|
803
|
+
})
|
|
562
804
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [] })
|
|
563
805
|
}
|
|
564
806
|
|
|
@@ -639,7 +881,8 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
639
881
|
rightSuffix,
|
|
640
882
|
bottomPrefix,
|
|
641
883
|
bottomSuffix,
|
|
642
|
-
bottomAbbreviated
|
|
884
|
+
bottomAbbreviated,
|
|
885
|
+
preserveOriginalDecimals
|
|
643
886
|
}
|
|
644
887
|
} = config
|
|
645
888
|
|
|
@@ -658,32 +901,52 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
658
901
|
} else {
|
|
659
902
|
roundToPlace = roundTo ? Number(roundTo) : 0
|
|
660
903
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
904
|
+
|
|
905
|
+
// If preserveOriginalDecimals is enabled, don't force decimal places
|
|
906
|
+
if (preserveOriginalDecimals) {
|
|
907
|
+
stringFormattingOptions = {
|
|
908
|
+
useGrouping: addColRoundTo ? true : config.dataFormat.commas ? true : false
|
|
909
|
+
}
|
|
910
|
+
} else {
|
|
911
|
+
stringFormattingOptions = {
|
|
912
|
+
useGrouping: addColRoundTo ? true : config.dataFormat.commas ? true : false,
|
|
913
|
+
minimumFractionDigits: roundToPlace,
|
|
914
|
+
maximumFractionDigits: roundToPlace
|
|
915
|
+
}
|
|
665
916
|
}
|
|
666
917
|
}
|
|
667
918
|
|
|
668
919
|
if (axis === 'right') {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
920
|
+
if (preserveOriginalDecimals) {
|
|
921
|
+
stringFormattingOptions = {
|
|
922
|
+
useGrouping: config.dataFormat.rightCommas ? true : false
|
|
923
|
+
}
|
|
924
|
+
} else {
|
|
925
|
+
stringFormattingOptions = {
|
|
926
|
+
useGrouping: config.dataFormat.rightCommas ? true : false,
|
|
927
|
+
minimumFractionDigits: rightRoundTo ? Number(rightRoundTo) : 0,
|
|
928
|
+
maximumFractionDigits: rightRoundTo ? Number(rightRoundTo) : 0
|
|
929
|
+
}
|
|
673
930
|
}
|
|
674
931
|
}
|
|
675
932
|
|
|
676
933
|
const resolveBottomTickRounding = () => {
|
|
677
|
-
if (config.forestPlot
|
|
934
|
+
if (config.forestPlot?.type === 'Logarithmic' && !bottomRoundTo) return 2
|
|
678
935
|
if (Number(bottomRoundTo)) return Number(bottomRoundTo)
|
|
679
936
|
return 0
|
|
680
937
|
}
|
|
681
938
|
|
|
682
939
|
if (axis === 'bottom') {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
940
|
+
if (preserveOriginalDecimals) {
|
|
941
|
+
stringFormattingOptions = {
|
|
942
|
+
useGrouping: config.dataFormat.bottomCommas ? true : false
|
|
943
|
+
}
|
|
944
|
+
} else {
|
|
945
|
+
stringFormattingOptions = {
|
|
946
|
+
useGrouping: config.dataFormat.bottomCommas ? true : false,
|
|
947
|
+
minimumFractionDigits: resolveBottomTickRounding(),
|
|
948
|
+
maximumFractionDigits: resolveBottomTickRounding()
|
|
949
|
+
}
|
|
687
950
|
}
|
|
688
951
|
}
|
|
689
952
|
|
|
@@ -821,7 +1084,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
821
1084
|
}
|
|
822
1085
|
|
|
823
1086
|
const pivotDynamicSeries = (config: ChartConfig): TableConfig => {
|
|
824
|
-
const tableConfig: TableConfig =
|
|
1087
|
+
const tableConfig: TableConfig = cloneConfig(config)
|
|
825
1088
|
const dynamicSeries = tableConfig.series.find(series => !!series.dynamicCategory)
|
|
826
1089
|
if (dynamicSeries) {
|
|
827
1090
|
const pivot: Pivot = { columnName: dynamicSeries.dynamicCategory, valueColumns: [dynamicSeries.dataKey] }
|
|
@@ -878,9 +1141,6 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
878
1141
|
{isEditor && <EditorPanel datasets={datasets} />}
|
|
879
1142
|
<Layout.Responsive isEditor={isEditor}>
|
|
880
1143
|
{config.newViz && <Confirm updateConfig={updateConfig} config={config} />}
|
|
881
|
-
{undefined === config.newViz && isEditor && config.runtime && config.runtime?.editorErrorMessage && (
|
|
882
|
-
<Error errorMessage={config.runtime.editorErrorMessage} />
|
|
883
|
-
)}
|
|
884
1144
|
{!missingRequiredSections(config) && !config.newViz && (
|
|
885
1145
|
<div
|
|
886
1146
|
className={`cdc-chart-inner-container cove-component__content type-${makeClassName(
|
|
@@ -893,16 +1153,25 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
893
1153
|
showTitle={config.showTitle}
|
|
894
1154
|
isDashboard={isDashboard}
|
|
895
1155
|
title={title}
|
|
896
|
-
superTitle={
|
|
1156
|
+
superTitle={processedSuperTitle}
|
|
897
1157
|
classes={['chart-title', `${config.theme}`, 'cove-component__header', 'mb-3']}
|
|
898
1158
|
style={undefined}
|
|
1159
|
+
config={config}
|
|
899
1160
|
/>
|
|
900
1161
|
|
|
1162
|
+
{/* Error Message Display - Show at top before visualization wrapper */}
|
|
1163
|
+
{/* {(() => {
|
|
1164
|
+
const errorMessage = config.runtime?.editorErrorMessage
|
|
1165
|
+
const hasError = errorMessage && typeof errorMessage === 'string' && errorMessage.trim() !== ''
|
|
1166
|
+
const shouldShow = undefined === config.newViz && isEditor && config.runtime && hasError
|
|
1167
|
+
return shouldShow ? <Error errorMessage={errorMessage} /> : null
|
|
1168
|
+
})()} */}
|
|
1169
|
+
|
|
901
1170
|
{/* Visualization Wrapper */}
|
|
902
1171
|
<div className={getChartWrapperClasses().join(' ')}>
|
|
903
1172
|
{/* Intro Text/Message */}
|
|
904
|
-
{
|
|
905
|
-
<section className={`introText mb-4`}>{parse(
|
|
1173
|
+
{processedIntroText && config.visualizationType !== 'Spark Line' && (
|
|
1174
|
+
<section className={`introText mb-4`}>{parse(processedIntroText)}</section>
|
|
906
1175
|
)}
|
|
907
1176
|
|
|
908
1177
|
{/* Filters */}
|
|
@@ -936,24 +1205,42 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
936
1205
|
: 'w-75'
|
|
937
1206
|
}
|
|
938
1207
|
>
|
|
939
|
-
{/*
|
|
940
|
-
{!
|
|
941
|
-
<div
|
|
942
|
-
|
|
943
|
-
{parent => (
|
|
944
|
-
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
945
|
-
)}
|
|
946
|
-
</ParentSize>
|
|
1208
|
+
{/* Check if there is data to display */}
|
|
1209
|
+
{(!filteredData || filteredData.length === 0) && (
|
|
1210
|
+
<div className='no-data-message' style={{ padding: '2rem', textAlign: 'center', color: '#666' }}>
|
|
1211
|
+
{config.chartMessage?.noData || 'No Data Available'}
|
|
947
1212
|
</div>
|
|
948
1213
|
)}
|
|
949
1214
|
|
|
950
|
-
{
|
|
1215
|
+
{/* All charts with LinearChart */}
|
|
1216
|
+
{filteredData &&
|
|
1217
|
+
filteredData.length > 0 &&
|
|
1218
|
+
!['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
|
|
1219
|
+
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1220
|
+
<ParentSize>
|
|
1221
|
+
{parent => (
|
|
1222
|
+
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1223
|
+
)}
|
|
1224
|
+
</ParentSize>
|
|
1225
|
+
</div>
|
|
1226
|
+
)}
|
|
1227
|
+
|
|
1228
|
+
{filteredData && filteredData.length > 0 && config.visualizationType === 'Pie' && (
|
|
951
1229
|
<ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
|
|
952
|
-
{parent =>
|
|
1230
|
+
{parent => (
|
|
1231
|
+
<PieChart
|
|
1232
|
+
ref={svgRef}
|
|
1233
|
+
parentWidth={parent.width}
|
|
1234
|
+
parentHeight={parent.height}
|
|
1235
|
+
interactionLabel={interactionLabel}
|
|
1236
|
+
/>
|
|
1237
|
+
)}
|
|
953
1238
|
</ParentSize>
|
|
954
1239
|
)}
|
|
955
1240
|
{/* Line Chart */}
|
|
956
|
-
{
|
|
1241
|
+
{filteredData &&
|
|
1242
|
+
filteredData.length > 0 &&
|
|
1243
|
+
config.visualizationType === 'Line' &&
|
|
957
1244
|
(convertLineToBarGraph ? (
|
|
958
1245
|
<div ref={parentRef} style={{ width: `100%` }}>
|
|
959
1246
|
<ParentSize>
|
|
@@ -993,9 +1280,9 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
993
1280
|
dimensions={dimensions}
|
|
994
1281
|
interactionLabel={interactionLabel}
|
|
995
1282
|
/>
|
|
996
|
-
{
|
|
1283
|
+
{processedIntroText && (
|
|
997
1284
|
<section className='introText mb-4' style={{ padding: '0px 0 35px' }}>
|
|
998
|
-
{parse(
|
|
1285
|
+
{parse(processedIntroText)}
|
|
999
1286
|
</section>
|
|
1000
1287
|
)}
|
|
1001
1288
|
<div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
|
|
@@ -1032,8 +1319,8 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1032
1319
|
: link && link}
|
|
1033
1320
|
{/* Description */}
|
|
1034
1321
|
|
|
1035
|
-
{
|
|
1036
|
-
<div className={getChartSubTextClasses().join(' ')}>{parse(
|
|
1322
|
+
{processedDescription && config.visualizationType !== 'Spark Line' && (
|
|
1323
|
+
<div className={getChartSubTextClasses().join(' ')}>{parse(processedDescription)}</div>
|
|
1037
1324
|
)}
|
|
1038
1325
|
|
|
1039
1326
|
{/* buttons */}
|
|
@@ -1064,43 +1351,63 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1064
1351
|
config.table.show &&
|
|
1065
1352
|
config.visualizationType !== 'Spark Line' &&
|
|
1066
1353
|
config.visualizationType !== 'Sankey') ||
|
|
1067
|
-
(config.visualizationType === 'Sankey' && config.table.show)) &&
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
rawData={
|
|
1354
|
+
(config.visualizationType === 'Sankey' && config.table.show)) &&
|
|
1355
|
+
(() => {
|
|
1356
|
+
let dataTableConfig = pivotDynamicSeries(config)
|
|
1357
|
+
let dataTableColumns = config.columns
|
|
1358
|
+
let dataTableRuntimeData = getTableRuntimeData()
|
|
1359
|
+
let dataTableRawData =
|
|
1074
1360
|
config.visualizationType === 'Sankey'
|
|
1075
1361
|
? config?.data?.[0]?.tableData
|
|
1076
1362
|
: config.table.customTableConfig
|
|
1077
1363
|
? filterVizData(config.filters, config.data)
|
|
1078
1364
|
: config.data
|
|
1365
|
+
|
|
1366
|
+
if (config.smallMultiples?.mode) {
|
|
1367
|
+
const prepared = prepareSmallMultiplesDataTable(config, config.columns, dataTableRuntimeData)
|
|
1368
|
+
dataTableConfig = prepared.config
|
|
1369
|
+
dataTableColumns = prepared.columns
|
|
1370
|
+
dataTableRuntimeData = prepared.runtimeData
|
|
1371
|
+
if (config.smallMultiples.mode === 'by-column') {
|
|
1372
|
+
dataTableRawData = prepared.config.data
|
|
1373
|
+
}
|
|
1079
1374
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1375
|
+
|
|
1376
|
+
return (
|
|
1377
|
+
<DataTable
|
|
1378
|
+
/* changing the "key" will force the table to re-render
|
|
1379
|
+
when the default sort changes while editing */
|
|
1380
|
+
key={dataTableDefaultSortBy}
|
|
1381
|
+
config={dataTableConfig}
|
|
1382
|
+
rawData={dataTableRawData}
|
|
1383
|
+
runtimeData={dataTableRuntimeData}
|
|
1384
|
+
expandDataTable={config.table.expanded}
|
|
1385
|
+
columns={dataTableColumns}
|
|
1386
|
+
defaultSortBy={dataTableDefaultSortBy}
|
|
1387
|
+
displayGeoName={name => name}
|
|
1388
|
+
applyLegendToRow={applyLegendToRow}
|
|
1389
|
+
tableTitle={config.table.label}
|
|
1390
|
+
indexTitle={config.table.indexLabel}
|
|
1391
|
+
vizTitle={title}
|
|
1392
|
+
viewport={currentViewport}
|
|
1393
|
+
tabbingId={handleChartTabbing(config, legendId)}
|
|
1394
|
+
colorScale={colorScale}
|
|
1395
|
+
interactionLabel={interactionLabel}
|
|
1396
|
+
/>
|
|
1397
|
+
)
|
|
1398
|
+
})()}
|
|
1095
1399
|
{config?.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
1096
1400
|
{/* show pdf or image button */}
|
|
1097
|
-
{
|
|
1098
|
-
<section className='footnotes pt-2 mt-4'>{parse(
|
|
1401
|
+
{processedLegacyFootnotes && (
|
|
1402
|
+
<section className='footnotes pt-2 mt-4'>{parse(processedLegacyFootnotes)}</section>
|
|
1099
1403
|
)}
|
|
1100
1404
|
</div>
|
|
1101
1405
|
<FootnotesStandAlone
|
|
1102
1406
|
config={configObj.footnotes}
|
|
1103
1407
|
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
1408
|
+
markupVariables={config.markupVariables}
|
|
1409
|
+
enableMarkupVariables={config.enableMarkupVariables}
|
|
1410
|
+
data={config.data}
|
|
1104
1411
|
/>
|
|
1105
1412
|
</div>
|
|
1106
1413
|
)}
|
|
@@ -1119,6 +1426,9 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1119
1426
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
1120
1427
|
}
|
|
1121
1428
|
|
|
1429
|
+
// Get version-specific color palettes based on current config
|
|
1430
|
+
const colorPalettes = filterChartColorPalettes(config)
|
|
1431
|
+
|
|
1122
1432
|
const contextValues = {
|
|
1123
1433
|
...state,
|
|
1124
1434
|
capitalize,
|
|
@@ -1137,6 +1447,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1137
1447
|
handleChartTabbing,
|
|
1138
1448
|
highlight,
|
|
1139
1449
|
handleShowAll,
|
|
1450
|
+
interactionLabel,
|
|
1140
1451
|
isDashboard,
|
|
1141
1452
|
isDebug,
|
|
1142
1453
|
handleDragStateChange,
|
|
@@ -1150,7 +1461,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1150
1461
|
outerContainerRef,
|
|
1151
1462
|
parentRef,
|
|
1152
1463
|
parseDate,
|
|
1153
|
-
rawData:
|
|
1464
|
+
rawData: stateData ?? {},
|
|
1154
1465
|
setConfig,
|
|
1155
1466
|
setEditing,
|
|
1156
1467
|
setParentConfig,
|
|
@@ -1160,7 +1471,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
|
|
|
1160
1471
|
tableData: filteredData || excludedData,
|
|
1161
1472
|
transformedData: getTransformedData({ brushData: state.brushData, filteredData, excludedData, clean }),
|
|
1162
1473
|
twoColorPalette,
|
|
1163
|
-
unfilteredData:
|
|
1474
|
+
unfilteredData: stateData,
|
|
1164
1475
|
updateConfig
|
|
1165
1476
|
}
|
|
1166
1477
|
|