@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.
Files changed (87) hide show
  1. package/dist/cdcchart.js +44197 -38258
  2. package/examples/cases-year.json +13379 -0
  3. package/examples/feature/annotations/index.json +542 -0
  4. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
  5. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
  6. package/examples/xaxis.json +493 -0
  7. package/index.html +20 -10
  8. package/package.json +5 -4
  9. package/src/CdcChart.tsx +462 -172
  10. package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
  11. package/src/_stories/Chart.stories.tsx +18 -171
  12. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  13. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  14. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  15. package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
  16. package/src/_stories/_mock/legend.gradient_mock.json +236 -0
  17. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  18. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  19. package/src/_stories/_mock/lollipop.json +171 -0
  20. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  21. package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
  22. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  23. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  24. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  25. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  26. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  27. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  28. package/src/components/Annotations/index.tsx +13 -0
  29. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  30. package/src/components/AreaChart/components/AreaChart.jsx +1 -1
  31. package/src/components/Axis/Categorical.Axis.tsx +145 -0
  32. package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
  33. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
  34. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
  35. package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
  36. package/src/components/BarChart/helpers/index.ts +91 -0
  37. package/src/components/BrushChart.tsx +205 -0
  38. package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
  39. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
  40. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
  41. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
  42. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
  43. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
  44. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  45. package/src/components/EditorPanel/components/panels.scss +4 -0
  46. package/src/components/EditorPanel/editor-panel.scss +35 -3
  47. package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
  48. package/src/components/Legend/Legend.Component.tsx +185 -194
  49. package/src/components/Legend/Legend.Suppression.tsx +146 -0
  50. package/src/components/Legend/Legend.tsx +21 -5
  51. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  52. package/src/components/Legend/helpers/index.ts +35 -0
  53. package/src/components/LegendWrapper.tsx +26 -0
  54. package/src/components/LineChart/LineChartProps.ts +1 -15
  55. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
  56. package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
  57. package/src/components/LineChart/helpers.ts +72 -14
  58. package/src/components/LineChart/index.tsx +117 -42
  59. package/src/components/LinearChart.jsx +179 -136
  60. package/src/components/LinearChart.tsx +1366 -0
  61. package/src/components/PairedBarChart.jsx +9 -9
  62. package/src/components/PieChart/PieChart.tsx +75 -18
  63. package/src/components/Sankey/index.tsx +89 -30
  64. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  65. package/src/components/Sparkline/components/SparkLine.tsx +2 -2
  66. package/src/components/ZoomBrush.tsx +90 -44
  67. package/src/data/initial-state.js +25 -7
  68. package/src/helpers/handleChartTabbing.ts +8 -0
  69. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  70. package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
  71. package/src/hooks/useColorScale.ts +1 -1
  72. package/src/hooks/useLegendClasses.ts +68 -0
  73. package/src/hooks/useMinMax.ts +12 -7
  74. package/src/hooks/useScales.ts +58 -26
  75. package/src/hooks/useTooltip.tsx +135 -25
  76. package/src/scss/DataTable.scss +2 -1
  77. package/src/scss/main.scss +128 -28
  78. package/src/types/ChartConfig.ts +83 -10
  79. package/src/types/ChartContext.ts +14 -4
  80. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  81. package/LICENSE +0 -201
  82. package/src/components/BrushHandle.jsx +0 -17
  83. package/src/components/LineChart/index.scss +0 -1
  84. package/src/helpers/filterData.ts +0 -18
  85. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  86. package/src/hooks/useLegendClasses.js +0 -31
  87. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -0,0 +1,205 @@
1
+ import { Group } from '@visx/group'
2
+ import { useContext, useEffect, useRef, useState } from 'react'
3
+ import ConfigContext from '../ConfigContext'
4
+ import * as d3 from 'd3'
5
+ import { invertValue } from '@cdc/core/helpers/scaling'
6
+ import { Text } from '@visx/text'
7
+ interface BrushChartProps {
8
+ xMax: number
9
+ yMax: number
10
+ }
11
+
12
+ const BrushChart = ({ xMax, yMax }: BrushChartProps) => {
13
+ const { tableData, config, setBrushConfig, getTextWidth, dashboardConfig, formatDate } = useContext(ConfigContext)
14
+ const [brushState, setBrushState] = useState({ isBrushing: false, selection: [] })
15
+ const [brushKey, setBrushKey] = useState(0)
16
+ const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
17
+ const isDashboardFilters = sharedFilters?.length > 0
18
+ const [tooltip, showTooltip] = useState(false)
19
+ const svgRef = useRef()
20
+ const brushheight = 25
21
+ const borderRadius = 15
22
+ const xDomain = d3.extent(tableData, d => new Date(d[config.runtime.originalXAxis.dataKey]))
23
+
24
+ const xScale = d3.scaleTime().domain(xDomain).range([0, xMax])
25
+
26
+ const tooltipText = 'Drag edges to focus on a specific segment '
27
+ const textWidth = getTextWidth(tooltipText, `normal ${16 / 1.1}px sans-serif`)
28
+
29
+ const calculateGroupTop = (): number => {
30
+ return Number(yMax) + config.xAxis.axisBBox + brushheight * 1.5
31
+ }
32
+
33
+ const handleMouseOver = () => {
34
+ // show tooltip text only once before brush triggered
35
+ if (brushState.selection[0] === 0 && xMax === brushState.selection[1]) {
36
+ showTooltip(true)
37
+ }
38
+ }
39
+ const handleMouseLeave = () => {
40
+ // hide tooltip text if brush was triggered
41
+ if (brushState.selection[0] !== 0 || brushState.selection[1] !== xMax) {
42
+ showTooltip(false)
43
+ }
44
+ showTooltip(false)
45
+ }
46
+
47
+ const brushHandle = (g, selection, firstDate, lastDate) => {
48
+ const textWidth = getTextWidth(firstDate, `normal ${16 / 1.1}px sans-serif`)
49
+ return g
50
+ .selectAll('.handle--custom')
51
+ .data([{ side: 'left' }, { side: 'right' }])
52
+ .join(enter => {
53
+ const handleGroup = enter.append('g').attr('class', 'handle--custom')
54
+ handleGroup
55
+ .append('text')
56
+ .attr('x', d => (d.side === 'left' ? 0 : -textWidth))
57
+ .attr('y', 30)
58
+ .text(d => (d.side === 'left' ? firstDate : lastDate))
59
+ .attr('font-size', '13px')
60
+ return handleGroup
61
+ })
62
+
63
+ .attr('display', 'block')
64
+ .attr('transform', selection === null ? null : (_, i) => `translate(${selection[i]},${'10'})`)
65
+ }
66
+
67
+ const initializeBrush = () => {
68
+ const svg = d3.select(svgRef.current).attr('overflow', 'visible')
69
+
70
+ svg
71
+ .append('rect') // prettier-ignore
72
+ .attr('fill', '#949494')
73
+ .attr('stroke', '#c5c5c5')
74
+ .attr('stroke-width', 2)
75
+ .attr('ry', borderRadius)
76
+ .attr('rx', borderRadius)
77
+ .attr('height', brushheight)
78
+ .attr('width', xMax)
79
+
80
+ const brushHandler = event => {
81
+ const selection = event?.selection
82
+ //if (!selection) return
83
+ let isUserBrushing = event.type === 'brush' && selection && selection.length > 0
84
+
85
+ const [x0, x1] = selection.map(value => xScale.invert(value))
86
+
87
+ // filter and update brush state directly
88
+ const newFilteredData = tableData.filter(d => {
89
+ const dateValue = d[config.runtime.originalXAxis.dataKey]
90
+ // Check if the date value exists and is valid
91
+ if (!dateValue) {
92
+ return false
93
+ }
94
+
95
+ const parsedDate = new Date(dateValue)
96
+
97
+ // Check if parsedDate is a valid date
98
+ if (isNaN(parsedDate.getTime())) {
99
+ return false
100
+ }
101
+
102
+ // Check if the date falls within the selection range
103
+ if (parsedDate >= x0 && parsedDate <= x1) {
104
+ return true
105
+ }
106
+ })
107
+
108
+ const firstDate = (newFilteredData.length && newFilteredData[0][config?.runtime?.originalXAxis?.dataKey]) ?? ''
109
+ const lastDate =
110
+ (newFilteredData.length &&
111
+ newFilteredData[newFilteredData.length - 1][config?.runtime?.originalXAxis?.dataKey]) ??
112
+ ''
113
+ // add custom blue colored handlers to each corners of brush
114
+ svg.selectAll('.handle--custom').remove()
115
+ // append handler
116
+ svg.call(brushHandle, selection, firstDate, lastDate)
117
+
118
+ setBrushConfig({
119
+ active: config.brush.active,
120
+ isBrushing: isUserBrushing,
121
+ data: newFilteredData
122
+ })
123
+ setBrushState({
124
+ isBrushing: true,
125
+ selection
126
+ })
127
+ }
128
+
129
+ const brush = d3
130
+ .brushX()
131
+ .extent([
132
+ [0, 0],
133
+ [xMax, 25]
134
+ ]) // brush extent
135
+ .on('start brush end', brushHandler)
136
+
137
+ const defaultSelection = [0, xMax]
138
+ let brushGroup = svg.append('g').call(brush).call(brush.move, defaultSelection)
139
+ brushGroup.select('.overlay').style('pointer-events', 'none')
140
+
141
+ brushGroup
142
+ .selectAll('.selection')
143
+ .attr('fill', '#474747')
144
+ .attr('fill-opacity', 1)
145
+ .attr('rx', borderRadius)
146
+ .attr('ry', borderRadius)
147
+ }
148
+
149
+ useEffect(() => {
150
+ const isFiltersActive = config.filters?.some(filter => filter.active)
151
+ const isExclusionsActive = config.exclusions?.active
152
+
153
+ if ((isFiltersActive || isExclusionsActive || isDashboardFilters) && config.brush?.active) {
154
+ setBrushKey(prevKey => prevKey + 1)
155
+ setBrushConfig(prev => {
156
+ return {
157
+ ...prev,
158
+ data: tableData
159
+ }
160
+ })
161
+ }
162
+ return () =>
163
+ setBrushConfig(prev => {
164
+ return {
165
+ ...prev,
166
+ data: []
167
+ }
168
+ })
169
+ }, [config.filters, config.exclusions, config.brush?.active, isDashboardFilters])
170
+ // Initialize brush when component is first rendered
171
+
172
+ // reset brush on keychange
173
+ useEffect(() => {
174
+ if (brushKey) {
175
+ initializeBrush()
176
+ }
177
+ }, [brushKey])
178
+
179
+ if (!brushState.isBrushing) {
180
+ initializeBrush()
181
+ }
182
+
183
+ return (
184
+ <Group
185
+ onMouseLeave={handleMouseLeave}
186
+ onMouseOver={handleMouseOver}
187
+ className='brush-container'
188
+ left={Number(config.runtime.yAxis.size)}
189
+ top={calculateGroupTop()}
190
+ >
191
+ <Text
192
+ pointerEvents='visiblePainted'
193
+ display={tooltip ? 'block' : 'none'}
194
+ fontSize={16}
195
+ x={(Number(xMax) - Number(textWidth)) / 2}
196
+ y={-10}
197
+ >
198
+ Drag edges to focus on a specific segment
199
+ </Text>
200
+ <svg width={'100%'} height={brushheight * 3} ref={svgRef}></svg>
201
+ </Group>
202
+ )
203
+ }
204
+
205
+ export default BrushChart