@cdc/chart 4.24.9 → 4.24.11
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 +45911 -41739
- package/examples/feature/boxplot/boxplot-data.json +88 -22
- package/examples/feature/boxplot/boxplot.json +540 -16
- package/examples/feature/boxplot/testing.csv +7 -7
- package/examples/feature/sankey/sankey-example-data.json +0 -1
- package/examples/private/test.json +20092 -0
- package/index.html +4 -4
- package/package.json +2 -2
- package/src/CdcChart.tsx +209 -188
- package/src/_stories/Chart.CustomColors.stories.tsx +19 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +27 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +74 -0
- package/src/_stories/Chart.stories.tsx +30 -3
- package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
- package/src/_stories/ChartEditor.stories.tsx +27 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +25 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +159 -0
- package/src/_stories/_mock/boxplot_multiseries.json +647 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +723 -0
- package/src/_stories/_mock/dynamic_series_config.json +979 -0
- package/src/_stories/_mock/horizontal_bar.json +257 -0
- package/src/_stories/_mock/large_x_axis_labels.json +261 -0
- package/src/_stories/_mock/paired-bar.json +262 -0
- package/src/_stories/_mock/pie_with_data.json +255 -0
- package/{examples/feature/scatterplot/scatterplot.json → src/_stories/_mock/scatterplot_mock.json} +62 -92
- package/src/_stories/_mock/simplified_line.json +1510 -0
- package/src/_stories/_mock/suppression_mock.json +1549 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +22 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +43 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +123 -47
- package/src/components/BarChart/helpers/index.ts +23 -5
- package/src/components/BoxPlot/BoxPlot.tsx +189 -0
- package/src/components/BrushChart.tsx +3 -2
- package/src/components/DeviationBar.jsx +58 -8
- package/src/components/EditorPanel/EditorPanel.tsx +127 -102
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +11 -28
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +51 -6
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +40 -9
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +121 -56
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +296 -35
- package/src/components/EditorPanel/components/panels.scss +4 -6
- package/src/components/EditorPanel/editor-panel.scss +0 -8
- package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +16 -1
- package/src/components/ForestPlot/ForestPlot.tsx +2 -3
- package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
- package/src/components/Legend/Legend.Component.tsx +23 -24
- package/src/components/Legend/Legend.Suppression.tsx +25 -20
- package/src/components/Legend/Legend.tsx +16 -18
- package/src/components/Legend/helpers/index.ts +16 -19
- package/src/components/LegendWrapper.tsx +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +10 -0
- package/src/components/LineChart/helpers.ts +48 -43
- package/src/components/LineChart/index.tsx +88 -82
- package/src/components/LinearChart.tsx +747 -562
- package/src/components/PairedBarChart.jsx +50 -10
- package/src/components/PieChart/PieChart.tsx +1 -6
- package/src/components/Regions/components/Regions.tsx +33 -19
- package/src/components/Sankey/index.tsx +50 -32
- package/src/components/Sankey/sankey.scss +6 -5
- package/src/components/Sankey/useSankeyAlert.tsx +60 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +20 -4
- package/src/components/ZoomBrush.tsx +25 -6
- package/src/coreStyles_chart.scss +3 -0
- package/src/data/initial-state.js +8 -10
- package/src/helpers/configHelpers.ts +28 -0
- package/src/helpers/handleRankByValue.ts +15 -0
- package/src/helpers/sizeHelpers.ts +25 -0
- package/src/helpers/tests/handleRankByValue.test.ts +37 -0
- package/src/helpers/tests/sizeHelpers.test.ts +80 -0
- package/src/hooks/useColorPalette.js +10 -2
- package/src/hooks/useLegendClasses.ts +13 -22
- package/src/hooks/useMinMax.ts +27 -13
- package/src/hooks/useReduceData.ts +43 -10
- package/src/hooks/useScales.ts +87 -38
- package/src/hooks/useTooltip.tsx +62 -53
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +5 -4
- package/src/scss/main.scss +57 -70
- package/src/types/ChartConfig.ts +43 -34
- package/src/types/ChartContext.ts +22 -15
- package/src/types/ForestPlot.ts +8 -0
- package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
- package/src/_stories/ChartBrush.stories.tsx +0 -19
- package/src/components/BoxPlot/BoxPlot.jsx +0 -111
- package/src/components/LinearChart.jsx +0 -817
|
@@ -6,9 +6,10 @@ import { Text } from '@visx/text'
|
|
|
6
6
|
|
|
7
7
|
import ConfigContext from '../ConfigContext'
|
|
8
8
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
9
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
9
10
|
|
|
10
11
|
const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
11
|
-
const { config, colorScale, transformedData: data, formatNumber, seriesHighlight
|
|
12
|
+
const { config, colorScale, transformedData: data, formatNumber, seriesHighlight } = useContext(ConfigContext)
|
|
12
13
|
|
|
13
14
|
if (!config || config?.series?.length < 2) return
|
|
14
15
|
|
|
@@ -79,14 +80,27 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
79
80
|
}
|
|
80
81
|
`}
|
|
81
82
|
</style>
|
|
82
|
-
<svg
|
|
83
|
+
<svg
|
|
84
|
+
id='cdc-visualization__paired-bar-chart'
|
|
85
|
+
width={originalWidth}
|
|
86
|
+
height={height}
|
|
87
|
+
viewBox={`0 0 ${width + Number(config.runtime.yAxis.size)} ${height}`}
|
|
88
|
+
role='img'
|
|
89
|
+
tabIndex={0}
|
|
90
|
+
>
|
|
83
91
|
<title>{`Paired bar chart graphic with the title ${config.title ? config.title : 'No Title Found'}`}</title>
|
|
84
92
|
<Group top={0} left={Number(config.xAxis.size)}>
|
|
85
93
|
{data
|
|
86
94
|
.filter(item => config.series[0].dataKey === groupOne.dataKey)
|
|
87
95
|
.map((d, index) => {
|
|
88
|
-
let transparentBar =
|
|
89
|
-
|
|
96
|
+
let transparentBar =
|
|
97
|
+
config.legend.behavior === 'highlight' &&
|
|
98
|
+
seriesHighlight.length > 0 &&
|
|
99
|
+
seriesHighlight.indexOf(config.series[0].dataKey) === -1
|
|
100
|
+
let displayBar =
|
|
101
|
+
config.legend.behavior === 'highlight' ||
|
|
102
|
+
seriesHighlight.length === 0 ||
|
|
103
|
+
seriesHighlight.indexOf(config.series[0].dataKey) !== -1
|
|
90
104
|
let barWidth = xScale(d[config.series[0].dataKey])
|
|
91
105
|
let barHeight = Number(config.barHeight) ? Number(config.barHeight) : 25
|
|
92
106
|
// update bar Y to give dynamic Y when user applyes BarSpace
|
|
@@ -95,7 +109,10 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
95
109
|
const totalheight = (Number(config.barSpace) + barHeight + borderWidth) * data.length
|
|
96
110
|
config.heights.horizontal = totalheight
|
|
97
111
|
// check if text fits inside of the bar including suffix/prefix,comma,fontSize ..etc
|
|
98
|
-
const textWidth = getTextWidth(
|
|
112
|
+
const textWidth = getTextWidth(
|
|
113
|
+
formatNumber(d[groupOne.dataKey], 'left'),
|
|
114
|
+
`normal ${fontSize[config.fontSize]}px sans-serif`
|
|
115
|
+
)
|
|
99
116
|
const textFits = textWidth < barWidth - 5 // minus padding dx(5)
|
|
100
117
|
|
|
101
118
|
return (
|
|
@@ -119,7 +136,14 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
119
136
|
tabIndex={-1}
|
|
120
137
|
/>
|
|
121
138
|
{config.yAxis.displayNumbersOnBar && displayBar && (
|
|
122
|
-
<Text
|
|
139
|
+
<Text
|
|
140
|
+
textAnchor={textFits ? 'start' : 'end'}
|
|
141
|
+
dx={textFits ? 5 : -5}
|
|
142
|
+
verticalAnchor='middle'
|
|
143
|
+
x={halfWidth - barWidth}
|
|
144
|
+
y={y + config.barHeight / 2}
|
|
145
|
+
fill={textFits ? groupOne.labelColor : '#000'}
|
|
146
|
+
>
|
|
123
147
|
{formatNumber(d[groupOne.dataKey], 'left')}
|
|
124
148
|
</Text>
|
|
125
149
|
)}
|
|
@@ -131,8 +155,14 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
131
155
|
.filter(item => config.series[1].dataKey === groupTwo.dataKey)
|
|
132
156
|
.map((d, index) => {
|
|
133
157
|
let barWidth = xScale(d[config.series[1].dataKey])
|
|
134
|
-
let transparentBar =
|
|
135
|
-
|
|
158
|
+
let transparentBar =
|
|
159
|
+
config.legend.behavior === 'highlight' &&
|
|
160
|
+
seriesHighlight.length > 0 &&
|
|
161
|
+
seriesHighlight.indexOf(config.series[1].dataKey) === -1
|
|
162
|
+
let displayBar =
|
|
163
|
+
config.legend.behavior === 'highlight' ||
|
|
164
|
+
seriesHighlight.length === 0 ||
|
|
165
|
+
seriesHighlight.indexOf(config.series[1].dataKey) !== -1
|
|
136
166
|
let barHeight = config.barHeight ? Number(config.barHeight) : 25
|
|
137
167
|
// update bar Y to give dynamic Y when user applyes BarSpace
|
|
138
168
|
let y = 0
|
|
@@ -140,7 +170,10 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
140
170
|
const totalheight = (Number(config.barSpace) + barHeight + borderWidth) * data.length
|
|
141
171
|
config.heights.horizontal = totalheight
|
|
142
172
|
// check if text fits inside of the bar including suffix/prefix,comma,fontSize ..etc
|
|
143
|
-
const textWidth = getTextWidth(
|
|
173
|
+
const textWidth = getTextWidth(
|
|
174
|
+
formatNumber(d[groupTwo.dataKey], 'left'),
|
|
175
|
+
`normal ${fontSize[config.fontSize]}px sans-serif`
|
|
176
|
+
)
|
|
144
177
|
const isTextFits = textWidth < barWidth - 5 // minus padding dx(5)
|
|
145
178
|
|
|
146
179
|
return (
|
|
@@ -171,7 +204,14 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
171
204
|
tabIndex={-1}
|
|
172
205
|
/>
|
|
173
206
|
{config.yAxis.displayNumbersOnBar && displayBar && (
|
|
174
|
-
<Text
|
|
207
|
+
<Text
|
|
208
|
+
textAnchor={isTextFits ? 'end' : 'start'}
|
|
209
|
+
dx={isTextFits ? -5 : 5}
|
|
210
|
+
verticalAnchor='middle'
|
|
211
|
+
x={halfWidth + barWidth}
|
|
212
|
+
y={y + config.barHeight / 2}
|
|
213
|
+
fill={isTextFits ? groupTwo.labelColor : '#000'}
|
|
214
|
+
>
|
|
175
215
|
{formatNumber(d[groupTwo.dataKey], 'left')}
|
|
176
216
|
</Text>
|
|
177
217
|
)}
|
|
@@ -38,9 +38,6 @@ const PieChart = props => {
|
|
|
38
38
|
config,
|
|
39
39
|
colorScale,
|
|
40
40
|
currentViewport,
|
|
41
|
-
dimensions,
|
|
42
|
-
highlight,
|
|
43
|
-
highlightReset,
|
|
44
41
|
seriesHighlight,
|
|
45
42
|
isDraggingAnnotation
|
|
46
43
|
} = useContext(ConfigContext)
|
|
@@ -232,8 +229,6 @@ const PieChart = props => {
|
|
|
232
229
|
}
|
|
233
230
|
}, [seriesHighlight]) // eslint-disable-line
|
|
234
231
|
|
|
235
|
-
const createLegendLabels = createFormatLabels(config, [], _data, _colorScale)
|
|
236
|
-
|
|
237
232
|
const getSvgClasses = () => {
|
|
238
233
|
let classes = ['animated-pie', 'group']
|
|
239
234
|
if (config.animate === false || animatedPie) {
|
|
@@ -280,7 +275,7 @@ const PieChart = props => {
|
|
|
280
275
|
<TooltipWithBounds
|
|
281
276
|
key={Math.random()}
|
|
282
277
|
className={'tooltip cdc-open-viz-module'}
|
|
283
|
-
left={tooltipLeft}
|
|
278
|
+
left={tooltipLeft + centerX - radius}
|
|
284
279
|
top={tooltipTop}
|
|
285
280
|
>
|
|
286
281
|
<ul>
|
|
@@ -19,7 +19,18 @@ type RegionsProps = {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// TODO: should regions be removed on categorical axis?
|
|
22
|
-
const Regions: React.FC<RegionsProps> = ({
|
|
22
|
+
const Regions: React.FC<RegionsProps> = ({
|
|
23
|
+
xScale,
|
|
24
|
+
barWidth = 0,
|
|
25
|
+
totalBarsInGroup = 1,
|
|
26
|
+
yMax,
|
|
27
|
+
handleTooltipMouseOff,
|
|
28
|
+
handleTooltipMouseOver,
|
|
29
|
+
handleTooltipClick,
|
|
30
|
+
tooltipData,
|
|
31
|
+
showTooltip,
|
|
32
|
+
hideTooltip
|
|
33
|
+
}) => {
|
|
23
34
|
const { parseDate, config } = useContext<ChartContext>(ConfigContext)
|
|
24
35
|
|
|
25
36
|
const { runtime, regions, visualizationType, orientation, xAxis } = config
|
|
@@ -44,7 +55,10 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
|
|
|
44
55
|
const previousDays = Number(region.from) || 0
|
|
45
56
|
const categoricalDomain = domain.map(d => formatDate(config.xAxis.dateParseFormat, new Date(d)))
|
|
46
57
|
const d = region.toType === 'Last Date' ? new Date(domain[domain.length - 1]).getTime() : new Date(region.to) // on categorical charts force leading zero 03/15/2016 vs 3/15/2016 for valid date format
|
|
47
|
-
const to =
|
|
58
|
+
const to =
|
|
59
|
+
config.xAxis.type === 'categorical'
|
|
60
|
+
? formatDate(config.xAxis.dateParseFormat, d)
|
|
61
|
+
: formatDate(config.xAxis.dateParseFormat, d)
|
|
48
62
|
const toDate = new Date(to)
|
|
49
63
|
from = new Date(toDate.setDate(toDate.getDate() - Number(previousDays)))
|
|
50
64
|
|
|
@@ -120,7 +134,12 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
|
|
|
120
134
|
}
|
|
121
135
|
if (region.toType === 'Last Date') {
|
|
122
136
|
const lastDate = domain[domain.length - 1]
|
|
123
|
-
to = Number(
|
|
137
|
+
to = Number(
|
|
138
|
+
xScale(lastDate) +
|
|
139
|
+
((visualizationType === 'Bar' || visualizationType === 'Combo') && config.xAxis.type === 'date'
|
|
140
|
+
? barWidth * totalBarsInGroup
|
|
141
|
+
: 0)
|
|
142
|
+
)
|
|
124
143
|
}
|
|
125
144
|
|
|
126
145
|
if (visualizationType === 'Line' || visualizationType === 'Area Chart') {
|
|
@@ -152,27 +171,22 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
|
|
|
152
171
|
if (!from) return null
|
|
153
172
|
if (!to) return null
|
|
154
173
|
|
|
155
|
-
const TopRegionBorderShape = () => {
|
|
156
|
-
return (
|
|
157
|
-
<path
|
|
158
|
-
stroke='#333'
|
|
159
|
-
d={`M${from} -5
|
|
160
|
-
L${from} 5
|
|
161
|
-
M${from} 0
|
|
162
|
-
L${to} 0
|
|
163
|
-
M${to} -5
|
|
164
|
-
L${to} 5`}
|
|
165
|
-
/>
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
174
|
const HighlightedArea = () => {
|
|
170
175
|
return <rect x={from} y={0} width={width} height={yMax} fill={region.background} opacity={0.3} />
|
|
171
176
|
}
|
|
172
177
|
|
|
173
178
|
return (
|
|
174
|
-
<Group
|
|
175
|
-
|
|
179
|
+
<Group
|
|
180
|
+
height={100}
|
|
181
|
+
fill='red'
|
|
182
|
+
className='regions regions-group--line zzz'
|
|
183
|
+
key={region.label}
|
|
184
|
+
onMouseMove={handleTooltipMouseOver}
|
|
185
|
+
onMouseLeave={handleTooltipMouseOff}
|
|
186
|
+
handleTooltipClick={handleTooltipClick}
|
|
187
|
+
tooltipData={JSON.stringify(tooltipData)}
|
|
188
|
+
showTooltip={showTooltip}
|
|
189
|
+
>
|
|
176
190
|
<HighlightedArea />
|
|
177
191
|
<Text x={from + width / 2} y={5} fill={region.color} verticalAnchor='start' textAnchor='middle'>
|
|
178
192
|
{region.label}
|
|
@@ -14,9 +14,10 @@ import ConfigContext from '@cdc/chart/src/ConfigContext'
|
|
|
14
14
|
import { ChartContext } from '../../types/ChartContext'
|
|
15
15
|
import type { SankeyNode, SankeyProps } from './types'
|
|
16
16
|
import { SankeyChartConfig, AllChartsConfig } from '../../types/ChartConfig'
|
|
17
|
+
import useSankeyAlert from './useSankeyAlert'
|
|
17
18
|
|
|
18
19
|
const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
19
|
-
const { config } = useContext<ChartContext>(ConfigContext)
|
|
20
|
+
const { config, handleChartTabbing, legendId } = useContext<ChartContext>(ConfigContext)
|
|
20
21
|
const { sankey: sankeyConfig } = config
|
|
21
22
|
|
|
22
23
|
const isSankeyChartConfig = (config: AllChartsConfig | SankeyChartConfig): config is SankeyChartConfig => {
|
|
@@ -28,8 +29,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
28
29
|
|
|
29
30
|
//Tooltip
|
|
30
31
|
const [tooltipID, setTooltipID] = useState<string>('')
|
|
31
|
-
|
|
32
|
-
const [showPopup, setShowPopup] = useState(false)
|
|
32
|
+
const { showAlert, alert } = useSankeyAlert()
|
|
33
33
|
|
|
34
34
|
const handleNodeClick = (nodeId: string) => {
|
|
35
35
|
// Store the previous tooltipID
|
|
@@ -46,16 +46,6 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (window.innerWidth < 768 && window.innerHeight > window.innerWidth) {
|
|
51
|
-
setShowPopup(true)
|
|
52
|
-
}
|
|
53
|
-
}, [window.innerWidth])
|
|
54
|
-
|
|
55
|
-
const closePopUp = () => {
|
|
56
|
-
setShowPopup(false)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
49
|
// Uses Visx Groups innerRef to get all Group elements that are mapped.
|
|
60
50
|
// Sets the largest group width in state and subtracts that group the svg width to calculate overall width.
|
|
61
51
|
useEffect(() => {
|
|
@@ -313,7 +303,11 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
313
303
|
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
314
304
|
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
315
305
|
>
|
|
316
|
-
<tspan className={classStyle}>
|
|
306
|
+
<tspan className={classStyle}>
|
|
307
|
+
{sankeyConfig.nodeValueStyle.textBefore +
|
|
308
|
+
(typeof node.value === 'number' ? node.value.toLocaleString() : node.value) +
|
|
309
|
+
sankeyConfig.nodeValueStyle.textAfter}
|
|
310
|
+
</tspan>
|
|
317
311
|
</text>
|
|
318
312
|
</>
|
|
319
313
|
)}
|
|
@@ -415,7 +409,16 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
415
409
|
>
|
|
416
410
|
{(data?.storyNodeText?.find(storyNode => storyNode.StoryNode === node.id) || {}).segmentTextBefore}
|
|
417
411
|
</Text>
|
|
418
|
-
<Text
|
|
412
|
+
<Text
|
|
413
|
+
verticalAnchor='end'
|
|
414
|
+
className={classStyle}
|
|
415
|
+
x={node.x0! + textPositionHorizontal}
|
|
416
|
+
y={(node.y1! + node.y0! + 25) / 2}
|
|
417
|
+
fill={sankeyConfig.storyNodeFontColor || sankeyConfig.nodeFontColor}
|
|
418
|
+
fontWeight='bold'
|
|
419
|
+
textAnchor='start'
|
|
420
|
+
style={{ pointerEvents: 'none' }}
|
|
421
|
+
>
|
|
419
422
|
{typeof node.value === 'number' ? node.value.toLocaleString() : node.value}
|
|
420
423
|
</Text>
|
|
421
424
|
<Text
|
|
@@ -434,7 +437,15 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
434
437
|
</>
|
|
435
438
|
) : (
|
|
436
439
|
<>
|
|
437
|
-
<text
|
|
440
|
+
<text
|
|
441
|
+
x={node.x0! + textPositionHorizontal}
|
|
442
|
+
y={(node.y1! + node.y0!) / 2 + textPositionVertical}
|
|
443
|
+
dominantBaseline='text-before-edge'
|
|
444
|
+
fill={sankeyConfig.nodeFontColor}
|
|
445
|
+
fontWeight='bold'
|
|
446
|
+
textAnchor='start'
|
|
447
|
+
style={{ pointerEvents: 'none' }}
|
|
448
|
+
>
|
|
438
449
|
<tspan id={node.id} className='node-id'>
|
|
439
450
|
{node.id}
|
|
440
451
|
</tspan>
|
|
@@ -451,7 +462,9 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
451
462
|
style={{ pointerEvents: 'none' }}
|
|
452
463
|
>
|
|
453
464
|
<tspan onClick={() => handleNodeClick(node.id)} className={classStyle}>
|
|
454
|
-
{sankeyConfig.nodeValueStyle.textBefore +
|
|
465
|
+
{sankeyConfig.nodeValueStyle.textBefore +
|
|
466
|
+
(typeof node.value === 'number' ? node.value.toLocaleString() : node.value) +
|
|
467
|
+
sankeyConfig.nodeValueStyle.textAfter}
|
|
455
468
|
</tspan>
|
|
456
469
|
</text>
|
|
457
470
|
</>
|
|
@@ -460,10 +473,15 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
460
473
|
)
|
|
461
474
|
})
|
|
462
475
|
|
|
463
|
-
return (
|
|
476
|
+
return !showAlert ? (
|
|
464
477
|
<>
|
|
465
478
|
<div className='sankey-chart'>
|
|
466
|
-
<svg
|
|
479
|
+
<svg
|
|
480
|
+
className='sankey-chart__diagram'
|
|
481
|
+
width={width}
|
|
482
|
+
height={Number(config.heights.vertical)}
|
|
483
|
+
style={{ overflow: 'visible' }}
|
|
484
|
+
>
|
|
467
485
|
<Group className='links'>{allLinks}</Group>
|
|
468
486
|
<Group className='nodes'>{allNodes}</Group>
|
|
469
487
|
<Group className='finalNodes' style={{ display: 'none' }}>
|
|
@@ -473,21 +491,21 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
473
491
|
|
|
474
492
|
{/* ReactTooltip needs to remain even if tooltips are disabled -- it handles when a user clicks off of the node and resets
|
|
475
493
|
the sankey diagram. When tooltips are disabled this will nothing */}
|
|
476
|
-
<ReactTooltip
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
</div>
|
|
488
|
-
)}
|
|
494
|
+
<ReactTooltip
|
|
495
|
+
id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
496
|
+
afterHide={() => setTooltipID('')}
|
|
497
|
+
events={['click']}
|
|
498
|
+
place={'bottom'}
|
|
499
|
+
style={{
|
|
500
|
+
backgroundColor: `rgba(238, 238, 238, 1)`,
|
|
501
|
+
color: 'black',
|
|
502
|
+
boxShadow: `0 3px 10px rgb(0 0 0 / 0.2)`
|
|
503
|
+
}}
|
|
504
|
+
/>
|
|
489
505
|
</div>
|
|
490
506
|
</>
|
|
507
|
+
) : (
|
|
508
|
+
alert
|
|
491
509
|
)
|
|
492
510
|
}
|
|
493
511
|
export default Sankey
|
|
@@ -25,8 +25,12 @@
|
|
|
25
25
|
margin: 10px 0;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
.alert-dismissible {
|
|
29
|
+
padding-right: 4rem !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
svg.sankey-chart__diagram {
|
|
29
|
-
position:relative;
|
|
33
|
+
position: relative;
|
|
30
34
|
font-family: 'Roboto', sans-serif;
|
|
31
35
|
height: auto;
|
|
32
36
|
width: 100%;
|
|
@@ -124,9 +128,6 @@
|
|
|
124
128
|
.popup {
|
|
125
129
|
display: block; /* Show the popup on smaller screens */
|
|
126
130
|
}
|
|
127
|
-
.sankey-chart__diagram {
|
|
128
|
-
opacity: .1;
|
|
129
|
-
}
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
@@ -150,4 +151,4 @@
|
|
|
150
151
|
font-size: 30px;
|
|
151
152
|
padding: 10px;
|
|
152
153
|
text-align: center;
|
|
153
|
-
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useState, useEffect, useContext } from 'react'
|
|
2
|
+
import { ChartContext } from '../../types/ChartContext'
|
|
3
|
+
import ConfigContext from '../../ConfigContext'
|
|
4
|
+
|
|
5
|
+
const useSankeyAlert = () => {
|
|
6
|
+
const { config, handleChartTabbing, legendId } = useContext<ChartContext>(ConfigContext)
|
|
7
|
+
|
|
8
|
+
//Mobile Pop Up
|
|
9
|
+
const [showAlert, setShowAlert] = useState(false)
|
|
10
|
+
const alertMessage = (
|
|
11
|
+
<>
|
|
12
|
+
For best viewing we recommend portrait mode. If you are unable to put your device in portrait mode, please review
|
|
13
|
+
the <a href={`#${handleChartTabbing(config, legendId)}`}>data table</a> below.{' '}
|
|
14
|
+
<a onClick={() => setShowAlert(false)} href={'#!'}>
|
|
15
|
+
Close this alert
|
|
16
|
+
</a>{' '}
|
|
17
|
+
to continue viewing the chart.
|
|
18
|
+
</>
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const handleCloseModal = () => {
|
|
22
|
+
setShowAlert(false)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const alert = showAlert ? (
|
|
26
|
+
<div className='alert alert-warning alert-dismissible' role='alert'>
|
|
27
|
+
<p style={{ padding: '35px' }}>{alertMessage}</p>
|
|
28
|
+
<button type='button' className='close' data-dismiss='alert' aria-label='Close' onClick={handleCloseModal}>
|
|
29
|
+
<span aria-hidden='true'>×</span>
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
) : null
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const handleResize = () => {
|
|
36
|
+
if (window.innerWidth < 768 && window.innerHeight > window.innerWidth) {
|
|
37
|
+
setShowAlert(true)
|
|
38
|
+
} else {
|
|
39
|
+
setShowAlert(false)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
window.addEventListener('resize', handleResize)
|
|
44
|
+
handleResize() // Call the function initially to set the state based on the initial window size
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
window.removeEventListener('resize', handleResize)
|
|
48
|
+
}
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
setShowAlert,
|
|
53
|
+
showAlert,
|
|
54
|
+
handleCloseModal,
|
|
55
|
+
alertMessage,
|
|
56
|
+
alert
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default useSankeyAlert
|
|
@@ -4,7 +4,14 @@ import { Group } from '@visx/group'
|
|
|
4
4
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
5
5
|
|
|
6
6
|
const ScatterPlot = ({ xScale, yScale }) => {
|
|
7
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
transformedData: data,
|
|
9
|
+
config,
|
|
10
|
+
tableData,
|
|
11
|
+
formatNumber,
|
|
12
|
+
seriesHighlight,
|
|
13
|
+
colorPalettes
|
|
14
|
+
} = useContext(ConfigContext)
|
|
8
15
|
|
|
9
16
|
// TODO: copied from line chart should probably be a constant somewhere.
|
|
10
17
|
const circleRadii = 4.5
|
|
@@ -23,10 +30,19 @@ const ScatterPlot = ({ xScale, yScale }) => {
|
|
|
23
30
|
}
|
|
24
31
|
])
|
|
25
32
|
const handleTooltip = (item, s, dataIndex) => `<div>
|
|
26
|
-
${
|
|
33
|
+
${
|
|
34
|
+
config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries
|
|
35
|
+
? `${config.runtime.seriesLabels[s] || ''}<br/>`
|
|
36
|
+
: ''
|
|
37
|
+
}
|
|
27
38
|
${config.xAxis.label}: ${formatNumber(item[config.xAxis.dataKey], 'bottom')} <br/>
|
|
28
39
|
${config.yAxis.label}: ${formatNumber(item[s], 'left')}<br/>
|
|
29
|
-
${additionalColumns
|
|
40
|
+
${additionalColumns
|
|
41
|
+
.map(
|
|
42
|
+
([label, name, options]) =>
|
|
43
|
+
`${label} : ${formatColNumber(tableData[dataIndex][name], 'left', false, config, options)}<br/>`
|
|
44
|
+
)
|
|
45
|
+
.join('')}
|
|
30
46
|
</div>`
|
|
31
47
|
|
|
32
48
|
return (
|
|
@@ -36,7 +52,7 @@ const ScatterPlot = ({ xScale, yScale }) => {
|
|
|
36
52
|
return config.runtime.seriesKeys.map((s, index) => {
|
|
37
53
|
const transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(s) === -1
|
|
38
54
|
const displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(s) !== -1
|
|
39
|
-
const seriesColor = config.palette ? colorPalettes[config.palette][index] : '#000'
|
|
55
|
+
const seriesColor = config?.customColors ? config.customColors[index] : config.palette ? colorPalettes[config.palette][index] : '#000'
|
|
40
56
|
|
|
41
57
|
let pointStyles = {
|
|
42
58
|
filter: 'unset',
|
|
@@ -7,6 +7,7 @@ import ConfigContext from '../ConfigContext'
|
|
|
7
7
|
import { ScaleLinear, ScaleBand } from 'd3-scale'
|
|
8
8
|
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
9
9
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
10
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
13
|
xScaleBrush: ScaleLinear<number, number>
|
|
@@ -15,7 +16,7 @@ interface Props {
|
|
|
15
16
|
yMax: number
|
|
16
17
|
}
|
|
17
18
|
const ZoomBrush: FC<Props> = props => {
|
|
18
|
-
const { tableData, config, parseDate, formatDate, setBrushConfig,
|
|
19
|
+
const { tableData, config, parseDate, formatDate, setBrushConfig, dashboardConfig } = useContext(ConfigContext)
|
|
19
20
|
const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
|
|
20
21
|
const isDashboardFilters = sharedFilters?.length > 0
|
|
21
22
|
const { fontSize } = useBarChart()
|
|
@@ -175,7 +176,6 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
175
176
|
<BrushHandle
|
|
176
177
|
left={Number(config.runtime.yAxis.size)}
|
|
177
178
|
showTooltip={showTooltip}
|
|
178
|
-
getTextWidth={getTextWidth}
|
|
179
179
|
pixelDistance={textProps.endPosition - textProps.startPosition}
|
|
180
180
|
textProps={textProps}
|
|
181
181
|
fontSize={fontSize[config.fontSize]}
|
|
@@ -202,7 +202,7 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
const BrushHandle = props => {
|
|
205
|
-
const { x, isBrushActive, isBrushing, className, textProps, fontSize, showTooltip, left
|
|
205
|
+
const { x, isBrushActive, isBrushing, className, textProps, fontSize, showTooltip, left } = props
|
|
206
206
|
const pathWidth = 8
|
|
207
207
|
if (!isBrushActive) {
|
|
208
208
|
return null
|
|
@@ -217,15 +217,34 @@ const BrushHandle = props => {
|
|
|
217
217
|
return (
|
|
218
218
|
<>
|
|
219
219
|
{showTooltip && (
|
|
220
|
-
<Text
|
|
220
|
+
<Text
|
|
221
|
+
x={(Number(textProps.xMax) - textWidth) / 2}
|
|
222
|
+
dy={-12}
|
|
223
|
+
pointerEvents='visiblePainted'
|
|
224
|
+
fontSize={fontSize / 1.1}
|
|
225
|
+
>
|
|
221
226
|
{tooltipText}
|
|
222
227
|
</Text>
|
|
223
228
|
)}
|
|
224
229
|
<Group left={x + pathWidth / 2} top={-2}>
|
|
225
|
-
<Text
|
|
230
|
+
<Text
|
|
231
|
+
pointerEvents='visiblePainted'
|
|
232
|
+
dominantBaseline='hanging'
|
|
233
|
+
x={isLeft ? 55 : -50}
|
|
234
|
+
y={25}
|
|
235
|
+
verticalAnchor='start'
|
|
236
|
+
textAnchor={textAnchor}
|
|
237
|
+
fontSize={fontSize / 1.4}
|
|
238
|
+
>
|
|
226
239
|
{isLeft ? textProps.startValue : textProps.endValue}
|
|
227
240
|
</Text>
|
|
228
|
-
<path
|
|
241
|
+
<path
|
|
242
|
+
cursor='ew-resize'
|
|
243
|
+
d='M0.5,10A6,6 0 0 1 6.5,16V14A6,6 0 0 1 0.5,20ZM2.5,18V12M4.5,18V12'
|
|
244
|
+
fill={'#297EF1'}
|
|
245
|
+
strokeWidth='1'
|
|
246
|
+
transform={transform}
|
|
247
|
+
></path>
|
|
229
248
|
</Group>
|
|
230
249
|
</>
|
|
231
250
|
)
|
|
@@ -28,6 +28,7 @@ export default {
|
|
|
28
28
|
showDownloadButton: false,
|
|
29
29
|
showMissingDataLabel: true,
|
|
30
30
|
showSuppressedSymbol: true,
|
|
31
|
+
showZeroValueData: true,
|
|
31
32
|
hideNullValue: true
|
|
32
33
|
},
|
|
33
34
|
padding: {
|
|
@@ -67,15 +68,8 @@ export default {
|
|
|
67
68
|
boxplot: {
|
|
68
69
|
plots: [],
|
|
69
70
|
borders: 'true',
|
|
70
|
-
firstQuartilePercentage: 25,
|
|
71
|
-
thirdQuartilePercentage: 75,
|
|
72
|
-
boxWidthPercentage: 40,
|
|
73
71
|
plotOutlierValues: false,
|
|
74
72
|
plotNonOutlierValues: true,
|
|
75
|
-
legend: {
|
|
76
|
-
showHowToReadText: false,
|
|
77
|
-
howToReadText: ''
|
|
78
|
-
},
|
|
79
73
|
labels: {
|
|
80
74
|
q1: 'Lower Quartile',
|
|
81
75
|
q2: 'q2',
|
|
@@ -87,7 +81,7 @@ export default {
|
|
|
87
81
|
median: 'Median',
|
|
88
82
|
sd: 'Standard Deviation',
|
|
89
83
|
iqr: 'Interquartile Range',
|
|
90
|
-
|
|
84
|
+
count: 'Count',
|
|
91
85
|
outliers: 'Outliers',
|
|
92
86
|
values: 'Values',
|
|
93
87
|
lowerBounds: 'Lower Bounds',
|
|
@@ -122,10 +116,13 @@ export default {
|
|
|
122
116
|
tickLabelColor: '#333',
|
|
123
117
|
tickColor: '#333',
|
|
124
118
|
numTicks: '',
|
|
125
|
-
labelOffset:
|
|
119
|
+
labelOffset: 0,
|
|
126
120
|
axisPadding: 200,
|
|
127
121
|
target: 0,
|
|
128
|
-
maxTickRotation: 0
|
|
122
|
+
maxTickRotation: 0,
|
|
123
|
+
padding: 5,
|
|
124
|
+
showYearsOnce: false,
|
|
125
|
+
sortByRecentDate: false
|
|
129
126
|
},
|
|
130
127
|
table: {
|
|
131
128
|
label: 'Data Table',
|
|
@@ -135,6 +132,7 @@ export default {
|
|
|
135
132
|
caption: '',
|
|
136
133
|
showDownloadUrl: false,
|
|
137
134
|
showDataTableLink: true,
|
|
135
|
+
showDownloadLinkBelow: true,
|
|
138
136
|
indexLabel: '',
|
|
139
137
|
download: false,
|
|
140
138
|
showVertical: true,
|