@cdc/chart 4.24.9 → 4.24.10
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/LICENSE +201 -0
- package/dist/cdcchart.js +43919 -40370
- package/index.html +1 -1
- package/package.json +2 -2
- package/src/CdcChart.tsx +129 -108
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +33 -0
- package/src/_stories/Chart.stories.tsx +28 -0
- package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +151 -0
- package/src/_stories/_mock/horizontal_bar.json +257 -0
- package/src/_stories/_mock/large_x_axis_labels.json +261 -0
- package/src/_stories/_mock/paired-bar.json +262 -0
- package/src/_stories/_mock/pie_with_data.json +255 -0
- package/src/_stories/_mock/simplified_line.json +1510 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +22 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
- package/src/components/BarChart/components/BarChart.Vertical.tsx +78 -20
- package/src/components/BarChart/helpers/index.ts +23 -4
- package/src/components/BrushChart.tsx +3 -2
- package/src/components/DeviationBar.jsx +58 -8
- package/src/components/EditorPanel/EditorPanel.tsx +63 -40
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +8 -25
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -35
- package/src/components/EditorPanel/components/panels.scss +4 -6
- package/src/components/EditorPanel/editor-panel.scss +0 -8
- package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -0
- package/src/components/ForestPlot/ForestPlot.tsx +2 -3
- package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
- package/src/components/Legend/Legend.Component.tsx +16 -16
- package/src/components/Legend/Legend.Suppression.tsx +25 -20
- package/src/components/Legend/Legend.tsx +0 -2
- package/src/components/Legend/helpers/index.ts +16 -19
- package/src/components/LegendWrapper.tsx +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +10 -0
- package/src/components/LinearChart.tsx +740 -562
- package/src/components/PairedBarChart.jsx +50 -10
- package/src/components/PieChart/PieChart.tsx +1 -6
- package/src/components/Regions/components/Regions.tsx +33 -19
- package/src/components/ZoomBrush.tsx +25 -6
- package/src/coreStyles_chart.scss +3 -0
- package/src/data/initial-state.js +6 -2
- package/src/helpers/configHelpers.ts +28 -0
- package/src/helpers/handleRankByValue.ts +15 -0
- package/src/helpers/sizeHelpers.ts +25 -0
- package/src/helpers/tests/handleRankByValue.test.ts +37 -0
- package/src/helpers/tests/sizeHelpers.test.ts +80 -0
- package/src/hooks/useColorPalette.js +10 -2
- package/src/hooks/useLegendClasses.ts +4 -0
- package/src/hooks/useScales.ts +31 -3
- package/src/hooks/useTooltip.tsx +9 -5
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +5 -4
- package/src/scss/main.scss +57 -52
- package/src/types/ChartConfig.ts +38 -16
- package/src/types/ChartContext.ts +18 -14
- package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
- package/src/_stories/ChartBrush.stories.tsx +0 -19
- package/src/components/LinearChart.jsx +0 -817
package/index.html
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
-->
|
|
50
50
|
|
|
51
51
|
<!-- GENERIC CHART TYPES -->
|
|
52
|
-
<div class="react-container" data-config="/examples/cases-year.json"></div>
|
|
52
|
+
<!-- <div class="react-container" data-config="/examples/cases-year.json"></div> -->
|
|
53
53
|
<!-- <div class="react-container" data-config="/examples/test.json"></div> -->
|
|
54
54
|
<!-- <div class="react-container" data-config="/examples/feature/line/line-chart.json"></div> -->
|
|
55
55
|
<!-- <div class="react-container" data-config="/examples/dev-8332.json"></div> -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/chart",
|
|
3
|
-
"version": "4.24.
|
|
3
|
+
"version": "4.24.10",
|
|
4
4
|
"description": "React component for visualizing tabular data in various types of charts",
|
|
5
5
|
"moduleName": "CdcChart",
|
|
6
6
|
"main": "dist/cdcchart",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"react": "^18.2.0",
|
|
62
62
|
"react-dom": "^18.2.0"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "a4d88d1bc91f596e1b0307d8e25c57ad8c668b75",
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/d3-sankey": "^0.12.4",
|
|
67
67
|
"resize-observer-polyfill": "^1.5.1"
|
package/src/CdcChart.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback, useRef, useId } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef, useId, useMemo } from 'react'
|
|
2
2
|
|
|
3
3
|
// IE11
|
|
4
4
|
import ResizeObserver from 'resize-observer-polyfill'
|
|
@@ -9,6 +9,7 @@ import Button from '@cdc/core/components/elements/Button'
|
|
|
9
9
|
|
|
10
10
|
//types
|
|
11
11
|
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
12
|
+
import { type DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
|
|
12
13
|
|
|
13
14
|
// External Libraries
|
|
14
15
|
import { scaleOrdinal } from '@visx/scale'
|
|
@@ -38,6 +39,7 @@ import { sortAsc, sortDesc } from './helpers/sort'
|
|
|
38
39
|
import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
|
|
39
40
|
import { lineOptions } from './helpers/lineOptions'
|
|
40
41
|
import { handleLineType } from './helpers/handleLineType'
|
|
42
|
+
import { handleRankByValue } from './helpers/handleRankByValue'
|
|
41
43
|
import { generateColorsArray } from './helpers/generateColorsArray'
|
|
42
44
|
import Loading from '@cdc/core/components/Loading'
|
|
43
45
|
import Filters from '@cdc/core/components/Filters'
|
|
@@ -45,7 +47,6 @@ import MediaControls from '@cdc/core/components/MediaControls'
|
|
|
45
47
|
import Annotation from './components/Annotations'
|
|
46
48
|
|
|
47
49
|
// Helpers
|
|
48
|
-
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
49
50
|
import { publish, subscribe, unsubscribe } from '@cdc/core/helpers/events'
|
|
50
51
|
import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
|
|
51
52
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
@@ -56,6 +57,8 @@ import isNumber from '@cdc/core/helpers/isNumber'
|
|
|
56
57
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
57
58
|
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
58
59
|
import { isConvertLineToBarGraph } from './helpers/isConvertLineToBarGraph'
|
|
60
|
+
import { calcInitialHeight } from './helpers/sizeHelpers'
|
|
61
|
+
import { isLegendWrapViewport, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
59
62
|
|
|
60
63
|
import './scss/main.scss'
|
|
61
64
|
// load both then config below determines which to use
|
|
@@ -69,8 +72,24 @@ import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
|
69
72
|
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
70
73
|
import { filterVizData } from '@cdc/core/helpers/filterVizData'
|
|
71
74
|
import LegendWrapper from './components/LegendWrapper'
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
import _ from 'lodash'
|
|
76
|
+
import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
|
|
77
|
+
|
|
78
|
+
interface CdcChartProps {
|
|
79
|
+
configUrl?: string
|
|
80
|
+
config?: ChartConfig
|
|
81
|
+
isEditor?: boolean
|
|
82
|
+
isDebug?: boolean
|
|
83
|
+
isDashboard?: boolean
|
|
84
|
+
setConfig?: (config: ChartConfig) => void
|
|
85
|
+
setEditing?: (editing: boolean) => void
|
|
86
|
+
hostname?: string
|
|
87
|
+
link?: string
|
|
88
|
+
setSharedFilter?: (filter: any) => void
|
|
89
|
+
setSharedFilterValue?: (value: any) => void
|
|
90
|
+
dashboardConfig?: DashboardConfig
|
|
91
|
+
}
|
|
92
|
+
const CdcChart = ({
|
|
74
93
|
configUrl,
|
|
75
94
|
config: configObj,
|
|
76
95
|
isEditor = false,
|
|
@@ -83,12 +102,13 @@ export default function CdcChart({
|
|
|
83
102
|
setSharedFilter,
|
|
84
103
|
setSharedFilterValue,
|
|
85
104
|
dashboardConfig
|
|
86
|
-
}) {
|
|
105
|
+
}: CdcChartProps) => {
|
|
87
106
|
const transform = new DataTransform()
|
|
88
107
|
const [loading, setLoading] = useState(true)
|
|
108
|
+
const svgRef = useRef(null)
|
|
89
109
|
const [colorScale, setColorScale] = useState(null)
|
|
90
110
|
const [config, setConfig] = useState<ChartConfig>({} as ChartConfig)
|
|
91
|
-
const [stateData, setStateData] = useState(
|
|
111
|
+
const [stateData, setStateData] = useState(_.cloneDeep(configObj?.data) || [])
|
|
92
112
|
const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
|
|
93
113
|
const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
|
|
94
114
|
const [seriesHighlight, setSeriesHighlight] = useState<string[]>(
|
|
@@ -108,6 +128,7 @@ export default function CdcChart({
|
|
|
108
128
|
isBrushing: false
|
|
109
129
|
})
|
|
110
130
|
|
|
131
|
+
const { description, visualizationType } = config
|
|
111
132
|
let [width] = dimensions
|
|
112
133
|
const useVertical = config.orientation === 'vertical'
|
|
113
134
|
const useMobileVertical = config.heights?.mobileVertical && ['xs', 'xxs'].includes(currentViewport)
|
|
@@ -115,12 +136,10 @@ export default function CdcChart({
|
|
|
115
136
|
const renderedOrientation = useVertical ? responsiveVertical : 'horizontal'
|
|
116
137
|
let height = config.aspectRatio ? width * config.aspectRatio : config?.heights?.[renderedOrientation]
|
|
117
138
|
if (config.visualizationType === 'Pie') height = config?.heights?.[renderedOrientation]
|
|
118
|
-
height = height + Number(config?.xAxis?.size) + 45
|
|
139
|
+
height = height + Number(config.orientation === 'horizontal' ? config.yAxis.size : config?.xAxis?.size) + 45
|
|
119
140
|
|
|
120
|
-
type Config = typeof config
|
|
121
|
-
let legendMemo = useRef(new Map()) // map collection
|
|
122
|
-
let innerContainerRef = useRef()
|
|
123
141
|
const legendRef = useRef(null)
|
|
142
|
+
const parentRef = useRef(null)
|
|
124
143
|
|
|
125
144
|
const handleDragStateChange = isDragging => {
|
|
126
145
|
setIsDraggingAnnotation(isDragging)
|
|
@@ -129,7 +148,7 @@ export default function CdcChart({
|
|
|
129
148
|
if (isDebug) console.log('Chart config, isEditor', config, isEditor)
|
|
130
149
|
|
|
131
150
|
// Destructure items from config for more readable JSX
|
|
132
|
-
let { legend, title
|
|
151
|
+
let { legend, title } = config
|
|
133
152
|
|
|
134
153
|
// set defaults on titles if blank AND only in editor
|
|
135
154
|
if (isEditor) {
|
|
@@ -138,7 +157,7 @@ export default function CdcChart({
|
|
|
138
157
|
|
|
139
158
|
if (config.table && (!config.table?.label || config.table?.label === '')) config.table.label = 'Data Table'
|
|
140
159
|
|
|
141
|
-
const {
|
|
160
|
+
const { lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
|
|
142
161
|
const legendId = useId()
|
|
143
162
|
|
|
144
163
|
const checkLineToBarGraph = () => {
|
|
@@ -201,6 +220,8 @@ export default function CdcChart({
|
|
|
201
220
|
|
|
202
221
|
Object.assign(data, { urlFiltered: true })
|
|
203
222
|
|
|
223
|
+
data = handleRankByValue(data, config)
|
|
224
|
+
|
|
204
225
|
updateConfig({ ...config, runtimeDataUrl: dataUrlFinal, data, formattedData: data })
|
|
205
226
|
|
|
206
227
|
if (data) {
|
|
@@ -212,7 +233,7 @@ export default function CdcChart({
|
|
|
212
233
|
}
|
|
213
234
|
|
|
214
235
|
const loadConfig = async () => {
|
|
215
|
-
|
|
236
|
+
const response = _.cloneDeep(configObj) || (await (await fetch(configUrl)).json())
|
|
216
237
|
|
|
217
238
|
// If data is included through a URL, fetch that and store
|
|
218
239
|
let data: any[] = response.data || []
|
|
@@ -263,6 +284,8 @@ export default function CdcChart({
|
|
|
263
284
|
data = transform.developerStandardize(data, response.dataDescription)
|
|
264
285
|
}
|
|
265
286
|
|
|
287
|
+
data = handleRankByValue(data, response)
|
|
288
|
+
|
|
266
289
|
if (data) {
|
|
267
290
|
setStateData(data)
|
|
268
291
|
setExcludedData(data)
|
|
@@ -276,14 +299,6 @@ export default function CdcChart({
|
|
|
276
299
|
}
|
|
277
300
|
}
|
|
278
301
|
let newConfig = { ...defaults, ...response }
|
|
279
|
-
if (newConfig.filters) {
|
|
280
|
-
newConfig.filters.forEach((filter, index) => {
|
|
281
|
-
const queryStringFilterValue = getQueryStringFilterValue(filter)
|
|
282
|
-
if (queryStringFilterValue) {
|
|
283
|
-
newConfig.filters[index].active = queryStringFilterValue
|
|
284
|
-
}
|
|
285
|
-
})
|
|
286
|
-
}
|
|
287
302
|
|
|
288
303
|
if (newConfig.visualizationType === 'Box Plot') {
|
|
289
304
|
newConfig.legend.hide = true
|
|
@@ -306,9 +321,12 @@ export default function CdcChart({
|
|
|
306
321
|
updateConfig(processedConfig, data)
|
|
307
322
|
}
|
|
308
323
|
|
|
309
|
-
const updateConfig = (
|
|
324
|
+
const updateConfig = (_config, dataOverride?: any[]) => {
|
|
325
|
+
const newConfig = _.cloneDeep(_config)
|
|
310
326
|
let data = dataOverride || stateData
|
|
311
327
|
|
|
328
|
+
data = handleRankByValue(data, newConfig)
|
|
329
|
+
|
|
312
330
|
// Deeper copy
|
|
313
331
|
Object.keys(defaults).forEach(key => {
|
|
314
332
|
if (newConfig[key] && 'object' === typeof newConfig[key] && !Array.isArray(newConfig[key])) {
|
|
@@ -356,28 +374,8 @@ export default function CdcChart({
|
|
|
356
374
|
// After data is grabbed, loop through and generate filter column values if there are any
|
|
357
375
|
let currentData: any[] = []
|
|
358
376
|
if (newConfig.filters) {
|
|
359
|
-
newConfig.filters
|
|
360
|
-
|
|
361
|
-
filter.filterStyle === 'nested-dropdown'
|
|
362
|
-
? filter.values
|
|
363
|
-
: filter.orderedValues ||
|
|
364
|
-
generateValuesForFilter(filter.columnName, newExcludedData).sort(
|
|
365
|
-
filter.order === 'desc' ? sortDesc : sortAsc
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
newConfig.filters[index].values = filterValues
|
|
369
|
-
// Initial filter should be active
|
|
370
|
-
|
|
371
|
-
const includes = (arr: any[], val: any): boolean => (arr || []).map(val => String(val)).includes(String(val))
|
|
372
|
-
newConfig.filters[index].active =
|
|
373
|
-
!newConfig.filters[index].active || !includes(filterValues, newConfig.filters[index].active)
|
|
374
|
-
? filterValues[0]
|
|
375
|
-
: newConfig.filters[index].active
|
|
376
|
-
newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle
|
|
377
|
-
? newConfig.filters[index].filterStyle
|
|
378
|
-
: 'dropdown'
|
|
379
|
-
})
|
|
380
|
-
currentData = filterVizData(newConfig.filters, newExcludedData)
|
|
377
|
+
const filtersWithValues = addValuesToFilters(newConfig.filters, newExcludedData)
|
|
378
|
+
currentData = filterVizData(filtersWithValues, newExcludedData)
|
|
381
379
|
setFilteredData(currentData)
|
|
382
380
|
}
|
|
383
381
|
|
|
@@ -398,7 +396,6 @@ export default function CdcChart({
|
|
|
398
396
|
Object.keys(finalData[0]).forEach(seriesKey => {
|
|
399
397
|
if (
|
|
400
398
|
seriesKey !== newConfig.xAxis.dataKey &&
|
|
401
|
-
finalData[0][seriesKey] &&
|
|
402
399
|
(!newConfig.filters || newConfig.filters.filter(filter => filter.columnName === seriesKey).length === 0) &&
|
|
403
400
|
(!newConfig.columns || Object.keys(newConfig.columns).indexOf(seriesKey) === -1)
|
|
404
401
|
) {
|
|
@@ -580,6 +577,7 @@ export default function CdcChart({
|
|
|
580
577
|
) {
|
|
581
578
|
newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
|
|
582
579
|
newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
|
|
580
|
+
newConfig.runtime.yAxis.labelOffset *= -1
|
|
583
581
|
|
|
584
582
|
newConfig.runtime.horizontal = false
|
|
585
583
|
newConfig.orientation = 'horizontal'
|
|
@@ -655,7 +653,7 @@ export default function CdcChart({
|
|
|
655
653
|
const resizeObserver = new ResizeObserver(entries => {
|
|
656
654
|
for (let entry of entries) {
|
|
657
655
|
let { width, height } = entry.contentRect
|
|
658
|
-
let svgMarginWidth =
|
|
656
|
+
let svgMarginWidth = 30
|
|
659
657
|
let editorWidth = 350
|
|
660
658
|
|
|
661
659
|
width = isEditor ? width - editorWidth : width
|
|
@@ -682,14 +680,14 @@ export default function CdcChart({
|
|
|
682
680
|
setContainer(node)
|
|
683
681
|
}, []) // eslint-disable-line
|
|
684
682
|
|
|
685
|
-
|
|
683
|
+
const isEmpty = obj => {
|
|
686
684
|
return Object.keys(obj).length === 0
|
|
687
685
|
}
|
|
688
686
|
|
|
689
687
|
// Load data when component first mounts
|
|
690
688
|
useEffect(() => {
|
|
691
689
|
loadConfig()
|
|
692
|
-
}, []) // eslint-disable-line
|
|
690
|
+
}, [configObj?.data?.length ? configObj.data : null]) // eslint-disable-line
|
|
693
691
|
|
|
694
692
|
useEffect(() => {
|
|
695
693
|
reloadURLData()
|
|
@@ -753,14 +751,6 @@ export default function CdcChart({
|
|
|
753
751
|
}
|
|
754
752
|
}, [externalFilters]) // eslint-disable-line
|
|
755
753
|
|
|
756
|
-
// Load data when configObj data changes
|
|
757
|
-
if (configObj) {
|
|
758
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
759
|
-
useEffect(() => {
|
|
760
|
-
loadConfig()
|
|
761
|
-
}, [configObj.data]) // eslint-disable-line
|
|
762
|
-
}
|
|
763
|
-
|
|
764
754
|
// This will set the bump chart's default scaling type to date-time
|
|
765
755
|
useEffect(() => {
|
|
766
756
|
if (['Bump Chart'].includes(config.visualizationType)) {
|
|
@@ -875,8 +865,22 @@ export default function CdcChart({
|
|
|
875
865
|
}
|
|
876
866
|
}
|
|
877
867
|
|
|
878
|
-
const formatDate = date => {
|
|
879
|
-
|
|
868
|
+
const formatDate = (date, prevDate) => {
|
|
869
|
+
let formattedDate = timeFormat(config.runtime[section].dateDisplayFormat)(date)
|
|
870
|
+
// Handle the case where all months work with '%b.' except for May
|
|
871
|
+
if (config.runtime[section].dateDisplayFormat?.includes('%b.') && formattedDate.includes('May.')) {
|
|
872
|
+
formattedDate = formattedDate.replace(/May\./g, 'May')
|
|
873
|
+
}
|
|
874
|
+
// Show years only once
|
|
875
|
+
if (config.xAxis.showYearsOnce && config.runtime[section].dateDisplayFormat?.includes('%Y') && prevDate) {
|
|
876
|
+
const prevFormattedDate = timeFormat(config.runtime[section].dateDisplayFormat)(prevDate)
|
|
877
|
+
const year = formattedDate.match(/\d{4}/)
|
|
878
|
+
const prevYear = prevFormattedDate.match(/\d{4}/)
|
|
879
|
+
if (year && prevYear && year[0] === prevYear[0]) {
|
|
880
|
+
formattedDate = formattedDate.replace(year, '')
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return formattedDate
|
|
880
884
|
}
|
|
881
885
|
|
|
882
886
|
const formatTooltipsDate = date => {
|
|
@@ -886,7 +890,16 @@ export default function CdcChart({
|
|
|
886
890
|
// Format numeric data based on settings in config OR from passed in settings for Additional Columns
|
|
887
891
|
// - use only for old horizontal data - newer formatNumber is in helper/formatNumber
|
|
888
892
|
// TODO: we should combine various formatNumber functions across this project.
|
|
889
|
-
|
|
893
|
+
// TODO suggestion: pass all options as object key/values to allow for more flexibility
|
|
894
|
+
const formatNumber = (
|
|
895
|
+
num,
|
|
896
|
+
axis,
|
|
897
|
+
shouldAbbreviate = false,
|
|
898
|
+
addColPrefix,
|
|
899
|
+
addColSuffix,
|
|
900
|
+
addColRoundTo,
|
|
901
|
+
{ index, length } = { index: null, length: null }
|
|
902
|
+
) => {
|
|
890
903
|
// if num is NaN return num
|
|
891
904
|
if (isNaN(num) || !num) return num
|
|
892
905
|
// Check if the input number is negative
|
|
@@ -913,7 +926,8 @@ export default function CdcChart({
|
|
|
913
926
|
rightSuffix,
|
|
914
927
|
bottomPrefix,
|
|
915
928
|
bottomSuffix,
|
|
916
|
-
bottomAbbreviated
|
|
929
|
+
bottomAbbreviated,
|
|
930
|
+
onlyShowTopPrefixSuffix
|
|
917
931
|
}
|
|
918
932
|
} = config
|
|
919
933
|
|
|
@@ -1006,7 +1020,9 @@ export default function CdcChart({
|
|
|
1006
1020
|
if (addColPrefix && axis === 'left') {
|
|
1007
1021
|
result = addColPrefix + result
|
|
1008
1022
|
} else {
|
|
1009
|
-
if
|
|
1023
|
+
// if onlyShowTopPrefixSuffix only show top prefix
|
|
1024
|
+
const suppressAllButLast = onlyShowTopPrefixSuffix && length - 1 !== index
|
|
1025
|
+
if (prefix && axis === 'left' && !suppressAllButLast) {
|
|
1010
1026
|
result += prefix
|
|
1011
1027
|
}
|
|
1012
1028
|
}
|
|
@@ -1025,7 +1041,7 @@ export default function CdcChart({
|
|
|
1025
1041
|
if (addColSuffix && axis === 'left') {
|
|
1026
1042
|
result += addColSuffix
|
|
1027
1043
|
} else {
|
|
1028
|
-
if (suffix && axis === 'left') {
|
|
1044
|
+
if (suffix && axis === 'left' && !onlyShowTopPrefixSuffix) {
|
|
1029
1045
|
result += suffix
|
|
1030
1046
|
}
|
|
1031
1047
|
}
|
|
@@ -1235,10 +1251,10 @@ export default function CdcChart({
|
|
|
1235
1251
|
}
|
|
1236
1252
|
|
|
1237
1253
|
const getChartWrapperClasses = () => {
|
|
1238
|
-
const isLegendOnBottom = legend?.position === 'bottom' ||
|
|
1254
|
+
const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
|
|
1239
1255
|
const classes = ['chart-container', 'p-relative']
|
|
1240
1256
|
if (legend?.position) {
|
|
1241
|
-
if (
|
|
1257
|
+
if (isLegendWrapViewport(currentViewport) && legend?.position !== 'top') {
|
|
1242
1258
|
classes.push('legend-bottom')
|
|
1243
1259
|
} else {
|
|
1244
1260
|
classes.push(`legend-${legend.position}`)
|
|
@@ -1255,7 +1271,7 @@ export default function CdcChart({
|
|
|
1255
1271
|
|
|
1256
1272
|
const getChartSubTextClasses = () => {
|
|
1257
1273
|
const classes = ['subtext ']
|
|
1258
|
-
const isLegendOnBottom = legend?.position === 'bottom' ||
|
|
1274
|
+
const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
|
|
1259
1275
|
|
|
1260
1276
|
if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
|
|
1261
1277
|
if (config.brush?.active && !isLegendOnBottom) classes.push('subtext--brush-active ')
|
|
@@ -1291,42 +1307,38 @@ export default function CdcChart({
|
|
|
1291
1307
|
classes={['chart-title', `${config.theme}`, 'cove-component__header']}
|
|
1292
1308
|
style={undefined}
|
|
1293
1309
|
/>
|
|
1294
|
-
{/* Intro Text/Message */}
|
|
1295
|
-
{config?.introText && config.visualizationType !== 'Spark Line' && (
|
|
1296
|
-
<section
|
|
1297
|
-
className={`introText legend_${config.legend.hide ? 'hidden' : 'visible'}_${config.legend.position} `}
|
|
1298
|
-
>
|
|
1299
|
-
{parse(config.introText)}
|
|
1300
|
-
</section>
|
|
1301
|
-
)}
|
|
1302
|
-
|
|
1303
|
-
{/* Filters */}
|
|
1304
|
-
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
|
|
1305
|
-
<Filters
|
|
1306
|
-
config={config}
|
|
1307
|
-
setConfig={setConfig}
|
|
1308
|
-
setFilteredData={setFilteredData}
|
|
1309
|
-
filteredData={filteredData}
|
|
1310
|
-
excludedData={excludedData}
|
|
1311
|
-
filterData={filterVizData}
|
|
1312
|
-
dimensions={dimensions}
|
|
1313
|
-
/>
|
|
1314
|
-
)}
|
|
1315
|
-
<SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
|
|
1316
|
-
{config.annotations?.length > 0 && (
|
|
1317
|
-
<SkipTo
|
|
1318
|
-
skipId={handleChartTabbing(config, legendId)}
|
|
1319
|
-
skipMessage={`Skip over annotations`}
|
|
1320
|
-
key={`skip-annotations`}
|
|
1321
|
-
/>
|
|
1322
|
-
)}
|
|
1323
1310
|
|
|
1324
1311
|
{/* Visualization Wrapper */}
|
|
1325
1312
|
<div className={getChartWrapperClasses().join(' ')}>
|
|
1313
|
+
{/* Intro Text/Message */}
|
|
1314
|
+
{config?.introText && config.visualizationType !== 'Spark Line' && (
|
|
1315
|
+
<section className={`introText `}>{parse(config.introText)}</section>
|
|
1316
|
+
)}
|
|
1317
|
+
|
|
1318
|
+
{/* Filters */}
|
|
1319
|
+
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
|
|
1320
|
+
<Filters
|
|
1321
|
+
config={config}
|
|
1322
|
+
setConfig={setConfig}
|
|
1323
|
+
setFilteredData={setFilteredData}
|
|
1324
|
+
filteredData={filteredData}
|
|
1325
|
+
excludedData={excludedData}
|
|
1326
|
+
filterData={filterVizData}
|
|
1327
|
+
dimensions={dimensions}
|
|
1328
|
+
/>
|
|
1329
|
+
)}
|
|
1330
|
+
<SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
|
|
1331
|
+
{config.annotations?.length > 0 && (
|
|
1332
|
+
<SkipTo
|
|
1333
|
+
skipId={handleChartTabbing(config, legendId)}
|
|
1334
|
+
skipMessage={`Skip over annotations`}
|
|
1335
|
+
key={`skip-annotations`}
|
|
1336
|
+
/>
|
|
1337
|
+
)}
|
|
1326
1338
|
<LegendWrapper>
|
|
1327
1339
|
<div
|
|
1328
1340
|
className={
|
|
1329
|
-
legend.hide ||
|
|
1341
|
+
legend.hide || isLegendWrapViewport(currentViewport)
|
|
1330
1342
|
? 'w-100'
|
|
1331
1343
|
: legend.position === 'bottom' || legend.position === 'top' || visualizationType === 'Sankey'
|
|
1332
1344
|
? 'w-100'
|
|
@@ -1335,30 +1347,36 @@ export default function CdcChart({
|
|
|
1335
1347
|
>
|
|
1336
1348
|
{/* All charts with LinearChart */}
|
|
1337
1349
|
{!['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
|
|
1338
|
-
<div style={{
|
|
1350
|
+
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1339
1351
|
<ParentSize>
|
|
1340
|
-
{parent =>
|
|
1352
|
+
{parent => (
|
|
1353
|
+
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1354
|
+
)}
|
|
1341
1355
|
</ParentSize>
|
|
1342
1356
|
</div>
|
|
1343
1357
|
)}
|
|
1344
1358
|
|
|
1345
1359
|
{config.visualizationType === 'Pie' && (
|
|
1346
|
-
<ParentSize className='justify-content-center d-flex' style={{
|
|
1347
|
-
{parent => <PieChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1360
|
+
<ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
|
|
1361
|
+
{parent => <PieChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1348
1362
|
</ParentSize>
|
|
1349
1363
|
)}
|
|
1350
1364
|
{/* Line Chart */}
|
|
1351
1365
|
{config.visualizationType === 'Line' &&
|
|
1352
1366
|
(checkLineToBarGraph() ? (
|
|
1353
|
-
<div style={{
|
|
1367
|
+
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1354
1368
|
<ParentSize>
|
|
1355
|
-
{parent =>
|
|
1369
|
+
{parent => (
|
|
1370
|
+
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1371
|
+
)}
|
|
1356
1372
|
</ParentSize>
|
|
1357
1373
|
</div>
|
|
1358
1374
|
) : (
|
|
1359
|
-
<div style={{
|
|
1375
|
+
<div ref={parentRef} style={{ width: `100%` }}>
|
|
1360
1376
|
<ParentSize>
|
|
1361
|
-
{parent =>
|
|
1377
|
+
{parent => (
|
|
1378
|
+
<LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
|
|
1379
|
+
)}
|
|
1362
1380
|
</ParentSize>
|
|
1363
1381
|
</div>
|
|
1364
1382
|
))}
|
|
@@ -1412,7 +1430,6 @@ export default function CdcChart({
|
|
|
1412
1430
|
{description && config.visualizationType !== 'Spark Line' && (
|
|
1413
1431
|
<div className={getChartSubTextClasses().join('')}>{parse(description)}</div>
|
|
1414
1432
|
)}
|
|
1415
|
-
{false && <Annotation.List />}
|
|
1416
1433
|
|
|
1417
1434
|
{/* buttons */}
|
|
1418
1435
|
<MediaControls.Section classes={['download-buttons']}>
|
|
@@ -1505,7 +1522,6 @@ export default function CdcChart({
|
|
|
1505
1522
|
formatDate,
|
|
1506
1523
|
formatNumber,
|
|
1507
1524
|
formatTooltipsDate,
|
|
1508
|
-
getTextWidth,
|
|
1509
1525
|
getXAxisData,
|
|
1510
1526
|
getYAxisData,
|
|
1511
1527
|
handleChartAriaLabels,
|
|
@@ -1514,19 +1530,21 @@ export default function CdcChart({
|
|
|
1514
1530
|
highlightReset,
|
|
1515
1531
|
imageId,
|
|
1516
1532
|
isDashboard,
|
|
1517
|
-
isLegendBottom: legend?.position === 'bottom' ||
|
|
1533
|
+
isLegendBottom: legend?.position === 'bottom' || isLegendWrapViewport(currentViewport),
|
|
1518
1534
|
isDebug,
|
|
1519
1535
|
isDraggingAnnotation,
|
|
1520
1536
|
handleDragStateChange,
|
|
1521
1537
|
isEditor,
|
|
1522
1538
|
isNumber,
|
|
1523
1539
|
legend,
|
|
1540
|
+
legendRef,
|
|
1524
1541
|
lineOptions,
|
|
1525
1542
|
loading,
|
|
1526
1543
|
missingRequiredSections,
|
|
1527
1544
|
outerContainerRef,
|
|
1545
|
+
parentRef,
|
|
1528
1546
|
parseDate,
|
|
1529
|
-
rawData: stateData ?? {},
|
|
1547
|
+
rawData: _.cloneDeep(stateData) ?? {},
|
|
1530
1548
|
seriesHighlight,
|
|
1531
1549
|
setBrushConfig,
|
|
1532
1550
|
setConfig,
|
|
@@ -1537,10 +1555,11 @@ export default function CdcChart({
|
|
|
1537
1555
|
setSeriesHighlight,
|
|
1538
1556
|
setSharedFilter,
|
|
1539
1557
|
setSharedFilterValue,
|
|
1558
|
+
svgRef,
|
|
1540
1559
|
tableData: filteredData || excludedData, // do not clean table data
|
|
1541
1560
|
transformedData: clean(filteredData || excludedData), // do this right before passing to components
|
|
1542
1561
|
twoColorPalette,
|
|
1543
|
-
unfilteredData: stateData,
|
|
1562
|
+
unfilteredData: _.cloneDeep(stateData),
|
|
1544
1563
|
updateConfig
|
|
1545
1564
|
}
|
|
1546
1565
|
|
|
@@ -1559,3 +1578,5 @@ export default function CdcChart({
|
|
|
1559
1578
|
</ConfigContext.Provider>
|
|
1560
1579
|
)
|
|
1561
1580
|
}
|
|
1581
|
+
|
|
1582
|
+
export default CdcChart
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import chartGradientConfig from './_mock/legend.gradient_mock.json'
|
|
3
|
+
import SimplifiedLineConfig from './_mock/simplified_line.json'
|
|
4
|
+
|
|
5
|
+
import Chart from '../CdcChart'
|
|
6
|
+
import { editConfigKeys } from '../helpers/configHelpers'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Chart> = {
|
|
9
|
+
title: 'Components/Templates/Chart/Legend',
|
|
10
|
+
component: Chart
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Chart>
|
|
14
|
+
|
|
15
|
+
export const Legend_Gradient: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
config: chartGradientConfig
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Labels_On_Line_Legend_On_Top: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
config: editConfigKeys(chartGradientConfig, [{ path: ['yAxis', 'labelsAboveGridlines'], value: true }])
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Legend_On_Right: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
config: SimplifiedLineConfig
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default meta
|
|
@@ -5,6 +5,10 @@ import Chart from '../CdcChart'
|
|
|
5
5
|
import lineChartTwoPointsRegressionTest from './_mock/line_chart_two_points_regression_test.json'
|
|
6
6
|
import lineChartTwoPointsNewChart from './_mock/line_chart_two_points_new_chart.json'
|
|
7
7
|
import lollipop from './_mock/lollipop.json'
|
|
8
|
+
import forestPlot from '../../examples/feature/forest-plot/forest-plot.json'
|
|
9
|
+
import pairedBar from './_mock/paired-bar.json'
|
|
10
|
+
import horizontalBarConfig from './_mock/horizontal_bar.json'
|
|
11
|
+
import pieConfig from './_mock/pie_with_data.json'
|
|
8
12
|
|
|
9
13
|
const meta: Meta<typeof Chart> = {
|
|
10
14
|
title: 'Components/Templates/Chart',
|
|
@@ -40,4 +44,28 @@ export const Suppression: Story = {
|
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
export const Forest_Plot: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
config: forestPlot
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const Horizontal_Bar: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
config: horizontalBarConfig
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const Pie: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
config: pieConfig
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const Paired_Bar: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
config: pairedBar
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
43
71
|
export default meta
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import SimplifiedLineConfig from './_mock/simplified_line.json'
|
|
3
|
+
|
|
4
|
+
import Chart from '../CdcChart'
|
|
5
|
+
import { editConfigKeys } from '../helpers/configHelpers'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Chart> = {
|
|
8
|
+
title: 'Components/Templates/Chart/Axis Labels',
|
|
9
|
+
component: Chart
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof Chart>
|
|
13
|
+
|
|
14
|
+
export const Abbreviated_Dates: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
config: editConfigKeys(SimplifiedLineConfig, [{ path: ['xAxis', 'showYearsOnce'], value: true }])
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default meta
|