@cdc/map 4.25.8 → 4.25.11

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 (137) hide show
  1. package/.claude/agents/typescript-organizer.md +118 -0
  2. package/.claude/settings.local.json +30 -0
  3. package/dist/{cdcmap-fce76882.es.js → cdcmap-BnB1QM5d.es.js} +6 -13
  4. package/dist/{cdcmap-c55ac1ea.es.js → cdcmap-D6CG2-Hb.es.js} +5 -12
  5. package/dist/{cdcmap-31a33da1.es.js → cdcmap-MXgURbdZ.es.js} +6 -13
  6. package/dist/{cdcmap-1a1724a1.es.js → cdcmap-dgT_1dIT.es.js} +136 -151
  7. package/dist/cdcmap.js +56991 -53706
  8. package/examples/example-city-state.json +9 -1
  9. package/examples/multi-country-centering.json +45 -0
  10. package/examples/private/c.json +290 -0
  11. package/examples/private/canvas-city-hover.json +787 -0
  12. package/examples/private/colors-2.json +221 -0
  13. package/examples/private/colors.json +221 -0
  14. package/examples/private/d.json +345 -0
  15. package/examples/private/g.json +1 -0
  16. package/examples/private/h.json +105911 -0
  17. package/examples/private/measles-data.json +378 -0
  18. package/examples/private/measles.json +211 -0
  19. package/examples/private/north-dakota.json +1132 -0
  20. package/examples/private/state-with-pattern.json +883 -0
  21. package/index.html +36 -34
  22. package/package.json +26 -5
  23. package/src/CdcMap.tsx +23 -8
  24. package/src/CdcMapComponent.tsx +238 -308
  25. package/src/_stories/CdcMap.ColumnWrap.stories.tsx +31 -0
  26. package/src/_stories/CdcMap.DistrictOfColumbia.stories.tsx +320 -0
  27. package/src/_stories/CdcMap.Editor.stories.tsx +3371 -0
  28. package/src/_stories/CdcMap.Filters.stories.tsx +2 -2
  29. package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +3 -3
  30. package/src/_stories/CdcMap.Legend.stories.tsx +7 -4
  31. package/src/_stories/CdcMap.Patterns.stories.tsx +2 -2
  32. package/src/_stories/CdcMap.SmallMultiples.stories.tsx +35 -0
  33. package/src/_stories/CdcMap.Table.stories.tsx +2 -2
  34. package/src/_stories/CdcMap.stories.tsx +37 -9
  35. package/src/_stories/GoogleMap.stories.tsx +2 -2
  36. package/src/_stories/UsaMap.NoData.stories.tsx +2 -2
  37. package/src/_stories/_mock/column-wrap-test.json +265 -0
  38. package/src/_stories/_mock/equal-number.json +1109 -0
  39. package/src/_stories/_mock/multi-country-hide.json +78 -0
  40. package/src/_stories/_mock/multi-country.json +95 -0
  41. package/src/_stories/_mock/multi-state.json +887 -20403
  42. package/src/_stories/_mock/small_multiples/multi-state-small-multiples.json +8399 -0
  43. package/src/_stories/_mock/small_multiples/region-small-multiples.json +657 -0
  44. package/src/_stories/_mock/small_multiples/wastewater-map-small-multiples.json +221 -0
  45. package/src/_stories/_mock/us-bubble-cities.json +306 -0
  46. package/src/_stories/_mock/usa-state-gradient.json +2 -4
  47. package/src/components/BubbleList.tsx +17 -13
  48. package/src/components/CityList.tsx +85 -107
  49. package/src/components/EditorPanel/components/EditorPanel.tsx +787 -709
  50. package/src/components/EditorPanel/components/HexShapeSettings.tsx +58 -95
  51. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +34 -42
  52. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +354 -0
  53. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  54. package/src/components/Geo.tsx +22 -3
  55. package/src/components/Legend/components/Legend.tsx +76 -40
  56. package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +10 -7
  57. package/src/components/Legend/components/index.scss +1 -1
  58. package/src/components/MapContainer.tsx +52 -0
  59. package/src/components/MapControls.tsx +44 -0
  60. package/src/components/NavigationMenu.tsx +27 -15
  61. package/src/components/SmallMultiples/SmallMultipleTile.tsx +163 -0
  62. package/src/components/SmallMultiples/SmallMultiples.css +32 -0
  63. package/src/components/SmallMultiples/SmallMultiples.tsx +150 -0
  64. package/src/components/SmallMultiples/SynchronizedTooltip.tsx +105 -0
  65. package/src/components/SmallMultiples/index.tsx +3 -0
  66. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +36 -4
  67. package/src/components/UsaMap/components/TerritoriesSection.tsx +26 -12
  68. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +30 -4
  69. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +23 -4
  70. package/src/components/UsaMap/components/Territory/TerritoryShape.ts +6 -0
  71. package/src/components/UsaMap/components/UsaMap.County.tsx +123 -37
  72. package/src/components/UsaMap/components/UsaMap.Region.tsx +36 -5
  73. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +30 -10
  74. package/src/components/UsaMap/components/UsaMap.State.tsx +53 -12
  75. package/src/components/UsaMap/helpers/map.ts +4 -4
  76. package/src/components/UsaMap/helpers/shapes.ts +9 -6
  77. package/src/components/WorldMap/WorldMap.tsx +193 -35
  78. package/src/components/ZoomControls.tsx +6 -9
  79. package/src/context/LegendMemoContext.tsx +30 -0
  80. package/src/context.ts +1 -40
  81. package/src/data/initial-state.js +153 -130
  82. package/src/data/supported-geos.js +25 -78
  83. package/src/helpers/addUIDs.ts +13 -2
  84. package/src/helpers/applyColorToLegend.ts +140 -20
  85. package/src/helpers/applyLegendToRow.ts +10 -6
  86. package/src/helpers/componentHelpers.ts +8 -0
  87. package/src/helpers/constants.ts +12 -14
  88. package/src/helpers/dataTableHelpers.ts +6 -0
  89. package/src/helpers/displayGeoName.ts +18 -3
  90. package/src/helpers/generateRuntimeLegend.ts +44 -10
  91. package/src/helpers/generateRuntimeLegendHash.ts +4 -2
  92. package/src/helpers/getColumnNames.ts +1 -1
  93. package/src/helpers/getCountriesPicked.ts +103 -0
  94. package/src/helpers/getMapContainerClasses.ts +7 -0
  95. package/src/helpers/getPatternForRow.ts +33 -0
  96. package/src/helpers/getStatesPicked.ts +8 -5
  97. package/src/helpers/index.ts +3 -3
  98. package/src/helpers/isLegendItemDisabled.ts +16 -0
  99. package/src/helpers/mapObserverHelpers.ts +40 -0
  100. package/src/helpers/resetLegendToggles.ts +3 -2
  101. package/src/helpers/smallMultiplesHelpers.ts +359 -0
  102. package/src/helpers/tests/titleCase.test.ts +76 -0
  103. package/src/helpers/titleCase.ts +13 -13
  104. package/src/helpers/toggleLegendActive.ts +6 -11
  105. package/src/helpers/urlDataHelpers.ts +70 -0
  106. package/src/hooks/useCountryZoom.tsx +241 -0
  107. package/src/hooks/useGeoClickHandler.ts +36 -2
  108. package/src/hooks/useLegendMemo.ts +17 -0
  109. package/src/hooks/useMapLayers.tsx +5 -4
  110. package/src/hooks/useProgrammaticMapTooltip.ts +110 -0
  111. package/src/hooks/useResizeObserver.ts +5 -2
  112. package/src/hooks/useStateZoom.tsx +30 -8
  113. package/src/hooks/useSynchronizedGeographies.ts +56 -0
  114. package/src/hooks/useTooltip.ts +1 -2
  115. package/src/index.jsx +1 -2
  116. package/src/scss/editor-panel.scss +4 -440
  117. package/src/scss/main.scss +1 -1
  118. package/src/scss/map.scss +12 -15
  119. package/src/store/map.actions.ts +7 -7
  120. package/src/store/map.reducer.ts +17 -6
  121. package/src/test/CdcMap.test.jsx +11 -0
  122. package/src/types/MapConfig.ts +46 -18
  123. package/src/types/MapContext.ts +6 -7
  124. package/src/types/runtimeLegend.ts +17 -1
  125. package/vite.config.js +2 -7
  126. package/vitest.config.ts +16 -0
  127. package/src/components/DataTable.tsx +0 -385
  128. package/src/components/EditorPanel/components/Inputs.tsx +0 -59
  129. package/src/coreStyles_map.scss +0 -3
  130. package/src/helpers/colorDistributions.ts +0 -12
  131. package/src/helpers/generateColorsArray.ts +0 -14
  132. package/src/helpers/tests/generateColorsArray.test.ts +0 -18
  133. package/src/helpers/tests/generateRuntimeLegendHash.test.ts +0 -11
  134. package/src/hooks/useActiveElement.ts +0 -19
  135. package/src/scss/mixins.scss +0 -47
  136. package/src/types/Annotations.ts +0 -24
  137. /package/dist/{cdcmap-548642e6.es.js → cdcmap-Ct2SB0vL.es.js} +0 -0
@@ -6,26 +6,20 @@ import {
6
6
  AccordionItemPanel,
7
7
  AccordionItemButton
8
8
  } from 'react-accessible-accordion'
9
+ import { Select } from '@cdc/core/components/EditorPanel/Inputs'
9
10
  import ConfigContext from '../../../context'
10
11
  import _ from 'lodash'
11
-
12
- const shapeOptions = ['Arrow Up', 'Arrow Down', 'Arrow Right', 'Arrow Left', 'None']
13
-
14
- // todo: Move duplicated operators to CORE
15
- export const DATA_OPERATOR_LESS = '<'
16
- export const DATA_OPERATOR_GREATER = '>'
17
- export const DATA_OPERATOR_LESSEQUAL = '<='
18
- export const DATA_OPERATOR_GREATEREQUAL = '>='
19
- export const DATA_OPERATOR_EQUAL = '='
20
- export const DATA_OPERATOR_NOTEQUAL = '≠'
21
- export const DATA_OPERATORS = [
12
+ import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
13
+ import {
22
14
  DATA_OPERATOR_LESS,
23
15
  DATA_OPERATOR_GREATER,
24
16
  DATA_OPERATOR_LESSEQUAL,
25
17
  DATA_OPERATOR_GREATEREQUAL,
26
18
  DATA_OPERATOR_EQUAL,
27
19
  DATA_OPERATOR_NOTEQUAL
28
- ]
20
+ } from '@cdc/core/helpers/constants'
21
+
22
+ const shapeOptions = ['Arrow Up', 'Arrow Down', 'Arrow Right', 'Arrow Left', 'None']
29
23
 
30
24
  /**
31
25
  * Notice: each shape Col has a legend title and description should the title/desc need to be different for different shapes.
@@ -105,7 +99,7 @@ const HexSettingShapeColumns = props => {
105
99
  type='text'
106
100
  value={shapeGroup.legendTitle || ''}
107
101
  onChange={e => {
108
- const newConfig = _.cloneDeep(config)
102
+ const newConfig = cloneConfig(config)
109
103
  newConfig.hexMap.shapeGroups[shapeGroupIndex].legendTitle = e.target.value
110
104
  setConfig(newConfig)
111
105
  }}
@@ -134,91 +128,60 @@ const HexSettingShapeColumns = props => {
134
128
  </AccordionItemHeading>
135
129
  <AccordionItemPanel>
136
130
  <>
131
+ <Select
132
+ label='Shape Column'
133
+ value={
134
+ config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].shape ||
135
+ 'Arrow Up'
136
+ }
137
+ options={shapeOptions}
138
+ fieldName={`shape-${shapeGroupIndex}-${itemIndex}`}
139
+ updateField={(section, subsection, fieldName, value) => {
140
+ handleItemUpdate('shape', value, shapeGroupIndex, itemIndex)
141
+ }}
142
+ />
143
+
144
+ <Select
145
+ label='Column'
146
+ value={config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].key || ''}
147
+ options={columnsOptions.map(c => c.key)}
148
+ fieldName={`key-${shapeGroupIndex}-${itemIndex}`}
149
+ updateField={(section, subsection, fieldName, value) =>
150
+ handleItemUpdate('key', value, shapeGroupIndex, itemIndex)
151
+ }
152
+ />
153
+
154
+ <Select
155
+ label='Operator'
156
+ value={
157
+ config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].operator || '='
158
+ }
159
+ options={[
160
+ DATA_OPERATOR_EQUAL,
161
+ DATA_OPERATOR_NOTEQUAL,
162
+ DATA_OPERATOR_LESS,
163
+ DATA_OPERATOR_GREATER,
164
+ DATA_OPERATOR_LESSEQUAL,
165
+ DATA_OPERATOR_GREATEREQUAL
166
+ ]}
167
+ fieldName={`operator-${shapeGroupIndex}-${itemIndex}`}
168
+ updateField={(section, subsection, fieldName, value) =>
169
+ handleItemUpdate('operator', value, shapeGroupIndex, itemIndex)
170
+ }
171
+ />
172
+
137
173
  <label>
138
- <span className='edit-label column-heading'>Shape Column</span>
139
- <select
174
+ <span className='edit-label column-heading'>Value</span>
175
+ <input
176
+ type='text'
140
177
  value={
141
- config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].shape
142
- ? config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].shape
143
- : 'select'
178
+ config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].value || ''
179
+ }
180
+ onChange={e =>
181
+ handleItemUpdate('value', e.target.value, shapeGroupIndex, itemIndex)
144
182
  }
145
- onChange={e => {
146
- handleItemUpdate('shape', e.target.value, shapeGroupIndex, itemIndex)
147
- }}
148
- >
149
- {shapeOptions.map(shape => (
150
- <option value={shape}>{shape}</option>
151
- ))}
152
- </select>
183
+ />
153
184
  </label>
154
-
155
- <div className='cove-input-group'>
156
- <label className=''>
157
- <span className='edit-label cove-input__label'>Column Conditional</span>
158
- </label>
159
- <div className='cove-accordion__panel-row cove-accordion__small-inputs'>
160
- <div className='cove-accordion__panel-col cove-input'>
161
- <select
162
- value={
163
- config.hexMap.shapeGroups[shapeGroupIndex].key === ''
164
- ? 'Select'
165
- : config.hexMap.shapeGroups[shapeGroupIndex].key
166
- }
167
- className='cove-input'
168
- onChange={e =>
169
- handleItemUpdate('key', e.target.value, shapeGroupIndex, itemIndex)
170
- }
171
- >
172
- {columnsOptions}
173
- </select>
174
- </div>
175
- <div className='cove-accordion__panel-col cove-input'>
176
- <select
177
- value={
178
- config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].operator ||
179
- '-SELECT-'
180
- }
181
- initial='Select'
182
- className='cove-input'
183
- onChange={e =>
184
- handleItemUpdate('operator', e.target.value, shapeGroupIndex, itemIndex)
185
- }
186
- >
187
- {[DATA_OPERATOR_EQUAL].map(option => {
188
- return <option value={option}>{option}</option>
189
- })}
190
- {[DATA_OPERATOR_NOTEQUAL].map(option => {
191
- return <option value={option}>{option}</option>
192
- })}
193
- {[DATA_OPERATOR_LESS].map(option => {
194
- return <option value={option}>{option}</option>
195
- })}
196
- {[DATA_OPERATOR_GREATER].map(option => {
197
- return <option value={option}>{option}</option>
198
- })}
199
- {[DATA_OPERATOR_LESSEQUAL].map(option => {
200
- return <option value={option}>{option}</option>
201
- })}
202
- {[DATA_OPERATOR_GREATEREQUAL].map(option => {
203
- return <option value={option}>{option}</option>
204
- })}
205
- </select>
206
- </div>
207
- <div className='cove-accordion__panel-col cove-input'>
208
- <input
209
- type='text'
210
- value={
211
- config.hexMap.shapeGroups[shapeGroupIndex].items[itemIndex].value || ''
212
- }
213
- className='cove-input'
214
- style={{ height: '100%' }}
215
- onChange={e =>
216
- handleItemUpdate('value', e.target.value, shapeGroupIndex, itemIndex)
217
- }
218
- />
219
- </div>
220
- </div>
221
- </div>
222
185
  <button
223
186
  className='cove-button cove-button--warn'
224
187
  style={{
@@ -243,7 +206,7 @@ const HexSettingShapeColumns = props => {
243
206
  className='cove-button'
244
207
  style={{ marginTop: '15px' }}
245
208
  onClick={() => {
246
- const newConfig = _.cloneDeep(config)
209
+ const newConfig = cloneConfig(config)
247
210
  _.set(
248
211
  newConfig,
249
212
  'hexMap.shapeGroups',
@@ -7,13 +7,16 @@ import {
7
7
  AccordionItemButton
8
8
  } from 'react-accessible-accordion'
9
9
  import ConfigContext from '../../../../context'
10
+ import { useLegendMemoContext } from '../../../../context/LegendMemoContext'
10
11
  import { type MapContext } from '../../../../types/MapContext'
11
12
  import Button from '@cdc/core/components/elements/Button'
12
13
  import Tooltip from '@cdc/core/components/ui/Tooltip'
13
14
  import Icon from '@cdc/core/components/ui/Icon'
15
+ import { Select } from '@cdc/core/components/EditorPanel/Inputs'
14
16
  import './Panel.PatternSettings-style.css'
15
17
  import Alert from '@cdc/core/components/Alert'
16
18
  import _ from 'lodash'
19
+ import { cloneConfig } from '@cdc/core/helpers/cloneConfig'
17
20
 
18
21
  // topojson helpers for checking color contrasts
19
22
  import { feature } from 'topojson-client'
@@ -26,8 +29,8 @@ type PanelProps = {
26
29
  }
27
30
 
28
31
  const PatternSettings = ({ name }: PanelProps) => {
29
- const { config, setConfig, runtimeData, legendMemo, legendSpecialClassLastMemo, runtimeLegend } =
30
- useContext<MapContext>(ConfigContext)
32
+ const { config, setConfig, runtimeData, runtimeLegend } = useContext<MapContext>(ConfigContext)
33
+ const { legendMemo, legendSpecialClassLastMemo } = useLegendMemoContext()
31
34
  const defaultPattern = 'circles'
32
35
  const patternTypes = ['circles', 'waves', 'lines']
33
36
 
@@ -114,14 +117,14 @@ const PatternSettings = ({ name }: PanelProps) => {
114
117
  }
115
118
 
116
119
  const handlePatternFieldUpdate = (field: string, color: string, patternIndex: number) => {
117
- const _newConfig = _.cloneDeep(config)
120
+ const _newConfig = cloneConfig(config)
118
121
  _newConfig.map.patterns[patternIndex][field] = color
119
122
  reviewColorContrast(_newConfig, patternIndex)
120
123
  setConfig(_newConfig)
121
124
  }
122
125
 
123
126
  const handleRemovePattern = index => {
124
- const _newConfig = _.cloneDeep(config)
127
+ const _newConfig = cloneConfig(config)
125
128
  const updatedPatterns = config.map.patterns.filter((pattern, i) => i !== index)
126
129
  _newConfig.map.patterns = updatedPatterns
127
130
  if (checkPatternContrasts()) {
@@ -161,7 +164,7 @@ const PatternSettings = ({ name }: PanelProps) => {
161
164
  {patterns.length > 0 && (
162
165
  <Alert
163
166
  type={checkPatternContrasts() ? 'success' : 'danger'}
164
- message='Pattern colors must comply with <br /> <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1</a> 3:1 contrast ratio.'
167
+ message='Pattern colors must comply with <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1</a> 3:1 contrast ratio.'
165
168
  />
166
169
  )}
167
170
  <br />
@@ -194,21 +197,16 @@ const PatternSettings = ({ name }: PanelProps) => {
194
197
  message='Error: <a href="https://webaim.org/resources/contrastchecker/" target="_blank"> Review Color Contrast</a>'
195
198
  />
196
199
  )}{' '}
197
- <label htmlFor={`pattern-dataKey--${patternIndex}`}>Data Key:</label>
198
- <select
199
- id={`pattern-dataKey--${patternIndex}`}
200
- value={pattern.dataKey !== '' ? pattern.dataKey : 'Select'}
201
- onChange={e => handlePatternFieldUpdate('dataKey', e.target.value, patternIndex)}
202
- >
203
- {/* TODO: sort these? */}
204
- {dataKeyOptions.map((d, index) => {
205
- return (
206
- <option value={d} key={index}>
207
- {d}
208
- </option>
209
- )
210
- })}
211
- </select>
200
+ <Select
201
+ label='Data Key:'
202
+ value={pattern.dataKey}
203
+ options={dataKeyOptions.filter(d => d !== 'Select')}
204
+ initial='Select'
205
+ fieldName={`pattern-dataKey--${patternIndex}`}
206
+ updateField={(section, subsection, fieldName, value) =>
207
+ handlePatternFieldUpdate('dataKey', value, patternIndex)
208
+ }
209
+ />
212
210
  <label htmlFor={`pattern-dataValue--${patternIndex}`}>
213
211
  Data Value:
214
212
  <input
@@ -227,30 +225,24 @@ const PatternSettings = ({ name }: PanelProps) => {
227
225
  value={pattern.label === '' ? '' : pattern.label}
228
226
  />
229
227
  </label>
230
- <label htmlFor={`pattern-type--${patternIndex}`}>Pattern Type:</label>
231
- <select
232
- id={`pattern-type--${patternIndex}`}
228
+ <Select
229
+ label='Pattern Type:'
233
230
  value={pattern?.pattern}
234
- onChange={e => handlePatternFieldUpdate('pattern', e.target.value, patternIndex)}
235
- >
236
- {patternTypes.map((patternName, index) => (
237
- <option value={patternName} key={index}>
238
- {patternName}
239
- </option>
240
- ))}
241
- </select>
242
- <label htmlFor={`pattern-size--${patternIndex}`}>Pattern Size:</label>
243
- <select
244
- id={`pattern-size--${patternIndex}`}
231
+ options={patternTypes}
232
+ fieldName={`pattern-type--${patternIndex}`}
233
+ updateField={(section, subsection, fieldName, value) =>
234
+ handlePatternFieldUpdate('pattern', value, patternIndex)
235
+ }
236
+ />
237
+ <Select
238
+ label='Pattern Size:'
245
239
  value={pattern?.size}
246
- onChange={e => handlePatternFieldUpdate('size', e.target.value, patternIndex)}
247
- >
248
- {['small', 'medium', 'large'].map((size, index) => (
249
- <option value={size} key={index}>
250
- {size}
251
- </option>
252
- ))}
253
- </select>
240
+ options={['small', 'medium', 'large']}
241
+ fieldName={`pattern-size--${patternIndex}`}
242
+ updateField={(section, subsection, fieldName, value) =>
243
+ handlePatternFieldUpdate('size', value, patternIndex)
244
+ }
245
+ />
254
246
  <div className='pattern-input__color'>
255
247
  <label htmlFor='patternColor'>
256
248
  Pattern Color