@cdc/chart 4.24.5 → 4.24.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 +44197 -38258
- package/examples/cases-year.json +13379 -0
- package/examples/feature/annotations/index.json +542 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
- package/examples/xaxis.json +493 -0
- package/index.html +20 -10
- package/package.json +5 -4
- package/src/CdcChart.tsx +462 -172
- package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
- package/src/_stories/Chart.stories.tsx +18 -171
- package/src/_stories/ChartAnnotation.stories.tsx +32 -0
- package/src/_stories/_mock/annotation_category_mock.json +473 -0
- package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
- package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
- package/src/_stories/_mock/legend.gradient_mock.json +236 -0
- package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
- package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
- package/src/_stories/_mock/lollipop.json +171 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
- package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
- package/src/components/Annotations/components/AnnotationList.tsx +42 -0
- package/src/components/Annotations/components/findNearestDatum.ts +138 -0
- package/src/components/Annotations/components/helpers/index.tsx +46 -0
- package/src/components/Annotations/index.tsx +13 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
- package/src/components/AreaChart/components/AreaChart.jsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +145 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
- package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
- package/src/components/BarChart/helpers/index.ts +91 -0
- package/src/components/BrushChart.tsx +205 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/panels.scss +4 -0
- package/src/components/EditorPanel/editor-panel.scss +35 -3
- package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
- package/src/components/Legend/Legend.Component.tsx +185 -194
- package/src/components/Legend/Legend.Suppression.tsx +146 -0
- package/src/components/Legend/Legend.tsx +21 -5
- package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
- package/src/components/Legend/helpers/index.ts +35 -0
- package/src/components/LegendWrapper.tsx +26 -0
- package/src/components/LineChart/LineChartProps.ts +1 -15
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
- package/src/components/LineChart/helpers.ts +72 -14
- package/src/components/LineChart/index.tsx +117 -42
- package/src/components/LinearChart.jsx +179 -136
- package/src/components/LinearChart.tsx +1366 -0
- package/src/components/PairedBarChart.jsx +9 -9
- package/src/components/PieChart/PieChart.tsx +75 -18
- package/src/components/Sankey/index.tsx +89 -30
- package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
- package/src/components/Sparkline/components/SparkLine.tsx +2 -2
- package/src/components/ZoomBrush.tsx +90 -44
- package/src/data/initial-state.js +25 -7
- package/src/helpers/handleChartTabbing.ts +8 -0
- package/src/helpers/isConvertLineToBarGraph.ts +4 -0
- package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
- package/src/hooks/useColorScale.ts +1 -1
- package/src/hooks/useLegendClasses.ts +68 -0
- package/src/hooks/useMinMax.ts +12 -7
- package/src/hooks/useScales.ts +58 -26
- package/src/hooks/useTooltip.tsx +135 -25
- package/src/scss/DataTable.scss +2 -1
- package/src/scss/main.scss +128 -28
- package/src/types/ChartConfig.ts +83 -10
- package/src/types/ChartContext.ts +14 -4
- package/tests-examples/helpers/testZeroValue.test.ts +30 -0
- package/LICENSE +0 -201
- package/src/components/BrushHandle.jsx +0 -17
- package/src/components/LineChart/index.scss +0 -1
- package/src/helpers/filterData.ts +0 -18
- package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
- package/src/hooks/useLegendClasses.js +0 -31
- /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
|
@@ -18,7 +18,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
18
18
|
const offset = 1.02 // Offset of the left bar from the Axis
|
|
19
19
|
|
|
20
20
|
const groupOne = {
|
|
21
|
-
parentKey: config.dataDescription
|
|
21
|
+
parentKey: config.dataDescription?.seriesKey,
|
|
22
22
|
dataKey: config.series[0].dataKey,
|
|
23
23
|
dataKeyLabel: config.runtime.seriesLabels[config.series[0].dataKey] || config.series[0].dataKey,
|
|
24
24
|
color: colorScale(config.runtime.seriesLabels[config.series[0].dataKey]),
|
|
@@ -30,7 +30,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const groupTwo = {
|
|
33
|
-
parentKey: config.dataDescription
|
|
33
|
+
parentKey: config.dataDescription?.seriesKey,
|
|
34
34
|
dataKey: config.series[1].dataKey,
|
|
35
35
|
dataKeyLabel: config.runtime.seriesLabels[config.series[1].dataKey] || config.series[1].dataKey,
|
|
36
36
|
color: colorScale(config.runtime.seriesLabels[config.series[1].dataKey]),
|
|
@@ -54,7 +54,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
54
54
|
|
|
55
55
|
const dataTipOne = d => {
|
|
56
56
|
return `<p>
|
|
57
|
-
${config.dataDescription
|
|
57
|
+
${config.dataDescription?.seriesKey}: ${groupOne.dataKeyLabel}<br/>
|
|
58
58
|
${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
|
|
59
59
|
${label}${formatNumber(d[groupOne.dataKey], 'left')}
|
|
60
60
|
</p>`
|
|
@@ -62,7 +62,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
62
62
|
|
|
63
63
|
const dataTipTwo = d => {
|
|
64
64
|
return `<p>
|
|
65
|
-
${config.dataDescription
|
|
65
|
+
${config.dataDescription?.seriesKey}: ${groupTwo.dataKeyLabel}<br/>
|
|
66
66
|
${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
|
|
67
67
|
${label}${formatNumber(d[groupTwo.dataKey], 'left')}
|
|
68
68
|
</p>`
|
|
@@ -102,9 +102,9 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
102
102
|
<>
|
|
103
103
|
<Group key={`group-${groupOne.dataKey}-${d[config.xAxis.dataKey]}`} className='horizontal'>
|
|
104
104
|
<Bar
|
|
105
|
-
id={`bar-${groupOne.dataKey}-${d[config.dataDescription
|
|
105
|
+
id={`bar-${groupOne.dataKey}-${d[config.dataDescription?.xKey]}`}
|
|
106
106
|
className='bar group-1'
|
|
107
|
-
key={`bar-${groupOne.dataKey}-${d[config.dataDescription
|
|
107
|
+
key={`bar-${groupOne.dataKey}-${d[config.dataDescription?.xKey]}`}
|
|
108
108
|
x={halfWidth - barWidth}
|
|
109
109
|
y={y}
|
|
110
110
|
width={xScale(d[config.series[0].dataKey])}
|
|
@@ -152,11 +152,11 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
152
152
|
}
|
|
153
153
|
`}
|
|
154
154
|
</style>
|
|
155
|
-
<Group key={`group-${groupTwo.dataKey}-${d[config.dataDescription
|
|
155
|
+
<Group key={`group-${groupTwo.dataKey}-${d[config.dataDescription?.xKey]}`} className='horizontal'>
|
|
156
156
|
<Bar
|
|
157
|
-
id={`bar-${groupTwo.dataKey}-${d[config.dataDescription
|
|
157
|
+
id={`bar-${groupTwo.dataKey}-${d[config.dataDescription?.xKey]}`}
|
|
158
158
|
className='bar group-2'
|
|
159
|
-
key={`bar-${groupTwo.dataKey}-${d[config.dataDescription
|
|
159
|
+
key={`bar-${groupTwo.dataKey}-${d[config.dataDescription?.xKey]}`}
|
|
160
160
|
x={halfWidth}
|
|
161
161
|
y={y}
|
|
162
162
|
width={xScale(d[config.series[1].dataKey])}
|
|
@@ -33,7 +33,17 @@ type TooltipData = {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const PieChart = props => {
|
|
36
|
-
const {
|
|
36
|
+
const {
|
|
37
|
+
transformedData: data,
|
|
38
|
+
config,
|
|
39
|
+
colorScale,
|
|
40
|
+
currentViewport,
|
|
41
|
+
dimensions,
|
|
42
|
+
highlight,
|
|
43
|
+
highlightReset,
|
|
44
|
+
seriesHighlight,
|
|
45
|
+
isDraggingAnnotation
|
|
46
|
+
} = useContext(ConfigContext)
|
|
37
47
|
const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip<TooltipData>()
|
|
38
48
|
const { handleTooltipMouseOver, handleTooltipMouseOff, TooltipListItem } = useCoveTooltip({
|
|
39
49
|
xScale: false,
|
|
@@ -135,7 +145,18 @@ const PieChart = props => {
|
|
|
135
145
|
<>
|
|
136
146
|
{transitions.map(({ item: arc, props, key }, animatedPieIndex) => {
|
|
137
147
|
return (
|
|
138
|
-
<Group
|
|
148
|
+
<Group
|
|
149
|
+
className={arc.data[config.xAxis.dataKey]}
|
|
150
|
+
key={`${key}-${animatedPieIndex}`}
|
|
151
|
+
style={{
|
|
152
|
+
opacity:
|
|
153
|
+
config.legend.behavior === 'highlight' &&
|
|
154
|
+
seriesHighlight.length > 0 &&
|
|
155
|
+
seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1
|
|
156
|
+
? 0.5
|
|
157
|
+
: 1
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
139
160
|
<animated.path
|
|
140
161
|
d={interpolate([props.startAngle, props.endAngle], (startAngle, endAngle) =>
|
|
141
162
|
path({
|
|
@@ -163,7 +184,14 @@ const PieChart = props => {
|
|
|
163
184
|
return (
|
|
164
185
|
<animated.g key={`${key}${i}`}>
|
|
165
186
|
{hasSpaceForLabel && (
|
|
166
|
-
<Text
|
|
187
|
+
<Text
|
|
188
|
+
style={{ fill: textColor }}
|
|
189
|
+
x={centroidX}
|
|
190
|
+
y={centroidY}
|
|
191
|
+
dy='.33em'
|
|
192
|
+
textAnchor='middle'
|
|
193
|
+
pointerEvents='none'
|
|
194
|
+
>
|
|
167
195
|
{Math.round((((arc.endAngle - arc.startAngle) * 180) / Math.PI / 360) * 100) + '%'}
|
|
168
196
|
</Text>
|
|
169
197
|
)}
|
|
@@ -174,17 +202,18 @@ const PieChart = props => {
|
|
|
174
202
|
)
|
|
175
203
|
}
|
|
176
204
|
|
|
177
|
-
let
|
|
205
|
+
let chartWidth = props.parentWidth
|
|
206
|
+
let width = props.parentWidth
|
|
178
207
|
|
|
179
208
|
if (config && config.legend && !config.legend.hide && currentViewport === 'lg') {
|
|
180
|
-
width =
|
|
209
|
+
width = Number(chartWidth) * 0.73
|
|
181
210
|
}
|
|
182
211
|
|
|
183
212
|
const height = config.heights.vertical
|
|
184
213
|
|
|
185
214
|
const radius = Math.min(width, height) / 2
|
|
186
215
|
const centerY = height / 2
|
|
187
|
-
const centerX =
|
|
216
|
+
const centerX = props.parentWidth / 2
|
|
188
217
|
const donutThickness = config.pieType === 'Donut' ? 75 : radius
|
|
189
218
|
|
|
190
219
|
useEffect(() => {
|
|
@@ -205,11 +234,25 @@ const PieChart = props => {
|
|
|
205
234
|
|
|
206
235
|
const createLegendLabels = createFormatLabels(config, [], _data, _colorScale)
|
|
207
236
|
|
|
237
|
+
const getSvgClasses = () => {
|
|
238
|
+
let classes = ['animated-pie', 'group']
|
|
239
|
+
if (config.animate === false || animatedPie) {
|
|
240
|
+
classes.push('animated')
|
|
241
|
+
}
|
|
242
|
+
return classes.join(' ')
|
|
243
|
+
}
|
|
244
|
+
|
|
208
245
|
return (
|
|
209
246
|
<>
|
|
210
247
|
<ErrorBoundary component='PieChart'>
|
|
211
|
-
<svg
|
|
212
|
-
|
|
248
|
+
<svg
|
|
249
|
+
width={radius * 2}
|
|
250
|
+
height={height}
|
|
251
|
+
className={getSvgClasses()}
|
|
252
|
+
role='img'
|
|
253
|
+
aria-label={handleChartAriaLabels(config)}
|
|
254
|
+
>
|
|
255
|
+
<Group top={centerY} left={radius}>
|
|
213
256
|
{/* prettier-ignore */}
|
|
214
257
|
<Pie
|
|
215
258
|
data={filteredData || _data}
|
|
@@ -223,17 +266,31 @@ const PieChart = props => {
|
|
|
223
266
|
</Group>
|
|
224
267
|
</svg>
|
|
225
268
|
<div ref={triggerRef} />
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
269
|
+
{!isDraggingAnnotation &&
|
|
270
|
+
tooltipData &&
|
|
271
|
+
Object.entries(tooltipData.data).length > 0 &&
|
|
272
|
+
tooltipOpen &&
|
|
273
|
+
showTooltip &&
|
|
274
|
+
tooltipData.dataYPosition &&
|
|
275
|
+
tooltipData.dataXPosition && (
|
|
276
|
+
<>
|
|
277
|
+
<style>{`.tooltip {background-color: rgba(255,255,255, ${
|
|
278
|
+
config.tooltips.opacity / 100
|
|
279
|
+
}) !important`}</style>
|
|
280
|
+
<TooltipWithBounds
|
|
281
|
+
key={Math.random()}
|
|
282
|
+
className={'tooltip cdc-open-viz-module'}
|
|
283
|
+
left={tooltipLeft}
|
|
284
|
+
top={tooltipTop}
|
|
285
|
+
>
|
|
286
|
+
<ul>
|
|
287
|
+
{typeof tooltipData === 'object' &&
|
|
288
|
+
Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}
|
|
289
|
+
</ul>
|
|
290
|
+
</TooltipWithBounds>
|
|
291
|
+
</>
|
|
292
|
+
)}
|
|
235
293
|
</ErrorBoundary>
|
|
236
|
-
<LegendComponent config={config} colorScale={_colorScale} seriesHighlight={seriesHighlight} highlight={highlight} highlightReset={highlightReset} currentViewport={currentViewport} formatLabels={createLegendLabels} />
|
|
237
294
|
</>
|
|
238
295
|
)
|
|
239
296
|
}
|
|
@@ -13,29 +13,38 @@ import 'react-tooltip/dist/react-tooltip.css'
|
|
|
13
13
|
import ConfigContext from '@cdc/chart/src/ConfigContext'
|
|
14
14
|
import { ChartContext } from '../../types/ChartContext'
|
|
15
15
|
import type { SankeyNode, SankeyProps } from './types'
|
|
16
|
+
import { SankeyChartConfig, AllChartsConfig } from '../../types/ChartConfig'
|
|
16
17
|
|
|
17
18
|
const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
18
|
-
const DEBUG = true
|
|
19
19
|
const { config } = useContext<ChartContext>(ConfigContext)
|
|
20
20
|
const { sankey: sankeyConfig } = config
|
|
21
|
-
|
|
22
|
-
const
|
|
21
|
+
|
|
22
|
+
const isSankeyChartConfig = (config: AllChartsConfig | SankeyChartConfig): config is SankeyChartConfig => {
|
|
23
|
+
return config.visualizationType === 'Sankey'
|
|
24
|
+
}
|
|
25
|
+
|
|
23
26
|
const [largestGroupWidth, setLargestGroupWidth] = useState(0)
|
|
24
27
|
const groupRefs = useRef([])
|
|
25
28
|
|
|
26
29
|
//Tooltip
|
|
27
30
|
const [tooltipID, setTooltipID] = useState<string>('')
|
|
31
|
+
//Mobile Pop Up
|
|
32
|
+
const [showPopup, setShowPopup] = useState(false)
|
|
28
33
|
|
|
29
34
|
const handleNodeClick = (nodeId: string) => {
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
// Store the previous tooltipID
|
|
36
|
+
const previousTooltipID = tooltipID
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
// If the previous tooltipID exists, clear it
|
|
39
|
+
if (previousTooltipID) {
|
|
40
|
+
setTooltipID('')
|
|
41
|
+
}
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
// Update the tooltipID with the new nodeId if it's different from the previous one
|
|
44
|
+
if (previousTooltipID !== nodeId) {
|
|
45
|
+
setTooltipID(nodeId)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
39
48
|
|
|
40
49
|
useEffect(() => {
|
|
41
50
|
if (window.innerWidth < 768 && window.innerHeight > window.innerWidth) {
|
|
@@ -60,6 +69,9 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
60
69
|
setLargestGroupWidth(largest)
|
|
61
70
|
}, [groupRefs, sankeyConfig, window.innerWidth])
|
|
62
71
|
|
|
72
|
+
if (!isSankeyChartConfig(config)) return
|
|
73
|
+
const data = config?.data[0]
|
|
74
|
+
|
|
63
75
|
//Retrieve all the unique values for the Nodes
|
|
64
76
|
const uniqueNodes = Array.from(new Set(data?.links?.flatMap(link => [link.source, link.target])))
|
|
65
77
|
|
|
@@ -109,6 +121,8 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
109
121
|
}
|
|
110
122
|
|
|
111
123
|
const activeConnection = (id: String) => {
|
|
124
|
+
if (!sankeyData?.nodes) return { sourceNodes: [], activeLinks: [] }
|
|
125
|
+
|
|
112
126
|
const currentNode = sankeyData.nodes.find(node => node.id === id)
|
|
113
127
|
|
|
114
128
|
const sourceNodes = []
|
|
@@ -137,12 +151,12 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
137
151
|
return { sourceNodes, activeLinks }
|
|
138
152
|
}
|
|
139
153
|
|
|
140
|
-
const tooltipVal = `${(data?.tooltips
|
|
141
|
-
const tooltipSummary = `${(data?.tooltips
|
|
142
|
-
const tooltipColumn1Label = (data?.tooltips
|
|
143
|
-
const tooltipColumn2Label = (data?.tooltips
|
|
144
|
-
const tooltipColumn1 = (data?.tooltips
|
|
145
|
-
const tooltipColumn2 = (data?.tooltips
|
|
154
|
+
const tooltipVal = `${(data?.tooltips?.find(item => item.node === tooltipID) || {}).value}`
|
|
155
|
+
const tooltipSummary = `${(data?.tooltips?.find(item => item.node === tooltipID) || {}).summary}`
|
|
156
|
+
const tooltipColumn1Label = (data?.tooltips?.find(item => item.node === tooltipID) || {}).column1Label
|
|
157
|
+
const tooltipColumn2Label = (data?.tooltips?.find(item => item.node === tooltipID) || {}).column2Label
|
|
158
|
+
const tooltipColumn1 = (data?.tooltips?.find(item => item.node === tooltipID) || {}).column1
|
|
159
|
+
const tooltipColumn2 = (data?.tooltips?.find(item => item.node === tooltipID) || {}).column2
|
|
146
160
|
|
|
147
161
|
const ColumnList = ({ columnData }) => {
|
|
148
162
|
return (
|
|
@@ -201,7 +215,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
201
215
|
fillOpacity={opacityValue}
|
|
202
216
|
rx={sankeyConfig.rxValue}
|
|
203
217
|
// todo: move enable tooltips to sankey
|
|
204
|
-
data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
|
|
218
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
205
219
|
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
206
220
|
onClick={() => handleNodeClick(node.id)}
|
|
207
221
|
style={{ pointerEvents: 'visible', cursor: 'pointer' }}
|
|
@@ -229,12 +243,27 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
229
243
|
*/
|
|
230
244
|
fill={sankeyConfig.nodeFontColor}
|
|
231
245
|
fontWeight='bold' // font weight
|
|
232
|
-
style={{ pointerEvents: 'none' }}
|
|
233
246
|
className='node-text'
|
|
247
|
+
style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
|
|
248
|
+
onClick={() => handleNodeClick(node.id)}
|
|
249
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
250
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
234
251
|
>
|
|
235
252
|
{(data?.storyNodeText?.find(storyNode => storyNode.StoryNode === node.id) || {}).segmentTextBefore}
|
|
236
253
|
</Text>
|
|
237
|
-
<Text
|
|
254
|
+
<Text
|
|
255
|
+
verticalAnchor='end'
|
|
256
|
+
className={classStyle}
|
|
257
|
+
x={node.x0! + textPositionHorizontal}
|
|
258
|
+
y={(node.y1! + node.y0! + 25) / 2}
|
|
259
|
+
fill={sankeyConfig.storyNodeFontColor || sankeyConfig.nodeFontColor}
|
|
260
|
+
fontWeight='bold'
|
|
261
|
+
textAnchor='start'
|
|
262
|
+
style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
|
|
263
|
+
onClick={() => handleNodeClick(node.id)}
|
|
264
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
265
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
266
|
+
>
|
|
238
267
|
{typeof node.value === 'number' ? node.value.toLocaleString() : node.value}
|
|
239
268
|
</Text>
|
|
240
269
|
<Text
|
|
@@ -244,20 +273,32 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
244
273
|
fill={sankeyConfig.nodeFontColor}
|
|
245
274
|
fontWeight='bold'
|
|
246
275
|
textAnchor={sankeyData.nodes.length === i ? 'end' : 'start'}
|
|
247
|
-
style={{ pointerEvents: 'none' }}
|
|
248
276
|
className='node-text'
|
|
249
277
|
verticalAnchor='end'
|
|
278
|
+
style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
|
|
279
|
+
onClick={() => handleNodeClick(node.id)}
|
|
280
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
281
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
250
282
|
>
|
|
251
283
|
{(data?.storyNodeText?.find(storyNode => storyNode.StoryNode === node.id) || {}).segmentTextAfter}
|
|
252
284
|
</Text>
|
|
253
285
|
</>
|
|
254
286
|
) : (
|
|
255
287
|
<>
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
288
|
+
<Text
|
|
289
|
+
style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
|
|
290
|
+
onClick={() => handleNodeClick(node.id)}
|
|
291
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
292
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
293
|
+
x={node.x0! + textPositionHorizontal}
|
|
294
|
+
y={(node.y1! + node.y0!) / 2 + textPositionVertical}
|
|
295
|
+
dominantBaseline='text-before-edge'
|
|
296
|
+
fill={sankeyConfig.nodeFontColor}
|
|
297
|
+
fontWeight='bold'
|
|
298
|
+
textAnchor='start'
|
|
299
|
+
>
|
|
300
|
+
{node.id}
|
|
301
|
+
</Text>
|
|
261
302
|
<text
|
|
262
303
|
x={node.x0! + textPositionHorizontal}
|
|
263
304
|
/* adding 30 allows the node value to be on the next line underneath the node id */
|
|
@@ -267,7 +308,10 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
267
308
|
//fontSize={16}
|
|
268
309
|
fontWeight='bold'
|
|
269
310
|
textAnchor='start'
|
|
270
|
-
style={{ pointerEvents: '
|
|
311
|
+
style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
|
|
312
|
+
onClick={() => handleNodeClick(node.id)}
|
|
313
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
314
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
271
315
|
>
|
|
272
316
|
<tspan className={classStyle}>{sankeyConfig.nodeValueStyle.textBefore + (typeof node.value === 'number' ? node.value.toLocaleString() : node.value) + sankeyConfig.nodeValueStyle.textAfter}</tspan>
|
|
273
317
|
</text>
|
|
@@ -291,7 +335,20 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
291
335
|
opacityValue = sankeyConfig.opacity.LinkOpacityInactive
|
|
292
336
|
}
|
|
293
337
|
|
|
294
|
-
return
|
|
338
|
+
return (
|
|
339
|
+
<path
|
|
340
|
+
key={i}
|
|
341
|
+
d={path!}
|
|
342
|
+
stroke={strokeColor}
|
|
343
|
+
fill='none'
|
|
344
|
+
strokeOpacity={opacityValue}
|
|
345
|
+
strokeWidth={link.width! + 2}
|
|
346
|
+
style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
|
|
347
|
+
onClick={() => handleNodeClick(link.target.id || null)}
|
|
348
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
349
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
|
|
350
|
+
/>
|
|
351
|
+
)
|
|
295
352
|
})
|
|
296
353
|
|
|
297
354
|
// max depth - calculates how many nodes deep the chart goes.
|
|
@@ -325,7 +382,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
325
382
|
fillOpacity={opacityValue}
|
|
326
383
|
rx={sankeyConfig.rxValue}
|
|
327
384
|
// todo: move enable tooltips to sankey
|
|
328
|
-
data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
|
|
385
|
+
data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
|
|
329
386
|
data-tooltip-id={`tooltip`}
|
|
330
387
|
onClick={() => handleNodeClick(node.id)}
|
|
331
388
|
style={{ pointerEvents: 'visible', cursor: 'pointer' }}
|
|
@@ -393,7 +450,9 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
393
450
|
textAnchor='start'
|
|
394
451
|
style={{ pointerEvents: 'none' }}
|
|
395
452
|
>
|
|
396
|
-
<tspan
|
|
453
|
+
<tspan onClick={() => handleNodeClick(node.id)} className={classStyle}>
|
|
454
|
+
{sankeyConfig.nodeValueStyle.textBefore + (typeof node.value === 'number' ? node.value.toLocaleString() : node.value) + sankeyConfig.nodeValueStyle.textAfter}
|
|
455
|
+
</tspan>
|
|
397
456
|
</text>
|
|
398
457
|
</>
|
|
399
458
|
)}
|
|
@@ -414,7 +473,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
|
|
|
414
473
|
|
|
415
474
|
{/* ReactTooltip needs to remain even if tooltips are disabled -- it handles when a user clicks off of the node and resets
|
|
416
475
|
the sankey diagram. When tooltips are disabled this will nothing */}
|
|
417
|
-
<ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`} afterHide={() =>
|
|
476
|
+
<ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`} afterHide={() => setTooltipID('')} events={['click']} place={'bottom'} style={{ backgroundColor: `rgba(238, 238, 238, 1)`, color: 'black', boxShadow: `0 3px 10px rgb(0 0 0 / 0.2)` }} />
|
|
418
477
|
{showPopup && (
|
|
419
478
|
<div className='popup'>
|
|
420
479
|
<div className='popup-content'>
|
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../../ConfigContext'
|
|
3
3
|
import { Group } from '@visx/group'
|
|
4
|
+
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
4
5
|
|
|
5
|
-
const ScatterPlot = ({ xScale, yScale
|
|
6
|
-
const {
|
|
6
|
+
const ScatterPlot = ({ xScale, yScale }) => {
|
|
7
|
+
const { transformedData: data, config, tableData, formatNumber, seriesHighlight, colorPalettes } = useContext(ConfigContext)
|
|
7
8
|
|
|
8
9
|
// TODO: copied from line chart should probably be a constant somewhere.
|
|
9
|
-
|
|
10
|
+
const circleRadii = 4.5
|
|
10
11
|
const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
|
|
11
|
-
|
|
12
|
-
const
|
|
12
|
+
// tooltips for additional columns
|
|
13
|
+
const additionalColumns = Object.entries(config.columns)
|
|
14
|
+
.filter(([_, value]) => value.tooltips)
|
|
15
|
+
.map(([_, value]) => [
|
|
16
|
+
value.label || value.name,
|
|
17
|
+
value.name,
|
|
18
|
+
{
|
|
19
|
+
addColPrefix: value.prefix,
|
|
20
|
+
addColSuffix: value.suffix,
|
|
21
|
+
addColRoundTo: value.roundToPlace,
|
|
22
|
+
addColCommas: value.commas
|
|
23
|
+
}
|
|
24
|
+
])
|
|
25
|
+
const handleTooltip = (item, s, dataIndex) => `<div>
|
|
13
26
|
${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[s] || ''}<br/>` : ''}
|
|
14
27
|
${config.xAxis.label}: ${formatNumber(item[config.xAxis.dataKey], 'bottom')} <br/>
|
|
15
|
-
${config.yAxis.label}: ${formatNumber(item[s], 'left')}
|
|
28
|
+
${config.yAxis.label}: ${formatNumber(item[s], 'left')}<br/>
|
|
29
|
+
${additionalColumns.map(([label, name, options]) => `${label} : ${formatColNumber(tableData[dataIndex][name], 'left', false, config, options)}<br/>`).join('')}
|
|
16
30
|
</div>`
|
|
17
31
|
|
|
18
32
|
return (
|
|
@@ -37,9 +51,9 @@ const ScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
|
|
|
37
51
|
cx={xScale(item[config.xAxis.dataKey])}
|
|
38
52
|
cy={yScale(item[s])}
|
|
39
53
|
fill={displayArea ? seriesColor : 'transparent'}
|
|
40
|
-
fillOpacity={transparentArea ? .25 : 1}
|
|
54
|
+
fillOpacity={transparentArea ? 0.25 : 1}
|
|
41
55
|
style={pointStyles}
|
|
42
|
-
data-tooltip-html={handleTooltip(item, s)}
|
|
56
|
+
data-tooltip-html={handleTooltip(item, s, dataIndex)}
|
|
43
57
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
44
58
|
tabIndex={-1}
|
|
45
59
|
/>
|
|
@@ -107,10 +107,10 @@ const SparkLine: React.FC<SparkLineProps> = props => {
|
|
|
107
107
|
opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
|
|
108
108
|
display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
|
|
109
109
|
>
|
|
110
|
-
{data.map((d, dataIndex) => {
|
|
110
|
+
{config.labels && data.map((d, dataIndex) => {
|
|
111
111
|
return (
|
|
112
112
|
<Group key={`series-${seriesKey}-point-${dataIndex}`}>
|
|
113
|
-
<Text
|
|
113
|
+
<Text x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
|
|
114
114
|
{formatNumber(d[seriesKey])}
|
|
115
115
|
</Text>
|
|
116
116
|
</Group>
|