@cdc/map 4.23.4 → 4.23.6

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.
@@ -66,11 +66,7 @@
66
66
  "properties": {
67
67
  "name": "Polygon 2",
68
68
  "styleUrl": "#poly-000000-1200-77-nodesc",
69
- "fill-opacity": 0.30196078431372547,
70
- "fill": "blue",
71
- "stroke-opacity": 1,
72
- "stroke": "red",
73
- "stroke-width": 5
69
+ "fill-opacity": 0.30196078431372547
74
70
  }
75
71
  },
76
72
  {
package/index.html CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  <body>
18
18
  <!-- DEFAULT EXAMPLES -->
19
- <!-- <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div> -->
19
+ <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div>
20
20
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-geocode.json"></div> -->
21
21
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-usa-regions.json"></div> -->
22
22
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-single-state.json"></div> -->
@@ -32,7 +32,7 @@
32
32
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
33
33
 
34
34
  <!-- TP4 EXAMPLES -->
35
- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div>
35
+ <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
36
36
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state-no-territories.json"></div> -->
37
37
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
38
38
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/map",
3
- "version": "4.23.4",
3
+ "version": "4.23.6",
4
4
  "description": "React component for visualizing tabular data on a map of the United States or the world.",
5
5
  "moduleName": "CdcMap",
6
6
  "main": "dist/cdcmap",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "@cdc/core": "^4.23.4",
27
+ "@cdc/core": "^4.23.6",
28
28
  "@emotion/core": "^10.0.28",
29
29
  "@emotion/react": "^11.1.5",
30
30
  "@hello-pangea/dnd": "^16.2.0",
@@ -51,5 +51,5 @@
51
51
  "react": "^18.2.0",
52
52
  "react-dom": "^18.2.0"
53
53
  },
54
- "gitHead": "dcd395d76f70b2d113f2b4c6fe50a52522655cd1"
54
+ "gitHead": "aaed0388b487adfeb3e7e278b4ce74df09cbaade"
55
55
  }
package/src/CdcMap.jsx CHANGED
@@ -8,11 +8,13 @@ import ResizeObserver from 'resize-observer-polyfill'
8
8
  // Third party
9
9
  import { Tooltip as ReactTooltip } from 'react-tooltip'
10
10
  import chroma from 'chroma-js'
11
+ import Papa from 'papaparse'
11
12
  import parse from 'html-react-parser'
12
13
  import 'react-tooltip/dist/react-tooltip.css'
13
14
 
14
15
  // Helpers
15
16
  import { publish } from '@cdc/core/helpers/events'
17
+ import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
16
18
 
17
19
  // Data
18
20
  import { countryCoordinates } from './data/country-coordinates'
@@ -29,11 +31,12 @@ import './scss/btn.scss'
29
31
 
30
32
  // Core
31
33
  import { DataTransform } from '@cdc/core/helpers/DataTransform'
32
- import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
34
+ import MediaControls from '@cdc/core/components/MediaControls'
33
35
  import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
34
36
  import getViewport from '@cdc/core/helpers/getViewport'
35
37
  import Loading from '@cdc/core/components/Loading'
36
38
  import numberFromString from '@cdc/core/helpers/numberFromString'
39
+ import DataTable from '@cdc/core/components/DataTable' // Future: Lazy
37
40
 
38
41
  // Child Components
39
42
  import ConfigContext from './context'
@@ -42,7 +45,6 @@ import Modal from './components/Modal'
42
45
  import Sidebar from './components/Sidebar'
43
46
 
44
47
  import CountyMap from './components/CountyMap' // Future: Lazy
45
- import DataTable from './components/DataTable' // Future: Lazy
46
48
  import EditorPanel from './components/EditorPanel' // Future: Lazy
47
49
  import NavigationMenu from './components/NavigationMenu' // Future: Lazy
48
50
  import SingleStateMap from './components/SingleStateMap' // Future: Lazy
@@ -133,6 +135,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
133
135
  let legendMemo = useRef(new Map())
134
136
  let innerContainerRef = useRef()
135
137
 
138
+ if (isDebug) console.log('CdcMap state=', state) // eslint-disable-line
139
+
136
140
  useEffect(() => {
137
141
  try {
138
142
  if (filteredCountryCode) {
@@ -195,7 +199,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
195
199
  for (let entry of entries) {
196
200
  let { width, height } = entry.contentRect
197
201
  let newViewport = getViewport(entry.contentRect.width)
198
- let svgMarginWidth = 32
199
202
  let editorWidth = 350
200
203
 
201
204
  setCurrentViewport(newViewport)
@@ -499,10 +502,19 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
499
502
 
500
503
  legendMemo.current = newLegendMemo
501
504
 
505
+ // before returning the legend result
506
+ // add property for bin number and set to index location
502
507
  result.forEach((row, i) => {
503
508
  row.bin = i // set bin number to index
504
509
  })
505
510
 
511
+ // Move all special legend items from "Special Classes" to the end of the legend
512
+ if (state.legend.showSpecialClassesLast) {
513
+ let specialRows = result.filter(d => d.special === true)
514
+ let otherRows = result.filter(d => !d.special)
515
+ result = [...otherRows, ...specialRows]
516
+ }
517
+
506
518
  return result
507
519
  }
508
520
 
@@ -770,9 +782,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
770
782
 
771
783
  if (hash) filters.fromHash = hash
772
784
 
773
- obj?.filters.forEach(({ columnName, label, active, values }, idx) => {
774
- if (undefined === columnName) return
775
-
785
+ obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown }, idx) => {
776
786
  let newFilter = runtimeFilters[idx]
777
787
 
778
788
  const sortAsc = (a, b) => {
@@ -783,20 +793,24 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
783
793
  return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
784
794
  }
785
795
 
786
- values = getUniqueValues(state.data, columnName)
796
+ if (type !== 'url') {
797
+ values = getUniqueValues(state.data, columnName)
787
798
 
788
- if (obj.filters[idx].order === 'asc') {
789
- values = values.sort(sortAsc)
790
- }
799
+ if (obj.filters[idx].order === 'asc') {
800
+ values = values.sort(sortAsc)
801
+ }
791
802
 
792
- if (obj.filters[idx].order === 'desc') {
793
- values = values.sort(sortDesc)
794
- }
803
+ if (obj.filters[idx].order === 'desc') {
804
+ values = values.sort(sortDesc)
805
+ }
795
806
 
796
- if (obj.filters[idx].order === 'cust') {
797
- if (obj.filters[idx]?.values.length > 0) {
798
- values = obj.filters[idx].values
807
+ if (obj.filters[idx].order === 'cust') {
808
+ if (obj.filters[idx]?.values.length > 0) {
809
+ values = obj.filters[idx].values
810
+ }
799
811
  }
812
+ } else {
813
+ values = values
800
814
  }
801
815
 
802
816
  if (undefined === newFilter) {
@@ -804,12 +818,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
804
818
  }
805
819
 
806
820
  newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
821
+ newFilter.type = type
807
822
  newFilter.label = label ?? ''
808
823
  newFilter.columnName = columnName
824
+ newFilter.orderedValues = orderedValues
825
+ newFilter.queryParameter = queryParameter
826
+ newFilter.labels = labels
809
827
  newFilter.values = values
810
828
  handleSorting(newFilter)
811
829
  newFilter.active = active ?? values[0] // Default to first found value
812
830
  newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
831
+ newFilter.showDropdown = showDropdown
813
832
 
814
833
  filters.push(newFilter)
815
834
  })
@@ -862,8 +881,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
862
881
  // Filters
863
882
  if (filters?.length) {
864
883
  for (let i = 0; i < filters.length; i++) {
865
- const { columnName, active } = filters[i]
866
- if (String(row[columnName]) !== String(active)) return false // Bail out, not part of filter
884
+ const { columnName, active, type } = filters[i]
885
+ if (type !== 'url' && String(row[columnName]) !== String(active)) return false // Bail out, not part of filter
867
886
  }
868
887
  }
869
888
 
@@ -899,6 +918,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
899
918
  return ''
900
919
  }
901
920
 
921
+ // if string of letters like 'Home' then dont need to format as a number
902
922
  if (typeof value === 'string' && value.length > 0 && state.legend.type === 'equalnumber') {
903
923
  return value
904
924
  }
@@ -907,6 +927,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
907
927
 
908
928
  let columnObj = state.columns[columnName]
909
929
 
930
+ if (columnObj === undefined) {
931
+ // then use left axis config
932
+ columnObj = state.columns.primary
933
+ // NOTE: Left Value Axis uses different names
934
+ // so map them below so the code below works
935
+ // - copy commas to useCommas to work below
936
+ columnObj['useCommas'] = columnObj.commas
937
+ // - copy roundTo to roundToPlace to work below
938
+ columnObj['roundToPlace'] = columnObj.roundTo ? columnObj.roundTo : ''
939
+ }
940
+
910
941
  if (columnObj) {
911
942
  // If value is a number, apply specific formattings
912
943
  if (Number(value)) {
@@ -939,11 +970,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
939
970
  }
940
971
 
941
972
  // this is passed DOWN into the various components
942
- // then they do a lookup based on the bin number as index into here (TT)
973
+ // then they do a lookup based on the bin number as index into here
943
974
  const applyLegendToRow = rowObj => {
944
975
  try {
945
976
  if (!rowObj) throw new Error('COVE: No rowObj in applyLegendToRow')
946
- // Navigation map
977
+ // Navigation mapchanged
947
978
  if ('navigation' === state.general.type) {
948
979
  let mapColorPalette = colorPalettes[state.color] || colorPalettes['bluegreenreverse']
949
980
  return generateColorsArray(mapColorPalette[3])
@@ -955,7 +986,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
955
986
  let idx = legendMemo.current.get(hash)
956
987
  if (runtimeLegend[idx]?.disabled) return false
957
988
 
958
- // DEV-784 changed to use bin prop to get color instead of idx
989
+ // changed to use bin prop to get color instead of idx
959
990
  // bc we re-order legend when showSpecialClassesLast is checked
960
991
  let legendBinColor = runtimeLegend.find(o => o.bin === idx)?.color
961
992
  return generateColorsArray(legendBinColor, runtimeLegend[idx]?.special)
@@ -994,16 +1025,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
994
1025
  const column = state.columns[columnKey]
995
1026
 
996
1027
  if (true === column.tooltip) {
997
- let label = column.label.length > 0 ? column.label : ''
1028
+ let label = column.label?.length > 0 ? column.label : ''
998
1029
 
999
1030
  let value
1000
1031
 
1001
1032
  if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
1002
1033
  // THIS CODE SHOULD NOT ACT ON THE ENTIRE ROW OF KEYS BUT ONLY THE ONE KEY IN THE SPECIAL CLASS
1003
1034
  for (let i = 0; i < state.legend.specialClasses.length; i++) {
1004
- // DEV-3303 - Special Classes label in HOVERS should only apply to selected special class key
1035
+ // Special Classes label in HOVERS should only apply to selected special class key
1005
1036
  // - you have to ALSO check that the key matches - putting here otherwise the if stmt too long
1006
- if (columnKey === state.legend.specialClasses[i].key) {
1037
+ if (column.name === state.legend.specialClasses[i].key) {
1007
1038
  if (String(row[state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
1008
1039
  value = displayDataAsText(state.legend.specialClasses[i].label, columnKey)
1009
1040
  break
@@ -1047,25 +1078,29 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1047
1078
  // - this function is used to prevent that and instead give the formatting that is wanted
1048
1079
  // Example: Desired city display in tooltip on map: "Inter-Tribal Indian Reservation"
1049
1080
  const titleCase = string => {
1050
- // if hyphen found, then split, uppercase each word, and put back together
1051
- if (string.includes('–') || string.includes('-')) {
1052
- let dashSplit = string.includes('–') ? string.split('–') : string.split('-') // determine hyphen or en dash to split on
1053
- let splitCharacter = string.includes('–') ? '–' : '-' // print hyphen or en dash later on.
1054
- let frontSplit = dashSplit[0]
1055
- .split(' ')
1056
- .map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
1057
- .join(' ')
1058
- let backSplit = dashSplit[1]
1059
- .split(' ')
1060
- .map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
1061
- .join(' ')
1062
- return frontSplit + splitCharacter + backSplit
1063
- } else {
1064
- // just return with each word uppercase
1065
- return string
1066
- .split(' ')
1067
- .map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
1068
- .join(' ')
1081
+ // guard clause else error in editor
1082
+ if (!string) return
1083
+ if (string !== undefined) {
1084
+ // if hyphen found, then split, uppercase each word, and put back together
1085
+ if (string.includes('–') || string.includes('-')) {
1086
+ let dashSplit = string.includes('–') ? string.split('–') : string.split('-') // determine hyphen or en dash to split on
1087
+ let splitCharacter = string.includes('–') ? '–' : '-' // print hyphen or en dash later on.
1088
+ let frontSplit = dashSplit[0]
1089
+ .split(' ')
1090
+ .map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
1091
+ .join(' ')
1092
+ let backSplit = dashSplit[1]
1093
+ .split(' ')
1094
+ .map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
1095
+ .join(' ')
1096
+ return frontSplit + splitCharacter + backSplit
1097
+ } else {
1098
+ // just return with each word uppercase
1099
+ return string
1100
+ .split(' ')
1101
+ .map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
1102
+ .join(' ')
1103
+ }
1069
1104
  }
1070
1105
  }
1071
1106
 
@@ -1129,7 +1164,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1129
1164
  if (true === Object.keys(dict).includes(value)) {
1130
1165
  value = dict[value]
1131
1166
  }
1132
- return titleCase(value)
1167
+
1168
+ // if you get here and it's 2 letters then DONT titleCase state abbreviations like "AL"
1169
+ if (value.length === 2) {
1170
+ return value
1171
+ } else {
1172
+ return titleCase(value)
1173
+ }
1133
1174
  }
1134
1175
 
1135
1176
  const navigationHandler = urlString => {
@@ -1232,6 +1273,67 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1232
1273
  }
1233
1274
  }
1234
1275
 
1276
+ const reloadURLData = async () => {
1277
+ if (state.dataUrl) {
1278
+ const dataUrl = new URL(state.runtimeDataUrl || state.dataUrl)
1279
+ let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
1280
+
1281
+ let isUpdateNeeded = false
1282
+ state.filters.forEach(filter => {
1283
+ if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
1284
+ qsParams[filter.queryParameter] = filter.active
1285
+ isUpdateNeeded = true
1286
+ }
1287
+ })
1288
+
1289
+ if (!isUpdateNeeded) return
1290
+
1291
+ let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
1292
+ .map((param, i) => {
1293
+ let qs = i === 0 ? '?' : '&'
1294
+ qs += param + '='
1295
+ qs += qsParams[param]
1296
+ return qs
1297
+ })
1298
+ .join('')}`
1299
+
1300
+ let data
1301
+
1302
+ try {
1303
+ const regex = /(?:\.([^.]+))?$/
1304
+
1305
+ const ext = regex.exec(dataUrl.pathname)[1]
1306
+ if ('csv' === ext) {
1307
+ data = await fetch(dataUrlFinal)
1308
+ .then(response => response.text())
1309
+ .then(responseText => {
1310
+ const parsedCsv = Papa.parse(responseText, {
1311
+ header: true,
1312
+ dynamicTyping: true,
1313
+ skipEmptyLines: true
1314
+ })
1315
+ return parsedCsv.data
1316
+ })
1317
+ } else if ('json' === ext) {
1318
+ data = await fetch(dataUrlFinal).then(response => response.json())
1319
+ } else {
1320
+ data = []
1321
+ }
1322
+ } catch (e) {
1323
+ console.error(`Cannot parse URL: ${dataUrlFinal}`) // eslint-disable-line
1324
+ console.log(e) // eslint-disable-line
1325
+ data = []
1326
+ }
1327
+
1328
+ if (state.dataDescription) {
1329
+ data = transform.autoStandardize(data)
1330
+ data = transform.developerStandardize(data, state.dataDescription)
1331
+ }
1332
+
1333
+ setState({ ...state, runtimeDataUrl: dataUrlFinal, data })
1334
+ }
1335
+ }
1336
+
1235
1337
  const loadConfig = async configObj => {
1236
1338
  // Set loading flag
1237
1339
  if (!loading) setLoading(true)
@@ -1242,8 +1344,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1242
1344
  ...configObj
1243
1345
  }
1244
1346
 
1245
- // If a dataUrl property exists, always pull from that.
1246
- if (newState.dataUrl) {
1347
+ const urlFilters = newState.filters ? (newState.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
1348
+
1349
+ if (newState.dataUrl && !urlFilters) {
1247
1350
  if (newState.dataUrl[0] === '/') {
1248
1351
  newState.dataUrl = 'http://' + hostname + newState.dataUrl
1249
1352
  }
@@ -1282,12 +1385,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1282
1385
  addUIDs(newState, newState.columns.geo.name || newState.columns.geo.fips)
1283
1386
  }
1284
1387
 
1285
- if (newState.dataTable.forceDisplay === undefined) {
1286
- newState.dataTable.forceDisplay = !isDashboard
1388
+ if (newState.table.forceDisplay === undefined) {
1389
+ newState.table.forceDisplay = !isDashboard
1287
1390
  }
1288
1391
 
1289
1392
  validateFipsCodeLength(newState)
1290
- setState(newState)
1393
+
1394
+ // add ability to rename state properties over time.
1395
+ const processedConfig = { ...(await coveUpdateWorker(newState)) }
1396
+
1397
+ setState(processedConfig)
1291
1398
  setLoading(false)
1292
1399
  }
1293
1400
 
@@ -1338,16 +1445,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1338
1445
 
1339
1446
  // DEV-769 make "Data Table" both a required field and default value
1340
1447
  useEffect(() => {
1341
- if (state.dataTable?.title === '' || state.dataTable?.title === undefined) {
1448
+ if (state.table?.label === '' || state.table?.label === undefined) {
1342
1449
  setState({
1343
1450
  ...state,
1344
- dataTable: {
1345
- ...state.dataTable,
1451
+ table: {
1452
+ ...state.table,
1346
1453
  title: 'Data Table'
1347
1454
  }
1348
1455
  })
1349
1456
  }
1350
- }, [state.dataTable]) // eslint-disable-line
1457
+ }, [state.table]) // eslint-disable-line
1351
1458
 
1352
1459
  // When geo label override changes
1353
1460
  // - redo the tooltips
@@ -1408,6 +1515,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1408
1515
  }
1409
1516
  }, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses]) // eslint-disable-line
1410
1517
 
1518
+ useEffect(() => {
1519
+ reloadURLData()
1520
+ }, [JSON.stringify(state.filters)])
1521
+
1411
1522
  if (config) {
1412
1523
  // eslint-disable-next-line react-hooks/rules-of-hooks
1413
1524
  useEffect(() => {
@@ -1416,14 +1527,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1416
1527
  }
1417
1528
 
1418
1529
  // Destructuring for more readable JSX
1419
- const { general, tooltips, dataTable } = state
1530
+ const { general, tooltips, table } = state
1420
1531
  let { title, subtext = '' } = general
1421
1532
 
1422
1533
  // if no title AND in editor then set a default
1423
1534
  if (isEditor) {
1424
1535
  if (!title || title === '') title = 'Map Title'
1425
1536
  }
1426
- if (!dataTable.title || dataTable.title === '') dataTable.title = 'Data Table'
1537
+ if (!table.label || table.label === '') table.label = 'Data Table'
1427
1538
 
1428
1539
  // Outer container classes
1429
1540
  let outerContainerClasses = ['cdc-open-viz-module', 'cdc-map-outer-container', currentViewport]
@@ -1475,7 +1586,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1475
1586
 
1476
1587
  if (!mapProps.data || !state.data) return <Loading />
1477
1588
 
1478
- const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading
1589
+ const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading
1479
1590
 
1480
1591
  const handleMapTabbing = () => {
1481
1592
  let tabbingID
@@ -1596,32 +1707,33 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1596
1707
  {'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
1597
1708
 
1598
1709
  {/* Link */}
1599
- {isDashboard && config.dataTable.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
1710
+ {isDashboard && config.table?.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
1600
1711
 
1601
1712
  {subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
1602
1713
 
1603
- <CoveMediaControls.Section classes={['download-buttons']}>
1604
- {state.general.showDownloadImgButton && <CoveMediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
1605
- {state.general.showDownloadPdfButton && <CoveMediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
1606
- </CoveMediaControls.Section>
1714
+ <MediaControls.Section classes={['download-buttons']}>
1715
+ {state.general.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
1716
+ {state.general.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
1717
+ </MediaControls.Section>
1607
1718
 
1608
- {state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading && (
1719
+ {state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading && (
1609
1720
  <DataTable
1610
- state={state}
1721
+ config={state}
1611
1722
  rawData={state.data}
1612
1723
  navigationHandler={navigationHandler}
1613
- expandDataTable={general.expandDataTable}
1724
+ expandDataTable={general.expandDataTable ? general.expandDataTable : table.expanded ? table.expanded : false}
1614
1725
  headerColor={general.headerColor}
1615
1726
  columns={state.columns}
1616
1727
  showDownloadButton={general.showDownloadButton}
1728
+ showFullGeoNameInCSV={table.showFullGeoNameInCSV}
1617
1729
  runtimeLegend={runtimeLegend}
1618
1730
  runtimeData={runtimeData}
1619
1731
  displayDataAsText={displayDataAsText}
1620
1732
  displayGeoName={displayGeoName}
1621
1733
  applyLegendToRow={applyLegendToRow}
1622
- tableTitle={dataTable.title}
1623
- indexTitle={dataTable.indexLabel}
1624
- mapTitle={general.title}
1734
+ tableTitle={table.label}
1735
+ indexTitle={table.indexLabel}
1736
+ vizTitle={general.title}
1625
1737
  viewport={currentViewport}
1626
1738
  formatLegendLocation={formatLegendLocation}
1627
1739
  setFilteredCountryCode={setFilteredCountryCode}
@@ -1631,6 +1743,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1631
1743
  innerContainerRef={innerContainerRef}
1632
1744
  outerContainerRef={outerContainerRef}
1633
1745
  imageRef={imageId}
1746
+ isDebug={isDebug}
1634
1747
  />
1635
1748
  )}
1636
1749
 
@@ -44,9 +44,6 @@ states.forEach(state => {
44
44
  countyIndecies[state.id] = [minIndex, maxIndex]
45
45
  })
46
46
 
47
- // CREATE STATE LINES
48
- const projection = geoAlbersUsaTerritories()
49
-
50
47
  // Ensures county map is only rerendered when it needs to (when one of the variables below is updated)
51
48
  function CountyMapChecks(prevState, nextState) {
52
49
  const equalNumberOptIn = prevState.state.general.equalNumberOptIn && nextState.state.general.equalNumberOptIn
@@ -62,6 +59,9 @@ function CountyMapChecks(prevState, nextState) {
62
59
  const CountyMap = props => {
63
60
  const { state, runtimeLegend, applyTooltipsToGeo, data, geoClickHandler, applyLegendToRow, displayGeoName, containerEl, handleMapAriaLabels } = props
64
61
 
62
+ // CREATE STATE LINES
63
+ const projection = geoAlbersUsaTerritories()
64
+
65
65
  const [focus, setFocus] = useState({})
66
66
 
67
67
  const pathGenerator = geoPath().projection(geoAlbersUsaTerritories())
@@ -160,7 +160,7 @@ const CountyMap = props => {
160
160
  if (isNaN(currentTooltipIndex) || !geoContains(mapData[currentTooltipIndex], pointCoordinates)) {
161
161
  const context = canvas.getContext('2d')
162
162
  const path = geoPath(projection, context)
163
- if (!isNaN(currentTooltipIndex)) {
163
+ if (!isNaN(currentTooltipIndex) && applyLegendToRow(data[mapData[currentTooltipIndex].id])) {
164
164
  context.fillStyle = applyLegendToRow(data[mapData[currentTooltipIndex].id])[0]
165
165
  context.strokeStyle = geoStrokeColor
166
166
  context.lineWidth = lineWidth
@@ -193,13 +193,16 @@ const CountyMap = props => {
193
193
 
194
194
  // If the hovered county is found, show the tooltip for that county, otherwise hide the tooltip
195
195
  if (county && data[county.id]) {
196
- context.fillStyle = applyLegendToRow(data[county.id])[1]
197
- context.strokeStyle = geoStrokeColor
198
- context.lineWidth = lineWidth
199
- context.beginPath()
200
- path(mapData[countyIndex])
201
- context.fill()
202
- context.stroke()
196
+ if (applyLegendToRow(data[county.id])) {
197
+ context.globalAlpha = 1
198
+ context.fillStyle = applyLegendToRow(data[county.id])[1]
199
+ context.strokeStyle = geoStrokeColor
200
+ context.lineWidth = lineWidth
201
+ context.beginPath()
202
+ path(mapData[countyIndex])
203
+ context.fill()
204
+ context.stroke()
205
+ }
203
206
 
204
207
  tooltipRef.current.style.display = 'block'
205
208
  tooltipRef.current.style.top = e.clientY + 'px'
@@ -235,7 +238,7 @@ const CountyMap = props => {
235
238
  }
236
239
  }
237
240
 
238
- if (hoveredGeo) {
241
+ if (hoveredGeo && applyLegendToRow(hoveredGeo)) {
239
242
  tooltipRef.current.style.display = 'block'
240
243
  tooltipRef.current.style.top = e.clientY + 'px'
241
244
  tooltipRef.current.style.left = e.clientX + 'px'