@cdc/chart 4.23.2 → 4.23.3

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 (48) hide show
  1. package/dist/cdcchart.js +41198 -39447
  2. package/examples/area-chart.json +187 -0
  3. package/examples/big-small-test-bar.json +328 -0
  4. package/examples/big-small-test-line.json +328 -0
  5. package/examples/big-small-test-negative.json +328 -0
  6. package/examples/box-plot.json +0 -1
  7. package/examples/example-bar-chart.json +4 -1
  8. package/examples/example-sparkline.json +76 -0
  9. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +31 -172
  10. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json +1 -0
  11. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +96 -14
  12. package/examples/gallery/line/line.json +1 -0
  13. package/examples/horizontal-chart-max-increase.json +38 -0
  14. package/examples/line-chart-max-increase.json +32 -0
  15. package/examples/line-chart-nonnumeric.json +5 -5
  16. package/examples/line-chart.json +6 -6
  17. package/examples/planet-deviation-config.json +168 -0
  18. package/examples/planet-deviation-data.json +38 -0
  19. package/examples/planet-example-config.json +139 -20
  20. package/examples/planet-example-data-max-increase.json +56 -0
  21. package/examples/planet-example-data.json +10 -10
  22. package/examples/scatterplot-continuous.csv +3 -3
  23. package/examples/scatterplot.json +2 -2
  24. package/examples/sparkline-chart-nonnumeric.json +3 -3
  25. package/index.html +26 -9
  26. package/package.json +6 -3
  27. package/src/CdcChart.jsx +146 -92
  28. package/src/components/AreaChart.jsx +198 -0
  29. package/src/components/BarChart.jsx +58 -34
  30. package/src/components/BoxPlot.jsx +28 -15
  31. package/src/components/DataTable.jsx +21 -17
  32. package/src/components/DeviationBar.jsx +191 -0
  33. package/src/components/EditorPanel.jsx +473 -168
  34. package/src/components/Filters.jsx +3 -2
  35. package/src/components/Legend.jsx +59 -46
  36. package/src/components/LineChart.jsx +3 -21
  37. package/src/components/LinearChart.jsx +158 -55
  38. package/src/components/PairedBarChart.jsx +0 -1
  39. package/src/components/PieChart.jsx +11 -14
  40. package/src/components/ScatterPlot.jsx +19 -16
  41. package/src/components/SparkLine.jsx +87 -85
  42. package/src/components/useIntersectionObserver.jsx +1 -1
  43. package/src/data/initial-state.js +20 -4
  44. package/src/hooks/useColorPalette.js +58 -48
  45. package/src/hooks/useReduceData.js +3 -4
  46. package/src/index.jsx +1 -1
  47. package/src/scss/editor-panel.scss +5 -0
  48. package/src/test/CdcChart.test.jsx +6 -0
@@ -3,39 +3,42 @@ import ConfigContext from '../ConfigContext'
3
3
  import { Group } from '@visx/group'
4
4
 
5
5
  const CoveScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
6
- const { colorScale, transformedData: data, config } = useContext(ConfigContext)
6
+ const { colorScale, transformedData: data, config, formatNumber, seriesHighlight, colorPalettes } = useContext(ConfigContext)
7
7
 
8
- // copied from line chart
9
- // should probably be a constant somewhere.
8
+ // TODO: copied from line chart should probably be a constant somewhere.
10
9
  let circleRadii = 4.5
11
-
12
- let pointStyles = {
13
- filter: 'unset',
14
- opacity: 1,
15
- stroke: 'black'
16
- }
10
+ const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
17
11
 
18
12
  const handleTooltip = (item, s) => `<div>
19
- ${config.xAxis.label}: ${item[config.xAxis.dataKey]} <br/>
20
- ${config.yAxis.label}: ${item[s]}
13
+ ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[s] || ''}<br/>` : ''}
14
+ ${config.xAxis.label}: ${formatNumber(item[config.xAxis.dataKey], 'bottom')} <br/>
15
+ ${config.yAxis.label}: ${formatNumber(item[s], 'left')}
21
16
  </div>`
22
17
 
23
18
  return (
24
19
  <Group className='scatter-plot' left={config.yAxis.size}>
25
20
  {data.map((item, dataIndex) => {
26
21
  // prettier-ignore
27
- return config.runtime.seriesKeys.map(s => {
22
+ return config.runtime.seriesKeys.map((s, index) => {
23
+ const transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(s) === -1
24
+ const displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(s) !== -1
25
+ const seriesColor = config.palette ? colorPalettes[config.palette][index] : '#000'
26
+
27
+ let pointStyles = {
28
+ filter: 'unset',
29
+ opacity: 1,
30
+ stroke: displayArea ? 'black' : ''
31
+ }
28
32
 
29
33
  return (
30
34
  <circle
31
- key={`${dataIndex}`}
35
+ key={`${dataIndex}-${index}`}
32
36
  r={circleRadii}
33
37
  cx={xScale(item[config.xAxis.dataKey])}
34
38
  cy={yScale(item[s])}
35
- fillOpacity={1}
36
- opacity={1}
39
+ fill={displayArea ? seriesColor : 'transparent'}
40
+ fillOpacity={transparentArea ? .25 : 1}
37
41
  style={pointStyles}
38
- fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s] : s) : '#000'}
39
42
  data-tooltip-html={handleTooltip(item, s)}
40
43
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
41
44
  />
@@ -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,13 +35,12 @@ 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
38
  // REMOVE bad data points from the data set
40
39
  // Examples: NA, N/A, "1,234", "anystring"
41
40
  // - if you dont call this on data into LineGroup below, for example
42
41
  // then entire data series are removed because of the defined statement
43
42
  // i.e. if a series has any bad data points the entire series wont plot
44
- const cleanData = (data, testing = false) => {
43
+ const cleanData = (data, testing = false) => {
45
44
  let cleanedup = []
46
45
  if (testing) console.log('## Data to clean=', data)
47
46
  data.forEach(function (d, i) {
@@ -52,8 +51,8 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
52
51
  cleanedSeries[key] = d[key]
53
52
  } else {
54
53
  // 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)
54
+ let tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,$]/g, '') : ''
55
+ if (testing) console.log('tmp no comma or $', tmp)
57
56
  if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
58
57
  cleanedSeries[key] = tmp
59
58
  } else {
@@ -66,11 +65,11 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
66
65
  })
67
66
  if (testing) console.log('## cleanedData =', cleanedup)
68
67
  return cleanedup
69
- }
68
+ }
70
69
 
71
70
  // Just do this once up front otherwise we end up
72
71
  // calling clean several times on same set of data (TT)
73
- const cleanedData = cleanData(data, config.xAxis.dataKey);
72
+ const cleanedData = cleanData(data, config.xAxis.dataKey)
74
73
 
75
74
  if (cleanedData) {
76
75
  let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
@@ -116,6 +115,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
116
115
  range: [margin.left, width - margin.right]
117
116
  })
118
117
 
118
+ // eslint-disable-next-line
119
119
  seriesScale = scalePoint({
120
120
  domain: config.runtime.barSeriesKeys || config.runtime.seriesKeys,
121
121
  range: [0, xMax]
@@ -128,88 +128,90 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
128
128
  return (
129
129
  <ErrorBoundary component='SparkLine'>
130
130
  <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>
131
+ {config.runtime.lineSeriesKeys.length > 0
132
+ ? config.runtime.lineSeriesKeys
133
+ : config.runtime.seriesKeys.map((seriesKey, index) => (
134
+ <>
135
+ <Group
136
+ className='sparkline-group'
137
+ height={parentHeight}
138
+ style={{ height: parentHeight }}
139
+ top={margin.top}
140
+ key={`series-${seriesKey}`}
141
+ opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
142
+ display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
143
+ >
144
+ {cleanedData.map((d, dataIndex) => {
145
+ let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
146
+ let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
147
+
148
+ const tooltip = `<div>
147
149
  ${yAxisTooltip}<br />
148
150
  ${xAxisTooltip}<br />
149
151
  ${config.seriesLabel ? `${config.seriesLabel}: ${seriesKey}` : ''}
150
152
  </div>`
151
153
 
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
- ))}
154
+ let circleRadii = 4.5
155
+ return (
156
+ <Group key={`series-${seriesKey}-point-${dataIndex}`}>
157
+ <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'>
158
+ {formatNumber(d[seriesKey])}
159
+ </Text>
160
+
161
+ {dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
162
+ <circle
163
+ key={`${seriesKey}-${dataIndex}`}
164
+ r={circleRadii}
165
+ cx={xScale(getXAxisData(d))}
166
+ cy={yScale(getYAxisData(d, seriesKey))}
167
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
168
+ style={{ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000' }}
169
+ data-tooltip-html={tooltip}
170
+ data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
171
+ />
172
+ )}
173
+ </Group>
174
+ )
175
+ })}
176
+ <LinePath
177
+ curve={allCurves.curveLinear}
178
+ data={cleanedData}
179
+ x={d => xScale(getXAxisData(d))}
180
+ y={d => yScale(getYAxisData(d, seriesKey))}
181
+ stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
182
+ strokeWidth={2}
183
+ strokeOpacity={1}
184
+ shapeRendering='geometricPrecision'
185
+ markerEnd={`url(#${'arrow'}--${index})`}
186
+ />
187
+ <MarkerArrow
188
+ id={`arrow--${index}`}
189
+ refX={2}
190
+ size={6}
191
+ markerEnd={`url(#${'arrow'}--${index})`}
192
+ strokeOpacity={1}
193
+ fillOpacity={1}
194
+ // stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
195
+ fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
196
+ />
197
+ </Group>
198
+ <AxisBottom
199
+ top={yMax + margin.top}
200
+ hideAxisLine
201
+ hideTicks
202
+ scale={xScale}
203
+ tickValues={handleSparkLineTicks}
204
+ tickFormat={formatDate}
205
+ stroke={'black'}
206
+ tickStroke={'black'}
207
+ tickLabelProps={() => ({
208
+ fill: 'black',
209
+ fontSize: 11,
210
+ textAnchor: 'middle'
211
+ })}
212
+ />
213
+ </>
214
+ ))}
213
215
  </svg>
214
216
  </ErrorBoundary>
215
217
  )
@@ -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,7 +65,7 @@ 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
70
  values: 'Values'
69
71
  }
@@ -81,6 +83,8 @@ export default {
81
83
  },
82
84
  xAxis: {
83
85
  type: 'categorical',
86
+ showTargetLabel: true,
87
+ targetLabel: 'Target',
84
88
  hideAxis: false,
85
89
  hideLabel: false,
86
90
  hideTicks: false,
@@ -93,7 +97,8 @@ export default {
93
97
  tickColor: '#333',
94
98
  numTicks: '',
95
99
  labelOffset: 65,
96
- axisPadding: 0
100
+ axisPadding: 0,
101
+ target: 0
97
102
  },
98
103
  table: {
99
104
  label: 'Data Table',
@@ -106,7 +111,6 @@ export default {
106
111
  orientation: 'vertical',
107
112
  legend: {
108
113
  behavior: 'isolate',
109
- position: 'right',
110
114
  singleRow: false,
111
115
  colorCode: '',
112
116
  reverseLabelOrder: false,
@@ -123,8 +127,20 @@ export default {
123
127
  },
124
128
  palette: 'qualitative-bold',
125
129
  isPaletteReversed: false,
130
+ twoColor: {
131
+ palette: 'monochrome-1',
132
+ isPaletteReversed: false
133
+ },
126
134
  labels: false,
127
- dataFormat: { commas: false, prefix: '', suffix: '', abbreviated: false },
135
+ dataFormat: {
136
+ commas: false,
137
+ prefix: '',
138
+ suffix: '',
139
+ abbreviated: false,
140
+ bottomSuffix: '',
141
+ bottomPrefix: '',
142
+ bottomAbbreviated: false
143
+ },
128
144
  confidenceKeys: {},
129
145
  visual: {
130
146
  border: true,
@@ -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
@@ -12,5 +12,5 @@ let domContainer = document.getElementsByClassName('react-container')[0]
12
12
  ReactDOM.createRoot(domContainer).render(
13
13
  <React.StrictMode>
14
14
  <CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} />
15
- </React.StrictMode>,
15
+ </React.StrictMode>
16
16
  )
@@ -419,6 +419,11 @@
419
419
  width: 33.3%;
420
420
  height: 100%;
421
421
  }
422
+
423
+ span.two-color {
424
+ width: 50%;
425
+ height: 100%;
426
+ }
422
427
  }
423
428
  }
424
429
 
@@ -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
+ })