@cdc/map 4.26.2 → 4.26.3
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/LICENSE +201 -0
- package/dist/cdcmap-vr9HZwRt.es.js +6 -0
- package/dist/cdcmap.js +26781 -24615
- package/examples/private/annotation-bug.json +642 -0
- package/package.json +3 -3
- package/src/CdcMap.tsx +3 -14
- package/src/CdcMapComponent.tsx +214 -159
- package/src/_stories/CdcMap.Defaults.stories.tsx +76 -0
- package/src/_stories/CdcMap.Editor.stories.tsx +187 -14
- package/src/_stories/CdcMap.stories.tsx +11 -1
- package/src/_stories/Map.HTMLInDataTable.stories.tsx +385 -0
- 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/EditorPanel/components/EditorPanel.tsx +426 -58
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings-style.css +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +5 -2
- package/src/components/EditorPanel/components/editorPanel.styles.css +34 -24
- package/src/components/Legend/components/Legend.tsx +9 -4
- package/src/components/Legend/components/LegendGroup/legend.group.css +5 -5
- 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/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 +410 -183
- 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 +13 -8
- 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 +14 -5
- package/src/data/legacy-defaults.ts +8 -0
- package/src/data/supported-geos.js +19 -0
- package/src/helpers/colors.ts +2 -1
- package/src/helpers/dataTableHelpers.ts +56 -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/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/useResizeObserver.ts +36 -22
- package/src/hooks/useTooltip.test.tsx +64 -0
- package/src/hooks/useTooltip.ts +28 -8
- 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 +2 -0
- package/src/store/map.reducer.ts +4 -0
- package/src/test/CdcMap.test.jsx +2 -2
- package/src/types/MapConfig.ts +22 -4
- package/src/types/MapContext.ts +3 -1
- package/dist/cdcmap-Cf9_fbQf.es.js +0 -6
- package/src/helpers/componentHelpers.ts +0 -8
package/src/CdcMapComponent.tsx
CHANGED
|
@@ -8,7 +8,7 @@ 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'
|
|
@@ -47,8 +47,8 @@ import { generateRuntimeLegend } from './helpers/generateRuntimeLegend'
|
|
|
47
47
|
import generateRuntimeData from './helpers/generateRuntimeData'
|
|
48
48
|
import { reloadURLData } from './helpers/urlDataHelpers'
|
|
49
49
|
import { observeMapSvgLoaded } from './helpers/mapObserverHelpers'
|
|
50
|
-
import { buildSectionClassNames } from './helpers/componentHelpers'
|
|
51
|
-
import { shouldShowDataTable } from './helpers/dataTableHelpers'
|
|
50
|
+
import { buildBodyWrapClassNames, buildSectionClassNames } from './helpers/componentHelpers'
|
|
51
|
+
import { shouldShowDataTable, filterCountyTableRuntimeDataByStateCode } from './helpers/dataTableHelpers'
|
|
52
52
|
import { prepareSmallMultiplesDataTable } from './helpers/smallMultiplesHelpers'
|
|
53
53
|
|
|
54
54
|
// Child Components
|
|
@@ -67,6 +67,9 @@ import { LegendMemoProvider } from './context/LegendMemoContext'
|
|
|
67
67
|
import { VizFilter } from '@cdc/core/types/VizFilter'
|
|
68
68
|
import { getInitialState, mapReducer } from './store/map.reducer'
|
|
69
69
|
import { RuntimeData } from './types/RuntimeData'
|
|
70
|
+
import defaults from './data/initial-state'
|
|
71
|
+
import { LEGACY_MAP_DEFAULTS } from './data/legacy-defaults'
|
|
72
|
+
import { backfillDefaults } from '@cdc/core/helpers/backfillDefaults'
|
|
70
73
|
import EditorContext from '@cdc/core/contexts/EditorContext'
|
|
71
74
|
import MapActions from './store/map.actions'
|
|
72
75
|
import _ from 'lodash'
|
|
@@ -74,6 +77,8 @@ import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
|
|
|
74
77
|
import useModal from './hooks/useModal'
|
|
75
78
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
76
79
|
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
80
|
+
import { ENABLE_CHART_MAP_TP5_TREATMENT } from '@cdc/core/helpers/constants'
|
|
81
|
+
import CalloutFlag from '@cdc/core/assets/callout-flag.svg?url'
|
|
77
82
|
|
|
78
83
|
type CdcMapComponent = {
|
|
79
84
|
config: MapConfig
|
|
@@ -104,6 +109,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
104
109
|
datasets,
|
|
105
110
|
interactionLabel = 'no link provided'
|
|
106
111
|
}) => {
|
|
112
|
+
backfillDefaults(configObj, defaults, LEGACY_MAP_DEFAULTS)
|
|
107
113
|
const initialState = getInitialState(configObj)
|
|
108
114
|
|
|
109
115
|
const [mapState, dispatch] = useReducer<MapReducerType<MapState, MapActions>>(mapReducer, initialState as MapState)
|
|
@@ -118,6 +124,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
118
124
|
modal,
|
|
119
125
|
accessibleStatus,
|
|
120
126
|
filteredCountryCode,
|
|
127
|
+
filteredStateCode,
|
|
121
128
|
position,
|
|
122
129
|
scale,
|
|
123
130
|
translate,
|
|
@@ -139,7 +146,9 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
useEffect(() => {
|
|
142
|
-
const
|
|
149
|
+
const configClone = cloneConfig(configObj)
|
|
150
|
+
backfillDefaults(configClone, defaults, LEGACY_MAP_DEFAULTS)
|
|
151
|
+
const _newConfig = getInitialState(configClone).config
|
|
143
152
|
if (configObj.data) {
|
|
144
153
|
_newConfig.data = configObj.data
|
|
145
154
|
}
|
|
@@ -157,7 +166,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
// Refs
|
|
160
|
-
const innerContainerRef = useRef()
|
|
169
|
+
const innerContainerRef = useRef<HTMLDivElement | null>(null)
|
|
161
170
|
const legendRef = useRef(null)
|
|
162
171
|
const mapSvg = useRef(null)
|
|
163
172
|
const tooltipRef = useRef(null)
|
|
@@ -290,7 +299,12 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
290
299
|
// Combine viz filters with dashboard filters for markup processing
|
|
291
300
|
const combinedFilters = [...(config.filters || []), ...(config.dashboardFilters || [])]
|
|
292
301
|
|
|
293
|
-
const markupOptions = {
|
|
302
|
+
const markupOptions = {
|
|
303
|
+
isEditor,
|
|
304
|
+
filters: combinedFilters,
|
|
305
|
+
locale: config.locale,
|
|
306
|
+
dataMetadata: config.dataMetadata
|
|
307
|
+
}
|
|
294
308
|
|
|
295
309
|
if (title) {
|
|
296
310
|
title = processMarkupVariables(title, config.data || [], config.markupVariables, markupOptions).processedContent
|
|
@@ -334,6 +348,17 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
334
348
|
}
|
|
335
349
|
|
|
336
350
|
if (!table.label || table.label === '') table.label = 'Data Table'
|
|
351
|
+
const isTp5Treatment = ENABLE_CHART_MAP_TP5_TREATMENT && config.visual?.tp5Treatment
|
|
352
|
+
const mapTitle = (
|
|
353
|
+
<Title
|
|
354
|
+
title={title}
|
|
355
|
+
superTitle={processedSuperTitle}
|
|
356
|
+
titleStyle={isTp5Treatment ? 'small' : general.titleStyle}
|
|
357
|
+
showTitle={general.showTitle}
|
|
358
|
+
config={config}
|
|
359
|
+
classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${headerColor}`]}
|
|
360
|
+
/>
|
|
361
|
+
)
|
|
337
362
|
|
|
338
363
|
const mapProps = {
|
|
339
364
|
setParentConfig,
|
|
@@ -344,6 +369,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
344
369
|
customNavigationHandler,
|
|
345
370
|
dimensions,
|
|
346
371
|
filteredCountryCode,
|
|
372
|
+
filteredStateCode,
|
|
347
373
|
isDashboard,
|
|
348
374
|
isEditor,
|
|
349
375
|
logo,
|
|
@@ -355,6 +381,7 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
355
381
|
runtimeLegend,
|
|
356
382
|
scale,
|
|
357
383
|
setConfig,
|
|
384
|
+
setFilteredStateCode: (stateCode: string) => dispatch({ type: 'SET_FILTERED_STATE_CODE', payload: stateCode }),
|
|
358
385
|
setSharedFilter,
|
|
359
386
|
setSharedFilterValue,
|
|
360
387
|
config,
|
|
@@ -368,6 +395,34 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
368
395
|
interactionLabel
|
|
369
396
|
}
|
|
370
397
|
|
|
398
|
+
// Memoize data table preparation and county filtering to avoid recomputing on unrelated renders.
|
|
399
|
+
const { dataTableConfig, dataTableColumns, dataTableRuntimeData } = useMemo(() => {
|
|
400
|
+
let preparedConfig = config
|
|
401
|
+
let preparedColumns = columns
|
|
402
|
+
let preparedRuntimeData = runtimeData
|
|
403
|
+
|
|
404
|
+
if (config.smallMultiples?.mode) {
|
|
405
|
+
const prepared = prepareSmallMultiplesDataTable(config, columns, runtimeData)
|
|
406
|
+
preparedConfig = prepared.config
|
|
407
|
+
preparedColumns = prepared.columns
|
|
408
|
+
preparedRuntimeData = prepared.runtimeData
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (config.general.geoType === 'us-county' && filteredStateCode) {
|
|
412
|
+
preparedRuntimeData = filterCountyTableRuntimeDataByStateCode(
|
|
413
|
+
preparedRuntimeData,
|
|
414
|
+
filteredStateCode,
|
|
415
|
+
preparedConfig
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
dataTableConfig: preparedConfig,
|
|
421
|
+
dataTableColumns: preparedColumns,
|
|
422
|
+
dataTableRuntimeData: preparedRuntimeData
|
|
423
|
+
}
|
|
424
|
+
}, [config, columns, runtimeData, filteredStateCode])
|
|
425
|
+
|
|
371
426
|
if (!config.data) return <></>
|
|
372
427
|
|
|
373
428
|
const tabId = handleMapTabbing(config, loading, legendId)
|
|
@@ -393,61 +448,45 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
393
448
|
</a>
|
|
394
449
|
)
|
|
395
450
|
|
|
396
|
-
// Prepare data table props (pivot if small multiples mode is enabled)
|
|
397
|
-
let dataTableConfig = config
|
|
398
|
-
let dataTableColumns = columns
|
|
399
|
-
let dataTableRuntimeData = runtimeData
|
|
400
|
-
if (config.smallMultiples?.mode) {
|
|
401
|
-
const prepared = prepareSmallMultiplesDataTable(config, columns, runtimeData)
|
|
402
|
-
dataTableConfig = prepared.config
|
|
403
|
-
dataTableColumns = prepared.columns
|
|
404
|
-
dataTableRuntimeData = prepared.runtimeData
|
|
405
|
-
}
|
|
406
|
-
|
|
407
451
|
return (
|
|
408
452
|
<LegendMemoProvider legendMemo={legendMemo} legendSpecialClassLastMemo={legendSpecialClassLastMemo}>
|
|
409
453
|
<ConfigContext.Provider value={mapProps}>
|
|
410
454
|
<MapDispatchContext.Provider value={dispatch}>
|
|
411
|
-
<
|
|
455
|
+
<VisualizationContainer
|
|
412
456
|
config={config}
|
|
413
457
|
isEditor={isEditor}
|
|
414
458
|
ref={outerContainerRef}
|
|
415
459
|
currentViewport={currentViewport}
|
|
416
460
|
imageId={imageId}
|
|
417
|
-
|
|
461
|
+
editorPanel={<EditorPanel datasets={datasets} />}
|
|
418
462
|
>
|
|
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 && (
|
|
463
|
+
{requiredColumns?.length > 0 && (
|
|
464
|
+
<Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
|
|
465
|
+
)}
|
|
466
|
+
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
467
|
+
<VisualizationContent
|
|
468
|
+
innerClassName={[
|
|
469
|
+
'cdc-map-inner-container',
|
|
470
|
+
currentViewport,
|
|
471
|
+
config?.runtime?.editorErrorMessage.length > 0 ? 'type-map--has-error' : ''
|
|
472
|
+
]
|
|
473
|
+
.filter(Boolean)
|
|
474
|
+
.join(' ')}
|
|
475
|
+
innerProps={{ 'aria-label': 'Map: ' + title, ref: innerContainerRef }}
|
|
476
|
+
bodyWrapClassName={isTp5Treatment ? 'cdc-callout d-flex flex-column' : ''}
|
|
477
|
+
bodyClassName={[
|
|
478
|
+
!config.visual?.border || isTp5Treatment ? 'no-borders' : '',
|
|
479
|
+
config.visual?.border && !isTp5Treatment ? 'component--has-legacy-border' : '',
|
|
480
|
+
config.visual?.borderColorTheme ? 'component--has-border-color-theme' : '',
|
|
481
|
+
config.visual?.accent ? 'component--has-accent' : '',
|
|
482
|
+
config.visual?.background ? 'component--has-background' : '',
|
|
483
|
+
config.visual?.hideBackgroundColor ? 'component--hide-background-color' : '',
|
|
484
|
+
isTp5Treatment ? 'component--tp5-treatment' : ''
|
|
485
|
+
]
|
|
486
|
+
.filter(Boolean)
|
|
487
|
+
.join(' ')}
|
|
488
|
+
filters={
|
|
489
|
+
config?.filters?.length > 0 ? (
|
|
451
490
|
<Filters
|
|
452
491
|
config={config}
|
|
453
492
|
setConfig={setConfig}
|
|
@@ -457,12 +496,100 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
457
496
|
standaloneMap={!config}
|
|
458
497
|
interactionLabel={interactionLabel}
|
|
459
498
|
/>
|
|
499
|
+
) : undefined
|
|
500
|
+
}
|
|
501
|
+
bodySubtext={processedSubtext.length > 0 ? <p className='subtext'>{parse(processedSubtext)}</p> : null}
|
|
502
|
+
bodyFooter={
|
|
503
|
+
<>
|
|
504
|
+
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink
|
|
505
|
+
? tableLink
|
|
506
|
+
: link && link}
|
|
507
|
+
|
|
508
|
+
{shouldShowDataTable(config, table, general, loading) ? (
|
|
509
|
+
<DataTable
|
|
510
|
+
columns={dataTableColumns}
|
|
511
|
+
config={dataTableConfig}
|
|
512
|
+
currentViewport={currentViewport}
|
|
513
|
+
displayGeoName={displayGeoName}
|
|
514
|
+
expandDataTable={table.expanded}
|
|
515
|
+
formatLegendLocation={key =>
|
|
516
|
+
formatLegendLocation(key, dataTableRuntimeData?.[key]?.[config.columns.geo.name])
|
|
517
|
+
}
|
|
518
|
+
imageRef={imageId}
|
|
519
|
+
indexTitle={table.indexLabel}
|
|
520
|
+
innerContainerRef={innerContainerRef}
|
|
521
|
+
legendMemo={legendMemo}
|
|
522
|
+
legendSpecialClassLastMemo={legendSpecialClassLastMemo}
|
|
523
|
+
navigationHandler={navigationHandler}
|
|
524
|
+
outerContainerRef={outerContainerRef}
|
|
525
|
+
rawData={dataTableConfig.data}
|
|
526
|
+
runtimeData={dataTableRuntimeData}
|
|
527
|
+
runtimeLegend={runtimeLegend}
|
|
528
|
+
showDownloadImgButton={showDownloadImgButton}
|
|
529
|
+
showDownloadPdfButton={showDownloadPdfButton}
|
|
530
|
+
includeContextInDownload={config.general?.includeContextInDownload}
|
|
531
|
+
tabbingId={tabId}
|
|
532
|
+
tableTitle={table.label}
|
|
533
|
+
vizTitle={general.title}
|
|
534
|
+
applyLegendToRow={applyLegendToRow}
|
|
535
|
+
getPatternForRow={getPatternForRow}
|
|
536
|
+
wrapColumns={table.wrapColumns}
|
|
537
|
+
hasSubtextAbove={processedSubtext.length > 0}
|
|
538
|
+
interactionLabel={interactionLabel}
|
|
539
|
+
/>
|
|
540
|
+
) : (
|
|
541
|
+
(showDownloadImgButton || showDownloadPdfButton) && (
|
|
542
|
+
<div className='w-100 d-flex justify-content-end'>
|
|
543
|
+
<MediaControls.Section classes={['download-links', 'mt-4', 'mb-2']}>
|
|
544
|
+
{showDownloadImgButton && (
|
|
545
|
+
<MediaControls.DownloadLink
|
|
546
|
+
type='image'
|
|
547
|
+
title='Download Map as Image'
|
|
548
|
+
state={config}
|
|
549
|
+
elementToCapture={imageId}
|
|
550
|
+
interactionLabel={interactionLabel}
|
|
551
|
+
includeContextInDownload={config.general?.includeContextInDownload}
|
|
552
|
+
/>
|
|
553
|
+
)}
|
|
554
|
+
{showDownloadPdfButton && (
|
|
555
|
+
<MediaControls.DownloadLink
|
|
556
|
+
type='pdf'
|
|
557
|
+
title='Download Map as PDF'
|
|
558
|
+
state={config}
|
|
559
|
+
elementToCapture={imageId}
|
|
560
|
+
interactionLabel={interactionLabel}
|
|
561
|
+
includeContextInDownload={config.general?.includeContextInDownload}
|
|
562
|
+
/>
|
|
563
|
+
)}
|
|
564
|
+
</MediaControls.Section>
|
|
565
|
+
</div>
|
|
566
|
+
)
|
|
567
|
+
)}
|
|
568
|
+
|
|
569
|
+
{config.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
570
|
+
|
|
571
|
+
{processedFootnotes && (
|
|
572
|
+
<section className='footnotes pt-2 mt-4'>{parse(processedFootnotes)}</section>
|
|
573
|
+
)}
|
|
574
|
+
</>
|
|
575
|
+
}
|
|
576
|
+
header={isTp5Treatment ? null : mapTitle}
|
|
577
|
+
messageIsIntroText={!!processedIntroText}
|
|
578
|
+
message={processedIntroText ? parse(processedIntroText) : null}
|
|
579
|
+
>
|
|
580
|
+
<>
|
|
581
|
+
{isTp5Treatment && <img src={CalloutFlag} alt='' className='cdc-callout__flag' aria-hidden='true' />}
|
|
582
|
+
{isTp5Treatment && mapTitle}
|
|
583
|
+
{config?.runtime?.editorErrorMessage.length > 0 && <Error />}
|
|
584
|
+
<SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
|
|
585
|
+
{config?.annotations?.length > 0 && (
|
|
586
|
+
<SkipTo skipId={tabId} skipMessage={`Skip over annotations`} key={`skip-annotations`} />
|
|
460
587
|
)}
|
|
461
588
|
|
|
462
589
|
<div
|
|
463
590
|
role='region'
|
|
464
591
|
tabIndex={0}
|
|
465
|
-
className={getMapContainerClasses(config, modal).join(' ')}
|
|
592
|
+
className={getMapContainerClasses(config, modal, currentViewport).join(' ')}
|
|
466
593
|
onClick={e => closeModal(e, modal)}
|
|
467
594
|
onKeyDown={e => {
|
|
468
595
|
if (e.key === 'Enter') {
|
|
@@ -502,114 +629,42 @@ const CdcMapComponent: React.FC<CdcMapComponent> = ({
|
|
|
502
629
|
navigationHandler={val => navigationHandler('_blank', val, customNavigationHandler)}
|
|
503
630
|
/>
|
|
504
631
|
)}
|
|
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}
|
|
632
|
+
</>
|
|
633
|
+
</VisualizationContent>
|
|
634
|
+
)}
|
|
635
|
+
|
|
636
|
+
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
637
|
+
{accessibleStatus}
|
|
638
|
+
</div>
|
|
639
|
+
|
|
640
|
+
{!isDraggingAnnotation && 'hover' === tooltips.appearanceType && (
|
|
641
|
+
<ReactTooltip
|
|
642
|
+
id={`tooltip__${tooltipId}`}
|
|
643
|
+
float={true}
|
|
644
|
+
className={`tooltip tooltip-test`}
|
|
645
|
+
style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }}
|
|
610
646
|
/>
|
|
611
|
-
|
|
612
|
-
|
|
647
|
+
)}
|
|
648
|
+
<div
|
|
649
|
+
ref={tooltipRef}
|
|
650
|
+
id={`tooltip__${tooltipId}-canvas`}
|
|
651
|
+
className='tooltip'
|
|
652
|
+
style={{
|
|
653
|
+
background: `rgba(255,255,255,${config.tooltips.opacity / 100})`,
|
|
654
|
+
position: 'absolute',
|
|
655
|
+
whiteSpace: 'nowrap',
|
|
656
|
+
display: 'none' // can't use d-none here
|
|
657
|
+
}}
|
|
658
|
+
></div>
|
|
659
|
+
<FootnotesStandAlone
|
|
660
|
+
config={config.footnotes}
|
|
661
|
+
filters={config.filters?.filter(f => f.filterFootnotes)}
|
|
662
|
+
markupVariables={config.markupVariables}
|
|
663
|
+
enableMarkupVariables={config.enableMarkupVariables}
|
|
664
|
+
data={config.data}
|
|
665
|
+
dataMetadata={config.dataMetadata}
|
|
666
|
+
/>
|
|
667
|
+
</VisualizationContainer>
|
|
613
668
|
</MapDispatchContext.Provider>
|
|
614
669
|
</ConfigContext.Provider>
|
|
615
670
|
</LegendMemoProvider>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { expect } from 'storybook/test'
|
|
3
|
+
import CdcMap from '../CdcMap'
|
|
4
|
+
import EqualNumberMap from './_mock/equal-number.json'
|
|
5
|
+
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
6
|
+
import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof CdcMap> = {
|
|
9
|
+
title: 'Components/Templates/Map/Defaults',
|
|
10
|
+
component: CdcMap
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof CdcMap>
|
|
14
|
+
|
|
15
|
+
const oldConfig = editConfigKeys(EqualNumberMap, [
|
|
16
|
+
{ path: ['legend', 'style'], value: 'circles' },
|
|
17
|
+
{ path: ['legend', 'position'], value: 'side' },
|
|
18
|
+
{ path: ['legend', 'numberOfItems'], value: 3 },
|
|
19
|
+
{ path: ['legend', 'hideBorder'], value: false }
|
|
20
|
+
])
|
|
21
|
+
|
|
22
|
+
const newConfig = editConfigKeys(EqualNumberMap, [
|
|
23
|
+
{ path: ['legend', 'style'], value: 'gradient' },
|
|
24
|
+
{ path: ['legend', 'position'], value: 'top' },
|
|
25
|
+
{ path: ['legend', 'numberOfItems'], value: 5 },
|
|
26
|
+
{ path: ['legend', 'hideBorder'], value: true }
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
export const OldConfig_Preserves_Legacy_Defaults: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
config: oldConfig,
|
|
32
|
+
isEditor: false
|
|
33
|
+
},
|
|
34
|
+
play: async ({ canvasElement }) => {
|
|
35
|
+
await assertVisualizationRendered(canvasElement)
|
|
36
|
+
|
|
37
|
+
await waitForPresence('aside[aria-label="Legend"]', canvasElement)
|
|
38
|
+
await waitForPresence('.legend-container__li', canvasElement)
|
|
39
|
+
const legend = canvasElement.querySelector('aside[aria-label="Legend"]')
|
|
40
|
+
expect(legend).toBeInTheDocument()
|
|
41
|
+
expect(legend?.classList.contains('side')).toBe(true)
|
|
42
|
+
|
|
43
|
+
expect(legend?.classList.contains('no-border')).toBe(false)
|
|
44
|
+
|
|
45
|
+
const legendContainer = canvasElement.querySelector('.legend-container')
|
|
46
|
+
const legendItems = legendContainer?.querySelectorAll('.legend-container__li')
|
|
47
|
+
expect(legendItems?.length).toBeGreaterThan(0)
|
|
48
|
+
|
|
49
|
+
const linearGradient = legendContainer?.querySelector('linearGradient')
|
|
50
|
+
expect(linearGradient).toBeNull()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const NewConfig_Gets_New_Defaults: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
config: newConfig,
|
|
57
|
+
isEditor: false
|
|
58
|
+
},
|
|
59
|
+
play: async ({ canvasElement }) => {
|
|
60
|
+
await assertVisualizationRendered(canvasElement)
|
|
61
|
+
|
|
62
|
+
await waitForPresence('aside[aria-label="Legend"]', canvasElement)
|
|
63
|
+
const legend = canvasElement.querySelector('aside[aria-label="Legend"]')
|
|
64
|
+
expect(legend).toBeInTheDocument()
|
|
65
|
+
expect(legend?.classList.contains('top')).toBe(true)
|
|
66
|
+
|
|
67
|
+
expect(legend?.classList.contains('no-border')).toBe(true)
|
|
68
|
+
|
|
69
|
+
await waitForPresence('linearGradient', canvasElement)
|
|
70
|
+
const legendContainer = canvasElement.querySelector('.legend-container')
|
|
71
|
+
const linearGradient = legendContainer?.querySelector('linearGradient')
|
|
72
|
+
expect(linearGradient).toBeInTheDocument()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default meta
|