@cdc/map 4.24.12 → 4.25.2-25

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 (64) hide show
  1. package/dist/cdcmap.js +50119 -48822
  2. package/examples/annotation/index.json +1 -1
  3. package/examples/custom-map-layers.json +1 -1
  4. package/examples/default-geocode.json +2 -2
  5. package/examples/example-city-state.json +1 -1
  6. package/examples/private/DEV-9989.json +229 -0
  7. package/examples/private/ardi.json +180 -0
  8. package/examples/private/colors 2.json +416 -0
  9. package/examples/private/colors.json +416 -0
  10. package/examples/private/colors.json.zip +0 -0
  11. package/examples/private/customColors.json +45348 -0
  12. package/examples/private/mmr.json +246 -0
  13. package/examples/private/test.json +1632 -0
  14. package/index.html +12 -14
  15. package/package.json +8 -3
  16. package/src/CdcMap.tsx +126 -396
  17. package/src/_stories/CdcMap.Filters.stories.tsx +19 -0
  18. package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +9 -0
  19. package/src/_stories/CdcMap.stories.tsx +1 -1
  20. package/src/_stories/GoogleMap.stories.tsx +19 -0
  21. package/src/_stories/_mock/DEV-10148.json +859 -0
  22. package/src/_stories/_mock/DEV-9989.json +229 -0
  23. package/src/_stories/_mock/example-city-state.json +1 -1
  24. package/src/_stories/_mock/google-map.json +819 -0
  25. package/src/_stories/_mock/wastewater-map.json +210 -206
  26. package/src/components/Annotation/Annotation.Draggable.tsx +34 -43
  27. package/src/components/Annotation/AnnotationDropdown.tsx +4 -4
  28. package/src/components/CityList.tsx +3 -9
  29. package/src/components/DataTable.tsx +8 -9
  30. package/src/components/EditorPanel/components/EditorPanel.tsx +255 -490
  31. package/src/components/GoogleMap/components/GoogleMap.tsx +67 -0
  32. package/src/components/GoogleMap/index.tsx +3 -0
  33. package/src/components/Legend/components/Legend.tsx +40 -30
  34. package/src/components/Legend/components/LegendItem.Hex.tsx +7 -3
  35. package/src/components/Legend/components/index.scss +22 -16
  36. package/src/components/Modal.tsx +6 -5
  37. package/src/components/NavigationMenu.tsx +4 -3
  38. package/src/components/UsaMap/components/TerritoriesSection.tsx +66 -0
  39. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +15 -16
  40. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +3 -3
  41. package/src/components/UsaMap/components/UsaMap.County.tsx +1 -1
  42. package/src/components/UsaMap/components/UsaMap.Region.tsx +12 -8
  43. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +2 -2
  44. package/src/components/UsaMap/components/UsaMap.State.tsx +23 -29
  45. package/src/components/WorldMap/WorldMap.tsx +3 -5
  46. package/src/context.ts +0 -12
  47. package/src/data/initial-state.js +2 -2
  48. package/src/data/supported-geos.js +23 -3
  49. package/src/helpers/applyColorToLegend.ts +3 -3
  50. package/src/helpers/closeModal.ts +9 -0
  51. package/src/helpers/handleMapAriaLabels.ts +38 -0
  52. package/src/helpers/indexOfIgnoreType.ts +8 -0
  53. package/src/helpers/navigationHandler.ts +21 -0
  54. package/src/helpers/toTitleCase.ts +44 -0
  55. package/src/helpers/validateFipsCodeLength.ts +30 -0
  56. package/src/hooks/useResizeObserver.ts +42 -0
  57. package/src/hooks/useTooltip.ts +4 -2
  58. package/src/index.jsx +1 -0
  59. package/src/scss/editor-panel.scss +2 -1
  60. package/src/scss/filters.scss +0 -5
  61. package/src/scss/main.scss +57 -61
  62. package/src/scss/map.scss +1 -13
  63. package/src/types/MapConfig.ts +20 -11
  64. package/src/types/MapContext.ts +4 -12
@@ -26,6 +26,7 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
26
26
  import Icon from '@cdc/core/components/ui/Icon'
27
27
  import InputToggle from '@cdc/core/components/inputs/InputToggle'
28
28
  import Tooltip from '@cdc/core/components/ui/Tooltip'
29
+ import VizFilterEditor from '@cdc/core/components/EditorPanel/VizFilterEditor'
29
30
 
30
31
  // Assets
31
32
  import UsaGraphic from '@cdc/core/assets/icon-map-usa.svg'
@@ -37,28 +38,23 @@ import usaDefaultConfig from '../../../../examples/default-usa.json'
37
38
  import countyDefaultConfig from '../../../../examples/default-county.json'
38
39
  import useMapLayers from '../../../hooks/useMapLayers.tsx'
39
40
 
40
- import { useFilters } from '@cdc/core/components/Filters'
41
-
42
41
  import HexSetting from './HexShapeSettings.jsx'
43
42
  import ConfigContext from '../../../context.ts'
44
43
  import { MapContext } from '../../../types/MapContext.js'
45
- import { TextField } from './Inputs'
46
44
  import Alert from '@cdc/core/components/Alert'
45
+ import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
46
+ import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
47
47
 
48
48
  // Todo: move to useReducer, seperate files out.
49
49
  const EditorPanel = ({ columnsRequiredChecker }) => {
50
50
  // prettier-ignore
51
51
  const {
52
- changeFilterActive,
53
- columnsInData = [],
54
52
  isDashboard,
55
53
  isDebug,
56
- isEditor,
57
54
  loadConfig,
58
55
  runtimeFilters,
59
56
  runtimeLegend,
60
57
  setParentConfig,
61
- setRuntimeFilters,
62
58
  setState,
63
59
  state,
64
60
  tooltipId,
@@ -70,6 +66,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
70
66
  } = useContext<MapContext>(ConfigContext)
71
67
 
72
68
  const { general, columns, legend, table, tooltips } = state
69
+ const columnsInData = state?.data?.[0] ? Object.keys(state.data[0]) : []
73
70
 
74
71
  const [configTextboxValue, setConfigTextbox] = useState({}) // eslint-disable-line
75
72
 
@@ -79,13 +76,6 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
79
76
 
80
77
  const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0])
81
78
 
82
- const { handleFilterOrder, filterOrderOptions, filterStyleOptions } = useFilters({
83
- config: state,
84
- setConfig: setState,
85
- filteredData: runtimeFilters,
86
- setFilteredData: setRuntimeFilters
87
- })
88
-
89
79
  const headerColors = [
90
80
  'theme-blue',
91
81
  'theme-purple',
@@ -241,6 +231,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
241
231
 
242
232
  const handleEditorChanges = async (property, value) => {
243
233
  switch (property) {
234
+ case 'navigationTarget':
235
+ setState({
236
+ ...state,
237
+ general: {
238
+ ...state.general,
239
+ navigationTarget: value
240
+ }
241
+ })
242
+ break
244
243
  // change these to be more generic.
245
244
  // updateVisualPropertyValue
246
245
  // updateGeneralPropertyValue, etc.
@@ -725,6 +724,14 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
725
724
  }
726
725
  })
727
726
  break
727
+ case 'google-map':
728
+ setState({
729
+ ...state,
730
+ general: {
731
+ ...state.general,
732
+ geoType: 'google-map'
733
+ }
734
+ })
728
735
  default:
729
736
  break
730
737
  }
@@ -1073,42 +1080,6 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1073
1080
  })
1074
1081
  }
1075
1082
 
1076
- const MapFilters = () => {
1077
- return (
1078
- <>
1079
- <label>
1080
- Filter Behavior
1081
- <select
1082
- value={state.filterBehavior}
1083
- onChange={e => {
1084
- setState({
1085
- ...state,
1086
- filterBehavior: e.target.value
1087
- })
1088
- }}
1089
- >
1090
- <option key='Apply Button' value='Apply Button'>
1091
- Apply Button
1092
- </option>
1093
- <option key='Filter Change' value='Filter Change'>
1094
- Filter Change
1095
- </option>
1096
- </select>
1097
- </label>
1098
- <label>
1099
- <TextField
1100
- type='textarea'
1101
- value={state.filterIntro}
1102
- fieldName='filterIntro'
1103
- label='Filter Intro text'
1104
- updateField={updateField}
1105
- />
1106
- </label>
1107
- {filtersJSX}
1108
- </>
1109
- )
1110
- }
1111
-
1112
1083
  const removeAdditionalColumn = columnName => {
1113
1084
  const newColumns = state.columns
1114
1085
 
@@ -1271,38 +1242,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1271
1242
  return true
1272
1243
  })
1273
1244
 
1274
- const updateField = (section, subsection, fieldName, newValue) => {
1275
- if (!section) {
1276
- setState({
1277
- ...state,
1278
- [fieldName]: newValue
1279
- })
1280
- return
1281
- }
1282
-
1283
- const isArray = Array.isArray(state[section])
1284
-
1285
- let sectionValue = isArray ? [...state[section], newValue] : { ...state[section], [fieldName]: newValue }
1286
-
1287
- if (null !== subsection) {
1288
- if (isArray) {
1289
- sectionValue = [...state[section]]
1290
- sectionValue[subsection] = { ...sectionValue[subsection], [fieldName]: newValue }
1291
- } else {
1292
- sectionValue = {
1293
- ...state[section],
1294
- [subsection]: { ...state[section][subsection], [fieldName]: newValue }
1295
- }
1296
- }
1297
- }
1298
-
1299
- let updatedState = {
1300
- ...state,
1301
- [section]: sectionValue
1302
- }
1303
-
1304
- setState(updatedState)
1305
- }
1245
+ const updateField = updateFieldFactory(state, setState)
1306
1246
 
1307
1247
  const onBackClick = () => {
1308
1248
  setDisplayPanel(!displayPanel)
@@ -1314,163 +1254,6 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1314
1254
 
1315
1255
  const usedFilterColumns = {}
1316
1256
 
1317
- const filtersJSX = state.filters.map((filter, index) => {
1318
- if (filter.type === 'url') return <></>
1319
-
1320
- if (filter.columnName) {
1321
- usedFilterColumns[filter.columnName] = true
1322
- }
1323
-
1324
- return (
1325
- <>
1326
- <fieldset className='edit-block' key={`filter-${index}`}>
1327
- <button
1328
- className='remove-column'
1329
- onClick={e => {
1330
- e.preventDefault()
1331
- changeFilter(index, 'remove')
1332
- }}
1333
- >
1334
- Remove
1335
- </button>
1336
- <TextField
1337
- value={state.filters[index].label}
1338
- section='filters'
1339
- subsection={index}
1340
- fieldName='label'
1341
- label='Label'
1342
- updateField={updateField}
1343
- />
1344
- <label>
1345
- <span className='edit-label column-heading'>
1346
- Filter Column
1347
- <Tooltip style={{ textTransform: 'none' }}>
1348
- <Tooltip.Target>
1349
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1350
- </Tooltip.Target>
1351
- <Tooltip.Content>
1352
- <p>
1353
- Selecting a column will add a dropdown menu below the map legend and allow users to filter based on
1354
- the values in this column.
1355
- </p>
1356
- </Tooltip.Content>
1357
- </Tooltip>
1358
- </span>
1359
- <select
1360
- value={filter.columnName}
1361
- onChange={event => {
1362
- changeFilter(index, 'columnName', event.target.value)
1363
- }}
1364
- >
1365
- {columnsOptions.filter(({ key }) => undefined === usedFilterColumns[key] || filter.columnName === key)}
1366
- </select>
1367
- </label>
1368
-
1369
- <label>
1370
- <span className='edit-showDropdown column-heading'>Show Filter Input</span>
1371
- <input
1372
- type='checkbox'
1373
- checked={filter.showDropdown === undefined ? true : filter.showDropdown}
1374
- onChange={e => {
1375
- changeFilter(index, 'showDropdown', e.target.checked)
1376
- }}
1377
- />
1378
- </label>
1379
-
1380
- <label>
1381
- <span className='edit-filterOrder column-heading'>Filter Style</span>
1382
- <select
1383
- value={filter.filterStyle}
1384
- onChange={e => {
1385
- changeFilter(index, 'filterStyle', e.target.value)
1386
- }}
1387
- >
1388
- {filterStyleOptions.map((option, index) => {
1389
- return (
1390
- <option value={option} key={`filter-${option}--${index}`}>
1391
- {option}
1392
- </option>
1393
- )
1394
- })}
1395
- </select>
1396
- </label>
1397
-
1398
- <label>
1399
- <span className='edit-filterOrder column-heading'>Filter Order</span>
1400
- <select
1401
- value={filter.order}
1402
- onChange={e => {
1403
- changeFilter(index, 'filterOrder', e.target.value)
1404
- changeFilterActive(index, filter.values[0])
1405
- }}
1406
- >
1407
- {filterOrderOptions.map((option, index) => {
1408
- return (
1409
- <option value={option.value} key={`filter-${index}`}>
1410
- {option.label}
1411
- </option>
1412
- )
1413
- })}
1414
- </select>
1415
- </label>
1416
-
1417
- <TextField
1418
- value={state.filters[index].setByQueryParameter}
1419
- section='filters'
1420
- subsection={index}
1421
- fieldName='setByQueryParameter'
1422
- label='Default Value Set By Query String Parameter'
1423
- updateField={updateField}
1424
- />
1425
-
1426
- {filter.order === 'cust' && (
1427
- <DragDropContext
1428
- onDragEnd={({ source, destination }) =>
1429
- handleFilterOrder(source.index, destination?.index, index, state.filters?.[index])
1430
- }
1431
- >
1432
- <Droppable droppableId='filter_order'>
1433
- {provided => (
1434
- <ul
1435
- {...provided.droppableProps}
1436
- className='sort-list'
1437
- ref={provided.innerRef}
1438
- style={{ marginTop: '1em' }}
1439
- >
1440
- {state.filters[index]?.values.map((value, index) => {
1441
- return (
1442
- <Draggable key={value} draggableId={`draggableFilter-${value}`} index={index}>
1443
- {(provided, snapshot) => (
1444
- <li>
1445
- <div
1446
- className={snapshot.isDragging ? 'currently-dragging' : ''}
1447
- style={getItemStyle(
1448
- snapshot.isDragging,
1449
- provided.draggableProps.style,
1450
- sortableItemStyles
1451
- )}
1452
- ref={provided.innerRef}
1453
- {...provided.draggableProps}
1454
- {...provided.dragHandleProps}
1455
- >
1456
- {value}
1457
- </div>
1458
- </li>
1459
- )}
1460
- </Draggable>
1461
- )
1462
- })}
1463
- {provided.placeholder}
1464
- </ul>
1465
- )}
1466
- </Droppable>
1467
- </DragDropContext>
1468
- )}
1469
- </fieldset>
1470
- </>
1471
- )
1472
- })
1473
-
1474
1257
  const StateOptionList = () => {
1475
1258
  const arrOfArrays = Object.entries(supportedStatesFipsCodes)
1476
1259
 
@@ -1564,27 +1347,6 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1564
1347
 
1565
1348
  const isLoadedFromUrl = state?.dataKey?.includes('http://') || state?.dataKey?.includes('https://')
1566
1349
 
1567
- // if isDebug = true, then try to set the Geography Col and Data col to reduce clicking
1568
- const setGeoColumn = () => {
1569
- // only for debug mode
1570
- let geoColFound = columnsInData.includes(state.columns.geo.name)
1571
- if (undefined !== isDebug && isDebug && !geoColFound) {
1572
- // then try to set the x axis to appropriate value so we dont have to manually do it
1573
- let mapcols = columnsInData[0]
1574
- if (mapcols !== '') editColumn('geo', 'name', mapcols)
1575
-
1576
- if (
1577
- !state.columns.hasOwnProperty('primary') ||
1578
- undefined === state.columns.primary.name ||
1579
- '' === state.columns.primary.name ||
1580
- !state.columns.primary.name
1581
- ) {
1582
- editColumn('primary', 'name', columnsInData[1]) // blindly picks first value col
1583
- }
1584
- }
1585
- }
1586
- if (isDebug) setGeoColumn()
1587
-
1588
1350
  return (
1589
1351
  <ErrorBoundary component='EditorPanel'>
1590
1352
  <Layout.Sidebar
@@ -1602,14 +1364,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1602
1364
  <AccordionItemButton>Type</AccordionItemButton>
1603
1365
  </AccordionItemHeading>
1604
1366
  <AccordionItemPanel>
1605
- {/* Geography */}
1606
1367
  <label>
1607
1368
  <span className='edit-label column-heading'>
1608
1369
  <span>Geography</span>
1609
1370
  </span>
1610
- <ul className='geo-buttons'>
1371
+ <ul className='geo-buttons d-grid' style={{ gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
1611
1372
  <button
1612
- className={state.general.geoType === 'us' || state.general.geoType === 'us-county' ? 'active' : ''}
1373
+ className={`${
1374
+ state.general.geoType === 'us' || state.general.geoType === 'us-county' ? 'active' : ''
1375
+ } full-width`}
1613
1376
  onClick={e => {
1614
1377
  e.preventDefault()
1615
1378
  handleEditorChanges('geoType', 'us')
@@ -1619,7 +1382,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1619
1382
  <span>United States</span>
1620
1383
  </button>
1621
1384
  <button
1622
- className={state.general.geoType === 'us-region' ? 'active' : ''}
1385
+ className={`${state.general.geoType === 'us-region' ? 'active' : ''} full-width`}
1623
1386
  onClick={e => {
1624
1387
  e.preventDefault()
1625
1388
  handleEditorChanges('geoType', 'us-region')
@@ -1629,7 +1392,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1629
1392
  <span>U.S. Region</span>
1630
1393
  </button>
1631
1394
  <button
1632
- className={state.general.geoType === 'world' ? 'active' : ''}
1395
+ className={`${state.general.geoType === 'world' ? 'active' : ''} full-width`}
1633
1396
  onClick={e => {
1634
1397
  e.preventDefault()
1635
1398
  handleEditorChanges('geoType', 'world')
@@ -1639,7 +1402,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1639
1402
  <span>World</span>
1640
1403
  </button>
1641
1404
  <button
1642
- className={state.general.geoType === 'single-state' ? 'active' : ''}
1405
+ className={`${state.general.geoType === 'single-state' ? 'active' : ''} full-width`}
1643
1406
  onClick={e => {
1644
1407
  e.preventDefault()
1645
1408
  handleEditorChanges('geoType', 'single-state')
@@ -1652,37 +1415,35 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1652
1415
  </label>
1653
1416
  {/* Select > State or County Map */}
1654
1417
  {(state.general.geoType === 'us' || state.general.geoType === 'us-county') && (
1655
- <label>
1656
- <span className='edit-label column-heading'>Geography Subtype</span>
1657
- <select
1658
- value={state.general.geoType}
1659
- onChange={event => {
1660
- handleEditorChanges('geoType', event.target.value)
1661
- }}
1662
- >
1663
- <option value='us'>US State-Level</option>
1664
- <option value='us-county'>US County-Level</option>
1665
- </select>
1666
- </label>
1418
+ <Select
1419
+ label='Geography Subtype'
1420
+ value={state.general.geoType}
1421
+ options={[
1422
+ { value: 'us', label: 'US State-Level' },
1423
+ { value: 'us-county', label: 'US County-Level' }
1424
+ ]}
1425
+ onChange={event => {
1426
+ handleEditorChanges('geoType', event.target.value)
1427
+ }}
1428
+ />
1667
1429
  )}
1668
1430
  {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1669
- <label>
1670
- <span className='edit-label column-heading'>County Census Year</span>
1671
- <select
1672
- value={state.general.countyCensusYear || '2019'}
1673
- onChange={event => {
1674
- handleEditorChanges('countyCensusYear', event.target.value)
1675
- }}
1676
- >
1677
- <option value='2022'>2022</option>
1678
- <option value='2021'>2021</option>
1679
- <option value='2020'>2020</option>
1680
- <option value='2019'>2019</option>
1681
- <option value='2015'>2015</option>
1682
- <option value='2014'>2014</option>
1683
- <option value='2013'>2013</option>
1684
- </select>
1685
- </label>
1431
+ <Select
1432
+ label='County Census Year'
1433
+ value={state.general.countyCensusYear || '2019'}
1434
+ options={[
1435
+ { value: '2022', label: '2022' },
1436
+ { value: '2021', label: '2021' },
1437
+ { value: '2020', label: '2020' },
1438
+ { value: '2019', label: '2019' },
1439
+ { value: '2015', label: '2015' },
1440
+ { value: '2014', label: '2014' },
1441
+ { value: '2013', label: '2013' }
1442
+ ]}
1443
+ onChange={event => {
1444
+ handleEditorChanges('countyCensusYear', event.target.value)
1445
+ }}
1446
+ />
1686
1447
  )}
1687
1448
  {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1688
1449
  <label>
@@ -1730,36 +1491,52 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1730
1491
  </label>
1731
1492
  )}
1732
1493
  {/* Type */}
1733
- <label>
1734
- <span className='edit-label column-heading'>
1735
- Map Type
1736
- <Tooltip style={{ textTransform: 'none' }}>
1737
- <Tooltip.Target>
1738
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1739
- </Tooltip.Target>
1740
- <Tooltip.Content>
1741
- <p>
1742
- Select "Data" to create a color-coded data map. To create a navigation-only map, select
1743
- "Navigation."
1744
- </p>
1745
- </Tooltip.Content>
1746
- </Tooltip>
1747
- </span>
1748
- <select
1749
- value={state.general.type}
1494
+ <Select
1495
+ label={
1496
+ <>
1497
+ Map Type
1498
+ <Tooltip style={{ textTransform: 'none' }}>
1499
+ <Tooltip.Target>
1500
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1501
+ </Tooltip.Target>
1502
+ <Tooltip.Content>
1503
+ <p>
1504
+ Select "Data" to create a color-coded data map. To create a navigation-only map, select
1505
+ "Navigation."
1506
+ </p>
1507
+ </Tooltip.Content>
1508
+ </Tooltip>
1509
+ </>
1510
+ }
1511
+ value={state.general.type}
1512
+ options={[
1513
+ { 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'
1518
+ ? [{ value: 'bubble', label: 'Bubble' }]
1519
+ : [])
1520
+ ]}
1521
+ onChange={event => {
1522
+ handleEditorChanges('editorMapType', event.target.value)
1523
+ }}
1524
+ />
1525
+
1526
+ {/* Navigation Behavior */}
1527
+ {(state.general.type === 'navigation' || state.general.type === 'data') && (
1528
+ <Select
1529
+ label='Navigation Behavior'
1530
+ value={state.general.navigationTarget}
1531
+ options={[
1532
+ { value: '_self', label: 'Same Window' },
1533
+ { value: '_blank', label: 'New Window' }
1534
+ ]}
1750
1535
  onChange={event => {
1751
- handleEditorChanges('editorMapType', event.target.value)
1536
+ handleEditorChanges('navigationTarget', event.target.value)
1752
1537
  }}
1753
- >
1754
- <option value='data'>Data</option>
1755
- {state.general.geoType === 'us-county' && <option value='us-geocode'>Geocode</option>}
1756
- {state.general.geoType === 'world' && <option value='world-geocode'>Geocode</option>}
1757
- {state.general.geoType !== 'us-county' && <option value='navigation'>Navigation</option>}
1758
- {(state.general.geoType === 'world' || state.general.geoType === 'us') && (
1759
- <option value='bubble'>Bubble</option>
1760
- )}
1761
- </select>
1762
- </label>
1538
+ />
1539
+ )}
1763
1540
  <label>
1764
1541
  <span className='edit-label'>Data Classification Type</span>
1765
1542
  <div>
@@ -1793,17 +1570,37 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1793
1570
  {'us' === state.general.geoType &&
1794
1571
  'bubble' !== state.general.type &&
1795
1572
  false === state.general.displayAsHex && (
1796
- <label className='checkbox'>
1797
- <input
1798
- type='checkbox'
1799
- checked={state.general.displayStateLabels}
1800
- onChange={event => {
1801
- handleEditorChanges('displayStateLabels', event.target.checked)
1802
- }}
1803
- />
1804
- <span className='edit-label'>Show state labels</span>
1805
- </label>
1573
+ <CheckBox
1574
+ label='Show state labels'
1575
+ checked={state.general.displayStateLabels}
1576
+ onChange={event => {
1577
+ handleEditorChanges('displayStateLabels', event.target.checked)
1578
+ }}
1579
+ tooltip={
1580
+ <Tooltip style={{ textTransform: 'none' }}>
1581
+ <Tooltip.Target>
1582
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1583
+ </Tooltip.Target>
1584
+ <Tooltip.Content>
1585
+ <p>Recommended set to display for Section 508 compliance.</p>
1586
+ </Tooltip.Content>
1587
+ </Tooltip>
1588
+ }
1589
+ />
1806
1590
  )}
1591
+
1592
+ {'us' === state.general.geoType && (
1593
+ <label className='checkbox'>
1594
+ <input
1595
+ type='checkbox'
1596
+ checked={general.territoriesAlwaysShow || false}
1597
+ onChange={event => {
1598
+ handleEditorChanges('territoriesAlwaysShow', event.target.checked)
1599
+ }}
1600
+ />
1601
+ <span className='edit-label'>Show All Territories</span>
1602
+ </label>
1603
+ )}
1807
1604
  </AccordionItemPanel>
1808
1605
  </AccordionItem>
1809
1606
  <AccordionItem>
@@ -1919,28 +1716,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1919
1716
  </Tooltip>
1920
1717
  }
1921
1718
  />
1922
- {'us' === state.general.geoType && (
1923
- <TextField
1924
- value={general.territoriesLabel}
1925
- updateField={updateField}
1926
- section='general'
1927
- fieldName='territoriesLabel'
1928
- label='Territories Label'
1929
- placeholder='Territories'
1930
- />
1931
- )}
1932
- {'us' === state.general.geoType && (
1933
- <label className='checkbox'>
1934
- <input
1935
- type='checkbox'
1936
- checked={general.territoriesAlwaysShow || false}
1937
- onChange={event => {
1938
- handleEditorChanges('territoriesAlwaysShow', event.target.checked)
1939
- }}
1940
- />
1941
- <span className='edit-label'>Show All Territories</span>
1942
- </label>
1943
- )}
1719
+
1944
1720
  {/* <label className="checkbox mt-4">
1945
1721
  <input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
1946
1722
  <span className="edit-label">Enable Media Download</span>
@@ -1970,14 +1746,13 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
1970
1746
  </Tooltip.Content>
1971
1747
  </Tooltip>
1972
1748
  </span>
1973
- <select
1749
+ <Select
1974
1750
  value={state.columns.geo ? state.columns.geo.name : columnsOptions[0]}
1751
+ options={columnsOptions.map(c => c.key)}
1975
1752
  onChange={event => {
1976
1753
  editColumn('geo', 'name', event.target.value)
1977
1754
  }}
1978
- >
1979
- {columnsOptions}
1980
- </select>
1755
+ />
1981
1756
  </label>
1982
1757
  {state.general.type === 'us-geocode' && (
1983
1758
  <label className='checkbox'>
@@ -2029,9 +1804,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2029
1804
  </fieldset>
2030
1805
  {'navigation' !== state.general.type && (
2031
1806
  <fieldset className='primary-fieldset edit-block'>
2032
- <label>
2033
- <span className='edit-label column-heading'>
2034
- Data Column
1807
+ <Select
1808
+ label='Data Column'
1809
+ value={columns.primary.name}
1810
+ options={columnsOptions.map(c => c.key)}
1811
+ onChange={event => {
1812
+ const _state = _.cloneDeep(state)
1813
+ _state.columns.primary.name = event.target.value
1814
+ _state.columns.primary.label = event.target.value
1815
+ setState(_state)
1816
+ }}
1817
+ tooltip={
2035
1818
  <Tooltip style={{ textTransform: 'none' }}>
2036
1819
  <Tooltip.Target>
2037
1820
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
@@ -2040,16 +1823,8 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2040
1823
  <p>Select the source column containing the categorical or numeric values to be mapped.</p>
2041
1824
  </Tooltip.Content>
2042
1825
  </Tooltip>
2043
- </span>
2044
- <select
2045
- value={state.columns.primary ? state.columns.primary.name : columnsOptions[0]}
2046
- onChange={event => {
2047
- editColumn('primary', 'name', event.target.value)
2048
- }}
2049
- >
2050
- {columnsOptions}
2051
- </select>
2052
- </label>
1826
+ }
1827
+ />
2053
1828
  <label className='checkbox'>
2054
1829
  <input
2055
1830
  type='checkbox'
@@ -2174,24 +1949,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2174
1949
  )}
2175
1950
  {
2176
1951
  <>
2177
- <label>Latitude Column</label>
2178
- <select
2179
- value={state.columns.latitude.name ? state.columns.latitude.name : ''}
1952
+ <Select
1953
+ label='Latitude Column'
1954
+ value={state.columns.latitude.name}
1955
+ options={columnsOptions.map(c => c.key)}
2180
1956
  onChange={e => {
2181
1957
  editColumn('latitude', 'name', e.target.value)
2182
1958
  }}
2183
- >
2184
- {columnsOptions}
2185
- </select>
2186
- <label>Longitude Column</label>
2187
- <select
2188
- value={state.columns.longitude.name ? state.columns.longitude.name : ''}
1959
+ />
1960
+ <Select
1961
+ label='Longitude Column'
1962
+ value={state.columns.longitude.name}
1963
+ options={columnsOptions.map(c => c.key)}
2189
1964
  onChange={e => {
2190
1965
  editColumn('longitude', 'name', e.target.value)
2191
1966
  }}
2192
- >
2193
- {columnsOptions}
2194
- </select>
1967
+ />
2195
1968
  </>
2196
1969
  }
2197
1970
 
@@ -2309,14 +2082,13 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2309
2082
  </Tooltip.Content>
2310
2083
  </Tooltip>
2311
2084
  </span>
2312
- <select
2313
- value={state.columns.navigate ? state.columns.navigate.name : columnsOptions[0]}
2085
+ <Select
2086
+ value={state.columns.navigate ? state.columns.navigate.name : ''}
2087
+ options={columnsOptions.map(c => c.key)}
2314
2088
  onChange={event => {
2315
2089
  editColumn('navigate', 'name', event.target.value)
2316
2090
  }}
2317
- >
2318
- {columnsOptions}
2319
- </select>
2091
+ />
2320
2092
  </label>
2321
2093
  {'navigation' !== state.general.type && (
2322
2094
  <fieldset className='primary-fieldset edit-block'>
@@ -2514,18 +2286,17 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2514
2286
  </AccordionItemHeading>
2515
2287
  <AccordionItemPanel>
2516
2288
  {(state.legend.type === 'equalnumber' || state.legend.type === 'equalinterval') && (
2517
- <label>
2518
- <span className='edit-label'>Legend Type</span>
2519
- <select
2520
- value={legend.type}
2521
- onChange={event => {
2522
- handleEditorChanges('legendType', event.target.value)
2523
- }}
2524
- >
2525
- <option value='equalnumber'>Equal Number (Quantiles)</option>
2526
- <option value='equalinterval'>Equal Interval</option>
2527
- </select>
2528
- </label>
2289
+ <Select
2290
+ label='Legend Type'
2291
+ value={legend.type}
2292
+ options={[
2293
+ { value: 'equalnumber', label: 'Equal Number (Quantiles)' },
2294
+ { value: 'equalinterval', label: 'Equal Interval' }
2295
+ ]}
2296
+ onChange={event => {
2297
+ handleEditorChanges('legendType', event.target.value)
2298
+ }}
2299
+ />
2529
2300
  )}
2530
2301
  {'navigation' !== state.general.type && (
2531
2302
  <label className='checkbox'>
@@ -2541,19 +2312,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2541
2312
  )}
2542
2313
  {'navigation' !== state.general.type && (
2543
2314
  <>
2544
- <label>
2545
- <span className='edit-label'>Legend Position</span>
2546
- <select
2547
- value={legend.position || false}
2548
- onChange={event => {
2549
- handleEditorChanges('sidebarPosition', event.target.value)
2550
- }}
2551
- >
2552
- <option value='side'>Side</option>
2553
- <option value='bottom'>Bottom</option>
2554
- <option value='top'>Top</option>
2555
- </select>
2556
- </label>
2315
+ <Select
2316
+ label='Legend Position'
2317
+ value={legend.position || ''}
2318
+ options={[
2319
+ { value: 'side', label: 'Side' },
2320
+ { value: 'bottom', label: 'Bottom' },
2321
+ { value: 'top', label: 'Top' }
2322
+ ]}
2323
+ onChange={event => {
2324
+ handleEditorChanges('sidebarPosition', event.target.value)
2325
+ }}
2326
+ />
2557
2327
  {(state.legend.position === 'side' || !state.legend.position) &&
2558
2328
  state.legend.style === 'gradient' && (
2559
2329
  <span style={{ color: 'red', fontSize: '14px' }}>
@@ -2563,36 +2333,36 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2563
2333
  </>
2564
2334
  )}
2565
2335
  {'navigation' !== state.general.type && (
2566
- <label>
2567
- <span className='edit-label column-heading'>
2568
- Legend Style
2569
- <Tooltip style={{ textTransform: 'none' }}>
2570
- <Tooltip.Target>
2571
- <Icon
2572
- display='question'
2573
- style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2574
- />
2575
- </Tooltip.Target>
2576
- <Tooltip.Content>
2577
- <p>
2578
- If using gradient style, limit the legend to five items for better mobile visibility, and
2579
- position the legend at the top or bottom.
2580
- </p>
2581
- </Tooltip.Content>
2582
- </Tooltip>
2583
- </span>
2584
-
2585
- <select
2586
- value={legend.style || ''}
2587
- onChange={event => {
2588
- handleEditorChanges('legendStyle', event.target.value)
2589
- }}
2590
- >
2591
- <option value='circles'>circles</option>
2592
- <option value='boxes'>boxes</option>
2593
- {legend.position !== 'side' && <option value='gradient'>gradient</option>}
2594
- </select>
2595
- </label>
2336
+ <Select
2337
+ label={
2338
+ <>
2339
+ Legend Style
2340
+ <Tooltip style={{ textTransform: 'none' }}>
2341
+ <Tooltip.Target>
2342
+ <Icon
2343
+ display='question'
2344
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2345
+ />
2346
+ </Tooltip.Target>
2347
+ <Tooltip.Content>
2348
+ <p>
2349
+ If using gradient style, limit the legend to five items for better mobile visibility, and
2350
+ position the legend at the top or bottom.
2351
+ </p>
2352
+ </Tooltip.Content>
2353
+ </Tooltip>
2354
+ </>
2355
+ }
2356
+ value={legend.style || ''}
2357
+ options={[
2358
+ { value: 'circles', label: 'circles' },
2359
+ { value: 'boxes', label: 'boxes' },
2360
+ ...(legend.position !== 'side' ? [{ value: 'gradient', label: 'gradient' }] : [])
2361
+ ]}
2362
+ onChange={event => {
2363
+ handleEditorChanges('legendStyle', event.target.value)
2364
+ }}
2365
+ />
2596
2366
  )}
2597
2367
  {'navigation' !== state.general.type && state.legend.style === 'gradient' && (
2598
2368
  <label>
@@ -2742,36 +2512,32 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2742
2512
  </label>
2743
2513
  )}
2744
2514
  {'category' !== legend.type && (
2745
- <label>
2746
- <span className='edit-label'>
2747
- Number of Items
2748
- <Tooltip style={{ textTransform: 'none' }}>
2749
- <Tooltip.Target>
2750
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2751
- </Tooltip.Target>
2752
- <Tooltip.Content>
2753
- <p>
2754
- For numeric maps, select the number of data classes. Do not include designated special
2755
- classes.
2756
- </p>
2757
- </Tooltip.Content>
2758
- </Tooltip>
2759
- </span>
2760
- <select
2761
- value={legend.numberOfItems}
2762
- onChange={event => {
2763
- handleEditorChanges('legendNumber', event.target.value)
2764
- }}
2765
- >
2766
- {[...Array(numberOfItemsLimit).keys()].map(num => {
2767
- return (
2768
- <option value={num + 1} key={num + 1}>
2769
- {num + 1}
2770
- </option>
2771
- )
2772
- })}
2773
- </select>
2774
- </label>
2515
+ <Select
2516
+ label={
2517
+ <>
2518
+ Number of Items
2519
+ <Tooltip style={{ textTransform: 'none' }}>
2520
+ <Tooltip.Target>
2521
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2522
+ </Tooltip.Target>
2523
+ <Tooltip.Content>
2524
+ <p>
2525
+ For numeric maps, select the number of data classes. Do not include designated special
2526
+ classes.
2527
+ </p>
2528
+ </Tooltip.Content>
2529
+ </Tooltip>
2530
+ </>
2531
+ }
2532
+ value={legend.numberOfItems}
2533
+ options={[...Array(numberOfItemsLimit).keys()].map(num => ({
2534
+ value: num + 1,
2535
+ label: num + 1
2536
+ }))}
2537
+ onChange={event => {
2538
+ handleEditorChanges('legendNumber', event.target.value)
2539
+ }}
2540
+ />
2775
2541
  )}
2776
2542
  {'category' === legend.type && (
2777
2543
  <React.Fragment>
@@ -2857,7 +2623,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2857
2623
  </label>
2858
2624
  </React.Fragment>
2859
2625
  )}
2860
- {filtersJSX.length > 0 && (
2626
+ {state.filters.length > 0 && (
2861
2627
  <label className='checkbox'>
2862
2628
  <input
2863
2629
  type='checkbox'
@@ -2885,7 +2651,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2885
2651
  </span>
2886
2652
  </label>
2887
2653
  )}
2888
- {(filtersJSX.length > 0 || state.general.type === 'bubble' || state.general.geoType === 'us') && (
2654
+ {(state.filters.length > 0 || state.general.type === 'bubble' || state.general.geoType === 'us') && (
2889
2655
  <label className='checkbox'>
2890
2656
  <input
2891
2657
  type='checkbox'
@@ -2922,20 +2688,7 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
2922
2688
  <AccordionItemButton>Filters</AccordionItemButton>
2923
2689
  </AccordionItemHeading>
2924
2690
  <AccordionItemPanel>
2925
- {filtersJSX.length > 0 ? (
2926
- <MapFilters />
2927
- ) : (
2928
- <p style={{ textAlign: 'center' }}>There are currently no filters.</p>
2929
- )}
2930
- <button
2931
- className={'btn btn-primary full-width'}
2932
- onClick={event => {
2933
- event.preventDefault()
2934
- changeFilter(null, 'addNew')
2935
- }}
2936
- >
2937
- Add Filter
2938
- </button>
2691
+ <VizFilterEditor config={state} updateField={updateField} rawData={state.data} />
2939
2692
  </AccordionItemPanel>
2940
2693
  </AccordionItem>
2941
2694
  )}
@@ -3563,6 +3316,18 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
3563
3316
  updateField={updateField}
3564
3317
  />
3565
3318
  </label>
3319
+ {/* Leaflet Map Type */}
3320
+ {state.general.geoType === 'leaflet' && (
3321
+ <>
3322
+ <Select
3323
+ label='Leaflet Theme'
3324
+ options={layerOptions}
3325
+ section={'leaflet'}
3326
+ fieldName='theme'
3327
+ updateField={updateField}
3328
+ />
3329
+ </>
3330
+ )}
3566
3331
  </AccordionItemPanel>
3567
3332
  </AccordionItem>
3568
3333
  <AccordionItem>