@cdc/map 2.6.0 → 2.6.3

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 (61) hide show
  1. package/convert-topojson.js +70 -0
  2. package/dist/cdcmap.js +190 -0
  3. package/examples/default-county.json +105 -0
  4. package/examples/default-single-state.json +109 -0
  5. package/examples/default-usa.json +968 -0
  6. package/examples/default-world.json +1495 -0
  7. package/examples/example-city-state.json +474 -0
  8. package/examples/example-world-map.json +1596 -0
  9. package/examples/gender-rate-map.json +1 -0
  10. package/package.json +50 -50
  11. package/src/CdcMap.js +1384 -0
  12. package/src/components/CityList.js +93 -0
  13. package/src/components/CountyMap.js +556 -0
  14. package/src/components/DataTable.js +357 -0
  15. package/src/components/EditorPanel.js +2111 -0
  16. package/src/components/Geo.js +21 -0
  17. package/src/components/Modal.js +31 -0
  18. package/src/components/NavigationMenu.js +66 -0
  19. package/src/components/Sidebar.js +167 -0
  20. package/src/components/SingleStateMap.js +326 -0
  21. package/src/components/UsaMap.js +342 -0
  22. package/src/components/WorldMap.js +175 -0
  23. package/src/components/ZoomableGroup.js +47 -0
  24. package/src/data/abbreviations.js +57 -0
  25. package/src/data/color-palettes.js +200 -0
  26. package/src/data/county-map-halfquality.json +58453 -0
  27. package/src/data/county-map-quarterquality.json +1 -0
  28. package/src/data/county-topo.json +1 -0
  29. package/src/data/dfc-map.json +1 -0
  30. package/src/data/initial-state.js +60 -0
  31. package/src/data/newtest.json +1 -0
  32. package/src/data/state-abbreviations.js +60 -0
  33. package/src/data/supported-geos.js +3775 -0
  34. package/src/data/test.json +1 -0
  35. package/src/data/us-hex-topo.json +1 -0
  36. package/src/data/us-topo.json +1 -0
  37. package/src/data/world-topo.json +1 -0
  38. package/src/hooks/useActiveElement.js +19 -0
  39. package/src/hooks/useZoomPan.js +110 -0
  40. package/src/images/active-checkmark.svg +1 -0
  41. package/src/images/asc.svg +1 -0
  42. package/src/images/close.svg +1 -0
  43. package/src/images/desc.svg +1 -0
  44. package/src/images/external-link.svg +1 -0
  45. package/src/images/icon-download-img.svg +1 -0
  46. package/src/images/icon-download-pdf.svg +1 -0
  47. package/src/images/inactive-checkmark.svg +1 -0
  48. package/src/images/map-folded.svg +1 -0
  49. package/src/index.html +29 -0
  50. package/src/index.js +20 -0
  51. package/src/scss/btn.scss +69 -0
  52. package/src/scss/datatable.scss +7 -0
  53. package/src/scss/editor-panel.scss +654 -0
  54. package/src/scss/main.scss +224 -0
  55. package/src/scss/map.scss +188 -0
  56. package/src/scss/sidebar.scss +146 -0
  57. package/src/scss/tooltips.scss +30 -0
  58. package/src/scss/variables.scss +1 -0
  59. package/uploads/upload-example-city-state.json +392 -0
  60. package/uploads/upload-example-world-data.json +1490 -0
  61. package/LICENSE +0 -201
@@ -0,0 +1,21 @@
1
+ import React, { memo } from 'react';
2
+
3
+ const Geo = ({path, styles, stroke, strokeWidth, ...props}) => {
4
+ return (
5
+ <g
6
+ className="geo-group"
7
+ css={styles}
8
+ {...props}
9
+ >
10
+ <path
11
+ tabIndex={-1}
12
+ className='single-geo'
13
+ stroke={stroke}
14
+ strokeWidth={strokeWidth}
15
+ d={path}
16
+ />
17
+ </g>
18
+ )
19
+ }
20
+
21
+ export default memo(Geo)
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import closeIcon from '../images/close.svg?inline';
3
+ import LegendCircle from '@cdc/core/components/LegendCircle';
4
+
5
+ const Modal = (props) => {
6
+ const {
7
+ applyTooltipsToGeo,
8
+ content,
9
+ capitalize,
10
+ applyLegendToRow,
11
+ viewport,
12
+ type
13
+ } = props;
14
+
15
+ const tooltip = applyTooltipsToGeo(content.geoName, content.keyedData, 'jsx');
16
+
17
+ const legendColors = applyLegendToRow(content.keyedData);
18
+
19
+ return (
20
+ <section className={capitalize ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport} aria-hidden="true">
21
+ <img src={closeIcon} className="modal-close" alt="Close Modal" />
22
+ {type === 'data' && <LegendCircle fill={legendColors[0]} />}
23
+ <div className="content">
24
+ {tooltip}
25
+ </div>
26
+ </section>
27
+ );
28
+ };
29
+
30
+
31
+ export default Modal;
@@ -0,0 +1,66 @@
1
+ import React, { useEffect, useState } from 'react';
2
+
3
+ const NavigationMenu = ({
4
+ data, navigationHandler, options, columns, displayGeoName
5
+ }) => {
6
+ const [activeGeo, setActiveGeo] = useState('');
7
+
8
+ const [dropdownItems, setDropdownItems] = useState({});
9
+
10
+ const handleSubmit = (event) => {
11
+ event.preventDefault();
12
+ if (activeGeo !== '') {
13
+ const urlString = data[dropdownItems[activeGeo]][columns.navigate.name];
14
+
15
+ navigationHandler(urlString);
16
+ }
17
+ };
18
+ let navSelect; let
19
+ navGo;
20
+
21
+ switch (options.language) {
22
+ case 'es':
23
+ navSelect = 'Selecciona un Artículo';
24
+ navGo = 'Ir';
25
+ break;
26
+ default:
27
+ navSelect = 'Select an Item';
28
+ navGo = 'Go';
29
+ }
30
+
31
+ useEffect(() => {
32
+ const sortedOptions = {};
33
+
34
+ const processedDropdown = {};
35
+
36
+ Object.keys(data).forEach((val) => {
37
+ const fullName = displayGeoName(val);
38
+
39
+ processedDropdown[fullName] = val;
40
+ });
41
+
42
+ Object.keys(processedDropdown).sort().forEach((key) => {
43
+ sortedOptions[key] = processedDropdown[key];
44
+ });
45
+
46
+ setDropdownItems(sortedOptions);
47
+
48
+ setActiveGeo(Object.keys(sortedOptions)[0]);
49
+ }, [data, displayGeoName]);
50
+
51
+ return (
52
+ <section className="navigation-menu">
53
+ <form onSubmit={handleSubmit} type="get">
54
+ <label htmlFor="dropdown">
55
+ <div className="select-heading">{navSelect}</div>
56
+ <select value={activeGeo} id="dropdown" onChange={(e) => setActiveGeo(e.target.value)}>
57
+ {Object.keys(dropdownItems).map((key, i) => <option key={key} value={key}>{key}</option>)}
58
+ </select>
59
+ </label>
60
+ <input type="submit" value={navGo} className={`${options.headerColor} btn`} id="cdcnavmap-dropdown-go" />
61
+ </form>
62
+ </section>
63
+ );
64
+ };
65
+
66
+ export default NavigationMenu;
@@ -0,0 +1,167 @@
1
+ import React from 'react';
2
+ import parse from 'html-react-parser';
3
+
4
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
5
+ import LegendCircle from '@cdc/core/components/LegendCircle'
6
+
7
+ const Sidebar = (props) => {
8
+ const {
9
+ legend,
10
+ runtimeFilters,
11
+ columns,
12
+ setAccessibleStatus,
13
+ changeFilterActive,
14
+ resetLegendToggles,
15
+ runtimeLegend,
16
+ setRuntimeLegend,
17
+ prefix,
18
+ suffix,
19
+ viewport
20
+ } = props;
21
+
22
+ const addCommas = (value) => {
23
+ // If value is a number, apply specific formattings
24
+ if (value && columns.primary.hasOwnProperty('useCommas') && columns.primary.useCommas === true) {
25
+ return value.toLocaleString('en-US', { style: 'decimal' });
26
+ }
27
+
28
+ return value;
29
+ };
30
+
31
+ // Toggles if a legend is active and being applied to the map and data table.
32
+ const toggleLegendActive = (i, legendLabel) => {
33
+ const newValue = !runtimeLegend[i].disabled;
34
+
35
+ runtimeLegend[i].disabled = newValue; // Toggle!
36
+
37
+ let newLegend = [...runtimeLegend]
38
+
39
+ newLegend[i].disabled = newValue
40
+
41
+ const disabledAmt = runtimeLegend.disabledAmt ?? 0
42
+
43
+ newLegend['disabledAmt'] = newValue ? disabledAmt + 1 : disabledAmt - 1
44
+
45
+ setRuntimeLegend(newLegend)
46
+
47
+ setAccessibleStatus(`Disabled legend item ${legendLabel ?? ''}. Please reference the data table to see updated values.`);
48
+ };
49
+
50
+ const legendList = runtimeLegend.map((entry, idx) => {
51
+ const entryMax = addCommas(entry.max);
52
+
53
+ const entryMin = addCommas(entry.min);
54
+
55
+ let formattedText = `${prefix + entryMin + suffix}${entryMax !== entryMin ? ` - ${prefix + entryMax + suffix}` : ''}`;
56
+
57
+ // If interval, add some formatting
58
+ if (legend.type === 'equalinterval' && idx !== runtimeLegend.length - 1) {
59
+ formattedText = `${prefix + entryMin + suffix} - < ${prefix + entryMax + suffix}`;
60
+ }
61
+
62
+ const { disabled } = entry;
63
+
64
+ if (legend.type === 'category') {
65
+ formattedText = prefix + entry.value + suffix;
66
+ }
67
+
68
+ if (entry.max === 0 && entry.min === 0) {
69
+ formattedText = '0';
70
+ }
71
+
72
+ let legendLabel = formattedText;
73
+
74
+ if (entry.hasOwnProperty('special')) {
75
+ legendLabel = entry.label || entry.value;
76
+ }
77
+
78
+ return (
79
+ <li
80
+ key={idx}
81
+ title={`Legend item ${legendLabel} - Click to disable`}
82
+ onClick={() => { toggleLegendActive(idx, legendLabel); }}
83
+ className={disabled ? 'disabled single-legend' : 'single-legend'}
84
+ ><LegendCircle fill={entry.color} /> <span className="label">{legendLabel}</span>
85
+ </li>
86
+ );
87
+ });
88
+
89
+ const filtersList = runtimeFilters.map((singleFilter, idx) => {
90
+ const values = [];
91
+
92
+ if(undefined === singleFilter.active) return null
93
+
94
+ singleFilter.values.forEach((filterOption, idx) => {
95
+ values.push(<option
96
+ key={idx}
97
+ value={filterOption}
98
+ >{filterOption}
99
+ </option>);
100
+ });
101
+
102
+ return (
103
+ <section className="filter-col" key={idx}>
104
+ {singleFilter.label.length > 0 && <label htmlFor={`filter-${idx}`}>{singleFilter.label}</label>}
105
+ <select
106
+ id={`filter-${idx}`}
107
+ className="filter-select"
108
+ value={singleFilter.active}
109
+ onChange={(val) => {
110
+ changeFilterActive(idx, val.target.value);
111
+ setAccessibleStatus(`Filter ${singleFilter.label} value has been changed to ${val.target.value}, please reference the data table to see updated values.`);
112
+ }}
113
+ >
114
+ {values}
115
+ </select>
116
+ </section>
117
+ );
118
+ });
119
+
120
+ return (
121
+ <ErrorBoundary component="Sidebar">
122
+ <aside id="legend" className={`${legend.position} ${legend.singleColumn ? 'single-column cdcdataviz-sr-focusable' : 'cdcdataviz-sr-focusable'} ${viewport}`} role="region" aria-label="Legend" tabIndex="0">
123
+ <section className="legend-section" aria-label="Map Legend">
124
+ {runtimeLegend.disabledAmt > 0 &&
125
+ (
126
+ <button
127
+ onClick={(e) => {
128
+ e.preventDefault();
129
+ resetLegendToggles();
130
+ setAccessibleStatus('Legend has been reset, please reference the data table to see updated values.');
131
+ }}
132
+ className="clear btn"
133
+ >Clear
134
+ </button>
135
+ )}
136
+ {legend.title && <span className="heading-2">{ parse(legend.title) }</span>}
137
+ {legend.dynamicDescription === false && legend.description
138
+ && <p>{ parse(legend.description) }</p>}
139
+ {legend.dynamicDescription === true && runtimeFilters.map((filter, idx) => {
140
+ const lookupStr = `${idx},${filter.values.indexOf(String(filter.active))}`;
141
+
142
+ // Do we have a custom description for this?
143
+ const desc = legend.descriptions[lookupStr] || '';
144
+
145
+ if (desc.length > 0) {
146
+ return (<p key={`dynamic-description-${lookupStr}`}>{desc}</p>);
147
+ }
148
+ return true;
149
+ })}
150
+ <ul className={legend.singleColumn ? 'single-column' : ''} aria-label="Legend items">
151
+ {legendList}
152
+ </ul>
153
+ </section>
154
+ {filtersList.length > 0 &&
155
+ <section className="filters-section" aria-label="Map Filters">
156
+ <span className="heading-3">Filters</span>
157
+ <form>
158
+ {filtersList}
159
+ </form>
160
+ </section>
161
+ }
162
+ </aside>
163
+ </ErrorBoundary>
164
+ );
165
+ };
166
+
167
+ export default Sidebar;
@@ -0,0 +1,326 @@
1
+ import React, { useState, useEffect, memo, useRef } from 'react';
2
+ /** @jsx jsx */
3
+ import { jsx } from '@emotion/react'
4
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
5
+ import { geoCentroid, geoPath } from "d3-geo";
6
+ import { feature, mesh } from "topojson-client";
7
+ import { CustomProjection } from '@visx/geo';
8
+ import colorPalettes from '../data/color-palettes';
9
+ import { geoAlbersUsaTerritories } from 'd3-composite-projections';
10
+ import testJSON from '../data/dfc-map.json';
11
+
12
+ const abbrs = {
13
+ Alabama: 'AL',
14
+ Alaska: 'AK',
15
+ Arizona: 'AZ',
16
+ Arkansas: 'AR',
17
+ California: 'CA',
18
+ Colorado: 'CO',
19
+ Connecticut: 'CT',
20
+ Delaware: 'DE',
21
+ Florida: 'FL',
22
+ Georgia: 'GA',
23
+ Hawaii: 'HI',
24
+ Idaho: 'ID',
25
+ Illinois: 'IL',
26
+ Indiana: 'IN',
27
+ Iowa: 'IA',
28
+ Kansas: 'KS',
29
+ Kentucky: 'KY',
30
+ Louisiana: 'LA',
31
+ Maine: 'ME',
32
+ Maryland: 'MD',
33
+ Massachusetts: 'MA',
34
+ Michigan: 'MI',
35
+ Minnesota: 'MN',
36
+ Mississippi: 'MS',
37
+ Missouri: 'MO',
38
+ Montana: 'MT',
39
+ Nebraska: 'NE',
40
+ Nevada: 'NV',
41
+ 'New Hampshire': 'NH',
42
+ 'New Jersey': 'NJ',
43
+ 'New Mexico': 'NM',
44
+ 'New York': 'NY',
45
+ 'North Carolina': 'NC',
46
+ 'North Dakota': 'ND',
47
+ Ohio: 'OH',
48
+ Oklahoma: 'OK',
49
+ Oregon: 'OR',
50
+ Pennsylvania: 'PA',
51
+ 'Rhode Island': 'RI',
52
+ 'South Carolina': 'SC',
53
+ 'South Dakota': 'SD',
54
+ Tennessee: 'TN',
55
+ Texas: 'TX',
56
+ Utah: 'UT',
57
+ Vermont: 'VT',
58
+ Virginia: 'VA',
59
+ Washington: 'WA',
60
+ 'West Virginia': 'WV',
61
+ Wisconsin: 'WI',
62
+ Wyoming: 'WY',
63
+ 'District of Columbia': 'DC',
64
+ Guam: 'GU',
65
+ 'Virgin Islands': 'VI',
66
+ 'Puerto Rico': 'PR',
67
+ 'American Samoa': 'AS'
68
+ }
69
+
70
+
71
+
72
+ // SVG ITEMS
73
+ const WIDTH = 880;
74
+ const HEIGHT = 500;
75
+ const PADDING = 25;
76
+
77
+ // DATA
78
+ let { features: counties } = feature(testJSON, testJSON.objects.counties)
79
+ let { features: states } = feature(testJSON, testJSON.objects.states);
80
+
81
+ // CONSTANTS
82
+ const STATE_BORDER = '#c0cad4';
83
+ const STATE_INACTIVE_FILL = '#F4F7FA';
84
+
85
+ // CREATE STATE LINES
86
+ // const projection = geoAlbersUsaTerritories().translate([WIDTH/2,HEIGHT/2])
87
+
88
+
89
+
90
+ const SingleStateMap = (props) => {
91
+
92
+ const {
93
+ state,
94
+ applyTooltipsToGeo,
95
+ data,
96
+ geoClickHandler,
97
+ applyLegendToRow,
98
+ displayGeoName,
99
+ supportedTerritories,
100
+ rebuildTooltips,
101
+ runtimeLegend,
102
+ generateColorsArray
103
+ } = props;
104
+
105
+
106
+
107
+ const projection = geoAlbersUsaTerritories().translate([WIDTH/2, HEIGHT/2])
108
+
109
+ const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
110
+
111
+ const [stateToShow, setStateToShow ] = useState( null )
112
+ const [countiesToShow, setCountiesToShow] = useState( null )
113
+ const [translate, setTranslate] = useState()
114
+ const [scale, setScale] = useState()
115
+ const [strokeWidth, setStrokeWidth ] = useState(.75)
116
+
117
+ let mapColorPalette = colorPalettes[state.color] || '#fff';
118
+ let focusedBorderColor = mapColorPalette[3];
119
+ useEffect(() => rebuildTooltips());
120
+
121
+ const path = geoPath().projection(projection)
122
+
123
+ // When choosing a state changes...
124
+ useEffect(() => {
125
+ if(state.general.hasOwnProperty('statePicked')) {
126
+ let statePicked = state.general.statePicked.stateName;
127
+ let statePickedData = states.find(s => s.properties.name === statePicked)
128
+ setStateToShow(statePickedData)
129
+
130
+ let countiesFound = counties.filter( c => c.id.substring(0,2) === state.general.statePicked.fipsCode)
131
+
132
+ setCountiesToShow(countiesFound)
133
+
134
+ const projection = geoAlbersUsaTerritories().translate([WIDTH/2,HEIGHT/2])
135
+ const newProjection = projection.fitExtent([[PADDING, PADDING], [WIDTH - PADDING, HEIGHT - PADDING]], statePickedData)
136
+ const newScale = newProjection.scale();
137
+ const newScaleWithHypot = newScale / 1070;
138
+
139
+ let [x, y] = newProjection.translate()
140
+ x = (x - WIDTH/2);
141
+ y = (y - HEIGHT/2);
142
+
143
+ setTranslate([x,y])
144
+ setScale(newScaleWithHypot)
145
+
146
+ }
147
+ }, [state.general.statePicked]);
148
+
149
+ const geoLabel = (geo, projection) => {
150
+ //projection = geoAlbersUsaTerritories().translate([WIDTH/2,HEIGHT/2])
151
+ //const newProjection = projection.fitExtent([[PADDING, PADDING], [WIDTH - PADDING, HEIGHT - PADDING]], geo)
152
+ let [x, y] = projection( geoCentroid(geo) )
153
+ let abbr = abbrs[geo.properties.name]
154
+ if (abbr === 'NJ') x += 3
155
+ if(undefined === abbr) return null
156
+ let [dx, dy] = offsets[geo.properties.name]
157
+
158
+ return (
159
+ <>
160
+ <line className="abbrLine" x1={x} y1={y} x2={x + dx} y2={y + dy} stroke="black" strokeWidth={1} />
161
+ <text className="abbrText" x={4} strokeWidth="0" fontSize={13} style={{fill: "#202020"}} alignmentBaseline="middle" transform={`translate(${x + dx}, ${y + dy})`}>
162
+ {abbr}
163
+ </text>
164
+ </>
165
+ )
166
+ }
167
+
168
+ // Constructs and displays markup for all geos on the map (except territories right now)
169
+ const constructGeoJsx = (geographies, projection) => {
170
+ const statePassed = geographies[0].feature.states;
171
+ const counties = geographies[0].feature.counties;
172
+
173
+ let geosJsx = [];
174
+
175
+
176
+ const StateOutput = () => {
177
+
178
+ let geo = testJSON.objects.states.geometries.filter( s => {
179
+ return s.id === statePassed.id
180
+ })
181
+
182
+
183
+ // const stateLine = path(mesh(testJSON, lines ))
184
+ let stateLines = path(mesh(testJSON, geo[0]))
185
+ return (
186
+ <g
187
+ key={'single-state'}
188
+ className="single-state"
189
+ style={{ fill: '#E6E6E6'}}
190
+ stroke={geoStrokeColor}
191
+ strokeWidth={ .95 / scale }
192
+ >
193
+ <path
194
+ tabIndex={-1}
195
+ className='state-path'
196
+ d={stateLines}
197
+ />
198
+ </g>
199
+ )
200
+ };
201
+
202
+ const countyOutput = ( counties.map(( county ) => {
203
+
204
+
205
+ // Map the name from the geo data with the appropriate key for the processed data
206
+ let geoKey = county.id;
207
+
208
+ if(!geoKey) return;
209
+
210
+ let countyPath = path(county);
211
+
212
+ let geoData = data[county.id];
213
+ let legendColors;
214
+
215
+ // Once we receive data for this geographic item, setup variables.
216
+ if (geoData !== undefined ) {
217
+ legendColors = applyLegendToRow(geoData);
218
+ }
219
+
220
+ const geoDisplayName = displayGeoName(geoKey);
221
+
222
+ // For some reason, these two geos are breaking the display.
223
+ if (geoDisplayName === 'Franklin City' || geoDisplayName === 'Waynesboro') return null;
224
+
225
+ const tooltip = applyTooltipsToGeo(geoDisplayName, geoData);
226
+
227
+ if (legendColors && legendColors[0] !== '#000000') {
228
+
229
+
230
+ let styles = {
231
+ fill: legendColors[0],
232
+ cursor: 'default',
233
+ '&:hover': {
234
+ fill: legendColors[1],
235
+ },
236
+ '&:active': {
237
+ fill: legendColors[2],
238
+ },
239
+ };
240
+
241
+ // When to add pointer cursor
242
+ if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'hover') {
243
+ styles.cursor = 'pointer'
244
+ }
245
+
246
+ return (
247
+ <g
248
+ data-for="tooltip"
249
+ data-tip={tooltip}
250
+ key={`key--${county.id}`}
251
+ className={`county county--${geoDisplayName.split(" ").join("")} county--${geoData[state.columns.geo.name]}`}
252
+ css={styles}
253
+ onClick={() => geoClickHandler(geoDisplayName, geoData)}
254
+ >
255
+ <path
256
+ tabIndex={-1}
257
+ className={`county`}
258
+ stroke={geoStrokeColor}
259
+ d={countyPath}
260
+ strokeWidth={ .75 / scale }
261
+ />
262
+ </g>
263
+ )
264
+ } else {
265
+ return (
266
+ <g
267
+ data-for="tooltip"
268
+ data-tip={tooltip}
269
+ key={`key--${county.id}`}
270
+ className={`county county--${geoDisplayName.split(" ").join("")}`}
271
+ style={{ fill : '#e6e6e6'}}
272
+ >
273
+ <path
274
+ tabIndex={-1}
275
+ className={`county`}
276
+ stroke={geoStrokeColor}
277
+ d={countyPath}
278
+ strokeWidth={ .75 / scale }
279
+ />
280
+ </g>
281
+ )
282
+ }
283
+
284
+ }));
285
+
286
+
287
+ geosJsx.push( <StateOutput /> );
288
+ geosJsx.push( countyOutput );
289
+
290
+ return geosJsx;
291
+ };
292
+
293
+
294
+ return (
295
+ <ErrorBoundary component="SingleStateMap">
296
+ {stateToShow &&
297
+ <svg viewBox={`0 0 ${WIDTH} ${HEIGHT}`} preserveAspectRatio="xMinYMin" className="svg-container">
298
+ <rect className="background center-container ocean" width={WIDTH} height={HEIGHT} fillOpacity={1} fill="white"></rect>
299
+ <CustomProjection
300
+ data={ [{states: stateToShow, counties: countiesToShow }] }
301
+ // translate={translate}
302
+ // scale={scale}
303
+ projection={geoAlbersUsaTerritories}
304
+ fitExtent={[[[PADDING, PADDING], [WIDTH - PADDING, HEIGHT - PADDING]], stateToShow]}
305
+ >
306
+ { ({ features, projection }) => {
307
+ return (
308
+ <g
309
+ id="mapGroup"
310
+ className="countyMapGroup"
311
+ transform={`translate(${translate}) scale(${scale})`}
312
+ data-scale=""
313
+ key="countyMapGroup">
314
+ { constructGeoJsx(features, geoAlbersUsaTerritories) }
315
+ </g>
316
+ )
317
+ }}
318
+ </CustomProjection>
319
+ </svg>
320
+ }
321
+ {!stateToShow && 'No State Picked'}
322
+ </ErrorBoundary>
323
+ );
324
+ };
325
+
326
+ export default memo(SingleStateMap)