@cdc/chart 4.24.2 → 4.24.4

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 (60) hide show
  1. package/dist/cdcchart.js +47933 -36918
  2. package/examples/chart-regression-1.json +378 -0
  3. package/examples/chart-regression-2.json +2360 -0
  4. package/examples/feature/filters/url-filter.json +1076 -0
  5. package/examples/feature/line/line-chart.json +362 -37
  6. package/examples/feature/regions/index.json +50 -4
  7. package/examples/feature/sankey/sankey-example-data.json +1364 -0
  8. package/examples/feature/sankey/sankey_chart_data.csv +20 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
  10. package/examples/region-issue.json +2065 -0
  11. package/examples/sparkline.json +868 -0
  12. package/examples/test.json +5409 -0
  13. package/index.html +130 -123
  14. package/package.json +4 -2
  15. package/src/CdcChart.tsx +178 -94
  16. package/src/_stories/ChartEditor.stories.tsx +14 -3
  17. package/src/_stories/_mock/url_filter.json +1076 -0
  18. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
  19. package/src/components/AreaChart/components/AreaChart.jsx +2 -1
  20. package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -63
  21. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
  22. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +32 -39
  23. package/src/components/BarChart/components/BarChart.Vertical.tsx +44 -59
  24. package/src/components/BoxPlot/BoxPlot.jsx +2 -1
  25. package/src/components/DeviationBar.jsx +3 -3
  26. package/src/components/EditorPanel/EditorPanel.tsx +1684 -1564
  27. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
  28. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +107 -0
  29. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +48 -4
  30. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +41 -0
  31. package/src/components/EditorPanel/components/Panels/index.tsx +9 -7
  32. package/src/components/EditorPanel/components/panels.scss +11 -0
  33. package/src/components/EditorPanel/editor-panel.scss +0 -724
  34. package/src/components/EditorPanel/useEditorPermissions.js +40 -14
  35. package/src/components/Legend/Legend.Component.tsx +43 -63
  36. package/src/components/Legend/Legend.tsx +8 -4
  37. package/src/components/LineChart/LineChartProps.ts +1 -0
  38. package/src/components/LineChart/helpers.ts +2 -2
  39. package/src/components/LineChart/index.tsx +7 -7
  40. package/src/components/LinearChart.jsx +11 -31
  41. package/src/components/PairedBarChart.jsx +6 -10
  42. package/src/components/PieChart/PieChart.tsx +3 -3
  43. package/src/components/Regions/components/Regions.tsx +120 -78
  44. package/src/components/Sankey/index.tsx +434 -0
  45. package/src/components/Sankey/sankey.scss +153 -0
  46. package/src/components/Sankey/types/index.ts +16 -0
  47. package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
  48. package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
  49. package/src/components/Sparkline/index.scss +3 -0
  50. package/src/components/Sparkline/index.tsx +1 -1
  51. package/src/components/ZoomBrush.tsx +2 -1
  52. package/src/data/initial-state.js +46 -2
  53. package/src/helpers/computeMarginBottom.ts +2 -1
  54. package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
  55. package/src/hooks/useBarChart.js +5 -2
  56. package/src/hooks/useScales.ts +47 -18
  57. package/src/hooks/useTooltip.tsx +9 -8
  58. package/src/scss/main.scss +33 -29
  59. package/src/types/ChartConfig.ts +32 -14
  60. package/src/types/ChartContext.ts +7 -0
package/src/CdcChart.tsx CHANGED
@@ -1,9 +1,11 @@
1
- import React, { useState, useEffect, useCallback, useRef } from 'react'
1
+ import React, { useState, useEffect, useCallback, useRef, useId } from 'react'
2
2
 
3
3
  // IE11
4
4
  import ResizeObserver from 'resize-observer-polyfill'
5
5
  import 'whatwg-fetch'
6
6
  import * as d3 from 'd3-array'
7
+ import Layout from '@cdc/core/components/Layout'
8
+ import Button from '@cdc/core/components/elements/Button'
7
9
 
8
10
  // External Libraries
9
11
  import { scaleOrdinal } from '@visx/scale'
@@ -15,8 +17,10 @@ import 'react-tooltip/dist/react-tooltip.css'
15
17
 
16
18
  // Primary Components
17
19
  import ConfigContext from './ConfigContext'
18
- import PieChart from './components/PieChart/PieChart'
20
+ import PieChart from './components/PieChart'
21
+ import SankeyChart from './components/Sankey'
19
22
  import LinearChart from './components/LinearChart'
23
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
20
24
 
21
25
  import { colorPalettesChart as colorPalettes, twoColorPalette } from '@cdc/core/data/colorPalettes'
22
26
 
@@ -46,6 +50,7 @@ import { DataTransform } from '@cdc/core/helpers/DataTransform'
46
50
  import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
47
51
  import isNumber from '@cdc/core/helpers/isNumber'
48
52
  import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
53
+ import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
49
54
 
50
55
  import './scss/main.scss'
51
56
  // load both then config below determines which to use
@@ -55,6 +60,7 @@ import Title from '@cdc/core/components/ui/Title'
55
60
  import { ChartConfig } from './types/ChartConfig'
56
61
  import { Label } from './types/Label'
57
62
  import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
63
+ import SkipTo from '@cdc/core/components/elements/SkipTo'
58
64
 
59
65
  export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
60
66
  const transform = new DataTransform()
@@ -64,7 +70,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
64
70
  const [stateData, setStateData] = useState(config.data || [])
65
71
  const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
66
72
  const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
67
- const [seriesHighlight, setSeriesHighlight] = useState<string[]>([])
73
+ const [seriesHighlight, setSeriesHighlight] = useState<string[]>(configObj && configObj?.legend?.seriesHighlight?.length ? [...configObj?.legend?.seriesHighlight] : [])
68
74
  const [currentViewport, setCurrentViewport] = useState('lg')
69
75
  const [dimensions, setDimensions] = useState<[number?, number?]>([])
70
76
  const [externalFilters, setExternalFilters] = useState<any[]>()
@@ -75,6 +81,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
75
81
  type Config = typeof config
76
82
  let legendMemo = useRef(new Map()) // map collection
77
83
  let innerContainerRef = useRef()
84
+ const legendRef = useRef(null)
78
85
 
79
86
  if (isDebug) console.log('Chart config, isEditor', config, isEditor)
80
87
 
@@ -89,8 +96,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
89
96
  if (config.table && (!config.table?.label || config.table?.label === '')) config.table.label = 'Data Table'
90
97
 
91
98
  const { barBorderClass, lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
92
-
93
- const handleChartTabbing = config.showSidebar ? `#legend` : config?.title ? `#dataTableSection__${config.title.replace(/\s/g, '')}` : `#dataTableSection`
99
+ const legendId = useId()
100
+ const handleChartTabbing = !config.legend?.hide ? legendId : config?.title ? `dataTableSection__${config.title.replace(/\s/g, '')}` : `dataTableSection`
94
101
 
95
102
  const reloadURLData = async () => {
96
103
  if (config.dataUrl) {
@@ -219,6 +226,15 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
219
226
  }
220
227
  }
221
228
  let newConfig = { ...defaults, ...response }
229
+ if (newConfig.filters) {
230
+ newConfig.filters.forEach((filter, index) => {
231
+ const queryStringFilterValue = getQueryStringFilterValue(filter)
232
+ if (queryStringFilterValue) {
233
+ newConfig.filters[index].active = queryStringFilterValue
234
+ }
235
+ })
236
+ }
237
+
222
238
  if (newConfig.visualizationType === 'Box Plot') {
223
239
  newConfig.legend.hide = true
224
240
  }
@@ -253,7 +269,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
253
269
  if (newConfig.exclusions && newConfig.exclusions.active) {
254
270
  if (newConfig.xAxis.type === 'categorical' && newConfig.exclusions.keys?.length > 0) {
255
271
  newExcludedData = data.filter(e => !newConfig.exclusions.keys.includes(e[newConfig.xAxis.dataKey]))
256
- } else if (newConfig.xAxis.type === 'date' && (newConfig.exclusions.dateStart || newConfig.exclusions.dateEnd) && newConfig.xAxis.dateParseFormat) {
272
+ } else if (isDateScale(newConfig.xAxis) && (newConfig.exclusions.dateStart || newConfig.exclusions.dateEnd) && newConfig.xAxis.dateParseFormat) {
257
273
  // Filter dates
258
274
  const timestamp = e => new Date(e).getTime()
259
275
 
@@ -297,13 +313,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
297
313
  setFilteredData(currentData)
298
314
  }
299
315
 
300
- if (!['Area Chart', 'Bar', 'Line', 'Combo'].includes(newConfig.visualizationType) || newConfig.orientation === 'horizontal') {
301
- newConfig.xAxis.sortDates = false
302
- }
303
-
304
- if (newConfig.xAxis.sortDates && newConfig.barThickness > 0.1) {
316
+ if (newConfig.xAxis.type === 'date-time' && newConfig.barThickness > 0.1) {
305
317
  newConfig.barThickness = 0.035
306
- } else if (!newConfig.xAxis.sortDates && newConfig.barThickness < 0.1) {
318
+ } else if (newConfig.xAxis.type !== 'date-time' && newConfig.barThickness < 0.1) {
307
319
  newConfig.barThickness = 0.35
308
320
  }
309
321
 
@@ -433,7 +445,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
433
445
  if (series.type === 'Forecasting') {
434
446
  newConfig.runtime.forecastingSeriesKeys.push(series)
435
447
  }
436
- if (series.type === 'Bar') {
448
+ if (series.type === 'Bar' || series.type === 'Combo') {
437
449
  newConfig.runtime.barSeriesKeys.push(series.dataKey)
438
450
  }
439
451
  if (series.type === 'Line' || series.type === 'dashed-sm' || series.type === 'dashed-md' || series.type === 'dashed-lg') {
@@ -483,6 +495,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
483
495
  newConfig.runtime.uniqueId = Date.now()
484
496
  newConfig.runtime.editorErrorMessage = newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey ? 'Data Key property in Y Axis section must be set for pie charts.' : ''
485
497
 
498
+ // Sankey Description box error message
499
+ newConfig.runtime.editorErrorMessage = newConfig.visualizationType === 'Sankey' && !newConfig.description ? 'SUBTEXT/CITATION field is empty: A description of the Sankey Diagram data must be inputted.' : ''
500
+
501
+ if (newConfig.legend.seriesHighlight?.length) {
502
+ setSeriesHighlight(newConfig.legend?.seriesHighlight)
503
+ }
504
+
486
505
  setConfig(newConfig)
487
506
  }
488
507
 
@@ -698,7 +717,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
698
717
  // Called on reset button click, unhighlights all data series
699
718
  const highlightReset = () => {
700
719
  try {
701
- const legend = document.getElementById('legend')
720
+ const legend = legendRef.current
702
721
  if (!legend) throw new Error('No legend available to set previous focus on.')
703
722
  legend.focus()
704
723
  } catch (e) {
@@ -903,6 +922,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
903
922
  }
904
923
 
905
924
  const missingRequiredSections = () => {
925
+ if (config.visualizationType === 'Sankey') return false // skip checks for now
906
926
  if (config.visualizationType === 'Forecasting') return false // skip required checks for now.
907
927
  if (config.visualizationType === 'Forest Plot') return false // skip required checks for now.
908
928
  if (config.visualizationType === 'Pie') {
@@ -990,6 +1010,63 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
990
1010
  return formattedValue
991
1011
  }
992
1012
 
1013
+ const Confirm = () => {
1014
+ const confirmDone = e => {
1015
+ if (e) {
1016
+ e.preventDefault()
1017
+ }
1018
+
1019
+ let newConfig = { ...config }
1020
+ delete newConfig.newViz
1021
+
1022
+ updateConfig(newConfig)
1023
+ }
1024
+
1025
+ const styles = {
1026
+ position: 'relative',
1027
+ height: '100vh',
1028
+ width: '100%',
1029
+ display: 'flex',
1030
+ justifyContent: 'center',
1031
+ alignItems: 'center',
1032
+ gridArea: 'content'
1033
+ }
1034
+
1035
+ return (
1036
+ <section className='waiting' style={styles}>
1037
+ <section className='waiting-container'>
1038
+ <h3>Finish Configuring</h3>
1039
+ <p>Set all required options to the left and confirm below to display a preview of the chart.</p>
1040
+ <Button className='btn' style={{ margin: '1em auto' }} disabled={missingRequiredSections()} onClick={e => confirmDone(e)}>
1041
+ I'm Done
1042
+ </Button>
1043
+ </section>
1044
+ </section>
1045
+ )
1046
+ }
1047
+
1048
+ const Error = () => {
1049
+ const styles = {
1050
+ position: 'absolute',
1051
+ background: 'white',
1052
+ zIndex: '999',
1053
+ height: '100vh',
1054
+ width: '100%',
1055
+ display: 'flex',
1056
+ justifyContent: 'center',
1057
+ alignItems: 'center',
1058
+ gridArea: 'content'
1059
+ }
1060
+ return (
1061
+ <section className='waiting' style={styles}>
1062
+ <section className='waiting-container'>
1063
+ <h3>Error With Configuration</h3>
1064
+ <p>{config.runtime.editorErrorMessage}</p>
1065
+ </section>
1066
+ </section>
1067
+ )
1068
+ }
1069
+
993
1070
  // this is passed DOWN into the various components
994
1071
  // then they do a lookup based on the bin number as index into here (TT)
995
1072
  const applyLegendToRow = rowObj => {
@@ -1024,6 +1101,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1024
1101
  // Prevent render if loading
1025
1102
  let body = <Loading />
1026
1103
 
1104
+ const getChartWrapperClasses = () => {
1105
+ const classes = ['chart-container', 'p-relative']
1106
+ if (config.legend.position === 'bottom') classes.push('bottom')
1107
+ if (config.legend.hide) classes.push('legend-hidden')
1108
+ if (lineDatapointClass) classes.push(lineDatapointClass)
1109
+ if (!config.barHasBorder) classes.push('chart-bar--no-border')
1110
+ if (isDebug) classes.push('debug')
1111
+ classes.push(...contentClasses)
1112
+ return classes
1113
+ }
1114
+
1027
1115
  if (!loading) {
1028
1116
  const tableLink = (
1029
1117
  <a href={`#data-table-${config.dataKey}`} className='margin-left-href'>
@@ -1033,90 +1121,92 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1033
1121
  body = (
1034
1122
  <>
1035
1123
  {isEditor && <EditorPanel />}
1036
- {!missingRequiredSections() && !config.newViz && (
1037
- <div className='cdc-chart-inner-container'>
1038
- <Title showTitle={config.showTitle} isDashboard={isDashboard} title={title} superTitle={config.superTitle} classes={['chart-title', `${config.theme}`, 'cove-component__header']} style={undefined} />
1039
-
1040
- <a id='skip-chart-container' className='cdcdataviz-sr-only-focusable' href={handleChartTabbing}>
1041
- Skip Over Chart Container
1042
- </a>
1043
- {/* Filters */}
1044
- {config.filters && !externalFilters && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />}
1045
- {/* Visualization */}
1046
- {config?.introText && config.visualizationType !== 'Spark Line' && <section className='introText'>{parse(config.introText)}</section>}
1047
- <div
1048
- style={{ marginBottom: computeMarginBottom(config, legend, currentViewport) }}
1049
- className={`chart-container p-relative ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')} ${isDebug ? 'debug' : ''}`}
1050
- >
1051
- {/* All charts except sparkline */}
1052
- {config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
1053
-
1054
- {/* Sparkline */}
1055
- {config.visualizationType === 'Spark Line' && (
1056
- <>
1057
- {config?.introText && (
1058
- <section className='introText' style={{ padding: '0px 0 35px' }}>
1059
- {parse(config.introText)}
1060
- </section>
1061
- )}
1062
- <div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
1063
- <ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
1064
- </div>
1065
- {description && (
1066
- <div className='subtext' style={{ padding: '35px 0 15px' }}>
1067
- {parse(description)}
1124
+ <Layout.Responsive isEditor={isEditor}>
1125
+ {config.newViz && <Confirm />}
1126
+ {undefined === config.newViz && isEditor && config.runtime && config.runtime?.editorErrorMessage && <Error />}
1127
+ {!missingRequiredSections() && !config.newViz && (
1128
+ <div className='cdc-chart-inner-container cove-component__content' aria-label={handleChartAriaLabels(config)} tabIndex={0}>
1129
+ <Title showTitle={config.showTitle} isDashboard={isDashboard} title={title} superTitle={config.superTitle} classes={['chart-title', `${config.theme}`, 'cove-component__header']} style={undefined} />
1130
+
1131
+ {/* 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' />
1134
+ {/* Visualization */}
1135
+ {config?.introText && config.visualizationType !== 'Spark Line' && <section className='introText'>{parse(config.introText)}</section>}
1136
+
1137
+ <div style={{ marginBottom: computeMarginBottom(config, legend, currentViewport) }} className={getChartWrapperClasses().join(' ')}>
1138
+ {/* All charts except sparkline */}
1139
+ {config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
1140
+
1141
+ {/* Sparkline */}
1142
+ {config.visualizationType === 'Spark Line' && (
1143
+ <>
1144
+ <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />
1145
+ {config?.introText && (
1146
+ <section className='introText' style={{ padding: '0px 0 35px' }}>
1147
+ {parse(config.introText)}
1148
+ </section>
1149
+ )}
1150
+ <div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
1151
+ <ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
1068
1152
  </div>
1069
- )}
1070
- </>
1153
+ {description && (
1154
+ <div className='subtext' style={{ padding: '35px 0 15px' }}>
1155
+ {parse(description)}
1156
+ </div>
1157
+ )}
1158
+ </>
1159
+ )}
1160
+ {/* Sankey */}
1161
+ {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} />}
1163
+ </div>
1164
+ {/* Link */}
1165
+ {isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
1166
+
1167
+ {/* Description */}
1168
+ {description && config.visualizationType !== 'Spark Line' && <div className={'column ' + config.isResponsiveTicks ? 'subtext--responsive-ticks' : 'subtext'}>{parse(description)}</div>}
1169
+
1170
+ {/* buttons */}
1171
+ <MediaControls.Section classes={['download-buttons']}>
1172
+ {config.table.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
1173
+ {config.table.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
1174
+ </MediaControls.Section>
1175
+
1176
+ {/* Data Table */}
1177
+ {((config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Sankey') || (config.visualizationType === 'Sankey' && config.table.show)) && (
1178
+ <DataTable
1179
+ 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)}
1182
+ expandDataTable={config.table.expanded}
1183
+ columns={config.columns}
1184
+ displayDataAsText={displayDataAsText}
1185
+ displayGeoName={displayGeoName}
1186
+ applyLegendToRow={applyLegendToRow}
1187
+ tableTitle={config.table.label}
1188
+ indexTitle={config.table.indexLabel}
1189
+ vizTitle={title}
1190
+ viewport={currentViewport}
1191
+ tabbingId={handleChartTabbing}
1192
+ colorScale={colorScale}
1193
+ />
1071
1194
  )}
1072
- {!config.legend.hide && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Forest Plot' && <Legend />}
1195
+ {config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
1196
+ {/* show pdf or image button */}
1073
1197
  </div>
1074
- {/* Link */}
1075
- {isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
1076
-
1077
- {/* Description */}
1078
- {description && config.visualizationType !== 'Spark Line' && <div className={'column ' + config.isResponsiveTicks ? 'subtext--responsive-ticks' : 'subtext'}>{parse(description)}</div>}
1079
-
1080
- {/* buttons */}
1081
- <MediaControls.Section classes={['download-buttons']}>
1082
- {config.table.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
1083
- {config.table.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
1084
- </MediaControls.Section>
1085
-
1086
- {/* Data Table */}
1087
- {config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
1088
- <DataTable
1089
- config={config}
1090
- rawData={config.table.customTableConfig ? filterData(config.filters, config.data) : config.data}
1091
- runtimeData={transform.applySuppression(filteredData || excludedData, config.suppressedData)}
1092
- expandDataTable={config.table.expanded}
1093
- columns={config.columns}
1094
- displayDataAsText={displayDataAsText}
1095
- displayGeoName={displayGeoName}
1096
- applyLegendToRow={applyLegendToRow}
1097
- tableTitle={config.table.label}
1098
- indexTitle={config.table.indexLabel}
1099
- vizTitle={title}
1100
- viewport={currentViewport}
1101
- tabbingId={handleChartTabbing}
1102
- colorScale={colorScale}
1103
- />
1104
- )}
1105
- {config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
1106
- {/* show pdf or image button */}
1107
- </div>
1108
- )}
1198
+ )}
1199
+ </Layout.Responsive>
1109
1200
  </>
1110
1201
  )
1111
1202
  }
1112
1203
 
1113
- const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
1204
+ const getXAxisData = d => (isDateScale(config.runtime.xAxis) ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
1114
1205
  const getYAxisData = (d, seriesKey) => d[seriesKey]
1115
1206
 
1116
1207
  const capitalize = str => {
1117
1208
  return str.charAt(0).toUpperCase() + str.slice(1)
1118
1209
  }
1119
-
1120
1210
  const contextValues = {
1121
1211
  capitalize,
1122
1212
  computeMarginBottom,
@@ -1167,17 +1257,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1167
1257
  debugSvg: isDebug
1168
1258
  }
1169
1259
 
1170
- const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
1171
-
1172
- config.visualizationType === 'Spark Line' && classes.push(`type-sparkline`)
1173
- isEditor && classes.push('spacing-wrapper')
1174
- isEditor && classes.push('isEditor')
1175
-
1176
1260
  return (
1177
1261
  <ConfigContext.Provider value={contextValues}>
1178
- <div className={`${classes.join(' ')}`} ref={outerContainerRef} data-lollipop={config.isLollipopChart} data-download-id={imageId}>
1262
+ <Layout.VisualizationWrapper config={config} isEditor={isEditor} currentViewport={currentViewport} ref={outerContainerRef} imageId={imageId} showEditorPanel={config?.showEditorPanel}>
1179
1263
  {body}
1180
- </div>
1264
+ </Layout.VisualizationWrapper>
1181
1265
  </ConfigContext.Provider>
1182
1266
  )
1183
1267
  }
@@ -1,12 +1,12 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react'
2
-
2
+ import { userEvent, within } from '@storybook/testing-library'
3
3
  import Chart from '../CdcChart'
4
-
5
4
  import pieChartExample from './_mock/pie_config.json'
6
5
  import pieData from './_mock/pie_data.json'
6
+ import urlFilterExample from './_mock/url_filter.json'
7
7
 
8
8
  const meta: Meta<typeof Chart> = {
9
- title: 'Components/Templates/Editor/Chart',
9
+ title: 'Components/Templates/Chart/Editor',
10
10
  component: Chart
11
11
  }
12
12
 
@@ -19,4 +19,15 @@ export const Primary: Story = {
19
19
  }
20
20
  }
21
21
 
22
+ const sleep = ms => {
23
+ return new Promise(r => setTimeout(r, ms))
24
+ }
25
+
26
+ export const Url_Filter: Story = {
27
+ args: {
28
+ config: urlFilterExample,
29
+ isEditor: true
30
+ }
31
+ }
32
+
22
33
  export default meta