@cdc/chart 4.22.10 → 4.22.11

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 (72) hide show
  1. package/README.md +5 -5
  2. package/dist/cdcchart.js +4 -4
  3. package/examples/age-adjusted-rates.json +1486 -1218
  4. package/examples/case-rate-example-config.json +1 -1
  5. package/examples/covid-confidence-example-config.json +33 -33
  6. package/examples/covid-example-config.json +34 -34
  7. package/examples/covid-example-data-confidence.json +30 -30
  8. package/examples/covid-example-data.json +20 -20
  9. package/examples/cutoff-example-config.json +36 -36
  10. package/examples/cutoff-example-data.json +36 -36
  11. package/examples/date-exclusions-config.json +1 -1
  12. package/examples/dynamic-legends.json +124 -124
  13. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +191 -197
  14. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +230 -240
  15. package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +239 -247
  16. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +136 -136
  17. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +79 -79
  18. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +80 -80
  19. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +67 -67
  20. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +110 -110
  21. package/examples/gallery/lollipop/lollipop-style-horizontal.json +215 -219
  22. package/examples/gallery/paired-bar/paired-bar-chart.json +195 -195
  23. package/examples/horizontal-chart.json +35 -35
  24. package/examples/horizontal-stacked-bar-chart.json +34 -34
  25. package/examples/line-chart.json +75 -75
  26. package/examples/paired-bar-data.json +16 -14
  27. package/examples/paired-bar-example.json +48 -48
  28. package/examples/paired-bar-formatted.json +36 -36
  29. package/examples/planet-chart-horizontal-example-config.json +33 -33
  30. package/examples/planet-combo-example-config.json +34 -31
  31. package/examples/planet-example-config.json +35 -33
  32. package/examples/planet-example-data.json +56 -56
  33. package/examples/planet-pie-example-config.json +28 -28
  34. package/examples/private/filters.json +170 -0
  35. package/examples/private/line-test-data.json +21 -21
  36. package/examples/private/line-test-two.json +209 -215
  37. package/examples/private/line-test.json +101 -101
  38. package/examples/private/new.json +48800 -0
  39. package/examples/private/shawn.json +1105 -1295
  40. package/examples/private/test.json +10123 -10123
  41. package/examples/private/yaxis-test.json +4 -3
  42. package/examples/private/yaxis.json +26 -26
  43. package/examples/stacked-vertical-bar-example.json +1 -1
  44. package/examples/temp-example-config.json +61 -54
  45. package/examples/temp-example-data.json +1 -1
  46. package/package.json +2 -2
  47. package/src/CdcChart.tsx +339 -380
  48. package/src/components/BarChart.tsx +425 -469
  49. package/src/components/DataTable.tsx +164 -195
  50. package/src/components/EditorPanel.js +1009 -710
  51. package/src/components/Legend.js +279 -329
  52. package/src/components/LineChart.tsx +90 -79
  53. package/src/components/LinearChart.tsx +376 -434
  54. package/src/components/PairedBarChart.tsx +197 -213
  55. package/src/components/PieChart.tsx +95 -151
  56. package/src/components/SparkLine.js +179 -201
  57. package/src/components/useIntersectionObserver.tsx +17 -20
  58. package/src/context.tsx +3 -3
  59. package/src/data/initial-state.js +37 -16
  60. package/src/hooks/useActiveElement.js +13 -13
  61. package/src/hooks/useChartClasses.js +34 -28
  62. package/src/hooks/useColorPalette.ts +56 -63
  63. package/src/hooks/useLegendClasses.js +18 -10
  64. package/src/hooks/useReduceData.ts +62 -78
  65. package/src/hooks/useRightAxis.js +25 -0
  66. package/src/hooks/useTopAxis.js +6 -0
  67. package/src/index.html +45 -45
  68. package/src/index.tsx +13 -16
  69. package/src/scss/DataTable.scss +5 -4
  70. package/src/scss/editor-panel.scss +71 -69
  71. package/src/scss/main.scss +157 -114
  72. package/src/scss/variables.scss +1 -1
@@ -1,46 +1,44 @@
1
1
  import React, { useState, useEffect, useCallback, memo, useContext } from 'react'
2
2
  import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'
3
3
 
4
- import {
5
- Accordion,
6
- AccordionItem,
7
- AccordionItemHeading,
8
- AccordionItemPanel,
9
- AccordionItemButton,
10
- } from 'react-accessible-accordion'
4
+ import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
11
5
 
12
6
  import { timeParse, timeFormat } from 'd3-time-format'
13
7
  import { useDebounce, useDebouncedCallback } from 'use-debounce'
14
8
 
15
9
  import Context from '../context'
16
10
  import WarningImage from '../images/warning.svg'
17
- import AdvancedEditor from '@cdc/core/components/AdvancedEditor';
11
+ import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
18
12
 
19
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
20
- import Waiting from '@cdc/core/components/Waiting';
21
- import QuestionIcon from '@cdc/core/assets/icon-question-circle.svg'; //TODO: Update with Icon component
22
- import {useColorPalette} from '../hooks/useColorPalette';
13
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
14
+ import Waiting from '@cdc/core/components/Waiting'
15
+ import QuestionIcon from '@cdc/core/assets/icon-question-circle.svg' //TODO: Update with Icon component
16
+ import { useColorPalette } from '../hooks/useColorPalette'
23
17
 
24
- import InputCheckbox from '@cdc/core/components/inputs/InputCheckbox';
25
- import InputToggle from '@cdc/core/components/inputs/InputToggle';
18
+ import InputCheckbox from '@cdc/core/components/inputs/InputCheckbox'
19
+ import InputToggle from '@cdc/core/components/inputs/InputToggle'
26
20
  import Tooltip from '@cdc/core/components/ui/Tooltip'
27
21
  import Icon from '@cdc/core/components/ui/Icon'
28
- import useReduceData from '../hooks/useReduceData';
22
+ import useReduceData from '../hooks/useReduceData'
23
+ import useRightAxis from '../hooks/useRightAxis'
29
24
 
30
- const TextField = memo(({label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = "input", i = null, min = null, ...attributes}) => {
31
- const [ value, setValue ] = useState(stateValue);
25
+ // TODO: Remove unused imports
26
+ // TDOO: Move inline styles to a scss file
32
27
 
33
- const [ debouncedValue ] = useDebounce(value, 500);
28
+ const TextField = memo(({ label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'input', i = null, min = null, ...attributes }) => {
29
+ const [value, setValue] = useState(stateValue)
30
+
31
+ const [debouncedValue] = useDebounce(value, 500)
34
32
 
35
33
  useEffect(() => {
36
34
  if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
37
35
  updateField(section, subsection, fieldName, debouncedValue, i)
38
36
  }
39
- }, [ debouncedValue ])
37
+ }, [debouncedValue])
40
38
 
41
39
  let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
42
40
 
43
- const onChange = (e) => {
41
+ const onChange = e => {
44
42
  if ('number' !== type || min === null) {
45
43
  setValue(e.target.value)
46
44
  } else {
@@ -52,52 +50,79 @@ const TextField = memo(({label, tooltip, section = null, subsection = null, fiel
52
50
  }
53
51
  }
54
52
 
55
- let formElement = <input type="text" name={name} onChange={onChange} {...attributes} value={value}/>
53
+ let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
56
54
 
57
55
  if ('textarea' === type) {
58
- formElement = (
59
- <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
60
- )
56
+ formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
61
57
  }
62
58
 
63
59
  if ('number' === type) {
64
- formElement = <input type="number" name={name} onChange={onChange} {...attributes} value={value}/>
60
+ formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
65
61
  }
66
62
 
67
63
  if ('date' === type) {
68
- formElement = <input type="date" name={name} onChange={onChange} {...attributes} value={value}/>
64
+ formElement = <input type='date' name={name} onChange={onChange} {...attributes} value={value} />
69
65
  }
70
66
 
71
67
  return (
72
68
  <label>
73
- <span className="edit-label column-heading">{label}{tooltip}</span>
69
+ <span className='edit-label column-heading'>
70
+ {label}
71
+ {tooltip}
72
+ </span>
74
73
  {formElement}
75
74
  </label>
76
75
  )
77
76
  })
78
77
 
79
78
  const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
80
- <label className="checkbox">
81
- <input type="checkbox" name={fieldName} checked={value} onChange={() => {
82
- updateField(section, subsection, fieldName, !value)
83
- }} {...attributes}/>
84
- <span className="edit-label">{label}{tooltip}</span>
79
+ <label className='checkbox'>
80
+ <input
81
+ type='checkbox'
82
+ name={fieldName}
83
+ checked={value}
84
+ onChange={() => {
85
+ updateField(section, subsection, fieldName, !value)
86
+ }}
87
+ {...attributes}
88
+ />
89
+ <span className='edit-label'>
90
+ {label}
91
+ {tooltip}
92
+ </span>
85
93
  </label>
86
94
  ))
87
95
 
88
96
  const Select = memo(({ label, value, options, fieldName, section = null, subsection = null, required = false, tooltip, updateField, initial: initialValue, ...attributes }) => {
89
- let optionsJsx = options.map((optionName, index) => <option value={optionName} key={index}>{optionName}</option>)
97
+ let optionsJsx = options.map((optionName, index) => (
98
+ <option value={optionName} key={index}>
99
+ {optionName}
100
+ </option>
101
+ ))
90
102
 
91
103
  if (initialValue) {
92
- optionsJsx.unshift(<option value="" key="initial">{initialValue}</option>)
104
+ optionsJsx.unshift(
105
+ <option value='' key='initial'>
106
+ {initialValue}
107
+ </option>
108
+ )
93
109
  }
94
110
 
95
111
  return (
96
112
  <label>
97
- <span className="edit-label">{label}{tooltip}</span>
98
- <select className={required && !value ? 'warning' : ''} name={fieldName} value={value} onChange={(event) => {
99
- updateField(section, subsection, fieldName, event.target.value)
100
- }} {...attributes}>
113
+ <span className='edit-label'>
114
+ {label}
115
+ {tooltip}
116
+ </span>
117
+ <select
118
+ className={required && !value ? 'warning' : ''}
119
+ name={fieldName}
120
+ value={value}
121
+ onChange={event => {
122
+ updateField(section, subsection, fieldName, event.target.value)
123
+ }}
124
+ {...attributes}
125
+ >
101
126
  {optionsJsx}
102
127
  </select>
103
128
  </label>
@@ -109,7 +134,7 @@ const Regions = memo(({ config, updateConfig }) => {
109
134
  let regions = []
110
135
 
111
136
  if (config.regions) {
112
- regions = [ ...config.regions ]
137
+ regions = [...config.regions]
113
138
  }
114
139
 
115
140
  regions[i][fieldName] = value
@@ -118,11 +143,11 @@ const Regions = memo(({ config, updateConfig }) => {
118
143
 
119
144
  let updateField = (section, subsection, fieldName, value, i) => regionUpdate(fieldName, value, i)
120
145
 
121
- let removeColumn = (i) => {
146
+ let removeColumn = i => {
122
147
  let regions = []
123
148
 
124
149
  if (config.regions) {
125
- regions = [ ...config.regions ]
150
+ regions = [...config.regions]
126
151
  }
127
152
 
128
153
  regions.splice(i, 1)
@@ -131,11 +156,10 @@ const Regions = memo(({ config, updateConfig }) => {
131
156
  }
132
157
 
133
158
  let addColumn = () => {
134
-
135
159
  let regions = []
136
160
 
137
161
  if (config.regions) {
138
- regions = [ ...config.regions ]
162
+ regions = [...config.regions]
139
163
  }
140
164
 
141
165
  regions.push({})
@@ -145,63 +169,83 @@ const Regions = memo(({ config, updateConfig }) => {
145
169
 
146
170
  return (
147
171
  <>
148
- {config.regions && config.regions.map(({ label, color, from, to, background }, i) => (
149
- <div className="edit-block" key={`region-${i}`}>
150
- <button type="button" className="remove-column" onClick={(event) => {
151
- event.preventDefault()
152
- removeColumn(i)
153
- }}>Remove
154
- </button>
155
- <TextField value={label} label="Region Label" fieldName="label" i={i} updateField={updateField}/>
156
- <div className="two-col-inputs">
157
- <TextField value={color} label="Text Color" fieldName="color" updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}/>
158
- <TextField value={background} label="Background" fieldName="background" updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}/>
172
+ {config.regions &&
173
+ config.regions.map(({ label, color, from, to, background }, i) => (
174
+ <div className='edit-block' key={`region-${i}`}>
175
+ <button
176
+ type='button'
177
+ className='remove-column'
178
+ onClick={event => {
179
+ event.preventDefault()
180
+ removeColumn(i)
181
+ }}
182
+ >
183
+ Remove
184
+ </button>
185
+ <TextField value={label} label='Region Label' fieldName='label' i={i} updateField={updateField} />
186
+ <div className='two-col-inputs'>
187
+ <TextField value={color} label='Text Color' fieldName='color' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
188
+ <TextField value={background} label='Background' fieldName='background' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
189
+ </div>
190
+ <div className='two-col-inputs'>
191
+ <TextField value={from} label='From Value' fieldName='from' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
192
+ <TextField value={to} label='To Value' fieldName='to' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
193
+ </div>
159
194
  </div>
160
- <div className="two-col-inputs">
161
- <TextField value={from} label="From Value" fieldName="from" updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}/>
162
- <TextField value={to} label="To Value" fieldName="to" updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}/>
163
- </div>
164
- </div>
165
- ))}
195
+ ))}
166
196
  {!config.regions && <p style={{ textAlign: 'center' }}>There are currently no regions.</p>}
167
- <button type="button" className="btn full-width" onClick={(e) => {
168
- e.preventDefault()
169
- addColumn()
170
- }}>Add Region
197
+ <button
198
+ type='button'
199
+ className='btn full-width'
200
+ onClick={e => {
201
+ e.preventDefault()
202
+ addColumn()
203
+ }}
204
+ >
205
+ Add Region
171
206
  </button>
172
207
  </>
173
208
  )
174
209
  })
175
210
 
176
- const headerColors = [ 'theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber' ]
211
+ const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
177
212
 
178
213
  const EditorPanel = () => {
179
- const {
180
- config,
181
- updateConfig,
182
- transformedData: data,
183
- loading,
184
- colorPalettes,
185
- unfilteredData,
186
- excludedData,
187
- transformedData,
188
- isDashboard,
189
- setParentConfig,
190
- missingRequiredSections,
191
- setFilteredData
192
- } = useContext(Context)
193
-
194
- const {minValue,maxValue,existPositiveValue} = useReduceData(config,unfilteredData);
195
- const {paletteName,isPaletteReversed,filteredPallets,filteredQualitative,dispatch} = useColorPalette(colorPalettes,config);
196
- useEffect(()=>{
197
- if(paletteName) updateConfig({...config, palette:paletteName})
214
+ const { config, updateConfig, transformedData: data, loading, colorPalettes, unfilteredData, excludedData, transformedData, isDashboard, setParentConfig, missingRequiredSections, setFilteredData } = useContext(Context)
215
+
216
+ const { minValue, maxValue, existPositiveValue } = useReduceData(config, unfilteredData)
217
+ const { paletteName, isPaletteReversed, filteredPallets, filteredQualitative, dispatch } = useColorPalette(colorPalettes, config)
218
+ useEffect(() => {
219
+ if (paletteName) updateConfig({ ...config, palette: paletteName })
198
220
  }, [paletteName])
199
221
 
222
+ useEffect(() => {
223
+ dispatch({ type: 'GET_PALETTE', payload: colorPalettes, paletteName: config.palette })
224
+ }, [dispatch, config.palette])
225
+
226
+ // when the visualization type changes we
227
+ // have to update the individual series type & axis details
228
+ // dataKey is unchanged here.
229
+ // ie. { dataKey: 'series_name', type: 'Bar', axis: 'Left'}
230
+ useEffect(() => {
231
+ let newSeries = []
232
+ if (config.series) {
233
+ newSeries = config.series.map(series => {
234
+ return {
235
+ ...series,
236
+ type: config.visualizationType === 'Combo' ? 'Bar' : config.visualizationType ? config.visualizationType : 'Bar',
237
+ axis: 'Left'
238
+ }
239
+ })
240
+ }
241
+
242
+ updateConfig({
243
+ ...config,
244
+ series: newSeries
245
+ })
246
+ }, [config.visualizationType])
200
247
 
201
-
202
- useEffect(()=>{
203
- dispatch({type:"GET_PALETTE",payload:colorPalettes,paletteName:config.palette})
204
- }, [dispatch, config.palette]);
248
+ const { hasRightAxis } = useRightAxis({ config: config, yMax: config.yAxis.size, data: config.data, updateConfig })
205
249
 
206
250
  const filterOptions = [
207
251
  {
@@ -219,7 +263,7 @@ const EditorPanel = () => {
219
263
  ]
220
264
 
221
265
  const getItemStyle = (isDragging, draggableStyle) => ({
222
- ...draggableStyle,
266
+ ...draggableStyle
223
267
  })
224
268
 
225
269
  const sortableItemStyles = {
@@ -235,10 +279,10 @@ const EditorPanel = () => {
235
279
  marginRight: '.3em',
236
280
  marginBottom: '.3em',
237
281
  cursor: 'move',
238
- zIndex: '999',
282
+ zIndex: '999'
239
283
  }
240
284
 
241
- const enforceRestrictions = (updatedConfig) => {
285
+ const enforceRestrictions = updatedConfig => {
242
286
  if (updatedConfig.orientation === 'horizontal') {
243
287
  updatedConfig.labels = false
244
288
  }
@@ -260,11 +304,11 @@ const EditorPanel = () => {
260
304
 
261
305
  const isArray = Array.isArray(config[section])
262
306
 
263
- let sectionValue = isArray ? [ ...config[section], newValue ] : { ...config[section], [fieldName]: newValue }
307
+ let sectionValue = isArray ? [...config[section], newValue] : { ...config[section], [fieldName]: newValue }
264
308
 
265
309
  if (null !== subsection) {
266
310
  if (isArray) {
267
- sectionValue = [ ...config[section] ]
311
+ sectionValue = [...config[section]]
268
312
  sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue }
269
313
  } else if (typeof newValue === 'string') {
270
314
  sectionValue[subsection] = newValue
@@ -280,22 +324,22 @@ const EditorPanel = () => {
280
324
  updateConfig(updatedConfig)
281
325
  }
282
326
 
283
- const [ displayPanel, setDisplayPanel ] = useState(true)
284
- const [ lollipopColorStyle, setLollipopColorStyle ] = useState('two-tone')
327
+ const [displayPanel, setDisplayPanel] = useState(true)
328
+ const [lollipopColorStyle, setLollipopColorStyle] = useState('two-tone')
285
329
 
286
330
  if (loading) {
287
331
  return null
288
332
  }
289
333
 
290
- const setLollipopShape = (shape) => {
334
+ const setLollipopShape = shape => {
291
335
  updateConfig({
292
336
  ...config,
293
337
  lollipopShape: shape
294
338
  })
295
339
  }
296
340
 
297
- const removeFilter = (index) => {
298
- let filters = [ ...config.filters ]
341
+ const removeFilter = index => {
342
+ let filters = [...config.filters]
299
343
 
300
344
  filters.splice(index, 1)
301
345
 
@@ -303,7 +347,7 @@ const EditorPanel = () => {
303
347
  }
304
348
 
305
349
  const updateFilterProp = (name, index, value) => {
306
- let filters = [ ...config.filters ]
350
+ let filters = [...config.filters]
307
351
 
308
352
  filters[index][name] = value
309
353
 
@@ -311,29 +355,28 @@ const EditorPanel = () => {
311
355
  }
312
356
 
313
357
  const addNewFilter = () => {
314
- let filters = config.filters ? [ ...config.filters ] : []
358
+ let filters = config.filters ? [...config.filters] : []
315
359
 
316
- filters.push({ values: [] });
360
+ filters.push({ values: [] })
317
361
 
318
- updateConfig({ ...config, filters });
362
+ updateConfig({ ...config, filters })
319
363
  }
320
364
 
321
- const addNewSeries = (seriesKey) => {
322
- let newSeries = config.series ? [ ...config.series ] : []
323
- newSeries.push({ dataKey: seriesKey, type: 'Bar' });
324
- updateConfig({ ...config, series: newSeries });
365
+ const addNewSeries = seriesKey => {
366
+ let newSeries = config.series ? [...config.series] : []
367
+ newSeries.push({ dataKey: seriesKey, type: 'Bar' })
368
+ updateConfig({ ...config, series: newSeries }) // left axis series keys
325
369
  }
326
370
 
327
- const sortSeries = (e) => {
371
+ const sortSeries = e => {
328
372
  const series = config.series[0].dataKey
329
- const sorted = data.sort((a, b) => a[series] - b[series]);
330
- const newData = e === "asc" ? sorted : sorted.reverse();
331
- updateConfig({ ...config }, newData);
373
+ const sorted = data.sort((a, b) => a[series] - b[series])
374
+ const newData = e === 'asc' ? sorted : sorted.reverse()
375
+ updateConfig({ ...config }, newData)
332
376
  }
333
377
 
334
- const removeSeries = (seriesKey) => {
335
-
336
- let series = [ ...config.series ]
378
+ const removeSeries = seriesKey => {
379
+ let series = [...config.series]
337
380
  let seriesIndex = -1
338
381
 
339
382
  for (let i = 0; i < series.length; i++) {
@@ -363,17 +406,17 @@ const EditorPanel = () => {
363
406
  }
364
407
  }
365
408
 
366
- const addNewExclusion = (exclusionKey) => {
367
- let newExclusion = [ ...config.exclusions.keys ]
409
+ const addNewExclusion = exclusionKey => {
410
+ let newExclusion = [...config.exclusions.keys]
368
411
  newExclusion.push(exclusionKey)
369
412
 
370
413
  let payload = { ...config.exclusions, keys: newExclusion }
371
414
  updateConfig({ ...config, exclusions: payload })
372
415
  }
373
416
 
374
- const removeExclusion = (excludeValue) => {
417
+ const removeExclusion = excludeValue => {
375
418
  let exclusionsIndex = -1
376
- let exclusions = [ ...config.exclusions.keys ]
419
+ let exclusions = [...config.exclusions.keys]
377
420
 
378
421
  for (let i = 0; i < exclusions.length; i++) {
379
422
  if (exclusions[i] === excludeValue) {
@@ -400,12 +443,12 @@ const EditorPanel = () => {
400
443
  let columns = {}
401
444
 
402
445
  unfilteredData.map(row => {
403
- Object.keys(row).forEach(columnName => columns[columnName] = true)
446
+ Object.keys(row).forEach(columnName => (columns[columnName] = true))
404
447
  })
405
448
 
406
449
  if (filter) {
407
- let confidenceUpper = config.confidenceKeys?.upper && config.confidenceKeys?.upper !== ''
408
- let confidenceLower = config.confidenceKeys?.lower && config.confidenceKeys?.lower !== ''
450
+ let confidenceUpper = config.confidenceKeys?.upper && config.confidenceKeys?.upper !== '' // TODO: remove?
451
+ let confidenceLower = config.confidenceKeys?.lower && config.confidenceKeys?.lower !== '' // TODO: remove?
409
452
 
410
453
  Object.keys(columns).forEach(key => {
411
454
  if (
@@ -424,32 +467,30 @@ const EditorPanel = () => {
424
467
  return Object.keys(columns)
425
468
  }
426
469
 
427
-
428
- const getDataValueOptions = (data)=>{
429
- if(!data) return [];
430
- const set = new Set();
431
- for (let i=0; i<data.length; i++){
432
- for (const [key, value] of Object.entries(data[i])) {
433
- set.add(key)
434
- }
435
-
470
+ const getDataValueOptions = data => {
471
+ if (!data) return []
472
+ const set = new Set()
473
+ for (let i = 0; i < data.length; i++) {
474
+ for (const [key, value] of Object.entries(data[i])) {
475
+ set.add(key)
436
476
  }
437
- return Array.from(set)
438
- };
439
-
477
+ }
478
+ return Array.from(set)
479
+ }
480
+
440
481
  const getDataValues = (dataKey, unique = false) => {
441
482
  let values = []
442
483
  excludedData.map(e => {
443
484
  values.push(e[dataKey])
444
485
  })
445
- return unique ? [ ...new Set(values) ] : values
486
+ return unique ? [...new Set(values)] : values
446
487
  }
447
488
 
448
- const showBarStyleOptions = ()=>{
449
- if (config.visualizationType === 'Bar' && config.visualizationSubType !== 'stacked' && (config.orientation==='horizontal' || config.orientation==='vertical') ) {
450
- return ['flat','rounded','lollipop']
489
+ const showBarStyleOptions = () => {
490
+ if (config.visualizationType === 'Bar' && config.visualizationSubType !== 'stacked' && (config.orientation === 'horizontal' || config.orientation === 'vertical')) {
491
+ return ['flat', 'rounded', 'lollipop']
451
492
  } else {
452
- return ['flat','rounded']
493
+ return ['flat', 'rounded']
453
494
  }
454
495
  }
455
496
 
@@ -459,18 +500,17 @@ const EditorPanel = () => {
459
500
 
460
501
  const Error = () => {
461
502
  return (
462
- <section className="waiting">
463
- <section className="waiting-container">
503
+ <section className='waiting'>
504
+ <section className='waiting-container'>
464
505
  <h3>Error With Configuration</h3>
465
506
  <p>{config.runtime.editorErrorMessage}</p>
466
507
  </section>
467
508
  </section>
468
509
  )
469
-
470
510
  }
471
511
 
472
512
  const Confirm = () => {
473
- const confirmDone = (e) => {
513
+ const confirmDone = e => {
474
514
  e.preventDefault()
475
515
 
476
516
  let newConfig = { ...config }
@@ -480,11 +520,13 @@ const EditorPanel = () => {
480
520
  }
481
521
 
482
522
  return (
483
- <section className="waiting">
484
- <section className="waiting-container">
523
+ <section className='waiting'>
524
+ <section className='waiting-container'>
485
525
  <h3>Finish Configuring</h3>
486
526
  <p>Set all required options to the left and confirm below to display a preview of the chart.</p>
487
- <button className="btn" style={{ margin: '1em auto' }} disabled={missingRequiredSections()} onClick={confirmDone}>I'm Done</button>
527
+ <button className='btn' style={{ margin: '1em auto' }} disabled={missingRequiredSections()} onClick={confirmDone}>
528
+ I'm Done
529
+ </button>
488
530
  </section>
489
531
  </section>
490
532
  )
@@ -508,17 +550,17 @@ const EditorPanel = () => {
508
550
  }
509
551
 
510
552
  // eslint-disable-next-line react-hooks/exhaustive-deps
511
- }, [ config ])
512
-
553
+ }, [config])
554
+
513
555
  // Set paired bars to be horizontal, even though that option doesn't display
514
556
  useEffect(() => {
515
- if(config.visualizationType === 'Paired Bar') {
516
- updateConfig({
517
- ...config,
518
- orientation: 'horizontal'
557
+ if (config.visualizationType === 'Paired Bar') {
558
+ updateConfig({
559
+ ...config,
560
+ orientation: 'horizontal'
519
561
  })
520
562
  }
521
- }, []);
563
+ }, [])
522
564
 
523
565
  useEffect(() => {
524
566
  if (config.orientation === 'horizontal') {
@@ -527,32 +569,32 @@ const EditorPanel = () => {
527
569
  lollipopShape: config.lollipopShape
528
570
  })
529
571
  }
530
- }, [ config.isLollipopChart, config.lollipopShape ])
572
+ }, [config.isLollipopChart, config.lollipopShape])
531
573
 
532
574
  const ExclusionsList = useCallback(() => {
533
- const exclusions = [ ...config.exclusions.keys ]
575
+ const exclusions = [...config.exclusions.keys]
534
576
  return (
535
- <ul className="series-list">
577
+ <ul className='series-list'>
536
578
  {exclusions.map((exclusion, index) => {
537
579
  return (
538
580
  <li key={exclusion}>
539
- <div className="series-list__name" data-title={exclusion}>
540
- <div className="series-list__name--text">
541
- {exclusion}
542
- </div>
581
+ <div className='series-list__name' data-title={exclusion}>
582
+ <div className='series-list__name--text'>{exclusion}</div>
543
583
  </div>
544
- <button className="series-list__remove" onClick={() => removeExclusion(exclusion)}>&#215;</button>
584
+ <button className='series-list__remove' onClick={() => removeExclusion(exclusion)}>
585
+ &#215;
586
+ </button>
545
587
  </li>
546
588
  )
547
589
  })}
548
590
  </ul>
549
591
  )
550
- }, [ config ])
592
+ }, [config])
551
593
 
552
594
  const ErrorWithLolliopChart = ({ message }) => {
553
595
  return (
554
- <section className="waiting">
555
- <section className="waiting-container">
596
+ <section className='waiting'>
597
+ <section className='waiting-container'>
556
598
  <h3>Error With Configuration</h3>
557
599
  <p>{message}</p>
558
600
  </section>
@@ -560,17 +602,21 @@ const EditorPanel = () => {
560
602
  )
561
603
  }
562
604
 
605
+ const checkIsLine = type => {
606
+ return type === ('Line' || 'dashed-sm')
607
+ }
608
+
563
609
  const handleFilterChange = (idx1, idx2, filterIndex, filter) => {
564
610
  let filterOrder = filter.values
565
- let [ movedItem ] = filterOrder.splice(idx1, 1)
611
+ let [movedItem] = filterOrder.splice(idx1, 1)
566
612
  filterOrder.splice(idx2, 0, movedItem)
567
- let filters = [ ...config.filters ]
613
+ let filters = [...config.filters]
568
614
  let filterItem = { ...config.filters[filterIndex] }
569
615
  filterItem.active = filter.values[0]
570
- filterItem.values = filterOrder
616
+ filterItem.orderedValues = filterOrder
571
617
  filterItem.order = 'cust'
572
618
  filters[filterIndex] = filterItem
573
- setFilteredData(filters)
619
+ updateConfig({ ...config, filters });
574
620
  }
575
621
 
576
622
  if (config.isLollipopChart && config?.series?.length > 1) {
@@ -581,500 +627,741 @@ const EditorPanel = () => {
581
627
  config.runtime.editorErrorMessage = 'Add a data series'
582
628
  }
583
629
 
584
- const section = config.orientation==='horizontal' ? 'xAxis' : 'yAxis';
585
- const [warningMsg,setWarningMsg] = useState({maxMsg:'',minMsg:''});
630
+ const section = config.orientation === 'horizontal' ? 'xAxis' : 'yAxis'
631
+ const [warningMsg, setWarningMsg] = useState({ maxMsg: '', minMsg: '' })
586
632
 
587
633
  const validateMaxValue = () => {
588
- const enteredValue = config[section].max;
589
- let message = '';
590
-
591
- switch(true){
592
- case (enteredValue && parseFloat(enteredValue) < parseFloat(maxValue) && existPositiveValue):
593
- message = 'Max value must be more than '+ maxValue;
594
- break;
595
- case (enteredValue && parseFloat(enteredValue) < 0 && !existPositiveValue):
596
- message = 'Value must be more than or equal to 0';
597
- break;
598
- default : message = '' ;
599
- }
600
- setWarningMsg(function(prevMsg){return{...prevMsg,maxMsg:message}});
601
- };
602
-
603
-
604
- const validateMinValue = ()=>{
605
- const enteredValue = config[section].min;
606
- let minVal = Number(minValue);
607
- let message = '';
608
-
609
- switch(true){
610
- case ((config.visualizationType === 'Line' || config.visualizationType === 'Spark Line') && (enteredValue && parseFloat(enteredValue) > minVal)):
611
- message = 'Value must be less than ' + minValue;
612
- break;
613
- case ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo' ) && enteredValue && minVal > 0 && parseFloat(enteredValue) > 0):
614
- message = 'Value must be less than or equal to 0';
615
- break;
616
- case ( enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal) :
617
- message = 'Value must be less than ' + minValue;
618
- break;
619
- default : message = ''
620
- };
621
- setWarningMsg(function (prevMsg) { return {...prevMsg, minMsg: message}});
622
- };
623
-
624
- useEffect(()=>{
625
- validateMinValue();
626
- validateMaxValue();
627
- },[minValue,maxValue,config]);
634
+ const enteredValue = config[section].max
635
+ let message = ''
636
+
637
+ switch (true) {
638
+ case enteredValue && parseFloat(enteredValue) < parseFloat(maxValue) && existPositiveValue:
639
+ message = 'Max value must be more than ' + maxValue
640
+ break
641
+ case enteredValue && parseFloat(enteredValue) < 0 && !existPositiveValue:
642
+ message = 'Value must be more than or equal to 0'
643
+ break
644
+ default:
645
+ message = ''
646
+ }
647
+ setWarningMsg(function (prevMsg) {
648
+ return { ...prevMsg, maxMsg: message }
649
+ })
650
+ }
651
+
652
+ const validateMinValue = () => {
653
+ const enteredValue = config[section].min
654
+ let minVal = Number(minValue)
655
+ let message = ''
656
+
657
+ switch (true) {
658
+ case (config.visualizationType === 'Line' || config.visualizationType === 'Spark Line') && enteredValue && parseFloat(enteredValue) > minVal:
659
+ message = 'Value must be less than ' + minValue
660
+ break
661
+ case (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && enteredValue && minVal > 0 && parseFloat(enteredValue) > 0:
662
+ message = 'Value must be less than or equal to 0'
663
+ break
664
+ case enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal:
665
+ message = 'Value must be less than ' + minValue
666
+ break
667
+ default:
668
+ message = ''
669
+ }
670
+ setWarningMsg(function (prevMsg) {
671
+ return { ...prevMsg, minMsg: message }
672
+ })
673
+ }
674
+
675
+ useEffect(() => {
676
+ validateMinValue()
677
+ validateMaxValue()
678
+ }, [minValue, maxValue, config])
628
679
 
629
680
  return (
630
- <ErrorBoundary component="EditorPanel">
631
- {config.newViz && <Confirm/>}
632
- {undefined === config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error/>}
681
+ <ErrorBoundary component='EditorPanel'>
682
+ {config.newViz && <Confirm />}
683
+ {undefined === config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error />}
633
684
  <button className={displayPanel ? `editor-toggle` : `editor-toggle collapsed`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick}></button>
634
685
  <section className={`${displayPanel ? 'editor-panel cove' : 'hidden editor-panel cove'}${isDashboard ? ' dashboard' : ''}`}>
635
- <div aria-level="2" role="heading" className="heading-2">Configure Chart</div>
636
- <section className="form-container">
686
+ <div aria-level='2' role='heading' className='heading-2'>
687
+ Configure Chart
688
+ </div>
689
+ <section className='form-container'>
637
690
  <form>
638
691
  <Accordion allowZeroExpanded={true}>
639
- <AccordionItem> {/* General */}
692
+ <AccordionItem>
693
+ {' '}
694
+ {/* General */}
640
695
  <AccordionItemHeading>
641
- <AccordionItemButton>
642
- General
643
- </AccordionItemButton>
696
+ <AccordionItemButton>General</AccordionItemButton>
644
697
  </AccordionItemHeading>
645
698
  <AccordionItemPanel>
646
- <Select value={config.visualizationType} fieldName="visualizationType" label="Chart Type" updateField={updateField} options={[ 'Pie', 'Line', 'Bar', 'Combo', 'Paired Bar', 'Spark Line' ]}/>
647
- {(config.visualizationType === 'Bar'|| config.visualizationType === 'Combo') && <Select value={config.visualizationSubType || 'Regular'} fieldName="visualizationSubType" label="Chart Subtype" updateField={updateField} options={[ 'regular', 'stacked' ]}/>}
648
- {config.visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName="orientation" label="Orientation" updateField={updateField} options={[ 'vertical', 'horizontal' ]}/>}
649
- {config.visualizationType === 'Bar' && <Select value={ config.isLollipopChart? 'lollipop': config.barStyle || 'flat'} fieldName="barStyle" label="bar style" updateField={updateField} options={showBarStyleOptions()}/>}
650
- {(config.visualizationType === 'Bar' && config.barStyle==='rounded' ) && <Select value={config.tipRounding||'top'} fieldName="tipRounding" label="tip rounding" updateField={updateField} options={['top','full']}/>}
651
- {(config.visualizationType === 'Bar' && config.barStyle==='rounded' ) && <Select value={config.roundingStyle||'standard'} fieldName="roundingStyle" label="rounding style" updateField={updateField} options={['standard','shallow','finger']}/>}
652
- {(config.visualizationType === 'Bar' && config.orientation === 'horizontal') &&
653
- <Select value={config.yAxis.labelPlacement || 'Below Bar'} section="yAxis" fieldName="labelPlacement" label="Label Placement" updateField={updateField} options={[ 'Below Bar', 'On Date/Category Axis' ]}/>
654
- }
655
- {config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis' || config.visualizationType === 'Paired Bar' ) ? (
656
- <CheckBox value={config.yAxis.displayNumbersOnBar} section="yAxis" fieldName="displayNumbersOnBar" label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField}/>
657
- ): config.visualizationType !== 'Pie' && (
658
- <CheckBox value={config.labels} fieldName="labels" label="Display label on data" updateField={updateField}/>
699
+ <Select value={config.visualizationType} fieldName='visualizationType' label='Chart Type' updateField={updateField} options={['Pie', 'Line', 'Bar', 'Combo', 'Paired Bar', 'Spark Line']} />
700
+ {(config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && <Select value={config.visualizationSubType || 'Regular'} fieldName='visualizationSubType' label='Chart Subtype' updateField={updateField} options={['regular', 'stacked']} />}
701
+ {config.visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName='orientation' label='Orientation' updateField={updateField} options={['vertical', 'horizontal']} />}
702
+ {config.visualizationType === 'Bar' && <Select value={config.isLollipopChart ? 'lollipop' : config.barStyle || 'flat'} fieldName='barStyle' label='bar style' updateField={updateField} options={showBarStyleOptions()} />}
703
+ {config.visualizationType === 'Bar' && config.barStyle === 'rounded' && <Select value={config.tipRounding || 'top'} fieldName='tipRounding' label='tip rounding' updateField={updateField} options={['top', 'full']} />}
704
+ {config.visualizationType === 'Bar' && config.barStyle === 'rounded' && <Select value={config.roundingStyle || 'standard'} fieldName='roundingStyle' label='rounding style' updateField={updateField} options={['standard', 'shallow', 'finger']} />}
705
+ {config.visualizationType === 'Bar' && config.orientation === 'horizontal' && <Select value={config.yAxis.labelPlacement || 'Below Bar'} section='yAxis' fieldName='labelPlacement' label='Label Placement' updateField={updateField} options={['Below Bar', 'On Date/Category Axis']} />}
706
+ {config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis' || config.visualizationType === 'Paired Bar') ? (
707
+ <CheckBox value={config.yAxis.displayNumbersOnBar} section='yAxis' fieldName='displayNumbersOnBar' label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField} />
708
+ ) : (
709
+ config.visualizationType !== 'Pie' && <CheckBox value={config.labels} fieldName='labels' label='Display label on data' updateField={updateField} />
659
710
  )}
660
- {config.visualizationType === 'Pie' && <Select fieldName="pieType" label="Pie Chart Type" updateField={updateField} options={[ 'Regular', 'Donut' ]}/>}
661
- <TextField value={config.title} fieldName="title" label="Title" updateField={updateField} />
662
-
711
+ {config.visualizationType === 'Pie' && <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />}
712
+ <TextField value={config.title} fieldName='title' label='Title' updateField={updateField} />
713
+
663
714
  <TextField
664
- value={config.superTitle}
665
- updateField={updateField}
666
- fieldName='superTitle'
667
- label='Super Title'
668
- placeholder='Super Title'
669
- tooltip={
670
- <Tooltip style={{textTransform: 'none'}}>
671
- <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
715
+ value={config.superTitle}
716
+ updateField={updateField}
717
+ fieldName='superTitle'
718
+ label='Super Title'
719
+ placeholder='Super Title'
720
+ tooltip={
721
+ <Tooltip style={{ textTransform: 'none' }}>
722
+ <Tooltip.Target>
723
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
724
+ </Tooltip.Target>
672
725
  <Tooltip.Content>
673
- <p>Super Title</p>
726
+ <p>Super Title</p>
674
727
  </Tooltip.Content>
675
728
  </Tooltip>
676
- }
729
+ }
677
730
  />
678
-
731
+
679
732
  <TextField
680
- type='textarea'
681
- value={config.introText}
682
- updateField={updateField}
683
- fieldName='introText'
684
- label='Intro Text'
685
- tooltip={
686
- <Tooltip style={{textTransform: 'none'}}>
687
- <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
733
+ type='textarea'
734
+ value={config.introText}
735
+ updateField={updateField}
736
+ fieldName='introText'
737
+ label='Intro Text'
738
+ tooltip={
739
+ <Tooltip style={{ textTransform: 'none' }}>
740
+ <Tooltip.Target>
741
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
742
+ </Tooltip.Target>
688
743
  <Tooltip.Content>
689
- <p>Intro Text</p>
744
+ <p>Intro Text</p>
690
745
  </Tooltip.Content>
691
746
  </Tooltip>
692
- }
693
- />
694
-
695
- <TextField type="textarea" value={config.description} fieldName="description" label="Subtext" updateField={updateField} tooltip={
696
- <Tooltip style={{ textTransform: 'none' }}>
697
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
698
- <Tooltip.Content>
699
- <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
700
- </Tooltip.Content>
701
- </Tooltip>
702
- } />
703
-
747
+ }
748
+ />
749
+
704
750
  <TextField
705
- type='textarea'
706
- value={config.footnotes}
707
- updateField={updateField}
708
- fieldName='footnotes'
709
- label='Footnotes'
710
- tooltip={
711
- <Tooltip style={{textTransform: 'none'}}>
712
- <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
751
+ type='textarea'
752
+ value={config.description}
753
+ fieldName='description'
754
+ label='Subtext'
755
+ updateField={updateField}
756
+ tooltip={
757
+ <Tooltip style={{ textTransform: 'none' }}>
758
+ <Tooltip.Target>
759
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
760
+ </Tooltip.Target>
713
761
  <Tooltip.Content>
714
- <p>Footnotes</p>
762
+ <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
715
763
  </Tooltip.Content>
716
764
  </Tooltip>
717
- }
718
- />
765
+ }
766
+ />
719
767
 
720
- {config.visualizationSubType !== 'horizontal' &&
721
- <TextField type="number" value={config.height} fieldName="height" label="Chart Height" updateField={updateField}/>
722
- }
768
+ <TextField
769
+ type='textarea'
770
+ value={config.footnotes}
771
+ updateField={updateField}
772
+ fieldName='footnotes'
773
+ label='Footnotes'
774
+ tooltip={
775
+ <Tooltip style={{ textTransform: 'none' }}>
776
+ <Tooltip.Target>
777
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
778
+ </Tooltip.Target>
779
+ <Tooltip.Content>
780
+ <p>Footnotes</p>
781
+ </Tooltip.Content>
782
+ </Tooltip>
783
+ }
784
+ />
785
+
786
+ {config.visualizationSubType !== 'horizontal' && <TextField type='number' value={config.height} fieldName='height' label='Chart Height' updateField={updateField} />}
723
787
  </AccordionItemPanel>
724
788
  </AccordionItem>
725
789
 
726
-
727
- {config.visualizationType !== 'Pie' &&
790
+ {config.visualizationType !== 'Pie' && (
728
791
  <AccordionItem>
729
792
  <AccordionItemHeading>
730
- <AccordionItemButton>
731
- Data Series {((!config.series || config.series.length === 0) || (config.visualizationType === 'Paired Bar' && config.series.length < 2)) && <WarningImage width="25" className="warning-icon"/>}
732
- </AccordionItemButton>
793
+ <AccordionItemButton>Data Series {(!config.series || config.series.length === 0 || (config.visualizationType === 'Paired Bar' && config.series.length < 2)) && <WarningImage width='25' className='warning-icon' />}</AccordionItemButton>
733
794
  </AccordionItemHeading>
734
795
  <AccordionItemPanel>
735
- {((!config.series || config.series.length === 0) && (config.visualizationType !== 'Paired Bar')) && <p className="warning">At least one series is required</p>}
736
- {((!config.series || config.series.length === 0 || config.series.length < 2) && (config.visualizationType === 'Paired Bar')) && <p className="warning">Select two data series for paired bar chart (e.g., Male and Female).</p>}
796
+ {(!config.series || config.series.length === 0) && config.visualizationType !== 'Paired Bar' && <p className='warning'>At least one series is required</p>}
797
+ {(!config.series || config.series.length === 0 || config.series.length < 2) && config.visualizationType === 'Paired Bar' && <p className='warning'>Select two data series for paired bar chart (e.g., Male and Female).</p>}
737
798
  {config.series && config.series.length !== 0 && (
738
799
  <>
739
800
  <fieldset>
740
- <legend className="edit-label float-left">
741
- Displaying
742
- </legend>
801
+ <legend className='edit-label float-left'>Displaying</legend>
743
802
  <Tooltip style={{ textTransform: 'none' }}>
744
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
803
+ <Tooltip.Target>
804
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
805
+ </Tooltip.Target>
745
806
  <Tooltip.Content>
746
807
  <p>A data series is a set of related data points plotted in a chart and typically represented in the chart legend.</p>
747
808
  </Tooltip.Content>
748
809
  </Tooltip>
749
810
  </fieldset>
750
- <ul className="series-list">
751
- {config.series.map((series, i) => {
811
+ <ul className='series-list'>
812
+ {config.series &&
813
+ config.series.map((series, i) => {
814
+ if (config.visualizationType === 'Combo') {
815
+ let changeType = (i, value) => {
816
+ let series = [...config.series]
817
+ series[i].type = value
818
+
819
+ series[i].axis = 'Left'
820
+
821
+ updateConfig({ ...config, series })
822
+ }
823
+
824
+ let typeDropdown = (
825
+ <select
826
+ value={series.type}
827
+ onChange={event => {
828
+ changeType(i, event.target.value)
829
+ }}
830
+ style={{ width: '100px', marginRight: '10px' }}
831
+ >
832
+ <option value='' default>
833
+ Select
834
+ </option>
835
+ <option value='Bar'>Bar</option>
836
+ <option value='Line'>Solid Line</option>
837
+ <option value='dashed-sm'>Small Dashed</option>
838
+ <option value='dashed-md'>Medium Dashed</option>
839
+ <option value='dashed-lg'>Large Dashed</option>
840
+ </select>
841
+ )
842
+
843
+ return (
844
+ <li key={series.dataKey}>
845
+ <div className={`series-list__name${series.dataKey.length > 15 ? ' series-list__name--truncate' : ''}`} data-title={series.dataKey}>
846
+ <div className='series-list__name-text'>{series.dataKey}</div>
847
+ </div>
848
+ <span>
849
+ <span className='series-list__dropdown'>{typeDropdown}</span>
850
+ {config.series && config.series.length > 1 && (
851
+ <button className='series-list__remove' onClick={() => removeSeries(series.dataKey)}>
852
+ &#215;
853
+ </button>
854
+ )}
855
+ </span>
856
+ </li>
857
+ )
858
+ }
859
+
860
+ return (
861
+ <li key={series.dataKey}>
862
+ <div className='series-list__name' data-title={series.dataKey}>
863
+ <div className='series-list__name--text'>{series.dataKey}</div>
864
+ </div>
865
+ {config.series && config.series.length > 1 && (
866
+ <button className='series-list__remove' onClick={() => removeSeries(series.dataKey)}>
867
+ &#215;
868
+ </button>
869
+ )}
870
+ </li>
871
+ )
872
+ })}
873
+ </ul>
874
+ </>
875
+ )}
876
+
877
+ <Select
878
+ fieldName='visualizationType'
879
+ label='Add Data Series'
880
+ initial='Select'
881
+ onChange={e => {
882
+ if (e.target.value !== '' && e.target.value !== 'Select') {
883
+ addNewSeries(e.target.value)
884
+ }
885
+ e.target.value = ''
886
+ }}
887
+ options={getColumns()}
888
+ />
889
+ {config.series && config.series.length <= 1 && config.visualizationType === 'Bar' && (
890
+ <>
891
+ <span className='divider-heading'>Confidence Keys</span>
892
+ <Select value={config.confidenceKeys.upper || ''} section='confidenceKeys' fieldName='upper' label='Upper' updateField={updateField} initial='Select' options={getColumns()} />
893
+ <Select value={config.confidenceKeys.lower || ''} section='confidenceKeys' fieldName='lower' label='Lower' updateField={updateField} initial='Select' options={getColumns()} />
894
+ </>
895
+ )}
752
896
 
753
- if (config.visualizationType === 'Combo') {
754
- let changeType = (i, value) => {
755
- let series = [ ...config.series ]
756
- series[i].type = value
897
+ {config.series && config.series.length === 1 && <Select fieldName='visualizationType' label='Rank by Value' initial='Select' onChange={e => sortSeries(e.target.value)} options={['asc', 'desc']} />}
898
+ </AccordionItemPanel>
899
+ </AccordionItem>
900
+ )}
901
+
902
+ {hasRightAxis && config.series && config.visualizationType === 'Combo' && (
903
+ <AccordionItem>
904
+ <AccordionItemHeading>
905
+ <AccordionItemButton>Assign Data Series Axis</AccordionItemButton>
906
+ </AccordionItemHeading>
907
+ <AccordionItemPanel>
908
+ <p>Only line series data can be assigned to the right axis. Check the data series section above.</p>
909
+ {config.series && config.series.filter(series => checkIsLine(series.type)) && (
910
+ <>
911
+ <fieldset>
912
+ <legend className='edit-label float-left'>Displaying</legend>
913
+ <Tooltip style={{ textTransform: 'none' }}>
914
+ <Tooltip.Target>
915
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
916
+ </Tooltip.Target>
917
+ <Tooltip.Content>
918
+ <p>Assign an axis for the series</p>
919
+ </Tooltip.Content>
920
+ </Tooltip>
921
+ </fieldset>
922
+ <ul className='series-list'>
923
+ {config.series &&
924
+ config.series.map((series, i) => {
925
+ if (series.type === 'Bar') return false // can't set individual bars atm.
926
+
927
+ let changeAxis = (i, value) => {
928
+ let series = [...config.series]
929
+ series[i].axis = value
757
930
  updateConfig({ ...config, series })
758
931
  }
759
932
 
760
- let typeDropdown = (
761
- <select value={series.type} onChange={(event) => {
762
- changeType(i, event.target.value)
763
- }} style={{ width: '100px', marginRight: '10px' }}>
764
- <option value="" default>Select</option>
765
- <option value="Bar">Bar</option>
766
- <option value="Line">Solid Line</option>
767
- <option value="dashed-sm">Small Dashed</option>
768
- <option value="dashed-md">Medium Dashed</option>
769
- <option value="dashed-lg">Large Dashed</option>
933
+ let axisDropdown = (
934
+ <select
935
+ value={series.axis}
936
+ onChange={event => {
937
+ changeAxis(i, event.target.value)
938
+ }}
939
+ style={{ width: '100px', marginRight: '10px' }}
940
+ >
941
+ <option value='Left' default>
942
+ left
943
+ </option>
944
+ <option value='Right'>right</option>
770
945
  </select>
771
946
  )
772
947
 
773
948
  return (
774
949
  <li key={series.dataKey}>
775
950
  <div className={`series-list__name${series.dataKey.length > 15 ? ' series-list__name--truncate' : ''}`} data-title={series.dataKey}>
776
- <div className="series-list__name-text">{series.dataKey}</div>
951
+ <div className='series-list__name-text'>{series.dataKey}</div>
777
952
  </div>
778
953
  <span>
779
- <span className="series-list__dropdown">{typeDropdown}</span>
780
- {config.series && config.series.length > 1 &&
781
- <button className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</button>
782
- }
954
+ <span className='series-list__dropdown'>{axisDropdown}</span>
783
955
  </span>
784
956
  </li>
785
957
  )
786
- }
787
-
788
- return (
789
- <li key={series.dataKey}>
790
- <div className="series-list__name" data-title={series.dataKey}>
791
- <div className="series-list__name--text">
792
- {series.dataKey}
793
- </div>
794
- </div>
795
- {config.series && config.series.length > 1 &&
796
- <button className="series-list__remove" onClick={() => removeSeries(series.dataKey)}>&#215;</button>
797
- }
798
- </li>
799
- )
800
- })}
958
+ })}
801
959
  </ul>
802
- </>)}
803
-
804
- <Select fieldName="visualizationType" label="Add Data Series" initial="Select" onChange={(e) => {
805
- if (e.target.value !== '' && e.target.value !== 'Select') {
806
- addNewSeries(e.target.value)
807
- }
808
- e.target.value = ''
809
- }} options={getColumns()}/>
810
- {config.series && config.series.length <= 1 && config.visualizationType === 'Bar' && (
811
- <>
812
- <span className="divider-heading">Confidence Keys</span>
813
- <Select value={config.confidenceKeys.upper || ''} section="confidenceKeys" fieldName="upper" label="Upper" updateField={updateField} initial="Select" options={getColumns()}/>
814
- <Select value={config.confidenceKeys.lower || ''} section="confidenceKeys" fieldName="lower" label="Lower" updateField={updateField} initial="Select" options={getColumns()}/>
815
960
  </>
816
961
  )}
817
-
818
- {config.series && config.series.length === 1 && <Select
819
- fieldName="visualizationType"
820
- label="Rank by Value"
821
- initial="Select"
822
- onChange={(e) => sortSeries(e.target.value)}
823
- options={['asc', 'desc']} />}
824
-
825
962
  </AccordionItemPanel>
826
963
  </AccordionItem>
827
- }
964
+ )}
828
965
 
829
966
  <AccordionItem>
830
967
  <AccordionItemHeading>
831
968
  <AccordionItemButton>
832
- {config.visualizationType !== 'Pie'
833
- ? config.visualizationType === 'Bar' ? 'Value Axis' : 'Value Axis'
834
- : 'Data Format'
835
- }
836
- {config.visualizationType === 'Pie' && !config.yAxis.dataKey && <WarningImage width="25" className="warning-icon"/>}
969
+ {config.visualizationType !== 'Pie' ? (config.visualizationType === 'Bar' ? 'Left Value Axis' : 'Left Value Axis') : 'Data Format'}
970
+ {config.visualizationType === 'Pie' && !config.yAxis.dataKey && <WarningImage width='25' className='warning-icon' />}
837
971
  </AccordionItemButton>
838
972
  </AccordionItemHeading>
839
973
  <AccordionItemPanel>
840
- {config.visualizationType === 'Pie' &&
841
- <Select value={config.yAxis.dataKey || ''} section="yAxis" fieldName="dataKey" label="Data Column" initial="Select" required={true} updateField={updateField} options={getColumns(false)} tooltip={
842
- <Tooltip style={{ textTransform: 'none' }}>
843
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
844
- <Tooltip.Content>
845
- <p>Select the source data to be visually represented.</p>
846
- </Tooltip.Content>
847
- </Tooltip>
848
- }/>
849
- }
974
+ {config.visualizationType === 'Pie' && (
975
+ <Select
976
+ value={config.yAxis.dataKey || ''}
977
+ section='yAxis'
978
+ fieldName='dataKey'
979
+ label='Data Column'
980
+ initial='Select'
981
+ required={true}
982
+ updateField={updateField}
983
+ options={getColumns(false)}
984
+ tooltip={
985
+ <Tooltip style={{ textTransform: 'none' }}>
986
+ <Tooltip.Target>
987
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
988
+ </Tooltip.Target>
989
+ <Tooltip.Content>
990
+ <p>Select the source data to be visually represented.</p>
991
+ </Tooltip.Content>
992
+ </Tooltip>
993
+ }
994
+ />
995
+ )}
850
996
  {config.visualizationType !== 'Pie' && (
851
997
  <>
852
- <TextField value={config.yAxis.label} section="yAxis" fieldName="label" label="Label" updateField={updateField}/>
853
- <TextField value={config.yAxis.numTicks} placeholder="Auto" type="number" section="yAxis" fieldName="numTicks" label="Number of ticks" className="number-narrow" updateField={updateField}/>
854
- <TextField value={config.yAxis.size} type="number" section="yAxis" fieldName="size" label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'} className="number-narrow" updateField={updateField} tooltip={
855
- <Tooltip style={{ textTransform: 'none' }}>
856
- <Tooltip.Target><Icon display="question"/></Tooltip.Target>
857
- <Tooltip.Content>
858
- <p>{`Increase the size if elements in the ${config.orientation} axis are being crowded or hidden behind other elements. Decrease if less space is required for the value axis.`}</p>
859
- </Tooltip.Content>
860
- </Tooltip>
861
- }/>
862
- {config.orientation !== 'horizontal' && <CheckBox value={config.yAxis.gridLines} section="yAxis" fieldName="gridLines" label="Display Gridlines" updateField={updateField}/>}
998
+ <TextField value={config.yAxis.label} section='yAxis' fieldName='label' label='Label' updateField={updateField} />
999
+ <CheckBox value={config.yAxis.isLegendValue} section='yAxis' fieldName='isLegendValue' label='Use Legend Value in Hover' updateField={updateField} />
1000
+ <TextField value={config.yAxis.numTicks} placeholder='Auto' type='number' section='yAxis' fieldName='numTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
1001
+ <TextField
1002
+ value={config.yAxis.size}
1003
+ type='number'
1004
+ section='yAxis'
1005
+ fieldName='size'
1006
+ label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'}
1007
+ className='number-narrow'
1008
+ updateField={updateField}
1009
+ tooltip={
1010
+ <Tooltip style={{ textTransform: 'none' }}>
1011
+ <Tooltip.Target>
1012
+ <Icon display='question' />
1013
+ </Tooltip.Target>
1014
+ <Tooltip.Content>
1015
+ <p>{`Increase the size if elements in the ${config.orientation} axis are being crowded or hidden behind other elements. Decrease if less space is required for the value axis.`}</p>
1016
+ </Tooltip.Content>
1017
+ </Tooltip>
1018
+ }
1019
+ />
1020
+ {config.orientation !== 'horizontal' && <CheckBox value={config.yAxis.gridLines} section='yAxis' fieldName='gridLines' label='Display Gridlines' updateField={updateField} />}
863
1021
  </>
864
1022
  )}
865
- <span className="divider-heading">Number Formatting</span>
866
- <CheckBox value={config.dataFormat.commas} section="dataFormat" fieldName="commas" label="Add commas" updateField={updateField}/>
867
- <TextField value={config.dataFormat.roundTo} type="number" section="dataFormat" fieldName="roundTo" label="Round to decimal point" className="number-narrow" updateField={updateField} min={0}/>
868
- <div className="two-col-inputs">
869
- <TextField value={config.dataFormat.prefix} section="dataFormat" fieldName="prefix" label="Prefix" updateField={updateField} tooltip={
870
- <Tooltip style={{ textTransform: 'none' }}>
871
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
872
- <Tooltip.Content>
873
- {config.visualizationType === 'Pie' && <p>Enter a data prefix to display in the data table and chart tooltips, if applicable.</p>}
874
- {config.visualizationType !== 'Pie' && <p>Enter a data prefix (such as "$"), if applicable.</p>}
875
- </Tooltip.Content>
876
- </Tooltip>
877
- }/>
878
- <TextField value={config.dataFormat.suffix} section="dataFormat" fieldName="suffix" label="Suffix" updateField={updateField} tooltip={
879
- <Tooltip style={{ textTransform: 'none' }}>
880
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
881
- <Tooltip.Content>
882
- {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
883
- {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
884
- </Tooltip.Content>
885
- </Tooltip>
886
- }/>
1023
+ <span className='divider-heading'>Number Formatting</span>
1024
+ <CheckBox value={config.dataFormat.commas} section='dataFormat' fieldName='commas' label='Add commas' updateField={updateField} />
1025
+ <TextField value={config.dataFormat.roundTo} type='number' section='dataFormat' fieldName='roundTo' label='Round to decimal point' className='number-narrow' updateField={updateField} min={0} />
1026
+ <div className='two-col-inputs'>
1027
+ <TextField
1028
+ value={config.dataFormat.prefix}
1029
+ section='dataFormat'
1030
+ fieldName='prefix'
1031
+ label='Prefix'
1032
+ updateField={updateField}
1033
+ tooltip={
1034
+ <Tooltip style={{ textTransform: 'none' }}>
1035
+ <Tooltip.Target>
1036
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1037
+ </Tooltip.Target>
1038
+ <Tooltip.Content>
1039
+ {config.visualizationType === 'Pie' && <p>Enter a data prefix to display in the data table and chart tooltips, if applicable.</p>}
1040
+ {config.visualizationType !== 'Pie' && <p>Enter a data prefix (such as "$"), if applicable.</p>}
1041
+ </Tooltip.Content>
1042
+ </Tooltip>
1043
+ }
1044
+ />
1045
+ <TextField
1046
+ value={config.dataFormat.suffix}
1047
+ section='dataFormat'
1048
+ fieldName='suffix'
1049
+ label='Suffix'
1050
+ updateField={updateField}
1051
+ tooltip={
1052
+ <Tooltip style={{ textTransform: 'none' }}>
1053
+ <Tooltip.Target>
1054
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1055
+ </Tooltip.Target>
1056
+ <Tooltip.Content>
1057
+ {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
1058
+ {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
1059
+ </Tooltip.Content>
1060
+ </Tooltip>
1061
+ }
1062
+ />
887
1063
  </div>
888
-
889
- {(config.orientation === 'horizontal') ? // horizontal - x is vertical y is horizontal
890
- <>
891
- <CheckBox value={config.xAxis.hideAxis} section="xAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField} />
892
- <CheckBox value={config.xAxis.hideLabel} section="xAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField} />
893
- <CheckBox value={config.xAxis.hideTicks} section="xAxis" fieldName="hideTicks" label="Hide Ticks" updateField={updateField} />
894
- <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='update max value' type='number' placeholder='Auto' updateField={updateField} />
895
- <span style={{color:'red',display:'block'}} >{warningMsg.maxMsg}</span>
896
- </>
897
- : (config.visualizationType !=='Pie') &&
1064
+
1065
+ {config.orientation === 'horizontal' ? ( // horizontal - x is vertical y is horizontal
898
1066
  <>
899
- <CheckBox value={config.yAxis.hideAxis} section="yAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField} />
900
- <CheckBox value={config.yAxis.hideLabel} section="yAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField} />
901
- <CheckBox value={config.yAxis.hideTicks} section="yAxis" fieldName="hideTicks" label="Hide Ticks" updateField={updateField} />
902
- <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='update max value' placeholder='Auto' updateField={updateField} />
903
- <span style={{color:'red',display:'block'}} >{warningMsg.maxMsg}</span>
904
- <TextField value={config.yAxis.min} section='yAxis' fieldName='min' type='number' label='update min value' placeholder='Auto' updateField={updateField} />
905
- <span style={{color:'red',display:'block'}} >{warningMsg.minMsg}</span>
1067
+ <CheckBox value={config.xAxis.hideAxis} section='xAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1068
+ <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1069
+ <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1070
+ <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='update max value' type='number' placeholder='Auto' updateField={updateField} />
1071
+ <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
906
1072
  </>
907
- }
1073
+ ) : (
1074
+ config.visualizationType !== 'Pie' && (
1075
+ <>
1076
+ <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1077
+ <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1078
+ <CheckBox value={config.yAxis.hideTicks} section='yAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1079
+ <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='update max value' placeholder='Auto' updateField={updateField} />
1080
+ <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
1081
+ <TextField value={config.yAxis.min} section='yAxis' fieldName='min' type='number' label='update min value' placeholder='Auto' updateField={updateField} />
1082
+ <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
1083
+ </>
1084
+ )
1085
+ )}
908
1086
  </AccordionItemPanel>
909
1087
  </AccordionItem>
910
1088
 
1089
+ {/* Right Value Axis Settings */}
1090
+ {hasRightAxis && (
1091
+ <AccordionItem>
1092
+ <AccordionItemHeading>
1093
+ <AccordionItemButton>Right Value Axis</AccordionItemButton>
1094
+ </AccordionItemHeading>
1095
+ <AccordionItemPanel>
1096
+ <TextField value={config.yAxis.rightLabel} section='yAxis' fieldName='rightLabel' label='Label' updateField={updateField} />
1097
+ <TextField value={config.yAxis.rightNumTicks} placeholder='Auto' type='number' section='yAxis' fieldName='rightNumTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
1098
+ <TextField value={config.yAxis.rightAxisSize} type='number' section='yAxis' fieldName='rightAxisSize' label='Size (Width)' className='number-narrow' updateField={updateField} />
1099
+ <TextField value={config.yAxis.rightLabelOffsetSize} type='number' section='yAxis' fieldName='rightLabelOffsetSize' label='Label Offset' className='number-narrow' updateField={updateField} />
1100
+
1101
+ <span className='divider-heading'>Number Formatting</span>
1102
+ <CheckBox value={config.dataFormat.rightCommas} section='dataFormat' fieldName='rightCommas' label='Add commas' updateField={updateField} />
1103
+ <TextField value={config.dataFormat.rightRoundTo} type='number' section='dataFormat' fieldName='rightRoundTo' label='Round to decimal point' className='number-narrow' updateField={updateField} min={0} />
1104
+ <div className='two-col-inputs'>
1105
+ <TextField
1106
+ value={config.dataFormat.rightPrefix}
1107
+ section='dataFormat'
1108
+ fieldName='rightPrefix'
1109
+ label='Prefix'
1110
+ updateField={updateField}
1111
+ tooltip={
1112
+ <Tooltip style={{ textTransform: 'none' }}>
1113
+ <Tooltip.Target>
1114
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1115
+ </Tooltip.Target>
1116
+ <Tooltip.Content>
1117
+ {config.visualizationType === 'Pie' && <p>Enter a data prefix to display in the data table and chart tooltips, if applicable.</p>}
1118
+ {config.visualizationType !== 'Pie' && <p>Enter a data prefix (such as "$"), if applicable.</p>}
1119
+ </Tooltip.Content>
1120
+ </Tooltip>
1121
+ }
1122
+ />
1123
+ <TextField
1124
+ value={config.dataFormat.rightSuffix}
1125
+ section='dataFormat'
1126
+ fieldName='rightSuffix'
1127
+ label='Suffix'
1128
+ updateField={updateField}
1129
+ tooltip={
1130
+ <Tooltip style={{ textTransform: 'none' }}>
1131
+ <Tooltip.Target>
1132
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1133
+ </Tooltip.Target>
1134
+ <Tooltip.Content>
1135
+ {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
1136
+ {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
1137
+ </Tooltip.Content>
1138
+ </Tooltip>
1139
+ }
1140
+ />
1141
+ </div>
1142
+
1143
+ <CheckBox value={config.yAxis.rightHideAxis} section='yAxis' fieldName='rightHideAxis' label='Hide Axis' updateField={updateField} />
1144
+ <CheckBox value={config.yAxis.rightHideLabel} section='yAxis' fieldName='rightHideLabel' label='Hide Label' updateField={updateField} />
1145
+ <CheckBox value={config.yAxis.rightHideTicks} section='yAxis' fieldName='rightHideTicks' label='Hide Ticks' updateField={updateField} />
1146
+ </AccordionItemPanel>
1147
+ </AccordionItem>
1148
+ )}
1149
+
911
1150
  <AccordionItem>
912
1151
  <AccordionItemHeading>
913
1152
  <AccordionItemButton>
914
- {config.visualizationType !== 'Pie'
915
- ? config.visualizationType === 'Bar' ? 'Date/Category Axis' : 'Date/Category Axis'
916
- : 'Segments'
917
- }
918
- {!config.xAxis.dataKey && <WarningImage width="25" className="warning-icon"/>}
1153
+ {config.visualizationType !== 'Pie' ? (config.visualizationType === 'Bar' ? 'Date/Category Axis' : 'Date/Category Axis') : 'Segments'}
1154
+ {!config.xAxis.dataKey && <WarningImage width='25' className='warning-icon' />}
919
1155
  </AccordionItemButton>
920
1156
  </AccordionItemHeading>
921
1157
  <AccordionItemPanel>
922
- {config.visualizationType !== 'Pie' && <>
923
- <Select value={config.xAxis.type} section="xAxis" fieldName="type" label="Data Type" updateField={updateField} options={[ 'categorical', 'date' ]}/>
924
- <Select value={config.xAxis.dataKey || ''} section="xAxis" fieldName="dataKey" label="Data Key" initial="Select" required={true} updateField={updateField} options={getColumns(false)} tooltip={
925
- <Tooltip style={{ textTransform: 'none' }}>
926
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
927
- <Tooltip.Content>
928
- <p>Select the column or row containing the categories or dates for this axis. </p>
929
- </Tooltip.Content>
930
- </Tooltip>
931
- }/>
932
- </>}
1158
+ {config.visualizationType !== 'Pie' && (
1159
+ <>
1160
+ <Select value={config.xAxis.type} section='xAxis' fieldName='type' label='Data Type' updateField={updateField} options={['categorical', 'date']} />
1161
+ <Select
1162
+ value={config.xAxis.dataKey || ''}
1163
+ section='xAxis'
1164
+ fieldName='dataKey'
1165
+ label='Data Key'
1166
+ initial='Select'
1167
+ required={true}
1168
+ updateField={updateField}
1169
+ options={getColumns(false)}
1170
+ tooltip={
1171
+ <Tooltip style={{ textTransform: 'none' }}>
1172
+ <Tooltip.Target>
1173
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1174
+ </Tooltip.Target>
1175
+ <Tooltip.Content>
1176
+ <p>Select the column or row containing the categories or dates for this axis. </p>
1177
+ </Tooltip.Content>
1178
+ </Tooltip>
1179
+ }
1180
+ />
1181
+ </>
1182
+ )}
933
1183
 
934
- {config.visualizationType === 'Pie' &&
935
- <Select value={config.xAxis.dataKey || ''} section="xAxis" fieldName="dataKey" label="Segment Labels" initial="Select" required={true} updateField={updateField} options={getColumns(false)} tooltip={
936
- <Tooltip style={{ textTransform: 'none' }}>
937
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
938
- <Tooltip.Content>
939
- <p>Select the source row or column that contains the segment labels. Depending on the data structure, it may be listed as "Key."</p>
940
- </Tooltip.Content>
941
- </Tooltip>
942
- }/>
943
- }
1184
+ {config.visualizationType === 'Pie' && (
1185
+ <Select
1186
+ value={config.xAxis.dataKey || ''}
1187
+ section='xAxis'
1188
+ fieldName='dataKey'
1189
+ label='Segment Labels'
1190
+ initial='Select'
1191
+ required={true}
1192
+ updateField={updateField}
1193
+ options={getColumns(false)}
1194
+ tooltip={
1195
+ <Tooltip style={{ textTransform: 'none' }}>
1196
+ <Tooltip.Target>
1197
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1198
+ </Tooltip.Target>
1199
+ <Tooltip.Content>
1200
+ <p>Select the source row or column that contains the segment labels. Depending on the data structure, it may be listed as "Key."</p>
1201
+ </Tooltip.Content>
1202
+ </Tooltip>
1203
+ }
1204
+ />
1205
+ )}
944
1206
 
945
1207
  {config.visualizationType !== 'Pie' && (
946
1208
  <>
947
- <TextField value={config.xAxis.label} section="xAxis" fieldName="label" label="Label" updateField={updateField}/>
1209
+ <TextField value={config.xAxis.label} section='xAxis' fieldName='label' label='Label' updateField={updateField} />
948
1210
 
949
1211
  {config.xAxis.type === 'date' && (
950
1212
  <>
951
- <p style={{ padding: '1.5em 0 0.5em', fontSize: '.9rem', lineHeight: '1rem' }}>Format how charts should parse and display your dates using <a href="https://github.com/d3/d3-time-format#locale_format" target="_blank" rel="noreferrer">these guidelines</a>.</p>
952
- <TextField value={config.xAxis.dateParseFormat} section="xAxis" fieldName="dateParseFormat" placeholder="Ex. %Y-%m-%d" label="Date Parse Format" updateField={updateField}/>
953
- <TextField value={config.xAxis.dateDisplayFormat} section="xAxis" fieldName="dateDisplayFormat" placeholder="Ex. %Y-%m-%d" label="Date Display Format" updateField={updateField}/>
1213
+ <p style={{ padding: '1.5em 0 0.5em', fontSize: '.9rem', lineHeight: '1rem' }}>
1214
+ Format how charts should parse and display your dates using{' '}
1215
+ <a href='https://github.com/d3/d3-time-format#locale_format' target='_blank' rel='noreferrer'>
1216
+ these guidelines
1217
+ </a>
1218
+ .
1219
+ </p>
1220
+ <TextField value={config.xAxis.dateParseFormat} section='xAxis' fieldName='dateParseFormat' placeholder='Ex. %Y-%m-%d' label='Date Parse Format' updateField={updateField} />
1221
+ <TextField value={config.xAxis.dateDisplayFormat} section='xAxis' fieldName='dateDisplayFormat' placeholder='Ex. %Y-%m-%d' label='Date Display Format' updateField={updateField} />
954
1222
  </>
955
1223
  )}
956
1224
 
957
- <CheckBox value={config.exclusions.active} section="exclusions" fieldName="active" label={config.xAxis.type === 'date' ? 'Limit by start and/or end dates' : 'Exclude one or more values'} tooltip={
958
- <Tooltip style={{ textTransform: 'none' }}>
959
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
960
- <Tooltip.Content>
961
- <p>When this option is checked, you can select source-file values for exclusion from the date/category axis. </p>
962
- </Tooltip.Content>
963
- </Tooltip>
964
- } updateField={updateField}/>
1225
+ <CheckBox
1226
+ value={config.exclusions.active}
1227
+ section='exclusions'
1228
+ fieldName='active'
1229
+ label={config.xAxis.type === 'date' ? 'Limit by start and/or end dates' : 'Exclude one or more values'}
1230
+ tooltip={
1231
+ <Tooltip style={{ textTransform: 'none' }}>
1232
+ <Tooltip.Target>
1233
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1234
+ </Tooltip.Target>
1235
+ <Tooltip.Content>
1236
+ <p>When this option is checked, you can select source-file values for exclusion from the date/category axis. </p>
1237
+ </Tooltip.Content>
1238
+ </Tooltip>
1239
+ }
1240
+ updateField={updateField}
1241
+ />
965
1242
 
966
- {config.exclusions.active &&
1243
+ {config.exclusions.active && (
967
1244
  <>
968
- {config.xAxis.type === 'categorical' &&
1245
+ {config.xAxis.type === 'categorical' && (
969
1246
  <>
970
- {config.exclusions.keys.length > 0 &&
1247
+ {config.exclusions.keys.length > 0 && (
971
1248
  <>
972
1249
  <fieldset>
973
- <legend className="edit-label">Excluded Keys</legend>
1250
+ <legend className='edit-label'>Excluded Keys</legend>
974
1251
  </fieldset>
975
- <ExclusionsList/>
1252
+ <ExclusionsList />
976
1253
  </>
977
- }
978
-
979
- <Select fieldName="visualizationType" label="Add Exclusion" initial="Select" onChange={(e) => {
980
- if (e.target.value !== '' && e.target.value !== 'Select') {
981
- addNewExclusion(e.target.value)
982
- }
983
- e.target.value = ''
984
- }} options={getDataValues(config.xAxis.dataKey, true)}/>
1254
+ )}
1255
+
1256
+ <Select
1257
+ fieldName='visualizationType'
1258
+ label='Add Exclusion'
1259
+ initial='Select'
1260
+ onChange={e => {
1261
+ if (e.target.value !== '' && e.target.value !== 'Select') {
1262
+ addNewExclusion(e.target.value)
1263
+ }
1264
+ e.target.value = ''
1265
+ }}
1266
+ options={getDataValues(config.xAxis.dataKey, true)}
1267
+ />
985
1268
  </>
986
- }
1269
+ )}
987
1270
 
988
- {config.xAxis.type === 'date' &&
1271
+ {config.xAxis.type === 'date' && (
989
1272
  <>
990
- <TextField type="date" section="exclusions" fieldName="dateStart" label="Start Date" updateField={updateField} value={config.exclusions.dateStart || ''}/>
991
- <TextField type="date" section="exclusions" fieldName="dateEnd" label="End Date" updateField={updateField} value={config.exclusions.dateEnd || ''}/>
1273
+ <TextField type='date' section='exclusions' fieldName='dateStart' label='Start Date' updateField={updateField} value={config.exclusions.dateStart || ''} />
1274
+ <TextField type='date' section='exclusions' fieldName='dateEnd' label='End Date' updateField={updateField} value={config.exclusions.dateEnd || ''} />
992
1275
  </>
993
- }
994
- </>
995
- }
996
-
997
- {config.xAxis.type === 'date' &&
998
- <>
999
- <TextField value={config.xAxis.numTicks} placeholder="Auto" type="number" min="1" section="xAxis" fieldName="numTicks" label="Number of ticks" className="number-narrow" updateField={updateField}/>
1276
+ )}
1000
1277
  </>
1001
- }
1278
+ )}
1279
+ <TextField value={config.xAxis.numTicks} placeholder='Auto' type='number' min='1' section='xAxis' fieldName='numTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
1002
1280
 
1003
- <TextField value={config.xAxis.size} type="number" min="0" section="xAxis" fieldName="size" label={config.orientation === 'horizontal' ? 'Size (Width)' : 'Size (Height)'} className="number-narrow" updateField={updateField}/>
1281
+ <TextField value={config.xAxis.size} type='number' min='0' section='xAxis' fieldName='size' label={config.orientation === 'horizontal' ? 'Size (Width)' : 'Size (Height)'} className='number-narrow' updateField={updateField} />
1004
1282
 
1005
- {config.yAxis.labelPlacement !== 'Below Bar' &&
1006
- <TextField value={config.xAxis.tickRotation} type="number" min="0" section="xAxis" fieldName="tickRotation" label="Tick rotation (Degrees)" className="number-narrow" updateField={updateField}/>
1007
- }
1008
- {(config.orientation === 'horizontal') ?
1283
+ {config.yAxis.labelPlacement !== 'Below Bar' && <TextField value={config.xAxis.tickRotation} type='number' min='0' section='xAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
1284
+ {config.orientation === 'horizontal' ? (
1009
1285
  <>
1010
- <CheckBox value={config.yAxis.hideAxis} section="yAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField}/>
1011
- <CheckBox value={config.yAxis.hideLabel} section="yAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField}/>
1286
+ <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1287
+ <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1012
1288
  </>
1013
- :
1289
+ ) : (
1014
1290
  <>
1015
- <CheckBox value={config.xAxis.hideAxis} section="xAxis" fieldName="hideAxis" label="Hide Axis" updateField={updateField}/>
1016
- <CheckBox value={config.xAxis.hideLabel} section="xAxis" fieldName="hideLabel" label="Hide Label" updateField={updateField}/>
1017
- <CheckBox value={config.xAxis.hideTicks} section="xAxis" fieldName="hideTicks" label="Hide Ticks" updateField={updateField}/>
1291
+ <CheckBox value={config.xAxis.hideAxis} section='xAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1292
+ <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1293
+ <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1018
1294
  </>
1019
- }
1295
+ )}
1020
1296
  </>
1021
1297
  )}
1022
1298
 
1023
- {config.visualizationType === 'Pie' &&
1299
+ {config.visualizationType === 'Pie' && (
1024
1300
  <>
1025
- <CheckBox value={config.exclusions.active} section="exclusions" fieldName="active" label={'Exclude one or more values'} updateField={updateField} tooltip={
1026
- <Tooltip style={{ textTransform: 'none' }}>
1027
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
1028
- <Tooltip.Content>
1029
- <p>When this option is checked, you can select values for exclusion from the pie segments.</p>
1030
- </Tooltip.Content>
1031
- </Tooltip>
1032
- }/>
1033
- {config.exclusions.active &&
1301
+ <CheckBox
1302
+ value={config.exclusions.active}
1303
+ section='exclusions'
1304
+ fieldName='active'
1305
+ label={'Exclude one or more values'}
1306
+ updateField={updateField}
1307
+ tooltip={
1308
+ <Tooltip style={{ textTransform: 'none' }}>
1309
+ <Tooltip.Target>
1310
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1311
+ </Tooltip.Target>
1312
+ <Tooltip.Content>
1313
+ <p>When this option is checked, you can select values for exclusion from the pie segments.</p>
1314
+ </Tooltip.Content>
1315
+ </Tooltip>
1316
+ }
1317
+ />
1318
+ {config.exclusions.active && (
1034
1319
  <>
1035
- {config.exclusions.keys.length > 0 &&
1320
+ {config.exclusions.keys.length > 0 && (
1036
1321
  <>
1037
1322
  <fieldset>
1038
- <legend className="edit-label">Excluded Keys</legend>
1323
+ <legend className='edit-label'>Excluded Keys</legend>
1039
1324
  </fieldset>
1040
- <ExclusionsList/>
1325
+ <ExclusionsList />
1041
1326
  </>
1042
- }
1043
-
1044
- <Select fieldName="visualizationType" label="Add Exclusion" initial="Select" onChange={(e) => {
1045
- if (e.target.value !== '' && e.target.value !== 'Select') {
1046
- addNewExclusion(e.target.value)
1047
- }
1048
- e.target.value = ''
1049
- }} options={getDataValues(config.xAxis.dataKey, true)}/>
1327
+ )}
1328
+
1329
+ <Select
1330
+ fieldName='visualizationType'
1331
+ label='Add Exclusion'
1332
+ initial='Select'
1333
+ onChange={e => {
1334
+ if (e.target.value !== '' && e.target.value !== 'Select') {
1335
+ addNewExclusion(e.target.value)
1336
+ }
1337
+ e.target.value = ''
1338
+ }}
1339
+ options={getDataValues(config.xAxis.dataKey, true)}
1340
+ />
1050
1341
  </>
1051
- }
1342
+ )}
1052
1343
  </>
1053
- }
1344
+ )}
1054
1345
  </AccordionItemPanel>
1055
1346
  </AccordionItem>
1056
1347
 
1057
- {(config.visualizationType !== 'Pie' && config.visualizationType !== 'Paired Bar') &&
1348
+ {config.visualizationType !== 'Pie' && config.visualizationType !== 'Paired Bar' && (
1058
1349
  <AccordionItem>
1059
1350
  <AccordionItemHeading>
1060
- <AccordionItemButton>
1061
- Regions
1062
- </AccordionItemButton>
1351
+ <AccordionItemButton>Regions</AccordionItemButton>
1063
1352
  </AccordionItemHeading>
1064
1353
  <AccordionItemPanel>
1065
- <Regions config={config} updateConfig={updateConfig}/>
1354
+ <Regions config={config} updateConfig={updateConfig} />
1066
1355
  </AccordionItemPanel>
1067
1356
  </AccordionItem>
1068
- }
1357
+ )}
1069
1358
 
1070
1359
  <AccordionItem>
1071
1360
  <AccordionItemHeading>
1072
- <AccordionItemButton>
1073
- Legend
1074
- </AccordionItemButton>
1361
+ <AccordionItemButton>Legend</AccordionItemButton>
1075
1362
  </AccordionItemHeading>
1076
1363
  <AccordionItemPanel>
1077
- <CheckBox value={config.legend.reverseLabelOrder} section="legend" fieldName="reverseLabelOrder" label="Reverse Labels" updateField={updateField}/>
1364
+ <CheckBox value={config.legend.reverseLabelOrder} section='legend' fieldName='reverseLabelOrder' label='Reverse Labels' updateField={updateField} />
1078
1365
  {/* <fieldset className="checkbox-group">
1079
1366
  <CheckBox value={config.legend.dynamicLegend} section="legend" fieldName="dynamicLegend" label="Dynamic Legend" updateField={updateField}/>
1080
1367
  {config.legend.dynamicLegend && (
@@ -1086,88 +1373,103 @@ useEffect(()=>{
1086
1373
  </>
1087
1374
  )}
1088
1375
  </fieldset> */}
1089
- <CheckBox value={config.legend.hide} section="legend" fieldName="hide" label="Hide Legend" updateField={updateField} tooltip={
1090
- <Tooltip style={{ textTransform: 'none' }}>
1091
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
1092
- <Tooltip.Content>
1093
- <p>With a single-series chart, consider hiding the legend to reduce visual clutter.</p>
1094
- </Tooltip.Content>
1095
- </Tooltip>
1096
- }/>
1097
-
1098
- {(config.visualizationType==='Bar' && config.visualizationSubType ==="regular" && config.runtime.seriesKeys.length===1 )&& (
1099
- <Select value={config.legend.colorCode} section="legend" fieldName="colorCode" label="Color code by category" initial="Select" updateField={updateField} options={getDataValueOptions(data)}/>
1376
+ <CheckBox
1377
+ value={config.legend.hide}
1378
+ section='legend'
1379
+ fieldName='hide'
1380
+ label='Hide Legend'
1381
+ updateField={updateField}
1382
+ tooltip={
1383
+ <Tooltip style={{ textTransform: 'none' }}>
1384
+ <Tooltip.Target>
1385
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1386
+ </Tooltip.Target>
1387
+ <Tooltip.Content>
1388
+ <p>With a single-series chart, consider hiding the legend to reduce visual clutter.</p>
1389
+ </Tooltip.Content>
1390
+ </Tooltip>
1391
+ }
1392
+ />
1393
+
1394
+ {config.visualizationType === 'Bar' && config.visualizationSubType === 'regular' && config.runtime.seriesKeys.length === 1 && (
1395
+ <Select value={config.legend.colorCode} section='legend' fieldName='colorCode' label='Color code by category' initial='Select' updateField={updateField} options={getDataValueOptions(data)} />
1100
1396
  )}
1101
- <Select value={config.legend.behavior} section="legend" fieldName="behavior" label="Legend Behavior (When clicked)" updateField={updateField} options={[ 'highlight', 'isolate' ]}/>
1102
- <TextField value={config.legend.label} section="legend" fieldName="label" label="Title" updateField={updateField}/>
1103
- <Select value={config.legend.position} section="legend" fieldName="position" label="Position" updateField={updateField} options={[ 'right', 'left' ]}/>
1104
- <TextField type='textarea' value={config.legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />
1397
+ <Select value={config.legend.behavior} section='legend' fieldName='behavior' label='Legend Behavior (When clicked)' updateField={updateField} options={['highlight', 'isolate']} />
1398
+ <TextField value={config.legend.label} section='legend' fieldName='label' label='Title' updateField={updateField} />
1399
+ <Select value={config.legend.position} section='legend' fieldName='position' label='Position' updateField={updateField} options={['right', 'left', 'bottom']} />
1400
+ {config.legend.position === 'bottom' && <CheckBox value={config.legend.singleRow} section='legend' fieldName='singleRow' label='Single Row Legend' updateField={updateField} />}
1401
+ <TextField type='textarea' value={config.legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />
1105
1402
  </AccordionItemPanel>
1106
1403
  </AccordionItem>
1107
1404
 
1108
1405
  <AccordionItem>
1109
1406
  <AccordionItemHeading>
1110
- <AccordionItemButton>
1111
- Filters
1112
- </AccordionItemButton>
1407
+ <AccordionItemButton>Filters</AccordionItemButton>
1113
1408
  </AccordionItemHeading>
1114
1409
  <AccordionItemPanel>
1115
- {config.filters && <ul className="filters-list">
1116
- {config.filters.map((filter, index) => (
1117
- <fieldset className="edit-block" key={index}>
1118
- <button type="button" className="remove-column" onClick={() => {
1119
- removeFilter(index)
1120
- }}>Remove
1410
+ {config.filters && (
1411
+ <ul className='filters-list'>
1412
+ {config.filters.map((filter, index) => (
1413
+ <fieldset className='edit-block' key={index}>
1414
+ <button
1415
+ type='button'
1416
+ className='remove-column'
1417
+ onClick={() => {
1418
+ removeFilter(index)
1419
+ }}
1420
+ >
1421
+ Remove
1121
1422
  </button>
1122
1423
  <label>
1123
- <span className="edit-label column-heading">Filter</span>
1124
- <select value={filter.columnName} onChange={(e) => {
1125
- updateFilterProp('columnName', index, e.target.value)
1126
- }}>
1127
- <option value="">- Select Option -</option>
1424
+ <span className='edit-label column-heading'>Filter</span>
1425
+ <select
1426
+ value={filter.columnName}
1427
+ onChange={e => {
1428
+ updateFilterProp('columnName', index, e.target.value)
1429
+ }}
1430
+ >
1431
+ <option value=''>- Select Option -</option>
1128
1432
  {getColumns().map((dataKey, index) => (
1129
- <option value={dataKey} key={index}>{dataKey}</option>
1433
+ <option value={dataKey} key={index}>
1434
+ {dataKey}
1435
+ </option>
1130
1436
  ))}
1131
1437
  </select>
1132
1438
  </label>
1133
1439
  <label>
1134
- <span className="edit-label column-heading">Label</span>
1135
- <input type="text" value={filter.label} onChange={(e) => {
1136
- updateFilterProp('label', index, e.target.value)
1137
- }}/>
1440
+ <span className='edit-label column-heading'>Label</span>
1441
+ <input
1442
+ type='text'
1443
+ value={filter.label}
1444
+ onChange={e => {
1445
+ updateFilterProp('label', index, e.target.value)
1446
+ }}
1447
+ />
1138
1448
  </label>
1139
1449
 
1140
1450
  <label>
1141
- <span className="edit-filterOrder column-heading">Filter Order</span>
1142
- <select value={filter.order ? filter.order : 'asc'} onChange={(e) => updateFilterProp('order', index, e.target.value)}>
1451
+ <span className='edit-filterOrder column-heading'>Filter Order</span>
1452
+ <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', index, e.target.value)}>
1143
1453
  {filterOptions.map((option, index) => {
1144
- return <option value={option.value} key={`filter-${index}`}>{option.label}</option>
1454
+ return (
1455
+ <option value={option.value} key={`filter-${index}`}>
1456
+ {option.label}
1457
+ </option>
1458
+ )
1145
1459
  })}
1146
1460
  </select>
1147
1461
 
1148
- {filter.order === 'cust' &&
1149
- <DragDropContext
1150
- onDragEnd={({ source, destination }) =>
1151
- handleFilterChange(source.index, destination.index, index, config.filters[index])
1152
- }>
1153
- <Droppable droppableId="filter_order">
1154
- {(provided) => (
1155
- <ul
1156
- {...provided.droppableProps}
1157
- className="sort-list"
1158
- ref={provided.innerRef}
1159
- style={{ marginTop: '1em' }}
1160
- >
1462
+ {filter.order === 'cust' && (
1463
+ <DragDropContext onDragEnd={({ source, destination }) => handleFilterChange(source.index, destination.index, index, config.filters[index])}>
1464
+ <Droppable droppableId='filter_order'>
1465
+ {provided => (
1466
+ <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
1161
1467
  {config.filters[index]?.values.map((value, index) => {
1162
1468
  return (
1163
1469
  <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
1164
1470
  {(provided, snapshot) => (
1165
1471
  <li>
1166
- <div className={snapshot.isDragging ? 'currently-dragging' : ''}
1167
- style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)}
1168
- ref={provided.innerRef}
1169
- {...provided.draggableProps}
1170
- {...provided.dragHandleProps}>
1472
+ <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
1171
1473
  {value}
1172
1474
  </div>
1173
1475
  </li>
@@ -1180,98 +1482,84 @@ useEffect(()=>{
1180
1482
  )}
1181
1483
  </Droppable>
1182
1484
  </DragDropContext>
1183
- }
1485
+ )}
1184
1486
  </label>
1185
-
1186
1487
  </fieldset>
1187
- )
1188
- )}
1189
- </ul>}
1488
+ ))}
1489
+ </ul>
1490
+ )}
1190
1491
  {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
1191
- <button type="button" onClick={addNewFilter} className="btn full-width">Add Filter</button>
1492
+ <button type='button' onClick={addNewFilter} className='btn full-width'>
1493
+ Add Filter
1494
+ </button>
1192
1495
  </AccordionItemPanel>
1193
1496
  </AccordionItem>
1194
1497
 
1195
1498
  <AccordionItem>
1196
1499
  <AccordionItemHeading>
1197
- <AccordionItemButton>
1198
- Visual
1199
- </AccordionItemButton>
1500
+ <AccordionItemButton>Visual</AccordionItemButton>
1200
1501
  </AccordionItemHeading>
1201
1502
  <AccordionItemPanel>
1202
-
1203
- {config.isLollipopChart &&
1503
+ {config.isLollipopChart && (
1204
1504
  <>
1205
- <fieldset className="header">
1206
- <legend className="edit-label">Lollipop Shape</legend>
1207
- <div onChange={(e) => {
1208
- setLollipopShape(e.target.value)
1209
- }}>
1210
- <label className="radio-label">
1211
- <input
1212
- type="radio"
1213
- name="lollipopShape"
1214
- value="circle"
1215
- checked={config.lollipopShape === 'circle'}
1216
- />
1505
+ <fieldset className='header'>
1506
+ <legend className='edit-label'>Lollipop Shape</legend>
1507
+ <div
1508
+ onChange={e => {
1509
+ setLollipopShape(e.target.value)
1510
+ }}
1511
+ >
1512
+ <label className='radio-label'>
1513
+ <input type='radio' name='lollipopShape' value='circle' checked={config.lollipopShape === 'circle'} />
1217
1514
  Circle
1218
1515
  </label>
1219
- <label className="radio-label">
1220
- <input
1221
- type="radio"
1222
- name="lollipopShape"
1223
- value="square"
1224
- checked={config.lollipopShape === 'square'}
1225
- />
1516
+ <label className='radio-label'>
1517
+ <input type='radio' name='lollipopShape' value='square' checked={config.lollipopShape === 'square'} />
1226
1518
  Square
1227
1519
  </label>
1228
1520
  </div>
1229
-
1230
1521
  </fieldset>
1231
- <Select value={config.lollipopColorStyle ? config.lollipopColorStyle : 'two-tone'} fieldName="lollipopColorStyle" label="Lollipop Color Style" updateField={updateField} options={[ 'regular', 'two-tone' ]}/>
1232
- <Select value={config.lollipopSize ? config.lollipopSize : 'small'} fieldName="lollipopSize" label="Lollipop Size" updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
1522
+ <Select value={config.lollipopColorStyle ? config.lollipopColorStyle : 'two-tone'} fieldName='lollipopColorStyle' label='Lollipop Color Style' updateField={updateField} options={['regular', 'two-tone']} />
1523
+ <Select value={config.lollipopSize ? config.lollipopSize : 'small'} fieldName='lollipopSize' label='Lollipop Size' updateField={updateField} options={['small', 'medium', 'large']} />
1233
1524
  </>
1234
- }
1525
+ )}
1235
1526
 
1236
- <Select value={config.fontSize} fieldName="fontSize" label="Font Size" updateField={updateField} options={[ 'small', 'medium', 'large' ]}/>
1527
+ <Select value={config.fontSize} fieldName='fontSize' label='Font Size' updateField={updateField} options={['small', 'medium', 'large']} />
1237
1528
 
1238
- {config.series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar') &&
1239
- <Select value={config.barHasBorder} fieldName="barHasBorder" label="Bar Borders" updateField={updateField} options={[ 'true', 'false' ]}/>
1240
- }
1529
+ {config.series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar') && <Select value={config.barHasBorder} fieldName='barHasBorder' label='Bar Borders' updateField={updateField} options={['true', 'false']} />}
1241
1530
 
1242
1531
  {/* <CheckBox value={config.animate} fieldName="animate" label="Animate Visualization" updateField={updateField} /> */}
1243
1532
 
1244
1533
  {/*<CheckBox value={config.animateReplay} fieldName="animateReplay" label="Replay Animation When Filters Are Changed" updateField={updateField} />*/}
1245
1534
 
1246
- {((config.series?.some(series => series.type === 'Line') && config.visualizationType === 'Combo') || config.visualizationType === 'Line' || config.visualizationType === "Spark Line") &&
1247
- <Select value={config.lineDatapointStyle} fieldName="lineDatapointStyle" label="Line Datapoint Style" updateField={updateField} options={[ 'hidden', 'hover', 'always show' ]}/>
1248
- }
1535
+ {((config.series?.some(series => series.type === 'Line') && config.visualizationType === 'Combo') || config.visualizationType === 'Line' || config.visualizationType === 'Spark Line') && (
1536
+ <Select value={config.lineDatapointStyle} fieldName='lineDatapointStyle' label='Line Datapoint Style' updateField={updateField} options={['hidden', 'hover', 'always show']} />
1537
+ )}
1249
1538
 
1250
- <label className="header">
1251
- <span className="edit-label">Header Theme</span>
1252
- <ul className="color-palette">
1253
- {headerColors.map((palette) => (
1254
- <button
1539
+ <label className='header'>
1540
+ <span className='edit-label'>Header Theme</span>
1541
+ <ul className='color-palette'>
1542
+ {headerColors.map(palette => (
1543
+ <button
1255
1544
  title={palette}
1256
- key={palette}
1257
- onClick={(e) => {
1258
- e.preventDefault();
1545
+ key={palette}
1546
+ onClick={e => {
1547
+ e.preventDefault()
1259
1548
  updateConfig({ ...config, theme: palette })
1260
- }}
1261
- className={config.theme === palette ? 'selected ' + palette : palette}>
1262
- </button>
1549
+ }}
1550
+ className={config.theme === palette ? 'selected ' + palette : palette}
1551
+ ></button>
1263
1552
  ))}
1264
1553
  </ul>
1265
1554
  </label>
1266
1555
  <label>
1267
- <span className="edit-label">Chart Color Palette</span>
1556
+ <span className='edit-label'>Chart Color Palette</span>
1268
1557
  </label>
1269
1558
  {/* <InputCheckbox fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} /> */}
1270
- <InputToggle fieldName="isPaletteReversed" size="small" label="Use selected palette in reverse order" updateField={updateField} value={isPaletteReversed}/>
1559
+ <InputToggle fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} />
1271
1560
  <span>Sequential</span>
1272
- <ul className="color-palette">
1273
- {filteredPallets.map((palette) => {
1274
-
1561
+ <ul className='color-palette'>
1562
+ {filteredPallets.map(palette => {
1275
1563
  const colorOne = {
1276
1564
  backgroundColor: colorPalettes[palette][2]
1277
1565
  }
@@ -1285,11 +1573,11 @@ useEffect(()=>{
1285
1573
  }
1286
1574
 
1287
1575
  return (
1288
- <button
1289
- title={palette}
1290
- key={palette}
1291
- onClick={(e) => {
1292
- e.preventDefault();
1576
+ <button
1577
+ title={palette}
1578
+ key={palette}
1579
+ onClick={e => {
1580
+ e.preventDefault()
1293
1581
  updateConfig({ ...config, palette })
1294
1582
  }}
1295
1583
  className={config.palette === palette ? 'selected' : ''}
@@ -1302,9 +1590,8 @@ useEffect(()=>{
1302
1590
  })}
1303
1591
  </ul>
1304
1592
  <span>Non-Sequential</span>
1305
- <ul className="color-palette">
1306
- {filteredQualitative.map((palette) => {
1307
-
1593
+ <ul className='color-palette'>
1594
+ {filteredQualitative.map(palette => {
1308
1595
  const colorOne = {
1309
1596
  backgroundColor: colorPalettes[palette][2]
1310
1597
  }
@@ -1317,13 +1604,12 @@ useEffect(()=>{
1317
1604
  backgroundColor: colorPalettes[palette][6]
1318
1605
  }
1319
1606
 
1320
-
1321
1607
  return (
1322
- <button
1323
- title={palette}
1324
- key={palette}
1325
- onClick={(e) => {
1326
- e.preventDefault();
1608
+ <button
1609
+ title={palette}
1610
+ key={palette}
1611
+ onClick={e => {
1612
+ e.preventDefault()
1327
1613
  updateConfig({ ...config, palette })
1328
1614
  }}
1329
1615
  className={config.palette === palette ? 'selected' : ''}
@@ -1338,82 +1624,95 @@ useEffect(()=>{
1338
1624
 
1339
1625
  {config.visualizationType !== 'Pie' && (
1340
1626
  <>
1341
- <TextField value={config.dataCutoff} type="number" fieldName="dataCutoff" className="number-narrow" label="Data Cutoff" updateField={updateField} tooltip={
1342
- <Tooltip style={{ textTransform: 'none' }}>
1343
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
1344
- <Tooltip.Content>
1345
- <p>Any value below the cut-off value is included in a special "less than" category. This option supports special conditions like suppressed data.</p>
1346
- </Tooltip.Content>
1347
- </Tooltip>
1348
- }/>
1627
+ <TextField
1628
+ value={config.dataCutoff}
1629
+ type='number'
1630
+ fieldName='dataCutoff'
1631
+ className='number-narrow'
1632
+ label='Data Cutoff'
1633
+ updateField={updateField}
1634
+ tooltip={
1635
+ <Tooltip style={{ textTransform: 'none' }}>
1636
+ <Tooltip.Target>
1637
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1638
+ </Tooltip.Target>
1639
+ <Tooltip.Content>
1640
+ <p>Any value below the cut-off value is included in a special "less than" category. This option supports special conditions like suppressed data.</p>
1641
+ </Tooltip.Content>
1642
+ </Tooltip>
1643
+ }
1644
+ />
1349
1645
  </>
1350
1646
  )}
1351
- {(config.orientation === 'horizontal' && config.yAxis.labelPlacement !== 'On Bar') &&
1352
- <TextField type="number" value={config.barHeight || '25'} fieldName="barHeight" label="Bar Thickness" updateField={updateField} min="15"/>
1353
- }
1354
- {((config.visualizationType === 'Bar' && config.orientation !== 'horizontal') || config.visualizationType === 'Combo') &&
1355
- <TextField value={config.barThickness} type="number" fieldName="barThickness" label="Bar Thickness" updateField={updateField}/>
1356
- }
1357
-
1358
- {config.visualizationType === "Spark Line" &&
1359
- <div className="cove-accordion__panel-section checkbox-group">
1360
- <CheckBox value={config.visual?.border} section="visual" fieldName="border" label="Display Border" updateField={updateField} />
1361
- <CheckBox value={config.visual?.borderColorTheme} section="visual" fieldName="borderColorTheme" label="Use Border Color Theme" updateField={updateField} />
1362
- <CheckBox value={config.visual?.accent} section="visual" fieldName="accent" label="Use Accent Style" updateField={updateField} />
1363
- <CheckBox value={config.visual?.background} section="visual" fieldName="background" label="Use Theme Background Color" updateField={updateField} />
1364
- <CheckBox value={config.visual?.hideBackgroundColor} section="visual" fieldName="hideBackgroundColor" label="Hide Background Color" updateField={updateField} />
1647
+ {config.orientation === 'horizontal' && config.yAxis.labelPlacement !== 'On Bar' && <TextField type='number' value={config.barHeight || '25'} fieldName='barHeight' label='Bar Thickness' updateField={updateField} min='15' />}
1648
+ {((config.visualizationType === 'Bar' && config.orientation !== 'horizontal') || config.visualizationType === 'Combo') && <TextField value={config.barThickness} type='number' fieldName='barThickness' label='Bar Thickness' updateField={updateField} />}
1649
+
1650
+ {(config.visualizationType === 'Bar' || config.visualizationType === 'Line' || config.visualizationType === 'Combo') && <CheckBox value={config.topAxis.hasLine} section='topAxis' fieldName='hasLine' label='Add Top Axis Line' updateField={updateField} />}
1651
+
1652
+ {config.visualizationType === 'Spark Line' && (
1653
+ <div className='cove-accordion__panel-section checkbox-group'>
1654
+ <CheckBox value={config.visual?.border} section='visual' fieldName='border' label='Display Border' updateField={updateField} />
1655
+ <CheckBox value={config.visual?.borderColorTheme} section='visual' fieldName='borderColorTheme' label='Use Border Color Theme' updateField={updateField} />
1656
+ <CheckBox value={config.visual?.accent} section='visual' fieldName='accent' label='Use Accent Style' updateField={updateField} />
1657
+ <CheckBox value={config.visual?.background} section='visual' fieldName='background' label='Use Theme Background Color' updateField={updateField} />
1658
+ <CheckBox value={config.visual?.hideBackgroundColor} section='visual' fieldName='hideBackgroundColor' label='Hide Background Color' updateField={updateField} />
1365
1659
  </div>
1366
- }
1660
+ )}
1367
1661
  </AccordionItemPanel>
1368
1662
  </AccordionItem>
1369
1663
 
1370
1664
  <AccordionItem>
1371
1665
  <AccordionItemHeading>
1372
- <AccordionItemButton>
1373
- Data Table
1374
- </AccordionItemButton>
1666
+ <AccordionItemButton>Data Table</AccordionItemButton>
1375
1667
  </AccordionItemHeading>
1376
1668
  <AccordionItemPanel>
1377
- <CheckBox value={config.table.show} section="table" fieldName="show" label="Show Table" updateField={updateField} tooltip={
1378
- <Tooltip style={{ textTransform: 'none' }}>
1379
- <Tooltip.Target><Icon display="question" style={{ marginLeft: '0.5rem' }}/></Tooltip.Target>
1380
- <Tooltip.Content>
1381
- <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
1382
- </Tooltip.Content>
1383
- </Tooltip>
1384
- }/>
1385
- <TextField
1386
- value={config.table.caption}
1387
- updateField={updateField}
1388
- section='table'
1389
- type='textarea'
1390
- fieldName='caption'
1391
- label='Data Table Caption'
1392
- placeholder=' Data table'
1393
- tooltip={
1394
- <Tooltip style={{textTransform: 'none'}}>
1395
- <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1396
- <Tooltip.Content>
1397
- <p>Enter a description of the data table to be read by screen readers.</p>
1398
- </Tooltip.Content>
1399
- </Tooltip>
1400
- }
1401
- />
1402
- <CheckBox value={config.table.limitHeight} section="table" fieldName="limitHeight" label="Limit Table Height" updateField={updateField}/>
1403
- {config.table.limitHeight && (
1404
- <TextField value={config.table.height} section="table" fieldName='height' label='Data Table Height' type="number" min="0" max="500" placeholder='Height(px)' updateField={updateField}/>
1405
- )}
1406
- <CheckBox value={config.table.expanded} section="table" fieldName="expanded" label="Expanded by Default" updateField={updateField}/>
1407
- <CheckBox value={config.table.download} section="table" fieldName="download" label="Display Download Button" updateField={updateField}/>
1408
- <TextField value={config.table.label} section="table" fieldName="label" label="Label" updateField={updateField}/>
1409
- {config.visualizationType !== 'Pie' && <TextField value={config.table.indexLabel} section="table" fieldName="indexLabel" label="Index Column Header" updateField={updateField}/>}
1669
+ <CheckBox
1670
+ value={config.table.show}
1671
+ section='table'
1672
+ fieldName='show'
1673
+ label='Show Table'
1674
+ updateField={updateField}
1675
+ tooltip={
1676
+ <Tooltip style={{ textTransform: 'none' }}>
1677
+ <Tooltip.Target>
1678
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1679
+ </Tooltip.Target>
1680
+ <Tooltip.Content>
1681
+ <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
1682
+ </Tooltip.Content>
1683
+ </Tooltip>
1684
+ }
1685
+ />
1686
+ <TextField
1687
+ value={config.table.caption}
1688
+ updateField={updateField}
1689
+ section='table'
1690
+ type='textarea'
1691
+ fieldName='caption'
1692
+ label='Data Table Caption'
1693
+ placeholder=' Data table'
1694
+ tooltip={
1695
+ <Tooltip style={{ textTransform: 'none' }}>
1696
+ <Tooltip.Target>
1697
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1698
+ </Tooltip.Target>
1699
+ <Tooltip.Content>
1700
+ <p>Enter a description of the data table to be read by screen readers.</p>
1701
+ </Tooltip.Content>
1702
+ </Tooltip>
1703
+ }
1704
+ />
1705
+ <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label='Limit Table Height' updateField={updateField} />
1706
+ {config.table.limitHeight && <TextField value={config.table.height} section='table' fieldName='height' label='Data Table Height' type='number' min='0' max='500' placeholder='Height(px)' updateField={updateField} />}
1707
+ <CheckBox value={config.table.expanded} section='table' fieldName='expanded' label='Expanded by Default' updateField={updateField} />
1708
+ <CheckBox value={config.table.download} section='table' fieldName='download' label='Display Download Button' updateField={updateField} />
1709
+ <TextField value={config.table.label} section='table' fieldName='label' label='Label' updateField={updateField} />
1710
+ {config.visualizationType !== 'Pie' && <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />}
1410
1711
  </AccordionItemPanel>
1411
1712
  </AccordionItem>
1412
1713
  </Accordion>
1413
1714
  </form>
1414
- {config.type !== 'Spark Line' &&
1415
- <AdvancedEditor loadConfig={updateConfig} state={config} convertStateToConfig={convertStateToConfig} />
1416
- }
1715
+ {config.type !== 'Spark Line' && <AdvancedEditor loadConfig={updateConfig} state={config} convertStateToConfig={convertStateToConfig} />}
1417
1716
  </section>
1418
1717
  </section>
1419
1718
  </ErrorBoundary>