@cdc/map 2.6.4 → 9.22.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 +22 -16
  2. package/examples/default-county.json +64 -12
  3. package/examples/default-hex.json +3 -1
  4. package/examples/example-city-state.json +10 -1
  5. package/examples/gallery/categorical-qualitative.json +797 -0
  6. package/examples/gallery/categorical-scale-based.json +739 -0
  7. package/examples/gallery/city-state.json +479 -0
  8. package/examples/gallery/county.json +22731 -0
  9. package/examples/gallery/equal-interval.json +1027 -0
  10. package/examples/gallery/equal-number.json +1027 -0
  11. package/examples/gallery/filterable.json +909 -0
  12. package/examples/gallery/hex-filtered.json +420 -0
  13. package/examples/gallery/hex.json +413 -0
  14. package/examples/gallery/single-state.json +21402 -0
  15. package/examples/gallery/world.json +1592 -0
  16. package/examples/private/city-state.json +428 -0
  17. package/examples/private/cty-issue.json +42768 -0
  18. package/examples/private/default-usa.json +460 -0
  19. package/examples/private/legend-issue.json +1 -0
  20. package/examples/private/map-rounding-error.json +42759 -0
  21. package/examples/private/monkeypox.json +376 -0
  22. package/examples/private/valid-data-map.csv +59 -0
  23. package/examples/private/wcmsrd-14492-data.json +292 -0
  24. package/examples/private/wcmsrd-14492.json +114 -0
  25. package/package.json +3 -3
  26. package/src/CdcMap.js +204 -127
  27. package/src/components/BubbleList.js +9 -5
  28. package/src/components/CityList.js +22 -4
  29. package/src/components/CountyMap.js +13 -4
  30. package/src/components/DataTable.js +9 -9
  31. package/src/components/EditorPanel.js +239 -121
  32. package/src/components/Modal.js +2 -1
  33. package/src/components/NavigationMenu.js +4 -3
  34. package/src/components/Sidebar.js +14 -19
  35. package/src/components/SingleStateMap.js +10 -4
  36. package/src/components/UsaMap.js +82 -30
  37. package/src/components/UsaRegionMap.js +3 -2
  38. package/src/components/WorldMap.js +7 -2
  39. package/src/data/{dfc-map.json → county-map.json} +0 -0
  40. package/src/data/initial-state.js +2 -1
  41. package/src/data/supported-geos.js +5 -0
  42. package/src/index.html +3 -8
  43. package/src/scss/editor-panel.scss +2 -2
  44. package/src/scss/main.scss +1 -1
  45. package/src/scss/map.scss +4 -0
  46. package/src/scss/sidebar.scss +2 -1
@@ -7,7 +7,7 @@ import { feature, mesh } from "topojson-client";
7
7
  import { CustomProjection } from '@visx/geo';
8
8
  import colorPalettes from '../../../core/data/colorPalettes';
9
9
  import { geoAlbersUsaTerritories } from 'd3-composite-projections';
10
- import testJSON from '../data/dfc-map.json';
10
+ import testJSON from '../data/county-map.json';
11
11
 
12
12
  const abbrs = {
13
13
  Alabama: 'AL',
@@ -99,7 +99,8 @@ const SingleStateMap = (props) => {
99
99
  supportedTerritories,
100
100
  rebuildTooltips,
101
101
  runtimeLegend,
102
- generateColorsArray
102
+ generateColorsArray,
103
+ handleMapAriaLabels
103
104
  } = props;
104
105
 
105
106
 
@@ -290,11 +291,16 @@ const SingleStateMap = (props) => {
290
291
  return geosJsx;
291
292
  };
292
293
 
293
-
294
294
  return (
295
295
  <ErrorBoundary component="SingleStateMap">
296
296
  {stateToShow &&
297
- <svg viewBox={`0 0 ${WIDTH} ${HEIGHT}`} preserveAspectRatio="xMinYMin" className="svg-container">
297
+ <svg
298
+ viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
299
+ preserveAspectRatio="xMinYMin"
300
+ className="svg-container"
301
+ role="img"
302
+ aria-label={handleMapAriaLabels(state)}
303
+ >
298
304
  <rect className="background center-container ocean" width={WIDTH} height={HEIGHT} fillOpacity={1} fill="white"></rect>
299
305
  <CustomProjection
300
306
  data={ [{states: stateToShow, counties: countiesToShow }] }
@@ -10,6 +10,7 @@ import { AlbersUsa, Mercator } from '@visx/geo';
10
10
  import chroma from 'chroma-js';
11
11
  import CityList from './CityList';
12
12
  import BubbleList from './BubbleList';
13
+ import { supportedCities, supportedStates } from '../data/supported-geos';
13
14
 
14
15
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.states)
15
16
  const { features: unitedStatesHex } = feature(hexTopoJSON, hexTopoJSON.objects.states)
@@ -70,9 +71,31 @@ const UsaMap = (props) => {
70
71
  supportedTerritories,
71
72
  rebuildTooltips,
72
73
  titleCase,
73
- handleCircleClick
74
+ handleCircleClick,
75
+ setSharedFilterValue,
76
+ handleMapAriaLabels
74
77
  } = props;
75
78
 
79
+ let isFilterValueSupported = false;
80
+
81
+ if(setSharedFilterValue){
82
+ Object.keys(supportedStates).forEach(supportedState => {
83
+ if(supportedStates[supportedState].indexOf(setSharedFilterValue.toUpperCase()) !== -1){
84
+ isFilterValueSupported = true;
85
+ }
86
+ });
87
+ Object.keys(supportedTerritories).forEach(supportedTerritory => {
88
+ if(supportedTerritories[supportedTerritory].indexOf(setSharedFilterValue.toUpperCase()) !== -1){
89
+ isFilterValueSupported = true;
90
+ }
91
+ });
92
+ Object.keys(supportedCities).forEach(supportedCity => {
93
+ if(supportedCity === setSharedFilterValue.toUpperCase()){
94
+ isFilterValueSupported = true;
95
+ }
96
+ });
97
+ }
98
+
76
99
  // "Choose State" options
77
100
  const [extent, setExtent] = useState(null)
78
101
  const [focusedStates, setFocusedStates] = useState(unitedStates)
@@ -125,7 +148,7 @@ const UsaMap = (props) => {
125
148
 
126
149
  if (legendColors) {
127
150
  // Use white text if the background is dark, and dark grey if it's light
128
- if (chroma.contrast(textColor, legendColors[0]) < 4.5) {
151
+ if (chroma.contrast(textColor, legendColors[0]) < 3.5) {
129
152
  textColor = '#202020';
130
153
  }
131
154
 
@@ -139,6 +162,8 @@ const UsaMap = (props) => {
139
162
  styles = {
140
163
  color: textColor,
141
164
  fill: legendColors[0],
165
+ opacity: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue !== territoryData[state.columns.geo.name] ? .5 : 1,
166
+ stroke: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue === territoryData[state.columns.geo.name] ? 'rgba(0, 0, 0, 1)' : geoStrokeColor,
142
167
  cursor: needsPointer ? 'pointer' : 'default',
143
168
  '&:hover': {
144
169
  fill: legendColors[1],
@@ -155,7 +180,6 @@ const UsaMap = (props) => {
155
180
  text={styles.color}
156
181
  data-tip={toolTip}
157
182
  data-for="tooltip"
158
- stroke={geoStrokeColor}
159
183
  strokeWidth={1.5}
160
184
  onClick={() => geoClickHandler(territory, territoryData)}
161
185
  />)
@@ -171,7 +195,7 @@ const UsaMap = (props) => {
171
195
  let textColor = "#FFF"
172
196
 
173
197
  // Dynamic text color
174
- if (chroma.contrast(textColor, bgColor) < 4.5 ) {
198
+ if (chroma.contrast(textColor, bgColor) < 3.5 ) {
175
199
  textColor = '#202020';
176
200
  }
177
201
 
@@ -208,6 +232,25 @@ const UsaMap = (props) => {
208
232
  const constructGeoJsx = (geographies, projection) => {
209
233
  let showLabel = state.general.displayStateLabels
210
234
 
235
+ // Order alphabetically. Important for accessibility if ever read out loud.
236
+ geographies.map ( state => {
237
+ if(!state.feature.properties.iso) return;
238
+ state.feature.properties.name = titleCase(supportedStates[state.feature.properties.iso][0])
239
+ })
240
+
241
+ geographies.sort( (a,b) => {
242
+ const first = a.feature.properties.name.toUpperCase(); // ignore upper and lowercase
243
+ const second = b.feature.properties.name.toUpperCase(); // ignore upper and lowercase
244
+ if (first < second) {
245
+ return -1;
246
+ }
247
+ if (first > second) {
248
+ return 1;
249
+ }
250
+
251
+ // names must be equal
252
+ return 0;
253
+ })
211
254
  const geosJsx = geographies.map(( {feature: geo, path = ''}) => {
212
255
  const key = isHex ? geo.properties.iso + '-hex-group' : geo.properties.iso + '-group'
213
256
 
@@ -218,6 +261,7 @@ const UsaMap = (props) => {
218
261
 
219
262
  // Map the name from the geo data with the appropriate key for the processed data
220
263
  let geoKey = geo.properties.iso;
264
+ let geoName = geo.properties.name;
221
265
 
222
266
  // Manually add Washington D.C. in for Hex maps
223
267
 
@@ -240,6 +284,8 @@ const UsaMap = (props) => {
240
284
 
241
285
  styles = {
242
286
  fill: state.general.type !== 'bubble' ? legendColors[0] : '#E6E6E6',
287
+ opacity: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue !== geoData[state.columns.geo.name] ? .5 : 1,
288
+ stroke: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue === geoData[state.columns.geo.name] ? 'rgba(0, 0, 0, 1)' : geoStrokeColor,
243
289
  cursor: 'default',
244
290
  '&:hover': {
245
291
  fill: state.general.type !== 'bubble' ? legendColors[1] : '#e6e6e6',
@@ -253,43 +299,43 @@ const UsaMap = (props) => {
253
299
  if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'click') {
254
300
  styles.cursor = 'pointer'
255
301
  }
256
-
257
302
  return (
303
+ <g data-name={geoName} key={key}>
304
+ <g
305
+ data-for="tooltip"
306
+ data-tip={tooltip}
307
+ className="geo-group"
308
+ css={styles}
309
+ onClick={() => geoClickHandler(geoDisplayName, geoData)}
310
+ >
311
+ <path
312
+ tabIndex={-1}
313
+ className='single-geo'
314
+ strokeWidth={1.3}
315
+ d={path}
316
+ />
317
+ {(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
318
+ </g>
319
+ </g>
320
+ )
321
+ }
322
+
323
+ // Default return state, just geo with no additional information
324
+ return (
325
+ <g data-name={geoName} key={key}>
258
326
  <g
259
- data-for="tooltip"
260
- data-tip={tooltip}
261
- key={key}
262
327
  className="geo-group"
263
328
  css={styles}
264
- onClick={() => geoClickHandler(geoDisplayName, geoData)}
265
329
  >
266
330
  <path
267
331
  tabIndex={-1}
268
332
  className='single-geo'
269
333
  stroke={geoStrokeColor}
270
- strokeWidth={1.3}
334
+ strokeWidth={1.3}
271
335
  d={path}
272
336
  />
273
- {(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
337
+ {(isHex || showLabel) && geoLabel(geo, styles.fill, projection)}
274
338
  </g>
275
- )
276
- }
277
-
278
- // Default return state, just geo with no additional information
279
- return (
280
- <g
281
- key={key}
282
- className="geo-group"
283
- css={styles}
284
- >
285
- <path
286
- tabIndex={-1}
287
- className='single-geo'
288
- stroke={geoStrokeColor}
289
- strokeWidth={1.3}
290
- d={path}
291
- />
292
- {(isHex || showLabel) && geoLabel(geo, styles.fill, projection)}
293
339
  </g>
294
340
  )
295
341
  });
@@ -307,6 +353,8 @@ const UsaMap = (props) => {
307
353
  displayGeoName={displayGeoName}
308
354
  applyLegendToRow={applyLegendToRow}
309
355
  titleCase={titleCase}
356
+ setSharedFilterValue={setSharedFilterValue}
357
+ isFilterValueSupported={isFilterValueSupported}
310
358
  />)
311
359
 
312
360
  // Bubbles
@@ -330,7 +378,11 @@ const UsaMap = (props) => {
330
378
 
331
379
  return (
332
380
  <ErrorBoundary component="UsaMap">
333
- <svg viewBox="0 0 880 500">
381
+ <svg
382
+ viewBox="0 0 880 500"
383
+ role="img"
384
+ aria-label={handleMapAriaLabels(state)}
385
+ >
334
386
  {state.general.displayAsHex ?
335
387
  (<Mercator data={unitedStatesHex} scale={650} translate={[1600, 775]}>
336
388
  {({ features, projection }) => constructGeoJsx(features, projection)}
@@ -32,7 +32,8 @@ const UsaRegionMap = (props) => {
32
32
  supportedTerritories,
33
33
  rebuildTooltips,
34
34
  titleCase,
35
- handleCircleClick
35
+ handleCircleClick,
36
+ handleMapAriaLabels
36
37
  } = props;
37
38
 
38
39
  // "Choose State" options
@@ -299,7 +300,7 @@ const UsaRegionMap = (props) => {
299
300
 
300
301
  return (
301
302
  <ErrorBoundary component="UsaRegionMap">
302
- <svg viewBox="0 0 880 500">
303
+ <svg viewBox="0 0 880 500" role="img" aria-label={handleMapAriaLabels(state)}>
303
304
 
304
305
  <Mercator data={focusedStates} scale={620} translate={[1500, 735]}>
305
306
  {({ features, projection }) => constructGeoJsx(features, projection)}
@@ -31,7 +31,8 @@ const WorldMap = (props) => {
31
31
  setFilteredCountryCode,
32
32
  position,
33
33
  setPosition,
34
- hasZoom
34
+ hasZoom,
35
+ handleMapAriaLabels
35
36
  } = props;
36
37
 
37
38
  // TODO Refactor - state should be set together here to avoid rerenders
@@ -198,7 +199,11 @@ const ZoomControls = ({position, setPosition, state, setState, setRuntimeData, g
198
199
  return (
199
200
  <ErrorBoundary component="WorldMap">
200
201
  {hasZoom ? (
201
- <svg viewBox="0 0 880 500">
202
+ <svg
203
+ viewBox="0 0 880 500"
204
+ role="img"
205
+ aria-label={handleMapAriaLabels(state)}
206
+ >
202
207
  <rect height={500} width={880} onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} fill="white"/>
203
208
  <ZoomableGroup
204
209
  zoom={position.zoom}
File without changes
@@ -70,7 +70,8 @@ export default {
70
70
  visual: {
71
71
  minBubbleSize: 1,
72
72
  maxBubbleSize: 20,
73
- extraBubbleBorder: false
73
+ extraBubbleBorder: false,
74
+ cityStyle: 'circle'
74
75
  },
75
76
  mapPosition:
76
77
  { coordinates: [0, 30], zoom: 1 }
@@ -497,7 +497,12 @@ export const supportedTerritories = {
497
497
  };
498
498
 
499
499
  export const supportedCities = {
500
+ 'GAINESVILLE': [-82.3248, 29.6516],
500
501
  'DISTRICT OF COLUMBIA': [-77.036873, 38.907192],
502
+ 'US-DC': [-77.036873, 38.907192],
503
+ 'WASHINGTON D.C.': [-77.036873, 38.907192],
504
+ 'WASHINGTON DC.': [-77.036873, 38.907192],
505
+ 'WASHINGTON DC': [-77.036873, 38.907192],
501
506
  'LOS ANGELES COUNTY': [-118.229362, 34.058762],
502
507
  MESA: [-111.831474, 33.415184],
503
508
  PHOENIX: [-112.074036, 33.448376],
package/src/index.html CHANGED
@@ -14,24 +14,19 @@
14
14
  </head>
15
15
  <body>
16
16
  <!-- DEFAULT EXAMPLES -->
17
- <!-- <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div> -->
18
- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div>
19
- <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-usa.json"></div>-->
17
+ <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div>
18
+ <!-- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div> -->
20
19
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-world.json"></div> -->
21
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/world.json"></div> -->
22
20
  <!-- <div class="react-container react-container--maps" data-config="/examples/bubble-us.json"></div/> -->
23
21
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/bubble-world.json"></div>-->
24
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/nav-only.json"></div> -->
25
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/example-hex-map-with-filter.json"></div> -->
26
22
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div> -->
27
23
 
28
24
  <!-- TP4 EXAMPLES -->
29
25
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
30
26
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
31
27
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
32
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/atsdr.json"></div> -->
33
-
34
28
 
29
+
35
30
  <noscript>You need to enable JavaScript to run this app.</noscript>
36
31
  </body>
37
32
  </html>
@@ -30,7 +30,8 @@
30
30
  fill: currentColor
31
31
  }
32
32
  }
33
- li {
33
+ button {
34
+ background: transparent;
34
35
  padding: .3em .75em;
35
36
  display: flex;
36
37
  border: $lightGray 1px solid;
@@ -239,7 +240,6 @@
239
240
  display: block;
240
241
  text-align: left;
241
242
  cursor: pointer;
242
- color: rgba(0,0,0,.5);
243
243
  text-decoration: underline;
244
244
  span {
245
245
  text-decoration: none;
@@ -221,4 +221,4 @@
221
221
  outline-color: rgb(0, 95, 204)
222
222
  }
223
223
 
224
- }
224
+ }
package/src/scss/map.scss CHANGED
@@ -155,6 +155,9 @@ header + .map-container.full-border {
155
155
  height: 1.75em;
156
156
  width: 1.75em;
157
157
  }
158
+ &:focus {
159
+ background: #005eaa;
160
+ }
158
161
  }
159
162
  > button:first-child {
160
163
  margin-right: 0.25em;
@@ -170,6 +173,7 @@ header + .map-container.full-border {
170
173
  width: 2.5em;
171
174
  }
172
175
  }
176
+
173
177
  }
174
178
 
175
179
  @include breakpointClass(md) {
@@ -68,6 +68,7 @@ aside {
68
68
  padding: 0;
69
69
  display: flex;
70
70
  flex-wrap: wrap;
71
+ button { font-size: unset; background: transparent; }
71
72
  li {
72
73
  flex-shrink: 0;
73
74
  display: inline-block;
@@ -76,7 +77,7 @@ aside {
76
77
  vertical-align: middle;
77
78
  transition: .1s opacity;
78
79
  display: flex;
79
- align-items: center;
80
+ // align-items: center;
80
81
  &.single-legend {
81
82
  cursor: pointer;
82
83
  }