@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.
Files changed (65) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcmap-vr9HZwRt.es.js +6 -0
  3. package/dist/cdcmap.js +26781 -24615
  4. package/examples/private/annotation-bug.json +642 -0
  5. package/package.json +3 -3
  6. package/src/CdcMap.tsx +3 -14
  7. package/src/CdcMapComponent.tsx +214 -159
  8. package/src/_stories/CdcMap.Defaults.stories.tsx +76 -0
  9. package/src/_stories/CdcMap.Editor.stories.tsx +187 -14
  10. package/src/_stories/CdcMap.stories.tsx +11 -1
  11. package/src/_stories/Map.HTMLInDataTable.stories.tsx +385 -0
  12. package/src/_stories/_mock/multi-state-show-unselected.json +82 -0
  13. package/src/cdcMapComponent.styles.css +2 -2
  14. package/src/components/Annotation/Annotation.Draggable.styles.css +4 -4
  15. package/src/components/Annotation/AnnotationDropdown.styles.css +1 -1
  16. package/src/components/Annotation/AnnotationList.styles.css +13 -13
  17. package/src/components/EditorPanel/components/EditorPanel.tsx +426 -58
  18. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings-style.css +1 -1
  19. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +5 -2
  20. package/src/components/EditorPanel/components/editorPanel.styles.css +34 -24
  21. package/src/components/Legend/components/Legend.tsx +9 -4
  22. package/src/components/Legend/components/LegendGroup/legend.group.css +5 -5
  23. package/src/components/Legend/components/index.scss +2 -3
  24. package/src/components/NavigationMenu.tsx +2 -1
  25. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  26. package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +32 -17
  27. package/src/components/UsaMap/components/TerritoriesSection.tsx +3 -2
  28. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +13 -8
  29. package/src/components/UsaMap/components/UsaMap.County.tsx +410 -183
  30. package/src/components/UsaMap/components/UsaMap.Region.styles.css +1 -1
  31. package/src/components/UsaMap/components/UsaMap.SingleState.styles.css +2 -2
  32. package/src/components/UsaMap/components/UsaMap.State.tsx +13 -8
  33. package/src/components/WorldMap/WorldMap.tsx +10 -13
  34. package/src/components/WorldMap/data/world-topo-updated.json +1 -0
  35. package/src/components/WorldMap/data/world-topo.json +1 -1
  36. package/src/components/WorldMap/worldMap.styles.css +1 -1
  37. package/src/components/ZoomControls.tsx +49 -18
  38. package/src/components/zoomControls.styles.css +27 -11
  39. package/src/data/initial-state.js +14 -5
  40. package/src/data/legacy-defaults.ts +8 -0
  41. package/src/data/supported-geos.js +19 -0
  42. package/src/helpers/colors.ts +2 -1
  43. package/src/helpers/dataTableHelpers.ts +56 -0
  44. package/src/helpers/displayGeoName.ts +19 -11
  45. package/src/helpers/getMapContainerClasses.ts +8 -2
  46. package/src/helpers/getMatchingPatternForRow.ts +67 -0
  47. package/src/helpers/getPatternForRow.ts +11 -18
  48. package/src/helpers/tests/dataTableHelpers.test.ts +78 -0
  49. package/src/helpers/tests/displayGeoName.test.ts +17 -0
  50. package/src/helpers/tests/getMatchingPatternForRow.test.ts +150 -0
  51. package/src/helpers/tests/getPatternForRow.test.ts +140 -2
  52. package/src/helpers/urlDataHelpers.ts +7 -1
  53. package/src/hooks/useResizeObserver.ts +36 -22
  54. package/src/hooks/useTooltip.test.tsx +64 -0
  55. package/src/hooks/useTooltip.ts +28 -8
  56. package/src/scss/editor-panel.scss +1 -1
  57. package/src/scss/main.scss +140 -6
  58. package/src/scss/map.scss +9 -4
  59. package/src/store/map.actions.ts +2 -0
  60. package/src/store/map.reducer.ts +4 -0
  61. package/src/test/CdcMap.test.jsx +2 -2
  62. package/src/types/MapConfig.ts +22 -4
  63. package/src/types/MapContext.ts +3 -1
  64. package/dist/cdcmap-Cf9_fbQf.es.js +0 -6
  65. package/src/helpers/componentHelpers.ts +0 -8
@@ -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 Layout from '@cdc/core/components/Layout'
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 _newConfig = getInitialState(cloneConfig(configObj)).config
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 = { isEditor, filters: combinedFilters }
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
- <Layout.VisualizationWrapper
455
+ <VisualizationContainer
412
456
  config={config}
413
457
  isEditor={isEditor}
414
458
  ref={outerContainerRef}
415
459
  currentViewport={currentViewport}
416
460
  imageId={imageId}
417
- showEditorPanel={config.showEditorPanel}
461
+ editorPanel={<EditorPanel datasets={datasets} />}
418
462
  >
419
- {isEditor && <EditorPanel datasets={datasets} />}
420
- <Layout.Responsive isEditor={isEditor}>
421
- {requiredColumns?.length > 0 && (
422
- <Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />
423
- )}
424
- {!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
425
- <section
426
- className={buildSectionClassNames(
427
- currentViewport,
428
- headerColor,
429
- config?.runtime?.editorErrorMessage.length > 0
430
- )}
431
- aria-label={'Map: ' + title}
432
- ref={innerContainerRef}
433
- >
434
- {config?.runtime?.editorErrorMessage.length > 0 && <Error />}
435
- <Title
436
- title={title}
437
- superTitle={processedSuperTitle}
438
- titleStyle={general.titleStyle}
439
- showTitle={general.showTitle}
440
- config={config}
441
- classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${headerColor}`]}
442
- />
443
- <SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
444
- {config?.annotations?.length > 0 && (
445
- <SkipTo skipId={tabId} skipMessage={`Skip over annotations`} key={`skip-annotations`} />
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
- {/* Link (to data table?) */}
507
- {isDashboard && config.table?.forceDisplay && config.table.showDataTableLink
508
- ? tableLink
509
- : link && link}
510
-
511
- {processedSubtext.length > 0 && <p className='subtext mt-4'>{parse(processedSubtext)}</p>}
512
-
513
- {/* Data Table or Download Links */}
514
- {shouldShowDataTable(config, table, general, loading) ? (
515
- <DataTable
516
- columns={dataTableColumns}
517
- config={dataTableConfig}
518
- currentViewport={currentViewport}
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
- </Layout.Responsive>
612
- </Layout.VisualizationWrapper>
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