@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
|
@@ -356,7 +356,7 @@ export const GeneralSectionTests: Story = {
|
|
|
356
356
|
expect(showTitleCheckbox).toBeTruthy()
|
|
357
357
|
|
|
358
358
|
const getTitleVisibility = () => {
|
|
359
|
-
const titleElement = canvasElement.querySelector('.cove-title, header.cove-
|
|
359
|
+
const titleElement = canvasElement.querySelector('.cove-title, header.cove-visualization__header')
|
|
360
360
|
return {
|
|
361
361
|
isPresent: Boolean(titleElement)
|
|
362
362
|
}
|
|
@@ -414,7 +414,7 @@ export const GeneralSectionTests: Story = {
|
|
|
414
414
|
|
|
415
415
|
const getTitleStyleVisual = () => {
|
|
416
416
|
const coveTitleElement = canvasElement.querySelector('.cove-title')
|
|
417
|
-
const legacyTitleElement = canvasElement.querySelector('header.cove-
|
|
417
|
+
const legacyTitleElement = canvasElement.querySelector('header.cove-visualization__header')
|
|
418
418
|
|
|
419
419
|
// For modern titles, check for h2 (large) or h3 (small) elements
|
|
420
420
|
const hasH2 = Boolean(coveTitleElement?.querySelector('h2'))
|
|
@@ -1738,7 +1738,7 @@ export const FiltersSectionTests: Story = {
|
|
|
1738
1738
|
const labelText = labels.join(',')
|
|
1739
1739
|
|
|
1740
1740
|
// Verify filter dropdown is rendered
|
|
1741
|
-
const vizContainer = canvasElement.querySelector('.
|
|
1741
|
+
const vizContainer = canvasElement.querySelector('.cove-visualization')
|
|
1742
1742
|
const filterSelect = Array.from(vizContainer?.querySelectorAll('select') || []).find(select => {
|
|
1743
1743
|
const options = Array.from(select.options).map(opt => opt.value)
|
|
1744
1744
|
return options.includes('Alabama')
|
|
@@ -2189,22 +2189,29 @@ export const VisualSectionTests: StoryObj<typeof CdcMap> = {
|
|
|
2189
2189
|
await performAndAssert(
|
|
2190
2190
|
'Header Theme → Select purple theme',
|
|
2191
2191
|
() => {
|
|
2192
|
-
|
|
2193
|
-
const
|
|
2192
|
+
// Check via the HeaderThemeSelector's selected button state
|
|
2193
|
+
const colorPalettes = canvasElement.querySelectorAll('.color-palette')
|
|
2194
|
+
const themeColorPalette = Array.from(colorPalettes).find(palette =>
|
|
2195
|
+
Array.from(palette.querySelectorAll('button')).some(btn => btn.classList.contains('theme-purple'))
|
|
2196
|
+
)
|
|
2197
|
+
const selectedThemeBtn = themeColorPalette?.querySelector('button.selected') as HTMLElement | null
|
|
2194
2198
|
return {
|
|
2195
|
-
currentTheme:
|
|
2199
|
+
currentTheme: selectedThemeBtn
|
|
2200
|
+
? Array.from(selectedThemeBtn.classList).find(cls => cls.startsWith('theme-')) || ''
|
|
2201
|
+
: ''
|
|
2196
2202
|
}
|
|
2197
2203
|
},
|
|
2198
2204
|
async () => {
|
|
2199
|
-
// Find the
|
|
2200
|
-
const
|
|
2201
|
-
const
|
|
2202
|
-
button.classList.contains('theme-purple')
|
|
2203
|
-
)
|
|
2205
|
+
// Find the header theme selector and click purple
|
|
2206
|
+
const colorPalettes = canvasElement.querySelectorAll('.color-palette')
|
|
2207
|
+
const themeColorPalette = Array.from(colorPalettes).find(palette =>
|
|
2208
|
+
Array.from(palette.querySelectorAll('button')).some(btn => btn.classList.contains('theme-purple'))
|
|
2209
|
+
)
|
|
2210
|
+
const purpleTheme = themeColorPalette?.querySelector('button.theme-purple') as HTMLElement
|
|
2204
2211
|
await userEvent.click(purpleTheme)
|
|
2205
2212
|
},
|
|
2206
2213
|
(before, after) => {
|
|
2207
|
-
// After clicking, the
|
|
2214
|
+
// After clicking, the purple theme button should be selected
|
|
2208
2215
|
return after.currentTheme === 'theme-purple'
|
|
2209
2216
|
}
|
|
2210
2217
|
)
|
|
@@ -2220,7 +2227,7 @@ export const VisualSectionTests: StoryObj<typeof CdcMap> = {
|
|
|
2220
2227
|
await performAndAssert(
|
|
2221
2228
|
'Show Title → Toggle off',
|
|
2222
2229
|
() => {
|
|
2223
|
-
const titleElement = canvasElement.querySelector('.cove-title, header.cove-
|
|
2230
|
+
const titleElement = canvasElement.querySelector('.cove-title, header.cove-visualization__header')
|
|
2224
2231
|
return {
|
|
2225
2232
|
isPresent: Boolean(titleElement)
|
|
2226
2233
|
}
|
|
@@ -2237,7 +2244,7 @@ export const VisualSectionTests: StoryObj<typeof CdcMap> = {
|
|
|
2237
2244
|
await performAndAssert(
|
|
2238
2245
|
'Show Title → Toggle back on',
|
|
2239
2246
|
() => {
|
|
2240
|
-
const titleElement = canvasElement.querySelector('.cove-title, header.cove-
|
|
2247
|
+
const titleElement = canvasElement.querySelector('.cove-title, header.cove-visualization__header')
|
|
2241
2248
|
return {
|
|
2242
2249
|
isPresent: Boolean(titleElement)
|
|
2243
2250
|
}
|
|
@@ -2742,6 +2749,108 @@ export const PatternSettingsSectionTests: Story = {
|
|
|
2742
2749
|
(before, after) => after.patternPathCount > 0
|
|
2743
2750
|
)
|
|
2744
2751
|
|
|
2752
|
+
await performAndAssert(
|
|
2753
|
+
'Pattern Settings → Broad match with blank dataKey (value 55)',
|
|
2754
|
+
() => {
|
|
2755
|
+
const allSvgs = canvasElement.querySelectorAll('svg')
|
|
2756
|
+
let patternPathCount = 0
|
|
2757
|
+
|
|
2758
|
+
allSvgs.forEach(svg => {
|
|
2759
|
+
const allPaths = Array.from(svg.querySelectorAll('path'))
|
|
2760
|
+
patternPathCount += allPaths.filter(path => path.getAttribute('fill')?.startsWith('url(#')).length
|
|
2761
|
+
})
|
|
2762
|
+
|
|
2763
|
+
return { patternPathCount }
|
|
2764
|
+
},
|
|
2765
|
+
async () => {
|
|
2766
|
+
const dataKeySelect = canvasElement.querySelector('select[name^="pattern-dataKey--"]') as HTMLSelectElement
|
|
2767
|
+
const dataValueInput = canvasElement.querySelector('input[id^="pattern-dataValue--"]') as HTMLInputElement
|
|
2768
|
+
if (!dataKeySelect || !dataValueInput) throw new Error('Pattern data controls not found')
|
|
2769
|
+
|
|
2770
|
+
await userEvent.selectOptions(dataKeySelect, '')
|
|
2771
|
+
await userEvent.clear(dataValueInput)
|
|
2772
|
+
await userEvent.type(dataValueInput, '55')
|
|
2773
|
+
await userEvent.tab()
|
|
2774
|
+
},
|
|
2775
|
+
(before, after) => after.patternPathCount > 0
|
|
2776
|
+
)
|
|
2777
|
+
|
|
2778
|
+
await performAndAssert(
|
|
2779
|
+
'Pattern Settings → Specific match beats broad match',
|
|
2780
|
+
() => {
|
|
2781
|
+
const allSvgs = canvasElement.querySelectorAll('svg')
|
|
2782
|
+
const patternTypeById: Record<string, 'lines' | 'circles' | 'waves' | 'unknown'> = {}
|
|
2783
|
+
const appliedRatePatternTypes = new Set<string>()
|
|
2784
|
+
|
|
2785
|
+
allSvgs.forEach(svg => {
|
|
2786
|
+
const patterns = svg.querySelectorAll('pattern')
|
|
2787
|
+
patterns.forEach(pattern => {
|
|
2788
|
+
const patternId = pattern.getAttribute('id')
|
|
2789
|
+
if (!patternId) return
|
|
2790
|
+
|
|
2791
|
+
if (pattern.querySelector('circle')) patternTypeById[patternId] = 'circles'
|
|
2792
|
+
else if (pattern.querySelector('line')) patternTypeById[patternId] = 'lines'
|
|
2793
|
+
else if (pattern.querySelector('path')) patternTypeById[patternId] = 'waves'
|
|
2794
|
+
else patternTypeById[patternId] = 'unknown'
|
|
2795
|
+
})
|
|
2796
|
+
|
|
2797
|
+
const ratePatternNodes = Array.from(svg.querySelectorAll('path.pattern-geoKey--Rate')).filter(node =>
|
|
2798
|
+
node.getAttribute('fill')?.startsWith('url(#')
|
|
2799
|
+
)
|
|
2800
|
+
|
|
2801
|
+
ratePatternNodes.forEach(node => {
|
|
2802
|
+
const fill = node.getAttribute('fill')
|
|
2803
|
+
const match = fill?.match(/^url\(#(.+)\)$/)
|
|
2804
|
+
const patternId = match?.[1]
|
|
2805
|
+
if (!patternId) return
|
|
2806
|
+
appliedRatePatternTypes.add(patternTypeById[patternId] || 'unknown')
|
|
2807
|
+
})
|
|
2808
|
+
})
|
|
2809
|
+
|
|
2810
|
+
return {
|
|
2811
|
+
hasRateCircle: appliedRatePatternTypes.has('circles'),
|
|
2812
|
+
hasRateWave: appliedRatePatternTypes.has('waves')
|
|
2813
|
+
}
|
|
2814
|
+
},
|
|
2815
|
+
async () => {
|
|
2816
|
+
const firstDataKey = canvasElement.querySelector('select[name="pattern-dataKey--0"]') as HTMLSelectElement
|
|
2817
|
+
const firstDataValue = canvasElement.querySelector('input[id="pattern-dataValue--0"]') as HTMLInputElement
|
|
2818
|
+
const firstPatternType = canvasElement.querySelector('select[name="pattern-type--0"]') as HTMLSelectElement
|
|
2819
|
+
if (!firstDataKey || !firstDataValue || !firstPatternType) {
|
|
2820
|
+
throw new Error('First pattern controls not found')
|
|
2821
|
+
}
|
|
2822
|
+
await userEvent.selectOptions(firstDataKey, 'Rate')
|
|
2823
|
+
await userEvent.clear(firstDataValue)
|
|
2824
|
+
await userEvent.type(firstDataValue, '55')
|
|
2825
|
+
await userEvent.selectOptions(firstPatternType, 'circles')
|
|
2826
|
+
|
|
2827
|
+
const buttons = Array.from(canvasElement.querySelectorAll('button'))
|
|
2828
|
+
const addPatternButton = buttons.find(btn => btn.textContent?.includes('Add Geo Pattern'))
|
|
2829
|
+
if (!addPatternButton) throw new Error('Add Geo Pattern button not found')
|
|
2830
|
+
await userEvent.click(addPatternButton)
|
|
2831
|
+
|
|
2832
|
+
const accordionButtons = Array.from(canvasElement.querySelectorAll('.accordion__button'))
|
|
2833
|
+
const selectColumnButtons = accordionButtons.filter(btn => btn.textContent?.includes('Select Column'))
|
|
2834
|
+
const secondPatternAccordionButton = selectColumnButtons[selectColumnButtons.length - 1] as HTMLElement
|
|
2835
|
+
if (!secondPatternAccordionButton) throw new Error('Second pattern accordion not found')
|
|
2836
|
+
await userEvent.click(secondPatternAccordionButton)
|
|
2837
|
+
|
|
2838
|
+
const secondDataKey = canvasElement.querySelector('select[name="pattern-dataKey--1"]') as HTMLSelectElement
|
|
2839
|
+
const secondDataValue = canvasElement.querySelector('input[id="pattern-dataValue--1"]') as HTMLInputElement
|
|
2840
|
+
const secondPatternType = canvasElement.querySelector('select[name="pattern-type--1"]') as HTMLSelectElement
|
|
2841
|
+
|
|
2842
|
+
if (!secondDataKey || !secondDataValue || !secondPatternType) {
|
|
2843
|
+
throw new Error('Second pattern controls not found')
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
await userEvent.selectOptions(secondDataKey, '')
|
|
2847
|
+
await userEvent.clear(secondDataValue)
|
|
2848
|
+
await userEvent.type(secondDataValue, '55')
|
|
2849
|
+
await userEvent.selectOptions(secondPatternType, 'waves')
|
|
2850
|
+
},
|
|
2851
|
+
(before, after) => after.hasRateCircle && !after.hasRateWave
|
|
2852
|
+
)
|
|
2853
|
+
|
|
2745
2854
|
await performAndAssert(
|
|
2746
2855
|
'Pattern Settings → Pattern remains after hover',
|
|
2747
2856
|
() => {
|
|
@@ -3135,6 +3244,70 @@ export const SmallMultiplesSectionTests: Story = {
|
|
|
3135
3244
|
}
|
|
3136
3245
|
}
|
|
3137
3246
|
|
|
3247
|
+
export const WorldDataMapZoomControlsTest: Story = {
|
|
3248
|
+
args: {
|
|
3249
|
+
...DEFAULT_ARGS
|
|
3250
|
+
},
|
|
3251
|
+
play: async ({ canvasElement }) => {
|
|
3252
|
+
const canvas = within(canvasElement)
|
|
3253
|
+
|
|
3254
|
+
await waitForEditor(canvas)
|
|
3255
|
+
await waitForPresence('.map-container', canvasElement)
|
|
3256
|
+
await openAccordion(canvas, 'Type')
|
|
3257
|
+
|
|
3258
|
+
const getZoomControlsState = () => {
|
|
3259
|
+
const zoomControls = canvasElement.querySelector('.zoom-controls')
|
|
3260
|
+
const zoomInButton = canvasElement.querySelector('button[aria-label="Zoom In"]')
|
|
3261
|
+
const zoomOutButton = canvasElement.querySelector('button[aria-label="Zoom Out"]')
|
|
3262
|
+
const mapContainer = canvasElement.querySelector('.map-container')
|
|
3263
|
+
|
|
3264
|
+
return {
|
|
3265
|
+
hasZoomControls: Boolean(zoomControls),
|
|
3266
|
+
hasZoomInButton: Boolean(zoomInButton),
|
|
3267
|
+
hasZoomOutButton: Boolean(zoomOutButton),
|
|
3268
|
+
mapClasses: mapContainer ? Array.from(mapContainer.classList) : []
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
const worldButton = Array.from(canvasElement.querySelectorAll('.geo-buttons button')).find(button =>
|
|
3273
|
+
button.textContent?.trim().toLowerCase().includes('world')
|
|
3274
|
+
) as HTMLButtonElement
|
|
3275
|
+
expect(worldButton).toBeTruthy()
|
|
3276
|
+
|
|
3277
|
+
await performAndAssert(
|
|
3278
|
+
'World Data Map → Switch geo type to world',
|
|
3279
|
+
getZoomControlsState,
|
|
3280
|
+
async () => {
|
|
3281
|
+
await userEvent.click(worldButton)
|
|
3282
|
+
},
|
|
3283
|
+
(before, after) => !before.mapClasses.includes('world') && after.mapClasses.includes('world')
|
|
3284
|
+
)
|
|
3285
|
+
|
|
3286
|
+
const mapTypeSelect = canvas.getByLabelText(/Map Type/i) as HTMLSelectElement
|
|
3287
|
+
await performAndAssert(
|
|
3288
|
+
'World Data Map → Switch type to data',
|
|
3289
|
+
getZoomControlsState,
|
|
3290
|
+
async () => {
|
|
3291
|
+
await userEvent.selectOptions(mapTypeSelect, 'data')
|
|
3292
|
+
},
|
|
3293
|
+
(_before, after) => after.mapClasses.includes('world')
|
|
3294
|
+
)
|
|
3295
|
+
|
|
3296
|
+
const allowMapZoomingCheckbox = canvas.getByLabelText(/Allow Map Zooming/i) as HTMLInputElement
|
|
3297
|
+
expect(allowMapZoomingCheckbox).toBeTruthy()
|
|
3298
|
+
|
|
3299
|
+
await performAndAssert(
|
|
3300
|
+
'World Data Map → Enable map zooming',
|
|
3301
|
+
getZoomControlsState,
|
|
3302
|
+
async () => {
|
|
3303
|
+
await userEvent.click(allowMapZoomingCheckbox)
|
|
3304
|
+
},
|
|
3305
|
+
(before, after) =>
|
|
3306
|
+
!before.hasZoomControls && after.hasZoomControls && after.hasZoomInButton && after.hasZoomOutButton
|
|
3307
|
+
)
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3138
3311
|
// ==========================================================================
|
|
3139
3312
|
// MULTI-COUNTRY MAP TESTS
|
|
3140
3313
|
// ==========================================================================
|
|
@@ -5,6 +5,7 @@ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
|
5
5
|
import EqualNumberOptInExample from './_mock/DEV-7286.json'
|
|
6
6
|
import EqualNumberMap from './_mock/equal-number.json'
|
|
7
7
|
import MultiState from './_mock/multi-state.json'
|
|
8
|
+
import MultiStateShowUnselected from './_mock/multi-state-show-unselected.json'
|
|
8
9
|
import MultiCountry from './_mock/multi-country.json'
|
|
9
10
|
import MultiCountryHide from './_mock/multi-country-hide.json'
|
|
10
11
|
import SingleStateWithFilters from './_mock/DEV-8942.json'
|
|
@@ -42,7 +43,7 @@ const testMapRendering = async (canvasElement: HTMLElement, storyName: string) =
|
|
|
42
43
|
})
|
|
43
44
|
|
|
44
45
|
await step('Verify COVE module wrapper is present', async () => {
|
|
45
|
-
const coveModule = canvasElement.querySelector('.
|
|
46
|
+
const coveModule = canvasElement.querySelector('.cove-visualization')
|
|
46
47
|
expect(coveModule).toBeInTheDocument()
|
|
47
48
|
})
|
|
48
49
|
}
|
|
@@ -149,6 +150,15 @@ export const Multi_State: Story = {
|
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
export const Multi_State_Show_Unselected: Story = {
|
|
154
|
+
args: {
|
|
155
|
+
config: MultiStateShowUnselected
|
|
156
|
+
},
|
|
157
|
+
play: async ({ canvasElement }) => {
|
|
158
|
+
await assertVisualizationRendered(canvasElement)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
152
162
|
export const Multi_Country: Story = {
|
|
153
163
|
args: {
|
|
154
164
|
config: MultiCountry
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import CdcMapComponent from '../CdcMapComponent'
|
|
3
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
4
|
+
import usaStateGradient from './_mock/usa-state-gradient.json'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof CdcMapComponent> = {
|
|
7
|
+
title: 'Components/Templates/Map/HTML in Data Table',
|
|
8
|
+
component: CdcMapComponent,
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'Demonstrates map data tables with colored legend circles rendered inline next to location names, and HTML content in a notes column.'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default meta
|
|
20
|
+
|
|
21
|
+
type Story = StoryObj<typeof CdcMapComponent>
|
|
22
|
+
|
|
23
|
+
const data = [
|
|
24
|
+
{ STATE: 'Overall', Rate: '55', Location: 'Vehicle', URL: 'https://www.cdc.gov', Notes: '' },
|
|
25
|
+
{
|
|
26
|
+
STATE: 'Alabama',
|
|
27
|
+
Rate: 130,
|
|
28
|
+
Location: 'Vehicle',
|
|
29
|
+
URL: 'https://www.cdc.gov/',
|
|
30
|
+
Notes: 'Rate increased <strong>8%</strong> from prior year'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
STATE: 'Alaska',
|
|
34
|
+
Rate: 40,
|
|
35
|
+
Location: 'Work',
|
|
36
|
+
URL: 'https://www.cdc.gov/',
|
|
37
|
+
Notes: 'Remote regions excluded from <em>survey</em>'
|
|
38
|
+
},
|
|
39
|
+
{ STATE: 'American Samoa', Rate: 55, Location: 'Home', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
40
|
+
{
|
|
41
|
+
STATE: 'Arizona',
|
|
42
|
+
Rate: 0,
|
|
43
|
+
Location: 'School',
|
|
44
|
+
URL: 'https://www.cdc.gov/',
|
|
45
|
+
Notes: '<span style="color:#d54309;">⚠ Below target</span> — outreach underway'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
STATE: 'Arkansas',
|
|
49
|
+
Rate: 60,
|
|
50
|
+
Location: 'School',
|
|
51
|
+
URL: 'https://www.cdc.gov/',
|
|
52
|
+
Notes: 'See <a href="https://www.cdc.gov" target="_blank">CDC report</a> for details'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
STATE: 'California',
|
|
56
|
+
Rate: 30,
|
|
57
|
+
Location: 'Home',
|
|
58
|
+
URL: 'https://www.cdc.gov/',
|
|
59
|
+
Notes: '<strong>Leading state</strong> in program participation'
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
STATE: 'Colorado',
|
|
63
|
+
Rate: 40,
|
|
64
|
+
Location: 'Vehicle',
|
|
65
|
+
URL: 'https://www.cdc.gov/',
|
|
66
|
+
Notes: 'Data validated by <em>state health dept</em>'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
STATE: 'Connecticut',
|
|
70
|
+
Rate: 55,
|
|
71
|
+
Location: 'Home',
|
|
72
|
+
URL: 'https://www.cdc.gov/',
|
|
73
|
+
Notes: '<span style="color:#2e8540;">✓ Target achieved</span>'
|
|
74
|
+
},
|
|
75
|
+
{ STATE: 'Deleware', Rate: '57', Location: 'Home', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
76
|
+
{ STATE: 'DC', Rate: 60, Location: 'Home', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
77
|
+
{
|
|
78
|
+
STATE: 'Florida',
|
|
79
|
+
Rate: 30,
|
|
80
|
+
Location: 'Work',
|
|
81
|
+
URL: 'https://www.cdc.gov/',
|
|
82
|
+
Notes: 'Excludes <em>federal facilities</em>'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
STATE: 'Georgia',
|
|
86
|
+
Rate: 40,
|
|
87
|
+
Location: 'Work',
|
|
88
|
+
URL: 'https://www.cdc.gov/',
|
|
89
|
+
Notes: 'Improved <strong>12 points</strong> since 2021'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
STATE: 'Hawaii',
|
|
93
|
+
Rate: 57,
|
|
94
|
+
Location: 'School',
|
|
95
|
+
URL: 'https://www.cdc.gov/',
|
|
96
|
+
Notes: 'Island populations surveyed <em>separately</em>'
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
STATE: 'Idaho',
|
|
100
|
+
Rate: 60,
|
|
101
|
+
Location: 'School',
|
|
102
|
+
URL: 'https://www.cdc.gov/',
|
|
103
|
+
Notes: 'Rural adjustment applied — see <strong>methodology</strong>'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
STATE: 'Illinois',
|
|
107
|
+
Rate: 30,
|
|
108
|
+
Location: 'Work',
|
|
109
|
+
URL: 'https://www.cdc.gov/',
|
|
110
|
+
Notes: 'Chicago metro reported <em>independently</em>'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
STATE: 'Indiana',
|
|
114
|
+
Rate: 40,
|
|
115
|
+
Location: 'Vehicle',
|
|
116
|
+
URL: 'https://www.cdc.gov/',
|
|
117
|
+
Notes: 'Rate includes <strong>all age groups</strong>'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
STATE: 'Iowa',
|
|
121
|
+
Rate: 55,
|
|
122
|
+
Location: 'Home',
|
|
123
|
+
URL: 'https://www.cdc.gov/',
|
|
124
|
+
Notes: 'Consistent with <em>national average</em>'
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
STATE: 'Kansas',
|
|
128
|
+
Rate: 57,
|
|
129
|
+
Location: 'Home',
|
|
130
|
+
URL: 'https://www.cdc.gov/',
|
|
131
|
+
Notes: 'Data from <strong>Q3 2023</strong> survey'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
STATE: 'Kentucky',
|
|
135
|
+
Rate: 60,
|
|
136
|
+
Location: 'NA',
|
|
137
|
+
URL: 'https://www.cdc.gov/',
|
|
138
|
+
Notes: '<span style="color:#d54309;">Below regional median</span>'
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
STATE: 'Louisiana',
|
|
142
|
+
Rate: 30,
|
|
143
|
+
Location: 'Vehicle',
|
|
144
|
+
URL: 'https://www.cdc.gov/',
|
|
145
|
+
Notes: 'Post-storm data collection <em>delayed</em>'
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
STATE: 'Maine',
|
|
149
|
+
Rate: 40,
|
|
150
|
+
Location: 'Work',
|
|
151
|
+
URL: 'https://www.cdc.gov/',
|
|
152
|
+
Notes: 'Coastal counties weighted <strong>separately</strong>'
|
|
153
|
+
},
|
|
154
|
+
{ STATE: 'Marshall Islands', Rate: 55, Location: 'Home', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
155
|
+
{
|
|
156
|
+
STATE: 'Maryland',
|
|
157
|
+
Rate: 57,
|
|
158
|
+
Location: 'School',
|
|
159
|
+
URL: 'https://www.cdc.gov/',
|
|
160
|
+
Notes: 'Includes DC metro area <em>overlap</em>'
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
STATE: 'Massachusetts',
|
|
164
|
+
Rate: 60,
|
|
165
|
+
Location: 'School',
|
|
166
|
+
URL: 'https://www.cdc.gov/',
|
|
167
|
+
Notes: '<span style="color:#2e8540;">✓ Highest in region</span>'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
STATE: 'Michigan',
|
|
171
|
+
Rate: 12,
|
|
172
|
+
Location: 'Work',
|
|
173
|
+
URL: 'https://www.cdc.gov/',
|
|
174
|
+
Notes: '<span style="color:#d54309;">⚠ Significant decline</span> noted'
|
|
175
|
+
},
|
|
176
|
+
{ STATE: 'Micronesia', Rate: 65, Location: 'Vehicle', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
177
|
+
{
|
|
178
|
+
STATE: 'Minnesota',
|
|
179
|
+
Rate: 55,
|
|
180
|
+
Location: 'Home',
|
|
181
|
+
URL: 'https://www.cdc.gov/',
|
|
182
|
+
Notes: 'Tribal nations surveyed <em>separately</em>'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
STATE: 'Mississippi',
|
|
186
|
+
Rate: 57,
|
|
187
|
+
Location: 'Home',
|
|
188
|
+
URL: 'https://www.cdc.gov/',
|
|
189
|
+
Notes: 'Additional outreach <strong>planned for 2024</strong>'
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
STATE: 'Montana',
|
|
193
|
+
Rate: 60,
|
|
194
|
+
Location: 'Home',
|
|
195
|
+
URL: 'https://www.cdc.gov/',
|
|
196
|
+
Notes: 'Large geographic area — <em>sample size limited</em>'
|
|
197
|
+
},
|
|
198
|
+
{ STATE: 'Montana', Rate: 30, Location: 'Vehicle', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
199
|
+
{
|
|
200
|
+
STATE: 'Nebraska',
|
|
201
|
+
Rate: 40,
|
|
202
|
+
Location: 'Work',
|
|
203
|
+
URL: 'https://www.cdc.gov/',
|
|
204
|
+
Notes: 'Rate stable over <strong>3-year period</strong>'
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
STATE: 'Nevada',
|
|
208
|
+
Rate: 55,
|
|
209
|
+
Location: 'Home',
|
|
210
|
+
URL: 'https://www.cdc.gov/',
|
|
211
|
+
Notes: 'Las Vegas metro excluded from <em>rural rate</em>'
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
STATE: 'New Hampshire',
|
|
215
|
+
Rate: 57,
|
|
216
|
+
Location: 'School',
|
|
217
|
+
URL: 'https://www.cdc.gov/',
|
|
218
|
+
Notes: 'Highest participation in <strong>New England</strong>'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
STATE: 'New Jersey',
|
|
222
|
+
Rate: 60,
|
|
223
|
+
Location: 'School',
|
|
224
|
+
URL: 'https://www.cdc.gov/',
|
|
225
|
+
Notes: 'Dense population — <em>high confidence interval</em>'
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
STATE: 'New Mexico',
|
|
229
|
+
Rate: 12,
|
|
230
|
+
Location: 'Work',
|
|
231
|
+
URL: 'https://www.cdc.gov/',
|
|
232
|
+
Notes: '<span style="color:#d54309;">Below target</span> — tribal programs expanding'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
STATE: 'New York',
|
|
236
|
+
Rate: 40,
|
|
237
|
+
Location: 'Vehicle',
|
|
238
|
+
URL: 'https://www.cdc.gov/',
|
|
239
|
+
Notes: 'NYC data reported <em>separately</em>'
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
STATE: 'North Carolina',
|
|
243
|
+
Rate: 55,
|
|
244
|
+
Location: 'Home',
|
|
245
|
+
URL: 'https://www.cdc.gov/',
|
|
246
|
+
Notes: 'Urban/rural split — see <strong>supplemental table</strong>'
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
STATE: 'North Dakota',
|
|
250
|
+
Rate: 57,
|
|
251
|
+
Location: 'Home',
|
|
252
|
+
URL: 'https://www.cdc.gov/',
|
|
253
|
+
Notes: 'Oil field worker population <em>adjusted</em>'
|
|
254
|
+
},
|
|
255
|
+
{ STATE: 'Northern Mariana Islands', Rate: '60', Location: 'Home', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
256
|
+
{
|
|
257
|
+
STATE: 'Ohio',
|
|
258
|
+
Rate: 88,
|
|
259
|
+
Location: 'Vehicle',
|
|
260
|
+
URL: 'https://www.cdc.gov/',
|
|
261
|
+
Notes: '<span style="color:#2e8540;">✓ Above national average</span>'
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
STATE: 'Oklahoma',
|
|
265
|
+
Rate: 40,
|
|
266
|
+
Location: 'Work',
|
|
267
|
+
URL: 'https://www.cdc.gov/',
|
|
268
|
+
Notes: 'Native American populations <em>surveyed separately</em>'
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
STATE: 'Oregon',
|
|
272
|
+
Rate: 55,
|
|
273
|
+
Location: 'Home',
|
|
274
|
+
URL: 'https://www.cdc.gov/',
|
|
275
|
+
Notes: 'Coast vs inland rates <strong>vary significantly</strong>'
|
|
276
|
+
},
|
|
277
|
+
{ STATE: 'Palau', Rate: 15, Location: 'School', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
278
|
+
{
|
|
279
|
+
STATE: 'Pennsylvania',
|
|
280
|
+
Rate: 60,
|
|
281
|
+
Location: 'School',
|
|
282
|
+
URL: 'https://www.cdc.gov/',
|
|
283
|
+
Notes: 'Philadelphia metro weighted <em>separately</em>'
|
|
284
|
+
},
|
|
285
|
+
{ STATE: 'Puerto Rico', Rate: 30, Location: 'Work', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
286
|
+
{
|
|
287
|
+
STATE: 'Rhode Island',
|
|
288
|
+
Rate: 40,
|
|
289
|
+
Location: 'Vehicle',
|
|
290
|
+
URL: 'https://www.cdc.gov/',
|
|
291
|
+
Notes: '<strong>Smallest state</strong> — high confidence in data'
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
STATE: 'South Carolina',
|
|
295
|
+
Rate: 55,
|
|
296
|
+
Location: 'Home',
|
|
297
|
+
URL: 'https://www.cdc.gov/',
|
|
298
|
+
Notes: 'Rate improved after <em>2022 initiative</em>'
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
STATE: 'South Dakota',
|
|
302
|
+
Rate: 86,
|
|
303
|
+
Location: 'Home',
|
|
304
|
+
URL: 'https://www.cdc.gov/',
|
|
305
|
+
Notes: '<span style="color:#2e8540;">✓ Above target</span>'
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
STATE: 'Tennessee',
|
|
309
|
+
Rate: 60,
|
|
310
|
+
Location: 'Home',
|
|
311
|
+
URL: 'https://www.cdc.gov/',
|
|
312
|
+
Notes: 'Nashville metro drives <strong>overall rate</strong>'
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
STATE: 'Texas',
|
|
316
|
+
Rate: 30,
|
|
317
|
+
Location: 'Vehicle',
|
|
318
|
+
URL: 'https://www.cdc.gov/',
|
|
319
|
+
Notes: 'Border region data <em>collected separately</em>'
|
|
320
|
+
},
|
|
321
|
+
{ STATE: 'Utah', Rate: 54, Location: 'Work', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
322
|
+
{
|
|
323
|
+
STATE: 'Vermont',
|
|
324
|
+
Rate: 40,
|
|
325
|
+
Location: 'Home',
|
|
326
|
+
URL: 'https://www.cdc.gov/',
|
|
327
|
+
Notes: 'Near-complete <em>population coverage</em>'
|
|
328
|
+
},
|
|
329
|
+
{ STATE: 'Virgin Islands', Rate: 55, Location: 'School', URL: 'https://www.cdc.gov/', Notes: '' },
|
|
330
|
+
{
|
|
331
|
+
STATE: 'Virginia',
|
|
332
|
+
Rate: 57,
|
|
333
|
+
Location: 'School',
|
|
334
|
+
URL: 'https://www.cdc.gov/',
|
|
335
|
+
Notes: 'Northern VA excluded — see <em>DC metro</em>'
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
STATE: 'Washington',
|
|
339
|
+
Rate: 62,
|
|
340
|
+
Location: 'Work',
|
|
341
|
+
URL: 'https://www.cdc.gov/',
|
|
342
|
+
Notes: '<span style="color:#2e8540;">✓ Strong program outcomes</span>'
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
STATE: 'West Virginia',
|
|
346
|
+
Rate: 25,
|
|
347
|
+
Location: 'Vehicle',
|
|
348
|
+
URL: 'https://www.cdc.gov/',
|
|
349
|
+
Notes: '<span style="color:#d54309;">⚠ Lowest in region</span>'
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
STATE: 'Wyoming',
|
|
353
|
+
Rate: 43,
|
|
354
|
+
Location: 'Vehicle',
|
|
355
|
+
URL: 'https://www.cdc.gov/',
|
|
356
|
+
Notes: 'Smallest population — <strong>widest confidence interval</strong>'
|
|
357
|
+
}
|
|
358
|
+
]
|
|
359
|
+
|
|
360
|
+
export const USAStateMap: Story = {
|
|
361
|
+
name: 'USA State Map',
|
|
362
|
+
args: {
|
|
363
|
+
config: {
|
|
364
|
+
...usaStateGradient,
|
|
365
|
+
data,
|
|
366
|
+
table: {
|
|
367
|
+
...usaStateGradient.table,
|
|
368
|
+
expanded: true
|
|
369
|
+
},
|
|
370
|
+
columns: {
|
|
371
|
+
...usaStateGradient.columns,
|
|
372
|
+
Notes: {
|
|
373
|
+
name: 'Notes',
|
|
374
|
+
label: 'Notes',
|
|
375
|
+
dataTable: true,
|
|
376
|
+
tooltip: false
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
isEditor: true
|
|
381
|
+
},
|
|
382
|
+
play: async ({ canvasElement }) => {
|
|
383
|
+
await assertVisualizationRendered(canvasElement)
|
|
384
|
+
}
|
|
385
|
+
}
|