@cdc/map 4.24.12 → 4.25.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 (60) hide show
  1. package/dist/cdcmap.js +51010 -49337
  2. package/examples/annotation/index.json +1 -1
  3. package/examples/custom-map-layers.json +1 -1
  4. package/examples/default-geocode.json +2 -2
  5. package/examples/private/DEV-9989.json +229 -0
  6. package/examples/private/ardi.json +180 -0
  7. package/examples/private/colors 2.json +416 -0
  8. package/examples/private/colors.json +416 -0
  9. package/examples/private/colors.json.zip +0 -0
  10. package/examples/private/customColors.json +45348 -0
  11. package/examples/private/mmr.json +246 -0
  12. package/examples/private/test.json +1632 -0
  13. package/index.html +12 -14
  14. package/package.json +8 -3
  15. package/src/CdcMap.tsx +126 -394
  16. package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +9 -0
  17. package/src/_stories/CdcMap.stories.tsx +1 -1
  18. package/src/_stories/GoogleMap.stories.tsx +19 -0
  19. package/src/_stories/_mock/DEV-10148.json +859 -0
  20. package/src/_stories/_mock/DEV-9989.json +229 -0
  21. package/src/_stories/_mock/example-city-state.json +1 -1
  22. package/src/_stories/_mock/google-map.json +819 -0
  23. package/src/components/Annotation/Annotation.Draggable.tsx +34 -43
  24. package/src/components/Annotation/AnnotationDropdown.tsx +4 -4
  25. package/src/components/CityList.tsx +2 -2
  26. package/src/components/DataTable.tsx +8 -9
  27. package/src/components/EditorPanel/components/EditorPanel.tsx +96 -270
  28. package/src/components/GoogleMap/components/GoogleMap.tsx +67 -0
  29. package/src/components/GoogleMap/index.tsx +3 -0
  30. package/src/components/Legend/components/Legend.tsx +40 -30
  31. package/src/components/Legend/components/LegendItem.Hex.tsx +7 -3
  32. package/src/components/Legend/components/index.scss +22 -16
  33. package/src/components/Modal.tsx +6 -5
  34. package/src/components/NavigationMenu.tsx +5 -4
  35. package/src/components/UsaMap/components/TerritoriesSection.tsx +56 -0
  36. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +3 -3
  37. package/src/components/UsaMap/components/UsaMap.County.tsx +1 -1
  38. package/src/components/UsaMap/components/UsaMap.Region.tsx +12 -8
  39. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +2 -2
  40. package/src/components/UsaMap/components/UsaMap.State.tsx +23 -29
  41. package/src/components/WorldMap/WorldMap.tsx +3 -5
  42. package/src/context.ts +0 -12
  43. package/src/data/initial-state.js +2 -2
  44. package/src/data/supported-geos.js +23 -3
  45. package/src/helpers/applyColorToLegend.ts +3 -3
  46. package/src/helpers/closeModal.ts +9 -0
  47. package/src/helpers/handleMapAriaLabels.ts +38 -0
  48. package/src/helpers/indexOfIgnoreType.ts +8 -0
  49. package/src/helpers/navigationHandler.ts +21 -0
  50. package/src/helpers/toTitleCase.ts +44 -0
  51. package/src/helpers/validateFipsCodeLength.ts +30 -0
  52. package/src/hooks/useResizeObserver.ts +42 -0
  53. package/src/hooks/useTooltip.ts +4 -2
  54. package/src/index.jsx +1 -0
  55. package/src/scss/editor-panel.scss +2 -1
  56. package/src/scss/filters.scss +0 -5
  57. package/src/scss/main.scss +57 -61
  58. package/src/scss/map.scss +1 -13
  59. package/src/types/MapConfig.ts +20 -11
  60. package/src/types/MapContext.ts +4 -12
@@ -1,5 +1,5 @@
1
1
  //TODO: Move legends to core
2
- import { forwardRef, useContext, useId } from 'react'
2
+ import { forwardRef, useContext } from 'react'
3
3
  import parse from 'html-react-parser'
4
4
 
5
5
  //types
@@ -17,8 +17,9 @@ import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
17
17
  import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
18
18
  import { Group } from '@visx/group'
19
19
  import './index.scss'
20
- import { ViewportSize } from '@cdc/chart/src/types/ChartConfig'
20
+ import { type ViewPort } from '@cdc/core/types/ViewPort'
21
21
  import { isBelowBreakpoint, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
22
+ import { displayDataAsText } from '@cdc/core/helpers/displayDataAsText'
22
23
 
23
24
  const LEGEND_PADDING = 30
24
25
 
@@ -26,22 +27,22 @@ type LegendProps = {
26
27
  skipId: string
27
28
  dimensions: DimensionsType
28
29
  containerWidthPadding: number
29
- currentViewport: ViewportSize
30
+ currentViewport: ViewPort
30
31
  }
31
32
 
32
33
  const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
33
- const { skipId, dimensions, containerWidthPadding, currentViewport } = props
34
+ const { skipId, containerWidthPadding } = props
35
+ const { isEditor, dimensions, currentViewport } = useContext(ConfigContext)
34
36
 
35
37
  const {
36
38
  // prettier-ignore
37
- displayDataAsText,
38
39
  resetLegendToggles,
39
40
  runtimeFilters,
40
41
  runtimeLegend,
41
42
  setAccessibleStatus,
42
43
  setRuntimeLegend,
43
44
  state,
44
- viewport,
45
+ currentViewport: viewport,
45
46
  mapId
46
47
  } = useContext(ConfigContext)
47
48
 
@@ -75,9 +76,9 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
75
76
  }
76
77
  const getFormattedLegendItems = () => {
77
78
  return runtimeLegend.map((entry, idx) => {
78
- const entryMax = displayDataAsText(entry.max, 'primary')
79
+ const entryMax = displayDataAsText(entry.max, 'primary', state)
79
80
 
80
- const entryMin = displayDataAsText(entry.min, 'primary')
81
+ const entryMin = displayDataAsText(entry.min, 'primary', state)
81
82
  let formattedText = `${entryMin}${entryMax !== entryMin ? ` - ${entryMax}` : ''}`
82
83
 
83
84
  // If interval, add some formatting
@@ -86,7 +87,7 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
86
87
  }
87
88
 
88
89
  if (legend.type === 'category') {
89
- formattedText = displayDataAsText(entry.value, 'primary')
90
+ formattedText = displayDataAsText(entry.value, 'primary', state)
90
91
  }
91
92
 
92
93
  if (entry.max === 0 && entry.min === 0) {
@@ -116,12 +117,14 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
116
117
  const legendList = (patternsOnly = false) => {
117
118
  const formattedItems = patternsOnly ? [] : getFormattedLegendItems()
118
119
  const patternsOnlyFont = isMobileHeightViewport(currentViewport) ? '12px' : '14px'
120
+ const hasDisabledItems = formattedItems.some(item => item.disabled)
119
121
  let legendItems
120
122
 
121
123
  legendItems = formattedItems.map((item, idx) => {
122
124
  const handleListItemClass = () => {
123
125
  let classes = ['legend-container__li', 'd-flex', 'align-items-center']
124
126
  if (item.disabled) classes.push('legend-container__li--disabled')
127
+ else if (hasDisabledItems) classes.push('legend-container__li--not-disabled')
125
128
  if (item.special) classes.push('legend-container__li--special-class')
126
129
  return classes.join(' ')
127
130
  }
@@ -264,26 +267,33 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
264
267
  ref={ref}
265
268
  >
266
269
  <section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
267
- {legend.title && <h3 className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</h3>}
268
- {legend.dynamicDescription === false && legend.description && (
269
- <p className={legendClasses.description.join(' ') || ''}>{parse(legend.description)}</p>
270
+ {(legend.title || legend.description || legend.dynamicDescription) && (
271
+ <div className='mb-3'>
272
+ {legend.title && <h3 className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</h3>}
273
+ {legend.dynamicDescription === false && legend.description && (
274
+ <p className={legendClasses.description.join(' ') || ''}>{parse(legend.description)}</p>
275
+ )}
276
+ {legend.dynamicDescription === true &&
277
+ runtimeFilters.map((filter, idx) => {
278
+ const lookupStr = `${idx},${filter.values.indexOf(String(filter.active))}`
279
+
280
+ // Do we have a custom description for this?
281
+ const desc = legend.descriptions[lookupStr] || ''
282
+
283
+ if (desc.length > 0) {
284
+ return (
285
+ <p
286
+ key={`dynamic-description-${lookupStr}`}
287
+ className={`dynamic-legend-description-${lookupStr} mt-2`}
288
+ >
289
+ {desc}
290
+ </p>
291
+ )
292
+ }
293
+ return true
294
+ })}
295
+ </div>
270
296
  )}
271
- {legend.dynamicDescription === true &&
272
- runtimeFilters.map((filter, idx) => {
273
- const lookupStr = `${idx},${filter.values.indexOf(String(filter.active))}`
274
-
275
- // Do we have a custom description for this?
276
- const desc = legend.descriptions[lookupStr] || ''
277
-
278
- if (desc.length > 0) {
279
- return (
280
- <p key={`dynamic-description-${lookupStr}`} className={`dynamic-legend-description-${lookupStr}`}>
281
- {desc}
282
- </p>
283
- )
284
- }
285
- return true
286
- })}
287
297
 
288
298
  <LegendGradient
289
299
  labels={getFormattedLegendItems().map(item => item?.label) ?? []}
@@ -334,8 +344,8 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
334
344
  </>
335
345
  )}
336
346
  {runtimeLegend.disabledAmt > 0 && (
337
- <Button className={legendClasses.resetButton.join(' ')} onClick={handleReset}>
338
- Reset
347
+ <Button className={legendClasses.showAllButton.join(' ')} onClick={handleReset}>
348
+ Show All
339
349
  </Button>
340
350
  )}
341
351
  </section>
@@ -3,7 +3,7 @@ import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight } from 'react
3
3
  import parse from 'html-react-parser'
4
4
 
5
5
  const LegendItemHex = props => {
6
- const { state, viewport } = props
6
+ const { state, currentViewport: viewport } = props
7
7
 
8
8
  const getItemShape = shape => {
9
9
  switch (shape) {
@@ -27,8 +27,12 @@ const LegendItemHex = props => {
27
27
  return (
28
28
  <aside id='legend' className={legendClasses.aside.join(' ')} role='region' aria-label='Legend' tabIndex={0}>
29
29
  <section className={legendClasses.section.join(' ')} aria-label='Map Legend'>
30
- {shapeGroup.legendTitle && <h3 className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</h3>}
31
- {shapeGroup.legendDescription && <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>}
30
+ {shapeGroup.legendTitle && (
31
+ <h3 className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</h3>
32
+ )}
33
+ {shapeGroup.legendDescription && (
34
+ <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>
35
+ )}
32
36
 
33
37
  <ul className={legendClasses.ul.join(' ')} aria-label='Legend items' style={{ listStyle: 'none' }}>
34
38
  {shapeGroup.items.map((item, itemIndex) => {
@@ -16,11 +16,9 @@
16
16
  background-color: #fff;
17
17
  z-index: 6;
18
18
  border-top: var(--cool-gray-10) 1px solid;
19
+ border-radius: 6px;
19
20
 
20
21
  @include breakpointClass(md) {
21
- .legend-container.legend-padding {
22
- padding: 15px;
23
- }
24
22
  &.bottom,
25
23
  &.top {
26
24
  border: var(--cool-gray-10) 1px solid;
@@ -70,21 +68,21 @@
70
68
  }
71
69
 
72
70
  .legend-container {
73
- --space-between-legend-items: 0.6em;
74
71
  position: relative;
75
- &.legend-padding {
76
- padding-top: 15px;
72
+
73
+ .tspan {
74
+ font-size: var(--legend-item-font-size) !important;
77
75
  }
76
+
78
77
  .legend-container__title {
79
- font-size: 1.3em;
78
+ font-size: var(--legend-title-font-size);
79
+ font-weight: var(--legend-title-font-weight);
80
80
  padding-bottom: 0;
81
81
  display: inline-block;
82
+ color: black;
82
83
  }
83
- .legend-container__title + p,
84
- .legend-container__title + ul,
85
- p + ul,
86
- p + p {
87
- padding-top: 1em;
84
+ .legend-container__description {
85
+ font-size: var(--legend-description-font-size);
88
86
  }
89
87
  .legend-container__reset-button {
90
88
  margin-top: 1em;
@@ -92,11 +90,17 @@
92
90
  p {
93
91
  line-height: 1.4em;
94
92
  }
93
+
94
+ .legend-container__ul {
95
+ line-height: 1;
96
+ row-gap: var(--space-between-legend-item-rows);
97
+ column-gap: var(--space-between-legend-item-columns);
98
+ }
95
99
  .legend-container__ul:not(.single-row) {
96
100
  list-style: none;
97
- padding-top: 1em;
98
101
  display: grid;
99
102
  grid-template-columns: 1fr;
103
+
100
104
  @include breakpoint(md) {
101
105
  grid-template-columns: 1fr 1fr;
102
106
  }
@@ -117,7 +121,6 @@
117
121
  flex-shrink: 0;
118
122
  display: inline-block;
119
123
  padding-right: 1em;
120
- margin-bottom: var(--space-between-legend-items);
121
124
  vertical-align: middle;
122
125
  transition: 0.1s opacity;
123
126
  display: flex;
@@ -134,6 +137,11 @@
134
137
  &.legend-container__li--disabled {
135
138
  opacity: 0.4;
136
139
  }
140
+ &.legend-container__li--not-disabled {
141
+ outline: 1px solid #005ea2;
142
+ outline-offset: 5px;
143
+ border-radius: 1px;
144
+ }
137
145
  }
138
146
  }
139
147
  .legend-container__ul.single-row {
@@ -145,10 +153,8 @@
145
153
  align-items: center;
146
154
  justify-content: flex-start;
147
155
  flex-wrap: wrap;
148
- row-gap: var(--space-between-legend-items);
149
156
 
150
157
  & > li {
151
- margin-right: 1em;
152
158
  white-space: wrap;
153
159
  display: flex;
154
160
  justify-content: center;
@@ -4,18 +4,19 @@ import ConfigContext from '../context'
4
4
  import Icon from '@cdc/core/components/ui/Icon'
5
5
 
6
6
  const Modal = () => {
7
- const { applyTooltipsToGeo, capitalize, applyLegendToRow, viewport, type, content } = useContext(ConfigContext)
8
-
7
+ const { applyTooltipsToGeo, applyLegendToRow, content, state, currentViewport: viewport } = useContext(ConfigContext)
8
+ const { capitalizeLabels } = state.tooltips
9
9
  const tooltip = applyTooltipsToGeo(content.geoName, content.keyedData, 'jsx')
10
-
10
+ const type = state.general.type
11
11
  const legendColors = applyLegendToRow(content.keyedData)
12
12
 
13
13
  return (
14
14
  <section
15
- className={capitalize ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport}
15
+ className={
16
+ capitalizeLabels ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport
17
+ }
16
18
  aria-hidden='true'
17
19
  >
18
- {type === 'data' && <LegendShape fill={legendColors[0]} />}
19
20
  <div className='content'>{tooltip}</div>
20
21
  <Icon display='close' alt='Close Modal' size={20} color='#000' className='modal-close' />
21
22
  </section>
@@ -1,8 +1,9 @@
1
- import React, { useEffect, useState } from 'react'
1
+ import React, { useContext, useEffect, useState } from 'react'
2
+ import ConfigContext from '../context'
2
3
 
3
4
  const NavigationMenu = ({ data, navigationHandler, options, columns, displayGeoName, mapTabbingID }) => {
5
+ const { state } = useContext(ConfigContext)
4
6
  const [activeGeo, setActiveGeo] = useState('')
5
-
6
7
  const [dropdownItems, setDropdownItems] = useState({})
7
8
 
8
9
  const handleSubmit = event => {
@@ -10,7 +11,7 @@ const NavigationMenu = ({ data, navigationHandler, options, columns, displayGeoN
10
11
  if (activeGeo !== '') {
11
12
  const urlString = data[dropdownItems[activeGeo]][columns.navigate.name]
12
13
 
13
- navigationHandler(urlString)
14
+ navigationHandler(state.general.navigationTarget, urlString)
14
15
  }
15
16
  }
16
17
 
@@ -55,7 +56,7 @@ const NavigationMenu = ({ data, navigationHandler, options, columns, displayGeoN
55
56
  <label htmlFor={mapTabbingID.replace('#', '')}>
56
57
  <div className='select-heading'>{navSelect}</div>
57
58
  <select value={activeGeo} id={mapTabbingID.replace('#', '')} onChange={e => setActiveGeo(e.target.value)}>
58
- {Object.keys(dropdownItems).map((key, i) => (
59
+ {Object.keys(dropdownItems).map(key => (
59
60
  <option key={key} value={key}>
60
61
  {key}
61
62
  </option>
@@ -0,0 +1,56 @@
1
+ import React from 'react'
2
+
3
+ type TerritoriesSectionProps = {
4
+ territories: JSX.Element[]
5
+ logo: string
6
+ config: any
7
+ }
8
+
9
+ const TerritoriesSection: React.FC<TerritoriesSectionProps> = ({ territories, logo, config }) => {
10
+ const usTerritories = territories
11
+ .filter(territory => {
12
+ return ['US-AS', 'US-GU', 'US-MP', 'US-PR', 'US-VI'].includes(territory?.props?.territory)
13
+ })
14
+ .sort((a, b) => {
15
+ return a.props.territory.localeCompare(b.props.territory)
16
+ })
17
+
18
+ const freelyAssociatedStates = territories
19
+ .filter(territory => {
20
+ return ['US-FM', 'US-MH', 'US-PW'].includes(territory?.props?.territory)
21
+ })
22
+ .sort((a, b) => {
23
+ return a.props.territory.localeCompare(b.props.territory)
24
+ })
25
+
26
+ return (
27
+ territories.length > 0 && (
28
+ <>
29
+ {/* Temporarily make the max width fit the image width */}
30
+ <div>
31
+ <div className='d-flex mt-4'>
32
+ {'data' === config.general.type && logo && (
33
+ <img src={logo} alt='' className='map-logo' style={{ maxWidth: '50px' }} />
34
+ )}
35
+ </div>
36
+ <div>
37
+ {usTerritories.length > 0 && (
38
+ <>
39
+ <h5 className='territories-label'>U.S. Territories</h5>
40
+ <span className='mt-1 mb-2 d-flex flex-wrap territories'>{usTerritories}</span>
41
+ </>
42
+ )}
43
+ {freelyAssociatedStates.length > 0 && (
44
+ <>
45
+ <h5 className='territories-label'>Freely Associated States</h5>
46
+ <span className='mt-1 mb-2 d-flex flex-wrap territories'>{freelyAssociatedStates}</span>
47
+ </>
48
+ )}
49
+ </div>
50
+ </div>
51
+ </>
52
+ )
53
+ )
54
+ }
55
+
56
+ export default TerritoriesSection
@@ -54,7 +54,7 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
54
54
 
55
55
  {state.map.patterns.map((patternData, patternIndex) => {
56
56
  const patternColor = patternData.color || getContrastColor('#FFF', backgroundColor)
57
- const hasMatchingValues = patternData.dataValue === territoryData[patternData.dataKey]
57
+ const hasMatchingValues = patternData.dataValue === territoryData?.[patternData.dataKey]
58
58
 
59
59
  if (!hasMatchingValues) return null
60
60
  if (!patternData.pattern) return null
@@ -96,8 +96,8 @@ const TerritoryRectangle: React.FC<TerritoryShape> = ({
96
96
  fill={`url(#territory-${territory}-${patternData?.dataKey}--${patternIndex})`}
97
97
  color={patternData ? 'white' : textColor}
98
98
  className={[
99
- `territory-pattern-${patternData.dataKey}`,
100
- `territory-pattern-${patternData.dataKey}--${patternData.dataValue}`
99
+ `territory-pattern-${patternData?.dataKey}`,
100
+ `territory-pattern-${patternData?.dataKey}--${patternData.dataValue}`
101
101
  ].join(' ')}
102
102
  />
103
103
  <text
@@ -22,6 +22,7 @@ import {
22
22
  createShapeProperties
23
23
  } from '../helpers/shapes'
24
24
  import { getGeoStrokeColor } from '../../../helpers/colors'
25
+ import { handleMapAriaLabels } from '../../../helpers/handleMapAriaLabels'
25
26
 
26
27
  const getCountyTopoURL = year => {
27
28
  return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`
@@ -137,7 +138,6 @@ const CountyMap = props => {
137
138
  data,
138
139
  displayGeoName,
139
140
  geoClickHandler,
140
- handleMapAriaLabels,
141
141
  runtimeFilters,
142
142
  runtimeLegend,
143
143
  setState,
@@ -1,17 +1,23 @@
1
1
  import { useState, useEffect, memo, useContext } from 'react'
2
- import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
3
2
 
4
3
  // 3rd party
5
4
  import { geoCentroid } from 'd3-geo'
6
5
  import { feature } from 'topojson-client'
7
6
  import { Mercator } from '@visx/geo'
8
7
 
9
- // cdc
8
+ // Cdc Components
10
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
11
- import topoJSON from '../data/us-regions-topo-2.json'
12
10
  import ConfigContext from '../../../context'
13
11
  import Annotation from '../../Annotation'
12
+
13
+ // Data
14
+ import topoJSON from '../data/us-regions-topo-2.json'
15
+ import { supportedTerritories } from '../../../data/supported-geos'
16
+
17
+ // Helpers
18
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
14
19
  import { getGeoFillColor, getGeoStrokeColor } from '../../../helpers/colors'
20
+ import { handleMapAriaLabels } from '../../../helpers/handleMapAriaLabels'
15
21
 
16
22
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.regions)
17
23
 
@@ -40,9 +46,7 @@ const UsaRegionMap = props => {
40
46
  data,
41
47
  displayGeoName,
42
48
  geoClickHandler,
43
- handleMapAriaLabels,
44
49
  state,
45
- supportedTerritories,
46
50
  tooltipId
47
51
  } = useContext(ConfigContext)
48
52
 
@@ -150,8 +154,8 @@ const UsaRegionMap = props => {
150
154
  <line
151
155
  x1={centroid[0]}
152
156
  y1={centroid[1]}
153
- x2={centroid[0] + dx}
154
- y2={centroid[1] + dy}
157
+ x2={centroid[0] + x}
158
+ y2={centroid[1] + y}
155
159
  stroke='rgba(0,0,0,.5)'
156
160
  strokeWidth={1}
157
161
  />
@@ -161,7 +165,7 @@ const UsaRegionMap = props => {
161
165
  fontSize={13}
162
166
  style={{ fill: '#202020' }}
163
167
  alignmentBaseline='middle'
164
- transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}
168
+ transform={`translate(${centroid[0] + x}, ${centroid[1] + y})`}
165
169
  >
166
170
  {abbr.substring(3)}
167
171
  </text>
@@ -15,6 +15,8 @@ import { MapContext } from '../../../types/MapContext'
15
15
  import useStateZoom from '../../../hooks/useStateZoom'
16
16
  import { Text } from '@visx/text'
17
17
  import { getGeoStrokeColor } from '../../../helpers/colors'
18
+ import { handleMapAriaLabels } from '../../../helpers/handleMapAriaLabels'
19
+ import { titleCase } from '../../../helpers/titleCase'
18
20
 
19
21
  // SVG ITEMS
20
22
  const WIDTH = 880
@@ -29,8 +31,6 @@ const SingleStateMap = props => {
29
31
  geoClickHandler,
30
32
  applyLegendToRow,
31
33
  displayGeoName,
32
- handleMapAriaLabels,
33
- titleCase,
34
34
  setSharedFilterValue,
35
35
  isFilterValueSupported,
36
36
  runtimeFilters,
@@ -5,6 +5,7 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
5
5
  // United States Topojson resources
6
6
  import topoJSON from '../data/us-topo.json'
7
7
  import hexTopoJSON from '../data/us-hex-topo.json'
8
+ import { supportedTerritories } from '../../../data/supported-geos'
8
9
 
9
10
  import { geoCentroid, geoPath } from 'd3-geo'
10
11
  import { feature } from 'topojson-client'
@@ -17,15 +18,16 @@ import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
17
18
  import HexIcon from './HexIcon'
18
19
  import { patternSizes } from '../helpers/patternSizes'
19
20
  import Annotation from '../../Annotation'
20
-
21
21
  import Territory from './Territory'
22
- import { cityKeys } from '../../../data/supported-geos'
23
22
 
24
23
  import useMapLayers from '../../../hooks/useMapLayers'
25
24
  import ConfigContext from '../../../context'
26
25
  import { MapContext } from '../../../types/MapContext'
27
- import { checkColorContrast, getContrastColor, getColorContrast } from '@cdc/core/helpers/cove/accessibility'
26
+ import { checkColorContrast, getContrastColor, outlinedTextColor } from '@cdc/core/helpers/cove/accessibility'
28
27
  import { getGeoFillColor, getGeoStrokeColor } from '../../../helpers/colors'
28
+ import { handleMapAriaLabels } from '../../../helpers/handleMapAriaLabels'
29
+ import { titleCase } from '../../../helpers/titleCase'
30
+ import TerritoriesSection from './TerritoriesSection'
29
31
 
30
32
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.states)
31
33
  const { features: unitedStatesHex } = feature(hexTopoJSON, hexTopoJSON.objects.states)
@@ -61,11 +63,8 @@ const UsaMap = () => {
61
63
  data,
62
64
  displayGeoName,
63
65
  geoClickHandler,
64
- handleMapAriaLabels,
65
66
  setSharedFilterValue,
66
67
  state,
67
- supportedTerritories,
68
- titleCase,
69
68
  tooltipId,
70
69
  handleDragStateChange,
71
70
  mapId,
@@ -407,7 +406,7 @@ const UsaMap = () => {
407
406
  {map.patterns.map((patternData, patternIndex) => {
408
407
  const { pattern, dataKey, size } = patternData
409
408
  const currentFill = styles.fill
410
- const hasMatchingValues = patternData.dataValue === geoData[patternData.dataKey]
409
+ const hasMatchingValues = patternData.dataValue === geoData?.[patternData.dataKey]
411
410
  const patternColor = patternData.color || getContrastColor('#000', currentFill)
412
411
 
413
412
  if (!hasMatchingValues) return
@@ -521,12 +520,11 @@ const UsaMap = () => {
521
520
 
522
521
  if (undefined === abbr) return null
523
522
 
524
- let textColor = getContrastColor('#FFF', bgColor)
525
-
526
- // always make HI black since it is off to the side
523
+ // HI background is always white since it is off to the side
527
524
  if (abbr === 'US-HI' && !general.displayAsHex) {
528
- textColor = '#000'
525
+ bgColor = '#FFF'
529
526
  }
527
+ const { textColor, strokeColor } = outlinedTextColor(bgColor)
530
528
 
531
529
  let x = 0,
532
530
  y = hexMap.type === 'shapes' && general.displayAsHex ? -10 : 5
@@ -540,7 +538,16 @@ const UsaMap = () => {
540
538
  if (undefined === offsets[abbr] || isHex) {
541
539
  return (
542
540
  <g transform={`translate(${centroid})`} tabIndex={-1}>
543
- <text x={x} y={y} fontSize={14} strokeWidth='0' style={{ fill: textColor }} textAnchor='middle'>
541
+ <text
542
+ x={x}
543
+ y={y}
544
+ fontSize={14}
545
+ strokeWidth='0'
546
+ // paintOrder='stroke' // PENDING DEV-9278: Adds a stroke around the text potentially for 508 compliance
547
+ // stroke={strokeColor}
548
+ style={{ fill: textColor }}
549
+ textAnchor='middle'
550
+ >
544
551
  {abbr.substring(3)}
545
552
  </text>
546
553
  </g>
@@ -562,8 +569,10 @@ const UsaMap = () => {
562
569
  <text
563
570
  x={4}
564
571
  strokeWidth='0'
572
+ // paintOrder='stroke' // PENDING DEV-9278: Adds a stroke around the text potentially for 508 compliance
573
+ // stroke={strokeColor}
565
574
  fontSize={13}
566
- style={{ fill: '#202020' }}
575
+ style={{ fill: textColor }}
567
576
  alignmentBaseline='middle'
568
577
  transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}
569
578
  >
@@ -588,22 +597,7 @@ const UsaMap = () => {
588
597
  {annotations.length > 0 && <Annotation.Draggable onDragStateChange={handleDragStateChange} />}
589
598
  </svg>
590
599
 
591
- {territories.length > 0 && (
592
- <>
593
- {/* Temporarily make the max width fit the image width */}
594
- <div>
595
- <div className='d-flex mt-2'>
596
- <h5>{general.territoriesLabel}</h5>
597
- {'data' === general.type && logo && (
598
- <img src={logo} alt='' className='map-logo' style={{ maxWidth: '50px' }} />
599
- )}
600
- </div>
601
- <div>
602
- <span className='mt-1 mb-2 d-flex flex-wrap territories'>{territories}</span>
603
- </div>
604
- </div>
605
- </>
606
- )}
600
+ <TerritoriesSection territories={territories} logo={logo} config={state} />
607
601
  </ErrorBoundary>
608
602
  )
609
603
  }
@@ -12,6 +12,9 @@ import BubbleList from '../BubbleList'
12
12
  import ConfigContext from '../../context'
13
13
  import ZoomControls from '../ZoomControls'
14
14
  import { getGeoFillColor, getGeoStrokeColor } from '../../helpers/colors'
15
+ import { supportedCountries } from '../../data/supported-geos'
16
+ import { handleMapAriaLabels } from '../../helpers/handleMapAriaLabels'
17
+ import { titleCase } from '../../helpers/titleCase'
15
18
 
16
19
  const { features: world } = feature(topoJSON, topoJSON.objects.countries)
17
20
 
@@ -26,7 +29,6 @@ const WorldMap = () => {
26
29
  displayGeoName,
27
30
  generateRuntimeData,
28
31
  geoClickHandler,
29
- handleMapAriaLabels,
30
32
  hasZoom,
31
33
  position,
32
34
  setFilteredCountryCode,
@@ -34,11 +36,7 @@ const WorldMap = () => {
34
36
  setRuntimeData,
35
37
  setState,
36
38
  state,
37
- supportedCountries,
38
- titleCase,
39
39
  tooltipId,
40
- setScale,
41
- setTranslate
42
40
  } = useContext(ConfigContext)
43
41
 
44
42
  // TODO Refactor - state should be set together here to avoid rerenders
package/src/context.ts CHANGED
@@ -4,17 +4,12 @@ import { MapConfig } from './types/MapConfig'
4
4
  type MapContext = {
5
5
  applyLegendToRow
6
6
  applyTooltipsToGeo
7
- closeModal
8
- columnsInData
9
- currentViewport
10
7
  data
11
- displayDataAsText
12
8
  displayGeoName
13
9
  filteredCountryCode
14
10
  generateColorsArray
15
11
  generateRuntimeData
16
12
  geoClickHandler
17
- handleMapAriaLabels
18
13
  handleCircleClick: Function
19
14
  hasZoom
20
15
  innerContainerRef
@@ -22,7 +17,6 @@ type MapContext = {
22
17
  isDebug
23
18
  isEditor
24
19
  loadConfig
25
- navigationHandler
26
20
  position
27
21
  resetLegendToggles
28
22
  runtimeFilters
@@ -37,13 +31,7 @@ type MapContext = {
37
31
  setSharedFilterValue
38
32
  setState
39
33
  state: MapConfig
40
- supportedCities
41
- supportedCounties
42
- supportedCountries
43
- supportedTerritories
44
- titleCase
45
34
  tooltipId: string
46
- viewport
47
35
  }
48
36
 
49
37
  const ConfigContext = createContext({} as MapContext)