@cdc/chart 4.24.9 → 4.24.11

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