@cdc/map 4.24.3 → 4.24.5
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 +29115 -28828
- package/examples/example-city-state.json +51 -17
- package/examples/hex-colors.json +507 -0
- package/index.html +8 -8
- package/package.json +3 -3
- package/src/CdcMap.tsx +151 -115
- package/src/components/BubbleList.jsx +8 -5
- package/src/components/CityList.jsx +5 -5
- package/src/components/EditorPanel/components/EditorPanel.tsx +1390 -1436
- package/src/components/EditorPanel/components/Error.tsx +12 -0
- package/src/components/EditorPanel/components/Panel.PatternSettings-style.css +5 -0
- package/src/components/EditorPanel/components/Panel.PatternSettings.tsx +18 -1
- package/src/components/Legend/components/Legend.tsx +18 -7
- package/src/components/Legend/components/LegendItem.Hex.tsx +1 -1
- package/src/components/UsaMap/components/HexIcon.tsx +2 -2
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +1 -1
- package/src/components/UsaMap/components/UsaMap.County.tsx +7 -4
- package/src/components/UsaMap/components/UsaMap.Region.tsx +3 -2
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +5 -3
- package/src/components/UsaMap/components/UsaMap.State.tsx +25 -10
- package/src/components/WorldMap/{components/WorldMap.jsx → WorldMap.tsx} +26 -12
- package/src/components/WorldMap/index.tsx +1 -1
- package/src/context.ts +2 -1
- package/src/data/supported-geos.js +1 -0
- package/src/hooks/useMapLayers.tsx +2 -2
- package/src/scss/editor-panel.scss +1 -12
- package/src/scss/main.scss +2 -1
- package/src/scss/map.scss +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const Error = ({ state }) => {
|
|
2
|
+
return (
|
|
3
|
+
<section className='waiting'>
|
|
4
|
+
<section className='waiting-container'>
|
|
5
|
+
<h3>Error With Configuration</h3>
|
|
6
|
+
<p>{state.runtime.editorErrorMessage}</p>
|
|
7
|
+
</section>
|
|
8
|
+
</section>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default Error
|
|
@@ -3,6 +3,9 @@ import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, Acc
|
|
|
3
3
|
import ConfigContext from '../../../context'
|
|
4
4
|
import { type MapContext } from '../../../types/MapContext'
|
|
5
5
|
import Button from '@cdc/core/components/elements/Button'
|
|
6
|
+
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
7
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
8
|
+
import './Panel.PatternSettings-style.css'
|
|
6
9
|
|
|
7
10
|
type PanelProps = {
|
|
8
11
|
name: string
|
|
@@ -32,7 +35,7 @@ const PatternSettings = ({ name }: PanelProps) => {
|
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/** Updates the map pattern at a given index */
|
|
35
|
-
const handleUpdateGeoPattern = (value: string, index: number, keyToUpdate: 'dataKey' | 'pattern' | 'dataValue' | 'size' | 'label') => {
|
|
38
|
+
const handleUpdateGeoPattern = (value: string, index: number, keyToUpdate: 'dataKey' | 'pattern' | 'dataValue' | 'size' | 'label' | 'color') => {
|
|
36
39
|
const updatedPatterns = [...state.map.patterns]
|
|
37
40
|
updatedPatterns[index] = { ...updatedPatterns[index], [keyToUpdate]: value }
|
|
38
41
|
|
|
@@ -120,6 +123,20 @@ const PatternSettings = ({ name }: PanelProps) => {
|
|
|
120
123
|
</option>
|
|
121
124
|
))}
|
|
122
125
|
</select>
|
|
126
|
+
<div className='pattern-input__color'>
|
|
127
|
+
<label htmlFor='patternColor'>
|
|
128
|
+
Pattern Color
|
|
129
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
130
|
+
<Tooltip.Target>
|
|
131
|
+
<Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
|
|
132
|
+
</Tooltip.Target>
|
|
133
|
+
<Tooltip.Content>
|
|
134
|
+
<p>{`If this setting is used, it is the reponsibility of the visualization author to verify the visualization colors meet WCAG 3:1 contrast ratios.`}</p>
|
|
135
|
+
</Tooltip.Content>
|
|
136
|
+
</Tooltip>
|
|
137
|
+
<input type='text' value={pattern.color || ''} id='patternColor' name='patternColor' onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'color')} />
|
|
138
|
+
</label>
|
|
139
|
+
</div>
|
|
123
140
|
<Button onClick={e => handleRemovePattern(patternIndex)} className='btn btn--remove warn'>
|
|
124
141
|
Remove Pattern
|
|
125
142
|
</Button>
|
|
@@ -14,7 +14,13 @@ import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from
|
|
|
14
14
|
import { Group } from '@visx/group'
|
|
15
15
|
import './index.scss'
|
|
16
16
|
|
|
17
|
+
type LegendProps = {
|
|
18
|
+
skipId: string
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
const Legend = forwardRef((props, ref) => {
|
|
22
|
+
const { skipId } = props
|
|
23
|
+
|
|
18
24
|
// prettier-ignore
|
|
19
25
|
const {
|
|
20
26
|
displayDataAsText,
|
|
@@ -28,6 +34,7 @@ const Legend = forwardRef((props, ref) => {
|
|
|
28
34
|
} = useContext(ConfigContext)
|
|
29
35
|
|
|
30
36
|
const { legend } = state
|
|
37
|
+
const fontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? { fontSize: '11px' } : null
|
|
31
38
|
|
|
32
39
|
// Toggles if a legend is active and being applied to the map and data table.
|
|
33
40
|
const toggleLegendActive = (i, legendLabel) => {
|
|
@@ -89,6 +96,7 @@ const Legend = forwardRef((props, ref) => {
|
|
|
89
96
|
return (
|
|
90
97
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions
|
|
91
98
|
<li
|
|
99
|
+
style={fontSize}
|
|
92
100
|
className={handleListItemClass().join(' ')}
|
|
93
101
|
key={idx}
|
|
94
102
|
title={`Legend item ${legendLabel} - Click to disable`}
|
|
@@ -101,10 +109,9 @@ const Legend = forwardRef((props, ref) => {
|
|
|
101
109
|
toggleLegendActive(idx, legendLabel)
|
|
102
110
|
}
|
|
103
111
|
}}
|
|
104
|
-
role='button'
|
|
105
112
|
tabIndex={0}
|
|
106
113
|
>
|
|
107
|
-
<LegendCircle fill={entry.color} /> <span>{legendLabel}</span>
|
|
114
|
+
<LegendCircle viewport={viewport} fill={entry.color} /> <span>{legendLabel}</span>
|
|
108
115
|
</li>
|
|
109
116
|
)
|
|
110
117
|
})
|
|
@@ -171,10 +178,14 @@ const Legend = forwardRef((props, ref) => {
|
|
|
171
178
|
return (
|
|
172
179
|
<ErrorBoundary component='Sidebar'>
|
|
173
180
|
<div className='legends'>
|
|
174
|
-
<aside id='legend' className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend' tabIndex={0} ref={ref}>
|
|
181
|
+
<aside id={skipId || 'legend'} className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend' tabIndex={0} ref={ref}>
|
|
175
182
|
<section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
|
|
176
|
-
{legend.title && <
|
|
177
|
-
{legend.dynamicDescription === false && legend.description &&
|
|
183
|
+
{legend.title && <h3 className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</h3>}
|
|
184
|
+
{legend.dynamicDescription === false && legend.description && (
|
|
185
|
+
<p style={fontSize} className={legendClasses.description.join(' ') || ''}>
|
|
186
|
+
{parse(legend.description)}
|
|
187
|
+
</p>
|
|
188
|
+
)}
|
|
178
189
|
{legend.dynamicDescription === true &&
|
|
179
190
|
runtimeFilters.map((filter, idx) => {
|
|
180
191
|
const lookupStr = `${idx},${filter.values.indexOf(String(filter.active))}`
|
|
@@ -184,7 +195,7 @@ const Legend = forwardRef((props, ref) => {
|
|
|
184
195
|
|
|
185
196
|
if (desc.length > 0) {
|
|
186
197
|
return (
|
|
187
|
-
<p key={`dynamic-description-${lookupStr}`} className={`dynamic-legend-description-${lookupStr}`}>
|
|
198
|
+
<p style={fontSize} key={`dynamic-description-${lookupStr}`} className={`dynamic-legend-description-${lookupStr}`}>
|
|
188
199
|
{desc}
|
|
189
200
|
</p>
|
|
190
201
|
)
|
|
@@ -218,7 +229,7 @@ const Legend = forwardRef((props, ref) => {
|
|
|
218
229
|
{cityStyleShapes[shape.toLowerCase()]}
|
|
219
230
|
</Group>
|
|
220
231
|
</svg>
|
|
221
|
-
<p>{label}</p>
|
|
232
|
+
<p style={fontSize}>{label}</p>
|
|
222
233
|
</div>
|
|
223
234
|
)
|
|
224
235
|
)}
|
|
@@ -27,7 +27,7 @@ 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 && <
|
|
30
|
+
{shapeGroup.legendTitle && <h3 className={legendClasses.title.join(' ')}>{parse(shapeGroup.legendTitle)}</h3>}
|
|
31
31
|
{shapeGroup.legendDescription && <p className={legendClasses.description.join(' ')}>{parse(shapeGroup.legendDescription)}</p>}
|
|
32
32
|
|
|
33
33
|
<ul className={legendClasses.ul.join(' ')} aria-label='Legend items' style={{ listStyle: 'none' }}>
|
|
@@ -16,12 +16,12 @@ type HexIconProps = {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const HexIcon: React.FC<HexIconProps> = props => {
|
|
19
|
-
const { item, index, centroid, iconSize, textColor, isTerritory } = props
|
|
19
|
+
const { item, index, centroid, iconSize, textColor = '#000', isTerritory } = props
|
|
20
20
|
if (!centroid) return
|
|
21
21
|
|
|
22
22
|
if (isTerritory) {
|
|
23
23
|
return (
|
|
24
|
-
<Group style={{ transform: `translate(36%, 50%)
|
|
24
|
+
<Group style={{ transform: `translate(36%, 50%)` }} key={`territory-hex--${index}`}>
|
|
25
25
|
{item.shape === 'Arrow Down' && <AiOutlineArrowDown size={12} stroke='none' fontWeight={100} />}
|
|
26
26
|
{item.shape === 'Arrow Up' && <AiOutlineArrowUp size={12} stroke='none' fontWeight={100} />}
|
|
27
27
|
{item.shape === 'Arrow Right' && <AiOutlineArrowRight size={12} stroke='none' fontWeight={100} />}
|
|
@@ -17,7 +17,7 @@ const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPa
|
|
|
17
17
|
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'
|
|
18
18
|
{...props}
|
|
19
19
|
/>
|
|
20
|
-
<text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: textColor, strokeWidth: 1 }} className='territory-text'
|
|
20
|
+
<text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: textColor, strokeWidth: 1 }} className='territory-text' paintOrder='stroke'>
|
|
21
21
|
{label}
|
|
22
22
|
</text>
|
|
23
23
|
|
|
@@ -127,7 +127,9 @@ const CountyMap = props => {
|
|
|
127
127
|
handleMapAriaLabels,
|
|
128
128
|
runtimeLegend,
|
|
129
129
|
state,
|
|
130
|
-
runtimeFilters
|
|
130
|
+
runtimeFilters,
|
|
131
|
+
tooltipId,
|
|
132
|
+
isEditor
|
|
131
133
|
} = useContext(ConfigContext)
|
|
132
134
|
|
|
133
135
|
// CREATE STATE LINES
|
|
@@ -138,7 +140,7 @@ const CountyMap = props => {
|
|
|
138
140
|
|
|
139
141
|
const pathGenerator = geoPath().projection(geoAlbersUsaTerritories())
|
|
140
142
|
|
|
141
|
-
const { featureArray } = useMapLayers(state, '', pathGenerator,
|
|
143
|
+
const { featureArray } = useMapLayers(state, '', pathGenerator, tooltipId)
|
|
142
144
|
|
|
143
145
|
useEffect(() => {
|
|
144
146
|
if (containerEl) {
|
|
@@ -322,7 +324,7 @@ const CountyMap = props => {
|
|
|
322
324
|
|
|
323
325
|
tooltipRef.current.style.display = 'block'
|
|
324
326
|
tooltipRef.current.style.top = e.clientY + 'px'
|
|
325
|
-
tooltipRef.current.style.left = e.clientX + 'px'
|
|
327
|
+
tooltipRef.current.style.left = isEditor ? Number(e.clientX - 350) + 'px' : e.clientX + 'px'
|
|
326
328
|
tooltipRef.current.innerHTML = applyTooltipsToGeo(displayGeoName(county.id), data[county.id])
|
|
327
329
|
tooltipRef.current.setAttribute('data-index', countyIndex)
|
|
328
330
|
} else {
|
|
@@ -520,8 +522,9 @@ const CountyMap = props => {
|
|
|
520
522
|
tooltipRef.current.setAttribute('data-index', null)
|
|
521
523
|
}}
|
|
522
524
|
onClick={canvasClick}
|
|
525
|
+
className='county-map-canvas'
|
|
523
526
|
></canvas>
|
|
524
|
-
<div ref={tooltipRef} id=
|
|
527
|
+
<div ref={tooltipRef} id={`tooltip__${tooltipId}`} className='tooltip' style={{ background: `rgba(255,255,255,${state.tooltips.opacity / 100})` }}></div>
|
|
525
528
|
<button className={`btn btn--reset`} onClick={onReset} ref={resetButton} tabIndex='0'>
|
|
526
529
|
Reset Zoom
|
|
527
530
|
</button>
|
|
@@ -41,6 +41,7 @@ const UsaRegionMap = props => {
|
|
|
41
41
|
handleMapAriaLabels,
|
|
42
42
|
state,
|
|
43
43
|
supportedTerritories,
|
|
44
|
+
tooltipId
|
|
44
45
|
} = useContext(ConfigContext)
|
|
45
46
|
|
|
46
47
|
// "Choose State" options
|
|
@@ -111,7 +112,7 @@ const UsaRegionMap = props => {
|
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
return <Shape key={label} label={label} css={styles} text={styles.color} stroke={geoStrokeColor} strokeWidth={1.5} onClick={() => geoClickHandler(territory, territoryData)} data-tooltip-id=
|
|
115
|
+
return <Shape key={label} label={label} css={styles} text={styles.color} stroke={geoStrokeColor} strokeWidth={1.5} onClick={() => geoClickHandler(territory, territoryData)} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip} />
|
|
115
116
|
}
|
|
116
117
|
})
|
|
117
118
|
|
|
@@ -201,7 +202,7 @@ const UsaRegionMap = props => {
|
|
|
201
202
|
const circleRadius = 15
|
|
202
203
|
|
|
203
204
|
return (
|
|
204
|
-
<g key={key} className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id=
|
|
205
|
+
<g key={key} className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip} tabIndex={-1}>
|
|
205
206
|
<path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1.3} d={path} />
|
|
206
207
|
<g id={`region-${index + 1}-label`}>
|
|
207
208
|
<circle fill='#fff' stroke='#999' cx={circleRadius} cy={circleRadius} r={circleRadius} />
|
|
@@ -112,7 +112,8 @@ const SingleStateMap = props => {
|
|
|
112
112
|
titleCase,
|
|
113
113
|
setSharedFilterValue,
|
|
114
114
|
isFilterValueSupported,
|
|
115
|
-
runtimeFilters
|
|
115
|
+
runtimeFilters,
|
|
116
|
+
tooltipId
|
|
116
117
|
} = useContext(ConfigContext)
|
|
117
118
|
|
|
118
119
|
const projection = geoAlbersUsaTerritories().translate([WIDTH / 2, HEIGHT / 2])
|
|
@@ -236,13 +237,13 @@ const SingleStateMap = props => {
|
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
return (
|
|
239
|
-
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[state.columns.geo.name]}`} style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id=
|
|
240
|
+
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[state.columns.geo.name]}`} style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip}>
|
|
240
241
|
<path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
|
|
241
242
|
</g>
|
|
242
243
|
)
|
|
243
244
|
} else {
|
|
244
245
|
return (
|
|
245
|
-
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')}`} style={{ fill: '#e6e6e6' }} data-tooltip-id=
|
|
246
|
+
<g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')}`} style={{ fill: '#e6e6e6' }} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip}>
|
|
246
247
|
<path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
|
|
247
248
|
</g>
|
|
248
249
|
)
|
|
@@ -264,6 +265,7 @@ const SingleStateMap = props => {
|
|
|
264
265
|
titleCase={titleCase}
|
|
265
266
|
setSharedFilterValue={setSharedFilterValue}
|
|
266
267
|
isFilterValueSupported={isFilterValueSupported}
|
|
268
|
+
tooltipId={tooltipId}
|
|
267
269
|
/>
|
|
268
270
|
)
|
|
269
271
|
|
|
@@ -64,6 +64,7 @@ const UsaMap = () => {
|
|
|
64
64
|
state,
|
|
65
65
|
supportedTerritories,
|
|
66
66
|
titleCase,
|
|
67
|
+
tooltipId
|
|
67
68
|
} = useContext<MapContext>(ConfigContext)
|
|
68
69
|
|
|
69
70
|
let isFilterValueSupported = false
|
|
@@ -163,7 +164,20 @@ const UsaMap = () => {
|
|
|
163
164
|
|
|
164
165
|
return (
|
|
165
166
|
<>
|
|
166
|
-
<Shape
|
|
167
|
+
<Shape
|
|
168
|
+
key={label}
|
|
169
|
+
label={label}
|
|
170
|
+
style={styles}
|
|
171
|
+
text={styles.color}
|
|
172
|
+
strokeWidth={1.5}
|
|
173
|
+
textColor={textColor}
|
|
174
|
+
onClick={() => geoClickHandler(territory, territoryData)}
|
|
175
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
176
|
+
data-tooltip-html={toolTip}
|
|
177
|
+
territory={territory}
|
|
178
|
+
territoryData={territoryData}
|
|
179
|
+
tabIndex={-1}
|
|
180
|
+
/>
|
|
167
181
|
</>
|
|
168
182
|
)
|
|
169
183
|
}
|
|
@@ -172,7 +186,7 @@ const UsaMap = () => {
|
|
|
172
186
|
let pathGenerator = geoPath().projection(geoAlbersUsa().translate(translate))
|
|
173
187
|
|
|
174
188
|
// Note: Layers are different than patterns
|
|
175
|
-
const { pathArray } = useMapLayers(state, '', pathGenerator)
|
|
189
|
+
const { pathArray } = useMapLayers(state, '', pathGenerator, tooltipId)
|
|
176
190
|
|
|
177
191
|
// Constructs and displays markup for all geos on the map (except territories right now)
|
|
178
192
|
const constructGeoJsx = (geographies, projection) => {
|
|
@@ -261,33 +275,33 @@ const UsaMap = () => {
|
|
|
261
275
|
switch (item.operator) {
|
|
262
276
|
case '=':
|
|
263
277
|
if (geoData[item.key] === item.value || Number(geoData[item.key]) === Number(item.value)) {
|
|
264
|
-
return <HexIcon item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
278
|
+
return <HexIcon textColor={textColor} item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
265
279
|
}
|
|
266
280
|
break
|
|
267
281
|
case '≠':
|
|
268
282
|
if (geoData[item.key] !== item.value && Number(geoData[item.key]) !== Number(item.value)) {
|
|
269
|
-
return <HexIcon item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
283
|
+
return <HexIcon textColor={textColor} item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
270
284
|
}
|
|
271
285
|
break
|
|
272
286
|
case '<':
|
|
273
287
|
if (Number(geoData[item.key]) < Number(item.value)) {
|
|
274
|
-
return <HexIcon item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
288
|
+
return <HexIcon textColor={textColor} item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
275
289
|
}
|
|
276
290
|
break
|
|
277
291
|
case '>':
|
|
278
292
|
if (Number(geoData[item.key]) > Number(item.value)) {
|
|
279
|
-
return <HexIcon item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
293
|
+
return <HexIcon textColor={textColor} item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
280
294
|
}
|
|
281
295
|
break
|
|
282
296
|
case '<=':
|
|
283
297
|
if (Number(geoData[item.key]) <= Number(item.value)) {
|
|
284
|
-
return <HexIcon item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
298
|
+
return <HexIcon textColor={textColor} item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
285
299
|
}
|
|
286
300
|
break
|
|
287
301
|
case '>=':
|
|
288
302
|
if (item.operator === '>=') {
|
|
289
303
|
if (Number(geoData[item.key]) >= Number(item.value)) {
|
|
290
|
-
return <HexIcon item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
304
|
+
return <HexIcon textColor={textColor} item={item} index={itemIndex} centroid={centroid} iconSize={iconSize} />
|
|
291
305
|
}
|
|
292
306
|
}
|
|
293
307
|
break
|
|
@@ -302,7 +316,7 @@ const UsaMap = () => {
|
|
|
302
316
|
|
|
303
317
|
return (
|
|
304
318
|
<g data-name={geoName} key={key} tabIndex={-1}>
|
|
305
|
-
<g className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} id={geoName} data-tooltip-id=
|
|
319
|
+
<g className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} id={geoName} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={tooltip} tabIndex={-1}>
|
|
306
320
|
{/* state path */}
|
|
307
321
|
<path tabIndex={-1} className='single-geo' strokeWidth={1.3} d={path} />
|
|
308
322
|
|
|
@@ -311,7 +325,7 @@ const UsaMap = () => {
|
|
|
311
325
|
const { pattern, dataKey, size } = patternData
|
|
312
326
|
const currentFill = styles.fill
|
|
313
327
|
const hasMatchingValues = patternData.dataValue === geoData[patternData.dataKey]
|
|
314
|
-
const patternColor = getContrastColor('#000', currentFill)
|
|
328
|
+
const patternColor = patternData.color || getContrastColor('#000', currentFill)
|
|
315
329
|
|
|
316
330
|
return (
|
|
317
331
|
hasMatchingValues && (
|
|
@@ -358,6 +372,7 @@ const UsaMap = () => {
|
|
|
358
372
|
setSharedFilterValue={setSharedFilterValue}
|
|
359
373
|
state={state}
|
|
360
374
|
titleCase={titleCase}
|
|
375
|
+
tooltipId={tooltipId}
|
|
361
376
|
/>
|
|
362
377
|
)
|
|
363
378
|
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { memo, useContext } from 'react'
|
|
2
2
|
|
|
3
|
-
import { jsx } from '@emotion/react'
|
|
4
3
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
5
4
|
import { geoMercator } from 'd3-geo'
|
|
6
5
|
import { Mercator } from '@visx/geo'
|
|
7
6
|
import { feature } from 'topojson-client'
|
|
8
|
-
import topoJSON from '
|
|
9
|
-
import ZoomableGroup from '
|
|
10
|
-
import Geo from '
|
|
11
|
-
import CityList from '
|
|
12
|
-
import BubbleList from '
|
|
13
|
-
import ConfigContext from '
|
|
7
|
+
import topoJSON from './data/world-topo.json'
|
|
8
|
+
import ZoomableGroup from '../ZoomableGroup'
|
|
9
|
+
import Geo from '../Geo'
|
|
10
|
+
import CityList from '../CityList'
|
|
11
|
+
import BubbleList from '../BubbleList'
|
|
12
|
+
import ConfigContext from '../../context'
|
|
14
13
|
|
|
15
14
|
const { features: world } = feature(topoJSON, topoJSON.objects.countries)
|
|
16
15
|
|
|
17
16
|
let projection = geoMercator()
|
|
18
17
|
|
|
19
|
-
const WorldMap =
|
|
18
|
+
const WorldMap = () => {
|
|
20
19
|
// prettier-ignore
|
|
21
20
|
const {
|
|
22
21
|
applyLegendToRow,
|
|
@@ -34,7 +33,8 @@ const WorldMap = props => {
|
|
|
34
33
|
setState,
|
|
35
34
|
state,
|
|
36
35
|
supportedCountries,
|
|
37
|
-
titleCase
|
|
36
|
+
titleCase,
|
|
37
|
+
tooltipId
|
|
38
38
|
} = useContext(ConfigContext)
|
|
39
39
|
|
|
40
40
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
@@ -126,7 +126,7 @@ const WorldMap = props => {
|
|
|
126
126
|
|
|
127
127
|
const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
|
|
128
128
|
|
|
129
|
-
let styles = {
|
|
129
|
+
let styles: Record<string, string | Record<string, string>> = {
|
|
130
130
|
fill: '#E6E6E6',
|
|
131
131
|
cursor: 'default'
|
|
132
132
|
}
|
|
@@ -155,7 +155,20 @@ const WorldMap = props => {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
return (
|
|
158
|
-
<Geo
|
|
158
|
+
<Geo
|
|
159
|
+
additionalData={additionalData}
|
|
160
|
+
geoData={geoData}
|
|
161
|
+
state={state}
|
|
162
|
+
key={i + '-geo'}
|
|
163
|
+
style={styles}
|
|
164
|
+
path={path}
|
|
165
|
+
stroke={geoStrokeColor}
|
|
166
|
+
strokeWidth={strokeWidth}
|
|
167
|
+
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
168
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
169
|
+
data-tooltip-html={toolTip}
|
|
170
|
+
tabIndex={-1}
|
|
171
|
+
/>
|
|
159
172
|
)
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -164,7 +177,7 @@ const WorldMap = props => {
|
|
|
164
177
|
})
|
|
165
178
|
|
|
166
179
|
// Cities
|
|
167
|
-
geosJsx.push(<CityList applyLegendToRow={applyLegendToRow} applyTooltipsToGeo={applyTooltipsToGeo} data={data} displayGeoName={displayGeoName} geoClickHandler={geoClickHandler} key='cities' projection={projection} state={state} titleCase={titleCase} />)
|
|
180
|
+
geosJsx.push(<CityList applyLegendToRow={applyLegendToRow} applyTooltipsToGeo={applyTooltipsToGeo} data={data} displayGeoName={displayGeoName} geoClickHandler={geoClickHandler} key='cities' projection={projection} state={state} titleCase={titleCase} tooltipId={tooltipId} />)
|
|
168
181
|
|
|
169
182
|
// Bubbles
|
|
170
183
|
if (state.general.type === 'bubble') {
|
|
@@ -178,6 +191,7 @@ const WorldMap = props => {
|
|
|
178
191
|
applyLegendToRow={applyLegendToRow}
|
|
179
192
|
applyTooltipsToGeo={applyTooltipsToGeo}
|
|
180
193
|
displayGeoName={displayGeoName}
|
|
194
|
+
tooltipId={tooltipId}
|
|
181
195
|
handleCircleClick={country => handleCircleClick(country, state, setState, setRuntimeData, generateRuntimeData)}
|
|
182
196
|
/>
|
|
183
197
|
)
|
package/src/context.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext } from 'react'
|
|
2
|
-
import { MapConfig } from './MapConfig'
|
|
2
|
+
import { MapConfig } from './types/MapConfig'
|
|
3
3
|
|
|
4
4
|
type MapContext = {
|
|
5
5
|
applyLegendToRow
|
|
@@ -42,6 +42,7 @@ type MapContext = {
|
|
|
42
42
|
supportedCountries
|
|
43
43
|
supportedTerritories
|
|
44
44
|
titleCase
|
|
45
|
+
tooltipId: string
|
|
45
46
|
viewport
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -684,6 +684,7 @@ export const supportedCities = {
|
|
|
684
684
|
'VIRGINIA BEACH': [-75.977982, 36.852924],
|
|
685
685
|
'WARREN': [-80.8184, 41.2376],
|
|
686
686
|
'WASHINGTON D.C.': [-77.036873, 38.907192],
|
|
687
|
+
'DC': [-77.036873, 38.907192],
|
|
687
688
|
'WASHINGTON DC.': [-77.036873, 38.907192],
|
|
688
689
|
'WASHINGTON DC': [-77.036873, 38.907192],
|
|
689
690
|
'WICHITA': [-97.330055, 37.687176],
|
|
@@ -16,7 +16,7 @@ import { MapConfig } from '../types/MapConfig'
|
|
|
16
16
|
* 3) Clean (ie. mapshaper -clean) and edit the shape as needed and export the new layer as geoJSON
|
|
17
17
|
* 4) Save the geoJSON somewhere external.
|
|
18
18
|
*/
|
|
19
|
-
export default function useMapLayers(config: MapConfig, setConfig, pathGenerator) {
|
|
19
|
+
export default function useMapLayers(config: MapConfig, setConfig, pathGenerator, tooltipId) {
|
|
20
20
|
const [fetchedTopoJSON, setFetchedTopoJSON] = useState([])
|
|
21
21
|
const geoId = useId()
|
|
22
22
|
|
|
@@ -158,7 +158,7 @@ export default function useMapLayers(config: MapConfig, setConfig, pathGenerator
|
|
|
158
158
|
key={geoId} data-id={geoId}
|
|
159
159
|
stroke={config.map.layers[index].stroke ? config.map.layers[index].stroke : item.properties.stroke ? item.properties.stroke : 'transparent'}
|
|
160
160
|
strokeWidth={config.map.layers[index].strokeWidth ? config.map.layers[index].strokeWidth : item.properties['stroke-width']}
|
|
161
|
-
data-tooltip-id=
|
|
161
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
162
162
|
data-tooltip-html={config.map.layers[index].tooltip ? config.map.layers[index].tooltip : ''}
|
|
163
163
|
/>
|
|
164
164
|
</Group>
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
color: #000;
|
|
83
83
|
font-size: 1em;
|
|
84
84
|
border: 0;
|
|
85
|
-
position:
|
|
85
|
+
position: fixed;
|
|
86
86
|
z-index: 100;
|
|
87
87
|
transition: 0.1s background;
|
|
88
88
|
cursor: pointer;
|
|
@@ -110,17 +110,6 @@
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
.editor-panel {
|
|
113
|
-
background: #fff;
|
|
114
|
-
width: $editorWidth;
|
|
115
|
-
overflow-y: overlay;
|
|
116
|
-
position: absolute;
|
|
117
|
-
z-index: 7;
|
|
118
|
-
display: flex;
|
|
119
|
-
flex-direction: column;
|
|
120
|
-
left: 0;
|
|
121
|
-
top: 0;
|
|
122
|
-
bottom: 0;
|
|
123
|
-
|
|
124
113
|
//TODO: Remove after COVE refactor
|
|
125
114
|
&.cove {
|
|
126
115
|
@import '@cdc/core/styles/v2/layout/tooltip.scss';
|
package/src/scss/main.scss
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
border: 0;
|
|
41
41
|
text-align: left;
|
|
42
42
|
max-width: 100%;
|
|
43
|
+
background-color: white;
|
|
43
44
|
.btn {
|
|
44
45
|
padding: 0.375em 0.75em;
|
|
45
46
|
border-radius: 0.3em;
|
|
@@ -129,7 +130,7 @@
|
|
|
129
130
|
svg {
|
|
130
131
|
display: inline-block;
|
|
131
132
|
max-width: 13px;
|
|
132
|
-
margin-left: .5em;
|
|
133
|
+
margin-left: 0.5em;
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
&.capitalize p {
|