@cdc/chart 4.24.4 → 4.24.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/cdcchart.js +39611 -36038
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +9 -8
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +115 -71
  7. package/src/_stories/Chart.stories.tsx +26 -171
  8. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  9. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  10. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  11. package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
  12. package/src/_stories/_mock/bar-chart-suppressed.json +474 -0
  13. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  14. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  15. package/src/_stories/_mock/lollipop.json +171 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  17. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  19. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  20. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  21. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  22. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  23. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  24. package/src/components/Annotations/index.tsx +13 -0
  25. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  26. package/src/components/AreaChart/components/AreaChart.jsx +2 -2
  27. package/src/components/BarChart/components/BarChart.Horizontal.tsx +78 -71
  28. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  29. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  30. package/src/components/BarChart/components/BarChart.Vertical.tsx +100 -87
  31. package/src/components/BarChart/helpers/index.ts +102 -0
  32. package/src/components/DeviationBar.jsx +4 -2
  33. package/src/components/EditorPanel/EditorPanel.tsx +435 -613
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +135 -7
  36. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  37. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -5
  38. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  39. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  40. package/src/components/EditorPanel/components/panels.scss +4 -0
  41. package/src/components/EditorPanel/editor-panel.scss +19 -0
  42. package/src/components/EditorPanel/useEditorPermissions.js +23 -3
  43. package/src/components/Legend/Legend.Component.tsx +66 -15
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  45. package/src/components/Legend/helpers/index.ts +5 -0
  46. package/src/components/LineChart/LineChartProps.ts +16 -6
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +22 -11
  48. package/src/components/LineChart/helpers.ts +148 -10
  49. package/src/components/LineChart/index.tsx +71 -44
  50. package/src/components/LinearChart.jsx +184 -125
  51. package/src/components/PairedBarChart.jsx +9 -9
  52. package/src/components/PieChart/PieChart.tsx +4 -4
  53. package/src/components/Sankey/index.tsx +73 -20
  54. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  55. package/src/components/ZoomBrush.tsx +120 -55
  56. package/src/data/initial-state.js +14 -6
  57. package/src/helpers/handleChartTabbing.ts +8 -0
  58. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  59. package/src/hooks/{useBarChart.js → useBarChart.ts} +9 -22
  60. package/src/hooks/useColorScale.ts +1 -1
  61. package/src/hooks/useMinMax.ts +29 -5
  62. package/src/hooks/useScales.ts +48 -26
  63. package/src/hooks/useTooltip.tsx +62 -15
  64. package/src/scss/main.scss +69 -12
  65. package/src/types/ChartConfig.ts +53 -16
  66. package/src/types/ChartContext.ts +13 -0
  67. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  68. package/LICENSE +0 -201
  69. package/src/_stories/ChartLine.preliminary.tsx +0 -19
  70. package/src/_stories/ChartSuppress.stories.tsx +0 -19
  71. package/src/_stories/_mock/suppress_mock.json +0 -911
  72. package/src/helpers/computeMarginBottom.ts +0 -56
  73. package/src/helpers/filterData.ts +0 -18
  74. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  75. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  76. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
package/src/CdcChart.tsx CHANGED
@@ -29,17 +29,17 @@ import Legend from './components/Legend'
29
29
  import defaults from './data/initial-state'
30
30
  import EditorPanel from './components/EditorPanel'
31
31
  import { abbreviateNumber } from './helpers/abbreviateNumber'
32
+ import { handleChartTabbing } from './helpers/handleChartTabbing'
32
33
  import { getQuartiles } from './helpers/getQuartiles'
33
34
  import { sortAsc, sortDesc } from './helpers/sort'
34
- import { filterData } from './helpers/filterData'
35
35
  import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
36
36
  import { lineOptions } from './helpers/lineOptions'
37
37
  import { handleLineType } from './helpers/handleLineType'
38
38
  import { generateColorsArray } from './helpers/generateColorsArray'
39
- import { computeMarginBottom } from './helpers/computeMarginBottom'
40
39
  import Loading from '@cdc/core/components/Loading'
41
40
  import Filters from '@cdc/core/components/Filters'
42
41
  import MediaControls from '@cdc/core/components/MediaControls'
42
+ import Annotation from './components/Annotations'
43
43
 
44
44
  // Helpers
45
45
  import { publish, subscribe, unsubscribe } from '@cdc/core/helpers/events'
@@ -51,6 +51,7 @@ import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
51
51
  import isNumber from '@cdc/core/helpers/isNumber'
52
52
  import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
53
53
  import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
54
+ import { isConvertLineToBarGraph } from './helpers/isConvertLineToBarGraph'
54
55
 
55
56
  import './scss/main.scss'
56
57
  // load both then config below determines which to use
@@ -59,8 +60,10 @@ import { getFileExtension } from '@cdc/core/helpers/getFileExtension'
59
60
  import Title from '@cdc/core/components/ui/Title'
60
61
  import { ChartConfig } from './types/ChartConfig'
61
62
  import { Label } from './types/Label'
63
+ import { type ViewportSize } from './types/ChartConfig'
62
64
  import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
63
65
  import SkipTo from '@cdc/core/components/elements/SkipTo'
66
+ import { filterVizData } from '@cdc/core/helpers/filterVizData'
64
67
 
65
68
  export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
66
69
  const transform = new DataTransform()
@@ -71,18 +74,28 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
71
74
  const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
72
75
  const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
73
76
  const [seriesHighlight, setSeriesHighlight] = useState<string[]>(configObj && configObj?.legend?.seriesHighlight?.length ? [...configObj?.legend?.seriesHighlight] : [])
74
- const [currentViewport, setCurrentViewport] = useState('lg')
77
+ const [currentViewport, setCurrentViewport] = useState<ViewportSize>('lg')
75
78
  const [dimensions, setDimensions] = useState<[number?, number?]>([])
76
79
  const [externalFilters, setExternalFilters] = useState<any[]>()
77
80
  const [container, setContainer] = useState()
78
81
  const [coveLoadedEventRan, setCoveLoadedEventRan] = useState(false)
82
+ const [isDraggingAnnotation, setIsDraggingAnnotation] = useState(false)
79
83
  const [dynamicLegendItems, setDynamicLegendItems] = useState<any[]>([])
80
84
  const [imageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`)
85
+ const [brushConfig, setBrushConfig] = useState({
86
+ data: [],
87
+ isActive: false,
88
+ isBrushing: false
89
+ })
81
90
  type Config = typeof config
82
91
  let legendMemo = useRef(new Map()) // map collection
83
92
  let innerContainerRef = useRef()
84
93
  const legendRef = useRef(null)
85
94
 
95
+ const handleDragStateChange = isDragging => {
96
+ setIsDraggingAnnotation(isDragging)
97
+ }
98
+
86
99
  if (isDebug) console.log('Chart config, isEditor', config, isEditor)
87
100
 
88
101
  // Destructure items from config for more readable JSX
@@ -97,7 +110,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
97
110
 
98
111
  const { barBorderClass, lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
99
112
  const legendId = useId()
100
- const handleChartTabbing = !config.legend?.hide ? legendId : config?.title ? `dataTableSection__${config.title.replace(/\s/g, '')}` : `dataTableSection`
113
+
114
+ const checkLineToBarGraph = () => {
115
+ return isConvertLineToBarGraph(config.visualizationType, filteredData, config.allowLineToBarGraph)
116
+ }
101
117
 
102
118
  const reloadURLData = async () => {
103
119
  if (config.dataUrl) {
@@ -160,7 +176,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
160
176
  if (data) {
161
177
  setStateData(data)
162
178
  setExcludedData(data)
163
- setFilteredData(filterData(config.filters, data))
179
+ setFilteredData(filterVizData(config.filters, data))
164
180
  }
165
181
  }
166
182
  }
@@ -240,12 +256,14 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
240
256
  }
241
257
  if (undefined === newConfig.table.show) newConfig.table.show = !isDashboard
242
258
 
243
- newConfig.series.map(series => {
244
- if (!series.tooltip) series.tooltip = true
259
+ newConfig.series.forEach(series => {
260
+ if (series.tooltip === undefined || series.tooltip === null) {
261
+ series.tooltip = true
262
+ }
245
263
  if (!series.axis) series.axis = 'Left'
246
264
  })
247
265
 
248
- if (!newConfig.data && data) {
266
+ if (data) {
249
267
  newConfig.data = data
250
268
  }
251
269
 
@@ -306,17 +324,15 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
306
324
  newConfig.filters[index].values = filterValues
307
325
  // Initial filter should be active
308
326
 
309
- newConfig.filters[index].active = newConfig.filters[index].active || filterValues[0]
327
+ newConfig.filters[index].active = !newConfig.filters[index].active || filterValues.indexOf(newConfig.filters[index].active) === -1 ? filterValues[0] : newConfig.filters[index].active
310
328
  newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle ? newConfig.filters[index].filterStyle : 'dropdown'
311
329
  })
312
- currentData = filterData(newConfig.filters, newExcludedData)
330
+ currentData = filterVizData(newConfig.filters, newExcludedData)
313
331
  setFilteredData(currentData)
314
332
  }
315
333
 
316
- if (newConfig.xAxis.type === 'date-time' && newConfig.barThickness > 0.1) {
317
- newConfig.barThickness = 0.035
318
- } else if (newConfig.xAxis.type !== 'date-time' && newConfig.barThickness < 0.1) {
319
- newConfig.barThickness = 0.35
334
+ if (newConfig.xAxis.type === 'date-time' && config.orientation === 'horizontal') {
335
+ newConfig.xAxis.type = 'date'
320
336
  }
321
337
 
322
338
  //Enforce default values that need to be calculated at runtime
@@ -481,7 +497,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
481
497
 
482
498
  newConfig.runtime.horizontal = false
483
499
  newConfig.orientation = 'horizontal'
484
- } else if (['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType)) {
500
+ } else if (['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType) && !checkLineToBarGraph()) {
485
501
  newConfig.runtime.xAxis = newConfig.xAxis
486
502
  newConfig.runtime.yAxis = newConfig.yAxis
487
503
  newConfig.runtime.horizontal = false
@@ -622,14 +638,14 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
622
638
  let configCopy = { ...config }
623
639
  delete configCopy['filters']
624
640
  setConfig(configCopy)
625
- setFilteredData(filterData(externalFilters, excludedData))
641
+ setFilteredData(filterVizData(externalFilters, excludedData))
626
642
  }
627
643
  }
628
644
 
629
645
  if (externalFilters && externalFilters.length > 0 && externalFilters.length > 0 && externalFilters[0].hasOwnProperty('active')) {
630
646
  let newConfigHere = { ...config, filters: externalFilters }
631
647
  setConfig(newConfigHere)
632
- setFilteredData(filterData(externalFilters, excludedData))
648
+ setFilteredData(filterVizData(externalFilters, excludedData))
633
649
  }
634
650
  }, [externalFilters]) // eslint-disable-line
635
651
 
@@ -749,7 +765,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
749
765
  }
750
766
 
751
767
  // function calculates the width of given text and its font-size
752
- function getTextWidth(text, font) {
768
+ function getTextWidth(text: string, font: string): number | undefined {
753
769
  const canvas = document.createElement('canvas')
754
770
  const context = canvas.getContext('2d')
755
771
  if (!context) {
@@ -907,7 +923,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
907
923
  }
908
924
 
909
925
  // Select appropriate chart type
910
- const chartComponents = {
926
+ const ChartComponents = {
911
927
  'Paired Bar': <LinearChart />,
912
928
  Forecasting: <LinearChart />,
913
929
  Bar: <LinearChart />,
@@ -1101,17 +1117,33 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1101
1117
  // Prevent render if loading
1102
1118
  let body = <Loading />
1103
1119
 
1120
+ const makeClassName = string => {
1121
+ if (!string || !string.toLowerCase) return
1122
+ return string.toLowerCase().replaceAll(/ /g, '-')
1123
+ }
1124
+
1104
1125
  const getChartWrapperClasses = () => {
1126
+ const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1105
1127
  const classes = ['chart-container', 'p-relative']
1106
- if (config.legend.position === 'bottom') classes.push('bottom')
1107
- if (config.legend.hide) classes.push('legend-hidden')
1128
+ if (config.legend?.position === 'bottom') classes.push('bottom')
1129
+ if (config.legend?.hide) classes.push('legend-hidden')
1108
1130
  if (lineDatapointClass) classes.push(lineDatapointClass)
1109
1131
  if (!config.barHasBorder) classes.push('chart-bar--no-border')
1110
- if (isDebug) classes.push('debug')
1132
+ if (config.brush?.active && dashboardConfig?.type === 'dashboard' && (!isLegendOnBottom || config.legend.hide)) classes.push('dashboard-brush')
1111
1133
  classes.push(...contentClasses)
1112
1134
  return classes
1113
1135
  }
1114
1136
 
1137
+ const getChartSubTextClasses = () => {
1138
+ const classes = ['subtext ']
1139
+ const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1140
+
1141
+ if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
1142
+ if (config.brush?.active && !isLegendOnBottom) classes.push('subtext--brush-active ')
1143
+ if (config.brush?.active && config.legend.hide) classes.push('subtext--brush-active ')
1144
+ return classes
1145
+ }
1146
+
1115
1147
  if (!loading) {
1116
1148
  const tableLink = (
1117
1149
  <a href={`#data-table-${config.dataKey}`} className='margin-left-href'>
@@ -1125,23 +1157,28 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1125
1157
  {config.newViz && <Confirm />}
1126
1158
  {undefined === config.newViz && isEditor && config.runtime && config.runtime?.editorErrorMessage && <Error />}
1127
1159
  {!missingRequiredSections() && !config.newViz && (
1128
- <div className='cdc-chart-inner-container cove-component__content' aria-label={handleChartAriaLabels(config)} tabIndex={0}>
1160
+ <div className={`cdc-chart-inner-container cove-component__content type-${makeClassName(config.visualizationType)}`} aria-label={handleChartAriaLabels(config)} tabIndex={0}>
1129
1161
  <Title showTitle={config.showTitle} isDashboard={isDashboard} title={title} superTitle={config.superTitle} classes={['chart-title', `${config.theme}`, 'cove-component__header']} style={undefined} />
1130
1162
 
1131
1163
  {/* Filters */}
1132
- {config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />}
1133
- <SkipTo skipId={handleChartTabbing} skipMessage='Skip Over Chart Container' />
1164
+ {config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterVizData} dimensions={dimensions} />}
1165
+ <SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
1166
+ {config.annotations?.length > 0 && <SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage={`Skip over annotations`} key={`skip-annotations`} />}
1167
+
1134
1168
  {/* Visualization */}
1135
1169
  {config?.introText && config.visualizationType !== 'Spark Line' && <section className='introText'>{parse(config.introText)}</section>}
1136
1170
 
1137
- <div style={{ marginBottom: computeMarginBottom(config, legend, currentViewport) }} className={getChartWrapperClasses().join(' ')}>
1138
- {/* All charts except sparkline */}
1139
- {config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
1171
+ <div className={getChartWrapperClasses().join(' ')}>
1172
+ {/* All charts except line and sparkline */}
1173
+ {config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Line' && ChartComponents[config.visualizationType]}
1174
+
1175
+ {/* Line Chart */}
1176
+ {config.visualizationType === 'Line' && (checkLineToBarGraph() ? ChartComponents['Bar'] : ChartComponents['Line'])}
1140
1177
 
1141
1178
  {/* Sparkline */}
1142
1179
  {config.visualizationType === 'Spark Line' && (
1143
1180
  <>
1144
- <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />
1181
+ <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterVizData} dimensions={dimensions} />
1145
1182
  {config?.introText && (
1146
1183
  <section className='introText' style={{ padding: '0px 0 35px' }}>
1147
1184
  {parse(config.introText)}
@@ -1159,26 +1196,26 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1159
1196
  )}
1160
1197
  {/* Sankey */}
1161
1198
  {config.visualizationType === 'Sankey' && <ParentSize aria-hidden='true'>{parent => <SankeyChart runtime={config.runtime} width={parent.width} height={parent.height} />}</ParentSize>}
1162
- {!config.legend.hide && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Sankey' && <Legend ref={legendRef} />}
1199
+ {!config.legend.hide && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Sankey' && <Legend ref={legendRef} skipId={handleChartTabbing(config, legendId)} />}
1163
1200
  </div>
1164
1201
  {/* Link */}
1165
1202
  {isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
1166
-
1167
1203
  {/* Description */}
1168
- {description && config.visualizationType !== 'Spark Line' && <div className={'column ' + config.isResponsiveTicks ? 'subtext--responsive-ticks' : 'subtext'}>{parse(description)}</div>}
1204
+
1205
+ {description && config.visualizationType !== 'Spark Line' && <div className={getChartSubTextClasses().join('')}>{parse(description)}</div>}
1206
+ {false && <Annotation.List />}
1169
1207
 
1170
1208
  {/* buttons */}
1171
1209
  <MediaControls.Section classes={['download-buttons']}>
1172
1210
  {config.table.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
1173
1211
  {config.table.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
1174
1212
  </MediaControls.Section>
1175
-
1176
1213
  {/* Data Table */}
1177
1214
  {((config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Sankey') || (config.visualizationType === 'Sankey' && config.table.show)) && (
1178
1215
  <DataTable
1179
1216
  config={config}
1180
- rawData={config.visualizationType === 'Sankey' ? config?.data?.[0]?.tableData : config.table.customTableConfig ? filterData(config.filters, config.data) : config.data}
1181
- runtimeData={config.visualizationType === 'Sankey' ? config?.data?.[0]?.tableData : transform.applySuppression(filteredData || excludedData, config.suppressedData)}
1217
+ rawData={config.visualizationType === 'Sankey' ? config?.data?.[0]?.tableData : config.table.customTableConfig ? filterVizData(config.filters, config.data) : config.data}
1218
+ runtimeData={config.visualizationType === 'Sankey' ? config?.data?.[0]?.tableData : filteredData || excludedData}
1182
1219
  expandDataTable={config.table.expanded}
1183
1220
  columns={config.columns}
1184
1221
  displayDataAsText={displayDataAsText}
@@ -1188,10 +1225,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1188
1225
  indexTitle={config.table.indexLabel}
1189
1226
  vizTitle={title}
1190
1227
  viewport={currentViewport}
1191
- tabbingId={handleChartTabbing}
1228
+ tabbingId={handleChartTabbing(config, legendId)}
1192
1229
  colorScale={colorScale}
1193
1230
  />
1194
1231
  )}
1232
+ {config?.annotations?.length > 0 && <Annotation.Dropdown />}
1195
1233
  {config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
1196
1234
  {/* show pdf or image button */}
1197
1235
  </div>
@@ -1207,54 +1245,60 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1207
1245
  const capitalize = str => {
1208
1246
  return str.charAt(0).toUpperCase() + str.slice(1)
1209
1247
  }
1248
+
1210
1249
  const contextValues = {
1250
+ brushConfig,
1211
1251
  capitalize,
1212
- computeMarginBottom,
1213
- getXAxisData,
1214
- getYAxisData,
1215
- config,
1216
- setConfig,
1217
- rawData: stateData ?? {},
1218
- excludedData: excludedData,
1219
- transformedData: clean(filteredData || excludedData), // do this right before passing to components
1220
- tableData: filteredData || excludedData, // do not clean table data
1221
- unfilteredData: stateData,
1222
- seriesHighlight,
1252
+ clean,
1253
+ colorPalettes,
1223
1254
  colorScale,
1224
- dimensions,
1255
+ config,
1225
1256
  currentViewport,
1226
- parseDate,
1257
+ dashboardConfig,
1258
+ debugSvg: isDebug,
1259
+ dimensions,
1260
+ dynamicLegendItems,
1261
+ excludedData: excludedData,
1227
1262
  formatDate,
1228
- formatTooltipsDate,
1229
1263
  formatNumber,
1230
- loading,
1231
- updateConfig,
1232
- colorPalettes,
1233
- isDashboard,
1234
- setParentConfig,
1235
- missingRequiredSections,
1236
- setEditing,
1237
- setFilteredData,
1264
+ formatTooltipsDate,
1265
+ getTextWidth,
1266
+ getXAxisData,
1267
+ getYAxisData,
1238
1268
  handleChartAriaLabels,
1269
+ handleLineType,
1239
1270
  highlight,
1240
1271
  highlightReset,
1241
- legend,
1242
- setSeriesHighlight,
1243
- dynamicLegendItems,
1244
- setDynamicLegendItems,
1245
- filterData,
1246
1272
  imageId,
1247
- handleLineType,
1248
- lineOptions,
1249
- isNumber,
1250
- getTextWidth,
1251
- twoColorPalette,
1252
- isEditor,
1273
+ isDashboard,
1274
+ isLegendBottom: legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport),
1253
1275
  isDebug,
1276
+ isDraggingAnnotation,
1277
+ handleDragStateChange,
1278
+ isEditor,
1279
+ isNumber,
1280
+ legend,
1281
+ lineOptions,
1282
+ loading,
1283
+ missingRequiredSections,
1284
+ outerContainerRef,
1285
+ parseDate,
1286
+ rawData: stateData ?? {},
1287
+ seriesHighlight,
1288
+ setBrushConfig,
1289
+ setConfig,
1290
+ setDynamicLegendItems,
1291
+ setEditing,
1292
+ setFilteredData,
1293
+ setParentConfig,
1294
+ setSeriesHighlight,
1254
1295
  setSharedFilter,
1255
1296
  setSharedFilterValue,
1256
- dashboardConfig,
1257
- debugSvg: isDebug
1297
+ tableData: filteredData || excludedData, // do not clean table data
1298
+ transformedData: clean(filteredData || excludedData), // do this right before passing to components
1299
+ twoColorPalette,
1300
+ unfilteredData: stateData,
1301
+ updateConfig
1258
1302
  }
1259
1303
 
1260
1304
  return (
@@ -1,6 +1,10 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react'
2
+ import SuppressedConfig from './_mock/bar-chart-suppressed.json'
2
3
 
3
4
  import Chart from '../CdcChart'
5
+ import lineChartTwoPointsRegressionTest from './_mock/line_chart_two_points_regression_test.json'
6
+ import lineChartTwoPointsNewChart from './_mock/line_chart_two_points_new_chart.json'
7
+ import lollipop from './_mock/lollipop.json'
4
8
 
5
9
  const meta: Meta<typeof Chart> = {
6
10
  title: 'Components/Templates/Chart',
@@ -9,179 +13,30 @@ const meta: Meta<typeof Chart> = {
9
13
 
10
14
  type Story = StoryObj<typeof Chart>
11
15
 
16
+ export const line_Chart_Two_Points_Regression_Test: Story = {
17
+ args: {
18
+ config: lineChartTwoPointsRegressionTest,
19
+ isEditor: false
20
+ }
21
+ }
22
+ export const line_Chart_Two_Points_New_Chart: Story = {
23
+ args: {
24
+ config: lineChartTwoPointsNewChart,
25
+ isEditor: false
26
+ }
27
+ }
28
+
12
29
  export const Lollipop: Story = {
13
30
  args: {
14
- config: {
15
- type: 'chart',
16
- title: 'Lollipop Style Horizontal Bar Chart - Number of Spills Occurring in the Home',
17
- showTitle: true,
18
- showDownloadMediaButton: false,
19
- theme: 'theme-blue',
20
- animate: true,
21
- fontSize: 'medium',
22
- lineDatapointStyle: 'hover',
23
- barHasBorder: 'false',
24
- isLollipopChart: true,
25
- lollipopShape: 'circle',
26
- lollipopColorStyle: 'two-tone',
27
- visualizationSubType: 'horizontal',
28
- barStyle: '',
29
- roundingStyle: 'standard',
30
- tipRounding: 'top',
31
- isResponsiveTicks: false,
32
- general: { showDownloadButton: false },
33
- padding: { left: 5, right: 5 },
34
- yAxis: {
35
- hideAxis: true,
36
- displayNumbersOnBar: true,
37
- hideLabel: false,
38
- hideTicks: false,
39
- size: '13',
40
- gridLines: false,
41
- enablePadding: false,
42
- min: '',
43
- max: '',
44
- labelColor: '#333',
45
- tickLabelColor: '#333',
46
- tickColor: '#333',
47
- rightHideAxis: true,
48
- rightAxisSize: 50,
49
- rightLabel: '',
50
- rightLabelOffsetSize: 0,
51
- rightAxisLabelColor: '#333',
52
- rightAxisTickLabelColor: '#333',
53
- rightAxisTickColor: '#333',
54
- numTicks: '9',
55
- axisPadding: 0,
56
- tickRotation: 0,
57
- anchors: [],
58
- type: 'chart',
59
- title: 'Lollipop Style Horizontal Bar Chart',
60
- theme: 'theme-blue',
61
- fontSize: 'medium',
62
- lineDatapointStyle: 'hover',
63
- barHasBorder: 'false',
64
- isLollipopChart: false,
65
- lollipopShape: 'circle',
66
- lollipopColorStyle: 'two-tone',
67
- visualizationSubType: 'horizontal',
68
- padding: { left: 5, right: 5 },
69
- yAxis: { size: 50, gridLines: false },
70
- barThickness: 0.35,
71
- height: 260,
72
- xAxis: { type: 'categorical', size: 75, tickRotation: 0, dataKey: 'Vehicle' },
73
- table: { label: 'Data Table', expanded: true, show: true },
74
- legend: { behavior: 'isolate', position: 'right' },
75
- exclusions: { active: false, keys: [] },
76
- palette: 'qualitative-bold',
77
- labels: false,
78
- dataFormat: {},
79
- confidenceKeys: {},
80
- data: [
81
- { Group: 'Combined Total of Group A', Vehicle: '100', Home: '120', Work: '140', Office: '120' },
82
- { Group: 'Combined Total of Group B', Vehicle: '150', Home: '140', Work: '100', Office: '90' },
83
- { Group: 'Combined Total of Group C', Vehicle: '90', Home: '90', Work: '80', Office: '80' },
84
- { Group: 'Combined Total of Group D', Vehicle: '70', Home: '60', Work: '50', Office: '70' }
85
- ],
86
- dataFileName: 'CSV_Source_Example_for_Horizontal_Bar_viz-cdcwp1619811744363.csv',
87
- dataFileSourceType: 'file',
88
- visualizationType: 'Bar',
89
- runtime: {
90
- seriesLabels: { Vehicle: 'Vehicle' },
91
- seriesLabelsAll: ['Vehicle'],
92
- originalXAxis: { type: 'categorical', size: 75, tickRotation: 0, dataKey: 'Vehicle' },
93
- seriesKeys: ['Vehicle'],
94
- xAxis: { size: 50, gridLines: false },
95
- yAxis: { type: 'categorical', size: 75, tickRotation: 0, dataKey: 'Vehicle' },
96
- horizontal: true,
97
- uniqueId: 1651765968212,
98
- editorErrorMessage: ''
99
- },
100
- description: 'Subtext can be added here for options like citing data sources or insight into reading the bar chart.',
101
- series: [{ dataKey: 'Vehicle', type: 'Bar' }],
102
- barHeight: 25,
103
- barPadding: 40,
104
- labelPlacement: 'Below Bar',
105
- label: 'Number of Accidents'
106
- },
107
- boxplot: [],
108
- topAxis: { hasLine: false },
109
- isLegendValue: false,
110
- barThickness: 0.35,
111
- barHeight: 6,
112
- barSpace: 15,
113
- heights: { vertical: 300, horizontal: 170.39999999999998 },
114
- xAxis: {
115
- anchors: [],
116
- type: 'categorical',
117
- showTargetLabel: true,
118
- targetLabel: 'Target',
119
- hideAxis: true,
120
- hideLabel: true,
121
- hideTicks: true,
122
- size: '16',
123
- tickRotation: 0,
124
- min: '',
125
- max: '160',
126
- labelColor: '#333',
127
- tickLabelColor: '#333',
128
- tickColor: '#333',
129
- numTicks: '',
130
- labelOffset: 65,
131
- axisPadding: 0,
132
- target: 0,
133
- maxTickRotation: 0,
134
- dataKey: 'Group'
135
- },
136
- table: { label: 'Data Table', expanded: false, limitHeight: false, height: '', caption: '', showDownloadUrl: false, showDataTableLink: true, indexLabel: 'Group', download: false, showVertical: true, show: true },
137
- orientation: 'horizontal',
138
- color: 'pinkpurple',
139
- columns: {},
140
- legend: {
141
- behavior: 'isolate',
142
- singleRow: false,
143
- colorCode: '',
144
- reverseLabelOrder: false,
145
- description: '',
146
- dynamicLegend: false,
147
- dynamicLegendDefaultText: 'Show All',
148
- dynamicLegendItemLimit: 5,
149
- dynamicLegendItemLimitMessage: 'Dynamic Legend Item Limit Hit.',
150
- dynamicLegendChartMessage: 'Select Options from the Legend',
151
- position: 'right',
152
- hide: true,
153
- label: 'Accident Location'
154
- },
155
- exclusions: { active: false, keys: [] },
156
- palette: 'qualitative-bold',
157
- isPaletteReversed: false,
158
- twoColor: { palette: 'monochrome-1', isPaletteReversed: false },
159
- labels: false,
160
- dataFormat: { commas: false, prefix: '', suffix: '', abbreviated: false, bottomSuffix: '', bottomPrefix: '', bottomAbbreviated: false },
161
- confidenceKeys: {},
162
- visual: { border: true, accent: true, background: true, verticalHoverLine: false, horizontalHoverLine: false },
163
- useLogScale: false,
164
- filterBehavior: 'Filter Change',
165
- highlightedBarValues: [],
166
- series: [{ dataKey: 'Home', type: 'Bar', tooltip: true }],
167
- tooltips: { opacity: 90 },
168
- height: 212,
169
- data: [
170
- { Group: 'Combined Total of Group A', Vehicle: '100', Home: '120', Work: '140', Office: '120' },
171
- { Group: 'Combined Total of Group B', Vehicle: '150', Home: '140', Work: '100', Office: '90' },
172
- { Group: 'Combined Total of Group C', Vehicle: '90', Home: '90', Work: '80', Office: '80' },
173
- { Group: 'Combined Total of Group D', Vehicle: '70', Home: '60', Work: '50', Office: '70' }
174
- ],
175
- dataFileName: 'CSV_Source_Example_for_Horizontal_Bar_viz-cdcwp1619811744363.csv',
176
- dataFileSourceType: 'file',
177
- visualizationType: 'Bar',
178
- description: 'Subtext can be added here for options like citing data sources or insight into reading the bar chart.',
179
- barPadding: 47,
180
- filters: [],
181
- lollipopSize: 'medium',
182
- validated: 4.23,
183
- dynamicMarginTop: 0
184
- }
31
+ config: lollipop,
32
+ isEditor: false
33
+ }
34
+ }
35
+
36
+ export const Suppression: Story = {
37
+ args: {
38
+ config: SuppressedConfig,
39
+ isEditor: false
185
40
  }
186
41
  }
187
42
 
@@ -0,0 +1,32 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import annotationConfig from './_mock/annotation_category_mock.json'
3
+ import annotationConfigDateLinear from './_mock/annotation_date-linear_mock.json'
4
+ import annotationConfigDateTime from './_mock/annotation_date-time_mock.json'
5
+ import Chart from '../CdcChart'
6
+
7
+ const meta: Meta<typeof Chart> = {
8
+ title: 'Components/Templates/Chart/Annotation',
9
+ component: Chart
10
+ }
11
+
12
+ type Story = StoryObj<typeof Chart>
13
+
14
+ export const Chart_Annotation_Categorical: Story = {
15
+ args: {
16
+ config: annotationConfig
17
+ }
18
+ }
19
+
20
+ export const Chart_Annotation_Date_Linear: Story = {
21
+ args: {
22
+ config: annotationConfigDateLinear
23
+ }
24
+ }
25
+
26
+ export const Chart_Annotation_Date_Time: Story = {
27
+ args: {
28
+ config: annotationConfigDateTime
29
+ }
30
+ }
31
+
32
+ export default meta