@cdc/chart 4.24.4 → 4.24.7

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 (76) hide show
  1. package/dist/cdcchart.js +39611 -36038
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +9 -8
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +115 -71
  7. package/src/_stories/Chart.stories.tsx +26 -171
  8. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  9. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  10. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  11. package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
  12. package/src/_stories/_mock/bar-chart-suppressed.json +474 -0
  13. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  14. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  15. package/src/_stories/_mock/lollipop.json +171 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  17. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  19. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  20. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  21. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  22. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  23. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  24. package/src/components/Annotations/index.tsx +13 -0
  25. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  26. package/src/components/AreaChart/components/AreaChart.jsx +2 -2
  27. package/src/components/BarChart/components/BarChart.Horizontal.tsx +78 -71
  28. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  29. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  30. package/src/components/BarChart/components/BarChart.Vertical.tsx +100 -87
  31. package/src/components/BarChart/helpers/index.ts +102 -0
  32. package/src/components/DeviationBar.jsx +4 -2
  33. package/src/components/EditorPanel/EditorPanel.tsx +435 -613
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +135 -7
  36. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  37. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -5
  38. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  39. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  40. package/src/components/EditorPanel/components/panels.scss +4 -0
  41. package/src/components/EditorPanel/editor-panel.scss +19 -0
  42. package/src/components/EditorPanel/useEditorPermissions.js +23 -3
  43. package/src/components/Legend/Legend.Component.tsx +66 -15
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  45. package/src/components/Legend/helpers/index.ts +5 -0
  46. package/src/components/LineChart/LineChartProps.ts +16 -6
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +22 -11
  48. package/src/components/LineChart/helpers.ts +148 -10
  49. package/src/components/LineChart/index.tsx +71 -44
  50. package/src/components/LinearChart.jsx +184 -125
  51. package/src/components/PairedBarChart.jsx +9 -9
  52. package/src/components/PieChart/PieChart.tsx +4 -4
  53. package/src/components/Sankey/index.tsx +73 -20
  54. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  55. package/src/components/ZoomBrush.tsx +120 -55
  56. package/src/data/initial-state.js +14 -6
  57. package/src/helpers/handleChartTabbing.ts +8 -0
  58. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  59. package/src/hooks/{useBarChart.js → useBarChart.ts} +9 -22
  60. package/src/hooks/useColorScale.ts +1 -1
  61. package/src/hooks/useMinMax.ts +29 -5
  62. package/src/hooks/useScales.ts +48 -26
  63. package/src/hooks/useTooltip.tsx +62 -15
  64. package/src/scss/main.scss +69 -12
  65. package/src/types/ChartConfig.ts +53 -16
  66. package/src/types/ChartContext.ts +13 -0
  67. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  68. package/LICENSE +0 -201
  69. package/src/_stories/ChartLine.preliminary.tsx +0 -19
  70. package/src/_stories/ChartSuppress.stories.tsx +0 -19
  71. package/src/_stories/_mock/suppress_mock.json +0 -911
  72. package/src/helpers/computeMarginBottom.ts +0 -56
  73. package/src/helpers/filterData.ts +0 -18
  74. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  75. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  76. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect, useCallback, memo, useContext } from 'react'
2
- import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
2
+ import { DragDropContext, Droppable } from '@hello-pangea/dnd'
3
3
  import { isDateScale } from '@cdc/core/helpers/cove/date'
4
4
  import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
5
5
  import Layout from '@cdc/core/components/Layout'
@@ -8,18 +8,18 @@ import Layout from '@cdc/core/components/Layout'
8
8
  import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
9
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
10
10
  import Icon from '@cdc/core/components/ui/Icon'
11
- import InputToggle from '@cdc/core/components/inputs/InputToggle'
11
+ import ColumnsEditor from '@cdc/core/components/EditorPanel/ColumnsEditor'
12
+ import DataTableEditor from '@cdc/core/components/EditorPanel/DataTableEditor'
13
+ import VizFilterEditor from '@cdc/core/components/EditorPanel/VizFilterEditor'
12
14
  import Tooltip from '@cdc/core/components/ui/Tooltip'
13
15
  import { Select, TextField, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
16
+ import { viewports } from '@cdc/core/helpers/getViewport'
14
17
 
15
18
  // chart components
16
19
  import Panels from './components/Panels'
17
- import Series from './components/Panel.Series.jsx'
18
20
 
19
21
  // cdc additional
20
- import { useColorPalette } from '../../hooks/useColorPalette'
21
22
  import { useEditorPermissions } from './useEditorPermissions'
22
- import { useFilters } from '@cdc/core/components/Filters'
23
23
  import { useHighlightedBars } from '../../hooks/useHighlightedBars'
24
24
  import ConfigContext from '../../ConfigContext'
25
25
  import useReduceData from '../../hooks/useReduceData'
@@ -27,113 +27,56 @@ import useRightAxis from '../../hooks/useRightAxis'
27
27
  import WarningImage from '../../images/warning.svg'
28
28
  import useMinMax from '../../hooks/useMinMax'
29
29
 
30
- import { type ChartConfig } from '../../types/ChartConfig'
31
30
  import { type ChartContext } from '../../types/ChartContext'
31
+ import { type ChartConfig } from '../../types/ChartConfig'
32
32
 
33
33
  import './editor-panel.scss'
34
34
  import { Anchor } from '@cdc/core/types/Axis'
35
- import DataTableEditor from '@cdc/core/components/EditorPanel/DataTableEditor'
36
35
  import EditorPanelContext from './EditorPanelContext'
36
+ import _ from 'lodash'
37
+ import { adjustedSymbols as symbolCodes } from '@cdc/core/helpers/footnoteSymbols'
37
38
 
38
- const DataSuppression = memo(({ config, updateConfig, data }: any) => {
39
- const getColumnOptions = () => {
40
- const keys = new Set()
41
- data.forEach(d => {
42
- Object.keys(d).forEach(key => {
43
- keys.add(key)
44
- })
45
- })
46
- return [...keys]
47
- }
48
-
49
- const getIconOptions = () => {
50
- return ['star']
51
- }
52
-
53
- let removeColumn = i => {
54
- let suppressedData = []
55
-
56
- if (config.suppressedData) {
57
- suppressedData = [...config.suppressedData]
58
- }
59
-
60
- suppressedData.splice(i, 1)
39
+ interface PreliminaryProps {
40
+ config: ChartConfig
41
+ updateConfig: Function
42
+ data: Record<string, any>[]
43
+ }
61
44
 
62
- updateConfig({ ...config, suppressedData })
63
- }
45
+ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, data }) => {
46
+ const isCombo = config.visualizationType === 'Combo'
47
+ const lineSeriesExists = config.runtime.lineSeriesKeys?.length > 0
48
+ const barSeriesExists = config.runtime.barSeriesKeys?.length > 0
49
+ const hasComboLineSeries = isCombo && lineSeriesExists
50
+ const hasComboBarSeries = isCombo && barSeriesExists
64
51
 
65
- let addColumn = () => {
66
- let suppressedData = config.suppressedData ? [...config.suppressedData] : []
67
- suppressedData.push({ label: '', column: '', value: '', icon: '' })
68
- updateConfig({ ...config, suppressedData })
52
+ const getColumnOptions = () => {
53
+ return _.uniq(_.flatMap(data, _.keys))
69
54
  }
70
55
 
71
- let update = (fieldName, value, i) => {
72
- let suppressedData = []
73
-
74
- if (config.suppressedData) {
75
- suppressedData = [...config.suppressedData]
76
- }
77
-
78
- suppressedData[i][fieldName] = value
79
- updateConfig({ ...config, suppressedData })
56
+ const getTypeOptions = () => {
57
+ return config.visualizationType === 'Line' || hasComboLineSeries ? ['effect', 'suppression'] : ['suppression']
80
58
  }
81
59
 
82
- return (
83
- <>
84
- {config.suppressedData &&
85
- config.suppressedData.map(({ label, column, value, icon }, i) => {
86
- return (
87
- <div key={`suppressed-${i}`} className='edit-block'>
88
- <button
89
- type='button'
90
- className='remove-column'
91
- onClick={event => {
92
- event.preventDefault()
93
- removeColumn(i)
94
- }}
95
- >
96
- Remove
97
- </button>
98
- <Select value={column} initial='Select' fieldName='column' label='Column' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getColumnOptions()} />
99
- <TextField value={value} fieldName='value' label='Value' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} />
100
- <Select value={icon} initial='Select' fieldName='icon' label='Icon' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getIconOptions()} />
101
- <TextField value={label} fieldName='label' label='Label' placeholder='suppressed' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} />
102
- </div>
103
- )
104
- })}
105
-
106
- <button type='button' onClick={addColumn} className='btn full-width'>
107
- Add Suppression Class
108
- </button>
109
- </>
110
- )
111
- })
112
- const PreliminaryData = memo(({ config, updateConfig, data }) => {
113
- const getColumnOptions = () => {
114
- const keys = new Set()
115
- data.forEach(d => {
116
- Object.keys(d).forEach(key => {
117
- keys.add(key)
118
- })
119
- })
120
- return [...keys]
60
+ const lineCodes = {
61
+ 'Dashed Small': '\u002D \u002D \u002D',
62
+ 'Dashed Medium': '\u2013 \u2013',
63
+ 'Dashed Large': '\u2014 \u2013',
64
+ 'Open Circles': '\u25EF',
65
+ 'Filled Circles': ''
121
66
  }
122
67
 
123
- const getTypeOptions = () => {
124
- if (config.visualizationType === 'Line' || config.visualizationType === 'Combo') {
125
- return ['effect']
68
+ const getStyleOptions = type => {
69
+ const options = Object.keys(lineCodes)
70
+ if (type === 'suppression') {
71
+ return options.slice(0, -1)
126
72
  } else {
127
- return ['suppression']
73
+ return options
128
74
  }
129
75
  }
130
76
 
131
- const getStyleOptions = () => {
132
- if (config.visualizationType === 'Line' || config.visualizationType === 'Combo') {
133
- return ['Dashed Small', 'Dashed Medium', 'Dashed Large', 'Open Circles']
134
- }
135
- if (config.visualizationType === 'Bar') {
136
- return ['star']
77
+ const getSymbolOptions = () => {
78
+ if (config.visualizationType === 'Bar' || hasComboBarSeries) {
79
+ return Object.keys(symbolCodes)
137
80
  }
138
81
  }
139
82
 
@@ -150,8 +93,26 @@ const PreliminaryData = memo(({ config, updateConfig, data }) => {
150
93
  }
151
94
 
152
95
  let addColumn = () => {
96
+ const defaultType = config.visualizationType === 'Line' ? 'effect' : 'suppression'
153
97
  let preliminaryData = config.preliminaryData ? [...config.preliminaryData] : []
154
- preliminaryData.push({ type: '', label: '', column: '', value: '', style: '' })
98
+ const defaultValues = {
99
+ type: defaultType,
100
+ seriesKey: '',
101
+ label: 'Suppressed',
102
+ column: '',
103
+ value: '',
104
+ style: '',
105
+ displayTooltip: true,
106
+ displayLegend: true,
107
+ displayTable: true,
108
+ symbol: '',
109
+ iconCode: '',
110
+ lineCode: '',
111
+ hideBarSymbol: false,
112
+ hideLineStyle: false,
113
+ circleSize: 6
114
+ }
115
+ preliminaryData.push(defaultValues)
155
116
  updateConfig({ ...config, preliminaryData })
156
117
  }
157
118
 
@@ -163,15 +124,22 @@ const PreliminaryData = memo(({ config, updateConfig, data }) => {
163
124
  }
164
125
 
165
126
  preliminaryData[i][fieldName] = value
127
+ if (fieldName === 'symbol') {
128
+ preliminaryData[i]['iconCode'] = symbolCodes[value]
129
+ }
130
+ if (fieldName === 'style') {
131
+ preliminaryData[i]['lineCode'] = lineCodes[value]
132
+ }
166
133
  updateConfig({ ...config, preliminaryData })
167
134
  }
168
135
 
169
136
  return (
170
137
  <>
171
138
  {config.preliminaryData &&
172
- config.preliminaryData.map(({ seriesKey, type, label, column, value, style }, i) => {
139
+ config.preliminaryData?.map(({ circleSize, column, displayLegend, displayTable, displayTooltip, label, seriesKey, style, symbol, type, value, hideBarSymbol, hideLineStyle }, i) => {
173
140
  return (
174
141
  <div key={`preliminaryData-${i}`} className='edit-block'>
142
+ <p> {type === 'suppression' ? 'Suppressed' : 'Effect'} Data</p>
175
143
  <button
176
144
  type='button'
177
145
  className='remove-column'
@@ -182,23 +150,178 @@ const PreliminaryData = memo(({ config, updateConfig, data }) => {
182
150
  >
183
151
  Remove
184
152
  </button>
185
- <Select value={type} initial='Select' fieldName='type' label='Type' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getTypeOptions()} />
186
- <Select value={seriesKey} initial='Select' fieldName='seriesKey' label='ASSOCIATE TO SERIES' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys} />
187
- <Select value={column} initial='Select' fieldName='column' label='COLUMN WITH CONFIGURATION VALUE' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getColumnOptions()} />
188
- <TextField value={value} fieldName='value' label='VALUE TO TRIGGER' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} />
189
- <Select value={style} initial='Select' fieldName='style' label='Style' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} options={getStyleOptions()} />
190
153
 
191
- <TextField value={label} fieldName='label' label='Label' placeholder='' updateField={(section, subsection, fieldName, value) => update(fieldName, value, i)} />
154
+ <Select value={type} initial={config.visualizationType == 'Bar' ? '' : 'Select'} fieldName='type' label='Type' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} options={getTypeOptions()} />
155
+ {type === 'suppression' ? (
156
+ <>
157
+ <Select
158
+ tooltip={
159
+ <Tooltip style={{ textTransform: 'none' }}>
160
+ <Tooltip.Target>
161
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
162
+ </Tooltip.Target>
163
+ <Tooltip.Content>
164
+ <p> If no “Data Series" is selected, the symbol will be applied to "all" suppressed values indicated in the dataset. If you select a particular data series, there's no need to fill in “suppression line style” and “suppression symbol” below.</p>
165
+ </Tooltip.Content>
166
+ </Tooltip>
167
+ }
168
+ value={column}
169
+ initial='Select'
170
+ fieldName='column'
171
+ label='Add Data Series'
172
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
173
+ options={config.runtime?.seriesKeys}
174
+ />
175
+ <TextField value={value} fieldName='value' label='Suppressed Data Value' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} />
176
+ {(hasComboLineSeries || config.visualizationType === 'Line') && (
177
+ <>
178
+ <Select
179
+ tooltip={
180
+ <Tooltip style={{ textTransform: 'none' }}>
181
+ <Tooltip.Target>
182
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
183
+ </Tooltip.Target>
184
+ <Tooltip.Content>
185
+ <p>The recommended approach for presenting data is to include a footnote indicating any data suppression.</p>
186
+ </Tooltip.Content>
187
+ </Tooltip>
188
+ }
189
+ value={style}
190
+ initial='Select'
191
+ fieldName='style'
192
+ label={'suppression line style'}
193
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
194
+ options={getStyleOptions(type)}
195
+ />
196
+ <CheckBox value={hideLineStyle} fieldName='hideLineStyle' label='Hide Suppressed line Style' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} />
197
+ </>
198
+ )}
199
+
200
+ {(hasComboBarSeries || config.visualizationType === 'Bar') && (
201
+ <>
202
+ <Select
203
+ tooltip={
204
+ <Tooltip style={{ textTransform: 'none' }}>
205
+ <Tooltip.Target>
206
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
207
+ </Tooltip.Target>
208
+ <Tooltip.Content>
209
+ <p>The suggested method for presenting suppressed data is to use "double asterisks". If "double asterisks" are already used elsewhere (e.g., footnotes), please select an alternative symbol from the menu to denote data suppression.</p>
210
+ </Tooltip.Content>
211
+ </Tooltip>
212
+ }
213
+ value={symbol}
214
+ initial='Select'
215
+ fieldName='symbol'
216
+ label={config.visualizationType === 'Combo' ? 'suppression bar symbol' : 'suppression symbol'}
217
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
218
+ options={getSymbolOptions()}
219
+ />
220
+ <CheckBox value={hideBarSymbol} fieldName='hideBarSymbol' label='Hide Suppressed Bar Symbol ' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} />
221
+ </>
222
+ )}
223
+
224
+ <TextField
225
+ tooltip={
226
+ <Tooltip style={{ textTransform: 'none' }}>
227
+ <Tooltip.Target>
228
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
229
+ </Tooltip.Target>
230
+ <Tooltip.Content>
231
+ <p>This label will display in the tooltip and legend.</p>
232
+ </Tooltip.Content>
233
+ </Tooltip>
234
+ }
235
+ value={label ? label : 'Suppressed'}
236
+ fieldName='label'
237
+ label='Suppressed Data Label'
238
+ placeholder=''
239
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
240
+ />
241
+ <CheckBox
242
+ tooltip={
243
+ <Tooltip style={{ textTransform: 'none' }}>
244
+ <Tooltip.Target>
245
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
246
+ </Tooltip.Target>
247
+ <Tooltip.Content>
248
+ <p>Enabling this tooltip will provide a clearer indication of 'suppressed' or 'zero data' values, whichever is applicable. Deselecting 'Display In Tooltip' indicates that you do not want to display 'suppressed' or 'zero data' values in tooltips when hovering over them.</p>
249
+ </Tooltip.Content>
250
+ </Tooltip>
251
+ }
252
+ value={displayTooltip}
253
+ fieldName='displayTooltip'
254
+ label='Display in tooltips'
255
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
256
+ />
257
+ <CheckBox
258
+ tooltip={
259
+ <Tooltip style={{ textTransform: 'none' }}>
260
+ <Tooltip.Target>
261
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
262
+ </Tooltip.Target>
263
+ <Tooltip.Content>
264
+ <p>Deselecting "Display in Legend" indicates that you do not want to display suppressed data in the legend.</p>
265
+ </Tooltip.Content>
266
+ </Tooltip>
267
+ }
268
+ value={displayLegend}
269
+ fieldName='displayLegend'
270
+ label='Display in legend'
271
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
272
+ />
273
+ <CheckBox
274
+ tooltip={
275
+ <Tooltip style={{ textTransform: 'none' }}>
276
+ <Tooltip.Target>
277
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
278
+ </Tooltip.Target>
279
+ <Tooltip.Content>
280
+ <p>Deselecting "Display In Data Table" indicates that you do not want to display suppressed data in the data table.</p>
281
+ </Tooltip.Content>
282
+ </Tooltip>
283
+ }
284
+ value={displayTable}
285
+ fieldName='displayTable'
286
+ label='Display in table'
287
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
288
+ />
289
+ </>
290
+ ) : (
291
+ <>
292
+ <Select value={seriesKey} initial='Select' fieldName='seriesKey' label='ASSOCIATE TO SERIES' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} options={config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys} />
293
+ <Select value={column} initial='Select' fieldName='column' label='COLUMN WITH CONFIGURATION VALUE' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} options={getColumnOptions()} />
294
+ <TextField
295
+ tooltip={
296
+ <Tooltip style={{ textTransform: 'none' }}>
297
+ <Tooltip.Target>
298
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
299
+ </Tooltip.Target>
300
+ <Tooltip.Content>
301
+ <p>If 'Filled Circles' is selected as the style, this field is optional, and the style 'Filled Circles' will apply to all points within the associated series data.</p>
302
+ </Tooltip.Content>
303
+ </Tooltip>
304
+ }
305
+ value={value}
306
+ fieldName='value'
307
+ label='VALUE TO TRIGGER'
308
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
309
+ />
310
+ <Select value={style} initial='Select' fieldName='style' label='Style' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} options={getStyleOptions(type)} />
311
+ {style.includes('Circles') && <TextField className='number-narrow' type='number' value={circleSize} fieldName='circleSize' label='circle size' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} />}
312
+ {style !== 'Filled Circles' && <TextField value={label} fieldName='label' label='Label' placeholder='' updateField={(_, __, fieldName, value) => update(fieldName, value, i)} />}
313
+ </>
314
+ )}
192
315
  </div>
193
316
  )
194
317
  })}
195
318
 
196
319
  <button type='button' onClick={addColumn} className='btn full-width'>
197
- {config.visualizationType === 'Line' || config.visualizationType === 'Combo' ? 'Add Special Line' : config.visualizationType === 'Bar' ? ' Add Special Bar' : 'Add Special Line/Bar'}
320
+ {config.visualizationType === 'Line' ? 'Add Special Line' : config.visualizationType === 'Bar' ? ' Add Special Bar' : 'Add Special Bar/Line'}
198
321
  </button>
199
322
  </>
200
323
  )
201
- })
324
+ }
202
325
 
203
326
  const EditorPanel = () => {
204
327
  const {
@@ -220,13 +343,11 @@ const EditorPanel = () => {
220
343
  lineOptions,
221
344
  rawData,
222
345
  highlight,
223
- highlightReset
346
+ highlightReset,
347
+ dimensions
224
348
  } = useContext<ChartContext>(ConfigContext)
225
349
 
226
350
  const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, unfilteredData)
227
-
228
- const { twoColorPalettes, sequential, nonSequential } = useColorPalette(config, updateConfig)
229
-
230
351
  const properties = { data, config }
231
352
  const { leftMax, rightMax } = useMinMax(properties)
232
353
 
@@ -276,9 +397,6 @@ const EditorPanel = () => {
276
397
  visHasDataSuppression
277
398
  } = useEditorPermissions()
278
399
 
279
- // argument acts as props
280
- const { handleFilterOrder, filterOrderOptions, filterStyleOptions } = useFilters({ config, setConfig: updateConfig, filteredData: data, setFilteredData })
281
-
282
400
  // when the visualization type changes we
283
401
  // have to update the individual series type & axis details
284
402
  // dataKey is unchanged here.
@@ -356,6 +474,12 @@ const EditorPanel = () => {
356
474
  if (isDateScale(updatedConfig.xAxis) && !updatedConfig.xAxis.padding) {
357
475
  updatedConfig.xAxis.padding = 6
358
476
  }
477
+ // DEV-8008 - Remove Bar styling when Line is converted to Bar
478
+ if (updatedConfig.visualizationType === 'Line') {
479
+ updatedConfig.visualizationSubType = 'regular'
480
+ updatedConfig.barStyle = 'flat'
481
+ updatedConfig.isLollipopChart = false
482
+ }
359
483
  }
360
484
 
361
485
  const updateField = (section, subsection, fieldName, newValue) => {
@@ -389,13 +513,18 @@ const EditorPanel = () => {
389
513
  return
390
514
  }
391
515
 
392
- if (section === 'columns' && subsection !== '' && fieldName !== '') {
516
+ const truthy = val => {
517
+ if (val === 0) return true // indexes can be used as keys
518
+ return !!val
519
+ }
520
+
521
+ if (section === 'columns' && truthy(subsection) && truthy(fieldName)) {
393
522
  updateConfig({
394
523
  ...config,
395
- [section]: {
396
- ...config[section],
524
+ columns: {
525
+ ...config.columns,
397
526
  [subsection]: {
398
- ...config[section][subsection],
527
+ ...config.columns[subsection],
399
528
  [fieldName]: newValue
400
529
  }
401
530
  }
@@ -403,6 +532,8 @@ const EditorPanel = () => {
403
532
  return
404
533
  }
405
534
  if (null === section && null === subsection) {
535
+ // special case that allows for updating the config object directly
536
+ if (!truthy(fieldName)) console.error('fieldName is required')
406
537
  let updatedConfig = { ...config, [fieldName]: newValue }
407
538
  enforceRestrictions(updatedConfig)
408
539
  updateConfig(updatedConfig)
@@ -413,13 +544,13 @@ const EditorPanel = () => {
413
544
 
414
545
  let sectionValue = isArray ? [...config[section], newValue] : { ...config[section], [fieldName]: newValue }
415
546
 
416
- if (null !== subsection) {
547
+ if (truthy(subsection)) {
417
548
  if (isArray) {
418
549
  sectionValue = [...config[section]]
419
550
  sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue }
420
551
  } else if (typeof newValue === 'string') {
421
552
  sectionValue[subsection] = newValue
422
- } else {
553
+ } else if (truthy(fieldName)) {
423
554
  sectionValue = { ...config[section], [subsection]: { ...config[section][subsection], [fieldName]: newValue } }
424
555
  }
425
556
  }
@@ -432,6 +563,7 @@ const EditorPanel = () => {
432
563
  }
433
564
 
434
565
  const [displayPanel, setDisplayPanel] = useState(true)
566
+ const [displayViewportOverrides, setDisplayViewportOverrides] = useState(false)
435
567
 
436
568
  if (loading) {
437
569
  return null
@@ -457,30 +589,6 @@ const EditorPanel = () => {
457
589
  })
458
590
  }
459
591
 
460
- const removeFilter = index => {
461
- let filters = [...config.filters]
462
-
463
- filters.splice(index, 1)
464
-
465
- updateConfig({ ...config, filters })
466
- }
467
-
468
- const updateFilterProp = (name, index, value) => {
469
- let filters = [...config.filters]
470
-
471
- filters[index][name] = value
472
-
473
- updateConfig({ ...config, filters })
474
- }
475
-
476
- const addNewFilter = () => {
477
- let filters = config.filters ? [...config.filters] : []
478
-
479
- filters.push({ values: [] })
480
-
481
- updateConfig({ ...config, filters })
482
- }
483
-
484
592
  const addNewSeries = seriesKey => {
485
593
  let newSeries = config.series ? [...config.series] : []
486
594
  let forecastingStages = Array.from(new Set(data.map(item => item[seriesKey])))
@@ -505,37 +613,6 @@ const EditorPanel = () => {
505
613
  updateConfig({ ...config }, newData)
506
614
  }
507
615
 
508
- const removeSeries = seriesKey => {
509
- let series = [...config.series]
510
- let seriesIndex = -1
511
-
512
- for (let i = 0; i < series.length; i++) {
513
- if (series[i].dataKey === seriesKey) {
514
- seriesIndex = i
515
- break
516
- }
517
- }
518
-
519
- if (seriesIndex !== -1) {
520
- series.splice(seriesIndex, 1)
521
-
522
- let newConfig = { ...config, series }
523
-
524
- if (series.length === 0) {
525
- delete newConfig.series
526
- }
527
-
528
- updateConfig(newConfig)
529
- }
530
-
531
- if (config.visualizationType === 'Paired Bar') {
532
- updateConfig({
533
- ...config,
534
- series: []
535
- })
536
- }
537
- }
538
-
539
616
  const addNewExclusion = exclusionKey => {
540
617
  let newExclusion = [...config.exclusions.keys]
541
618
  newExclusion.push(exclusionKey)
@@ -569,16 +646,6 @@ const EditorPanel = () => {
569
646
  }
570
647
  }
571
648
 
572
- const getFilters = () => {
573
- let columns = {}
574
-
575
- unfilteredData.forEach(row => {
576
- Object.keys(row).forEach(columnName => (columns[columnName] = true))
577
- })
578
-
579
- return Object.keys(columns)
580
- }
581
-
582
649
  const getColumns = (filter = true) => {
583
650
  let columns = {}
584
651
  unfilteredData.forEach(row => {
@@ -902,40 +969,8 @@ const EditorPanel = () => {
902
969
  })
903
970
  }
904
971
 
905
- // prevents adding duplicates
906
- const additionalColumns = Object.keys(config.columns).filter(value => {
907
- const defaultCols = [config.xAxis.dataKey] // ['geo', 'navigate', 'primary', 'latitude', 'longitude']
908
-
909
- if (true === defaultCols.includes(value)) {
910
- return false
911
- }
912
- return true
913
- })
914
-
915
- // just adds a new column but not set to any data yet
916
- const addAdditionalColumn = number => {
917
- const columnKey = `additionalColumn${number}`
918
-
919
- updateConfig({
920
- ...config,
921
- columns: {
922
- ...config.columns,
923
- [columnKey]: {
924
- label: 'New Column',
925
- dataTable: false,
926
- tooltips: false,
927
- prefix: '',
928
- suffix: '',
929
- forestPlot: false,
930
- startingPoint: '0',
931
- forestPlotAlignRight: false
932
- }
933
- }
934
- })
935
- }
936
-
937
972
  const removeAdditionalColumn = columnName => {
938
- const newColumns = config.columns
973
+ const newColumns = _.cloneDeep(config.columns)
939
974
 
940
975
  delete newColumns[columnName]
941
976
 
@@ -1019,6 +1054,14 @@ const EditorPanel = () => {
1019
1054
  updateConfig(updatedConfig)
1020
1055
  }
1021
1056
 
1057
+ const updateViewportOverrides = (property, viewport, numTicks) => {
1058
+ const propertyObject = { ...config.xAxis[property] }
1059
+ propertyObject[viewport] = numTicks
1060
+ const updatedConfig = { ...config, xAxis: { ...config.xAxis, [property]: propertyObject } }
1061
+
1062
+ updateConfig(updatedConfig)
1063
+ }
1064
+
1022
1065
  const editorContextValues = {
1023
1066
  addNewExclusion,
1024
1067
  data,
@@ -1155,7 +1198,26 @@ const EditorPanel = () => {
1155
1198
  {config.runtime.seriesKeys && config.runtime.seriesKeys.length === 1 && !['Box Plot', 'Deviation Bar', 'Forest Plot'].includes(config.visualizationType) && (
1156
1199
  <CheckBox value={config.isLegendValue} fieldName='isLegendValue' label='Use Legend Value in Hover' updateField={updateField} />
1157
1200
  )}
1158
- <TextField value={config.yAxis.numTicks} placeholder='Auto' type='number' section='yAxis' fieldName='numTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
1201
+ <TextField
1202
+ value={config.yAxis.numTicks}
1203
+ placeholder='Auto'
1204
+ type='number'
1205
+ section='yAxis'
1206
+ fieldName='numTicks'
1207
+ label='Number of ticks'
1208
+ className='number-narrow'
1209
+ tooltip={
1210
+ <Tooltip style={{ textTransform: 'none' }}>
1211
+ <Tooltip.Target>
1212
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1213
+ </Tooltip.Target>
1214
+ <Tooltip.Content>
1215
+ <p>Apporoximate number of ticks. Other factors such as space available and data may change the exact number of ticks used.</p>
1216
+ </Tooltip.Content>
1217
+ </Tooltip>
1218
+ }
1219
+ updateField={updateField}
1220
+ />
1159
1221
  <TextField
1160
1222
  value={config.yAxis.size}
1161
1223
  type='number'
@@ -1175,6 +1237,7 @@ const EditorPanel = () => {
1175
1237
  </Tooltip>
1176
1238
  }
1177
1239
  />
1240
+ <TextField value={config.yAxis.labelOffset} section='yAxis' fieldName='labelOffset' label='Label offset' type='number' className='number-narrow' updateField={updateField} />
1178
1241
  {config.orientation === 'horizontal' && config.visualizationType !== 'Paired Bar' && <CheckBox value={config.isResponsiveTicks} fieldName='isResponsiveTicks' label='Use Responsive Ticks' updateField={updateField} />}
1179
1242
  {(config.orientation === 'vertical' || !config.isResponsiveTicks) && <TextField value={config.yAxis.tickRotation || 0} type='number' min={0} section='yAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
1180
1243
  {config.isResponsiveTicks && config.orientation === 'horizontal' && config.visualizationType !== 'Paired Bar' && (
@@ -1202,7 +1265,6 @@ const EditorPanel = () => {
1202
1265
 
1203
1266
  {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
1204
1267
  {/* <TextField value={config.yAxis.axisPadding} type='number' max={10} min={0} section='yAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateField} /> */}
1205
- {config.orientation === 'horizontal' && <TextField value={config.xAxis.labelOffset} section='xAxis' fieldName='labelOffset' label='Label offset' type='number' className='number-narrow' updateField={updateField} />}
1206
1268
  {visSupportsValueAxisGridLines() && <CheckBox value={config.yAxis.gridLines} section='yAxis' fieldName='gridLines' label='Show Gridlines' updateField={updateField} />}
1207
1269
  <CheckBox value={config.yAxis.enablePadding} section='yAxis' fieldName='enablePadding' label='Add Padding to Value Axis Scale' updateField={updateField} />
1208
1270
  {config.yAxis.enablePadding && <TextField type='number' section='yAxis' fieldName='scalePadding' label='Padding Percentage' className='number-narrow' updateField={updateField} value={config.yAxis.scalePadding} />}
@@ -1210,7 +1272,23 @@ const EditorPanel = () => {
1210
1272
  </>
1211
1273
  )}
1212
1274
  <span className='divider-heading'>Number Formatting</span>
1213
- <CheckBox value={config.dataFormat.commas} section='dataFormat' fieldName='commas' label='Add commas' updateField={updateField} />
1275
+ <CheckBox
1276
+ value={config.dataFormat.commas}
1277
+ section='dataFormat'
1278
+ fieldName='commas'
1279
+ label='Add commas'
1280
+ updateField={updateField}
1281
+ tooltip={
1282
+ <Tooltip style={{ textTransform: 'none' }}>
1283
+ <Tooltip.Target>
1284
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1285
+ </Tooltip.Target>
1286
+ <Tooltip.Content>
1287
+ <p>{`Selecting this option will add commas to the left value axis, tooltip hover, and data table.`}</p>
1288
+ </Tooltip.Content>
1289
+ </Tooltip>
1290
+ }
1291
+ />
1214
1292
  <CheckBox
1215
1293
  value={config.dataFormat.abbreviated}
1216
1294
  section='dataFormat'
@@ -1271,7 +1349,7 @@ const EditorPanel = () => {
1271
1349
  {config.orientation === 'horizontal' ? ( // horizontal - x is vertical y is horizontal
1272
1350
  <>
1273
1351
  {visSupportsValueAxisLine() && <CheckBox value={config.xAxis.hideAxis} section='xAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />}
1274
- {visSupportsValueAxisLabels() && <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />}
1352
+ {visSupportsValueAxisLabels() && <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Tick Labels' updateField={updateField} />}
1275
1353
  {visSupportsValueAxisTicks() && <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />}
1276
1354
  {visSupportsValueAxisMax() && <TextField value={config.xAxis.max} section='xAxis' fieldName='max' label='max value' type='number' placeholder='Auto' updateField={updateField} />}
1277
1355
  <span style={{ color: 'red', display: 'block' }}>{warningMsg.maxMsg}</span>
@@ -1289,7 +1367,7 @@ const EditorPanel = () => {
1289
1367
  config.visualizationType !== 'Pie' && (
1290
1368
  <>
1291
1369
  <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
1292
- <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />
1370
+ <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Tick Labels' updateField={updateField} />
1293
1371
  <CheckBox value={config.yAxis.hideTicks} section='yAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />
1294
1372
 
1295
1373
  <TextField value={config.yAxis.max} section='yAxis' fieldName='max' type='number' label='left axis max value' placeholder='Auto' updateField={updateField} />
@@ -1611,7 +1689,7 @@ const EditorPanel = () => {
1611
1689
  </div>
1612
1690
 
1613
1691
  <CheckBox value={config.yAxis.rightHideAxis} section='yAxis' fieldName='rightHideAxis' label='Hide Axis' updateField={updateField} />
1614
- <CheckBox value={config.yAxis.rightHideLabel} section='yAxis' fieldName='rightHideLabel' label='Hide Label' updateField={updateField} />
1692
+ <CheckBox value={config.yAxis.rightHideLabel} section='yAxis' fieldName='rightHideLabel' label='Hide Tick Labels' updateField={updateField} />
1615
1693
  <CheckBox value={config.yAxis.rightHideTicks} section='yAxis' fieldName='rightHideTicks' label='Hide Ticks' updateField={updateField} />
1616
1694
 
1617
1695
  <TextField value={config.yAxis.max} section='yAxis' fieldName='rightMax' type='number' label='right axis max value' placeholder='Auto' updateField={updateField} />
@@ -1898,7 +1976,25 @@ const EditorPanel = () => {
1898
1976
  }
1899
1977
  updateField={updateField}
1900
1978
  />
1901
- {/* {visHasBrushChart && <CheckBox value={config.brush.active} section='brush' fieldName='active' label='Brush Slider ' updateField={updateField} />} */}
1979
+ {false && visHasBrushChart && (
1980
+ <CheckBox
1981
+ value={config.brush?.active}
1982
+ section='brush'
1983
+ fieldName='active'
1984
+ label='Brush Slider '
1985
+ updateField={updateField}
1986
+ tooltip={
1987
+ <Tooltip style={{ textTransform: 'none' }}>
1988
+ <Tooltip.Target>
1989
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1990
+ </Tooltip.Target>
1991
+ <Tooltip.Content>
1992
+ <p>Use the brush slider to narrow down your data view to specific values along the axis. This tool is useful for examining detailed data segments within the larger dataset. </p>
1993
+ </Tooltip.Content>
1994
+ </Tooltip>
1995
+ }
1996
+ />
1997
+ )}
1902
1998
 
1903
1999
  {config.exclusions.active && (
1904
2000
  <>
@@ -1938,12 +2034,101 @@ const EditorPanel = () => {
1938
2034
  )}
1939
2035
 
1940
2036
  {visSupportsDateCategoryNumTicks() && config.xAxis.type !== 'date-time' && config.xAxis.manual && (
1941
- <TextField value={config.xAxis.manualStep} placeholder='Auto' type='number' min={1} section='xAxis' fieldName='manualStep' label='Step count' className='number-narrow' updateField={updateField} />
2037
+ <>
2038
+ <TextField
2039
+ value={config.xAxis.manualStep}
2040
+ placeholder='Auto'
2041
+ type='number'
2042
+ min={1}
2043
+ section='xAxis'
2044
+ fieldName='manualStep'
2045
+ label='Step count'
2046
+ className='number-narrow'
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
+ <p>Number of data points which are assigned a tick, starting from the right most data point. Value of 1 will show a tick at every data point, value of 2 will show a tick for every other, etc.</p>
2055
+ </Tooltip.Content>
2056
+ </Tooltip>
2057
+ }
2058
+ />
2059
+ <div className='viewport-overrides'>
2060
+ <label>
2061
+ <button onClick={() => setDisplayViewportOverrides(!displayViewportOverrides)} className='edit-label'>
2062
+ Step Count: viewport overrides <span style={{ transform: `rotate(${displayViewportOverrides ? '90deg' : '0deg'})` }}>&gt;</span>
2063
+ </button>
2064
+ </label>
2065
+ {displayViewportOverrides && (
2066
+ <div className='edit-block'>
2067
+ {Object.keys(viewports).map(viewport => (
2068
+ <TextField
2069
+ key={`viewport-step-count-input-${viewport}`}
2070
+ value={config.xAxis.viewportStepCount ? config.xAxis.viewportStepCount[viewport] : undefined}
2071
+ placeholder='Auto'
2072
+ type='number'
2073
+ label={viewport}
2074
+ className='number-narrow'
2075
+ updateField={(section, fieldName, label, val) => updateViewportOverrides('viewportStepCount', viewport, val)}
2076
+ />
2077
+ ))}
2078
+ </div>
2079
+ )}
2080
+ </div>
2081
+ </>
1942
2082
  )}
1943
2083
  {visSupportsDateCategoryNumTicks() && (config.xAxis.type === 'date-time' || !config.xAxis.manual) && (
1944
- <TextField value={config.xAxis.numTicks} placeholder='Auto' type='number' min={1} section='xAxis' fieldName='numTicks' label='Number of ticks' className='number-narrow' updateField={updateField} />
2084
+ <>
2085
+ <TextField
2086
+ value={config.xAxis.numTicks}
2087
+ placeholder='Auto'
2088
+ type='number'
2089
+ min={1}
2090
+ section='xAxis'
2091
+ fieldName='numTicks'
2092
+ label='Number of ticks'
2093
+ className='number-narrow'
2094
+ updateField={updateField}
2095
+ tooltip={
2096
+ <Tooltip style={{ textTransform: 'none' }}>
2097
+ <Tooltip.Target>
2098
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2099
+ </Tooltip.Target>
2100
+ <Tooltip.Content>
2101
+ <p>Apporoximate number of ticks. Other factors such as space available and data may change the exact number of ticks used. To enforce an exact number of ticks, check "Manual Ticks" above.</p>
2102
+ </Tooltip.Content>
2103
+ </Tooltip>
2104
+ }
2105
+ />
2106
+ <div className='viewport-overrides'>
2107
+ <label>
2108
+ <button onClick={() => setDisplayViewportOverrides(!displayViewportOverrides)} className='edit-label'>
2109
+ Number of ticks: viewport overrides <span style={{ transform: `rotate(${displayViewportOverrides ? '90deg' : '0deg'})` }}>&gt;</span>
2110
+ </button>
2111
+ </label>
2112
+ {displayViewportOverrides && (
2113
+ <div className='edit-block'>
2114
+ {Object.keys(viewports).map(viewport => (
2115
+ <TextField
2116
+ key={`viewport-num-ticks-input-${viewport}`}
2117
+ value={config.xAxis.viewportNumTicks ? config.xAxis.viewportNumTicks[viewport] : undefined}
2118
+ placeholder='Auto'
2119
+ type='number'
2120
+ label={viewport}
2121
+ className='number-narrow'
2122
+ updateField={(section, fieldName, label, val) => updateViewportOverrides('viewportNumTicks', viewport, val)}
2123
+ />
2124
+ ))}
2125
+ </div>
2126
+ )}
2127
+ </div>
2128
+ </>
1945
2129
  )}
1946
2130
  {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} />}
2131
+ <TextField value={config.xAxis.labelOffset} section='xAxis' fieldName='labelOffset' label='Label offset' type='number' className='number-narrow' updateField={updateField} />
1947
2132
 
1948
2133
  {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
1949
2134
  {/* <TextField value={config.xAxis.axisPadding} type='number' max={10} min={0} section='xAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateField} /> */}
@@ -1983,12 +2168,12 @@ const EditorPanel = () => {
1983
2168
  {config.orientation === 'horizontal' ? (
1984
2169
  <>
1985
2170
  {visSupportsDateCategoryAxisLine() && <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />}
1986
- {visSupportsDateCategoryAxisLabel() && <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />}
2171
+ {visSupportsDateCategoryAxisLabel() && <CheckBox value={config.yAxis.hideLabel} section='yAxis' fieldName='hideLabel' label='Hide Tick Labels' updateField={updateField} />}
1987
2172
  </>
1988
2173
  ) : (
1989
2174
  <>
1990
2175
  {visSupportsDateCategoryAxisLine() && <CheckBox value={config.xAxis.hideAxis} section='xAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />}
1991
- {visSupportsDateCategoryAxisLabel() && <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Label' updateField={updateField} />}
2176
+ {visSupportsDateCategoryAxisLabel() && <CheckBox value={config.xAxis.hideLabel} section='xAxis' fieldName='hideLabel' label='Hide Tick Labels' updateField={updateField} />}
1992
2177
  {visSupportsDateCategoryAxisTicks() && <CheckBox value={config.xAxis.hideTicks} section='xAxis' fieldName='hideTicks' label='Hide Ticks' updateField={updateField} />}
1993
2178
  </>
1994
2179
  )}
@@ -2338,242 +2523,15 @@ const EditorPanel = () => {
2338
2523
  </AccordionItem>
2339
2524
  )}
2340
2525
  <Panels.Regions name='Regions' />
2526
+
2341
2527
  {/* Columns */}
2342
- {config.visualizationType !== 'Box Plot' && (
2528
+ {config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Sankey' && (
2343
2529
  <AccordionItem>
2344
2530
  <AccordionItemHeading>
2345
2531
  <AccordionItemButton>Columns</AccordionItemButton>
2346
2532
  </AccordionItemHeading>
2347
2533
  <AccordionItemPanel>
2348
- {'navigation' !== config.type && (
2349
- <fieldset className='primary-fieldset edit-block'>
2350
- <label>
2351
- <span className='edit-label'>
2352
- Additional Columns
2353
- <Tooltip style={{ textTransform: 'none' }}>
2354
- <Tooltip.Target>
2355
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2356
- </Tooltip.Target>
2357
- <Tooltip.Content>
2358
- <p>You can specify additional columns to display in tooltips and / or the supporting data table.</p>
2359
- </Tooltip.Content>
2360
- </Tooltip>
2361
- </span>
2362
- </label>
2363
- {additionalColumns.map(val => (
2364
- <fieldset className='edit-block' key={val}>
2365
- <button
2366
- className='remove-column'
2367
- onClick={event => {
2368
- event.preventDefault()
2369
- removeAdditionalColumn(val)
2370
- }}
2371
- >
2372
- Remove
2373
- </button>
2374
- <label>
2375
- <span className='edit-label column-heading'>Column</span>
2376
- <select
2377
- value={config.columns[val] ? config.columns[val].name : getColumns()[0]}
2378
- onChange={event => {
2379
- editColumn(val, 'name', event.target.value)
2380
- }}
2381
- >
2382
- {getColumns().map(option => (
2383
- <option>{option}</option>
2384
- ))}
2385
- </select>
2386
- </label>
2387
- <label>
2388
- <span className='edit-label column-heading'>Associate to Series</span>
2389
- <select
2390
- value={config.columns[val] ? config.columns[val].series : ''}
2391
- onChange={event => {
2392
- editColumn(val, 'series', event.target.value)
2393
- }}
2394
- >
2395
- <option value=''>Select series</option>
2396
- {config.series.map(series => (
2397
- <option>{series.dataKey}</option>
2398
- ))}
2399
- </select>
2400
- </label>
2401
- <TextField value={config.columns[val].label} section='columns' subsection={val} fieldName='label' label='Label' updateField={updateField} />
2402
- <ul className='column-edit'>
2403
- <li className='three-col'>
2404
- <TextField value={config.columns[val].prefix} section='columns' subsection={val} fieldName='prefix' label='Prefix' updateField={updateField} />
2405
- <TextField value={config.columns[val].suffix} section='columns' subsection={val} fieldName='suffix' label='Suffix' updateField={updateField} />
2406
- <TextField type='number' value={config.columns[val].roundToPlace} section='columns' subsection={val} fieldName='roundToPlace' label='Round' updateField={updateField} />
2407
- </li>
2408
- <li>
2409
- <label className='checkbox'>
2410
- <input
2411
- type='checkbox'
2412
- checked={config.columns[val].commas}
2413
- onChange={event => {
2414
- editColumn(val, 'commas', event.target.checked)
2415
- }}
2416
- />
2417
- <span className='edit-label'>Add Commas to Numbers</span>
2418
- </label>
2419
- </li>
2420
- <li>
2421
- {config.table.showVertical && (
2422
- <label className='checkbox'>
2423
- <input
2424
- type='checkbox'
2425
- checked={config.columns[val].dataTable}
2426
- onChange={event => {
2427
- editColumn(val, 'dataTable', event.target.checked)
2428
- }}
2429
- />
2430
- <span className='edit-label'>Show in Data Table</span>
2431
- </label>
2432
- )}
2433
- </li>
2434
- {config.visualizationType === 'Pie' && (
2435
- <li>
2436
- <label className='checkbox'>
2437
- <input
2438
- type='checkbox'
2439
- checked={config.columns[val].showInViz}
2440
- onChange={event => {
2441
- editColumn(val, 'showInViz', event.target.checked)
2442
- }}
2443
- />
2444
- <span className='edit-label'>Show in Visualization</span>
2445
- </label>
2446
- </li>
2447
- )}
2448
-
2449
- {/* disable for now */}
2450
-
2451
- <li>
2452
- <label className='checkbox'>
2453
- <input
2454
- type='checkbox'
2455
- checked={config.columns[val].tooltips || false}
2456
- onChange={event => {
2457
- updateSeriesTooltip(val, event.target.checked)
2458
- }}
2459
- />
2460
- <span className='edit-label'>Show in tooltip</span>
2461
- </label>
2462
- </li>
2463
-
2464
- {config.visualizationType === 'Forest Plot' && (
2465
- <>
2466
- <li>
2467
- <label className='checkbox'>
2468
- <input
2469
- type='checkbox'
2470
- checked={config.columns[val].forestPlot || false}
2471
- onChange={event => {
2472
- editColumn(val, 'forestPlot', event.target.checked)
2473
- }}
2474
- />
2475
- <span className='edit-label'>Show in Forest Plot</span>
2476
- </label>
2477
- </li>
2478
- <li>
2479
- <label className='checkbox'>
2480
- <input
2481
- type='checkbox'
2482
- checked={config.columns[val].forestPlotAlignRight || false}
2483
- onChange={event => {
2484
- editColumn(val, 'forestPlotAlignRight', event.target.checked)
2485
- }}
2486
- />
2487
- <span className='edit-label'>Align Right</span>
2488
- </label>
2489
- </li>
2490
-
2491
- {!config.columns[val].forestPlotAlignRight && (
2492
- <li>
2493
- <label className='text'>
2494
- <span className='edit-label'>Forest Plot Starting Point</span>
2495
- <input
2496
- type='number'
2497
- value={config.columns[val].forestPlotStartingPoint || 0}
2498
- onChange={event => {
2499
- editColumn(val, 'forestPlotStartingPoint', event.target.value)
2500
- }}
2501
- />
2502
- </label>
2503
- </li>
2504
- )}
2505
- </>
2506
- )}
2507
- </ul>
2508
- </fieldset>
2509
- ))}
2510
- <button
2511
- className={'btn full-width'}
2512
- onClick={event => {
2513
- event.preventDefault()
2514
- addAdditionalColumn(additionalColumns.length + 1)
2515
- }}
2516
- >
2517
- Add Column
2518
- </button>
2519
- </fieldset>
2520
- )}
2521
- {'category' === config.legend.type && (
2522
- <fieldset className='primary-fieldset edit-block'>
2523
- <label>
2524
- <span className='edit-label'>
2525
- Additional Category
2526
- <Tooltip style={{ textTransform: 'none' }}>
2527
- <Tooltip.Target>
2528
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2529
- </Tooltip.Target>
2530
- <Tooltip.Content>
2531
- <p>You can provide additional categories to ensure they appear in the legend</p>
2532
- </Tooltip.Content>
2533
- </Tooltip>
2534
- </span>
2535
- </label>
2536
- {config.legend.additionalCategories &&
2537
- config.legend.additionalCategories.map((val, i) => (
2538
- <fieldset className='edit-block' key={val}>
2539
- <button
2540
- className='remove-column'
2541
- onClick={event => {
2542
- event.preventDefault()
2543
- const updatedAdditionaCategories = [...config.legend.additionalCategories]
2544
- updatedAdditionaCategories.splice(i, 1)
2545
- updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2546
- }}
2547
- >
2548
- Remove
2549
- </button>
2550
- <TextField
2551
- value={val}
2552
- label='Category'
2553
- section='legend'
2554
- subsection={null}
2555
- fieldName='additionalCategories'
2556
- updateField={(section, subsection, fieldName, value) => {
2557
- const updatedAdditionaCategories = [...config.legend.additionalCategories]
2558
- updatedAdditionaCategories[i] = value
2559
- updateField(section, subsection, fieldName, updatedAdditionaCategories)
2560
- }}
2561
- />
2562
- </fieldset>
2563
- ))}
2564
- <button
2565
- className={'btn full-width'}
2566
- onClick={event => {
2567
- event.preventDefault()
2568
- const updatedAdditionaCategories = [...(config.legend.additionalCategories || [])]
2569
- updatedAdditionaCategories.push('')
2570
- updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2571
- }}
2572
- >
2573
- Add Category
2574
- </button>
2575
- </fieldset>
2576
- )}
2534
+ <ColumnsEditor config={config} updateField={updateField} deleteColumn={removeAdditionalColumn} />{' '}
2577
2535
  </AccordionItemPanel>
2578
2536
  </AccordionItem>
2579
2537
  )}
@@ -2613,6 +2571,23 @@ const EditorPanel = () => {
2613
2571
  </Tooltip>
2614
2572
  }
2615
2573
  />
2574
+ <CheckBox
2575
+ value={config.legend.hideSuppressedLabels}
2576
+ section='legend'
2577
+ fieldName='hideSuppressedLabels'
2578
+ label='Hide Suppressed Labels'
2579
+ updateField={updateField}
2580
+ tooltip={
2581
+ <Tooltip style={{ textTransform: 'none' }}>
2582
+ <Tooltip.Target>
2583
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2584
+ </Tooltip.Target>
2585
+ <Tooltip.Content>
2586
+ <p>Hiding suppressed labels will not override the 'Special Class' assigned to line chart indicating "suppressed" data in the Data Series Panel.</p>
2587
+ </Tooltip.Content>
2588
+ </Tooltip>
2589
+ }
2590
+ />
2616
2591
  {/* {config.visualizationType === 'Box Plot' &&
2617
2592
  <>
2618
2593
  <CheckBox value={config.boxplot.legend.displayHowToReadText} fieldName='displayHowToReadText' section='boxplot' subsection='legend' label='Display How To Read Text' updateField={updateField} />
@@ -2697,7 +2672,7 @@ const EditorPanel = () => {
2697
2672
  {/* end: isolated values */}
2698
2673
 
2699
2674
  <TextField value={config.legend.label} section='legend' fieldName='label' label='Title' updateField={updateField} />
2700
- <Select value={config.legend.position} section='legend' fieldName='position' label='Position' updateField={updateField} options={['right', 'left', 'bottom']} />
2675
+ <Select value={config.legend?.position} section='legend' fieldName='position' label='Position' updateField={updateField} options={['right', 'left', 'bottom']} />
2701
2676
  {config.legend.position === 'bottom' && (
2702
2677
  <>
2703
2678
  <CheckBox value={config.legend.singleRow} section='legend' fieldName='singleRow' label='Single Row Legend' updateField={updateField} />
@@ -2714,161 +2689,7 @@ const EditorPanel = () => {
2714
2689
  <AccordionItemButton>Filters</AccordionItemButton>
2715
2690
  </AccordionItemHeading>
2716
2691
  <AccordionItemPanel>
2717
- {config.filters && (
2718
- <>
2719
- {/* prettier-ignore */}
2720
- <Select
2721
- value={config.filterBehavior}
2722
- fieldName='filterBehavior'
2723
- label='Filter Behavior'
2724
- updateField={updateField}
2725
- options={['Apply Button', 'Filter Change']}
2726
- tooltip={
2727
- <Tooltip style={{ textTransform: 'none' }}>
2728
- <Tooltip.Target>
2729
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2730
- </Tooltip.Target>
2731
- <Tooltip.Content>
2732
- <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>
2733
- </Tooltip.Content>
2734
- </Tooltip>
2735
- }
2736
- />
2737
- <br />
2738
- </>
2739
- )}
2740
- {config.filters && (
2741
- <ul className='filters-list'>
2742
- {/* Whether filters should apply onChange or Apply Button */}
2743
-
2744
- {config.filters.map((filter, index) => {
2745
- if (filter.type === 'url') return <></>
2746
-
2747
- return (
2748
- <fieldset className='edit-block' key={index}>
2749
- <button
2750
- type='button'
2751
- className='remove-column'
2752
- onClick={() => {
2753
- removeFilter(index)
2754
- }}
2755
- >
2756
- Remove
2757
- </button>
2758
- <label>
2759
- <span className='edit-label column-heading'>Filter</span>
2760
- <select
2761
- value={filter.columnName}
2762
- onChange={e => {
2763
- updateFilterProp('columnName', index, e.target.value)
2764
- }}
2765
- >
2766
- <option value=''>- Select Option -</option>
2767
- {getFilters().map((dataKey, index) => (
2768
- <option value={dataKey} key={index}>
2769
- {dataKey}
2770
- </option>
2771
- ))}
2772
- </select>
2773
- </label>
2774
-
2775
- <label>
2776
- <span className='edit-showDropdown column-heading'>Show Filter Input</span>
2777
- <input
2778
- type='checkbox'
2779
- checked={filter.showDropdown === undefined ? true : filter.showDropdown}
2780
- onChange={e => {
2781
- updateFilterProp('showDropdown', index, e.target.checked)
2782
- }}
2783
- />
2784
- </label>
2785
-
2786
- <label>
2787
- <span className='edit-label column-heading'>Filter Style</span>
2788
-
2789
- <select
2790
- value={filter.filterStyle}
2791
- onChange={e => {
2792
- updateFilterProp('filterStyle', index, e.target.value)
2793
- }}
2794
- >
2795
- {filterStyleOptions.map((item, index) => {
2796
- return (
2797
- <option key={`filter-style-${index}`} value={item}>
2798
- {item}
2799
- </option>
2800
- )
2801
- })}
2802
- </select>
2803
- </label>
2804
- <label>
2805
- <span className='edit-label column-heading'>Label</span>
2806
- <input
2807
- type='text'
2808
- value={filter.label}
2809
- onChange={e => {
2810
- updateFilterProp('label', index, e.target.value)
2811
- }}
2812
- />
2813
- </label>
2814
-
2815
- <label>
2816
- <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
2817
- <input
2818
- type='text'
2819
- value={filter.setByQueryParameter}
2820
- onChange={e => {
2821
- updateFilterProp('setByQueryParameter', index, e.target.value)
2822
- }}
2823
- />
2824
- </label>
2825
-
2826
- <label>
2827
- <span className='edit-filterOrder column-heading'>Filter Order</span>
2828
- <select value={filter.order ? filter.order : 'asc'} onChange={e => updateFilterProp('order', index, e.target.value)}>
2829
- {filterOrderOptions.map((option, index) => {
2830
- return (
2831
- <option value={option.value} key={`filter-${index}`}>
2832
- {option.label}
2833
- </option>
2834
- )
2835
- })}
2836
- </select>
2837
-
2838
- {filter.order === 'cust' && (
2839
- <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, index, config.filters[index])}>
2840
- <Droppable droppableId='filter_order'>
2841
- {provided => (
2842
- <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef} style={{ marginTop: '1em' }}>
2843
- {config.filters[index]?.values.map((value, index) => {
2844
- return (
2845
- <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
2846
- {(provided, snapshot) => (
2847
- <li>
2848
- <div className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
2849
- {value}
2850
- </div>
2851
- </li>
2852
- )}
2853
- </Draggable>
2854
- )
2855
- })}
2856
- {provided.placeholder}
2857
- </ul>
2858
- )}
2859
- </Droppable>
2860
- </DragDropContext>
2861
- )}
2862
- </label>
2863
- </fieldset>
2864
- )
2865
- })}
2866
- </ul>
2867
- )}
2868
- {!config.filters && <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
2869
- <button type='button' onClick={addNewFilter} className='btn full-width'>
2870
- Add Filter
2871
- </button>
2692
+ <VizFilterEditor config={config} updateField={updateField} rawData={rawData} />
2872
2693
  </AccordionItemPanel>
2873
2694
  </AccordionItem>
2874
2695
  )}
@@ -2884,9 +2705,10 @@ const EditorPanel = () => {
2884
2705
  </AccordionItemPanel>
2885
2706
  </AccordionItem>
2886
2707
  )}
2708
+ <Panels.Annotate name='Text Annotations' />
2887
2709
  {/* {(config.visualizationType === 'Bar' || config.visualizationType === 'Line') && <Panels.DateHighlighting name='Date Highlighting' />} */}
2888
2710
  </Accordion>
2889
- {config.type !== 'Spark Line' && <AdvancedEditor loadConfig={updateConfig} state={config} convertStateToConfig={convertStateToConfig} />}
2711
+ {config.type !== 'Spark Line' && <AdvancedEditor loadConfig={updateConfig} config={config} convertStateToConfig={convertStateToConfig} />}
2890
2712
  </Layout.Sidebar>
2891
2713
  </ErrorBoundary>
2892
2714
  </EditorPanelContext.Provider>