@cdc/chart 4.24.9 → 4.24.10

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/LICENSE +201 -0
  2. package/dist/cdcchart.js +43919 -40370
  3. package/index.html +1 -1
  4. package/package.json +2 -2
  5. package/src/CdcChart.tsx +129 -108
  6. package/src/_stories/Chart.Legend.Gradient.stories.tsx +33 -0
  7. package/src/_stories/Chart.stories.tsx +28 -0
  8. package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
  9. package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
  10. package/src/_stories/ChartPrefixSuffix.stories.tsx +151 -0
  11. package/src/_stories/_mock/horizontal_bar.json +257 -0
  12. package/src/_stories/_mock/large_x_axis_labels.json +261 -0
  13. package/src/_stories/_mock/paired-bar.json +262 -0
  14. package/src/_stories/_mock/pie_with_data.json +255 -0
  15. package/src/_stories/_mock/simplified_line.json +1510 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
  17. package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
  18. package/src/components/Axis/Categorical.Axis.tsx +22 -4
  19. package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
  20. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
  21. package/src/components/BarChart/components/BarChart.Vertical.tsx +78 -20
  22. package/src/components/BarChart/helpers/index.ts +23 -4
  23. package/src/components/BrushChart.tsx +3 -2
  24. package/src/components/DeviationBar.jsx +58 -8
  25. package/src/components/EditorPanel/EditorPanel.tsx +63 -40
  26. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +8 -25
  27. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
  28. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -35
  29. package/src/components/EditorPanel/components/panels.scss +4 -6
  30. package/src/components/EditorPanel/editor-panel.scss +0 -8
  31. package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
  32. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
  33. package/src/components/EditorPanel/useEditorPermissions.ts +1 -0
  34. package/src/components/ForestPlot/ForestPlot.tsx +2 -3
  35. package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
  36. package/src/components/Legend/Legend.Component.tsx +16 -16
  37. package/src/components/Legend/Legend.Suppression.tsx +25 -20
  38. package/src/components/Legend/Legend.tsx +0 -2
  39. package/src/components/Legend/helpers/index.ts +16 -19
  40. package/src/components/LegendWrapper.tsx +3 -1
  41. package/src/components/LineChart/components/LineChart.Circle.tsx +10 -0
  42. package/src/components/LinearChart.tsx +740 -562
  43. package/src/components/PairedBarChart.jsx +50 -10
  44. package/src/components/PieChart/PieChart.tsx +1 -6
  45. package/src/components/Regions/components/Regions.tsx +33 -19
  46. package/src/components/ZoomBrush.tsx +25 -6
  47. package/src/coreStyles_chart.scss +3 -0
  48. package/src/data/initial-state.js +6 -2
  49. package/src/helpers/configHelpers.ts +28 -0
  50. package/src/helpers/handleRankByValue.ts +15 -0
  51. package/src/helpers/sizeHelpers.ts +25 -0
  52. package/src/helpers/tests/handleRankByValue.test.ts +37 -0
  53. package/src/helpers/tests/sizeHelpers.test.ts +80 -0
  54. package/src/hooks/useColorPalette.js +10 -2
  55. package/src/hooks/useLegendClasses.ts +4 -0
  56. package/src/hooks/useScales.ts +31 -3
  57. package/src/hooks/useTooltip.tsx +9 -5
  58. package/src/index.jsx +1 -0
  59. package/src/scss/DataTable.scss +5 -4
  60. package/src/scss/main.scss +57 -52
  61. package/src/types/ChartConfig.ts +38 -16
  62. package/src/types/ChartContext.ts +18 -14
  63. package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
  64. package/src/_stories/ChartBrush.stories.tsx +0 -19
  65. package/src/components/LinearChart.jsx +0 -817
package/index.html CHANGED
@@ -49,7 +49,7 @@
49
49
  -->
50
50
 
51
51
  <!-- GENERIC CHART TYPES -->
52
- <div class="react-container" data-config="/examples/cases-year.json"></div>
52
+ <!-- <div class="react-container" data-config="/examples/cases-year.json"></div> -->
53
53
  <!-- <div class="react-container" data-config="/examples/test.json"></div> -->
54
54
  <!-- <div class="react-container" data-config="/examples/feature/line/line-chart.json"></div> -->
55
55
  <!-- <div class="react-container" data-config="/examples/dev-8332.json"></div> -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/chart",
3
- "version": "4.24.9",
3
+ "version": "4.24.10",
4
4
  "description": "React component for visualizing tabular data in various types of charts",
5
5
  "moduleName": "CdcChart",
6
6
  "main": "dist/cdcchart",
@@ -61,7 +61,7 @@
61
61
  "react": "^18.2.0",
62
62
  "react-dom": "^18.2.0"
63
63
  },
64
- "gitHead": "c4b0402afe6ed209a85b7078711549b9fd7dae7d",
64
+ "gitHead": "a4d88d1bc91f596e1b0307d8e25c57ad8c668b75",
65
65
  "devDependencies": {
66
66
  "@types/d3-sankey": "^0.12.4",
67
67
  "resize-observer-polyfill": "^1.5.1"
package/src/CdcChart.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useCallback, useRef, useId } from 'react'
1
+ import React, { useState, useEffect, useCallback, useRef, useId, useMemo } from 'react'
2
2
 
3
3
  // IE11
4
4
  import ResizeObserver from 'resize-observer-polyfill'
@@ -9,6 +9,7 @@ import Button from '@cdc/core/components/elements/Button'
9
9
 
10
10
  //types
11
11
  import { DimensionsType } from '@cdc/core/types/Dimensions'
12
+ import { type DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
12
13
 
13
14
  // External Libraries
14
15
  import { scaleOrdinal } from '@visx/scale'
@@ -38,6 +39,7 @@ import { sortAsc, sortDesc } from './helpers/sort'
38
39
  import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
39
40
  import { lineOptions } from './helpers/lineOptions'
40
41
  import { handleLineType } from './helpers/handleLineType'
42
+ import { handleRankByValue } from './helpers/handleRankByValue'
41
43
  import { generateColorsArray } from './helpers/generateColorsArray'
42
44
  import Loading from '@cdc/core/components/Loading'
43
45
  import Filters from '@cdc/core/components/Filters'
@@ -45,7 +47,6 @@ import MediaControls from '@cdc/core/components/MediaControls'
45
47
  import Annotation from './components/Annotations'
46
48
 
47
49
  // Helpers
48
- import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
49
50
  import { publish, subscribe, unsubscribe } from '@cdc/core/helpers/events'
50
51
  import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
51
52
  import numberFromString from '@cdc/core/helpers/numberFromString'
@@ -56,6 +57,8 @@ import isNumber from '@cdc/core/helpers/isNumber'
56
57
  import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
57
58
  import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
58
59
  import { isConvertLineToBarGraph } from './helpers/isConvertLineToBarGraph'
60
+ import { calcInitialHeight } from './helpers/sizeHelpers'
61
+ import { isLegendWrapViewport, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
59
62
 
60
63
  import './scss/main.scss'
61
64
  // load both then config below determines which to use
@@ -69,8 +72,24 @@ import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
69
72
  import SkipTo from '@cdc/core/components/elements/SkipTo'
70
73
  import { filterVizData } from '@cdc/core/helpers/filterVizData'
71
74
  import LegendWrapper from './components/LegendWrapper'
72
-
73
- export default function CdcChart({
75
+ import _ from 'lodash'
76
+ import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
77
+
78
+ interface CdcChartProps {
79
+ configUrl?: string
80
+ config?: ChartConfig
81
+ isEditor?: boolean
82
+ isDebug?: boolean
83
+ isDashboard?: boolean
84
+ setConfig?: (config: ChartConfig) => void
85
+ setEditing?: (editing: boolean) => void
86
+ hostname?: string
87
+ link?: string
88
+ setSharedFilter?: (filter: any) => void
89
+ setSharedFilterValue?: (value: any) => void
90
+ dashboardConfig?: DashboardConfig
91
+ }
92
+ const CdcChart = ({
74
93
  configUrl,
75
94
  config: configObj,
76
95
  isEditor = false,
@@ -83,12 +102,13 @@ export default function CdcChart({
83
102
  setSharedFilter,
84
103
  setSharedFilterValue,
85
104
  dashboardConfig
86
- }) {
105
+ }: CdcChartProps) => {
87
106
  const transform = new DataTransform()
88
107
  const [loading, setLoading] = useState(true)
108
+ const svgRef = useRef(null)
89
109
  const [colorScale, setColorScale] = useState(null)
90
110
  const [config, setConfig] = useState<ChartConfig>({} as ChartConfig)
91
- const [stateData, setStateData] = useState(config.data || [])
111
+ const [stateData, setStateData] = useState(_.cloneDeep(configObj?.data) || [])
92
112
  const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
93
113
  const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
94
114
  const [seriesHighlight, setSeriesHighlight] = useState<string[]>(
@@ -108,6 +128,7 @@ export default function CdcChart({
108
128
  isBrushing: false
109
129
  })
110
130
 
131
+ const { description, visualizationType } = config
111
132
  let [width] = dimensions
112
133
  const useVertical = config.orientation === 'vertical'
113
134
  const useMobileVertical = config.heights?.mobileVertical && ['xs', 'xxs'].includes(currentViewport)
@@ -115,12 +136,10 @@ export default function CdcChart({
115
136
  const renderedOrientation = useVertical ? responsiveVertical : 'horizontal'
116
137
  let height = config.aspectRatio ? width * config.aspectRatio : config?.heights?.[renderedOrientation]
117
138
  if (config.visualizationType === 'Pie') height = config?.heights?.[renderedOrientation]
118
- height = height + Number(config?.xAxis?.size) + 45
139
+ height = height + Number(config.orientation === 'horizontal' ? config.yAxis.size : config?.xAxis?.size) + 45
119
140
 
120
- type Config = typeof config
121
- let legendMemo = useRef(new Map()) // map collection
122
- let innerContainerRef = useRef()
123
141
  const legendRef = useRef(null)
142
+ const parentRef = useRef(null)
124
143
 
125
144
  const handleDragStateChange = isDragging => {
126
145
  setIsDraggingAnnotation(isDragging)
@@ -129,7 +148,7 @@ export default function CdcChart({
129
148
  if (isDebug) console.log('Chart config, isEditor', config, isEditor)
130
149
 
131
150
  // Destructure items from config for more readable JSX
132
- let { legend, title, description, visualizationType } = config
151
+ let { legend, title } = config
133
152
 
134
153
  // set defaults on titles if blank AND only in editor
135
154
  if (isEditor) {
@@ -138,7 +157,7 @@ export default function CdcChart({
138
157
 
139
158
  if (config.table && (!config.table?.label || config.table?.label === '')) config.table.label = 'Data Table'
140
159
 
141
- const { barBorderClass, lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
160
+ const { lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
142
161
  const legendId = useId()
143
162
 
144
163
  const checkLineToBarGraph = () => {
@@ -201,6 +220,8 @@ export default function CdcChart({
201
220
 
202
221
  Object.assign(data, { urlFiltered: true })
203
222
 
223
+ data = handleRankByValue(data, config)
224
+
204
225
  updateConfig({ ...config, runtimeDataUrl: dataUrlFinal, data, formattedData: data })
205
226
 
206
227
  if (data) {
@@ -212,7 +233,7 @@ export default function CdcChart({
212
233
  }
213
234
 
214
235
  const loadConfig = async () => {
215
- let response = configObj || (await (await fetch(configUrl)).json())
236
+ const response = _.cloneDeep(configObj) || (await (await fetch(configUrl)).json())
216
237
 
217
238
  // If data is included through a URL, fetch that and store
218
239
  let data: any[] = response.data || []
@@ -263,6 +284,8 @@ export default function CdcChart({
263
284
  data = transform.developerStandardize(data, response.dataDescription)
264
285
  }
265
286
 
287
+ data = handleRankByValue(data, response)
288
+
266
289
  if (data) {
267
290
  setStateData(data)
268
291
  setExcludedData(data)
@@ -276,14 +299,6 @@ export default function CdcChart({
276
299
  }
277
300
  }
278
301
  let newConfig = { ...defaults, ...response }
279
- if (newConfig.filters) {
280
- newConfig.filters.forEach((filter, index) => {
281
- const queryStringFilterValue = getQueryStringFilterValue(filter)
282
- if (queryStringFilterValue) {
283
- newConfig.filters[index].active = queryStringFilterValue
284
- }
285
- })
286
- }
287
302
 
288
303
  if (newConfig.visualizationType === 'Box Plot') {
289
304
  newConfig.legend.hide = true
@@ -306,9 +321,12 @@ export default function CdcChart({
306
321
  updateConfig(processedConfig, data)
307
322
  }
308
323
 
309
- const updateConfig = (newConfig, dataOverride?: any[]) => {
324
+ const updateConfig = (_config, dataOverride?: any[]) => {
325
+ const newConfig = _.cloneDeep(_config)
310
326
  let data = dataOverride || stateData
311
327
 
328
+ data = handleRankByValue(data, newConfig)
329
+
312
330
  // Deeper copy
313
331
  Object.keys(defaults).forEach(key => {
314
332
  if (newConfig[key] && 'object' === typeof newConfig[key] && !Array.isArray(newConfig[key])) {
@@ -356,28 +374,8 @@ export default function CdcChart({
356
374
  // After data is grabbed, loop through and generate filter column values if there are any
357
375
  let currentData: any[] = []
358
376
  if (newConfig.filters) {
359
- newConfig.filters.forEach((filter, index) => {
360
- const filterValues =
361
- filter.filterStyle === 'nested-dropdown'
362
- ? filter.values
363
- : filter.orderedValues ||
364
- generateValuesForFilter(filter.columnName, newExcludedData).sort(
365
- filter.order === 'desc' ? sortDesc : sortAsc
366
- )
367
-
368
- newConfig.filters[index].values = filterValues
369
- // Initial filter should be active
370
-
371
- const includes = (arr: any[], val: any): boolean => (arr || []).map(val => String(val)).includes(String(val))
372
- newConfig.filters[index].active =
373
- !newConfig.filters[index].active || !includes(filterValues, newConfig.filters[index].active)
374
- ? filterValues[0]
375
- : newConfig.filters[index].active
376
- newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle
377
- ? newConfig.filters[index].filterStyle
378
- : 'dropdown'
379
- })
380
- currentData = filterVizData(newConfig.filters, newExcludedData)
377
+ const filtersWithValues = addValuesToFilters(newConfig.filters, newExcludedData)
378
+ currentData = filterVizData(filtersWithValues, newExcludedData)
381
379
  setFilteredData(currentData)
382
380
  }
383
381
 
@@ -398,7 +396,6 @@ export default function CdcChart({
398
396
  Object.keys(finalData[0]).forEach(seriesKey => {
399
397
  if (
400
398
  seriesKey !== newConfig.xAxis.dataKey &&
401
- finalData[0][seriesKey] &&
402
399
  (!newConfig.filters || newConfig.filters.filter(filter => filter.columnName === seriesKey).length === 0) &&
403
400
  (!newConfig.columns || Object.keys(newConfig.columns).indexOf(seriesKey) === -1)
404
401
  ) {
@@ -580,6 +577,7 @@ export default function CdcChart({
580
577
  ) {
581
578
  newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
582
579
  newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
580
+ newConfig.runtime.yAxis.labelOffset *= -1
583
581
 
584
582
  newConfig.runtime.horizontal = false
585
583
  newConfig.orientation = 'horizontal'
@@ -655,7 +653,7 @@ export default function CdcChart({
655
653
  const resizeObserver = new ResizeObserver(entries => {
656
654
  for (let entry of entries) {
657
655
  let { width, height } = entry.contentRect
658
- let svgMarginWidth = 32
656
+ let svgMarginWidth = 30
659
657
  let editorWidth = 350
660
658
 
661
659
  width = isEditor ? width - editorWidth : width
@@ -682,14 +680,14 @@ export default function CdcChart({
682
680
  setContainer(node)
683
681
  }, []) // eslint-disable-line
684
682
 
685
- function isEmpty(obj) {
683
+ const isEmpty = obj => {
686
684
  return Object.keys(obj).length === 0
687
685
  }
688
686
 
689
687
  // Load data when component first mounts
690
688
  useEffect(() => {
691
689
  loadConfig()
692
- }, []) // eslint-disable-line
690
+ }, [configObj?.data?.length ? configObj.data : null]) // eslint-disable-line
693
691
 
694
692
  useEffect(() => {
695
693
  reloadURLData()
@@ -753,14 +751,6 @@ export default function CdcChart({
753
751
  }
754
752
  }, [externalFilters]) // eslint-disable-line
755
753
 
756
- // Load data when configObj data changes
757
- if (configObj) {
758
- // eslint-disable-next-line react-hooks/rules-of-hooks
759
- useEffect(() => {
760
- loadConfig()
761
- }, [configObj.data]) // eslint-disable-line
762
- }
763
-
764
754
  // This will set the bump chart's default scaling type to date-time
765
755
  useEffect(() => {
766
756
  if (['Bump Chart'].includes(config.visualizationType)) {
@@ -875,8 +865,22 @@ export default function CdcChart({
875
865
  }
876
866
  }
877
867
 
878
- const formatDate = date => {
879
- return timeFormat(config.runtime[section].dateDisplayFormat)(date)
868
+ const formatDate = (date, prevDate) => {
869
+ let formattedDate = timeFormat(config.runtime[section].dateDisplayFormat)(date)
870
+ // Handle the case where all months work with '%b.' except for May
871
+ if (config.runtime[section].dateDisplayFormat?.includes('%b.') && formattedDate.includes('May.')) {
872
+ formattedDate = formattedDate.replace(/May\./g, 'May')
873
+ }
874
+ // Show years only once
875
+ if (config.xAxis.showYearsOnce && config.runtime[section].dateDisplayFormat?.includes('%Y') && prevDate) {
876
+ const prevFormattedDate = timeFormat(config.runtime[section].dateDisplayFormat)(prevDate)
877
+ const year = formattedDate.match(/\d{4}/)
878
+ const prevYear = prevFormattedDate.match(/\d{4}/)
879
+ if (year && prevYear && year[0] === prevYear[0]) {
880
+ formattedDate = formattedDate.replace(year, '')
881
+ }
882
+ }
883
+ return formattedDate
880
884
  }
881
885
 
882
886
  const formatTooltipsDate = date => {
@@ -886,7 +890,16 @@ export default function CdcChart({
886
890
  // Format numeric data based on settings in config OR from passed in settings for Additional Columns
887
891
  // - use only for old horizontal data - newer formatNumber is in helper/formatNumber
888
892
  // TODO: we should combine various formatNumber functions across this project.
889
- const formatNumber = (num, axis, shouldAbbreviate = false, addColPrefix, addColSuffix, addColRoundTo) => {
893
+ // TODO suggestion: pass all options as object key/values to allow for more flexibility
894
+ const formatNumber = (
895
+ num,
896
+ axis,
897
+ shouldAbbreviate = false,
898
+ addColPrefix,
899
+ addColSuffix,
900
+ addColRoundTo,
901
+ { index, length } = { index: null, length: null }
902
+ ) => {
890
903
  // if num is NaN return num
891
904
  if (isNaN(num) || !num) return num
892
905
  // Check if the input number is negative
@@ -913,7 +926,8 @@ export default function CdcChart({
913
926
  rightSuffix,
914
927
  bottomPrefix,
915
928
  bottomSuffix,
916
- bottomAbbreviated
929
+ bottomAbbreviated,
930
+ onlyShowTopPrefixSuffix
917
931
  }
918
932
  } = config
919
933
 
@@ -1006,7 +1020,9 @@ export default function CdcChart({
1006
1020
  if (addColPrefix && axis === 'left') {
1007
1021
  result = addColPrefix + result
1008
1022
  } else {
1009
- if (prefix && axis === 'left') {
1023
+ // if onlyShowTopPrefixSuffix only show top prefix
1024
+ const suppressAllButLast = onlyShowTopPrefixSuffix && length - 1 !== index
1025
+ if (prefix && axis === 'left' && !suppressAllButLast) {
1010
1026
  result += prefix
1011
1027
  }
1012
1028
  }
@@ -1025,7 +1041,7 @@ export default function CdcChart({
1025
1041
  if (addColSuffix && axis === 'left') {
1026
1042
  result += addColSuffix
1027
1043
  } else {
1028
- if (suffix && axis === 'left') {
1044
+ if (suffix && axis === 'left' && !onlyShowTopPrefixSuffix) {
1029
1045
  result += suffix
1030
1046
  }
1031
1047
  }
@@ -1235,10 +1251,10 @@ export default function CdcChart({
1235
1251
  }
1236
1252
 
1237
1253
  const getChartWrapperClasses = () => {
1238
- const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1254
+ const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
1239
1255
  const classes = ['chart-container', 'p-relative']
1240
1256
  if (legend?.position) {
1241
- if (['sm', 'xs', 'xxs'].includes(currentViewport) && legend?.position !== 'top') {
1257
+ if (isLegendWrapViewport(currentViewport) && legend?.position !== 'top') {
1242
1258
  classes.push('legend-bottom')
1243
1259
  } else {
1244
1260
  classes.push(`legend-${legend.position}`)
@@ -1255,7 +1271,7 @@ export default function CdcChart({
1255
1271
 
1256
1272
  const getChartSubTextClasses = () => {
1257
1273
  const classes = ['subtext ']
1258
- const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1274
+ const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
1259
1275
 
1260
1276
  if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
1261
1277
  if (config.brush?.active && !isLegendOnBottom) classes.push('subtext--brush-active ')
@@ -1291,42 +1307,38 @@ export default function CdcChart({
1291
1307
  classes={['chart-title', `${config.theme}`, 'cove-component__header']}
1292
1308
  style={undefined}
1293
1309
  />
1294
- {/* Intro Text/Message */}
1295
- {config?.introText && config.visualizationType !== 'Spark Line' && (
1296
- <section
1297
- className={`introText legend_${config.legend.hide ? 'hidden' : 'visible'}_${config.legend.position} `}
1298
- >
1299
- {parse(config.introText)}
1300
- </section>
1301
- )}
1302
-
1303
- {/* Filters */}
1304
- {config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
1305
- <Filters
1306
- config={config}
1307
- setConfig={setConfig}
1308
- setFilteredData={setFilteredData}
1309
- filteredData={filteredData}
1310
- excludedData={excludedData}
1311
- filterData={filterVizData}
1312
- dimensions={dimensions}
1313
- />
1314
- )}
1315
- <SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
1316
- {config.annotations?.length > 0 && (
1317
- <SkipTo
1318
- skipId={handleChartTabbing(config, legendId)}
1319
- skipMessage={`Skip over annotations`}
1320
- key={`skip-annotations`}
1321
- />
1322
- )}
1323
1310
 
1324
1311
  {/* Visualization Wrapper */}
1325
1312
  <div className={getChartWrapperClasses().join(' ')}>
1313
+ {/* Intro Text/Message */}
1314
+ {config?.introText && config.visualizationType !== 'Spark Line' && (
1315
+ <section className={`introText `}>{parse(config.introText)}</section>
1316
+ )}
1317
+
1318
+ {/* Filters */}
1319
+ {config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
1320
+ <Filters
1321
+ config={config}
1322
+ setConfig={setConfig}
1323
+ setFilteredData={setFilteredData}
1324
+ filteredData={filteredData}
1325
+ excludedData={excludedData}
1326
+ filterData={filterVizData}
1327
+ dimensions={dimensions}
1328
+ />
1329
+ )}
1330
+ <SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
1331
+ {config.annotations?.length > 0 && (
1332
+ <SkipTo
1333
+ skipId={handleChartTabbing(config, legendId)}
1334
+ skipMessage={`Skip over annotations`}
1335
+ key={`skip-annotations`}
1336
+ />
1337
+ )}
1326
1338
  <LegendWrapper>
1327
1339
  <div
1328
1340
  className={
1329
- legend.hide || ['xxs', 'xs', 'sm'].includes(currentViewport)
1341
+ legend.hide || isLegendWrapViewport(currentViewport)
1330
1342
  ? 'w-100'
1331
1343
  : legend.position === 'bottom' || legend.position === 'top' || visualizationType === 'Sankey'
1332
1344
  ? 'w-100'
@@ -1335,30 +1347,36 @@ export default function CdcChart({
1335
1347
  >
1336
1348
  {/* All charts with LinearChart */}
1337
1349
  {!['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
1338
- <div style={{ height, width: `100%` }}>
1350
+ <div ref={parentRef} style={{ width: `100%` }}>
1339
1351
  <ParentSize>
1340
- {parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
1352
+ {parent => (
1353
+ <LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
1354
+ )}
1341
1355
  </ParentSize>
1342
1356
  </div>
1343
1357
  )}
1344
1358
 
1345
1359
  {config.visualizationType === 'Pie' && (
1346
- <ParentSize className='justify-content-center d-flex' style={{ height, width: `100%` }}>
1347
- {parent => <PieChart parentWidth={parent.width} parentHeight={parent.height} />}
1360
+ <ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
1361
+ {parent => <PieChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />}
1348
1362
  </ParentSize>
1349
1363
  )}
1350
1364
  {/* Line Chart */}
1351
1365
  {config.visualizationType === 'Line' &&
1352
1366
  (checkLineToBarGraph() ? (
1353
- <div style={{ height: config?.heights?.vertical, width: `100%` }}>
1367
+ <div ref={parentRef} style={{ width: `100%` }}>
1354
1368
  <ParentSize>
1355
- {parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
1369
+ {parent => (
1370
+ <LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
1371
+ )}
1356
1372
  </ParentSize>
1357
1373
  </div>
1358
1374
  ) : (
1359
- <div style={{ height, width: `100%` }}>
1375
+ <div ref={parentRef} style={{ width: `100%` }}>
1360
1376
  <ParentSize>
1361
- {parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
1377
+ {parent => (
1378
+ <LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
1379
+ )}
1362
1380
  </ParentSize>
1363
1381
  </div>
1364
1382
  ))}
@@ -1412,7 +1430,6 @@ export default function CdcChart({
1412
1430
  {description && config.visualizationType !== 'Spark Line' && (
1413
1431
  <div className={getChartSubTextClasses().join('')}>{parse(description)}</div>
1414
1432
  )}
1415
- {false && <Annotation.List />}
1416
1433
 
1417
1434
  {/* buttons */}
1418
1435
  <MediaControls.Section classes={['download-buttons']}>
@@ -1505,7 +1522,6 @@ export default function CdcChart({
1505
1522
  formatDate,
1506
1523
  formatNumber,
1507
1524
  formatTooltipsDate,
1508
- getTextWidth,
1509
1525
  getXAxisData,
1510
1526
  getYAxisData,
1511
1527
  handleChartAriaLabels,
@@ -1514,19 +1530,21 @@ export default function CdcChart({
1514
1530
  highlightReset,
1515
1531
  imageId,
1516
1532
  isDashboard,
1517
- isLegendBottom: legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport),
1533
+ isLegendBottom: legend?.position === 'bottom' || isLegendWrapViewport(currentViewport),
1518
1534
  isDebug,
1519
1535
  isDraggingAnnotation,
1520
1536
  handleDragStateChange,
1521
1537
  isEditor,
1522
1538
  isNumber,
1523
1539
  legend,
1540
+ legendRef,
1524
1541
  lineOptions,
1525
1542
  loading,
1526
1543
  missingRequiredSections,
1527
1544
  outerContainerRef,
1545
+ parentRef,
1528
1546
  parseDate,
1529
- rawData: stateData ?? {},
1547
+ rawData: _.cloneDeep(stateData) ?? {},
1530
1548
  seriesHighlight,
1531
1549
  setBrushConfig,
1532
1550
  setConfig,
@@ -1537,10 +1555,11 @@ export default function CdcChart({
1537
1555
  setSeriesHighlight,
1538
1556
  setSharedFilter,
1539
1557
  setSharedFilterValue,
1558
+ svgRef,
1540
1559
  tableData: filteredData || excludedData, // do not clean table data
1541
1560
  transformedData: clean(filteredData || excludedData), // do this right before passing to components
1542
1561
  twoColorPalette,
1543
- unfilteredData: stateData,
1562
+ unfilteredData: _.cloneDeep(stateData),
1544
1563
  updateConfig
1545
1564
  }
1546
1565
 
@@ -1559,3 +1578,5 @@ export default function CdcChart({
1559
1578
  </ConfigContext.Provider>
1560
1579
  )
1561
1580
  }
1581
+
1582
+ export default CdcChart
@@ -0,0 +1,33 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import chartGradientConfig from './_mock/legend.gradient_mock.json'
3
+ import SimplifiedLineConfig from './_mock/simplified_line.json'
4
+
5
+ import Chart from '../CdcChart'
6
+ import { editConfigKeys } from '../helpers/configHelpers'
7
+
8
+ const meta: Meta<typeof Chart> = {
9
+ title: 'Components/Templates/Chart/Legend',
10
+ component: Chart
11
+ }
12
+
13
+ type Story = StoryObj<typeof Chart>
14
+
15
+ export const Legend_Gradient: Story = {
16
+ args: {
17
+ config: chartGradientConfig
18
+ }
19
+ }
20
+
21
+ export const Labels_On_Line_Legend_On_Top: Story = {
22
+ args: {
23
+ config: editConfigKeys(chartGradientConfig, [{ path: ['yAxis', 'labelsAboveGridlines'], value: true }])
24
+ }
25
+ }
26
+
27
+ export const Legend_On_Right: Story = {
28
+ args: {
29
+ config: SimplifiedLineConfig
30
+ }
31
+ }
32
+
33
+ export default meta
@@ -5,6 +5,10 @@ import Chart from '../CdcChart'
5
5
  import lineChartTwoPointsRegressionTest from './_mock/line_chart_two_points_regression_test.json'
6
6
  import lineChartTwoPointsNewChart from './_mock/line_chart_two_points_new_chart.json'
7
7
  import lollipop from './_mock/lollipop.json'
8
+ import forestPlot from '../../examples/feature/forest-plot/forest-plot.json'
9
+ import pairedBar from './_mock/paired-bar.json'
10
+ import horizontalBarConfig from './_mock/horizontal_bar.json'
11
+ import pieConfig from './_mock/pie_with_data.json'
8
12
 
9
13
  const meta: Meta<typeof Chart> = {
10
14
  title: 'Components/Templates/Chart',
@@ -40,4 +44,28 @@ export const Suppression: Story = {
40
44
  }
41
45
  }
42
46
 
47
+ export const Forest_Plot: Story = {
48
+ args: {
49
+ config: forestPlot
50
+ }
51
+ }
52
+
53
+ export const Horizontal_Bar: Story = {
54
+ args: {
55
+ config: horizontalBarConfig
56
+ }
57
+ }
58
+
59
+ export const Pie: Story = {
60
+ args: {
61
+ config: pieConfig
62
+ }
63
+ }
64
+
65
+ export const Paired_Bar: Story = {
66
+ args: {
67
+ config: pairedBar
68
+ }
69
+ }
70
+
43
71
  export default meta
@@ -0,0 +1,20 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import SimplifiedLineConfig from './_mock/simplified_line.json'
3
+
4
+ import Chart from '../CdcChart'
5
+ import { editConfigKeys } from '../helpers/configHelpers'
6
+
7
+ const meta: Meta<typeof Chart> = {
8
+ title: 'Components/Templates/Chart/Axis Labels',
9
+ component: Chart
10
+ }
11
+
12
+ type Story = StoryObj<typeof Chart>
13
+
14
+ export const Abbreviated_Dates: Story = {
15
+ args: {
16
+ config: editConfigKeys(SimplifiedLineConfig, [{ path: ['xAxis', 'showYearsOnce'], value: true }])
17
+ }
18
+ }
19
+
20
+ export default meta