@cdc/chart 4.23.8 → 4.23.9
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 +8961 -8920
- package/index.html +23 -5
- package/package.json +2 -2
- package/src/components/BarChart.StackedVertical.jsx +2 -0
- package/src/components/DataTable.jsx +134 -14
- package/src/components/EditorPanel.jsx +10 -2
- package/src/components/Legend.jsx +11 -3
- package/src/data/initial-state.js +4 -1
- package/src/hooks/useLegendClasses.js +14 -11
- package/src/hooks/useReduceData.js +2 -2
- package/src/hooks/useScales.js +1 -1
- package/src/hooks/useTooltip.jsx +2 -2
- package/src/scss/legend.scss +206 -0
- package/src/scss/main.scss +19 -12
- package/LICENSE +0 -201
- package/examples/private/confidence_interval_test.json +0 -248
- package/examples/private/tooltip-issue.json +0 -45275
package/index.html
CHANGED
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
-->
|
|
35
35
|
|
|
36
36
|
<!-- GENERIC CHART TYPES -->
|
|
37
|
-
<div class="react-container" data-config="/examples/private/
|
|
37
|
+
<!-- <div class="react-container" data-config="/examples/private/double.json"></div> -->
|
|
38
38
|
<!-- <div class="react-container" data-config="https://cdc.gov/poxvirus/mpox/modules/data-viz/mpx-trends_1.json"></div> -->
|
|
39
39
|
<!-- <div class="react-container" data-config="/examples/private/mpox-bar-test-aug-31.json"></div> -->
|
|
40
|
-
|
|
40
|
+
<div class="react-container" data-config="/examples/feature/area/area-chart-date-city-temperature.json"></div>
|
|
41
41
|
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-date-apple.json"></div> -->
|
|
42
42
|
<!-- <div class="react-container" data-config="/examples/feature/forest-plot/broken.json"></div> -->
|
|
43
43
|
<!-- <div class="react-container" data-config="/examples/feature/forest-plot/forest-plot.json"></div> -->
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
<!-- <div class="react-container" data-config="/examples/feature/forecasting/forecasting.json"></div> -->
|
|
47
47
|
<!-- <div class="react-container" data-config="/examples/feature/forecasting/combo-forecasting.json"></div> -->
|
|
48
48
|
<!-- <div class="react-container" data-config="/examples/feature/forecasting/effective_reproduction.json"></div> -->
|
|
49
|
-
<div class="react-container" data-config="/examples/feature/area/area-chart-date.json"></div>
|
|
49
|
+
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-date.json"></div> -->
|
|
50
50
|
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-category.json"></div> -->
|
|
51
51
|
<!-- <div class="react-container" data-config="/examples/feature/scatterplot/scatterplot.json"></div> -->
|
|
52
52
|
<!-- <div class="react-container" data-config="/examples/feature/deviation/planet-deviation-config.json"></div> -->
|
|
@@ -60,13 +60,31 @@
|
|
|
60
60
|
<!-- <div class="react-container" data-config="/examples/feature/bar/planet-chart-horizontal-example-config.json"></div> -->
|
|
61
61
|
<!-- <div class="react-container" data-config="/examples/feature/bar/example-bar-chart.json"></div> -->
|
|
62
62
|
<!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-chart-max-increase.json"></div> -->
|
|
63
|
-
<div class="react-container" data-config="/examples/feature/bar/horizontal-chart.json"></div>
|
|
63
|
+
<!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-chart.json"></div> -->
|
|
64
64
|
<!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-stacked-bar-chart.json"></div> -->
|
|
65
65
|
<!-- <div class="react-container" data-config="/examples/feature/bar/planet-chart-horizontal-example-config.json"></div> -->
|
|
66
66
|
|
|
67
67
|
<!-- SPARKLINE -->
|
|
68
68
|
<!-- <div class="react-container" data-config="/examples/feature/sparkline/example-sparkline.json"></div> -->
|
|
69
69
|
|
|
70
|
+
<!-- TESTS DATA TABLE SORTING -->
|
|
71
|
+
<!-- Bar Chart with Confidence Intervals (bottom of page) -->
|
|
72
|
+
<!-- <div class="react-container" data-config="https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/examples/Example_Bar_CI.json"></div> -->
|
|
73
|
+
<!-- TPOXX Patient Data -->
|
|
74
|
+
<!-- <div class="react-container" data-config="https://www.cdc.gov/poxvirus/mpox/modules/data-viz/tpoxx_weekly_race_eth.json"></div> -->
|
|
75
|
+
<!-- <div class="react-container" data-config="https://www.cdc.gov/poxvirus/mpox/modules/data-viz/mpx-tpoxx-age-gender.json"></div> -->
|
|
76
|
+
<!-- Non-Variola Orthopoxvirus (NVO) Laboratory Testing by Demographics -->
|
|
77
|
+
<!-- <div class="react-container" data-config="https://wwwdev-cdc.msappproxy.net/poxvirus/mpox/modules/data-viz/lab-data.json"></div> -->
|
|
78
|
+
<!-- <div class="react-container" data-config="https://wwwdev-cdc.msappproxy.net/poxvirus/mpox/modules/data-viz/lab-data-age.json"></div> -->
|
|
79
|
+
<!-- PROBLEM WITH THEIR PAGE CONFIG or CONFLICT with -->
|
|
80
|
+
<!-- <div class="react-container" data-config="/examples/private/outbreak-repro-bug.json"></div> -->
|
|
81
|
+
<!-- CORS ERROR pulling data on Covid Flu RSV page at https://wwwdev-cdc.msappproxy.net/respiratory-viruses/index.html-->
|
|
82
|
+
<!-- <div class="react-container" data-config="https://wwwdev-cdc.msappproxy.net/respiratory-viruses/modules/emergency-dept-visits_live.json"></div> -->
|
|
83
|
+
<!-- MPOX -->
|
|
84
|
+
<!-- <div class="react-container" data-config="https://www.cdc.gov/poxvirus/mpox/modules/data-viz/mpox-demographics-monthly.json"></div> -->
|
|
85
|
+
<!-- <div class="react-container" data-config="/https://www.cdc.gov/poxvirus/mpox/modules/data-viz/mpx-age-sex1.json"></div> -->
|
|
86
|
+
|
|
87
|
+
|
|
70
88
|
<!-- TESTS DATE EXCLUSIONS -->
|
|
71
89
|
<!-- <div class="react-container" data-config="/examples/feature/tests-date-exclusions/date-exclusions-config.json"></div> -->
|
|
72
90
|
<!-- <div class="react-container" data-config="/examples/feature/tests-case-rate/case-rate-example-config.json"></div> -->
|
|
@@ -112,7 +130,7 @@
|
|
|
112
130
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json"></div> -->
|
|
113
131
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
|
|
114
132
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
|
|
115
|
-
<div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart.json"></div>
|
|
133
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart.json"></div> -->
|
|
116
134
|
|
|
117
135
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
118
136
|
</body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/chart",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.9",
|
|
4
4
|
"description": "React component for visualizing tabular data in various types of charts",
|
|
5
5
|
"moduleName": "CdcChart",
|
|
6
6
|
"main": "dist/cdcchart",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"react": "^18.2.0",
|
|
59
59
|
"react-dom": "^18.2.0"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "1a737cd5d226963d5ad9c6efb8d59e4a1311141c",
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"resize-observer-polyfill": "^1.5.1"
|
|
64
64
|
}
|
|
@@ -27,6 +27,8 @@ const BarChartStackedVertical = props => {
|
|
|
27
27
|
const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
|
|
28
28
|
const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0, 'left')
|
|
29
29
|
|
|
30
|
+
if(!yAxisValue) return <></>
|
|
31
|
+
|
|
30
32
|
const style = applyRadius(barStack.index)
|
|
31
33
|
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
32
34
|
const xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
|
|
@@ -13,7 +13,10 @@ import ConfigContext from '../ConfigContext'
|
|
|
13
13
|
|
|
14
14
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const DataTable = props => {
|
|
17
|
+
// had to pass in runtimeData as prop to get the raw prop names in the inbound data (TT)
|
|
18
|
+
const { runtimeData, isDebug } = props
|
|
19
|
+
|
|
17
20
|
const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes, currentViewport } = useContext(ConfigContext)
|
|
18
21
|
|
|
19
22
|
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
@@ -150,7 +153,8 @@ export default function DataTable() {
|
|
|
150
153
|
},
|
|
151
154
|
id: `${d[config.runtime.originalXAxis.dataKey]}--${index}`,
|
|
152
155
|
sortType: 'custom',
|
|
153
|
-
canSort: true
|
|
156
|
+
canSort: true,
|
|
157
|
+
defaultCanSort: true
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
newTableColumns.push(newCol)
|
|
@@ -231,7 +235,9 @@ export default function DataTable() {
|
|
|
231
235
|
<path d='M0 0l5 5 5-5z' />
|
|
232
236
|
</svg>
|
|
233
237
|
)
|
|
234
|
-
|
|
238
|
+
const getSpecificCellData = (array, value) => {
|
|
239
|
+
return array.filter(data => JSON.stringify(data).toLowerCase().indexOf(value.toLowerCase()) !== -1)
|
|
240
|
+
}
|
|
235
241
|
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable(
|
|
236
242
|
{
|
|
237
243
|
columns: tableColumns,
|
|
@@ -240,27 +246,139 @@ export default function DataTable() {
|
|
|
240
246
|
disableSortRemove: true, // otherwise 3rd click on header removes sorting entirely
|
|
241
247
|
sortTypes: {
|
|
242
248
|
custom: (rowA, rowB, columnId) => {
|
|
249
|
+
// NOTE:
|
|
250
|
+
// 1) Main issue causing all this code:
|
|
251
|
+
// rowA and rowB are coming in with all values undefined
|
|
252
|
+
// - if it passed the values we could just use the columnId to get the correct sort value
|
|
253
|
+
// but since it's not there we have to go through a bunch of code to get it because
|
|
254
|
+
// we also do not know the Y axis data key (TT)
|
|
255
|
+
// 2). if formattedData did not truncate the strings we could get it from there
|
|
256
|
+
// but Hispanic or Latino is truncated to just Hispanic as the key
|
|
257
|
+
// and 'White, Non-Hispanic/Latino' gets truncated to remove the /Latino
|
|
258
|
+
|
|
243
259
|
// rowA.original - is the row data field name to access the value
|
|
244
|
-
// columnId = the column indicator
|
|
260
|
+
// columnId = the column indicator typically date or date--index
|
|
261
|
+
let a, b
|
|
262
|
+
if (columnId === 'series-label') {
|
|
263
|
+
// comparing strings
|
|
264
|
+
a = rowA.original
|
|
265
|
+
b = rowB.original
|
|
266
|
+
return a.localeCompare(b)
|
|
267
|
+
}
|
|
268
|
+
|
|
245
269
|
let dataKey = config.xAxis.dataKey
|
|
246
|
-
let
|
|
247
|
-
|
|
270
|
+
let columnIdIndexRemoved = columnId.split('--')[0] // have to remove index for compare
|
|
271
|
+
//get all the data from that column
|
|
272
|
+
let colData = runtimeData.filter(obj => {
|
|
273
|
+
// problem is dates can be in different formats
|
|
274
|
+
if (config.xAxis.type === 'date' && !isNaN(Date.parse(obj[dataKey])) && !isNaN(Date.parse(columnIdIndexRemoved))) {
|
|
275
|
+
// must convert to datetime number to compare
|
|
276
|
+
return parseDate(obj[dataKey]).getTime() === parseDate(columnIdIndexRemoved).getTime()
|
|
277
|
+
} else {
|
|
278
|
+
return obj[dataKey] === columnIdIndexRemoved // have to remove index
|
|
279
|
+
}
|
|
248
280
|
})
|
|
249
|
-
|
|
281
|
+
|
|
282
|
+
if (colData === undefined || colData[0] === undefined) {
|
|
250
283
|
return -1
|
|
251
284
|
}
|
|
252
|
-
// NOW we can get the sort values
|
|
253
|
-
const a = transform.cleanDataPoint(colObj[0][rowA.original]) // issue was that a was UNDEFINED therefore it CANT SORT
|
|
254
|
-
const b = transform.cleanDataPoint(colObj[0][rowB.original])
|
|
255
285
|
|
|
256
|
-
|
|
286
|
+
let rowA_cellObj = getSpecificCellData(colData, rowA.original)
|
|
287
|
+
let rowB_cellObj = getSpecificCellData(colData, rowB.original)
|
|
288
|
+
|
|
289
|
+
// - ** REMOVE any data points NOT selected in the data series ***
|
|
290
|
+
// I dont understand why not selected data series are still sent down in the data
|
|
291
|
+
// - would be better to scrub outside of here (TT)
|
|
292
|
+
let newRowA_cellObj = []
|
|
293
|
+
let newRowB_cellObj = []
|
|
294
|
+
if (config.runtime.seriesKeys) {
|
|
295
|
+
config.runtime.seriesKeys.forEach(seriesKey => {
|
|
296
|
+
if (seriesKey in rowA_cellObj[0]) newRowA_cellObj.push(rowA_cellObj[0][seriesKey])
|
|
297
|
+
if (seriesKey in rowB_cellObj[0]) newRowB_cellObj.push(rowB_cellObj[0][seriesKey])
|
|
298
|
+
})
|
|
299
|
+
// copy back over
|
|
300
|
+
rowA_cellObj[0] = newRowA_cellObj
|
|
301
|
+
rowB_cellObj[0] = newRowB_cellObj
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// REMOVE the following:
|
|
305
|
+
// - value equal to column date
|
|
306
|
+
// - value that is the .original
|
|
307
|
+
// - any data still in that's not really a number
|
|
308
|
+
let rowA_valueObj = Object.values(rowA_cellObj[0]).filter(value => value !== columnIdIndexRemoved && value !== rowA.original && !isNaN(value))
|
|
309
|
+
let rowB_valueObj = Object.values(rowB_cellObj[0]).filter(value => value !== columnIdIndexRemoved && value !== rowB.original && !isNaN(value))
|
|
310
|
+
|
|
311
|
+
// NOW we can get the sort values from the cell object
|
|
312
|
+
a = rowA_valueObj.length > 1 ? rowA_valueObj[rowA.id] : rowA_valueObj[0]
|
|
313
|
+
b = rowB_valueObj.length > 1 ? rowB_valueObj[rowB.id] : rowB_valueObj[0]
|
|
314
|
+
|
|
315
|
+
// force null and undefined to the bottom
|
|
316
|
+
a = a === null || a === undefined ? '' : transform.cleanDataPoint(a)
|
|
317
|
+
b = b === null || b === undefined ? '' : transform.cleanDataPoint(b)
|
|
318
|
+
if (a === '' || a === null) {
|
|
319
|
+
if (b === '' || b === null) {
|
|
320
|
+
return 0 // Both empty/null
|
|
321
|
+
}
|
|
322
|
+
return -1 // Sort a to an index lower than b
|
|
323
|
+
}
|
|
324
|
+
if (b === '' || b === null) {
|
|
325
|
+
if (a === '' || a === null) {
|
|
326
|
+
return 0 // Both empty/null
|
|
327
|
+
}
|
|
328
|
+
return 1 // Sort b to an index lower than a
|
|
329
|
+
}
|
|
330
|
+
// End code for forcing NULLS to bottom
|
|
331
|
+
|
|
332
|
+
// convert any strings that are actually numbers to proper data type
|
|
333
|
+
const aNum = Number(a)
|
|
334
|
+
|
|
335
|
+
if (!Number.isNaN(aNum)) {
|
|
336
|
+
a = aNum
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const bNum = Number(b)
|
|
340
|
+
|
|
341
|
+
if (!Number.isNaN(bNum)) {
|
|
342
|
+
b = bNum
|
|
343
|
+
}
|
|
344
|
+
// remove iso code prefixes
|
|
345
|
+
if (typeof a === 'string') {
|
|
346
|
+
a = a.replace('us-', '')
|
|
347
|
+
a = displayGeoName(a)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (typeof b === 'string') {
|
|
351
|
+
b = b.replace('us-', '')
|
|
352
|
+
b = displayGeoName(b)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// force any string values to lowercase
|
|
356
|
+
a = typeof a === 'string' ? a.toLowerCase() : a
|
|
357
|
+
b = typeof b === 'string' ? b.toLowerCase() : b
|
|
358
|
+
|
|
359
|
+
// When comparing a number to a string, always send string to bottom
|
|
360
|
+
if (typeof a === 'number' && typeof b === 'string') {
|
|
361
|
+
return 1
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (typeof b === 'number' && typeof a === 'string') {
|
|
257
365
|
return -1
|
|
258
366
|
}
|
|
259
|
-
|
|
260
|
-
|
|
367
|
+
|
|
368
|
+
// Return either 1 or -1 to indicate a sort priority
|
|
369
|
+
if (a > b) {
|
|
370
|
+
return 1
|
|
371
|
+
}
|
|
372
|
+
if (a < b) {
|
|
373
|
+
return -1
|
|
261
374
|
}
|
|
262
|
-
|
|
375
|
+
// returning 0, undefined or any falsey value will use subsequent sorts or
|
|
376
|
+
// the index as a tiebreaker
|
|
377
|
+
return 0
|
|
263
378
|
}
|
|
379
|
+
},
|
|
380
|
+
initialState: {
|
|
381
|
+
sortBy: [{ id: 'series-label', desc: false }] // default sort on 1st column -
|
|
264
382
|
}
|
|
265
383
|
},
|
|
266
384
|
useSortBy,
|
|
@@ -372,3 +490,5 @@ export default function DataTable() {
|
|
|
372
490
|
</ErrorBoundary>
|
|
373
491
|
)
|
|
374
492
|
}
|
|
493
|
+
|
|
494
|
+
export default DataTable
|
|
@@ -1826,6 +1826,7 @@ const EditorPanel = () => {
|
|
|
1826
1826
|
{config.visualizationType !== 'Forest Plot' && (
|
|
1827
1827
|
<Select value={config.xAxis.type} section='xAxis' fieldName='type' label='Data Type' updateField={updateField} options={config.visualizationType !== 'Scatter Plot' ? ['categorical', 'date'] : ['categorical', 'continuous', 'date']} />
|
|
1828
1828
|
)}
|
|
1829
|
+
<CheckBox value={config.xAxis.sortDates} section='xAxis' fieldName='sortDates' label='Force Date Scale (Sort Dates)' updateField={updateField} />{' '}
|
|
1829
1830
|
<Select
|
|
1830
1831
|
value={config.xAxis.dataKey || setCategoryAxis() || ''}
|
|
1831
1832
|
section='xAxis'
|
|
@@ -2678,13 +2679,20 @@ const EditorPanel = () => {
|
|
|
2678
2679
|
|
|
2679
2680
|
{config.visualizationType !== 'Box Plot' && <CheckBox value={config.legend.showLegendValuesTooltip ? true : false} section='legend' fieldName='showLegendValuesTooltip' label='Show Legend Values in Tooltip' updateField={updateField} />}
|
|
2680
2681
|
|
|
2682
|
+
{config.visualizationType === 'Line' && <CheckBox value={config.legend.lineMode} section='legend' fieldName='lineMode' label='Show Lined Style Legend' updateField={updateField} />}
|
|
2683
|
+
|
|
2681
2684
|
{config.visualizationType === 'Bar' && config.visualizationSubType === 'regular' && config.runtime.seriesKeys.length === 1 && (
|
|
2682
2685
|
<Select value={config.legend.colorCode} section='legend' fieldName='colorCode' label='Color code by category' initial='Select' updateField={updateField} options={getDataValueOptions(data)} />
|
|
2683
2686
|
)}
|
|
2684
2687
|
<Select value={config.legend.behavior} section='legend' fieldName='behavior' label='Legend Behavior (When clicked)' updateField={updateField} options={['highlight', 'isolate']} />
|
|
2685
2688
|
<TextField value={config.legend.label} section='legend' fieldName='label' label='Title' updateField={updateField} />
|
|
2686
2689
|
<Select value={config.legend.position} section='legend' fieldName='position' label='Position' updateField={updateField} options={['right', 'left', 'bottom']} />
|
|
2687
|
-
{config.legend.position === 'bottom' &&
|
|
2690
|
+
{config.legend.position === 'bottom' && (
|
|
2691
|
+
<>
|
|
2692
|
+
<CheckBox value={config.legend.singleRow} section='legend' fieldName='singleRow' label='Single Row Legend' updateField={updateField} />
|
|
2693
|
+
<CheckBox value={config.legend.verticalSorted} section='legend' fieldName='verticalSorted' label='Vertical sorted Legend' updateField={updateField} />
|
|
2694
|
+
</>
|
|
2695
|
+
)}
|
|
2688
2696
|
<TextField type='textarea' value={config.legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />
|
|
2689
2697
|
</AccordionItemPanel>
|
|
2690
2698
|
</AccordionItem>
|
|
@@ -3175,7 +3183,7 @@ const EditorPanel = () => {
|
|
|
3175
3183
|
{isDashboard && <CheckBox value={config.table.showDataTableLink} section='table' fieldName='showDataTableLink' label='Show Data Table Name & Link' updateField={updateField} />}
|
|
3176
3184
|
{isLoadedFromUrl && <CheckBox value={config.table.showDownloadUrl} section='table' fieldName='showDownloadUrl' label='Show URL to Automatically Updated Data' updateField={updateField} />}
|
|
3177
3185
|
<CheckBox value={config.table.download} section='table' fieldName='download' label='Show Download CSV Link' updateField={updateField} />
|
|
3178
|
-
|
|
3186
|
+
<CheckBox value={config.table.showDownloadImgButton} section='table' fieldName='showDownloadImgButton' label='Display Image Button' updateField={updateField} />
|
|
3179
3187
|
{/* <CheckBox value={config.table.showDownloadPdfButton} section='table' fieldName='showDownloadPdfButton' label='Display PDF Button' updateField={updateField} /> */}
|
|
3180
3188
|
</AccordionItemPanel>
|
|
3181
3189
|
</AccordionItem>
|
|
@@ -6,6 +6,7 @@ import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
|
6
6
|
|
|
7
7
|
import useLegendClasses from './../hooks/useLegendClasses'
|
|
8
8
|
import { useHighlightedBars } from '../hooks/useHighlightedBars'
|
|
9
|
+
import { Line } from '@visx/shape'
|
|
9
10
|
|
|
10
11
|
// * FILE REVIEW *
|
|
11
12
|
// TODO: fix eslint-disable jsxa11y issues
|
|
@@ -28,7 +29,8 @@ const Legend = () => {
|
|
|
28
29
|
highlightReset,
|
|
29
30
|
transformedData: data,
|
|
30
31
|
colorPalettes,
|
|
31
|
-
currentViewport
|
|
32
|
+
currentViewport,
|
|
33
|
+
handleLineType
|
|
32
34
|
} = useContext(ConfigContext)
|
|
33
35
|
|
|
34
36
|
const { innerClasses, containerClasses } = useLegendClasses(config)
|
|
@@ -165,7 +167,6 @@ const Legend = () => {
|
|
|
165
167
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
166
168
|
|
|
167
169
|
let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
|
|
168
|
-
|
|
169
170
|
if (!legend) return null
|
|
170
171
|
|
|
171
172
|
return (
|
|
@@ -213,7 +214,14 @@ const Legend = () => {
|
|
|
213
214
|
highlight(label)
|
|
214
215
|
}}
|
|
215
216
|
>
|
|
216
|
-
|
|
217
|
+
{config.visualizationType === 'Line' && config.legend.lineMode ? (
|
|
218
|
+
<svg width={40} height={20}>
|
|
219
|
+
<Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={label.value} strokeWidth={2} strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')} />
|
|
220
|
+
</svg>
|
|
221
|
+
) : (
|
|
222
|
+
<LegendCircle fill={label.value} />
|
|
223
|
+
)}
|
|
224
|
+
|
|
217
225
|
<LegendLabel align='left' margin='0 0 0 4px'>
|
|
218
226
|
{label.text}
|
|
219
227
|
</LegendLabel>
|
|
@@ -92,6 +92,7 @@ export default {
|
|
|
92
92
|
horizontal: 750
|
|
93
93
|
},
|
|
94
94
|
xAxis: {
|
|
95
|
+
sortDates: false,
|
|
95
96
|
anchors: [],
|
|
96
97
|
type: 'categorical',
|
|
97
98
|
showTargetLabel: true,
|
|
@@ -139,7 +140,9 @@ export default {
|
|
|
139
140
|
dynamicLegendDefaultText: 'Show All',
|
|
140
141
|
dynamicLegendItemLimit: 5,
|
|
141
142
|
dynamicLegendItemLimitMessage: 'Dynamic Legend Item Limit Hit.',
|
|
142
|
-
dynamicLegendChartMessage: 'Select Options from the Legend'
|
|
143
|
+
dynamicLegendChartMessage: 'Select Options from the Legend',
|
|
144
|
+
lineMode: false,
|
|
145
|
+
verticalSorted: false
|
|
143
146
|
},
|
|
144
147
|
exclusions: {
|
|
145
148
|
active: false,
|
|
@@ -2,24 +2,27 @@ export default function useLegendClasses(config) {
|
|
|
2
2
|
let containerClasses = ['legend-container']
|
|
3
3
|
let innerClasses = ['legend-container__inner']
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
// Legend Positioning
|
|
6
|
+
if (config.legend.position === 'left') {
|
|
7
|
+
containerClasses.push('left')
|
|
8
|
+
}
|
|
9
|
+
if (config.legend.position === 'bottom') {
|
|
10
|
+
containerClasses.push('bottom')
|
|
11
|
+
innerClasses.push('bottom')
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (config.legend.position === 'bottom' && config.legend.singleRow) {
|
|
15
|
+
innerClasses.push('single-row')
|
|
16
|
+
}
|
|
17
17
|
|
|
18
18
|
// Legend > Item Ordering
|
|
19
19
|
if (config.legend.reverseLabelOrder) {
|
|
20
20
|
innerClasses.push('d-flex')
|
|
21
21
|
innerClasses.push('flex-column-reverse')
|
|
22
22
|
}
|
|
23
|
+
if (config.legend.position === 'bottom' && config.legend.verticalSorted) {
|
|
24
|
+
innerClasses.push('vertical-sorted')
|
|
25
|
+
}
|
|
23
26
|
|
|
24
27
|
return {
|
|
25
28
|
containerClasses,
|
|
@@ -3,13 +3,13 @@ import isNumber from '@cdc/core/helpers/isNumber'
|
|
|
3
3
|
function useReduceData(config, data) {
|
|
4
4
|
const isBar = config.series.every(({ type }) => type === 'Bar')
|
|
5
5
|
const isAllLine = config.series.every(({ type }) => ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg'].includes(type))
|
|
6
|
-
const sumYValues = seriesKeys => xValue => seriesKeys.reduce((yTotal, k) => yTotal + Number(xValue[k]), 0)
|
|
6
|
+
const sumYValues = seriesKeys => xValue => seriesKeys.reduce((yTotal, k) => (isNaN(Number(xValue[k])) ? yTotal : yTotal + Number(xValue[k])), 0)
|
|
7
7
|
|
|
8
8
|
const getMaxValueFromData = () => {
|
|
9
9
|
let max = Math.max(...data.map(d => Math.max(...config.runtime.seriesKeys.map(key => (isNumber(d[key]) ? Number(cleanChars(d[key])) : 0)))))
|
|
10
10
|
|
|
11
11
|
if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && isBar)) && config.visualizationSubType === 'stacked') {
|
|
12
|
-
const yTotals = data.map(sumYValues(config.runtime.seriesKeys))
|
|
12
|
+
const yTotals = data.map(sumYValues(config.runtime.seriesKeys)).filter(num => !isNaN(num))
|
|
13
13
|
max = Math.max(...yTotals)
|
|
14
14
|
}
|
|
15
15
|
|
package/src/hooks/useScales.js
CHANGED
|
@@ -48,7 +48,7 @@ const useScales = properties => {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// handle Area chart
|
|
51
|
-
if (config.
|
|
51
|
+
if (config.xAxis.type === 'date' && config.xAxis.sortDates) {
|
|
52
52
|
xScale = scaleTime({
|
|
53
53
|
domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)],
|
|
54
54
|
range: [0, xMax]
|
package/src/hooks/useTooltip.jsx
CHANGED
|
@@ -170,7 +170,7 @@ export const useTooltip = props => {
|
|
|
170
170
|
const getXValueFromCoordinate = x => {
|
|
171
171
|
if (visualizationType === 'Pie') return
|
|
172
172
|
if (orientation === 'horizontal') return
|
|
173
|
-
if (xScale.type === 'point' || xAxis.type === 'continuous') {
|
|
173
|
+
if (xScale.type === 'point' || xAxis.type === 'continuous' || xAxis.type === 'date') {
|
|
174
174
|
// Find the closest x value by calculating the minimum distance
|
|
175
175
|
let closestX = null
|
|
176
176
|
let minDistance = Number.MAX_VALUE
|
|
@@ -315,7 +315,7 @@ export const useTooltip = props => {
|
|
|
315
315
|
if (!config.dashboard) {
|
|
316
316
|
switch (visualizationType) {
|
|
317
317
|
case 'Combo':
|
|
318
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.
|
|
318
|
+
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys, ...stageColumns, ...ciItems]
|
|
319
319
|
break
|
|
320
320
|
case 'Forecasting':
|
|
321
321
|
standardLoopItems = [runtime.xAxis.dataKey, ...stageColumns, ...ciItems]
|