@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.
- package/dist/cdcmap.js +51010 -49337
- package/examples/annotation/index.json +1 -1
- package/examples/custom-map-layers.json +1 -1
- package/examples/default-geocode.json +2 -2
- package/examples/private/DEV-9989.json +229 -0
- package/examples/private/ardi.json +180 -0
- package/examples/private/colors 2.json +416 -0
- package/examples/private/colors.json +416 -0
- package/examples/private/colors.json.zip +0 -0
- package/examples/private/customColors.json +45348 -0
- package/examples/private/mmr.json +246 -0
- package/examples/private/test.json +1632 -0
- package/index.html +12 -14
- package/package.json +8 -3
- package/src/CdcMap.tsx +126 -394
- package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +9 -0
- package/src/_stories/CdcMap.stories.tsx +1 -1
- package/src/_stories/GoogleMap.stories.tsx +19 -0
- package/src/_stories/_mock/DEV-10148.json +859 -0
- package/src/_stories/_mock/DEV-9989.json +229 -0
- package/src/_stories/_mock/example-city-state.json +1 -1
- package/src/_stories/_mock/google-map.json +819 -0
- package/src/components/Annotation/Annotation.Draggable.tsx +34 -43
- package/src/components/Annotation/AnnotationDropdown.tsx +4 -4
- package/src/components/CityList.tsx +2 -2
- package/src/components/DataTable.tsx +8 -9
- package/src/components/EditorPanel/components/EditorPanel.tsx +96 -270
- package/src/components/GoogleMap/components/GoogleMap.tsx +67 -0
- package/src/components/GoogleMap/index.tsx +3 -0
- package/src/components/Legend/components/Legend.tsx +40 -30
- package/src/components/Legend/components/LegendItem.Hex.tsx +7 -3
- package/src/components/Legend/components/index.scss +22 -16
- package/src/components/Modal.tsx +6 -5
- package/src/components/NavigationMenu.tsx +5 -4
- package/src/components/UsaMap/components/TerritoriesSection.tsx +56 -0
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +3 -3
- package/src/components/UsaMap/components/UsaMap.County.tsx +1 -1
- package/src/components/UsaMap/components/UsaMap.Region.tsx +12 -8
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +2 -2
- package/src/components/UsaMap/components/UsaMap.State.tsx +23 -29
- package/src/components/WorldMap/WorldMap.tsx +3 -5
- package/src/context.ts +0 -12
- package/src/data/initial-state.js +2 -2
- package/src/data/supported-geos.js +23 -3
- package/src/helpers/applyColorToLegend.ts +3 -3
- package/src/helpers/closeModal.ts +9 -0
- package/src/helpers/handleMapAriaLabels.ts +38 -0
- package/src/helpers/indexOfIgnoreType.ts +8 -0
- package/src/helpers/navigationHandler.ts +21 -0
- package/src/helpers/toTitleCase.ts +44 -0
- package/src/helpers/validateFipsCodeLength.ts +30 -0
- package/src/hooks/useResizeObserver.ts +42 -0
- package/src/hooks/useTooltip.ts +4 -2
- package/src/index.jsx +1 -0
- package/src/scss/editor-panel.scss +2 -1
- package/src/scss/filters.scss +0 -5
- package/src/scss/main.scss +57 -61
- package/src/scss/map.scss +1 -13
- package/src/types/MapConfig.ts +20 -11
- package/src/types/MapContext.ts +4 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//TODO: Move legends to core
|
|
2
|
-
import { forwardRef, useContext
|
|
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 {
|
|
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:
|
|
30
|
+
currentViewport: ViewPort
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
33
|
-
const { skipId,
|
|
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
|
|
268
|
-
|
|
269
|
-
|
|
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.
|
|
338
|
-
|
|
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 &&
|
|
31
|
-
|
|
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
|
-
|
|
76
|
-
|
|
72
|
+
|
|
73
|
+
.tspan {
|
|
74
|
+
font-size: var(--legend-item-font-size) !important;
|
|
77
75
|
}
|
|
76
|
+
|
|
78
77
|
.legend-container__title {
|
|
79
|
-
font-size:
|
|
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-
|
|
84
|
-
|
|
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;
|
package/src/components/Modal.tsx
CHANGED
|
@@ -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,
|
|
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={
|
|
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(
|
|
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
|
|
100
|
-
`territory-pattern-${patternData
|
|
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
|
-
//
|
|
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] +
|
|
154
|
-
y2={centroid[1] +
|
|
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] +
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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)
|