@cdc/chart 4.24.10 → 4.24.12-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdcchart.js +35019 -34301
- package/examples/feature/boxplot/boxplot-data.json +88 -22
- package/examples/feature/boxplot/boxplot.json +540 -16
- package/examples/feature/boxplot/testing.csv +7 -7
- package/examples/feature/sankey/sankey-example-data.json +126 -14
- package/examples/feature/tests-date-exclusions/date-exclusions-config.json +372 -12
- package/examples/private/DEV-8850-2.json +493 -0
- package/examples/private/DEV-9822.json +574 -0
- package/examples/private/DEV-9840.json +553 -0
- package/examples/private/DEV-9850-3.json +461 -0
- package/examples/private/chart.json +1084 -0
- package/examples/private/ci_formatted.json +202 -0
- package/examples/private/ci_issue.json +3016 -0
- package/examples/private/completed.json +634 -0
- package/examples/private/dem-data-long.csv +20 -0
- package/examples/private/dem-data-long.json +36 -0
- package/examples/private/demographic_data.csv +157 -0
- package/examples/private/demographic_data.json +2654 -0
- package/examples/private/demographic_dynamic.json +443 -0
- package/examples/private/demographic_standard.json +560 -0
- package/examples/private/ehdi.json +29939 -0
- package/examples/private/test.json +493 -0
- package/index.html +10 -7
- package/package.json +2 -2
- package/src/CdcChart.tsx +132 -152
- package/src/_stories/Chart.Anchors.stories.tsx +31 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +19 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +34 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +42 -1
- package/src/_stories/Chart.stories.tsx +37 -6
- package/src/_stories/ChartAxisLabels.stories.tsx +4 -1
- package/src/_stories/ChartEditor.stories.tsx +27 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +25 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +8 -0
- package/{examples/feature/area/area-chart-date-city-temperature.json → src/_stories/_mock/area_chart_stacked.json} +125 -27
- package/src/_stories/_mock/boxplot_multiseries.json +647 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +723 -0
- package/src/_stories/_mock/dynamic_series_config.json +979 -0
- package/src/_stories/_mock/line_chart_dynamic_ci.json +493 -0
- package/src/_stories/_mock/line_chart_non_dynamic_ci.json +522 -0
- package/{examples/feature/scatterplot/scatterplot.json → src/_stories/_mock/scatterplot_mock.json} +62 -92
- package/src/_stories/_mock/short_dates.json +288 -0
- package/src/_stories/_mock/suppression_mock.json +1549 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +15 -3
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -37
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +43 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +53 -47
- package/src/components/BarChart/helpers/getBarData.ts +28 -0
- package/src/components/BarChart/helpers/index.ts +1 -2
- package/src/components/BarChart/helpers/tests/getBarData.test.ts +74 -0
- package/src/components/BoxPlot/BoxPlot.tsx +131 -0
- package/src/components/BoxPlot/helpers/index.ts +54 -0
- package/src/components/BrushChart.tsx +23 -26
- package/src/components/EditorPanel/EditorPanel.tsx +117 -139
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +51 -6
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +40 -9
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +122 -56
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -2
- package/src/components/EditorPanel/useEditorPermissions.ts +20 -2
- package/src/components/Legend/Legend.Component.tsx +11 -12
- package/src/components/Legend/Legend.tsx +16 -16
- package/src/components/Legend/helpers/getLegendClasses.ts +59 -0
- package/src/components/Legend/helpers/index.ts +2 -1
- package/src/components/Legend/tests/getLegendClasses.test.ts +115 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +1 -1
- package/src/components/LineChart/helpers.ts +49 -43
- package/src/components/LineChart/index.tsx +135 -83
- package/src/components/LinearChart.tsx +196 -181
- package/src/components/PieChart/PieChart.tsx +7 -1
- package/src/components/Sankey/components/ColumnList.tsx +19 -0
- package/src/components/Sankey/components/Sankey.tsx +479 -0
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +33 -0
- package/src/components/Sankey/index.tsx +1 -492
- package/src/components/Sankey/sankey.scss +22 -21
- package/src/components/Sankey/types/index.ts +1 -1
- package/src/components/Sankey/useSankeyAlert.tsx +60 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +20 -4
- package/src/data/initial-state.js +7 -12
- package/src/helpers/countNumOfTicks.ts +57 -0
- package/src/helpers/getQuartiles.ts +15 -18
- package/src/hooks/useMinMax.ts +44 -16
- package/src/hooks/useReduceData.ts +43 -10
- package/src/hooks/useScales.ts +90 -35
- package/src/hooks/useTooltip.tsx +59 -50
- package/src/scss/DataTable.scss +5 -0
- package/src/scss/main.scss +6 -20
- package/src/types/ChartConfig.ts +6 -19
- package/src/types/ChartContext.ts +4 -1
- package/src/types/ForestPlot.ts +8 -0
- package/src/components/BoxPlot/BoxPlot.jsx +0 -111
- package/src/hooks/useLegendClasses.ts +0 -72
package/src/CdcChart.tsx
CHANGED
|
@@ -35,7 +35,6 @@ import EditorPanel from './components/EditorPanel'
|
|
|
35
35
|
import { abbreviateNumber } from './helpers/abbreviateNumber'
|
|
36
36
|
import { handleChartTabbing } from './helpers/handleChartTabbing'
|
|
37
37
|
import { getQuartiles } from './helpers/getQuartiles'
|
|
38
|
-
import { sortAsc, sortDesc } from './helpers/sort'
|
|
39
38
|
import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
|
|
40
39
|
import { lineOptions } from './helpers/lineOptions'
|
|
41
40
|
import { handleLineType } from './helpers/handleLineType'
|
|
@@ -55,17 +54,16 @@ import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
|
55
54
|
import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
56
55
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
57
56
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
58
|
-
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
59
57
|
import { isConvertLineToBarGraph } from './helpers/isConvertLineToBarGraph'
|
|
60
|
-
import {
|
|
61
|
-
import { isLegendWrapViewport, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
58
|
+
import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
|
|
62
59
|
|
|
63
60
|
import './scss/main.scss'
|
|
64
61
|
// load both then config below determines which to use
|
|
65
62
|
import DataTable from '@cdc/core/components/DataTable'
|
|
63
|
+
import type { TableConfig } from '@cdc/core/components/DataTable/types/TableConfig'
|
|
66
64
|
import { getFileExtension } from '@cdc/core/helpers/getFileExtension'
|
|
67
65
|
import Title from '@cdc/core/components/ui/Title'
|
|
68
|
-
import { ChartConfig } from './types/ChartConfig'
|
|
66
|
+
import { AllChartsConfig, ChartConfig } from './types/ChartConfig'
|
|
69
67
|
import { Label } from './types/Label'
|
|
70
68
|
import { type ViewportSize } from './types/ChartConfig'
|
|
71
69
|
import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
@@ -74,6 +72,8 @@ import { filterVizData } from '@cdc/core/helpers/filterVizData'
|
|
|
74
72
|
import LegendWrapper from './components/LegendWrapper'
|
|
75
73
|
import _ from 'lodash'
|
|
76
74
|
import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
|
|
75
|
+
import { Runtime } from '@cdc/core/types/Runtime'
|
|
76
|
+
import { Pivot } from '@cdc/core/types/Table'
|
|
77
77
|
|
|
78
78
|
interface CdcChartProps {
|
|
79
79
|
configUrl?: string
|
|
@@ -129,14 +129,6 @@ const CdcChart = ({
|
|
|
129
129
|
})
|
|
130
130
|
|
|
131
131
|
const { description, visualizationType } = config
|
|
132
|
-
let [width] = dimensions
|
|
133
|
-
const useVertical = config.orientation === 'vertical'
|
|
134
|
-
const useMobileVertical = config.heights?.mobileVertical && ['xs', 'xxs'].includes(currentViewport)
|
|
135
|
-
const responsiveVertical = useMobileVertical ? 'mobileVertical' : 'vertical'
|
|
136
|
-
const renderedOrientation = useVertical ? responsiveVertical : 'horizontal'
|
|
137
|
-
let height = config.aspectRatio ? width * config.aspectRatio : config?.heights?.[renderedOrientation]
|
|
138
|
-
if (config.visualizationType === 'Pie') height = config?.heights?.[renderedOrientation]
|
|
139
|
-
height = height + Number(config.orientation === 'horizontal' ? config.yAxis.size : config?.xAxis?.size) + 45
|
|
140
132
|
|
|
141
133
|
const legendRef = useRef(null)
|
|
142
134
|
const parentRef = useRef(null)
|
|
@@ -300,9 +292,6 @@ const CdcChart = ({
|
|
|
300
292
|
}
|
|
301
293
|
let newConfig = { ...defaults, ...response }
|
|
302
294
|
|
|
303
|
-
if (newConfig.visualizationType === 'Box Plot') {
|
|
304
|
-
newConfig.legend.hide = true
|
|
305
|
-
}
|
|
306
295
|
if (undefined === newConfig.table.show) newConfig.table.show = !isDashboard
|
|
307
296
|
|
|
308
297
|
newConfig.series.forEach(series => {
|
|
@@ -321,7 +310,7 @@ const CdcChart = ({
|
|
|
321
310
|
updateConfig(processedConfig, data)
|
|
322
311
|
}
|
|
323
312
|
|
|
324
|
-
const updateConfig = (_config, dataOverride?: any[]) => {
|
|
313
|
+
const updateConfig = (_config: AllChartsConfig, dataOverride?: any[]) => {
|
|
325
314
|
const newConfig = _.cloneDeep(_config)
|
|
326
315
|
let data = dataOverride || stateData
|
|
327
316
|
|
|
@@ -384,15 +373,15 @@ const CdcChart = ({
|
|
|
384
373
|
}
|
|
385
374
|
|
|
386
375
|
//Enforce default values that need to be calculated at runtime
|
|
387
|
-
newConfig.runtime = {}
|
|
388
|
-
newConfig.runtime.series = newConfig.dynamicSeries ? [] : newConfig.series
|
|
376
|
+
newConfig.runtime = {} as Runtime
|
|
377
|
+
newConfig.runtime.series = newConfig.dynamicSeries ? [] : _.cloneDeep(newConfig.series)
|
|
389
378
|
newConfig.runtime.seriesLabels = {}
|
|
390
379
|
newConfig.runtime.seriesLabelsAll = []
|
|
391
380
|
newConfig.runtime.originalXAxis = newConfig.xAxis
|
|
392
381
|
|
|
393
382
|
if (newConfig.dynamicSeries) {
|
|
394
383
|
let finalData = dataOverride || newConfig.formattedData || newConfig.data
|
|
395
|
-
if (finalData
|
|
384
|
+
if (finalData?.length) {
|
|
396
385
|
Object.keys(finalData[0]).forEach(seriesKey => {
|
|
397
386
|
if (
|
|
398
387
|
seriesKey !== newConfig.xAxis.dataKey &&
|
|
@@ -414,113 +403,104 @@ const CdcChart = ({
|
|
|
414
403
|
newConfig.runtime.seriesKeys = (dataOverride || data).map(d => d[newConfig.xAxis.dataKey])
|
|
415
404
|
newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
|
|
416
405
|
} else {
|
|
417
|
-
newConfig.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
406
|
+
const finalData = dataOverride || newConfig.formattedData || newConfig.data
|
|
407
|
+
newConfig.runtime.seriesKeys = (newConfig.runtime.series || []).flatMap(series => {
|
|
408
|
+
if (series.dynamicCategory) {
|
|
409
|
+
_.remove(newConfig.runtime.seriesLabelsAll, label => label === series.dataKey)
|
|
410
|
+
_.remove(newConfig.runtime.series, s => s.dataKey === series.dataKey)
|
|
411
|
+
// grab the dynamic series keys from the data
|
|
412
|
+
const seriesKeys: string[] = _.uniq(finalData.map(d => d[series.dynamicCategory]))
|
|
413
|
+
// for each of those keys perform side effects
|
|
414
|
+
seriesKeys.forEach(dataKey => {
|
|
415
|
+
newConfig.runtime.seriesLabels[dataKey] = dataKey
|
|
416
|
+
newConfig.runtime.seriesLabelsAll.push(dataKey)
|
|
417
|
+
newConfig.runtime.series.push({
|
|
418
|
+
dataKey,
|
|
419
|
+
type: series.type,
|
|
420
|
+
lineType: series.lineType,
|
|
421
|
+
originalDataKey: series.dataKey,
|
|
422
|
+
dynamicCategory: series.dynamicCategory,
|
|
423
|
+
tooltip: true
|
|
424
|
+
})
|
|
422
425
|
})
|
|
423
|
-
|
|
426
|
+
// return the series keys
|
|
427
|
+
return seriesKeys
|
|
428
|
+
} else {
|
|
429
|
+
newConfig.runtime.seriesLabels[series.dataKey] = series.name || series.label || series.dataKey
|
|
430
|
+
newConfig.runtime.seriesLabelsAll.push(series.name || series.dataKey)
|
|
431
|
+
// return the series keys
|
|
432
|
+
return [series.dataKey]
|
|
433
|
+
}
|
|
434
|
+
})
|
|
424
435
|
}
|
|
425
436
|
|
|
426
437
|
if (newConfig.visualizationType === 'Box Plot' && newConfig.series) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
let allValues = newExcludedData
|
|
431
|
-
? newExcludedData.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
432
|
-
: data.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
433
|
-
|
|
434
|
-
const uniqueArray = function (arrArg) {
|
|
435
|
-
return arrArg.filter(function (elem, pos, arr) {
|
|
436
|
-
return arr.indexOf(elem) === pos
|
|
437
|
-
})
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const groups = uniqueArray(allKeys)
|
|
441
|
-
let tableData: any[] = []
|
|
438
|
+
const combinedData = filteredData || data
|
|
439
|
+
const groups = _.uniq(_.map(combinedData, newConfig.xAxis.dataKey))
|
|
440
|
+
const seriesKeys = _.map(newConfig.series, 'dataKey')
|
|
442
441
|
const plots: any[] = []
|
|
443
442
|
|
|
444
|
-
// group specific statistics
|
|
445
|
-
// prevent re-renders
|
|
446
|
-
if (!groups) return
|
|
447
443
|
groups.forEach(g => {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
444
|
+
seriesKeys.forEach(seriesKey => {
|
|
445
|
+
try {
|
|
446
|
+
if (!g) throw new Error('No groups resolved in box plots')
|
|
447
|
+
|
|
448
|
+
// Start handle operations on combinedData
|
|
449
|
+
const { count, sortedData } = _.chain(combinedData)
|
|
450
|
+
// Filter by xAxis data key
|
|
451
|
+
.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
452
|
+
// perform multiple operations on the filtered data
|
|
453
|
+
.thru(filteredData => ({
|
|
454
|
+
count: filteredData.length,
|
|
455
|
+
sortedData: _.map(filteredData, item => Number(item[seriesKey])).sort()
|
|
456
|
+
}))
|
|
457
|
+
// get the results from the chain
|
|
458
|
+
.value()
|
|
459
|
+
|
|
460
|
+
// ! - Notice d3.quantile doesn't work here, and we had to take a custom route.
|
|
461
|
+
const quartiles = getQuartiles(sortedData)
|
|
462
|
+
|
|
463
|
+
if (!sortedData) throw new Error('boxplots dont have data yet')
|
|
464
|
+
if (!plots) throw new Error('boxplots dont have plots yet')
|
|
465
|
+
|
|
466
|
+
const q1 = quartiles.q1
|
|
467
|
+
const q3 = quartiles.q3
|
|
468
|
+
|
|
469
|
+
const iqr = q3 - q1
|
|
470
|
+
const lowerBounds = q1 - 1.5 * iqr
|
|
471
|
+
const upperBounds = q3 + 1.5 * iqr
|
|
472
|
+
const filteredData = sortedData.filter(d => d <= upperBounds)
|
|
473
|
+
const max = d3.max(filteredData)
|
|
474
|
+
plots.push({
|
|
475
|
+
columnCategory: g,
|
|
476
|
+
columnMax: max,
|
|
477
|
+
columnThirdQuartile: _.round(q3, newConfig.dataFormat.roundTo),
|
|
478
|
+
columnMedian: Number(d3.median(sortedData)).toFixed(newConfig.dataFormat.roundTo),
|
|
479
|
+
columnFirstQuartile: _.round(q1, newConfig.dataFormat.roundTo),
|
|
480
|
+
columnMin: _.min(sortedData),
|
|
481
|
+
columnCount: count,
|
|
482
|
+
columnSd: Number(d3.deviation(sortedData)).toFixed(newConfig.dataFormat.roundTo),
|
|
483
|
+
columnMean: Number(d3.mean(sortedData)).toFixed(newConfig.dataFormat.roundTo),
|
|
484
|
+
columnIqr: _.round(iqr, newConfig.dataFormat.roundTo),
|
|
485
|
+
values: sortedData,
|
|
486
|
+
columnLowerBounds: lowerBounds,
|
|
487
|
+
columnUpperBounds: upperBounds,
|
|
488
|
+
columnOutliers: _.filter(sortedData, value => value < lowerBounds || value > upperBounds),
|
|
489
|
+
columnNonOutliers: _.filter(sortedData, value => value >= lowerBounds && value <= upperBounds)
|
|
490
|
+
})
|
|
491
|
+
} catch (e) {
|
|
492
|
+
console.error('COVE: ', e.message) // eslint-disable-line
|
|
472
493
|
}
|
|
473
|
-
|
|
474
|
-
const q1 = quartiles.q1
|
|
475
|
-
const q3 = quartiles.q3
|
|
476
|
-
const iqr = q3 - q1
|
|
477
|
-
const lowerBounds = q1 - (q3 - q1) * 1.5
|
|
478
|
-
const upperBounds = q3 + (q3 - q1) * 1.5
|
|
479
|
-
|
|
480
|
-
const outliers = sortedData.filter(v => v < lowerBounds || v > upperBounds)
|
|
481
|
-
let nonOutliers = filteredDataValues
|
|
482
|
-
|
|
483
|
-
nonOutliers = nonOutliers.filter(item => !outliers.includes(item))
|
|
484
|
-
const minValue: number = d3.min<number>(filteredDataValues) || 0
|
|
485
|
-
const _colMin = d3.max<number>([minValue, q1 - 1.5 * iqr])
|
|
486
|
-
plots.push({
|
|
487
|
-
columnCategory: g,
|
|
488
|
-
columnMax: d3.min([d3.max(filteredDataValues), q1 + 1.5 * iqr]),
|
|
489
|
-
columnThirdQuartile: Number(q3).toFixed(newConfig.dataFormat.roundTo),
|
|
490
|
-
columnMedian: Number(d3.median(filteredDataValues)).toFixed(newConfig.dataFormat.roundTo),
|
|
491
|
-
columnFirstQuartile: q1.toFixed(newConfig.dataFormat.roundTo),
|
|
492
|
-
columnMin: _colMin,
|
|
493
|
-
columnTotal: filteredDataValues.reduce((partialSum, a) => partialSum + a, 0),
|
|
494
|
-
columnSd: Number(d3.deviation(filteredDataValues)).toFixed(newConfig.dataFormat.roundTo),
|
|
495
|
-
columnMean: Number(d3.mean(filteredDataValues)).toFixed(newConfig.dataFormat.roundTo),
|
|
496
|
-
columnIqr: Number(iqr).toFixed(newConfig.dataFormat.roundTo),
|
|
497
|
-
columnLowerBounds: _colMin,
|
|
498
|
-
columnUpperBounds: d3.min([d3.max(sortedData), q1 + 1.5 * iqr]),
|
|
499
|
-
columnOutliers: outliers,
|
|
500
|
-
values: filteredDataValues,
|
|
501
|
-
nonOutlierValues: nonOutliers
|
|
502
|
-
})
|
|
503
|
-
} catch (e) {
|
|
504
|
-
console.error('COVE: ', e.message) // eslint-disable-line
|
|
505
|
-
}
|
|
506
|
-
})
|
|
507
|
-
|
|
508
|
-
// make deep copy so we can remove some fields for data
|
|
509
|
-
// this appears to be the easiest option instead of running logic against the datatable cell...
|
|
510
|
-
tableData = JSON.parse(JSON.stringify(plots))
|
|
511
|
-
tableData.map(table => {
|
|
512
|
-
table.columnIqr = undefined
|
|
513
|
-
table.nonOutlierValues = undefined
|
|
514
|
-
table.columnLowerBounds = undefined
|
|
515
|
-
table.columnUpperBounds = undefined
|
|
516
|
-
return null // resolve eslint
|
|
494
|
+
})
|
|
517
495
|
})
|
|
496
|
+
// Generate a flat list of categories based on seriesKeys and groups
|
|
497
|
+
const categories =
|
|
498
|
+
seriesKeys.length > 1
|
|
499
|
+
? _.flatMap(groups, value => _.map(seriesKeys, key => `${_.capitalize(key)} - ${_.capitalize(value)}`))
|
|
500
|
+
: groups
|
|
518
501
|
|
|
519
|
-
|
|
520
|
-
newConfig.boxplot['allValues'] = allValues
|
|
521
|
-
newConfig.boxplot['categories'] = groups
|
|
502
|
+
newConfig.boxplot['categories'] = categories
|
|
522
503
|
newConfig.boxplot.plots = plots
|
|
523
|
-
newConfig.boxplot.tableData = tableData
|
|
524
504
|
}
|
|
525
505
|
|
|
526
506
|
if (newConfig.visualizationType === 'Combo' && newConfig.series) {
|
|
@@ -604,10 +584,7 @@ const CdcChart = ({
|
|
|
604
584
|
: ''
|
|
605
585
|
|
|
606
586
|
// Sankey Description box error message
|
|
607
|
-
newConfig.runtime.editorErrorMessage =
|
|
608
|
-
newConfig.visualizationType === 'Sankey' && !newConfig.description
|
|
609
|
-
? 'SUBTEXT/CITATION field is empty: A description of the Sankey Diagram data must be inputted.'
|
|
610
|
-
: ''
|
|
587
|
+
newConfig.runtime.editorErrorMessage = ''
|
|
611
588
|
|
|
612
589
|
if (newConfig.legend.seriesHighlight?.length) {
|
|
613
590
|
setSeriesHighlight(newConfig.legend?.seriesHighlight)
|
|
@@ -616,21 +593,6 @@ const CdcChart = ({
|
|
|
616
593
|
setConfig(newConfig)
|
|
617
594
|
}
|
|
618
595
|
|
|
619
|
-
// Gets filter values from dataset
|
|
620
|
-
const generateValuesForFilter = (columnName, data = this.state.data) => {
|
|
621
|
-
const values: any[] = []
|
|
622
|
-
|
|
623
|
-
data.forEach(row => {
|
|
624
|
-
const value = row[columnName]
|
|
625
|
-
//@ts-ignore
|
|
626
|
-
if (value && false === values.includes(value)) {
|
|
627
|
-
values.push(value)
|
|
628
|
-
}
|
|
629
|
-
})
|
|
630
|
-
|
|
631
|
-
return values
|
|
632
|
-
}
|
|
633
|
-
|
|
634
596
|
// Sorts data series for horizontal bar charts
|
|
635
597
|
const sortData = (a, b) => {
|
|
636
598
|
let sortKey =
|
|
@@ -653,12 +615,12 @@ const CdcChart = ({
|
|
|
653
615
|
const resizeObserver = new ResizeObserver(entries => {
|
|
654
616
|
for (let entry of entries) {
|
|
655
617
|
let { width, height } = entry.contentRect
|
|
656
|
-
|
|
657
|
-
|
|
618
|
+
const svgMarginWidth = 15
|
|
619
|
+
const editorWidth = 350
|
|
658
620
|
|
|
659
621
|
width = isEditor ? width - editorWidth : width
|
|
660
622
|
|
|
661
|
-
|
|
623
|
+
const newViewport = getViewport(width)
|
|
662
624
|
|
|
663
625
|
setCurrentViewport(newViewport)
|
|
664
626
|
|
|
@@ -865,14 +827,15 @@ const CdcChart = ({
|
|
|
865
827
|
}
|
|
866
828
|
}
|
|
867
829
|
|
|
868
|
-
const formatDate = (date,
|
|
830
|
+
const formatDate = (date, i, ticks) => {
|
|
869
831
|
let formattedDate = timeFormat(config.runtime[section].dateDisplayFormat)(date)
|
|
870
832
|
// Handle the case where all months work with '%b.' except for May
|
|
871
833
|
if (config.runtime[section].dateDisplayFormat?.includes('%b.') && formattedDate.includes('May.')) {
|
|
872
834
|
formattedDate = formattedDate.replace(/May\./g, 'May')
|
|
873
835
|
}
|
|
874
836
|
// Show years only once
|
|
875
|
-
if (config.xAxis.showYearsOnce && config.runtime[section].dateDisplayFormat?.includes('%Y') &&
|
|
837
|
+
if (config.xAxis.showYearsOnce && config.runtime[section].dateDisplayFormat?.includes('%Y') && ticks) {
|
|
838
|
+
const prevDate = ticks[i - 1] ? ticks[i - 1].value : null
|
|
876
839
|
const prevFormattedDate = timeFormat(config.runtime[section].dateDisplayFormat)(prevDate)
|
|
877
840
|
const year = formattedDate.match(/\d{4}/)
|
|
878
841
|
const prevYear = prevFormattedDate.match(/\d{4}/)
|
|
@@ -1177,7 +1140,7 @@ const CdcChart = ({
|
|
|
1177
1140
|
<h3>Finish Configuring</h3>
|
|
1178
1141
|
<p>Set all required options to the left and confirm below to display a preview of the chart.</p>
|
|
1179
1142
|
<Button
|
|
1180
|
-
className='btn'
|
|
1143
|
+
className='btn btn-primary'
|
|
1181
1144
|
style={{ margin: '1em auto' }}
|
|
1182
1145
|
disabled={missingRequiredSections()}
|
|
1183
1146
|
onClick={e => confirmDone(e)}
|
|
@@ -1234,12 +1197,31 @@ const CdcChart = ({
|
|
|
1234
1197
|
// cleaning is deleting data we need in forecasting charts.
|
|
1235
1198
|
if (!Array.isArray(data)) return []
|
|
1236
1199
|
if (config.visualizationType === 'Forecasting') return data
|
|
1200
|
+
if (config.series?.some(series => !!series.dynamicCategory)) return data
|
|
1237
1201
|
return config?.xAxis?.dataKey ? transform.cleanData(data, config.xAxis.dataKey) : data
|
|
1238
1202
|
}
|
|
1239
1203
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1204
|
+
const getTableRuntimeData = () => {
|
|
1205
|
+
if (visualizationType === 'Sankey') return config?.data?.[0]?.tableData
|
|
1206
|
+
const data = filteredData || excludedData
|
|
1207
|
+
const dynamicSeries = config.series.find(series => !!series.dynamicCategory)
|
|
1208
|
+
if (!dynamicSeries) return data
|
|
1209
|
+
const usedColumns = Object.values(config.columns)
|
|
1210
|
+
.filter(col => col.dataTable)
|
|
1211
|
+
.map(col => col.name)
|
|
1212
|
+
.concat([dynamicSeries.dynamicCategory, dynamicSeries.dataKey])
|
|
1213
|
+
if (config.xAxis?.dataKey) usedColumns.push(config.xAxis.dataKey)
|
|
1214
|
+
return data.map(d => _.pick(d, usedColumns))
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
const pivotDynamicSeries = (config: ChartConfig): TableConfig => {
|
|
1218
|
+
const tableConfig: TableConfig = _.cloneDeep(config)
|
|
1219
|
+
const dynamicSeries = tableConfig.series.find(series => !!series.dynamicCategory)
|
|
1220
|
+
if (dynamicSeries) {
|
|
1221
|
+
const pivot: Pivot = { columnName: dynamicSeries.dynamicCategory, valueColumns: [dynamicSeries.dataKey] }
|
|
1222
|
+
tableConfig.table.pivot = pivot
|
|
1223
|
+
}
|
|
1224
|
+
return tableConfig
|
|
1243
1225
|
}
|
|
1244
1226
|
|
|
1245
1227
|
// Prevent render if loading
|
|
@@ -1459,7 +1441,7 @@ const CdcChart = ({
|
|
|
1459
1441
|
config.visualizationType !== 'Sankey') ||
|
|
1460
1442
|
(config.visualizationType === 'Sankey' && config.table.show)) && (
|
|
1461
1443
|
<DataTable
|
|
1462
|
-
config={config}
|
|
1444
|
+
config={pivotDynamicSeries(config)}
|
|
1463
1445
|
rawData={
|
|
1464
1446
|
config.visualizationType === 'Sankey'
|
|
1465
1447
|
? config?.data?.[0]?.tableData
|
|
@@ -1467,15 +1449,11 @@ const CdcChart = ({
|
|
|
1467
1449
|
? filterVizData(config.filters, config.data)
|
|
1468
1450
|
: config.data
|
|
1469
1451
|
}
|
|
1470
|
-
runtimeData={
|
|
1471
|
-
config.visualizationType === 'Sankey'
|
|
1472
|
-
? config?.data?.[0]?.tableData
|
|
1473
|
-
: filteredData || excludedData
|
|
1474
|
-
}
|
|
1452
|
+
runtimeData={getTableRuntimeData()}
|
|
1475
1453
|
expandDataTable={config.table.expanded}
|
|
1476
1454
|
columns={config.columns}
|
|
1477
1455
|
displayDataAsText={displayDataAsText}
|
|
1478
|
-
displayGeoName={
|
|
1456
|
+
displayGeoName={name => name}
|
|
1479
1457
|
applyLegendToRow={applyLegendToRow}
|
|
1480
1458
|
tableTitle={config.table.label}
|
|
1481
1459
|
indexTitle={config.table.indexLabel}
|
|
@@ -1518,7 +1496,7 @@ const CdcChart = ({
|
|
|
1518
1496
|
debugSvg: isDebug,
|
|
1519
1497
|
dimensions,
|
|
1520
1498
|
dynamicLegendItems,
|
|
1521
|
-
excludedData
|
|
1499
|
+
excludedData,
|
|
1522
1500
|
formatDate,
|
|
1523
1501
|
formatNumber,
|
|
1524
1502
|
formatTooltipsDate,
|
|
@@ -1526,6 +1504,7 @@ const CdcChart = ({
|
|
|
1526
1504
|
getYAxisData,
|
|
1527
1505
|
handleChartAriaLabels,
|
|
1528
1506
|
handleLineType,
|
|
1507
|
+
handleChartTabbing,
|
|
1529
1508
|
highlight,
|
|
1530
1509
|
highlightReset,
|
|
1531
1510
|
imageId,
|
|
@@ -1537,6 +1516,7 @@ const CdcChart = ({
|
|
|
1537
1516
|
isEditor,
|
|
1538
1517
|
isNumber,
|
|
1539
1518
|
legend,
|
|
1519
|
+
legendId,
|
|
1540
1520
|
legendRef,
|
|
1541
1521
|
lineOptions,
|
|
1542
1522
|
loading,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Meta, Story } from '@storybook/react'
|
|
3
|
+
import Chart from './../CdcChart'
|
|
4
|
+
import exampleComboBarNonNumeric from './../../examples/feature/tests-date-exclusions/date-exclusions-config.json'
|
|
5
|
+
import { editConfigKeys } from '../helpers/configHelpers'
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Components/Templates/Chart/Anchors',
|
|
9
|
+
component: Chart
|
|
10
|
+
} as Meta
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof Chart>
|
|
13
|
+
|
|
14
|
+
export const Anchor_DateLinear: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
config: exampleComboBarNonNumeric,
|
|
17
|
+
isEditor: false
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Anchor_Categorical: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
config: editConfigKeys(exampleComboBarNonNumeric, [{ path: ['xAxis', 'type'], value: 'categorical' }])
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Anchor_Date_Time: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
config: editConfigKeys(exampleComboBarNonNumeric, [{ path: ['xAxis', 'type'], value: 'date-time' }])
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import Chart from '../CdcChart'
|
|
3
|
+
import scatterPlotCustomColorConfig from './_mock/scatterplot_mock.json'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Chart> = {
|
|
6
|
+
title: 'Components/Templates/Chart/Custom Colors',
|
|
7
|
+
component: Chart
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type Story = StoryObj<typeof Chart>
|
|
11
|
+
|
|
12
|
+
export const ScatterPlot: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
config: scatterPlotCustomColorConfig,
|
|
15
|
+
isEditor: false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default meta
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import DynamicSeriesConfig from './_mock/dynamic_series_config.json'
|
|
2
|
+
import DynamicSeriesBarConfig from './_mock/dynamic_series_bar_config.json'
|
|
3
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
4
|
+
import Chart from '../CdcChart'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Chart> = {
|
|
7
|
+
title: 'Components/Templates/Chart/Dynamic Series',
|
|
8
|
+
component: Chart
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof Chart>
|
|
12
|
+
|
|
13
|
+
export const Line: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
config: DynamicSeriesConfig,
|
|
16
|
+
isEditor: false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Bar_Vertical: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
config: DynamicSeriesBarConfig,
|
|
23
|
+
isEditor: false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Bar_Horizontal: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
config: { ...DynamicSeriesBarConfig, orientation: 'horizontal' },
|
|
30
|
+
isEditor: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default meta
|
|
@@ -12,12 +12,18 @@ const meta: Meta<typeof Chart> = {
|
|
|
12
12
|
|
|
13
13
|
type Story = StoryObj<typeof Chart>
|
|
14
14
|
|
|
15
|
-
export const
|
|
15
|
+
export const Legend_Gradient_Smooth: Story = {
|
|
16
16
|
args: {
|
|
17
17
|
config: chartGradientConfig
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export const Legend_Gradient_Linear_Blocks: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
config: editConfigKeys(chartGradientConfig, [{ path: ['legend', 'subStyle'], value: 'linear blocks' }])
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
export const Labels_On_Line_Legend_On_Top: Story = {
|
|
22
28
|
args: {
|
|
23
29
|
config: editConfigKeys(chartGradientConfig, [{ path: ['yAxis', 'labelsAboveGridlines'], value: true }])
|
|
@@ -30,4 +36,39 @@ export const Legend_On_Right: Story = {
|
|
|
30
36
|
}
|
|
31
37
|
}
|
|
32
38
|
|
|
39
|
+
export const Legend_On_Right_With_Box: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
config: editConfigKeys(SimplifiedLineConfig, [{ path: ['legend', 'hideBorder'], value: false }])
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const Legend_Gradient_With_box: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
config: editConfigKeys(chartGradientConfig, [{ path: ['legend', 'hideBorder'], value: false }])
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const Legend_Gradient_With_Text: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
config: editConfigKeys(chartGradientConfig, [
|
|
54
|
+
{ path: ['legend', 'title'], value: 'Title' },
|
|
55
|
+
{ path: ['legend', 'description'], value: 'Description' },
|
|
56
|
+
{ path: ['legend', 'hideBorder'], value: false }
|
|
57
|
+
])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export const Legend_Gradient_Wrapping_Label: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
config: JSON.parse(
|
|
63
|
+
JSON.stringify(
|
|
64
|
+
editConfigKeys(chartGradientConfig, [
|
|
65
|
+
{ path: ['legend', 'title'], value: 'Title' },
|
|
66
|
+
{ path: ['legend', 'description'], value: 'Description' },
|
|
67
|
+
{ path: ['legend', 'hideBorder'], value: false }
|
|
68
|
+
])
|
|
69
|
+
).replace(/Data 1/g, 'This is a long string that should wrap')
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
33
74
|
export default meta
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
-
import SuppressedConfig from './_mock/bar-chart-suppressed.json'
|
|
3
2
|
|
|
4
3
|
import Chart from '../CdcChart'
|
|
5
4
|
import lineChartTwoPointsRegressionTest from './_mock/line_chart_two_points_regression_test.json'
|
|
@@ -9,6 +8,12 @@ import forestPlot from '../../examples/feature/forest-plot/forest-plot.json'
|
|
|
9
8
|
import pairedBar from './_mock/paired-bar.json'
|
|
10
9
|
import horizontalBarConfig from './_mock/horizontal_bar.json'
|
|
11
10
|
import pieConfig from './_mock/pie_with_data.json'
|
|
11
|
+
import boxPlotConfig from './_mock/boxplot_multiseries.json'
|
|
12
|
+
import areaChartStacked from './_mock/area_chart_stacked.json'
|
|
13
|
+
import multipleLines from './_mock/short_dates.json'
|
|
14
|
+
import lineChartDynamicCI from './_mock/line_chart_dynamic_ci.json'
|
|
15
|
+
import lineChartNonDynamicCI from './_mock/line_chart_non_dynamic_ci.json'
|
|
16
|
+
import { editConfigKeys } from '../helpers/configHelpers'
|
|
12
17
|
|
|
13
18
|
const meta: Meta<typeof Chart> = {
|
|
14
19
|
title: 'Components/Templates/Chart',
|
|
@@ -17,6 +22,20 @@ const meta: Meta<typeof Chart> = {
|
|
|
17
22
|
|
|
18
23
|
type Story = StoryObj<typeof Chart>
|
|
19
24
|
|
|
25
|
+
export const line_Chart_Dynamic_Confidence_Intervals: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
config: lineChartDynamicCI,
|
|
28
|
+
isEditor: false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const line_Chart_Non_Dynamic_Confidence_Intervals: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
config: lineChartNonDynamicCI,
|
|
35
|
+
isEditor: false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
20
39
|
export const line_Chart_Two_Points_Regression_Test: Story = {
|
|
21
40
|
args: {
|
|
22
41
|
config: lineChartTwoPointsRegressionTest,
|
|
@@ -30,16 +49,15 @@ export const line_Chart_Two_Points_New_Chart: Story = {
|
|
|
30
49
|
}
|
|
31
50
|
}
|
|
32
51
|
|
|
33
|
-
export const
|
|
52
|
+
export const multiple_lines: Story = {
|
|
34
53
|
args: {
|
|
35
|
-
config:
|
|
36
|
-
isEditor: false
|
|
54
|
+
config: editConfigKeys(multipleLines, [{ path: ['tooltips', 'dateDisplayFormat'], value: '%b. %d %Y' }])
|
|
37
55
|
}
|
|
38
56
|
}
|
|
39
57
|
|
|
40
|
-
export const
|
|
58
|
+
export const Lollipop: Story = {
|
|
41
59
|
args: {
|
|
42
|
-
config:
|
|
60
|
+
config: lollipop,
|
|
43
61
|
isEditor: false
|
|
44
62
|
}
|
|
45
63
|
}
|
|
@@ -67,5 +85,18 @@ export const Paired_Bar: Story = {
|
|
|
67
85
|
config: pairedBar
|
|
68
86
|
}
|
|
69
87
|
}
|
|
88
|
+
export const BoxPlot_Multiseries: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
config: boxPlotConfig,
|
|
91
|
+
isEditor: false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const Area_Chart_stacked: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
config: areaChartStacked,
|
|
98
|
+
isEditor: false
|
|
99
|
+
}
|
|
100
|
+
}
|
|
70
101
|
|
|
71
102
|
export default meta
|