@cdc/chart 4.25.5-1 → 4.25.6
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 +32848 -27824
- package/index.html +130 -130
- package/package.json +2 -2
- package/src/CdcChartComponent.tsx +66 -26
- package/src/_stories/Chart.stories.tsx +99 -93
- package/src/_stories/ChartPrefixSuffix.stories.tsx +29 -32
- package/src/_stories/_mock/pie_calculated_area.json +417 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +4 -13
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +3 -14
- package/src/components/BarChart/components/BarChart.Vertical.tsx +2 -8
- package/src/components/Brush/BrushChart.tsx +73 -0
- package/src/components/Brush/BrushController..tsx +39 -0
- package/src/components/DeviationBar.jsx +0 -1
- package/src/components/EditorPanel/EditorPanel.tsx +232 -147
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +3 -2
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +2 -1
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +8 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +7 -4
- package/src/components/HoverLine/HoverLine.tsx +74 -0
- package/src/components/Legend/Legend.Suppression.tsx +47 -3
- package/src/components/Legend/helpers/index.ts +1 -1
- package/src/components/LineChart/index.tsx +3 -6
- package/src/components/LinearChart.tsx +161 -132
- package/src/components/PieChart/PieChart.tsx +58 -13
- package/src/data/initial-state.js +8 -5
- package/src/helpers/getNewRuntime.ts +35 -0
- package/src/helpers/getPiePercent.ts +22 -0
- package/src/helpers/getTransformedData.ts +22 -0
- package/src/helpers/tests/getNewRuntime.test.ts +82 -0
- package/src/helpers/tests/getPiePercent.test.ts +38 -0
- package/src/hooks/useRightAxis.ts +1 -1
- package/src/hooks/useScales.ts +8 -3
- package/src/hooks/useTooltip.tsx +24 -10
- package/src/store/chart.actions.ts +2 -6
- package/src/store/chart.reducer.ts +23 -23
- package/src/types/ChartConfig.ts +6 -3
- package/src/types/ChartContext.ts +0 -2
- package/src/components/ZoomBrush.tsx +0 -251
|
@@ -435,9 +435,9 @@ const PanelGeneral: FC<PanelProps> = props => {
|
|
|
435
435
|
{visSupportsFootnotes() && (
|
|
436
436
|
<TextField
|
|
437
437
|
type='textarea'
|
|
438
|
-
value={config.
|
|
438
|
+
value={config.legacyFootnotes}
|
|
439
439
|
updateField={updateField}
|
|
440
|
-
fieldName='
|
|
440
|
+
fieldName='legacyFootnotes'
|
|
441
441
|
label='Footnotes'
|
|
442
442
|
tooltip={
|
|
443
443
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
@@ -627,6 +627,7 @@ const SeriesItem = props => {
|
|
|
627
627
|
['Bar', 'Line'].includes(config.visualizationType) &&
|
|
628
628
|
config.visualizationSubType !== 'Stacked' &&
|
|
629
629
|
!config.series.find(s => s.dynamicCategory && s.dataKey !== series.dataKey)
|
|
630
|
+
const SELECT = '- Select -'
|
|
630
631
|
return (
|
|
631
632
|
<Draggable key={series.dataKey} draggableId={`draggableFilter-${series.dataKey}`} index={i}>
|
|
632
633
|
{(provided, snapshot) => (
|
|
@@ -661,9 +662,9 @@ const SeriesItem = props => {
|
|
|
661
662
|
<Select
|
|
662
663
|
label='Dynamic Category'
|
|
663
664
|
value={series.dynamicCategory}
|
|
664
|
-
options={[
|
|
665
|
+
options={[SELECT, ...getColumns().filter(col => series.dataKey !== col)]}
|
|
665
666
|
updateField={(_section, _subsection, _fieldName, value) => {
|
|
666
|
-
if (value ===
|
|
667
|
+
if (value === SELECT) value = ''
|
|
667
668
|
updateSeries(i, value, 'dynamicCategory')
|
|
668
669
|
}}
|
|
669
670
|
tooltip={
|
|
@@ -23,6 +23,7 @@ import { useEditorPanelContext } from '../../EditorPanelContext.js'
|
|
|
23
23
|
import ConfigContext from '../../../../ConfigContext.js'
|
|
24
24
|
import { PanelProps } from '../PanelProps'
|
|
25
25
|
import { LineChartConfig } from '../../../../types/ChartConfig'
|
|
26
|
+
import './panelVisual.styles.css'
|
|
26
27
|
|
|
27
28
|
const PanelVisual: FC<PanelProps> = props => {
|
|
28
29
|
const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
|
|
@@ -45,7 +46,7 @@ const PanelVisual: FC<PanelProps> = props => {
|
|
|
45
46
|
const { twoColorPalettes, sequential, nonSequential, accessibleColors } = useColorPalette(config, updateConfig)
|
|
46
47
|
|
|
47
48
|
const updateColor = (property, _value) => {
|
|
48
|
-
console.
|
|
49
|
+
console.error('value', _value)
|
|
49
50
|
if (property === 'storyNodeFontColor') {
|
|
50
51
|
updateConfig({
|
|
51
52
|
...config,
|
|
@@ -197,7 +197,7 @@ export const useEditorPermissions = () => {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
const visSupportsTooltipLines = () => {
|
|
200
|
-
const enabledCharts = ['Combo', 'Forecasting', 'Area Chart', 'Line', 'Bar']
|
|
200
|
+
const enabledCharts = ['Combo', 'Forecasting', 'Area Chart', 'Line', 'Bar', 'Scatter Plot']
|
|
201
201
|
if (enabledCharts.includes(visualizationType)) return true
|
|
202
202
|
return false
|
|
203
203
|
}
|
|
@@ -357,8 +357,11 @@ export const useEditorPermissions = () => {
|
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
const visSupportsReactTooltip = () => {
|
|
360
|
-
if (config.yAxis.type === 'categorical') return true
|
|
361
|
-
if (
|
|
360
|
+
if (config.yAxis.type === 'categorical' && config.tooltips.singleSeries) return true
|
|
361
|
+
if (
|
|
362
|
+
['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar', 'Bar'].includes(visualizationType) &&
|
|
363
|
+
config.tooltips.singleSeries
|
|
364
|
+
) {
|
|
362
365
|
return true
|
|
363
366
|
}
|
|
364
367
|
}
|
|
@@ -381,7 +384,7 @@ export const useEditorPermissions = () => {
|
|
|
381
384
|
}
|
|
382
385
|
|
|
383
386
|
const visSupportsYPadding = () => {
|
|
384
|
-
return !config.
|
|
387
|
+
return !config.yAxis.inlineLabel || !config.yAxis.inlineLabel?.includes(' ')
|
|
385
388
|
}
|
|
386
389
|
|
|
387
390
|
const visHasSingleSeriesTooltip = () => {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../../ConfigContext'
|
|
3
|
+
import { Group } from '@visx/group'
|
|
4
|
+
import { Line } from '@visx/shape'
|
|
5
|
+
|
|
6
|
+
type HoverLineProps = {
|
|
7
|
+
tooltipData?: any // same as @visx/tooltip TooltipData
|
|
8
|
+
xMax?: number
|
|
9
|
+
yMax?: number
|
|
10
|
+
point: { x: number; y: number }
|
|
11
|
+
orientation: 'horizontal' | 'vertical'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const HoverLine: React.FC<HoverLineProps> = ({ tooltipData, xMax, yMax, point, orientation }) => {
|
|
15
|
+
const { config } = useContext(ConfigContext)
|
|
16
|
+
const { verticalHoverLine, horizontalHoverLine } = config.visual
|
|
17
|
+
const { visualizationType } = config
|
|
18
|
+
const isScatterPlot = visualizationType === 'Scatter Plot'
|
|
19
|
+
|
|
20
|
+
const isVertical = orientation === 'vertical'
|
|
21
|
+
const isHorizontal = orientation === 'horizontal'
|
|
22
|
+
const showVerticalHoverLine = verticalHoverLine || (verticalHoverLine && isScatterPlot)
|
|
23
|
+
const showHorizontalHoverLine = horizontalHoverLine || (horizontalHoverLine && isScatterPlot)
|
|
24
|
+
|
|
25
|
+
const getX = () => {
|
|
26
|
+
if (point.x > xMax + Number(config.yAxis.size)) return xMax
|
|
27
|
+
if (point.x < config.yAxis.size) return config.yAxis.size
|
|
28
|
+
return point.x
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getY = () => {
|
|
32
|
+
if (point.y > yMax) return yMax
|
|
33
|
+
return point.y
|
|
34
|
+
}
|
|
35
|
+
if (isVertical) {
|
|
36
|
+
return (
|
|
37
|
+
showVerticalHoverLine && (
|
|
38
|
+
<Group key={`tooltipLine-vertical${point.y}${point.x}`} className='vertical-tooltip-line'>
|
|
39
|
+
<Line
|
|
40
|
+
from={{ x: getX(), y: 0 }}
|
|
41
|
+
to={{ x: getX(), y: yMax }}
|
|
42
|
+
stroke={'black'}
|
|
43
|
+
strokeWidth={1}
|
|
44
|
+
pointerEvents='none'
|
|
45
|
+
strokeDasharray='5,5'
|
|
46
|
+
className='vertical-tooltip-line'
|
|
47
|
+
/>
|
|
48
|
+
</Group>
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
if (isHorizontal) {
|
|
53
|
+
return (
|
|
54
|
+
showHorizontalHoverLine && (
|
|
55
|
+
<Group
|
|
56
|
+
key={`tooltipLine-horizontal${point.y}${point.x}`}
|
|
57
|
+
className='horizontal-tooltip-line'
|
|
58
|
+
left={config.yAxis.size ? config.yAxis.size : 0}
|
|
59
|
+
>
|
|
60
|
+
<Line
|
|
61
|
+
from={{ x: 0, y: getY() }}
|
|
62
|
+
to={{ x: xMax, y: getY() }}
|
|
63
|
+
stroke={'black'}
|
|
64
|
+
strokeWidth={1}
|
|
65
|
+
pointerEvents='none'
|
|
66
|
+
strokeDasharray='5,5'
|
|
67
|
+
className='horizontal-tooltip-line'
|
|
68
|
+
/>
|
|
69
|
+
</Group>
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export default HoverLine
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
2
|
import { ChartConfig } from '../../types/ChartConfig'
|
|
3
3
|
import RichTooltip from '@cdc/core/components/RichTooltip/RichTooltip'
|
|
4
4
|
interface LegendProps {
|
|
@@ -7,8 +7,8 @@ interface LegendProps {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) => {
|
|
10
|
-
const { preliminaryData, visualizationType, visualizationSubType, legend } = config
|
|
11
|
-
|
|
10
|
+
const { preliminaryData, visualizationType, visualizationSubType, legend, data } = config
|
|
11
|
+
const showPiePercent = config.dataFormat.showPiePercent && config.visualizationType === 'Pie'
|
|
12
12
|
const hasOpenCircleEffects = () =>
|
|
13
13
|
preliminaryData?.some(pd => pd.label && pd.type === 'effect' && pd.style !== 'Filled Circles') &&
|
|
14
14
|
['Line', 'Combo'].includes(visualizationType)
|
|
@@ -95,10 +95,31 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
|
|
|
95
95
|
const getLegendContainerClass = () =>
|
|
96
96
|
legend.singleRow && isLegendBottom ? 'legend-container__inner bottom single-row' : ''
|
|
97
97
|
|
|
98
|
+
const sumSeries = (data, seriesKey) => {
|
|
99
|
+
if (!Array.isArray(data)) return 0
|
|
100
|
+
|
|
101
|
+
return data.reduce((total, row) => {
|
|
102
|
+
const raw = row[seriesKey]
|
|
103
|
+
const num = parseFloat(raw)
|
|
104
|
+
return total + (Number.isFinite(num) ? num : 0)
|
|
105
|
+
}, 0)
|
|
106
|
+
}
|
|
107
|
+
const total = sumSeries(data, config.runtime.yAxis.dataKey)
|
|
108
|
+
|
|
98
109
|
const shouldShowSuppressedInfo = () =>
|
|
99
110
|
!config.legend.hideSuppressionLink &&
|
|
100
111
|
config.visualizationSubType !== 'stacked' &&
|
|
101
112
|
preliminaryData?.some(pd => pd.label && pd.type === 'suppression' && pd.value && (pd?.style || pd.symbol))
|
|
113
|
+
// controls Pie chart Legend for calculated Area
|
|
114
|
+
|
|
115
|
+
const renderCalculatedAreaItems = () => {
|
|
116
|
+
return (
|
|
117
|
+
<div key={'pie-asterisk'} className='legend-preliminary'>
|
|
118
|
+
<span className={'Asterisk'}>{'**'}</span>
|
|
119
|
+
<p>{'Calculated Area'}</p>
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
102
123
|
|
|
103
124
|
return (
|
|
104
125
|
<React.Fragment>
|
|
@@ -108,6 +129,12 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
|
|
|
108
129
|
<div className={getLegendContainerClass()}>{renderEffectItems()}</div>
|
|
109
130
|
</React.Fragment>
|
|
110
131
|
)}
|
|
132
|
+
{showPiePercent && total < 100 && (
|
|
133
|
+
<React.Fragment>
|
|
134
|
+
<hr />
|
|
135
|
+
<div className={getLegendContainerClass()}>{renderCalculatedAreaItems()}</div>
|
|
136
|
+
</React.Fragment>
|
|
137
|
+
)}
|
|
111
138
|
|
|
112
139
|
{shouldShowSuppressedLabels() && (
|
|
113
140
|
<React.Fragment>
|
|
@@ -132,6 +159,23 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
|
|
|
132
159
|
</p>
|
|
133
160
|
</div>
|
|
134
161
|
)}
|
|
162
|
+
{showPiePercent && (total < 100 || total > 100) && (
|
|
163
|
+
<div className='legend-container__outer link-container'>
|
|
164
|
+
<p>
|
|
165
|
+
{total < 100 ? '** This graph contains a' : 'The sum of percentages in this graph is larger than 100 '}
|
|
166
|
+
<RichTooltip
|
|
167
|
+
tooltipContent={
|
|
168
|
+
total < 100
|
|
169
|
+
? 'Calculated Areas are used to supplement the pie chart when the sum of the values in the data is less than 100%.'
|
|
170
|
+
: 'Calculated Areas are disabled when the total exceeds 100%.'
|
|
171
|
+
}
|
|
172
|
+
linkText={total < 100 ? 'calculated area' : ''}
|
|
173
|
+
href={null}
|
|
174
|
+
tooltipOpacity={config.tooltips.opacity}
|
|
175
|
+
/>
|
|
176
|
+
</p>
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
135
179
|
</React.Fragment>
|
|
136
180
|
)
|
|
137
181
|
}
|
|
@@ -36,7 +36,7 @@ export const getMarginBottom = (isLegendBottom, config) => {
|
|
|
36
36
|
|
|
37
37
|
if (isLegendTop) marginBottom = 27
|
|
38
38
|
|
|
39
|
-
if (isLegendTop && config.
|
|
39
|
+
if (isLegendTop && config.yAxis?.inlineLabel) marginBottom += 9
|
|
40
40
|
|
|
41
41
|
if (isLegendBottom) marginBottom += 9
|
|
42
42
|
|
|
@@ -38,16 +38,13 @@ const LineChart = (props: LineChartProps) => {
|
|
|
38
38
|
} = props
|
|
39
39
|
|
|
40
40
|
// prettier-ignore
|
|
41
|
-
const { colorScale, config, formatNumber, handleLineType, parseDate, seriesHighlight, tableData, transformedData, updateConfig,
|
|
42
|
-
const { yScaleRight } = useRightAxis({ config, yMax, data
|
|
41
|
+
const { colorScale, config, formatNumber, handleLineType, parseDate, seriesHighlight, tableData, transformedData:data, updateConfig, } = useContext<ChartContext>(ConfigContext)
|
|
42
|
+
const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
|
|
43
43
|
const showSingleSeries = config.tooltips.singleSeries
|
|
44
|
+
if (!handleTooltipMouseOver) return
|
|
44
45
|
|
|
45
46
|
const DEBUG = false
|
|
46
47
|
const { lineDatapointStyle, showLineSeriesLabels, legend } = config
|
|
47
|
-
const isBrushOn = brushConfig.data.length > 0 && config.brush?.active
|
|
48
|
-
// if brush on use brush data and clean
|
|
49
|
-
const data = isBrushOn ? clean(brushConfig.data) : transformedData
|
|
50
|
-
const _tableData = isBrushOn ? clean(brushConfig.data) : tableData
|
|
51
48
|
|
|
52
49
|
const xPos = d => {
|
|
53
50
|
return xScale(getXAxisData(d)) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
|