@cdc/map 4.26.3 → 4.26.5
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 +268 -0
- package/README.md +74 -24
- package/dist/cdcmap-CY9IcPSi.es.js +6 -0
- package/dist/cdcmap-DlpiY3fQ.es.js +4 -0
- package/dist/cdcmap.js +29168 -27482
- 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 +6 -3
- package/examples/minimal-example.json +73 -0
- package/examples/private/annotation-bug.json +2 -2
- 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/CdcMapComponent.tsx +107 -14
- package/src/_stories/CdcMap.AltText.stories.tsx +122 -0
- package/src/_stories/CdcMap.Editor.ColumnsSectionTests.stories.tsx +600 -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.FocusVisibility.stories.tsx +87 -0
- package/src/_stories/CdcMap.HiddenMount.stories.tsx +69 -0
- package/src/_stories/CdcMap.ResetBehavior.stories.tsx +32 -0
- package/src/_stories/CdcMap.Zoom.stories.tsx +111 -0
- package/src/_stories/{CdcMap.stories.tsx → CdcMap.smoke.stories.tsx} +60 -0
- package/src/_stories/_mock/alt_text_metadata.json +65 -0
- package/src/_stories/_mock/legends/legend-tests.json +3 -3
- package/src/_stories/_mock/world-bubble-reset.json +138 -0
- package/src/_stories/_mock/world-data-zoom-filters.json +166 -0
- package/src/components/Annotation/AnnotationList.tsx +1 -1
- package/src/components/BubbleList.tsx +13 -0
- package/src/components/EditorPanel/components/EditorPanel.tsx +637 -382
- 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.tsx +26 -13
- package/src/components/EditorPanel/components/editorPanel.styles.css +22 -2
- package/src/components/FilterControls.tsx +21 -0
- package/src/components/Legend/components/Legend.tsx +3 -3
- package/src/components/Legend/components/LegendItem.Hex.tsx +4 -2
- package/src/components/SmallMultiples/SmallMultiples.tsx +2 -2
- package/src/components/SmallMultiples/SynchronizedTooltip.tsx +1 -1
- package/src/components/UsaMap/components/UsaMap.County.tsx +309 -108
- package/src/components/UsaMap/components/UsaMap.Region.tsx +5 -2
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +33 -10
- package/src/components/UsaMap/components/UsaMap.State.tsx +10 -3
- 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 +37 -4
- package/src/components/WorldMap/data/world-topo.json +1 -1
- package/src/components/ZoomableGroup.tsx +23 -3
- package/src/components/filterControls.styles.css +6 -0
- package/src/data/initial-state.js +3 -0
- package/src/data/supported-counties.json +1 -1
- package/src/helpers/countyTerritories.ts +38 -0
- package/src/helpers/dataTableHelpers.ts +35 -6
- package/src/helpers/generateRuntimeFilters.ts +2 -1
- package/src/helpers/handleMapAriaLabels.ts +45 -30
- package/src/helpers/shouldAutoResetSingleStateZoom.ts +22 -0
- package/src/helpers/tests/countyTerritories.test.ts +87 -0
- package/src/helpers/tests/handleMapAriaLabels.test.ts +71 -0
- package/src/helpers/tests/shouldAutoResetSingleStateZoom.test.ts +71 -0
- package/src/hooks/useApplyTooltipsToGeo.tsx +7 -4
- package/src/hooks/useGeoClickHandler.ts +13 -1
- package/src/hooks/useMapLayers.tsx +1 -1
- package/src/hooks/useStateZoom.tsx +39 -20
- package/src/hooks/useTooltip.test.tsx +2 -16
- package/src/hooks/useTooltip.ts +18 -7
- package/src/index.jsx +5 -2
- package/src/scss/main.scss +6 -21
- package/src/scss/map.scss +20 -0
- package/src/store/map.actions.ts +5 -2
- package/src/store/map.reducer.ts +12 -3
- package/src/test/CdcMap.test.jsx +24 -0
- package/src/types/MapConfig.ts +11 -0
- package/src/types/MapContext.ts +6 -1
- package/topojson-updater/README.txt +1 -1
- package/dist/cdcmap-vr9HZwRt.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 -3648
- 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.Defaults.stories.tsx → CdcMap.Defaults.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
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { within, userEvent, expect } from 'storybook/test'
|
|
3
|
+
import CdcMap from '../CdcMap'
|
|
4
|
+
import usaStateGradientConfig from './_mock/usa-state-gradient.json'
|
|
5
|
+
import multiCountryConfig from './_mock/multi-country.json'
|
|
6
|
+
import wastewaterMapSmallMultiplesConfig from './_mock/small_multiples/wastewater-map-small-multiples.json'
|
|
7
|
+
import { performAndAssert, waitForEditor, waitForPresence, openAccordion } from '@cdc/core/helpers/testing'
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof CdcMap>
|
|
10
|
+
|
|
11
|
+
const mapMeta: Meta<typeof CdcMap> = {
|
|
12
|
+
title: 'Components/Templates/Map/Editor Tests',
|
|
13
|
+
component: CdcMap,
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: 'fullscreen'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default mapMeta
|
|
20
|
+
|
|
21
|
+
const DEFAULT_ARGS = {
|
|
22
|
+
isEditor: true,
|
|
23
|
+
config: usaStateGradientConfig
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const GeneralSectionTests: Story = {
|
|
27
|
+
args: {
|
|
28
|
+
...DEFAULT_ARGS
|
|
29
|
+
},
|
|
30
|
+
play: async ({ canvasElement }) => {
|
|
31
|
+
const canvas = within(canvasElement)
|
|
32
|
+
|
|
33
|
+
await waitForEditor(canvas)
|
|
34
|
+
await waitForPresence('.map-container', canvasElement)
|
|
35
|
+
|
|
36
|
+
await openAccordion(canvas, 'General')
|
|
37
|
+
|
|
38
|
+
// ==========================================================================
|
|
39
|
+
// TEST: Title field
|
|
40
|
+
// Verifies: Title text appears in the Title component on the visualization
|
|
41
|
+
// ==========================================================================
|
|
42
|
+
const titleInput = canvasElement.querySelector('[data-testid="title-input"]') as HTMLInputElement
|
|
43
|
+
expect(titleInput).toBeTruthy()
|
|
44
|
+
|
|
45
|
+
const getTitleVisual = () => {
|
|
46
|
+
const titleElement = canvasElement.querySelector('.cove-title, .map-title')
|
|
47
|
+
return {
|
|
48
|
+
titleText: titleElement?.textContent || '',
|
|
49
|
+
hasTitleElement: Boolean(titleElement)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await performAndAssert(
|
|
54
|
+
'Title → Update text',
|
|
55
|
+
getTitleVisual,
|
|
56
|
+
async () => {
|
|
57
|
+
await userEvent.clear(titleInput)
|
|
58
|
+
await userEvent.type(titleInput, 'Test Map Title')
|
|
59
|
+
},
|
|
60
|
+
(before, after) => before.titleText !== after.titleText && after.titleText.includes('Test Map Title')
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
// ==========================================================================
|
|
64
|
+
// TEST: Show Title checkbox
|
|
65
|
+
// Verifies: Title element visibility is controlled by showTitle
|
|
66
|
+
// ==========================================================================
|
|
67
|
+
const generalAccordion = canvasElement.querySelector('[aria-expanded="true"]')?.closest('.accordion__item')
|
|
68
|
+
const showTitleLabel = Array.from(generalAccordion?.querySelectorAll('label') || []).find(label =>
|
|
69
|
+
label.textContent?.includes('Show Title')
|
|
70
|
+
)
|
|
71
|
+
const showTitleCheckbox = showTitleLabel?.querySelector('input[type="checkbox"]') as HTMLInputElement
|
|
72
|
+
expect(showTitleCheckbox).toBeTruthy()
|
|
73
|
+
|
|
74
|
+
const getTitleVisibility = () => {
|
|
75
|
+
const titleElement = canvasElement.querySelector('.cove-title, header.cove-visualization__header')
|
|
76
|
+
return {
|
|
77
|
+
isPresent: Boolean(titleElement)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Test config has showTitle: true, so title starts visible (present in DOM)
|
|
82
|
+
await performAndAssert(
|
|
83
|
+
'Show Title → Hide',
|
|
84
|
+
getTitleVisibility,
|
|
85
|
+
async () => {
|
|
86
|
+
await userEvent.click(showTitleCheckbox)
|
|
87
|
+
},
|
|
88
|
+
(before, after) => before.isPresent && !after.isPresent
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
await performAndAssert(
|
|
92
|
+
'Show Title → Show',
|
|
93
|
+
getTitleVisibility,
|
|
94
|
+
async () => {
|
|
95
|
+
await userEvent.click(showTitleCheckbox)
|
|
96
|
+
},
|
|
97
|
+
(before, after) => !before.isPresent && after.isPresent
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// ==========================================================================
|
|
101
|
+
// TEST: Super Title field
|
|
102
|
+
// Verifies: Super title text appears in the Title component
|
|
103
|
+
// ==========================================================================
|
|
104
|
+
const superTitleInput = canvas.getByLabelText(/Super Title/i) as HTMLInputElement
|
|
105
|
+
expect(superTitleInput).toBeTruthy()
|
|
106
|
+
|
|
107
|
+
const getSuperTitleVisual = () => {
|
|
108
|
+
const titleElement = canvasElement.querySelector('.cove-title, .map-title')
|
|
109
|
+
return {
|
|
110
|
+
titleText: titleElement?.textContent || ''
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await performAndAssert(
|
|
115
|
+
'Super Title → Add text',
|
|
116
|
+
getSuperTitleVisual,
|
|
117
|
+
async () => {
|
|
118
|
+
await userEvent.clear(superTitleInput)
|
|
119
|
+
await userEvent.type(superTitleInput, 'Super Title Text')
|
|
120
|
+
},
|
|
121
|
+
(before, after) => !before.titleText.includes('Super Title Text') && after.titleText.includes('Super Title Text')
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
// ==========================================================================
|
|
125
|
+
// TEST: Title Style dropdown
|
|
126
|
+
// Verifies: Changing title style changes the heading element (h2/h3) used
|
|
127
|
+
// ==========================================================================
|
|
128
|
+
const titleStyleSelect = canvas.getByLabelText(/Title Style/i) as HTMLSelectElement
|
|
129
|
+
expect(titleStyleSelect).toBeTruthy()
|
|
130
|
+
|
|
131
|
+
const getTitleStyleVisual = () => {
|
|
132
|
+
const coveTitleElement = canvasElement.querySelector('.cove-title')
|
|
133
|
+
const legacyTitleElement = canvasElement.querySelector('header.cove-visualization__header')
|
|
134
|
+
|
|
135
|
+
// For modern titles, check for h2 (large) or h3 (small) elements
|
|
136
|
+
const hasH2 = Boolean(coveTitleElement?.querySelector('h2'))
|
|
137
|
+
const hasH3 = Boolean(coveTitleElement?.querySelector('h3'))
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
hasCoveTitle: Boolean(coveTitleElement),
|
|
141
|
+
hasLegacyTitle: Boolean(legacyTitleElement),
|
|
142
|
+
isSmall: hasH3,
|
|
143
|
+
isLarge: hasH2
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Current config has titleStyle: 'small'
|
|
148
|
+
// Test: Change to 'large'
|
|
149
|
+
await performAndAssert(
|
|
150
|
+
'Title Style → Change to Large',
|
|
151
|
+
getTitleStyleVisual,
|
|
152
|
+
async () => {
|
|
153
|
+
await userEvent.selectOptions(titleStyleSelect, 'large')
|
|
154
|
+
},
|
|
155
|
+
(before, after) => before.isSmall && after.isLarge && after.hasCoveTitle && !after.hasLegacyTitle
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
// Test: Change to 'legacy'
|
|
159
|
+
await performAndAssert(
|
|
160
|
+
'Title Style → Change to Legacy',
|
|
161
|
+
getTitleStyleVisual,
|
|
162
|
+
async () => {
|
|
163
|
+
await userEvent.selectOptions(titleStyleSelect, 'legacy')
|
|
164
|
+
},
|
|
165
|
+
(before, after) => before.hasCoveTitle && !after.hasCoveTitle && after.hasLegacyTitle
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
// Test: Change back to 'small'
|
|
169
|
+
await performAndAssert(
|
|
170
|
+
'Title Style → Change back to Small',
|
|
171
|
+
getTitleStyleVisual,
|
|
172
|
+
async () => {
|
|
173
|
+
await userEvent.selectOptions(titleStyleSelect, 'small')
|
|
174
|
+
},
|
|
175
|
+
(before, after) => before.hasLegacyTitle && !after.hasLegacyTitle && after.isSmall && after.hasCoveTitle
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
// ==========================================================================
|
|
179
|
+
// TEST: Message/Intro Text field
|
|
180
|
+
// Verifies: Intro text appears in section with class 'introText'
|
|
181
|
+
// ==========================================================================
|
|
182
|
+
const messageInput = canvas.getByLabelText(/Message/i) as HTMLTextAreaElement
|
|
183
|
+
expect(messageInput).toBeTruthy()
|
|
184
|
+
|
|
185
|
+
const getIntroTextVisual = () => {
|
|
186
|
+
const introSection = canvasElement.querySelector('.introText')
|
|
187
|
+
return {
|
|
188
|
+
introText: introSection?.textContent || '',
|
|
189
|
+
hasIntroSection: Boolean(introSection)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await performAndAssert(
|
|
194
|
+
'Message → Add intro text',
|
|
195
|
+
getIntroTextVisual,
|
|
196
|
+
async () => {
|
|
197
|
+
await userEvent.clear(messageInput)
|
|
198
|
+
await userEvent.type(messageInput, 'This is test intro text')
|
|
199
|
+
},
|
|
200
|
+
(before, after) =>
|
|
201
|
+
!before.introText.includes('This is test intro text') &&
|
|
202
|
+
after.introText.includes('This is test intro text') &&
|
|
203
|
+
after.hasIntroSection
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
// ==========================================================================
|
|
207
|
+
// TEST: Subtext field
|
|
208
|
+
// Verifies: Subtext appears in paragraph with class 'subtext'
|
|
209
|
+
// ==========================================================================
|
|
210
|
+
const subtextInput = canvas.getByLabelText(/Subtext/i) as HTMLTextAreaElement
|
|
211
|
+
expect(subtextInput).toBeTruthy()
|
|
212
|
+
|
|
213
|
+
const getSubtextVisual = () => {
|
|
214
|
+
const subtextElement = canvasElement.querySelector('.subtext')
|
|
215
|
+
return {
|
|
216
|
+
subtextContent: subtextElement?.textContent || '',
|
|
217
|
+
hasSubtext: Boolean(subtextElement)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await performAndAssert(
|
|
222
|
+
'Subtext → Add text',
|
|
223
|
+
getSubtextVisual,
|
|
224
|
+
async () => {
|
|
225
|
+
await userEvent.clear(subtextInput)
|
|
226
|
+
await userEvent.type(subtextInput, 'This is test subtext')
|
|
227
|
+
},
|
|
228
|
+
(before, after) =>
|
|
229
|
+
!before.subtextContent.includes('This is test subtext') &&
|
|
230
|
+
after.subtextContent.includes('This is test subtext') &&
|
|
231
|
+
after.hasSubtext
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
// ==========================================================================
|
|
235
|
+
// TEST: Footnotes field
|
|
236
|
+
// Verifies: Footnotes appear in section with class 'footnotes'
|
|
237
|
+
// ==========================================================================
|
|
238
|
+
const footnotesInput = canvas.getByLabelText(/Footnotes/i) as HTMLTextAreaElement
|
|
239
|
+
expect(footnotesInput).toBeTruthy()
|
|
240
|
+
|
|
241
|
+
const getFootnotesVisual = () => {
|
|
242
|
+
const footnotesSection = canvasElement.querySelector('.footnotes')
|
|
243
|
+
return {
|
|
244
|
+
footnotesContent: footnotesSection?.textContent || '',
|
|
245
|
+
hasFootnotes: Boolean(footnotesSection)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
await performAndAssert(
|
|
250
|
+
'Footnotes → Add text',
|
|
251
|
+
getFootnotesVisual,
|
|
252
|
+
async () => {
|
|
253
|
+
await userEvent.clear(footnotesInput)
|
|
254
|
+
await userEvent.type(footnotesInput, 'Test footnote text')
|
|
255
|
+
},
|
|
256
|
+
(before, after) =>
|
|
257
|
+
!before.footnotesContent.includes('Test footnote text') &&
|
|
258
|
+
after.footnotesContent.includes('Test footnote text') &&
|
|
259
|
+
after.hasFootnotes
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
}
|