@cdc/map 4.26.3 → 4.26.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CONFIG.md +235 -0
  2. package/README.md +70 -24
  3. package/dist/cdcmap-CY9IcPSi.es.js +6 -0
  4. package/dist/cdcmap-DlpiY3fQ.es.js +4 -0
  5. package/dist/cdcmap.js +27405 -26257
  6. package/examples/{testing-layer-2.json → __data__/testing-layer-2.json} +1 -1
  7. package/examples/{testing-layer.json → __data__/testing-layer.json} +1 -1
  8. package/examples/county-hsa-toggle.json +51993 -0
  9. package/examples/custom-map-layers.json +2 -2
  10. package/examples/default-county.json +3 -3
  11. package/examples/minimal-example.json +69 -0
  12. package/examples/private/annotation-bug.json +2 -2
  13. package/examples/private/css-issue.json +314 -0
  14. package/examples/private/region-breaking.json +1639 -0
  15. package/examples/private/test1.json +27247 -0
  16. package/package.json +4 -4
  17. package/src/CdcMapComponent.tsx +96 -13
  18. package/src/_stories/CdcMap.Editor.ColumnsSectionTests.stories.tsx +601 -0
  19. package/src/_stories/CdcMap.Editor.DataTableSectionTests.stories.tsx +404 -0
  20. package/src/_stories/CdcMap.Editor.FiltersSectionTests.stories.tsx +229 -0
  21. package/src/_stories/CdcMap.Editor.GeneralSectionTests.stories.tsx +262 -0
  22. package/src/_stories/CdcMap.Editor.LegendSectionTests.stories.tsx +541 -0
  23. package/src/_stories/CdcMap.Editor.MultiCountryWorldMapTests.stories.tsx +359 -0
  24. package/src/_stories/CdcMap.Editor.PatternSettingsSectionTests.stories.tsx +516 -0
  25. package/src/_stories/CdcMap.Editor.SmallMultiplesSectionTests.stories.tsx +165 -0
  26. package/src/_stories/CdcMap.Editor.TextAnnotationsSectionTests.stories.tsx +145 -0
  27. package/src/_stories/CdcMap.Editor.TypeSectionTests.stories.tsx +312 -0
  28. package/src/_stories/CdcMap.Editor.VisualSectionTests.stories.tsx +359 -0
  29. package/src/_stories/CdcMap.Editor.ZoomControlsTests.stories.tsx +88 -0
  30. package/src/_stories/{CdcMap.stories.tsx → CdcMap.smoke.stories.tsx} +12 -0
  31. package/src/_stories/_mock/legends/legend-tests.json +3 -3
  32. package/src/components/Annotation/AnnotationList.tsx +1 -1
  33. package/src/components/EditorPanel/components/EditorPanel.tsx +504 -383
  34. package/src/components/EditorPanel/components/HexShapeSettings.tsx +1 -1
  35. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +112 -117
  36. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +26 -13
  37. package/src/components/EditorPanel/components/editorPanel.styles.css +22 -2
  38. package/src/components/Legend/components/Legend.tsx +3 -3
  39. package/src/components/Legend/components/LegendItem.Hex.tsx +4 -2
  40. package/src/components/SmallMultiples/SynchronizedTooltip.tsx +1 -1
  41. package/src/components/UsaMap/components/UsaMap.County.tsx +271 -100
  42. package/src/components/UsaMap/components/UsaMap.State.tsx +1 -1
  43. package/src/components/UsaMap/data/cb_2019_us_county_20m.json +75817 -1
  44. package/src/components/UsaMap/data/hsa_fips_mapping.json +3144 -0
  45. package/src/components/WorldMap/data/world-topo.json +1 -1
  46. package/src/data/initial-state.js +1 -0
  47. package/src/data/supported-counties.json +1 -1
  48. package/src/helpers/countyTerritories.ts +38 -0
  49. package/src/helpers/dataTableHelpers.ts +35 -6
  50. package/src/helpers/tests/countyTerritories.test.ts +87 -0
  51. package/src/hooks/useApplyTooltipsToGeo.tsx +7 -4
  52. package/src/hooks/useMapLayers.tsx +1 -1
  53. package/src/hooks/useTooltip.ts +18 -7
  54. package/src/store/map.actions.ts +5 -2
  55. package/src/store/map.reducer.ts +12 -3
  56. package/src/test/CdcMap.test.jsx +24 -0
  57. package/src/types/MapConfig.ts +6 -0
  58. package/src/types/MapContext.ts +3 -1
  59. package/topojson-updater/README.txt +1 -1
  60. package/LICENSE +0 -201
  61. package/dist/cdcmap-vr9HZwRt.es.js +0 -6
  62. package/examples/__data__/city-state-data.json +0 -668
  63. package/examples/city-state.json +0 -434
  64. package/examples/default-world-data.json +0 -1450
  65. package/examples/new-cities.json +0 -656
  66. package/src/_stories/CdcMap.Editor.stories.tsx +0 -3648
  67. package/topojson-updater/package-lock.json +0 -223
  68. /package/src/_stories/{CdcMap.ColumnWrap.stories.tsx → CdcMap.ColumnWrap.smoke.stories.tsx} +0 -0
  69. /package/src/_stories/{CdcMap.Defaults.stories.tsx → CdcMap.Defaults.smoke.stories.tsx} +0 -0
  70. /package/src/_stories/{CdcMap.DistrictOfColumbia.stories.tsx → CdcMap.DistrictOfColumbia.smoke.stories.tsx} +0 -0
  71. /package/src/_stories/{CdcMap.Filters.stories.tsx → CdcMap.Filters.smoke.stories.tsx} +0 -0
  72. /package/src/_stories/{CdcMap.Legend.Gradient.stories.tsx → CdcMap.Legend.Gradient.smoke.stories.tsx} +0 -0
  73. /package/src/_stories/{CdcMap.Legend.stories.tsx → CdcMap.Legend.smoke.stories.tsx} +0 -0
  74. /package/src/_stories/{CdcMap.Patterns.stories.tsx → CdcMap.Patterns.smoke.stories.tsx} +0 -0
  75. /package/src/_stories/{CdcMap.SmallMultiples.stories.tsx → CdcMap.SmallMultiples.smoke.stories.tsx} +0 -0
  76. /package/src/_stories/{CdcMap.Table.stories.tsx → CdcMap.Table.smoke.stories.tsx} +0 -0
  77. /package/src/_stories/{CdcMap.ZeroColor.stories.tsx → CdcMap.ZeroColor.smoke.stories.tsx} +0 -0
  78. /package/src/_stories/{GoogleMap.stories.tsx → GoogleMap.smoke.stories.tsx} +0 -0
  79. /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
+ }