@cdc/map 2.6.3 → 9.22.9

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 (73) hide show
  1. package/dist/cdcmap.js +32 -18
  2. package/examples/bubble-us.json +363 -0
  3. package/examples/bubble-world.json +427 -0
  4. package/examples/default-county.json +64 -12
  5. package/examples/default-hex.json +477 -0
  6. package/examples/default-usa-regions.json +118 -0
  7. package/examples/default-usa.json +1 -1
  8. package/examples/default-world-data.json +1450 -0
  9. package/examples/default-world.json +5 -3
  10. package/examples/example-city-state.json +46 -1
  11. package/examples/gallery/categorical-qualitative.json +797 -0
  12. package/examples/gallery/categorical-scale-based.json +739 -0
  13. package/examples/gallery/city-state.json +479 -0
  14. package/examples/gallery/county.json +22731 -0
  15. package/examples/gallery/equal-interval.json +1027 -0
  16. package/examples/gallery/equal-number.json +1027 -0
  17. package/examples/gallery/filterable.json +909 -0
  18. package/examples/gallery/hex-filtered.json +420 -0
  19. package/examples/gallery/hex.json +413 -0
  20. package/examples/gallery/single-state.json +21402 -0
  21. package/examples/gallery/world.json +1592 -0
  22. package/examples/private/atsdr.json +439 -0
  23. package/examples/private/atsdr_new.json +436 -0
  24. package/examples/private/bubble.json +285 -0
  25. package/examples/private/city-state.json +428 -0
  26. package/examples/private/cty-issue.json +42768 -0
  27. package/examples/private/default-usa.json +460 -0
  28. package/examples/private/default-world-data.json +1444 -0
  29. package/examples/private/default.json +968 -0
  30. package/examples/private/legend-issue.json +1 -0
  31. package/examples/private/map-rounding-error.json +42759 -0
  32. package/examples/private/map.csv +60 -0
  33. package/examples/private/mdx.json +210 -0
  34. package/examples/private/monkeypox.json +376 -0
  35. package/examples/private/regions.json +52 -0
  36. package/examples/private/valid-data-map.csv +59 -0
  37. package/examples/private/wcmsrd-13881-data.json +2858 -0
  38. package/examples/private/wcmsrd-13881.json +5823 -0
  39. package/examples/private/wcmsrd-14492-data.json +292 -0
  40. package/examples/private/wcmsrd-14492.json +114 -0
  41. package/examples/private/wcmsrd-test.json +268 -0
  42. package/examples/private/world.json +1580 -0
  43. package/examples/private/worldmap.json +1490 -0
  44. package/package.json +51 -50
  45. package/src/CdcMap.js +496 -158
  46. package/src/components/BubbleList.js +244 -0
  47. package/src/components/CityList.js +41 -5
  48. package/src/components/CountyMap.js +16 -6
  49. package/src/components/DataTable.js +25 -18
  50. package/src/components/EditorPanel.js +915 -404
  51. package/src/components/Geo.js +1 -1
  52. package/src/components/Modal.js +2 -1
  53. package/src/components/NavigationMenu.js +4 -3
  54. package/src/components/Sidebar.js +14 -19
  55. package/src/components/SingleStateMap.js +11 -5
  56. package/src/components/UsaMap.js +103 -36
  57. package/src/components/UsaRegionMap.js +320 -0
  58. package/src/components/WorldMap.js +116 -34
  59. package/src/data/country-coordinates.js +250 -0
  60. package/src/data/{dfc-map.json → county-map.json} +0 -0
  61. package/src/data/initial-state.js +20 -2
  62. package/src/data/state-coordinates.js +55 -0
  63. package/src/data/supported-geos.js +96 -15
  64. package/src/data/us-regions-topo-2.json +360525 -0
  65. package/src/data/us-regions-topo.json +37894 -0
  66. package/src/hooks/useColorPalette.ts +96 -0
  67. package/src/index.html +7 -4
  68. package/src/scss/editor-panel.scss +78 -57
  69. package/src/scss/main.scss +1 -1
  70. package/src/scss/map.scss +112 -2
  71. package/src/scss/sidebar.scss +2 -1
  72. package/src/data/color-palettes.js +0 -200
  73. package/src/images/map-folded.svg +0 -1
@@ -10,30 +10,28 @@ import ReactTooltip from 'react-tooltip';
10
10
  import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
11
11
  import { useDebounce } from 'use-debounce';
12
12
 
13
+ import colorPalettes from '@cdc/core/data/colorPalettes';
14
+ import { supportedStatesFipsCodes } from '../data/supported-geos';
15
+ import { GET_PALETTE,useColorPalette } from '../hooks/useColorPalette';
16
+
13
17
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
14
18
  import Waiting from '@cdc/core/components/Waiting';
15
19
 
16
- import MapIcon from '../images/map-folded.svg';
17
- import UsaGraphic from '@cdc/core/assets/usa-graphic.svg';
18
- import WorldGraphic from '@cdc/core/assets/world-graphic.svg';
19
- import AlabamaGraphic from '@cdc/core/assets/alabama-graphic.svg';
20
- import colorPalettes from '../data/color-palettes';
20
+ import UsaGraphic from '@cdc/core/assets/icon-map-usa.svg';
21
+ import UsaRegionGraphic from '@cdc/core/assets/usa-region-graphic.svg';
22
+ import WorldGraphic from '@cdc/core/assets/icon-map-world.svg';
23
+ import AlabamaGraphic from '@cdc/core/assets/icon-map-alabama.svg';
21
24
  import worldDefaultConfig from '../../examples/default-world.json';
22
25
  import usaDefaultConfig from '../../examples/default-usa.json';
23
26
  import countyDefaultConfig from '../../examples/default-county.json';
24
- import QuestionIcon from '@cdc/core/assets/question-circle.svg';
25
27
 
26
- import { supportedStatesFipsCodes } from '../data/supported-geos';
28
+ import InputToggle from '@cdc/core/components/inputs/InputToggle';
29
+ import Tooltip from '@cdc/core/components/ui/Tooltip'
30
+ import Icon from '@cdc/core/components/ui/Icon'
27
31
 
28
- const ReactTags = require('react-tag-autocomplete'); // Future: Lazy
32
+ import AdvancedEditor from '@cdc/core/components/AdvancedEditor';
29
33
 
30
- const Helper = ({ text }) => {
31
- return (
32
- <span className='tooltip helper' data-tip={text}>
33
- <QuestionIcon />
34
- </span>
35
- );
36
- };
34
+ const ReactTags = require('react-tag-autocomplete'); // Future: Lazy
37
35
 
38
36
  const TextField = ({
39
37
  label,
@@ -43,7 +41,7 @@ const TextField = ({
43
41
  updateField,
44
42
  value: stateValue,
45
43
  type = 'input',
46
- helper = null,
44
+ tooltip,
47
45
  ...attributes
48
46
  }) => {
49
47
  const [value, setValue] = useState(stateValue);
@@ -73,7 +71,7 @@ const TextField = ({
73
71
  return (
74
72
  <label>
75
73
  <span className='edit-label column-heading'>
76
- {label} {helper && <Helper text={helper} />}
74
+ {label}{tooltip}
77
75
  </span>
78
76
  {formElement}
79
77
  </label>
@@ -107,6 +105,8 @@ const EditorPanel = (props) => {
107
105
 
108
106
  const [activeFilterValueForDescription, setActiveFilterValueForDescription] = useState([0, 0]);
109
107
 
108
+ const {filteredPallets,filteredQualitative,isPaletteReversed,paletteName} = useColorPalette(colorPalettes,state);
109
+
110
110
  const [editorCatOrder, setEditorCatOrder] = useState(state.legend.categoryValuesOrder || []);
111
111
 
112
112
  const headerColors = [
@@ -162,7 +162,7 @@ const EditorPanel = (props) => {
162
162
  };
163
163
 
164
164
 
165
- const DynamicDesc = ({ label, fieldName, value: stateValue, type = 'input', helper = null, ...attributes }) => {
165
+ const DynamicDesc = ({ label, fieldName, value: stateValue, type = 'input', ...attributes }) => {
166
166
  const [value, setValue] = useState(stateValue);
167
167
 
168
168
  const [debouncedValue] = useDebounce(value, 500);
@@ -182,7 +182,75 @@ const EditorPanel = (props) => {
182
182
  };
183
183
 
184
184
  const handleEditorChanges = async (property, value) => {
185
+
185
186
  switch (property) {
187
+
188
+ // change these to be more generic.
189
+ // updateVisualPropertyValue
190
+ // updateGeneralPropertyValue, etc.
191
+ case 'showBubbleZeros':
192
+ setState({
193
+ ...state,
194
+ visual: {
195
+ ...state.visual,
196
+ showBubbleZeros: value
197
+ }
198
+ })
199
+ break;
200
+
201
+ case 'showEqualNumber':
202
+ setState({
203
+ ...state,
204
+ general: {
205
+ ...state.general,
206
+ equalNumberOptIn: value
207
+ }
208
+ })
209
+ break;
210
+
211
+ case 'hideGeoColumnInTooltip':
212
+ setState({
213
+ ...state,
214
+ general: {
215
+ ...state.general,
216
+ [property]: value
217
+ }
218
+ })
219
+ break;
220
+
221
+ case 'toggleExtraBubbleBorder':
222
+ setState({
223
+ ...state,
224
+ visual: {
225
+ ...state.visual,
226
+ extraBubbleBorder: value
227
+ }
228
+ })
229
+ break;
230
+ case 'allowMapZoom':
231
+ setState({
232
+ ...state,
233
+ general: {
234
+ ...state.general,
235
+ allowMapZoom: value
236
+ }
237
+ })
238
+ break;
239
+ case 'hideGeoColumnInTooltip':
240
+ setState({
241
+ ...state,
242
+ [property]: value
243
+ })
244
+ break;
245
+ case 'hidePrimaryColumnInTooltip':
246
+ setState({
247
+ ...state,
248
+ general: {
249
+ ...state.general,
250
+ [property]: value
251
+ }
252
+ })
253
+ break;
186
254
  case 'showTitle':
187
255
  setState({
188
256
  ...state,
@@ -234,6 +302,14 @@ const EditorPanel = (props) => {
234
302
  },
235
303
  });
236
304
  break;
305
+ case 'handleCityStyle':
306
+ setState({
307
+ ...state,
308
+ visual: {
309
+ cityStyle: value,
310
+ },
311
+ });
312
+ break;
237
313
  case 'geoBorderColor':
238
314
  setState({
239
315
  ...state,
@@ -274,12 +350,33 @@ const EditorPanel = (props) => {
274
350
  });
275
351
  break;
276
352
  case 'legendType':
353
+
354
+ let testForType = Number(typeof state.data[0][state.columns.primary.name]);
355
+ let hasValue = state.data[0][state.columns.primary.name];
356
+ let messages = [];
357
+
358
+ if(!hasValue) {
359
+ messages.push(`There appears to be values missing for data in the primary column ${state.columns.primary.name}`);
360
+ }
361
+
362
+ if (testForType === 'string' && isNaN(testForType) && value !== 'category') {
363
+ messages.push(
364
+ "Error with legend. Primary columns that are text must use a categorical legend type. Try changing the legend type to DEV-12345categorical."
365
+ );
366
+ } else {
367
+ messages = []
368
+ }
369
+
277
370
  setState({
278
371
  ...state,
279
372
  legend: {
280
373
  ...state.legend,
281
374
  type: value,
282
375
  },
376
+ runtime: {
377
+ ...state.runtime,
378
+ editorErrorMessage: messages
379
+ }
283
380
  });
284
381
  break;
285
382
  case 'legendNumber':
@@ -367,6 +464,20 @@ const EditorPanel = (props) => {
367
464
  },
368
465
  });
369
466
  break;
467
+ case 'bubble':
468
+ setState({
469
+ ...state,
470
+ general: {
471
+ ...state.general,
472
+ showSidebar: false,
473
+ type: 'bubble',
474
+ },
475
+ tooltips: {
476
+ ...state.tooltips,
477
+ appearanceType: 'hover',
478
+ },
479
+ });
480
+ break;
370
481
  default:
371
482
  console.warn('Map type not set');
372
483
  break;
@@ -407,6 +518,20 @@ const EditorPanel = (props) => {
407
518
  });
408
519
  ReactTooltip.rebuild();
409
520
  break;
521
+ case 'us-region':
522
+ setState({
523
+ ...state,
524
+ general: {
525
+ ...state.general,
526
+ geoType: 'us-region',
527
+ },
528
+ dataTable: {
529
+ ...state.dataTable,
530
+ forceDisplay: true,
531
+ },
532
+ });
533
+ ReactTooltip.rebuild();
534
+ break;
410
535
  case 'world':
411
536
  setState({
412
537
  ...state,
@@ -558,6 +683,15 @@ const EditorPanel = (props) => {
558
683
  },
559
684
  });
560
685
  break;
686
+ case "classificationType":
687
+ setState({
688
+ ...state,
689
+ legend: {
690
+ ...state.legend,
691
+ type: value,
692
+ },
693
+ });
694
+ break;
561
695
  default:
562
696
  console.warn(`Did not recognize editor property.`);
563
697
  break;
@@ -804,7 +938,7 @@ const EditorPanel = (props) => {
804
938
  }, [state]);
805
939
 
806
940
  useEffect(() => {
807
- if ('category' === state.legend.type && editorCatOrder.length === 0) {
941
+ if ('category' === state.legend.type) {
808
942
  let arr = runtimeLegend.filter((item) => !item.special).map(({ value }) => value);
809
943
 
810
944
  setEditorCatOrder(arr);
@@ -948,7 +1082,12 @@ const EditorPanel = (props) => {
948
1082
  <label>
949
1083
  <span className='edit-label column-heading'>
950
1084
  Filter Column{' '}
951
- <Helper text='Selecting a column will add a dropdown menu below the map legend and allow users to filter based on the values in this column.' />
1085
+ <Tooltip style={{textTransform: 'none'}}>
1086
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1087
+ <Tooltip.Content>
1088
+ <p>Selecting a column will add a dropdown menu below the map legend and allow users to filter based on the values in this column.</p>
1089
+ </Tooltip.Content>
1090
+ </Tooltip>
952
1091
  </span>
953
1092
  <select
954
1093
  value={filter.columnName}
@@ -961,7 +1100,7 @@ const EditorPanel = (props) => {
961
1100
  )}
962
1101
  </select>
963
1102
  </label>
964
-
1103
+
965
1104
  <label>
966
1105
  <span className="edit-filterOrder column-heading">Filter Order</span>
967
1106
  <select value={filter.order} onChange={ (e) => {
@@ -1009,7 +1148,7 @@ const EditorPanel = (props) => {
1009
1148
  </Droppable>
1010
1149
  </DragDropContext>
1011
1150
  }
1012
-
1151
+
1013
1152
  </fieldset>
1014
1153
  );
1015
1154
  });
@@ -1043,21 +1182,28 @@ const EditorPanel = (props) => {
1043
1182
  });
1044
1183
  }
1045
1184
 
1185
+
1186
+ useEffect(()=>{
1187
+ if(paletteName) handleEditorChanges('color',paletteName)
1188
+ },[paletteName]) // dont add handleEditorChanges as a dependency even if it requires
1189
+
1046
1190
  useEffect(() => {
1047
1191
  const parsedData = convertStateToConfig();
1048
1192
  const formattedData = JSON.stringify(parsedData, undefined, 2);
1049
1193
 
1050
1194
  setConfigTextbox(formattedData);
1195
+ }, [state]);
1051
1196
 
1197
+ useEffect(() => {
1052
1198
  // Pass up to Editor if needed
1053
1199
  if (setParentConfig) {
1054
1200
  const newConfig = convertStateToConfig();
1055
1201
  setParentConfig(newConfig);
1056
1202
  }
1057
1203
 
1058
- // eslint-disable-next-line react-hooks/exhaustive-deps
1059
1204
  }, [state]);
1060
1205
 
1206
+
1061
1207
  let numberOfItemsLimit = 8;
1062
1208
 
1063
1209
  const getItemStyle = (isDragging, draggableStyle) => ({
@@ -1084,8 +1230,20 @@ const EditorPanel = (props) => {
1084
1230
  ));
1085
1231
  };
1086
1232
 
1233
+ const Error = () => {
1234
+ return (
1235
+ <section className="waiting">
1236
+ <section className="waiting-container">
1237
+ <h3>Error With Configuration</h3>
1238
+ <p>{state.runtime.editorErrorMessage}</p>
1239
+ </section>
1240
+ </section>
1241
+ );
1242
+ }
1243
+
1087
1244
  return (
1088
1245
  <ErrorBoundary component='EditorPanel'>
1246
+ {state?.runtime?.editorErrorMessage.length > 0 && <Error />}
1089
1247
  {requiredColumns && (
1090
1248
  <Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
1091
1249
  )}
@@ -1095,7 +1253,8 @@ const EditorPanel = (props) => {
1095
1253
  onClick={onBackClick}
1096
1254
  data-html2canvas-ignore
1097
1255
  ></button>
1098
- <section className={displayPanel ? 'editor-panel' : 'hidden editor-panel'} data-html2canvas-ignore>
1256
+
1257
+ <section className={displayPanel ? 'editor-panel cove' : 'hidden editor-panel cove'} data-html2canvas-ignore>
1099
1258
  <ReactTooltip html={true} multiline={true} />
1100
1259
  <span className='base-label'>Configure Map</span>
1101
1260
  <section className='form-container'>
@@ -1114,38 +1273,61 @@ const EditorPanel = (props) => {
1114
1273
  <span>Geography</span>
1115
1274
  </span>
1116
1275
  <ul className='geo-buttons'>
1117
- <li
1276
+ <button
1118
1277
  className={
1119
1278
  state.general.geoType === 'us' ||
1120
1279
  state.general.geoType === 'us-county'
1121
1280
  ? 'active'
1122
1281
  : ''
1123
1282
  }
1124
- onClick={() => handleEditorChanges('geoType', 'us')}
1283
+ onClick={ (e) => {
1284
+ e.preventDefault();
1285
+ handleEditorChanges('geoType', 'us')
1286
+ }}
1125
1287
  >
1126
1288
  <UsaGraphic />
1127
1289
  <span>United States</span>
1128
- </li>
1129
- <li
1290
+ </button>
1291
+ <button
1292
+ className={state.general.geoType === 'us-region' ? 'active' : ''}
1293
+ onClick={ (e) => {
1294
+ e.preventDefault();
1295
+ handleEditorChanges('geoType', 'us-region')
1296
+ }}
1297
+ >
1298
+ <UsaRegionGraphic />
1299
+ <span>U.S. Region</span>
1300
+ </button>
1301
+ <button
1130
1302
  className={state.general.geoType === 'world' ? 'active' : ''}
1131
- onClick={() => handleEditorChanges('geoType', 'world')}
1303
+ onClick={ (e) => {
1304
+ e.preventDefault();
1305
+ handleEditorChanges('geoType', 'world')
1306
+ }
1307
+ }
1132
1308
  >
1133
1309
  <WorldGraphic />
1134
1310
  <span>World</span>
1135
- </li>
1136
- <li
1311
+ </button>
1312
+ <button
1137
1313
  className={state.general.geoType === 'single-state' ? 'active' : ''}
1138
- onClick={() => handleEditorChanges('geoType', 'single-state')}
1314
+ onClick={(e) => {
1315
+ e.preventDefault();
1316
+ handleEditorChanges('geoType', 'single-state')
1317
+ }}
1139
1318
  >
1140
1319
  <AlabamaGraphic />
1141
1320
  <span>U.S. State</span>
1142
- </li>
1321
+ </button>
1143
1322
  </ul>
1144
1323
  </label>
1145
1324
  {/* Select > State or County Map */}
1146
1325
  {(state.general.geoType === 'us' || state.general.geoType === 'us-county') && (
1147
1326
  <label>
1148
- <span className='edit-label column-heading'>Map Type</span>
1327
+ <span className='edit-label column-heading'>
1328
+ Geography Subtype
1329
+
1330
+ </span>
1149
1331
  <select
1150
1332
  value={state.general.geoType}
1151
1333
  onChange={(event) => {
@@ -1178,7 +1360,15 @@ const EditorPanel = (props) => {
1178
1360
  )}
1179
1361
  {/* Type */}
1180
1362
  <label>
1181
- <span className='edit-label column-heading'>Map Type</span>
1363
+ <span className='edit-label column-heading'>
1364
+ Map Type
1365
+ <Tooltip style={{textTransform: 'none'}}>
1366
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1367
+ <Tooltip.Content>
1368
+ <p>Select "Data" to create a color-coded data map. To create a navigation-only map, select "Navigation."</p>
1369
+ </Tooltip.Content>
1370
+ </Tooltip>
1371
+ </span>
1182
1372
  <select
1183
1373
  value={state.general.type}
1184
1374
  onChange={(event) => {
@@ -1187,8 +1377,44 @@ const EditorPanel = (props) => {
1187
1377
  >
1188
1378
  <option value='data'>Data</option>
1189
1379
  <option value='navigation'>Navigation</option>
1380
+ { (state.general.geoType === 'world' || state.general.geoType === 'us') && <option value="bubble">Bubble</option>}
1190
1381
  </select>
1191
1382
  </label>
1383
+ <label>
1384
+ <span className="edit-label">Data Classification Type</span>
1385
+ <div>
1386
+ <label>
1387
+ <input
1388
+ type="radio"
1389
+ name="equalnumber"
1390
+ value="equalnumber"
1391
+ checked={state.legend.type === "equalnumber"}
1392
+ onChange={(e) =>
1393
+ handleEditorChanges(
1394
+ "classificationType",
1395
+ e.target.value
1396
+ )
1397
+ }
1398
+ />
1399
+ Numeric/Quantitative
1400
+ </label>
1401
+ <label>
1402
+ <input
1403
+ type="radio"
1404
+ name="category"
1405
+ value="category"
1406
+ checked={state.legend.type === "category"}
1407
+ onChange={(e) =>
1408
+ handleEditorChanges(
1409
+ "classificationType",
1410
+ e.target.value
1411
+ )
1412
+ }
1413
+ />
1414
+ Categorical
1415
+ </label>
1416
+ </div>
1417
+ </label>
1192
1418
  {/* SubType */}
1193
1419
  {'us' === state.general.geoType && 'data' === state.general.type && (
1194
1420
  <label className='checkbox mt-4'>
@@ -1203,7 +1429,7 @@ const EditorPanel = (props) => {
1203
1429
  </label>
1204
1430
  )}
1205
1431
  {'us' === state.general.geoType &&
1206
- 'data' === state.general.type &&
1432
+ 'bubble' !== state.general.type &&
1207
1433
  false === state.general.displayAsHex && (
1208
1434
  <label className='checkbox'>
1209
1435
  <input
@@ -1226,13 +1452,51 @@ const EditorPanel = (props) => {
1226
1452
  </AccordionItemHeading>
1227
1453
  <AccordionItemPanel>
1228
1454
  <TextField
1229
- value={state.general.title}
1455
+ value={general.title}
1230
1456
  updateField={updateField}
1231
1457
  section='general'
1232
1458
  fieldName='title'
1233
1459
  label='Title'
1234
1460
  placeholder='Map Title'
1235
- helper='For accessibility reasons, you should enter a title even if you are not planning on displaying it.'
1461
+ tooltip={
1462
+ <Tooltip style={{textTransform: 'none'}}>
1463
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1464
+ <Tooltip.Content>
1465
+ <p>For accessibility reasons, you should enter a title even if you are not planning on displaying it.</p>
1466
+ </Tooltip.Content>
1467
+ </Tooltip>
1468
+ }
1469
+ />
1470
+ <TextField
1471
+ value={general.superTitle || ''}
1472
+ updateField={updateField}
1473
+ section='general'
1474
+ fieldName='superTitle'
1475
+ label='Super Title'
1476
+ tooltip={
1477
+ <Tooltip style={{textTransform: 'none'}}>
1478
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1479
+ <Tooltip.Content>
1480
+ <p>Super Title</p>
1481
+ </Tooltip.Content>
1482
+ </Tooltip>
1483
+ }
1484
+ />
1485
+ <TextField
1486
+ type='textarea'
1487
+ value={general.introText}
1488
+ updateField={updateField}
1489
+ section='general'
1490
+ fieldName='introText'
1491
+ label='Intro Text'
1492
+ tooltip={
1493
+ <Tooltip style={{textTransform: 'none'}}>
1494
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1495
+ <Tooltip.Content>
1496
+ <p>Intro Text</p>
1497
+ </Tooltip.Content>
1498
+ </Tooltip>
1499
+ }
1236
1500
  />
1237
1501
  <TextField
1238
1502
  type='textarea'
@@ -1241,6 +1505,30 @@ const EditorPanel = (props) => {
1241
1505
  section='general'
1242
1506
  fieldName='subtext'
1243
1507
  label='Subtext'
1508
+ tooltip={
1509
+ <Tooltip style={{textTransform: 'none'}}>
1510
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1511
+ <Tooltip.Content>
1512
+ <p>Enter supporting text to display below the data visualization, if applicable. The following HTML tags are supported: strong, em, sup, and sub.</p>
1513
+ </Tooltip.Content>
1514
+ </Tooltip>
1515
+ }
1516
+ />
1517
+ <TextField
1518
+ type='textarea'
1519
+ value={general.footnotes}
1520
+ updateField={updateField}
1521
+ section='general'
1522
+ fieldName='footnotes'
1523
+ label='Footnotes'
1524
+ tooltip={
1525
+ <Tooltip style={{textTransform: 'none'}}>
1526
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1527
+ <Tooltip.Content>
1528
+ <p>Footnotes</p>
1529
+ </Tooltip.Content>
1530
+ </Tooltip>
1531
+ }
1244
1532
  />
1245
1533
  {'us' === state.general.geoType && (
1246
1534
  <TextField
@@ -1266,7 +1554,15 @@ const EditorPanel = (props) => {
1266
1554
  </AccordionItemHeading>
1267
1555
  <AccordionItemPanel>
1268
1556
  <label className='edit-block geo'>
1269
- <span className='edit-label column-heading'>Geography</span>
1557
+ <span className='edit-label column-heading'>
1558
+ Geography
1559
+ <Tooltip style={{textTransform: 'none'}}>
1560
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1561
+ <Tooltip.Content>
1562
+ <p>Select the source column containing the map location names or, for county-level maps, the FIPS codes.</p>
1563
+ </Tooltip.Content>
1564
+ </Tooltip>
1565
+ </span>
1270
1566
  <select
1271
1567
  value={state.columns.geo ? state.columns.geo.name : columnsOptions[0]}
1272
1568
  onChange={(event) => {
@@ -1276,155 +1572,223 @@ const EditorPanel = (props) => {
1276
1572
  {columnsOptions}
1277
1573
  </select>
1278
1574
  </label>
1279
- {'navigation' !== state.general.type && (
1280
- <fieldset className='primary-fieldset edit-block'>
1281
- <label>
1282
- <span className='edit-label column-heading'>Primary</span>
1283
- <select
1284
- value={
1285
- state.columns.primary
1286
- ? state.columns.primary.name
1287
- : columnsOptions[0]
1288
- }
1289
- onChange={(event) => {
1290
- editColumn('primary', 'name', event.target.value);
1291
- }}
1292
- >
1293
- {columnsOptions}
1294
- </select>
1295
- </label>
1296
- <TextField
1297
- value={columns.primary.label}
1298
- section='columns'
1299
- subsection='primary'
1300
- fieldName='label'
1301
- label='Label'
1302
- updateField={updateField}
1303
- />
1304
- <ul className='column-edit'>
1305
- <li className='three-col'>
1306
- <TextField
1307
- value={columns.primary.prefix}
1308
- section='columns'
1309
- subsection='primary'
1310
- fieldName='prefix'
1311
- label='Prefix'
1312
- updateField={updateField}
1313
- />
1314
- <TextField
1315
- value={columns.primary.suffix}
1316
- section='columns'
1317
- subsection='primary'
1318
- fieldName='suffix'
1319
- label='Suffix'
1320
- updateField={updateField}
1321
- />
1322
- <TextField
1323
- type='number'
1324
- value={columns.primary.roundToPlace}
1325
- section='columns'
1326
- subsection='primary'
1327
- fieldName='roundToPlace'
1328
- label='Round'
1329
- updateField={updateField}
1330
- />
1331
- </li>
1332
- <li>
1333
- <label className='checkbox'>
1334
- <input
1335
- type='checkbox'
1336
- checked={state.columns.primary.useCommas}
1337
- onChange={(event) => {
1338
- editColumn(
1339
- 'primary',
1340
- 'useCommas',
1341
- event.target.checked
1342
- );
1343
- }}
1344
- />
1345
- <span className='edit-label'>Add Commas to Numbers</span>
1346
- </label>
1347
- </li>
1348
- <li>
1349
- <label className='checkbox'>
1350
- <input
1351
- type='checkbox'
1352
- checked={state.columns.primary.dataTable || false}
1353
- onChange={(event) => {
1354
- editColumn(
1355
- 'primary',
1356
- 'dataTable',
1357
- event.target.checked
1358
- );
1359
- }}
1360
- />
1361
- <span className='edit-label'>Display in Data Table</span>
1362
- </label>
1363
- </li>
1364
- <li>
1365
- <label className='checkbox'>
1366
- <input
1367
- type='checkbox'
1368
- checked={state.columns.primary.tooltip || false}
1369
- onChange={(event) => {
1370
- editColumn('primary', 'tooltip', event.target.checked);
1371
- }}
1372
- />
1373
- <span className='edit-label'>Display in Tooltips</span>
1374
- </label>
1375
- </li>
1376
- <li>
1377
- <label>
1378
- <span className='edit-label'>Special Classes</span>
1379
- </label>
1380
- {specialClasses.map((specialClass, i) => (
1381
- <div className="edit-block" key={`special-class-${i}`}>
1382
- <button className="remove-column"
1383
- onClick={(e) => {
1384
- e.preventDefault();
1385
- editColumn('primary', 'specialClassDelete', i);
1386
- }}
1387
- >Remove</button>
1388
- <p>Special Class {i + 1}</p>
1389
- <label>
1390
- <span className="edit-label column-heading">Data Key</span>
1391
- <select value={specialClass.key} onChange={(e) => {
1392
- editColumn('primary', 'specialClassEdit', {prop: 'key', index: i, value: e.target.value});
1393
- }}>
1394
- {columnsOptions}
1395
- </select>
1396
- </label>
1397
- <label>
1398
- <span className="edit-label column-heading">Value</span>
1399
- <select value={specialClass.value} onChange={(e) => {
1400
- editColumn('primary', 'specialClassEdit', {prop: 'value', index: i, value: e.target.value});
1401
- }}>
1402
- <option value="">- Select Value -</option>
1403
- {columnsByKey[specialClass.key] && columnsByKey[specialClass.key].sort().map(option => (
1404
- <option key={`special-class-value-option-${i}-${option}`}>{option}</option>
1405
- ))}
1406
- </select>
1407
- </label>
1408
- <label>
1409
- <span className="edit-label column-heading">Label</span>
1410
- <input type="text" value={specialClass.label} onChange={(e) => {
1411
- editColumn('primary', 'specialClassEdit', {prop: 'label', index: i, value: e.target.value});
1412
- }} />
1413
- </label>
1414
- </div>
1415
- ))}
1416
- <button className="btn full-width"
1417
- onClick={(e) => {
1418
- e.preventDefault();
1419
- editColumn('primary', 'specialClassAdd', {});
1420
- }}
1421
- >Add SpecialClass</button>
1422
- </li>
1423
- </ul>
1424
- </fieldset>
1425
- )}
1575
+
1576
+ {'navigation' !== state.general.type && (
1577
+ <fieldset className='primary-fieldset edit-block'>
1578
+ <label>
1579
+ <span className='edit-label column-heading'>
1580
+ Data Column
1581
+ <Tooltip style={{textTransform: 'none'}}>
1582
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1583
+ <Tooltip.Content>
1584
+ <p>Select the source column containing the categorical or numeric values to be mapped.</p>
1585
+ </Tooltip.Content>
1586
+ </Tooltip>
1587
+ </span>
1588
+ <select
1589
+ value={
1590
+ state.columns.primary
1591
+ ? state.columns.primary.name
1592
+ : columnsOptions[0]
1593
+ }
1594
+ onChange={(event) => {
1595
+ editColumn('primary', 'name', event.target.value);
1596
+ }}
1597
+ >
1598
+ {columnsOptions}
1599
+ </select>
1600
+ </label>
1601
+ <TextField
1602
+ value={columns.primary.label}
1603
+ section='columns'
1604
+ subsection='primary'
1605
+ fieldName='label'
1606
+ label='Label'
1607
+ updateField={updateField}
1608
+ tooltip={
1609
+ <Tooltip style={{textTransform: 'none'}}>
1610
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1611
+ <Tooltip.Content>
1612
+ <p>Enter a data label for use in tooltips and the data table.</p>
1613
+ </Tooltip.Content>
1614
+ </Tooltip>
1615
+ }
1616
+ />
1617
+ <ul className='column-edit'>
1618
+ <li className='three-col'>
1619
+ <TextField
1620
+ value={columns.primary.prefix}
1621
+ section='columns'
1622
+ subsection='primary'
1623
+ fieldName='prefix'
1624
+ label='Prefix'
1625
+ updateField={updateField}
1626
+ />
1627
+ <TextField
1628
+ value={columns.primary.suffix}
1629
+ section='columns'
1630
+ subsection='primary'
1631
+ fieldName='suffix'
1632
+ label='Suffix'
1633
+ updateField={updateField}
1634
+ />
1635
+ <TextField
1636
+ type='number'
1637
+ value={columns.primary.roundToPlace}
1638
+ section='columns'
1639
+ subsection='primary'
1640
+ fieldName='roundToPlace'
1641
+ label='Round'
1642
+ updateField={updateField}
1643
+ min={0}
1644
+ />
1645
+ </li>
1646
+ <li>
1647
+ <label className='checkbox'>
1648
+ <input
1649
+ type='checkbox'
1650
+ checked={state.columns.primary.useCommas}
1651
+ onChange={(event) => {
1652
+ editColumn(
1653
+ 'primary',
1654
+ 'useCommas',
1655
+ event.target.checked
1656
+ );
1657
+ }}
1658
+ />
1659
+ <span className='edit-label'>Add Commas to Numbers</span>
1660
+ </label>
1661
+ </li>
1662
+ <li>
1663
+ <label className='checkbox'>
1664
+ <input
1665
+ type='checkbox'
1666
+ checked={state.columns.primary.dataTable || false}
1667
+ onChange={(event) => {
1668
+ editColumn(
1669
+ 'primary',
1670
+ 'dataTable',
1671
+ event.target.checked
1672
+ );
1673
+ }}
1674
+ />
1675
+ <span className='edit-label'>Display in Data Table</span>
1676
+ </label>
1677
+ </li>
1678
+ <li>
1679
+ <label className='checkbox'>
1680
+ <input
1681
+ type='checkbox'
1682
+ checked={state.columns.primary.tooltip || false}
1683
+ onChange={(event) => {
1684
+ editColumn('primary', 'tooltip', event.target.checked);
1685
+ }}
1686
+ />
1687
+ <span className='edit-label'>Display in Tooltips</span>
1688
+ </label>
1689
+ </li>
1690
+ </ul>
1691
+ </fieldset>
1692
+ )}
1693
+
1694
+
1695
+
1696
+ { (state.general.type === 'bubble') && state.legend.type === 'category' && (
1697
+ <fieldset className='primary-fieldset edit-block'>
1698
+ <label>
1699
+ <span className='edit-label column-heading'>
1700
+ Category Column
1701
+ <Tooltip style={{textTransform: 'none'}}>
1702
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1703
+ <Tooltip.Content>
1704
+ <p>Select the source column containing the categorical bubble values to be mapped.</p>
1705
+ </Tooltip.Content>
1706
+ </Tooltip>
1707
+ </span>
1708
+ <select
1709
+ value={
1710
+ state.columns.categorical
1711
+ ? state.columns.categorical.name
1712
+ : columnsOptions[0]
1713
+ }
1714
+ onChange={(event) => {
1715
+ editColumn('categorical', 'name', event.target.value);
1716
+ }}
1717
+ >
1718
+ {columnsOptions}
1719
+ </select>
1720
+ </label>
1721
+ </fieldset>
1722
+ )}
1723
+
1724
+ {'navigation' !== state.general.type && (
1725
+ <fieldset className="primary-fieldset edit-block">
1726
+ <label>
1727
+ <span className='edit-label'>
1728
+ Special Classes
1729
+ <Tooltip style={{textTransform: 'none'}}>
1730
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1731
+ <Tooltip.Content>
1732
+ <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>
1733
+ </Tooltip.Content>
1734
+ </Tooltip>
1735
+ </span>
1736
+ </label>
1737
+ {specialClasses.map((specialClass, i) => (
1738
+ <div className="edit-block" key={`special-class-${i}`}>
1739
+ <button className="remove-column"
1740
+ onClick={(e) => {
1741
+ e.preventDefault();
1742
+ editColumn('primary', 'specialClassDelete', i);
1743
+ }}
1744
+ >Remove</button>
1745
+ <p>Special Class {i + 1}</p>
1746
+ <label>
1747
+ <span className="edit-label column-heading">Data Key</span>
1748
+ <select value={specialClass.key} onChange={(e) => {
1749
+ editColumn('primary', 'specialClassEdit', {prop: 'key', index: i, value: e.target.value});
1750
+ }}>
1751
+ {columnsOptions}
1752
+ </select>
1753
+ </label>
1754
+ <label>
1755
+ <span className="edit-label column-heading">Value</span>
1756
+ <select value={specialClass.value} onChange={(e) => {
1757
+ editColumn('primary', 'specialClassEdit', {prop: 'value', index: i, value: e.target.value});
1758
+ }}>
1759
+ <option value="">- Select Value -</option>
1760
+ {columnsByKey[specialClass.key] && columnsByKey[specialClass.key].sort().map(option => (
1761
+ <option key={`special-class-value-option-${i}-${option}`}>{option}</option>
1762
+ ))}
1763
+ </select>
1764
+ </label>
1765
+ <label>
1766
+ <span className="edit-label column-heading">Label</span>
1767
+ <input type="text" value={specialClass.label} onChange={(e) => {
1768
+ editColumn('primary', 'specialClassEdit', {prop: 'label', index: i, value: e.target.value});
1769
+ }} />
1770
+ </label>
1771
+ </div>
1772
+ ))}
1773
+ <button className="btn full-width" onClick={(e) => {
1774
+ e.preventDefault();
1775
+ editColumn('primary', 'specialClassAdd', {});
1776
+ }}>
1777
+ Add Special Class
1778
+ </button>
1779
+ </fieldset>
1780
+ )}
1781
+
1426
1782
  <label className='edit-block navigate column-heading'>
1427
- <span className='edit-label column-heading'>Navigation</span>
1783
+ <span className='edit-label column-heading'>
1784
+ Navigation
1785
+ <Tooltip style={{textTransform: 'none'}}>
1786
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1787
+ <Tooltip.Content>
1788
+ <p>To provide end users with navigation functionality, select the source column containing the navigation URLs.</p>
1789
+ </Tooltip.Content>
1790
+ </Tooltip>
1791
+ </span>
1428
1792
  <select
1429
1793
  value={
1430
1794
  state.columns.navigate ? state.columns.navigate.name : columnsOptions[0]
@@ -1437,118 +1801,130 @@ const EditorPanel = (props) => {
1437
1801
  </select>
1438
1802
  </label>
1439
1803
  {'navigation' !== state.general.type &&
1440
- additionalColumns.map((val) => (
1441
- <fieldset className='edit-block' key={val}>
1442
- <button
1443
- className='remove-column'
1444
- onClick={(event) => {
1445
- event.preventDefault();
1446
- removeAdditionalColumn(val);
1447
- }}
1448
- >
1449
- Remove
1450
- </button>
1451
- <label>
1452
- <span className='edit-label column-heading'>Column</span>
1453
- <select
1454
- value={
1455
- state.columns[val]
1456
- ? state.columns[val].name
1457
- : columnsOptions[0]
1458
- }
1459
- onChange={(event) => {
1460
- editColumn(val, 'name', event.target.value);
1461
- }}
1462
- >
1463
- {columnsOptions}
1464
- </select>
1465
- </label>
1466
- <TextField
1467
- value={columns[val].label}
1468
- section='columns'
1469
- subsection={val}
1470
- fieldName='label'
1471
- label='Label'
1472
- updateField={updateField}
1473
- />
1474
- <ul className='column-edit'>
1475
- <li className='three-col'>
1476
- <TextField
1477
- value={columns[val].prefix}
1478
- section='columns'
1479
- subsection={val}
1480
- fieldName='prefix'
1481
- label='Prefix'
1482
- updateField={updateField}
1483
- />
1484
- <TextField
1485
- value={columns[val].suffix}
1486
- section='columns'
1487
- subsection={val}
1488
- fieldName='suffix'
1489
- label='Suffix'
1490
- updateField={updateField}
1491
- />
1492
- <TextField
1493
- type='number'
1494
- value={columns[val].roundToPlace}
1495
- section='columns'
1496
- subsection={val}
1497
- fieldName='roundToPlace'
1498
- label='Round'
1499
- updateField={updateField}
1500
- />
1501
- </li>
1502
- <li>
1503
- <label className='checkbox'>
1504
- <input
1505
- type='checkbox'
1506
- checked={state.columns[val].useCommas}
1507
- onChange={(event) => {
1508
- editColumn(val, 'useCommas', event.target.checked);
1509
- }}
1510
- />
1511
- <span className='edit-label'>Add Commas to Numbers</span>
1512
- </label>
1513
- </li>
1514
- <li>
1515
- <label className='checkbox'>
1516
- <input
1517
- type='checkbox'
1518
- checked={state.columns[val].dataTable}
1519
- onChange={(event) => {
1520
- editColumn(val, 'dataTable', event.target.checked);
1521
- }}
1522
- />
1523
- <span className='edit-label'>Display in Data Table</span>
1524
- </label>
1525
- </li>
1526
- <li>
1527
- <label className='checkbox'>
1528
- <input
1529
- type='checkbox'
1530
- checked={state.columns[val].tooltip}
1531
- onChange={(event) => {
1532
- editColumn(val, 'tooltip', event.target.checked);
1533
- }}
1534
- />
1535
- <span className='edit-label'>Display in Tooltips</span>
1536
- </label>
1537
- </li>
1538
- </ul>
1539
- </fieldset>
1540
- ))}
1541
- {'navigation' !== state.general.type && (
1542
- <button
1543
- className={'btn full-width'}
1544
- onClick={(event) => {
1545
- event.preventDefault();
1546
- addAdditionalColumn(additionalColumns.length + 1);
1547
- }}
1548
- >
1549
- Add Column
1550
- </button>
1551
- )}
1804
+ <fieldset className="primary-fieldset edit-block">
1805
+ <label>
1806
+ <span className="edit-label">
1807
+ Additional Columns
1808
+ <Tooltip style={{textTransform: 'none'}}>
1809
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
1810
+ <Tooltip.Content>
1811
+ <p>You can specify additional columns to display in tooltips and / or the supporting data table.</p>
1812
+ </Tooltip.Content>
1813
+ </Tooltip>
1814
+ </span>
1815
+ </label>
1816
+ {additionalColumns.map((val) => (
1817
+ <fieldset className="edit-block" key={val}>
1818
+ <button
1819
+ className="remove-column"
1820
+ onClick={(event) => {
1821
+ event.preventDefault()
1822
+ removeAdditionalColumn(val)
1823
+ }}
1824
+ >
1825
+ Remove
1826
+ </button>
1827
+ <label>
1828
+ <span className="edit-label column-heading">Column</span>
1829
+ <select
1830
+ value={
1831
+ state.columns[val]
1832
+ ? state.columns[val].name
1833
+ : columnsOptions[0]
1834
+ }
1835
+ onChange={(event) => {
1836
+ editColumn(val, 'name', event.target.value)
1837
+ }}
1838
+ >
1839
+ {columnsOptions}
1840
+ </select>
1841
+ </label>
1842
+ <TextField
1843
+ value={columns[val].label}
1844
+ section="columns"
1845
+ subsection={val}
1846
+ fieldName="label"
1847
+ label="Label"
1848
+ updateField={updateField}
1849
+ />
1850
+ <ul className="column-edit">
1851
+ <li className="three-col">
1852
+ <TextField
1853
+ value={columns[val].prefix}
1854
+ section="columns"
1855
+ subsection={val}
1856
+ fieldName="prefix"
1857
+ label="Prefix"
1858
+ updateField={updateField}
1859
+ />
1860
+ <TextField
1861
+ value={columns[val].suffix}
1862
+ section="columns"
1863
+ subsection={val}
1864
+ fieldName="suffix"
1865
+ label="Suffix"
1866
+ updateField={updateField}
1867
+ />
1868
+ <TextField
1869
+ type="number"
1870
+ value={columns[val].roundToPlace}
1871
+ section="columns"
1872
+ subsection={val}
1873
+ fieldName="roundToPlace"
1874
+ label="Round"
1875
+ updateField={updateField}
1876
+ />
1877
+ </li>
1878
+ <li>
1879
+ <label className="checkbox">
1880
+ <input
1881
+ type="checkbox"
1882
+ checked={state.columns[val].useCommas}
1883
+ onChange={(event) => {
1884
+ editColumn(val, '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[val].dataTable}
1895
+ onChange={(event) => {
1896
+ editColumn(val, 'dataTable', event.target.checked)
1897
+ }}
1898
+ />
1899
+ <span className="edit-label">Display in Data Table</span>
1900
+ </label>
1901
+ </li>
1902
+ <li>
1903
+ <label className="checkbox">
1904
+ <input
1905
+ type="checkbox"
1906
+ checked={state.columns[val].tooltip}
1907
+ onChange={(event) => {
1908
+ editColumn(val, 'tooltip', event.target.checked)
1909
+ }}
1910
+ />
1911
+ <span className="edit-label">Display in Tooltips</span>
1912
+ </label>
1913
+ </li>
1914
+ </ul>
1915
+ </fieldset>
1916
+ ))}
1917
+ <button
1918
+ className={'btn full-width'}
1919
+ onClick={(event) => {
1920
+ event.preventDefault();
1921
+ addAdditionalColumn(additionalColumns.length + 1);
1922
+ }}
1923
+ >
1924
+ Add Column
1925
+ </button>
1926
+ </fieldset>
1927
+ }
1552
1928
  </AccordionItemPanel>
1553
1929
  </AccordionItem>{' '}
1554
1930
  {/* Columns */}
@@ -1560,6 +1936,7 @@ const EditorPanel = (props) => {
1560
1936
  <AccordionItemButton>Legend</AccordionItemButton>
1561
1937
  </AccordionItemHeading>
1562
1938
  <AccordionItemPanel>
1939
+ {(state.legend.type === "equalnumber" || state.legend.type === 'equalinterval') && (
1563
1940
  <label>
1564
1941
  <span className='edit-label'>Legend Type</span>
1565
1942
  <select
@@ -1568,11 +1945,49 @@ const EditorPanel = (props) => {
1568
1945
  handleEditorChanges('legendType', event.target.value);
1569
1946
  }}
1570
1947
  >
1571
- <option value='equalnumber'>Equal Number</option>
1948
+ <option value='equalnumber'>Equal Number (Quantiles)</option>
1572
1949
  <option value='equalinterval'>Equal Interval</option>
1573
- <option value='category'>Categorical</option>
1574
1950
  </select>
1951
+ </label>
1952
+ )}
1953
+ {'navigation' !== state.general.type && (
1954
+ <label className='checkbox'>
1955
+ <input
1956
+ type='checkbox'
1957
+ checked={state.general.showSidebar || false}
1958
+ onChange={(event) => {
1959
+ handleEditorChanges('showSidebar', event.target.checked);
1960
+ }}
1961
+ />
1962
+ <span className='edit-label'>Show Legend</span>
1963
+ </label>
1964
+ )}
1965
+ {'navigation' !== state.general.type && (
1966
+ <label>
1967
+ <span className='edit-label'>Legend Position</span>
1968
+ <select
1969
+ value={legend.position || false}
1970
+ onChange={(event) => {
1971
+ handleEditorChanges('sidebarPosition', event.target.value);
1972
+ }}
1973
+ >
1974
+ <option value='side'>Side</option>
1975
+ <option value='bottom'>Bottom</option>
1976
+ </select>
1977
+ </label>
1978
+ )}
1979
+ {'side' === legend.position && (
1980
+ <label className='checkbox'>
1981
+ <input
1982
+ type='checkbox'
1983
+ checked={legend.singleColumn}
1984
+ onChange={(event) => {
1985
+ handleEditorChanges('singleColumnLegend', event.target.checked);
1986
+ }}
1987
+ />
1988
+ <span className='edit-label'>Single Column Legend</span>
1575
1989
  </label>
1990
+ )}
1576
1991
  {'category' !== legend.type && (
1577
1992
  <label className='checkbox'>
1578
1993
  <input
@@ -1582,12 +1997,43 @@ const EditorPanel = (props) => {
1582
1997
  handleEditorChanges('separateZero', event.target.checked)
1583
1998
  }
1584
1999
  />
1585
- <span className='edit-label'>Separate Zero</span>
2000
+ <span className='edit-label'>
2001
+ Separate Zero
2002
+ <Tooltip style={{textTransform: 'none'}}>
2003
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2004
+ <Tooltip.Content>
2005
+ <p>For numeric data, you can separate the zero value as its own data class.</p>
2006
+ </Tooltip.Content>
2007
+ </Tooltip>
2008
+ </span>
2009
+
1586
2010
  </label>
1587
2011
  )}
2012
+ {/* Temp Checkbox */}
2013
+
2014
+ {state.legend.type === 'equalnumber' &&
2015
+ <label className="checkbox mt-4">
2016
+ <input type="checkbox" checked={ state.general.equalNumberOptIn } onChange={(event) => { handleEditorChanges("showEqualNumber", event.target.checked) }} />
2017
+ <span className="edit-label">Use new quantile legend</span>
2018
+ <Tooltip style={{textTransform: 'none'}}>
2019
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2020
+ <Tooltip.Content>
2021
+ <p>This prevents numbers from being used in more than one category (ie. 0-1, 1-2, 2-3) </p>
2022
+ </Tooltip.Content>
2023
+ </Tooltip>
2024
+ </label>
2025
+ }
1588
2026
  {'category' !== legend.type && (
1589
2027
  <label>
1590
- <span className='edit-label'>Number of Items</span>
2028
+ <span className='edit-label'>
2029
+ Number of Items
2030
+ <Tooltip style={{textTransform: 'none'}}>
2031
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2032
+ <Tooltip.Content>
2033
+ <p>For numeric maps, select the number of data classes. Do not include designated special classes.</p>
2034
+ </Tooltip.Content>
2035
+ </Tooltip>
2036
+ </span>
1591
2037
  <select
1592
2038
  value={legend.numberOfItems}
1593
2039
  onChange={(event) => {
@@ -1607,7 +2053,15 @@ const EditorPanel = (props) => {
1607
2053
  {'category' === legend.type && (
1608
2054
  <React.Fragment>
1609
2055
  <label>
1610
- <span className='edit-label'>Category Order</span>
2056
+ <span className='edit-label'>
2057
+ Category Order
2058
+ <Tooltip style={{textTransform: 'none'}}>
2059
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2060
+ <Tooltip.Content>
2061
+ <p>Drag map categories into preferred legend order. </p>
2062
+ </Tooltip.Content>
2063
+ </Tooltip>
2064
+ </span>
1611
2065
  </label>
1612
2066
  {/* 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. */}
1613
2067
  <DragDropContext
@@ -1628,13 +2082,13 @@ const EditorPanel = (props) => {
1628
2082
  )}
1629
2083
  </Droppable>
1630
2084
  </DragDropContext>
1631
- {editorCatOrder.length >= 9 && (
2085
+ {editorCatOrder.length >= 10 && (
1632
2086
  <section className='error-box my-2'>
1633
2087
  <div>
1634
2088
  <strong className='pt-1'>Warning</strong>
1635
2089
  <p>
1636
- The maximum number of categorical legend items is 9. If
1637
- your data has more than 9 categories your map will not
2090
+ The maximum number of categorical legend items is 10. If
2091
+ your data has more than 10 categories your map will not
1638
2092
  display properly.
1639
2093
  </p>
1640
2094
  </div>
@@ -1706,10 +2160,18 @@ const EditorPanel = (props) => {
1706
2160
  );
1707
2161
  }}
1708
2162
  />
1709
- <span className='edit-label'>Dynamic Legend Description</span>
2163
+ <span className='edit-label'>
2164
+ Dynamic Legend Description
2165
+ <Tooltip style={{textTransform: 'none'}}>
2166
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2167
+ <Tooltip.Content>
2168
+ <p>Check this option if the map has multiple filter controls and you want to specify a description for each filter selection.</p>
2169
+ </Tooltip.Content>
2170
+ </Tooltip>
2171
+ </span>
1710
2172
  </label>
1711
2173
  )}
1712
- {filtersJSX.length > 0 && (
2174
+ {(filtersJSX.length > 0 || state.general.type === 'bubble' || state.general.geoType === 'us') && (
1713
2175
  <label className='checkbox'>
1714
2176
  <input
1715
2177
  type='checkbox'
@@ -1718,7 +2180,15 @@ const EditorPanel = (props) => {
1718
2180
  handleEditorChanges('unifiedLegend', event.target.checked)
1719
2181
  }
1720
2182
  />
1721
- <span className='edit-label'>Unified Legend</span>
2183
+ <span className='edit-label'>
2184
+ Unified Legend
2185
+ <Tooltip style={{textTransform: 'none'}}>
2186
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2187
+ <Tooltip.Content>
2188
+ <p>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.</p>
2189
+ </Tooltip.Content>
2190
+ </Tooltip>
2191
+ </span>
1722
2192
  </label>
1723
2193
  )}
1724
2194
  </AccordionItemPanel>
@@ -1766,12 +2236,20 @@ const EditorPanel = (props) => {
1766
2236
  placeholder='Data Table'
1767
2237
  />
1768
2238
  <TextField
1769
- value={dataTable.indexTitle}
2239
+ value={dataTable.indexLabel || ''}
1770
2240
  updateField={updateField}
1771
2241
  section='dataTable'
1772
- fieldName='indexTitle'
1773
- label='Index Column Title'
2242
+ fieldName='indexLabel'
2243
+ label='Index Column Header'
1774
2244
  placeholder='Location'
2245
+ tooltip={
2246
+ <Tooltip style={{textTransform: 'none'}}>
2247
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2248
+ <Tooltip.Content>
2249
+ <p>To comply with 508 standards, if the first column in the data table has no header, enter a brief one here.</p>
2250
+ </Tooltip.Content>
2251
+ </Tooltip>
2252
+ }
1775
2253
  />
1776
2254
  <TextField
1777
2255
  value={dataTable.caption}
@@ -1780,7 +2258,14 @@ const EditorPanel = (props) => {
1780
2258
  fieldName='caption'
1781
2259
  label='Data Table Caption'
1782
2260
  placeholder='Data Table'
1783
- helper='Text that describes the data table for screen'
2261
+ tooltip={
2262
+ <Tooltip style={{textTransform: 'none'}}>
2263
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2264
+ <Tooltip.Content>
2265
+ <p>Enter a description of the data table to be read by screen readers.</p>
2266
+ </Tooltip.Content>
2267
+ </Tooltip>
2268
+ }
1784
2269
  type="textarea"
1785
2270
  />
1786
2271
  <label className='checkbox'>
@@ -1795,8 +2280,15 @@ const EditorPanel = (props) => {
1795
2280
  handleEditorChanges('showDataTable', event.target.checked);
1796
2281
  }}
1797
2282
  />
1798
- <span className='edit-label'>Show Table</span>
1799
- <Helper text='Data tables are required for 508 compliance. When choosing to hide this data table, replace with your own version.' />
2283
+ <span className='edit-label'>
2284
+ Show Table
2285
+ <Tooltip style={{textTransform: 'none'}}>
2286
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2287
+ <Tooltip.Content>
2288
+ <p>Data tables are required for 508 compliance. When choosing to hide this data table, replace with your own version.</p>
2289
+ </Tooltip.Content>
2290
+ </Tooltip>
2291
+ </span>
1800
2292
  </label>
1801
2293
  <label className='checkbox'>
1802
2294
  <input
@@ -1854,7 +2346,12 @@ const EditorPanel = (props) => {
1854
2346
  <label>
1855
2347
  <span className='edit-label'>
1856
2348
  Detail displays on{' '}
1857
- <Helper text='At mobile sizes, information always appears in a popover modal when a user taps on an item.' />
2349
+ <Tooltip style={{textTransform: 'none'}}>
2350
+ <Tooltip.Target><Icon display="question" style={{marginLeft: '0.5rem'}}/></Tooltip.Target>
2351
+ <Tooltip.Content>
2352
+ <p>At mobile sizes, information always appears in a popover modal when a user taps on an item.</p>
2353
+ </Tooltip.Content>
2354
+ </Tooltip>
1858
2355
  </span>
1859
2356
  <select
1860
2357
  value={state.tooltips.appearanceType}
@@ -1894,7 +2391,7 @@ const EditorPanel = (props) => {
1894
2391
  <AccordionItemButton>Visual</AccordionItemButton>
1895
2392
  </AccordionItemHeading>
1896
2393
  <AccordionItemPanel>
1897
- <label className='header'>
2394
+ <label>
1898
2395
  <span className='edit-label'>Header Theme</span>
1899
2396
  <ul className='color-palette'>
1900
2397
  {headerColors.map((palette) => {
@@ -1925,44 +2422,28 @@ const EditorPanel = (props) => {
1925
2422
  />
1926
2423
  <span className='edit-label'>Show Title</span>
1927
2424
  </label>
1928
- {'navigation' !== state.general.type && (
1929
- <label className='checkbox'>
1930
- <input
1931
- type='checkbox'
1932
- checked={state.general.showSidebar || false}
1933
- onChange={(event) => {
1934
- handleEditorChanges('showSidebar', event.target.checked);
1935
- }}
1936
- />
1937
- <span className='edit-label'>Show Legend</span>
1938
- </label>
1939
- )}
1940
- {'navigation' !== state.general.type && (
1941
- <label>
1942
- <span className='edit-label'>Legend Position</span>
1943
- <select
1944
- value={legend.position || false}
1945
- onChange={(event) => {
1946
- handleEditorChanges('sidebarPosition', event.target.value);
1947
- }}
1948
- >
1949
- <option value='side'>Side</option>
1950
- <option value='bottom'>Bottom</option>
1951
- </select>
1952
- </label>
1953
- )}
1954
- {'side' === legend.position && (
1955
- <label className='checkbox'>
1956
- <input
1957
- type='checkbox'
1958
- checked={legend.singleColumn}
1959
- onChange={(event) => {
1960
- handleEditorChanges('singleColumnLegend', event.target.checked);
1961
- }}
1962
- />
1963
- <span className='edit-label'>Single Column Legend</span>
1964
- </label>
1965
- )}
2425
+
2426
+ <label className='checkbox'>
2427
+ <input
2428
+ type='checkbox'
2429
+ checked={state.general.hideGeoColumnInTooltip || false}
2430
+ onChange={(event) => {
2431
+ handleEditorChanges('hideGeoColumnInTooltip', event.target.checked);
2432
+ }}
2433
+ />
2434
+ <span className='edit-label'>Hide Geography Column Name in Tooltip</span>
2435
+ </label>
2436
+
2437
+ <label className='checkbox'>
2438
+ <input
2439
+ type='checkbox'
2440
+ checked={state.general.hidePrimaryColumnInTooltip || false}
2441
+ onChange={(event) => {
2442
+ handleEditorChanges('hidePrimaryColumnInTooltip', event.target.checked);
2443
+ }}
2444
+ />
2445
+ <span className='edit-label'>Hide Primary Column Name in Tooltip</span>
2446
+ </label>
1966
2447
  {'navigation' === state.general.type && (
1967
2448
  <label className='checkbox'>
1968
2449
  <input
@@ -1990,10 +2471,11 @@ const EditorPanel = (props) => {
1990
2471
  <label>
1991
2472
  <span className='edit-label'>Map Color Palette</span>
1992
2473
  </label>
1993
- <span className='h5'>Sequential</span>
2474
+ {/* <InputCheckbox section="general" subsection="palette" fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} /> */}
2475
+ <InputToggle type='3d' section="general" subsection="palette" fieldName='isReversed' size='small' label='Use selected palette in reverse order' updateField={updateField} value={isPaletteReversed} />
2476
+ <span>Sequential</span>
1994
2477
  <ul className='color-palette'>
1995
- {Object.keys(colorPalettes)
1996
- .filter((name) => !name.includes('qualitative'))
2478
+ {filteredPallets
1997
2479
  .map((palette) => {
1998
2480
  const colorOne = {
1999
2481
  backgroundColor: colorPalettes[palette][2],
@@ -2023,10 +2505,9 @@ const EditorPanel = (props) => {
2023
2505
  );
2024
2506
  })}
2025
2507
  </ul>
2026
- <span className='h5'>Non-Sequential</span>
2508
+ <span>Non-Sequential</span>
2027
2509
  <ul className='color-palette'>
2028
- {Object.keys(colorPalettes)
2029
- .filter((name) => name.includes('qualitative'))
2510
+ {filteredQualitative
2030
2511
  .map((palette) => {
2031
2512
  const colorOne = {
2032
2513
  backgroundColor: colorPalettes[palette][2],
@@ -2040,6 +2521,10 @@ const EditorPanel = (props) => {
2040
2521
  backgroundColor: colorPalettes[palette][6],
2041
2522
  };
2042
2523
 
2524
+ // hide palettes with too few colors for region maps
2525
+ if ( colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region' ) {
2526
+ return('');
2527
+ }
2043
2528
  return (
2044
2529
  <li
2045
2530
  title={palette}
@@ -2056,52 +2541,78 @@ const EditorPanel = (props) => {
2056
2541
  );
2057
2542
  })}
2058
2543
  </ul>
2544
+ {(state.general.type === 'bubble') && <><TextField
2545
+ type='number'
2546
+ value={state.visual.minBubbleSize}
2547
+ section='visual'
2548
+ fieldName='minBubbleSize'
2549
+ label='Minimum Bubble Size'
2550
+ updateField={updateField}
2551
+ />
2552
+ <TextField
2553
+ type='number'
2554
+ value={state.visual.maxBubbleSize}
2555
+ section='visual'
2556
+ fieldName='maxBubbleSize'
2557
+ label='Maximum Bubble Size'
2558
+ updateField={updateField}
2559
+ /></>}
2560
+ { (state.general.geoType === 'world' || state.general.geoType === 'us' && state.general.type === 'bubble') &&
2561
+ <label className='checkbox'>
2562
+ <input
2563
+ type='checkbox'
2564
+ checked={state.visual.showBubbleZeros}
2565
+ onChange={(event) => {
2566
+ handleEditorChanges('showBubbleZeros', event.target.checked);
2567
+ }}
2568
+ />
2569
+ <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
2570
+ </label>
2571
+ }
2572
+ {state.general.geoType === 'world' &&
2573
+ <label className='checkbox'>
2574
+ <input
2575
+ type='checkbox'
2576
+ checked={state.general.allowMapZoom}
2577
+ onChange={(event) => {
2578
+ handleEditorChanges('allowMapZoom', event.target.checked);
2579
+ }}
2580
+ />
2581
+ <span className='edit-label'>Allow Map Zooming</span>
2582
+ </label>
2583
+ }
2584
+ {state.general.type === 'bubble' &&
2585
+ <label className='checkbox'>
2586
+ <input
2587
+ type='checkbox'
2588
+ checked={state.visual.extraBubbleBorder}
2589
+ onChange={(event) => {
2590
+ handleEditorChanges('toggleExtraBubbleBorder', event.target.checked);
2591
+ }}
2592
+ />
2593
+ <span className='edit-label'>Bubble Map has extra border</span>
2594
+ </label>
2595
+ }
2596
+ {state.general.geoType === 'us' &&
2597
+ <label>
2598
+ <span className='edit-label'>City Style</span>
2599
+ <select
2600
+ value={state.visual.cityStyle || false}
2601
+ onChange={(event) => {
2602
+ handleEditorChanges('handleCityStyle', event.target.value);
2603
+ }}
2604
+ >
2605
+ <option value='circle'>Circle</option>
2606
+ <option value='pin'>Pin</option>
2607
+ </select>
2608
+ </label>
2609
+ }
2059
2610
  </AccordionItemPanel>
2060
2611
  </AccordionItem>
2061
2612
  </Accordion>
2062
2613
  </form>
2063
- <a
2064
- href='https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html'
2065
- target='_blank'
2066
- rel='noopener noreferrer'
2067
- className='guidance-link'
2068
- >
2069
- <MapIcon />
2070
- <div>
2071
- <span className='heading-3'>Get Maps Help</span>
2072
- <p>Examples and documentation</p>
2073
- </div>
2074
- </a>
2075
- <div className='advanced'>
2076
- <span className='advanced-toggle-link' onClick={() => setAdvancedToggle(!advancedToggle)}>
2077
- <span>{advancedToggle ? `— ` : `+ `}</span>Advanced Options
2078
- </span>
2079
- {advancedToggle && (
2080
- <React.Fragment>
2081
- <section className='error-box py-2 px-3 my-2'>
2082
- <div>
2083
- <strong className='pt-1'>Warning</strong>
2084
- <p>This can cause serious errors in your map.</p>
2085
- </div>
2086
- </section>
2087
- <p className='pb-2'>
2088
- This tool displays the actual map configuration{' '}
2089
- <acronym title='JavaScript Object Notation'>JSON</acronym> that is generated by this
2090
- editor and allows you to edit properties directly and apply them.
2091
- </p>
2092
- <textarea
2093
- value={configTextboxValue}
2094
- onChange={(event) => setConfigTextbox(event.target.value)}
2095
- />
2096
- <button
2097
- className='btn full-width'
2098
- onClick={() => loadConfig(JSON.parse(configTextboxValue))}
2099
- >
2100
- Apply
2101
- </button>
2102
- </React.Fragment>
2103
- )}
2104
- </div>
2614
+ <AdvancedEditor loadConfig={loadConfig} state={state} convertStateToConfig={convertStateToConfig} />
2615
+
2105
2616
  </section>
2106
2617
  </section>
2107
2618
  </ErrorBoundary>