@cdc/chart 4.24.11 → 4.24.12-2

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 (65) hide show
  1. package/dist/cdcchart.js +32134 -32039
  2. package/examples/feature/sankey/sankey-example-data.json +126 -13
  3. package/examples/feature/tests-date-exclusions/date-exclusions-config.json +372 -12
  4. package/examples/private/DEV-8850-2.json +493 -0
  5. package/examples/private/DEV-9822.json +574 -0
  6. package/examples/private/DEV-9840.json +553 -0
  7. package/examples/private/DEV-9850-3.json +461 -0
  8. package/examples/private/chart.json +1084 -0
  9. package/examples/private/ci_formatted.json +202 -0
  10. package/examples/private/ci_issue.json +3016 -0
  11. package/examples/private/completed.json +634 -0
  12. package/examples/private/dem-data-long.csv +20 -0
  13. package/examples/private/dem-data-long.json +36 -0
  14. package/examples/private/demographic_data.csv +157 -0
  15. package/examples/private/demographic_data.json +2654 -0
  16. package/examples/private/demographic_dynamic.json +443 -0
  17. package/examples/private/demographic_standard.json +560 -0
  18. package/examples/private/ehdi.json +29939 -0
  19. package/examples/private/test.json +448 -20047
  20. package/index.html +9 -6
  21. package/package.json +2 -2
  22. package/src/CdcChart.tsx +62 -82
  23. package/src/_stories/Chart.Anchors.stories.tsx +31 -0
  24. package/src/_stories/Chart.DynamicSeries.stories.tsx +8 -1
  25. package/src/_stories/Chart.stories.tsx +32 -0
  26. package/src/_stories/ChartAxisLabels.stories.tsx +4 -1
  27. package/{examples/feature/area/area-chart-date-city-temperature.json → src/_stories/_mock/area_chart_stacked.json} +125 -27
  28. package/src/_stories/_mock/line_chart_dynamic_ci.json +493 -0
  29. package/src/_stories/_mock/line_chart_non_dynamic_ci.json +522 -0
  30. package/src/_stories/_mock/short_dates.json +288 -0
  31. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +15 -3
  32. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  33. package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -37
  34. package/src/components/BarChart/components/BarChart.Vertical.tsx +28 -40
  35. package/src/components/BarChart/helpers/getBarData.ts +28 -0
  36. package/src/components/BarChart/helpers/tests/getBarData.test.ts +74 -0
  37. package/src/components/BoxPlot/BoxPlot.tsx +12 -70
  38. package/src/components/BoxPlot/helpers/index.ts +54 -0
  39. package/src/components/BrushChart.tsx +23 -26
  40. package/src/components/EditorPanel/EditorPanel.tsx +55 -79
  41. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +1 -0
  42. package/src/components/EditorPanel/useEditorPermissions.ts +5 -1
  43. package/src/components/Legend/Legend.Component.tsx +2 -2
  44. package/src/{hooks/useLegendClasses.ts → components/Legend/helpers/getLegendClasses.ts} +5 -5
  45. package/src/components/Legend/helpers/index.ts +2 -1
  46. package/src/components/Legend/tests/getLegendClasses.test.ts +115 -0
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +1 -1
  48. package/src/components/LineChart/helpers.ts +1 -0
  49. package/src/components/LineChart/index.tsx +47 -1
  50. package/src/components/LinearChart.tsx +180 -172
  51. package/src/components/PieChart/PieChart.tsx +7 -1
  52. package/src/components/Sankey/components/ColumnList.tsx +19 -0
  53. package/src/components/Sankey/components/Sankey.tsx +479 -0
  54. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +33 -0
  55. package/src/components/Sankey/index.tsx +1 -510
  56. package/src/components/Sankey/sankey.scss +16 -16
  57. package/src/components/Sankey/types/index.ts +1 -1
  58. package/src/data/initial-state.js +4 -3
  59. package/src/helpers/countNumOfTicks.ts +57 -0
  60. package/src/helpers/getQuartiles.ts +15 -18
  61. package/src/hooks/useMinMax.ts +18 -4
  62. package/src/hooks/useScales.ts +38 -4
  63. package/src/hooks/useTooltip.tsx +5 -1
  64. package/src/scss/DataTable.scss +5 -0
  65. package/src/scss/main.scss +6 -2
@@ -0,0 +1,288 @@
1
+ {
2
+ "annotations": [],
3
+ "type": "chart",
4
+ "debugSvg": false,
5
+ "chartMessage": { "noData": "No Data Available" },
6
+ "title": "",
7
+ "showTitle": true,
8
+ "showDownloadMediaButton": false,
9
+ "theme": "theme-blue",
10
+ "animate": false,
11
+ "fontSize": "medium",
12
+ "lineDatapointStyle": "hover",
13
+ "lineDatapointColor": "Same as Line",
14
+ "barHasBorder": "false",
15
+ "isLollipopChart": false,
16
+ "lollipopShape": "circle",
17
+ "lollipopColorStyle": "two-tone",
18
+ "visualizationSubType": "regular",
19
+ "barStyle": "flat",
20
+ "roundingStyle": "standard",
21
+ "tipRounding": "top",
22
+ "isResponsiveTicks": false,
23
+ "general": {
24
+ "annotationDropdownText": "Annotations",
25
+ "showDownloadButton": false,
26
+ "showMissingDataLabel": true,
27
+ "showSuppressedSymbol": true,
28
+ "showZeroValueData": true,
29
+ "hideNullValue": true,
30
+ "showAnnotationDropdown": false
31
+ },
32
+ "padding": { "left": 5, "right": 5 },
33
+ "preliminaryData": [],
34
+ "yAxis": {
35
+ "hideAxis": true,
36
+ "displayNumbersOnBar": false,
37
+ "hideLabel": false,
38
+ "hideTicks": true,
39
+ "size": "50",
40
+ "gridLines": true,
41
+ "enablePadding": true,
42
+ "min": "0",
43
+ "max": "99",
44
+ "labelColor": "#333",
45
+ "tickLabelColor": "#333",
46
+ "tickColor": "#333",
47
+ "rightHideAxis": true,
48
+ "rightAxisSize": 0,
49
+ "rightLabel": "",
50
+ "rightLabelOffsetSize": 0,
51
+ "rightAxisLabelColor": "#333",
52
+ "rightAxisTickLabelColor": "#333",
53
+ "rightAxisTickColor": "#333",
54
+ "numTicks": "5",
55
+ "axisPadding": 0,
56
+ "scalePadding": 10,
57
+ "tickRotation": 0,
58
+ "anchors": [],
59
+ "shoMissingDataLabel": true,
60
+ "showMissingDataLine": true,
61
+ "categories": [],
62
+ "label": "",
63
+ "maxValue": 108.9
64
+ },
65
+ "boxplot": {
66
+ "plots": [],
67
+ "borders": "true",
68
+ "plotOutlierValues": false,
69
+ "plotNonOutlierValues": true,
70
+ "labels": {
71
+ "q1": "Lower Quartile",
72
+ "q2": "q2",
73
+ "q3": "Upper Quartile",
74
+ "q4": "q4",
75
+ "minimum": "Minimum",
76
+ "maximum": "Maximum",
77
+ "mean": "Mean",
78
+ "median": "Median",
79
+ "sd": "Standard Deviation",
80
+ "iqr": "Interquartile Range",
81
+ "total": "Total",
82
+ "outliers": "Outliers",
83
+ "values": "Values",
84
+ "lowerBounds": "Lower Bounds",
85
+ "upperBounds": "Upper Bounds"
86
+ },
87
+ "firstQuartilePercentage": 25,
88
+ "thirdQuartilePercentage": 75,
89
+ "boxWidthPercentage": 40,
90
+ "legend": { "showHowToReadText": false, "howToReadText": "" }
91
+ },
92
+ "topAxis": { "hasLine": false },
93
+ "isLegendValue": false,
94
+ "barThickness": 0.35,
95
+ "barHeight": 25,
96
+ "barSpace": 15,
97
+ "heights": { "vertical": "250", "horizontal": 750, "mobileVertical": "150" },
98
+ "xAxis": {
99
+ "sortDates": false,
100
+ "anchors": [],
101
+ "type": "date-time",
102
+ "showTargetLabel": true,
103
+ "targetLabel": "Target",
104
+ "hideAxis": false,
105
+ "hideLabel": false,
106
+ "hideTicks": false,
107
+ "size": "0",
108
+ "tickRotation": 0,
109
+ "min": "",
110
+ "max": "",
111
+ "labelColor": "#333",
112
+ "tickLabelColor": "#333",
113
+ "tickColor": "#333",
114
+ "numTicks": "6",
115
+ "labelOffset": 0,
116
+ "axisPadding": 200,
117
+ "target": 0,
118
+ "maxTickRotation": "45",
119
+ "padding": "0",
120
+ "showYearsOnce": false,
121
+ "sortByRecentDate": false,
122
+ "dataKey": "week_end",
123
+ "label": "",
124
+ "dateParseFormat": "%Y-%m-%d",
125
+ "dateDisplayFormat": "%b. %-d %Y",
126
+ "axisBBox": 24.939998626708984,
127
+ "tickWidthMax": 108,
128
+ "viewportNumTicks": { "xxs": "4" },
129
+ "manual": true
130
+ },
131
+ "table": {
132
+ "label": "Data Table",
133
+ "expanded": false,
134
+ "limitHeight": false,
135
+ "height": "",
136
+ "caption": "",
137
+ "showDownloadUrl": false,
138
+ "showDataTableLink": true,
139
+ "showDownloadLinkBelow": true,
140
+ "indexLabel": "Week Ending",
141
+ "download": true,
142
+ "showVertical": true,
143
+ "dateDisplayFormat": "",
144
+ "showMissingDataLabel": true,
145
+ "showSuppressedSymbol": true,
146
+ "show": true
147
+ },
148
+ "orientation": "vertical",
149
+ "color": "pinkpurple",
150
+ "columns": {},
151
+ "legend": {
152
+ "hide": false,
153
+ "behavior": "isolate",
154
+ "axisAlign": true,
155
+ "singleRow": true,
156
+ "colorCode": "",
157
+ "reverseLabelOrder": false,
158
+ "description": "",
159
+ "dynamicLegend": false,
160
+ "dynamicLegendDefaultText": "Show All",
161
+ "dynamicLegendItemLimit": 5,
162
+ "dynamicLegendItemLimitMessage": "Dynamic Legend Item Limit Hit.",
163
+ "dynamicLegendChartMessage": "Select Options from the Legend",
164
+ "label": "",
165
+ "lineMode": false,
166
+ "verticalSorted": false,
167
+ "highlightOnHover": false,
168
+ "hideSuppressedLabels": false,
169
+ "hideSuppressionLink": false,
170
+ "seriesHighlight": [],
171
+ "style": "lines",
172
+ "subStyle": "linear blocks",
173
+ "tickRotation": "",
174
+ "hideBorder": { "side": false, "topBottom": true },
175
+ "position": "bottom"
176
+ },
177
+ "brush": { "height": 25, "active": false },
178
+ "exclusions": { "active": false, "keys": [] },
179
+ "palette": "qualitative-bold",
180
+ "isPaletteReversed": false,
181
+ "twoColor": { "palette": "monochrome-1", "isPaletteReversed": false },
182
+ "labels": false,
183
+ "dataFormat": {
184
+ "commas": true,
185
+ "prefix": "",
186
+ "suffix": " percent vaccinated",
187
+ "abbreviated": true,
188
+ "bottomSuffix": "",
189
+ "bottomPrefix": "",
190
+ "bottomAbbreviated": false,
191
+ "roundTo": "1",
192
+ "onlyShowTopPrefixSuffix": true
193
+ },
194
+ "confidenceKeys": {},
195
+ "visual": {
196
+ "border": true,
197
+ "accent": true,
198
+ "background": true,
199
+ "verticalHoverLine": true,
200
+ "horizontalHoverLine": false
201
+ },
202
+ "useLogScale": false,
203
+ "filterBehavior": "Filter Change",
204
+ "highlightedBarValues": [],
205
+ "series": [
206
+ { "dataKey": "Adults", "type": "Line", "axis": "Left", "tooltip": true, "name": "Adults (18+)" },
207
+ { "dataKey": "Children", "type": "dashed-md", "axis": "Left", "tooltip": true, "name": "Children (under 18)" }
208
+ ],
209
+ "tooltips": { "opacity": 90, "singleSeries": false, "dateDisplayFormat": "%B %-d, %Y" },
210
+ "forestPlot": {
211
+ "startAt": 0,
212
+ "colors": { "line": "", "shape": "" },
213
+ "lineOfNoEffect": { "show": true },
214
+ "type": "",
215
+ "pooledResult": { "diamondHeight": 5, "column": "" },
216
+ "estimateField": "",
217
+ "estimateRadius": "",
218
+ "shape": "square",
219
+ "rowHeight": 20,
220
+ "description": { "show": true, "text": "description", "location": 0 },
221
+ "result": { "show": true, "text": "result", "location": 100 },
222
+ "radius": { "min": 2, "max": 10, "scalingColumn": "" },
223
+ "regression": { "lower": 0, "upper": 0, "estimateField": 0 },
224
+ "leftWidthOffset": 0,
225
+ "rightWidthOffset": 0,
226
+ "showZeroLine": false,
227
+ "leftLabel": "",
228
+ "rightLabel": ""
229
+ },
230
+ "area": { "isStacked": false },
231
+ "sankey": {
232
+ "title": { "defaultColor": "black" },
233
+ "iterations": 1,
234
+ "rxValue": 0.9,
235
+ "overallSize": { "width": 900, "height": 700 },
236
+ "margin": { "margin_y": 25, "margin_x": 0 },
237
+ "nodeSize": { "nodeWidth": 26, "nodeHeight": 40 },
238
+ "nodePadding": 55,
239
+ "nodeFontColor": "black",
240
+ "nodeColor": { "default": "#ff8500", "inactive": "#808080" },
241
+ "linkColor": { "default": "#ffc900", "inactive": "#D3D3D3" },
242
+ "opacity": {
243
+ "nodeOpacityDefault": 1,
244
+ "nodeOpacityInactive": 0.1,
245
+ "LinkOpacityDefault": 1,
246
+ "LinkOpacityInactive": 0.1
247
+ },
248
+ "storyNodeFontColor": "#006778",
249
+ "storyNodeText": [],
250
+ "nodeValueStyle": { "textBefore": "(", "textAfter": ")" },
251
+ "data": []
252
+ },
253
+ "visualizationType": "Line",
254
+ "customColors": ["#f06f19", "#f06f19", "#f06f19", "#000000", "#0A6C75", "#C0F2FD", "#C0F2FD", "#C0F2FD", "#C0F2FD"],
255
+ "dataFileName": "/wcms/vizdata/Respitory_Viruses/NISVaccinationsCumulative7a.json",
256
+ "dataFileSourceType": "url",
257
+ "dataUrl": "https://www.cdc.gov/wcms/vizdata/Respitory_Viruses/NISVaccinationsCumulative7a.json",
258
+ "dataDescription": {
259
+ "horizontal": false,
260
+ "series": true,
261
+ "singleRow": false,
262
+ "xKey": "week_end",
263
+ "valueKeysTallSupport": ["value"],
264
+ "ignoredKeys": [
265
+ "suppression_flag",
266
+ "95_confidence_interval_lower",
267
+ "95_confidence_interval_upper",
268
+ "95_confidence_internal_range"
269
+ ],
270
+ "seriesKey": "demographic_group"
271
+ },
272
+ "version": "4.24.10",
273
+ "description": "<div class=\"text-left\"><p class=\"fnote\">95% confidence intervals for the point estimates are presented at the data.cdc.gov link below.<br><br> Data last updated on <span data-timestamp=\"NISVaccinationsCumulative7a:Data_as_of\"></span> and presented through <span data-timestamp=\"NISVaccinationsCumulative7a:Data_Presented_Through\"></span>. <a href=\"https://data.cdc.gov/Vaccinations/Weekly-Respiratory-Virus-Vaccination-Data-Children/5c6r-xi2t\">View this dataset</a> on data.CDC.gov.</p></div>",
274
+ "dynamicMarginTop": 0,
275
+ "filters": [
276
+ {
277
+ "values": ["COVID-19", "Influenza", "RSV"],
278
+ "filterStyle": "dropdown",
279
+ "id": 1726688167656,
280
+ "columnName": "pathogen",
281
+ "showDropdown": false,
282
+ "active": "COVID-19"
283
+ }
284
+ ],
285
+ "runtimeDataUrl": "https://wcms-wp.cdc.gov/wcms/vizdata/Respitory_Viruses/NISVaccinationsCumulative7a.json",
286
+ "showLineSeriesLabels": false,
287
+ "colorMatchLineSeriesLabels": false
288
+ }
@@ -33,7 +33,13 @@ const AreaChartStacked = ({ xScale, yScale, yMax, xMax, handleTooltipMouseOver,
33
33
  data && (
34
34
  <svg height={Number(yMax)}>
35
35
  <ErrorBoundary component='AreaChartStacked'>
36
- <Group className='area-chart' key='area-wrapper' left={Number(config.yAxis.size) + strokeWidth / 2} height={Number(yMax)} style={{ overflow: 'hidden' }}>
36
+ <Group
37
+ className='area-chart'
38
+ key='area-wrapper'
39
+ left={Number(config.yAxis.size) + strokeWidth / 2}
40
+ height={Number(yMax)}
41
+ style={{ overflow: 'hidden' }}
42
+ >
37
43
  <AreaStack
38
44
  data={data}
39
45
  keys={config.runtime.areaSeriesKeys.map(s => s.dataKey) || config.series.map(s => s.dataKey)}
@@ -44,8 +50,14 @@ const AreaChartStacked = ({ xScale, yScale, yMax, xMax, handleTooltipMouseOver,
44
50
  >
45
51
  {({ stacks, path }) => {
46
52
  return stacks.map((stack, stackIndex) => {
47
- let transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(stack.key) === -1
48
- let displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(stack.key) !== -1
53
+ let transparentArea =
54
+ config.legend.behavior === 'highlight' &&
55
+ seriesHighlight.length > 0 &&
56
+ seriesHighlight.indexOf(stack.key) === -1
57
+ let displayArea =
58
+ config.legend.behavior === 'highlight' ||
59
+ seriesHighlight.length === 0 ||
60
+ seriesHighlight.indexOf(stack.key) !== -1
49
61
 
50
62
  return (
51
63
  // prettier-ignore
@@ -44,7 +44,7 @@ const CategoricalYAxis = ({ yMax, leftSize, max, xMax }) => {
44
44
  if (categoryObj[lastheight] === '') {
45
45
  // Calculate the sum of the numeric values of all other heights
46
46
  const sumOfValues = heights.slice(0, -1).reduce((sum, label) => {
47
- const value = parseInt(categoryObj[label], 10)
47
+ const value = Number(categoryObj[label])
48
48
  return sum + (isNaN(value) ? 0 : value)
49
49
  }, 0)
50
50
 
@@ -52,7 +52,7 @@ const CategoricalYAxis = ({ yMax, leftSize, max, xMax }) => {
52
52
  const newValue = max - sumOfValues
53
53
 
54
54
  // Update the last height with the new value
55
- categoryObj[lastheight] = newValue.toString()
55
+ categoryObj[lastheight] = newValue
56
56
  }
57
57
 
58
58
  return [categoryObj]
@@ -22,6 +22,8 @@ import chroma from 'chroma-js'
22
22
  // Local context and types
23
23
  import BarChartContext, { BarChartContextValues } from './context'
24
24
  import { ChartContext } from '../../../types/ChartContext'
25
+ import _ from 'lodash'
26
+ import { getBarData } from '../helpers/getBarData'
25
27
 
26
28
  export const BarChartHorizontal = () => {
27
29
  const { xScale, yScale, yMax, seriesScale } = useContext<BarChartContextValues>(BarChartContext)
@@ -35,9 +37,7 @@ export const BarChartHorizontal = () => {
35
37
  formatDate,
36
38
  parseDate,
37
39
  setSharedFilter,
38
- isNumber,
39
- getYAxisData,
40
- getXAxisData
40
+ isNumber
41
41
  } = useContext<ChartContext>(ConfigContext)
42
42
  const {
43
43
  isHorizontal,
@@ -60,13 +60,21 @@ export const BarChartHorizontal = () => {
60
60
 
61
61
  const { HighLightedBarUtils } = useHighlightedBars(config)
62
62
 
63
+ const hasConfidenceInterval = Object.keys(config.confidenceKeys).length > 0
64
+
65
+ const _data = getBarData(config, data, hasConfidenceInterval)
66
+
67
+ const root = document.documentElement
68
+
69
+ const coolGray90 = getComputedStyle(root).getPropertyValue('--cool-gray-90')
70
+
63
71
  return (
64
72
  config.visualizationSubType !== 'stacked' &&
65
73
  config.visualizationType === 'Bar' &&
66
74
  config.orientation === 'horizontal' && (
67
75
  <Group>
68
76
  <BarGroup
69
- data={config.preliminaryData?.some(pd => pd.value && pd.type === 'suppression') ? tableData : data}
77
+ data={config.preliminaryData?.some(pd => pd.value && pd.type === 'suppression') ? tableData : _data}
70
78
  keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys}
71
79
  height={yMax}
72
80
  x0={d => d[config.runtime.originalXAxis.dataKey]}
@@ -86,6 +94,8 @@ export const BarChartHorizontal = () => {
86
94
  top={barGroup.y}
87
95
  >
88
96
  {barGroup.bars.map((bar, index) => {
97
+ const datum = _data[barGroup.index]
98
+ const dataValue = datum[config.runtime.originalXAxis.dataKey]
89
99
  const scaleVal = config.yAxis.type === 'logarithmic' ? 0.1 : 0
90
100
  let highlightedBarValues = config.highlightedBarValues
91
101
  .map(item => item.value)
@@ -119,9 +129,7 @@ export const BarChartHorizontal = () => {
119
129
  const barX = bar.value < 0 ? Math.abs(xScale(bar.value)) : xScale(scaleVal)
120
130
  const yAxisValue = formatNumber(bar.value, 'left')
121
131
  const xAxisValue =
122
- config.runtime[section].type === 'date'
123
- ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
124
- : data[barGroup.index][config.runtime.originalXAxis.dataKey]
132
+ config.runtime[section].type === 'date' ? formatDate(parseDate(dataValue)) : dataValue
125
133
 
126
134
  const barPosition = !isPositiveBar ? 'below' : 'above'
127
135
  const absentDataLabel = getAbsentDataLabel(yAxisValue)
@@ -167,7 +175,10 @@ export const BarChartHorizontal = () => {
167
175
  config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key]
168
176
  ? colorScale(config.runtime.seriesLabels[bar.key])
169
177
  : colorScale(bar.key)
170
- barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor) // Color code by category
178
+ const hasDynamicCategory = config.series.find(s => s.dynamicCategory)
179
+ if (!hasDynamicCategory) {
180
+ barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor) // Color code by category
181
+ }
171
182
  const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
172
183
  const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
173
184
  const isHighlightedBar = highlightedBarValues?.includes(xAxisValue)
@@ -199,6 +210,16 @@ export const BarChartHorizontal = () => {
199
210
  return barColor
200
211
  }
201
212
 
213
+ // Confidence Interval Variables
214
+ const tickWidth = 5
215
+ const yPos = barHeight * bar.index + barHeight / 2
216
+ const [upperPos, lowerPos] = ['upper', 'lower'].map(position => {
217
+ if (!hasConfidenceInterval) return
218
+ const d = datum.dynamicData ? datum.CI[bar.key][position] : datum[config.confidenceKeys[position]]
219
+ return xScale(d)
220
+ })
221
+ // End Confidence Interval Variables
222
+
202
223
  return (
203
224
  <Group key={`${barGroup.index}--${index}`}>
204
225
  <Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
@@ -321,10 +342,10 @@ export const BarChartHorizontal = () => {
321
342
  textAnchor={'start'}
322
343
  >
323
344
  {config.runtime.yAxis.type === 'date'
324
- ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
345
+ ? formatDate(parseDate(dataValue))
325
346
  : isHorizontal
326
- ? data[barGroup.index][config.runtime.originalXAxis.dataKey]
327
- : formatNumber(data[barGroup.index][config.runtime.originalXAxis.dataKey])}
347
+ ? dataValue
348
+ : formatNumber(dataValue)}
328
349
  </Text>
329
350
  )}
330
351
 
@@ -357,6 +378,20 @@ export const BarChartHorizontal = () => {
357
378
  <animate attributeName='height' values={`0, ${lollipopShapeSize}`} dur='2.5s' />
358
379
  </rect>
359
380
  )}
381
+ {hasConfidenceInterval && (
382
+ <path
383
+ key={`confidence-interval-h-${yPos}-${datum[config.runtime.originalXAxis.dataKey]}`}
384
+ stroke={coolGray90}
385
+ strokeWidth='px'
386
+ d={`
387
+ M${lowerPos} ${yPos - tickWidth}
388
+ L${lowerPos} ${yPos + tickWidth}
389
+ M${lowerPos} ${yPos}
390
+ L${upperPos} ${yPos}
391
+ M${upperPos} ${yPos - tickWidth}
392
+ L${upperPos} ${yPos + tickWidth} `}
393
+ />
394
+ )}
360
395
  </Group>
361
396
  </Group>
362
397
  )
@@ -365,32 +400,6 @@ export const BarChartHorizontal = () => {
365
400
  ))
366
401
  }}
367
402
  </BarGroup>
368
-
369
- {Object.keys(config.confidenceKeys).length > 0
370
- ? data.map(d => {
371
- let xPos, yPos
372
- let upperPos
373
- let lowerPos
374
- let tickWidth = 5
375
- yPos = yScale(getXAxisData(d)) - 0.75 * config.barHeight
376
- upperPos = xScale(getYAxisData(d, config.confidenceKeys.upper))
377
- lowerPos = xScale(getYAxisData(d, config.confidenceKeys.lower))
378
- return (
379
- <path
380
- key={`confidence-interval-h-${yPos}-${d[config.runtime.originalXAxis.dataKey]}`}
381
- stroke='#333'
382
- strokeWidth='px'
383
- d={`
384
- M${lowerPos} ${yPos - tickWidth}
385
- L${lowerPos} ${yPos + tickWidth}
386
- M${lowerPos} ${yPos}
387
- L${upperPos} ${yPos}
388
- M${upperPos} ${yPos - tickWidth}
389
- L${upperPos} ${yPos + tickWidth} `}
390
- />
391
- )
392
- })
393
- : ''}
394
403
  </Group>
395
404
  )
396
405
  )
@@ -21,7 +21,8 @@ import createBarElement from '@cdc/core/components/createBarElement'
21
21
  import chroma from 'chroma-js'
22
22
  // Types
23
23
  import { type ChartContext } from '../../../types/ChartContext'
24
- import _, { has } from 'lodash'
24
+ import _ from 'lodash'
25
+ import { getBarData } from '../helpers/getBarData'
25
26
 
26
27
  export const BarChartVertical = () => {
27
28
  const { xScale, yScale, xMax, yMax, seriesScale } = useContext<BarChartContextValues>(BarChartContext)
@@ -44,8 +45,14 @@ export const BarChartVertical = () => {
44
45
  } = useBarChart()
45
46
 
46
47
  // prettier-ignore
47
- const { colorScale, config, dashboardConfig, tableData, formatDate, formatNumber, getXAxisData, getYAxisData, parseDate, seriesHighlight, setSharedFilter, transformedData, brushConfig } = useContext<ChartContext>(ConfigContext)
48
+ const { colorScale, config, dashboardConfig, tableData, formatDate, formatNumber, parseDate, seriesHighlight, setSharedFilter, transformedData, brushConfig } = useContext<ChartContext>(ConfigContext)
49
+
48
50
  const { HighLightedBarUtils } = useHighlightedBars(config)
51
+
52
+ const root = document.documentElement
53
+
54
+ const coolGray90 = getComputedStyle(root).getPropertyValue('--cool-gray-90')
55
+
49
56
  let data = transformedData
50
57
  // check if user add suppression
51
58
  const isSuppressionActive = config.preliminaryData.some(pd => pd.value && pd.type === 'suppression')
@@ -60,32 +67,7 @@ export const BarChartVertical = () => {
60
67
 
61
68
  const hasConfidenceInterval = Object.keys(config.confidenceKeys).length > 0
62
69
 
63
- const getData = () => {
64
- const dynamicSeries = config.series.find(s => s.dynamicCategory)
65
- if (!dynamicSeries) return data
66
- const { dynamicCategory, dataKey } = dynamicSeries
67
- const xAxisKey = config.runtime.originalXAxis.dataKey
68
- const xAxisGroupDataLookup = _.groupBy(data, xAxisKey)
69
- return Object.values(xAxisGroupDataLookup).map(group => {
70
- return group.reduce((acc, datum) => {
71
- const dataValue = datum[dataKey]
72
- const dataCategory = datum[dynamicCategory]
73
- if (hasConfidenceInterval) {
74
- const { lower, upper } = config.confidenceKeys
75
- if (!acc.CI) acc.CI = {}
76
- const lowerValue = datum[lower]
77
- const upperValue = datum[upper]
78
- acc.CI[dataCategory] = { lower: lowerValue, upper: upperValue }
79
- }
80
- acc[dataCategory] = dataValue
81
- acc[xAxisKey] = datum[xAxisKey]
82
- acc.dynamicData = true
83
- return acc
84
- }, {})
85
- })
86
- }
87
-
88
- const _data = getData()
70
+ const _data = getBarData(config, data, hasConfidenceInterval)
89
71
  return (
90
72
  config.visualizationSubType !== 'stacked' &&
91
73
  (config.visualizationType === 'Bar' ||
@@ -109,11 +91,11 @@ export const BarChartVertical = () => {
109
91
  }}
110
92
  >
111
93
  {barGroups => {
112
- return barGroups.map((barGroup, index) => (
94
+ return barGroups.map((barGroup, _index) => (
113
95
  <Group
114
- className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${config.orientation}`}
115
- key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
116
- id={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
96
+ className={`bar-group-${barGroup.index}-${barGroup.x0}--${_index} ${config.orientation}`}
97
+ key={`bar-group-${barGroup.index}-${barGroup.x0}--${_index}`}
98
+ id={`bar-group-${barGroup.index}-${barGroup.x0}--${_index}`}
117
99
  left={barGroup.x0}
118
100
  >
119
101
  {barGroup.bars.map((bar, index) => {
@@ -249,11 +231,17 @@ export const BarChartVertical = () => {
249
231
  // Confidence Interval Variables
250
232
  const tickWidth = 5
251
233
  const xPos = barX + (config.xAxis.type !== 'date-time' ? barWidth / 2 : 0)
252
- const [upperPos, lowerPos] = ['upper', 'lower'].map(position => {
253
- if (!hasConfidenceInterval) return
254
- const d = datum.dynamicData ? datum.CI[bar.key][position] : datum[config.confidenceKeys[position]]
255
- return yScale(d)
256
- })
234
+
235
+ const upperPos = yScale(
236
+ datum.dynamicData && datum?.CI?.[bar.key]
237
+ ? datum.CI[bar.key].upper
238
+ : datum[config.confidenceKeys.upper]
239
+ )
240
+ const lowerPos = yScale(
241
+ datum.dynamicData && datum?.CI?.[bar.key]
242
+ ? datum.CI[bar.key].lower
243
+ : datum[config.confidenceKeys.lower]
244
+ )
257
245
  // End Confidence Interval Variables
258
246
 
259
247
  return (
@@ -372,7 +360,7 @@ export const BarChartVertical = () => {
372
360
  <rect
373
361
  display={displaylollipopShape}
374
362
  x={barX - lollipopBarWidth / 2}
375
- y={barY}
363
+ y={bar.y}
376
364
  width={lollipopShapeSize}
377
365
  height={lollipopShapeSize}
378
366
  fill={getBarBackgroundColor(colorScale(config.runtime.seriesLabels[bar.key]))}
@@ -384,10 +372,10 @@ export const BarChartVertical = () => {
384
372
  <animate attributeName='height' values={`0, ${lollipopShapeSize}`} dur='2.5s' />
385
373
  </rect>
386
374
  )}
387
- {hasConfidenceInterval && (
375
+ {hasConfidenceInterval && bar.value !== undefined && datum && (
388
376
  <path
389
377
  key={`confidence-interval-v-${datum[config.runtime.originalXAxis.dataKey]}`}
390
- stroke='#333'
378
+ stroke={coolGray90}
391
379
  strokeWidth='px'
392
380
  d={`M${xPos - tickWidth} ${upperPos}
393
381
  L${xPos + tickWidth} ${upperPos}
@@ -0,0 +1,28 @@
1
+ import _ from 'lodash'
2
+ import { TransformedData } from '../../../types/ChartContext'
3
+ import { ChartConfig } from '../../../types/ChartConfig'
4
+
5
+ export const getBarData = (config: ChartConfig, data: TransformedData[], hasConfidenceInterval: boolean) => {
6
+ const dynamicSeries = config.series.find(s => s.dynamicCategory)
7
+ if (!dynamicSeries) return data
8
+ const { dynamicCategory, dataKey } = dynamicSeries
9
+ const xAxisKey = config.runtime.originalXAxis.dataKey
10
+ const xAxisGroupDataLookup = _.groupBy(data, xAxisKey)
11
+ return Object.values(xAxisGroupDataLookup).map(group => {
12
+ return group.reduce((acc, datum) => {
13
+ const dataValue = datum[dataKey]
14
+ const dataCategory = datum[dynamicCategory]
15
+ if (hasConfidenceInterval) {
16
+ const { lower, upper } = config.confidenceKeys
17
+ if (!acc.CI) acc.CI = {}
18
+ const lowerValue = datum[lower]
19
+ const upperValue = datum[upper]
20
+ acc.CI[dataCategory] = { lower: lowerValue, upper: upperValue }
21
+ }
22
+ acc[dataCategory] = dataValue
23
+ acc[xAxisKey] = datum[xAxisKey]
24
+ acc.dynamicData = true
25
+ return acc
26
+ }, {})
27
+ })
28
+ }