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