@cdc/map 4.24.7 → 4.24.9-1

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