@cdc/chart 4.24.9-1 → 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 (62) hide show
  1. package/dist/cdcchart.js +37673 -36530
  2. package/index.html +1 -1
  3. package/package.json +2 -2
  4. package/src/CdcChart.tsx +128 -106
  5. package/src/_stories/Chart.Legend.Gradient.stories.tsx +33 -0
  6. package/src/_stories/Chart.stories.tsx +28 -0
  7. package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
  8. package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
  9. package/src/_stories/ChartPrefixSuffix.stories.tsx +151 -0
  10. package/src/_stories/_mock/horizontal_bar.json +257 -0
  11. package/src/_stories/_mock/large_x_axis_labels.json +261 -0
  12. package/src/_stories/_mock/paired-bar.json +262 -0
  13. package/src/_stories/_mock/pie_with_data.json +255 -0
  14. package/src/_stories/_mock/simplified_line.json +1510 -0
  15. package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
  16. package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
  17. package/src/components/Axis/Categorical.Axis.tsx +22 -4
  18. package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
  19. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
  20. package/src/components/BarChart/components/BarChart.Vertical.tsx +78 -20
  21. package/src/components/BarChart/helpers/index.ts +23 -4
  22. package/src/components/BrushChart.tsx +3 -2
  23. package/src/components/DeviationBar.jsx +58 -8
  24. package/src/components/EditorPanel/EditorPanel.tsx +62 -39
  25. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +6 -23
  26. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
  27. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -35
  28. package/src/components/EditorPanel/components/panels.scss +4 -6
  29. package/src/components/EditorPanel/editor-panel.scss +0 -8
  30. package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
  31. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
  32. package/src/components/EditorPanel/useEditorPermissions.ts +1 -0
  33. package/src/components/ForestPlot/ForestPlot.tsx +2 -3
  34. package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
  35. package/src/components/Legend/Legend.Component.tsx +16 -16
  36. package/src/components/Legend/Legend.Suppression.tsx +25 -20
  37. package/src/components/Legend/Legend.tsx +0 -2
  38. package/src/components/Legend/helpers/index.ts +16 -19
  39. package/src/components/LegendWrapper.tsx +3 -1
  40. package/src/components/LinearChart.tsx +740 -562
  41. package/src/components/PairedBarChart.jsx +50 -10
  42. package/src/components/PieChart/PieChart.tsx +1 -6
  43. package/src/components/Regions/components/Regions.tsx +33 -19
  44. package/src/components/ZoomBrush.tsx +25 -6
  45. package/src/coreStyles_chart.scss +3 -0
  46. package/src/data/initial-state.js +6 -2
  47. package/src/helpers/configHelpers.ts +28 -0
  48. package/src/helpers/handleRankByValue.ts +15 -0
  49. package/src/helpers/sizeHelpers.ts +25 -0
  50. package/src/helpers/tests/handleRankByValue.test.ts +37 -0
  51. package/src/helpers/tests/sizeHelpers.test.ts +80 -0
  52. package/src/hooks/useColorPalette.js +10 -2
  53. package/src/hooks/useLegendClasses.ts +4 -0
  54. package/src/hooks/useScales.ts +31 -3
  55. package/src/hooks/useTooltip.tsx +9 -5
  56. package/src/index.jsx +1 -0
  57. package/src/scss/DataTable.scss +5 -4
  58. package/src/scss/main.scss +57 -52
  59. package/src/types/ChartConfig.ts +38 -16
  60. package/src/types/ChartContext.ts +18 -14
  61. package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
  62. package/src/_stories/ChartBrush.stories.tsx +0 -19
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-1",
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": "4a77c2fa79c8fa6074da3b6dfee3d8e32f0b2586",
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)
@@ -117,10 +138,8 @@ export default function CdcChart({
117
138
  if (config.visualizationType === 'Pie') height = config?.heights?.[renderedOrientation]
118
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
 
@@ -579,6 +577,7 @@ export default function CdcChart({
579
577
  ) {
580
578
  newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
581
579
  newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
580
+ newConfig.runtime.yAxis.labelOffset *= -1
582
581
 
583
582
  newConfig.runtime.horizontal = false
584
583
  newConfig.orientation = 'horizontal'
@@ -654,7 +653,7 @@ export default function CdcChart({
654
653
  const resizeObserver = new ResizeObserver(entries => {
655
654
  for (let entry of entries) {
656
655
  let { width, height } = entry.contentRect
657
- let svgMarginWidth = 32
656
+ let svgMarginWidth = 30
658
657
  let editorWidth = 350
659
658
 
660
659
  width = isEditor ? width - editorWidth : width
@@ -681,14 +680,14 @@ export default function CdcChart({
681
680
  setContainer(node)
682
681
  }, []) // eslint-disable-line
683
682
 
684
- function isEmpty(obj) {
683
+ const isEmpty = obj => {
685
684
  return Object.keys(obj).length === 0
686
685
  }
687
686
 
688
687
  // Load data when component first mounts
689
688
  useEffect(() => {
690
689
  loadConfig()
691
- }, []) // eslint-disable-line
690
+ }, [configObj?.data?.length ? configObj.data : null]) // eslint-disable-line
692
691
 
693
692
  useEffect(() => {
694
693
  reloadURLData()
@@ -752,14 +751,6 @@ export default function CdcChart({
752
751
  }
753
752
  }, [externalFilters]) // eslint-disable-line
754
753
 
755
- // Load data when configObj data changes
756
- if (configObj) {
757
- // eslint-disable-next-line react-hooks/rules-of-hooks
758
- useEffect(() => {
759
- loadConfig()
760
- }, [configObj.data]) // eslint-disable-line
761
- }
762
-
763
754
  // This will set the bump chart's default scaling type to date-time
764
755
  useEffect(() => {
765
756
  if (['Bump Chart'].includes(config.visualizationType)) {
@@ -874,8 +865,22 @@ export default function CdcChart({
874
865
  }
875
866
  }
876
867
 
877
- const formatDate = date => {
878
- 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
879
884
  }
880
885
 
881
886
  const formatTooltipsDate = date => {
@@ -885,7 +890,16 @@ export default function CdcChart({
885
890
  // Format numeric data based on settings in config OR from passed in settings for Additional Columns
886
891
  // - use only for old horizontal data - newer formatNumber is in helper/formatNumber
887
892
  // TODO: we should combine various formatNumber functions across this project.
888
- 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
+ ) => {
889
903
  // if num is NaN return num
890
904
  if (isNaN(num) || !num) return num
891
905
  // Check if the input number is negative
@@ -912,7 +926,8 @@ export default function CdcChart({
912
926
  rightSuffix,
913
927
  bottomPrefix,
914
928
  bottomSuffix,
915
- bottomAbbreviated
929
+ bottomAbbreviated,
930
+ onlyShowTopPrefixSuffix
916
931
  }
917
932
  } = config
918
933
 
@@ -1005,7 +1020,9 @@ export default function CdcChart({
1005
1020
  if (addColPrefix && axis === 'left') {
1006
1021
  result = addColPrefix + result
1007
1022
  } else {
1008
- if (prefix && axis === 'left') {
1023
+ // if onlyShowTopPrefixSuffix only show top prefix
1024
+ const suppressAllButLast = onlyShowTopPrefixSuffix && length - 1 !== index
1025
+ if (prefix && axis === 'left' && !suppressAllButLast) {
1009
1026
  result += prefix
1010
1027
  }
1011
1028
  }
@@ -1024,7 +1041,7 @@ export default function CdcChart({
1024
1041
  if (addColSuffix && axis === 'left') {
1025
1042
  result += addColSuffix
1026
1043
  } else {
1027
- if (suffix && axis === 'left') {
1044
+ if (suffix && axis === 'left' && !onlyShowTopPrefixSuffix) {
1028
1045
  result += suffix
1029
1046
  }
1030
1047
  }
@@ -1234,10 +1251,10 @@ export default function CdcChart({
1234
1251
  }
1235
1252
 
1236
1253
  const getChartWrapperClasses = () => {
1237
- const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1254
+ const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
1238
1255
  const classes = ['chart-container', 'p-relative']
1239
1256
  if (legend?.position) {
1240
- if (['sm', 'xs', 'xxs'].includes(currentViewport) && legend?.position !== 'top') {
1257
+ if (isLegendWrapViewport(currentViewport) && legend?.position !== 'top') {
1241
1258
  classes.push('legend-bottom')
1242
1259
  } else {
1243
1260
  classes.push(`legend-${legend.position}`)
@@ -1254,7 +1271,7 @@ export default function CdcChart({
1254
1271
 
1255
1272
  const getChartSubTextClasses = () => {
1256
1273
  const classes = ['subtext ']
1257
- const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1274
+ const isLegendOnBottom = legend?.position === 'bottom' || isLegendWrapViewport(currentViewport)
1258
1275
 
1259
1276
  if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
1260
1277
  if (config.brush?.active && !isLegendOnBottom) classes.push('subtext--brush-active ')
@@ -1290,42 +1307,38 @@ export default function CdcChart({
1290
1307
  classes={['chart-title', `${config.theme}`, 'cove-component__header']}
1291
1308
  style={undefined}
1292
1309
  />
1293
- {/* Intro Text/Message */}
1294
- {config?.introText && config.visualizationType !== 'Spark Line' && (
1295
- <section
1296
- className={`introText legend_${config.legend.hide ? 'hidden' : 'visible'}_${config.legend.position} `}
1297
- >
1298
- {parse(config.introText)}
1299
- </section>
1300
- )}
1301
-
1302
- {/* Filters */}
1303
- {config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
1304
- <Filters
1305
- config={config}
1306
- setConfig={setConfig}
1307
- setFilteredData={setFilteredData}
1308
- filteredData={filteredData}
1309
- excludedData={excludedData}
1310
- filterData={filterVizData}
1311
- dimensions={dimensions}
1312
- />
1313
- )}
1314
- <SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
1315
- {config.annotations?.length > 0 && (
1316
- <SkipTo
1317
- skipId={handleChartTabbing(config, legendId)}
1318
- skipMessage={`Skip over annotations`}
1319
- key={`skip-annotations`}
1320
- />
1321
- )}
1322
1310
 
1323
1311
  {/* Visualization Wrapper */}
1324
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
+ )}
1325
1338
  <LegendWrapper>
1326
1339
  <div
1327
1340
  className={
1328
- legend.hide || ['xxs', 'xs', 'sm'].includes(currentViewport)
1341
+ legend.hide || isLegendWrapViewport(currentViewport)
1329
1342
  ? 'w-100'
1330
1343
  : legend.position === 'bottom' || legend.position === 'top' || visualizationType === 'Sankey'
1331
1344
  ? 'w-100'
@@ -1334,30 +1347,36 @@ export default function CdcChart({
1334
1347
  >
1335
1348
  {/* All charts with LinearChart */}
1336
1349
  {!['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
1337
- <div style={{ height, width: `100%` }}>
1350
+ <div ref={parentRef} style={{ width: `100%` }}>
1338
1351
  <ParentSize>
1339
- {parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
1352
+ {parent => (
1353
+ <LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
1354
+ )}
1340
1355
  </ParentSize>
1341
1356
  </div>
1342
1357
  )}
1343
1358
 
1344
1359
  {config.visualizationType === 'Pie' && (
1345
- <ParentSize className='justify-content-center d-flex' style={{ height, width: `100%` }}>
1346
- {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} />}
1347
1362
  </ParentSize>
1348
1363
  )}
1349
1364
  {/* Line Chart */}
1350
1365
  {config.visualizationType === 'Line' &&
1351
1366
  (checkLineToBarGraph() ? (
1352
- <div style={{ height: config?.heights?.vertical, width: `100%` }}>
1367
+ <div ref={parentRef} style={{ width: `100%` }}>
1353
1368
  <ParentSize>
1354
- {parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
1369
+ {parent => (
1370
+ <LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
1371
+ )}
1355
1372
  </ParentSize>
1356
1373
  </div>
1357
1374
  ) : (
1358
- <div style={{ height, width: `100%` }}>
1375
+ <div ref={parentRef} style={{ width: `100%` }}>
1359
1376
  <ParentSize>
1360
- {parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
1377
+ {parent => (
1378
+ <LinearChart ref={svgRef} parentWidth={parent.width} parentHeight={parent.height} />
1379
+ )}
1361
1380
  </ParentSize>
1362
1381
  </div>
1363
1382
  ))}
@@ -1411,7 +1430,6 @@ export default function CdcChart({
1411
1430
  {description && config.visualizationType !== 'Spark Line' && (
1412
1431
  <div className={getChartSubTextClasses().join('')}>{parse(description)}</div>
1413
1432
  )}
1414
- {false && <Annotation.List />}
1415
1433
 
1416
1434
  {/* buttons */}
1417
1435
  <MediaControls.Section classes={['download-buttons']}>
@@ -1504,7 +1522,6 @@ export default function CdcChart({
1504
1522
  formatDate,
1505
1523
  formatNumber,
1506
1524
  formatTooltipsDate,
1507
- getTextWidth,
1508
1525
  getXAxisData,
1509
1526
  getYAxisData,
1510
1527
  handleChartAriaLabels,
@@ -1513,19 +1530,21 @@ export default function CdcChart({
1513
1530
  highlightReset,
1514
1531
  imageId,
1515
1532
  isDashboard,
1516
- isLegendBottom: legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport),
1533
+ isLegendBottom: legend?.position === 'bottom' || isLegendWrapViewport(currentViewport),
1517
1534
  isDebug,
1518
1535
  isDraggingAnnotation,
1519
1536
  handleDragStateChange,
1520
1537
  isEditor,
1521
1538
  isNumber,
1522
1539
  legend,
1540
+ legendRef,
1523
1541
  lineOptions,
1524
1542
  loading,
1525
1543
  missingRequiredSections,
1526
1544
  outerContainerRef,
1545
+ parentRef,
1527
1546
  parseDate,
1528
- rawData: stateData ?? {},
1547
+ rawData: _.cloneDeep(stateData) ?? {},
1529
1548
  seriesHighlight,
1530
1549
  setBrushConfig,
1531
1550
  setConfig,
@@ -1536,10 +1555,11 @@ export default function CdcChart({
1536
1555
  setSeriesHighlight,
1537
1556
  setSharedFilter,
1538
1557
  setSharedFilterValue,
1558
+ svgRef,
1539
1559
  tableData: filteredData || excludedData, // do not clean table data
1540
1560
  transformedData: clean(filteredData || excludedData), // do this right before passing to components
1541
1561
  twoColorPalette,
1542
- unfilteredData: stateData,
1562
+ unfilteredData: _.cloneDeep(stateData),
1543
1563
  updateConfig
1544
1564
  }
1545
1565
 
@@ -1558,3 +1578,5 @@ export default function CdcChart({
1558
1578
  </ConfigContext.Provider>
1559
1579
  )
1560
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