@cdc/map 4.24.7 → 4.24.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 (46) hide show
  1. package/dist/cdcmap.js +40720 -38422
  2. package/examples/county-year.csv +10 -0
  3. package/examples/default-geocode.json +44 -10
  4. package/examples/default-patterns.json +0 -2
  5. package/examples/default-single-state.json +279 -108
  6. package/examples/map-issue-3.json +646 -0
  7. package/examples/single-state-filter.json +153 -0
  8. package/index.html +9 -6
  9. package/package.json +3 -3
  10. package/src/CdcMap.tsx +322 -126
  11. package/src/_stories/CdcMap.stories.tsx +7 -0
  12. package/src/_stories/_mock/DEV-8942.json +270 -0
  13. package/src/components/Annotation/AnnotationDropdown.tsx +1 -0
  14. package/src/components/{BubbleList.jsx → BubbleList.tsx} +1 -1
  15. package/src/components/{CityList.jsx → CityList.tsx} +28 -2
  16. package/src/components/{DataTable.jsx → DataTable.tsx} +2 -2
  17. package/src/components/EditorPanel/components/EditorPanel.tsx +647 -127
  18. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +0 -22
  19. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +61 -11
  20. package/src/components/Legend/components/Legend.tsx +125 -36
  21. package/src/components/Legend/components/index.scss +42 -42
  22. package/src/components/Modal.tsx +25 -0
  23. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +74 -0
  24. package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +29 -0
  25. package/src/components/UsaMap/components/SingleState/index.tsx +9 -0
  26. package/src/components/UsaMap/components/UsaMap.County.tsx +84 -33
  27. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +173 -206
  28. package/src/components/UsaMap/components/UsaMap.State.tsx +161 -26
  29. package/src/components/UsaMap/data/us-extended-geography.json +1 -0
  30. package/src/components/UsaMap/helpers/map.ts +111 -0
  31. package/src/components/WorldMap/WorldMap.tsx +17 -32
  32. package/src/components/ZoomControls.tsx +41 -0
  33. package/src/data/initial-state.js +7 -1
  34. package/src/data/supported-geos.js +15 -4
  35. package/src/helpers/generateRuntimeLegendHash.ts +2 -2
  36. package/src/hooks/useStateZoom.tsx +157 -0
  37. package/src/hooks/{useZoomPan.js → useZoomPan.ts} +6 -5
  38. package/src/scss/editor-panel.scss +0 -4
  39. package/src/scss/main.scss +23 -1
  40. package/src/scss/map.scss +8 -0
  41. package/src/types/MapConfig.ts +9 -1
  42. package/src/types/MapContext.ts +14 -2
  43. package/src/components/Modal.jsx +0 -22
  44. /package/src/components/{Geo.jsx → Geo.tsx} +0 -0
  45. /package/src/components/{NavigationMenu.jsx → NavigationMenu.tsx} +0 -0
  46. /package/src/components/{ZoomableGroup.jsx → ZoomableGroup.tsx} +0 -0
package/src/CdcMap.tsx CHANGED
@@ -6,6 +6,10 @@ import Annotation from './components/Annotation'
6
6
  import Error from './components/EditorPanel/components/Error'
7
7
  import _ from 'lodash'
8
8
 
9
+ // types
10
+ import { type ViewportSize } from './types/MapConfig'
11
+ import { type DimensionsType } from '@cdc/core/types/Dimensions'
12
+
9
13
  // IE11
10
14
  import 'whatwg-fetch'
11
15
  import ResizeObserver from 'resize-observer-polyfill'
@@ -26,10 +30,20 @@ import { publish } from '@cdc/core/helpers/events'
26
30
  import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
27
31
  import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
28
32
  import Title from '@cdc/core/components/ui/Title'
33
+ import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
29
34
 
30
35
  // Data
31
36
  import { countryCoordinates } from './data/country-coordinates'
32
- import { supportedStates, supportedTerritories, supportedCountries, supportedCounties, supportedCities, supportedStatesFipsCodes, stateFipsToTwoDigit, supportedRegions } from './data/supported-geos'
37
+ import {
38
+ supportedStates,
39
+ supportedTerritories,
40
+ supportedCountries,
41
+ supportedCounties,
42
+ supportedCities,
43
+ supportedStatesFipsCodes,
44
+ stateFipsToTwoDigit,
45
+ supportedRegions
46
+ } from './data/supported-geos'
33
47
  import colorPalettes from '@cdc/core/data/colorPalettes'
34
48
  import initialState from './data/initial-state'
35
49
 
@@ -82,16 +96,33 @@ const indexOfIgnoreType = (arr, item) => {
82
96
  return -1
83
97
  }
84
98
 
85
- const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, isDebug = false, configUrl, logo = '', setConfig, setSharedFilter, setSharedFilterValue, link }) => {
99
+ const CdcMap = ({
100
+ className,
101
+ config,
102
+ navigationHandler: customNavigationHandler,
103
+ isDashboard = false,
104
+ isEditor = false,
105
+ isDebug = false,
106
+ configUrl,
107
+ logo = '',
108
+ setConfig,
109
+ setSharedFilter,
110
+ setSharedFilterValue,
111
+ link
112
+ }) => {
86
113
  const transform = new DataTransform()
114
+ const [translate, setTranslate] = useState([0, 0])
115
+ const [scale, setScale] = useState(1)
87
116
  const [state, setState] = useState({ ...initialState })
88
117
  const [isDraggingAnnotation, setIsDraggingAnnotation] = useState(false)
89
118
  const [loading, setLoading] = useState(true)
90
119
  const [displayPanel, setDisplayPanel] = useState(true)
91
- const [currentViewport, setCurrentViewport] = useState()
120
+ const [currentViewport, setCurrentViewport] = useState<ViewportSize>('lg')
121
+ const [topoData, setTopoData] = useState<Topology | {}>({})
92
122
  const [runtimeFilters, setRuntimeFilters] = useState([])
93
123
  const [runtimeLegend, setRuntimeLegend] = useState([])
94
124
  const [runtimeData, setRuntimeData] = useState({ init: true })
125
+ const [stateToShow, setStateToShow] = useState(null)
95
126
  const [modal, setModal] = useState(null)
96
127
  const [accessibleStatus, setAccessibleStatus] = useState('')
97
128
  const [filteredCountryCode, setFilteredCountryCode] = useState()
@@ -99,13 +130,15 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
99
130
  const [coveLoadedHasRan, setCoveLoadedHasRan] = useState(false)
100
131
  const [container, setContainer] = useState()
101
132
  const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
102
- const [dimensions, setDimensions] = useState()
133
+ const [dimensions, setDimensions] = useState<DimensionsType>([0, 0])
103
134
  const [requiredColumns, setRequiredColumns] = useState(null) // Simple state so we know if we need more information before parsing the map
135
+ const [projection, setProjection] = useState(null)
104
136
 
105
137
  const legendRef = useRef(null)
106
138
  const tooltipRef = useRef(null)
107
139
  const legendId = useId()
108
140
  const tooltipId = useId()
141
+ const mapId = useId()
109
142
 
110
143
  const { changeFilterActive, handleSorting } = useFilters({ config: state, setConfig: setState })
111
144
  let legendMemo = useRef(new Map())
@@ -132,15 +165,24 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
132
165
  }
133
166
 
134
167
  // Navigate is required for navigation maps
135
- if ('navigation' === state.general.type && ('' === state.columns.navigate.name || undefined === state.columns.navigate)) {
168
+ if (
169
+ 'navigation' === state.general.type &&
170
+ ('' === state.columns.navigate.name || undefined === state.columns.navigate)
171
+ ) {
136
172
  columnList.push('Navigation')
137
173
  }
138
174
 
139
- if (('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && '' === state.columns.latitude.name) {
175
+ if (
176
+ ('us-geocode' === state.general.type || 'world-geocode' === state.general.type) &&
177
+ '' === state.columns.latitude.name
178
+ ) {
140
179
  columnList.push('Latitude')
141
180
  }
142
181
 
143
- if (('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && '' === state.columns.longitude.name) {
182
+ if (
183
+ ('us-geocode' === state.general.type || 'world-geocode' === state.general.type) &&
184
+ '' === state.columns.longitude.name
185
+ ) {
144
186
  columnList.push('Longitude')
145
187
  }
146
188
 
@@ -189,6 +231,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
189
231
  for (let entry of entries) {
190
232
  let { width, height } = entry.contentRect
191
233
  let newViewport = getViewport(entry.contentRect.width)
234
+
192
235
  let editorWidth = 350
193
236
 
194
237
  setCurrentViewport(newViewport)
@@ -204,7 +247,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
204
247
  // We are mutating state in place here (depending on where called) - but it's okay, this isn't used for rerender
205
248
  // eslint-disable-next-line
206
249
  const addUIDs = useCallback((obj, fromColumn) => {
207
- obj.data.forEach(row => {
250
+ obj.data.forEach((row, index) => {
208
251
  let uid = null
209
252
 
210
253
  if (row.uid) row.uid = null // Wipe existing UIDs
@@ -264,7 +307,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
264
307
  }
265
308
 
266
309
  // County Check
267
- if (('us-county' === obj.general.geoType || 'single-state' === obj.general.geoType) && 'us-geocode' !== obj.general.type) {
310
+ if (
311
+ ('us-county' === obj.general.geoType || 'single-state' === obj.general.geoType) &&
312
+ 'us-geocode' !== obj.general.type
313
+ ) {
268
314
  const fips = row[obj.columns.geo.name]
269
315
  uid = countyKeys.find(key => key === fips)
270
316
  }
@@ -273,8 +319,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
273
319
  uid = row[state.columns.geo.name]
274
320
  }
275
321
 
276
- if (!uid && state.columns.latitude?.name && state.columns.longitude?.name && row[state.columns.latitude?.name] && row[state.columns.longitude?.name]) {
277
- uid = row[state.columns.geo.name]
322
+ if (
323
+ !uid &&
324
+ state.columns.latitude?.name &&
325
+ state.columns.longitude?.name &&
326
+ row[state.columns.latitude?.name] &&
327
+ row[state.columns.longitude?.name]
328
+ ) {
329
+ uid = `${row[state.columns.geo.name]}`
278
330
  }
279
331
 
280
332
  if (uid) {
@@ -293,6 +345,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
293
345
  const newLegendMemo = new Map() // Reset memoization
294
346
  const newLegendSpecialClassLastMemo = new Map() // Reset bin memoization
295
347
  let primaryCol = obj.columns.primary.name,
348
+ isSingleState = obj.general.geoType === 'single-state',
296
349
  isBubble = obj.general.type === 'bubble',
297
350
  categoricalCol = obj.columns.categorical ? obj.columns.categorical.name : undefined,
298
351
  type = obj.legend.type,
@@ -627,7 +680,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
627
680
  const getDomain = () => {
628
681
  // backwards compatibility
629
682
  if (state?.columns?.primary?.roundToPlace !== undefined && state?.general?.equalNumberOptIn) {
630
- return _.uniq(dataSet.map(item => Number(item[state.columns.primary.name]).toFixed(Number(state?.columns?.primary?.roundToPlace))))
683
+ return _.uniq(
684
+ dataSet.map(item =>
685
+ Number(item[state.columns.primary.name]).toFixed(Number(state?.columns?.primary?.roundToPlace))
686
+ )
687
+ )
631
688
  }
632
689
  return _.uniq(dataSet.map(item => Math.round(Number(item[state.columns.primary.name]))))
633
690
  }
@@ -807,57 +864,73 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
807
864
 
808
865
  if (hash) filters.fromHash = hash
809
866
 
810
- obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown, setByQueryParameter }, idx) => {
811
- let newFilter = runtimeFilters[idx]
867
+ obj?.filters.forEach(
868
+ (
869
+ {
870
+ columnName,
871
+ label,
872
+ labels,
873
+ queryParameter,
874
+ orderedValues,
875
+ active,
876
+ values,
877
+ type,
878
+ showDropdown,
879
+ setByQueryParameter
880
+ },
881
+ idx
882
+ ) => {
883
+ let newFilter = runtimeFilters[idx]
884
+
885
+ const sortAsc = (a, b) => {
886
+ return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
887
+ }
812
888
 
813
- const sortAsc = (a, b) => {
814
- return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
815
- }
889
+ const sortDesc = (a, b) => {
890
+ return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
891
+ }
816
892
 
817
- const sortDesc = (a, b) => {
818
- return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
819
- }
893
+ if (type !== 'url') {
894
+ values = getUniqueValues(state.data, columnName)
820
895
 
821
- if (type !== 'url') {
822
- values = getUniqueValues(state.data, columnName)
896
+ if (obj.filters[idx].order === 'asc') {
897
+ values = values.sort(sortAsc)
898
+ }
823
899
 
824
- if (obj.filters[idx].order === 'asc') {
825
- values = values.sort(sortAsc)
826
- }
900
+ if (obj.filters[idx].order === 'desc') {
901
+ values = values.sort(sortDesc)
902
+ }
827
903
 
828
- if (obj.filters[idx].order === 'desc') {
829
- values = values.sort(sortDesc)
904
+ if (obj.filters[idx].order === 'cust') {
905
+ if (obj.filters[idx]?.values.length > 0) {
906
+ values = obj.filters[idx].values
907
+ }
908
+ }
909
+ } else {
910
+ values = values
830
911
  }
831
912
 
832
- if (obj.filters[idx].order === 'cust') {
833
- if (obj.filters[idx]?.values.length > 0) {
834
- values = obj.filters[idx].values
835
- }
913
+ if (undefined === newFilter) {
914
+ newFilter = {}
836
915
  }
837
- } else {
838
- values = values
839
- }
840
916
 
841
- if (undefined === newFilter) {
842
- newFilter = {}
917
+ newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
918
+ newFilter.type = type
919
+ newFilter.label = label ?? ''
920
+ newFilter.columnName = columnName
921
+ newFilter.orderedValues = orderedValues
922
+ newFilter.queryParameter = queryParameter
923
+ newFilter.labels = labels
924
+ newFilter.values = values
925
+ newFilter.setByQueryParameter = setByQueryParameter
926
+ handleSorting(newFilter)
927
+ newFilter.active = active ?? values[0] // Default to first found value
928
+ newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
929
+ newFilter.showDropdown = showDropdown
930
+
931
+ filters.push(newFilter)
843
932
  }
844
-
845
- newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
846
- newFilter.type = type
847
- newFilter.label = label ?? ''
848
- newFilter.columnName = columnName
849
- newFilter.orderedValues = orderedValues
850
- newFilter.queryParameter = queryParameter
851
- newFilter.labels = labels
852
- newFilter.values = values
853
- newFilter.setByQueryParameter = setByQueryParameter
854
- handleSorting(newFilter)
855
- newFilter.active = active ?? values[0] // Default to first found value
856
- newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
857
- newFilter.showDropdown = showDropdown
858
-
859
- filters.push(newFilter)
860
- })
933
+ )
861
934
 
862
935
  return filters
863
936
  })
@@ -882,11 +955,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
882
955
 
883
956
  if (undefined === row.uid) return false // No UID for this row, we can't use for mapping
884
957
 
885
- // When on a single state map filter runtime data by state
886
- if (!(String(row[obj.columns.geo.name]).substring(0, 2) === obj.general?.statePicked?.fipsCode) && obj.general.geoType === 'single-state' && obj.general.type !== 'us-geocode') {
887
- return false
888
- }
889
-
890
958
  if (row[obj.columns.primary.name]) {
891
959
  row[obj.columns.primary.name] = numberFromString(row[obj.columns.primary.name], state)
892
960
  }
@@ -934,7 +1002,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
934
1002
  const mapSvg = useRef(null)
935
1003
 
936
1004
  const closeModal = ({ target }) => {
937
- if ('string' === typeof target.className && (target.className.includes('modal-close') || target.className.includes('modal-background')) && null !== modal) {
1005
+ if (
1006
+ 'string' === typeof target.className &&
1007
+ (target.className.includes('modal-close') || target.className.includes('modal-background')) &&
1008
+ null !== modal
1009
+ ) {
938
1010
  setModal(null)
939
1011
  }
940
1012
  }
@@ -945,7 +1017,12 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
945
1017
  }
946
1018
 
947
1019
  // if string of letters like 'Home' then dont need to format as a number
948
- if (typeof value === 'string' && value.length > 0 && /[a-zA-Z]/.test(value) && state.legend.type === 'equalnumber') {
1020
+ if (
1021
+ typeof value === 'string' &&
1022
+ value.length > 0 &&
1023
+ /[a-zA-Z]/.test(value) &&
1024
+ state.legend.type === 'equalnumber'
1025
+ ) {
949
1026
  return value
950
1027
  }
951
1028
 
@@ -1092,6 +1169,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1092
1169
  let value = key
1093
1170
  let formattedName = ''
1094
1171
  let stateName = stateFipsToTwoDigit[key?.substring(0, 2)]
1172
+ ? stateFipsToTwoDigit[key?.substring(0, 2)]
1173
+ : key
1174
+ ? runtimeData?.[key]?.[state.columns.geo.name]
1175
+ : ''
1095
1176
 
1096
1177
  if (stateName) {
1097
1178
  formattedName += stateName
@@ -1207,9 +1288,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1207
1288
  }
1208
1289
 
1209
1290
  // If world-geocode map zoom to geo point
1210
- if ('world-geocode' === state.general.type) {
1211
- let lat = value[state.columns.latitude.name]
1212
- let long = value[state.columns.longitude.name]
1291
+ if (['world-geocode'].includes(state.general.type)) {
1292
+ const lat = value[state.columns.latitude.name]
1293
+ const long = value[state.columns.longitude.name]
1213
1294
 
1214
1295
  setState({
1215
1296
  ...state,
@@ -1234,10 +1315,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1234
1315
  }
1235
1316
 
1236
1317
  const validateFipsCodeLength = newState => {
1237
- if (newState.general.geoType === 'us-county' || newState.general.geoType === 'single-state' || (newState.general.geoType === 'us' && newState?.data)) {
1318
+ if (
1319
+ newState.general.geoType === 'us-county' ||
1320
+ newState.general.geoType === 'single-state' ||
1321
+ (newState.general.geoType === 'us' && newState?.data)
1322
+ ) {
1238
1323
  newState?.data.forEach(dataPiece => {
1239
1324
  if (dataPiece[newState.columns.geo.name]) {
1240
- if (!isNaN(parseInt(dataPiece[newState.columns.geo.name])) && dataPiece[newState.columns.geo.name].length === 4) {
1325
+ if (
1326
+ !isNaN(parseInt(dataPiece[newState.columns.geo.name])) &&
1327
+ dataPiece[newState.columns.geo.name].length === 4
1328
+ ) {
1241
1329
  dataPiece[newState.columns.geo.name] = 0 + dataPiece[newState.columns.geo.name]
1242
1330
  }
1243
1331
  dataPiece[newState.columns.geo.name] = dataPiece[newState.columns.geo.name].toString()
@@ -1355,7 +1443,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1355
1443
  ...configObj
1356
1444
  }
1357
1445
 
1358
- const urlFilters = newState.filters ? (newState.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
1446
+ const urlFilters = newState.filters
1447
+ ? newState.filters.filter(filter => filter.type === 'url').length > 0
1448
+ ? true
1449
+ : false
1450
+ : false
1359
1451
 
1360
1452
  if (newState.dataUrl && !urlFilters) {
1361
1453
  // handle urls with spaces in the name.
@@ -1399,7 +1491,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1399
1491
  validateFipsCodeLength(newState)
1400
1492
 
1401
1493
  // add ability to rename state properties over time.
1402
- const processedConfig = { ...(await coveUpdateWorker(newState)) }
1494
+ const processedConfig = { ...coveUpdateWorker(newState) }
1403
1495
 
1404
1496
  setState(processedConfig)
1405
1497
  setLoading(false)
@@ -1436,13 +1528,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1436
1528
  }
1437
1529
  }, [state, container]) // eslint-disable-line
1438
1530
 
1439
- useEffect(() => {
1440
- if (state.data) {
1441
- let newData = generateRuntimeData(state)
1442
- setRuntimeData(newData)
1443
- }
1444
- }, [state.general.statePicked]) // eslint-disable-line
1445
-
1446
1531
  useEffect(() => {
1447
1532
  // When geotype changes - add UID
1448
1533
  if (state.data && state.columns.geo.name) {
@@ -1503,6 +1588,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1503
1588
  geo: state.columns.geo.name,
1504
1589
  primary: state.columns.primary.name,
1505
1590
  mapPosition: state.mapPosition,
1591
+ map: state.map,
1506
1592
  ...runtimeFilters
1507
1593
  })
1508
1594
 
@@ -1525,7 +1611,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1525
1611
  // Legend - Update when runtimeData does
1526
1612
  const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
1527
1613
  setRuntimeLegend(legend)
1528
- }, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses, state.legend.additionalCategories]) // eslint-disable-line
1614
+ }, [
1615
+ runtimeData,
1616
+ state.legend.unified,
1617
+ state.legend.showSpecialClassesLast,
1618
+ state.legend.separateZero,
1619
+ state.general.equalNumberOptIn,
1620
+ state.legend.numberOfItems,
1621
+ state.legend.specialClasses,
1622
+ state.legend.additionalCategories
1623
+ ]) // eslint-disable-line
1529
1624
 
1530
1625
  useEffect(() => {
1531
1626
  reloadURLData()
@@ -1549,7 +1644,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1549
1644
  if (!table.label || table.label === '') table.label = 'Data Table'
1550
1645
 
1551
1646
  // Map container classes
1552
- let mapContainerClasses = ['map-container', state.legend?.position, state.general.type, state.general.geoType, 'outline-none']
1647
+ let mapContainerClasses = [
1648
+ 'map-container',
1649
+ state.legend?.position,
1650
+ state.general.type,
1651
+ state.general.geoType,
1652
+ 'outline-none'
1653
+ ]
1553
1654
 
1554
1655
  if (modal) {
1555
1656
  mapContainerClasses.push('modal-background')
@@ -1561,6 +1662,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1561
1662
 
1562
1663
  // Props passed to all map types
1563
1664
  const mapProps = {
1665
+ projection,
1666
+ setProjection,
1667
+ stateToShow,
1668
+ setStateToShow,
1669
+ setScale,
1670
+ setTranslate,
1671
+ scale,
1672
+ translate,
1564
1673
  isDraggingAnnotation,
1565
1674
  handleDragStateChange,
1566
1675
  applyLegendToRow,
@@ -1591,6 +1700,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1591
1700
  resetLegendToggles,
1592
1701
  runtimeFilters,
1593
1702
  runtimeLegend,
1703
+ runtimeData,
1594
1704
  setAccessibleStatus,
1595
1705
  setFilteredCountryCode,
1596
1706
  setParentConfig: setConfig,
@@ -1609,12 +1719,20 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1609
1719
  type: general.type,
1610
1720
  viewport: currentViewport,
1611
1721
  tooltipId,
1612
- tooltipRef
1722
+ tooltipRef,
1723
+ topoData,
1724
+ setTopoData,
1725
+ getTextWidth,
1726
+ mapId
1613
1727
  }
1614
1728
 
1615
1729
  if (!mapProps.data || !state.data) return <></>
1616
1730
 
1617
- const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading
1731
+ const hasDataTable =
1732
+ state.runtime.editorErrorMessage.length === 0 &&
1733
+ true === table.forceDisplay &&
1734
+ general.type !== 'navigation' &&
1735
+ false === loading
1618
1736
 
1619
1737
  const handleMapTabbing = () => {
1620
1738
  let tabbingID
@@ -1647,15 +1765,29 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1647
1765
  </a>
1648
1766
  )
1649
1767
 
1768
+ const sectionClassNames = () => {
1769
+ const classes = ['cove-component__content', 'cdc-map-inner-container', `${currentViewport}`]
1770
+ if (config?.runtime?.editorErrorMessage.length > 0) classes.push('type-map--has-error')
1771
+ return classes.join(' ')
1772
+ }
1773
+
1650
1774
  return (
1651
1775
  <ConfigContext.Provider value={mapProps}>
1652
- <Layout.VisualizationWrapper config={state} isEditor={isEditor} ref={outerContainerRef} imageId={imageId} showEditorPanel={state.showEditorPanel}>
1776
+ <Layout.VisualizationWrapper
1777
+ config={state}
1778
+ isEditor={isEditor}
1779
+ ref={outerContainerRef}
1780
+ imageId={imageId}
1781
+ showEditorPanel={state.showEditorPanel}
1782
+ >
1653
1783
  {isEditor && <EditorPanel columnsRequiredChecker={columnsRequiredChecker} />}
1654
1784
  <Layout.Responsive isEditor={isEditor}>
1655
- {state?.runtime?.editorErrorMessage.length > 0 && <Error state={state} />}
1656
- {requiredColumns && <Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />}
1785
+ {requiredColumns && (
1786
+ <Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
1787
+ )}
1657
1788
  {!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
1658
- <section className={`cove-component__content cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
1789
+ <section className={sectionClassNames()} aria-label={'Map: ' + title} ref={innerContainerRef}>
1790
+ {state?.runtime?.editorErrorMessage.length > 0 && <Error state={state} />}
1659
1791
  {/* prettier-ignore */}
1660
1792
  <Title
1661
1793
  title={title}
@@ -1664,12 +1796,26 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1664
1796
  classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${general.headerColor}`]}
1665
1797
  />
1666
1798
  <SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
1667
- {state?.annotations?.length > 0 && <SkipTo skipId={tabId} skipMessage={`Skip over annotations`} key={`skip-annotations`} />}
1799
+ {state?.annotations?.length > 0 && (
1800
+ <SkipTo skipId={tabId} skipMessage={`Skip over annotations`} key={`skip-annotations`} />
1801
+ )}
1668
1802
 
1669
- {general.introText && <section className='introText'>{parse(general.introText)}</section>}
1803
+ {general.introText && (
1804
+ <section className='introText' style={{ padding: '15px', margin: '0px' }}>
1805
+ {parse(general.introText)}
1806
+ </section>
1807
+ )}
1670
1808
 
1671
- {/* prettier-ignore */}
1672
- {state?.filters?.length > 0 && <Filters config={state} setConfig={setState} getUniqueValues={getUniqueValues} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
1809
+ {state?.filters?.length > 0 && (
1810
+ <Filters
1811
+ config={state}
1812
+ setConfig={setState}
1813
+ getUniqueValues={getUniqueValues}
1814
+ filteredData={runtimeFilters}
1815
+ setFilteredData={setRuntimeFilters}
1816
+ dimensions={dimensions}
1817
+ />
1818
+ )}
1673
1819
 
1674
1820
  <div
1675
1821
  role='region'
@@ -1681,9 +1827,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1681
1827
  closeModal(e)
1682
1828
  }
1683
1829
  }}
1830
+ style={{ padding: '15px 25px', margin: '0px' }}
1684
1831
  >
1685
1832
  {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
1686
- <section className='outline-none geography-container' ref={mapSvg} tabIndex='0' style={{ width: '100%' }}>
1833
+ <section className='outline-none geography-container w-100' ref={mapSvg} tabIndex='0'>
1687
1834
  {currentViewport && (
1688
1835
  <>
1689
1836
  {modal && <Modal />}
@@ -1697,10 +1844,21 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1697
1844
  )}
1698
1845
  </section>
1699
1846
 
1700
- {general.showSidebar && 'navigation' !== general.type && <Legend ref={legendRef} skipId={tabId} />}
1847
+ {general.showSidebar && 'navigation' !== general.type && (
1848
+ <Legend dimensions={dimensions} currentViewport={currentViewport} ref={legendRef} skipId={tabId} />
1849
+ )}
1701
1850
  </div>
1702
1851
 
1703
- {'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
1852
+ {'navigation' === general.type && (
1853
+ <NavigationMenu
1854
+ mapTabbingID={tabId}
1855
+ displayGeoName={displayGeoName}
1856
+ data={runtimeData}
1857
+ options={general}
1858
+ columns={state.columns}
1859
+ navigationHandler={val => navigationHandler(val)}
1860
+ />
1861
+ )}
1704
1862
 
1705
1863
  {/* Link */}
1706
1864
  {isDashboard && config.table?.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
@@ -1708,41 +1866,62 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1708
1866
  {subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
1709
1867
 
1710
1868
  <MediaControls.Section classes={['download-buttons']}>
1711
- {state.general.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
1712
- {state.general.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
1869
+ {state.general.showDownloadImgButton && (
1870
+ <MediaControls.Button
1871
+ text='Download Image'
1872
+ title='Download Chart as Image'
1873
+ type='image'
1874
+ state={state}
1875
+ elementToCapture={imageId}
1876
+ />
1877
+ )}
1878
+ {state.general.showDownloadPdfButton && (
1879
+ <MediaControls.Button
1880
+ text='Download PDF'
1881
+ title='Download Chart as PDF'
1882
+ type='pdf'
1883
+ state={state}
1884
+ elementToCapture={imageId}
1885
+ />
1886
+ )}
1713
1887
  </MediaControls.Section>
1714
1888
 
1715
- {state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading && (
1716
- <DataTable
1717
- config={state}
1718
- rawData={state.data}
1719
- navigationHandler={navigationHandler}
1720
- expandDataTable={table.expanded}
1721
- headerColor={general.headerColor}
1722
- columns={state.columns}
1723
- showDownloadButton={general.showDownloadButton}
1724
- showFullGeoNameInCSV={table.showFullGeoNameInCSV}
1725
- runtimeLegend={runtimeLegend}
1726
- runtimeData={runtimeData}
1727
- displayDataAsText={displayDataAsText}
1728
- displayGeoName={displayGeoName}
1729
- applyLegendToRow={applyLegendToRow}
1730
- tableTitle={table.label}
1731
- indexTitle={table.indexLabel}
1732
- vizTitle={general.title}
1733
- viewport={currentViewport}
1734
- formatLegendLocation={formatLegendLocation}
1735
- setFilteredCountryCode={setFilteredCountryCode}
1736
- tabbingId={tabId}
1737
- showDownloadImgButton={state.general.showDownloadImgButton}
1738
- showDownloadPdfButton={state.general.showDownloadPdfButton}
1739
- innerContainerRef={innerContainerRef}
1740
- outerContainerRef={outerContainerRef}
1741
- imageRef={imageId}
1742
- isDebug={isDebug}
1743
- wrapColumns={table.wrapColumns}
1744
- />
1745
- )}
1889
+ {state.runtime.editorErrorMessage.length === 0 &&
1890
+ true === table.forceDisplay &&
1891
+ general.type !== 'navigation' &&
1892
+ false === loading && (
1893
+ <DataTable
1894
+ config={state}
1895
+ rawData={state.data}
1896
+ navigationHandler={navigationHandler}
1897
+ expandDataTable={table.expanded}
1898
+ headerColor={general.headerColor}
1899
+ columns={state.columns}
1900
+ showDownloadButton={general.showDownloadButton}
1901
+ showFullGeoNameInCSV={table.showFullGeoNameInCSV}
1902
+ runtimeLegend={runtimeLegend}
1903
+ runtimeData={runtimeData}
1904
+ displayDataAsText={displayDataAsText}
1905
+ displayGeoName={displayGeoName}
1906
+ applyLegendToRow={applyLegendToRow}
1907
+ tableTitle={table.label}
1908
+ indexTitle={table.indexLabel}
1909
+ vizTitle={general.title}
1910
+ viewport={currentViewport}
1911
+ formatLegendLocation={formatLegendLocation}
1912
+ setFilteredCountryCode={setFilteredCountryCode}
1913
+ tabbingId={tabId}
1914
+ showDownloadImgButton={state.general.showDownloadImgButton}
1915
+ showDownloadPdfButton={state.general.showDownloadPdfButton}
1916
+ innerContainerRef={innerContainerRef}
1917
+ outerContainerRef={outerContainerRef}
1918
+ imageRef={imageId}
1919
+ isDebug={isDebug}
1920
+ wrapColumns={table.wrapColumns}
1921
+ />
1922
+ )}
1923
+
1924
+ {state.annotations.length > 0 && <Annotation.Dropdown />}
1746
1925
 
1747
1926
  {state.annotations.length > 0 && <Annotation.Dropdown />}
1748
1927
 
@@ -1754,10 +1933,27 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1754
1933
  {accessibleStatus}
1755
1934
  </div>
1756
1935
 
1757
- {!isDraggingAnnotation && !window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
1758
- <ReactTooltip id={`tooltip__${tooltipId}`} float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip tooltip-test' : 'tooltip tooltip-test'}`} style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }} />
1759
- )}
1760
- <div ref={tooltipRef} id={`tooltip__${tooltipId}-canvas`} className='tooltip' style={{ background: `rgba(255,255,255,${state.tooltips.opacity / 100})`, position: 'absolute', whiteSpace: 'nowrap' }}></div>
1936
+ {!isDraggingAnnotation &&
1937
+ !window.matchMedia('(any-hover: none)').matches &&
1938
+ 'hover' === tooltips.appearanceType && (
1939
+ <ReactTooltip
1940
+ id={`tooltip__${tooltipId}`}
1941
+ float={true}
1942
+ className={`${tooltips.capitalizeLabels ? 'capitalize tooltip tooltip-test' : 'tooltip tooltip-test'}`}
1943
+ style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }}
1944
+ />
1945
+ )}
1946
+ <div
1947
+ ref={tooltipRef}
1948
+ id={`tooltip__${tooltipId}-canvas`}
1949
+ className='tooltip'
1950
+ style={{
1951
+ background: `rgba(255,255,255,${state.tooltips.opacity / 100})`,
1952
+ position: 'absolute',
1953
+ whiteSpace: 'nowrap',
1954
+ display: 'none' // can't use d-none here
1955
+ }}
1956
+ ></div>
1761
1957
  </Layout.Responsive>
1762
1958
  </Layout.VisualizationWrapper>
1763
1959
  </ConfigContext.Provider>