@cdc/map 4.24.2 → 4.24.4

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 (38) hide show
  1. package/dist/cdcmap.js +33089 -32197
  2. package/examples/508.json +548 -0
  3. package/examples/default-county.json +0 -28
  4. package/examples/default-hex.json +110 -13
  5. package/examples/default-usa.json +69 -28
  6. package/examples/example-city-state.json +51 -17
  7. package/examples/hex-colors.json +507 -0
  8. package/examples/usa-special-class-legend.json +501 -0
  9. package/index.html +10 -9
  10. package/package.json +3 -3
  11. package/src/CdcMap.tsx +200 -125
  12. package/src/components/BubbleList.jsx +16 -6
  13. package/src/components/CityList.jsx +64 -12
  14. package/src/components/DataTable.jsx +7 -7
  15. package/src/components/EditorPanel/components/EditorPanel.tsx +1457 -1367
  16. package/src/components/EditorPanel/components/Error.tsx +12 -0
  17. package/src/components/EditorPanel/components/HexShapeSettings.tsx +16 -1
  18. package/src/components/EditorPanel/components/Panel.PatternSettings-style.css +5 -0
  19. package/src/components/EditorPanel/components/Panel.PatternSettings.tsx +18 -1
  20. package/src/components/Legend/components/Legend.tsx +80 -15
  21. package/src/components/Legend/components/LegendItem.Hex.tsx +1 -1
  22. package/src/components/Legend/components/index.scss +31 -5
  23. package/src/components/UsaMap/components/HexIcon.tsx +41 -0
  24. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +38 -19
  25. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +11 -22
  26. package/src/components/UsaMap/components/UsaMap.County.tsx +7 -4
  27. package/src/components/UsaMap/components/UsaMap.Region.tsx +13 -38
  28. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +5 -3
  29. package/src/components/UsaMap/components/UsaMap.State.tsx +77 -60
  30. package/src/components/UsaMap/helpers/patternSizes.tsx +5 -0
  31. package/src/components/WorldMap/components/WorldMap.jsx +20 -3
  32. package/src/data/initial-state.js +3 -0
  33. package/src/data/supported-geos.js +2 -1
  34. package/src/hooks/useMapLayers.tsx +2 -2
  35. package/src/scss/editor-panel.scss +4 -12
  36. package/src/scss/main.scss +3 -1
  37. package/src/scss/map.scss +1 -1
  38. package/src/types/MapConfig.ts +7 -0
@@ -7,6 +7,7 @@ import { useDebounce } from 'use-debounce'
7
7
  // import ReactTags from 'react-tag-autocomplete'
8
8
  import { Tooltip as ReactTooltip } from 'react-tooltip'
9
9
  import Panels from './Panels.tsx'
10
+ import Layout from '@cdc/core/components/Layout'
10
11
 
11
12
  // Data
12
13
  import colorPalettes from '@cdc/core/data/colorPalettes'
@@ -18,7 +19,6 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
18
19
  import Icon from '@cdc/core/components/ui/Icon'
19
20
  import InputToggle from '@cdc/core/components/inputs/InputToggle'
20
21
  import Tooltip from '@cdc/core/components/ui/Tooltip'
21
- import Waiting from '@cdc/core/components/Waiting'
22
22
 
23
23
  // Assets
24
24
  import UsaGraphic from '@cdc/core/assets/icon-map-usa.svg'
@@ -38,7 +38,7 @@ import { MapContext } from '../../../types/MapContext.js'
38
38
  import { Checkbox, TextField } from './Inputs'
39
39
 
40
40
  // Todo: move to useReducer, seperate files out.
41
- const EditorPanel = props => {
41
+ const EditorPanel = ({ columnsRequiredChecker }) => {
42
42
  // prettier-ignore
43
43
  const {
44
44
  changeFilterActive,
@@ -53,12 +53,11 @@ const EditorPanel = props => {
53
53
  setRuntimeFilters,
54
54
  setState,
55
55
  state,
56
+ tooltipId
56
57
  } = useContext<MapContext>(ConfigContext)
57
58
 
58
59
  const { general, columns, legend, table, tooltips } = state
59
60
 
60
- const [requiredColumns, setRequiredColumns] = useState(null) // Simple state so we know if we need more information before parsing the map
61
-
62
61
  const [configTextboxValue, setConfigTextbox] = useState({}) // eslint-disable-line
63
62
 
64
63
  const [loadedDefault, setLoadedDefault] = useState(false)
@@ -78,7 +77,7 @@ const EditorPanel = props => {
78
77
  handleAddLayer,
79
78
  handleRemoveLayer
80
79
  }
81
- } = useMapLayers(state, setState, false, true)
80
+ } = useMapLayers(state, setState, false, tooltipId)
82
81
 
83
82
  const categoryMove = (idx1, idx2) => {
84
83
  let categoryValuesOrder = [...state.legend.categoryValuesOrder]
@@ -117,6 +116,75 @@ const EditorPanel = props => {
117
116
  specialClasses = legend.specialClasses || []
118
117
  }
119
118
 
119
+ const getCityStyleOptions = target => {
120
+ switch (target) {
121
+ case 'value': {
122
+ const values = ['Circle', 'Square', 'Triangle', 'Diamond', 'Star', 'Pin']
123
+ const filteredValues = values.filter(val => String(state.visual.cityStyle).toLocaleLowerCase() !== val.toLocaleLowerCase())
124
+
125
+ return (
126
+ <>
127
+ <option value='' key={'Select Option'}>
128
+ - Select Option -
129
+ </option>
130
+ {filteredValues.map((val, i) => {
131
+ return (
132
+ <option key={i} value={val}>
133
+ {val}
134
+ </option>
135
+ )
136
+ })}
137
+ </>
138
+ )
139
+ }
140
+ }
141
+ }
142
+
143
+ const editCityStyles = (target, index, fieldName, value) => {
144
+ switch (target) {
145
+ case 'add': {
146
+ const additionalCityStyles = state.visual.additionalCityStyles ? [...state.visual.additionalCityStyles] : []
147
+ additionalCityStyles.push({ label: '', column: '', value: '', shape: '' })
148
+ setState({
149
+ ...state,
150
+ visual: {
151
+ ...state.visual,
152
+ additionalCityStyles: additionalCityStyles
153
+ }
154
+ })
155
+ break
156
+ }
157
+ case 'remove': {
158
+ let additionalCityStyles = []
159
+ if (state.visual.additionalCityStyles) {
160
+ additionalCityStyles = [...state.visual.additionalCityStyles]
161
+ }
162
+
163
+ additionalCityStyles.splice(index, 1)
164
+ setState({
165
+ ...state,
166
+ visual: {
167
+ ...state.visual,
168
+ additionalCityStyles: additionalCityStyles
169
+ }
170
+ })
171
+ break
172
+ }
173
+ case 'update': {
174
+ let additionalCityStyles = []
175
+ additionalCityStyles = [...state.visual.additionalCityStyles]
176
+ additionalCityStyles[index][fieldName] = value
177
+ setState({
178
+ ...state,
179
+ visual: {
180
+ ...state.visual,
181
+ additionalCityStyles: additionalCityStyles
182
+ }
183
+ })
184
+ }
185
+ }
186
+ }
187
+
120
188
  const DynamicDesc = ({ label, fieldName, value: stateValue, type = 'input', ...attributes }) => {
121
189
  const [value, setValue] = useState(stateValue)
122
190
 
@@ -379,20 +447,6 @@ const EditorPanel = props => {
379
447
  }
380
448
  })
381
449
  break
382
- case 'toggleDownloadButton':
383
- setState({
384
- ...state,
385
- general: {
386
- ...state.general,
387
- showDownloadButton: !state.general.showDownloadButton
388
- },
389
- table: {
390
- // setting both bc DataTable new core needs it here
391
- ...state.table,
392
- download: !state.general.showDownloadButton
393
- }
394
- })
395
- break
396
450
  case 'toggleShowFullGeoNameInCSV':
397
451
  setState({
398
452
  ...state,
@@ -767,37 +821,6 @@ const EditorPanel = props => {
767
821
  }
768
822
  }
769
823
 
770
- const columnsRequiredChecker = useCallback(() => {
771
- let columnList = []
772
-
773
- // Geo is always required
774
- if ('' === state.columns.geo.name) {
775
- columnList.push('Geography')
776
- }
777
-
778
- // Primary is required if we're on a data map or a point map
779
- if ('navigation' !== state.general.type && '' === state.columns.primary.name) {
780
- columnList.push('Primary')
781
- }
782
-
783
- // Navigate is required for navigation maps
784
- if ('navigation' === state.general.type && ('' === state.columns.navigate.name || undefined === state.columns.navigate)) {
785
- columnList.push('Navigation')
786
- }
787
-
788
- if (('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && '' === state.columns.latitude.name) {
789
- columnList.push('Latitude')
790
- }
791
-
792
- if (('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && '' === state.columns.longitude.name) {
793
- columnList.push('Longitude')
794
- }
795
-
796
- if (columnList.length === 0) columnList = null
797
-
798
- setRequiredColumns(columnList)
799
- }, [state.columns, state.general.type])
800
-
801
824
  const editColumn = async (columnName, editTarget, value) => {
802
825
  let newSpecialClasses
803
826
  switch (editTarget) {
@@ -1195,6 +1218,10 @@ const EditorPanel = props => {
1195
1218
 
1196
1219
  const onBackClick = () => {
1197
1220
  setDisplayPanel(!displayPanel)
1221
+ setState({
1222
+ ...state,
1223
+ showEditorPanel: !displayPanel
1224
+ })
1198
1225
  }
1199
1226
 
1200
1227
  const usedFilterColumns = {}
@@ -1289,6 +1316,8 @@ const EditorPanel = props => {
1289
1316
  </select>
1290
1317
  </label>
1291
1318
 
1319
+ <TextField value={state.filters[index].setByQueryParameter} section='filters' subsection={index} fieldName='setByQueryParameter' label='Default Value Set By Query String Parameter' updateField={updateField} />
1320
+
1292
1321
  {filter.order === 'cust' && (
1293
1322
  <DragDropContext onDragEnd={({ source, destination }) => handleFilterOrder(source.index, destination.index, index, state.filters[index])}>
1294
1323
  <Droppable droppableId='filter_order'>
@@ -1386,17 +1415,6 @@ const EditorPanel = props => {
1386
1415
  )
1387
1416
  }
1388
1417
 
1389
- const Error = () => {
1390
- return (
1391
- <section className='waiting'>
1392
- <section className='waiting-container'>
1393
- <h3>Error With Configuration</h3>
1394
- <p>{state.runtime.editorErrorMessage}</p>
1395
- </section>
1396
- </section>
1397
- )
1398
- }
1399
-
1400
1418
  const isLoadedFromUrl = state?.dataKey?.includes('http://') || state?.dataKey?.includes('https://')
1401
1419
 
1402
1420
  // if isDebug = true, then try to set the Geography Col and Data col to reduce clicking
@@ -1417,382 +1435,428 @@ const EditorPanel = props => {
1417
1435
 
1418
1436
  return (
1419
1437
  <ErrorBoundary component='EditorPanel'>
1420
- {state?.runtime?.editorErrorMessage.length > 0 && <Error />}
1421
- {requiredColumns && <Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />}
1422
- <button className={displayPanel ? `editor-toggle` : `editor-toggle collapsed`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick} data-html2canvas-ignore></button>
1423
-
1424
- <section className={displayPanel ? 'editor-panel cove' : 'hidden editor-panel cove'} data-html2canvas-ignore>
1438
+ <Layout.Sidebar isDashboard={isDashboard} displayPanel={displayPanel} title='Configure Map' onBackClick={onBackClick}>
1425
1439
  <ReactTooltip multiline={true} />
1426
- <span className='base-label'>Configure Map</span>
1427
- <section className='form-container'>
1428
- <Accordion allowZeroExpanded={true}>
1429
- <AccordionItem>
1430
- {' '}
1440
+ <Accordion allowZeroExpanded={true}>
1441
+ <AccordionItem>
1442
+ {' '}
1443
+ {/* Type */}
1444
+ <AccordionItemHeading>
1445
+ <AccordionItemButton>Type</AccordionItemButton>
1446
+ </AccordionItemHeading>
1447
+ <AccordionItemPanel>
1448
+ {/* Geography */}
1449
+ <label>
1450
+ <span className='edit-label column-heading'>
1451
+ <span>Geography</span>
1452
+ </span>
1453
+ <ul className='geo-buttons'>
1454
+ <button
1455
+ className={state.general.geoType === 'us' || state.general.geoType === 'us-county' ? 'active' : ''}
1456
+ onClick={e => {
1457
+ e.preventDefault()
1458
+ handleEditorChanges('geoType', 'us')
1459
+ }}
1460
+ >
1461
+ <UsaGraphic />
1462
+ <span>United States</span>
1463
+ </button>
1464
+ <button
1465
+ className={state.general.geoType === 'us-region' ? 'active' : ''}
1466
+ onClick={e => {
1467
+ e.preventDefault()
1468
+ handleEditorChanges('geoType', 'us-region')
1469
+ }}
1470
+ >
1471
+ <UsaRegionGraphic />
1472
+ <span>U.S. Region</span>
1473
+ </button>
1474
+ <button
1475
+ className={state.general.geoType === 'world' ? 'active' : ''}
1476
+ onClick={e => {
1477
+ e.preventDefault()
1478
+ handleEditorChanges('geoType', 'world')
1479
+ }}
1480
+ >
1481
+ <WorldGraphic />
1482
+ <span>World</span>
1483
+ </button>
1484
+ <button
1485
+ className={state.general.geoType === 'single-state' ? 'active' : ''}
1486
+ onClick={e => {
1487
+ e.preventDefault()
1488
+ handleEditorChanges('geoType', 'single-state')
1489
+ }}
1490
+ >
1491
+ <AlabamaGraphic />
1492
+ <span>U.S. State</span>
1493
+ </button>
1494
+ </ul>
1495
+ </label>
1496
+ {/* Select > State or County Map */}
1497
+ {(state.general.geoType === 'us' || state.general.geoType === 'us-county') && (
1498
+ <label>
1499
+ <span className='edit-label column-heading'>Geography Subtype</span>
1500
+ <select
1501
+ value={state.general.geoType}
1502
+ onChange={event => {
1503
+ handleEditorChanges('geoType', event.target.value)
1504
+ }}
1505
+ >
1506
+ <option value='us'>US State-Level</option>
1507
+ <option value='us-county'>US County-Level</option>
1508
+ </select>
1509
+ </label>
1510
+ )}
1511
+ {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1512
+ <label>
1513
+ <span className='edit-label column-heading'>County Census Year</span>
1514
+ <select
1515
+ value={state.general.countyCensusYear || '2019'}
1516
+ onChange={event => {
1517
+ handleEditorChanges('countyCensusYear', event.target.value)
1518
+ }}
1519
+ >
1520
+ <option value='2022'>2022</option>
1521
+ <option value='2021'>2021</option>
1522
+ <option value='2020'>2020</option>
1523
+ <option value='2019'>2019</option>
1524
+ <option value='2015'>2015</option>
1525
+ <option value='2014'>2014</option>
1526
+ <option value='2013'>2013</option>
1527
+ </select>
1528
+ </label>
1529
+ )}
1530
+ {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1531
+ <label>
1532
+ <span className='edit-label column-heading'>Filter Controlling County Census Year</span>
1533
+ <select
1534
+ value={state.general.filterControlsCountyYear || ''}
1535
+ onChange={event => {
1536
+ handleEditorChanges('filterControlsCountyYear', event.target.value)
1537
+ }}
1538
+ >
1539
+ <option value=''>None</option>
1540
+ {state.filters && state.filters.map(filter => <option>{filter.columnName}</option>)}
1541
+ </select>
1542
+ </label>
1543
+ )}
1431
1544
  {/* Type */}
1432
- <AccordionItemHeading>
1433
- <AccordionItemButton>Type</AccordionItemButton>
1434
- </AccordionItemHeading>
1435
- <AccordionItemPanel>
1436
- {/* Geography */}
1545
+ {/* Select > Filter a state */}
1546
+ {state.general.geoType === 'single-state' && (
1437
1547
  <label>
1438
- <span className='edit-label column-heading'>
1439
- <span>Geography</span>
1440
- </span>
1441
- <ul className='geo-buttons'>
1442
- <button
1443
- className={state.general.geoType === 'us' || state.general.geoType === 'us-county' ? 'active' : ''}
1444
- onClick={e => {
1445
- e.preventDefault()
1446
- handleEditorChanges('geoType', 'us')
1447
- }}
1448
- >
1449
- <UsaGraphic />
1450
- <span>United States</span>
1451
- </button>
1452
- <button
1453
- className={state.general.geoType === 'us-region' ? 'active' : ''}
1454
- onClick={e => {
1455
- e.preventDefault()
1456
- handleEditorChanges('geoType', 'us-region')
1457
- }}
1458
- >
1459
- <UsaRegionGraphic />
1460
- <span>U.S. Region</span>
1461
- </button>
1462
- <button
1463
- className={state.general.geoType === 'world' ? 'active' : ''}
1464
- onClick={e => {
1465
- e.preventDefault()
1466
- handleEditorChanges('geoType', 'world')
1467
- }}
1468
- >
1469
- <WorldGraphic />
1470
- <span>World</span>
1471
- </button>
1472
- <button
1473
- className={state.general.geoType === 'single-state' ? 'active' : ''}
1474
- onClick={e => {
1475
- e.preventDefault()
1476
- handleEditorChanges('geoType', 'single-state')
1477
- }}
1478
- >
1479
- <AlabamaGraphic />
1480
- <span>U.S. State</span>
1481
- </button>
1482
- </ul>
1548
+ <span className='edit-label column-heading'>State Selector</span>
1549
+ <select
1550
+ value={state.general.statePicked.stateName}
1551
+ onChange={event => {
1552
+ handleEditorChanges('chooseState', event.target.value)
1553
+ }}
1554
+ >
1555
+ <StateOptionList />
1556
+ </select>
1483
1557
  </label>
1484
- {/* Select > State or County Map */}
1485
- {(state.general.geoType === 'us' || state.general.geoType === 'us-county') && (
1486
- <label>
1487
- <span className='edit-label column-heading'>Geography Subtype</span>
1488
- <select
1489
- value={state.general.geoType}
1490
- onChange={event => {
1491
- handleEditorChanges('geoType', event.target.value)
1492
- }}
1493
- >
1494
- <option value='us'>US State-Level</option>
1495
- <option value='us-county'>US County-Level</option>
1496
- </select>
1497
- </label>
1498
- )}
1499
- {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1500
- <label>
1501
- <span className='edit-label column-heading'>County Census Year</span>
1502
- <select
1503
- value={state.general.countyCensusYear || '2019'}
1504
- onChange={event => {
1505
- handleEditorChanges('countyCensusYear', event.target.value)
1506
- }}
1507
- >
1508
- <option value='2022'>2022</option>
1509
- <option value='2021'>2021</option>
1510
- <option value='2020'>2020</option>
1511
- <option value='2019'>2019</option>
1512
- <option value='2015'>2015</option>
1513
- <option value='2014'>2014</option>
1514
- <option value='2013'>2013</option>
1515
- </select>
1516
- </label>
1517
- )}
1518
- {(state.general.geoType === 'us-county' || state.general.geoType === 'single-state') && (
1558
+ )}
1559
+ {/* Type */}
1560
+ <label>
1561
+ <span className='edit-label column-heading'>
1562
+ Map Type
1563
+ <Tooltip style={{ textTransform: 'none' }}>
1564
+ <Tooltip.Target>
1565
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1566
+ </Tooltip.Target>
1567
+ <Tooltip.Content>
1568
+ <p>Select "Data" to create a color-coded data map. To create a navigation-only map, select "Navigation."</p>
1569
+ </Tooltip.Content>
1570
+ </Tooltip>
1571
+ </span>
1572
+ <select
1573
+ value={state.general.type}
1574
+ onChange={event => {
1575
+ handleEditorChanges('editorMapType', event.target.value)
1576
+ }}
1577
+ >
1578
+ <option value='data'>Data</option>
1579
+ {state.general.geoType === 'us-county' && <option value='us-geocode'>Geocode</option>}
1580
+ {state.general.geoType === 'world' && <option value='world-geocode'>Geocode</option>}
1581
+ {state.general.geoType !== 'us-county' && <option value='navigation'>Navigation</option>}
1582
+ {(state.general.geoType === 'world' || state.general.geoType === 'us') && <option value='bubble'>Bubble</option>}
1583
+ </select>
1584
+ </label>
1585
+ <label>
1586
+ <span className='edit-label'>Data Classification Type</span>
1587
+ <div>
1519
1588
  <label>
1520
- <span className='edit-label column-heading'>Filter Controlling County Census Year</span>
1521
- <select
1522
- value={state.general.filterControlsCountyYear || ''}
1523
- onChange={event => {
1524
- handleEditorChanges('filterControlsCountyYear', event.target.value)
1525
- }}
1526
- >
1527
- <option value=''>None</option>
1528
- {state.filters && state.filters.map(filter => <option>{filter.columnName}</option>)}
1529
- </select>
1589
+ <input type='radio' name='equalnumber' value='equalnumber' checked={state.legend.type === 'equalnumber'} onChange={e => handleEditorChanges('classificationType', e.target.value)} />
1590
+ Numeric/Quantitative
1530
1591
  </label>
1531
- )}
1532
- {/* Type */}
1533
- {/* Select > Filter a state */}
1534
- {state.general.geoType === 'single-state' && (
1535
1592
  <label>
1536
- <span className='edit-label column-heading'>State Selector</span>
1537
- <select
1538
- value={state.general.statePicked.stateName}
1539
- onChange={event => {
1540
- handleEditorChanges('chooseState', event.target.value)
1541
- }}
1542
- >
1543
- <StateOptionList />
1544
- </select>
1593
+ <input type='radio' name='category' value='category' checked={state.legend.type === 'category'} onChange={e => handleEditorChanges('classificationType', e.target.value)} />
1594
+ Categorical
1545
1595
  </label>
1546
- )}
1547
- {/* Type */}
1596
+ </div>
1597
+ </label>
1598
+
1599
+ <HexSetting.DisplayAsHexMap state={state} setState={setState} handleEditorChanges={handleEditorChanges} />
1600
+ <HexSetting.DisplayShapesOnHex state={state} setState={setState} />
1601
+ <HexSetting.ShapeColumns state={state} setState={setState} columnsOptions={columnsOptions} />
1602
+
1603
+ {'us' === state.general.geoType && 'bubble' !== state.general.type && false === state.general.displayAsHex && (
1604
+ <label className='checkbox'>
1605
+ <input
1606
+ type='checkbox'
1607
+ checked={state.general.displayStateLabels}
1608
+ onChange={event => {
1609
+ handleEditorChanges('displayStateLabels', event.target.checked)
1610
+ }}
1611
+ />
1612
+ <span className='edit-label'>Show state labels</span>
1613
+ </label>
1614
+ )}
1615
+ </AccordionItemPanel>
1616
+ </AccordionItem>
1617
+ <AccordionItem>
1618
+ {' '}
1619
+ {/* General */}
1620
+ <AccordionItemHeading>
1621
+ <AccordionItemButton>General</AccordionItemButton>
1622
+ </AccordionItemHeading>
1623
+ <AccordionItemPanel>
1624
+ <TextField
1625
+ value={general.title}
1626
+ data-testid='title-input'
1627
+ updateField={updateField}
1628
+ section='general'
1629
+ fieldName='title'
1630
+ id='title'
1631
+ label='Title'
1632
+ placeholder='Map Title'
1633
+ tooltip={
1634
+ <Tooltip style={{ textTransform: 'none' }}>
1635
+ <Tooltip.Target>
1636
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1637
+ </Tooltip.Target>
1638
+ <Tooltip.Content>
1639
+ <p>Title is required to set the name of the download file but can be hidden using the option below.</p>
1640
+ </Tooltip.Content>
1641
+ </Tooltip>
1642
+ }
1643
+ />
1644
+ <label className='checkbox'>
1645
+ <input
1646
+ type='checkbox'
1647
+ checked={state.general.showTitle || false}
1648
+ onChange={event => {
1649
+ handleEditorChanges('showTitle', event.target.checked)
1650
+ }}
1651
+ />
1652
+ <span className='edit-label'>Show Title</span>
1653
+ </label>
1654
+ <TextField
1655
+ value={general.superTitle || ''}
1656
+ updateField={updateField}
1657
+ section='general'
1658
+ fieldName='superTitle'
1659
+ label='Super Title'
1660
+ tooltip={
1661
+ <Tooltip style={{ textTransform: 'none' }}>
1662
+ <Tooltip.Target>
1663
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1664
+ </Tooltip.Target>
1665
+ <Tooltip.Content>
1666
+ <p>Super Title</p>
1667
+ </Tooltip.Content>
1668
+ </Tooltip>
1669
+ }
1670
+ />
1671
+ <TextField
1672
+ type='textarea'
1673
+ value={general.introText}
1674
+ updateField={updateField}
1675
+ section='general'
1676
+ fieldName='introText'
1677
+ label='Message'
1678
+ tooltip={
1679
+ <Tooltip style={{ textTransform: 'none' }}>
1680
+ <Tooltip.Target>
1681
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1682
+ </Tooltip.Target>
1683
+ <Tooltip.Content>
1684
+ <p>Intro Text</p>
1685
+ </Tooltip.Content>
1686
+ </Tooltip>
1687
+ }
1688
+ />
1689
+ <TextField
1690
+ type='textarea'
1691
+ value={general.subtext}
1692
+ updateField={updateField}
1693
+ section='general'
1694
+ fieldName='subtext'
1695
+ label='Subtext'
1696
+ tooltip={
1697
+ <Tooltip style={{ textTransform: 'none' }}>
1698
+ <Tooltip.Target>
1699
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1700
+ </Tooltip.Target>
1701
+ <Tooltip.Content>
1702
+ <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
1703
+ </Tooltip.Content>
1704
+ </Tooltip>
1705
+ }
1706
+ />
1707
+ <TextField
1708
+ type='textarea'
1709
+ value={general.footnotes}
1710
+ updateField={updateField}
1711
+ section='general'
1712
+ fieldName='footnotes'
1713
+ label='Footnotes'
1714
+ tooltip={
1715
+ <Tooltip style={{ textTransform: 'none' }}>
1716
+ <Tooltip.Target>
1717
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1718
+ </Tooltip.Target>
1719
+ <Tooltip.Content>
1720
+ <p>Footnotes</p>
1721
+ </Tooltip.Content>
1722
+ </Tooltip>
1723
+ }
1724
+ />
1725
+ {'us' === state.general.geoType && <TextField value={general.territoriesLabel} updateField={updateField} section='general' fieldName='territoriesLabel' label='Territories Label' placeholder='Territories' />}
1726
+ {'us' === state.general.geoType && (
1727
+ <label className='checkbox'>
1728
+ <input
1729
+ type='checkbox'
1730
+ checked={general.territoriesAlwaysShow || false}
1731
+ onChange={event => {
1732
+ handleEditorChanges('territoriesAlwaysShow', event.target.checked)
1733
+ }}
1734
+ />
1735
+ <span className='edit-label'>Show All Territories</span>
1736
+ </label>
1737
+ )}
1738
+ {/* <label className="checkbox mt-4">
1739
+ <input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
1740
+ <span className="edit-label">Enable Media Download</span>
1741
+ </label> */}
1742
+ </AccordionItemPanel>
1743
+ </AccordionItem>
1744
+ <AccordionItem>
1745
+ {' '}
1746
+ {/* Columns */}
1747
+ <AccordionItemHeading>
1748
+ <AccordionItemButton>Columns</AccordionItemButton>
1749
+ </AccordionItemHeading>
1750
+ <AccordionItemPanel>
1751
+ <fieldset className='primary-fieldset edit-block'>
1548
1752
  <label>
1549
1753
  <span className='edit-label column-heading'>
1550
- Map Type
1754
+ Geography
1551
1755
  <Tooltip style={{ textTransform: 'none' }}>
1552
1756
  <Tooltip.Target>
1553
1757
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1554
1758
  </Tooltip.Target>
1555
1759
  <Tooltip.Content>
1556
- <p>Select "Data" to create a color-coded data map. To create a navigation-only map, select "Navigation."</p>
1760
+ <p>Select the source column containing the map location names or, for county-level maps, the FIPS codes.</p>
1557
1761
  </Tooltip.Content>
1558
1762
  </Tooltip>
1559
1763
  </span>
1560
1764
  <select
1561
- value={state.general.type}
1765
+ value={state.columns.geo ? state.columns.geo.name : columnsOptions[0]}
1562
1766
  onChange={event => {
1563
- handleEditorChanges('editorMapType', event.target.value)
1767
+ editColumn('geo', 'name', event.target.value)
1564
1768
  }}
1565
1769
  >
1566
- <option value='data'>Data</option>
1567
- {state.general.geoType === 'us-county' && <option value='us-geocode'>Geocode</option>}
1568
- {state.general.geoType === 'world' && <option value='world-geocode'>Geocode</option>}
1569
- {state.general.geoType !== 'us-county' && <option value='navigation'>Navigation</option>}
1570
- {(state.general.geoType === 'world' || state.general.geoType === 'us') && <option value='bubble'>Bubble</option>}
1770
+ {columnsOptions}
1571
1771
  </select>
1572
1772
  </label>
1573
- <label>
1574
- <span className='edit-label'>Data Classification Type</span>
1575
- <div>
1576
- <label>
1577
- <input type='radio' name='equalnumber' value='equalnumber' checked={state.legend.type === 'equalnumber'} onChange={e => handleEditorChanges('classificationType', e.target.value)} />
1578
- Numeric/Quantitative
1579
- </label>
1580
- <label>
1581
- <input type='radio' name='category' value='category' checked={state.legend.type === 'category'} onChange={e => handleEditorChanges('classificationType', e.target.value)} />
1582
- Categorical
1583
- </label>
1584
- </div>
1585
- </label>
1586
-
1587
- <HexSetting.DisplayAsHexMap state={state} setState={setState} handleEditorChanges={handleEditorChanges} />
1588
- <HexSetting.DisplayShapesOnHex state={state} setState={setState} />
1589
- <HexSetting.ShapeColumns state={state} setState={setState} columnsOptions={columnsOptions} />
1590
-
1591
- {'us' === state.general.geoType && 'bubble' !== state.general.type && false === state.general.displayAsHex && (
1773
+ {state.general.type === 'us-geocode' && (
1592
1774
  <label className='checkbox'>
1593
1775
  <input
1594
1776
  type='checkbox'
1595
- checked={state.general.displayStateLabels}
1777
+ checked={state.general.convertFipsCodes}
1596
1778
  onChange={event => {
1597
- handleEditorChanges('displayStateLabels', event.target.checked)
1779
+ setState({
1780
+ ...state,
1781
+ general: {
1782
+ ...state.general,
1783
+ convertFipsCodes: event.target.checked
1784
+ }
1785
+ })
1598
1786
  }}
1599
1787
  />
1600
- <span className='edit-label'>Show state labels</span>
1788
+ <span className='edit-label'>Convert FIPS Codes to Geography Name</span>
1601
1789
  </label>
1602
1790
  )}
1603
- </AccordionItemPanel>
1604
- </AccordionItem>
1605
- <AccordionItem>
1606
- {' '}
1607
- {/* General */}
1608
- <AccordionItemHeading>
1609
- <AccordionItemButton>General</AccordionItemButton>
1610
- </AccordionItemHeading>
1611
- <AccordionItemPanel>
1612
- <TextField
1613
- value={general.title}
1614
- data-testid='title-input'
1615
- updateField={updateField}
1616
- section='general'
1617
- fieldName='title'
1618
- id='title'
1619
- label='Title'
1620
- placeholder='Map Title'
1621
- tooltip={
1622
- <Tooltip style={{ textTransform: 'none' }}>
1623
- <Tooltip.Target>
1624
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1625
- </Tooltip.Target>
1626
- <Tooltip.Content>
1627
- <p>Title is required to set the name of the download file but can be hidden using the option below.</p>
1628
- </Tooltip.Content>
1629
- </Tooltip>
1630
- }
1631
- />
1791
+
1632
1792
  <label className='checkbox'>
1633
1793
  <input
1634
1794
  type='checkbox'
1635
- checked={state.general.showTitle || false}
1795
+ checked={state.general.hideGeoColumnInTooltip || false}
1636
1796
  onChange={event => {
1637
- handleEditorChanges('showTitle', event.target.checked)
1797
+ handleEditorChanges('hideGeoColumnInTooltip', event.target.checked)
1638
1798
  }}
1639
1799
  />
1640
- <span className='edit-label'>Show Title</span>
1800
+ <span className='edit-label'>Hide Geography Column Name in Tooltip</span>
1641
1801
  </label>
1642
1802
  <TextField
1643
- value={general.superTitle || ''}
1644
- updateField={updateField}
1645
- section='general'
1646
- fieldName='superTitle'
1647
- label='Super Title'
1648
- tooltip={
1649
- <Tooltip style={{ textTransform: 'none' }}>
1650
- <Tooltip.Target>
1651
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1652
- </Tooltip.Target>
1653
- <Tooltip.Content>
1654
- <p>Super Title</p>
1655
- </Tooltip.Content>
1656
- </Tooltip>
1657
- }
1658
- />
1659
- <TextField
1660
- type='textarea'
1661
- value={general.introText}
1662
- updateField={updateField}
1663
- section='general'
1664
- fieldName='introText'
1665
- label='Message'
1666
- tooltip={
1667
- <Tooltip style={{ textTransform: 'none' }}>
1668
- <Tooltip.Target>
1669
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1670
- </Tooltip.Target>
1671
- <Tooltip.Content>
1672
- <p>Intro Text</p>
1673
- </Tooltip.Content>
1674
- </Tooltip>
1675
- }
1676
- />
1677
- <TextField
1678
- type='textarea'
1679
- value={general.subtext}
1680
- updateField={updateField}
1803
+ value={state.general.geoLabelOverride}
1681
1804
  section='general'
1682
- fieldName='subtext'
1683
- label='Subtext'
1684
- tooltip={
1685
- <Tooltip style={{ textTransform: 'none' }}>
1686
- <Tooltip.Target>
1687
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1688
- </Tooltip.Target>
1689
- <Tooltip.Content>
1690
- <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
1691
- </Tooltip.Content>
1692
- </Tooltip>
1693
- }
1694
- />
1695
- <TextField
1696
- type='textarea'
1697
- value={general.footnotes}
1805
+ fieldName='geoLabelOverride'
1806
+ label='Geography Label'
1807
+ className='edit-label'
1698
1808
  updateField={updateField}
1699
- section='general'
1700
- fieldName='footnotes'
1701
- label='Footnotes'
1702
1809
  tooltip={
1703
1810
  <Tooltip style={{ textTransform: 'none' }}>
1704
1811
  <Tooltip.Target>
1705
1812
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1706
1813
  </Tooltip.Target>
1707
1814
  <Tooltip.Content>
1708
- <p>Footnotes</p>
1815
+ <p>Enter a geography label for use in tooltips.</p>
1709
1816
  </Tooltip.Content>
1710
1817
  </Tooltip>
1711
1818
  }
1712
1819
  />
1713
- {'us' === state.general.geoType && <TextField value={general.territoriesLabel} updateField={updateField} section='general' fieldName='territoriesLabel' label='Territories Label' placeholder='Territories' />}
1714
- {'us' === state.general.geoType && (
1715
- <label className='checkbox'>
1716
- <input
1717
- type='checkbox'
1718
- checked={general.territoriesAlwaysShow || false}
1719
- onChange={event => {
1720
- handleEditorChanges('territoriesAlwaysShow', event.target.checked)
1721
- }}
1722
- />
1723
- <span className='edit-label'>Show All Territories</span>
1724
- </label>
1725
- )}
1726
- {/* <label className="checkbox mt-4">
1727
- <input type="checkbox" checked={ state.general.showDownloadMediaButton } onChange={(event) => { handleEditorChanges("toggleDownloadMediaButton", event.target.checked) }} />
1728
- <span className="edit-label">Enable Media Download</span>
1729
- </label> */}
1730
- </AccordionItemPanel>
1731
- </AccordionItem>
1732
- <AccordionItem>
1733
- {' '}
1734
- {/* Columns */}
1735
- <AccordionItemHeading>
1736
- <AccordionItemButton>Columns</AccordionItemButton>
1737
- </AccordionItemHeading>
1738
- <AccordionItemPanel>
1820
+ </fieldset>
1821
+ {'navigation' !== state.general.type && (
1739
1822
  <fieldset className='primary-fieldset edit-block'>
1740
1823
  <label>
1741
1824
  <span className='edit-label column-heading'>
1742
- Geography
1825
+ Data Column
1743
1826
  <Tooltip style={{ textTransform: 'none' }}>
1744
1827
  <Tooltip.Target>
1745
1828
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1746
1829
  </Tooltip.Target>
1747
1830
  <Tooltip.Content>
1748
- <p>Select the source column containing the map location names or, for county-level maps, the FIPS codes.</p>
1831
+ <p>Select the source column containing the categorical or numeric values to be mapped.</p>
1749
1832
  </Tooltip.Content>
1750
1833
  </Tooltip>
1751
1834
  </span>
1752
1835
  <select
1753
- value={state.columns.geo ? state.columns.geo.name : columnsOptions[0]}
1836
+ value={state.columns.primary ? state.columns.primary.name : columnsOptions[0]}
1754
1837
  onChange={event => {
1755
- editColumn('geo', 'name', event.target.value)
1838
+ editColumn('primary', 'name', event.target.value)
1756
1839
  }}
1757
1840
  >
1758
1841
  {columnsOptions}
1759
1842
  </select>
1760
1843
  </label>
1761
- {state.general.type === 'us-geocode' && (
1762
- <label className='checkbox'>
1763
- <input
1764
- type='checkbox'
1765
- checked={state.general.convertFipsCodes}
1766
- onChange={event => {
1767
- setState({
1768
- ...state,
1769
- general: {
1770
- ...state.general,
1771
- convertFipsCodes: event.target.checked
1772
- }
1773
- })
1774
- }}
1775
- />
1776
- <span className='edit-label'>Convert FIPS Codes to Geography Name</span>
1777
- </label>
1778
- )}
1779
-
1780
1844
  <label className='checkbox'>
1781
1845
  <input
1782
1846
  type='checkbox'
1783
- checked={state.general.hideGeoColumnInTooltip || false}
1847
+ checked={state.general.hidePrimaryColumnInTooltip || false}
1784
1848
  onChange={event => {
1785
- handleEditorChanges('hideGeoColumnInTooltip', event.target.checked)
1849
+ handleEditorChanges('hidePrimaryColumnInTooltip', event.target.checked)
1786
1850
  }}
1787
1851
  />
1788
- <span className='edit-label'>Hide Geography Column Name in Tooltip</span>
1852
+ <span className='edit-label'>Hide Data Column Name in Tooltip</span>
1789
1853
  </label>
1790
1854
  <TextField
1791
- value={state.general.geoLabelOverride}
1792
- section='general'
1793
- fieldName='geoLabelOverride'
1794
- label='Geography Label'
1795
- className='edit-label'
1855
+ value={columns.primary.label}
1856
+ section='columns'
1857
+ subsection='primary'
1858
+ fieldName='label'
1859
+ label='Data Label'
1796
1860
  updateField={updateField}
1797
1861
  tooltip={
1798
1862
  <Tooltip style={{ textTransform: 'none' }}>
@@ -1800,1107 +1864,1044 @@ const EditorPanel = props => {
1800
1864
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1801
1865
  </Tooltip.Target>
1802
1866
  <Tooltip.Content>
1803
- <p>Enter a geography label for use in tooltips.</p>
1867
+ <p>Enter a data label for use in tooltips and the data table.</p>
1804
1868
  </Tooltip.Content>
1805
1869
  </Tooltip>
1806
1870
  }
1807
1871
  />
1872
+ <ul className='column-edit'>
1873
+ <li className='three-col'>
1874
+ <TextField value={columns.primary.prefix} section='columns' subsection='primary' fieldName='prefix' label='Prefix' updateField={updateField} />
1875
+ <TextField value={columns.primary.suffix} section='columns' subsection='primary' fieldName='suffix' label='Suffix' updateField={updateField} />
1876
+ <TextField type='number' value={columns.primary.roundToPlace} section='columns' subsection='primary' fieldName='roundToPlace' label='Round' updateField={updateField} min={0} />
1877
+ </li>
1878
+ <li>
1879
+ <label className='checkbox'>
1880
+ <input
1881
+ type='checkbox'
1882
+ checked={state.columns.primary.useCommas}
1883
+ onChange={event => {
1884
+ editColumn('primary', 'useCommas', event.target.checked)
1885
+ }}
1886
+ />
1887
+ <span className='edit-label'>Add Commas to Numbers</span>
1888
+ </label>
1889
+ </li>
1890
+ <li>
1891
+ <label className='checkbox'>
1892
+ <input
1893
+ type='checkbox'
1894
+ checked={state.columns.primary.dataTable || false}
1895
+ onChange={event => {
1896
+ editColumn('primary', 'dataTable', event.target.checked)
1897
+ }}
1898
+ />
1899
+ <span className='edit-label'>Show in Data Table</span>
1900
+ </label>
1901
+ </li>
1902
+ <li>
1903
+ <label className='checkbox'>
1904
+ <input
1905
+ type='checkbox'
1906
+ checked={state.columns.primary.tooltip || false}
1907
+ onChange={event => {
1908
+ editColumn('primary', 'tooltip', event.target.checked)
1909
+ }}
1910
+ />
1911
+ <span className='edit-label'>Show in Tooltips</span>
1912
+ </label>
1913
+ </li>
1914
+ </ul>
1808
1915
  </fieldset>
1809
- {'navigation' !== state.general.type && (
1810
- <fieldset className='primary-fieldset edit-block'>
1811
- <label>
1812
- <span className='edit-label column-heading'>
1813
- Data Column
1814
- <Tooltip style={{ textTransform: 'none' }}>
1815
- <Tooltip.Target>
1816
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1817
- </Tooltip.Target>
1818
- <Tooltip.Content>
1819
- <p>Select the source column containing the categorical or numeric values to be mapped.</p>
1820
- </Tooltip.Content>
1821
- </Tooltip>
1822
- </span>
1823
- <select
1824
- value={state.columns.primary ? state.columns.primary.name : columnsOptions[0]}
1825
- onChange={event => {
1826
- editColumn('primary', 'name', event.target.value)
1827
- }}
1828
- >
1829
- {columnsOptions}
1830
- </select>
1831
- </label>
1832
- <label className='checkbox'>
1833
- <input
1834
- type='checkbox'
1835
- checked={state.general.hidePrimaryColumnInTooltip || false}
1836
- onChange={event => {
1837
- handleEditorChanges('hidePrimaryColumnInTooltip', event.target.checked)
1838
- }}
1839
- />
1840
- <span className='edit-label'>Hide Data Column Name in Tooltip</span>
1841
- </label>
1842
- <TextField
1843
- value={columns.primary.label}
1844
- section='columns'
1845
- subsection='primary'
1846
- fieldName='label'
1847
- label='Data Label'
1848
- updateField={updateField}
1849
- tooltip={
1850
- <Tooltip style={{ textTransform: 'none' }}>
1851
- <Tooltip.Target>
1852
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1853
- </Tooltip.Target>
1854
- <Tooltip.Content>
1855
- <p>Enter a data label for use in tooltips and the data table.</p>
1856
- </Tooltip.Content>
1857
- </Tooltip>
1858
- }
1859
- />
1860
- <ul className='column-edit'>
1861
- <li className='three-col'>
1862
- <TextField value={columns.primary.prefix} section='columns' subsection='primary' fieldName='prefix' label='Prefix' updateField={updateField} />
1863
- <TextField value={columns.primary.suffix} section='columns' subsection='primary' fieldName='suffix' label='Suffix' updateField={updateField} />
1864
- <TextField type='number' value={columns.primary.roundToPlace} section='columns' subsection='primary' fieldName='roundToPlace' label='Round' updateField={updateField} min={0} />
1865
- </li>
1866
- <li>
1867
- <label className='checkbox'>
1868
- <input
1869
- type='checkbox'
1870
- checked={state.columns.primary.useCommas}
1871
- onChange={event => {
1872
- editColumn('primary', 'useCommas', event.target.checked)
1873
- }}
1874
- />
1875
- <span className='edit-label'>Add Commas to Numbers</span>
1876
- </label>
1877
- </li>
1878
- <li>
1879
- <label className='checkbox'>
1880
- <input
1881
- type='checkbox'
1882
- checked={state.columns.primary.dataTable || false}
1883
- onChange={event => {
1884
- editColumn('primary', 'dataTable', event.target.checked)
1885
- }}
1886
- />
1887
- <span className='edit-label'>Show in Data Table</span>
1888
- </label>
1889
- </li>
1890
- <li>
1891
- <label className='checkbox'>
1892
- <input
1893
- type='checkbox'
1894
- checked={state.columns.primary.tooltip || false}
1895
- onChange={event => {
1896
- editColumn('primary', 'tooltip', event.target.checked)
1897
- }}
1898
- />
1899
- <span className='edit-label'>Show in Tooltips</span>
1900
- </label>
1901
- </li>
1902
- </ul>
1903
- </fieldset>
1904
- )}
1916
+ )}
1905
1917
 
1906
- {state.general.type === 'bubble' && state.legend.type === 'category' && (
1907
- <fieldset className='primary-fieldset edit-block'>
1908
- <label>
1909
- <span className='edit-label column-heading'>
1910
- Category Column
1911
- <Tooltip style={{ textTransform: 'none' }}>
1912
- <Tooltip.Target>
1913
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1914
- </Tooltip.Target>
1915
- <Tooltip.Content>
1916
- <p>Select the source column containing the categorical bubble values to be mapped.</p>
1917
- </Tooltip.Content>
1918
- </Tooltip>
1919
- </span>
1920
- <select
1921
- value={state.columns.categorical ? state.columns.categorical.name : columnsOptions[0]}
1922
- onChange={event => {
1923
- editColumn('categorical', 'name', event.target.value)
1924
- }}
1925
- >
1926
- {columnsOptions}
1927
- </select>
1928
- </label>
1929
- </fieldset>
1930
- )}
1931
- {(
1932
- <>
1933
- <label>Latitude Column</label>
1934
- <select
1935
- value={state.columns.latitude.name ? state.columns.latitude.name : ''}
1936
- onChange={e => {
1937
- editColumn('latitude', 'name', e.target.value)
1938
- }}
1939
- >
1940
- {columnsOptions}
1941
- </select>
1942
- <label>Longitude Column</label>
1918
+ {state.general.type === 'bubble' && state.legend.type === 'category' && (
1919
+ <fieldset className='primary-fieldset edit-block'>
1920
+ <label>
1921
+ <span className='edit-label column-heading'>
1922
+ Category Column
1923
+ <Tooltip style={{ textTransform: 'none' }}>
1924
+ <Tooltip.Target>
1925
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1926
+ </Tooltip.Target>
1927
+ <Tooltip.Content>
1928
+ <p>Select the source column containing the categorical bubble values to be mapped.</p>
1929
+ </Tooltip.Content>
1930
+ </Tooltip>
1931
+ </span>
1943
1932
  <select
1944
- value={state.columns.longitude.name ? state.columns.longitude.name : ''}
1945
- onChange={e => {
1946
- editColumn('longitude', 'name', e.target.value)
1933
+ value={state.columns.categorical ? state.columns.categorical.name : columnsOptions[0]}
1934
+ onChange={event => {
1935
+ editColumn('categorical', 'name', event.target.value)
1947
1936
  }}
1948
1937
  >
1949
1938
  {columnsOptions}
1950
1939
  </select>
1951
- </>
1952
- )}
1953
-
1954
- {'navigation' !== state.general.type && (
1955
- <fieldset className='primary-fieldset edit-block'>
1956
- <label>
1957
- <span className='edit-label'>
1958
- Special Classes
1959
- <Tooltip style={{ textTransform: 'none' }}>
1960
- <Tooltip.Target>
1961
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1962
- </Tooltip.Target>
1963
- <Tooltip.Content>
1964
- <p>For secondary values such as "NA", the system can automatically color-code them in shades of gray, one shade for each special class.</p>
1965
- </Tooltip.Content>
1966
- </Tooltip>
1967
- </span>
1968
- </label>
1969
- {specialClasses.map((specialClass, i) => (
1970
- <div className='edit-block' key={`special-class-${i}`}>
1971
- <button
1972
- className='remove-column'
1973
- onClick={e => {
1974
- e.preventDefault()
1975
- editColumn('primary', 'specialClassDelete', i)
1976
- }}
1977
- >
1978
- Remove
1979
- </button>
1980
- <p>Special Class {i + 1}</p>
1981
- <label>
1982
- <span className='edit-label column-heading'>Data Key</span>
1983
- <select
1984
- value={specialClass.key}
1985
- onChange={e => {
1986
- editColumn('primary', 'specialClassEdit', { prop: 'key', index: i, value: e.target.value })
1987
- }}
1988
- >
1989
- {columnsOptions}
1990
- </select>
1991
- </label>
1992
- <label>
1993
- <span className='edit-label column-heading'>Value</span>
1994
- <select
1995
- value={specialClass.value}
1996
- onChange={e => {
1997
- editColumn('primary', 'specialClassEdit', { prop: 'value', index: i, value: e.target.value })
1998
- }}
1999
- >
2000
- <option value=''>- Select Value -</option>
2001
- {columnsByKey[specialClass.key] && columnsByKey[specialClass.key].sort().map(option => <option key={`special-class-value-option-${i}-${option}`}>{option}</option>)}
2002
- </select>
2003
- </label>
2004
- <label>
2005
- <span className='edit-label column-heading'>Label</span>
2006
- <input
2007
- type='text'
2008
- value={specialClass.label}
2009
- onChange={e => {
2010
- editColumn('primary', 'specialClassEdit', { prop: 'label', index: i, value: e.target.value })
2011
- }}
2012
- />
2013
- </label>
2014
- </div>
2015
- ))}
2016
- <button
2017
- className='btn full-width'
2018
- onClick={e => {
2019
- e.preventDefault()
2020
- editColumn('primary', 'specialClassAdd', {})
2021
- }}
2022
- >
2023
- Add Special Class
2024
- </button>
2025
- </fieldset>
2026
- )}
2027
-
2028
- <label className='edit-block navigate column-heading'>
2029
- <span className='edit-label column-heading'>
2030
- Navigation
2031
- <Tooltip style={{ textTransform: 'none' }}>
2032
- <Tooltip.Target>
2033
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2034
- </Tooltip.Target>
2035
- <Tooltip.Content>
2036
- <p>To provide end users with navigation functionality, select the source column containing the navigation URLs.</p>
2037
- </Tooltip.Content>
2038
- </Tooltip>
2039
- </span>
1940
+ </label>
1941
+ </fieldset>
1942
+ )}
1943
+ {
1944
+ <>
1945
+ <label>Latitude Column</label>
2040
1946
  <select
2041
- value={state.columns.navigate ? state.columns.navigate.name : columnsOptions[0]}
2042
- onChange={event => {
2043
- editColumn('navigate', 'name', event.target.value)
1947
+ value={state.columns.latitude.name ? state.columns.latitude.name : ''}
1948
+ onChange={e => {
1949
+ editColumn('latitude', 'name', e.target.value)
2044
1950
  }}
2045
1951
  >
2046
1952
  {columnsOptions}
2047
1953
  </select>
2048
- </label>
2049
- {'navigation' !== state.general.type && (
2050
- <fieldset className='primary-fieldset edit-block'>
2051
- <label>
2052
- <span className='edit-label'>
2053
- Additional Columns
2054
- <Tooltip style={{ textTransform: 'none' }}>
2055
- <Tooltip.Target>
2056
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2057
- </Tooltip.Target>
2058
- <Tooltip.Content>
2059
- <p>You can specify additional columns to display in tooltips and / or the supporting data table.</p>
2060
- </Tooltip.Content>
2061
- </Tooltip>
2062
- </span>
2063
- </label>
2064
- {additionalColumns.map(val => (
2065
- <fieldset className='edit-block' key={val}>
2066
- <button
2067
- className='remove-column'
2068
- onClick={event => {
2069
- event.preventDefault()
2070
- removeAdditionalColumn(val)
2071
- }}
2072
- >
2073
- Remove
2074
- </button>
2075
- <label>
2076
- <span className='edit-label column-heading'>Column</span>
2077
- <select
2078
- value={state.columns[val] ? state.columns[val].name : columnsOptions[0]}
2079
- onChange={event => {
2080
- editColumn(val, 'name', event.target.value)
2081
- }}
2082
- >
2083
- {columnsOptions}
2084
- </select>
2085
- </label>
2086
- <TextField value={columns[val].label} section='columns' subsection={val} fieldName='label' label='Label' updateField={updateField} />
2087
- <ul className='column-edit'>
2088
- <li className='three-col'>
2089
- <TextField value={columns[val].prefix} section='columns' subsection={val} fieldName='prefix' label='Prefix' updateField={updateField} />
2090
- <TextField value={columns[val].suffix} section='columns' subsection={val} fieldName='suffix' label='Suffix' updateField={updateField} />
2091
- <TextField type='number' value={columns[val].roundToPlace} section='columns' subsection={val} fieldName='roundToPlace' label='Round' updateField={updateField} />
2092
- </li>
2093
- <li>
2094
- <label className='checkbox'>
2095
- <input
2096
- type='checkbox'
2097
- checked={state.columns[val].useCommas}
2098
- onChange={event => {
2099
- editColumn(val, 'useCommas', event.target.checked)
2100
- }}
2101
- />
2102
- <span className='edit-label'>Add Commas to Numbers</span>
2103
- </label>
2104
- </li>
2105
- <li>
2106
- <label className='checkbox'>
2107
- <input
2108
- type='checkbox'
2109
- checked={state.columns[val].dataTable}
2110
- onChange={event => {
2111
- editColumn(val, 'dataTable', event.target.checked)
2112
- }}
2113
- />
2114
- <span className='edit-label'>Show in Data Table</span>
2115
- </label>
2116
- </li>
2117
- <li>
2118
- <label className='checkbox'>
2119
- <input
2120
- type='checkbox'
2121
- checked={state.columns[val].tooltip}
2122
- onChange={event => {
2123
- editColumn(val, 'tooltip', event.target.checked)
2124
- }}
2125
- />
2126
- <span className='edit-label'>Show in Tooltips</span>
2127
- </label>
2128
- </li>
2129
- </ul>
2130
- </fieldset>
2131
- ))}
2132
- <button
2133
- className={'btn full-width'}
2134
- onClick={event => {
2135
- event.preventDefault()
2136
- addAdditionalColumn(additionalColumns.length + 1)
2137
- }}
2138
- >
2139
- Add Column
2140
- </button>
2141
- </fieldset>
2142
- )}
2143
- {'category' === state.legend.type && (
2144
- <fieldset className='primary-fieldset edit-block'>
2145
- <label>
2146
- <span className='edit-label'>
2147
- Additional Category
2148
- <Tooltip style={{ textTransform: 'none' }}>
2149
- <Tooltip.Target>
2150
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2151
- </Tooltip.Target>
2152
- <Tooltip.Content>
2153
- <p>You can provide additional categories to ensure they appear in the legend</p>
2154
- </Tooltip.Content>
2155
- </Tooltip>
2156
- </span>
2157
- </label>
2158
- {state.legend.additionalCategories &&
2159
- state.legend.additionalCategories.map((val, i) => (
2160
- <fieldset className='edit-block' key={val}>
2161
- <button
2162
- className='remove-column'
2163
- onClick={event => {
2164
- event.preventDefault()
2165
- const updatedAdditionaCategories = [...state.legend.additionalCategories]
2166
- updatedAdditionaCategories.splice(i, 1)
2167
- updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2168
- }}
2169
- >
2170
- Remove
2171
- </button>
2172
- <label>
2173
- <span className='edit-label column-heading'>Category</span>
2174
- <TextField
2175
- value={val}
2176
- section='legend'
2177
- subsection={null}
2178
- fieldName='additionalCategories'
2179
- updateField={(section, subsection, fieldName, value) => {
2180
- const updatedAdditionaCategories = [...state.legend.additionalCategories]
2181
- updatedAdditionaCategories[i] = value
2182
- updateField(section, subsection, fieldName, updatedAdditionaCategories)
2183
- }}
2184
- />
2185
- </label>
2186
- </fieldset>
2187
- ))}
2188
- <button
2189
- className={'btn full-width'}
2190
- onClick={event => {
2191
- event.preventDefault()
2192
- const updatedAdditionaCategories = [...(state.legend.additionalCategories || [])]
2193
- updatedAdditionaCategories.push('')
2194
- updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2195
- }}
2196
- >
2197
- Add Category
2198
- </button>
2199
- </fieldset>
2200
- )}
2201
- </AccordionItemPanel>
2202
- </AccordionItem>{' '}
2203
- {/* Columns */}
2204
- {'navigation' !== state.general.type && (
2205
- <AccordionItem>
2206
- {' '}
2207
- {/* Legend */}
2208
- <AccordionItemHeading>
2209
- <AccordionItemButton>Legend</AccordionItemButton>
2210
- </AccordionItemHeading>
2211
- <AccordionItemPanel>
2212
- {(state.legend.type === 'equalnumber' || state.legend.type === 'equalinterval') && (
2213
- <label>
2214
- <span className='edit-label'>Legend Type</span>
2215
- <select
2216
- value={legend.type}
2217
- onChange={event => {
2218
- handleEditorChanges('legendType', event.target.value)
2219
- }}
2220
- >
2221
- <option value='equalnumber'>Equal Number (Quantiles)</option>
2222
- <option value='equalinterval'>Equal Interval</option>
2223
- </select>
2224
- </label>
2225
- )}
2226
- {'navigation' !== state.general.type && (
2227
- <label className='checkbox'>
2228
- <input
2229
- type='checkbox'
2230
- checked={state.general.showSidebar || false}
2231
- onChange={event => {
2232
- handleEditorChanges('showSidebar', event.target.checked)
2233
- }}
2234
- />
2235
- <span className='edit-label'>Show Legend</span>
2236
- </label>
2237
- )}
2238
- {'navigation' !== state.general.type && (
2239
- <label>
2240
- <span className='edit-label'>Legend Position</span>
2241
- <select
2242
- value={legend.position || false}
2243
- onChange={event => {
2244
- handleEditorChanges('sidebarPosition', event.target.value)
2245
- }}
2246
- >
2247
- <option value='side'>Side</option>
2248
- <option value='bottom'>Bottom</option>
2249
- </select>
2250
- </label>
2251
- )}
2252
- {'side' === legend.position && (
2253
- <label className='checkbox'>
2254
- <input
2255
- type='checkbox'
2256
- checked={legend.singleColumn}
2257
- onChange={event => {
2258
- handleEditorChanges('singleColumnLegend', event.target.checked)
2259
- }}
2260
- />
2261
- <span className='edit-label'>Single Column Legend</span>
2262
- </label>
2263
- )}
2264
- {'bottom' === legend.position && (
2265
- <label className='checkbox'>
2266
- <input
2267
- type='checkbox'
2268
- checked={legend.singleRow}
2269
- onChange={event => {
2270
- handleEditorChanges('singleRowLegend', event.target.checked)
2271
- }}
2272
- />
2273
- <span className='edit-label'>Single Row Legend</span>
2274
- </label>
2275
- )}
2276
- <label className='checkbox'>
2277
- <input
2278
- type='checkbox'
2279
- checked={legend.verticalSorted}
2280
- onChange={event => {
2281
- handleEditorChanges('verticalSortedLegend', event.target.checked)
2282
- }}
2283
- />
2284
- <span className='edit-label'>Vertical sorted legend</span>
2285
- </label>
2286
- {/* always show */}
2287
- {
2288
- <label className='checkbox'>
2289
- <input
2290
- type='checkbox'
2291
- checked={legend.showSpecialClassesLast}
2292
- onChange={event => {
2293
- handleEditorChanges('legendShowSpecialClassesLast', event.target.checked)
2294
- }}
2295
- />
2296
- <span className='edit-label'>Show Special Classes Last</span>
2297
- </label>
2298
- }
2299
- {'category' !== legend.type && (
2300
- <label className='checkbox'>
2301
- <input type='checkbox' checked={legend.separateZero || false} onChange={event => handleEditorChanges('separateZero', event.target.checked)} />
2302
- <span className='edit-label column-heading'>
2303
- Separate Zero
2304
- <Tooltip style={{ textTransform: 'none' }}>
2305
- <Tooltip.Target>
2306
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2307
- </Tooltip.Target>
2308
- <Tooltip.Content>
2309
- <p>For numeric data, you can separate the zero value as its own data class.</p>
2310
- </Tooltip.Content>
2311
- </Tooltip>
2312
- </span>
2313
- </label>
2314
- )}
2315
- {/* Temp Checkbox */}
2316
- {state.legend.type === 'equalnumber' && (
2317
- <label className='checkbox'>
2318
- <input
2319
- type='checkbox'
2320
- checked={state.general.equalNumberOptIn}
2321
- onChange={event => {
2322
- handleEditorChanges('showEqualNumber', event.target.checked)
2323
- }}
2324
- />
2325
- <span className='edit-label column-heading'>Use new quantile legend</span>
1954
+ <label>Longitude Column</label>
1955
+ <select
1956
+ value={state.columns.longitude.name ? state.columns.longitude.name : ''}
1957
+ onChange={e => {
1958
+ editColumn('longitude', 'name', e.target.value)
1959
+ }}
1960
+ >
1961
+ {columnsOptions}
1962
+ </select>
1963
+ </>
1964
+ }
1965
+
1966
+ {'navigation' !== state.general.type && (
1967
+ <fieldset className='primary-fieldset edit-block'>
1968
+ <label>
1969
+ <span className='edit-label'>
1970
+ Special Classes
2326
1971
  <Tooltip style={{ textTransform: 'none' }}>
2327
1972
  <Tooltip.Target>
2328
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1973
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2329
1974
  </Tooltip.Target>
2330
1975
  <Tooltip.Content>
2331
- <p>This prevents numbers from being used in more than one category (ie. 0-1, 1-2, 2-3) </p>
1976
+ <p>For secondary values such as "NA", the system can automatically color-code them in shades of gray, one shade for each special class.</p>
2332
1977
  </Tooltip.Content>
2333
1978
  </Tooltip>
2334
- </label>
2335
- )}
2336
- {'category' !== legend.type && (
2337
- <label>
2338
- <span className='edit-label'>
2339
- Number of Items
2340
- <Tooltip style={{ textTransform: 'none' }}>
2341
- <Tooltip.Target>
2342
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2343
- </Tooltip.Target>
2344
- <Tooltip.Content>
2345
- <p>For numeric maps, select the number of data classes. Do not include designated special classes.</p>
2346
- </Tooltip.Content>
2347
- </Tooltip>
2348
- </span>
2349
- <select
2350
- value={legend.numberOfItems}
2351
- onChange={event => {
2352
- handleEditorChanges('legendNumber', event.target.value)
1979
+ </span>
1980
+ </label>
1981
+ {specialClasses.map((specialClass, i) => (
1982
+ <div className='edit-block' key={`special-class-${i}`}>
1983
+ <button
1984
+ className='remove-column'
1985
+ onClick={e => {
1986
+ e.preventDefault()
1987
+ editColumn('primary', 'specialClassDelete', i)
2353
1988
  }}
2354
1989
  >
2355
- {[...Array(numberOfItemsLimit).keys()].map(num => {
2356
- return (
2357
- <option value={num + 1} key={num + 1}>
2358
- {num + 1}
2359
- </option>
2360
- )
2361
- })}
2362
- </select>
2363
- </label>
2364
- )}
2365
- {'category' === legend.type && (
2366
- <React.Fragment>
1990
+ Remove
1991
+ </button>
1992
+ <p>Special Class {i + 1}</p>
2367
1993
  <label>
2368
- <span className='edit-label'>
2369
- Category Order
2370
- <Tooltip style={{ textTransform: 'none' }}>
2371
- <Tooltip.Target>
2372
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2373
- </Tooltip.Target>
2374
- <Tooltip.Content>
2375
- <p>Drag map categories into preferred legend order. </p>
2376
- </Tooltip.Content>
2377
- </Tooltip>
2378
- </span>
2379
- </label>
2380
- {/* TODO: Swap out this drag and drop library back to something simpler. I had to remove the old one because it hadn't been updated and wouldn't work with Webpack 5. This is overkill for our needs. */}
2381
- <DragDropContext onDragEnd={({ source, destination }) => categoryMove(source.index, destination.index)}>
2382
- <Droppable droppableId='category_order'>
2383
- {provided => (
2384
- <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef}>
2385
- <CategoryList />
2386
- {provided.placeholder}
2387
- </ul>
2388
- )}
2389
- </Droppable>
2390
- </DragDropContext>
2391
- {state.legend.categoryValuesOrder && state.legend.categoryValuesOrder.length >= 10 && (
2392
- <section className='error-box my-2'>
2393
- <div>
2394
- <strong className='pt-1'>Warning</strong>
2395
- <p>The maximum number of categorical legend items is 10. If your data has more than 10 categories your map will not display properly.</p>
2396
- </div>
2397
- </section>
2398
- )}
2399
- </React.Fragment>
2400
- )}
2401
- <TextField value={legend.title} updateField={updateField} section='legend' fieldName='title' label='Legend Title' placeholder='Legend Title' />
2402
- {false === legend.dynamicDescription && <TextField type='textarea' value={legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />}
2403
- {true === legend.dynamicDescription && (
2404
- <React.Fragment>
2405
- <label>
2406
- <span>Legend Description</span>
2407
- <span className='subtext'>For {displayFilterLegendValue(activeFilterValueForDescription)}</span>
2408
- <DynamicDesc value={legend.descriptions[String(activeFilterValueForDescription)]} />
1994
+ <span className='edit-label column-heading'>Data Key</span>
1995
+ <select
1996
+ value={specialClass.key}
1997
+ onChange={e => {
1998
+ editColumn('primary', 'specialClassEdit', { prop: 'key', index: i, value: e.target.value })
1999
+ }}
2000
+ >
2001
+ {columnsOptions}
2002
+ </select>
2409
2003
  </label>
2410
2004
  <label>
2005
+ <span className='edit-label column-heading'>Value</span>
2411
2006
  <select
2412
- value={String(activeFilterValueForDescription)}
2413
- onChange={event => {
2414
- handleEditorChanges('changeActiveFilterValue', event.target.value)
2007
+ value={specialClass.value}
2008
+ onChange={e => {
2009
+ editColumn('primary', 'specialClassEdit', { prop: 'value', index: i, value: e.target.value })
2415
2010
  }}
2416
2011
  >
2417
- {filterValueOptionList.map((arr, i) => {
2418
- return (
2419
- <option value={arr} key={i}>
2420
- {displayFilterLegendValue(arr)}
2421
- </option>
2422
- )
2423
- })}
2012
+ <option value=''>- Select Value -</option>
2013
+ {columnsByKey[specialClass.key] && columnsByKey[specialClass.key].sort().map(option => <option key={`special-class-value-option-${i}-${option}`}>{option}</option>)}
2424
2014
  </select>
2425
2015
  </label>
2426
- </React.Fragment>
2427
- )}
2428
- {filtersJSX.length > 0 && (
2429
- <label className='checkbox'>
2430
- <input
2431
- type='checkbox'
2432
- checked={legend.dynamicDescription}
2433
- onChange={() => {
2434
- handleEditorChanges('dynamicDescription', filterValueOptionList[0])
2016
+ <label>
2017
+ <span className='edit-label column-heading'>Label</span>
2018
+ <input
2019
+ type='text'
2020
+ value={specialClass.label}
2021
+ onChange={e => {
2022
+ editColumn('primary', 'specialClassEdit', { prop: 'label', index: i, value: e.target.value })
2023
+ }}
2024
+ />
2025
+ </label>
2026
+ </div>
2027
+ ))}
2028
+ <button
2029
+ className='btn full-width'
2030
+ onClick={e => {
2031
+ e.preventDefault()
2032
+ editColumn('primary', 'specialClassAdd', {})
2033
+ }}
2034
+ >
2035
+ Add Special Class
2036
+ </button>
2037
+ </fieldset>
2038
+ )}
2039
+
2040
+ <label className='edit-block navigate column-heading'>
2041
+ <span className='edit-label column-heading'>
2042
+ Navigation
2043
+ <Tooltip style={{ textTransform: 'none' }}>
2044
+ <Tooltip.Target>
2045
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2046
+ </Tooltip.Target>
2047
+ <Tooltip.Content>
2048
+ <p>To provide end users with navigation functionality, select the source column containing the navigation URLs.</p>
2049
+ </Tooltip.Content>
2050
+ </Tooltip>
2051
+ </span>
2052
+ <select
2053
+ value={state.columns.navigate ? state.columns.navigate.name : columnsOptions[0]}
2054
+ onChange={event => {
2055
+ editColumn('navigate', 'name', event.target.value)
2056
+ }}
2057
+ >
2058
+ {columnsOptions}
2059
+ </select>
2060
+ </label>
2061
+ {'navigation' !== state.general.type && (
2062
+ <fieldset className='primary-fieldset edit-block'>
2063
+ <label>
2064
+ <span className='edit-label'>
2065
+ Additional Columns
2066
+ <Tooltip style={{ textTransform: 'none' }}>
2067
+ <Tooltip.Target>
2068
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2069
+ </Tooltip.Target>
2070
+ <Tooltip.Content>
2071
+ <p>You can specify additional columns to display in tooltips and / or the supporting data table.</p>
2072
+ </Tooltip.Content>
2073
+ </Tooltip>
2074
+ </span>
2075
+ </label>
2076
+ {additionalColumns.map(val => (
2077
+ <fieldset className='edit-block' key={val}>
2078
+ <button
2079
+ className='remove-column'
2080
+ onClick={event => {
2081
+ event.preventDefault()
2082
+ removeAdditionalColumn(val)
2435
2083
  }}
2436
- />
2437
- <span className='edit-label column-heading'>
2438
- Dynamic Legend Description
2439
- <Tooltip style={{ textTransform: 'none' }}>
2440
- <Tooltip.Target>
2441
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2442
- </Tooltip.Target>
2443
- <Tooltip.Content>
2444
- <p>Check this option if the map has multiple filter controls and you want to specify a description for each filter selection.</p>
2445
- </Tooltip.Content>
2446
- </Tooltip>
2447
- </span>
2448
- </label>
2449
- )}
2450
- {(filtersJSX.length > 0 || state.general.type === 'bubble' || state.general.geoType === 'us') && (
2451
- <label className='checkbox'>
2452
- <input type='checkbox' checked={legend.unified} onChange={event => handleEditorChanges('unifiedLegend', event.target.checked)} />
2453
- <span className='edit-label column-heading'>
2454
- Unified Legend
2455
- <Tooltip style={{ textTransform: 'none' }}>
2456
- <Tooltip.Target>
2457
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2458
- </Tooltip.Target>
2459
- <Tooltip.Content>
2460
- <p>
2461
- For a map with filters, check this option if you want the high and low values in the legend to be based on <em>all</em> mapped values.
2462
- </p>
2463
- </Tooltip.Content>
2464
- </Tooltip>
2465
- </span>
2466
- </label>
2467
- )}
2468
- </AccordionItemPanel>
2469
- </AccordionItem>
2470
- )}
2471
- {'navigation' !== state.general.type && (
2472
- <AccordionItem>
2473
- {' '}
2474
- {/* Filters */}
2475
- <AccordionItemHeading>
2476
- <AccordionItemButton>Filters</AccordionItemButton>
2477
- </AccordionItemHeading>
2478
- <AccordionItemPanel>
2479
- {filtersJSX.length > 0 ? <MapFilters /> : <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
2084
+ >
2085
+ Remove
2086
+ </button>
2087
+ <label>
2088
+ <span className='edit-label column-heading'>Column</span>
2089
+ <select
2090
+ value={state.columns[val] ? state.columns[val].name : columnsOptions[0]}
2091
+ onChange={event => {
2092
+ editColumn(val, 'name', event.target.value)
2093
+ }}
2094
+ >
2095
+ {columnsOptions}
2096
+ </select>
2097
+ </label>
2098
+ <TextField value={columns[val].label} section='columns' subsection={val} fieldName='label' label='Label' updateField={updateField} />
2099
+ <ul className='column-edit'>
2100
+ <li className='three-col'>
2101
+ <TextField value={columns[val].prefix} section='columns' subsection={val} fieldName='prefix' label='Prefix' updateField={updateField} />
2102
+ <TextField value={columns[val].suffix} section='columns' subsection={val} fieldName='suffix' label='Suffix' updateField={updateField} />
2103
+ <TextField type='number' value={columns[val].roundToPlace} section='columns' subsection={val} fieldName='roundToPlace' label='Round' updateField={updateField} />
2104
+ </li>
2105
+ <li>
2106
+ <label className='checkbox'>
2107
+ <input
2108
+ type='checkbox'
2109
+ checked={state.columns[val].useCommas}
2110
+ onChange={event => {
2111
+ editColumn(val, 'useCommas', event.target.checked)
2112
+ }}
2113
+ />
2114
+ <span className='edit-label'>Add Commas to Numbers</span>
2115
+ </label>
2116
+ </li>
2117
+ <li>
2118
+ <label className='checkbox'>
2119
+ <input
2120
+ type='checkbox'
2121
+ checked={state.columns[val].dataTable}
2122
+ onChange={event => {
2123
+ editColumn(val, 'dataTable', event.target.checked)
2124
+ }}
2125
+ />
2126
+ <span className='edit-label'>Show in Data Table</span>
2127
+ </label>
2128
+ </li>
2129
+ <li>
2130
+ <label className='checkbox'>
2131
+ <input
2132
+ type='checkbox'
2133
+ checked={state.columns[val].tooltip}
2134
+ onChange={event => {
2135
+ editColumn(val, 'tooltip', event.target.checked)
2136
+ }}
2137
+ />
2138
+ <span className='edit-label'>Show in Tooltips</span>
2139
+ </label>
2140
+ </li>
2141
+ </ul>
2142
+ </fieldset>
2143
+ ))}
2480
2144
  <button
2481
2145
  className={'btn full-width'}
2482
2146
  onClick={event => {
2483
2147
  event.preventDefault()
2484
- changeFilter(null, 'addNew')
2148
+ addAdditionalColumn(additionalColumns.length + 1)
2485
2149
  }}
2486
2150
  >
2487
- Add Filter
2151
+ Add Column
2488
2152
  </button>
2489
- </AccordionItemPanel>
2490
- </AccordionItem>
2491
- )}
2492
- {'navigation' !== state.general.type && (
2493
- <AccordionItem>
2494
- {' '}
2495
- {/* Data Table */}
2496
- <AccordionItemHeading>
2497
- <AccordionItemButton>Data Table</AccordionItemButton>
2498
- </AccordionItemHeading>
2499
- <AccordionItemPanel>
2500
- <TextField
2501
- value={table.label}
2502
- updateField={updateField}
2503
- section='table'
2504
- fieldName='label'
2505
- id='dataTableTitle'
2506
- label='Data Table Title'
2507
- placeholder='Data Table'
2508
- tooltip={
2153
+ </fieldset>
2154
+ )}
2155
+ {'category' === state.legend.type && (
2156
+ <fieldset className='primary-fieldset edit-block'>
2157
+ <label>
2158
+ <span className='edit-label'>
2159
+ Additional Category
2509
2160
  <Tooltip style={{ textTransform: 'none' }}>
2510
2161
  <Tooltip.Target>
2511
2162
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2512
2163
  </Tooltip.Target>
2513
2164
  <Tooltip.Content>
2514
- <p>Label is required for Data Table for 508 Compliance</p>
2165
+ <p>You can provide additional categories to ensure they appear in the legend</p>
2515
2166
  </Tooltip.Content>
2516
2167
  </Tooltip>
2517
- }
2518
- />
2168
+ </span>
2169
+ </label>
2170
+ {state.legend.additionalCategories &&
2171
+ state.legend.additionalCategories.map((val, i) => (
2172
+ <fieldset className='edit-block' key={val}>
2173
+ <button
2174
+ className='remove-column'
2175
+ onClick={event => {
2176
+ event.preventDefault()
2177
+ const updatedAdditionaCategories = [...state.legend.additionalCategories]
2178
+ updatedAdditionaCategories.splice(i, 1)
2179
+ updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2180
+ }}
2181
+ >
2182
+ Remove
2183
+ </button>
2184
+ <label>
2185
+ <span className='edit-label column-heading'>Category</span>
2186
+ <TextField
2187
+ value={val}
2188
+ section='legend'
2189
+ subsection={null}
2190
+ fieldName='additionalCategories'
2191
+ updateField={(section, subsection, fieldName, value) => {
2192
+ const updatedAdditionaCategories = [...state.legend.additionalCategories]
2193
+ updatedAdditionaCategories[i] = value
2194
+ updateField(section, subsection, fieldName, updatedAdditionaCategories)
2195
+ }}
2196
+ />
2197
+ </label>
2198
+ </fieldset>
2199
+ ))}
2200
+ <button
2201
+ className={'btn full-width'}
2202
+ onClick={event => {
2203
+ event.preventDefault()
2204
+ const updatedAdditionaCategories = [...(state.legend.additionalCategories || [])]
2205
+ updatedAdditionaCategories.push('')
2206
+ updateField('legend', null, 'additionalCategories', updatedAdditionaCategories)
2207
+ }}
2208
+ >
2209
+ Add Category
2210
+ </button>
2211
+ </fieldset>
2212
+ )}
2213
+ </AccordionItemPanel>
2214
+ </AccordionItem>{' '}
2215
+ {/* Columns */}
2216
+ {'navigation' !== state.general.type && (
2217
+ <AccordionItem>
2218
+ {' '}
2219
+ {/* Legend */}
2220
+ <AccordionItemHeading>
2221
+ <AccordionItemButton>Legend</AccordionItemButton>
2222
+ </AccordionItemHeading>
2223
+ <AccordionItemPanel>
2224
+ {(state.legend.type === 'equalnumber' || state.legend.type === 'equalinterval') && (
2225
+ <label>
2226
+ <span className='edit-label'>Legend Type</span>
2227
+ <select
2228
+ value={legend.type}
2229
+ onChange={event => {
2230
+ handleEditorChanges('legendType', event.target.value)
2231
+ }}
2232
+ >
2233
+ <option value='equalnumber'>Equal Number (Quantiles)</option>
2234
+ <option value='equalinterval'>Equal Interval</option>
2235
+ </select>
2236
+ </label>
2237
+ )}
2238
+ {'navigation' !== state.general.type && (
2519
2239
  <label className='checkbox'>
2520
2240
  <input
2521
2241
  type='checkbox'
2522
- checked={state.table.wrapColumns}
2242
+ checked={state.general.showSidebar || false}
2523
2243
  onChange={event => {
2524
- setState({
2525
- ...state,
2526
- table: {
2527
- ...state.table,
2528
- wrapColumns: event.target.checked
2529
- }
2530
- })
2244
+ handleEditorChanges('showSidebar', event.target.checked)
2245
+ }}
2246
+ />
2247
+ <span className='edit-label'>Show Legend</span>
2248
+ </label>
2249
+ )}
2250
+ {'navigation' !== state.general.type && (
2251
+ <label>
2252
+ <span className='edit-label'>Legend Position</span>
2253
+ <select
2254
+ value={legend.position || false}
2255
+ onChange={event => {
2256
+ handleEditorChanges('sidebarPosition', event.target.value)
2257
+ }}
2258
+ >
2259
+ <option value='side'>Side</option>
2260
+ <option value='bottom'>Bottom</option>
2261
+ </select>
2262
+ </label>
2263
+ )}
2264
+ {'side' === legend.position && (
2265
+ <label className='checkbox'>
2266
+ <input
2267
+ type='checkbox'
2268
+ checked={legend.singleColumn}
2269
+ onChange={event => {
2270
+ handleEditorChanges('singleColumnLegend', event.target.checked)
2271
+ }}
2272
+ />
2273
+ <span className='edit-label'>Single Column Legend</span>
2274
+ </label>
2275
+ )}
2276
+ {'bottom' === legend.position && (
2277
+ <label className='checkbox'>
2278
+ <input
2279
+ type='checkbox'
2280
+ checked={legend.singleRow}
2281
+ onChange={event => {
2282
+ handleEditorChanges('singleRowLegend', event.target.checked)
2531
2283
  }}
2532
2284
  />
2533
- <span className='edit-label column-heading'>WRAP DATA TABLE COLUMNS</span>
2285
+ <span className='edit-label'>Single Row Legend</span>
2534
2286
  </label>
2287
+ )}
2288
+ <label className='checkbox'>
2289
+ <input
2290
+ type='checkbox'
2291
+ checked={legend.verticalSorted}
2292
+ onChange={event => {
2293
+ handleEditorChanges('verticalSortedLegend', event.target.checked)
2294
+ }}
2295
+ />
2296
+ <span className='edit-label'>Vertical sorted legend</span>
2297
+ </label>
2298
+ {/* always show */}
2299
+ {
2535
2300
  <label className='checkbox'>
2536
2301
  <input
2537
2302
  type='checkbox'
2538
- checked={state.table.forceDisplay !== undefined ? state.table.forceDisplay : !isDashboard}
2303
+ checked={legend.showSpecialClassesLast}
2539
2304
  onChange={event => {
2540
- handleEditorChanges('showDataTable', event.target.checked)
2305
+ handleEditorChanges('legendShowSpecialClassesLast', event.target.checked)
2541
2306
  }}
2542
2307
  />
2308
+ <span className='edit-label'>Show Special Classes Last</span>
2309
+ </label>
2310
+ }
2311
+ {'category' !== legend.type && (
2312
+ <label className='checkbox'>
2313
+ <input type='checkbox' checked={legend.separateZero || false} onChange={event => handleEditorChanges('separateZero', event.target.checked)} />
2543
2314
  <span className='edit-label column-heading'>
2544
- Show Data Table
2315
+ Separate Zero
2545
2316
  <Tooltip style={{ textTransform: 'none' }}>
2546
2317
  <Tooltip.Target>
2547
2318
  <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2548
2319
  </Tooltip.Target>
2549
2320
  <Tooltip.Content>
2550
- <p>Data tables are required for 508 compliance. When choosing to hide this data table, replace with your own version.</p>
2321
+ <p>For numeric data, you can separate the zero value as its own data class.</p>
2551
2322
  </Tooltip.Content>
2552
2323
  </Tooltip>
2553
2324
  </span>
2554
2325
  </label>
2555
- <TextField
2556
- value={table.indexLabel || ''}
2557
- updateField={updateField}
2558
- section='table'
2559
- fieldName='indexLabel'
2560
- label='Index Column Header'
2561
- placeholder='Location'
2562
- tooltip={
2563
- <Tooltip style={{ textTransform: 'none' }}>
2564
- <Tooltip.Target>
2565
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2566
- </Tooltip.Target>
2567
- <Tooltip.Content>
2568
- <p>To comply with 508 standards, if the first column in the data table has no header, enter a brief one here.</p>
2569
- </Tooltip.Content>
2570
- </Tooltip>
2571
- }
2572
- />
2573
- <TextField
2574
- value={state.table.caption}
2575
- updateField={updateField}
2576
- section='table'
2577
- fieldName='caption'
2578
- label='Screen Reader Description'
2579
- placeholder='Data Table'
2580
- tooltip={
2581
- <Tooltip style={{ textTransform: 'none' }}>
2582
- <Tooltip.Target>
2583
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2584
- </Tooltip.Target>
2585
- <Tooltip.Content>
2586
- <p>Enter a description of the data table to be read by screen readers.</p>
2587
- </Tooltip.Content>
2588
- </Tooltip>
2589
- }
2590
- type='textarea'
2591
- />
2326
+ )}
2327
+ {/* Temp Checkbox */}
2328
+ {state.legend.type === 'equalnumber' && (
2592
2329
  <label className='checkbox'>
2593
2330
  <input
2594
2331
  type='checkbox'
2595
- checked={state.table.limitHeight}
2332
+ checked={state.general.equalNumberOptIn}
2596
2333
  onChange={event => {
2597
- handleEditorChanges('limitDataTableHeight', event.target.checked)
2334
+ handleEditorChanges('showEqualNumber', event.target.checked)
2598
2335
  }}
2599
2336
  />
2600
- <span className='edit-label'>Limit Table Height</span>
2337
+ <span className='edit-label column-heading'>Use new quantile legend</span>
2338
+ <Tooltip style={{ textTransform: 'none' }}>
2339
+ <Tooltip.Target>
2340
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2341
+ </Tooltip.Target>
2342
+ <Tooltip.Content>
2343
+ <p>This prevents numbers from being used in more than one category (ie. 0-1, 1-2, 2-3) </p>
2344
+ </Tooltip.Content>
2345
+ </Tooltip>
2601
2346
  </label>
2602
- {state.table.limitHeight && <TextField value={table.height} updateField={updateField} section='table' fieldName='height' label='Data Table Height' placeholder='Height(px)' type='number' min='0' max='500' />}
2603
- <label className='checkbox'>
2604
- <input
2605
- type='checkbox'
2606
- checked={state.table.expanded || false}
2347
+ )}
2348
+ {'category' !== legend.type && (
2349
+ <label>
2350
+ <span className='edit-label'>
2351
+ Number of Items
2352
+ <Tooltip style={{ textTransform: 'none' }}>
2353
+ <Tooltip.Target>
2354
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2355
+ </Tooltip.Target>
2356
+ <Tooltip.Content>
2357
+ <p>For numeric maps, select the number of data classes. Do not include designated special classes.</p>
2358
+ </Tooltip.Content>
2359
+ </Tooltip>
2360
+ </span>
2361
+ <select
2362
+ value={legend.numberOfItems}
2607
2363
  onChange={event => {
2608
- handleEditorChanges('expandDataTable', event.target.checked)
2364
+ handleEditorChanges('legendNumber', event.target.value)
2609
2365
  }}
2610
- />
2611
- <span className='edit-label'>Map loads with data table expanded</span>
2366
+ >
2367
+ {[...Array(numberOfItemsLimit).keys()].map(num => {
2368
+ return (
2369
+ <option value={num + 1} key={num + 1}>
2370
+ {num + 1}
2371
+ </option>
2372
+ )
2373
+ })}
2374
+ </select>
2612
2375
  </label>
2613
- {isDashboard && (
2614
- <label className='checkbox'>
2615
- <input
2616
- type='checkbox'
2617
- checked={state.table.showDataTableLink}
2618
- onChange={event => {
2619
- handleEditorChanges('toggleDataTableLink', event.target.checked)
2620
- }}
2621
- />
2622
- <span className='edit-label'>Show Data Table Name & Link</span>
2376
+ )}
2377
+ {'category' === legend.type && (
2378
+ <React.Fragment>
2379
+ <label>
2380
+ <span className='edit-label'>
2381
+ Category Order
2382
+ <Tooltip style={{ textTransform: 'none' }}>
2383
+ <Tooltip.Target>
2384
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2385
+ </Tooltip.Target>
2386
+ <Tooltip.Content>
2387
+ <p>Drag map categories into preferred legend order. </p>
2388
+ </Tooltip.Content>
2389
+ </Tooltip>
2390
+ </span>
2623
2391
  </label>
2624
- )}
2625
- {isLoadedFromUrl && (
2626
- <label className='checkbox'>
2627
- <input
2628
- type='checkbox'
2629
- checked={state.table.showDownloadUrl}
2392
+ {/* TODO: Swap out this drag and drop library back to something simpler. I had to remove the old one because it hadn't been updated and wouldn't work with Webpack 5. This is overkill for our needs. */}
2393
+ <DragDropContext onDragEnd={({ source, destination }) => categoryMove(source.index, destination.index)}>
2394
+ <Droppable droppableId='category_order'>
2395
+ {provided => (
2396
+ <ul {...provided.droppableProps} className='sort-list' ref={provided.innerRef}>
2397
+ <CategoryList />
2398
+ {provided.placeholder}
2399
+ </ul>
2400
+ )}
2401
+ </Droppable>
2402
+ </DragDropContext>
2403
+ {state.legend.categoryValuesOrder && state.legend.categoryValuesOrder.length >= 10 && (
2404
+ <section className='error-box my-2'>
2405
+ <div>
2406
+ <strong className='pt-1'>Warning</strong>
2407
+ <p>The maximum number of categorical legend items is 10. If your data has more than 10 categories your map will not display properly.</p>
2408
+ </div>
2409
+ </section>
2410
+ )}
2411
+ </React.Fragment>
2412
+ )}
2413
+ <TextField value={legend.title} updateField={updateField} section='legend' fieldName='title' label='Legend Title' placeholder='Legend Title' />
2414
+ {false === legend.dynamicDescription && <TextField type='textarea' value={legend.description} updateField={updateField} section='legend' fieldName='description' label='Legend Description' />}
2415
+ {true === legend.dynamicDescription && (
2416
+ <React.Fragment>
2417
+ <label>
2418
+ <span>Legend Description</span>
2419
+ <span className='subtext'>For {displayFilterLegendValue(activeFilterValueForDescription)}</span>
2420
+ <DynamicDesc value={legend.descriptions[String(activeFilterValueForDescription)]} />
2421
+ </label>
2422
+ <label>
2423
+ <select
2424
+ value={String(activeFilterValueForDescription)}
2630
2425
  onChange={event => {
2631
- handleEditorChanges('toggleDataUrl', event.target.checked)
2426
+ handleEditorChanges('changeActiveFilterValue', event.target.value)
2632
2427
  }}
2633
- />
2634
- <span className='edit-label'>Show URL to Automatically Updated Data</span>
2428
+ >
2429
+ {filterValueOptionList.map((arr, i) => {
2430
+ return (
2431
+ <option value={arr} key={i}>
2432
+ {displayFilterLegendValue(arr)}
2433
+ </option>
2434
+ )
2435
+ })}
2436
+ </select>
2635
2437
  </label>
2636
- )}
2637
- <label className='checkbox'>
2638
- <input
2639
- type='checkbox'
2640
- checked={state.general.showDownloadButton}
2641
- onChange={event => {
2642
- handleEditorChanges('toggleDownloadButton', event.target.checked)
2643
- }}
2644
- />
2645
- <span className='edit-label'>Show Download CSV Link</span>
2646
- </label>
2438
+ </React.Fragment>
2439
+ )}
2440
+ {filtersJSX.length > 0 && (
2647
2441
  <label className='checkbox'>
2648
2442
  <input
2649
2443
  type='checkbox'
2650
- checked={state.general.showFullGeoNameInCSV}
2651
- onChange={event => {
2652
- handleEditorChanges('toggleShowFullGeoNameInCSV', event.target.checked)
2444
+ checked={legend.dynamicDescription}
2445
+ onChange={() => {
2446
+ handleEditorChanges('dynamicDescription', filterValueOptionList[0])
2653
2447
  }}
2654
2448
  />
2655
- <span className='edit-label'>Include Full Geo Name in CSV Download</span>
2449
+ <span className='edit-label column-heading'>
2450
+ Dynamic Legend Description
2451
+ <Tooltip style={{ textTransform: 'none' }}>
2452
+ <Tooltip.Target>
2453
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2454
+ </Tooltip.Target>
2455
+ <Tooltip.Content>
2456
+ <p>Check this option if the map has multiple filter controls and you want to specify a description for each filter selection.</p>
2457
+ </Tooltip.Content>
2458
+ </Tooltip>
2459
+ </span>
2656
2460
  </label>
2461
+ )}
2462
+ {(filtersJSX.length > 0 || state.general.type === 'bubble' || state.general.geoType === 'us') && (
2657
2463
  <label className='checkbox'>
2658
- <input
2659
- type='checkbox'
2660
- checked={state.general.showDownloadImgButton}
2661
- onChange={event => {
2662
- handleEditorChanges('toggleDownloadImgButton', event.target.checked)
2663
- }}
2664
- />
2665
- <span className='edit-label'>Enable Image Download</span>
2464
+ <input type='checkbox' checked={legend.unified} onChange={event => handleEditorChanges('unifiedLegend', event.target.checked)} />
2465
+ <span className='edit-label column-heading'>
2466
+ Unified Legend
2467
+ <Tooltip style={{ textTransform: 'none' }}>
2468
+ <Tooltip.Target>
2469
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2470
+ </Tooltip.Target>
2471
+ <Tooltip.Content>
2472
+ <p>
2473
+ For a map with filters, check this option if you want the high and low values in the legend to be based on <em>all</em> mapped values.
2474
+ </p>
2475
+ </Tooltip.Content>
2476
+ </Tooltip>
2477
+ </span>
2666
2478
  </label>
2667
- {/* <label className='checkbox'>
2668
- <input
2669
- type='checkbox'
2670
- checked={state.general.showDownloadPdfButton}
2671
- onChange={event => {
2672
- handleEditorChanges('toggleDownloadPdfButton', event.target.checked)
2673
- }}
2674
- />
2675
- <span className='edit-label'>Enable Pdf Download</span>
2676
- </label> */}
2677
- </AccordionItemPanel>
2678
- </AccordionItem>
2679
- )}
2479
+ )}
2480
+ </AccordionItemPanel>
2481
+ </AccordionItem>
2482
+ )}
2483
+ {'navigation' !== state.general.type && (
2680
2484
  <AccordionItem>
2681
2485
  {' '}
2682
- {/* Tooltips */}
2486
+ {/* Filters */}
2683
2487
  <AccordionItemHeading>
2684
- <AccordionItemButton>Interactivity</AccordionItemButton>
2488
+ <AccordionItemButton>Filters</AccordionItemButton>
2685
2489
  </AccordionItemHeading>
2686
2490
  <AccordionItemPanel>
2687
- <label>
2688
- <span className='edit-label'>
2689
- Detail displays on{' '}
2491
+ {filtersJSX.length > 0 ? <MapFilters /> : <p style={{ textAlign: 'center' }}>There are currently no filters.</p>}
2492
+ <button
2493
+ className={'btn full-width'}
2494
+ onClick={event => {
2495
+ event.preventDefault()
2496
+ changeFilter(null, 'addNew')
2497
+ }}
2498
+ >
2499
+ Add Filter
2500
+ </button>
2501
+ </AccordionItemPanel>
2502
+ </AccordionItem>
2503
+ )}
2504
+ {'navigation' !== state.general.type && (
2505
+ <AccordionItem>
2506
+ {' '}
2507
+ {/* Data Table */}
2508
+ <AccordionItemHeading>
2509
+ <AccordionItemButton>Data Table</AccordionItemButton>
2510
+ </AccordionItemHeading>
2511
+ <AccordionItemPanel>
2512
+ <TextField
2513
+ value={table.label}
2514
+ updateField={updateField}
2515
+ section='table'
2516
+ fieldName='label'
2517
+ id='dataTableTitle'
2518
+ label='Data Table Title'
2519
+ placeholder='Data Table'
2520
+ tooltip={
2521
+ <Tooltip style={{ textTransform: 'none' }}>
2522
+ <Tooltip.Target>
2523
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2524
+ </Tooltip.Target>
2525
+ <Tooltip.Content>
2526
+ <p>Label is required for Data Table for 508 Compliance</p>
2527
+ </Tooltip.Content>
2528
+ </Tooltip>
2529
+ }
2530
+ />
2531
+ <label className='checkbox'>
2532
+ <input
2533
+ type='checkbox'
2534
+ checked={state.table.wrapColumns}
2535
+ onChange={event => {
2536
+ setState({
2537
+ ...state,
2538
+ table: {
2539
+ ...state.table,
2540
+ wrapColumns: event.target.checked
2541
+ }
2542
+ })
2543
+ }}
2544
+ />
2545
+ <span className='edit-label column-heading'>WRAP DATA TABLE COLUMNS</span>
2546
+ </label>
2547
+ <label className='checkbox'>
2548
+ <input
2549
+ type='checkbox'
2550
+ checked={state.table.forceDisplay !== undefined ? state.table.forceDisplay : !isDashboard}
2551
+ onChange={event => {
2552
+ handleEditorChanges('showDataTable', event.target.checked)
2553
+ }}
2554
+ />
2555
+ <span className='edit-label column-heading'>
2556
+ Show Data Table
2690
2557
  <Tooltip style={{ textTransform: 'none' }}>
2691
2558
  <Tooltip.Target>
2692
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2559
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2693
2560
  </Tooltip.Target>
2694
2561
  <Tooltip.Content>
2695
- <p>At mobile sizes, information always appears in a popover modal when a user taps on an item.</p>
2562
+ <p>Data tables are required for 508 compliance. When choosing to hide this data table, replace with your own version.</p>
2696
2563
  </Tooltip.Content>
2697
2564
  </Tooltip>
2698
2565
  </span>
2699
- <select
2700
- value={state.tooltips.appearanceType}
2701
- onChange={event => {
2702
- handleEditorChanges('appearanceType', event.target.value)
2703
- }}
2704
- >
2705
- <option value='hover'>Hover - Tooltip</option>
2706
- <option value='click'>Click - Popover Modal</option>
2707
- </select>
2708
2566
  </label>
2709
- {'click' === state.tooltips.appearanceType && <TextField value={tooltips.linkLabel} section='tooltips' fieldName='linkLabel' label='Tooltips Link Label' updateField={updateField} />}
2567
+ <TextField
2568
+ value={table.indexLabel || ''}
2569
+ updateField={updateField}
2570
+ section='table'
2571
+ fieldName='indexLabel'
2572
+ label='Index Column Header'
2573
+ placeholder='Location'
2574
+ tooltip={
2575
+ <Tooltip style={{ textTransform: 'none' }}>
2576
+ <Tooltip.Target>
2577
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2578
+ </Tooltip.Target>
2579
+ <Tooltip.Content>
2580
+ <p>To comply with 508 standards, if the first column in the data table has no header, enter a brief one here.</p>
2581
+ </Tooltip.Content>
2582
+ </Tooltip>
2583
+ }
2584
+ />
2585
+ <TextField
2586
+ value={state.table.caption}
2587
+ updateField={updateField}
2588
+ section='table'
2589
+ fieldName='caption'
2590
+ label='Screen Reader Description'
2591
+ placeholder='Data Table'
2592
+ tooltip={
2593
+ <Tooltip style={{ textTransform: 'none' }}>
2594
+ <Tooltip.Target>
2595
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2596
+ </Tooltip.Target>
2597
+ <Tooltip.Content>
2598
+ <p>Enter a description of the data table to be read by screen readers.</p>
2599
+ </Tooltip.Content>
2600
+ </Tooltip>
2601
+ }
2602
+ type='textarea'
2603
+ />
2710
2604
  <label className='checkbox'>
2711
2605
  <input
2712
2606
  type='checkbox'
2713
- checked={state.tooltips.capitalizeLabels}
2607
+ checked={state.table.limitHeight}
2714
2608
  onChange={event => {
2715
- handleEditorChanges('capitalizeLabels', event.target.checked)
2609
+ handleEditorChanges('limitDataTableHeight', event.target.checked)
2716
2610
  }}
2717
2611
  />
2718
- <span className='edit-label'>Capitalize text inside tooltip</span>
2719
- </label>
2720
- </AccordionItemPanel>
2721
- </AccordionItem>
2722
- <AccordionItem>
2723
- {' '}
2724
- {/* Visual */}
2725
- <AccordionItemHeading>
2726
- <AccordionItemButton>Visual</AccordionItemButton>
2727
- </AccordionItemHeading>
2728
- <AccordionItemPanel>
2729
- <label>
2730
- <span className='edit-label'>Header Theme</span>
2731
- <ul className='color-palette'>
2732
- {headerColors.map(palette => {
2733
- return (
2734
- <li
2735
- title={palette}
2736
- key={palette}
2737
- onClick={() => {
2738
- handleEditorChanges('headerColor', palette)
2739
- }}
2740
- className={state.general.headerColor === palette ? 'selected ' + palette : palette}
2741
- ></li>
2742
- )
2743
- })}
2744
- </ul>
2612
+ <span className='edit-label'>Limit Table Height</span>
2745
2613
  </label>
2614
+ {state.table.limitHeight && <TextField value={table.height} updateField={updateField} section='table' fieldName='height' label='Data Table Height' placeholder='Height(px)' type='number' min='0' max='500' />}
2746
2615
  <label className='checkbox'>
2747
2616
  <input
2748
2617
  type='checkbox'
2749
- checked={state.general.showTitle || false}
2618
+ checked={state.table.expanded || false}
2750
2619
  onChange={event => {
2751
- handleEditorChanges('showTitle', event.target.checked)
2620
+ handleEditorChanges('expandDataTable', event.target.checked)
2752
2621
  }}
2753
2622
  />
2754
- <span className='edit-label'>Show Title</span>
2623
+ <span className='edit-label'>Map loads with data table expanded</span>
2755
2624
  </label>
2756
-
2757
- {'navigation' === state.general.type && (
2625
+ {isDashboard && (
2758
2626
  <label className='checkbox'>
2759
2627
  <input
2760
2628
  type='checkbox'
2761
- checked={state.general.fullBorder || false}
2629
+ checked={state.table.showDataTableLink}
2762
2630
  onChange={event => {
2763
- handleEditorChanges('fullBorder', event.target.checked)
2631
+ handleEditorChanges('toggleDataTableLink', event.target.checked)
2764
2632
  }}
2765
2633
  />
2766
- <span className='edit-label'>Add border around map</span>
2634
+ <span className='edit-label'>Show Data Table Name & Link</span>
2767
2635
  </label>
2768
2636
  )}
2769
- <label>
2770
- <span className='edit-label'>Geo Border Color</span>
2771
- <select
2772
- value={state.general.geoBorderColor || false}
2637
+ {isLoadedFromUrl && (
2638
+ <label className='checkbox'>
2639
+ <input
2640
+ type='checkbox'
2641
+ checked={state.table.showDownloadUrl}
2642
+ onChange={event => {
2643
+ handleEditorChanges('toggleDataUrl', event.target.checked)
2644
+ }}
2645
+ />
2646
+ <span className='edit-label'>Show URL to Automatically Updated Data</span>
2647
+ </label>
2648
+ )}
2649
+ <label className='checkbox'>
2650
+ <input
2651
+ type='checkbox'
2652
+ checked={state.general.showFullGeoNameInCSV}
2773
2653
  onChange={event => {
2774
- handleEditorChanges('geoBorderColor', event.target.value)
2654
+ handleEditorChanges('toggleShowFullGeoNameInCSV', event.target.checked)
2775
2655
  }}
2776
- >
2777
- <option value='darkGray'>Dark Gray (Default)</option>
2778
- <option value='sameAsBackground'>White</option>
2779
- </select>
2656
+ />
2657
+ <span className='edit-label'>Include Full Geo Name in CSV Download</span>
2780
2658
  </label>
2781
- <label>
2782
- <span className='edit-label'>Map Color Palette</span>
2659
+ <label className='checkbox'>
2660
+ <input
2661
+ type='checkbox'
2662
+ checked={state.general.showDownloadImgButton}
2663
+ onChange={event => {
2664
+ handleEditorChanges('toggleDownloadImgButton', event.target.checked)
2665
+ }}
2666
+ />
2667
+ <span className='edit-label'>Enable Image Download</span>
2783
2668
  </label>
2784
- {/* <InputCheckbox section="general" subsection="palette" fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} /> */}
2785
- <InputToggle type='3d' section='general' subsection='palette' fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={state.general.palette.isReversed} />
2786
- <span>Sequential</span>
2669
+ {/* <label className='checkbox'>
2670
+ <input
2671
+ type='checkbox'
2672
+ checked={state.general.showDownloadPdfButton}
2673
+ onChange={event => {
2674
+ handleEditorChanges('toggleDownloadPdfButton', event.target.checked)
2675
+ }}
2676
+ />
2677
+ <span className='edit-label'>Enable Pdf Download</span>
2678
+ </label> */}
2679
+ </AccordionItemPanel>
2680
+ </AccordionItem>
2681
+ )}
2682
+ <AccordionItem>
2683
+ {' '}
2684
+ {/* Tooltips */}
2685
+ <AccordionItemHeading>
2686
+ <AccordionItemButton>Interactivity</AccordionItemButton>
2687
+ </AccordionItemHeading>
2688
+ <AccordionItemPanel>
2689
+ <label>
2690
+ <span className='edit-label'>
2691
+ Detail displays on{' '}
2692
+ <Tooltip style={{ textTransform: 'none' }}>
2693
+ <Tooltip.Target>
2694
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2695
+ </Tooltip.Target>
2696
+ <Tooltip.Content>
2697
+ <p>At mobile sizes, information always appears in a popover modal when a user taps on an item.</p>
2698
+ </Tooltip.Content>
2699
+ </Tooltip>
2700
+ </span>
2701
+ <select
2702
+ value={state.tooltips.appearanceType}
2703
+ onChange={event => {
2704
+ handleEditorChanges('appearanceType', event.target.value)
2705
+ }}
2706
+ >
2707
+ <option value='hover'>Hover - Tooltip</option>
2708
+ <option value='click'>Click - Popover Modal</option>
2709
+ </select>
2710
+ </label>
2711
+ {'click' === state.tooltips.appearanceType && <TextField value={tooltips.linkLabel} section='tooltips' fieldName='linkLabel' label='Tooltips Link Label' updateField={updateField} />}
2712
+ <label className='checkbox'>
2713
+ <input
2714
+ type='checkbox'
2715
+ checked={state.tooltips.capitalizeLabels}
2716
+ onChange={event => {
2717
+ handleEditorChanges('capitalizeLabels', event.target.checked)
2718
+ }}
2719
+ />
2720
+ <span className='edit-label'>Capitalize text inside tooltip</span>
2721
+ </label>
2722
+ </AccordionItemPanel>
2723
+ </AccordionItem>
2724
+ <AccordionItem>
2725
+ {' '}
2726
+ {/* Visual */}
2727
+ <AccordionItemHeading>
2728
+ <AccordionItemButton>Visual</AccordionItemButton>
2729
+ </AccordionItemHeading>
2730
+ <AccordionItemPanel>
2731
+ <label>
2732
+ <span className='edit-label'>Header Theme</span>
2787
2733
  <ul className='color-palette'>
2788
- {sequential.map(palette => {
2789
- const colorOne = {
2790
- backgroundColor: colorPalettes[palette][2]
2791
- }
2792
-
2793
- const colorTwo = {
2794
- backgroundColor: colorPalettes[palette][4]
2795
- }
2796
-
2797
- const colorThree = {
2798
- backgroundColor: colorPalettes[palette][6]
2799
- }
2800
-
2734
+ {headerColors.map(palette => {
2801
2735
  return (
2802
2736
  <li
2803
2737
  title={palette}
2804
2738
  key={palette}
2805
2739
  onClick={() => {
2806
- handleEditorChanges('color', palette)
2740
+ handleEditorChanges('headerColor', palette)
2807
2741
  }}
2808
- className={state.color === palette ? 'selected' : ''}
2809
- >
2810
- <span style={colorOne}></span>
2811
- <span style={colorTwo}></span>
2812
- <span style={colorThree}></span>
2813
- </li>
2742
+ className={state.general.headerColor === palette ? 'selected ' + palette : palette}
2743
+ ></li>
2814
2744
  )
2815
2745
  })}
2816
2746
  </ul>
2817
- <span>Non-Sequential</span>
2818
- <ul className='color-palette'>
2819
- {nonSequential.map(palette => {
2820
- const colorOne = {
2821
- backgroundColor: colorPalettes[palette][2]
2822
- }
2747
+ </label>
2748
+ <label className='checkbox'>
2749
+ <input
2750
+ type='checkbox'
2751
+ checked={state.general.showTitle || false}
2752
+ onChange={event => {
2753
+ handleEditorChanges('showTitle', event.target.checked)
2754
+ }}
2755
+ />
2756
+ <span className='edit-label'>Show Title</span>
2757
+ </label>
2823
2758
 
2824
- const colorTwo = {
2825
- backgroundColor: colorPalettes[palette][4]
2826
- }
2759
+ {'navigation' === state.general.type && (
2760
+ <label className='checkbox'>
2761
+ <input
2762
+ type='checkbox'
2763
+ checked={state.general.fullBorder || false}
2764
+ onChange={event => {
2765
+ handleEditorChanges('fullBorder', event.target.checked)
2766
+ }}
2767
+ />
2768
+ <span className='edit-label'>Add border around map</span>
2769
+ </label>
2770
+ )}
2771
+ <label>
2772
+ <span className='edit-label'>Geo Border Color</span>
2773
+ <select
2774
+ value={state.general.geoBorderColor || false}
2775
+ onChange={event => {
2776
+ handleEditorChanges('geoBorderColor', event.target.value)
2777
+ }}
2778
+ >
2779
+ <option value='darkGray'>Dark Gray (Default)</option>
2780
+ <option value='sameAsBackground'>White</option>
2781
+ </select>
2782
+ </label>
2783
+ <label>
2784
+ <span className='edit-label'>Map Color Palette</span>
2785
+ </label>
2786
+ {/* <InputCheckbox section="general" subsection="palette" fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} /> */}
2787
+ <InputToggle type='3d' section='general' subsection='palette' fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={state.general.palette.isReversed} />
2788
+ <span>Sequential</span>
2789
+ <ul className='color-palette'>
2790
+ {sequential.map(palette => {
2791
+ const colorOne = {
2792
+ backgroundColor: colorPalettes[palette][2]
2793
+ }
2827
2794
 
2828
- const colorThree = {
2829
- backgroundColor: colorPalettes[palette][6]
2830
- }
2795
+ const colorTwo = {
2796
+ backgroundColor: colorPalettes[palette][4]
2797
+ }
2831
2798
 
2832
- // hide palettes with too few colors for region maps
2833
- if (colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region') {
2834
- return ''
2835
- }
2836
- return (
2837
- <li
2838
- title={palette}
2839
- key={palette}
2840
- onClick={() => {
2841
- handleEditorChanges('color', palette)
2842
- }}
2843
- className={state.color === palette ? 'selected' : ''}
2844
- >
2845
- <span style={colorOne}></span>
2846
- <span style={colorTwo}></span>
2847
- <span style={colorThree}></span>
2848
- </li>
2849
- )
2850
- })}
2851
- </ul>
2852
- {state.visual.cityStyle === 'circle' && (
2853
- <label>
2854
- Geocode Settings
2855
- <TextField type='number' value={state.visual.geoCodeCircleSize} section='visual' max='10' fieldName='geoCodeCircleSize' label='Geocode Circle Size' updateField={updateField} />
2856
- </label>
2857
- )}
2799
+ const colorThree = {
2800
+ backgroundColor: colorPalettes[palette][6]
2801
+ }
2858
2802
 
2859
- {state.general.type === 'bubble' && (
2860
- <>
2861
- <TextField type='number' value={state.visual.minBubbleSize} section='visual' fieldName='minBubbleSize' label='Minimum Bubble Size' updateField={updateField} />
2862
- <TextField type='number' value={state.visual.maxBubbleSize} section='visual' fieldName='maxBubbleSize' label='Maximum Bubble Size' updateField={updateField} />
2863
- </>
2864
- )}
2865
- {(state.general.geoType === 'world' || (state.general.geoType === 'us' && state.general.type === 'bubble')) && (
2866
- <label className='checkbox'>
2867
- <input
2868
- type='checkbox'
2869
- checked={state.visual.showBubbleZeros}
2870
- onChange={event => {
2871
- handleEditorChanges('showBubbleZeros', event.target.checked)
2872
- }}
2873
- />
2874
- <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
2875
- </label>
2876
- )}
2877
- {state.general.geoType === 'world' && (
2878
- <label className='checkbox'>
2879
- <input
2880
- type='checkbox'
2881
- checked={state.general.allowMapZoom}
2882
- onChange={event => {
2883
- handleEditorChanges('allowMapZoom', event.target.checked)
2803
+ return (
2804
+ <li
2805
+ title={palette}
2806
+ key={palette}
2807
+ onClick={() => {
2808
+ handleEditorChanges('color', palette)
2884
2809
  }}
2885
- />
2886
- <span className='edit-label'>Allow Map Zooming</span>
2887
- </label>
2888
- )}
2889
- {state.general.type === 'bubble' && (
2890
- <label className='checkbox'>
2891
- <input
2892
- type='checkbox'
2893
- checked={state.visual.extraBubbleBorder}
2894
- onChange={event => {
2895
- handleEditorChanges('toggleExtraBubbleBorder', event.target.checked)
2810
+ className={state.color === palette ? 'selected' : ''}
2811
+ >
2812
+ <span style={colorOne}></span>
2813
+ <span style={colorTwo}></span>
2814
+ <span style={colorThree}></span>
2815
+ </li>
2816
+ )
2817
+ })}
2818
+ </ul>
2819
+ <span>Non-Sequential</span>
2820
+ <ul className='color-palette'>
2821
+ {nonSequential.map(palette => {
2822
+ const colorOne = {
2823
+ backgroundColor: colorPalettes[palette][2]
2824
+ }
2825
+
2826
+ const colorTwo = {
2827
+ backgroundColor: colorPalettes[palette][4]
2828
+ }
2829
+
2830
+ const colorThree = {
2831
+ backgroundColor: colorPalettes[palette][6]
2832
+ }
2833
+
2834
+ // hide palettes with too few colors for region maps
2835
+ if (colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region') {
2836
+ return ''
2837
+ }
2838
+ return (
2839
+ <li
2840
+ title={palette}
2841
+ key={palette}
2842
+ onClick={() => {
2843
+ handleEditorChanges('color', palette)
2896
2844
  }}
2897
- />
2898
- <span className='edit-label'>Bubble Map has extra border</span>
2899
- </label>
2900
- )}
2901
- {(state.general.geoType === 'us' || state.general.geoType === 'us-county' || state.general.geoType === 'world') && (
2845
+ className={state.color === palette ? 'selected' : ''}
2846
+ >
2847
+ <span style={colorOne}></span>
2848
+ <span style={colorTwo}></span>
2849
+ <span style={colorThree}></span>
2850
+ </li>
2851
+ )
2852
+ })}
2853
+ </ul>
2854
+ <label>
2855
+ Geocode Settings
2856
+ <TextField type='number' value={state.visual.geoCodeCircleSize} section='visual' max='10' fieldName='geoCodeCircleSize' label='Geocode Circle Size' updateField={updateField} />
2857
+ </label>
2858
+
2859
+ {state.general.type === 'bubble' && (
2860
+ <>
2861
+ <TextField type='number' value={state.visual.minBubbleSize} section='visual' fieldName='minBubbleSize' label='Minimum Bubble Size' updateField={updateField} />
2862
+ <TextField type='number' value={state.visual.maxBubbleSize} section='visual' fieldName='maxBubbleSize' label='Maximum Bubble Size' updateField={updateField} />
2863
+ </>
2864
+ )}
2865
+ {(state.general.geoType === 'world' || (state.general.geoType === 'us' && state.general.type === 'bubble')) && (
2866
+ <label className='checkbox'>
2867
+ <input
2868
+ type='checkbox'
2869
+ checked={state.visual.showBubbleZeros}
2870
+ onChange={event => {
2871
+ handleEditorChanges('showBubbleZeros', event.target.checked)
2872
+ }}
2873
+ />
2874
+ <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
2875
+ </label>
2876
+ )}
2877
+ {state.general.geoType === 'world' && (
2878
+ <label className='checkbox'>
2879
+ <input
2880
+ type='checkbox'
2881
+ checked={state.general.allowMapZoom}
2882
+ onChange={event => {
2883
+ handleEditorChanges('allowMapZoom', event.target.checked)
2884
+ }}
2885
+ />
2886
+ <span className='edit-label'>Allow Map Zooming</span>
2887
+ </label>
2888
+ )}
2889
+ {state.general.type === 'bubble' && (
2890
+ <label className='checkbox'>
2891
+ <input
2892
+ type='checkbox'
2893
+ checked={state.visual.extraBubbleBorder}
2894
+ onChange={event => {
2895
+ handleEditorChanges('toggleExtraBubbleBorder', event.target.checked)
2896
+ }}
2897
+ />
2898
+ <span className='edit-label'>Bubble Map has extra border</span>
2899
+ </label>
2900
+ )}
2901
+ {(state.general.geoType === 'us' || state.general.geoType === 'us-county' || state.general.geoType === 'world') && (
2902
+ <>
2902
2903
  <label>
2903
- <span className='edit-label'>City Style</span>
2904
+ <span className='edit-label'>Default City Style</span>
2904
2905
  <select
2905
2906
  value={state.visual.cityStyle || false}
2906
2907
  onChange={event => {
@@ -2909,65 +2910,154 @@ const EditorPanel = props => {
2909
2910
  >
2910
2911
  <option value='circle'>Circle</option>
2911
2912
  <option value='pin'>Pin</option>
2913
+ <option value='square'>Square</option>
2914
+ <option value='triangle'>Triangle</option>
2915
+ <option value='diamond'>Diamond</option>
2916
+ <option value='star'>Star</option>
2912
2917
  </select>
2913
2918
  </label>
2914
- )}
2915
- <label htmlFor='opacity'>
2916
- <TextField type='number' min={0} max={100} value={state.tooltips.opacity ? state.tooltips.opacity : 100} section='tooltips' fieldName='opacity' label='Tooltip Opacity (%)' updateField={updateField} />
2917
- </label>
2918
- </AccordionItemPanel>
2919
- </AccordionItem>
2920
- <AccordionItem>
2921
- <AccordionItemHeading>
2922
- <AccordionItemButton>Custom Map Layers</AccordionItemButton>
2923
- </AccordionItemHeading>
2924
- <AccordionItemPanel>
2925
- {state.map.layers.length === 0 && <p>There are currently no layers.</p>}
2919
+ <TextField
2920
+ value={state.visual.cityStyleLabel}
2921
+ section='visual'
2922
+ fieldName='cityStyleLabel'
2923
+ label='Label (Optional) '
2924
+ updateField={updateField}
2925
+ tooltip={
2926
+ <Tooltip style={{ textTransform: 'none' }}>
2927
+ <Tooltip.Target>
2928
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
2929
+ </Tooltip.Target>
2930
+ <Tooltip.Content>
2931
+ <p>When a label is provided, the default city style will appear in the legend.</p>
2932
+ </Tooltip.Content>
2933
+ </Tooltip>
2934
+ }
2935
+ />
2936
+ </>
2937
+ )}
2938
+ {/* <AdditionalCityStyles /> */}
2939
+ <>
2940
+ {state.visual.additionalCityStyles.length > 0 &&
2941
+ state.visual.additionalCityStyles.map(({ label, column, value, shape }, i) => {
2942
+ return (
2943
+ <div className='edit-block' key={`additional-city-style-${i}`}>
2944
+ <button
2945
+ className='remove-column'
2946
+ onClick={e => {
2947
+ e.preventDefault()
2948
+ editCityStyles('remove', i, '', '')
2949
+ }}
2950
+ >
2951
+ Remove
2952
+ </button>
2953
+ <p>City Style {i + 1}</p>
2954
+ <label>
2955
+ <span className='edit-label column-heading'>Column with configuration value</span>
2956
+ <select
2957
+ value={column}
2958
+ onChange={e => {
2959
+ editCityStyles('update', i, 'column', e.target.value)
2960
+ }}
2961
+ >
2962
+ {columnsOptions}
2963
+ </select>
2964
+ </label>
2965
+ <label>
2966
+ <span className='edit-label column-heading'>Value to Trigger</span>
2967
+ <input
2968
+ type='text'
2969
+ value={value}
2970
+ onChange={e => {
2971
+ editCityStyles('update', i, 'value', e.target.value)
2972
+ }}
2973
+ ></input>
2974
+ </label>
2975
+ <label>
2976
+ <span className='edit-label column-heading'>Shape</span>
2977
+ <select
2978
+ value={shape}
2979
+ onChange={e => {
2980
+ editCityStyles('update', i, 'shape', e.target.value)
2981
+ }}
2982
+ >
2983
+ {getCityStyleOptions('value')}
2984
+ </select>
2985
+ </label>
2986
+ <label>
2987
+ <span className='edit-label column-heading'>Label</span>
2988
+ <input
2989
+ key={i}
2990
+ type='text'
2991
+ value={label}
2992
+ onChange={e => {
2993
+ editCityStyles('update', i, 'label', e.target.value)
2994
+ }}
2995
+ />
2996
+ </label>
2997
+ </div>
2998
+ )
2999
+ })}
2926
3000
 
2927
- {state.map.layers.map((layer, index) => {
2928
- return (
2929
- <>
2930
- <Accordion allowZeroExpanded>
2931
- <AccordionItem className='series-item map-layers-list'>
2932
- <AccordionItemHeading className='series-item__title map-layers-list--title'>
2933
- <AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
2934
- </AccordionItemHeading>
2935
- <AccordionItemPanel>
2936
- <div className='map-layers-panel'>
2937
- <label htmlFor='layerName'>Layer Name:</label>
2938
- <input type='text' name='layerName' value={layer.name} onChange={e => handleMapLayer(e, index, 'name')} />
2939
- <label htmlFor='layerFilename'>File:</label>
2940
- <input type='text' name='layerFilename' value={layer.url} onChange={e => handleMapLayer(e, index, 'url')} />
2941
- <label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
2942
- <input type='text' name='layerNamespace' value={layer.namespace} onChange={e => handleMapLayer(e, index, 'namespace')} />
2943
- <label htmlFor='layerFill'>Fill Color:</label>
2944
- <input type='text' name='layerFill' value={layer.fill} onChange={e => handleMapLayer(e, index, 'fill')} />
2945
- <label htmlFor='layerFill'>Fill Opacity (%):</label>
2946
- <input type='number' min={0} max={100} name='layerFill' value={layer.fillOpacity ? layer.fillOpacity * 100 : ''} onChange={e => handleMapLayer(e, index, 'fillOpacity')} />
2947
- <label htmlFor='layerStroke'>Stroke Color:</label>
2948
- <input type='text' name='layerStroke' value={layer.stroke} onChange={e => handleMapLayer(e, index, 'stroke')} />
2949
- <label htmlFor='layerStroke'>Stroke Width:</label>
2950
- <input type='number' min={0} max={5} name='layerStrokeWidth' value={layer.strokeWidth} onChange={e => handleMapLayer(e, index, 'strokeWidth')} />
2951
- <label htmlFor='layerTooltip'>Tooltip:</label>
2952
- <textarea name='layerTooltip' value={layer.tooltip} onChange={e => handleMapLayer(e, index, 'tooltip')}></textarea>
2953
- <button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
2954
- </div>
2955
- </AccordionItemPanel>
2956
- </AccordionItem>
2957
- </Accordion>
2958
- </>
2959
- )
2960
- })}
2961
- <button className={'btn full-width'} onClick={handleAddLayer}>
2962
- Add Map Layer
3001
+ <button type='button' onClick={() => editCityStyles('add', 0, '', '')} className='btn full-width'>
3002
+ Add city style
2963
3003
  </button>
2964
- </AccordionItemPanel>
2965
- </AccordionItem>
2966
- {state.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
2967
- </Accordion>
2968
- <AdvancedEditor loadConfig={loadConfig} state={state} convertStateToConfig={convertStateToConfig} />
2969
- </section>
2970
- </section>
3004
+ </>
3005
+ <label htmlFor='opacity'>
3006
+ <TextField type='number' min={0} max={100} value={state.tooltips.opacity ? state.tooltips.opacity : 100} section='tooltips' fieldName='opacity' label='Tooltip Opacity (%)' updateField={updateField} />
3007
+ </label>
3008
+ </AccordionItemPanel>
3009
+ </AccordionItem>
3010
+ <AccordionItem>
3011
+ <AccordionItemHeading>
3012
+ <AccordionItemButton>Custom Map Layers</AccordionItemButton>
3013
+ </AccordionItemHeading>
3014
+ <AccordionItemPanel>
3015
+ {state.map.layers.length === 0 && <p>There are currently no layers.</p>}
3016
+
3017
+ {state.map.layers.map((layer, index) => {
3018
+ return (
3019
+ <>
3020
+ <Accordion allowZeroExpanded>
3021
+ <AccordionItem className='series-item map-layers-list'>
3022
+ <AccordionItemHeading className='series-item__title map-layers-list--title'>
3023
+ <AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
3024
+ </AccordionItemHeading>
3025
+ <AccordionItemPanel>
3026
+ <div className='map-layers-panel'>
3027
+ <label htmlFor='layerName'>Layer Name:</label>
3028
+ <input type='text' name='layerName' value={layer.name} onChange={e => handleMapLayer(e, index, 'name')} />
3029
+ <label htmlFor='layerFilename'>File:</label>
3030
+ <input type='text' name='layerFilename' value={layer.url} onChange={e => handleMapLayer(e, index, 'url')} />
3031
+ <label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
3032
+ <input type='text' name='layerNamespace' value={layer.namespace} onChange={e => handleMapLayer(e, index, 'namespace')} />
3033
+ <label htmlFor='layerFill'>Fill Color:</label>
3034
+ <input type='text' name='layerFill' value={layer.fill} onChange={e => handleMapLayer(e, index, 'fill')} />
3035
+ <label htmlFor='layerFill'>Fill Opacity (%):</label>
3036
+ <input type='number' min={0} max={100} name='layerFill' value={layer.fillOpacity ? layer.fillOpacity * 100 : ''} onChange={e => handleMapLayer(e, index, 'fillOpacity')} />
3037
+ <label htmlFor='layerStroke'>Stroke Color:</label>
3038
+ <input type='text' name='layerStroke' value={layer.stroke} onChange={e => handleMapLayer(e, index, 'stroke')} />
3039
+ <label htmlFor='layerStroke'>Stroke Width:</label>
3040
+ <input type='number' min={0} max={5} name='layerStrokeWidth' value={layer.strokeWidth} onChange={e => handleMapLayer(e, index, 'strokeWidth')} />
3041
+ <label htmlFor='layerTooltip'>Tooltip:</label>
3042
+ <textarea name='layerTooltip' value={layer.tooltip} onChange={e => handleMapLayer(e, index, 'tooltip')}></textarea>
3043
+ <button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
3044
+ </div>
3045
+ </AccordionItemPanel>
3046
+ </AccordionItem>
3047
+ </Accordion>
3048
+ </>
3049
+ )
3050
+ })}
3051
+ <button className={'btn full-width'} onClick={handleAddLayer}>
3052
+ Add Map Layer
3053
+ </button>
3054
+ <p className='layer-purpose-details'>Context should be added to your visualization or associated page to describe the significance of layers that are added to maps.</p>
3055
+ </AccordionItemPanel>
3056
+ </AccordionItem>
3057
+ {state.general.geoType === 'us' && <Panels.PatternSettings name='Pattern Settings' />}
3058
+ </Accordion>
3059
+ <AdvancedEditor loadConfig={loadConfig} state={state} convertStateToConfig={convertStateToConfig} />
3060
+ </Layout.Sidebar>
2971
3061
  </ErrorBoundary>
2972
3062
  )
2973
3063
  }