@cdc/map 2.6.3 → 4.22.10

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 (75) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcmap.js +32 -18
  3. package/examples/bubble-us.json +363 -0
  4. package/examples/bubble-world.json +427 -0
  5. package/examples/default-county.json +64 -12
  6. package/examples/default-geocode.json +746 -0
  7. package/examples/default-hex.json +477 -0
  8. package/examples/default-usa-regions.json +118 -0
  9. package/examples/default-usa.json +1 -1
  10. package/examples/default-world-data.json +1450 -0
  11. package/examples/default-world.json +5 -3
  12. package/examples/example-city-state.json +46 -1
  13. package/examples/gallery/categorical-qualitative.json +797 -0
  14. package/examples/gallery/categorical-scale-based.json +739 -0
  15. package/examples/gallery/city-state.json +479 -0
  16. package/examples/gallery/county.json +22731 -0
  17. package/examples/gallery/equal-interval.json +1027 -0
  18. package/examples/gallery/equal-number.json +1027 -0
  19. package/examples/gallery/filterable.json +909 -0
  20. package/examples/gallery/hex-filtered.json +420 -0
  21. package/examples/gallery/hex.json +413 -0
  22. package/examples/gallery/single-state.json +21402 -0
  23. package/examples/gallery/world.json +1592 -0
  24. package/examples/private/atsdr.json +439 -0
  25. package/examples/private/atsdr_new.json +436 -0
  26. package/examples/private/bubble.json +285 -0
  27. package/examples/private/city-state.json +428 -0
  28. package/examples/private/cty-issue.json +42768 -0
  29. package/examples/private/default-usa.json +460 -0
  30. package/examples/private/default-world-data.json +1444 -0
  31. package/examples/private/default.json +968 -0
  32. package/examples/private/legend-issue.json +1 -0
  33. package/examples/private/map-rounding-error.json +42759 -0
  34. package/examples/private/map.csv +60 -0
  35. package/examples/private/mdx.json +210 -0
  36. package/examples/private/monkeypox.json +376 -0
  37. package/examples/private/regions.json +52 -0
  38. package/examples/private/valid-data-map.csv +59 -0
  39. package/examples/private/wcmsrd-13881-data.json +2858 -0
  40. package/examples/private/wcmsrd-13881.json +5823 -0
  41. package/examples/private/wcmsrd-14492-data.json +292 -0
  42. package/examples/private/wcmsrd-14492.json +114 -0
  43. package/examples/private/wcmsrd-test.json +268 -0
  44. package/examples/private/world.json +1580 -0
  45. package/examples/private/worldmap.json +1490 -0
  46. package/package.json +51 -50
  47. package/src/CdcMap.js +1384 -1075
  48. package/src/components/BubbleList.js +244 -0
  49. package/src/components/CityList.js +79 -17
  50. package/src/components/CountyMap.js +104 -44
  51. package/src/components/DataTable.js +32 -22
  52. package/src/components/EditorPanel.js +977 -414
  53. package/src/components/Geo.js +1 -1
  54. package/src/components/Modal.js +2 -1
  55. package/src/components/NavigationMenu.js +4 -3
  56. package/src/components/Sidebar.js +14 -19
  57. package/src/components/SingleStateMap.js +178 -249
  58. package/src/components/UsaMap.js +104 -36
  59. package/src/components/UsaRegionMap.js +320 -0
  60. package/src/components/WorldMap.js +117 -34
  61. package/src/data/country-coordinates.js +250 -0
  62. package/src/data/{dfc-map.json → county-map.json} +0 -0
  63. package/src/data/initial-state.js +23 -3
  64. package/src/data/state-coordinates.js +55 -0
  65. package/src/data/supported-geos.js +101 -15
  66. package/src/data/us-regions-topo-2.json +360525 -0
  67. package/src/data/us-regions-topo.json +37894 -0
  68. package/src/hooks/useColorPalette.ts +96 -0
  69. package/src/index.html +8 -4
  70. package/src/scss/editor-panel.scss +78 -57
  71. package/src/scss/main.scss +1 -6
  72. package/src/scss/map.scss +126 -2
  73. package/src/scss/sidebar.scss +2 -1
  74. package/src/data/color-palettes.js +0 -200
  75. package/src/images/map-folded.svg +0 -1
@@ -0,0 +1,244 @@
1
+ import React, {memo, useState, useEffect} from 'react'
2
+ import { scaleLinear } from 'd3-scale';
3
+ import { countryCoordinates } from '../data/country-coordinates';
4
+ import stateCoordinates from '../data/state-coordinates';
5
+ import ReactTooltip from 'react-tooltip'
6
+
7
+ export const BubbleList = (
8
+ {
9
+ data: dataImport,
10
+ state,
11
+ projection,
12
+ applyLegendToRow,
13
+ applyTooltipsToGeo,
14
+ handleCircleClick,
15
+ runtimeData,
16
+ displayGeoName
17
+ }) => {
18
+
19
+ useEffect(() => {
20
+ ReactTooltip.hide()
21
+ }, [runtimeData]);
22
+
23
+ const maxDataValue = Math.max(...dataImport.map(d => d[state.columns.primary.name]))
24
+ const hasBubblesWithZeroOnMap = state.visual.showBubbleZeros ? 0 : 1;
25
+ // sort runtime data. Smaller bubbles should appear on top.
26
+ const sortedRuntimeData = Object.values(runtimeData).sort((a, b) => a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1 )
27
+ if(!sortedRuntimeData) return;
28
+
29
+ const clickTolerance = 10;
30
+
31
+ // Set bubble sizes
32
+ var size = scaleLinear()
33
+ .domain([hasBubblesWithZeroOnMap, maxDataValue])
34
+ .range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
35
+
36
+ // Start looping through the countries to create the bubbles.
37
+ if(state.general.geoType === 'world') {
38
+ const countries = sortedRuntimeData && sortedRuntimeData.map( (country, index) => {
39
+
40
+ let coordinates = countryCoordinates[country.uid]
41
+
42
+ if(!coordinates) return true;
43
+
44
+ const countryName = displayGeoName(country[state.columns.geo.name]);
45
+ const toolTip = applyTooltipsToGeo(countryName, country);
46
+ const legendColors = applyLegendToRow(country);
47
+
48
+ let primaryKey = state.columns.primary.name
49
+ if ((Math.floor(Number(size(country[primaryKey]))) === 0 || country[primaryKey] === "") && !state.visual.showBubbleZeros) return;
50
+
51
+ let transform = `translate(${projection([coordinates[1], coordinates[0]])})`
52
+
53
+ let pointerX, pointerY;
54
+
55
+ if( !projection(coordinates) ) return true;
56
+
57
+ const circle = (
58
+ <>
59
+ <circle
60
+ key={`circle-${countryName.replace(' ', '')}`}
61
+ data-tip={toolTip}
62
+ data-for="tooltip"
63
+ className={`bubble country--${countryName}`}
64
+ cx={ Number(projection(coordinates[1], coordinates[0])[0]) || 0 } // || 0 handles error on loads where the data isn't ready
65
+ cy={ Number(projection(coordinates[1], coordinates[0])[1]) || 0 }
66
+ r={ Number(size(country[primaryKey])) }
67
+ fill={legendColors[0] }
68
+ stroke={legendColors[0]}
69
+ strokeWidth={1.25}
70
+ fillOpacity={.4}
71
+ onPointerDown={(e) => {
72
+ pointerX = e.clientX;
73
+ pointerY = e.clientY;
74
+ }}
75
+ onPointerUp={(e) => {
76
+ if(pointerX && pointerY &&
77
+ e.clientX > (pointerX - clickTolerance) &&
78
+ e.clientX < (pointerX + clickTolerance) &&
79
+ e.clientY > (pointerY - clickTolerance) &&
80
+ e.clientY < (pointerY + clickTolerance)
81
+ ){
82
+ handleCircleClick(country)
83
+ pointerX = undefined;
84
+ pointerY = undefined;
85
+ }
86
+ }}
87
+ transform={transform}
88
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
89
+ />
90
+
91
+ {state.visual.extraBubbleBorder &&
92
+ <circle
93
+ key={`circle-${countryName.replace(' ', '')}`}
94
+ data-tip={toolTip}
95
+ data-for="tooltip"
96
+ className="bubble"
97
+ cx={ Number(projection(coordinates[1], coordinates[0])[0]) || 0 } // || 0 handles error on loads where the data isn't ready
98
+ cy={ Number(projection(coordinates[1], coordinates[0])[1]) || 0 }
99
+ r={ Number(size(country[primaryKey])) + 1 }
100
+ fill={ "transparent" }
101
+ stroke={ "white" }
102
+ strokeWidth={.5}
103
+ onPointerDown={(e) => {
104
+ pointerX = e.clientX;
105
+ pointerY = e.clientY;
106
+ }}
107
+ onPointerUp={(e) => {
108
+ if(pointerX && pointerY &&
109
+ e.clientX > (pointerX - clickTolerance) &&
110
+ e.clientX < (pointerX + clickTolerance) &&
111
+ e.clientY > (pointerY - clickTolerance) &&
112
+ e.clientY < (pointerY + clickTolerance)
113
+ ){
114
+ handleCircleClick(country)
115
+ pointerX = undefined;
116
+ pointerY = undefined;
117
+ }
118
+ }}
119
+ transform={transform}
120
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
121
+ />
122
+ }
123
+ </>
124
+ );
125
+
126
+
127
+ return (
128
+ <g key={`group-${countryName.replace(' ', '')}`}>
129
+ {circle}
130
+ </g>
131
+ )
132
+ })
133
+ return countries;
134
+ }
135
+
136
+ if(state.general.geoType === 'us') {
137
+ const bubbles = sortedRuntimeData && sortedRuntimeData.map( (item, index) => {
138
+ let stateData = stateCoordinates[item.uid]
139
+ let primaryKey = state?.columns?.primary?.name
140
+ if ( Number(size(item[primaryKey])) === 0) return;
141
+
142
+ if (item[primaryKey] === null) item[primaryKey] = ""
143
+
144
+ // Return if hiding zeros on the map
145
+ if( (Math.floor(Number(size(item[primaryKey]))) === 0 || item[primaryKey] === "" )&& !state.visual.showBubbleZeros ) return;
146
+
147
+ if(!stateData) return true;
148
+ let longitude = Number( stateData.Longitude);
149
+ let latitude = Number( stateData.Latitude);
150
+ let coordinates = [longitude, latitude]
151
+ //console.log('projection', projection([longitude, latitude]))
152
+ let stateName = stateData.Name;
153
+ if (!coordinates) return true;
154
+
155
+ stateName = displayGeoName(stateName);
156
+ const toolTip = applyTooltipsToGeo(stateName, item);
157
+ const legendColors = applyLegendToRow(item);
158
+
159
+
160
+ let transform = `translate(${projection([coordinates[1], coordinates[0]])})`
161
+
162
+ if ( !projection(coordinates) ) return true;
163
+
164
+ let pointerX, pointerY;
165
+ const circle = (
166
+ <>
167
+ <circle
168
+ key={`circle-${stateName.replace(' ', '')}`}
169
+ data-tip={toolTip}
170
+ data-for="tooltip"
171
+ className="bubble"
172
+ cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
173
+ cy={projection(coordinates)[1] || 0}
174
+ r={Number(size(item[primaryKey]))}
175
+ fill={legendColors[0]}
176
+ stroke={legendColors[0]}
177
+ strokeWidth={1.25}
178
+ fillOpacity={.4}
179
+ onPointerDown={(e) => {
180
+ pointerX = e.clientX;
181
+ pointerY = e.clientY;
182
+ }}
183
+ onPointerUp={(e) => {
184
+ if (pointerX && pointerY &&
185
+ e.clientX > (pointerX - clickTolerance) &&
186
+ e.clientX < (pointerX + clickTolerance) &&
187
+ e.clientY > (pointerY - clickTolerance) &&
188
+ e.clientY < (pointerY + clickTolerance)
189
+ ) {
190
+ handleCircleClick(state)
191
+ pointerX = undefined;
192
+ pointerY = undefined;
193
+ }
194
+ }}
195
+ transform={transform}
196
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
197
+ />
198
+ { state.visual.extraBubbleBorder &&
199
+ <circle
200
+ key={`circle-${stateName.replace(' ', '')}`}
201
+ data-tip={toolTip}
202
+ data-for="tooltip"
203
+ className="bubble"
204
+ cx={ projection(coordinates)[0] || 0 } // || 0 handles error on loads where the data isn't ready
205
+ cy={ projection(coordinates)[1] || 0 }
206
+ r={ Number(size(item[primaryKey])) + 1 }
207
+ fill={"transparent"}
208
+ stroke={"white"}
209
+ strokeWidth={.5}
210
+ fillOpacity={.4}
211
+ onPointerDown={(e) => {
212
+ pointerX = e.clientX;
213
+ pointerY = e.clientY;
214
+ }}
215
+ onPointerUp={(e) => {
216
+ if (pointerX && pointerY &&
217
+ e.clientX > (pointerX - clickTolerance) &&
218
+ e.clientX < (pointerX + clickTolerance) &&
219
+ e.clientY > (pointerY - clickTolerance) &&
220
+ e.clientY < (pointerY + clickTolerance)
221
+ ) {
222
+ handleCircleClick(state)
223
+ pointerX = undefined;
224
+ pointerY = undefined;
225
+ }
226
+ }}
227
+ transform={transform}
228
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
229
+ />
230
+ }
231
+ </>
232
+ );
233
+
234
+
235
+ return (
236
+ <g key={`group-${stateName.replace(' ', '')}`}>
237
+ {circle}
238
+ </g>
239
+ )
240
+ })
241
+ return bubbles;
242
+ }
243
+ }
244
+ export default BubbleList
@@ -2,6 +2,8 @@ import React, { useState, useEffect, useContext } from 'react';
2
2
  /** @jsx jsx */
3
3
  import { jsx } from '@emotion/react'
4
4
  import { supportedCities } from '../data/supported-geos';
5
+ import { scaleLinear } from 'd3-scale';
6
+ import ReactTooltip from 'react-tooltip';
5
7
 
6
8
  const CityList = (({
7
9
  data,
@@ -11,26 +13,56 @@ const CityList = (({
11
13
  displayGeoName,
12
14
  applyLegendToRow,
13
15
  projection,
14
- titleCase
16
+ titleCase,
17
+ setSharedFilterValue,
18
+ isFilterValueSupported,
19
+ isGeoCodeMap
15
20
  }) => {
21
+
16
22
  const [citiesData, setCitiesData] = useState({});
17
23
 
18
24
  useEffect(() => {
19
- const citiesList = Object.keys(data).filter((item) => Object.keys(supportedCities).includes(item));
25
+ ReactTooltip.rebuild()
26
+ });
20
27
 
21
- const citiesDictionary = {};
28
+ useEffect(() => {
29
+ if(!isGeoCodeMap) {
30
+ const citiesList = Object.keys(data).filter((item) => Object.keys(supportedCities).includes(item));
31
+
32
+ const citiesDictionary = {};
33
+
34
+ citiesList.map((city) => citiesDictionary[city] = data[city]);
35
+
36
+ setCitiesData(citiesDictionary);
37
+ } else {
38
+ const citiesDictionary = {};
39
+ state.data.map(city => citiesDictionary[city[state.columns.geo.name]] = city)
40
+ setCitiesData(citiesDictionary);
41
+ }
42
+ }, [data, state.data]);
22
43
 
23
- citiesList.map((city) => citiesDictionary[city] = data[city]);
44
+ if (state.general.type === 'bubble') {
45
+ const maxDataValue = Math.max(...state.data.map(d => d[state.columns.primary.name]))
46
+ const sortedRuntimeData = Object.values(data).sort((a, b) => a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1)
47
+ if (!sortedRuntimeData) return;
24
48
 
25
- setCitiesData(citiesDictionary);
26
- }, [data]);
49
+ // Set bubble sizes
50
+ var size = scaleLinear()
51
+ .domain([1, maxDataValue])
52
+ .range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
27
53
 
28
- const cityList = Object.keys(citiesData).filter((c) => undefined !== data[c]);
54
+ }
55
+ let cityList = isGeoCodeMap ? Object.keys(citiesData).filter((c) => undefined !== c) : Object.keys(citiesData).filter((c) => undefined !== data[c]);
56
+ if(!cityList) return true;
29
57
 
58
+ // Cities output
30
59
  const cities = cityList.map((city, i) => {
31
- const cityDisplayName = titleCase( displayGeoName(city) );
32
60
 
33
- const legendColors = applyLegendToRow(data[city]);
61
+ const geoData = isGeoCodeMap ? state.data.filter(item => city === item[state.columns.geo.name])[0] : data[city];
62
+
63
+ const cityDisplayName = isGeoCodeMap ? city : titleCase( displayGeoName(city) );
64
+
65
+ const legendColors = (isGeoCodeMap && geoData) ? applyLegendToRow(geoData) : data[city] ? applyLegendToRow(data[city]) : false;
34
66
 
35
67
  if (legendColors === false) {
36
68
  return true;
@@ -38,8 +70,8 @@ const CityList = (({
38
70
 
39
71
  const styles = {
40
72
  fill: legendColors[0],
41
- outline: 0,
42
- stroke: 'rgba(0, 0, 0, 0.4)',
73
+ opacity: setSharedFilterValue && isFilterValueSupported && data[city][state.columns.geo.name] !== setSharedFilterValue ? .5 : 1,
74
+ stroke: setSharedFilterValue && isFilterValueSupported && data[city][state.columns.geo.name] === setSharedFilterValue ? 'rgba(0, 0, 0, 1)' : 'rgba(0, 0, 0, 0.4)',
43
75
  '&:hover': {
44
76
  fill: legendColors[1],
45
77
  outline: 0
@@ -50,16 +82,20 @@ const CityList = (({
50
82
  }
51
83
  };
52
84
 
53
- const geoData = data[city];
85
+
54
86
 
55
87
  const toolTip = applyTooltipsToGeo(cityDisplayName, data[city]);
56
88
 
57
89
  // If we need to add a cursor pointer
58
- if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'click') {
90
+ if ((state.columns.navigate && (geoData?.[state.columns.navigate.name] && geoData[state.columns.navigate.name]) ) || state.tooltips.appearanceType === 'click') {
59
91
  styles.cursor = 'pointer'
60
92
  }
61
93
 
62
- const radius = state.general.geoType === 'us' ? 8 : 4;
94
+ const radius = state.general.geoType === 'us' && !isGeoCodeMap ? 8 : isGeoCodeMap ? 2 : 4;
95
+
96
+ const additionalProps = {
97
+ fillOpacity: state.general.type === 'bubble' ? .4 : 1
98
+ }
63
99
 
64
100
  const circle = (
65
101
  <circle
@@ -67,13 +103,38 @@ const CityList = (({
67
103
  data-for="tooltip"
68
104
  cx={0}
69
105
  cy={0}
70
- r={radius}
106
+ r={ state.general.type === 'bubble' ? size(geoData[state.columns.primary.name]) : radius}
71
107
  title="Click for more information"
72
108
  onClick={() => geoClickHandler(cityDisplayName, geoData)}
109
+ {...additionalProps}
73
110
  />
74
111
  );
75
112
 
76
- let transform = `translate(${projection(supportedCities[city])})`
113
+ const pin = (
114
+ <path
115
+ className="marker"
116
+ d="M0,0l-8.8-17.7C-12.1-24.3-7.4-32,0-32h0c7.4,0,12.1,7.7,8.8,14.3L0,0z"
117
+ title="Click for more information"
118
+ onClick={() => geoClickHandler(cityDisplayName, geoData)}
119
+ data-tip={toolTip}
120
+ data-for="tooltip"
121
+ strokeWidth={2}
122
+ stroke={'black'}
123
+ {...additionalProps}
124
+ >
125
+ </path>
126
+ );
127
+
128
+ let transform = '';
129
+
130
+ if (!isGeoCodeMap) {
131
+ transform = `translate(${projection(supportedCities[city])})`
132
+ }
133
+
134
+ if (isGeoCodeMap) {
135
+ let coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
136
+ transform = `translate(${projection(coords)})`
137
+ }
77
138
 
78
139
  return (
79
140
  <g
@@ -82,7 +143,8 @@ const CityList = (({
82
143
  css={styles}
83
144
  className="geo-point"
84
145
  >
85
- {circle}
146
+ {state.visual.cityStyle === 'circle' && circle }
147
+ {state.visual.cityStyle === 'pin' && pin }
86
148
  </g>
87
149
  );
88
150
  });
@@ -1,14 +1,16 @@
1
1
  import React, { useState, useEffect, memo, useRef } from 'react';
2
+ import Loading from '@cdc/core/components/Loading';
2
3
  /** @jsx jsx */
3
4
  import { jsx } from '@emotion/react';
4
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
5
6
  import { geoCentroid, geoPath } from 'd3-geo';
6
7
  import { feature, mesh } from 'topojson-client';
7
8
  import { CustomProjection } from '@visx/geo';
8
- import colorPalettes from '../data/color-palettes';
9
+ import colorPalettes from '../../../core/data/colorPalettes'
9
10
  import { geoAlbersUsaTerritories } from 'd3-composite-projections';
10
- import testJSON from '../data/dfc-map.json';
11
+ import testJSON from '../data/county-map.json';
11
12
  import { abbrs } from '../data/abbreviations';
13
+ import CityList from './CityList';
12
14
 
13
15
  const offsets = {
14
16
  Vermont: [50, -8],
@@ -43,31 +45,21 @@ const STATE_INACTIVE_FILL = '#F4F7FA';
43
45
  const projection = geoAlbersUsaTerritories().translate([WIDTH / 2, HEIGHT / 2]);
44
46
  const path = geoPath().projection(projection);
45
47
  const stateLines = path(mesh(testJSON, testJSON.objects.states));
48
+ const countyLines = path(mesh(testJSON, testJSON.objects.counties));
46
49
 
47
- const nudges = {
48
- 'US-FL': [15, 3],
49
- 'US-AK': [0, -8],
50
- 'US-CA': [-10, 0],
51
- 'US-NY': [5, 0],
52
- 'US-MI': [13, 20],
53
- 'US-LA': [-10, -3],
54
- 'US-HI': [-10, 10],
55
- 'US-ID': [0, 10],
56
- 'US-WV': [-2, 2],
57
- };
58
50
 
59
51
  function CountyMapChecks(prevState, nextState) {
52
+ const equalNumberOptIn = prevState.state.general.equalNumberOptIn && nextState.state.general.equalNumberOptIn;
60
53
  const equalColumnName = prevState.state.general.type && nextState.state.general.type;
61
54
  const equalNavColumn = prevState.state.columns.navigate && nextState.state.columns.navigate;
62
55
  const equalLegend = prevState.runtimeLegend === nextState.runtimeLegend;
63
56
  const equalBorderColors = prevState.state.general.geoBorderColor === nextState.state.general.geoBorderColor; // update when geoborder color changes
64
57
  const equalMapColors = prevState.state.color === nextState.state.color; // update when map colors change
65
58
  const equalData = prevState.data === nextState.data; // update when data changes
66
- return equalMapColors && equalData && equalBorderColors && equalLegend && equalColumnName && equalNavColumn ? true : false;
59
+ return equalMapColors && equalData && equalBorderColors && equalLegend && equalColumnName && equalNavColumn && equalNumberOptIn ? true : false;
67
60
  }
68
61
 
69
62
  const CountyMap = (props) => {
70
-
71
63
  let mapData = states.concat(counties);
72
64
 
73
65
  const {
@@ -79,6 +71,10 @@ const CountyMap = (props) => {
79
71
  displayGeoName,
80
72
  rebuildTooltips,
81
73
  containerEl,
74
+ handleMapAriaLabels,
75
+ titleCase,
76
+ setSharedFilterValue,
77
+ isFilterValueSupported
82
78
  } = props;
83
79
 
84
80
  useEffect(() => {
@@ -213,27 +209,42 @@ const CountyMap = (props) => {
213
209
  };
214
210
 
215
211
  const onReset = (e) => {
216
- e.preventDefault();
217
- const svg = document.querySelector('.svg-container')
218
212
 
219
- svg.setAttribute('data-scaleZoom', 0)
213
+ if (state.general.type !== 'us-geocode') {
214
+ e.preventDefault();
215
+ const svg = document.querySelector('.svg-container')
220
216
 
217
+ svg.setAttribute('data-scaleZoom', 0)
221
218
 
222
- const allStates = document.querySelectorAll('.state path');
223
- const allCounties = document.querySelectorAll('.county path');
224
219
 
225
- stateLinesPath.current.setAttribute('stroke', geoStrokeColor);
226
- stateLinesPath.current.setAttribute('stroke-width', startingLineWidth);
220
+ const allStates = document.querySelectorAll('.state path');
221
+ const allCounties = document.querySelectorAll('.county path');
222
+
223
+ stateLinesPath.current.setAttribute('stroke', geoStrokeColor);
224
+ stateLinesPath.current.setAttribute('stroke-width', startingLineWidth);
227
225
 
228
- let otherStates = document.querySelectorAll(`.state--inactive`);
229
- otherStates.forEach((el) => (el.style.display = 'none'));
230
- allCounties.forEach((el) => (el.style.strokeWidth = 0.85));
231
- allStates.forEach((state) => state.setAttribute('stroke-width', .75 / .85 ));
226
+ let otherStates = document.querySelectorAll(`.state--inactive`);
227
+ otherStates.forEach((el) => (el.style.display = 'none'));
228
+ allCounties.forEach((el) => (el.style.strokeWidth = 0.85));
229
+ allStates.forEach((state) => state.setAttribute('stroke-width', .75 / .85));
232
230
 
233
- mapGroup.current.setAttribute('transform', `translate(${[0, 0]}) scale(${0.85})`);
231
+ mapGroup.current.setAttribute('transform', `translate(${[0, 0]}) scale(${0.85})`);
234
232
 
235
- // reset button
236
- resetButton.current.style.display = 'none';
233
+ // reset button
234
+ resetButton.current.style.display = 'none';
235
+ } else {
236
+ const svg = document.querySelector('.svg-container')
237
+ const allStates = document.querySelectorAll('.state');
238
+ document.querySelector('#focusedBorder path').style.stroke = 'none';
239
+ allStates.forEach(item => item.classList.remove('state--inactive'))
240
+ //document.querySelectorAll('.state path').forEach(item => item.style.fill = 'rgb(244, 247, 250)')
241
+ document.querySelectorAll('.state').forEach(item => item.style.display = 'block')
242
+ stateLinesPath.current.setAttribute('stroke', geoStrokeColor);
243
+ stateLinesPath.current.setAttribute('stroke-width', startingLineWidth);
244
+ svg.setAttribute('data-scaleZoom', 0)
245
+ mapGroup.current.setAttribute('transform', `translate(${[0, 0]}) scale(${0.85})`);
246
+ resetButton.current.style.display = 'none';
247
+ }
237
248
  };
238
249
 
239
250
  function setStateLeave() {
@@ -259,11 +270,11 @@ const CountyMap = (props) => {
259
270
  focusedBorderPath.current.setAttribute('d', focusedStateLine);
260
271
  focusedBorderPath.current.setAttribute('stroke', '#000');
261
272
 
262
- if(scale) {
263
- allStates.forEach( state => state.setAttribute('stroke-width', 0.75 / scale))
264
- focusedBorderPath.current.setAttribute('stroke-width', 0.75 / scale );
273
+ if (scale) {
274
+ allStates.forEach(state => state.setAttribute('stroke-width', 0.75 / scale))
275
+ focusedBorderPath.current.setAttribute('stroke-width', 0.75 / scale);
265
276
  }
266
-
277
+
267
278
  }
268
279
 
269
280
  const StateLines = memo(({ stateLines, lineWidth, geoStrokeColor }) => {
@@ -422,6 +433,12 @@ const CountyMap = (props) => {
422
433
  return output;
423
434
  });
424
435
 
436
+ const GeoCodeCountyLines = memo(() => {
437
+ return (
438
+ <path d={countyLines} className="county-borders" style={{ stroke: geoStrokeColor}} />
439
+ )
440
+ })
441
+
425
442
  const StateOutput = memo(({ geographies, states }) => {
426
443
  let output = [];
427
444
  output.push(
@@ -444,13 +461,25 @@ const CountyMap = (props) => {
444
461
 
445
462
  const geoDisplayName = displayGeoName(geoKey);
446
463
 
447
- let stateStyles = {
448
- cursor: 'default',
449
- stroke: STATE_BORDER,
450
- strokeWidth: 0.75 / scale,
451
- display: !focusedState ? 'none' : focusedState && focusedState !== geo.id ? 'block' : 'none',
452
- fill: focusedState && focusedState !== geo.id ? STATE_INACTIVE_FILL : 'none',
453
- };
464
+ let stateStyles = {}
465
+
466
+ if (state.general.type !== 'us-geocode') {
467
+ stateStyles = {
468
+ cursor: 'default',
469
+ stroke: STATE_BORDER,
470
+ strokeWidth: 0.75 / scale,
471
+ display: !focusedState ? 'none' : focusedState && focusedState !== geo.id ? 'block' : 'none',
472
+ fill: focusedState && focusedState !== geo.id ? STATE_INACTIVE_FILL : 'none',
473
+ };
474
+ } else {
475
+ stateStyles = {
476
+ cursor: 'default',
477
+ stroke: STATE_BORDER,
478
+ strokeWidth: 0.75 / scale,
479
+ display: 'block',
480
+ fill: '#f4f7fa',
481
+ };
482
+ }
454
483
 
455
484
  let stateSelectedStyles = {
456
485
  fillOpacity: 1,
@@ -501,7 +530,14 @@ const CountyMap = (props) => {
501
530
  const states = geographies.slice(0, 56);
502
531
  const counties = geographies.slice(56);
503
532
  let geosJsx = [];
504
- geosJsx.push(<CountyOutput geographies={geographies} counties={counties} key="county-key" />);
533
+ {
534
+ 'us-geocode' !== state.general.type &&
535
+ geosJsx.push(<CountyOutput geographies={geographies} counties={counties} key="county-key" />);
536
+ }
537
+ {
538
+ 'us-geocode' === state.general.type &&
539
+ geosJsx.push(<GeoCodeCountyLines />);
540
+ }
505
541
  geosJsx.push(<StateOutput geographies={geographies} states={states} key="state-key" />);
506
542
  geosJsx.push(
507
543
  <StateLines
@@ -512,12 +548,34 @@ const CountyMap = (props) => {
512
548
  />
513
549
  );
514
550
  geosJsx.push(<FocusedStateBorder key="focused-border-key" />);
551
+ geosJsx.push(<CityList
552
+ projection={projection}
553
+ key="cities"
554
+ data={data}
555
+ state={state}
556
+ geoClickHandler={geoClickHandler}
557
+ applyTooltipsToGeo={applyTooltipsToGeo}
558
+ displayGeoName={displayGeoName}
559
+ applyLegendToRow={applyLegendToRow}
560
+ titleCase={titleCase}
561
+ setSharedFilterValue={setSharedFilterValue}
562
+ isFilterValueSupported={isFilterValueSupported}
563
+ isGeoCodeMap={state.general.type === 'us-geocode'}
564
+ />)
515
565
  return geosJsx;
516
566
  };
517
-
567
+ if(!data) <Loading />
518
568
  return (
519
569
  <ErrorBoundary component='CountyMap'>
520
- <svg viewBox={`0 0 ${WIDTH} ${HEIGHT}`} preserveAspectRatio='xMinYMin' className='svg-container' data-scale={scale ? scale : ''} data-translate={translate ? translate : ''}>
570
+ <svg
571
+ viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
572
+ preserveAspectRatio='xMinYMin'
573
+ className='svg-container'
574
+ data-scale={scale ? scale : ''}
575
+ data-translate={translate ? translate : ''}
576
+ role="img"
577
+ aria-label={handleMapAriaLabels(state)}
578
+ >
521
579
  <rect
522
580
  className='background center-container ocean'
523
581
  width={WIDTH}
@@ -540,12 +598,14 @@ const CountyMap = (props) => {
540
598
  transform={`translate(${translate}) scale(${scale})`}
541
599
  key='countyMapGroup'
542
600
  >
543
- {constructGeoJsx(features, geoAlbersUsaTerritories)}
601
+ {constructGeoJsx(features, projection)}
544
602
  </g>
545
603
  );
546
604
  }}
547
605
  </CustomProjection>
548
606
  </svg>
607
+
608
+ {/* TODO: Refactor to COVE button */}
549
609
  <button className={`btn btn--reset`} onClick={onReset} ref={resetButton} style={{ display: 'none' }} tabIndex="0">
550
610
  Reset Zoom
551
611
  </button>