@cdc/chart 4.24.4 → 4.24.7

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 (76) hide show
  1. package/dist/cdcchart.js +39611 -36038
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +9 -8
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +115 -71
  7. package/src/_stories/Chart.stories.tsx +26 -171
  8. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  9. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  10. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  11. package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
  12. package/src/_stories/_mock/bar-chart-suppressed.json +474 -0
  13. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  14. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  15. package/src/_stories/_mock/lollipop.json +171 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  17. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  19. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  20. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  21. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  22. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  23. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  24. package/src/components/Annotations/index.tsx +13 -0
  25. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  26. package/src/components/AreaChart/components/AreaChart.jsx +2 -2
  27. package/src/components/BarChart/components/BarChart.Horizontal.tsx +78 -71
  28. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  29. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  30. package/src/components/BarChart/components/BarChart.Vertical.tsx +100 -87
  31. package/src/components/BarChart/helpers/index.ts +102 -0
  32. package/src/components/DeviationBar.jsx +4 -2
  33. package/src/components/EditorPanel/EditorPanel.tsx +435 -613
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +135 -7
  36. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  37. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -5
  38. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  39. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  40. package/src/components/EditorPanel/components/panels.scss +4 -0
  41. package/src/components/EditorPanel/editor-panel.scss +19 -0
  42. package/src/components/EditorPanel/useEditorPermissions.js +23 -3
  43. package/src/components/Legend/Legend.Component.tsx +66 -15
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  45. package/src/components/Legend/helpers/index.ts +5 -0
  46. package/src/components/LineChart/LineChartProps.ts +16 -6
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +22 -11
  48. package/src/components/LineChart/helpers.ts +148 -10
  49. package/src/components/LineChart/index.tsx +71 -44
  50. package/src/components/LinearChart.jsx +184 -125
  51. package/src/components/PairedBarChart.jsx +9 -9
  52. package/src/components/PieChart/PieChart.tsx +4 -4
  53. package/src/components/Sankey/index.tsx +73 -20
  54. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  55. package/src/components/ZoomBrush.tsx +120 -55
  56. package/src/data/initial-state.js +14 -6
  57. package/src/helpers/handleChartTabbing.ts +8 -0
  58. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  59. package/src/hooks/{useBarChart.js → useBarChart.ts} +9 -22
  60. package/src/hooks/useColorScale.ts +1 -1
  61. package/src/hooks/useMinMax.ts +29 -5
  62. package/src/hooks/useScales.ts +48 -26
  63. package/src/hooks/useTooltip.tsx +62 -15
  64. package/src/scss/main.scss +69 -12
  65. package/src/types/ChartConfig.ts +53 -16
  66. package/src/types/ChartContext.ts +13 -0
  67. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  68. package/LICENSE +0 -201
  69. package/src/_stories/ChartLine.preliminary.tsx +0 -19
  70. package/src/_stories/ChartSuppress.stories.tsx +0 -19
  71. package/src/_stories/_mock/suppress_mock.json +0 -911
  72. package/src/helpers/computeMarginBottom.ts +0 -56
  73. package/src/helpers/filterData.ts +0 -18
  74. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  75. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  76. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -1,4 +1,6 @@
1
1
  import { ChartConfig } from '../types/ChartConfig'
2
+ import _ from 'lodash'
3
+ import { isConvertLineToBarGraph } from '../helpers/isConvertLineToBarGraph'
2
4
 
3
5
  type UseMinMaxProps = {
4
6
  /** config - standard chart config */
@@ -11,11 +13,13 @@ type UseMinMaxProps = {
11
13
  existPositiveValue: boolean
12
14
  /** data - standard data array */
13
15
  data: Object[]
16
+ /** Table data -data array Filtered & Excluded */
17
+ tableData: Object[]
14
18
  /** isAllLine: if all series are line type including dashed lines */
15
19
  isAllLine: boolean
16
20
  }
17
21
 
18
- const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAllLine }: UseMinMaxProps) => {
22
+ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAllLine, tableData }: UseMinMaxProps) => {
19
23
  let min = 0
20
24
  let max = 0
21
25
 
@@ -27,6 +31,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
27
31
  return { min, max }
28
32
  }
29
33
 
34
+ const checkLineToBarGraph = () => {
35
+ return isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)
36
+ }
37
+
30
38
  const { visualizationType, series } = config
31
39
  const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
32
40
  const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
@@ -123,10 +131,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
123
131
  }
124
132
 
125
133
  // this should not apply to bar charts if there is negative CI data
126
- if ((visualizationType === 'Bar' || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
134
+ if ((visualizationType === 'Bar' || checkLineToBarGraph() || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
127
135
  min = 0
128
136
  }
129
- if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
137
+ if ((config.visualizationType === 'Bar' || checkLineToBarGraph() || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
130
138
  min = min * 1.1
131
139
  }
132
140
 
@@ -145,9 +153,25 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
145
153
  min = enteredMinValue && isMinValid ? enteredMinValue : 0
146
154
  }
147
155
 
148
- if (config.visualizationType === 'Line') {
156
+ if (config.visualizationType === 'Line' && !checkLineToBarGraph()) {
149
157
  const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
150
- min = enteredMinValue && isMinValid ? enteredMinValue : minValue
158
+ // update minValue for (0) Suppression points
159
+ const suppressedMinValue = tableData?.some((dataItem, index) => {
160
+ return config.preliminaryData?.some(pd => {
161
+ if (pd.type !== 'suppression' || !pd.style) return false
162
+
163
+ // Filter data item based on current series keys and check if pd.value is present
164
+ const relevantData = _.pick(dataItem, config.runtime?.seriesKeys)
165
+ const isValuePresent = _.values(relevantData).includes(pd.value)
166
+
167
+ // Check for value match condition
168
+ const valueMatch = pd.column ? dataItem[pd.column] === pd.value : isValuePresent
169
+
170
+ // Return true if the value matches and it's either the first or the last item
171
+ return valueMatch && (index === 0 || index === tableData.length - 1)
172
+ })
173
+ })
174
+ min = enteredMinValue && isMinValid ? enteredMinValue : suppressedMinValue ? 0 : minValue
151
175
  }
152
176
  //If data value max wasn't provided, calculate it
153
177
  if (max === Number.MIN_VALUE) {
@@ -44,6 +44,10 @@ const useScales = (properties: useScaleProps) => {
44
44
  let seriesScale = null
45
45
  let xScaleNoPadding = null
46
46
  let xScaleBrush = null
47
+ let xScaleAnnotation = scaleLinear({
48
+ domain: [0, 100],
49
+ range: [0, xMax]
50
+ })
47
51
 
48
52
  // handle Horizontal bars
49
53
  if (isHorizontal) {
@@ -62,7 +66,12 @@ const useScales = (properties: useScaleProps) => {
62
66
  seriesScale = composeScaleBand(seriesDomain, [0, xScale.bandwidth()], 0)
63
67
  }
64
68
 
65
- // handle Area chart
69
+ // handle Linear scaled viz
70
+ if (config.xAxis.type === 'date' && !isHorizontal) {
71
+ const xAxisDataMappedSorted = xAxisDataMapped ? xAxisDataMapped.sort() : []
72
+ xScale = composeScaleBand(xAxisDataMappedSorted, [0, xMax], 1 - config.barThickness)
73
+ }
74
+
66
75
  if (config.xAxis.type === 'date-time') {
67
76
  let xAxisMin = Math.min(...xAxisDataMapped)
68
77
  let xAxisMax = Math.max(...xAxisDataMapped)
@@ -72,9 +81,22 @@ const useScales = (properties: useScaleProps) => {
72
81
  domain: [xAxisMin, xAxisMax],
73
82
  range: [0, xMax]
74
83
  })
75
- xScaleBrush = xScale
84
+
76
85
  xScale.type = scaleTypes.TIME
77
- seriesScale = composeScaleBand(seriesDomain, [0, config.barThickness * xMax], 0)
86
+
87
+ let minDistance = Number.MAX_VALUE
88
+ let xAxisDataMappedSorted = xAxisDataMapped ? xAxisDataMapped.sort() : []
89
+ for (let i = 0; i < xAxisDataMappedSorted.length - 1; i++) {
90
+ let distance = xScale(xAxisDataMappedSorted[i + 1]) - xScale(xAxisDataMappedSorted[i])
91
+
92
+ if (distance < minDistance) minDistance = distance
93
+ }
94
+
95
+ if (xAxisDataMapped.length === 1 || minDistance > xMax / 4) {
96
+ minDistance = xMax / 4
97
+ }
98
+
99
+ seriesScale = composeScaleBand(seriesDomain, [0, (config.barThickness || 1) * minDistance], 0)
78
100
  }
79
101
 
80
102
  // handle Deviation bar
@@ -233,40 +255,40 @@ const useScales = (properties: useScaleProps) => {
233
255
  }
234
256
  }
235
257
  }
236
- return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush }
258
+ return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush, xScaleAnnotation }
237
259
  }
238
260
 
239
261
  export default useScales
240
262
 
241
263
  export const getTickValues = (xAxisDataMapped, xScale, num) => {
242
- const xDomain = xScale.domain();
243
-
244
- if(xScale.type === 'time'){
245
- const xDomainMax = xAxisDataMapped[xAxisDataMapped.length - 1];
246
- const xDomainMin = xAxisDataMapped[0];
247
- const step = (xDomainMax - xDomainMin) / (num - 1);
248
- const tickValues = [];
249
- for(let i = xDomainMax; i >= xDomainMin; i -= step){
250
- tickValues.push(i);
264
+ const xDomain = xScale.domain()
265
+
266
+ if (xScale.type === 'time') {
267
+ const xDomainMax = xAxisDataMapped[xAxisDataMapped.length - 1]
268
+ const xDomainMin = xAxisDataMapped[0]
269
+ const step = (xDomainMax - xDomainMin) / (num - 1)
270
+ const tickValues = []
271
+ for (let i = xDomainMax; i >= xDomainMin; i -= step) {
272
+ tickValues.push(i)
251
273
  }
252
- if(tickValues[tickValues.length - 1] !== xDomainMin){
253
- tickValues.push(xDomainMin);
274
+ if (tickValues[tickValues.length - 1] !== xDomainMin) {
275
+ tickValues.push(xDomainMin)
254
276
  }
255
- tickValues.reverse();
256
-
257
- return tickValues;
277
+ tickValues.reverse()
278
+
279
+ return tickValues
258
280
  }
259
281
 
260
- if(xDomain.length > 2){
261
- const step = num || 1;
262
- const tickValues = [];
263
- for(let i = xDomain.length; i > 0; i -= step){
264
- const adjustedIndex = Math.max(Math.round(i) - 1, 0);
265
- tickValues.push(xDomain[adjustedIndex]);
282
+ if (xDomain.length > 2) {
283
+ const step = num || 1
284
+ const tickValues = []
285
+ for (let i = xDomain.length; i > 0; i -= step) {
286
+ const adjustedIndex = Math.max(Math.round(i) - 1, 0)
287
+ tickValues.push(xDomain[adjustedIndex])
266
288
  }
267
- tickValues.reverse();
289
+ tickValues.reverse()
268
290
 
269
- return tickValues;
291
+ return tickValues
270
292
  }
271
293
  }
272
294
 
@@ -1,21 +1,18 @@
1
1
  import { useContext } from 'react'
2
+ // Local imports
2
3
  import ConfigContext from '../ConfigContext'
3
4
  import { type ChartContext } from '../types/ChartContext'
4
-
5
- // third party
6
- import { localPoint } from '@visx/event'
7
- import { bisector } from 'd3-array'
8
- import { DataTransform } from '@cdc/core/helpers/DataTransform'
9
- const transform = new DataTransform()
10
-
11
5
  import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
12
6
  import { isDateScale } from '@cdc/core/helpers/cove/date'
7
+ // Third-party library imports
8
+ import { localPoint } from '@visx/event'
9
+ import { bisector } from 'd3-array'
13
10
 
14
11
  export const useTooltip = props => {
15
- const { tableData, config, formatNumber, capitalize, formatDate, formatTooltipsDate, parseDate, setSharedFilter } = useContext<ChartContext>(ConfigContext)
12
+ const { tableData: data, config, formatNumber, capitalize, formatDate, formatTooltipsDate, parseDate, setSharedFilter, isDraggingAnnotation } = useContext<ChartContext>(ConfigContext)
16
13
  const { xScale, yScale, showTooltip, hideTooltip } = props
17
14
  const { xAxis, visualizationType, orientation, yAxis, runtime } = config
18
- const data = transform.applySuppression(tableData, config.suppressedData)
15
+
19
16
  /**
20
17
  * Provides the tooltip information based on the tooltip data array and svg cursor coordinates
21
18
  * @function getTooltipInformation
@@ -23,6 +20,44 @@ export const useTooltip = props => {
23
20
  * @param {Object} eventSvgCoords - The object containing the SVG coordinates of the event.
24
21
  * @return {Object} - The tooltip information with tooltip data.
25
22
  */
23
+
24
+ // function handles only Single series hovred data tooltips
25
+ const findDataKeyByThreshold = (mouseY, datapoint) => {
26
+ let sum = 0
27
+ let threshold = Number(yScale.invert(mouseY))
28
+ let hoveredKey = null
29
+ let hoveredValue = null
30
+
31
+ for (let key of config.runtime?.seriesKeys) {
32
+ if (datapoint.hasOwnProperty(key)) {
33
+ sum += Number(datapoint[key])
34
+ if (sum >= threshold) {
35
+ hoveredValue = datapoint[key]
36
+ hoveredKey = key
37
+ break
38
+ }
39
+ }
40
+ }
41
+
42
+ // Return null if no matching data is found
43
+ return [hoveredKey, hoveredValue]
44
+ }
45
+
46
+ const getFormattedValue = (seriesKey, value, config, getAxisPosition) => {
47
+ // handle case where data is missing
48
+ const showMissingDataValue = config.general.showMissingDataLabel && (!value || value === 'null')
49
+ let formattedValue = seriesKey === config.xAxis.dataKey ? value : formatNumber(value, getAxisPosition(seriesKey))
50
+ formattedValue = showMissingDataValue ? 'N/A' : formattedValue
51
+
52
+ const suppressed = config.preliminaryData?.find(pd => pd.label && pd.type === 'suppression' && pd.displayTooltip && value === pd.value && (!pd.column || seriesKey === pd.column))
53
+
54
+ if (suppressed) {
55
+ formattedValue = suppressed.label
56
+ }
57
+
58
+ return formattedValue
59
+ }
60
+
26
61
  const getTooltipInformation = (tooltipDataArray, eventSvgCoords) => {
27
62
  const { x, y } = eventSvgCoords
28
63
  let initialTooltipData = tooltipDataArray || {}
@@ -50,6 +85,8 @@ export const useTooltip = props => {
50
85
  */
51
86
  const handleTooltipMouseOver = (e, additionalChartData) => {
52
87
  e.stopPropagation()
88
+ if (isDraggingAnnotation) return
89
+
53
90
  const eventSvgCoords = localPoint(e)
54
91
  const { x, y } = eventSvgCoords
55
92
 
@@ -137,18 +174,29 @@ export const useTooltip = props => {
137
174
  if (visualizationType === 'Forest Plot') {
138
175
  tooltipItems.push([config.xAxis.dataKey, getClosestYValue(y)])
139
176
  }
140
-
141
- if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot') {
177
+ // handle tooltip for all hovered series
178
+ if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot' && !config.tooltips.singleSeries) {
142
179
  tooltipItems.push(
143
180
  ...getIncludedTooltipSeries()
144
- ?.filter(Boolean)
181
+ ?.filter(seriesKey => config.series?.find(item => item.dataKey === seriesKey && item?.tooltip) || config.xAxis?.dataKey == seriesKey || visualizationType === 'Forecasting')
145
182
  ?.flatMap(seriesKey => {
146
- const formattedValue = seriesKey === config.xAxis.dataKey ? resolvedScaleValues[0]?.[seriesKey] : formatNumber(resolvedScaleValues[0]?.[seriesKey], getAxisPosition(seriesKey))
147
- return resolvedScaleValues?.[0]?.[seriesKey] ? [[seriesKey, formattedValue, getAxisPosition(seriesKey)]] : []
183
+ const value = resolvedScaleValues[0]?.[seriesKey]
184
+ const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
185
+ return [[seriesKey, formattedValue, getAxisPosition(seriesKey)]]
148
186
  })
149
187
  )
150
188
  }
151
189
 
190
+ // handle tooltip for single hovered series
191
+ if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot' && config.tooltips.singleSeries) {
192
+ const [seriesKey, value] = findDataKeyByThreshold(y, resolvedScaleValues[0])
193
+ if (seriesKey && value) {
194
+ tooltipItems.push([config.xAxis.dataKey, closestXScaleValue])
195
+ const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
196
+ tooltipItems.push([seriesKey, formattedValue])
197
+ }
198
+ }
199
+
152
200
  return [...tooltipItems, ...additionalTooltipItems]
153
201
  }
154
202
 
@@ -323,7 +371,6 @@ export const useTooltip = props => {
323
371
  const yScaleValues = dataToSearch.map(object => {
324
372
  return Object.fromEntries(Object.entries(object).filter(([key, value]) => includedSeries.includes(key)))
325
373
  })
326
-
327
374
  return yScaleValues
328
375
  } catch (error) {
329
376
  console.error('COVE', error)
@@ -13,9 +13,21 @@
13
13
  flex-direction: column-reverse;
14
14
  }
15
15
 
16
+ .cdc-open-viz-module.type-dashboard {
17
+ .cdc-open-viz-module.type-chart.isEditor {
18
+ .cdc-chart-inner-container {
19
+ background: white;
20
+ }
21
+ }
22
+ }
23
+
16
24
  .cdc-open-viz-module.type-chart {
17
25
  @import 'DataTable';
18
26
 
27
+ &.isEditor .cdc-chart-inner-container {
28
+ background: white;
29
+ }
30
+
19
31
  border-radius: 3px;
20
32
 
21
33
  .checkbox-group {
@@ -75,11 +87,19 @@
75
87
  margin-bottom: 20px;
76
88
  }
77
89
 
90
+ .subtext,
91
+ .subtext--responsive-ticks,
78
92
  .section-subtext {
79
- padding: 15px;
93
+ padding: 0 1rem;
94
+ &--brush-active {
95
+ margin-top: 2.5em;
96
+ }
80
97
  }
81
- .subtext--responsive-ticks {
82
- margin-top: 3em;
98
+
99
+ .type-pie {
100
+ svg.animated-pie {
101
+ width: auto !important;
102
+ }
83
103
  }
84
104
 
85
105
  .legend-container {
@@ -130,9 +150,6 @@
130
150
  word-wrap: break-word;
131
151
  white-space: pre-wrap;
132
152
  word-break: break-word;
133
- @include breakpoint(xs) {
134
- font-size: $font-small;
135
- }
136
153
  }
137
154
  }
138
155
 
@@ -153,10 +170,8 @@
153
170
  h3,
154
171
  p {
155
172
  margin-bottom: 0.8em;
156
- @include breakpoint(xs) {
157
- font-size: $font-small + 0.2em;
158
- }
159
173
  }
174
+
160
175
  & div.legend-item {
161
176
  margin-bottom: 0.5em !important;
162
177
 
@@ -198,18 +213,47 @@
198
213
  }
199
214
  }
200
215
 
216
+ .div[class*='Asterisk'] {
217
+ transform: 0;
218
+ }
219
+
201
220
  .legend-preliminary {
202
221
  display: flex;
203
222
  flex-direction: row;
204
223
  align-items: center;
205
- @include breakpoint(xs) {
206
- font-size: $font-small;
224
+ font-size: 1em;
225
+ vertical-align: middle;
226
+ margin-bottom: 0.5em;
227
+
228
+ & > span {
229
+ display: flex;
230
+ justify-items: center;
231
+ align-items: center;
232
+ white-space: nowrap;
233
+ font-size: 1em;
234
+ margin-right: 8px;
235
+ max-height: 1px;
236
+ }
237
+
238
+ & > span[class*='Asterisk'] {
239
+ transform: scale(1.8);
240
+ margin-top: 15px;
241
+ }
242
+ & > span[class*='Dagger'] {
243
+ margin-top: 2px;
244
+ }
245
+ & > span[class*='Sign'] {
246
+ margin-top: 1px;
207
247
  }
208
248
 
209
249
  & > svg {
210
250
  width: 50px;
211
251
  height: 23px;
212
252
  }
253
+
254
+ & > p {
255
+ margin: 0;
256
+ }
213
257
  }
214
258
 
215
259
  .visx-tooltip {
@@ -236,6 +280,17 @@
236
280
  &.legend-hidden > svg {
237
281
  width: 100% !important;
238
282
  }
283
+ &.dashboard-brush {
284
+ margin-bottom: 2.5em;
285
+ }
286
+
287
+ svg.dragging-annotation * {
288
+ user-select: none;
289
+ }
290
+
291
+ svg.dragging-annotation path {
292
+ pointer-events: none;
293
+ }
239
294
 
240
295
  > svg {
241
296
  overflow: visible;
@@ -651,8 +706,10 @@
651
706
  padding: 1em;
652
707
  }
653
708
 
654
- .subtext {
709
+ .subtext,
710
+ .subtext--responsive-ticks {
655
711
  margin-top: 0px;
712
+ padding: 0 1rem;
656
713
  }
657
714
 
658
715
  .isEditor {
@@ -12,7 +12,10 @@ import { Legend } from '@cdc/core/types/Legend'
12
12
  import { ConfidenceInterval } from '@cdc/core/types/ConfidenceInterval'
13
13
  import { Region } from '@cdc/core/types/Region'
14
14
  import { type PreliminaryDataItem } from '../components/LineChart/LineChartProps'
15
+ import { VizFilter } from '@cdc/core/types/VizFilter'
16
+ import { type Annotation } from '@cdc/core/types/Annotation'
15
17
 
18
+ export type ViewportSize = 'sm' | 'xs' | 'xxs' | 'lg'
16
19
  export type ChartColumns = Record<string, Column>
17
20
 
18
21
  type DataFormat = {
@@ -39,18 +42,6 @@ type Exclusions = {
39
42
  dateEnd: string
40
43
  }
41
44
 
42
- type Filter = {
43
- active: string
44
- type: 'url'
45
- columnName: string
46
- showDropdown: boolean
47
- filterStyle: string
48
- label: string
49
- order: 'asc' | 'desc' | 'cust'
50
- values: string[]
51
- queryParameter: string
52
- }
53
-
54
45
  export type Legend = {
55
46
  seriesHighlight: string[]
56
47
  additionalCategories: string[]
@@ -81,7 +72,8 @@ type Visual = {
81
72
  horizontalHoverLine?: boolean
82
73
  }
83
74
 
84
- type AllChartsConfig = {
75
+ export type AllChartsConfig = {
76
+ annotations: Annotation[]
85
77
  animate: boolean
86
78
  general: General
87
79
  barHasBorder: 'true' | 'false'
@@ -109,7 +101,7 @@ type AllChartsConfig = {
109
101
  description: string
110
102
  dynamicMarginTop: number
111
103
  exclusions: Exclusions
112
- filters: Filter[]
104
+ filters: VizFilter[]
113
105
  filterBehavior: FilterBehavior
114
106
  fontSize: 'small' | 'medium' | 'large'
115
107
  footnotes: string
@@ -161,6 +153,7 @@ type AllChartsConfig = {
161
153
  twoColor: { palette: string }
162
154
  type: 'chart' | 'dashboard'
163
155
  useLogScale: boolean
156
+ uid: string | number
164
157
  visual: Visual
165
158
  visualizationType: 'Area Chart' | 'Bar' | 'Box Plot' | 'Deviation Bar' | 'Forest Plot' | 'Line' | 'Paired Bar' | 'Pie' | 'Scatter Plot' | 'Spark Line' | 'Combo' | 'Forecasting' | 'Sankey'
166
159
  visualizationSubType: string
@@ -198,8 +191,52 @@ export type ForestPlotConfig = {
198
191
  } & AllChartsConfig
199
192
 
200
193
  export type LineChartConfig = {
201
- visualizationType: 'Line'
194
+ allowLineToBarGraph: boolean
195
+ convertLineToBarGraph: boolean
202
196
  lineDatapointStyle: 'hidden' | 'always show' | 'hover'
197
+ visualizationType: 'Line'
198
+ } & AllChartsConfig
199
+
200
+ export type SankeyLink = {
201
+ depth: number
202
+ height: number
203
+ id: string
204
+ index: number
205
+ layer: number
206
+ sourceLinks: SankeyLink[]
207
+ targetLinks: SankeyLink[]
208
+ value: number
209
+ x0: number
210
+ x1: number
211
+ y0: number
212
+ y1: number
213
+ }
214
+
215
+ type StoryNode = {
216
+ StoryNode: string
217
+ segmentTextAfter: string
218
+ segmentTextBefore: string
219
+ }
220
+
221
+ export type SankeyChartConfig = {
222
+ enableTooltips: boolean
223
+ data: [
224
+ {
225
+ tooltips: Object[]
226
+ // data to display in the sankey chart tooltips
227
+ tooltipData: Object[]
228
+ // data to display in the data table, bypasses the default data table output
229
+ tableData: Object[]
230
+ links: {
231
+ source: SankeyLink
232
+ target: SankeyLink
233
+ value: number
234
+ }[],
235
+ storyNodeText: StoryNode[]
236
+ :
237
+ }
238
+ ]
239
+ visualizationType: 'Sankey'
203
240
  } & AllChartsConfig
204
241
 
205
- export type ChartConfig = LineChartConfig | ForestPlotConfig | AllChartsConfig
242
+ export type ChartConfig = SankeyChartConfig | LineChartConfig | ForestPlotConfig | AllChartsConfig
@@ -1,6 +1,7 @@
1
1
  import { type ChartConfig } from './ChartConfig'
2
2
  import { PickD3Scale } from '@visx/scale'
3
3
  import { type SharedFilter } from '@cdc/dashboard/src/types/SharedFilter'
4
+ import { type Annotation } from '@cdc/core/types/Annotation'
4
5
 
5
6
  export type ColorScale = PickD3Scale<'ordinal', any, any>
6
7
 
@@ -21,10 +22,21 @@ type SharedChartContext = {
21
22
  legendIsolateValues?: string[]
22
23
  setLegendIsolateValues?: Function
23
24
  getTextWidth?: () => string | number
25
+ brushConfig: { data: []; isBrushing: boolean; isActive: boolean }
26
+ setBrushConfig: Function
27
+ clean: Function
28
+ capitalize: (value: string) => string
29
+ // whether or not the legend is appearing below the chart
30
+ isLegendBottom?: boolean
31
+ // whether or not the chart is viewed within the editor screen
32
+ isEditor?: boolean
33
+ // whether or not the user is dragging an annotation
34
+ isDraggingAnnotation?: boolean
24
35
  }
25
36
 
26
37
  // Line Chart Specific Context
27
38
  type LineChartContext = SharedChartContext & {
39
+ convertLineToBarGraph: boolean
28
40
  dimensions: [screenWidth: number, screenHeight: number]
29
41
  formatDate: Function
30
42
  formatTooltipsDate: Function
@@ -46,6 +58,7 @@ type LineChartContext = SharedChartContext & {
46
58
  export type ChartContext =
47
59
  | LineChartContext
48
60
  | (SharedChartContext & {
61
+ annotations: Annotation[]
49
62
  dimensions: [screenWidth: number, screenHeight: number]
50
63
  formatDate?: Function
51
64
  formatTooltipsDate: Function
@@ -0,0 +1,30 @@
1
+ import { testZeroValue } from '../../src/components/BarChart/helpers'
2
+
3
+ describe('testZeroValue', () => {
4
+ test('returns true for "0" as string', () => {
5
+ expect(testZeroValue('0')).toBe(true)
6
+ })
7
+
8
+ test('returns true for "0" as number', () => {
9
+ expect(testZeroValue(0)).toBe(true)
10
+ })
11
+
12
+ test('returns true for 0.0 as number', () => {
13
+ expect(testZeroValue(0.0)).toBe(true)
14
+ })
15
+
16
+ // check false case
17
+ test('returns false for "ABC" as string', () => {
18
+ expect(testZeroValue('ABC')).toBe(false)
19
+ })
20
+ test('returns false for 999 as number', () => {
21
+ expect(testZeroValue(999)).toBe(false)
22
+ })
23
+ // test for null & undefined cases
24
+ test('returns undefined for a null', () => {
25
+ expect(testZeroValue(null)).toBe(undefined)
26
+ })
27
+ test('returns undefined for a undefined', () => {
28
+ expect(testZeroValue(undefined)).toBe(undefined)
29
+ })
30
+ })