@cdc/map 4.26.2 → 4.26.4
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/CONFIG.md +235 -0
- package/README.md +70 -24
- package/dist/cdcmap-CY9IcPSi.es.js +6 -0
- package/dist/cdcmap-DlpiY3fQ.es.js +4 -0
- package/dist/cdcmap.js +31260 -27946
- package/examples/{testing-layer-2.json → __data__/testing-layer-2.json} +1 -1
- package/examples/{testing-layer.json → __data__/testing-layer.json} +1 -1
- package/examples/county-hsa-toggle.json +51993 -0
- package/examples/custom-map-layers.json +2 -2
- package/examples/default-county.json +3 -3
- package/examples/minimal-example.json +69 -0
- package/examples/private/annotation-bug.json +642 -0
- package/examples/private/css-issue.json +314 -0
- package/examples/private/region-breaking.json +1639 -0
- package/examples/private/test1.json +27247 -0
- package/package.json +4 -4
- package/src/CdcMap.tsx +3 -14
- package/src/CdcMapComponent.tsx +302 -164
- package/src/_stories/CdcMap.Defaults.smoke.stories.tsx +76 -0
- package/src/_stories/CdcMap.Editor.ColumnsSectionTests.stories.tsx +601 -0
- package/src/_stories/CdcMap.Editor.DataTableSectionTests.stories.tsx +404 -0
- package/src/_stories/CdcMap.Editor.FiltersSectionTests.stories.tsx +229 -0
- package/src/_stories/CdcMap.Editor.GeneralSectionTests.stories.tsx +262 -0
- package/src/_stories/CdcMap.Editor.LegendSectionTests.stories.tsx +541 -0
- package/src/_stories/CdcMap.Editor.MultiCountryWorldMapTests.stories.tsx +359 -0
- package/src/_stories/CdcMap.Editor.PatternSettingsSectionTests.stories.tsx +516 -0
- package/src/_stories/CdcMap.Editor.SmallMultiplesSectionTests.stories.tsx +165 -0
- package/src/_stories/CdcMap.Editor.TextAnnotationsSectionTests.stories.tsx +145 -0
- package/src/_stories/CdcMap.Editor.TypeSectionTests.stories.tsx +312 -0
- package/src/_stories/CdcMap.Editor.VisualSectionTests.stories.tsx +359 -0
- package/src/_stories/CdcMap.Editor.ZoomControlsTests.stories.tsx +88 -0
- package/src/_stories/{CdcMap.stories.tsx → CdcMap.smoke.stories.tsx} +23 -1
- package/src/_stories/Map.HTMLInDataTable.stories.tsx +385 -0
- package/src/_stories/_mock/legends/legend-tests.json +3 -3
- package/src/_stories/_mock/multi-state-show-unselected.json +82 -0
- package/src/cdcMapComponent.styles.css +2 -2
- package/src/components/Annotation/Annotation.Draggable.styles.css +4 -4
- package/src/components/Annotation/AnnotationDropdown.styles.css +1 -1
- package/src/components/Annotation/AnnotationList.styles.css +13 -13
- package/src/components/Annotation/AnnotationList.tsx +1 -1
- package/src/components/EditorPanel/components/EditorPanel.tsx +905 -416
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +112 -117
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings-style.css +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +31 -15
- package/src/components/EditorPanel/components/editorPanel.styles.css +55 -25
- package/src/components/Legend/components/Legend.tsx +12 -7
- package/src/components/Legend/components/LegendGroup/legend.group.css +5 -5
- package/src/components/Legend/components/LegendItem.Hex.tsx +4 -2
- package/src/components/Legend/components/index.scss +2 -3
- package/src/components/NavigationMenu.tsx +2 -1
- package/src/components/SmallMultiples/SmallMultiples.css +5 -5
- package/src/components/SmallMultiples/SynchronizedTooltip.tsx +1 -1
- package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +32 -17
- package/src/components/UsaMap/components/TerritoriesSection.tsx +3 -2
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +13 -8
- package/src/components/UsaMap/components/UsaMap.County.tsx +629 -231
- package/src/components/UsaMap/components/UsaMap.Region.styles.css +1 -1
- package/src/components/UsaMap/components/UsaMap.SingleState.styles.css +2 -2
- package/src/components/UsaMap/components/UsaMap.State.tsx +14 -9
- package/src/components/UsaMap/data/cb_2019_us_county_20m.json +75817 -1
- package/src/components/UsaMap/data/hsa_fips_mapping.json +3144 -0
- package/src/components/WorldMap/WorldMap.tsx +10 -13
- package/src/components/WorldMap/data/world-topo-updated.json +1 -0
- package/src/components/WorldMap/data/world-topo.json +1 -1
- package/src/components/WorldMap/worldMap.styles.css +1 -1
- package/src/components/ZoomControls.tsx +49 -18
- package/src/components/zoomControls.styles.css +27 -11
- package/src/data/initial-state.js +15 -5
- package/src/data/legacy-defaults.ts +8 -0
- package/src/data/supported-counties.json +1 -1
- package/src/data/supported-geos.js +19 -0
- package/src/helpers/colors.ts +2 -1
- package/src/helpers/countyTerritories.ts +38 -0
- package/src/helpers/dataTableHelpers.ts +85 -0
- package/src/helpers/displayGeoName.ts +19 -11
- package/src/helpers/getMapContainerClasses.ts +8 -2
- package/src/helpers/getMatchingPatternForRow.ts +67 -0
- package/src/helpers/getPatternForRow.ts +11 -18
- package/src/helpers/tests/countyTerritories.test.ts +87 -0
- package/src/helpers/tests/dataTableHelpers.test.ts +78 -0
- package/src/helpers/tests/displayGeoName.test.ts +17 -0
- package/src/helpers/tests/getMatchingPatternForRow.test.ts +150 -0
- package/src/helpers/tests/getPatternForRow.test.ts +140 -2
- package/src/helpers/urlDataHelpers.ts +7 -1
- package/src/hooks/useApplyTooltipsToGeo.tsx +7 -4
- package/src/hooks/useMapLayers.tsx +1 -1
- package/src/hooks/useResizeObserver.ts +36 -22
- package/src/hooks/useTooltip.test.tsx +64 -0
- package/src/hooks/useTooltip.ts +46 -15
- package/src/scss/editor-panel.scss +1 -1
- package/src/scss/main.scss +140 -6
- package/src/scss/map.scss +9 -4
- package/src/store/map.actions.ts +5 -0
- package/src/store/map.reducer.ts +13 -0
- package/src/test/CdcMap.test.jsx +26 -2
- package/src/types/MapConfig.ts +28 -4
- package/src/types/MapContext.ts +5 -1
- package/topojson-updater/README.txt +1 -1
- package/dist/cdcmap-Cf9_fbQf.es.js +0 -6
- package/examples/__data__/city-state-data.json +0 -668
- package/examples/city-state.json +0 -434
- package/examples/default-world-data.json +0 -1450
- package/examples/new-cities.json +0 -656
- package/src/_stories/CdcMap.Editor.stories.tsx +0 -3475
- package/src/helpers/componentHelpers.ts +0 -8
- package/topojson-updater/package-lock.json +0 -223
- /package/src/_stories/{CdcMap.ColumnWrap.stories.tsx → CdcMap.ColumnWrap.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.DistrictOfColumbia.stories.tsx → CdcMap.DistrictOfColumbia.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.Filters.stories.tsx → CdcMap.Filters.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.Legend.Gradient.stories.tsx → CdcMap.Legend.Gradient.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.Legend.stories.tsx → CdcMap.Legend.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.Patterns.stories.tsx → CdcMap.Patterns.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.SmallMultiples.stories.tsx → CdcMap.SmallMultiples.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.Table.stories.tsx → CdcMap.Table.smoke.stories.tsx} +0 -0
- /package/src/_stories/{CdcMap.ZeroColor.stories.tsx → CdcMap.ZeroColor.smoke.stories.tsx} +0 -0
- /package/src/_stories/{GoogleMap.stories.tsx → GoogleMap.smoke.stories.tsx} +0 -0
- /package/src/_stories/{UsaMap.NoData.stories.tsx → UsaMap.NoData.smoke.stories.tsx} +0 -0
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/map",
|
|
3
|
-
"version": "4.26.
|
|
3
|
+
"version": "4.26.4",
|
|
4
4
|
"description": "React component for visualizing tabular data on a map of the United States or the world.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bugs": "https://github.com/CDCgov/cdc-open-viz/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@cdc/core": "^4.26.
|
|
8
|
+
"@cdc/core": "^4.26.4",
|
|
9
9
|
"@googlemaps/markerclusterer": "^2.5.3",
|
|
10
10
|
"@hello-pangea/dnd": "^16.2.0",
|
|
11
11
|
"@react-google-maps/api": "^2.20.8",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"d3-scale": "^4.0.2",
|
|
25
25
|
"d3-selection": "^3.0.0",
|
|
26
26
|
"d3-zoom": "^3.0.0",
|
|
27
|
-
"dompurify": "^3.
|
|
27
|
+
"dompurify": "^3.4.0",
|
|
28
28
|
"html-react-parser": "^5.2.3",
|
|
29
29
|
"leaflet": "^1.9.4",
|
|
30
30
|
"lodash": "^4.17.23",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"vite-plugin-svgr": "^4.2.0",
|
|
43
43
|
"whatwg-fetch": "^3.6.20"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "432a2d1acab22915fafe793cb9da1f10318ff793",
|
|
46
46
|
"main": "dist/cdcmap",
|
|
47
47
|
"moduleName": "CdcMap",
|
|
48
48
|
"peerDependencies": {
|
package/src/CdcMap.tsx
CHANGED
|
@@ -58,7 +58,8 @@ const CdcMap: React.FC<CdcMapProps> = ({
|
|
|
58
58
|
...configToLoad
|
|
59
59
|
}
|
|
60
60
|
if (newState.dataUrl) {
|
|
61
|
-
let newData = await fetchRemoteData(newState.dataUrl
|
|
61
|
+
let { data: newData, dataMetadata } = await fetchRemoteData(newState.dataUrl)
|
|
62
|
+
newState.dataMetadata = dataMetadata
|
|
62
63
|
|
|
63
64
|
if (newState.vegaConfig) {
|
|
64
65
|
newData = extractCoveData(updateVegaData(newState.vegaConfig, newData))
|
|
@@ -79,18 +80,6 @@ const CdcMap: React.FC<CdcMapProps> = ({
|
|
|
79
80
|
newState.data = transform.developerStandardize(newState.data, newState.dataDescription)
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
Object.keys(newState).forEach(key => {
|
|
83
|
-
if ('object' === typeof newState[key] && false === Array.isArray(newState[key])) {
|
|
84
|
-
if (initialState[key]) {
|
|
85
|
-
Object.keys(initialState[key]).forEach(property => {
|
|
86
|
-
if (undefined === newState[key][property]) {
|
|
87
|
-
newState[key][property] = initialState[key][property]
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
|
|
94
83
|
if (newState.columns.geo.name || newState.columns.geo.fips) {
|
|
95
84
|
addUIDs(newState, newState.columns.geo.name || newState.columns.geo.fips)
|
|
96
85
|
}
|
|
@@ -113,7 +102,7 @@ const CdcMap: React.FC<CdcMapProps> = ({
|
|
|
113
102
|
let _newConfig = cloneConfig(config ?? initialState)
|
|
114
103
|
|
|
115
104
|
if (configUrl) {
|
|
116
|
-
_newConfig = await
|
|
105
|
+
_newConfig = await fetch(configUrl).then(r => r.json())
|
|
117
106
|
}
|
|
118
107
|
if ('object' === typeof _newConfig) {
|
|
119
108
|
loadConfig(_newConfig)
|
package/src/CdcMapComponent.tsx
CHANGED
|
@@ -8,12 +8,13 @@ import 'react-tooltip/dist/react-tooltip.css'
|
|
|
8
8
|
// Core Components
|
|
9
9
|
import DataTable from '@cdc/core/components/DataTable'
|
|
10
10
|
import Filters from '@cdc/core/components/Filters'
|
|
11
|
-
import
|
|
11
|
+
import { VisualizationContainer, VisualizationContent } from '@cdc/core/components/Layout'
|
|
12
12
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
13
13
|
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
14
14
|
import Title from '@cdc/core/components/ui/Title'
|
|
15
15
|
import Waiting from '@cdc/core/components/Waiting'
|
|
16
16
|
import FootnotesStandAlone from '@cdc/core/components/Footnotes/FootnotesStandAlone'
|
|
17
|
+
import { supportedStatesFipsCodes, supportedCounties } from './data/supported-geos'
|
|
17
18
|
|
|
18
19
|
// types
|
|
19
20
|
import { type MapConfig } from './types/MapConfig'
|
|
@@ -24,7 +25,13 @@ import './scss/main.scss'
|
|
|
24
25
|
import './cdcMapComponent.styles.css'
|
|
25
26
|
|
|
26
27
|
// Core Helpers
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
getQueryStringFilterValue,
|
|
30
|
+
isFilterHiddenByQuery,
|
|
31
|
+
removeQueryParam,
|
|
32
|
+
updateQueryParam,
|
|
33
|
+
updateQueryParams
|
|
34
|
+
} from '@cdc/core/helpers/queryStringUtils'
|
|
28
35
|
import { generateRuntimeFilters } from './helpers/generateRuntimeFilters'
|
|
29
36
|
import { type MapReducerType, MapState } from './store/map.reducer'
|
|
30
37
|
import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
|
|
@@ -47,8 +54,7 @@ import { generateRuntimeLegend } from './helpers/generateRuntimeLegend'
|
|
|
47
54
|
import generateRuntimeData from './helpers/generateRuntimeData'
|
|
48
55
|
import { reloadURLData } from './helpers/urlDataHelpers'
|
|
49
56
|
import { observeMapSvgLoaded } from './helpers/mapObserverHelpers'
|
|
50
|
-
import {
|
|
51
|
-
import { shouldShowDataTable } from './helpers/dataTableHelpers'
|
|
57
|
+
import { shouldShowDataTable, filterCountyTableRuntimeDataByStateCode } from './helpers/dataTableHelpers'
|
|
52
58
|
import { prepareSmallMultiplesDataTable } from './helpers/smallMultiplesHelpers'
|
|
53
59
|
|
|
54
60
|
// Child Components
|
|
@@ -66,7 +72,9 @@ import useLegendMemo from './hooks/useLegendMemo'
|
|
|
66
72
|
import { LegendMemoProvider } from './context/LegendMemoContext'
|
|
67
73
|
import { VizFilter } from '@cdc/core/types/VizFilter'
|
|
68
74
|
import { getInitialState, mapReducer } from './store/map.reducer'
|
|
69
|
-
import
|
|
75
|
+
import defaults from './data/initial-state'
|
|
76
|
+
import { LEGACY_MAP_DEFAULTS } from './data/legacy-defaults'
|
|
77
|
+
import { backfillDefaults } from '@cdc/core/helpers/backfillDefaults'
|
|
70
78
|
import EditorContext from '@cdc/core/contexts/EditorContext'
|
|
71
79
|
import MapActions from './store/map.actions'
|
|
72
80
|
import _ from 'lodash'
|
|
@@ -74,6 +82,9 @@ import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
|
|
|
74
82
|
import useModal from './hooks/useModal'
|
|
75
83
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
76
84
|
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
85
|
+
import { ENABLE_CHART_MAP_TP5_TREATMENT } from '@cdc/core/helpers/constants'
|
|
86
|
+
import CalloutFlag from '@cdc/core/assets/callout-flag.svg?url'
|
|
87
|
+
import { useQueryParamsListener } from '@cdc/core/hooks/useQueryParamsListener'
|
|
77
88
|
|
|
78
89
|
type CdcMapComponent = {
|
|
79
90
|
config: MapConfig
|
|
@@ -104,6 +115,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
104
115
|
datasets,
|
|
105
116
|
interactionLabel = 'no link provided'
|
|
106
117
|
}) => {
|
|
118
|
+
backfillDefaults(configObj, defaults, LEGACY_MAP_DEFAULTS)
|
|
107
119
|
const initialState = getInitialState(configObj)
|
|
108
120
|
|
|
109
121
|
const [mapState, dispatch] = useReducer<MapReducerType<MapState, MapActions>>(mapReducer, initialState as MapState)
|
|
@@ -118,6 +130,8 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
118
130
|
modal,
|
|
119
131
|
accessibleStatus,
|
|
120
132
|
filteredCountryCode,
|
|
133
|
+
filteredCountyCode,
|
|
134
|
+
filteredStateCode,
|
|
121
135
|
position,
|
|
122
136
|
scale,
|
|
123
137
|
translate,
|
|
@@ -139,7 +153,9 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
useEffect(() => {
|
|
142
|
-
const
|
|
156
|
+
const configClone = cloneConfig(configObj)
|
|
157
|
+
backfillDefaults(configClone, defaults, LEGACY_MAP_DEFAULTS)
|
|
158
|
+
const _newConfig = getInitialState(configClone).config
|
|
143
159
|
if (configObj.data) {
|
|
144
160
|
_newConfig.data = configObj.data
|
|
145
161
|
}
|
|
@@ -156,8 +172,24 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
156
172
|
}
|
|
157
173
|
}
|
|
158
174
|
|
|
175
|
+
const setFilters = (filters: VizFilter[]) => {
|
|
176
|
+
const filterCopy = _.cloneDeep(filters)
|
|
177
|
+
if (config.general.showStateDropdown) {
|
|
178
|
+
const [stateFilter, countyFilter] = filterCopy.filter(
|
|
179
|
+
f => f.staticFilter && ['state', 'county'].includes(f.columnName)
|
|
180
|
+
)
|
|
181
|
+
const stateCode = (stateFilter?.active as string) || ''
|
|
182
|
+
const countyCode = (countyFilter?.active as string) || ''
|
|
183
|
+
|
|
184
|
+
setFilteredStateCountyCode(stateCode, countyCode)
|
|
185
|
+
if (countyFilter) filterCopy.pop() // remove county filter
|
|
186
|
+
filterCopy.pop() // remove state filter
|
|
187
|
+
}
|
|
188
|
+
_setRuntimeData(filterCopy)
|
|
189
|
+
}
|
|
190
|
+
|
|
159
191
|
// Refs
|
|
160
|
-
const innerContainerRef = useRef()
|
|
192
|
+
const innerContainerRef = useRef<HTMLDivElement | null>(null)
|
|
161
193
|
const legendRef = useRef(null)
|
|
162
194
|
const mapSvg = useRef(null)
|
|
163
195
|
const tooltipRef = useRef(null)
|
|
@@ -290,7 +322,12 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
290
322
|
// Combine viz filters with dashboard filters for markup processing
|
|
291
323
|
const combinedFilters = [...(config.filters || []), ...(config.dashboardFilters || [])]
|
|
292
324
|
|
|
293
|
-
const markupOptions = {
|
|
325
|
+
const markupOptions = {
|
|
326
|
+
isEditor,
|
|
327
|
+
filters: combinedFilters,
|
|
328
|
+
locale: config.locale,
|
|
329
|
+
dataMetadata: config.dataMetadata
|
|
330
|
+
}
|
|
294
331
|
|
|
295
332
|
if (title) {
|
|
296
333
|
title = processMarkupVariables(title, config.data || [], config.markupVariables, markupOptions).processedContent
|
|
@@ -334,6 +371,43 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
334
371
|
}
|
|
335
372
|
|
|
336
373
|
if (!table.label || table.label === '') table.label = 'Data Table'
|
|
374
|
+
const isTp5Treatment = ENABLE_CHART_MAP_TP5_TREATMENT && config.visual?.tp5Treatment
|
|
375
|
+
const mapTitle = (
|
|
376
|
+
<Title
|
|
377
|
+
title={title}
|
|
378
|
+
superTitle={processedSuperTitle}
|
|
379
|
+
titleStyle={isTp5Treatment ? 'small' : general.titleStyle}
|
|
380
|
+
showTitle={general.showTitle}
|
|
381
|
+
config={config}
|
|
382
|
+
classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${headerColor}`]}
|
|
383
|
+
/>
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
const STATE_CODE = 'state-code'
|
|
387
|
+
const COUNTY_CODE = 'county-code'
|
|
388
|
+
const setFilteredStateCountyCode = (stateCode: string, countyCode?: string) => {
|
|
389
|
+
const stateCodePattern = /^\d\d$/
|
|
390
|
+
const normalizedStateCode = stateCodePattern.test(stateCode) ? stateCode : ''
|
|
391
|
+
let _countyCode = ''
|
|
392
|
+
if (countyCode) {
|
|
393
|
+
const countyCodePattern = /^\d{5}$/
|
|
394
|
+
_countyCode = countyCodePattern.test(countyCode) ? countyCode : ''
|
|
395
|
+
}
|
|
396
|
+
if (!normalizedStateCode) {
|
|
397
|
+
updateQueryParams({ [STATE_CODE]: '', [COUNTY_CODE]: '' })
|
|
398
|
+
} else {
|
|
399
|
+
updateQueryParams({ [STATE_CODE]: normalizedStateCode, [COUNTY_CODE]: _countyCode })
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const setFilteredStateCodeFromQuery = ({
|
|
404
|
+
[STATE_CODE]: stateCode,
|
|
405
|
+
[COUNTY_CODE]: countyCode
|
|
406
|
+
}: Record<string, string>) => {
|
|
407
|
+
dispatch({ type: 'SET_FILTERED_STATE_COUNTY_CODE', payload: { stateCode, countyCode } })
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
useQueryParamsListener([STATE_CODE, COUNTY_CODE], setFilteredStateCodeFromQuery)
|
|
337
411
|
|
|
338
412
|
const mapProps = {
|
|
339
413
|
setParentConfig,
|
|
@@ -344,6 +418,8 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
344
418
|
customNavigationHandler,
|
|
345
419
|
dimensions,
|
|
346
420
|
filteredCountryCode,
|
|
421
|
+
filteredCountyCode,
|
|
422
|
+
filteredStateCode,
|
|
347
423
|
isDashboard,
|
|
348
424
|
isEditor,
|
|
349
425
|
logo,
|
|
@@ -355,6 +431,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
355
431
|
runtimeLegend,
|
|
356
432
|
scale,
|
|
357
433
|
setConfig,
|
|
434
|
+
setFilteredStateCountyCode,
|
|
358
435
|
setSharedFilter,
|
|
359
436
|
setSharedFilterValue,
|
|
360
437
|
config,
|
|
@@ -368,6 +445,34 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
368
445
|
interactionLabel
|
|
369
446
|
}
|
|
370
447
|
|
|
448
|
+
// Memoize data table preparation and county filtering to avoid recomputing on unrelated renders.
|
|
449
|
+
const { dataTableConfig, dataTableColumns, dataTableRuntimeData } = useMemo(() => {
|
|
450
|
+
let preparedConfig = config
|
|
451
|
+
let preparedColumns = columns
|
|
452
|
+
let preparedRuntimeData = runtimeData
|
|
453
|
+
|
|
454
|
+
if (config.smallMultiples?.mode) {
|
|
455
|
+
const prepared = prepareSmallMultiplesDataTable(config, columns, runtimeData)
|
|
456
|
+
preparedConfig = prepared.config
|
|
457
|
+
preparedColumns = prepared.columns
|
|
458
|
+
preparedRuntimeData = prepared.runtimeData
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (config.general.geoType === 'us-county' && filteredStateCode) {
|
|
462
|
+
preparedRuntimeData = filterCountyTableRuntimeDataByStateCode(
|
|
463
|
+
preparedRuntimeData,
|
|
464
|
+
filteredStateCode,
|
|
465
|
+
preparedConfig
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
dataTableConfig: preparedConfig,
|
|
471
|
+
dataTableColumns: preparedColumns,
|
|
472
|
+
dataTableRuntimeData: preparedRuntimeData
|
|
473
|
+
}
|
|
474
|
+
}, [config, columns, runtimeData, filteredStateCode])
|
|
475
|
+
|
|
371
476
|
if (!config.data) return <></>
|
|
372
477
|
|
|
373
478
|
const tabId = handleMapTabbing(config, loading, legendId)
|
|
@@ -393,76 +498,181 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
393
498
|
</a>
|
|
394
499
|
)
|
|
395
500
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
501
|
+
const applyStateFilter = (config: MapConfig): MapConfig => {
|
|
502
|
+
if (config.general.showStateDropdown && config.general.geoType === 'us-county') {
|
|
503
|
+
const stateFilter: VizFilter = {
|
|
504
|
+
columnName: 'state',
|
|
505
|
+
label: 'Select Location',
|
|
506
|
+
filterStyle: 'dropdown',
|
|
507
|
+
labels: supportedStatesFipsCodes,
|
|
508
|
+
values: Object.keys(supportedStatesFipsCodes),
|
|
509
|
+
resetLabel: 'United States',
|
|
510
|
+
staticFilter: true,
|
|
511
|
+
active: filteredStateCode
|
|
512
|
+
}
|
|
513
|
+
let countyFilter: VizFilter | undefined
|
|
514
|
+
if (filteredStateCode) {
|
|
515
|
+
const counties = Object.keys(supportedCounties).filter(countyCode => countyCode.startsWith(filteredStateCode))
|
|
516
|
+
countyFilter = {
|
|
517
|
+
columnName: 'county',
|
|
518
|
+
label: 'Select County',
|
|
519
|
+
filterStyle: 'dropdown',
|
|
520
|
+
labels: supportedCounties,
|
|
521
|
+
values: counties,
|
|
522
|
+
resetLabel: 'All Counties',
|
|
523
|
+
staticFilter: true,
|
|
524
|
+
active: filteredCountyCode
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
...config,
|
|
529
|
+
filters: [...(config.filters || []), stateFilter, ...(countyFilter ? [countyFilter] : [])]
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return config
|
|
405
533
|
}
|
|
406
534
|
|
|
407
535
|
return (
|
|
408
536
|
<LegendMemoProvider legendMemo={legendMemo} legendSpecialClassLastMemo={legendSpecialClassLastMemo}>
|
|
409
537
|
<ConfigContext.Provider value={mapProps}>
|
|
410
538
|
<MapDispatchContext.Provider value={dispatch}>
|
|
411
|
-
<
|
|
539
|
+
<VisualizationContainer
|
|
412
540
|
config={config}
|
|
413
541
|
isEditor={isEditor}
|
|
414
542
|
ref={outerContainerRef}
|
|
415
543
|
currentViewport={currentViewport}
|
|
416
544
|
imageId={imageId}
|
|
417
|
-
|
|
545
|
+
editorPanel={<EditorPanel datasets={datasets} />}
|
|
418
546
|
>
|
|
419
|
-
{
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
)}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
)}
|
|
447
|
-
|
|
448
|
-
{processedIntroText && <section className='introText mb-4'>{parse(processedIntroText)}</section>}
|
|
449
|
-
|
|
450
|
-
{config?.filters?.length > 0 && (
|
|
547
|
+
{requiredColumns?.length > 0 && (
|
|
548
|
+
<Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
|
|
549
|
+
)}
|
|
550
|
+
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
551
|
+
<VisualizationContent
|
|
552
|
+
innerClassName={[
|
|
553
|
+
'cdc-map-inner-container',
|
|
554
|
+
currentViewport,
|
|
555
|
+
config?.runtime?.editorErrorMessage.length > 0 ? 'type-map--has-error' : ''
|
|
556
|
+
]
|
|
557
|
+
.filter(Boolean)
|
|
558
|
+
.join(' ')}
|
|
559
|
+
innerProps={{ 'aria-label': 'Map: ' + title, ref: innerContainerRef }}
|
|
560
|
+
bodyWrapClassName={isTp5Treatment ? 'cdc-callout d-flex flex-column' : ''}
|
|
561
|
+
bodyClassName={[
|
|
562
|
+
!config.visual?.border || isTp5Treatment ? 'no-borders' : '',
|
|
563
|
+
config.visual?.border && !isTp5Treatment ? 'component--has-legacy-border' : '',
|
|
564
|
+
config.visual?.borderColorTheme ? 'component--has-border-color-theme' : '',
|
|
565
|
+
config.visual?.accent ? 'component--has-accent' : '',
|
|
566
|
+
config.visual?.background ? 'component--has-background' : '',
|
|
567
|
+
config.visual?.hideBackgroundColor ? 'component--hide-background-color' : '',
|
|
568
|
+
isTp5Treatment ? 'component--tp5-treatment' : ''
|
|
569
|
+
]
|
|
570
|
+
.filter(Boolean)
|
|
571
|
+
.join(' ')}
|
|
572
|
+
filters={
|
|
573
|
+
config?.filters?.length > 0 || config.general.showStateDropdown ? (
|
|
451
574
|
<Filters
|
|
452
|
-
config={config}
|
|
453
|
-
|
|
454
|
-
filteredData={runtimeFilters}
|
|
455
|
-
setFilters={_setRuntimeData}
|
|
575
|
+
config={applyStateFilter(config)}
|
|
576
|
+
setFilters={setFilters}
|
|
456
577
|
dimensions={dimensions}
|
|
457
|
-
standaloneMap={!config}
|
|
458
578
|
interactionLabel={interactionLabel}
|
|
459
579
|
/>
|
|
580
|
+
) : undefined
|
|
581
|
+
}
|
|
582
|
+
bodySubtext={
|
|
583
|
+
processedSubtext.length > 0 ? <p className='subtext cove-prose'>{parse(processedSubtext)}</p> : null
|
|
584
|
+
}
|
|
585
|
+
bodyFooter={
|
|
586
|
+
<>
|
|
587
|
+
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink
|
|
588
|
+
? tableLink
|
|
589
|
+
: link && link}
|
|
590
|
+
|
|
591
|
+
{shouldShowDataTable(config, table, general, loading) ? (
|
|
592
|
+
<DataTable
|
|
593
|
+
columns={dataTableColumns}
|
|
594
|
+
config={dataTableConfig}
|
|
595
|
+
currentViewport={currentViewport}
|
|
596
|
+
displayGeoName={displayGeoName}
|
|
597
|
+
expandDataTable={table.expanded}
|
|
598
|
+
formatLegendLocation={key =>
|
|
599
|
+
formatLegendLocation(key, dataTableRuntimeData?.[key]?.[config.columns.geo.name])
|
|
600
|
+
}
|
|
601
|
+
imageRef={imageId}
|
|
602
|
+
indexTitle={table.indexLabel}
|
|
603
|
+
innerContainerRef={innerContainerRef}
|
|
604
|
+
legendMemo={legendMemo}
|
|
605
|
+
legendSpecialClassLastMemo={legendSpecialClassLastMemo}
|
|
606
|
+
navigationHandler={navigationHandler}
|
|
607
|
+
outerContainerRef={outerContainerRef}
|
|
608
|
+
rawData={dataTableConfig.data}
|
|
609
|
+
runtimeData={dataTableRuntimeData}
|
|
610
|
+
runtimeLegend={runtimeLegend}
|
|
611
|
+
showDownloadImgButton={showDownloadImgButton}
|
|
612
|
+
showDownloadPdfButton={showDownloadPdfButton}
|
|
613
|
+
includeContextInDownload={config.general?.includeContextInDownload}
|
|
614
|
+
tabbingId={tabId}
|
|
615
|
+
tableTitle={table.label}
|
|
616
|
+
vizTitle={general.title}
|
|
617
|
+
applyLegendToRow={applyLegendToRow}
|
|
618
|
+
getPatternForRow={getPatternForRow}
|
|
619
|
+
wrapColumns={table.wrapColumns}
|
|
620
|
+
hasSubtextAbove={processedSubtext.length > 0}
|
|
621
|
+
interactionLabel={interactionLabel}
|
|
622
|
+
/>
|
|
623
|
+
) : (
|
|
624
|
+
(showDownloadImgButton || showDownloadPdfButton) && (
|
|
625
|
+
<div className='w-100 d-flex justify-content-end'>
|
|
626
|
+
<MediaControls.Section classes={['download-links', 'mt-4', 'mb-2']}>
|
|
627
|
+
{showDownloadImgButton && (
|
|
628
|
+
<MediaControls.DownloadLink
|
|
629
|
+
type='image'
|
|
630
|
+
title='Download Map as Image'
|
|
631
|
+
state={config}
|
|
632
|
+
elementToCapture={imageId}
|
|
633
|
+
interactionLabel={interactionLabel}
|
|
634
|
+
includeContextInDownload={config.general?.includeContextInDownload}
|
|
635
|
+
/>
|
|
636
|
+
)}
|
|
637
|
+
{showDownloadPdfButton && (
|
|
638
|
+
<MediaControls.DownloadLink
|
|
639
|
+
type='pdf'
|
|
640
|
+
title='Download Map as PDF'
|
|
641
|
+
state={config}
|
|
642
|
+
elementToCapture={imageId}
|
|
643
|
+
interactionLabel={interactionLabel}
|
|
644
|
+
includeContextInDownload={config.general?.includeContextInDownload}
|
|
645
|
+
/>
|
|
646
|
+
)}
|
|
647
|
+
</MediaControls.Section>
|
|
648
|
+
</div>
|
|
649
|
+
)
|
|
650
|
+
)}
|
|
651
|
+
|
|
652
|
+
{config.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
653
|
+
|
|
654
|
+
{processedFootnotes && (
|
|
655
|
+
<section className='footnotes cove-prose pt-2 mt-4'>{parse(processedFootnotes)}</section>
|
|
656
|
+
)}
|
|
657
|
+
</>
|
|
658
|
+
}
|
|
659
|
+
header={isTp5Treatment ? null : mapTitle}
|
|
660
|
+
messageIsIntroText={!!processedIntroText}
|
|
661
|
+
message={processedIntroText ? <div className='cove-prose'>{parse(processedIntroText)}</div> : null}
|
|
662
|
+
>
|
|
663
|
+
<>
|
|
664
|
+
{isTp5Treatment && <img src={CalloutFlag} alt='' className='cdc-callout__flag' aria-hidden='true' />}
|
|
665
|
+
{isTp5Treatment && mapTitle}
|
|
666
|
+
{config?.runtime?.editorErrorMessage.length > 0 && <Error />}
|
|
667
|
+
<SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
|
|
668
|
+
{config?.annotations?.length > 0 && (
|
|
669
|
+
<SkipTo skipId={tabId} skipMessage={`Skip over annotations`} key={`skip-annotations`} />
|
|
460
670
|
)}
|
|
461
671
|
|
|
462
672
|
<div
|
|
463
673
|
role='region'
|
|
464
674
|
tabIndex={0}
|
|
465
|
-
className={getMapContainerClasses(config, modal).join(' ')}
|
|
675
|
+
className={getMapContainerClasses(config, modal, currentViewport).join(' ')}
|
|
466
676
|
onClick={e => closeModal(e, modal)}
|
|
467
677
|
onKeyDown={e => {
|
|
468
678
|
if (e.key === 'Enter') {
|
|
@@ -502,114 +712,42 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
502
712
|
navigationHandler={val => navigationHandler('_blank', val, customNavigationHandler)}
|
|
503
713
|
/>
|
|
504
714
|
)}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
displayGeoName={displayGeoName}
|
|
520
|
-
expandDataTable={table.expanded}
|
|
521
|
-
formatLegendLocation={key =>
|
|
522
|
-
formatLegendLocation(key, dataTableRuntimeData?.[key]?.[config.columns.geo.name])
|
|
523
|
-
}
|
|
524
|
-
headerColor={general.headerColor}
|
|
525
|
-
imageRef={imageId}
|
|
526
|
-
indexTitle={table.indexLabel}
|
|
527
|
-
innerContainerRef={innerContainerRef}
|
|
528
|
-
legendMemo={legendMemo}
|
|
529
|
-
legendSpecialClassLastMemo={legendSpecialClassLastMemo}
|
|
530
|
-
navigationHandler={navigationHandler}
|
|
531
|
-
outerContainerRef={outerContainerRef}
|
|
532
|
-
rawData={dataTableConfig.data}
|
|
533
|
-
runtimeData={dataTableRuntimeData}
|
|
534
|
-
runtimeLegend={runtimeLegend}
|
|
535
|
-
showDownloadImgButton={showDownloadImgButton}
|
|
536
|
-
showDownloadPdfButton={showDownloadPdfButton}
|
|
537
|
-
includeContextInDownload={config.general?.includeContextInDownload}
|
|
538
|
-
tabbingId={tabId}
|
|
539
|
-
tableTitle={table.label}
|
|
540
|
-
vizTitle={general.title}
|
|
541
|
-
applyLegendToRow={applyLegendToRow}
|
|
542
|
-
getPatternForRow={getPatternForRow}
|
|
543
|
-
wrapColumns={table.wrapColumns}
|
|
544
|
-
interactionLabel={interactionLabel}
|
|
545
|
-
/>
|
|
546
|
-
) : (
|
|
547
|
-
(showDownloadImgButton || showDownloadPdfButton) && (
|
|
548
|
-
<div className='w-100 d-flex justify-content-end'>
|
|
549
|
-
<MediaControls.Section classes={['download-links', 'mt-4', 'mb-2']}>
|
|
550
|
-
{showDownloadImgButton && (
|
|
551
|
-
<MediaControls.DownloadLink
|
|
552
|
-
type='image'
|
|
553
|
-
title='Download Map as Image'
|
|
554
|
-
state={config}
|
|
555
|
-
elementToCapture={imageId}
|
|
556
|
-
interactionLabel={interactionLabel}
|
|
557
|
-
includeContextInDownload={config.general?.includeContextInDownload}
|
|
558
|
-
/>
|
|
559
|
-
)}
|
|
560
|
-
{showDownloadPdfButton && (
|
|
561
|
-
<MediaControls.DownloadLink
|
|
562
|
-
type='pdf'
|
|
563
|
-
title='Download Map as PDF'
|
|
564
|
-
state={config}
|
|
565
|
-
elementToCapture={imageId}
|
|
566
|
-
interactionLabel={interactionLabel}
|
|
567
|
-
includeContextInDownload={config.general?.includeContextInDownload}
|
|
568
|
-
/>
|
|
569
|
-
)}
|
|
570
|
-
</MediaControls.Section>
|
|
571
|
-
</div>
|
|
572
|
-
)
|
|
573
|
-
)}
|
|
574
|
-
|
|
575
|
-
{config.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
576
|
-
|
|
577
|
-
{processedFootnotes && <section className='footnotes pt-2 mt-4'>{parse(processedFootnotes)}</section>}
|
|
578
|
-
</section>
|
|
579
|
-
)}
|
|
580
|
-
|
|
581
|
-
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
582
|
-
{accessibleStatus}
|
|
583
|
-
</div>
|
|
584
|
-
|
|
585
|
-
{!isDraggingAnnotation && 'hover' === tooltips.appearanceType && (
|
|
586
|
-
<ReactTooltip
|
|
587
|
-
id={`tooltip__${tooltipId}`}
|
|
588
|
-
float={true}
|
|
589
|
-
className={`tooltip tooltip-test`}
|
|
590
|
-
style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }}
|
|
591
|
-
/>
|
|
592
|
-
)}
|
|
593
|
-
<div
|
|
594
|
-
ref={tooltipRef}
|
|
595
|
-
id={`tooltip__${tooltipId}-canvas`}
|
|
596
|
-
className='tooltip'
|
|
597
|
-
style={{
|
|
598
|
-
background: `rgba(255,255,255,${config.tooltips.opacity / 100})`,
|
|
599
|
-
position: 'absolute',
|
|
600
|
-
whiteSpace: 'nowrap',
|
|
601
|
-
display: 'none' // can't use d-none here
|
|
602
|
-
}}
|
|
603
|
-
></div>
|
|
604
|
-
<FootnotesStandAlone
|
|
605
|
-
config={config.footnotes}
|
|
606
|
-
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
607
|
-
markupVariables={config.markupVariables}
|
|
608
|
-
enableMarkupVariables={config.enableMarkupVariables}
|
|
609
|
-
data={config.data}
|
|
715
|
+
</>
|
|
716
|
+
</VisualizationContent>
|
|
717
|
+
)}
|
|
718
|
+
|
|
719
|
+
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
720
|
+
{accessibleStatus}
|
|
721
|
+
</div>
|
|
722
|
+
|
|
723
|
+
{!isDraggingAnnotation && 'hover' === tooltips.appearanceType && (
|
|
724
|
+
<ReactTooltip
|
|
725
|
+
id={`tooltip__${tooltipId}`}
|
|
726
|
+
float={true}
|
|
727
|
+
className={`tooltip tooltip-test`}
|
|
728
|
+
style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }}
|
|
610
729
|
/>
|
|
611
|
-
|
|
612
|
-
|
|
730
|
+
)}
|
|
731
|
+
<div
|
|
732
|
+
ref={tooltipRef}
|
|
733
|
+
id={`tooltip__${tooltipId}-canvas`}
|
|
734
|
+
className='tooltip'
|
|
735
|
+
style={{
|
|
736
|
+
background: `rgba(255,255,255,${config.tooltips.opacity / 100})`,
|
|
737
|
+
position: 'absolute',
|
|
738
|
+
whiteSpace: 'nowrap',
|
|
739
|
+
display: 'none' // can't use d-none here
|
|
740
|
+
}}
|
|
741
|
+
></div>
|
|
742
|
+
<FootnotesStandAlone
|
|
743
|
+
config={config.footnotes}
|
|
744
|
+
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
745
|
+
markupVariables={config.markupVariables}
|
|
746
|
+
enableMarkupVariables={config.enableMarkupVariables}
|
|
747
|
+
data={config.data}
|
|
748
|
+
dataMetadata={config.dataMetadata}
|
|
749
|
+
/>
|
|
750
|
+
</VisualizationContainer>
|
|
613
751
|
</MapDispatchContext.Provider>
|
|
614
752
|
</ConfigContext.Provider>
|
|
615
753
|
</LegendMemoProvider>
|