@cdc/chart 4.23.2 → 4.23.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 (94) hide show
  1. package/dist/cdcchart.js +42292 -40337
  2. package/examples/feature/__data__/area-chart.json +56 -0
  3. package/examples/{planet-example-data.json → feature/__data__/planet-example-data-max-increase.json} +4 -4
  4. package/examples/feature/__data__/planet-example-data.json +68 -0
  5. package/examples/feature/area/area-chart.json +244 -0
  6. package/examples/{example-bar-chart.json → feature/bar/example-bar-chart.json} +4 -1
  7. package/examples/feature/bar/horizontal-chart-max-increase.json +44 -0
  8. package/examples/{horizontal-chart.json → feature/bar/horizontal-chart.json} +10 -4
  9. package/examples/{horizontal-stacked-bar-chart.json → feature/bar/horizontal-stacked-bar-chart.json} +7 -3
  10. package/examples/{planet-chart-horizontal-example-config.json → feature/bar/planet-chart-horizontal-example-config.json} +8 -3
  11. package/examples/feature/bar/planet-example-config.json +156 -0
  12. package/examples/{box-plot.json → feature/boxplot/boxplot.json} +7 -8
  13. package/examples/feature/boxplot/testing.csv +38 -0
  14. package/examples/feature/combo/combochart-categories_are_numbers .json +18 -0
  15. package/examples/{planet-combo-example-config.json → feature/combo/planet-combo-example-config.json} +1 -1
  16. package/examples/feature/deviation/planet-deviation-config.json +168 -0
  17. package/examples/feature/deviation/planet-deviation-data.json +38 -0
  18. package/examples/feature/filters/filter-testing.json +178 -0
  19. package/examples/feature/forecasting/case_date_example.csv +130 -0
  20. package/examples/feature/forecasting/effective_reproduction.json +202 -0
  21. package/examples/feature/forecasting/r_data.csv +130 -0
  22. package/examples/feature/line/line-chart-max-increase.json +32 -0
  23. package/examples/feature/line/line-chart.json +124 -0
  24. package/examples/{paired-bar-example.json → feature/paired-bar/paired-bar-example.json} +10 -4
  25. package/examples/{planet-pie-example-config.json → feature/pie/planet-pie-example-config.json} +2 -2
  26. package/examples/{scatterplot-continuous.csv → feature/scatterplot/scatterplot-continuous.csv} +3 -3
  27. package/examples/{scatterplot.json → feature/scatterplot/scatterplot.json} +3 -3
  28. package/examples/feature/sparkline/example-sparkline.json +76 -0
  29. package/examples/feature/tests-big-small/big-small-test-bar.json +328 -0
  30. package/examples/feature/tests-big-small/big-small-test-line.json +328 -0
  31. package/examples/feature/tests-big-small/big-small-test-negative.json +328 -0
  32. package/examples/{case-rate-example-config.json → feature/tests-case-rate/case-rate-example-config.json} +2 -2
  33. package/examples/{covid-confidence-example-config.json → feature/tests-covid/covid-confidence-example-config.json} +8 -3
  34. package/examples/{covid-example-config.json → feature/tests-covid/covid-example-config.json} +7 -3
  35. package/examples/{cutoff-example-config.json → feature/tests-cutoff/cutoff-example-config.json} +7 -3
  36. package/examples/{date-exclusions-config.json → feature/tests-date-exclusions/date-exclusions-config.json} +2 -2
  37. package/examples/{example-bar-chart-nonnumeric.json → feature/tests-non-numerics/example-bar-chart-nonnumeric.json} +1 -1
  38. package/examples/{line-chart-nonnumeric.json → feature/tests-non-numerics/line-chart-nonnumeric.json} +5 -5
  39. package/examples/{planet-pie-example-config-nonnumeric.json → feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json} +2 -2
  40. package/examples/{sparkline-chart-nonnumeric.json → feature/tests-non-numerics/sparkline-chart-nonnumeric.json} +2 -2
  41. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +31 -172
  42. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +145 -7
  43. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json +1 -0
  44. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +96 -14
  45. package/examples/gallery/line/line.json +1 -0
  46. package/examples/gallery/paired-bar/paired-bar-chart.json +1 -0
  47. package/index.html +76 -35
  48. package/package.json +6 -3
  49. package/src/CdcChart.jsx +245 -106
  50. package/src/components/AreaChart.jsx +233 -0
  51. package/src/components/BarChart.jsx +103 -62
  52. package/src/components/BoxPlot.jsx +39 -18
  53. package/src/components/DataTable.jsx +26 -21
  54. package/src/components/DeviationBar.jsx +191 -0
  55. package/src/components/EditorPanel.jsx +662 -298
  56. package/src/components/Legend.jsx +59 -46
  57. package/src/components/LineChart.jsx +12 -36
  58. package/src/components/LinearChart.jsx +163 -64
  59. package/src/components/PairedBarChart.jsx +6 -7
  60. package/src/components/PieChart.jsx +12 -17
  61. package/src/components/ScatterPlot.jsx +19 -16
  62. package/src/components/SparkLine.jsx +84 -118
  63. package/src/components/useIntersectionObserver.jsx +1 -1
  64. package/src/data/initial-state.js +27 -7
  65. package/src/hooks/useColorPalette.js +58 -48
  66. package/src/hooks/useReduceData.js +3 -4
  67. package/src/index.jsx +3 -2
  68. package/src/scss/editor-panel.scss +20 -0
  69. package/src/scss/main.scss +8 -6
  70. package/src/test/CdcChart.test.jsx +6 -0
  71. package/examples/box-plot.csv +0 -5
  72. package/examples/dynamic-legends.json +0 -125
  73. package/examples/line-chart.json +0 -34
  74. package/examples/planet-example-config.json +0 -37
  75. package/examples/temp-example-config.json +0 -64
  76. package/examples/temp-example-data.json +0 -130
  77. package/src/components/Filters.jsx +0 -125
  78. /package/examples/{age-adjusted-rates.json → feature/__data__/age-adjusted-rates.json} +0 -0
  79. /package/examples/{new-data.csv → feature/__data__/new-data.csv} +0 -0
  80. /package/examples/{Barchart_with_negative.json → feature/bar/Barchart_with_negative.json} +0 -0
  81. /package/examples/{stacked-vertical-bar-example-negative.json → feature/bar/stacked-vertical-bar-example-negative.json} +0 -0
  82. /package/examples/{stacked-vertical-bar-example.json → feature/bar/stacked-vertical-bar-example.json} +0 -0
  83. /package/examples/{box-plot-data.json → feature/boxplot/box-plot-data.json} +0 -0
  84. /package/examples/{newdata.json → feature/boxplot/boxplot-data.json} +0 -0
  85. /package/examples/{paired-bar-data.json → feature/paired-bar/paired-bar-data.json} +0 -0
  86. /package/examples/{paired-bar-formatted.json → feature/paired-bar/paired-bar-formatted.json} +0 -0
  87. /package/examples/{case-rate-example-data.json → feature/tests-case-rate/case-rate-example-data.json} +0 -0
  88. /package/examples/{covid-example-data-confidence.json → feature/tests-covid/covid-example-data-confidence.json} +0 -0
  89. /package/examples/{covid-example-data.json → feature/tests-covid/covid-example-data.json} +0 -0
  90. /package/examples/{cutoff-example-data.json → feature/tests-cutoff/cutoff-example-data.json} +0 -0
  91. /package/examples/{date-exclusions-data.json → feature/tests-date-exclusions/date-exclusions-data.json} +0 -0
  92. /package/examples/{example-combo-bar-nonnumeric.json → feature/tests-non-numerics/example-combo-bar-nonnumeric.json} +0 -0
  93. /package/examples/{planet-example-data-nonnumeric.json → feature/tests-non-numerics/planet-example-data-nonnumeric.json} +0 -0
  94. /package/examples/{stacked-vertical-bar-example-nonnumerics.json → feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json} +0 -0
@@ -1,4 +1,4 @@
1
- import React, { useContext, useEffect } from 'react'
1
+ import React, { useContext } from 'react'
2
2
 
3
3
  import * as allCurves from '@visx/curve'
4
4
  import { Group } from '@visx/group'
@@ -15,7 +15,7 @@ import useReduceData from '../hooks/useReduceData'
15
15
  import ConfigContext from '../ConfigContext'
16
16
 
17
17
  export default function SparkLine({ width: parentWidth, height: parentHeight }) {
18
- const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, seriesHighlight, formatNumber, colorScale, isNumber, handleChartAriaLabels } = useContext(ConfigContext)
18
+ const { transformedData: data, config, parseDate, formatDate, seriesHighlight, formatNumber, colorScale, handleChartAriaLabels } = useContext(ConfigContext)
19
19
  let width = parentWidth
20
20
  const { minValue, maxValue } = useReduceData(config, data, ConfigContext)
21
21
 
@@ -35,44 +35,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
35
35
  const isMaxValid = Number(enteredMaxValue) >= Number(maxValue)
36
36
  const isMinValid = Number(enteredMinValue) <= Number(minValue)
37
37
 
38
-
39
- // REMOVE bad data points from the data set
40
- // Examples: NA, N/A, "1,234", "anystring"
41
- // - if you dont call this on data into LineGroup below, for example
42
- // then entire data series are removed because of the defined statement
43
- // i.e. if a series has any bad data points the entire series wont plot
44
- const cleanData = (data, testing = false) => {
45
- let cleanedup = []
46
- if (testing) console.log('## Data to clean=', data)
47
- data.forEach(function (d, i) {
48
- let cleanedSeries = {}
49
- Object.keys(d).forEach(function (key) {
50
- if (key === 'Date') {
51
- // pass thru the dates
52
- cleanedSeries[key] = d[key]
53
- } else {
54
- // remove comma and dollar signs
55
- let tmp = d[key] != null && d[key] != '' ? d[key].replace(/[,\$]/g, '') : ''
56
- if (testing) console.log("tmp no comma or $", tmp)
57
- if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
58
- cleanedSeries[key] = tmp
59
- } else {
60
- // return nothing to skip bad data point
61
- cleanedSeries[key] = '' // returning blank fixes broken chart draw
62
- }
63
- }
64
- })
65
- cleanedup.push(cleanedSeries)
66
- })
67
- if (testing) console.log('## cleanedData =', cleanedup)
68
- return cleanedup
69
- }
70
-
71
- // Just do this once up front otherwise we end up
72
- // calling clean several times on same set of data (TT)
73
- const cleanedData = cleanData(data, config.xAxis.dataKey);
74
-
75
- if (cleanedData) {
38
+ if (data) {
76
39
  let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
77
40
  let max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
78
41
 
@@ -87,7 +50,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
87
50
  max += paddingValue
88
51
  }
89
52
 
90
- let xAxisDataMapped = cleanedData.map(d => getXAxisData(d))
53
+ let xAxisDataMapped = data.map(d => getXAxisData(d))
91
54
 
92
55
  if (config.runtime.horizontal) {
93
56
  xScale = scaleLinear({
@@ -116,6 +79,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
116
79
  range: [margin.left, width - margin.right]
117
80
  })
118
81
 
82
+ // eslint-disable-next-line
119
83
  seriesScale = scalePoint({
120
84
  domain: config.runtime.barSeriesKeys || config.runtime.seriesKeys,
121
85
  range: [0, xMax]
@@ -128,88 +92,90 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
128
92
  return (
129
93
  <ErrorBoundary component='SparkLine'>
130
94
  <svg role='img' aria-label={handleChartAriaLabels(config)} width={width} height={height} className={'sparkline'} tabIndex={0}>
131
- {(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => (
132
- <>
133
- <Group
134
- className='sparkline-group'
135
- height={parentHeight}
136
- style={{ height: parentHeight }}
137
- top={margin.top}
138
- key={`series-${seriesKey}`}
139
- opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
140
- display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
141
- >
142
- {cleanedData.map((d, dataIndex) => {
143
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
144
- let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
145
-
146
- const tooltip = `<div>
95
+ {config.runtime.lineSeriesKeys?.length > 0
96
+ ? config.runtime.lineSeriesKeys
97
+ : config.runtime.seriesKeys.map((seriesKey, index) => (
98
+ <>
99
+ <Group
100
+ className='sparkline-group'
101
+ height={parentHeight}
102
+ style={{ height: parentHeight }}
103
+ top={margin.top}
104
+ key={`series-${seriesKey}`}
105
+ opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
106
+ display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
107
+ >
108
+ {data.map((d, dataIndex) => {
109
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
110
+ let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
111
+
112
+ const tooltip = `<div>
147
113
  ${yAxisTooltip}<br />
148
114
  ${xAxisTooltip}<br />
149
115
  ${config.seriesLabel ? `${config.seriesLabel}: ${seriesKey}` : ''}
150
116
  </div>`
151
117
 
152
- let circleRadii = 4.5
153
- return (
154
- <Group key={`series-${seriesKey}-point-${dataIndex}`}>
155
- <Text display={config.labels ? 'block' : 'none'} x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
156
- {formatNumber(d[seriesKey])}
157
- </Text>
158
-
159
- {dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
160
- <circle
161
- key={`${seriesKey}-${dataIndex}`}
162
- r={circleRadii}
163
- cx={xScale(getXAxisData(d))}
164
- cy={yScale(getYAxisData(d, seriesKey))}
165
- fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
166
- style={{ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000' }}
167
- data-tooltip-html={tooltip}
168
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
169
- />
170
- )}
171
- </Group>
172
- )
173
- })}
174
- <LinePath
175
- curve={allCurves.curveLinear}
176
- data={cleanedData}
177
- x={d => xScale(getXAxisData(d))}
178
- y={d => yScale(getYAxisData(d, seriesKey))}
179
- stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
180
- strokeWidth={2}
181
- strokeOpacity={1}
182
- shapeRendering='geometricPrecision'
183
- markerEnd={`url(#${'arrow'}--${index})`}
184
- />
185
- <MarkerArrow
186
- id={`arrow--${index}`}
187
- refX={2}
188
- size={6}
189
- markerEnd={`url(#${'arrow'}--${index})`}
190
- strokeOpacity={1}
191
- fillOpacity={1}
192
- // stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
193
- fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
194
- />
195
- </Group>
196
- <AxisBottom
197
- top={yMax + margin.top}
198
- hideAxisLine
199
- hideTicks
200
- scale={xScale}
201
- tickValues={handleSparkLineTicks}
202
- tickFormat={formatDate}
203
- stroke={'black'}
204
- tickStroke={'black'}
205
- tickLabelProps={() => ({
206
- fill: 'black',
207
- fontSize: 11,
208
- textAnchor: 'middle'
209
- })}
210
- />
211
- </>
212
- ))}
118
+ let circleRadii = 4.5
119
+ return (
120
+ <Group key={`series-${seriesKey}-point-${dataIndex}`}>
121
+ <Text display={config.labels ? 'block' : 'none'} x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
122
+ {formatNumber(d[seriesKey])}
123
+ </Text>
124
+
125
+ {dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
126
+ <circle
127
+ key={`${seriesKey}-${dataIndex}`}
128
+ r={circleRadii}
129
+ cx={xScale(getXAxisData(d))}
130
+ cy={yScale(getYAxisData(d, seriesKey))}
131
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
132
+ style={{ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000' }}
133
+ data-tooltip-html={tooltip}
134
+ data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
135
+ />
136
+ )}
137
+ </Group>
138
+ )
139
+ })}
140
+ <LinePath
141
+ curve={allCurves.curveLinear}
142
+ data={data}
143
+ x={d => xScale(getXAxisData(d))}
144
+ y={d => yScale(getYAxisData(d, seriesKey))}
145
+ stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
146
+ strokeWidth={2}
147
+ strokeOpacity={1}
148
+ shapeRendering='geometricPrecision'
149
+ markerEnd={`url(#${'arrow'}--${index})`}
150
+ />
151
+ <MarkerArrow
152
+ id={`arrow--${index}`}
153
+ refX={2}
154
+ size={6}
155
+ markerEnd={`url(#${'arrow'}--${index})`}
156
+ strokeOpacity={1}
157
+ fillOpacity={1}
158
+ // stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
159
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
160
+ />
161
+ </Group>
162
+ <AxisBottom
163
+ top={yMax + margin.top}
164
+ hideAxisLine
165
+ hideTicks
166
+ scale={xScale}
167
+ tickValues={handleSparkLineTicks}
168
+ tickFormat={tick => (config.xAxis.type === 'date' ? formatDate(tick) : null)}
169
+ stroke={'black'}
170
+ tickStroke={'black'}
171
+ tickLabelProps={() => ({
172
+ fill: 'black',
173
+ fontSize: 11,
174
+ textAnchor: 'middle'
175
+ })}
176
+ />
177
+ </>
178
+ ))}
213
179
  </svg>
214
180
  </ErrorBoundary>
215
181
  )
@@ -22,7 +22,7 @@ export default function useIntersectionObserver(elementRef, { threshold = 0, roo
22
22
  observer.observe(node)
23
23
 
24
24
  return () => observer.disconnect()
25
- }, 500);
25
+ }, 500)
26
26
  }, [elementRef, threshold, root, rootMargin, frozen])
27
27
 
28
28
  return entry
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  type: 'chart',
3
3
  title: '',
4
+ showTitle: true,
4
5
  theme: 'theme-blue',
5
6
  animate: false,
6
7
  fontSize: 'medium',
@@ -24,6 +25,7 @@ export default {
24
25
  hideTicks: false,
25
26
  size: 50,
26
27
  gridLines: false,
28
+ enablePadding: false,
27
29
  min: '',
28
30
  max: '',
29
31
  labelColor: '#333',
@@ -63,9 +65,11 @@ export default {
63
65
  median: 'Median',
64
66
  sd: 'Standard Deviation',
65
67
  iqr: 'Interquartile Range',
66
- count: 'Count',
68
+ total: 'Total',
67
69
  outliers: 'Outliers',
68
- values: 'Values'
70
+ values: 'Values',
71
+ lowerBounds: 'Lower Bounds',
72
+ upperBounds: 'Upper Bounds'
69
73
  }
70
74
  },
71
75
  topAxis: {
@@ -81,6 +85,8 @@ export default {
81
85
  },
82
86
  xAxis: {
83
87
  type: 'categorical',
88
+ showTargetLabel: true,
89
+ targetLabel: 'Target',
84
90
  hideAxis: false,
85
91
  hideLabel: false,
86
92
  hideTicks: false,
@@ -93,7 +99,8 @@ export default {
93
99
  tickColor: '#333',
94
100
  numTicks: '',
95
101
  labelOffset: 65,
96
- axisPadding: 0
102
+ axisPadding: 0,
103
+ target: 0
97
104
  },
98
105
  table: {
99
106
  label: 'Data Table',
@@ -101,12 +108,12 @@ export default {
101
108
  limitHeight: false,
102
109
  height: '',
103
110
  caption: '',
104
- showDownloadUrl: false
111
+ showDownloadUrl: false,
112
+ showDataTableLink: true
105
113
  },
106
114
  orientation: 'vertical',
107
115
  legend: {
108
116
  behavior: 'isolate',
109
- position: 'right',
110
117
  singleRow: false,
111
118
  colorCode: '',
112
119
  reverseLabelOrder: false,
@@ -123,12 +130,25 @@ export default {
123
130
  },
124
131
  palette: 'qualitative-bold',
125
132
  isPaletteReversed: false,
133
+ twoColor: {
134
+ palette: 'monochrome-1',
135
+ isPaletteReversed: false
136
+ },
126
137
  labels: false,
127
- dataFormat: { commas: false, prefix: '', suffix: '', abbreviated: false },
138
+ dataFormat: {
139
+ commas: false,
140
+ prefix: '',
141
+ suffix: '',
142
+ abbreviated: false,
143
+ bottomSuffix: '',
144
+ bottomPrefix: '',
145
+ bottomAbbreviated: false
146
+ },
128
147
  confidenceKeys: {},
129
148
  visual: {
130
149
  border: true,
131
150
  accent: true,
132
151
  background: true
133
- }
152
+ },
153
+ filterBehavior: 'Filter Change'
134
154
  }
@@ -1,58 +1,68 @@
1
- import { useEffect, useReducer } from 'react'
2
-
3
- // constants
4
- const SEQUENTIAL = 'SEQUENTIAL'
5
- const SEQUENTIAL_REVERSE = 'SEQUENTIAL_REVERSE'
6
- export const GET_PALETTE = 'GET_PALETTE'
7
-
8
- // create initial state
9
- const initialState = {
10
- filteredPallets: [],
11
- isPaletteReversed: false,
12
- filteredQualitative: [],
13
- paletteName: undefined
14
- }
1
+ import { colorPalettesChart, twoColorPalette } from '@cdc/core/data/colorPalettes'
2
+ import { useEffect } from 'react'
15
3
 
16
- function reducer(state, action) {
17
- const palletNamesArr = Object.keys(action.payload) // action.payload === colorPalettes object
18
- let paletteName = ''
19
- switch (action.type) {
20
- case GET_PALETTE:
21
- return { ...state, paletteName: action.paletteName }
22
- case SEQUENTIAL:
23
- paletteName = state.paletteName && state.paletteName.endsWith('reverse') ? String(state.paletteName).substring(0, state.paletteName.length - 7) : String(state.paletteName)
24
- const qualitative = palletNamesArr.filter((name) => String(name).startsWith('qualitative') && !String(name).endsWith('reverse'))
25
- const sequential = palletNamesArr.filter((name) => String(name).startsWith('sequential') && !String(name).endsWith('reverse'))
26
- return { ...state, filteredPallets: sequential, filteredQualitative: qualitative, paletteName: paletteName, isPaletteReversed: false }
27
-
28
- case SEQUENTIAL_REVERSE:
29
- paletteName = palletNamesArr.find((name) => name === String(state.paletteName).concat('reverse') || name === String(state.paletteName).concat('-reverse'))
30
- const qualitativeReverse = palletNamesArr.filter((name) => String(name).startsWith('qualitative') && String(name).endsWith('reverse'))
31
- const sequentialReverse = palletNamesArr.filter((name) => String(name).startsWith('sequential') && String(name).endsWith('reverse'))
32
- return { ...state, filteredQualitative: qualitativeReverse, filteredPallets: sequentialReverse, paletteName: paletteName, isPaletteReversed: true }
33
- default:
34
- return state
35
- }
36
- }
4
+ export const useColorPalette = (config, updateConfig) => {
5
+ let twoColorPalettes = []
6
+ let sequential = []
7
+ let nonSequential = []
37
8
 
38
- function init(initialState) {
39
- return initialState
40
- }
9
+ // Get two color palettes if visualization type is Paired Bar
10
+ if (config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar') {
11
+ const isReversed = config.twoColor.isPaletteReversed
12
+ twoColorPalettes = Object.keys(twoColorPalette).filter(name => (isReversed ? name.endsWith('reverse') : !name.endsWith('reverse')))
13
+ } else {
14
+ // Get sequential and non-sequential palettes for other visualization types
15
+ const seqPalettes = []
16
+ const nonSeqPalettes = []
17
+
18
+ for (const paletteName in colorPalettesChart) {
19
+ const isSequential = paletteName.startsWith('sequential')
20
+ const isQualitative = paletteName.startsWith('qualitative')
21
+ const isReversed = paletteName.endsWith('reverse')
41
22
 
42
- export function useColorPalette(colorPalettes, configState) {
43
- const [state, dispatch] = useReducer(reducer, initialState, init)
44
- const { paletteName, isPaletteReversed, filteredPallets, filteredQualitative } = state
23
+ if (isSequential && ((!config.isPaletteReversed && !isReversed) || (config.isPaletteReversed && isReversed))) {
24
+ seqPalettes.push(paletteName)
25
+ }
26
+
27
+ if (isQualitative && ((!config.isPaletteReversed && !isReversed) || (config.isPaletteReversed && isReversed))) {
28
+ nonSeqPalettes.push(paletteName)
29
+ }
30
+ }
31
+
32
+ sequential = seqPalettes
33
+ nonSequential = nonSeqPalettes
34
+ }
45
35
 
36
+ // Update pairedBar.palette based on isPaletteReversed
46
37
  useEffect(() => {
47
- dispatch({ type: SEQUENTIAL, payload: colorPalettes })
48
- }, [])
38
+ let palette = ''
49
39
 
40
+ if (config.twoColor.isPaletteReversed && !config.twoColor.palette.endsWith('reverse')) {
41
+ palette = config.twoColor.palette + 'reverse'
42
+ }
43
+
44
+ if (!config.twoColor.isPaletteReversed && config.twoColor.palette.endsWith('reverse')) {
45
+ palette = config.twoColor.palette.slice(0, -7)
46
+ }
47
+
48
+ updateConfig({ ...config, twoColor: { ...config.twoColor, palette: palette } })
49
+ }, [config.twoColor.isPaletteReversed])
50
+
51
+ // Update palette based on isPaletteReversed
50
52
  useEffect(() => {
51
- if (configState.isPaletteReversed) {
52
- dispatch({ type: 'SEQUENTIAL_REVERSE', payload: colorPalettes })
53
- return () => dispatch({ type: 'SEQUENTIAL', payload: colorPalettes })
53
+ let palette = ''
54
+
55
+ if (config.isPaletteReversed && !config.palette.endsWith('reverse')) {
56
+ palette = config.palette + 'reverse'
57
+ }
58
+
59
+ if (!config.isPaletteReversed && config.palette.endsWith('reverse')) {
60
+ palette = config.palette.slice(0, -7)
54
61
  }
55
- }, [configState.isPaletteReversed, dispatch, colorPalettes])
56
62
 
57
- return { paletteName, isPaletteReversed, filteredPallets, filteredQualitative, dispatch }
63
+ updateConfig({ ...config, palette: palette })
64
+ }, [config.isPaletteReversed])
65
+
66
+ // Return all palettes
67
+ return { twoColorPalettes, sequential, nonSequential }
58
68
  }
@@ -9,7 +9,7 @@ function useReduceData(config, data) {
9
9
  // remove comma and $ signs
10
10
  let tmp
11
11
  if (typeof value === 'string') {
12
- tmp = value !== null && value !== '' ? value.replace(/[,\$]/g, '') : ''
12
+ tmp = value !== null && value !== '' ? value.replace(/[,$]/g, '') : ''
13
13
  } else {
14
14
  tmp = value !== null && value !== '' ? value : ''
15
15
  }
@@ -18,7 +18,6 @@ function useReduceData(config, data) {
18
18
  const getMaxValueFromData = () => {
19
19
  let max // will hold max number from data.
20
20
  if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && isBar)) && config.visualizationSubType === 'stacked') {
21
-
22
21
  const yTotals = data.reduce((allTotals, xValue) => {
23
22
  const totalYValues = config.runtime.seriesKeys.reduce((yTotal, k) => {
24
23
  yTotal += Number(xValue[k])
@@ -33,7 +32,7 @@ function useReduceData(config, data) {
33
32
  }, [])
34
33
 
35
34
  max = Math.max(...yTotals)
36
- } else if (config.visualizationType === 'Bar' && config.series && config.series.dataKey) {
35
+ } else if ((config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar') && config.series && config.series.dataKey) {
37
36
  max = Math.max(...data.map(d => (isNumber(d[config.series.dataKey]) ? Number(cleanChars(d[config.series.dataKey])) : 0)))
38
37
  //max = Math.max(...data.map(d => Number(d[config.series.dataKey])))
39
38
  } else if (config.visualizationType === 'Combo' && config.visualizationSubType === 'stacked' && !isBar) {
@@ -46,7 +45,7 @@ function useReduceData(config, data) {
46
45
  yTotal += Number(d[k])
47
46
  return yTotal
48
47
  }, 0)
49
- total.push(totalYValues)
48
+ return total.push(totalYValues)
50
49
  })
51
50
  // get lineSeries largest values
52
51
  const lineMax = Math.max(...data.map(d => Math.max(...config.runtime.lineSeriesKeys.map(key => Number(cleanChars(d[key]))))))
package/src/index.jsx CHANGED
@@ -6,11 +6,12 @@ import CdcChart from './CdcChart'
6
6
  import 'react-tooltip/dist/react-tooltip.css'
7
7
 
8
8
  let isEditor = window.location.href.includes('editor=true')
9
+ let isDebug = window.location.href.includes('debug=true')
9
10
 
10
11
  let domContainer = document.getElementsByClassName('react-container')[0]
11
12
 
12
13
  ReactDOM.createRoot(domContainer).render(
13
14
  <React.StrictMode>
14
- <CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} />
15
- </React.StrictMode>,
15
+ <CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} isDebug={isDebug} />
16
+ </React.StrictMode>
16
17
  )
@@ -75,11 +75,16 @@
75
75
  align-items: center;
76
76
  justify-content: space-between;
77
77
  font-size: 0.9em;
78
+ position: relative;
78
79
 
79
80
  &:hover {
80
81
  background-color: $lightestGray;
81
82
  }
82
83
 
84
+ div {
85
+ width: 100%;
86
+ }
87
+
83
88
  .series-list__name {
84
89
  position: relative;
85
90
  user-select: none;
@@ -132,7 +137,13 @@
132
137
  }
133
138
 
134
139
  .series-list__dropdown {
140
+ width: 100%;
141
+ display: block;
135
142
  font-size: 0.8em;
143
+ margin-bottom: 10px;
144
+ select {
145
+ width: 100%;
146
+ }
136
147
  }
137
148
 
138
149
  .series-list__remove {
@@ -140,6 +151,9 @@
140
151
  font-size: 1.125rem;
141
152
  color: #f00;
142
153
  cursor: pointer;
154
+ position: absolute;
155
+ top: 5px;
156
+ right: 5px;
143
157
  }
144
158
 
145
159
  + li {
@@ -150,6 +164,7 @@
150
164
 
151
165
  .series-list__name-text {
152
166
  max-width: 150px;
167
+ margin-bottom: 10px;
153
168
  white-space: nowrap;
154
169
  text-overflow: ellipsis;
155
170
  overflow: hidden;
@@ -419,6 +434,11 @@
419
434
  width: 33.3%;
420
435
  height: 100%;
421
436
  }
437
+
438
+ span.two-color {
439
+ width: 50%;
440
+ height: 100%;
441
+ }
422
442
  }
423
443
  }
424
444
 
@@ -231,9 +231,13 @@
231
231
  }
232
232
  }
233
233
 
234
+ .visx-tooltip {
235
+ z-index: 100000;
236
+ }
237
+
234
238
  .tooltip {
235
- border: rgba(0,0,0,.3) 1px solid;
236
- box-shadow: rgba(0,0,0,.1) 3px 3px 7px;
239
+ border: rgba(0, 0, 0, 0.3) 1px solid;
240
+ box-shadow: rgba(0, 0, 0, 0.1) 3px 3px 7px;
237
241
  opacity: 1;
238
242
  line-height: 1.4em;
239
243
  font-size: 1em;
@@ -242,8 +246,8 @@
242
246
  z-index: 1;
243
247
 
244
248
  .react-tooltip-arrow {
245
- border-bottom: rgba(0,0,0,.3) 1px solid;
246
- border-right: rgba(0,0,0,.3) 1px solid;
249
+ border-bottom: rgba(0, 0, 0, 0.3) 1px solid;
250
+ border-right: rgba(0, 0, 0, 0.3) 1px solid;
247
251
  backface-visibility: hidden;
248
252
  }
249
253
  }
@@ -337,8 +341,6 @@
337
341
  }
338
342
 
339
343
  &__wrapper {
340
- margin-bottom: 40px;
341
-
342
344
  hr {
343
345
  margin-bottom: 20px;
344
346
  }
@@ -0,0 +1,6 @@
1
+ // Placeholder test until we add them in.
2
+ describe('Chart', () => {
3
+ it('has a test.', async () => {
4
+ return true
5
+ })
6
+ })
@@ -1,5 +0,0 @@
1
- x,min,firstQuartile,median,thirdQuartile,max,outliers
2
- Group 1,-3.529439249291613,2.498913996933074,6,6.517816161082865,12.546169407307552,-4.222649440089676,-3.7995254226812145,12.669680018702707,,,,,,,,,,,,
3
- Group 2,-2.961278855962981,3.1553763355078277,5.31642875968012,7.233146463155033,13.349801654625843,-4.5962772985737645,14.42798091207488,14.832072412460995,,,,,,,,,,,,
4
- Group 3,-0.784362943544294,5.600233888319485,7.606034555385235,9.85663177622867,16.24122860809245,-2.0568265981730285,-2.036299998010181,-1.634595257757523,-0.9751707921193091,-0.9256799494292718,-0.813852054679872,16.255225428689318,19.221712546396496,,,,,,,
5
- Group 4,-3.4976011611041598,2.362493132101971,4.364242960871863,6.269222660906058,12.129316954112188,-5.912277243480174,-4.535668956980487,-4.255719319016919,-4.175200716132927,-4.1021204775116455,-3.7913796362224352,-3.6909919981778567,-3.6261129831962697,12.169135739844744,12.1724073804239,12.191268834215071,12.236896118210165,12.34513716605812,12.826785108558722,13.048968511771164