@cdc/map 4.25.3 → 4.25.5-1

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 (111) hide show
  1. package/dist/cdcmap.js +38945 -41511
  2. package/examples/hex-colors.json +3 -3
  3. package/examples/private/test.json +470 -1457
  4. package/examples/private/{mmr.json → wastewatermap.json} +86 -115
  5. package/index.html +13 -41
  6. package/package.json +4 -10
  7. package/src/CdcMap.tsx +51 -1555
  8. package/src/CdcMapComponent.tsx +594 -0
  9. package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +10 -0
  10. package/src/_stories/CdcMap.Legend.stories.tsx +67 -0
  11. package/src/_stories/CdcMap.stories.tsx +4 -1
  12. package/src/_stories/UsaMap.NoData.stories.tsx +4 -4
  13. package/{examples/private/default-patterns.json → src/_stories/_mock/legends/legend-tests.json} +36 -131
  14. package/src/cdcMapComponent.styles.css +9 -0
  15. package/src/components/Annotation/Annotation.Draggable.tsx +27 -26
  16. package/src/components/Annotation/AnnotationDropdown.tsx +5 -6
  17. package/src/components/BubbleList.tsx +135 -49
  18. package/src/components/CityList.tsx +89 -87
  19. package/src/components/DataTable.tsx +8 -8
  20. package/src/components/EditorPanel/components/EditorPanel.tsx +714 -820
  21. package/src/components/EditorPanel/components/Error.tsx +9 -2
  22. package/src/components/EditorPanel/components/HexShapeSettings.tsx +127 -141
  23. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +55 -86
  24. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +89 -75
  25. package/src/components/EditorPanel/components/editorPanel.styles.css +95 -0
  26. package/src/components/Geo.tsx +9 -1
  27. package/src/components/GoogleMap/components/GoogleMap.tsx +1 -1
  28. package/src/components/Legend/components/Legend.tsx +92 -87
  29. package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +128 -0
  30. package/src/components/Legend/components/LegendGroup/legend.group.css +27 -0
  31. package/src/components/Legend/components/LegendItem.Hex.tsx +4 -1
  32. package/src/components/Legend/components/index.scss +18 -6
  33. package/src/components/Modal.tsx +17 -7
  34. package/src/components/NavigationMenu.tsx +11 -9
  35. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +12 -8
  36. package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +4 -4
  37. package/src/components/UsaMap/components/TerritoriesSection.tsx +33 -10
  38. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +12 -10
  39. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +12 -14
  40. package/src/components/UsaMap/components/Territory/TerritoryShape.ts +2 -1
  41. package/src/components/UsaMap/components/UsaMap.County.tsx +138 -96
  42. package/src/components/UsaMap/components/UsaMap.Region.styles.css +72 -0
  43. package/src/components/UsaMap/components/UsaMap.Region.tsx +56 -103
  44. package/src/components/UsaMap/components/UsaMap.SingleState.styles.css +10 -0
  45. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +59 -66
  46. package/src/components/UsaMap/components/UsaMap.State.tsx +112 -91
  47. package/src/components/UsaMap/helpers/map.ts +1 -1
  48. package/src/components/UsaMap/helpers/shapes.ts +20 -7
  49. package/src/components/WorldMap/WorldMap.tsx +64 -118
  50. package/src/components/WorldMap/worldMap.styles.css +28 -0
  51. package/src/components/ZoomControls.tsx +15 -13
  52. package/src/components/zoomControls.styles.css +53 -0
  53. package/src/context.ts +17 -9
  54. package/src/data/initial-state.js +5 -2
  55. package/src/helpers/addUIDs.ts +151 -0
  56. package/src/helpers/applyColorToLegend.ts +39 -64
  57. package/src/helpers/applyLegendToRow.ts +51 -0
  58. package/src/helpers/colorDistributions.ts +12 -0
  59. package/src/helpers/constants.ts +44 -0
  60. package/src/helpers/displayGeoName.ts +9 -2
  61. package/src/helpers/generateColorsArray.ts +2 -1
  62. package/src/helpers/generateRuntimeData.ts +74 -0
  63. package/src/helpers/generateRuntimeFilters.ts +63 -0
  64. package/src/helpers/generateRuntimeLegend.ts +537 -0
  65. package/src/helpers/generateRuntimeLegendHash.ts +16 -15
  66. package/src/helpers/getColumnNames.ts +19 -0
  67. package/src/helpers/getMapContainerClasses.ts +23 -0
  68. package/src/helpers/handleMapTabbing.ts +31 -0
  69. package/src/helpers/hashObj.ts +1 -1
  70. package/src/helpers/index.ts +22 -0
  71. package/src/helpers/navigationHandler.ts +3 -3
  72. package/src/helpers/resetLegendToggles.ts +13 -0
  73. package/src/helpers/setBinNumbers.ts +5 -0
  74. package/src/helpers/sortSpecialClassesLast.ts +7 -0
  75. package/src/helpers/tests/getColumnNames.test.ts +52 -0
  76. package/src/helpers/titleCase.ts +1 -1
  77. package/src/helpers/toggleLegendActive.ts +25 -0
  78. package/src/hooks/useApplyTooltipsToGeo.tsx +51 -0
  79. package/src/hooks/useColumnsRequiredChecker.ts +51 -0
  80. package/src/hooks/useGeoClickHandler.ts +45 -0
  81. package/src/hooks/useLegendSeparators.ts +26 -0
  82. package/src/hooks/useMapLayers.tsx +34 -60
  83. package/src/hooks/useModal.ts +22 -0
  84. package/src/hooks/useResizeObserver.ts +4 -5
  85. package/src/hooks/useStateZoom.tsx +52 -75
  86. package/src/hooks/useTooltip.ts +2 -3
  87. package/src/index.jsx +3 -9
  88. package/src/scss/editor-panel.scss +3 -99
  89. package/src/scss/main.scss +1 -19
  90. package/src/scss/map.scss +15 -220
  91. package/src/store/map.actions.ts +46 -0
  92. package/src/store/map.reducer.ts +96 -0
  93. package/src/types/Annotations.ts +24 -0
  94. package/src/types/MapConfig.ts +23 -3
  95. package/src/types/MapContext.ts +36 -35
  96. package/src/types/Modal.ts +1 -0
  97. package/src/types/RuntimeData.ts +3 -0
  98. package/LICENSE +0 -201
  99. package/examples/private/DEV-9644.json +0 -184
  100. package/examples/private/DEV-9989.json +0 -229
  101. package/examples/private/ardi.json +0 -180
  102. package/examples/private/colors 2.json +0 -416
  103. package/examples/private/colors.json +0 -416
  104. package/examples/private/colors.json.zip +0 -0
  105. package/examples/private/customColors.json +0 -45348
  106. package/examples/test.json +0 -183
  107. package/src/helpers/closeModal.ts +0 -9
  108. package/src/scss/btn.scss +0 -69
  109. package/src/scss/filters.scss +0 -27
  110. package/src/scss/variables.scss +0 -1
  111. /package/src/hooks/{useActiveElement.js → useActiveElement.ts} +0 -0
@@ -1,14 +1,14 @@
1
- import React, { useState, useEffect, useContext } from 'react'
1
+ import React, { useContext, useEffect, useState } from 'react'
2
2
 
3
3
  // Third Party
4
4
  import {
5
5
  Accordion,
6
6
  AccordionItem,
7
+ AccordionItemButton,
7
8
  AccordionItemHeading,
8
- AccordionItemPanel,
9
- AccordionItemButton
9
+ AccordionItemPanel
10
10
  } from 'react-accessible-accordion'
11
- import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
11
+ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
12
12
  import { useDebounce } from 'use-debounce'
13
13
  import _ from 'lodash'
14
14
  // import ReactTags from 'react-tag-autocomplete'
@@ -39,83 +39,64 @@ import countyDefaultConfig from '../../../../examples/default-county.json'
39
39
  import useMapLayers from '../../../hooks/useMapLayers.tsx'
40
40
 
41
41
  import HexSetting from './HexShapeSettings.jsx'
42
- import ConfigContext from '../../../context.ts'
42
+ import ConfigContext, { MapDispatchContext } from '../../../context.ts'
43
43
  import { MapContext } from '../../../types/MapContext.js'
44
44
  import Alert from '@cdc/core/components/Alert'
45
45
  import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
46
46
  import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
47
+ import useColumnsRequiredChecker from '../../../hooks/useColumnsRequiredChecker'
48
+ import { addUIDs, HEADER_COLORS } from '../../../helpers'
49
+ import './editorPanel.styles.css'
47
50
 
48
- // Todo: move to useReducer, seperate files out.
49
- const EditorPanel = ({ columnsRequiredChecker }) => {
50
- // prettier-ignore
51
+ const EditorPanel = () => {
51
52
  const {
53
+ setParentConfig,
52
54
  isDashboard,
53
- isDebug,
55
+ isEditor,
54
56
  loadConfig,
55
57
  runtimeFilters,
56
58
  runtimeLegend,
57
- setParentConfig,
58
- setState,
59
- state,
59
+ setConfig,
60
+ config,
60
61
  tooltipId,
61
62
  runtimeData,
62
- setRuntimeData,
63
- generateRuntimeData,
64
-
65
-
63
+ setRuntimeData
66
64
  } = useContext<MapContext>(ConfigContext)
67
65
 
68
- const { general, columns, legend, table, tooltips } = state
69
- const columnsInData = state?.data?.[0] ? Object.keys(state.data[0]) : []
70
-
71
- const [configTextboxValue, setConfigTextbox] = useState({}) // eslint-disable-line
66
+ const { columnsRequiredChecker } = useColumnsRequiredChecker()
67
+ const dispatch = useContext(MapDispatchContext)
68
+ const { general, columns, legend, table, tooltips } = config
69
+ const columnsInData = config?.data?.[0] ? Object.keys(config.data[0]) : []
72
70
 
73
71
  const [loadedDefault, setLoadedDefault] = useState(false)
74
-
75
72
  const [displayPanel, setDisplayPanel] = useState(true)
76
-
77
73
  const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0])
78
74
 
79
- const headerColors = [
80
- 'theme-blue',
81
- 'theme-purple',
82
- 'theme-brown',
83
- 'theme-teal',
84
- 'theme-pink',
85
- 'theme-orange',
86
- 'theme-slate',
87
- 'theme-indigo',
88
- 'theme-cyan',
89
- 'theme-green',
90
- 'theme-amber'
91
- ]
92
-
93
75
  const {
94
- // prettier-ignore
95
- MapLayerHandlers: {
96
- handleMapLayer,
97
- handleAddLayer,
98
- handleRemoveLayer
76
+ MapLayerHandlers: { handleMapLayer, handleAddLayer, handleRemoveLayer }
77
+ } = useMapLayers(config, setConfig, false, tooltipId)
78
+
79
+ useEffect(() => {
80
+ // Pass up to Editor if needed
81
+ if (setParentConfig) {
82
+ setParentConfig(convertStateToConfig())
99
83
  }
100
- } = useMapLayers(state, setState, false, tooltipId)
84
+ }, [config])
101
85
 
102
86
  const categoryMove = (idx1, idx2) => {
103
87
  let categoryValuesOrder = getCategoryValuesOrder()
104
-
105
88
  let [movedItem] = categoryValuesOrder.splice(idx1, 1)
106
-
107
89
  categoryValuesOrder.splice(idx2, 0, movedItem)
108
-
109
- state.legend.categoryValuesOrder?.forEach(value => {
90
+ config.legend.categoryValuesOrder?.forEach(value => {
110
91
  if (categoryValuesOrder.indexOf(value) === -1) {
111
92
  categoryValuesOrder.push(value)
112
93
  }
113
94
  })
114
95
 
115
- setState({
116
- ...state,
96
+ setConfig({
97
+ ...config,
117
98
  legend: {
118
- ...state.legend,
99
+ ...config.legend,
119
100
  categoryValuesOrder
120
101
  }
121
102
  })
@@ -125,16 +106,16 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
125
106
  if (legend.specialClasses && legend.specialClasses.length && typeof legend.specialClasses[0] === 'string') {
126
107
  legend.specialClasses.forEach(specialClass => {
127
108
  specialClasses.push({
128
- key: state.columns.primary && state.columns.primary.name ? state.columns.primary.name : columnsInData[0],
109
+ key: config.columns.primary && config.columns.primary.name ? config.columns.primary.name : columnsInData[0],
129
110
  value: specialClass,
130
111
  label: specialClass
131
112
  })
132
113
  })
133
114
  // DEV-3303 - since the above was a repair of bad config - need to backpopulate into the state
134
- setState({
135
- ...state,
115
+ setConfig({
116
+ ...config,
136
117
  legend: {
137
- ...state.legend,
118
+ ...config.legend,
138
119
  specialClasses: specialClasses
139
120
  }
140
121
  })
@@ -142,12 +123,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
142
123
  specialClasses = legend.specialClasses || []
143
124
  }
144
125
 
126
+ const allowLegendSeparators = legend.style === 'gradient' && legend.subStyle === 'linear blocks'
127
+
145
128
  const getCityStyleOptions = target => {
146
129
  switch (target) {
147
130
  case 'value': {
148
131
  const values = ['Circle', 'Square', 'Triangle', 'Diamond', 'Star', 'Pin']
149
132
  const filteredValues = values.filter(
150
- val => String(state.visual.cityStyle).toLocaleLowerCase() !== val.toLocaleLowerCase()
133
+ val => String(config.visual.cityStyle).toLocaleLowerCase() !== val.toLocaleLowerCase()
151
134
  )
152
135
 
153
136
  return (
@@ -171,12 +154,12 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
171
154
  const editCityStyles = (target, index, fieldName, value) => {
172
155
  switch (target) {
173
156
  case 'add': {
174
- const additionalCityStyles = state.visual.additionalCityStyles ? [...state.visual.additionalCityStyles] : []
157
+ const additionalCityStyles = config.visual.additionalCityStyles ? [...config.visual.additionalCityStyles] : []
175
158
  additionalCityStyles.push({ label: '', column: '', value: '', shape: '' })
176
- setState({
177
- ...state,
159
+ setConfig({
160
+ ...config,
178
161
  visual: {
179
- ...state.visual,
162
+ ...config.visual,
180
163
  additionalCityStyles: additionalCityStyles
181
164
  }
182
165
  })
@@ -184,15 +167,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
184
167
  }
185
168
  case 'remove': {
186
169
  let additionalCityStyles = []
187
- if (state.visual.additionalCityStyles) {
188
- additionalCityStyles = [...state.visual.additionalCityStyles]
170
+ if (config.visual.additionalCityStyles) {
171
+ additionalCityStyles = [...config.visual.additionalCityStyles]
189
172
  }
190
173
 
191
174
  additionalCityStyles.splice(index, 1)
192
- setState({
193
- ...state,
175
+ setConfig({
176
+ ...config,
194
177
  visual: {
195
- ...state.visual,
178
+ ...config.visual,
196
179
  additionalCityStyles: additionalCityStyles
197
180
  }
198
181
  })
@@ -200,12 +183,12 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
200
183
  }
201
184
  case 'update': {
202
185
  let additionalCityStyles = []
203
- additionalCityStyles = [...state.visual.additionalCityStyles]
186
+ additionalCityStyles = [...config.visual.additionalCityStyles]
204
187
  additionalCityStyles[index][fieldName] = value
205
- setState({
206
- ...state,
188
+ setConfig({
189
+ ...config,
207
190
  visual: {
208
- ...state.visual,
191
+ ...config.visual,
209
192
  additionalCityStyles: additionalCityStyles
210
193
  }
211
194
  })
@@ -222,7 +205,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
222
205
  if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
223
206
  handleEditorChanges('changeLegendDescription', [String(activeFilterValueForDescription), debouncedValue])
224
207
  }
225
- }, [debouncedValue]) // eslint-disable-line
208
+ }, [debouncedValue])
226
209
 
227
210
  const onChange = e => setValue(e.target.value)
228
211
 
@@ -231,277 +214,150 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
231
214
 
232
215
  const handleEditorChanges = async (property, value) => {
233
216
  switch (property) {
234
- case 'navigationTarget':
235
- setState({
236
- ...state,
237
- general: {
238
- ...state.general,
239
- navigationTarget: value
240
- }
241
- })
242
- break
243
- // change these to be more generic.
244
- // updateVisualPropertyValue
245
- // updateGeneralPropertyValue, etc.
246
- case 'showBubbleZeros':
247
- setState({
248
- ...state,
249
- visual: {
250
- ...state.visual,
251
- showBubbleZeros: value
252
- }
253
- })
254
- break
255
- case 'showEqualNumber':
256
- setState({
257
- ...state,
258
- general: {
259
- ...state.general,
260
- equalNumberOptIn: value
261
- }
262
- })
263
- break
264
- case 'hideGeoColumnInTooltip':
265
- setState({
266
- ...state,
267
- general: {
268
- ...state.general,
269
- [property]: value
270
- }
271
- })
272
- break
273
-
274
- case 'toggleDataTableLink':
275
- setState({
276
- ...state,
277
- table: {
278
- ...state.table,
279
- showDataTableLink: value
280
- }
281
- })
282
- break
283
-
284
- case 'toggleDataUrl':
285
- setState({
286
- ...state,
287
- table: {
288
- ...state.table,
289
- showDownloadUrl: value
290
- }
291
- })
292
- break
293
- case 'toggleExtraBubbleBorder':
294
- setState({
295
- ...state,
296
- visual: {
297
- ...state.visual,
298
- extraBubbleBorder: value
299
- }
300
- })
301
- break
302
- case 'allowMapZoom':
303
- setState({
304
- ...state,
305
- general: {
306
- ...state.general,
307
- allowMapZoom: value
308
- },
309
- mapPosition: {
310
- coordinates: state.general.geoType === 'world' ? [0, 30] : [0, 0],
311
- zoom: 1
312
- }
313
- })
314
- break
315
217
  case 'hidePrimaryColumnInTooltip':
316
- setState({
317
- ...state,
218
+ setConfig({
219
+ ...config,
318
220
  general: {
319
- ...state.general,
221
+ ...config.general,
320
222
  [property]: value
321
223
  }
322
224
  })
323
225
  break
324
226
  case 'geoLabelOverride':
325
- setState({
326
- ...state,
227
+ setConfig({
228
+ ...config,
327
229
  general: {
328
- ...state.general,
230
+ ...config.general,
329
231
  geoLabelOverride: value
330
232
  }
331
233
  })
332
234
  break
333
235
  case 'showTitle':
334
- setState({
335
- ...state,
236
+ setConfig({
237
+ ...config,
336
238
  general: {
337
- ...state.general,
239
+ ...config.general,
338
240
  showTitle: value
339
241
  }
340
242
  })
341
243
  break
342
- case 'showSidebar':
343
- setState({
344
- ...state,
345
- general: {
346
- ...state.general,
347
- showSidebar: value
348
- }
349
- })
350
- break
351
- case 'fullBorder':
352
- setState({
353
- ...state,
354
- general: {
355
- ...state.general,
356
- fullBorder: value
357
- }
358
- })
359
- break
360
244
  case 'expandDataTable':
361
- setState({
362
- ...state,
245
+ setConfig({
246
+ ...config,
363
247
  table: {
364
- ...state.table,
248
+ ...config.table,
365
249
  expanded: value
366
250
  }
367
251
  })
368
252
  break
369
- case 'color':
370
- setState({
371
- ...state,
372
- color: value
373
- })
374
- break
375
253
  case 'sidebarPosition':
376
- setState({
377
- ...state,
254
+ setConfig({
255
+ ...config,
378
256
  legend: {
379
- ...state.legend,
257
+ ...config.legend,
380
258
  position: value,
381
259
  hideBorder: _.includes(['top', 'bottom'], value)
382
260
  }
383
261
  })
384
262
  break
385
263
  case 'legendStyle':
386
- setState({
387
- ...state,
264
+ setConfig({
265
+ ...config,
388
266
  legend: {
389
- ...state.legend,
267
+ ...config.legend,
390
268
  style: value
391
269
  }
392
270
  })
393
271
  break
394
272
  case 'legendSubStyle':
395
- setState({
396
- ...state,
273
+ setConfig({
274
+ ...config,
397
275
  legend: {
398
- ...state.legend,
276
+ ...config.legend,
399
277
  subStyle: value
400
278
  }
401
279
  })
402
280
  break
281
+ case 'legendGroupBy':
282
+ setConfig({
283
+ ...config,
284
+ legend: {
285
+ ...config.legend,
286
+ groupBy: value
287
+ }
288
+ })
289
+ break
403
290
  case 'legendTickRotation':
404
- setState({
405
- ...state,
291
+ setConfig({
292
+ ...config,
406
293
  legend: {
407
- ...state.legend,
294
+ ...config.legend,
408
295
  tickRotation: value
409
296
  }
410
297
  })
411
298
  break
412
299
  case 'legendBorder':
413
- setState({
414
- ...state,
300
+ setConfig({
301
+ ...config,
415
302
  legend: {
416
- ...state.legend,
303
+ ...config.legend,
417
304
  hideBorder: value
418
305
  }
419
306
  })
420
307
  break
421
308
  case 'handleCityStyle':
422
- setState({
423
- ...state,
309
+ setConfig({
310
+ ...config,
424
311
  visual: {
425
- ...state.visual,
312
+ ...config.visual,
426
313
  cityStyle: value
427
314
  }
428
315
  })
429
316
  break
430
317
  case 'geoBorderColor':
431
- setState({
432
- ...state,
318
+ setConfig({
319
+ ...config,
433
320
  general: {
434
- ...state.general,
321
+ ...config.general,
435
322
  geoBorderColor: value
436
323
  }
437
324
  })
438
325
  break
439
326
  case 'headerColor':
440
- setState({
441
- ...state,
327
+ setConfig({
328
+ ...config,
442
329
  general: {
443
- ...state.general,
330
+ ...config.general,
444
331
  headerColor: value
445
332
  }
446
333
  })
447
334
  break
448
335
  case 'navigateColumn':
449
- setState({
450
- ...state,
336
+ setConfig({
337
+ ...config,
451
338
  columns: {
452
- ...state.columns,
339
+ ...config.columns,
453
340
  navigate: {
454
- ...state.columns.navigate,
341
+ ...config.columns.navigate,
455
342
  name: value
456
343
  }
457
344
  }
458
345
  })
459
346
  break
460
347
  case 'legendDescription':
461
- setState({
462
- ...state,
348
+ setConfig({
349
+ ...config,
463
350
  legend: {
464
- ...state.legend,
351
+ ...config.legend,
465
352
  description: value
466
353
  }
467
354
  })
468
355
  break
469
- case 'legendType':
470
- let testForType = Number(typeof state.data[0][state.columns.primary.name])
471
- let hasValue = state.data[0][state.columns.primary.name]
472
- let messages = []
473
-
474
- if (!hasValue) {
475
- messages.push(
476
- `There appears to be values missing for data in the primary column ${state.columns.primary.name}`
477
- )
478
- }
479
-
480
- if (testForType === 'string' && isNaN(testForType) && value !== 'category') {
481
- messages.push(
482
- 'Error with legend. Primary columns that are text must use a categorical legend type. Try changing the legend type to DEV-12345categorical.'
483
- )
484
- } else {
485
- messages = []
486
- }
487
-
488
- setState({
489
- ...state,
490
- legend: {
491
- ...state.legend,
492
- type: value
493
- },
494
- runtime: {
495
- ...state.runtime,
496
- editorErrorMessage: messages
497
- }
498
- })
499
- break
500
356
  case 'legendNumber':
501
- setState({
502
- ...state,
357
+ setConfig({
358
+ ...config,
503
359
  legend: {
504
- ...state.legend,
360
+ ...config.legend,
505
361
  numberOfItems: parseInt(value)
506
362
  }
507
363
  })
@@ -512,123 +368,105 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
512
368
  setActiveFilterValueForDescription(arrVal)
513
369
  break
514
370
  case 'unifiedLegend':
515
- setState({
516
- ...state,
371
+ setConfig({
372
+ ...config,
517
373
  legend: {
518
- ...state.legend,
374
+ ...config.legend,
519
375
  unified: value
520
376
  }
521
377
  })
522
378
  break
523
- case 'separateZero':
524
- setState({
525
- ...state,
526
- legend: {
527
- ...state.legend,
528
- separateZero: value
529
- }
530
- })
531
- break
532
379
  case 'toggleShowFullGeoNameInCSV':
533
- setState({
534
- ...state,
380
+ setConfig({
381
+ ...config,
535
382
  table: {
536
383
  // setting both bc DataTable new core needs it here
537
- ...state.table,
538
- showFullGeoNameInCSV: !state.table.showFullGeoNameInCSV
384
+ ...config.table,
385
+ showFullGeoNameInCSV: !config.table.showFullGeoNameInCSV
539
386
  }
540
387
  })
541
388
  break
542
389
  case 'toggleDownloadImgButton':
543
- setState({
544
- ...state,
390
+ setConfig({
391
+ ...config,
545
392
  general: {
546
- ...state.general,
547
- showDownloadImgButton: !state.general.showDownloadImgButton
393
+ ...config.general,
394
+ showDownloadImgButton: !config.general.showDownloadImgButton
548
395
  }
549
396
  })
550
397
  break
551
398
  case 'toggleDownloadLinkBelow':
552
- setState({
553
- ...state,
399
+ setConfig({
400
+ ...config,
554
401
  table: {
555
- ...state.table,
556
- showDownloadLinkBelow: !state.table.showDownloadLinkBelow
402
+ ...config.table,
403
+ showDownloadLinkBelow: !config.table.showDownloadLinkBelow
557
404
  }
558
405
  })
559
406
  break
560
407
  case 'toggleDownloadPdfButton':
561
- setState({
562
- ...state,
408
+ setConfig({
409
+ ...config,
563
410
  general: {
564
- ...state.general,
565
- showDownloadPdfButton: !state.general.showDownloadPdfButton
566
- }
567
- })
568
- break
569
- case 'displayAsHex':
570
- setState({
571
- ...state,
572
- general: {
573
- ...state.general,
574
- displayAsHex: value
411
+ ...config.general,
412
+ showDownloadPdfButton: !config.general.showDownloadPdfButton
575
413
  }
576
414
  })
577
415
  break
578
416
  case 'editorMapType':
579
417
  switch (value) {
580
418
  case 'us-geocode':
581
- setState({
582
- ...state,
419
+ setConfig({
420
+ ...config,
583
421
  general: {
584
- ...state.general,
422
+ ...config.general,
585
423
  type: value
586
424
  }
587
425
  })
588
426
  break
589
427
  case 'world-geocode':
590
- setState({
591
- ...state,
428
+ setConfig({
429
+ ...config,
592
430
  general: {
593
- ...state.general,
431
+ ...config.general,
594
432
  type: value
595
433
  }
596
434
  })
597
435
  break
598
436
  case 'data':
599
- setState({
600
- ...state,
437
+ setConfig({
438
+ ...config,
601
439
  general: {
602
- ...state.general,
440
+ ...config.general,
603
441
  showSidebar: true,
604
442
  type: 'data'
605
443
  }
606
444
  })
607
445
  break
608
446
  case 'navigation':
609
- setState({
610
- ...state,
447
+ setConfig({
448
+ ...config,
611
449
  general: {
612
- ...state.general,
450
+ ...config.general,
613
451
  showSidebar: false,
614
452
  type: 'navigation'
615
453
  },
616
454
  tooltips: {
617
- ...state.tooltips,
455
+ ...config.tooltips,
618
456
  appearanceType: 'hover'
619
457
  }
620
458
  })
621
459
  break
622
460
  case 'bubble':
623
- setState({
624
- ...state,
461
+ setConfig({
462
+ ...config,
625
463
  general: {
626
- ...state.general,
464
+ ...config.general,
627
465
  showSidebar: false,
628
466
  type: 'bubble'
629
467
  },
630
468
  tooltips: {
631
- ...state.tooltips,
469
+ ...config.tooltips,
632
470
  appearanceType: 'hover'
633
471
  }
634
472
  })
@@ -639,6 +477,9 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
639
477
  }
640
478
  break
641
479
  case 'geoType':
480
+ addUIDs(config, config.columns.geo.name)
481
+ dispatch({ type: 'SET_POSITION', payload: [0, 30] })
482
+
642
483
  // If we're still working with default data, switch to the world default to show it as an example
643
484
  if (true === loadedDefault && 'world' === value) {
644
485
  loadConfig(worldDefaultConfig)
@@ -657,78 +498,78 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
657
498
 
658
499
  switch (value) {
659
500
  case 'us':
660
- setState({
661
- ...state,
501
+ setConfig({
502
+ ...config,
662
503
  general: {
663
- ...state.general,
504
+ ...config.general,
664
505
  geoType: 'us',
665
- type: state.type === 'us-geocode' ? 'data' : state.type
506
+ type: config.type === 'us-geocode' ? 'data' : config.type
666
507
  },
667
508
  table: {
668
- ...state.table,
509
+ ...config.table,
669
510
  forceDisplay: true
670
511
  }
671
512
  })
672
513
  break
673
514
  case 'us-region':
674
- setState({
675
- ...state,
515
+ setConfig({
516
+ ...config,
676
517
  general: {
677
- ...state.general,
518
+ ...config.general,
678
519
  geoType: 'us-region'
679
520
  },
680
521
  table: {
681
- ...state.table,
522
+ ...config.table,
682
523
  forceDisplay: true
683
524
  }
684
525
  })
685
526
  break
686
527
  case 'world':
687
- setState({
688
- ...state,
528
+ setConfig({
529
+ ...config,
689
530
  general: {
690
- ...state.general,
531
+ ...config.general,
691
532
  geoType: 'world'
692
533
  },
693
534
  table: {
694
- ...state.table,
535
+ ...config.table,
695
536
  forceDisplay: true
696
537
  }
697
538
  })
698
539
  break
699
540
  case 'us-county':
700
- setState({
701
- ...state,
541
+ setConfig({
542
+ ...config,
702
543
  general: {
703
- ...state.general,
544
+ ...config.general,
704
545
  geoType: 'us-county'
705
546
  },
706
547
  table: {
707
- ...state.table,
548
+ ...config.table,
708
549
  expanded: false,
709
550
  forceDisplay: true
710
551
  }
711
552
  })
712
553
  break
713
554
  case 'single-state':
714
- setState({
715
- ...state,
555
+ setConfig({
556
+ ...config,
716
557
  general: {
717
- ...state.general,
558
+ ...config.general,
718
559
  geoType: 'single-state'
719
560
  },
720
561
  table: {
721
- ...state.table,
562
+ ...config.table,
722
563
  expanded: false,
723
564
  forceDisplay: true
724
565
  }
725
566
  })
726
567
  break
727
568
  case 'google-map':
728
- setState({
729
- ...state,
569
+ setConfig({
570
+ ...config,
730
571
  general: {
731
- ...state.general,
572
+ ...config.general,
732
573
  geoType: 'google-map'
733
574
  }
734
575
  })
@@ -736,125 +577,103 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
736
577
  break
737
578
  }
738
579
 
739
- break
740
- case 'singleColumnLegend':
741
- setState({
742
- ...state,
743
- legend: {
744
- ...state.legend,
745
- singleColumn: !state.legend.singleColumn,
746
- singleRow: false,
747
- verticalSorted: false
748
- }
749
- })
750
- break
751
- case 'singleRowLegend':
752
- setState({
753
- ...state,
754
- legend: {
755
- ...state.legend,
756
- singleRow: !state.legend.singleRow,
757
- singleColumn: false,
758
- verticalSorted: false
759
- }
760
- })
761
580
  break
762
581
  case 'verticalSortedLegend':
763
- setState({
764
- ...state,
582
+ setConfig({
583
+ ...config,
765
584
  legend: {
766
- ...state.legend,
767
- verticalSorted: !state.legend.verticalSorted,
585
+ ...config.legend,
586
+ verticalSorted: !config.legend.verticalSorted,
768
587
  singleRow: false,
769
588
  singleColumn: false
770
589
  }
771
590
  })
772
591
  break
773
592
  case 'legendShowSpecialClassesLast':
774
- setState({
775
- ...state,
593
+ setConfig({
594
+ ...config,
776
595
  legend: {
777
- ...state.legend,
778
- showSpecialClassesLast: !state.legend.showSpecialClassesLast
596
+ ...config.legend,
597
+ showSpecialClassesLast: !config.legend.showSpecialClassesLast
779
598
  }
780
599
  })
781
600
  break
782
601
  case 'dynamicDescription':
783
- setState({
784
- ...state,
602
+ setConfig({
603
+ ...config,
785
604
  editor: {
786
- ...state.editor,
605
+ ...config.editor,
787
606
  activeFilterValueForDescription: value
788
607
  },
789
608
  legend: {
790
- ...state.legend,
791
- dynamicDescription: !state.legend.dynamicDescription
609
+ ...config.legend,
610
+ dynamicDescription: !config.legend.dynamicDescription
792
611
  }
793
612
  })
794
613
  break
795
614
  case 'changeLegendDescription':
796
615
  const [filterValKey, filterValDesc] = value
797
- setState({
798
- ...state,
616
+ setConfig({
617
+ ...config,
799
618
  legend: {
800
- ...state.legend,
619
+ ...config.legend,
801
620
  descriptions: {
802
- ...state.legend.descriptions,
621
+ ...config.legend.descriptions,
803
622
  [filterValKey]: [filterValDesc]
804
623
  }
805
624
  }
806
625
  })
807
626
  break
808
627
  case 'appearanceType':
809
- setState({
810
- ...state,
628
+ setConfig({
629
+ ...config,
811
630
  tooltips: {
812
- ...state.tooltips,
631
+ ...config.tooltips,
813
632
  appearanceType: value
814
633
  }
815
634
  })
816
635
  break
817
636
  case 'linkLabel':
818
- setState({
819
- ...state,
637
+ setConfig({
638
+ ...config,
820
639
  tooltips: {
821
- ...state.tooltips,
640
+ ...config.tooltips,
822
641
  linkLabel: value
823
642
  }
824
643
  })
825
644
  break
826
645
  case 'displayStateLabels':
827
- setState({
828
- ...state,
646
+ setConfig({
647
+ ...config,
829
648
  general: {
830
- ...state.general,
831
- displayStateLabels: !state.general.displayStateLabels
649
+ ...config.general,
650
+ displayStateLabels: !config.general.displayStateLabels
832
651
  }
833
652
  })
834
653
  break
835
654
  case 'capitalizeLabels':
836
- setState({
837
- ...state,
655
+ setConfig({
656
+ ...config,
838
657
  tooltips: {
839
- ...state.tooltips,
658
+ ...config.tooltips,
840
659
  capitalizeLabels: value
841
660
  }
842
661
  })
843
662
  break
844
663
  case 'showDataTable':
845
- setState({
846
- ...state,
664
+ setConfig({
665
+ ...config,
847
666
  table: {
848
- ...state.table,
667
+ ...config.table,
849
668
  forceDisplay: value
850
669
  }
851
670
  })
852
671
  break
853
672
  case 'limitDataTableHeight':
854
- setState({
855
- ...state,
673
+ setConfig({
674
+ ...config,
856
675
  table: {
857
- ...state.table,
676
+ ...config.table,
858
677
  limitHeight: value
859
678
  }
860
679
  })
@@ -864,67 +683,58 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
864
683
  let stateName = value
865
684
  let stateData = { fipsCode, stateName }
866
685
 
867
- setState({
868
- ...state,
686
+ setConfig({
687
+ ...config,
869
688
  general: {
870
- ...state.general,
689
+ ...config.general,
871
690
  statePicked: stateData
872
691
  }
873
692
  })
874
693
 
875
- if (state) {
876
- const newData = generateRuntimeData(state)
694
+ if (config) {
695
+ const newData = generateRuntimeData(config)
877
696
  setRuntimeData(newData)
878
697
  }
879
698
  break
880
699
  case 'classificationType':
881
- setState({
882
- ...state,
700
+ setConfig({
701
+ ...config,
883
702
  legend: {
884
- ...state.legend,
703
+ ...config.legend,
885
704
  type: value
886
705
  }
887
706
  })
888
707
  break
889
- case 'territoriesAlwaysShow':
890
- setState({
891
- ...state,
892
- general: {
893
- ...state.general,
894
- territoriesAlwaysShow: value
895
- }
896
- })
897
- break
898
708
  case 'countyCensusYear':
899
- setState({
900
- ...state,
709
+ setConfig({
710
+ ...config,
901
711
  general: {
902
- ...state.general,
712
+ ...config.general,
903
713
  countyCensusYear: value
904
714
  }
905
715
  })
906
716
  break
907
717
  case 'filterControlsCountyYear':
908
- setState({
909
- ...state,
718
+ setConfig({
719
+ ...config,
910
720
  general: {
911
- ...state.general,
721
+ ...config.general,
912
722
  filterControlsCountyYear: value
913
723
  }
914
724
  })
915
725
  break
916
726
  case 'filterControlsStatePicked':
917
- setState({
918
- ...state,
727
+ setConfig({
728
+ ...config,
919
729
  general: {
920
- ...state.general,
730
+ ...config.general,
921
731
  filterControlsStatePicked: value
922
732
  }
923
733
  })
924
734
  break
925
735
  case 'filterBehavior':
926
- setState({
927
- ...state,
736
+ setConfig({
737
+ ...config,
928
738
  filterBehavior: value
929
739
  })
930
740
  break
@@ -942,10 +752,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
942
752
 
943
753
  newSpecialClasses[value.index][value.prop] = value.value
944
754
 
945
- setState({
946
- ...state,
755
+ setConfig({
756
+ ...config,
947
757
  legend: {
948
- ...state.legend,
758
+ ...config.legend,
949
759
  specialClasses: newSpecialClasses
950
760
  }
951
761
  })
@@ -955,10 +765,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
955
765
 
956
766
  newSpecialClasses.splice(value, 1)
957
767
 
958
- setState({
959
- ...state,
768
+ setConfig({
769
+ ...config,
960
770
  legend: {
961
- ...state.legend,
771
+ ...config.legend,
962
772
  specialClasses: newSpecialClasses
963
773
  }
964
774
  })
@@ -968,21 +778,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
968
778
 
969
779
  newSpecialClasses.push(value)
970
780
 
971
- setState({
972
- ...state,
781
+ setConfig({
782
+ ...config,
973
783
  legend: {
974
- ...state.legend,
784
+ ...config.legend,
975
785
  specialClasses: newSpecialClasses
976
786
  }
977
787
  })
978
788
  break
979
789
  case 'name':
980
- setState({
981
- ...state,
790
+ addUIDs(config, config.columns.geo.name)
791
+ setConfig({
792
+ ...config,
982
793
  columns: {
983
- ...state.columns,
794
+ ...config.columns,
984
795
  [columnName]: {
985
- ...state.columns[columnName],
796
+ ...config.columns[columnName],
986
797
  [editTarget]: value
987
798
  }
988
799
  }
@@ -990,12 +801,12 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
990
801
 
991
802
  break
992
803
  default:
993
- setState({
994
- ...state,
804
+ setConfig({
805
+ ...config,
995
806
  columns: {
996
- ...state.columns,
807
+ ...config.columns,
997
808
  [columnName]: {
998
- ...state.columns[columnName],
809
+ ...config.columns[columnName],
999
810
  [editTarget]: value
1000
811
  }
1001
812
  }
@@ -1004,71 +815,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1004
815
  }
1005
816
  }
1006
817
 
1007
- const changeFilter = async (idx, target, value) => {
1008
- let newFilters = [...state.filters]
1009
-
1010
- switch (target) {
1011
- case 'addNew':
1012
- newFilters.push({
1013
- label: '',
1014
- values: []
1015
- })
1016
- break
1017
- case 'remove':
1018
- if (newFilters.length === 1) {
1019
- newFilters = []
1020
- } else {
1021
- newFilters.splice(idx, 1)
1022
- }
1023
- break
1024
- case 'filterStyle':
1025
- newFilters[idx] = { ...newFilters[idx] }
1026
- newFilters[idx].filterStyle = value
1027
- break
1028
- case 'showDropdown':
1029
- newFilters[idx] = { ...newFilters[idx] }
1030
- newFilters[idx].showDropdown = value
1031
- break
1032
- case 'columnName':
1033
- newFilters[idx] = { ...newFilters[idx] }
1034
- newFilters[idx].columnName = value
1035
- newFilters[idx].values = [] // when a column name changes knock the previous values out
1036
- break
1037
- case 'filterOrder':
1038
- if (value === 'desc') {
1039
- newFilters[idx] = { ...runtimeFilters[idx] }
1040
- delete newFilters[idx].active
1041
- newFilters[idx].order = 'desc'
1042
- }
1043
- if (value === 'asc') {
1044
- newFilters[idx] = { ...runtimeFilters[idx] }
1045
- delete newFilters[idx].active
1046
- newFilters[idx].order = 'asc'
1047
- }
1048
- if (value === 'cust') {
1049
- newFilters[idx] = { ...runtimeFilters[idx] }
1050
- newFilters[idx].order = 'cust'
1051
- }
1052
- break
1053
- default:
1054
- newFilters[idx][target] = value
1055
- break
1056
- }
1057
-
1058
- setState({
1059
- ...state,
1060
- filters: newFilters
1061
- })
1062
- }
1063
-
1064
818
  // just adds a new column but not set to any data yet
1065
819
  const addAdditionalColumn = number => {
1066
820
  const columnKey = `additionalColumn${number}`
1067
821
 
1068
- setState({
1069
- ...state,
822
+ setConfig({
823
+ ...config,
1070
824
  columns: {
1071
- ...state.columns,
825
+ ...config.columns,
1072
826
  [columnKey]: {
1073
827
  label: 'New Column',
1074
828
  dataTable: false,
@@ -1081,18 +835,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1081
835
  }
1082
836
 
1083
837
  const removeAdditionalColumn = columnName => {
1084
- const newColumns = state.columns
838
+ const newColumns = config.columns
1085
839
 
1086
840
  delete newColumns[columnName]
1087
841
 
1088
- setState({
1089
- ...state,
842
+ setConfig({
843
+ ...config,
1090
844
  columns: newColumns
1091
845
  })
1092
846
  }
1093
847
 
1094
848
  const displayFilterLegendValue = arr => {
1095
- const filterName = state.filters[arr[0]].label || `Unlabeled Legend`
849
+ const filterName = config.filters?.[arr?.[0]]?.label || `Unlabeled Legend`
1096
850
 
1097
851
  const filterValue = runtimeFilters[arr[0]]
1098
852
 
@@ -1116,15 +870,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1116
870
  }
1117
871
 
1118
872
  const convertStateToConfig = () => {
1119
- let strippedState = _.cloneDeep(state) // Deep copy
873
+ let strippedState = _.cloneDeep(config) // Deep copy
1120
874
 
1121
875
  // Strip ref
1122
876
  delete strippedState['']
1123
877
 
1124
- delete strippedState.newViz
878
+ if (strippedState.columns.geo.name && strippedState.columns.primary.name) {
879
+ delete strippedState.newViz
880
+ }
1125
881
 
1126
882
  // Remove the legend
1127
- let strippedLegend = _.cloneDeep(state.legend)
883
+ let strippedLegend = _.cloneDeep(config.legend)
1128
884
 
1129
885
  delete strippedLegend.disabledAmt
1130
886
 
@@ -1134,19 +890,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1134
890
  delete strippedState.defaultData
1135
891
 
1136
892
  // Remove tooltips if they're active in the editor
1137
- let strippedGeneral = _.cloneDeep(state.general)
1138
-
1139
- strippedState.general = strippedGeneral
893
+ strippedState.general = _.cloneDeep(config.general)
1140
894
 
1141
895
  // Add columns property back to data if it's there
1142
- if (state.columns) {
1143
- strippedState.columns = state.columns
896
+ if (config.columns) {
897
+ strippedState.columns = config.columns
1144
898
  }
1145
899
 
1146
900
  return strippedState
1147
901
  }
1148
902
 
1149
- const isReversed = state.general.palette.isReversed
903
+ const isReversed = config.general.palette.isReversed
904
+
1150
905
  function filterColorPalettes() {
1151
906
  let sequential = []
1152
907
  let nonSequential = []
@@ -1189,23 +944,16 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1189
944
  const [sequential, nonSequential, accessibleColors] = filterColorPalettes()
1190
945
 
1191
946
  useEffect(() => {
1192
- let paletteName = ''
1193
- if (isReversed && !state.color.endsWith('reverse')) {
1194
- paletteName = state.color + 'reverse'
1195
- }
1196
- if (!isReversed && state.color.endsWith('reverse')) {
1197
- paletteName = state.color.slice(0, -7)
1198
- }
1199
- if (paletteName) {
1200
- handleEditorChanges('color', paletteName)
1201
- }
1202
- }, [isReversed])
947
+ setLoadedDefault(config.defaultData)
948
+ columnsRequiredChecker()
949
+ }, [config])
1203
950
 
1204
951
  useEffect(() => {
1205
- setLoadedDefault(state.defaultData)
1206
-
1207
- columnsRequiredChecker()
1208
- }, [state]) // eslint-disable-line
952
+ const newConfig = convertStateToConfig()
953
+ if (isEditor && setParentConfig) {
954
+ setParentConfig(newConfig)
955
+ }
956
+ }, [config])
1209
957
 
1210
958
  const columnsOptions = [
1211
959
  <option value='' key={'Select Option'}>
@@ -1222,7 +970,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1222
970
  })
1223
971
 
1224
972
  let columnsByKey = {}
1225
- state.data.forEach(datum => {
973
+ config.data.forEach(datum => {
1226
974
  Object.keys(datum).forEach(key => {
1227
975
  columnsByKey[key] = columnsByKey[key] || []
1228
976
  const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
@@ -1233,27 +981,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1233
981
  })
1234
982
  })
1235
983
 
1236
- const additionalColumns = Object.keys(state.columns).filter(value => {
984
+ const additionalColumns = Object.keys(config.columns).filter(value => {
1237
985
  const defaultCols = ['geo', 'navigate', 'primary', 'latitude', 'longitude']
1238
986
 
1239
- if (true === defaultCols.includes(value)) {
1240
- return false
1241
- }
1242
- return true
987
+ return true !== defaultCols.includes(value)
1243
988
  })
1244
989
 
1245
- const updateField = updateFieldFactory(state, setState)
990
+ const updateField = updateFieldFactory(config, setConfig)
1246
991
 
1247
992
  const onBackClick = () => {
1248
993
  setDisplayPanel(!displayPanel)
1249
- setState({
1250
- ...state,
994
+ setConfig({
995
+ ...config,
1251
996
  showEditorPanel: !displayPanel
1252
997
  })
1253
998
  }
1254
999
 
1255
- const usedFilterColumns = {}
1256
-
1257
1000
  const StateOptionList = () => {
1258
1001
  const arrOfArrays = Object.entries(supportedStatesFipsCodes)
1259
1002
 
@@ -1283,36 +1026,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1283
1026
  })
1284
1027
  }
1285
1028
 
1286
- useEffect(() => {
1287
- const parsedData = convertStateToConfig()
1288
- const formattedData = JSON.stringify(parsedData, undefined, 2)
1289
-
1290
- setConfigTextbox(formattedData)
1291
- }, [state]) // eslint-disable-line
1292
-
1293
- useEffect(() => {
1294
- // Pass up to Editor if needed
1295
- if (setParentConfig) {
1296
- const newConfig = convertStateToConfig()
1297
- setParentConfig(newConfig)
1298
- }
1299
- }, [state]) // eslint-disable-line
1300
-
1301
1029
  let numberOfItemsLimit = 8
1302
1030
 
1303
1031
  const getItemStyle = (isDragging, draggableStyle) => ({
1304
1032
  ...draggableStyle
1305
1033
  })
1306
1034
 
1307
- const getCategoryValuesOrder = () => {
1308
- let values = runtimeLegend
1309
- ? runtimeLegend.filter(item => !item.special).map(runtimeLegendItem => runtimeLegendItem.value)
1310
- : []
1035
+ const getCategoryValuesOrder = (): string[] | [] => {
1036
+ let values =
1037
+ runtimeLegend?.items?.length > 0
1038
+ ? runtimeLegend.items.filter(item => !item.special).map(runtimeLegendItem => runtimeLegendItem.value)
1039
+ : []
1311
1040
 
1312
- if (state.legend.cateogryValuesOrder) {
1041
+ if (config.legend.cateogryValuesOrder) {
1313
1042
  return values.sort((a, b) => {
1314
- let aVal = state.legend.cateogryValuesOrder.indexOf(a)
1315
- let bVal = state.legend.cateogryValuesOrder.indexOf(b)
1043
+ let aVal = config.legend.cateogryValuesOrder.indexOf(a)
1044
+ let bVal = config.legend.cateogryValuesOrder.indexOf(b)
1316
1045
  if (aVal === bVal) return 0
1317
1046
  if (aVal === -1) return 1
1318
1047
  if (bVal === -1) return -1
@@ -1345,7 +1074,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1345
1074
  ))
1346
1075
  }
1347
1076
 
1348
- const isLoadedFromUrl = state?.dataKey?.includes('http://') || state?.dataKey?.includes('https://')
1077
+ const isLoadedFromUrl = config?.dataKey?.includes('http://') || config?.dataKey?.includes('https://')
1349
1078
 
1350
1079
  return (
1351
1080
  <ErrorBoundary component='EditorPanel'>
@@ -1371,7 +1100,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1371
1100
  <ul className='geo-buttons d-grid' style={{ gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
1372
1101
  <button
1373
1102
  className={`${
1374
- state.general.geoType === 'us' || state.general.geoType === 'us-county' ? 'active' : ''
1103
+ config.general.geoType === 'us' || config.general.geoType === 'us-county' ? 'active' : ''
1375
1104
  } full-width`}
1376
1105
  onClick={e => {
1377
1106
  e.preventDefault()
@@ -1382,7 +1111,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1382
1111
  <span>United States</span>
1383
1112
  </button>
1384
1113
  <button
1385
- className={`${state.general.geoType === 'us-region' ? 'active' : ''} full-width`}
1114
+ className={`${config.general.geoType === 'us-region' ? 'active' : ''} full-width`}
1386
1115
  onClick={e => {
1387
1116
  e.preventDefault()
1388
1117
  handleEditorChanges('geoType', 'us-region')
@@ -1392,7 +1121,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1392
1121
  <span>U.S. Region</span>
1393
1122
  </button>
1394
1123
  <button
1395
- className={`${state.general.geoType === 'world' ? 'active' : ''} full-width`}
1124
+ className={`${config.general.geoType === 'world' ? 'active' : ''} full-width`}
1396
1125
  onClick={e => {
1397
1126
  e.preventDefault()
1398
1127
  handleEditorChanges('geoType', 'world')
@@ -1402,7 +1131,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1402
1131
  <span>World</span>
1403
1132
  </button>
1404
1133
  <button
1405
- className={`${state.general.geoType === 'single-state' ? 'active' : ''} full-width`}
1134
+ className={`${config.general.geoType === 'single-state' ? 'active' : ''} full-width`}
1406
1135
  onClick={e => {
1407
1136
  e.preventDefault()
1408
1137
  handleEditorChanges('geoType', 'single-state')
@@ -1414,10 +1143,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1414
1143
  </ul>
1415
1144
  </label>
1416
1145
  {/* Select > State or County Map */}
1417
- {(state.general.geoType === 'us' || state.general.geoType === 'us-county') && (
1146
+ {(config.general.geoType === 'us' || config.general.geoType === 'us-county') && (
1418
1147
  <Select
1419
1148
  label='Geography Subtype'
1420
- value={state.general.geoType}
1149
+ value={config.general.geoType}
1421
1150
  options={[
1422
1151
  { value: 'us', label: 'US State-Level' },
1423
1152
  { value: 'us-county', label: 'US County-Level' }
@@ -1427,10 +1156,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1427
1156
  }}
1428
1157
  />
1429
1158
  )}
1430
- {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1159
+ {(config.general.geoType === 'us-county' || config.general.geoType === 'single-state') && (
1431
1160
  <Select
1432
1161
  label='County Census Year'
1433
- value={state.general.countyCensusYear || '2019'}
1162
+ value={config.general.countyCensusYear || '2019'}
1434
1163
  options={[
1435
1164
  { value: '2022', label: '2022' },
1436
1165
  { value: '2021', label: '2021' },
@@ -1445,50 +1174,50 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1445
1174
  }}
1446
1175
  />
1447
1176
  )}
1448
- {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1449
- <label>
1450
- <span className='edit-label column-heading'>Filter Controlling County Census Year</span>
1451
- <select
1452
- value={state.general.filterControlsCountyYear || ''}
1453
- onChange={event => {
1454
- handleEditorChanges('filterControlsCountyYear', event.target.value)
1455
- }}
1456
- >
1457
- <option value=''>None</option>
1458
- {state.filters && state.filters.map(filter => <option>{filter.columnName}</option>)}
1459
- </select>
1460
- </label>
1177
+ {(config.general.geoType === 'us-county' || config.general.geoType === 'single-state') && (
1178
+ <Select
1179
+ label='Filter Controlling County Census Year'
1180
+ value={config.general.filterControlsCountyYear || ''}
1181
+ options={[
1182
+ { value: '', label: 'None' },
1183
+ ...(config.filters
1184
+ ? config.filters.map(filter => ({ value: filter.columnName, label: filter.columnName }))
1185
+ : [])
1186
+ ]}
1187
+ onChange={event => {
1188
+ handleEditorChanges('filterControlsCountyYear', event.target.value)
1189
+ }}
1190
+ />
1461
1191
  )}
1462
1192
 
1463
- {state.general.geoType === 'single-state' && runtimeData && (
1464
- <label>
1465
- <span className='edit-label column-heading'>Filter Controlling State Picked</span>
1466
- <select
1467
- value={state.general.filterControlsStatePicked || ''}
1468
- onChange={event => {
1469
- handleEditorChanges('filterControlsStatePicked', event.target.value)
1470
- }}
1471
- >
1472
- <option value=''>None</option>
1473
- {runtimeData && columnsInData?.map(col => <option>{col}</option>)}
1474
- </select>
1475
- </label>
1193
+ {config.general.geoType === 'single-state' && runtimeData && (
1194
+ <Select
1195
+ label='Filter Controlling State Picked'
1196
+ value={config.general.filterControlsStatePicked || ''}
1197
+ options={[
1198
+ { value: '', label: 'None' },
1199
+ ...(runtimeData && columnsInData?.map(col => ({ value: col, label: col })))
1200
+ ]}
1201
+ onChange={event => {
1202
+ handleEditorChanges('filterControlsStatePicked', event.target.value)
1203
+ }}
1204
+ />
1476
1205
  )}
1477
1206
 
1478
1207
  {/* Type */}
1479
1208
  {/* Select > Filter a state */}
1480
- {state.general.geoType === 'single-state' && (
1481
- <label>
1482
- <span className='edit-label column-heading'>State Selector</span>
1483
- <select
1484
- value={state.general.statePicked.stateName}
1485
- onChange={event => {
1486
- handleEditorChanges('chooseState', event.target.value)
1487
- }}
1488
- >
1489
- <StateOptionList />
1490
- </select>
1491
- </label>
1209
+ {config.general.geoType === 'single-state' && (
1210
+ <Select
1211
+ label='State Selector'
1212
+ value={config.general.statePicked?.stateName || ''}
1213
+ options={StateOptionList().map(option => ({
1214
+ value: option.props.value,
1215
+ label: option.props.children
1216
+ }))}
1217
+ onChange={event => {
1218
+ handleEditorChanges('chooseState', event.target.value)
1219
+ }}
1220
+ />
1492
1221
  )}
1493
1222
  {/* Type */}
1494
1223
  <Select
@@ -1508,13 +1237,13 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1508
1237
  </Tooltip>
1509
1238
  </>
1510
1239
  }
1511
- value={state.general.type}
1240
+ value={config.general.type}
1512
1241
  options={[
1513
1242
  { value: 'data', label: 'Data' },
1514
- ...(state.general.geoType === 'us-county' ? [{ value: 'us-geocode', label: 'Geocode' }] : []),
1515
- ...(state.general.geoType === 'world' ? [{ value: 'world-geocode', label: 'Geocode' }] : []),
1516
- ...(state.general.geoType !== 'us-county' ? [{ value: 'navigation', label: 'Navigation' }] : []),
1517
- ...(state.general.geoType === 'world' || state.general.geoType === 'us'
1243
+ ...(config.general.geoType === 'us-county' ? [{ value: 'us-geocode', label: 'Geocode' }] : []),
1244
+ ...(config.general.geoType === 'world' ? [{ value: 'world-geocode', label: 'Geocode' }] : []),
1245
+ ...(config.general.geoType !== 'us-county' ? [{ value: 'navigation', label: 'Navigation' }] : []),
1246
+ ...(config.general.geoType === 'world' || config.general.geoType === 'us'
1518
1247
  ? [{ value: 'bubble', label: 'Bubble' }]
1519
1248
  : [])
1520
1249
  ]}
@@ -1524,16 +1253,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1524
1253
  />
1525
1254
 
1526
1255
  {/* Navigation Behavior */}
1527
- {(state.general.type === 'navigation' || state.general.type === 'data') && (
1256
+ {(config.general.type === 'navigation' || config.general.type === 'data') && (
1528
1257
  <Select
1529
1258
  label='Navigation Behavior'
1530
- value={state.general.navigationTarget}
1259
+ value={config.general.navigationTarget}
1531
1260
  options={[
1532
1261
  { value: '_self', label: 'Same Window' },
1533
1262
  { value: '_blank', label: 'New Window' }
1534
1263
  ]}
1535
1264
  onChange={event => {
1536
- handleEditorChanges('navigationTarget', event.target.value)
1265
+ const _newConfig = _.cloneDeep(config)
1266
+ _newConfig.general.navigationTarget = event.target.value
1267
+ setConfig(_newConfig)
1537
1268
  }}
1538
1269
  />
1539
1270
  )}
@@ -1545,7 +1276,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1545
1276
  type='radio'
1546
1277
  name='equalnumber'
1547
1278
  value='equalnumber'
1548
- checked={state.legend.type === 'equalnumber' || state.legend.type === 'equalinterval'}
1279
+ checked={config.legend.type === 'equalnumber' || config.legend.type === 'equalinterval'}
1549
1280
  onChange={e => handleEditorChanges('classificationType', e.target.value)}
1550
1281
  />
1551
1282
  Numeric/Quantitative
@@ -1555,7 +1286,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1555
1286
  type='radio'
1556
1287
  name='category'
1557
1288
  value='category'
1558
- checked={state.legend.type === 'category'}
1289
+ checked={config.legend.type === 'category'}
1559
1290
  onChange={e => handleEditorChanges('classificationType', e.target.value)}
1560
1291
  />
1561
1292
  Categorical
@@ -1563,16 +1294,47 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1563
1294
  </div>
1564
1295
  </label>
1565
1296
 
1566
- <HexSetting.DisplayAsHexMap state={state} setState={setState} handleEditorChanges={handleEditorChanges} />
1567
- <HexSetting.DisplayShapesOnHex state={state} setState={setState} />
1568
- <HexSetting.ShapeColumns state={state} setState={setState} columnsOptions={columnsOptions} />
1297
+ {/* Display as Hex */}
1298
+ {general.geoType === 'us' && general.type !== 'navigation' && general.type !== 'bubble' && (
1299
+ <label className='checkbox mt-4'>
1300
+ <input
1301
+ type='checkbox'
1302
+ checked={config.general.displayAsHex}
1303
+ onChange={event => {
1304
+ const _newConfig = _.cloneDeep(config)
1305
+ _newConfig.general.displayAsHex = event.target.checked
1306
+ setConfig(_newConfig)
1307
+ }}
1308
+ />
1309
+ <span className='edit-label'>Display As Hex Map</span>
1310
+ </label>
1311
+ )}
1569
1312
 
1570
- {'us' === state.general.geoType &&
1571
- 'bubble' !== state.general.type &&
1572
- false === state.general.displayAsHex && (
1313
+ {/* Shapes on Hex */}
1314
+ <label className='checkbox mt-4'>
1315
+ <input
1316
+ type='checkbox'
1317
+ checked={config.hexMap.type === 'shapes'}
1318
+ onChange={event => {
1319
+ setConfig({
1320
+ ...config,
1321
+ hexMap: {
1322
+ ...config.hexMap,
1323
+ type: event.target.checked ? 'shapes' : 'standard'
1324
+ }
1325
+ })
1326
+ }}
1327
+ />
1328
+ <span className='edit-label'>Display Shapes on Hex Map</span>
1329
+ </label>
1330
+ <HexSetting.ShapeColumns columnsOptions={columnsOptions} />
1331
+
1332
+ {'us' === config.general.geoType &&
1333
+ 'bubble' !== config.general.type &&
1334
+ false === config.general.displayAsHex && (
1573
1335
  <CheckBox
1574
1336
  label='Show state labels'
1575
- checked={state.general.displayStateLabels}
1337
+ checked={config.general.displayStateLabels}
1576
1338
  onChange={event => {
1577
1339
  handleEditorChanges('displayStateLabels', event.target.checked)
1578
1340
  }}
@@ -1589,13 +1351,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1589
1351
  />
1590
1352
  )}
1591
1353
 
1592
- {'us' === state.general.geoType && (
1354
+ {'us' === config.general.geoType && (
1593
1355
  <label className='checkbox'>
1594
1356
  <input
1595
1357
  type='checkbox'
1596
1358
  checked={general.territoriesAlwaysShow || false}
1597
1359
  onChange={event => {
1598
- handleEditorChanges('territoriesAlwaysShow', event.target.checked)
1360
+ const _newConfig = _.cloneDeep(config)
1361
+ _newConfig.general.territoriesAlwaysShow = event.target.checked
1362
+ setConfig(_newConfig)
1599
1363
  }}
1600
1364
  />
1601
1365
  <span className='edit-label'>Show All Territories</span>
@@ -1635,9 +1399,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1635
1399
  <label className='checkbox'>
1636
1400
  <input
1637
1401
  type='checkbox'
1638
- checked={state.general.showTitle || false}
1402
+ checked={config.general.showTitle || false}
1639
1403
  onChange={event => {
1640
- handleEditorChanges('showTitle', event.target.checked)
1404
+ const _newConfig = _.cloneDeep(config)
1405
+ _newConfig.general.showTitle = event.target.checked
1406
+ setConfig(_newConfig)
1641
1407
  }}
1642
1408
  />
1643
1409
  <span className='edit-label'>Show Title</span>
@@ -1747,23 +1513,24 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1747
1513
  </Tooltip>
1748
1514
  </span>
1749
1515
  <Select
1750
- value={state.columns.geo ? state.columns.geo.name : columnsOptions[0]}
1516
+ value={config.columns.geo ? config.columns.geo.name : columnsOptions[0]}
1751
1517
  options={columnsOptions.map(c => c.key)}
1752
1518
  onChange={event => {
1753
1519
  editColumn('geo', 'name', event.target.value)
1520
+ checkConfigurationNeeded(config)
1754
1521
  }}
1755
1522
  />
1756
1523
  </label>
1757
- {state.general.type === 'us-geocode' && (
1524
+ {config.general.type === 'us-geocode' && (
1758
1525
  <label className='checkbox'>
1759
1526
  <input
1760
1527
  type='checkbox'
1761
- checked={state.general.convertFipsCodes}
1528
+ checked={config.general.convertFipsCodes}
1762
1529
  onChange={event => {
1763
- setState({
1764
- ...state,
1530
+ setConfig({
1531
+ ...config,
1765
1532
  general: {
1766
- ...state.general,
1533
+ ...config.general,
1767
1534
  convertFipsCodes: event.target.checked
1768
1535
  }
1769
1536
  })
@@ -1776,15 +1543,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1776
1543
  <label className='checkbox'>
1777
1544
  <input
1778
1545
  type='checkbox'
1779
- checked={state.general.hideGeoColumnInTooltip || false}
1546
+ checked={config.general.hideGeoColumnInTooltip || false}
1780
1547
  onChange={event => {
1781
- handleEditorChanges('hideGeoColumnInTooltip', event.target.checked)
1548
+ const _newConfig = _.cloneDeep(config)
1549
+ _newConfig.general.hideGeoColumnInTooltip = event.target.checked
1550
+ setConfig(_newConfig)
1782
1551
  }}
1783
1552
  />
1784
1553
  <span className='edit-label'>Hide Geography Column Name in Tooltip</span>
1785
1554
  </label>
1786
1555
  <TextField
1787
- value={state.general.geoLabelOverride}
1556
+ value={config.general.geoLabelOverride}
1788
1557
  section='general'
1789
1558
  fieldName='geoLabelOverride'
1790
1559
  label='Geography Label'
@@ -1802,17 +1571,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1802
1571
  }
1803
1572
  />
1804
1573
  </fieldset>
1805
- {'navigation' !== state.general.type && (
1574
+ {'navigation' !== config.general.type && (
1806
1575
  <fieldset className='primary-fieldset edit-block'>
1807
1576
  <Select
1808
1577
  label='Data Column'
1809
1578
  value={columns.primary.name}
1810
1579
  options={columnsOptions.map(c => c.key)}
1811
1580
  onChange={event => {
1812
- const _state = _.cloneDeep(state)
1581
+ const _state = _.cloneDeep(config)
1813
1582
  _state.columns.primary.name = event.target.value
1814
1583
  _state.columns.primary.label = event.target.value
1815
- setState(_state)
1584
+ setConfig(_state)
1585
+ checkConfigurationNeeded(_state)
1816
1586
  }}
1817
1587
  tooltip={
1818
1588
  <Tooltip style={{ textTransform: 'none' }}>
@@ -1828,7 +1598,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1828
1598
  <label className='checkbox'>
1829
1599
  <input
1830
1600
  type='checkbox'
1831
- checked={state.general.hidePrimaryColumnInTooltip || false}
1601
+ checked={config.general.hidePrimaryColumnInTooltip || false}
1832
1602
  onChange={event => {
1833
1603
  handleEditorChanges('hidePrimaryColumnInTooltip', event.target.checked)
1834
1604
  }}
@@ -1886,7 +1656,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1886
1656
  <label className='checkbox'>
1887
1657
  <input
1888
1658
  type='checkbox'
1889
- checked={state.columns.primary.useCommas}
1659
+ checked={config.columns.primary.useCommas}
1890
1660
  onChange={event => {
1891
1661
  editColumn('primary', 'useCommas', event.target.checked)
1892
1662
  }}
@@ -1898,7 +1668,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1898
1668
  <label className='checkbox'>
1899
1669
  <input
1900
1670
  type='checkbox'
1901
- checked={state.columns.primary.dataTable || false}
1671
+ checked={config.columns.primary.dataTable || false}
1902
1672
  onChange={event => {
1903
1673
  editColumn('primary', 'dataTable', event.target.checked)
1904
1674
  }}
@@ -1910,7 +1680,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1910
1680
  <label className='checkbox'>
1911
1681
  <input
1912
1682
  type='checkbox'
1913
- checked={state.columns.primary.tooltip || false}
1683
+ checked={config.columns.primary.tooltip || false}
1914
1684
  onChange={event => {
1915
1685
  editColumn('primary', 'tooltip', event.target.checked)
1916
1686
  }}
@@ -1922,7 +1692,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1922
1692
  </fieldset>
1923
1693
  )}
1924
1694
 
1925
- {state.general.type === 'bubble' && state.legend.type === 'category' && (
1695
+ {config.general.type === 'bubble' && config.legend.type === 'category' && (
1926
1696
  <fieldset className='primary-fieldset edit-block'>
1927
1697
  <label>
1928
1698
  <span className='edit-label column-heading'>
@@ -1937,7 +1707,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1937
1707
  </Tooltip>
1938
1708
  </span>
1939
1709
  <select
1940
- value={state.columns.categorical ? state.columns.categorical.name : columnsOptions[0]}
1710
+ value={config.columns.categorical ? config.columns.categorical.name : columnsOptions[0]}
1941
1711
  onChange={event => {
1942
1712
  editColumn('categorical', 'name', event.target.value)
1943
1713
  }}
@@ -1951,7 +1721,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1951
1721
  <>
1952
1722
  <Select
1953
1723
  label='Latitude Column'
1954
- value={state.columns.latitude.name}
1724
+ value={config.columns.latitude.name}
1955
1725
  options={columnsOptions.map(c => c.key)}
1956
1726
  onChange={e => {
1957
1727
  editColumn('latitude', 'name', e.target.value)
@@ -1959,7 +1729,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1959
1729
  />
1960
1730
  <Select
1961
1731
  label='Longitude Column'
1962
- value={state.columns.longitude.name}
1732
+ value={config.columns.longitude.name}
1963
1733
  options={columnsOptions.map(c => c.key)}
1964
1734
  onChange={e => {
1965
1735
  editColumn('longitude', 'name', e.target.value)
@@ -1968,7 +1738,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1968
1738
  </>
1969
1739
  }
1970
1740
 
1971
- {'navigation' !== state.general.type && (
1741
+ {'navigation' !== config.general.type && (
1972
1742
  <fieldset className='primary-fieldset edit-block'>
1973
1743
  <label>
1974
1744
  <span className='edit-label'>
@@ -1986,7 +1756,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1986
1756
  </Tooltip>
1987
1757
  </span>
1988
1758
  </label>
1989
- {state.legend.specialClasses.length === 2 && (
1759
+ {config.legend.specialClasses.length === 2 && (
1990
1760
  <Alert
1991
1761
  type='info'
1992
1762
  message='If a third special class is needed you can apply a pattern to set it apart.'
@@ -2005,38 +1775,38 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2005
1775
  Remove
2006
1776
  </button>
2007
1777
  <p>Special Class {i + 1}</p>
2008
- <label>
2009
- <span className='edit-label column-heading'>Data Key</span>
2010
- <select
2011
- value={specialClass.key}
2012
- onChange={e => {
2013
- editColumn('primary', 'specialClassEdit', { prop: 'key', index: i, value: e.target.value })
2014
- }}
2015
- >
2016
- {columnsOptions}
2017
- </select>
2018
- </label>
2019
- <label>
2020
- <span className='edit-label column-heading'>Value</span>
2021
- <select
2022
- value={specialClass.value}
2023
- onChange={e => {
2024
- editColumn('primary', 'specialClassEdit', {
2025
- prop: 'value',
2026
- index: i,
2027
- value: e.target.value
2028
- })
2029
- }}
2030
- >
2031
- <option value=''>- Select Value -</option>
2032
- {columnsByKey[specialClass.key] &&
2033
- columnsByKey[specialClass.key]
2034
- .sort()
2035
- .map(option => (
2036
- <option key={`special-class-value-option-${i}-${option}`}>{option}</option>
2037
- ))}
2038
- </select>
2039
- </label>
1778
+ <Select
1779
+ label='Data Key'
1780
+ value={specialClass.key}
1781
+ options={columnsOptions.map(option => ({
1782
+ value: option.key,
1783
+ label: option.key
1784
+ }))}
1785
+ onChange={event => {
1786
+ editColumn('primary', 'specialClassEdit', {
1787
+ prop: 'key',
1788
+ index: i,
1789
+ value: event.target.value
1790
+ })
1791
+ }}
1792
+ />
1793
+ <Select
1794
+ label='Value'
1795
+ value={specialClass.value}
1796
+ options={[
1797
+ { value: '', label: '- Select Value -' },
1798
+ ...(columnsByKey[specialClass.key] || [])
1799
+ .sort()
1800
+ .map(option => ({ value: option, label: option }))
1801
+ ]}
1802
+ onChange={event => {
1803
+ editColumn('primary', 'specialClassEdit', {
1804
+ prop: 'value',
1805
+ index: i,
1806
+ value: event.target.value
1807
+ })
1808
+ }}
1809
+ />
2040
1810
  <label>
2041
1811
  <span className='edit-label column-heading'>Label</span>
2042
1812
  <input
@@ -2053,7 +1823,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2053
1823
  </label>
2054
1824
  </div>
2055
1825
  ))}
2056
- {state.legend.specialClasses.length < 2 && (
1826
+ {config.legend.specialClasses.length < 2 && (
2057
1827
  <button
2058
1828
  className='btn btn-primary full-width'
2059
1829
  onClick={e => {
@@ -2083,14 +1853,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2083
1853
  </Tooltip>
2084
1854
  </span>
2085
1855
  <Select
2086
- value={state.columns.navigate ? state.columns.navigate.name : ''}
1856
+ value={config.columns.navigate ? config.columns.navigate.name : ''}
2087
1857
  options={columnsOptions.map(c => c.key)}
2088
1858
  onChange={event => {
2089
1859
  editColumn('navigate', 'name', event.target.value)
2090
1860
  }}
2091
1861
  />
2092
1862
  </label>
2093
- {'navigation' !== state.general.type && (
1863
+ {'navigation' !== config.general.type && (
2094
1864
  <fieldset className='primary-fieldset edit-block'>
2095
1865
  <label>
2096
1866
  <span className='edit-label'>
@@ -2119,17 +1889,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2119
1889
  >
2120
1890
  Remove
2121
1891
  </button>
2122
- <label>
2123
- <span className='edit-label column-heading'>Column</span>
2124
- <select
2125
- value={state.columns[val] ? state.columns[val].name : columnsOptions[0]}
2126
- onChange={event => {
2127
- editColumn(val, 'name', event.target.value)
2128
- }}
2129
- >
2130
- {columnsOptions}
2131
- </select>
2132
- </label>
1892
+ <Select
1893
+ label='Column'
1894
+ value={config.columns[val] ? config.columns[val].name : ''}
1895
+ options={columnsOptions.map(option => ({
1896
+ value: option.props.value,
1897
+ label: option.props.children
1898
+ }))}
1899
+ onChange={event => {
1900
+ editColumn(val, 'name', event.target.value)
1901
+ }}
1902
+ />
2133
1903
  <TextField
2134
1904
  value={columns[val].label}
2135
1905
  section='columns'
@@ -2170,7 +1940,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2170
1940
  <label className='checkbox'>
2171
1941
  <input
2172
1942
  type='checkbox'
2173
- checked={state.columns[val].useCommas}
1943
+ checked={config.columns[val].useCommas}
2174
1944
  onChange={event => {
2175
1945
  editColumn(val, 'useCommas', event.target.checked)
2176
1946
  }}
@@ -2182,7 +1952,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2182
1952
  <label className='checkbox'>
2183
1953
  <input
2184
1954
  type='checkbox'
2185
- checked={state.columns[val].dataTable}
1955
+ checked={config.columns[val].dataTable}
2186
1956
  onChange={event => {
2187
1957
  editColumn(val, 'dataTable', event.target.checked)
2188
1958
  }}
@@ -2194,7 +1964,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2194
1964
  <label className='checkbox'>
2195
1965
  <input
2196
1966
  type='checkbox'
2197
- checked={state.columns[val].tooltip}
1967
+ checked={config.columns[val].tooltip}
2198
1968
  onChange={event => {
2199
1969
  editColumn(val, 'tooltip', event.target.checked)
2200
1970
  }}
@@ -2216,7 +1986,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2216
1986
  </button>
2217
1987
  </fieldset>
2218
1988
  )}
2219
- {'category' === state.legend.type && (
1989
+ {'category' === config.legend.type && (
2220
1990
  <fieldset className='primary-fieldset edit-block'>
2221
1991
  <label>
2222
1992
  <span className='edit-label'>
@@ -2231,14 +2001,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2231
2001
  </Tooltip>
2232
2002
  </span>
2233
2003
  </label>
2234
- {state.legend.additionalCategories &&
2235
- state.legend.additionalCategories.map((val, i) => (
2004
+ {config.legend.additionalCategories &&
2005
+ config.legend.additionalCategories.map((val, i) => (
2236
2006
  <fieldset className='edit-block' key={val}>
2237
2007
  <button
2238
2008
  className='remove-column'
2239
2009
  onClick={event => {
2240
2010
  event.preventDefault()
2241
- const updatedAdditionaCategories = [...state.legend.additionalCategories]
2011
+ const updatedAdditionaCategories = [...config.legend.additionalCategories]
2242
2012
  updatedAdditionaCategories.splice(i, 1)
2243
2013
  updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2244
2014
  }}
@@ -2253,7 +2023,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2253
2023
  subsection={null}
2254
2024
  fieldName='additionalCategories'
2255
2025
  updateField={(section, subsection, fieldName, value) => {
2256
- const updatedAdditionaCategories = [...state.legend.additionalCategories]
2026
+ const updatedAdditionaCategories = [...config.legend.additionalCategories]
2257
2027
  updatedAdditionaCategories[i] = value
2258
2028
  updateField(section, subsection, fieldName, updatedAdditionaCategories)
2259
2029
  }}
@@ -2265,7 +2035,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2265
2035
  className={'btn btn-primary full-width'}
2266
2036
  onClick={event => {
2267
2037
  event.preventDefault()
2268
- const updatedAdditionaCategories = [...(state.legend.additionalCategories || [])]
2038
+ const updatedAdditionaCategories = [...(config.legend.additionalCategories || [])]
2269
2039
  updatedAdditionaCategories.push('')
2270
2040
  updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2271
2041
  }}
@@ -2277,7 +2047,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2277
2047
  </AccordionItemPanel>
2278
2048
  </AccordionItem>{' '}
2279
2049
  {/* Columns */}
2280
- {'navigation' !== state.general.type && (
2050
+ {'navigation' !== config.general.type && (
2281
2051
  <AccordionItem>
2282
2052
  {' '}
2283
2053
  {/* Legend */}
@@ -2285,7 +2055,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2285
2055
  <AccordionItemButton>Legend</AccordionItemButton>
2286
2056
  </AccordionItemHeading>
2287
2057
  <AccordionItemPanel>
2288
- {(state.legend.type === 'equalnumber' || state.legend.type === 'equalinterval') && (
2058
+ {(config.legend.type === 'equalnumber' || config.legend.type === 'equalinterval') && (
2289
2059
  <Select
2290
2060
  label='Legend Type'
2291
2061
  value={legend.type}
@@ -2294,23 +2064,46 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2294
2064
  { value: 'equalinterval', label: 'Equal Interval' }
2295
2065
  ]}
2296
2066
  onChange={event => {
2297
- handleEditorChanges('legendType', event.target.value)
2067
+ let testForType = Number(typeof config.data[0][config.columns.primary.name])
2068
+ let hasValue = config.data[0][config.columns.primary.name]
2069
+ let messages = []
2070
+
2071
+ if (!hasValue) {
2072
+ messages.push(
2073
+ `There appears to be values missing for data in the primary column ${config.columns.primary.name}`
2074
+ )
2075
+ }
2076
+
2077
+ if (testForType === 'string' && isNaN(testForType) && value !== 'category') {
2078
+ messages.push(
2079
+ 'Error with legend. Primary columns that are text must use a categorical legend type. Try changing the legend type to DEV-12345categorical.'
2080
+ )
2081
+ } else {
2082
+ messages = []
2083
+ }
2084
+
2085
+ const _newConfig = _.cloneDeep(config)
2086
+ _newConfig.legend.type = event.target.value
2087
+ _newConfig.runtime.editorErrorMessage = messages
2088
+ setConfig(_newConfig)
2298
2089
  }}
2299
2090
  />
2300
2091
  )}
2301
- {'navigation' !== state.general.type && (
2092
+ {'navigation' !== config.general.type && (
2302
2093
  <label className='checkbox'>
2303
2094
  <input
2304
2095
  type='checkbox'
2305
- checked={state.general.showSidebar || false}
2096
+ checked={config.general.showSidebar || false}
2306
2097
  onChange={event => {
2307
- handleEditorChanges('showSidebar', event.target.checked)
2098
+ const _newConfig = _.cloneDeep(config)
2099
+ _newConfig.general.showSidebar = event.target.checked
2100
+ setConfig(_newConfig)
2308
2101
  }}
2309
2102
  />
2310
2103
  <span className='edit-label'>Show Legend</span>
2311
2104
  </label>
2312
2105
  )}
2313
- {'navigation' !== state.general.type && (
2106
+ {'navigation' !== config.general.type && (
2314
2107
  <>
2315
2108
  <Select
2316
2109
  label='Legend Position'
@@ -2324,15 +2117,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2324
2117
  handleEditorChanges('sidebarPosition', event.target.value)
2325
2118
  }}
2326
2119
  />
2327
- {(state.legend.position === 'side' || !state.legend.position) &&
2328
- state.legend.style === 'gradient' && (
2120
+ {(config.legend.position === 'side' || !config.legend.position) &&
2121
+ config.legend.style === 'gradient' && (
2329
2122
  <span style={{ color: 'red', fontSize: '14px' }}>
2330
2123
  Position must be set to top or bottom to use gradient style.
2331
2124
  </span>
2332
2125
  )}
2333
2126
  </>
2334
2127
  )}
2335
- {'navigation' !== state.general.type && (
2128
+ {'navigation' !== config.general.type && (
2336
2129
  <Select
2337
2130
  label={
2338
2131
  <>
@@ -2364,7 +2157,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2364
2157
  }}
2365
2158
  />
2366
2159
  )}
2367
- {'navigation' !== state.general.type && state.legend.style === 'gradient' && (
2160
+ {'navigation' !== config.general.type && config.legend.style === 'gradient' && (
2368
2161
  <label>
2369
2162
  <span className='edit-label'>Gradient Style</span>
2370
2163
  <select
@@ -2378,7 +2171,29 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2378
2171
  </select>
2379
2172
  </label>
2380
2173
  )}
2381
- {'navigation' !== state.general.type && state.legend.style === 'gradient' && (
2174
+ {allowLegendSeparators && (
2175
+ <TextField
2176
+ value={legend.separators}
2177
+ updateField={updateField}
2178
+ section='legend'
2179
+ fieldName='separators'
2180
+ label='Legend Separators'
2181
+ placeholder='ex: 1,4'
2182
+ tooltip={
2183
+ <Tooltip style={{ textTransform: 'none' }}>
2184
+ <Tooltip.Target>
2185
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2186
+ </Tooltip.Target>
2187
+ <Tooltip.Content>
2188
+ <p>
2189
+ Separators between legend items represented by the legend item numbers separated by commas.
2190
+ </p>
2191
+ </Tooltip.Content>
2192
+ </Tooltip>
2193
+ }
2194
+ />
2195
+ )}
2196
+ {'navigation' !== config.general.type && config.legend.style === 'gradient' && (
2382
2197
  <label>
2383
2198
  <span className='edit-label'>Tick Rotation (Degrees)</span>
2384
2199
  <input
@@ -2420,7 +2235,12 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2420
2235
  type='checkbox'
2421
2236
  checked={legend.singleColumn}
2422
2237
  onChange={event => {
2423
- handleEditorChanges('singleColumnLegend', event.target.checked)
2238
+ const _newConfig = _.cloneDeep(config)
2239
+ _newConfig.legend.singleColumn = !event.target.checked
2240
+ _newConfig.legend.singleRow = false
2241
+ _newConfig.legend.verticalSorted = false
2242
+
2243
+ setConfig(_newConfig)
2424
2244
  }}
2425
2245
  />
2426
2246
  <span className='edit-label'>Single Column Legend</span>
@@ -2432,19 +2252,39 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2432
2252
  type='checkbox'
2433
2253
  checked={legend.singleRow}
2434
2254
  onChange={event => {
2435
- handleEditorChanges('singleRowLegend', event.target.checked)
2255
+ const _newConfig = _.cloneDeep(config)
2256
+ _newConfig.legend.singleRow = !event.target.checked
2257
+ _newConfig.legend.singleColumn = false
2258
+ _newConfig.legend.verticalSorted = false
2259
+
2260
+ setConfig(_newConfig)
2436
2261
  }}
2437
2262
  />
2438
2263
  <span className='edit-label'>Single Row Legend</span>
2439
2264
  </label>
2440
2265
  )}
2441
- {state.legend.style !== 'gradient' && (
2266
+
2267
+ {'navigation' !== config.general.type && config.legend.type === 'category' && (
2268
+ <Select
2269
+ label='Legend Group By :'
2270
+ value={legend.groupBy || ''}
2271
+ options={columnsOptions.map(c => c.key)}
2272
+ onChange={event => {
2273
+ const _newConfig = _.cloneDeep(config)
2274
+ _newConfig.legend.groupBy = event.target.value
2275
+ setConfig(_newConfig)
2276
+ }}
2277
+ />
2278
+ )}
2279
+ {config.legend.style !== 'gradient' && (
2442
2280
  <label className='checkbox'>
2443
2281
  <input
2444
2282
  type='checkbox'
2445
2283
  checked={legend.verticalSorted}
2446
2284
  onChange={event => {
2447
- handleEditorChanges('verticalSortedLegend', event.target.checked)
2285
+ const _newConfig = _.cloneDeep(config)
2286
+ _newConfig.legend.verticalSorted = event.target.checked
2287
+ setConfig(_newConfig)
2448
2288
  }}
2449
2289
  />
2450
2290
  <span className='edit-label'>Vertical sorted legend</span>
@@ -2469,7 +2309,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2469
2309
  <input
2470
2310
  type='checkbox'
2471
2311
  checked={legend.separateZero || false}
2472
- onChange={event => handleEditorChanges('separateZero', event.target.checked)}
2312
+ onChange={event => {
2313
+ const _newConfig = _.cloneDeep(config)
2314
+ _newConfig.legend.separateZero = event.target.checked
2315
+ return setConfig(_newConfig)
2316
+ }}
2473
2317
  />
2474
2318
  <span className='edit-label column-heading'>
2475
2319
  Separate Zero
@@ -2487,14 +2331,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2487
2331
  </span>
2488
2332
  </label>
2489
2333
  )}
2334
+
2490
2335
  {/* Temp Checkbox */}
2491
- {state.legend.type === 'equalnumber' && (
2336
+ {config.legend.type === 'equalnumber' && (
2492
2337
  <label className='checkbox'>
2493
2338
  <input
2494
2339
  type='checkbox'
2495
- checked={state.general.equalNumberOptIn}
2340
+ checked={config.general.equalNumberOptIn}
2496
2341
  onChange={event => {
2497
- handleEditorChanges('showEqualNumber', event.target.checked)
2342
+ const _newConfig = _.clone(config)
2343
+ _newConfig.general.equalNumberOptIn = event.target.checked
2344
+ setConfig(_newConfig)
2498
2345
  }}
2499
2346
  />
2500
2347
  <span className='edit-label column-heading'>Use new quantile legend</span>
@@ -2511,6 +2358,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2511
2358
  </Tooltip>
2512
2359
  </label>
2513
2360
  )}
2361
+
2514
2362
  {'category' !== legend.type && (
2515
2363
  <Select
2516
2364
  label={
@@ -2623,7 +2471,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2623
2471
  </label>
2624
2472
  </React.Fragment>
2625
2473
  )}
2626
- {state.filters.length > 0 && (
2474
+ {config.filters.length > 0 && (
2627
2475
  <label className='checkbox'>
2628
2476
  <input
2629
2477
  type='checkbox'
@@ -2651,7 +2499,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2651
2499
  </span>
2652
2500
  </label>
2653
2501
  )}
2654
- {(state.filters.length > 0 || state.general.type === 'bubble' || state.general.geoType === 'us') && (
2502
+ {(config.filters.length > 0 || config.general.type === 'bubble' || config.general.geoType === 'us') && (
2655
2503
  <label className='checkbox'>
2656
2504
  <input
2657
2505
  type='checkbox'
@@ -2680,7 +2528,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2680
2528
  </AccordionItemPanel>
2681
2529
  </AccordionItem>
2682
2530
  )}
2683
- {'navigation' !== state.general.type && (
2531
+ {'navigation' !== config.general.type && (
2684
2532
  <AccordionItem>
2685
2533
  {' '}
2686
2534
  {/* Filters */}
@@ -2688,11 +2536,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2688
2536
  <AccordionItemButton>Filters</AccordionItemButton>
2689
2537
  </AccordionItemHeading>
2690
2538
  <AccordionItemPanel>
2691
- <VizFilterEditor config={state} updateField={updateField} rawData={state.data} />
2539
+ <VizFilterEditor config={config} updateField={updateField} rawData={config.data} />
2692
2540
  </AccordionItemPanel>
2693
2541
  </AccordionItem>
2694
2542
  )}
2695
- {'navigation' !== state.general.type && (
2543
+ {'navigation' !== config.general.type && (
2696
2544
  <AccordionItem>
2697
2545
  {' '}
2698
2546
  {/* Data Table */}
@@ -2722,12 +2570,12 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2722
2570
  <label className='checkbox'>
2723
2571
  <input
2724
2572
  type='checkbox'
2725
- checked={state.table.wrapColumns}
2573
+ checked={config.table.wrapColumns}
2726
2574
  onChange={event => {
2727
- setState({
2728
- ...state,
2575
+ setConfig({
2576
+ ...config,
2729
2577
  table: {
2730
- ...state.table,
2578
+ ...config.table,
2731
2579
  wrapColumns: event.target.checked
2732
2580
  }
2733
2581
  })
@@ -2738,7 +2586,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2738
2586
  <label className='checkbox'>
2739
2587
  <input
2740
2588
  type='checkbox'
2741
- checked={state.table.forceDisplay !== undefined ? state.table.forceDisplay : !isDashboard}
2589
+ checked={config.table.forceDisplay !== undefined ? config.table.forceDisplay : !isDashboard}
2742
2590
  onChange={event => {
2743
2591
  handleEditorChanges('showDataTable', event.target.checked)
2744
2592
  }}
@@ -2783,7 +2631,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2783
2631
  }
2784
2632
  />
2785
2633
  <TextField
2786
- value={state.table.caption}
2634
+ value={config.table.caption}
2787
2635
  updateField={updateField}
2788
2636
  section='table'
2789
2637
  fieldName='caption'
@@ -2804,14 +2652,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2804
2652
  <label className='checkbox'>
2805
2653
  <input
2806
2654
  type='checkbox'
2807
- checked={state.table.limitHeight}
2655
+ checked={config.table.limitHeight}
2808
2656
  onChange={event => {
2809
2657
  handleEditorChanges('limitDataTableHeight', event.target.checked)
2810
2658
  }}
2811
2659
  />
2812
2660
  <span className='edit-label'>Limit Table Height</span>
2813
2661
  </label>
2814
- {state.table.limitHeight && (
2662
+ {config.table.limitHeight && (
2815
2663
  <TextField
2816
2664
  value={table.height}
2817
2665
  updateField={updateField}
@@ -2824,10 +2672,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2824
2672
  max='500'
2825
2673
  />
2826
2674
  )}
2675
+
2676
+ <TextField
2677
+ value={table.cellMinWidth}
2678
+ updateField={updateField}
2679
+ section='table'
2680
+ fieldName='cellMinWidth'
2681
+ label='Table Cell Min Width'
2682
+ type='number'
2683
+ min='0'
2684
+ max='500'
2685
+ />
2686
+
2827
2687
  <label className='checkbox'>
2828
2688
  <input
2829
2689
  type='checkbox'
2830
- checked={state.table.expanded || false}
2690
+ checked={config.table.expanded || false}
2831
2691
  onChange={event => {
2832
2692
  handleEditorChanges('expandDataTable', event.target.checked)
2833
2693
  }}
@@ -2835,19 +2695,19 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2835
2695
  <span className='edit-label'>Map loads with data table expanded</span>
2836
2696
  </label>
2837
2697
  <CheckBox
2838
- value={state.table.download}
2698
+ value={config.table.download}
2839
2699
  fieldName='download'
2840
2700
  label='Show Download CSV Link'
2841
2701
  section='table'
2842
2702
  updateField={updateField}
2843
2703
  />
2844
- {state.table.download && (
2704
+ {config.table.download && (
2845
2705
  <>
2846
2706
  <label className='checkbox'>
2847
2707
  <input
2848
2708
  type='checkbox'
2849
2709
  className='ms-4'
2850
- checked={state.table.showDownloadLinkBelow}
2710
+ checked={config.table.showDownloadLinkBelow}
2851
2711
  onChange={event => {
2852
2712
  handleEditorChanges('toggleDownloadLinkBelow', event.target.checked)
2853
2713
  }}
@@ -2855,7 +2715,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2855
2715
  <span className='edit-label'>Show Link Below Table</span>
2856
2716
  </label>
2857
2717
  <CheckBox
2858
- value={state.table.downloadVisibleDataOnly}
2718
+ value={config.table.downloadVisibleDataOnly}
2859
2719
  fieldName='downloadVisibleDataOnly'
2860
2720
  className='ms-4'
2861
2721
  label='Download only visible data'
@@ -2868,9 +2728,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2868
2728
  <label className='checkbox'>
2869
2729
  <input
2870
2730
  type='checkbox'
2871
- checked={state.table.showDataTableLink}
2731
+ checked={config.table.showDataTableLink}
2872
2732
  onChange={event => {
2873
- handleEditorChanges('toggleDataTableLink', event.target.checked)
2733
+ const _newConfig = _.cloneDeep(config)
2734
+ _newConfig.table.showDataTableLink = event.target.checked
2735
+ setConfig(_newConfig)
2874
2736
  }}
2875
2737
  />
2876
2738
  <span className='edit-label'>Show Data Table Name & Link</span>
@@ -2880,9 +2742,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2880
2742
  <label className='checkbox'>
2881
2743
  <input
2882
2744
  type='checkbox'
2883
- checked={state.table.showDownloadUrl}
2745
+ checked={config.table.showDownloadUrl}
2884
2746
  onChange={event => {
2885
- handleEditorChanges('toggleDataUrl', event.target.checked)
2747
+ const _newConfig = _.cloneDeep(config)
2748
+ _newConfig.table.showDownloadUrl = event.target.checked
2749
+ setConfig(_newConfig)
2886
2750
  }}
2887
2751
  />
2888
2752
  <span className='edit-label'>Show URL to Automatically Updated Data</span>
@@ -2891,7 +2755,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2891
2755
  <label className='checkbox'>
2892
2756
  <input
2893
2757
  type='checkbox'
2894
- checked={state.general.showFullGeoNameInCSV}
2758
+ checked={config.table.showFullGeoNameInCSV}
2895
2759
  onChange={event => {
2896
2760
  handleEditorChanges('toggleShowFullGeoNameInCSV', event.target.checked)
2897
2761
  }}
@@ -2901,7 +2765,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2901
2765
  <label className='checkbox'>
2902
2766
  <input
2903
2767
  type='checkbox'
2904
- checked={state.general.showDownloadImgButton}
2768
+ checked={config.general.showDownloadImgButton}
2905
2769
  onChange={event => {
2906
2770
  handleEditorChanges('toggleDownloadImgButton', event.target.checked)
2907
2771
  }}
@@ -2929,29 +2793,32 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2929
2793
  <AccordionItemButton>Interactivity</AccordionItemButton>
2930
2794
  </AccordionItemHeading>
2931
2795
  <AccordionItemPanel>
2932
- <label>
2933
- <span className='edit-label'>
2934
- Detail displays on{' '}
2935
- <Tooltip style={{ textTransform: 'none' }}>
2936
- <Tooltip.Target>
2937
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2938
- </Tooltip.Target>
2939
- <Tooltip.Content>
2940
- <p>At mobile sizes, information always appears in a popover modal when a user taps on an item.</p>
2941
- </Tooltip.Content>
2942
- </Tooltip>
2943
- </span>
2944
- <select
2945
- value={state.tooltips.appearanceType}
2946
- onChange={event => {
2947
- handleEditorChanges('appearanceType', event.target.value)
2948
- }}
2949
- >
2950
- <option value='hover'>Hover - Tooltip</option>
2951
- <option value='click'>Click - Popover Modal</option>
2952
- </select>
2953
- </label>
2954
- {'click' === state.tooltips.appearanceType && (
2796
+ <Select
2797
+ label={
2798
+ <>
2799
+ Detail displays on{' '}
2800
+ <Tooltip style={{ textTransform: 'none' }}>
2801
+ <Tooltip.Target>
2802
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2803
+ </Tooltip.Target>
2804
+ <Tooltip.Content>
2805
+ <p>
2806
+ At mobile sizes, information always appears in a popover modal when a user taps on an item.
2807
+ </p>
2808
+ </Tooltip.Content>
2809
+ </Tooltip>
2810
+ </>
2811
+ }
2812
+ value={config.tooltips.appearanceType}
2813
+ options={[
2814
+ { value: 'hover', label: 'Hover - Tooltip' },
2815
+ { value: 'click', label: 'Click - Popover Modal' }
2816
+ ]}
2817
+ onChange={event => {
2818
+ handleEditorChanges('appearanceType', event.target.value)
2819
+ }}
2820
+ />
2821
+ {'click' === config.tooltips.appearanceType && (
2955
2822
  <TextField
2956
2823
  value={tooltips.linkLabel}
2957
2824
  section='tooltips'
@@ -2963,7 +2830,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2963
2830
  <label className='checkbox'>
2964
2831
  <input
2965
2832
  type='checkbox'
2966
- checked={state.tooltips.capitalizeLabels}
2833
+ checked={config.tooltips.capitalizeLabels}
2967
2834
  onChange={event => {
2968
2835
  handleEditorChanges('capitalizeLabels', event.target.checked)
2969
2836
  }}
@@ -2982,7 +2849,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2982
2849
  <label>
2983
2850
  <span className='edit-label'>Header Theme</span>
2984
2851
  <ul className='color-palette'>
2985
- {headerColors.map(palette => {
2852
+ {HEADER_COLORS.map(palette => {
2986
2853
  return (
2987
2854
  <li
2988
2855
  title={palette}
@@ -2990,7 +2857,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2990
2857
  onClick={() => {
2991
2858
  handleEditorChanges('headerColor', palette)
2992
2859
  }}
2993
- className={state.general.headerColor === palette ? 'selected ' + palette : palette}
2860
+ className={config.general.headerColor === palette ? 'selected ' + palette : palette}
2994
2861
  ></li>
2995
2862
  )
2996
2863
  })}
@@ -2999,7 +2866,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2999
2866
  <label className='checkbox'>
3000
2867
  <input
3001
2868
  type='checkbox'
3002
- checked={state.general.showTitle || false}
2869
+ checked={config.general.showTitle || false}
3003
2870
  onChange={event => {
3004
2871
  handleEditorChanges('showTitle', event.target.checked)
3005
2872
  }}
@@ -3007,30 +2874,31 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3007
2874
  <span className='edit-label'>Show Title</span>
3008
2875
  </label>
3009
2876
 
3010
- {'navigation' === state.general.type && (
2877
+ {'navigation' === config.general.type && (
3011
2878
  <label className='checkbox'>
3012
2879
  <input
3013
2880
  type='checkbox'
3014
- checked={state.general.fullBorder || false}
2881
+ checked={config.general.fullBorder || false}
3015
2882
  onChange={event => {
3016
- handleEditorChanges('fullBorder', event.target.checked)
2883
+ const _newConfig = _.cloneDeep(config)
2884
+ _newConfig.general.fullBorder = event.target.checked
2885
+ setConfig(_newConfig)
3017
2886
  }}
3018
2887
  />
3019
2888
  <span className='edit-label'>Add border around map</span>
3020
2889
  </label>
3021
2890
  )}
3022
- <label>
3023
- <span className='edit-label'>Geo Border Color</span>
3024
- <select
3025
- value={state.general.geoBorderColor || false}
3026
- onChange={event => {
3027
- handleEditorChanges('geoBorderColor', event.target.value)
3028
- }}
3029
- >
3030
- <option value='darkGray'>Dark Gray (Default)</option>
3031
- <option value='sameAsBackground'>White</option>
3032
- </select>
3033
- </label>
2891
+ <Select
2892
+ label='Geo Border Color'
2893
+ value={config.general.geoBorderColor || ''}
2894
+ options={[
2895
+ { value: 'darkGray', label: 'Dark Gray (Default)' },
2896
+ { value: 'sameAsBackground', label: 'White' }
2897
+ ]}
2898
+ onChange={event => {
2899
+ handleEditorChanges('geoBorderColor', event.target.value)
2900
+ }}
2901
+ />
3034
2902
  <label>
3035
2903
  <span className='edit-label'>Map Color Palette</span>
3036
2904
  </label>
@@ -3041,8 +2909,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3041
2909
  fieldName='isReversed'
3042
2910
  size='small'
3043
2911
  label='Use selected palette in reverse order'
3044
- updateField={updateField}
3045
- value={state.general.palette.isReversed}
2912
+ onClick={() => {
2913
+ const _state = _.cloneDeep(config)
2914
+ _state.general.palette.isReversed = !_state.general.palette.isReversed
2915
+ let paletteName = ''
2916
+ if (_state.general.palette.isReversed && !config.color.endsWith('reverse')) {
2917
+ paletteName = config.color + 'reverse'
2918
+ }
2919
+ if (!_state.general.palette.isReversed && config.color.endsWith('reverse')) {
2920
+ paletteName = config.color.slice(0, -7)
2921
+ }
2922
+ if (paletteName) {
2923
+ _state.color = paletteName
2924
+ }
2925
+ setConfig(_state)
2926
+ }}
2927
+ value={config.general.palette.isReversed}
3046
2928
  />
3047
2929
  <span>Sequential</span>
3048
2930
  <ul className='color-palette'>
@@ -3064,9 +2946,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3064
2946
  title={palette}
3065
2947
  key={palette}
3066
2948
  onClick={() => {
3067
- handleEditorChanges('color', palette)
2949
+ const _newConfig = _.cloneDeep(config)
2950
+ _newConfig.color = palette
2951
+ setConfig(_newConfig)
3068
2952
  }}
3069
- className={state.color === palette ? 'selected' : ''}
2953
+ className={config.color === palette ? 'selected' : ''}
3070
2954
  >
3071
2955
  <span style={colorOne}></span>
3072
2956
  <span style={colorTwo}></span>
@@ -3091,7 +2975,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3091
2975
  }
3092
2976
 
3093
2977
  // hide palettes with too few colors for region maps
3094
- if (colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region') {
2978
+ if (colorPalettes[palette].length <= 8 && config.general.geoType === 'us-region') {
3095
2979
  return ''
3096
2980
  }
3097
2981
  return (
@@ -3099,9 +2983,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3099
2983
  title={palette}
3100
2984
  key={palette}
3101
2985
  onClick={() => {
3102
- handleEditorChanges('color', palette)
2986
+ const _newConfig = _.cloneDeep(config)
2987
+ _newConfig.color = palette
2988
+ setConfig(_newConfig)
3103
2989
  }}
3104
- className={state.color === palette ? 'selected' : ''}
2990
+ className={config.color === palette ? 'selected' : ''}
3105
2991
  >
3106
2992
  <span style={colorOne}></span>
3107
2993
  <span style={colorTwo}></span>
@@ -3126,7 +3012,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3126
3012
  }
3127
3013
 
3128
3014
  // hide palettes with too few colors for region maps
3129
- if (colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region') {
3015
+ if (colorPalettes[palette].length <= 8 && config.general.geoType === 'us-region') {
3130
3016
  return ''
3131
3017
  }
3132
3018
  return (
@@ -3136,7 +3022,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3136
3022
  onClick={() => {
3137
3023
  handleEditorChanges('color', palette)
3138
3024
  }}
3139
- className={state.color === palette ? 'selected' : ''}
3025
+ className={config.color === palette ? 'selected' : ''}
3140
3026
  >
3141
3027
  <span style={colorOne}></span>
3142
3028
  <span style={colorTwo}></span>
@@ -3149,7 +3035,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3149
3035
  Geocode Settings
3150
3036
  <TextField
3151
3037
  type='number'
3152
- value={state.visual.geoCodeCircleSize}
3038
+ value={config.visual.geoCodeCircleSize}
3153
3039
  section='visual'
3154
3040
  max='10'
3155
3041
  fieldName='geoCodeCircleSize'
@@ -3158,11 +3044,11 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3158
3044
  />
3159
3045
  </label>
3160
3046
 
3161
- {state.general.type === 'bubble' && (
3047
+ {config.general.type === 'bubble' && (
3162
3048
  <>
3163
3049
  <TextField
3164
3050
  type='number'
3165
- value={state.visual.minBubbleSize}
3051
+ value={config.visual.minBubbleSize}
3166
3052
  section='visual'
3167
3053
  fieldName='minBubbleSize'
3168
3054
  label='Minimum Bubble Size'
@@ -3170,7 +3056,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3170
3056
  />
3171
3057
  <TextField
3172
3058
  type='number'
3173
- value={state.visual.maxBubbleSize}
3059
+ value={config.visual.maxBubbleSize}
3174
3060
  section='visual'
3175
3061
  fieldName='maxBubbleSize'
3176
3062
  label='Maximum Bubble Size'
@@ -3178,51 +3064,59 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3178
3064
  />
3179
3065
  </>
3180
3066
  )}
3181
- {(state.general.geoType === 'world' ||
3182
- (state.general.geoType === 'us' && state.general.type === 'bubble')) && (
3067
+ {(config.general.geoType === 'world' ||
3068
+ (config.general.geoType === 'us' && config.general.type === 'bubble')) && (
3183
3069
  <label className='checkbox'>
3184
3070
  <input
3185
3071
  type='checkbox'
3186
- checked={state.visual.showBubbleZeros}
3072
+ checked={config.visual.showBubbleZeros}
3187
3073
  onChange={event => {
3188
- handleEditorChanges('showBubbleZeros', event.target.checked)
3074
+ const _newConfig = _.cloneDeep(config)
3075
+ _newConfig.visual.showBubbleZeros = event.target.checked
3076
+ setConfig(_newConfig)
3189
3077
  }}
3190
3078
  />
3191
3079
  <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
3192
3080
  </label>
3193
3081
  )}
3194
- {(state.general.geoType === 'world' || state.general.geoType === 'single-state') && (
3082
+ {(config.general.geoType === 'world' || config.general.geoType === 'single-state') && (
3195
3083
  <label className='checkbox'>
3196
3084
  <input
3197
3085
  type='checkbox'
3198
- checked={state.general.allowMapZoom}
3086
+ checked={config.general.allowMapZoom}
3199
3087
  onChange={event => {
3200
- handleEditorChanges('allowMapZoom', event.target.checked)
3088
+ const _newConfig = _.cloneDeep(config)
3089
+ _newConfig.general.allowMapZoom = event.target.checked
3090
+ _newConfig.mapPosition.coordinates = config.general.geoType === 'world' ? [0, 30] : [0, 0]
3091
+ _newConfig.mapPosition.zoom = 1
3092
+ setConfig(_newConfig)
3201
3093
  }}
3202
3094
  />
3203
3095
  <span className='edit-label'>Allow Map Zooming</span>
3204
3096
  </label>
3205
3097
  )}
3206
- {state.general.type === 'bubble' && (
3098
+ {config.general.type === 'bubble' && (
3207
3099
  <label className='checkbox'>
3208
3100
  <input
3209
3101
  type='checkbox'
3210
- checked={state.visual.extraBubbleBorder}
3102
+ checked={config.visual.extraBubbleBorder}
3211
3103
  onChange={event => {
3212
- handleEditorChanges('toggleExtraBubbleBorder', event.target.checked)
3104
+ const _newConfig = _.cloneDeep(config)
3105
+ _newConfig.visual.extraBubbleBorder = event.target.checked
3106
+ setConfig(_newConfig)
3213
3107
  }}
3214
3108
  />
3215
3109
  <span className='edit-label'>Bubble Map has extra border</span>
3216
3110
  </label>
3217
3111
  )}
3218
- {(state.general.geoType === 'us' ||
3219
- state.general.geoType === 'us-county' ||
3220
- state.general.geoType === 'world') && (
3112
+ {(config.general.geoType === 'us' ||
3113
+ config.general.geoType === 'us-county' ||
3114
+ config.general.geoType === 'world') && (
3221
3115
  <>
3222
3116
  <label>
3223
3117
  <span className='edit-label'>Default City Style</span>
3224
3118
  <select
3225
- value={state.visual.cityStyle || false}
3119
+ value={config.visual.cityStyle || false}
3226
3120
  onChange={event => {
3227
3121
  handleEditorChanges('handleCityStyle', event.target.value)
3228
3122
  }}
@@ -3236,7 +3130,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3236
3130
  </select>
3237
3131
  </label>
3238
3132
  <TextField
3239
- value={state.visual.cityStyleLabel}
3133
+ value={config.visual.cityStyleLabel}
3240
3134
  section='visual'
3241
3135
  fieldName='cityStyleLabel'
3242
3136
  label='Label (Optional) '
@@ -3256,8 +3150,8 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3256
3150
  )}
3257
3151
  {/* <AdditionalCityStyles /> */}
3258
3152
  <>
3259
- {state.visual.additionalCityStyles.length > 0 &&
3260
- state.visual.additionalCityStyles.map(({ label, column, value, shape }, i) => {
3153
+ {config.visual.additionalCityStyles.length > 0 &&
3154
+ config.visual.additionalCityStyles.map(({ label, column, value, shape }, i) => {
3261
3155
  return (
3262
3156
  <div className='edit-block' key={`additional-city-style-${i}`}>
3263
3157
  <button
@@ -3330,7 +3224,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3330
3224
  type='number'
3331
3225
  min={0}
3332
3226
  max={100}
3333
- value={state.tooltips.opacity ? state.tooltips.opacity : 100}
3227
+ value={config.tooltips.opacity ? config.tooltips.opacity : 100}
3334
3228
  section='tooltips'
3335
3229
  fieldName='opacity'
3336
3230
  label='Tooltip Opacity (%)'
@@ -3338,7 +3232,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3338
3232
  />
3339
3233
  </label>
3340
3234
  {/* Leaflet Map Type */}
3341
- {state.general.geoType === 'leaflet' && (
3235
+ {config.general.geoType === 'leaflet' && (
3342
3236
  <>
3343
3237
  <Select
3344
3238
  label='Leaflet Theme'
@@ -3356,9 +3250,9 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3356
3250
  <AccordionItemButton>Custom Map Layers</AccordionItemButton>
3357
3251
  </AccordionItemHeading>
3358
3252
  <AccordionItemPanel>
3359
- {state.map.layers.length === 0 && <p>There are currently no layers.</p>}
3253
+ {config.map.layers.length === 0 && <p>There are currently no layers.</p>}
3360
3254
 
3361
- {state.map.layers.map((layer, index) => {
3255
+ {config.map.layers.map((layer, index) => {
3362
3256
  return (
3363
3257
  <>
3364
3258
  <Accordion allowZeroExpanded>
@@ -3444,10 +3338,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3444
3338
  </p>
3445
3339
  </AccordionItemPanel>
3446
3340
  </AccordionItem>
3447
- {state.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
3448
- {state.general.geoType !== 'us-county' && <Panels.Annotate name='Text Annotations' />}
3341
+ {config.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
3342
+ {config.general.geoType !== 'us-county' && <Panels.Annotate name='Text Annotations' />}
3449
3343
  </Accordion>
3450
- <AdvancedEditor loadConfig={loadConfig} config={state} convertStateToConfig={convertStateToConfig} />
3344
+ <AdvancedEditor loadConfig={setConfig} config={config} convertStateToConfig={convertStateToConfig} />
3451
3345
  </Layout.Sidebar>
3452
3346
  </ErrorBoundary>
3453
3347
  )