@cdc/chart 4.23.11 → 4.24.2

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 (104) hide show
  1. package/dist/cdcchart.js +35740 -35027
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/tall-data.json +98 -0
  4. package/examples/feature/forest-plot/forest-plot.json +63 -19
  5. package/examples/feature/forest-plot/linear.json +52 -3
  6. package/examples/feature/forest-plot/log.json +26 -0
  7. package/examples/feature/forest-plot/logarithmic.json +0 -35
  8. package/examples/feature/line/line-chart-preliminary.json +393 -0
  9. package/examples/feature/regions/index.json +9 -5
  10. package/examples/feature/scatterplot/scatterplot.json +272 -33
  11. package/index.html +10 -8
  12. package/package.json +2 -2
  13. package/src/CdcChart.tsx +70 -234
  14. package/src/ConfigContext.tsx +6 -0
  15. package/src/_stories/ChartEditor.stories.tsx +22 -0
  16. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  17. package/src/_stories/_mock/pie_config.json +192 -0
  18. package/src/_stories/_mock/pie_data.json +218 -0
  19. package/src/_stories/_mock/preliminary_mock.json +346 -0
  20. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
  21. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +2 -26
  22. package/src/components/AreaChart/index.tsx +4 -0
  23. package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
  24. package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
  25. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +108 -0
  26. package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
  27. package/src/components/BarChart/components/BarChart.jsx +39 -0
  28. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  29. package/src/components/BarChart/components/context.tsx +13 -0
  30. package/src/components/BarChart/index.tsx +3 -0
  31. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +10 -9
  32. package/src/components/BoxPlot/index.tsx +3 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
  34. package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
  35. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  36. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
  37. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
  38. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
  39. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
  40. package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
  41. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
  42. package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
  43. package/src/components/EditorPanel/components/panels.scss +72 -0
  44. package/src/components/EditorPanel/editor-panel.scss +739 -0
  45. package/src/components/EditorPanel/index.tsx +3 -0
  46. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -2
  47. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  48. package/src/components/Forecasting/index.tsx +3 -0
  49. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  50. package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
  51. package/src/components/ForestPlot/index.tsx +1 -209
  52. package/src/components/Legend/Legend.Component.tsx +199 -0
  53. package/src/components/Legend/Legend.tsx +28 -0
  54. package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
  55. package/src/components/Legend/index.tsx +3 -0
  56. package/src/components/LineChart/LineChartProps.ts +29 -0
  57. package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
  58. package/src/components/LineChart/helpers.ts +45 -0
  59. package/src/components/LineChart/index.tsx +111 -23
  60. package/src/components/LinearChart.jsx +55 -72
  61. package/src/components/PairedBarChart.jsx +4 -2
  62. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
  63. package/src/components/PieChart/index.tsx +3 -0
  64. package/src/components/Regions/components/Regions.tsx +144 -0
  65. package/src/components/Regions/index.tsx +3 -0
  66. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  67. package/src/components/ScatterPlot/index.tsx +3 -0
  68. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  69. package/src/components/Sparkline/index.tsx +3 -0
  70. package/src/data/initial-state.js +10 -8
  71. package/src/helpers/abbreviateNumber.ts +17 -0
  72. package/src/helpers/computeMarginBottom.ts +55 -0
  73. package/src/helpers/filterData.ts +18 -0
  74. package/src/helpers/generateColorsArray.ts +8 -0
  75. package/src/helpers/getQuartiles.ts +30 -0
  76. package/src/helpers/handleChartAriaLabels.ts +19 -0
  77. package/src/helpers/handleLineType.ts +18 -0
  78. package/src/helpers/lineOptions.ts +18 -0
  79. package/src/helpers/sort.ts +7 -0
  80. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  81. package/src/hooks/useBarChart.js +7 -6
  82. package/src/hooks/useHighlightedBars.js +1 -1
  83. package/src/hooks/useMinMax.ts +3 -3
  84. package/src/hooks/useScales.ts +19 -6
  85. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
  86. package/src/scss/main.scss +0 -3
  87. package/src/types/ChartConfig.ts +167 -23
  88. package/src/types/ChartContext.ts +34 -12
  89. package/src/types/ForestPlot.ts +7 -14
  90. package/src/types/Label.ts +7 -0
  91. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  92. package/src/ConfigContext.jsx +0 -5
  93. package/src/components/BarChart.StackedVertical.tsx +0 -91
  94. package/src/components/BarChart.jsx +0 -30
  95. package/src/components/EditorPanel.jsx +0 -3356
  96. package/src/components/ForestPlot/Readme.md +0 -0
  97. package/src/components/Legend.jsx +0 -310
  98. package/src/components/LineChart/LineChart.Circle.tsx +0 -105
  99. package/src/scss/LinearChart.scss +0 -0
  100. package/src/scss/editor-panel.scss +0 -745
  101. package/src/scss/legend.scss +0 -206
  102. package/src/scss/mixins.scss +0 -0
  103. package/src/scss/variables.scss +0 -1
  104. package/src/types/ChartProps.ts +0 -7
@@ -1,3356 +0,0 @@
1
- import React, { useState, useEffect, useCallback, memo, useContext } from 'react'
2
- import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
3
-
4
- import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
5
-
6
- import { useDebounce } from 'use-debounce'
7
-
8
- // @cdc/core
9
- import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
10
- import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
11
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
12
- import Icon from '@cdc/core/components/ui/Icon'
13
- import InputToggle from '@cdc/core/components/inputs/InputToggle'
14
- import Tooltip from '@cdc/core/components/ui/Tooltip'
15
-
16
- // chart components
17
- import ForestPlotSettings from './ForestPlotSettings'
18
- import Series from './Series'
19
-
20
- // cdc additional
21
- import { useColorPalette } from '../hooks/useColorPalette'
22
- import { useEditorPermissions } from '../hooks/useEditorPermissions'
23
- import { useFilters } from '@cdc/core/components/Filters'
24
- import { useHighlightedBars } from '../hooks/useHighlightedBars'
25
- import ConfigContext from '../ConfigContext'
26
- import useReduceData from '../hooks/useReduceData'
27
- import useRightAxis from '../hooks/useRightAxis'
28
- import WarningImage from '../images/warning.svg'
29
- import useMinMax from './../hooks/useMinMax'
30
-
31
- /* eslint-disable react-hooks/rules-of-hooks */
32
- const TextField = memo(({ label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'input', i = null, min = null, ...attributes }) => {
33
- const [value, setValue] = useState(stateValue)
34
-
35
- const [debouncedValue] = useDebounce(value, 500)
36
-
37
- useEffect(() => {
38
- if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
39
- updateField(section, subsection, fieldName, debouncedValue, i)
40
- }
41
- }, [debouncedValue]) // eslint-disable-line
42
-
43
- let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
44
-
45
- const onChange = e => {
46
- if ('number' !== type || min === null) {
47
- setValue(e.target.value)
48
- } else {
49
- if (!e.target.value || min <= parseFloat(e.target.value)) {
50
- setValue(e.target.value)
51
- } else {
52
- setValue(min.toString())
53
- }
54
- }
55
- }
56
-
57
- let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
58
-
59
- if ('textarea' === type) {
60
- formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
61
- }
62
-
63
- if ('number' === type) {
64
- formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
65
- }
66
-
67
- if ('date' === type) {
68
- formElement = <input type='date' name={name} onChange={onChange} {...attributes} value={value} />
69
- }
70
-
71
- return (
72
- <label>
73
- <span className='edit-label column-heading'>
74
- {label}
75
- {tooltip}
76
- </span>
77
- {formElement}
78
- </label>
79
- )
80
- })
81
-
82
- const CheckBox = memo(({ label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes }) => (
83
- <label className='checkbox column-heading'>
84
- <input
85
- type='checkbox'
86
- name={fieldName}
87
- checked={value}
88
- onChange={e => {
89
- updateField(section, subsection, fieldName, !value)
90
- }}
91
- {...attributes}
92
- />
93
- <span className='edit-label'>
94
- {label}
95
- {tooltip}
96
- </span>
97
- </label>
98
- ))
99
-
100
- const Select = memo(({ label, value, options, fieldName, section = null, subsection = null, required = false, tooltip, updateField, initial: initialValue, ...attributes }) => {
101
- let optionsJsx = options.map((optionName, index) => (
102
- <option value={optionName} key={index}>
103
- {optionName}
104
- </option>
105
- ))
106
-
107
- if (initialValue) {
108
- optionsJsx.unshift(
109
- <option value='' key='initial'>
110
- {initialValue}
111
- </option>
112
- )
113
- }
114
-
115
- return (
116
- <label>
117
- <span className='edit-label'>
118
- {label}
119
- {tooltip}
120
- </span>
121
- <select
122
- className={required && !value ? 'warning' : ''}
123
- name={fieldName}
124
- value={value}
125
- onChange={event => {
126
- updateField(section, subsection, fieldName, event.target.value)
127
- }}
128
- {...attributes}
129
- >
130
- {optionsJsx}
131
- </select>
132
- </label>
133
- )
134
- })
135
-
136
- const DataSuppression = memo(({ config, updateConfig, data }) => {
137
- const getColumnOptions = () => {
138
- const keys = new Set()
139
- data.forEach(d => {
140
- Object.keys(d).forEach(key => {
141
- keys.add(key)
142
- })
143
- })
144
- return [...keys]
145
- }
146
-
147
- const getIconOptions = () => {
148
- return ['star']
149
- }
150
-
151
- let removeColumn = i => {
152
- let suppressedData = []
153
-
154
- if (config.suppressedData) {
155
- suppressedData = [...config.suppressedData]
156
- }
157
-
158
- suppressedData.splice(i, 1)
159
-
160
- updateConfig({ ...config, suppressedData })
161
- }
162
-
163
- let addColumn = () => {
164
- let suppressedData = config.suppressedData ? [...config.suppressedData] : []
165
- suppressedData.push({ label: '', column: '', value: '', icon: '' })
166
- updateConfig({ ...config, suppressedData })
167
- }
168
-
169
- let update = (fieldName, value, i) => {
170
- let suppressedData = []
171
-
172
- if (config.suppressedData) {
173
- suppressedData = [...config.suppressedData]
174
- }
175
-
176
- suppressedData[i][fieldName] = value
177
- updateConfig({ ...config, suppressedData })
178
- }
179
-
180
- return (
181
- <>
182
- {config.suppressedData &&
183
- config.suppressedData.map(({ label, column, value, icon }, i) => {
184
- return (
185
- <div key={`suppressed-${i}`} className='edit-block'>
186
- <button
187
- type='button'
188
- className='remove-column'
189
- onClick={event => {
190
- event.preventDefault()
191
- removeColumn(i)
192
- }}
193
- >
194
- Remove
195
- </button>
196
- <Select value={column} initial='Select' fieldName='column' label='Column' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getColumnOptions()} />
197
- <TextField value={value} fieldName='value' label='Value' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} />
198
- <Select value={icon} initial='Select' fieldName='icon' label='Icon' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getIconOptions()} />
199
- <TextField value={label} fieldName='label' label='Label' placeholder='suppressed' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} />
200
- </div>
201
- )
202
- })}
203
-
204
- <button type='button' onClick={addColumn} className='btn full-width'>
205
- Add Suppression Class
206
- </button>
207
- </>
208
- )
209
- })
210
-
211
- const Regions = memo(({ config, updateConfig }) => {
212
- let regionUpdate = (fieldName, value, i) => {
213
- let regions = []
214
-
215
- if (config.regions) {
216
- regions = [...config.regions]
217
- }
218
-
219
- regions[i][fieldName] = value
220
- updateConfig({ ...config, regions })
221
- }
222
-
223
- // only for Regions
224
- let updateField = (section, subsection, fieldName, value, i) => regionUpdate(fieldName, value, i)
225
-
226
- let removeColumn = i => {
227
- let regions = []
228
-
229
- if (config.regions) {
230
- regions = [...config.regions]
231
- }
232
-
233
- regions.splice(i, 1)
234
-
235
- updateConfig({ ...config, regions })
236
- }
237
-
238
- let addColumn = () => {
239
- let regions = []
240
-
241
- if (config.regions) {
242
- regions = [...config.regions]
243
- }
244
-
245
- regions.push({})
246
-
247
- updateConfig({ ...config, regions })
248
- }
249
-
250
- return (
251
- <>
252
- {config.regions &&
253
- config.regions.map(({ label, color, from, to, background }, i) => (
254
- <div className='edit-block' key={`region-${i}`}>
255
- <button
256
- type='button'
257
- className='remove-column'
258
- onClick={event => {
259
- event.preventDefault()
260
- removeColumn(i)
261
- }}
262
- >
263
- Remove
264
- </button>
265
- <TextField value={label} label='Region Label' fieldName='label' i={i} updateField={updateField} />
266
- <div className='two-col-inputs'>
267
- <TextField value={color} label='Text Color' fieldName='color' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
268
- <TextField value={background} label='Background' fieldName='background' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
269
- </div>
270
- <div className='two-col-inputs'>
271
- <TextField
272
- value={from}
273
- label='From Value'
274
- fieldName='from'
275
- updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}
276
- tooltip={
277
- <Tooltip style={{ textTransform: 'none' }}>
278
- <Tooltip.Target>
279
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
280
- </Tooltip.Target>
281
- <Tooltip.Content>
282
- <p>The date needs to be in the original format of the data. Not the displayed format of the data.</p>
283
- </Tooltip.Content>
284
- </Tooltip>
285
- }
286
- />
287
- <TextField value={to} label='To Value' fieldName='to' updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)} />
288
- </div>
289
- </div>
290
- ))}
291
- {!config.regions && <p style={{ textAlign: 'center' }}>There are currently no regions.</p>}
292
- <button
293
- type='button'
294
- className='btn full-width'
295
- onClick={e => {
296
- e.preventDefault()
297
- addColumn()
298
- }}
299
- >
300
- Add Region
301
- </button>
302
- </>
303
- )
304
- })
305
-
306
- const EditorPanel = () => {
307
- const { config, updateConfig, transformedData: data, loading, colorPalettes, twoColorPalette, unfilteredData, excludedData, isDashboard, setParentConfig, missingRequiredSections, isDebug, setFilteredData, lineOptions, rawData } = useContext(ConfigContext)
308
-
309
- const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, unfilteredData)
310
-
311
- const { twoColorPalettes, sequential, nonSequential } = useColorPalette(config, updateConfig)
312
-
313
- const properties = { data, config }
314
- const { leftMax, rightMax } = useMinMax(properties)
315
-
316
- const {
317
- enabledChartTypes,
318
- headerColors,
319
- visSupportsTooltipLines,
320
- visSupportsNonSequentialPallete,
321
- visSupportsSequentialPallete,
322
- visSupportsReverseColorPalette,
323
- visHasLabelOnData,
324
- visHasNumbersOnBars,
325
- visHasAnchors,
326
- visHasBarBorders,
327
- visHasDataCutoff,
328
- visCanAnimate,
329
- visHasLegend,
330
- visHasBrushChart,
331
- visSupportsDateCategoryAxisLabel,
332
- visSupportsDateCategoryAxisLine,
333
- visSupportsDateCategoryAxisTicks,
334
- visSupportsDateCategoryTickRotation,
335
- visSupportsDateCategoryNumTicks,
336
- visSupportsRegions,
337
- visSupportsFilters,
338
- visSupportsValueAxisGridLines,
339
- visSupportsValueAxisLine,
340
- visSupportsValueAxisTicks,
341
- visSupportsValueAxisLabels,
342
- visSupportsBarSpace,
343
- visSupportsBarThickness,
344
- visSupportsFootnotes,
345
- visSupportsSuperTitle,
346
- visSupportsDataCutoff,
347
- visSupportsChartHeight,
348
- visSupportsLeftValueAxis,
349
- visSupportsTooltipOpacity,
350
- visSupportsRankByValue,
351
- visSupportsResponsiveTicks,
352
- visSupportsDateCategoryHeight,
353
- visHasDataSuppression
354
- } = useEditorPermissions()
355
-
356
- // argument acts as props
357
- const { handleFilterOrder, filterOrderOptions, filterStyleOptions } = useFilters({ config, setConfig: updateConfig, filteredData: data, setFilteredData })
358
-
359
- // when the visualization type changes we
360
- // have to update the individual series type & axis details
361
- // dataKey is unchanged here.
362
- // ie. { dataKey: 'series_name', type: 'Bar', axis: 'Left'}
363
- useEffect(() => {
364
- let newSeries = []
365
- if (config.series) {
366
- newSeries = config.series.map(series => {
367
- return {
368
- ...series,
369
- type: config.visualizationType === 'Combo' ? 'Bar' : config.visualizationType ? config.visualizationType : 'Bar',
370
- axis: 'Left'
371
- }
372
- })
373
- }
374
-
375
- updateConfig({
376
- ...config,
377
- series: newSeries
378
- })
379
- }, [config.visualizationType]) // eslint-disable-line
380
-
381
- // Scatter Plots default date/category axis is 'continuous'
382
- useEffect(() => {
383
- if (config.visualizationType === 'Scatter Plot') {
384
- updateConfig({
385
- ...config,
386
- xAxis: {
387
- ...config.xAxis,
388
- type: 'continuous'
389
- }
390
- })
391
- }
392
- }, [])
393
-
394
- useEffect(() => {
395
- if (config.visualizationType !== 'Bar') {
396
- updateConfig({ ...config, tooltips: { ...config.tooltips, singleSeries: false } })
397
- }
398
- }, [config.visualizationType])
399
-
400
- const { hasRightAxis } = useRightAxis({ config: config, yMax: config.yAxis.size, data: config.data, updateConfig })
401
-
402
- const getItemStyle = (isDragging, draggableStyle) => ({
403
- ...draggableStyle
404
- })
405
-
406
- const sortableItemStyles = {
407
- animate: false,
408
- animateReplay: true,
409
- display: 'block',
410
- boxSizing: 'border-box',
411
- border: '1px solid #D1D1D1',
412
- borderRadius: '2px',
413
- background: '#F1F1F1',
414
- padding: '.4em .6em',
415
- fontSize: '.8em',
416
- marginRight: '.3em',
417
- marginBottom: '.3em',
418
- cursor: 'move',
419
- zIndex: '999'
420
- }
421
-
422
- const enforceRestrictions = updatedConfig => {
423
- if (updatedConfig.orientation === 'horizontal') {
424
- updatedConfig.labels = false
425
- }
426
- if (updatedConfig.table.show === undefined) {
427
- updatedConfig.table.show = !isDashboard
428
- }
429
- // DEV-3293 - Force combo to always be vertical
430
- if (updatedConfig.visualizationType === 'Combo') {
431
- updatedConfig.orientation = 'vertical'
432
- }
433
- }
434
-
435
- const updateField = (section, subsection, fieldName, newValue) => {
436
- if (isDebug) console.log('#COVE: CHART: EditorPanel: section, subsection, fieldName, newValue', section, subsection, fieldName, newValue) // eslint-disable-line
437
-
438
- if (section === 'boxplot' && subsection === 'legend') {
439
- updateConfig({
440
- ...config,
441
- [section]: {
442
- ...config[section],
443
- [subsection]: {
444
- ...config.boxplot[subsection],
445
- [fieldName]: newValue
446
- }
447
- }
448
- })
449
- return
450
- }
451
-
452
- if (section === 'boxplot' && subsection === 'labels') {
453
- updateConfig({
454
- ...config,
455
- [section]: {
456
- ...config[section],
457
- [subsection]: {
458
- ...config.boxplot[subsection],
459
- [fieldName]: newValue
460
- }
461
- }
462
- })
463
- return
464
- }
465
-
466
- if (section === 'columns' && subsection !== '' && fieldName !== '') {
467
- updateConfig({
468
- ...config,
469
- [section]: {
470
- ...config[section],
471
- [subsection]: {
472
- ...config[section][subsection],
473
- [fieldName]: newValue
474
- }
475
- }
476
- })
477
- return
478
- }
479
- if (null === section && null === subsection) {
480
- let updatedConfig = { ...config, [fieldName]: newValue }
481
- enforceRestrictions(updatedConfig)
482
- updateConfig(updatedConfig)
483
- return
484
- }
485
-
486
- const isArray = Array.isArray(config[section])
487
-
488
- let sectionValue = isArray ? [...config[section], newValue] : { ...config[section], [fieldName]: newValue }
489
-
490
- if (null !== subsection) {
491
- if (isArray) {
492
- sectionValue = [...config[section]]
493
- sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue }
494
- } else if (typeof newValue === 'string') {
495
- sectionValue[subsection] = newValue
496
- } else {
497
- sectionValue = { ...config[section], [subsection]: { ...config[section][subsection], [fieldName]: newValue } }
498
- }
499
- }
500
-
501
- let updatedConfig = { ...config, [section]: sectionValue }
502
-
503
- enforceRestrictions(updatedConfig)
504
-
505
- updateConfig(updatedConfig)
506
- }
507
-
508
- const [displayPanel, setDisplayPanel] = useState(true)
509
-
510
- if (loading) {
511
- return null
512
- }
513
-
514
- useEffect(() => {
515
- if (!config.general?.boxplot) return
516
- if (!config.general.boxplot.firstQuartilePercentage) {
517
- updateConfig({
518
- ...config,
519
- boxplot: {
520
- ...config.boxplot,
521
- firstQuartilePercentage: 25
522
- }
523
- })
524
- }
525
- }, [config])
526
-
527
- const setLollipopShape = shape => {
528
- updateConfig({
529
- ...config,
530
- lollipopShape: shape
531
- })
532
- }
533
-
534
- const removeFilter = index => {
535
- let filters = [...config.filters]
536
-
537
- filters.splice(index, 1)
538
-
539
- updateConfig({ ...config, filters })
540
- }
541
-
542
- const updateFilterProp = (name, index, value) => {
543
- let filters = [...config.filters]
544
-
545
- filters[index][name] = value
546
-
547
- updateConfig({ ...config, filters })
548
- }
549
-
550
- const addNewFilter = () => {
551
- let filters = config.filters ? [...config.filters] : []
552
-
553
- filters.push({ values: [] })
554
-
555
- updateConfig({ ...config, filters })
556
- }
557
-
558
- const addNewSeries = seriesKey => {
559
- let newSeries = config.series ? [...config.series] : []
560
- let forecastingStages = Array.from(new Set(data.map(item => item[seriesKey])))
561
- let forecastingStageArr = []
562
-
563
- forecastingStages.forEach(stage => {
564
- forecastingStageArr.push({ key: stage })
565
- })
566
-
567
- if (config.visualizationType === 'Forecasting') {
568
- newSeries.push({ dataKey: seriesKey, type: config.visualizationType, stages: forecastingStageArr, stageColumn: seriesKey, axis: 'Left', tooltip: true })
569
- } else {
570
- newSeries.push({ dataKey: seriesKey, type: config.visualizationType, axis: 'Left', tooltip: true })
571
- }
572
- updateConfig({ ...config, series: newSeries }) // left axis series keys
573
- }
574
-
575
- const sortSeries = e => {
576
- const series = config.series[0].dataKey
577
- const sorted = data.sort((a, b) => a[series] - b[series])
578
- const newData = e === 'asc' ? sorted : sorted.reverse()
579
- updateConfig({ ...config }, newData)
580
- }
581
-
582
- const removeSeries = seriesKey => {
583
- let series = [...config.series]
584
- let seriesIndex = -1
585
-
586
- for (let i = 0; i < series.length; i++) {
587
- if (series[i].dataKey === seriesKey) {
588
- seriesIndex = i
589
- break
590
- }
591
- }
592
-
593
- if (seriesIndex !== -1) {
594
- series.splice(seriesIndex, 1)
595
-
596
- let newConfig = { ...config, series }
597
-
598
- if (series.length === 0) {
599
- delete newConfig.series
600
- }
601
-
602
- updateConfig(newConfig)
603
- }
604
-
605
- if (config.visualizationType === 'Paired Bar') {
606
- updateConfig({
607
- ...config,
608
- series: []
609
- })
610
- }
611
- }
612
-
613
- const addNewExclusion = exclusionKey => {
614
- let newExclusion = [...config.exclusions.keys]
615
- newExclusion.push(exclusionKey)
616
-
617
- let payload = { ...config.exclusions, keys: newExclusion }
618
- updateConfig({ ...config, exclusions: payload })
619
- }
620
-
621
- const removeExclusion = excludeValue => {
622
- let exclusionsIndex = -1
623
- let exclusions = [...config.exclusions.keys]
624
-
625
- for (let i = 0; i < exclusions.length; i++) {
626
- if (exclusions[i] === excludeValue) {
627
- exclusionsIndex = i
628
- break
629
- }
630
- }
631
-
632
- if (exclusionsIndex !== -1) {
633
- exclusions.splice(exclusionsIndex, 1)
634
-
635
- let newExclusions = { ...config.exclusions, keys: exclusions }
636
- let newExclusionsPayload = { ...config, exclusions: newExclusions }
637
-
638
- if (exclusions.length === 0) {
639
- delete newExclusionsPayload.exclusions.keys
640
- }
641
-
642
- updateConfig(newExclusionsPayload)
643
- }
644
- }
645
-
646
- const getFilters = () => {
647
- let columns = {}
648
-
649
- unfilteredData.forEach(row => {
650
- Object.keys(row).forEach(columnName => (columns[columnName] = true))
651
- })
652
-
653
- return Object.keys(columns)
654
- }
655
-
656
- const getColumns = (filter = true) => {
657
- let columns = {}
658
- unfilteredData.forEach(row => {
659
- Object.keys(row).forEach(columnName => (columns[columnName] = true))
660
- })
661
-
662
- if (filter) {
663
- const { lower, upper } = config.confidenceKeys || {}
664
- Object.keys(columns).forEach(key => {
665
- if ((config.series && config.series.filter(series => series.dataKey === key).length > 0) || (config.confidenceKeys && Object.keys(config.confidenceKeys).includes(key) && ((lower && upper) || lower || upper) && key !== lower && key !== upper)) {
666
- delete columns[key]
667
- }
668
- })
669
- }
670
-
671
- return Object.keys(columns)
672
- }
673
-
674
- const getDataValueOptions = data => {
675
- if (!data) return []
676
- const set = new Set()
677
- for (let i = 0; i < data.length; i++) {
678
- for (const [key] of Object.entries(data[i])) {
679
- set.add(key)
680
- }
681
- }
682
- return Array.from(set)
683
- }
684
-
685
- const getDataValues = (dataKey, unique = false) => {
686
- let values = []
687
- excludedData.forEach(e => {
688
- values.push(e[dataKey])
689
- })
690
- return unique ? [...new Set(values)] : values
691
- }
692
-
693
- const showBarStyleOptions = () => {
694
- if ((config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar') && config.visualizationSubType !== 'stacked' && (config.orientation === 'horizontal' || config.orientation === 'vertical')) {
695
- return ['flat', 'rounded', 'lollipop']
696
- } else {
697
- return ['flat', 'rounded']
698
- }
699
- }
700
-
701
- const onBackClick = () => {
702
- setDisplayPanel(!displayPanel)
703
- }
704
-
705
- const Error = () => {
706
- return (
707
- <section className='waiting'>
708
- <section className='waiting-container'>
709
- <h3>Error With Configuration</h3>
710
- <p>{config.runtime.editorErrorMessage}</p>
711
- </section>
712
- </section>
713
- )
714
- }
715
-
716
- const Confirm = () => {
717
- const confirmDone = e => {
718
- e.preventDefault()
719
-
720
- let newConfig = { ...config }
721
- delete newConfig.newViz
722
-
723
- updateConfig(newConfig)
724
- }
725
-
726
- return (
727
- <section className='waiting'>
728
- <section className='waiting-container'>
729
- <h3>Finish Configuring</h3>
730
- <p>Set all required options to the left and confirm below to display a preview of the chart.</p>
731
- <button className='btn' style={{ margin: '1em auto' }} disabled={missingRequiredSections()} onClick={confirmDone}>
732
- I'm Done
733
- </button>
734
- </section>
735
- </section>
736
- )
737
- }
738
-
739
- const convertStateToConfig = () => {
740
- let strippedState = JSON.parse(JSON.stringify(config))
741
- if (false === missingRequiredSections()) {
742
- delete strippedState.newViz
743
- }
744
- delete strippedState.runtime
745
-
746
- return strippedState
747
- }
748
-
749
- useEffect(() => {
750
- // Pass up to Editor if needed
751
- if (setParentConfig) {
752
- const newConfig = convertStateToConfig()
753
- setParentConfig(newConfig)
754
- }
755
-
756
- // eslint-disable-next-line react-hooks/exhaustive-deps
757
- }, [config])
758
-
759
- // when the orientation changes, swap x and y axis anchors
760
- useEffect(() => {
761
- const prevXAnchors = config.xAxis.anchors.length > 0 ? config.xAxis.anchors : []
762
- const prevYAnchors = config.yAxis.anchors.length > 0 ? config.yAxis.anchors : []
763
-
764
- updateConfig({
765
- ...config,
766
- xAxis: {
767
- ...config.xAxis,
768
- anchors: prevYAnchors
769
- },
770
- yAxis: {
771
- ...config.yAxis,
772
- anchors: prevXAnchors
773
- }
774
- })
775
- }, [config.orientation])
776
-
777
- // Set paired bars to be horizontal, even though that option doesn't display
778
- useEffect(() => {
779
- if (config.visualizationType === 'Paired Bar') {
780
- updateConfig({
781
- ...config,
782
- orientation: 'horizontal'
783
- })
784
- }
785
- }, []) // eslint-disable-line
786
-
787
- useEffect(() => {
788
- if (config.orientation === 'horizontal') {
789
- updateConfig({
790
- ...config,
791
- lollipopShape: config.lollipopShape
792
- })
793
- }
794
- }, [config.isLollipopChart, config.lollipopShape]) // eslint-disable-line
795
-
796
- /// temporary force orientation untill we support Vartical deviaton bar
797
- useEffect(() => {
798
- if (config.visualizationType === 'Deviation Bar') {
799
- updateConfig({ ...config, orientation: 'horizontal' })
800
- }
801
- }, [config.visualizationType])
802
-
803
- const ExclusionsList = useCallback(() => {
804
- const exclusions = [...config.exclusions.keys]
805
- return (
806
- <ul className='series-list'>
807
- {exclusions.map((exclusion, index) => {
808
- return (
809
- <li key={exclusion}>
810
- <div className='series-list__name' data-title={exclusion}>
811
- <div className='series-list__name--text'>{exclusion}</div>
812
- </div>
813
- <button className='series-list__remove' onClick={() => removeExclusion(exclusion)}>
814
- &#215;
815
- </button>
816
- </li>
817
- )
818
- })}
819
- </ul>
820
- )
821
- }, [config]) // eslint-disable-line
822
-
823
- const handleSeriesChange = (idx1, idx2) => {
824
- let seriesOrder = config.series
825
- let [movedItem] = seriesOrder.splice(idx1, 1)
826
- seriesOrder.splice(idx2, 0, movedItem)
827
- updateConfig({ ...config, series: seriesOrder })
828
- }
829
-
830
- if (config.isLollipopChart && config?.series?.length > 1) {
831
- config.runtime.editorErrorMessage = 'Lollipop charts must use only one data series'
832
- }
833
- if (config.visualizationType === 'Paired Bar' && config?.series?.length !== 2) {
834
- config.runtime.editorErrorMessage = 'Paired Bar charts must use exactly two data series'
835
- }
836
-
837
- if (config.visualizationType === 'Deviation Bar' && config?.series?.length !== 1) {
838
- config.runtime.editorErrorMessage = 'Deviation Bar charts must use exactly one data series'
839
- }
840
- if (config.isLollipopChart && config?.series?.length === 0) {
841
- config.runtime.editorErrorMessage = 'Add a data series'
842
- }
843
-
844
- const section = config.orientation === 'horizontal' ? 'xAxis' : 'yAxis'
845
- const [warningMsg, setWarningMsg] = useState({ maxMsg: '', minMsg: '', rightMaxMessage: '', minMsgRight: '' })
846
-
847
- const validateMaxValue = () => {
848
- const enteredValue = config[section].max
849
- const enteredRightMax = config[section].rightMax
850
-
851
- let message = ''
852
- let rightMaxMessage = ''
853
-
854
- if (config.visualizationType !== 'Combo') {
855
- switch (true) {
856
- case enteredValue && parseFloat(enteredValue) < parseFloat(maxValue) && existPositiveValue:
857
- message = 'Max value must be more than ' + maxValue
858
- break
859
- case enteredValue && parseFloat(enteredValue) < 0 && !existPositiveValue:
860
- message = 'Value must be more than or equal to 0'
861
- break
862
- default:
863
- message = ''
864
- }
865
- }
866
-
867
- if (config.visualizationType === 'Combo') {
868
- switch (true) {
869
- case enteredValue && parseFloat(enteredValue) < leftMax:
870
- message = 'Max value must be more than ' + leftMax
871
- break
872
- case enteredRightMax && parseFloat(enteredRightMax) < rightMax:
873
- rightMaxMessage = 'Max value must be more than ' + rightMax
874
- break
875
- case enteredValue && parseFloat(enteredValue) < 0 && !existPositiveValue:
876
- message = 'Value must be more than or equal to 0'
877
- break
878
- default:
879
- message = ''
880
- }
881
- }
882
-
883
- setWarningMsg(prevMsg => ({ ...prevMsg, maxMsg: message, rightMaxMessage: rightMaxMessage }))
884
- }
885
-
886
- const validateMinValue = () => {
887
- const enteredValue = config[section].min
888
- let minVal = Number(minValue)
889
- let message = ''
890
-
891
- switch (true) {
892
- case config.useLogScale && ['Line', 'Combo', 'Bar'].includes(config.visualizationType) && enteredValue < 0:
893
- message = 'Negative numbers are not supported in logarithmic scale'
894
- break
895
- case (config.visualizationType === 'Line' || config.visualizationType === 'Spark Line') && enteredValue && parseFloat(enteredValue) > minVal:
896
- message = 'Value should not exceed ' + minValue
897
- break
898
- case config.visualizationType === 'Combo' && isAllLine && enteredValue && parseFloat(enteredValue) > minVal:
899
- message = 'Value should not exceed ' + minValue
900
- break
901
- case (config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && enteredValue && minVal > 0 && parseFloat(enteredValue) > 0:
902
- message = config.useLogScale ? 'Value must be equal to 0' : 'Value must be less than or equal to 0'
903
- break
904
- case config.visualizationType === 'Deviation Bar' && parseFloat(enteredValue) >= Math.min(minVal, config.xAxis.target):
905
- message = 'Value must be less than ' + Math.min(minVal, config.xAxis.target)
906
- break
907
- case config.visualizationType !== 'Deviation Bar' && enteredValue && minVal < 0 && parseFloat(enteredValue) > minVal:
908
- message = 'Value should not exceed ' + minValue
909
- break
910
- default:
911
- message = ''
912
- }
913
- setWarningMsg(prevMsg => ({ ...prevMsg, minMsg: message }))
914
- }
915
- useEffect(() => {
916
- validateMinValue()
917
- validateMaxValue()
918
- }, [minValue, maxValue, config]) // eslint-disable-line
919
-
920
- const isLoadedFromUrl = config?.dataKey?.includes('http://') || config?.dataKey?.includes('https://')
921
-
922
- // if isDebug = true, then try to set the category and data col to reduce clicking
923
- const setCategoryAxis = () => {
924
- // only for debug mode
925
- if (undefined !== isDebug && isDebug && !config?.xAxis?.dataKey) {
926
- // then try to set the x axis to appropriate value so we dont have to manually do it
927
- let datakeys = getColumns(false)
928
- if (datakeys.includes('Date')) return 'Date'
929
- if (datakeys.includes('Race')) return 'Race'
930
- if (datakeys.includes('Month')) return 'Month'
931
- // add other known Category cols here to extend debug
932
- }
933
- return config?.xAxis?.dataKey || ''
934
- }
935
- const setDataColumn = () => {
936
- // only for debug mode
937
- if (undefined !== isDebug && isDebug && getColumns(false).length > 0) {
938
- // then try to set the x axis to appropriate value so we dont have to manually do it
939
- let datacols = getColumns(false).filter(x => x !== setCategoryAxis())
940
- if (datacols.length > 0) {
941
- return datacols[0]
942
- }
943
- }
944
- return ''
945
- }
946
- if (isDebug && !config.xAxis.dataKey) config.xAxis.dataKey = setCategoryAxis()
947
- if (isDebug && config?.series?.length === 0) {
948
- let setdatacol = setDataColumn()
949
- if (setdatacol !== '') addNewSeries(setdatacol)
950
- if (isDebug) console.log('### COVE DEBUG: Chart: Setting default datacol=', setdatacol) // eslint-disable-line
951
- }
952
-
953
- const chartsWithOptions = ['Area Chart', 'Combo', 'Line', 'Bar', 'Forecasting']
954
-
955
- const columnsOptions = [
956
- <option value='' key={'Select Option'}>
957
- - Select Option -
958
- </option>
959
- ]
960
-
961
- if (config.data && config.series) {
962
- Object.keys(config.data?.[0] || []).map(colName => {
963
- // OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
964
- const found = config?.series.some(series => series.dataKey === colName)
965
- if (colName !== config.xAxis.dataKey && !found) {
966
- // if not the index then add it
967
- return columnsOptions.push(
968
- <option value={colName} key={colName}>
969
- {colName}
970
- </option>
971
- )
972
- }
973
- })
974
-
975
- let columnsByKey = {}
976
- config.data.forEach(datum => {
977
- Object.keys(datum).forEach(key => {
978
- columnsByKey[key] = columnsByKey[key] || []
979
- const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
980
-
981
- if (columnsByKey[key].indexOf(value) === -1) {
982
- columnsByKey[key].push(value)
983
- }
984
- })
985
- })
986
- }
987
-
988
- // for pie charts
989
- if (!config.data && data) {
990
- if (!data[0]) return
991
- Object.keys(data[0]).map(colName => {
992
- // OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
993
- const found = data.some(el => el.dataKey === colName)
994
- if (colName !== config.xAxis.dataKey && !found) {
995
- // if not the index then add it
996
- return columnsOptions.push(
997
- <option value={colName} key={colName}>
998
- {colName}
999
- </option>
1000
- )
1001
- }
1002
- })
1003
-
1004
- let columnsByKey = {}
1005
- data.forEach(datum => {
1006
- Object.keys(datum).forEach(key => {
1007
- columnsByKey[key] = columnsByKey[key] || []
1008
- const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
1009
-
1010
- if (columnsByKey[key].indexOf(value) === -1) {
1011
- columnsByKey[key].push(value)
1012
- }
1013
- })
1014
- })
1015
- }
1016
-
1017
- // prevents adding duplicates
1018
- const additionalColumns = Object.keys(config.columns).filter(value => {
1019
- const defaultCols = [config.xAxis.dataKey] // ['geo', 'navigate', 'primary', 'latitude', 'longitude']
1020
-
1021
- if (true === defaultCols.includes(value)) {
1022
- return false
1023
- }
1024
- return true
1025
- })
1026
-
1027
- // just adds a new column but not set to any data yet
1028
- const addAdditionalColumn = number => {
1029
- const columnKey = `additionalColumn${number}`
1030
-
1031
- updateConfig({
1032
- ...config,
1033
- columns: {
1034
- ...config.columns,
1035
- [columnKey]: {
1036
- label: 'New Column',
1037
- dataTable: false,
1038
- tooltips: false,
1039
- prefix: '',
1040
- suffix: '',
1041
- forestPlot: false,
1042
- startingPoint: '0',
1043
- forestPlotAlignRight: false
1044
- }
1045
- }
1046
- })
1047
- }
1048
-
1049
- const removeAdditionalColumn = columnName => {
1050
- const newColumns = config.columns
1051
-
1052
- delete newColumns[columnName]
1053
-
1054
- updateConfig({
1055
- ...config,
1056
- columns: newColumns
1057
- })
1058
- }
1059
-
1060
- const editColumn = async (addCol, columnName, setval) => {
1061
- // not using special classes like in map editorpanel so removed those cases
1062
- switch (columnName) {
1063
- case 'name':
1064
- updateConfig({
1065
- ...config,
1066
- columns: {
1067
- ...config.columns,
1068
- [addCol]: {
1069
- ...config.columns[addCol],
1070
- [columnName]: setval
1071
- }
1072
- }
1073
- })
1074
- break
1075
- default:
1076
- updateConfig({
1077
- ...config,
1078
- columns: {
1079
- ...config.columns,
1080
- [addCol]: {
1081
- ...config.columns[addCol],
1082
- [columnName]: setval
1083
- }
1084
- }
1085
- })
1086
- break
1087
- }
1088
- }
1089
-
1090
- // prettier-ignore
1091
- const {
1092
- highlightedBarValues,
1093
- highlightedSeriesValues,
1094
- handleUpdateHighlightedBar,
1095
- handleAddNewHighlightedBar,
1096
- handleRemoveHighlightedBar,
1097
- handleUpdateHighlightedBarColor,
1098
- handleHighlightedBarLegendLabel,
1099
- handleUpdateHighlightedBorderWidth
1100
- } = useHighlightedBars(config, updateConfig)
1101
-
1102
- const updateSeriesTooltip = (column, event) => {
1103
- let updatedColumns = config.columns
1104
-
1105
- updatedColumns[column].tooltips = event
1106
-
1107
- updateConfig({
1108
- ...config,
1109
- columns: updatedColumns
1110
- })
1111
- }
1112
-
1113
- return (
1114
- <ErrorBoundary component='EditorPanel'>
1115
- {config.newViz && <Confirm />}
1116
- {undefined === config.newViz && config.runtime && config.runtime.editorErrorMessage && <Error />}
1117
- <button className={displayPanel ? `editor-toggle` : `editor-toggle collapsed`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick}></button>
1118
- <section className={`${displayPanel ? 'editor-panel cove' : 'hidden editor-panel cove'}${isDashboard ? ' dashboard' : ''}`}>
1119
- <div aria-level='2' role='heading' className='heading-2'>
1120
- Configure Chart
1121
- </div>
1122
- <section className='form-container'>
1123
- <Accordion allowZeroExpanded={true}>
1124
- <AccordionItem>
1125
- {' '}
1126
- {/* General */}
1127
- <AccordionItemHeading>
1128
- <AccordionItemButton>General</AccordionItemButton>
1129
- </AccordionItemHeading>
1130
- <AccordionItemPanel>
1131
- <Select value={config.visualizationType} fieldName='visualizationType' label='Chart Type' updateField={updateField} options={enabledChartTypes} />
1132
- {(config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || config.visualizationType === 'Area Chart') && (
1133
- <Select value={config.visualizationSubType || 'Regular'} fieldName='visualizationSubType' label='Chart Subtype' updateField={updateField} options={['regular', 'stacked']} />
1134
- )}
1135
- {config.visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked' && (
1136
- <Select value={config.stackedAreaChartLineType || 'Linear'} fieldName='stackedAreaChartLineType' label='Stacked Area Chart Line Type' updateField={updateField} options={Object.keys(approvedCurveTypes)} />
1137
- )}
1138
- {config.visualizationType === 'Bar' && <Select value={config.orientation || 'vertical'} fieldName='orientation' label='Orientation' updateField={updateField} options={['vertical', 'horizontal']} />}
1139
- {config.visualizationType === 'Deviation Bar' && <Select label='Orientation' options={['horizontal']} />}
1140
- {(config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar') && <Select value={config.isLollipopChart ? 'lollipop' : config.barStyle || 'flat'} fieldName='barStyle' label='bar style' updateField={updateField} options={showBarStyleOptions()} />}
1141
- {(config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar') && config.barStyle === 'rounded' && <Select value={config.tipRounding || 'top'} fieldName='tipRounding' label='tip rounding' updateField={updateField} options={['top', 'full']} />}
1142
- {(config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar') && config.barStyle === 'rounded' && (
1143
- <Select value={config.roundingStyle || 'standard'} fieldName='roundingStyle' label='rounding style' updateField={updateField} options={['standard', 'shallow', 'finger']} />
1144
- )}
1145
- {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']} />}
1146
- {visHasNumbersOnBars() ? (
1147
- <CheckBox value={config.yAxis.displayNumbersOnBar} section='yAxis' fieldName='displayNumbersOnBar' label={config.isLollipopChart ? 'Display Numbers after Bar' : 'Display Numbers on Bar'} updateField={updateField} />
1148
- ) : (
1149
- visHasLabelOnData() && <CheckBox value={config.labels} fieldName='labels' label='Display label on data' updateField={updateField} />
1150
- )}
1151
- {config.visualizationType === 'Pie' && <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />}
1152
-
1153
- <TextField
1154
- value={config.title || 'Chart Title'}
1155
- fieldName='title'
1156
- id='title'
1157
- label='Title'
1158
- placeholder='Chart Title'
1159
- //defaultValue='Chart Title'
1160
- updateField={updateField}
1161
- //onChange={handleTitleChange}
1162
- tooltip={
1163
- <Tooltip style={{ textTransform: 'none' }}>
1164
- <Tooltip.Target>
1165
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1166
- </Tooltip.Target>
1167
- <Tooltip.Content>
1168
- <p>Title is required to set the name of the download file but can be hidden using the option below.</p>
1169
- </Tooltip.Content>
1170
- </Tooltip>
1171
- }
1172
- />
1173
- <CheckBox value={config.showTitle} fieldName='showTitle' label='Show Title' updateField={updateField} />
1174
-
1175
- {visSupportsSuperTitle() && (
1176
- <TextField
1177
- value={config.superTitle}
1178
- updateField={updateField}
1179
- fieldName='superTitle'
1180
- label='Super Title'
1181
- placeholder='Super Title'
1182
- tooltip={
1183
- <Tooltip style={{ textTransform: 'none' }}>
1184
- <Tooltip.Target>
1185
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1186
- </Tooltip.Target>
1187
- <Tooltip.Content>
1188
- <p>Super Title</p>
1189
- </Tooltip.Content>
1190
- </Tooltip>
1191
- }
1192
- />
1193
- )}
1194
-
1195
- <TextField
1196
- type='textarea'
1197
- value={config.introText}
1198
- updateField={updateField}
1199
- fieldName='introText'
1200
- label='Message'
1201
- tooltip={
1202
- <Tooltip style={{ textTransform: 'none' }}>
1203
- <Tooltip.Target>
1204
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1205
- </Tooltip.Target>
1206
- <Tooltip.Content>
1207
- <p>Intro Text</p>
1208
- </Tooltip.Content>
1209
- </Tooltip>
1210
- }
1211
- />
1212
-
1213
- <TextField
1214
- type='textarea'
1215
- value={config.description}
1216
- fieldName='description'
1217
- label='Subtext/Citation'
1218
- updateField={updateField}
1219
- tooltip={
1220
- <Tooltip style={{ textTransform: 'none' }}>
1221
- <Tooltip.Target>
1222
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1223
- </Tooltip.Target>
1224
- <Tooltip.Content>
1225
- <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
1226
- </Tooltip.Content>
1227
- </Tooltip>
1228
- }
1229
- />
1230
-
1231
- {visSupportsFootnotes() && (
1232
- <TextField
1233
- type='textarea'
1234
- value={config.footnotes}
1235
- updateField={updateField}
1236
- fieldName='footnotes'
1237
- label='Footnotes'
1238
- tooltip={
1239
- <Tooltip style={{ textTransform: 'none' }}>
1240
- <Tooltip.Target>
1241
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1242
- </Tooltip.Target>
1243
- <Tooltip.Content>
1244
- <p>Footnotes</p>
1245
- </Tooltip.Content>
1246
- </Tooltip>
1247
- }
1248
- />
1249
- )}
1250
-
1251
- {visSupportsChartHeight() && config.orientation === 'vertical' && <TextField type='number' value={config.heights.vertical} section='heights' fieldName='vertical' label='Chart Height' updateField={updateField} />}
1252
- </AccordionItemPanel>
1253
- </AccordionItem>
1254
- {config.visualizationType === 'Forest Plot' && <ForestPlotSettings />}
1255
- {config.visualizationType !== 'Pie' && config.visualizationType !== 'Forest Plot' && (
1256
- <AccordionItem>
1257
- <AccordionItemHeading>
1258
- <AccordionItemButton>Data Series {(!config.series || config.series.length === 0 || (config.visualizationType === 'Paired Bar' && config.series.length < 2)) && <WarningImage width='25' className='warning-icon' />}</AccordionItemButton>
1259
- </AccordionItemHeading>
1260
- <AccordionItemPanel>
1261
- {(!config.series || config.series.length === 0) && config.visualizationType !== 'Paired Bar' && <p className='warning'>At least one series is required</p>}
1262
- {(!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>}
1263
- <>
1264
- <Select
1265
- fieldName='visualizationType'
1266
- label='Add Data Series'
1267
- initial='Select'
1268
- onChange={e => {
1269
- if (e.target.value !== '' && e.target.value !== 'Select') {
1270
- addNewSeries(e.target.value)
1271
- }
1272
- e.target.value = ''
1273
- }}
1274
- options={getColumns()}
1275
- />
1276
- {config.series && config.series.length !== 0 && (
1277
- <Series.Wrapper getColumns={getColumns}>
1278
- <fieldset>
1279
- <legend className='edit-label float-left'>Displaying</legend>
1280
- <Tooltip style={{ textTransform: 'none' }}>
1281
- <Tooltip.Target>
1282
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1283
- </Tooltip.Target>
1284
- <Tooltip.Content>
1285
- <p>A data series is a set of related data points plotted in a chart and typically represented in the chart legend.</p>
1286
- </Tooltip.Content>
1287
- </Tooltip>
1288
- </fieldset>
1289
-
1290
- <DragDropContext onDragEnd={({ source, destination }) => handleSeriesChange(source.index, destination.index)}>
1291
- <Droppable droppableId='filter_order'>
1292
- {/* prettier-ignore */}
1293
- {provided => {
1294
- return (
1295
- <ul {...provided.droppableProps} className='series-list' ref={provided.innerRef}>
1296
- <Series.List series={config.series} getItemStyle={getItemStyle} sortableItemStyles={sortableItemStyles} chartsWithOptions={chartsWithOptions} />
1297
- {provided.placeholder}
1298
- </ul>
1299
- )
1300
- }}
1301
- </Droppable>
1302
- </DragDropContext>
1303
- </Series.Wrapper>
1304
- )}
1305
- </>
1306
- {config.series && config.series.length <= 1 && config.visualizationType === 'Bar' && (
1307
- <>
1308
- <span className='divider-heading'>Confidence Keys</span>
1309
- <Select value={config.confidenceKeys.upper || ''} section='confidenceKeys' fieldName='upper' label='Upper' updateField={updateField} initial='Select' options={getColumns()} />
1310
- <Select value={config.confidenceKeys.lower || ''} section='confidenceKeys' fieldName='lower' label='Lower' updateField={updateField} initial='Select' options={getColumns()} />
1311
- </>
1312
- )}
1313
- {visSupportsRankByValue() && config.series && config.series.length === 1 && <Select fieldName='visualizationType' label='Rank by Value' initial='Select' onChange={e => sortSeries(e.target.value)} options={['asc', 'desc']} />}
1314
- {/* {visHasDataSuppression() && <DataSuppression config={config} updateConfig={updateConfig} data={data} />} */}
1315
- </AccordionItemPanel>
1316
- </AccordionItem>
1317
- )}
1318
- {config.visualizationType === 'Box Plot' && (
1319
- <AccordionItem>
1320
- <AccordionItemHeading>
1321
- <AccordionItemButton>Measures</AccordionItemButton>
1322
- </AccordionItemHeading>
1323
- <AccordionItemPanel>
1324
- <h4 style={{ fontSize: '18px' }}>Labels for 5-Number Summary</h4>
1325
-
1326
- {/* prettier-ignore */}
1327
- {/* max */}
1328
- <TextField
1329
- type='text'
1330
- value={config.boxplot.labels.maximum}
1331
- fieldName='maximum'
1332
- section='boxplot'
1333
- subsection='labels'
1334
- label='Maximum'
1335
- updateField={updateField}
1336
- tooltip={
1337
- <Tooltip style={{ textTransform: 'none' }}>
1338
- <Tooltip.Target>
1339
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1340
- </Tooltip.Target>
1341
- <Tooltip.Content>
1342
- <p>Highest value, excluding outliers</p>
1343
- </Tooltip.Content>
1344
- </Tooltip>
1345
- }
1346
- />
1347
-
1348
- {/* prettier-ignore */}
1349
- {/* Q3 */}
1350
- <TextField
1351
- type='text'
1352
- value={config.boxplot.labels.q3}
1353
- fieldName='q3'
1354
- section='boxplot'
1355
- subsection='labels'
1356
- label='Upper Quartile'
1357
- updateField={updateField}
1358
- tooltip={
1359
- <Tooltip style={{ textTransform: 'none' }}>
1360
- <Tooltip.Target>
1361
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1362
- </Tooltip.Target>
1363
- <Tooltip.Content>
1364
- <p>Represented by top line of box. 25% of data are higher.</p>
1365
- </Tooltip.Content>
1366
- </Tooltip>
1367
- }
1368
- />
1369
-
1370
- {/* prettier-ignore */}
1371
- {/* median */}
1372
- <TextField
1373
- type='text'
1374
- value={config.boxplot.labels.median}
1375
- fieldName='median'
1376
- section='boxplot'
1377
- subsection='labels'
1378
- label='Median'
1379
- updateField={updateField}
1380
- tooltip={
1381
- <Tooltip style={{ textTransform: 'none' }}>
1382
- <Tooltip.Target>
1383
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1384
- </Tooltip.Target>
1385
- <Tooltip.Content>
1386
- <p>Middle data point. Half of data are higher value.</p>
1387
- </Tooltip.Content>
1388
- </Tooltip>
1389
- }
1390
- />
1391
-
1392
- {/* prettier-ignore */}
1393
- {/* q1 */}
1394
- <TextField
1395
- type='text'
1396
- value={config.boxplot.labels.q1}
1397
- fieldName='q1'
1398
- section='boxplot'
1399
- subsection='labels'
1400
- label='Lower Quartile'
1401
- updateField={updateField}
1402
- tooltip={
1403
- <Tooltip style={{ textTransform: 'none' }}>
1404
- <Tooltip.Target>
1405
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1406
- </Tooltip.Target>
1407
- <Tooltip.Content>
1408
- <p>Represented by bottom line of box. 25% of data are lower.</p>
1409
- </Tooltip.Content>
1410
- </Tooltip>
1411
- }
1412
- />
1413
-
1414
- {/* prettier-ignore */}
1415
- {/* minimum */}
1416
- <TextField
1417
- type='text'
1418
- value={config.boxplot.labels.minimum}
1419
- fieldName='minimum'
1420
- section='boxplot'
1421
- subsection='labels'
1422
- label='Minimum'
1423
- updateField={updateField}
1424
- tooltip={
1425
- <Tooltip style={{ textTransform: 'none' }}>
1426
- <Tooltip.Target>
1427
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1428
- </Tooltip.Target>
1429
- <Tooltip.Content>
1430
- <p>Lowest value, excluding outliers</p>
1431
- </Tooltip.Content>
1432
- </Tooltip>
1433
- }
1434
- />
1435
- <br />
1436
- <h4 style={{ fontSize: '18px' }}>Labels for Additional Measures</h4>
1437
-
1438
- {/* iqr */}
1439
- <TextField type='text' value={config.boxplot.labels.iqr} fieldName='iqr' section='boxplot' subsection='labels' label='Interquartile Range' updateField={updateField} />
1440
-
1441
- {/* count */}
1442
- <TextField type='text' value={config.boxplot.labels.total} fieldName='total' section='boxplot' subsection='labels' label='Total' updateField={updateField} />
1443
-
1444
- {/* mean */}
1445
- <TextField type='text' value={config.boxplot.labels.mean} fieldName='mean' section='boxplot' subsection='labels' label='Mean' updateField={updateField} />
1446
- {/* outliers */}
1447
- <TextField type='text' value={config.boxplot.labels.outliers} fieldName='outliers' section='boxplot' subsection='labels' label='Outliers' updateField={updateField} />
1448
- {/* values */}
1449
- <TextField type='text' value={config.boxplot.labels.values} fieldName='values' section='boxplot' subsection='labels' label='Values' updateField={updateField} />
1450
- </AccordionItemPanel>
1451
- </AccordionItem>
1452
- )}
1453
- {/* Left Value Axis */}
1454
- {visSupportsLeftValueAxis() && (
1455
- <AccordionItem>
1456
- <AccordionItemHeading>
1457
- <AccordionItemButton>
1458
- {config.visualizationType === 'Pie' ? 'Data Format' : config.orientation === 'vertical' ? 'Left Value Axis' : 'Value Axis'}
1459
- {config.visualizationType === 'Pie' && !config.yAxis.dataKey && <WarningImage width='25' className='warning-icon' />}
1460
- </AccordionItemButton>
1461
- </AccordionItemHeading>
1462
- <AccordionItemPanel>
1463
- {config.visualizationType === 'Pie' && (
1464
- <Select
1465
- value={config.yAxis.dataKey || ''}
1466
- section='yAxis'
1467
- fieldName='dataKey'
1468
- label='Data Column'
1469
- initial='Select'
1470
- required={true}
1471
- updateField={updateField}
1472
- options={getColumns(false)}
1473
- tooltip={
1474
- <Tooltip style={{ textTransform: 'none' }}>
1475
- <Tooltip.Target>
1476
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1477
- </Tooltip.Target>
1478
- <Tooltip.Content>
1479
- <p>Select the source data to be visually represented.</p>
1480
- </Tooltip.Content>
1481
- </Tooltip>
1482
- }
1483
- />
1484
- )}
1485
- {config.visualizationType !== 'Pie' && (
1486
- <>
1487
- <TextField value={config.yAxis.label} section='yAxis' fieldName='label' label='Label' updateField={updateField} />
1488
- {config.runtime.seriesKeys && config.runtime.seriesKeys.length === 1 && !['Box Plot', 'Deviation Bar', 'Forest Plot'].includes(config.visualizationType) && (
1489
- <CheckBox value={config.isLegendValue} fieldName='isLegendValue' label='Use Legend Value in Hover' updateField={updateField} />
1490
- )}
1491
- <TextField value={config.yAxis.numTicks} placeholder='Auto' type='number' section='yAxis' fieldName='numTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
1492
- {config.visualizationType === 'Paired Bar' && <TextField value={config.yAxis.tickRotation || 0} type='number' min='0' section='yAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
1493
- <TextField
1494
- value={config.yAxis.size}
1495
- type='number'
1496
- section='yAxis'
1497
- fieldName='size'
1498
- label={config.orientation === 'horizontal' ? 'Size (Height)' : 'Size (Width)'}
1499
- className='number-narrow'
1500
- updateField={updateField}
1501
- tooltip={
1502
- <Tooltip style={{ textTransform: 'none' }}>
1503
- <Tooltip.Target>
1504
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1505
- </Tooltip.Target>
1506
- <Tooltip.Content>
1507
- <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>
1508
- </Tooltip.Content>
1509
- </Tooltip>
1510
- }
1511
- />
1512
- {config.orientation === 'horizontal' && config.visualizationType !== 'Paired Bar' && <CheckBox value={config.isResponsiveTicks} fieldName='isResponsiveTicks' label='Use Responsive Ticks' updateField={updateField} />}
1513
- {(config.orientation === 'vertical' || !config.isResponsiveTicks) && <TextField value={config.yAxis.tickRotation} type='number' min='0' section='yAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
1514
- {config.isResponsiveTicks && config.orientation === 'horizontal' && config.visualizationType !== 'Paired Bar' && (
1515
- <TextField
1516
- value={config.xAxis.maxTickRotation}
1517
- type='number'
1518
- min='0'
1519
- section='xAxis'
1520
- fieldName='maxTickRotation'
1521
- label='Max Tick Rotation'
1522
- className='number-narrow'
1523
- updateField={updateField}
1524
- tooltip={
1525
- <Tooltip style={{ textTransform: 'none' }}>
1526
- <Tooltip.Target>
1527
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1528
- </Tooltip.Target>
1529
- <Tooltip.Content>
1530
- <p>Degrees ticks will be rotated if values overlap, especially in smaller viewports.</p>
1531
- </Tooltip.Content>
1532
- </Tooltip>
1533
- }
1534
- />
1535
- )}
1536
-
1537
- {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
1538
- {/* <TextField value={config.yAxis.axisPadding} type='number' max={10} min={0} section='yAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateField} /> */}
1539
- {config.orientation === 'horizontal' && <TextField value={config.xAxis.labelOffset} section='xAxis' fieldName='labelOffset' label='Label offset' type='number' className='number-narrow' updateField={updateField} />}
1540
- {visSupportsValueAxisGridLines() && <CheckBox value={config.yAxis.gridLines} section='yAxis' fieldName='gridLines' label='Show Gridlines' updateField={updateField} />}
1541
- <CheckBox value={config.yAxis.enablePadding} section='yAxis' fieldName='enablePadding' label='Add Padding to Value Axis Scale' updateField={updateField} />
1542
- {config.visualizationSubType === 'regular' && <CheckBox value={config.useLogScale} fieldName='useLogScale' label='use logarithmic scale' updateField={updateField} />}
1543
- </>
1544
- )}
1545
- <span className='divider-heading'>Number Formatting</span>
1546
- <CheckBox value={config.dataFormat.commas} section='dataFormat' fieldName='commas' label='Add commas' updateField={updateField} />
1547
- <CheckBox
1548
- value={config.dataFormat.abbreviated}
1549
- section='dataFormat'
1550
- fieldName='abbreviated'
1551
- label='Abbreviate Axis Values'
1552
- updateField={updateField}
1553
- tooltip={
1554
- <Tooltip style={{ textTransform: 'none' }}>
1555
- <Tooltip.Target>
1556
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1557
- </Tooltip.Target>
1558
- <Tooltip.Content>
1559
- <p>{`This option abbreviates very large or very small numbers on the value axis`}</p>
1560
- </Tooltip.Content>
1561
- </Tooltip>
1562
- }
1563
- />
1564
- <TextField value={config.dataFormat.roundTo ? config.dataFormat.roundTo : 0} type='number' section='dataFormat' fieldName='roundTo' label='Round to decimal point' className='number-narrow' updateField={updateField} min={0} />
1565
- <div className='two-col-inputs'>
1566
- <TextField
1567
- value={config.dataFormat.prefix}
1568
- section='dataFormat'
1569
- fieldName='prefix'
1570
- label='Prefix'
1571
- updateField={updateField}
1572
- tooltip={
1573
- <Tooltip style={{ textTransform: 'none' }}>
1574
- <Tooltip.Target>
1575
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1576
- </Tooltip.Target>
1577
- <Tooltip.Content>
1578
- {config.visualizationType === 'Pie' && <p>Enter a data prefix to display in the data table and chart tooltips, if applicable.</p>}
1579
- {config.visualizationType !== 'Pie' && <p>Enter a data prefix (such as "$"), if applicable.</p>}
1580
- </Tooltip.Content>
1581
- </Tooltip>
1582
- }
1583
- />
1584
- <TextField
1585
- value={config.dataFormat.suffix}
1586
- section='dataFormat'
1587
- fieldName='suffix'
1588
- label='Suffix'
1589
- updateField={updateField}
1590
- tooltip={
1591
- <Tooltip style={{ textTransform: 'none' }}>
1592
- <Tooltip.Target>
1593
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1594
- </Tooltip.Target>
1595
- <Tooltip.Content>
1596
- {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
1597
- {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
1598
- </Tooltip.Content>
1599
- </Tooltip>
1600
- }
1601
- />
1602
- </div>
1603
-
1604
- {config.orientation === 'horizontal' ? ( // horizontal - x is vertical y is horizontal
1605
- <>
1606
- {visSupportsValueAxisLine() && <CheckBox value={config.xAxis.hideAxis} section='xAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />}
1607
- {visSupportsValueAxisLabels() && <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />}
1608
- {visSupportsValueAxisTicks() && <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />}
1609
- <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='max value' type='number' placeholder='Auto' updateField={updateField} />
1610
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
1611
- <TextField value={config.xAxis.min} section='xAxis' fieldName='min' type='number' label='min value' placeholder='Auto' updateField={updateField} />
1612
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
1613
- {config.visualizationType === 'Deviation Bar' && (
1614
- <>
1615
- <TextField value={config.xAxis.target} section='xAxis' fieldName='target' type='number' label='Deviation point' placeholder='Auto' updateField={updateField} />
1616
- <TextField value={config.xAxis.targetLabel || 'Target'} section='xAxis' fieldName='targetLabel' type='text' label='Deviation point Label' updateField={updateField} />
1617
- <CheckBox value={config.xAxis.showTargetLabel} section='xAxis' fieldName='showTargetLabel' label='Show Deviation point label' updateField={updateField} />
1618
- </>
1619
- )}
1620
- </>
1621
- ) : (
1622
- config.visualizationType !== 'Pie' && (
1623
- <>
1624
- <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1625
- <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1626
- <CheckBox value={config.yAxis.hideTicks} section='yAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1627
-
1628
- <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='left axis max value' placeholder='Auto' updateField={updateField} />
1629
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
1630
- <TextField value={config.yAxis.min} section='yAxis' fieldName='min' type='number' label='left axis min value' placeholder='Auto' updateField={updateField} />
1631
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
1632
- </>
1633
- )
1634
- )}
1635
-
1636
- {/* start: anchors */}
1637
- {visHasAnchors() && config.orientation !== 'horizontal' && (
1638
- <div className='edit-block'>
1639
- <span className='edit-label column-heading'>Anchors</span>
1640
- <Accordion allowZeroExpanded>
1641
- {config.yAxis?.anchors?.map((anchor, index) => (
1642
- <AccordionItem className='series-item series-item--chart' key={`yaxis-anchors-2-${index}`}>
1643
- <AccordionItemHeading className='series-item__title'>
1644
- <>
1645
- <AccordionItemButton className={'accordion__button accordion__button'}>
1646
- Anchor {index + 1}
1647
- <button
1648
- className='series-list__remove'
1649
- onClick={e => {
1650
- e.preventDefault()
1651
- const copiedAnchorGroups = [...config.yAxis.anchors]
1652
- copiedAnchorGroups.splice(index, 1)
1653
- updateConfig({
1654
- ...config,
1655
- yAxis: {
1656
- ...config.yAxis,
1657
- anchors: copiedAnchorGroups
1658
- }
1659
- })
1660
- }}
1661
- >
1662
- Remove
1663
- </button>
1664
- </AccordionItemButton>
1665
- </>
1666
- </AccordionItemHeading>
1667
- <AccordionItemPanel>
1668
- <label>
1669
- <span>Anchor Value</span>
1670
- <Tooltip style={{ textTransform: 'none' }}>
1671
- <Tooltip.Target>
1672
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1673
- </Tooltip.Target>
1674
- <Tooltip.Content>
1675
- <p>Enter the value as its shown in the data column</p>
1676
- </Tooltip.Content>
1677
- </Tooltip>
1678
- <input
1679
- type='text'
1680
- value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
1681
- onChange={e => {
1682
- e.preventDefault()
1683
- const copiedAnchors = [...config.yAxis.anchors]
1684
- copiedAnchors[index].value = e.target.value
1685
- updateConfig({
1686
- ...config,
1687
- yAxis: {
1688
- ...config.yAxis,
1689
- anchors: copiedAnchors
1690
- }
1691
- })
1692
- }}
1693
- />
1694
- </label>
1695
-
1696
- <label>
1697
- <span>Anchor Color</span>
1698
- <input
1699
- type='text'
1700
- value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
1701
- onChange={e => {
1702
- e.preventDefault()
1703
- const copiedAnchors = [...config.yAxis.anchors]
1704
- copiedAnchors[index].color = e.target.value
1705
- updateConfig({
1706
- ...config,
1707
- yAxis: {
1708
- ...config.yAxis,
1709
- anchors: copiedAnchors
1710
- }
1711
- })
1712
- }}
1713
- />
1714
- </label>
1715
-
1716
- <label>
1717
- Anchor Line Style
1718
- <select
1719
- value={config.yAxis.anchors[index].lineStyle || ''}
1720
- onChange={e => {
1721
- const copiedAnchors = [...config.yAxis.anchors]
1722
- copiedAnchors[index].lineStyle = e.target.value
1723
- updateConfig({
1724
- ...config,
1725
- yAxis: {
1726
- ...config.yAxis,
1727
- anchors: copiedAnchors
1728
- }
1729
- })
1730
- }}
1731
- >
1732
- <option>Select</option>
1733
- {lineOptions.map(line => (
1734
- <option key={line.key}>{line.value}</option>
1735
- ))}
1736
- </select>
1737
- </label>
1738
- </AccordionItemPanel>
1739
- </AccordionItem>
1740
- ))}
1741
- </Accordion>
1742
-
1743
- <button
1744
- className='btn full-width'
1745
- onClick={e => {
1746
- e.preventDefault()
1747
- const anchors = [...config.yAxis.anchors]
1748
- anchors.push({})
1749
- updateConfig({
1750
- ...config,
1751
- yAxis: {
1752
- ...config.yAxis,
1753
- anchors
1754
- }
1755
- })
1756
- }}
1757
- >
1758
- Add Anchor
1759
- </button>
1760
- </div>
1761
- )}
1762
-
1763
- {visHasAnchors() && config.orientation === 'horizontal' && (
1764
- <div className='edit-block'>
1765
- <span className='edit-label column-heading'>Anchors</span>
1766
- <Accordion allowZeroExpanded>
1767
- {config.xAxis?.anchors?.map((anchor, index) => (
1768
- <AccordionItem className='series-item series-item--chart' key={`xaxis-anchors-${index}`}>
1769
- <AccordionItemHeading className='series-item__title'>
1770
- <>
1771
- <AccordionItemButton className={'accordion__button accordion__button'}>
1772
- Anchor {index + 1}
1773
- <button
1774
- className='series-list__remove'
1775
- onClick={e => {
1776
- e.preventDefault()
1777
- const copiedAnchorGroups = [...config.xAxis.anchors]
1778
- copiedAnchorGroups.splice(index, 1)
1779
- updateConfig({
1780
- ...config,
1781
- xAxis: {
1782
- ...config.xAxis,
1783
- anchors: copiedAnchorGroups
1784
- }
1785
- })
1786
- }}
1787
- >
1788
- Remove
1789
- </button>
1790
- </AccordionItemButton>
1791
- </>
1792
- </AccordionItemHeading>
1793
- <AccordionItemPanel>
1794
- <label>
1795
- <span>Anchor Value</span>
1796
- <Tooltip style={{ textTransform: 'none' }}>
1797
- <Tooltip.Target>
1798
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1799
- </Tooltip.Target>
1800
- <Tooltip.Content>
1801
- <p>Enter the value as its shown in the data column</p>
1802
- </Tooltip.Content>
1803
- </Tooltip>
1804
- <input
1805
- type='text'
1806
- value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
1807
- onChange={e => {
1808
- e.preventDefault()
1809
- const copiedAnchors = [...config.xAxis.anchors]
1810
- copiedAnchors[index].value = e.target.value
1811
- updateConfig({
1812
- ...config,
1813
- xAxis: {
1814
- ...config.xAxis,
1815
- anchors: copiedAnchors
1816
- }
1817
- })
1818
- }}
1819
- />
1820
- </label>
1821
-
1822
- <label>
1823
- <span>Anchor Color</span>
1824
- <input
1825
- type='text'
1826
- value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
1827
- onChange={e => {
1828
- e.preventDefault()
1829
- const copiedAnchors = [...config.xAxis.anchors]
1830
- copiedAnchors[index].color = e.target.value
1831
- updateConfig({
1832
- ...config,
1833
- xAxis: {
1834
- ...config.xAxis,
1835
- anchors: copiedAnchors
1836
- }
1837
- })
1838
- }}
1839
- />
1840
- </label>
1841
-
1842
- <label>
1843
- Anchor Line Style
1844
- <select
1845
- value={config.xAxis.anchors[index].lineStyle || ''}
1846
- onChange={e => {
1847
- const copiedAnchors = [...config.xAxis.anchors]
1848
- copiedAnchors[index].lineStyle = e.target.value
1849
- updateConfig({
1850
- ...config,
1851
- xAxis: {
1852
- ...config.xAxis,
1853
- anchors: copiedAnchors
1854
- }
1855
- })
1856
- }}
1857
- >
1858
- <option>Select</option>
1859
- {lineOptions.map(line => (
1860
- <option key={line.key}>{line.value}</option>
1861
- ))}
1862
- </select>
1863
- </label>
1864
- </AccordionItemPanel>
1865
- </AccordionItem>
1866
- ))}
1867
- </Accordion>
1868
-
1869
- <button
1870
- className='btn full-width'
1871
- onClick={e => {
1872
- e.preventDefault()
1873
- const anchors = [...config.xAxis.anchors]
1874
- anchors.push({})
1875
- updateConfig({
1876
- ...config,
1877
- xAxis: {
1878
- ...config.xAxis,
1879
- anchors
1880
- }
1881
- })
1882
- }}
1883
- >
1884
- Add Anchor
1885
- </button>
1886
- </div>
1887
- )}
1888
- {/* end: anchors */}
1889
- </AccordionItemPanel>
1890
- </AccordionItem>
1891
- )}
1892
- {/* Right Value Axis Settings */}
1893
- {hasRightAxis && (
1894
- <AccordionItem>
1895
- <AccordionItemHeading>
1896
- <AccordionItemButton>Right Value Axis</AccordionItemButton>
1897
- </AccordionItemHeading>
1898
- <AccordionItemPanel>
1899
- <TextField value={config.yAxis.rightLabel} section='yAxis' fieldName='rightLabel' label='Label' updateField={updateField} />
1900
- <TextField value={config.yAxis.rightNumTicks} placeholder='Auto' type='number' section='yAxis' fieldName='rightNumTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
1901
- <TextField value={config.yAxis.rightAxisSize} type='number' section='yAxis' fieldName='rightAxisSize' label='Size (Width)' className='number-narrow' updateField={updateField} />
1902
- <TextField value={config.yAxis.rightLabelOffsetSize} type='number' section='yAxis' fieldName='rightLabelOffsetSize' label='Label Offset' className='number-narrow' updateField={updateField} />
1903
-
1904
- <span className='divider-heading'>Number Formatting</span>
1905
- <CheckBox value={config.dataFormat.rightCommas} section='dataFormat' fieldName='rightCommas' label='Add commas' updateField={updateField} />
1906
- <TextField value={config.dataFormat.rightRoundTo} type='number' section='dataFormat' fieldName='rightRoundTo' label='Round to decimal point' className='number-narrow' updateField={updateField} min={0} />
1907
- <div className='two-col-inputs'>
1908
- <TextField
1909
- value={config.dataFormat.rightPrefix}
1910
- section='dataFormat'
1911
- fieldName='rightPrefix'
1912
- label='Prefix'
1913
- updateField={updateField}
1914
- tooltip={
1915
- <Tooltip style={{ textTransform: 'none' }}>
1916
- <Tooltip.Target>
1917
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1918
- </Tooltip.Target>
1919
- <Tooltip.Content>
1920
- {config.visualizationType === 'Pie' && <p>Enter a data prefix to display in the data table and chart tooltips, if applicable.</p>}
1921
- {config.visualizationType !== 'Pie' && <p>Enter a data prefix (such as "$"), if applicable.</p>}
1922
- </Tooltip.Content>
1923
- </Tooltip>
1924
- }
1925
- />
1926
- <TextField
1927
- value={config.dataFormat.rightSuffix}
1928
- section='dataFormat'
1929
- fieldName='rightSuffix'
1930
- label='Suffix'
1931
- updateField={updateField}
1932
- tooltip={
1933
- <Tooltip style={{ textTransform: 'none' }}>
1934
- <Tooltip.Target>
1935
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1936
- </Tooltip.Target>
1937
- <Tooltip.Content>
1938
- {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
1939
- {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
1940
- </Tooltip.Content>
1941
- </Tooltip>
1942
- }
1943
- />
1944
- </div>
1945
-
1946
- <CheckBox value={config.yAxis.rightHideAxis} section='yAxis' fieldName='rightHideAxis' label='Hide Axis' updateField={updateField} />
1947
- <CheckBox value={config.yAxis.rightHideLabel} section='yAxis' fieldName='rightHideLabel' label='Hide Label' updateField={updateField} />
1948
- <CheckBox value={config.yAxis.rightHideTicks} section='yAxis' fieldName='rightHideTicks' label='Hide Ticks' updateField={updateField} />
1949
-
1950
- <TextField value={config.yAxis.max} section='yAxis' fieldName='rightMax' type='number' label='right axis max value' placeholder='Auto' updateField={updateField} />
1951
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.rightMaxMessage}</span>
1952
- <TextField value={config.yAxis.min} section='yAxis' fieldName='rightMin' type='number' label='right axis min value' placeholder='Auto' updateField={updateField} />
1953
- <span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
1954
- </AccordionItemPanel>
1955
- </AccordionItem>
1956
- )}
1957
- <AccordionItem>
1958
- <AccordionItemHeading>
1959
- <AccordionItemButton>
1960
- {config.visualizationType === 'Pie' ? 'Segments' : 'Date/Category Axis'}
1961
- {!config.xAxis.dataKey && <WarningImage width='25' className='warning-icon' />}
1962
- </AccordionItemButton>
1963
- </AccordionItemHeading>
1964
- <AccordionItemPanel>
1965
- {config.visualizationType !== 'Pie' && (
1966
- <>
1967
- {config.visualizationType !== 'Forest Plot' && (
1968
- <Select value={config.xAxis.type} section='xAxis' fieldName='type' label='Data Type' updateField={updateField} options={config.visualizationType !== 'Scatter Plot' ? ['categorical', 'date'] : ['categorical', 'continuous', 'date']} />
1969
- )}
1970
- <CheckBox value={config.xAxis.sortDates} section='xAxis' fieldName='sortDates' label='Force Date Scale (Sort Dates)' updateField={updateField} />{' '}
1971
- <Select
1972
- value={config.xAxis.dataKey || setCategoryAxis() || ''}
1973
- section='xAxis'
1974
- fieldName='dataKey'
1975
- label='Data Key'
1976
- initial='Select'
1977
- required={true}
1978
- updateField={updateField}
1979
- options={getColumns(false)}
1980
- tooltip={
1981
- <Tooltip style={{ textTransform: 'none' }}>
1982
- <Tooltip.Target>
1983
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1984
- </Tooltip.Target>
1985
- <Tooltip.Content>
1986
- <p>Select the column or row containing the categories or dates for this axis. </p>
1987
- </Tooltip.Content>
1988
- </Tooltip>
1989
- }
1990
- />
1991
- </>
1992
- )}
1993
-
1994
- {config.visualizationType === 'Pie' && (
1995
- <Select
1996
- value={config.xAxis.dataKey || ''}
1997
- section='xAxis'
1998
- fieldName='dataKey'
1999
- label='Segment Labels'
2000
- initial='Select'
2001
- required={true}
2002
- updateField={updateField}
2003
- options={getColumns(false)}
2004
- tooltip={
2005
- <Tooltip style={{ textTransform: 'none' }}>
2006
- <Tooltip.Target>
2007
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2008
- </Tooltip.Target>
2009
- <Tooltip.Content>
2010
- <p>Select the source row or column that contains the segment labels. Depending on the data structure, it may be listed as "Key."</p>
2011
- </Tooltip.Content>
2012
- </Tooltip>
2013
- }
2014
- />
2015
- )}
2016
-
2017
- {config.visualizationType !== 'Pie' && (
2018
- <>
2019
- <TextField value={config.xAxis.label} section='xAxis' fieldName='label' label='Label' updateField={updateField} />
2020
-
2021
- {config.xAxis.type === 'continuous' && (
2022
- <>
2023
- <TextField
2024
- value={config.dataFormat.bottomPrefix}
2025
- section='dataFormat'
2026
- fieldName='bottomPrefix'
2027
- label='Prefix'
2028
- updateField={updateField}
2029
- tooltip={
2030
- <Tooltip style={{ textTransform: 'none' }}>
2031
- <Tooltip.Target>
2032
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2033
- </Tooltip.Target>
2034
- <Tooltip.Content>
2035
- {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
2036
- {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
2037
- </Tooltip.Content>
2038
- </Tooltip>
2039
- }
2040
- />
2041
-
2042
- <TextField
2043
- value={config.dataFormat.bottomSuffix}
2044
- section='dataFormat'
2045
- fieldName='bottomSuffix'
2046
- label='Suffix'
2047
- updateField={updateField}
2048
- tooltip={
2049
- <Tooltip style={{ textTransform: 'none' }}>
2050
- <Tooltip.Target>
2051
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2052
- </Tooltip.Target>
2053
- <Tooltip.Content>
2054
- {config.visualizationType === 'Pie' && <p>Enter a data suffix to display in the data table and tooltips, if applicable.</p>}
2055
- {config.visualizationType !== 'Pie' && <p>Enter a data suffix (such as "%"), if applicable.</p>}
2056
- </Tooltip.Content>
2057
- </Tooltip>
2058
- }
2059
- />
2060
-
2061
- <CheckBox
2062
- value={config.dataFormat.bottomAbbreviated}
2063
- section='dataFormat'
2064
- fieldName='bottomAbbreviated'
2065
- label='Abbreviate Axis Values'
2066
- updateField={updateField}
2067
- tooltip={
2068
- <Tooltip style={{ textTransform: 'none' }}>
2069
- <Tooltip.Target>
2070
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2071
- </Tooltip.Target>
2072
- <Tooltip.Content>
2073
- <p>{`This option abbreviates very large or very small numbers on the value axis`}</p>
2074
- </Tooltip.Content>
2075
- </Tooltip>
2076
- }
2077
- />
2078
- </>
2079
- )}
2080
-
2081
- {config.xAxis.type === 'date' && (
2082
- <>
2083
- <p style={{ padding: '1.5em 0 0.5em', fontSize: '.9rem', lineHeight: '1rem' }}>
2084
- Format how charts should parse and display your dates using{' '}
2085
- <a href='https://github.com/d3/d3-time-format#locale_format' target='_blank' rel='noreferrer'>
2086
- these guidelines
2087
- </a>
2088
- .
2089
- </p>
2090
- <TextField value={config.xAxis.dateParseFormat} section='xAxis' fieldName='dateParseFormat' placeholder='Ex. %Y-%m-%d' label='Date Parse Format' updateField={updateField} />
2091
- <TextField value={config.xAxis.dateDisplayFormat} section='xAxis' fieldName='dateDisplayFormat' placeholder='Ex. %Y-%m-%d' label='Date Display Format' updateField={updateField} />
2092
- </>
2093
- )}
2094
-
2095
- <CheckBox
2096
- value={config.exclusions.active}
2097
- section='exclusions'
2098
- fieldName='active'
2099
- label={config.xAxis.type === 'date' ? 'Limit by start and/or end dates' : 'Exclude one or more values'}
2100
- tooltip={
2101
- <Tooltip style={{ textTransform: 'none' }}>
2102
- <Tooltip.Target>
2103
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2104
- </Tooltip.Target>
2105
- <Tooltip.Content>
2106
- <p>When this option is checked, you can select source-file values for exclusion from the date/category axis. </p>
2107
- </Tooltip.Content>
2108
- </Tooltip>
2109
- }
2110
- updateField={updateField}
2111
- />
2112
- {/* {visHasBrushChart && <CheckBox value={config.brush.active} section='brush' fieldName='active' label='Brush Slider ' updateField={updateField} />} */}
2113
-
2114
- {config.exclusions.active && (
2115
- <>
2116
- {config.xAxis.type === 'categorical' && (
2117
- <>
2118
- {config.exclusions.keys.length > 0 && (
2119
- <>
2120
- <fieldset>
2121
- <legend className='edit-label'>Excluded Keys</legend>
2122
- </fieldset>
2123
- <ExclusionsList />
2124
- </>
2125
- )}
2126
-
2127
- <Select
2128
- fieldName='visualizationType'
2129
- label='Add Exclusion'
2130
- initial='Select'
2131
- onChange={e => {
2132
- if (e.target.value !== '' && e.target.value !== 'Select') {
2133
- addNewExclusion(e.target.value)
2134
- }
2135
- e.target.value = ''
2136
- }}
2137
- options={getDataValues(config.xAxis.dataKey, true)}
2138
- />
2139
- </>
2140
- )}
2141
-
2142
- {config.xAxis.type === 'date' && (
2143
- <>
2144
- <TextField type='date' section='exclusions' fieldName='dateStart' label='Start Date' updateField={updateField} value={config.exclusions.dateStart || ''} />
2145
- <TextField type='date' section='exclusions' fieldName='dateEnd' label='End Date' updateField={updateField} value={config.exclusions.dateEnd || ''} />
2146
- </>
2147
- )}
2148
- </>
2149
- )}
2150
-
2151
- {visSupportsDateCategoryNumTicks() && <TextField value={config.xAxis.numTicks} placeholder='Auto' type='number' min='1' section='xAxis' fieldName='numTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />}
2152
- {visSupportsDateCategoryHeight() && <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} />}
2153
-
2154
- {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
2155
- {/* <TextField value={config.xAxis.axisPadding} type='number' max={10} min={0} section='xAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateField} /> */}
2156
- {(config.xAxis.type === 'continuous' || config.forestPlot.type === 'Logarithmic') && (
2157
- <>
2158
- <CheckBox value={config.dataFormat.bottomCommas} section='dataFormat' fieldName='bottomCommas' label='Add commas' updateField={updateField} />
2159
- <TextField value={config.dataFormat.bottomRoundTo} type='number' section='dataFormat' fieldName='bottomRoundTo' label='Round to decimal point' className='number-narrow' updateField={updateField} min={0} />
2160
- </>
2161
- )}
2162
- {visSupportsResponsiveTicks() && config.orientation === 'vertical' && config.visualizationType !== 'Paired Bar' && <CheckBox value={config.isResponsiveTicks} fieldName='isResponsiveTicks' label='Use Responsive Ticks' updateField={updateField} />}
2163
- {(config.orientation === 'horizontal' || !config.isResponsiveTicks) && visSupportsDateCategoryTickRotation() && (
2164
- <TextField value={config.xAxis.tickRotation} type='number' min='0' section='xAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />
2165
- )}
2166
- {config.orientation === 'vertical' && config.isResponsiveTicks && config.visualizationType !== 'Paired Bar' && (
2167
- <TextField
2168
- value={config.xAxis.maxTickRotation}
2169
- type='number'
2170
- min='0'
2171
- section='xAxis'
2172
- fieldName='maxTickRotation'
2173
- label='Max Tick Rotation'
2174
- className='number-narrow'
2175
- updateField={updateField}
2176
- tooltip={
2177
- <Tooltip style={{ textTransform: 'none' }}>
2178
- <Tooltip.Target>
2179
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2180
- </Tooltip.Target>
2181
- <Tooltip.Content>
2182
- <p>Degrees ticks will be rotated if values overlap, especially in smaller viewports.</p>
2183
- </Tooltip.Content>
2184
- </Tooltip>
2185
- }
2186
- />
2187
- )}
2188
-
2189
- {config.orientation === 'horizontal' ? (
2190
- <>
2191
- {visSupportsDateCategoryAxisLine() && <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />}
2192
- {visSupportsDateCategoryAxisLabel() && <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />}
2193
- </>
2194
- ) : (
2195
- <>
2196
- {visSupportsDateCategoryAxisLine() && <CheckBox value={config.xAxis.hideAxis} section='xAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />}
2197
- {visSupportsDateCategoryAxisLabel() && <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />}
2198
- {visSupportsDateCategoryAxisTicks() && <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />}
2199
- </>
2200
- )}
2201
-
2202
- {config.series?.length === 1 && config.visualizationType === 'Bar' && (
2203
- <>
2204
- {/* HIGHLIGHTED BARS */}
2205
- <label htmlFor='barHighlight'>Bar Highlighting</label>
2206
- {config.series.length === 1 &&
2207
- highlightedBarValues.map((highlightedBarValue, i) => (
2208
- <fieldset>
2209
- <div className='edit-block' key={`highlighted-bar-${i}`}>
2210
- <button className='remove-column' onClick={e => handleRemoveHighlightedBar(e, i)}>
2211
- Remove
2212
- </button>
2213
- <p>Highlighted Bar {i + 1}</p>
2214
- <label>
2215
- <span className='edit-label column-heading'>Value</span>
2216
- <select value={config.highlightedBarValues[i].value} onChange={e => handleUpdateHighlightedBar(e, i)}>
2217
- <option value=''>- Select Value -</option>
2218
- {highlightedSeriesValues && [...new Set(highlightedSeriesValues)].sort().map(option => <option key={`special-class-value-option-${i}-${option}`}>{option}</option>)}
2219
- </select>
2220
- </label>
2221
- <label>
2222
- <span className='edit-label column-heading'>Color</span>
2223
- <input type='text' value={config.highlightedBarValues[i].color ? config.highlightedBarValues[i].color : ''} onChange={e => handleUpdateHighlightedBarColor(e, i)} />
2224
- </label>
2225
- <label>
2226
- <span className='edit-label column-heading'>Border Width</span>
2227
- <input max='5' min='0' type='number' value={config.highlightedBarValues[i].borderWidth ? config.highlightedBarValues[i].borderWidth : ''} onChange={e => handleUpdateHighlightedBorderWidth(e, i)} />
2228
- </label>
2229
- <label>
2230
- <span className='edit-label column-heading'>Legend Label</span>
2231
- <input type='text' value={config.highlightedBarValues[i].legendLabel ? config.highlightedBarValues[i].legendLabel : ''} onChange={e => handleHighlightedBarLegendLabel(e, i)} />
2232
- </label>
2233
- </div>
2234
- </fieldset>
2235
- ))}
2236
- <button className='btn full-width' onClick={e => handleAddNewHighlightedBar(e)}>
2237
- Add Highlighted Bar
2238
- </button>
2239
- </>
2240
- )}
2241
- </>
2242
- )}
2243
-
2244
- {config.visualizationType === 'Pie' && (
2245
- <>
2246
- <CheckBox
2247
- value={config.exclusions.active}
2248
- section='exclusions'
2249
- fieldName='active'
2250
- label={'Exclude one or more values'}
2251
- updateField={updateField}
2252
- tooltip={
2253
- <Tooltip style={{ textTransform: 'none' }}>
2254
- <Tooltip.Target>
2255
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2256
- </Tooltip.Target>
2257
- <Tooltip.Content>
2258
- <p>When this option is checked, you can select values for exclusion from the pie segments.</p>
2259
- </Tooltip.Content>
2260
- </Tooltip>
2261
- }
2262
- />
2263
- {config.exclusions.active && (
2264
- <>
2265
- {config.exclusions.keys.length > 0 && (
2266
- <>
2267
- <fieldset>
2268
- <legend className='edit-label'>Excluded Keys</legend>
2269
- </fieldset>
2270
- <ExclusionsList />
2271
- </>
2272
- )}
2273
-
2274
- <Select
2275
- fieldName='visualizationType'
2276
- label='Add Exclusion'
2277
- initial='Select'
2278
- onChange={e => {
2279
- if (e.target.value !== '' && e.target.value !== 'Select') {
2280
- addNewExclusion(e.target.value)
2281
- }
2282
- e.target.value = ''
2283
- }}
2284
- options={getDataValues(config.xAxis.dataKey, true)}
2285
- />
2286
- </>
2287
- )}
2288
- </>
2289
- )}
2290
-
2291
- {/* anchors */}
2292
- {visHasAnchors() && config.orientation !== 'horizontal' && (
2293
- <div className='edit-block'>
2294
- <span className='edit-label column-heading'>Anchors</span>
2295
- <Accordion allowZeroExpanded>
2296
- {config.xAxis?.anchors?.map((anchor, index) => (
2297
- <AccordionItem className='series-item series-item--chart' key={`xaxis-anchors-2-${index}`}>
2298
- <AccordionItemHeading className='series-item__title'>
2299
- <>
2300
- <AccordionItemButton className={'accordion__button accordion__button'}>
2301
- Anchor {index + 1}
2302
- <button
2303
- className='series-list__remove'
2304
- onClick={e => {
2305
- e.preventDefault()
2306
- const copiedAnchorGroups = [...config.xAxis.anchors]
2307
- copiedAnchorGroups.splice(index, 1)
2308
- updateConfig({
2309
- ...config,
2310
- xAxis: {
2311
- ...config.xAxis,
2312
- anchors: copiedAnchorGroups
2313
- }
2314
- })
2315
- }}
2316
- >
2317
- Remove
2318
- </button>
2319
- </AccordionItemButton>
2320
- </>
2321
- </AccordionItemHeading>
2322
- <AccordionItemPanel>
2323
- <label>
2324
- <span>Anchor Value</span>
2325
- <Tooltip style={{ textTransform: 'none' }}>
2326
- <Tooltip.Target>
2327
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2328
- </Tooltip.Target>
2329
- <Tooltip.Content>
2330
- <p>Enter the value as its shown in the data column</p>
2331
- </Tooltip.Content>
2332
- </Tooltip>
2333
- <input
2334
- type='text'
2335
- value={config.xAxis.anchors[index].value ? config.xAxis.anchors[index].value : ''}
2336
- onChange={e => {
2337
- e.preventDefault()
2338
- const copiedAnchors = [...config.xAxis.anchors]
2339
- copiedAnchors[index].value = e.target.value
2340
- updateConfig({
2341
- ...config,
2342
- xAxis: {
2343
- ...config.xAxis,
2344
- anchors: copiedAnchors
2345
- }
2346
- })
2347
- }}
2348
- />
2349
- </label>
2350
-
2351
- <label>
2352
- <span>Anchor Color</span>
2353
- <input
2354
- type='text'
2355
- value={config.xAxis.anchors[index].color ? config.xAxis.anchors[index].color : ''}
2356
- onChange={e => {
2357
- e.preventDefault()
2358
- const copiedAnchors = [...config.xAxis.anchors]
2359
- copiedAnchors[index].color = e.target.value
2360
- updateConfig({
2361
- ...config,
2362
- xAxis: {
2363
- ...config.xAxis,
2364
- anchors: copiedAnchors
2365
- }
2366
- })
2367
- }}
2368
- />
2369
- </label>
2370
-
2371
- <label>
2372
- Anchor Line Style
2373
- <select
2374
- value={config.xAxis.anchors[index].lineStyle || ''}
2375
- onChange={e => {
2376
- const copiedAnchors = [...config.xAxis.anchors]
2377
- copiedAnchors[index].lineStyle = e.target.value
2378
- updateConfig({
2379
- ...config,
2380
- xAxis: {
2381
- ...config.xAxis,
2382
- anchors: copiedAnchors
2383
- }
2384
- })
2385
- }}
2386
- >
2387
- <option>Select</option>
2388
- {lineOptions.map(line => (
2389
- <option key={line.key}>{line.value}</option>
2390
- ))}
2391
- </select>
2392
- </label>
2393
- </AccordionItemPanel>
2394
- </AccordionItem>
2395
- ))}
2396
- </Accordion>
2397
-
2398
- <button
2399
- className='btn full-width'
2400
- onClick={e => {
2401
- e.preventDefault()
2402
- const anchors = [...config.xAxis.anchors]
2403
- anchors.push({})
2404
- updateConfig({
2405
- ...config,
2406
- xAxis: {
2407
- ...config.xAxis,
2408
- anchors
2409
- }
2410
- })
2411
- }}
2412
- >
2413
- Add Anchor
2414
- </button>
2415
- </div>
2416
- )}
2417
-
2418
- {visHasAnchors() && config.orientation === 'horizontal' && (
2419
- <div className='edit-block'>
2420
- <span className='edit-label column-heading'>Anchors</span>
2421
- <Accordion allowZeroExpanded>
2422
- {config.yAxis?.anchors?.map((anchor, index) => (
2423
- <AccordionItem className='series-item series-item--chart' key={`accordion-yaxis-anchors-${index}`}>
2424
- <AccordionItemHeading className='series-item__title'>
2425
- <>
2426
- <AccordionItemButton className={'accordion__button accordion__button'}>
2427
- Anchor {index + 1}
2428
- <button
2429
- className='series-list__remove'
2430
- onClick={e => {
2431
- e.preventDefault()
2432
- const copiedAnchorGroups = [...config.yAxis.anchors]
2433
- copiedAnchorGroups.splice(index, 1)
2434
- updateConfig({
2435
- ...config,
2436
- yAxis: {
2437
- ...config.yAxis,
2438
- anchors: copiedAnchorGroups
2439
- }
2440
- })
2441
- }}
2442
- >
2443
- Remove
2444
- </button>
2445
- </AccordionItemButton>
2446
- </>
2447
- </AccordionItemHeading>
2448
- <AccordionItemPanel>
2449
- <label>
2450
- <span>Anchor Value</span>
2451
- <Tooltip style={{ textTransform: 'none' }}>
2452
- <Tooltip.Target>
2453
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2454
- </Tooltip.Target>
2455
- <Tooltip.Content>
2456
- <p>Enter the value as its shown in the data column</p>
2457
- </Tooltip.Content>
2458
- </Tooltip>
2459
- <input
2460
- type='text'
2461
- value={config.yAxis.anchors[index].value ? config.yAxis.anchors[index].value : ''}
2462
- onChange={e => {
2463
- e.preventDefault()
2464
- const copiedAnchors = [...config.yAxis.anchors]
2465
- copiedAnchors[index].value = e.target.value
2466
- updateConfig({
2467
- ...config,
2468
- yAxis: {
2469
- ...config.yAxis,
2470
- anchors: copiedAnchors
2471
- }
2472
- })
2473
- }}
2474
- />
2475
- </label>
2476
-
2477
- <label>
2478
- <span>Anchor Color</span>
2479
- <input
2480
- type='text'
2481
- value={config.yAxis.anchors[index].color ? config.yAxis.anchors[index].color : ''}
2482
- onChange={e => {
2483
- e.preventDefault()
2484
- const copiedAnchors = [...config.yAxis.anchors]
2485
- copiedAnchors[index].color = e.target.value
2486
- updateConfig({
2487
- ...config,
2488
- yAxis: {
2489
- ...config.yAxis,
2490
- anchors: copiedAnchors
2491
- }
2492
- })
2493
- }}
2494
- />
2495
- </label>
2496
-
2497
- <label>
2498
- Anchor Line Style
2499
- <select
2500
- value={config.yAxis.anchors[index].lineStyle || ''}
2501
- onChange={e => {
2502
- const copiedAnchors = [...config.yAxis.anchors]
2503
- copiedAnchors[index].lineStyle = e.target.value
2504
- updateConfig({
2505
- ...config,
2506
- yAxis: {
2507
- ...config.yAxis,
2508
- anchors: copiedAnchors
2509
- }
2510
- })
2511
- }}
2512
- >
2513
- <option>Select</option>
2514
- {lineOptions.map(line => (
2515
- <option key={line.key}>{line.value}</option>
2516
- ))}
2517
- </select>
2518
- </label>
2519
- </AccordionItemPanel>
2520
- </AccordionItem>
2521
- ))}
2522
- </Accordion>
2523
-
2524
- <button
2525
- className='btn full-width'
2526
- onClick={e => {
2527
- e.preventDefault()
2528
- const anchors = [...config.yAxis.anchors]
2529
- anchors.push({})
2530
- updateConfig({
2531
- ...config,
2532
- yAxis: {
2533
- ...config.yAxis,
2534
- anchors
2535
- }
2536
- })
2537
- }}
2538
- >
2539
- Add Anchor
2540
- </button>
2541
- </div>
2542
- )}
2543
- </AccordionItemPanel>
2544
- </AccordionItem>
2545
- {visSupportsRegions() && (
2546
- <AccordionItem>
2547
- <AccordionItemHeading>
2548
- <AccordionItemButton>Regions</AccordionItemButton>
2549
- </AccordionItemHeading>
2550
- <AccordionItemPanel>
2551
- <Regions config={config} updateConfig={updateConfig} />
2552
- </AccordionItemPanel>
2553
- </AccordionItem>
2554
- )}{' '}
2555
- {/* Columns */}
2556
- {config.visualizationType !== 'Box Plot' && (
2557
- <AccordionItem>
2558
- <AccordionItemHeading>
2559
- <AccordionItemButton>Columns</AccordionItemButton>
2560
- </AccordionItemHeading>
2561
- <AccordionItemPanel>
2562
- {'navigation' !== config.type && (
2563
- <fieldset className='primary-fieldset edit-block'>
2564
- <label>
2565
- <span className='edit-label'>
2566
- Additional Columns
2567
- <Tooltip style={{ textTransform: 'none' }}>
2568
- <Tooltip.Target>
2569
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2570
- </Tooltip.Target>
2571
- <Tooltip.Content>
2572
- <p>You can specify additional columns to display in tooltips and / or the supporting data table.</p>
2573
- </Tooltip.Content>
2574
- </Tooltip>
2575
- </span>
2576
- </label>
2577
- {additionalColumns.map(val => (
2578
- <fieldset className='edit-block' key={val}>
2579
- <button
2580
- className='remove-column'
2581
- onClick={event => {
2582
- event.preventDefault()
2583
- removeAdditionalColumn(val)
2584
- }}
2585
- >
2586
- Remove
2587
- </button>
2588
- <label>
2589
- <span className='edit-label column-heading'>Column</span>
2590
- <select
2591
- value={config.columns[val] ? config.columns[val].name : columnsOptions[0]}
2592
- onChange={event => {
2593
- editColumn(val, 'name', event.target.value)
2594
- }}
2595
- >
2596
- {columnsOptions}
2597
- </select>
2598
- </label>
2599
- <TextField value={config.columns[val].label} section='columns' subsection={val} fieldName='label' label='Label' updateField={updateField} />
2600
- <ul className='column-edit'>
2601
- <li className='three-col'>
2602
- <TextField value={config.columns[val].prefix} section='columns' subsection={val} fieldName='prefix' label='Prefix' updateField={updateField} />
2603
- <TextField value={config.columns[val].suffix} section='columns' subsection={val} fieldName='suffix' label='Suffix' updateField={updateField} />
2604
- <TextField type='number' value={config.columns[val].roundToPlace} section='columns' subsection={val} fieldName='roundToPlace' label='Round' updateField={updateField} />
2605
- </li>
2606
- <li>
2607
- <label className='checkbox'>
2608
- <input
2609
- type='checkbox'
2610
- checked={config.columns[val].commas}
2611
- onChange={event => {
2612
- editColumn(val, 'commas', event.target.checked)
2613
- }}
2614
- />
2615
- <span className='edit-label'>Add Commas to Numbers</span>
2616
- </label>
2617
- </li>
2618
- <li>
2619
- {config.table.showVertical && (
2620
- <label className='checkbox'>
2621
- <input
2622
- type='checkbox'
2623
- checked={config.columns[val].dataTable}
2624
- onChange={event => {
2625
- editColumn(val, 'dataTable', event.target.checked)
2626
- }}
2627
- />
2628
- <span className='edit-label'>Show in Data Table</span>
2629
- </label>
2630
- )}
2631
- </li>
2632
- {/* disable for now */}
2633
-
2634
- <li>
2635
- <label className='checkbox'>
2636
- <input
2637
- type='checkbox'
2638
- checked={config.columns[val].tooltips || false}
2639
- onChange={event => {
2640
- updateSeriesTooltip(val, event.target.checked)
2641
- }}
2642
- />
2643
- <span className='edit-label'>Show in tooltip</span>
2644
- </label>
2645
- </li>
2646
-
2647
- {config.visualizationType === 'Forest Plot' && (
2648
- <>
2649
- <li>
2650
- <label className='checkbox'>
2651
- <input
2652
- type='checkbox'
2653
- checked={config.columns[val].forestPlot || false}
2654
- onChange={event => {
2655
- editColumn(val, 'forestPlot', event.target.checked)
2656
- }}
2657
- />
2658
- <span className='edit-label'>Show in Forest Plot</span>
2659
- </label>
2660
- </li>
2661
- <li>
2662
- <label className='checkbox'>
2663
- <input
2664
- type='checkbox'
2665
- checked={config.columns[val].tooltips || false}
2666
- onChange={event => {
2667
- updateSeriesTooltip(val, event.target.checked)
2668
- }}
2669
- />
2670
- <span className='edit-label'>Show in tooltip</span>
2671
- </label>
2672
- </li>
2673
- <li>
2674
- <label className='checkbox'>
2675
- <input
2676
- type='checkbox'
2677
- checked={config.columns[val].forestPlotAlignRight || false}
2678
- onChange={event => {
2679
- editColumn(val, 'forestPlotAlignRight', event.target.checked)
2680
- }}
2681
- />
2682
- <span className='edit-label'>Align Right</span>
2683
- </label>
2684
- </li>
2685
-
2686
- {!config.columns[val].forestPlotAlignRight && (
2687
- <li>
2688
- <label className='text'>
2689
- <span className='edit-label'>Forest Plot Starting Point</span>
2690
- <input
2691
- type='number'
2692
- value={config.columns[val].forestPlotStartingPoint || 0}
2693
- onChange={event => {
2694
- editColumn(val, 'forestPlotStartingPoint', event.target.value)
2695
- }}
2696
- />
2697
- </label>
2698
- </li>
2699
- )}
2700
- </>
2701
- )}
2702
- </ul>
2703
- </fieldset>
2704
- ))}
2705
- <button
2706
- className={'btn full-width'}
2707
- onClick={event => {
2708
- event.preventDefault()
2709
- addAdditionalColumn(additionalColumns.length + 1)
2710
- }}
2711
- >
2712
- Add Column
2713
- </button>
2714
- </fieldset>
2715
- )}
2716
- {'category' === config.legend.type && (
2717
- <fieldset className='primary-fieldset edit-block'>
2718
- <label>
2719
- <span className='edit-label'>
2720
- Additional Category
2721
- <Tooltip style={{ textTransform: 'none' }}>
2722
- <Tooltip.Target>
2723
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2724
- </Tooltip.Target>
2725
- <Tooltip.Content>
2726
- <p>You can provide additional categories to ensure they appear in the legend</p>
2727
- </Tooltip.Content>
2728
- </Tooltip>
2729
- </span>
2730
- </label>
2731
- {config.legend.additionalCategories &&
2732
- config.legend.additionalCategories.map((val, i) => (
2733
- <fieldset className='edit-block' key={val}>
2734
- <button
2735
- className='remove-column'
2736
- onClick={event => {
2737
- event.preventDefault()
2738
- const updatedAdditionaCategories = [...config.legend.additionalCategories]
2739
- updatedAdditionaCategories.splice(i, 1)
2740
- updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2741
- }}
2742
- >
2743
- Remove
2744
- </button>
2745
- <label>
2746
- <span className='edit-label column-heading'>Category</span>
2747
- <TextField
2748
- value={val}
2749
- section='legend'
2750
- subsection={null}
2751
- fieldName='additionalCategories'
2752
- updateField={(section, subsection, fieldName, value) => {
2753
- const updatedAdditionaCategories = [...config.legend.additionalCategories]
2754
- updatedAdditionaCategories[i] = value
2755
- updateField(section, subsection, fieldName, updatedAdditionaCategories)
2756
- }}
2757
- />
2758
- </label>
2759
- </fieldset>
2760
- ))}
2761
- <button
2762
- className={'btn full-width'}
2763
- onClick={event => {
2764
- event.preventDefault()
2765
- const updatedAdditionaCategories = [...(config.legend.additionalCategories || [])]
2766
- updatedAdditionaCategories.push('')
2767
- updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2768
- }}
2769
- >
2770
- Add Category
2771
- </button>
2772
- </fieldset>
2773
- )}
2774
- </AccordionItemPanel>
2775
- </AccordionItem>
2776
- )}
2777
- {/* End Columns */}
2778
- {visHasLegend() && (
2779
- <AccordionItem>
2780
- <AccordionItemHeading>
2781
- <AccordionItemButton>Legend</AccordionItemButton>
2782
- </AccordionItemHeading>
2783
- <AccordionItemPanel>
2784
- <CheckBox value={config.legend.reverseLabelOrder} section='legend' fieldName='reverseLabelOrder' label='Reverse Labels' updateField={updateField} />
2785
- {/* <fieldset className="checkbox-group">
2786
- <CheckBox value={config.legend.dynamicLegend} section="legend" fieldName="dynamicLegend" label="Dynamic Legend" updateField={updateField}/>
2787
- {config.legend.dynamicLegend && (
2788
- <>
2789
- <TextField value={config.legend.dynamicLegendDefaultText} section="legend" fieldName="dynamicLegendDefaultText" label="Dynamic Legend Default Text" updateField={updateField} />
2790
- <TextField value={config.legend.dynamicLegendItemLimit} type="number" min="0" section="legend" fieldName="dynamicLegendItemLimit" label={'Dynamic Legend Limit'} className="number-narrow" updateField={updateField}/>
2791
- <TextField value={config.legend.dynamicLegendItemLimitMessage} section="legend" fieldName="dynamicLegendItemLimitMessage" label="Dynamic Legend Item Limit Message" updateField={updateField} />
2792
- <TextField value={config.legend.dynamicLegendChartMessage} section="legend" fieldName="dynamicLegendChartMessage" label="Dynamic Legend Chart Message" updateField={updateField} />
2793
- </>
2794
- )}
2795
- </fieldset> */}
2796
- <CheckBox
2797
- value={config.legend.hide ? true : false}
2798
- section='legend'
2799
- fieldName='hide'
2800
- label='Hide Legend'
2801
- updateField={updateField}
2802
- tooltip={
2803
- <Tooltip style={{ textTransform: 'none' }}>
2804
- <Tooltip.Target>
2805
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2806
- </Tooltip.Target>
2807
- <Tooltip.Content>
2808
- <p>With a single-series chart, consider hiding the legend to reduce visual clutter.</p>
2809
- </Tooltip.Content>
2810
- </Tooltip>
2811
- }
2812
- />
2813
- {/* {config.visualizationType === 'Box Plot' &&
2814
- <>
2815
- <CheckBox value={config.boxplot.legend.displayHowToReadText} fieldName='displayHowToReadText' section='boxplot' subsection='legend' label='Display How To Read Text' updateField={updateField} />
2816
- <TextField type='textarea' value={config.boxplot.legend.howToReadText} updateField={updateField} fieldName='howToReadText' section='boxplot' subsection='legend' label='How to read text' />
2817
- </>
2818
- } */}
2819
- {config.visualizationType === 'Line' && <CheckBox value={config.legend.lineMode} section='legend' fieldName='lineMode' label='Show Lined Style Legend' updateField={updateField} />}
2820
- {config.visualizationType === 'Bar' && config.visualizationSubType === 'regular' && config.runtime.seriesKeys.length === 1 && (
2821
- <Select value={config.legend.colorCode} section='legend' fieldName='colorCode' label='Color code by category' initial='Select' updateField={updateField} options={getDataValueOptions(data)} />
2822
- )}
2823
- <Select value={config.legend.behavior} section='legend' fieldName='behavior' label='Legend Behavior (When clicked)' updateField={updateField} options={['highlight', 'isolate']} />
2824
- {config.legend.behavior === 'highlight' && config.tooltips.singleSeries && <CheckBox value={config.legend.highlightOnHover} section='legend' fieldName='highlightOnHover' label='HIGHLIGHT DATA SERIES ON HOVER' updateField={updateField} />}
2825
- <TextField value={config.legend.label} section='legend' fieldName='label' label='Title' updateField={updateField} />
2826
- <Select value={config.legend.position} section='legend' fieldName='position' label='Position' updateField={updateField} options={['right', 'left', 'bottom']} />
2827
- {config.legend.position === 'bottom' && (
2828
- <>
2829
- <CheckBox value={config.legend.singleRow} section='legend' fieldName='singleRow' label='Single Row Legend' updateField={updateField} />
2830
- <CheckBox value={config.legend.verticalSorted} section='legend' fieldName='verticalSorted' label='Vertical sorted Legend' updateField={updateField} />
2831
- </>
2832
- )}
2833
- <TextField type='textarea' value={config.legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />
2834
- </AccordionItemPanel>
2835
- </AccordionItem>
2836
- )}
2837
- {visSupportsFilters() && (
2838
- <AccordionItem>
2839
- <AccordionItemHeading>
2840
- <AccordionItemButton>Filters</AccordionItemButton>
2841
- </AccordionItemHeading>
2842
- <AccordionItemPanel>
2843
- {config.filters && (
2844
- <>
2845
- {/* prettier-ignore */}
2846
- <Select
2847
- value={config.filterBehavior}
2848
- fieldName='filterBehavior'
2849
- label='Filter Behavior'
2850
- updateField={updateField}
2851
- options={['Apply Button', 'Filter Change']}
2852
- tooltip={
2853
- <Tooltip style={{ textTransform: 'none' }}>
2854
- <Tooltip.Target>
2855
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2856
- </Tooltip.Target>
2857
- <Tooltip.Content>
2858
- <p>The Apply Button option changes the visualization when the user clicks "apply". The Filter Change option immediately changes the visualization when the selection is changed.</p>
2859
- </Tooltip.Content>
2860
- </Tooltip>
2861
- }
2862
- />
2863
- <br />
2864
- </>
2865
- )}
2866
- {config.filters && (
2867
- <ul className='filters-list'>
2868
- {/* Whether filters should apply onChange or Apply Button */}
2869
-
2870
- {config.filters.map((filter, index) => {
2871
- if (filter.type === 'url') return <></>
2872
-
2873
- return (
2874
- <fieldset className='edit-block' key={index}>
2875
- <button
2876
- type='button'
2877
- className='remove-column'
2878
- onClick={() => {
2879
- removeFilter(index)
2880
- }}
2881
- >
2882
- Remove
2883
- </button>
2884
- <label>
2885
- <span className='edit-label column-heading'>Filter</span>
2886
- <select
2887
- value={filter.columnName}
2888
- onChange={e => {
2889
- updateFilterProp('columnName', index, e.target.value)
2890
- }}
2891
- >
2892
- <option value=''>- Select Option -</option>
2893
- {getFilters(true).map((dataKey, index) => (
2894
- <option value={dataKey} key={index}>
2895
- {dataKey}
2896
- </option>
2897
- ))}
2898
- </select>
2899
- </label>
2900
-
2901
- <label>
2902
- <span className='edit-showDropdown column-heading'>Show Filter Input</span>
2903
- <input
2904
- type='checkbox'
2905
- checked={filter.showDropdown === undefined ? true : filter.showDropdown}
2906
- onChange={e => {
2907
- updateFilterProp('showDropdown', index, e.target.checked)
2908
- }}
2909
- />
2910
- </label>
2911
-
2912
- <label>
2913
- <span className='edit-label column-heading'>Filter Style</span>
2914
-
2915
- <select
2916
- value={filter.filterStyle}
2917
- onChange={e => {
2918
- updateFilterProp('filterStyle', index, e.target.value)
2919
- }}
2920
- >
2921
- {filterStyleOptions.map(item => {
2922
- return <option value={item}>{item}</option>
2923
- })}
2924
- </select>
2925
- </label>
2926
- <label>
2927
- <span className='edit-label column-heading'>Label</span>
2928
- <input
2929
- type='text'
2930
- value={filter.label}
2931
- onChange={e => {
2932
- updateFilterProp('label', index, e.target.value)
2933
- }}
2934
- />
2935
- </label>
2936
-
2937
- <label>
2938
- <span className='edit-filterOrder column-heading'>Filter Order</span>
2939
- <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', index, e.target.value)}>
2940
- {filterOrderOptions.map((option, index) => {
2941
- return (
2942
- <option value={option.value} key={`filter-${index}`}>
2943
- {option.label}
2944
- </option>
2945
- )
2946
- })}
2947
- </select>
2948
-
2949
- {filter.order === 'cust' && (
2950
- <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, index, config.filters[index])}>
2951
- <Droppable droppableId='filter_order'>
2952
- {provided => (
2953
- <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
2954
- {config.filters[index]?.values.map((value, index) => {
2955
- return (
2956
- <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
2957
- {(provided, snapshot) => (
2958
- <li>
2959
- <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
2960
- {value}
2961
- </div>
2962
- </li>
2963
- )}
2964
- </Draggable>
2965
- )
2966
- })}
2967
- {provided.placeholder}
2968
- </ul>
2969
- )}
2970
- </Droppable>
2971
- </DragDropContext>
2972
- )}
2973
- </label>
2974
- </fieldset>
2975
- )
2976
- })}
2977
- </ul>
2978
- )}
2979
- {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
2980
- <button type='button' onClick={addNewFilter} className='btn full-width'>
2981
- Add Filter
2982
- </button>
2983
- </AccordionItemPanel>
2984
- </AccordionItem>
2985
- )}
2986
- <AccordionItem>
2987
- <AccordionItemHeading>
2988
- <AccordionItemButton>Visual</AccordionItemButton>
2989
- </AccordionItemHeading>
2990
- <AccordionItemPanel>
2991
- {config.isLollipopChart && (
2992
- <>
2993
- <fieldset className='header'>
2994
- <legend className='edit-label'>Lollipop Shape</legend>
2995
- <div
2996
- onChange={e => {
2997
- setLollipopShape(e.target.value)
2998
- }}
2999
- >
3000
- <label className='radio-label'>
3001
- <input type='radio' name='lollipopShape' value='circle' checked={config.lollipopShape === 'circle'} />
3002
- Circle
3003
- </label>
3004
- <label className='radio-label'>
3005
- <input type='radio' name='lollipopShape' value='square' checked={config.lollipopShape === 'square'} />
3006
- Square
3007
- </label>
3008
- </div>
3009
- </fieldset>
3010
- <Select value={config.lollipopColorStyle ? config.lollipopColorStyle : 'two-tone'} fieldName='lollipopColorStyle' label='Lollipop Color Style' updateField={updateField} options={['regular', 'two-tone']} />
3011
- <Select value={config.lollipopSize ? config.lollipopSize : 'small'} fieldName='lollipopSize' label='Lollipop Size' updateField={updateField} options={['small', 'medium', 'large']} />
3012
- </>
3013
- )}
3014
-
3015
- {config.visualizationType === 'Box Plot' && (
3016
- <fieldset className='fieldset fieldset--boxplot'>
3017
- <legend className=''>Box Plot Settings</legend>
3018
- <Select value={config.boxplot.borders} fieldName='borders' section='boxplot' label='Box Plot Borders' updateField={updateField} options={['true', 'false']} />
3019
- <CheckBox value={config.boxplot.plotOutlierValues} fieldName='plotOutlierValues' section='boxplot' label='Plot Outliers' updateField={updateField} />
3020
- <CheckBox value={config.boxplot.plotNonOutlierValues} fieldName='plotNonOutlierValues' section='boxplot' label='Plot non-outlier values' updateField={updateField} />
3021
- </fieldset>
3022
- )}
3023
-
3024
- <Select value={config.fontSize} fieldName='fontSize' label='Font Size' updateField={updateField} options={['small', 'medium', 'large']} />
3025
- {visHasBarBorders() && <Select value={config.barHasBorder} fieldName='barHasBorder' label='Bar Borders' updateField={updateField} options={['true', 'false']} />}
3026
- {visCanAnimate() && <CheckBox value={config.animate} fieldName='animate' label='Animate Visualization' updateField={updateField} />}
3027
-
3028
- {/*<CheckBox value={config.animateReplay} fieldName="animateReplay" label="Replay Animation When Filters Are Changed" updateField={updateField} />*/}
3029
-
3030
- {((config.series?.some(series => series.type === 'Line' || series.type === 'dashed-lg' || series.type === 'dashed-sm' || series.type === 'dashed-md') && config.visualizationType === 'Combo') || config.visualizationType === 'Line') && (
3031
- <>
3032
- <Select value={config.lineDatapointStyle} fieldName='lineDatapointStyle' label='Line Datapoint Style' updateField={updateField} options={['hidden', 'hover', 'always show']} />
3033
- <Select value={config.lineDatapointColor} fieldName='lineDatapointColor' label='Line Datapoint Color' updateField={updateField} options={['Same as Line', 'Lighter than Line']} />
3034
- </>
3035
- )}
3036
-
3037
- {/* eslint-disable */}
3038
- <label className='header'>
3039
- <span className='edit-label'>Header Theme</span>
3040
- <ul className='color-palette'>
3041
- {headerColors.map(palette => (
3042
- <button
3043
- title={palette}
3044
- key={palette}
3045
- onClick={e => {
3046
- e.preventDefault()
3047
- updateConfig({ ...config, theme: palette })
3048
- }}
3049
- className={config.theme === palette ? 'selected ' + palette : palette}
3050
- ></button>
3051
- ))}
3052
- </ul>
3053
- </label>
3054
- {/* eslint-enable */}
3055
- {(visSupportsNonSequentialPallete() || visSupportsNonSequentialPallete()) && (
3056
- <>
3057
- <label>
3058
- <span className='edit-label'>Chart Color Palette</span>
3059
- </label>
3060
- {visSupportsReverseColorPalette() && <InputToggle fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={config.isPaletteReversed} />}
3061
- {visSupportsSequentialPallete() && (
3062
- <>
3063
- <span>Sequential</span>
3064
- <ul className='color-palette'>
3065
- {sequential.map(palette => {
3066
- const colorOne = {
3067
- backgroundColor: colorPalettes[palette][2]
3068
- }
3069
-
3070
- const colorTwo = {
3071
- backgroundColor: colorPalettes[palette][3]
3072
- }
3073
-
3074
- const colorThree = {
3075
- backgroundColor: colorPalettes[palette][5]
3076
- }
3077
-
3078
- return (
3079
- <button
3080
- title={palette}
3081
- key={palette}
3082
- onClick={e => {
3083
- e.preventDefault()
3084
- updateConfig({ ...config, palette })
3085
- }}
3086
- className={config.palette === palette ? 'selected' : ''}
3087
- >
3088
- <span style={colorOne}></span>
3089
- <span style={colorTwo}></span>
3090
- <span style={colorThree}></span>
3091
- </button>
3092
- )
3093
- })}
3094
- </ul>
3095
- </>
3096
- )}
3097
- {visSupportsNonSequentialPallete() && (
3098
- <>
3099
- <span>Non-Sequential</span>
3100
- <ul className='color-palette'>
3101
- {nonSequential.map(palette => {
3102
- const colorOne = {
3103
- backgroundColor: colorPalettes[palette][2]
3104
- }
3105
-
3106
- const colorTwo = {
3107
- backgroundColor: colorPalettes[palette][4]
3108
- }
3109
-
3110
- const colorThree = {
3111
- backgroundColor: colorPalettes[palette][6]
3112
- }
3113
-
3114
- return (
3115
- <button
3116
- title={palette}
3117
- key={palette}
3118
- onClick={e => {
3119
- e.preventDefault()
3120
- updateConfig({ ...config, palette })
3121
- }}
3122
- className={config.palette === palette ? 'selected' : ''}
3123
- >
3124
- <span style={colorOne}></span>
3125
- <span style={colorTwo}></span>
3126
- <span style={colorThree}></span>
3127
- </button>
3128
- )
3129
- })}
3130
- </ul>
3131
- </>
3132
- )}
3133
- </>
3134
- )}
3135
- {(config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar') && (
3136
- <>
3137
- <InputToggle section='twoColor' fieldName='isPaletteReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={config.twoColor.isPaletteReversed} />
3138
- <ul className='color-palette'>
3139
- {twoColorPalettes.map(palette => {
3140
- const colorOne = {
3141
- backgroundColor: twoColorPalette[palette][0]
3142
- }
3143
-
3144
- const colorTwo = {
3145
- backgroundColor: twoColorPalette[palette][1]
3146
- }
3147
-
3148
- return (
3149
- <button
3150
- title={palette}
3151
- key={palette}
3152
- onClick={e => {
3153
- e.preventDefault()
3154
- updateConfig({ ...config, twoColor: { ...config.twoColor, palette } })
3155
- }}
3156
- className={config.twoColor.palette === palette ? 'selected' : ''}
3157
- >
3158
- <span className='two-color' style={colorOne}></span>
3159
- <span className='two-color' style={colorTwo}></span>
3160
- </button>
3161
- )
3162
- })}
3163
- </ul>
3164
- </>
3165
- )}
3166
-
3167
- {visHasDataCutoff() && (
3168
- <>
3169
- <TextField
3170
- value={config.dataCutoff}
3171
- type='number'
3172
- fieldName='dataCutoff'
3173
- className='number-narrow'
3174
- label='Data Cutoff'
3175
- updateField={updateField}
3176
- tooltip={
3177
- <Tooltip style={{ textTransform: 'none' }}>
3178
- <Tooltip.Target>
3179
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3180
- </Tooltip.Target>
3181
- <Tooltip.Content>
3182
- <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>
3183
- </Tooltip.Content>
3184
- </Tooltip>
3185
- }
3186
- />
3187
- </>
3188
- )}
3189
- {visSupportsBarThickness() && config.orientation === 'horizontal' && !config.isLollipopChart && config.yAxis.labelPlacement !== 'On Bar' && <TextField type='number' value={config.barHeight || '25'} fieldName='barHeight' label=' Bar Thickness' updateField={updateField} min='15' />}
3190
- {((config.visualizationType === 'Bar' && config.orientation !== 'horizontal') || config.visualizationType === 'Combo') && <TextField value={config.barThickness} type='number' fieldName='barThickness' label='Bar Thickness' updateField={updateField} />}
3191
- {visSupportsBarSpace() && <TextField type='number' value={config.barSpace || '15'} fieldName='barSpace' label='Bar Space' updateField={updateField} min='0' />}
3192
- {(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} />}
3193
-
3194
- {config.visualizationType === 'Spark Line' && (
3195
- <div className='cove-accordion__panel-section checkbox-group'>
3196
- <CheckBox value={config.visual?.border} section='visual' fieldName='border' label='Show Border' updateField={updateField} />
3197
- <CheckBox value={config.visual?.borderColorTheme} section='visual' fieldName='borderColorTheme' label='Use Border Color Theme' updateField={updateField} />
3198
- <CheckBox value={config.visual?.accent} section='visual' fieldName='accent' label='Use Accent Style' updateField={updateField} />
3199
- <CheckBox value={config.visual?.background} section='visual' fieldName='background' label='Use Theme Background Color' updateField={updateField} />
3200
- <CheckBox value={config.visual?.hideBackgroundColor} section='visual' fieldName='hideBackgroundColor' label='Hide Background Color' updateField={updateField} />
3201
- </div>
3202
- )}
3203
-
3204
- {(config.visualizationType === 'Line' || config.visualizationType === 'Combo') && <CheckBox value={config.showLineSeriesLabels} fieldName='showLineSeriesLabels' label='Append Series Name to End of Line Charts' updateField={updateField} />}
3205
- {(config.visualizationType === 'Line' || config.visualizationType === 'Combo') && config.showLineSeriesLabels && (
3206
- <CheckBox value={config.colorMatchLineSeriesLabels} fieldName='colorMatchLineSeriesLabels' label='Match Series Color to Name at End of Line Charts' updateField={updateField} />
3207
- )}
3208
-
3209
- {visSupportsTooltipLines() && (
3210
- <>
3211
- <CheckBox value={config.visual.verticalHoverLine} fieldName='verticalHoverLine' section='visual' label='Vertical Hover Line' updateField={updateField} />
3212
- <CheckBox value={config.visual.horizontalHoverLine} fieldName='horizontalHoverLine' section='visual' label='Horizontal Hover Line' updateField={updateField} />
3213
- </>
3214
- )}
3215
- {visSupportsTooltipOpacity() && (
3216
- <label>
3217
- <span className='edit-label column-heading'>Tooltip Opacity</span>
3218
- <input
3219
- type='number'
3220
- value={config.tooltips.opacity ? config.tooltips.opacity : 100}
3221
- onChange={e =>
3222
- updateConfig({
3223
- ...config,
3224
- tooltips: {
3225
- ...config.tooltips,
3226
- opacity: e.target.value
3227
- }
3228
- })
3229
- }
3230
- />
3231
- </label>
3232
- )}
3233
- {config.visualizationType === 'Bar' && <CheckBox value={config.tooltips.singleSeries} fieldName='singleSeries' section='tooltips' label='SHOW HOVER FOR SINGLE DATA SERIES' updateField={updateField} />}
3234
-
3235
- <label>
3236
- <span className='edit-label column-heading'>No Data Message</span>
3237
- <input
3238
- type='text'
3239
- value={config.chartMessage.noData ? config.chartMessage.noData : ''}
3240
- onChange={e =>
3241
- updateConfig({
3242
- ...config,
3243
- chartMessage: {
3244
- ...config.chartMessage,
3245
- noData: e.target.value
3246
- }
3247
- })
3248
- }
3249
- />
3250
- </label>
3251
- </AccordionItemPanel>
3252
- </AccordionItem>
3253
- {/* Spark Line has no data table */}
3254
- {config.visualizationType !== 'Spark Line' && (
3255
- <AccordionItem>
3256
- <AccordionItemHeading>
3257
- <AccordionItemButton>Data Table</AccordionItemButton>
3258
- </AccordionItemHeading>
3259
- <AccordionItemPanel>
3260
- <TextField
3261
- value={config.table.label}
3262
- updateField={updateField}
3263
- section='table'
3264
- fieldName='label'
3265
- id='tableLabel'
3266
- label='Data Table Title'
3267
- placeholder='Data Table'
3268
- tooltip={
3269
- <Tooltip style={{ textTransform: 'none' }}>
3270
- <Tooltip.Target>
3271
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3272
- </Tooltip.Target>
3273
- <Tooltip.Content>
3274
- <p>Label is required for Data Table for 508 Compliance</p>
3275
- </Tooltip.Content>
3276
- </Tooltip>
3277
- }
3278
- />
3279
- <CheckBox
3280
- value={config.table.show}
3281
- section='table'
3282
- fieldName='show'
3283
- label='Show Data Table'
3284
- updateField={updateField}
3285
- className='column-heading'
3286
- tooltip={
3287
- <Tooltip style={{ textTransform: 'none' }}>
3288
- <Tooltip.Target>
3289
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
3290
- </Tooltip.Target>
3291
- <Tooltip.Content>
3292
- <p>Hiding the data table may affect accessibility. An alternate form of accessing visualization data is a 508 requirement.</p>
3293
- </Tooltip.Content>
3294
- </Tooltip>
3295
- }
3296
- />
3297
- {config.visualizationType !== 'Box Plot' && (
3298
- <CheckBox
3299
- value={config.table.showVertical}
3300
- section='table'
3301
- fieldName='showVertical'
3302
- label='Show Vertical Data'
3303
- updateField={updateField}
3304
- className='column-heading'
3305
- tooltip={
3306
- <Tooltip style={{ textTransform: 'none' }}>
3307
- <Tooltip.Target>
3308
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
3309
- </Tooltip.Target>
3310
- <Tooltip.Content>
3311
- <p>This will draw the data table with vertical data instead of horizontal.</p>
3312
- </Tooltip.Content>
3313
- </Tooltip>
3314
- }
3315
- />
3316
- )}
3317
- <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />
3318
- <TextField
3319
- value={config.table.caption}
3320
- updateField={updateField}
3321
- section='table'
3322
- type='textarea'
3323
- fieldName='caption'
3324
- label='Screen Reader Description'
3325
- placeholder=' Data table'
3326
- tooltip={
3327
- <Tooltip style={{ textTransform: 'none' }}>
3328
- <Tooltip.Target>
3329
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3330
- </Tooltip.Target>
3331
- <Tooltip.Content>
3332
- <p>Enter a description of the data table to be read by screen readers.</p>
3333
- </Tooltip.Content>
3334
- </Tooltip>
3335
- }
3336
- />
3337
- <CheckBox value={config.table.limitHeight} section='table' fieldName='limitHeight' label='Limit Table Height' updateField={updateField} />
3338
- {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} />}
3339
- <CheckBox value={config.table.expanded} section='table' fieldName='expanded' label='Expanded by Default' updateField={updateField} />
3340
- {isDashboard && <CheckBox value={config.table.showDataTableLink} section='table' fieldName='showDataTableLink' label='Show Data Table Name & Link' updateField={updateField} />}
3341
- {isLoadedFromUrl && <CheckBox value={config.table.showDownloadUrl} section='table' fieldName='showDownloadUrl' label='Show URL to Automatically Updated Data' updateField={updateField} />}
3342
- <CheckBox value={config.table.download} section='table' fieldName='download' label='Show Download CSV Link' updateField={updateField} />
3343
- <CheckBox value={config.table.showDownloadImgButton} section='table' fieldName='showDownloadImgButton' label='Display Image Button' updateField={updateField} />
3344
- {/* <CheckBox value={config.table.showDownloadPdfButton} section='table' fieldName='showDownloadPdfButton' label='Display PDF Button' updateField={updateField} /> */}
3345
- </AccordionItemPanel>
3346
- </AccordionItem>
3347
- )}
3348
- </Accordion>
3349
- {config.type !== 'Spark Line' && <AdvancedEditor loadConfig={updateConfig} state={config} convertStateToConfig={convertStateToConfig} />}
3350
- </section>
3351
- </section>
3352
- </ErrorBoundary>
3353
- )
3354
- }
3355
-
3356
- export default EditorPanel