@cdc/map 4.24.1 → 4.24.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 (43) hide show
  1. package/dist/cdcmap.js +53648 -47918
  2. package/examples/508.json +548 -0
  3. package/examples/default-county.json +0 -28
  4. package/examples/default-hex.json +110 -13
  5. package/examples/default-usa.json +69 -28
  6. package/examples/test.json +0 -9614
  7. package/examples/usa-special-class-legend.json +501 -0
  8. package/examples/{private/zika-issue.json → zika.json} +47 -51
  9. package/index.html +11 -5
  10. package/package.json +3 -3
  11. package/src/CdcMap.tsx +84 -32
  12. package/src/components/BubbleList.jsx +9 -1
  13. package/src/components/CityList.jsx +94 -31
  14. package/src/components/DataTable.jsx +7 -7
  15. package/src/components/EditorPanel/components/EditorPanel.tsx +181 -46
  16. package/src/components/EditorPanel/components/HexShapeSettings.tsx +18 -3
  17. package/src/components/Geo.jsx +4 -2
  18. package/src/components/Legend/components/Legend.tsx +67 -13
  19. package/src/components/Legend/components/LegendItem.Hex.tsx +5 -9
  20. package/src/components/Legend/components/index.scss +31 -5
  21. package/src/components/UsaMap/components/HexIcon.tsx +41 -0
  22. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +38 -19
  23. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +10 -21
  24. package/src/components/UsaMap/components/UsaMap.Region.tsx +11 -37
  25. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +0 -1
  26. package/src/components/UsaMap/components/UsaMap.State.tsx +62 -61
  27. package/src/components/UsaMap/helpers/patternSizes.tsx +5 -0
  28. package/src/components/WorldMap/components/WorldMap.jsx +16 -8
  29. package/src/components/WorldMap/data/world-topo-guiana-update.json +1 -0
  30. package/src/components/WorldMap/data/world-topo-old.json +1 -0
  31. package/{examples/private/new-world.json → src/components/WorldMap/data/world-topo-recent.json} +23137 -22280
  32. package/src/components/WorldMap/data/world-topo.json +1 -1
  33. package/src/data/initial-state.js +5 -2
  34. package/src/data/supported-geos.js +21 -1
  35. package/src/hooks/useTooltip.ts +4 -4
  36. package/src/scss/editor-panel.scss +5 -3
  37. package/src/scss/main.scss +2 -1
  38. package/src/scss/map.scss +22 -12
  39. package/src/types/MapConfig.ts +7 -0
  40. package/examples/private/map-text-wrap.json +0 -574
  41. package/examples/private/map-world-data.json +0 -1046
  42. package/examples/world-geocode-data.json +0 -18
  43. package/examples/world-geocode.json +0 -108
@@ -1,18 +1,20 @@
1
1
  //TODO: Move legends to core
2
- import { useContext } from 'react'
2
+ import { forwardRef, useContext } from 'react'
3
3
  import parse from 'html-react-parser'
4
- import chroma from 'chroma-js'
5
4
 
6
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
6
  import LegendCircle from '@cdc/core/components/LegendCircle'
8
7
  import LegendItemHex from './LegendItem.Hex'
8
+ import Button from '@cdc/core/components/elements/Button'
9
9
 
10
10
  import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
11
11
  import ConfigContext from '../../../context'
12
12
  import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
13
+ import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
14
+ import { Group } from '@visx/group'
13
15
  import './index.scss'
14
16
 
15
- const Legend = () => {
17
+ const Legend = forwardRef((props, ref) => {
16
18
  // prettier-ignore
17
19
  const {
18
20
  displayDataAsText,
@@ -93,8 +95,16 @@ const Legend = () => {
93
95
  onClick={() => {
94
96
  toggleLegendActive(idx, legendLabel)
95
97
  }}
98
+ onKeyDown={e => {
99
+ if (e.key === 'Enter') {
100
+ e.preventDefault()
101
+ toggleLegendActive(idx, legendLabel)
102
+ }
103
+ }}
104
+ role='button'
105
+ tabIndex={0}
96
106
  >
97
- <LegendCircle fill={entry.color} /> <span className='label'>{legendLabel}</span>
107
+ <LegendCircle fill={entry.color} /> <span>{legendLabel}</span>
98
108
  </li>
99
109
  )
100
110
  })
@@ -114,7 +124,7 @@ const Legend = () => {
114
124
 
115
125
  legendItems.push(
116
126
  <>
117
- <li className={`legend-container__li legend-container__li--geo-pattern`}>
127
+ <li className={`legend-container__li legend-container__li--geo-pattern`} aria-label='You are on a pattern button. We dont support toggling patterns on this legend at the moment, but provide the area as being focusable for congruity.' tabIndex={0}>
118
128
  <span className='legend-item' style={{ border: 'unset' }}>
119
129
  <svg width={legendSize} height={legendSize}>
120
130
  {pattern === 'waves' && <PatternWaves id={`${dataKey}--${patternDataIndex}`} height={sizes[size] ?? 10} width={sizes[size] ?? 10} fill={defaultPatternColor} />}
@@ -136,21 +146,33 @@ const Legend = () => {
136
146
  const { legendClasses } = useDataVizClasses(state, viewport)
137
147
 
138
148
  const handleReset = e => {
139
- e.preventDefault()
149
+ const legend = ref.current
150
+ if (e) {
151
+ e.preventDefault()
152
+ }
140
153
  resetLegendToggles()
141
154
  setAccessibleStatus('Legend has been reset, please reference the data table to see updated values.')
155
+ if (legend) {
156
+ legend.focus()
157
+ }
158
+ }
159
+
160
+ const pin = <path className='marker' 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' strokeWidth={2} stroke={'black'} transform={`scale(0.5)`} />
161
+
162
+ const cityStyleShapes = {
163
+ pin: pin,
164
+ circle: <GlyphCircle color='#000' size={150} />,
165
+ square: <GlyphSquare color='#000' size={150} />,
166
+ diamond: <GlyphDiamond color='#000' size={150} />,
167
+ star: <GlyphStar color='#000' size={150} />,
168
+ triangle: <GlyphTriangle color='#000' size={150} />
142
169
  }
143
170
 
144
171
  return (
145
172
  <ErrorBoundary component='Sidebar'>
146
173
  <div className='legends'>
147
- <aside id='legend' className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend'>
174
+ <aside id='legend' className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend' tabIndex={0} ref={ref}>
148
175
  <section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
149
- {runtimeLegend.disabledAmt > 0 && (
150
- <button onClick={handleReset} className={legendClasses.resetButton.join(' ') || ''}>
151
- Clear
152
- </button>
153
- )}
154
176
  {legend.title && <span className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</span>}
155
177
  {legend.dynamicDescription === false && legend.description && <p className={legendClasses.description.join(' ') || ''}>{parse(legend.description)}</p>}
156
178
  {legend.dynamicDescription === true &&
@@ -172,12 +194,44 @@ const Legend = () => {
172
194
  <ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
173
195
  {legendList()}
174
196
  </ul>
197
+ {(state.visual.additionalCityStyles.some(c => c.label) || state.visual.cityStyleLabel) && (
198
+ <>
199
+ <hr />
200
+ <div className={legendClasses.div.join(' ') || ''}>
201
+ {state.visual.cityStyleLabel && (
202
+ <div>
203
+ <svg>
204
+ <Group top={state.visual.cityStyle === 'pin' ? 19 : state.visual.cityStyle === 'triangle' ? 13 : 11} left={10}>
205
+ {cityStyleShapes[state.visual.cityStyle.toLowerCase()]}
206
+ </Group>
207
+ </svg>
208
+ <p>{state.visual.cityStyleLabel}</p>
209
+ </div>
210
+ )}
211
+
212
+ {state.visual.additionalCityStyles.map(
213
+ ({ shape, label }) =>
214
+ label && (
215
+ <div>
216
+ <svg>
217
+ <Group top={shape === 'Pin' ? 19 : shape === 'Triangle' ? 13 : 11} left={10}>
218
+ {cityStyleShapes[shape.toLowerCase()]}
219
+ </Group>
220
+ </svg>
221
+ <p>{label}</p>
222
+ </div>
223
+ )
224
+ )}
225
+ </div>
226
+ </>
227
+ )}
228
+ {runtimeLegend.disabledAmt > 0 && <Button onClick={handleReset}>Reset</Button>}
175
229
  </section>
176
230
  </aside>
177
231
  {state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex && <LegendItemHex state={state} runtimeLegend={runtimeLegend} viewport={viewport} />}
178
232
  </div>
179
233
  </ErrorBoundary>
180
234
  )
181
- }
235
+ })
182
236
 
183
237
  export default Legend
@@ -3,11 +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, runtimeLegend, viewport } = props
7
- const { legend } = state
8
- const { title } = state.general
9
-
10
- const columnLogic = legend.position === 'side' && legend.singleColumn ? 'single-column' : legend.position === 'bottom' && legend.singleRow ? 'single-row' : ''
6
+ const { state, viewport } = props
11
7
 
12
8
  const getItemShape = shape => {
13
9
  switch (shape) {
@@ -29,15 +25,15 @@ const LegendItemHex = props => {
29
25
  state.hexMap.type === 'shapes' &&
30
26
  state.hexMap.shapeGroups.map((shapeGroup, shapeGroupIndex) => {
31
27
  return (
32
- <aside id='legend' className={legendClasses.aside.join(' ')} role='region' aria-label='Legend' tabIndex='0'>
28
+ <aside id='legend' className={legendClasses.aside.join(' ')} role='region' aria-label='Legend' tabIndex={0}>
33
29
  <section className={legendClasses.section.join(' ')} aria-label='Map Legend'>
34
- {legend.title && <span className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</span>}
35
- {legend.dynamicDescription === false && legend.description && <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>}
30
+ {shapeGroup.legendTitle && <span className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</span>}
31
+ {shapeGroup.legendDescription && <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>}
36
32
 
37
33
  <ul className={legendClasses.ul.join(' ')} aria-label='Legend items' style={{ listStyle: 'none' }}>
38
34
  {shapeGroup.items.map((item, itemIndex) => {
39
35
  return (
40
- <li className={legendClasses.li.join(' ')}>
36
+ <li className={legendClasses.li.join(' ')} key={`hex-legend-item-${itemIndex}`}>
41
37
  {getItemShape(item.shape)} {item.value}
42
38
  </li>
43
39
  )
@@ -89,17 +89,14 @@
89
89
  }
90
90
  .legend-container__reset-button {
91
91
  font-size: 0.75em;
92
- color: rgba(0, 0, 0, 0.6);
93
- position: absolute;
94
92
  right: 1em;
95
- background: rgba(0, 0, 0, 0.1);
96
93
  text-transform: uppercase;
97
94
  transition: 0.2s all;
98
95
  padding: 0.2em 0.5em;
99
96
  border: rgba(0, 0, 0, 0.2) 1px solid;
97
+ padding: 0.375rem;
100
98
  &:hover {
101
99
  text-decoration: none;
102
- background: rgba(0, 0, 0, 0.15);
103
100
  transition: 0.2s all;
104
101
  }
105
102
  }
@@ -118,7 +115,7 @@
118
115
  flex-direction: column;
119
116
  }
120
117
 
121
- &:not(.vertical-sorted, .legend-container__ul--single-column) {
118
+ &:not(.vertical-sorted, .legend-container__ul--single-column, .single-row) {
122
119
  width: 100%;
123
120
  @include breakpoint(sm) {
124
121
  .legend-container__li {
@@ -231,5 +228,34 @@
231
228
  }
232
229
  }
233
230
  }
231
+ & .shape-container {
232
+ &.single-row {
233
+ display: flex;
234
+ flex-direction: row;
235
+ align-items: center;
236
+ justify-content: flex-start;
237
+ flex-wrap: wrap;
238
+
239
+ & > div {
240
+ margin-right: 2em;
241
+ }
242
+ & > div:last-child {
243
+ margin-right: 0;
244
+ }
245
+ }
246
+
247
+ & > div {
248
+ display: flex;
249
+ flex-direction: row;
250
+ }
251
+ & div > svg {
252
+ width: 25px;
253
+ height: 32px;
254
+ }
255
+ & div > p {
256
+ white-space: nowrap;
257
+ margin-top: 1px;
258
+ }
259
+ }
234
260
  }
235
261
  }
@@ -0,0 +1,41 @@
1
+ import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight, AiOutlineArrowLeft } from 'react-icons/ai'
2
+ import { Group } from '@visx/group'
3
+
4
+ type HexIconProps = {
5
+ item: {
6
+ operator: '=' | '≠' | '<' | '>' | '<=' | '>='
7
+ shape: 'Arrow Up' | 'Arrow Down' | 'Arrow Right' | 'Arrow Left'
8
+ key: string
9
+ value: string
10
+ }
11
+ index: number
12
+ centroid: [number, number]
13
+ iconSize: number
14
+ textColor: string
15
+ isTerritory: boolean
16
+ }
17
+
18
+ const HexIcon: React.FC<HexIconProps> = props => {
19
+ const { item, index, centroid, iconSize, textColor, isTerritory } = props
20
+ if (!centroid) return
21
+
22
+ if (isTerritory) {
23
+ return (
24
+ <Group style={{ transform: `translate(36%, 50%)`, fill: 'currentColor' }} key={`territory-hex--${index}`}>
25
+ {item.shape === 'Arrow Down' && <AiOutlineArrowDown size={12} stroke='none' fontWeight={100} />}
26
+ {item.shape === 'Arrow Up' && <AiOutlineArrowUp size={12} stroke='none' fontWeight={100} />}
27
+ {item.shape === 'Arrow Right' && <AiOutlineArrowRight size={12} stroke='none' fontWeight={100} />}
28
+ {item.shape === 'Arrow Left' && <AiOutlineArrowLeft size={12} stroke='none' fontWeight={100} />}
29
+ </Group>
30
+ )
31
+ }
32
+ return (
33
+ <Group top={centroid[1] - 5} left={centroid[0] - iconSize} color={textColor} textAnchor='start' key={`hex--${item.key}-${item.value}-${index}`}>
34
+ {item.shape === 'Arrow Down' && <AiOutlineArrowDown />}
35
+ {item.shape === 'Arrow Up' && <AiOutlineArrowUp />}
36
+ {item.shape === 'Arrow Right' && <AiOutlineArrowRight />}
37
+ {item.shape === 'Arrow Left' && <AiOutlineArrowLeft />}
38
+ </Group>
39
+ )
40
+ }
41
+ export default HexIcon
@@ -2,10 +2,9 @@ import { useContext } from 'react'
2
2
  import { geoCentroid, geoPath } from 'd3-geo'
3
3
  import ConfigContext from './../../../../context'
4
4
  import { MapContext } from './../../../../types/MapContext'
5
- import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight } from 'react-icons/ai'
6
- import { Group } from '@visx/group'
5
+ import HexIcon from '../HexIcon'
7
6
  import { Text } from '@visx/text'
8
- import chroma from 'chroma-js'
7
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
9
8
 
10
9
  const offsets = {
11
10
  'US-VT': [50, -8],
@@ -52,16 +51,41 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
52
51
  <>
53
52
  {state.hexMap.shapeGroups.map((group, groupIndex) => {
54
53
  return group.items.map((item, itemIndex) => {
55
- if (item.operator === '=') {
56
- if (geoData[item.key] === item.value) {
57
- return (
58
- <Group style={{ transform: `translate(36%, 50%)`, fill: 'currentColor' }}>
59
- {item.shape === 'Arrow Down' && <AiOutlineArrowDown size={12} stroke='none' fontWeight={100} />}
60
- {item.shape === 'Arrow Up' && <AiOutlineArrowUp size={12} stroke='none' fontWeight={100} />}
61
- {item.shape === 'Arrow Right' && <AiOutlineArrowRight size={12} stroke='none' fontWeight={100} />}
62
- </Group>
63
- )
64
- }
54
+ switch (item.operator) {
55
+ case '=':
56
+ if (geoData[item.key] === item.value || Number(geoData[item.key]) === Number(item.value)) {
57
+ return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
58
+ }
59
+ break
60
+ case '':
61
+ if (geoData[item.key] !== item.value && Number(geoData[item.key]) !== Number(item.value)) {
62
+ return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
63
+ }
64
+ break
65
+ case '<':
66
+ if (Number(geoData[item.key]) < Number(item.value)) {
67
+ return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
68
+ }
69
+ break
70
+ case '>':
71
+ if (Number(geoData[item.key]) > Number(item.value)) {
72
+ return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
73
+ }
74
+ break
75
+ case '<=':
76
+ if (Number(geoData[item.key]) <= Number(item.value)) {
77
+ return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
78
+ }
79
+ break
80
+ case '>=':
81
+ if (item.operator === '>=') {
82
+ if (Number(geoData[item.key]) >= Number(item.value)) {
83
+ return <HexIcon item={item} index={itemIndex} centroid={centroid} isTerritory />
84
+ }
85
+ }
86
+ break
87
+ default:
88
+ break
65
89
  }
66
90
  })
67
91
  })}
@@ -71,12 +95,7 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
71
95
 
72
96
  if (undefined === abbr) return null
73
97
 
74
- let textColor = '#FFF'
75
-
76
- // Dynamic text color
77
- if (chroma.contrast(textColor, bgColor) < 3.5) {
78
- textColor = '#202020' // dark gray
79
- }
98
+ let textColor = getContrastColor('#FFF', bgColor)
80
99
 
81
100
  // always make HI black since it is off to the side
82
101
  if (abbr === 'US-HI') {
@@ -1,21 +1,16 @@
1
1
  import { useContext } from 'react'
2
2
  import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
3
- import chroma from 'chroma-js'
4
3
  import ConfigContext from './../../../../context'
5
4
  import { type MapContext } from '../../../../types/MapContext'
6
- // todo: move this somewhere that makes better sense for pattern sizes.
7
- const sizes = {
8
- small: '8',
9
- medium: '10',
10
- large: '12'
11
- }
5
+ import { patternSizes } from './../../helpers/patternSizes'
6
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
12
7
 
13
8
  const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPattern, territory, ...props }) => {
14
9
  const { state, supportedTerritories } = useContext<MapContext>(ConfigContext)
15
10
 
16
11
  return (
17
12
  <svg viewBox='0 0 45 28'>
18
- <g {...props} strokeLinejoin='round'>
13
+ <g {...props} strokeLinejoin='round' tabIndex={-1}>
19
14
  <path
20
15
  stroke={stroke}
21
16
  strokeWidth={strokeWidth}
@@ -27,32 +22,26 @@ const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPa
27
22
  </text>
28
23
 
29
24
  {state.map.patterns.map((patternData, patternIndex) => {
30
- let defaultPatternColor = 'black'
31
-
32
- const hasMatchingValues = supportedTerritories[territory].includes(patternData?.dataValue)
33
- console.log('has match', patternData)
34
-
35
- if (chroma.contrast(defaultPatternColor, props.style.fill) < 3.5) {
36
- defaultPatternColor = 'white'
37
- }
25
+ const patternColor = getContrastColor('#FFF', props.style.fill)
26
+ const hasMatchingValues = supportedTerritories[territory]?.includes(patternData?.dataValue)
38
27
 
39
28
  if (!hasMatchingValues) return null
40
29
  if (!patternData.pattern) return null
41
30
 
42
31
  return (
43
32
  <>
44
- {patternData?.pattern === 'waves' && <PatternWaves id={`territory-${patternData?.dataKey}--${patternIndex}`} height={sizes[patternData?.size] ?? 10} width={sizes[patternData?.size] ?? 10} fill={defaultPatternColor} complement />}
45
- {patternData?.pattern === 'circles' && <PatternCircles id={`territory-${patternData?.dataKey}--${patternIndex}`} height={sizes[patternData?.size] ?? 10} width={sizes[patternData?.size] ?? 10} fill={defaultPatternColor} complement />}
46
- {patternData?.pattern === 'lines' && <PatternLines id={`territory-${patternData?.dataKey}--${patternIndex}`} height={sizes[patternData?.size] ?? 6} width={sizes[patternData?.size] ?? 6} stroke={defaultPatternColor} strokeWidth={1} orientation={['diagonalRightToLeft']} />}
33
+ {patternData?.pattern === 'waves' && <PatternWaves id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 10} width={patternSizes[patternData?.size] ?? 10} fill={patternColor} complement />}
34
+ {patternData?.pattern === 'circles' && <PatternCircles id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 10} width={patternSizes[patternData?.size] ?? 10} fill={patternColor} complement />}
35
+ {patternData?.pattern === 'lines' && <PatternLines id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 6} width={patternSizes[patternData?.size] ?? 6} stroke={patternColor} strokeWidth={1} orientation={['diagonalRightToLeft']} />}
47
36
  <path
48
37
  stroke={stroke}
49
38
  strokeWidth={strokeWidth}
50
39
  d='M40,0.5 C41.2426407,0.5 42.3676407,1.00367966 43.1819805,1.81801948 C43.9963203,2.63235931 44.5,3.75735931 44.5,5 L44.5,5 L44.5,23 C44.5,24.2426407 43.9963203,25.3676407 43.1819805,26.1819805 C42.3676407,26.9963203 41.2426407,27.5 40,27.5 L40,27.5 L5,27.5 C3.75735931,27.5 2.63235931,26.9963203 1.81801948,26.1819805 C1.00367966,25.3676407 0.5,24.2426407 0.5,23 L0.5,23 L0.5,5 C0.5,3.75735931 1.00367966,2.63235931 1.81801948,1.81801948 C2.63235931,1.00367966 3.75735931,0.5 5,0.5 L5,0.5 Z'
51
40
  fill={`url(#territory-${patternData?.dataKey}--${patternIndex})`}
52
- color='white'
41
+ color={patternData ? 'white' : textColor}
53
42
  className={[`territory-pattern-${patternData.dataKey}`, `territory-pattern-${patternData.dataKey}--${patternData.dataValue}`].join(' ')}
54
43
  />
55
- <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: patternData ? 'black' : textColor, strokeWidth: patternData ? 6 : 0 }} className='territory-text' paint-order='stroke'>
44
+ <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ fill: patternData ? 'white' : 'black', stroke: patternData ? 'black' : textColor, strokeWidth: patternData ? 6 : 0 }} className='territory-text' paint-order='stroke'>
56
45
  {label}
57
46
  </text>
58
47
  </>
@@ -1,12 +1,14 @@
1
1
  import { useState, useEffect, memo, useContext } from 'react'
2
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
2
3
 
3
- import { jsx } from '@emotion/react'
4
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
4
+ // 3rd party
5
5
  import { geoCentroid } from 'd3-geo'
6
6
  import { feature } from 'topojson-client'
7
- import topoJSON from '../data/us-regions-topo-2.json'
8
7
  import { Mercator } from '@visx/geo'
9
- import chroma from 'chroma-js'
8
+
9
+ // cdc
10
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
11
+ import topoJSON from '../data/us-regions-topo-2.json'
10
12
  import ConfigContext from '../../../context'
11
13
 
12
14
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.regions)
@@ -74,7 +76,7 @@ const UsaRegionMap = props => {
74
76
 
75
77
  let toolTip
76
78
 
77
- let styles = {
79
+ let styles: React.CSSProperties = {
78
80
  fill: '#E6E6E6',
79
81
  color: '#202020'
80
82
  }
@@ -87,13 +89,8 @@ const UsaRegionMap = props => {
87
89
 
88
90
  const legendColors = applyLegendToRow(territoryData)
89
91
 
90
- let textColor = '#FFF'
91
-
92
92
  if (legendColors) {
93
- // Use white text if the background is dark, and dark grey if it's light
94
- if (chroma.contrast(textColor, legendColors[0]) < 3.5) {
95
- textColor = '#202020'
96
- }
93
+ const textColor = getContrastColor('#FFF', legendColors[0])
97
94
 
98
95
  let needsPointer = false
99
96
 
@@ -124,12 +121,7 @@ const UsaRegionMap = props => {
124
121
 
125
122
  if (undefined === abbr) return null
126
123
 
127
- let textColor = '#FFF'
128
-
129
- // Dynamic text color
130
- if (chroma.contrast(textColor, bgColor) < 3.5) {
131
- textColor = '#202020'
132
- }
124
+ const textColor = getContrastColor('#FFF', bgColor)
133
125
 
134
126
  let x = 0,
135
127
  y = 5
@@ -195,11 +187,7 @@ const UsaRegionMap = props => {
195
187
 
196
188
  const TerratoryRect = props => {
197
189
  const { posX = 0, tName } = props
198
- let textColor = '#fff'
199
-
200
- if (chroma.contrast(textColor, legendColors[0]) < 4.5) {
201
- textColor = '#202020'
202
- }
190
+ const textColor = getContrastColor('#FFF', legendColors[0])
203
191
  return (
204
192
  <>
205
193
  <rect x={posX} width='36' height='24' rx='6' stroke='#fff' strokeWidth='1' />
@@ -212,28 +200,14 @@ const UsaRegionMap = props => {
212
200
 
213
201
  const circleRadius = 15
214
202
 
215
- // SIDE CHART EXPERIMENT
216
- // const height = state.data[index].Change;
217
- // const barHeight = Math.abs(height * 20 );
218
- // const barPositive = height > 0;
219
- // const barY = barPositive ? -barHeight + 15 : 15;
220
- // const baseY = 14;
221
- // const barFill = barPositive ? "#fff" : "#fff";
222
-
223
203
  return (
224
- <g key={key} className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip}>
204
+ <g key={key} className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} tabIndex={-1}>
225
205
  <path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1.3} d={path} />
226
206
  <g id={`region-${index + 1}-label`}>
227
207
  <circle fill='#fff' stroke='#999' cx={circleRadius} cy={circleRadius} r={circleRadius} />
228
208
  <text fill='#333' x='15px' y='20px' textAnchor='middle'>
229
209
  {index + 1}
230
210
  </text>
231
- {/* SIDE CHART EXPERIMENT */}
232
- {/*<g y={barY*20}>*/}
233
- {/* <rect x="-20" y={barY} width="10" height={barHeight} fill={barFill} stroke="#333"/>*/}
234
- {/* <rect x="-23" y={baseY} width="16" height="2" fill="#000" />*/}
235
- {/*</g>*/}
236
- {/* / SIDE CHART EXPERIMENT */}
237
211
  </g>
238
212
  {geoKey === 'region 2' && (
239
213
  <g id='region-2-territories'>
@@ -264,7 +264,6 @@ const SingleStateMap = props => {
264
264
  titleCase={titleCase}
265
265
  setSharedFilterValue={setSharedFilterValue}
266
266
  isFilterValueSupported={isFilterValueSupported}
267
- isGeoCodeMap={state.general.type === 'us-geocode'}
268
267
  />
269
268
  )
270
269