@cdc/map 4.23.6 → 4.23.8
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 +27677 -26560
- package/examples/default-geocode.json +142 -117
- package/index.html +8 -5
- package/package.json +3 -3
- package/src/CdcMap.jsx +45 -77
- package/src/components/BubbleList.jsx +4 -4
- package/src/components/CountyMap.jsx +58 -12
- package/src/components/EditorPanel.jsx +84 -83
- package/src/components/SingleStateMap.jsx +2 -15
- package/src/components/UsaMap.jsx +11 -4
- package/src/components/UsaRegionMap.jsx +2 -18
- package/src/components/WorldMap.jsx +33 -62
- package/src/data/initial-state.js +7 -2
- package/src/data/supported-geos.js +3 -0
- package/src/hooks/useTooltip.js +135 -0
- package/src/scss/editor-panel.scss +1 -7
- package/src/scss/main.scss +1 -1
- package/src/scss/map.scss +243 -204
- package/src/scss/tooltips.scss +0 -36
|
@@ -372,11 +372,18 @@ const UsaMap = props => {
|
|
|
372
372
|
</AlbersUsa>
|
|
373
373
|
)}
|
|
374
374
|
</svg>
|
|
375
|
+
|
|
375
376
|
{territories.length > 0 && (
|
|
376
|
-
|
|
377
|
-
<
|
|
378
|
-
|
|
379
|
-
|
|
377
|
+
<>
|
|
378
|
+
<div className='two-col'>
|
|
379
|
+
<div>
|
|
380
|
+
<span className='territories-label label'>{state.general.territoriesLabel}</span>
|
|
381
|
+
</div>
|
|
382
|
+
<div>
|
|
383
|
+
<span className={window.visualViewport.width < 500 ? 'territories--mobile' : 'territories'}>{territories}</span>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
</>
|
|
380
387
|
)}
|
|
381
388
|
</ErrorBoundary>
|
|
382
389
|
)
|
|
@@ -103,17 +103,7 @@ const UsaRegionMap = props => {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
return <Shape
|
|
107
|
-
key={label}
|
|
108
|
-
label={label}
|
|
109
|
-
css={styles}
|
|
110
|
-
text={styles.color}
|
|
111
|
-
stroke={geoStrokeColor}
|
|
112
|
-
strokeWidth={1.5}
|
|
113
|
-
onClick={() => geoClickHandler(territory, territoryData)}
|
|
114
|
-
data-tooltip-id="tooltip"
|
|
115
|
-
data-tooltip-html={toolTip}
|
|
116
|
-
/>
|
|
106
|
+
return <Shape key={label} label={label} css={styles} text={styles.color} stroke={geoStrokeColor} strokeWidth={1.5} onClick={() => geoClickHandler(territory, territoryData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} />
|
|
117
107
|
}
|
|
118
108
|
})
|
|
119
109
|
|
|
@@ -220,13 +210,7 @@ const UsaRegionMap = props => {
|
|
|
220
210
|
// const barFill = barPositive ? "#fff" : "#fff";
|
|
221
211
|
|
|
222
212
|
return (
|
|
223
|
-
<g key={key}
|
|
224
|
-
className='geo-group'
|
|
225
|
-
css={styles}
|
|
226
|
-
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
227
|
-
data-tooltip-id="tooltip"
|
|
228
|
-
data-tooltip-html={toolTip}
|
|
229
|
-
>
|
|
213
|
+
<g key={key} className='geo-group' css={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip}>
|
|
230
214
|
<path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1.3} d={path} />
|
|
231
215
|
<g id={`region-${index + 1}-label`}>
|
|
232
216
|
<circle fill='#fff' stroke='#999' cx={circleRadius} cy={circleRadius} r={circleRadius} />
|
|
@@ -16,21 +16,7 @@ const { features: world } = feature(topoJSON, topoJSON.objects.countries)
|
|
|
16
16
|
let projection = geoMercator()
|
|
17
17
|
|
|
18
18
|
const WorldMap = props => {
|
|
19
|
-
const {
|
|
20
|
-
state,
|
|
21
|
-
applyTooltipsToGeo,
|
|
22
|
-
data,
|
|
23
|
-
geoClickHandler,
|
|
24
|
-
applyLegendToRow,
|
|
25
|
-
displayGeoName,
|
|
26
|
-
supportedCountries,
|
|
27
|
-
setState, setRuntimeData,
|
|
28
|
-
generateRuntimeData,
|
|
29
|
-
setFilteredCountryCode,
|
|
30
|
-
position, setPosition,
|
|
31
|
-
hasZoom,
|
|
32
|
-
handleMapAriaLabels,
|
|
33
|
-
titleCase } = props
|
|
19
|
+
const { state, applyTooltipsToGeo, data, geoClickHandler, applyLegendToRow, displayGeoName, supportedCountries, setState, setRuntimeData, generateRuntimeData, setFilteredCountryCode, position, setPosition, hasZoom, handleMapAriaLabels, titleCase } = props
|
|
34
20
|
|
|
35
21
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
36
22
|
// Resets to original data & zooms out
|
|
@@ -54,31 +40,34 @@ const WorldMap = props => {
|
|
|
54
40
|
setPosition(pos => ({ ...pos, zoom: pos.zoom / 1.5 }))
|
|
55
41
|
}
|
|
56
42
|
|
|
57
|
-
const ZoomControls = ({ position, setPosition, state, setState, setRuntimeData, generateRuntimeData }) =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
67
|
-
<line x1='5' y1='12' x2='19' y2='12' />
|
|
68
|
-
</svg>
|
|
69
|
-
</button>
|
|
70
|
-
{state.general.type === 'bubble' && (
|
|
71
|
-
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom and Map Filters'>
|
|
72
|
-
Reset Filters
|
|
43
|
+
const ZoomControls = ({ position, setPosition, state, setState, setRuntimeData, generateRuntimeData }) => {
|
|
44
|
+
if (!state.general.allowMapZoom) return
|
|
45
|
+
return (
|
|
46
|
+
<div className='zoom-controls' data-html2canvas-ignore>
|
|
47
|
+
<button onClick={() => handleZoomIn(position, setPosition)} aria-label='Zoom In'>
|
|
48
|
+
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
49
|
+
<line x1='12' y1='5' x2='12' y2='19' />
|
|
50
|
+
<line x1='5' y1='12' x2='19' y2='12' />
|
|
51
|
+
</svg>
|
|
73
52
|
</button>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
53
|
+
<button onClick={() => handleZoomOut(position, setPosition)} aria-label='Zoom Out'>
|
|
54
|
+
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
55
|
+
<line x1='5' y1='12' x2='19' y2='12' />
|
|
56
|
+
</svg>
|
|
78
57
|
</button>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
58
|
+
{state.general.type === 'bubble' && (
|
|
59
|
+
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom and Map Filters'>
|
|
60
|
+
Reset Filters
|
|
61
|
+
</button>
|
|
62
|
+
)}
|
|
63
|
+
{state.general.type === 'world-geocode' && (
|
|
64
|
+
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom'>
|
|
65
|
+
Reset Zoom
|
|
66
|
+
</button>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
82
71
|
|
|
83
72
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
84
73
|
const handleCircleClick = (country, state, setState, setRuntimeData, generateRuntimeData) => {
|
|
@@ -95,7 +84,7 @@ const WorldMap = props => {
|
|
|
95
84
|
const geosJsx = geographies.map(({ feature: geo, path }, i) => {
|
|
96
85
|
const geoKey = geo.properties.iso
|
|
97
86
|
|
|
98
|
-
if (!geoKey) return null
|
|
87
|
+
if (!geoKey) return null
|
|
99
88
|
|
|
100
89
|
const geoData = data[geoKey]
|
|
101
90
|
|
|
@@ -138,16 +127,7 @@ const WorldMap = props => {
|
|
|
138
127
|
styles.cursor = 'pointer'
|
|
139
128
|
}
|
|
140
129
|
|
|
141
|
-
return <Geo
|
|
142
|
-
key={i + '-geo'}
|
|
143
|
-
css={styles}
|
|
144
|
-
path={path}
|
|
145
|
-
stroke={geoStrokeColor}
|
|
146
|
-
strokeWidth={strokeWidth}
|
|
147
|
-
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
148
|
-
data-tooltip-id="tooltip"
|
|
149
|
-
data-tooltip-html={toolTip}
|
|
150
|
-
/>
|
|
130
|
+
return <Geo key={i + '-geo'} css={styles} path={path} stroke={geoStrokeColor} strokeWidth={strokeWidth} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} />
|
|
151
131
|
}
|
|
152
132
|
|
|
153
133
|
// Default return state, just geo with no additional information
|
|
@@ -156,18 +136,7 @@ const WorldMap = props => {
|
|
|
156
136
|
|
|
157
137
|
// Cities
|
|
158
138
|
geosJsx.push(
|
|
159
|
-
<CityList
|
|
160
|
-
applyLegendToRow={applyLegendToRow}
|
|
161
|
-
applyTooltipsToGeo={applyTooltipsToGeo}
|
|
162
|
-
data={data}
|
|
163
|
-
displayGeoName={displayGeoName}
|
|
164
|
-
geoClickHandler={geoClickHandler}
|
|
165
|
-
isGeoCodeMap={state.general.type === 'world-geocode'}
|
|
166
|
-
key='cities'
|
|
167
|
-
projection={projection}
|
|
168
|
-
state={state}
|
|
169
|
-
titleCase={titleCase}
|
|
170
|
-
/>
|
|
139
|
+
<CityList applyLegendToRow={applyLegendToRow} applyTooltipsToGeo={applyTooltipsToGeo} data={data} displayGeoName={displayGeoName} geoClickHandler={geoClickHandler} isGeoCodeMap={state.general.type === 'world-geocode'} key='cities' projection={projection} state={state} titleCase={titleCase} />
|
|
171
140
|
)
|
|
172
141
|
|
|
173
142
|
// Bubbles
|
|
@@ -206,7 +175,9 @@ const WorldMap = props => {
|
|
|
206
175
|
</ZoomableGroup>
|
|
207
176
|
</svg>
|
|
208
177
|
)}
|
|
209
|
-
{(state.general.type === 'data' || (state.general.type === 'world-geocode' && hasZoom) || (state.general.type === 'bubble' && hasZoom)) &&
|
|
178
|
+
{(state.general.type === 'data' || (state.general.type === 'world-geocode' && hasZoom) || (state.general.type === 'bubble' && hasZoom)) && (
|
|
179
|
+
<ZoomControls position={position} setPosition={setPosition} setRuntimeData={setRuntimeData} state={state} setState={setState} generateRuntimeData={generateRuntimeData} />
|
|
180
|
+
)}
|
|
210
181
|
</ErrorBoundary>
|
|
211
182
|
)
|
|
212
183
|
}
|
|
@@ -22,7 +22,11 @@ export default {
|
|
|
22
22
|
},
|
|
23
23
|
allowMapZoom: true,
|
|
24
24
|
hideGeoColumnInTooltip: false,
|
|
25
|
-
hidePrimaryColumnInTooltip: false
|
|
25
|
+
hidePrimaryColumnInTooltip: false,
|
|
26
|
+
statePicked: {
|
|
27
|
+
fipsCode: '01',
|
|
28
|
+
stateName: 'Alabama'
|
|
29
|
+
}
|
|
26
30
|
},
|
|
27
31
|
type: 'map',
|
|
28
32
|
color: 'pinkpurple',
|
|
@@ -77,7 +81,8 @@ export default {
|
|
|
77
81
|
tooltips: {
|
|
78
82
|
appearanceType: 'hover',
|
|
79
83
|
linkLabel: 'Learn More',
|
|
80
|
-
capitalizeLabels: true
|
|
84
|
+
capitalizeLabels: true,
|
|
85
|
+
opacity: 90
|
|
81
86
|
},
|
|
82
87
|
runtime: {
|
|
83
88
|
editorErrorMessage: []
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const useTooltip = props => {
|
|
2
|
+
const { state, displayGeoName, displayDataAsText, supportedStatesFipsCodes } = props
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* On county maps there's a need to append the state name
|
|
6
|
+
* @param {String} toolTipText - previous tooltip text to build upon
|
|
7
|
+
* @param {Object} row - row of data to lookup fips code with using the geo column.
|
|
8
|
+
* @returns {String} toolTipText - new toolTipText
|
|
9
|
+
*/
|
|
10
|
+
const handleTooltipStateNameColumn = (toolTipText, row) => {
|
|
11
|
+
const { geoType, type, hideGeoColumnInTooltip } = state.general
|
|
12
|
+
if (geoType === 'us-county' && type !== 'us-geocode') {
|
|
13
|
+
let stateFipsCode = row[state.columns.geo.name].substring(0, 2)
|
|
14
|
+
const stateName = supportedStatesFipsCodes[stateFipsCode]
|
|
15
|
+
toolTipText += hideGeoColumnInTooltip ? `<strong>${stateName}</strong><br/>` : `<strong>Location: ${stateName}</strong><br/>`
|
|
16
|
+
}
|
|
17
|
+
return toolTipText
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* On county and state maps, adds the ability to hide the geo column name (prefix)
|
|
22
|
+
* @param {String} geoName - feature name
|
|
23
|
+
* @returns {String} text to be appended to toolTipText
|
|
24
|
+
*/
|
|
25
|
+
const handleTooltipGeoColumn = geoName => {
|
|
26
|
+
const { hideGeoColumnInTooltip } = state.general
|
|
27
|
+
|
|
28
|
+
const handleTooltipPrefix = toolTipText => {
|
|
29
|
+
const { geoType, geoLabelOverride } = state.general
|
|
30
|
+
switch (geoType) {
|
|
31
|
+
case 'us':
|
|
32
|
+
toolTipText = 'State: '
|
|
33
|
+
break
|
|
34
|
+
case 'us-county':
|
|
35
|
+
toolTipText = 'County: '
|
|
36
|
+
break
|
|
37
|
+
case 'single-state':
|
|
38
|
+
toolTipText = 'County: '
|
|
39
|
+
break
|
|
40
|
+
default:
|
|
41
|
+
toolTipText = ''
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (geoLabelOverride) toolTipText = `${geoLabelOverride}: `
|
|
46
|
+
|
|
47
|
+
return toolTipText
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const prefix = handleTooltipPrefix()
|
|
51
|
+
|
|
52
|
+
if (hideGeoColumnInTooltip) return `<strong>${displayGeoName(geoName)}</strong>`
|
|
53
|
+
return `<p class="tooltip-heading">${prefix}${displayGeoName(geoName)}</p>`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Handles special class descriptions in tooltips
|
|
58
|
+
*/
|
|
59
|
+
const handleTooltipSpecialClassText = (specialClasses, column, row, value, columnKey) => {
|
|
60
|
+
if (specialClasses && specialClasses.length && typeof specialClasses[0] === 'object') {
|
|
61
|
+
for (const specialClass of specialClasses) {
|
|
62
|
+
if (column.name === specialClass.key && String(row[specialClass.key]) === specialClass.value) {
|
|
63
|
+
value = displayDataAsText(specialClass.label, columnKey)
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return value
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const handleTooltipPrimaryColumn = (tooltipValue, column) => {
|
|
72
|
+
const { hidePrimaryColumnInTooltip } = state.general
|
|
73
|
+
let tooltipPrefix = column.label?.length > 0 ? column.label : ''
|
|
74
|
+
if (hidePrimaryColumnInTooltip || !tooltipPrefix) return `<li class="tooltip-body">${tooltipValue}</li>`
|
|
75
|
+
return `<li class="tooltip-body">${tooltipPrefix}: ${tooltipValue}</li>`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
* @param {String} toolTipText - previous tooltipText to build upon
|
|
81
|
+
* @param {Object} row - row of data
|
|
82
|
+
* @returns {String} new tooltipText value
|
|
83
|
+
*/
|
|
84
|
+
const handleTooltipColumns = (toolTipText, row) => {
|
|
85
|
+
const tooltipEnabledMaps = ['data', 'bubble', 'us-geocode', 'world-geocode', 'map']
|
|
86
|
+
const {
|
|
87
|
+
general: { type: currentMapType },
|
|
88
|
+
columns,
|
|
89
|
+
legend: { specialClasses }
|
|
90
|
+
} = state
|
|
91
|
+
|
|
92
|
+
if (tooltipEnabledMaps.includes(currentMapType) && undefined !== row) {
|
|
93
|
+
toolTipText += `<ul className="capitalize">`
|
|
94
|
+
|
|
95
|
+
// if tooltips are allowed, loop through each column
|
|
96
|
+
Object.keys(columns).forEach(columnKey => {
|
|
97
|
+
const column = state.columns[columnKey]
|
|
98
|
+
|
|
99
|
+
if (column.tooltip) {
|
|
100
|
+
let tooltipValue = handleTooltipSpecialClassText(specialClasses, column, row, '', columnKey)
|
|
101
|
+
|
|
102
|
+
if (!tooltipValue) {
|
|
103
|
+
tooltipValue = displayDataAsText(row[column.name], columnKey)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
toolTipText += handleTooltipPrimaryColumn(tooltipValue, column)
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
toolTipText += `</ul>`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return toolTipText
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const buildTooltip = (row, geoName, toolTipText = '') => {
|
|
116
|
+
if (!row) return
|
|
117
|
+
|
|
118
|
+
// Handle County Location Columns
|
|
119
|
+
toolTipText += handleTooltipStateNameColumn(toolTipText, row)
|
|
120
|
+
|
|
121
|
+
// Handle Data > Geo Column In tooltips
|
|
122
|
+
toolTipText += handleTooltipGeoColumn(geoName)
|
|
123
|
+
|
|
124
|
+
// Handle Data > Primary Column in tooltips
|
|
125
|
+
toolTipText = handleTooltipColumns(toolTipText, row)
|
|
126
|
+
|
|
127
|
+
return toolTipText
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
buildTooltip
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default useTooltip
|
package/src/scss/main.scss
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@import 'variables';
|
|
4
4
|
@import 'editor-panel';
|
|
5
5
|
@import 'filters';
|
|
6
|
+
@import '@cdc/core/styles/v2/components/ui/tooltip';
|
|
6
7
|
|
|
7
8
|
.cdc-map-outer-container {
|
|
8
9
|
position: relative;
|
|
@@ -20,7 +21,6 @@
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
.cdc-map-inner-container {
|
|
23
|
-
@import './tooltips';
|
|
24
24
|
@import './map';
|
|
25
25
|
@import './sidebar';
|
|
26
26
|
@import './datatable';
|