@cdc/map 4.25.8 → 4.25.10
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/.claude/settings.local.json +30 -0
- package/dist/cdcmap.js +54263 -52600
- package/examples/private/c.json +290 -0
- package/examples/private/canvas-city-hover.json +787 -0
- package/examples/private/d.json +345 -0
- package/examples/private/g.json +1 -0
- package/examples/private/h.json +105911 -0
- package/examples/private/measles-data.json +378 -0
- package/examples/private/measles.json +211 -0
- package/examples/private/north-dakota.json +1132 -0
- package/examples/private/state-with-pattern.json +883 -0
- package/index.html +35 -34
- package/package.json +26 -5
- package/src/CdcMap.tsx +23 -8
- package/src/CdcMapComponent.tsx +215 -309
- package/src/_stories/CdcMap.Filters.stories.tsx +2 -2
- package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +3 -3
- package/src/_stories/CdcMap.Legend.stories.tsx +7 -4
- package/src/_stories/CdcMap.Patterns.stories.tsx +2 -2
- package/src/_stories/CdcMap.Table.stories.tsx +2 -2
- package/src/_stories/CdcMap.stories.tsx +15 -5
- package/src/_stories/GoogleMap.stories.tsx +2 -2
- package/src/_stories/UsaMap.NoData.stories.tsx +2 -2
- package/src/_stories/_mock/equal-number.json +1109 -0
- package/src/_stories/_mock/us-bubble-cities.json +306 -0
- package/src/components/BubbleList.tsx +16 -12
- package/src/components/CityList.tsx +85 -107
- package/src/components/DataTable.tsx +37 -9
- package/src/components/EditorPanel/components/EditorPanel.tsx +177 -165
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +3 -2
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +7 -5
- package/src/components/Geo.tsx +2 -0
- package/src/components/Legend/components/Legend.tsx +109 -73
- package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +10 -7
- package/src/components/MapContainer.tsx +52 -0
- package/src/components/MapControls.tsx +44 -0
- package/src/components/NavigationMenu.tsx +11 -2
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +24 -7
- package/src/components/UsaMap/components/UsaMap.County.tsx +111 -37
- package/src/components/UsaMap/components/UsaMap.Region.tsx +23 -5
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +6 -6
- package/src/components/UsaMap/components/UsaMap.State.tsx +28 -10
- package/src/components/UsaMap/helpers/map.ts +2 -2
- package/src/components/WorldMap/WorldMap.tsx +113 -25
- package/src/components/ZoomControls.tsx +6 -9
- package/src/context/LegendMemoContext.tsx +30 -0
- package/src/context.ts +1 -40
- package/src/data/initial-state.js +143 -130
- package/src/data/supported-geos.js +17 -2
- package/src/helpers/applyColorToLegend.ts +116 -20
- package/src/helpers/applyLegendToRow.ts +10 -6
- package/src/helpers/componentHelpers.ts +8 -0
- package/src/helpers/constants.ts +12 -0
- package/src/helpers/dataTableHelpers.ts +6 -0
- package/src/helpers/displayGeoName.ts +1 -1
- package/src/helpers/generateRuntimeLegend.ts +44 -8
- package/src/helpers/generateRuntimeLegendHash.ts +4 -2
- package/src/helpers/getColumnNames.ts +1 -1
- package/src/helpers/getPatternForRow.ts +36 -0
- package/src/helpers/getStatesPicked.ts +8 -5
- package/src/helpers/index.ts +11 -3
- package/src/helpers/isLegendItemDisabled.ts +16 -0
- package/src/helpers/mapObserverHelpers.ts +40 -0
- package/src/helpers/resetLegendToggles.ts +3 -2
- package/src/helpers/toggleLegendActive.ts +6 -11
- package/src/helpers/urlDataHelpers.ts +70 -0
- package/src/hooks/useGeoClickHandler.ts +35 -1
- package/src/hooks/useLegendMemo.ts +17 -0
- package/src/hooks/useMapLayers.tsx +5 -4
- package/src/hooks/useStateZoom.tsx +25 -6
- package/src/hooks/useTooltip.ts +1 -2
- package/src/index.jsx +0 -2
- package/src/store/map.reducer.ts +17 -6
- package/src/test/CdcMap.test.jsx +11 -0
- package/src/types/MapConfig.ts +23 -14
- package/src/types/MapContext.ts +0 -7
- package/src/types/runtimeLegend.ts +17 -1
- package/vite.config.js +2 -7
- package/vitest.config.ts +16 -0
- package/src/coreStyles_map.scss +0 -3
- package/src/helpers/colorDistributions.ts +0 -12
- package/src/helpers/generateColorsArray.ts +0 -14
- package/src/helpers/tests/generateColorsArray.test.ts +0 -18
- package/src/helpers/tests/generateRuntimeLegendHash.test.ts +0 -11
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//TODO: Move legends to core
|
|
2
|
-
import { forwardRef, useContext } from 'react'
|
|
2
|
+
import { forwardRef, useContext, useMemo } from 'react'
|
|
3
3
|
import parse from 'html-react-parser'
|
|
4
|
+
import { processMarkupVariables } from '@cdc/core/helpers/markupProcessor'
|
|
4
5
|
|
|
5
6
|
//types
|
|
6
7
|
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
@@ -20,11 +21,12 @@ import './index.scss'
|
|
|
20
21
|
import { type ViewPort } from '@cdc/core/types/ViewPort'
|
|
21
22
|
import { isBelowBreakpoint, isMobileFontViewport } from '@cdc/core/helpers/viewports'
|
|
22
23
|
import { displayDataAsText } from '@cdc/core/helpers/displayDataAsText'
|
|
23
|
-
import { toggleLegendActive } from '
|
|
24
|
+
import { toggleLegendActive } from '../../../helpers/toggleLegendActive'
|
|
24
25
|
import { resetLegendToggles } from '../../../helpers'
|
|
25
26
|
import { MapContext } from '../../../types/MapContext'
|
|
26
27
|
import LegendGroup from './LegendGroup/Legend.Group'
|
|
27
28
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
29
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
28
30
|
|
|
29
31
|
const LEGEND_PADDING = 30
|
|
30
32
|
|
|
@@ -45,8 +47,7 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
45
47
|
dimensions,
|
|
46
48
|
mapId,
|
|
47
49
|
runtimeFilters,
|
|
48
|
-
runtimeLegend
|
|
49
|
-
setRuntimeLegend
|
|
50
|
+
runtimeLegend
|
|
50
51
|
} = useContext<MapContext>(ConfigContext)
|
|
51
52
|
|
|
52
53
|
const dispatch = useContext(MapDispatchContext)
|
|
@@ -61,7 +62,10 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
61
62
|
|
|
62
63
|
const getFormattedLegendItems = () => {
|
|
63
64
|
try {
|
|
64
|
-
if (!runtimeLegend.items)
|
|
65
|
+
if (!runtimeLegend.items) {
|
|
66
|
+
console.warn('No runtime legend data available')
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
65
69
|
return runtimeLegend.items.map((entry, idx) => {
|
|
66
70
|
const entryMax = displayDataAsText(entry.max, 'primary', config)
|
|
67
71
|
|
|
@@ -120,34 +124,36 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
120
124
|
return classes.join(' ')
|
|
121
125
|
}
|
|
122
126
|
|
|
123
|
-
const setAccessibleStatus = (message: string) => {
|
|
124
|
-
dispatch({ type: 'SET_ACCESSIBLE_STATUS', payload: message })
|
|
125
|
-
}
|
|
126
|
-
|
|
127
127
|
return (
|
|
128
128
|
<li
|
|
129
129
|
className={handleListItemClass()}
|
|
130
130
|
key={idx}
|
|
131
131
|
title={`Legend item ${item.label} - Click to disable`}
|
|
132
132
|
onClick={() => {
|
|
133
|
-
toggleLegendActive(idx, item.label, runtimeLegend,
|
|
134
|
-
publishAnalyticsEvent(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
'
|
|
139
|
-
|
|
133
|
+
toggleLegendActive(idx, item.label, runtimeLegend, dispatch)
|
|
134
|
+
publishAnalyticsEvent({
|
|
135
|
+
vizType: config.type,
|
|
136
|
+
vizSubType: getVizSubType(config),
|
|
137
|
+
eventType: `map_legend_item_toggled`,
|
|
138
|
+
eventAction: 'click',
|
|
139
|
+
eventLabel: `${interactionLabel}`,
|
|
140
|
+
vizTitle: getVizTitle(config),
|
|
141
|
+
specifics: `mode: isolate, label: ${item.label}`
|
|
142
|
+
})
|
|
140
143
|
}}
|
|
141
144
|
onKeyDown={e => {
|
|
142
145
|
if (e.key === 'Enter') {
|
|
143
146
|
e.preventDefault()
|
|
144
|
-
toggleLegendActive(idx, item.label, runtimeLegend,
|
|
145
|
-
publishAnalyticsEvent(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
'
|
|
150
|
-
|
|
147
|
+
toggleLegendActive(idx, item.label, runtimeLegend, dispatch)
|
|
148
|
+
publishAnalyticsEvent({
|
|
149
|
+
vizType: config.type,
|
|
150
|
+
vizSubType: getVizSubType(config),
|
|
151
|
+
eventType: `map_legend_item_toggled`,
|
|
152
|
+
eventAction: 'keydown',
|
|
153
|
+
eventLabel: `${interactionLabel}`,
|
|
154
|
+
vizTitle: getVizTitle(config),
|
|
155
|
+
specifics: `mode: isolate, label: ${item.label}`
|
|
156
|
+
})
|
|
151
157
|
}
|
|
152
158
|
}}
|
|
153
159
|
tabIndex={0}
|
|
@@ -165,9 +171,9 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
165
171
|
const { pattern, dataKey, size } = patternData
|
|
166
172
|
let defaultPatternColor = 'black'
|
|
167
173
|
const sizes = {
|
|
168
|
-
small:
|
|
169
|
-
medium:
|
|
170
|
-
large:
|
|
174
|
+
small: 8,
|
|
175
|
+
medium: 10,
|
|
176
|
+
large: 12
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
const legendSize = 16
|
|
@@ -240,8 +246,15 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
240
246
|
if (e) {
|
|
241
247
|
e.preventDefault()
|
|
242
248
|
}
|
|
243
|
-
publishAnalyticsEvent(
|
|
244
|
-
|
|
249
|
+
publishAnalyticsEvent({
|
|
250
|
+
vizType: config.type,
|
|
251
|
+
vizSubType: getVizSubType(config),
|
|
252
|
+
eventType: 'map_legend_reset',
|
|
253
|
+
eventAction: 'click',
|
|
254
|
+
eventLabel: interactionLabel,
|
|
255
|
+
vizTitle: getVizTitle(config)
|
|
256
|
+
})
|
|
257
|
+
resetLegendToggles(runtimeLegend, dispatch)
|
|
245
258
|
dispatch({
|
|
246
259
|
type: 'SET_ACCESSIBLE_STATUS',
|
|
247
260
|
payload: 'Legend has been reset, please reference the data table to see updated values.'
|
|
@@ -261,14 +274,17 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
261
274
|
/>
|
|
262
275
|
)
|
|
263
276
|
|
|
264
|
-
const cityStyleShapes =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
277
|
+
const cityStyleShapes = useMemo(
|
|
278
|
+
() => ({
|
|
279
|
+
pin: pin,
|
|
280
|
+
circle: <GlyphCircle color='#000' size={150} />,
|
|
281
|
+
square: <GlyphSquare color='#000' size={150} />,
|
|
282
|
+
diamond: <GlyphDiamond color='#000' size={150} />,
|
|
283
|
+
star: <GlyphStar color='#000' size={150} />,
|
|
284
|
+
triangle: <GlyphTriangle color='#000' size={150} />
|
|
285
|
+
}),
|
|
286
|
+
[pin]
|
|
287
|
+
)
|
|
272
288
|
|
|
273
289
|
const shouldRenderLegendList = legendListItems.length > 0 && ['Select Option', ''].includes(config.legend.groupBy)
|
|
274
290
|
|
|
@@ -286,9 +302,29 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
286
302
|
<section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
|
|
287
303
|
{(legend.title || legend.description || legend.dynamicDescription) && (
|
|
288
304
|
<div className='mb-3'>
|
|
289
|
-
{legend.title &&
|
|
305
|
+
{legend.title && (
|
|
306
|
+
<h3 className={legendClasses.title.join(' ') || ''}>
|
|
307
|
+
{parse(
|
|
308
|
+
config.enableMarkupVariables && config.markupVariables?.length > 0
|
|
309
|
+
? processMarkupVariables(legend.title, config.data || [], config.markupVariables, {
|
|
310
|
+
isEditor: false,
|
|
311
|
+
filters: config.filters || []
|
|
312
|
+
}).processedContent
|
|
313
|
+
: legend.title
|
|
314
|
+
)}
|
|
315
|
+
</h3>
|
|
316
|
+
)}
|
|
290
317
|
{legend.dynamicDescription === false && legend.description && (
|
|
291
|
-
<p className={legendClasses.description.join(' ') || ''}>
|
|
318
|
+
<p className={legendClasses.description.join(' ') || ''}>
|
|
319
|
+
{parse(
|
|
320
|
+
config.enableMarkupVariables && config.markupVariables?.length > 0
|
|
321
|
+
? processMarkupVariables(legend.description, config.data || [], config.markupVariables, {
|
|
322
|
+
isEditor: false,
|
|
323
|
+
filters: config.filters || []
|
|
324
|
+
}).processedContent
|
|
325
|
+
: legend.description
|
|
326
|
+
)}
|
|
327
|
+
</p>
|
|
292
328
|
)}
|
|
293
329
|
{legend.dynamicDescription === true &&
|
|
294
330
|
runtimeFilters.map((filter, idx) => {
|
|
@@ -331,41 +367,41 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
331
367
|
|
|
332
368
|
{((config.visual.additionalCityStyles && config.visual.additionalCityStyles.some(c => c.label)) ||
|
|
333
369
|
config.visual.cityStyleLabel) && (
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
370
|
+
<>
|
|
371
|
+
<hr />
|
|
372
|
+
<div className={legendClasses.div.join(' ') || ''}>
|
|
373
|
+
{config.visual.cityStyleLabel && (
|
|
374
|
+
<div>
|
|
375
|
+
<svg>
|
|
376
|
+
<Group
|
|
377
|
+
top={
|
|
378
|
+
config.visual.cityStyle === 'pin' ? 19 : config.visual.cityStyle === 'triangle' ? 13 : 11
|
|
379
|
+
}
|
|
380
|
+
left={10}
|
|
381
|
+
>
|
|
382
|
+
{cityStyleShapes[config.visual.cityStyle.toLowerCase()]}
|
|
383
|
+
</Group>
|
|
384
|
+
</svg>
|
|
385
|
+
<p>{config.visual.cityStyleLabel}</p>
|
|
386
|
+
</div>
|
|
387
|
+
)}
|
|
388
|
+
|
|
389
|
+
{config.visual.additionalCityStyles.map(
|
|
390
|
+
({ shape, label }, index) =>
|
|
391
|
+
label && (
|
|
392
|
+
<div key={`additional-city-style-${index}-${shape}`}>
|
|
393
|
+
<svg>
|
|
394
|
+
<Group top={shape === 'Pin' ? 19 : shape === 'Triangle' ? 13 : 11} left={10}>
|
|
395
|
+
{cityStyleShapes[shape.toLowerCase()]}
|
|
396
|
+
</Group>
|
|
397
|
+
</svg>
|
|
398
|
+
<p>{label}</p>
|
|
399
|
+
</div>
|
|
400
|
+
)
|
|
401
|
+
)}
|
|
402
|
+
</div>
|
|
403
|
+
</>
|
|
404
|
+
)}
|
|
369
405
|
{runtimeLegend.disabledAmt > 0 && (
|
|
370
406
|
<Button className={legendClasses.showAllButton.join(' ')} onClick={handleReset}>
|
|
371
407
|
Show All
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useContext, useMemo } from 'react'
|
|
2
2
|
import './legend.group.css'
|
|
3
3
|
import LegendShape from '@cdc/core/components/LegendShape'
|
|
4
|
-
import { toggleLegendActive } from '
|
|
4
|
+
import { toggleLegendActive } from '../../../../helpers/toggleLegendActive'
|
|
5
5
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
6
6
|
import ConfigContext, { MapDispatchContext } from '../../../../context'
|
|
7
7
|
|
|
@@ -17,7 +17,7 @@ interface GroupedData {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const LegendGroup = ({ legendItems }) => {
|
|
20
|
-
const { runtimeLegend,
|
|
20
|
+
const { runtimeLegend, config } = useContext(ConfigContext)
|
|
21
21
|
const dispatch = useContext(MapDispatchContext)
|
|
22
22
|
const groupLegendItems = (items: LegendItem[], data: object[], groupByKey: string): GroupedData => {
|
|
23
23
|
if (!groupByKey || !data || !items) return {}
|
|
@@ -59,10 +59,13 @@ const LegendGroup = ({ legendItems }) => {
|
|
|
59
59
|
const wasDisabled = runtimeLegend.items.find(i => i.value === item.label)?.disabled
|
|
60
60
|
const delta = wasDisabled ? -1 : 1
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
dispatch({
|
|
63
|
+
type: 'SET_RUNTIME_LEGEND',
|
|
64
|
+
payload: {
|
|
65
|
+
...runtimeLegend,
|
|
66
|
+
items: newItems,
|
|
67
|
+
disabledAmt: (runtimeLegend.disabledAmt ?? 0) + delta
|
|
68
|
+
}
|
|
66
69
|
})
|
|
67
70
|
const message = `${wasDisabled ? 'Enabled' : 'Disabled'} legend item ${
|
|
68
71
|
item.label
|
|
@@ -109,7 +112,7 @@ const LegendGroup = ({ legendItems }) => {
|
|
|
109
112
|
onKeyDown={e => {
|
|
110
113
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
111
114
|
e.preventDefault()
|
|
112
|
-
toggleLegendActive(index, item.label, runtimeLegend,
|
|
115
|
+
toggleLegendActive(index, item.label, runtimeLegend, dispatch)
|
|
113
116
|
}
|
|
114
117
|
}}
|
|
115
118
|
>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Modal from './Modal'
|
|
3
|
+
import UsaMap from './UsaMap'
|
|
4
|
+
import WorldMap from './WorldMap'
|
|
5
|
+
import GoogleMap from './GoogleMap'
|
|
6
|
+
import { MapConfig } from '../types/MapConfig'
|
|
7
|
+
import { LOGO_MAX_WIDTH } from '../helpers/constants'
|
|
8
|
+
|
|
9
|
+
interface MapContainerProps {
|
|
10
|
+
config: MapConfig
|
|
11
|
+
modal: any
|
|
12
|
+
currentViewport: string
|
|
13
|
+
geoType: string
|
|
14
|
+
general: any
|
|
15
|
+
logo: string
|
|
16
|
+
mapSvgRef: React.RefObject<HTMLElement>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MapContainer: React.FC<MapContainerProps> = ({
|
|
20
|
+
config,
|
|
21
|
+
modal,
|
|
22
|
+
currentViewport,
|
|
23
|
+
geoType,
|
|
24
|
+
general,
|
|
25
|
+
logo,
|
|
26
|
+
mapSvgRef
|
|
27
|
+
}) => {
|
|
28
|
+
return (
|
|
29
|
+
<section className='outline-none geography-container w-100 position-relative' ref={mapSvgRef} tabIndex={0}>
|
|
30
|
+
{currentViewport && (
|
|
31
|
+
<>
|
|
32
|
+
{modal && <Modal />}
|
|
33
|
+
{'single-state' === geoType && <UsaMap.SingleState />}
|
|
34
|
+
{'us' === geoType && 'us-geocode' !== config.general.type && <UsaMap.State />}
|
|
35
|
+
{'us-region' === geoType && <UsaMap.Region />}
|
|
36
|
+
{'us-county' === geoType && <UsaMap.County />}
|
|
37
|
+
{'world' === geoType && <WorldMap />}
|
|
38
|
+
{'google-map' === geoType && <GoogleMap />}
|
|
39
|
+
{
|
|
40
|
+
/* logo is handled in UsaMap.State when applicable */
|
|
41
|
+
// prettier-ignore
|
|
42
|
+
'data' === general.type && logo && ('us' !== geoType || 'us-geocode' === general.type) && (
|
|
43
|
+
<img src={logo} alt='' className='map-logo' style={{ maxWidth: LOGO_MAX_WIDTH }} />
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
</>
|
|
47
|
+
)}
|
|
48
|
+
</section>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default MapContainer
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import MediaControls from '@cdc/core/components/MediaControls'
|
|
3
|
+
import { MapConfig } from '../types/MapConfig'
|
|
4
|
+
|
|
5
|
+
interface MapControlsProps {
|
|
6
|
+
config: MapConfig
|
|
7
|
+
imageId: string
|
|
8
|
+
interactionLabel: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const MapControls: React.FC<MapControlsProps> = ({ config, imageId, interactionLabel }) => {
|
|
12
|
+
const { showDownloadImgButton, showDownloadPdfButton } = config.general
|
|
13
|
+
|
|
14
|
+
if (!showDownloadImgButton && !showDownloadPdfButton) {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<MediaControls.Section classes={['download-buttons']}>
|
|
20
|
+
{showDownloadImgButton && (
|
|
21
|
+
<MediaControls.Button
|
|
22
|
+
text='Download Image'
|
|
23
|
+
title='Download Chart as Image'
|
|
24
|
+
type='image'
|
|
25
|
+
state={config}
|
|
26
|
+
elementToCapture={imageId}
|
|
27
|
+
interactionLabel={interactionLabel}
|
|
28
|
+
/>
|
|
29
|
+
)}
|
|
30
|
+
{showDownloadPdfButton && (
|
|
31
|
+
<MediaControls.Button
|
|
32
|
+
text='Download PDF'
|
|
33
|
+
title='Download Chart as PDF'
|
|
34
|
+
type='pdf'
|
|
35
|
+
state={config}
|
|
36
|
+
interactionLabel={interactionLabel}
|
|
37
|
+
elementToCapture={imageId}
|
|
38
|
+
/>
|
|
39
|
+
)}
|
|
40
|
+
</MediaControls.Section>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default MapControls
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useContext, useEffect, useState } from 'react'
|
|
2
2
|
import ConfigContext from '../context'
|
|
3
3
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
4
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
4
5
|
|
|
5
6
|
const NavigationMenu = ({ data, navigationHandler, options, columns, displayGeoName, mapTabbingID }) => {
|
|
6
|
-
const { interactionLabel } = useContext(ConfigContext)
|
|
7
|
+
const { interactionLabel, config } = useContext(ConfigContext)
|
|
7
8
|
const [activeGeo, setActiveGeo] = useState('')
|
|
8
9
|
const [dropdownItems, setDropdownItems] = useState({})
|
|
9
10
|
|
|
@@ -12,7 +13,15 @@ const NavigationMenu = ({ data, navigationHandler, options, columns, displayGeoN
|
|
|
12
13
|
if (activeGeo !== '') {
|
|
13
14
|
const urlString = data[dropdownItems[activeGeo]][columns.navigate.name]
|
|
14
15
|
|
|
15
|
-
publishAnalyticsEvent(
|
|
16
|
+
publishAnalyticsEvent({
|
|
17
|
+
vizType: config.type,
|
|
18
|
+
vizSubType: getVizSubType(config),
|
|
19
|
+
eventType: `map_navigation_menu`,
|
|
20
|
+
eventAction: 'submit',
|
|
21
|
+
eventLabel: `${interactionLabel}`,
|
|
22
|
+
vizTitle: getVizTitle(config),
|
|
23
|
+
specifics: `url: ${urlString}, activeGeo: ${activeGeo}`
|
|
24
|
+
})
|
|
16
25
|
|
|
17
26
|
navigationHandler(urlString)
|
|
18
27
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import React, { useContext } from 'react'
|
|
1
|
+
import React, { useContext, useState } from 'react'
|
|
2
2
|
import ConfigContext from '../../../../context'
|
|
3
|
+
import { useLegendMemoContext } from '../../../../context/LegendMemoContext'
|
|
3
4
|
import { MapContext } from '../../../../types/MapContext'
|
|
4
5
|
import { getGeoFillColor, displayGeoName } from '../../../../helpers'
|
|
5
6
|
import useApplyTooltipsToGeo from '../../../../hooks/useApplyTooltipsToGeo'
|
|
6
7
|
import { applyLegendToRow } from '../../../../helpers/applyLegendToRow'
|
|
7
8
|
import useGeoClickHandler, { geoClickHandler } from '././../../../../hooks/useGeoClickHandler'
|
|
9
|
+
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
10
|
+
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
8
11
|
|
|
9
12
|
interface CountyOutputProps {
|
|
10
13
|
counties: any[]
|
|
@@ -15,14 +18,15 @@ interface CountyOutputProps {
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoStrokeColor, tooltipId }) => {
|
|
18
|
-
const { config,
|
|
21
|
+
const { config, runtimeData, runtimeLegend, interactionLabel } = useContext<MapContext>(ConfigContext)
|
|
22
|
+
const { legendMemo, legendSpecialClassLastMemo } = useLegendMemoContext()
|
|
19
23
|
const { applyTooltipsToGeo } = useApplyTooltipsToGeo()
|
|
20
24
|
const geoFillColor = getGeoFillColor(config)
|
|
21
25
|
const { geoClickHandler } = useGeoClickHandler()
|
|
22
26
|
|
|
23
27
|
return (
|
|
24
28
|
<>
|
|
25
|
-
{counties.map(county => {
|
|
29
|
+
{counties.map((county, countyIndex) => {
|
|
26
30
|
// Map the name from the geo data with the appropriate key for the processed data
|
|
27
31
|
const geoKey = county.id
|
|
28
32
|
|
|
@@ -30,7 +34,7 @@ const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoS
|
|
|
30
34
|
|
|
31
35
|
const countyPath = path(county)
|
|
32
36
|
|
|
33
|
-
const geoData =
|
|
37
|
+
const geoData = runtimeData[county.id]
|
|
34
38
|
let legendColors
|
|
35
39
|
|
|
36
40
|
// Once we receive data for this geographic item, setup variables.
|
|
@@ -68,13 +72,26 @@ const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoS
|
|
|
68
72
|
return (
|
|
69
73
|
<g
|
|
70
74
|
key={`key--${county.id}`}
|
|
71
|
-
className={`county county--${geoDisplayName.split(' ').join('')} county--${
|
|
72
|
-
|
|
73
|
-
}`}
|
|
75
|
+
className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[config.columns.geo.name]
|
|
76
|
+
}`}
|
|
74
77
|
style={styles}
|
|
75
78
|
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
76
79
|
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
77
80
|
data-tooltip-html={toolTip}
|
|
81
|
+
onMouseEnter={() => {
|
|
82
|
+
// Track hover analytics event if this is a new location
|
|
83
|
+
const locationName = geoDisplayName.replace(/[^a-zA-Z0-9]/g, '_')
|
|
84
|
+
publishAnalyticsEvent({
|
|
85
|
+
vizType: config.type,
|
|
86
|
+
vizSubType: getVizSubType(config),
|
|
87
|
+
eventType: `map_hover`,
|
|
88
|
+
eventAction: 'hover',
|
|
89
|
+
eventLabel: interactionLabel,
|
|
90
|
+
vizTitle: getVizTitle(config),
|
|
91
|
+
location: geoDisplayName,
|
|
92
|
+
specifics: `location: ${locationName?.toLowerCase()}`
|
|
93
|
+
})
|
|
94
|
+
}}
|
|
78
95
|
>
|
|
79
96
|
<path
|
|
80
97
|
tabIndex={-1}
|