@cdc/chart 4.25.3-6 → 4.25.5-1
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-1a1724a1.es.js +4886 -0
- package/dist/cdcchart.js +50347 -75521
- package/index.html +1 -0
- package/package.json +22 -27
- package/src/CdcChart.tsx +1 -22
- package/src/CdcChartComponent.tsx +35 -21
- package/src/_stories/Chart.CI.stories.tsx +43 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +6 -0
- package/src/_stories/Chart.stories.tsx +7 -16
- package/src/_stories/_mock/bar_chart_ci_labels.json +620 -0
- package/src/_stories/_mock/barchart_labels.mock.json +612 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
- package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
- package/{examples/private/line-issue.json → src/_stories/_mock/legend_groupBy_mock.json} +46 -69
- package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
- package/src/components/AreaChart/components/AreaChart.jsx +33 -5
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +51 -41
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +19 -9
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +20 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -31
- package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
- package/src/components/BarChart/components/context.tsx +20 -2
- package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
- package/src/components/BarChart/helpers/index.ts +5 -2
- package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
- package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +11 -47
- package/src/components/BoxPlot/BoxPlot.tsx +2 -1
- package/src/components/DeviationBar.jsx +2 -1
- package/src/components/EditorPanel/EditorPanel.tsx +60 -24
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +4 -3
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
- package/src/components/ForestPlot/ForestPlot.tsx +2 -2
- package/src/components/Legend/Legend.Component.tsx +69 -58
- package/src/components/Legend/Legend.Suppression.tsx +12 -22
- package/src/components/Legend/Legend.tsx +3 -1
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +40 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +103 -0
- package/src/components/Legend/LegendGroup/index.tsx +3 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
- package/src/components/LineChart/LineChartProps.ts +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +77 -119
- package/src/components/LineChart/helpers.ts +133 -56
- package/src/components/LineChart/index.tsx +125 -60
- package/src/components/LinearChart.tsx +74 -115
- package/src/components/PairedBarChart.jsx +3 -2
- package/src/components/PieChart/PieChart.tsx +71 -91
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
- package/src/components/Sparkline/components/SparkLine.tsx +80 -18
- package/src/components/ZoomBrush.tsx +4 -4
- package/src/data/initial-state.js +4 -2
- package/src/helpers/countNumOfTicks.ts +1 -1
- package/src/helpers/dataHelpers.ts +31 -0
- package/src/helpers/sizeHelpers.ts +23 -0
- package/src/hooks/useMinMax.ts +21 -28
- package/src/hooks/useRightAxis.ts +4 -2
- package/src/hooks/useScales.ts +12 -14
- package/src/hooks/useTooltip.tsx +204 -203
- package/src/index.jsx +2 -2
- package/src/scss/main.scss +13 -6
- package/src/store/chart.actions.ts +1 -1
- package/src/types/ChartConfig.ts +7 -1
- package/LICENSE +0 -201
- package/examples/private/DEV-8850-2.json +0 -493
- package/examples/private/DEV-9822.json +0 -574
- package/examples/private/DEV-9840.json +0 -553
- package/examples/private/DEV-9850-3.json +0 -461
- package/examples/private/chart.json +0 -1084
- package/examples/private/ci_formatted.json +0 -202
- package/examples/private/ci_issue.json +0 -3016
- package/examples/private/completed.json +0 -634
- package/examples/private/dem-data-long.csv +0 -20
- package/examples/private/dem-data-long.json +0 -36
- package/examples/private/demographic_data.csv +0 -157
- package/examples/private/demographic_data.json +0 -2654
- package/examples/private/demographic_dynamic.json +0 -443
- package/examples/private/demographic_standard.json +0 -560
- package/examples/private/ehdi.json +0 -29939
- package/examples/private/not-loading.json +0 -360
- package/examples/private/test.json +0 -493
|
@@ -906,7 +906,7 @@ const EditorPanel = () => {
|
|
|
906
906
|
return Object.keys(columns)
|
|
907
907
|
}
|
|
908
908
|
|
|
909
|
-
const getLegendStyleOptions = (option: 'style' | 'subStyle' | 'shapes'): string[] => {
|
|
909
|
+
const getLegendStyleOptions = (option: 'style' | 'subStyle' | 'shapes' | 'groupBy'): string[] => {
|
|
910
910
|
const options: string[] = []
|
|
911
911
|
|
|
912
912
|
switch (option) {
|
|
@@ -929,6 +929,9 @@ const EditorPanel = () => {
|
|
|
929
929
|
options.push('linear blocks', 'smooth')
|
|
930
930
|
}
|
|
931
931
|
|
|
932
|
+
break
|
|
933
|
+
case 'groupBy':
|
|
934
|
+
options.push(...getColumns())
|
|
932
935
|
break
|
|
933
936
|
}
|
|
934
937
|
return options
|
|
@@ -962,7 +965,7 @@ const EditorPanel = () => {
|
|
|
962
965
|
}
|
|
963
966
|
|
|
964
967
|
const convertStateToConfig = () => {
|
|
965
|
-
let strippedState =
|
|
968
|
+
let strippedState = _.cloneDeep(config)
|
|
966
969
|
if (false === missingRequiredSections(config)) {
|
|
967
970
|
delete strippedState.newViz
|
|
968
971
|
}
|
|
@@ -2698,7 +2701,7 @@ const EditorPanel = () => {
|
|
|
2698
2701
|
</>
|
|
2699
2702
|
)}
|
|
2700
2703
|
|
|
2701
|
-
{isDateScale(config.xAxis) && (
|
|
2704
|
+
{(isDateScale(config.xAxis) || config?.visualizationType === 'Bump Chart') && (
|
|
2702
2705
|
<>
|
|
2703
2706
|
<p style={{ padding: '1.5em 0 0.5em', fontSize: '.9rem', lineHeight: '1rem' }}>
|
|
2704
2707
|
Format how charts should parse and display your dates using{' '}
|
|
@@ -2909,27 +2912,26 @@ const EditorPanel = () => {
|
|
|
2909
2912
|
</>
|
|
2910
2913
|
)}
|
|
2911
2914
|
|
|
2912
|
-
{config.xAxis.type === 'date' ||
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
))}
|
|
2915
|
+
{(config.xAxis.type === 'date' || config.xAxis.type === 'date-time') && (
|
|
2916
|
+
<>
|
|
2917
|
+
<TextField
|
|
2918
|
+
type='date'
|
|
2919
|
+
section='exclusions'
|
|
2920
|
+
fieldName='dateStart'
|
|
2921
|
+
label='Start Date'
|
|
2922
|
+
updateField={updateField}
|
|
2923
|
+
value={config.exclusions.dateStart || ''}
|
|
2924
|
+
/>
|
|
2925
|
+
<TextField
|
|
2926
|
+
type='date'
|
|
2927
|
+
section='exclusions'
|
|
2928
|
+
fieldName='dateEnd'
|
|
2929
|
+
label='End Date'
|
|
2930
|
+
updateField={updateField}
|
|
2931
|
+
value={config.exclusions.dateEnd || ''}
|
|
2932
|
+
/>
|
|
2933
|
+
</>
|
|
2934
|
+
)}
|
|
2933
2935
|
</>
|
|
2934
2936
|
)}
|
|
2935
2937
|
|
|
@@ -3691,6 +3693,17 @@ const EditorPanel = () => {
|
|
|
3691
3693
|
updateField={updateField}
|
|
3692
3694
|
options={getLegendStyleOptions('style')}
|
|
3693
3695
|
/>
|
|
3696
|
+
|
|
3697
|
+
<Select
|
|
3698
|
+
value={config.legend.groupBy}
|
|
3699
|
+
section='legend'
|
|
3700
|
+
fieldName='groupBy'
|
|
3701
|
+
initial='Select'
|
|
3702
|
+
label='Legend Group By:'
|
|
3703
|
+
updateField={updateField}
|
|
3704
|
+
options={getLegendStyleOptions('groupBy')}
|
|
3705
|
+
/>
|
|
3706
|
+
|
|
3694
3707
|
<CheckBox
|
|
3695
3708
|
tooltip={
|
|
3696
3709
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
@@ -3986,6 +3999,29 @@ const EditorPanel = () => {
|
|
|
3986
3999
|
fieldName='description'
|
|
3987
4000
|
label='Legend Description'
|
|
3988
4001
|
/>
|
|
4002
|
+
<CheckBox
|
|
4003
|
+
value={config.legend.unified}
|
|
4004
|
+
section='legend'
|
|
4005
|
+
fieldName='unified'
|
|
4006
|
+
label='Unified Legend'
|
|
4007
|
+
updateField={updateField}
|
|
4008
|
+
tooltip={
|
|
4009
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
4010
|
+
<Tooltip.Target>
|
|
4011
|
+
<Icon
|
|
4012
|
+
display='question'
|
|
4013
|
+
style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
|
|
4014
|
+
/>
|
|
4015
|
+
</Tooltip.Target>
|
|
4016
|
+
<Tooltip.Content>
|
|
4017
|
+
<p>
|
|
4018
|
+
For a chart with filters, check this option if you want the legend to contain an item for
|
|
4019
|
+
every series in the data set, including those that are filtered.
|
|
4020
|
+
</p>
|
|
4021
|
+
</Tooltip.Content>
|
|
4022
|
+
</Tooltip>
|
|
4023
|
+
}
|
|
4024
|
+
/>
|
|
3989
4025
|
</AccordionItemPanel>
|
|
3990
4026
|
</AccordionItem>
|
|
3991
4027
|
)}
|
|
@@ -113,7 +113,7 @@ const PanelGeneral: FC<PanelProps> = props => {
|
|
|
113
113
|
)}
|
|
114
114
|
</div>
|
|
115
115
|
)}
|
|
116
|
-
{(visualizationType === 'Bar' || visualizationType === 'Combo'
|
|
116
|
+
{(visualizationType === 'Bar' || visualizationType === 'Combo') && (
|
|
117
117
|
<Select
|
|
118
118
|
value={visualizationSubType || 'Regular'}
|
|
119
119
|
fieldName='visualizationSubType'
|
|
@@ -209,10 +209,8 @@ const PanelGeneral: FC<PanelProps> = props => {
|
|
|
209
209
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
210
210
|
</Tooltip.Target>
|
|
211
211
|
<Tooltip.Content>
|
|
212
|
-
<p>
|
|
213
|
-
|
|
214
|
-
</p>
|
|
215
|
-
<hr/>
|
|
212
|
+
<p>Recommended set to display for Section 508 compliance.</p>
|
|
213
|
+
<hr />
|
|
216
214
|
<p>
|
|
217
215
|
Selecting this option will <i> not </i> hide the display of "zero value", "suppressed data", or
|
|
218
216
|
"missing data" indicators on the chart (if applicable).
|
|
@@ -245,35 +243,7 @@ const PanelGeneral: FC<PanelProps> = props => {
|
|
|
245
243
|
label='Display "Zero Data" Label'
|
|
246
244
|
updateField={updateField}
|
|
247
245
|
/>
|
|
248
|
-
|
|
249
|
-
tooltip={
|
|
250
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
251
|
-
<Tooltip.Target>
|
|
252
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
253
|
-
</Tooltip.Target>
|
|
254
|
-
<Tooltip.Content>
|
|
255
|
-
{config.visualizationSubType === 'stacked' && (
|
|
256
|
-
<p>
|
|
257
|
-
We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose
|
|
258
|
-
to proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g.
|
|
259
|
-
nothing will display in chart).
|
|
260
|
-
</p>
|
|
261
|
-
)}
|
|
262
|
-
{config.visualizationSubType !== 'stacked' && (
|
|
263
|
-
<p>
|
|
264
|
-
Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in
|
|
265
|
-
the data table to indicate missing or undefined data values.
|
|
266
|
-
</p>
|
|
267
|
-
)}
|
|
268
|
-
</Tooltip.Content>
|
|
269
|
-
</Tooltip>
|
|
270
|
-
}
|
|
271
|
-
value={config.general.showMissingDataLabel}
|
|
272
|
-
section='general'
|
|
273
|
-
fieldName='showMissingDataLabel'
|
|
274
|
-
label='Display "Missing Data" Label'
|
|
275
|
-
updateField={updateField}
|
|
276
|
-
/>
|
|
246
|
+
|
|
277
247
|
<CheckBox
|
|
278
248
|
display={config.visualizationType === 'Bar' || config.visualizationType === 'Combo'}
|
|
279
249
|
tooltip={
|
|
@@ -329,6 +299,36 @@ const PanelGeneral: FC<PanelProps> = props => {
|
|
|
329
299
|
</>
|
|
330
300
|
)}
|
|
331
301
|
|
|
302
|
+
<CheckBox
|
|
303
|
+
tooltip={
|
|
304
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
305
|
+
<Tooltip.Target>
|
|
306
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
307
|
+
</Tooltip.Target>
|
|
308
|
+
<Tooltip.Content>
|
|
309
|
+
{config.visualizationSubType === 'stacked' && (
|
|
310
|
+
<p>
|
|
311
|
+
We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose to
|
|
312
|
+
proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g. nothing
|
|
313
|
+
will display in chart).
|
|
314
|
+
</p>
|
|
315
|
+
)}
|
|
316
|
+
{config.visualizationSubType !== 'stacked' && (
|
|
317
|
+
<p>
|
|
318
|
+
Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in the
|
|
319
|
+
data table to indicate missing or undefined data values.
|
|
320
|
+
</p>
|
|
321
|
+
)}
|
|
322
|
+
</Tooltip.Content>
|
|
323
|
+
</Tooltip>
|
|
324
|
+
}
|
|
325
|
+
value={config.general.showMissingDataLabel}
|
|
326
|
+
section='general'
|
|
327
|
+
fieldName='showMissingDataLabel'
|
|
328
|
+
label='Display "Missing Data" Label'
|
|
329
|
+
updateField={updateField}
|
|
330
|
+
/>
|
|
331
|
+
|
|
332
332
|
{visualizationType === 'Pie' && (
|
|
333
333
|
<Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />
|
|
334
334
|
)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../../../../ConfigContext'
|
|
3
3
|
|
|
4
4
|
// Core
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
AccordionItemPanel,
|
|
18
18
|
AccordionItemButton
|
|
19
19
|
} from 'react-accessible-accordion'
|
|
20
|
-
import { Draggable } from '@hello-pangea/dnd'
|
|
20
|
+
import { Draggable, DragDropContext, Droppable } from '@hello-pangea/dnd'
|
|
21
21
|
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
22
22
|
|
|
23
23
|
const SeriesContext = React.createContext({})
|
|
@@ -609,9 +609,20 @@ const SeriesButtonRemove = props => {
|
|
|
609
609
|
}
|
|
610
610
|
|
|
611
611
|
const SeriesItem = props => {
|
|
612
|
-
const { config } = useContext(ConfigContext)
|
|
612
|
+
const { config, updateConfig } = useContext(ConfigContext)
|
|
613
613
|
const { updateSeries, getColumns } = useContext(SeriesContext)
|
|
614
614
|
const { series, getItemStyle, sortableItemStyles, chartsWithOptions, index: i } = props
|
|
615
|
+
const legendOrderOptions: { label: string; value: string }[] = [
|
|
616
|
+
{ label: 'Order By Data Column', value: 'dataColumn' },
|
|
617
|
+
{
|
|
618
|
+
label: 'Ascending Alphanumeric',
|
|
619
|
+
value: 'asc'
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
label: 'Descending Alphanumeric',
|
|
623
|
+
value: 'desc'
|
|
624
|
+
}
|
|
625
|
+
]
|
|
615
626
|
const showDynamicCategory =
|
|
616
627
|
['Bar', 'Line'].includes(config.visualizationType) &&
|
|
617
628
|
config.visualizationSubType !== 'Stacked' &&
|
|
@@ -646,28 +657,43 @@ const SeriesItem = props => {
|
|
|
646
657
|
<AccordionItemPanel>
|
|
647
658
|
<Series.Input.Name series={series} index={i} />
|
|
648
659
|
{showDynamicCategory && (
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
<Tooltip
|
|
660
|
-
<
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
<
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
660
|
+
<>
|
|
661
|
+
<Select
|
|
662
|
+
label='Dynamic Category'
|
|
663
|
+
value={series.dynamicCategory}
|
|
664
|
+
options={['- Select - ', ...getColumns().filter(col => series.dataKey !== col)]}
|
|
665
|
+
updateField={(_section, _subsection, _fieldName, value) => {
|
|
666
|
+
if (value === '- Select -') value = ''
|
|
667
|
+
updateSeries(i, value, 'dynamicCategory')
|
|
668
|
+
}}
|
|
669
|
+
tooltip={
|
|
670
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
671
|
+
<Tooltip.Target>
|
|
672
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
673
|
+
</Tooltip.Target>
|
|
674
|
+
<Tooltip.Content>
|
|
675
|
+
<p>
|
|
676
|
+
This field is Optional. If you have a dynamic data series you can select the category
|
|
677
|
+
field here. You can only add one dynamic category per visualization.
|
|
678
|
+
</p>
|
|
679
|
+
</Tooltip.Content>
|
|
680
|
+
</Tooltip>
|
|
681
|
+
}
|
|
682
|
+
/>
|
|
683
|
+
|
|
684
|
+
<>
|
|
685
|
+
<Select
|
|
686
|
+
value={config.legend.order}
|
|
687
|
+
options={legendOrderOptions}
|
|
688
|
+
section='legend'
|
|
689
|
+
fieldName='order'
|
|
690
|
+
updateField={(_section, _subsection, _fieldName, value) => {
|
|
691
|
+
updateConfig({ ...config, legend: { ...config.legend, order: value } })
|
|
692
|
+
}}
|
|
693
|
+
label='Dynamic Series Order'
|
|
694
|
+
/>
|
|
695
|
+
</>
|
|
696
|
+
</>
|
|
671
697
|
)}
|
|
672
698
|
<Series.Input.Weight series={series} index={i} />
|
|
673
699
|
<Series.Dropdown.SeriesType series={series} index={i} />
|
|
@@ -22,6 +22,7 @@ import { useEditorPermissions } from '../../useEditorPermissions.js'
|
|
|
22
22
|
import { useEditorPanelContext } from '../../EditorPanelContext.js'
|
|
23
23
|
import ConfigContext from '../../../../ConfigContext.js'
|
|
24
24
|
import { PanelProps } from '../PanelProps'
|
|
25
|
+
import { LineChartConfig } from '../../../../types/ChartConfig'
|
|
25
26
|
|
|
26
27
|
const PanelVisual: FC<PanelProps> = props => {
|
|
27
28
|
const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
|
|
@@ -147,9 +148,7 @@ const PanelVisual: FC<PanelProps> = props => {
|
|
|
147
148
|
/>
|
|
148
149
|
</Tooltip.Target>
|
|
149
150
|
<Tooltip.Content>
|
|
150
|
-
<p>
|
|
151
|
-
Recommended set to display for Section 508 compliance.
|
|
152
|
-
</p>
|
|
151
|
+
<p>Recommended set to display for Section 508 compliance.</p>
|
|
153
152
|
</Tooltip.Content>
|
|
154
153
|
</Tooltip>
|
|
155
154
|
}
|
|
@@ -222,6 +221,14 @@ const PanelVisual: FC<PanelProps> = props => {
|
|
|
222
221
|
updateField={updateField}
|
|
223
222
|
options={['Same as Line', 'Lighter than Line']}
|
|
224
223
|
/>
|
|
224
|
+
<CheckBox
|
|
225
|
+
value={!(config as LineChartConfig).isolatedDotsSameSize}
|
|
226
|
+
fieldName='isolatedDotsSameSize'
|
|
227
|
+
label='Accentuate isolated data points'
|
|
228
|
+
updateField={(section, subsection, fieldname, value) =>
|
|
229
|
+
updateField(section, subsection, fieldname, !value)
|
|
230
|
+
}
|
|
231
|
+
/>
|
|
225
232
|
</>
|
|
226
233
|
)}
|
|
227
234
|
{/* eslint-disable */}
|
|
@@ -30,9 +30,10 @@ export const updateFieldRankByValue = (
|
|
|
30
30
|
newConfig.rankByValue = newValue
|
|
31
31
|
|
|
32
32
|
if (config.rankByValue && !newValue) {
|
|
33
|
-
const CIkeys: string[] = Object.values(config
|
|
34
|
-
const seriesKeys: string[] = config
|
|
35
|
-
const keysToClean: string[] = seriesKeys
|
|
33
|
+
const CIkeys: string[] = Object.values(_.get(config, 'confidenceKeys', {})) as string[]
|
|
34
|
+
const seriesKeys: string[] = _.get(config, 'series', []).map((s: any) => s.dataKey)
|
|
35
|
+
const keysToClean: string[] = [...(seriesKeys ?? []), ...(CIkeys ?? [])]
|
|
36
|
+
|
|
36
37
|
const cleanData = config?.xAxis?.dataKey
|
|
37
38
|
? transform.cleanData(config.data, config.xAxis.dataKey, keysToClean)
|
|
38
39
|
: config.data
|
|
@@ -358,10 +358,7 @@ export const useEditorPermissions = () => {
|
|
|
358
358
|
|
|
359
359
|
const visSupportsReactTooltip = () => {
|
|
360
360
|
if (config.yAxis.type === 'categorical') return true
|
|
361
|
-
if (
|
|
362
|
-
['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) ||
|
|
363
|
-
(visualizationType === 'Bar' && config.tooltips.singleSeries)
|
|
364
|
-
) {
|
|
361
|
+
if (['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType)) {
|
|
365
362
|
return true
|
|
366
363
|
}
|
|
367
364
|
}
|
|
@@ -14,7 +14,7 @@ import { type ChartContext } from '@cdc/chart/src/types/ChartContext'
|
|
|
14
14
|
|
|
15
15
|
// cdc
|
|
16
16
|
import ConfigContext from '../../ConfigContext'
|
|
17
|
-
import {
|
|
17
|
+
import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
|
|
18
18
|
|
|
19
19
|
const ForestPlot = ({
|
|
20
20
|
xScale,
|
|
@@ -254,7 +254,7 @@ const ForestPlot = ({
|
|
|
254
254
|
<LinePath
|
|
255
255
|
data={regressionPoints}
|
|
256
256
|
x={d => d.x}
|
|
257
|
-
y={d => d.y -
|
|
257
|
+
y={d => d.y - APP_FONT_SIZE / 2}
|
|
258
258
|
stroke='black'
|
|
259
259
|
strokeWidth={2}
|
|
260
260
|
fill={'black'}
|
|
@@ -15,6 +15,8 @@ import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
|
|
|
15
15
|
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
16
16
|
import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
|
|
17
17
|
import LegendLineShape from './LegendLine.Shape'
|
|
18
|
+
import LegendGroup from './LegendGroup'
|
|
19
|
+
import { getSeriesWithData } from '../../helpers/dataHelpers'
|
|
18
20
|
|
|
19
21
|
const LEGEND_PADDING = 36
|
|
20
22
|
|
|
@@ -29,6 +31,7 @@ export interface LegendProps {
|
|
|
29
31
|
seriesHighlight: string[]
|
|
30
32
|
skipId: string
|
|
31
33
|
dimensions: DimensionsType // for responsive width legend
|
|
34
|
+
transformedData: any
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
@@ -43,12 +46,17 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
43
46
|
currentViewport,
|
|
44
47
|
formatLabels,
|
|
45
48
|
skipId = 'legend',
|
|
46
|
-
dimensions
|
|
49
|
+
dimensions,
|
|
50
|
+
transformedData: data
|
|
47
51
|
},
|
|
48
52
|
ref
|
|
49
53
|
) => {
|
|
50
54
|
const { innerClasses, containerClasses } = getLegendClasses(config)
|
|
51
55
|
const { runtime, legend } = config
|
|
56
|
+
const { series } = runtime
|
|
57
|
+
|
|
58
|
+
const seriesWithData = getSeriesWithData(config)
|
|
59
|
+
const dontFilterLegendItems = !series.length || legend.unified || !seriesWithData.length
|
|
52
60
|
|
|
53
61
|
const isLegendBottom =
|
|
54
62
|
legend?.position === 'bottom' ||
|
|
@@ -84,77 +92,80 @@ const Legend: React.FC<LegendProps> = forwardRef(
|
|
|
84
92
|
dimensions={dimensions}
|
|
85
93
|
parentPaddingToSubtract={legend.hideBorder ? 0 : LEGEND_PADDING}
|
|
86
94
|
/>
|
|
95
|
+
<LegendGroup formatLabels={formatLabels} />
|
|
87
96
|
|
|
88
97
|
<LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
|
|
89
98
|
{labels => {
|
|
90
99
|
return (
|
|
91
100
|
<>
|
|
92
101
|
<div className={innerClasses.join(' ')}>
|
|
93
|
-
{formatLabels(labels as Label[])
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
{formatLabels(labels as Label[])
|
|
103
|
+
.filter(label => dontFilterLegendItems || seriesWithData.includes(label.datum))
|
|
104
|
+
.map((label, i) => {
|
|
105
|
+
let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
|
|
106
|
+
let itemName = label.datum
|
|
107
|
+
|
|
108
|
+
// Filter excluded data keys from legend
|
|
109
|
+
if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
|
|
110
|
+
return null
|
|
111
|
+
}
|
|
101
112
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
if (runtime.seriesLabels) {
|
|
114
|
+
let index = config.runtime.seriesLabelsAll.indexOf(itemName)
|
|
115
|
+
itemName = config.runtime.seriesKeys[index]
|
|
105
116
|
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
if (runtime?.forecastingSeriesKeys?.length > 0) {
|
|
118
|
+
itemName = label.text
|
|
119
|
+
}
|
|
108
120
|
}
|
|
109
|
-
}
|
|
110
121
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
122
|
+
if (seriesHighlight.length) {
|
|
123
|
+
if (!seriesHighlight.includes(itemName)) {
|
|
124
|
+
className.push('inactive')
|
|
125
|
+
} else className.push('highlighted')
|
|
126
|
+
}
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
if (config.legend.style === 'gradient' || config.legend.groupBy) {
|
|
129
|
+
return <></>
|
|
130
|
+
}
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
return (
|
|
133
|
+
<LegendItem
|
|
134
|
+
className={className.join(' ')}
|
|
135
|
+
tabIndex={0}
|
|
136
|
+
key={`legend-quantile-${i}`}
|
|
137
|
+
onKeyDown={e => {
|
|
138
|
+
if (e.key === 'Enter') {
|
|
139
|
+
e.preventDefault()
|
|
140
|
+
highlight(label)
|
|
141
|
+
}
|
|
142
|
+
}}
|
|
143
|
+
onClick={e => {
|
|
128
144
|
e.preventDefault()
|
|
129
145
|
highlight(label)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
{label.text}
|
|
154
|
-
</LegendLabel>
|
|
155
|
-
</LegendItem>
|
|
156
|
-
)
|
|
157
|
-
})}
|
|
146
|
+
}}
|
|
147
|
+
role='button'
|
|
148
|
+
>
|
|
149
|
+
<>
|
|
150
|
+
{config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
|
|
151
|
+
<React.Fragment>
|
|
152
|
+
<LegendLineShape index={i} label={label} config={config} />
|
|
153
|
+
</React.Fragment>
|
|
154
|
+
) : (
|
|
155
|
+
<>
|
|
156
|
+
<LegendShape
|
|
157
|
+
shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
|
|
158
|
+
fill={label.value}
|
|
159
|
+
/>
|
|
160
|
+
</>
|
|
161
|
+
)}
|
|
162
|
+
</>
|
|
163
|
+
<LegendLabel align='left' className='m-0'>
|
|
164
|
+
{parse(label.text)}
|
|
165
|
+
</LegendLabel>
|
|
166
|
+
</LegendItem>
|
|
167
|
+
)
|
|
168
|
+
})}
|
|
158
169
|
|
|
159
170
|
{highLightedLegendItems.map((bar, i) => {
|
|
160
171
|
// if duplicates only return first item
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { ChartConfig } from '../../types/ChartConfig'
|
|
3
|
-
import
|
|
4
|
-
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
3
|
+
import RichTooltip from '@cdc/core/components/RichTooltip/RichTooltip'
|
|
5
4
|
interface LegendProps {
|
|
6
5
|
config: ChartConfig
|
|
7
6
|
isLegendBottom: boolean
|
|
@@ -35,10 +34,6 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
|
|
|
35
34
|
</div>
|
|
36
35
|
)
|
|
37
36
|
)
|
|
38
|
-
const handleLinkClick = event => {
|
|
39
|
-
// prevent defintion link to change URl
|
|
40
|
-
event.preventDefault()
|
|
41
|
-
}
|
|
42
37
|
|
|
43
38
|
const renderSuppressedItems = () => {
|
|
44
39
|
const getStyle = displayGray => {
|
|
@@ -121,27 +116,22 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
|
|
|
121
116
|
</React.Fragment>
|
|
122
117
|
)}
|
|
123
118
|
{shouldShowSuppressedInfo() && (
|
|
124
|
-
<div className='legend-container__outer
|
|
125
|
-
<Icon alt='info-icon' display='info' />
|
|
119
|
+
<div className='legend-container__outer link-container'>
|
|
126
120
|
<p>
|
|
127
121
|
This chart contains
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
suppressed data
|
|
135
|
-
|
|
122
|
+
<RichTooltip
|
|
123
|
+
tooltipContent={`Data is
|
|
124
|
+
suppressed to maintain statistical reliability.
|
|
125
|
+
This occurs when the number of respondents or
|
|
126
|
+
reported values does not meet the minimum
|
|
127
|
+
reporting threshold.`}
|
|
128
|
+
linkText='suppressed data'
|
|
129
|
+
href={null}
|
|
130
|
+
tooltipOpacity={config.tooltips.opacity}
|
|
131
|
+
/>
|
|
136
132
|
</p>
|
|
137
133
|
</div>
|
|
138
134
|
)}
|
|
139
|
-
|
|
140
|
-
<ReactTooltip // prettier-ignore
|
|
141
|
-
id='my-tooltip'
|
|
142
|
-
variant='light'
|
|
143
|
-
style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black', maxWidth: '100%' }}
|
|
144
|
-
/>
|
|
145
135
|
</React.Fragment>
|
|
146
136
|
)
|
|
147
137
|
}
|