@cdc/chart 4.23.11 → 4.24.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 +35740 -35027
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/tall-data.json +98 -0
- package/examples/feature/forest-plot/forest-plot.json +63 -19
- package/examples/feature/forest-plot/linear.json +52 -3
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +0 -35
- package/examples/feature/line/line-chart-preliminary.json +393 -0
- package/examples/feature/regions/index.json +9 -5
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/index.html +10 -8
- package/package.json +2 -2
- package/src/CdcChart.tsx +70 -234
- package/src/ConfigContext.tsx +6 -0
- package/src/_stories/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -0
- package/src/_stories/_mock/pie_config.json +192 -0
- package/src/_stories/_mock/pie_data.json +218 -0
- package/src/_stories/_mock/preliminary_mock.json +346 -0
- package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +2 -26
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
- package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +108 -0
- package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
- package/src/components/BarChart/components/BarChart.jsx +39 -0
- package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
- package/src/components/BarChart/components/context.tsx +13 -0
- package/src/components/BarChart/index.tsx +3 -0
- package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +10 -9
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
- package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
- package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +739 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -2
- package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
- package/src/components/Forecasting/index.tsx +3 -0
- package/src/components/ForestPlot/ForestPlot.tsx +254 -0
- package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
- package/src/components/ForestPlot/index.tsx +1 -209
- package/src/components/Legend/Legend.Component.tsx +199 -0
- package/src/components/Legend/Legend.tsx +28 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +29 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.tsx +111 -23
- package/src/components/LinearChart.jsx +55 -72
- package/src/components/PairedBarChart.jsx +4 -2
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +144 -0
- package/src/components/Regions/index.tsx +3 -0
- package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
- package/src/components/ScatterPlot/index.tsx +3 -0
- package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
- package/src/components/Sparkline/index.tsx +3 -0
- package/src/data/initial-state.js +10 -8
- package/src/helpers/abbreviateNumber.ts +17 -0
- package/src/helpers/computeMarginBottom.ts +55 -0
- package/src/helpers/filterData.ts +18 -0
- package/src/helpers/generateColorsArray.ts +8 -0
- package/src/helpers/getQuartiles.ts +30 -0
- package/src/helpers/handleChartAriaLabels.ts +19 -0
- package/src/helpers/handleLineType.ts +18 -0
- package/src/helpers/lineOptions.ts +18 -0
- package/src/helpers/sort.ts +7 -0
- package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
- package/src/hooks/useBarChart.js +7 -6
- package/src/hooks/useHighlightedBars.js +1 -1
- package/src/hooks/useMinMax.ts +3 -3
- package/src/hooks/useScales.ts +19 -6
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
- package/src/scss/main.scss +0 -3
- package/src/types/ChartConfig.ts +167 -23
- package/src/types/ChartContext.ts +34 -12
- package/src/types/ForestPlot.ts +7 -14
- package/src/types/Label.ts +7 -0
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.tsx +0 -91
- package/src/components/BarChart.jsx +0 -30
- package/src/components/EditorPanel.jsx +0 -3356
- package/src/components/ForestPlot/Readme.md +0 -0
- package/src/components/Legend.jsx +0 -310
- package/src/components/LineChart/LineChart.Circle.tsx +0 -105
- package/src/scss/LinearChart.scss +0 -0
- package/src/scss/editor-panel.scss +0 -745
- package/src/scss/legend.scss +0 -206
- package/src/scss/mixins.scss +0 -0
- package/src/scss/variables.scss +0 -1
- package/src/types/ChartProps.ts +0 -7
package/src/CdcChart.tsx
CHANGED
|
@@ -12,19 +12,27 @@ import { timeParse, timeFormat } from 'd3-time-format'
|
|
|
12
12
|
import Papa from 'papaparse'
|
|
13
13
|
import parse from 'html-react-parser'
|
|
14
14
|
import 'react-tooltip/dist/react-tooltip.css'
|
|
15
|
-
import chroma from 'chroma-js'
|
|
16
15
|
|
|
17
16
|
// Primary Components
|
|
18
17
|
import ConfigContext from './ConfigContext'
|
|
19
|
-
import PieChart from './components/PieChart'
|
|
18
|
+
import PieChart from './components/PieChart/PieChart'
|
|
20
19
|
import LinearChart from './components/LinearChart'
|
|
21
20
|
|
|
22
21
|
import { colorPalettesChart as colorPalettes, twoColorPalette } from '@cdc/core/data/colorPalettes'
|
|
23
22
|
|
|
24
|
-
import SparkLine from './components/
|
|
23
|
+
import SparkLine from './components/Sparkline'
|
|
25
24
|
import Legend from './components/Legend'
|
|
26
25
|
import defaults from './data/initial-state'
|
|
27
26
|
import EditorPanel from './components/EditorPanel'
|
|
27
|
+
import { abbreviateNumber } from './helpers/abbreviateNumber'
|
|
28
|
+
import { getQuartiles } from './helpers/getQuartiles'
|
|
29
|
+
import { sortAsc, sortDesc } from './helpers/sort'
|
|
30
|
+
import { filterData } from './helpers/filterData'
|
|
31
|
+
import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
|
|
32
|
+
import { lineOptions } from './helpers/lineOptions'
|
|
33
|
+
import { handleLineType } from './helpers/handleLineType'
|
|
34
|
+
import { generateColorsArray } from './helpers/generateColorsArray'
|
|
35
|
+
import { computeMarginBottom } from './helpers/computeMarginBottom'
|
|
28
36
|
import Loading from '@cdc/core/components/Loading'
|
|
29
37
|
import Filters from '@cdc/core/components/Filters'
|
|
30
38
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
@@ -44,23 +52,19 @@ import './scss/main.scss'
|
|
|
44
52
|
import DataTable from '@cdc/core/components/DataTable'
|
|
45
53
|
import { getFileExtension } from '@cdc/core/helpers/getFileExtension'
|
|
46
54
|
import Title from '@cdc/core/components/ui/Title'
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let hoverColor = special ? colorObj.brighten(0.5).hex() : colorObj.saturate(1.3).hex()
|
|
51
|
-
|
|
52
|
-
return [color, hoverColor, colorObj.darken(0.3).hex()]
|
|
53
|
-
}
|
|
55
|
+
import { ChartConfig } from './types/ChartConfig'
|
|
56
|
+
import { Label } from './types/Label'
|
|
57
|
+
import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
54
58
|
|
|
55
59
|
export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
|
|
56
60
|
const transform = new DataTransform()
|
|
57
61
|
const [loading, setLoading] = useState(true)
|
|
58
62
|
const [colorScale, setColorScale] = useState(null)
|
|
59
|
-
const [config, setConfig] = useState<
|
|
63
|
+
const [config, setConfig] = useState<ChartConfig>({} as ChartConfig)
|
|
60
64
|
const [stateData, setStateData] = useState(config.data || [])
|
|
61
65
|
const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
|
|
62
66
|
const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
|
|
63
|
-
const [seriesHighlight, setSeriesHighlight] = useState<
|
|
67
|
+
const [seriesHighlight, setSeriesHighlight] = useState<string[]>([])
|
|
64
68
|
const [currentViewport, setCurrentViewport] = useState('lg')
|
|
65
69
|
const [dimensions, setDimensions] = useState<[number?, number?]>([])
|
|
66
70
|
const [externalFilters, setExternalFilters] = useState<any[]>()
|
|
@@ -88,41 +92,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
88
92
|
|
|
89
93
|
const handleChartTabbing = config.showSidebar ? `#legend` : config?.title ? `#dataTableSection__${config.title.replace(/\s/g, '')}` : `#dataTableSection`
|
|
90
94
|
|
|
91
|
-
const sortAsc = (a, b) => {
|
|
92
|
-
return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const sortDesc = (a, b) => {
|
|
96
|
-
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const handleChartAriaLabels = (state, testing = false) => {
|
|
100
|
-
if (testing) console.log(`handleChartAriaLabels Testing On:`, state) // eslint-disable-line
|
|
101
|
-
try {
|
|
102
|
-
if (!state.visualizationType) throw Error('handleChartAriaLabels: no visualization type found in state')
|
|
103
|
-
let ariaLabel = ''
|
|
104
|
-
|
|
105
|
-
if (state.visualizationType) {
|
|
106
|
-
ariaLabel += `${state.visualizationType} chart`
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (state.title && state.visualizationType) {
|
|
110
|
-
ariaLabel += ` with the title: ${state.title}`
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return ariaLabel
|
|
114
|
-
} catch (e) {
|
|
115
|
-
console.error('COVE: ', e.message) // eslint-disable-line
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
95
|
const reloadURLData = async () => {
|
|
120
96
|
if (config.dataUrl) {
|
|
121
97
|
const dataUrl = new URL(config.runtimeDataUrl || config.dataUrl, window.location.origin)
|
|
122
98
|
let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
123
99
|
|
|
124
100
|
let isUpdateNeeded = false
|
|
125
|
-
config.filters
|
|
101
|
+
config.filters?.forEach(filter => {
|
|
126
102
|
if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
|
|
127
103
|
qsParams[filter.queryParameter] = filter.active
|
|
128
104
|
isUpdateNeeded = true
|
|
@@ -143,8 +119,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
143
119
|
let data: any[] = []
|
|
144
120
|
|
|
145
121
|
try {
|
|
146
|
-
const ext = getFileExtension(dataUrl.
|
|
147
|
-
if ('csv' === ext) {
|
|
122
|
+
const ext = getFileExtension(dataUrl.href)
|
|
123
|
+
if ('csv' === ext || isSolrCsv(dataUrlFinal)) {
|
|
148
124
|
data = await fetch(dataUrlFinal)
|
|
149
125
|
.then(response => response.text())
|
|
150
126
|
.then(responseText => {
|
|
@@ -155,7 +131,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
155
131
|
})
|
|
156
132
|
return parsedCsv.data
|
|
157
133
|
})
|
|
158
|
-
} else if ('json' === ext) {
|
|
134
|
+
} else if ('json' === ext || isSolrJson(dataUrlFinal)) {
|
|
159
135
|
data = await fetch(dataUrlFinal).then(response => response.json())
|
|
160
136
|
} else {
|
|
161
137
|
data = []
|
|
@@ -182,44 +158,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
182
158
|
}
|
|
183
159
|
}
|
|
184
160
|
|
|
185
|
-
const handleLineType = lineType => {
|
|
186
|
-
switch (lineType) {
|
|
187
|
-
case 'dashed-sm':
|
|
188
|
-
return '5 5'
|
|
189
|
-
case 'Dashed Small':
|
|
190
|
-
return '5 5'
|
|
191
|
-
case 'dashed-md':
|
|
192
|
-
return '10 5'
|
|
193
|
-
case 'Dashed Medium':
|
|
194
|
-
return '10 5'
|
|
195
|
-
case 'dashed-lg':
|
|
196
|
-
return '15 5'
|
|
197
|
-
case 'Dashed Large':
|
|
198
|
-
return '15 5'
|
|
199
|
-
default:
|
|
200
|
-
return 0
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const lineOptions = [
|
|
205
|
-
{
|
|
206
|
-
value: 'Dashed Small',
|
|
207
|
-
key: 'dashed-sm'
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
value: 'Dashed Medium',
|
|
211
|
-
key: 'dashed-md'
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
value: 'Dashed Large',
|
|
215
|
-
key: 'dashed-lg'
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
value: 'Solid Line',
|
|
219
|
-
key: 'solid-line'
|
|
220
|
-
}
|
|
221
|
-
]
|
|
222
|
-
|
|
223
161
|
const loadConfig = async () => {
|
|
224
162
|
let response = configObj || (await (await fetch(configUrl)).json())
|
|
225
163
|
|
|
@@ -231,7 +169,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
231
169
|
if (response.dataUrl && !urlFilters) {
|
|
232
170
|
try {
|
|
233
171
|
const ext = getFileExtension(response.dataUrl)
|
|
234
|
-
if ('csv' === ext) {
|
|
172
|
+
if ('csv' === ext || isSolrCsv(response.dataUrl)) {
|
|
235
173
|
data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`)
|
|
236
174
|
.then(response => response.text())
|
|
237
175
|
.then(responseText => {
|
|
@@ -254,7 +192,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
254
192
|
})
|
|
255
193
|
}
|
|
256
194
|
|
|
257
|
-
if ('json' === ext) {
|
|
195
|
+
if ('json' === ext || isSolrJson(response.dataUrl)) {
|
|
258
196
|
data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`).then(response => response.json())
|
|
259
197
|
}
|
|
260
198
|
} catch {
|
|
@@ -359,6 +297,16 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
359
297
|
setFilteredData(currentData)
|
|
360
298
|
}
|
|
361
299
|
|
|
300
|
+
if (!['Area Chart', 'Bar', 'Line', 'Combo'].includes(newConfig.visualizationType) || newConfig.orientation === 'horizontal') {
|
|
301
|
+
newConfig.xAxis.sortDates = false
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (newConfig.xAxis.sortDates && newConfig.barThickness > 0.1) {
|
|
305
|
+
newConfig.barThickness = 0.035
|
|
306
|
+
} else if (!newConfig.xAxis.sortDates && newConfig.barThickness < 0.1) {
|
|
307
|
+
newConfig.barThickness = 0.35
|
|
308
|
+
}
|
|
309
|
+
|
|
362
310
|
//Enforce default values that need to be calculated at runtime
|
|
363
311
|
newConfig.runtime = {}
|
|
364
312
|
newConfig.runtime.seriesLabels = {}
|
|
@@ -392,37 +340,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
392
340
|
let tableData: any[] = []
|
|
393
341
|
const plots: any[] = []
|
|
394
342
|
|
|
395
|
-
/**
|
|
396
|
-
* Calculates the first quartile (q1) and third quartile (q3) from an array of integers or decimals.
|
|
397
|
-
*
|
|
398
|
-
* @param {Array} arr - The array of integers or decimals.
|
|
399
|
-
* @returns {Object} An object containing the q1 and q3 values.
|
|
400
|
-
*/
|
|
401
|
-
const getQuartiles = arr => {
|
|
402
|
-
arr.sort((a, b) => a - b)
|
|
403
|
-
|
|
404
|
-
// Calculate the index of the median value of the array
|
|
405
|
-
const medianIndex = Math.floor(arr.length / 2)
|
|
406
|
-
|
|
407
|
-
// Check if the length of the array is even or odd
|
|
408
|
-
const isEvenLength = arr.length % 2 === 0
|
|
409
|
-
|
|
410
|
-
// Split the array into two subarrays based on the median index
|
|
411
|
-
const q1Array = isEvenLength ? arr.slice(0, medianIndex) : arr.slice(0, medianIndex + 1)
|
|
412
|
-
const q3Array = isEvenLength ? arr.slice(medianIndex) : arr.slice(medianIndex + 1)
|
|
413
|
-
|
|
414
|
-
// Calculate the median of the first subarray to get the q1 value
|
|
415
|
-
const q1Index = Math.floor(q1Array.length / 2)
|
|
416
|
-
const q1 = isEvenLength ? (q1Array[q1Index - 1] + q1Array[q1Index]) / 2 : q1Array[q1Index]
|
|
417
|
-
|
|
418
|
-
// Calculate the median of the second subarray to get the q3 value
|
|
419
|
-
const q3Index = Math.floor(q3Array.length / 2)
|
|
420
|
-
const q3 = isEvenLength ? (q3Array[q3Index - 1] + q3Array[q3Index]) / 2 : q3Array[q3Index]
|
|
421
|
-
|
|
422
|
-
// Return an object containing the q1 and q3 values
|
|
423
|
-
return { q1, q3 }
|
|
424
|
-
}
|
|
425
|
-
|
|
426
343
|
// group specific statistics
|
|
427
344
|
// prevent re-renders
|
|
428
345
|
if (!groups) return
|
|
@@ -522,6 +439,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
522
439
|
if (series.type === 'Line' || series.type === 'dashed-sm' || series.type === 'dashed-md' || series.type === 'dashed-lg') {
|
|
523
440
|
newConfig.runtime.lineSeriesKeys.push(series.dataKey)
|
|
524
441
|
}
|
|
442
|
+
if (series.type === 'Combo') {
|
|
443
|
+
series.type = 'Bar'
|
|
444
|
+
}
|
|
525
445
|
})
|
|
526
446
|
}
|
|
527
447
|
|
|
@@ -544,14 +464,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
544
464
|
}
|
|
545
465
|
|
|
546
466
|
if ((newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') || ['Deviation Bar', 'Paired Bar', 'Forest Plot'].includes(newConfig.visualizationType)) {
|
|
547
|
-
newConfig.runtime.xAxis = newConfig.yAxis
|
|
548
|
-
newConfig.runtime.yAxis = newConfig.xAxis
|
|
467
|
+
newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
|
|
468
|
+
newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
|
|
549
469
|
|
|
550
|
-
|
|
551
|
-
newConfig.runtime.xAxis.type = newConfig.forestPlot.type
|
|
552
|
-
newConfig.runtime.xAxis.tickRotation = newConfig.xAxis.tickRotation
|
|
553
|
-
}
|
|
554
|
-
newConfig.runtime.horizontal = true
|
|
470
|
+
newConfig.runtime.horizontal = false
|
|
555
471
|
newConfig.orientation = 'horizontal'
|
|
556
472
|
} else if (['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType)) {
|
|
557
473
|
newConfig.runtime.xAxis = newConfig.xAxis
|
|
@@ -563,31 +479,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
563
479
|
newConfig.runtime.yAxis = newConfig.yAxis
|
|
564
480
|
newConfig.runtime.horizontal = false
|
|
565
481
|
}
|
|
482
|
+
|
|
566
483
|
newConfig.runtime.uniqueId = Date.now()
|
|
567
484
|
newConfig.runtime.editorErrorMessage = newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey ? 'Data Key property in Y Axis section must be set for pie charts.' : ''
|
|
568
485
|
|
|
569
486
|
setConfig(newConfig)
|
|
570
487
|
}
|
|
571
488
|
|
|
572
|
-
const filterData = (filters, data) => {
|
|
573
|
-
let filteredData: any[] = []
|
|
574
|
-
|
|
575
|
-
data.forEach(row => {
|
|
576
|
-
let add = true
|
|
577
|
-
filters
|
|
578
|
-
.filter(filter => filter.type !== 'url')
|
|
579
|
-
.forEach(filter => {
|
|
580
|
-
if (row[filter.columnName] != filter.active) {
|
|
581
|
-
add = false
|
|
582
|
-
}
|
|
583
|
-
})
|
|
584
|
-
|
|
585
|
-
if (add) filteredData.push(row)
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
return filteredData
|
|
589
|
-
}
|
|
590
|
-
|
|
591
489
|
// Gets filter values from dataset
|
|
592
490
|
const generateValuesForFilter = (columnName, data = this.state.data) => {
|
|
593
491
|
const values: any[] = []
|
|
@@ -726,9 +624,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
726
624
|
|
|
727
625
|
// Generates color palette to pass to child chart component
|
|
728
626
|
useEffect(() => {
|
|
729
|
-
if (stateData && config.xAxis && config.runtime
|
|
730
|
-
const configPalette =
|
|
731
|
-
const allPalettes = { ...colorPalettes, ...twoColorPalette }
|
|
627
|
+
if (stateData && config.xAxis && config.runtime?.seriesKeys) {
|
|
628
|
+
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType) ? config.twoColor.palette : config.palette
|
|
629
|
+
const allPalettes: Record<string, string[]> = { ...colorPalettes, ...twoColorPalette }
|
|
732
630
|
let palette = config.customColors || allPalettes[configPalette]
|
|
733
631
|
let numberOfKeys = config.runtime.seriesKeys.length
|
|
734
632
|
let newColorScale
|
|
@@ -756,27 +654,22 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
756
654
|
}, [config, stateData]) // eslint-disable-line
|
|
757
655
|
|
|
758
656
|
// Called on legend click, highlights/unhighlights the data series with the given label
|
|
759
|
-
const highlight = label => {
|
|
760
|
-
const newSeriesHighlight: any[] = []
|
|
761
|
-
|
|
657
|
+
const highlight = (label: Label) => {
|
|
762
658
|
// If we're highlighting all the series, reset them
|
|
763
659
|
if (seriesHighlight.length + 1 === config.runtime.seriesKeys.length && config.visualizationType !== 'Forecasting') {
|
|
764
660
|
highlightReset()
|
|
765
661
|
return
|
|
766
662
|
}
|
|
767
663
|
|
|
768
|
-
|
|
769
|
-
newSeriesHighlight.push(value)
|
|
770
|
-
})
|
|
664
|
+
const newSeriesHighlight = [...seriesHighlight]
|
|
771
665
|
|
|
772
666
|
let newHighlight = label.datum
|
|
773
667
|
if (config.runtime.seriesLabels) {
|
|
774
|
-
|
|
775
|
-
if (config.runtime.seriesLabels[
|
|
776
|
-
newHighlight =
|
|
777
|
-
break
|
|
668
|
+
config.runtime.seriesKeys.forEach(key => {
|
|
669
|
+
if (config.runtime.seriesLabels[key] === label.datum) {
|
|
670
|
+
newHighlight = key
|
|
778
671
|
}
|
|
779
|
-
}
|
|
672
|
+
})
|
|
780
673
|
}
|
|
781
674
|
|
|
782
675
|
if (newSeriesHighlight.indexOf(newHighlight) !== -1) {
|
|
@@ -832,6 +725,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
832
725
|
return timeFormat(config.runtime[section].dateDisplayFormat)(date)
|
|
833
726
|
}
|
|
834
727
|
|
|
728
|
+
const formatTooltipsDate = date => {
|
|
729
|
+
return timeFormat(config.tooltips.dateDisplayFormat)(date)
|
|
730
|
+
}
|
|
731
|
+
|
|
835
732
|
// function calculates the width of given text and its font-size
|
|
836
733
|
function getTextWidth(text, font) {
|
|
837
734
|
const canvas = document.createElement('canvas')
|
|
@@ -845,24 +742,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
845
742
|
return Math.ceil(context.measureText(text).width)
|
|
846
743
|
}
|
|
847
744
|
|
|
848
|
-
const abbreviateNumber = num => {
|
|
849
|
-
let unit = ''
|
|
850
|
-
let absNum = Math.abs(num)
|
|
851
|
-
|
|
852
|
-
if (absNum >= 1e9) {
|
|
853
|
-
unit = 'B'
|
|
854
|
-
num = num / 1e9
|
|
855
|
-
} else if (absNum >= 1e6) {
|
|
856
|
-
unit = 'M'
|
|
857
|
-
num = num / 1e6
|
|
858
|
-
} else if (absNum >= 1e3) {
|
|
859
|
-
unit = 'K'
|
|
860
|
-
num = num / 1e3
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
return num + unit
|
|
864
|
-
}
|
|
865
|
-
|
|
866
745
|
// Format numeric data based on settings in config OR from passed in settings for Additional Columns
|
|
867
746
|
// - use only for old horizontal data - newer formatNumber is in helper/formatNumber
|
|
868
747
|
// TODO: we should combine various formatNumber functions across this project.
|
|
@@ -914,11 +793,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
914
793
|
}
|
|
915
794
|
}
|
|
916
795
|
|
|
796
|
+
const resolveBottomTickRounding = () => {
|
|
797
|
+
if (config.forestPlot.type === 'Logarithmic' && !bottomRoundTo) return 2
|
|
798
|
+
if (Number(bottomRoundTo)) return Number(bottomRoundTo)
|
|
799
|
+
return 0
|
|
800
|
+
}
|
|
801
|
+
|
|
917
802
|
if (axis === 'bottom') {
|
|
918
803
|
stringFormattingOptions = {
|
|
919
804
|
useGrouping: config.dataFormat.bottomCommas ? true : false,
|
|
920
|
-
minimumFractionDigits:
|
|
921
|
-
maximumFractionDigits:
|
|
805
|
+
minimumFractionDigits: resolveBottomTickRounding(),
|
|
806
|
+
maximumFractionDigits: resolveBottomTickRounding()
|
|
922
807
|
}
|
|
923
808
|
}
|
|
924
809
|
|
|
@@ -1136,61 +1021,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1136
1021
|
return key
|
|
1137
1022
|
}
|
|
1138
1023
|
|
|
1139
|
-
const computeMarginBottom = (config: Config): string => {
|
|
1140
|
-
const isLegendBottom = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
1141
|
-
const isHorizontal = config.orientation === 'horizontal'
|
|
1142
|
-
const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
|
|
1143
|
-
const isBrush = config.brush.active
|
|
1144
|
-
const offset = 20
|
|
1145
|
-
const brushHeight = config.brush.height
|
|
1146
|
-
let bottom = 0
|
|
1147
|
-
if (!isLegendBottom && isHorizontal && !config.yAxis.label) {
|
|
1148
|
-
bottom = Number(config.xAxis.labelOffset)
|
|
1149
|
-
}
|
|
1150
|
-
if (!isLegendBottom && isHorizontal && config.yAxis.label && !config.isResponsiveTicks) {
|
|
1151
|
-
bottom = Number(config.runtime.xAxis.size) + Number(config.xAxis.labelOffset)
|
|
1152
|
-
}
|
|
1153
|
-
if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
|
|
1154
|
-
bottom = config.dynamicMarginTop + offset
|
|
1155
|
-
}
|
|
1156
|
-
if (!isLegendBottom && isHorizontal && !config.yAxis.label && config.isResponsiveTicks) {
|
|
1157
|
-
bottom = config.dynamicMarginTop ? config.dynamicMarginTop - offset : Number(config.xAxis.labelOffset) - offset
|
|
1158
|
-
}
|
|
1159
|
-
if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
|
|
1160
|
-
bottom = config.dynamicMarginTop ? config.dynamicMarginTop + offset : Number(config.xAxis.labelOffset)
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
if (!isHorizontal && !isLegendBottom && config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
|
|
1164
|
-
bottom = isBrush ? brushHeight + config.xAxis.tickWidthMax + -config.xAxis.size + config.xAxis.labelOffset + offset : config.xAxis.tickWidthMax + offset + -config.xAxis.size + config.xAxis.labelOffset
|
|
1165
|
-
}
|
|
1166
|
-
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
|
|
1167
|
-
}
|
|
1168
|
-
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
|
|
1169
|
-
bottom = isBrush ? config.xAxis.tickWidthMax + brushHeight + offset + -config.xAxis.size : 0
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
if (!isHorizontal && !isLegendBottom && config.xAxis.label && !tickRotation && !config.isResponsiveTicks) {
|
|
1173
|
-
bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset + offset : -config.xAxis.size + config.xAxis.labelOffset + offset
|
|
1174
|
-
}
|
|
1175
|
-
if (!isHorizontal && !isLegendBottom && config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
1176
|
-
bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size + offset
|
|
1177
|
-
}
|
|
1178
|
-
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
1179
|
-
bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size - offset
|
|
1180
|
-
}
|
|
1181
|
-
if (!isHorizontal && !isLegendBottom && config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
1182
|
-
bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + 25 : config.xAxis.labelOffset + -config.xAxis.size + offset
|
|
1183
|
-
}
|
|
1184
|
-
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
|
|
1185
|
-
bottom = -config.xAxis.size + offset + config.xAxis.labelOffset
|
|
1186
|
-
}
|
|
1187
|
-
if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
|
|
1188
|
-
bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset : 0
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
return `${bottom}px`
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
1024
|
// Prevent render if loading
|
|
1195
1025
|
let body = <Loading />
|
|
1196
1026
|
|
|
@@ -1214,7 +1044,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1214
1044
|
{config.filters && !externalFilters && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />}
|
|
1215
1045
|
{/* Visualization */}
|
|
1216
1046
|
{config?.introText && config.visualizationType !== 'Spark Line' && <section className='introText'>{parse(config.introText)}</section>}
|
|
1217
|
-
<div
|
|
1047
|
+
<div
|
|
1048
|
+
style={{ marginBottom: computeMarginBottom(config, legend, currentViewport) }}
|
|
1049
|
+
className={`chart-container p-relative ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')} ${isDebug ? 'debug' : ''}`}
|
|
1050
|
+
>
|
|
1218
1051
|
{/* All charts except sparkline */}
|
|
1219
1052
|
{config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
|
|
1220
1053
|
|
|
@@ -1236,7 +1069,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1236
1069
|
)}
|
|
1237
1070
|
</>
|
|
1238
1071
|
)}
|
|
1239
|
-
{!config.legend.hide && config.visualizationType !== 'Spark Line' && <Legend />}
|
|
1072
|
+
{!config.legend.hide && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Forest Plot' && <Legend />}
|
|
1240
1073
|
</div>
|
|
1241
1074
|
{/* Link */}
|
|
1242
1075
|
{isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
|
|
@@ -1254,7 +1087,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1254
1087
|
{config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
|
|
1255
1088
|
<DataTable
|
|
1256
1089
|
config={config}
|
|
1257
|
-
rawData={config.data}
|
|
1090
|
+
rawData={config.table.customTableConfig ? filterData(config.filters, config.data) : config.data}
|
|
1258
1091
|
runtimeData={transform.applySuppression(filteredData || excludedData, config.suppressedData)}
|
|
1259
1092
|
expandDataTable={config.table.expanded}
|
|
1260
1093
|
columns={config.columns}
|
|
@@ -1286,6 +1119,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1286
1119
|
|
|
1287
1120
|
const contextValues = {
|
|
1288
1121
|
capitalize,
|
|
1122
|
+
computeMarginBottom,
|
|
1289
1123
|
getXAxisData,
|
|
1290
1124
|
getYAxisData,
|
|
1291
1125
|
config,
|
|
@@ -1301,6 +1135,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1301
1135
|
currentViewport,
|
|
1302
1136
|
parseDate,
|
|
1303
1137
|
formatDate,
|
|
1138
|
+
formatTooltipsDate,
|
|
1304
1139
|
formatNumber,
|
|
1305
1140
|
loading,
|
|
1306
1141
|
updateConfig,
|
|
@@ -1328,7 +1163,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1328
1163
|
isDebug,
|
|
1329
1164
|
setSharedFilter,
|
|
1330
1165
|
setSharedFilterValue,
|
|
1331
|
-
dashboardConfig
|
|
1166
|
+
dashboardConfig,
|
|
1167
|
+
debugSvg: isDebug
|
|
1332
1168
|
}
|
|
1333
1169
|
|
|
1334
1170
|
const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
|
|
3
|
+
import Chart from '../CdcChart'
|
|
4
|
+
|
|
5
|
+
import pieChartExample from './_mock/pie_config.json'
|
|
6
|
+
import pieData from './_mock/pie_data.json'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Chart> = {
|
|
9
|
+
title: 'Components/Templates/Editor/Chart',
|
|
10
|
+
component: Chart
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Chart>
|
|
14
|
+
|
|
15
|
+
export const Primary: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
config: { ...pieChartExample, data: pieData, columns: { someCol: { name: 'females', showInViz: true } } },
|
|
18
|
+
isEditor: true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default meta
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import chartLinepreliminary from './_mock/preliminary_mock.json'
|
|
3
|
+
|
|
4
|
+
import Chart from '../CdcChart'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Chart> = {
|
|
7
|
+
title: 'Components/Templates/Chart/Line/Preliminary',
|
|
8
|
+
component: Chart
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof Chart>
|
|
12
|
+
|
|
13
|
+
export const Line_Chart: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
config: chartLinepreliminary
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default meta
|