@cdc/map 4.24.5 → 4.24.9

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 (67) hide show
  1. package/dist/cdcmap.js +71853 -64936
  2. package/examples/annotation/index.json +552 -0
  3. package/examples/annotation/usa-map.json +900 -0
  4. package/examples/county-year.csv +10 -0
  5. package/examples/default-geocode.json +44 -10
  6. package/examples/default-patterns.json +0 -2
  7. package/examples/default-single-state.json +279 -108
  8. package/examples/map-issue-3.json +646 -0
  9. package/examples/single-state-filter.json +153 -0
  10. package/index.html +10 -6
  11. package/package.json +6 -5
  12. package/src/CdcMap.tsx +367 -199
  13. package/src/_stories/CdcMap.stories.tsx +14 -0
  14. package/src/_stories/_mock/DEV-7286.json +165 -0
  15. package/src/_stories/_mock/DEV-8942.json +270 -0
  16. package/src/components/Annotation/Annotation.Draggable.styles.css +18 -0
  17. package/src/components/Annotation/Annotation.Draggable.tsx +152 -0
  18. package/src/components/Annotation/AnnotationDropdown.styles.css +14 -0
  19. package/src/components/Annotation/AnnotationDropdown.tsx +70 -0
  20. package/src/components/Annotation/AnnotationList.styles.css +45 -0
  21. package/src/components/Annotation/AnnotationList.tsx +42 -0
  22. package/src/components/Annotation/index.tsx +11 -0
  23. package/src/components/{BubbleList.jsx → BubbleList.tsx} +1 -1
  24. package/src/components/{CityList.jsx → CityList.tsx} +28 -2
  25. package/src/components/{DataTable.jsx → DataTable.tsx} +2 -2
  26. package/src/components/EditorPanel/components/EditorPanel.tsx +650 -129
  27. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +336 -0
  28. package/src/components/EditorPanel/components/{Panel.PatternSettings.tsx → Panels/Panel.PatternSettings.tsx} +63 -13
  29. package/src/components/EditorPanel/components/{Panels.tsx → Panels/index.tsx} +3 -0
  30. package/src/components/Legend/components/Legend.tsx +125 -42
  31. package/src/components/Legend/components/index.scss +42 -42
  32. package/src/components/Modal.tsx +25 -0
  33. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +74 -0
  34. package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +29 -0
  35. package/src/components/UsaMap/components/SingleState/index.tsx +9 -0
  36. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +4 -3
  37. package/src/components/UsaMap/components/UsaMap.County.tsx +114 -36
  38. package/src/components/UsaMap/components/UsaMap.Region.tsx +2 -0
  39. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +175 -206
  40. package/src/components/UsaMap/components/UsaMap.State.tsx +188 -44
  41. package/src/components/UsaMap/data/us-extended-geography.json +1 -0
  42. package/src/components/UsaMap/helpers/map.ts +111 -0
  43. package/src/components/WorldMap/WorldMap.tsx +17 -32
  44. package/src/components/ZoomControls.tsx +41 -0
  45. package/src/data/initial-state.js +11 -2
  46. package/src/data/supported-geos.js +15 -4
  47. package/src/helpers/generateColorsArray.ts +13 -0
  48. package/src/helpers/generateRuntimeLegendHash.ts +23 -0
  49. package/src/helpers/getUniqueValues.ts +19 -0
  50. package/src/helpers/hashObj.ts +25 -0
  51. package/src/helpers/tests/generateColorsArray.test.ts +18 -0
  52. package/src/helpers/tests/generateRuntimeLegendHash.test.ts +11 -0
  53. package/src/helpers/tests/hashObj.test.ts +10 -0
  54. package/src/hooks/useStateZoom.tsx +157 -0
  55. package/src/hooks/{useZoomPan.js → useZoomPan.ts} +6 -5
  56. package/src/scss/editor-panel.scss +0 -4
  57. package/src/scss/main.scss +23 -1
  58. package/src/scss/map.scss +14 -3
  59. package/src/types/MapConfig.ts +9 -1
  60. package/src/types/MapContext.ts +16 -2
  61. package/LICENSE +0 -201
  62. package/src/components/Modal.jsx +0 -22
  63. package/src/test/CdcMap.test.jsx +0 -19
  64. /package/src/components/EditorPanel/components/{Panel.PatternSettings-style.css → Panels/Panel.PatternSettings-style.css} +0 -0
  65. /package/src/components/{Geo.jsx → Geo.tsx} +0 -0
  66. /package/src/components/{NavigationMenu.jsx → NavigationMenu.tsx} +0 -0
  67. /package/src/components/{ZoomableGroup.jsx → ZoomableGroup.tsx} +0 -0
@@ -1,9 +1,13 @@
1
1
  //TODO: Move legends to core
2
- import { forwardRef, useContext } from 'react'
2
+ import { forwardRef, useContext, useId } from 'react'
3
3
  import parse from 'html-react-parser'
4
4
 
5
+ //types
6
+ import { DimensionsType } from '@cdc/core/types/Dimensions'
7
+
5
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
- import LegendCircle from '@cdc/core/components/LegendCircle'
9
+ import LegendShape from '@cdc/core/components/LegendShape'
10
+ import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
7
11
  import LegendItemHex from './LegendItem.Hex'
8
12
  import Button from '@cdc/core/components/elements/Button'
9
13
 
@@ -11,18 +15,21 @@ import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
11
15
  import ConfigContext from '../../../context'
12
16
  import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
13
17
  import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
18
+ import { type ViewportSize } from '../../../types/MapConfig'
14
19
  import { Group } from '@visx/group'
15
20
  import './index.scss'
16
21
 
17
22
  type LegendProps = {
18
23
  skipId: string
24
+ currentViewport: ViewportSize
25
+ dimensions: DimensionsType
19
26
  }
20
27
 
21
- const Legend = forwardRef((props, ref) => {
22
- const { skipId } = props
28
+ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
29
+ const { skipId, currentViewport, dimensions } = props
23
30
 
24
- // prettier-ignore
25
31
  const {
32
+ // prettier-ignore
26
33
  displayDataAsText,
27
34
  resetLegendToggles,
28
35
  runtimeFilters,
@@ -31,10 +38,11 @@ const Legend = forwardRef((props, ref) => {
31
38
  setRuntimeLegend,
32
39
  state,
33
40
  viewport,
41
+ getTextWidth,
42
+ mapId
34
43
  } = useContext(ConfigContext)
35
44
 
36
45
  const { legend } = state
37
- const fontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? { fontSize: '11px' } : null
38
46
 
39
47
  // Toggles if a legend is active and being applied to the map and data table.
40
48
  const toggleLegendActive = (i, legendLabel) => {
@@ -52,17 +60,15 @@ const Legend = forwardRef((props, ref) => {
52
60
 
53
61
  setRuntimeLegend(newLegend)
54
62
 
55
- setAccessibleStatus(`Disabled legend item ${legendLabel ?? ''}. Please reference the data table to see updated values.`)
63
+ setAccessibleStatus(
64
+ `Disabled legend item ${legendLabel ?? ''}. Please reference the data table to see updated values.`
65
+ )
56
66
  }
57
-
58
- const legendList = () => {
59
- let legendItems
60
-
61
- legendItems = runtimeLegend.map((entry, idx) => {
67
+ const getFormattedLegendItems = () => {
68
+ return runtimeLegend.map((entry, idx) => {
62
69
  const entryMax = displayDataAsText(entry.max, 'primary')
63
70
 
64
71
  const entryMin = displayDataAsText(entry.min, 'primary')
65
-
66
72
  let formattedText = `${entryMin}${entryMax !== entryMin ? ` - ${entryMax}` : ''}`
67
73
 
68
74
  // If interval, add some formatting
@@ -70,8 +76,6 @@ const Legend = forwardRef((props, ref) => {
70
76
  formattedText = `${entryMin} - < ${entryMax}`
71
77
  }
72
78
 
73
- const { disabled } = entry
74
-
75
79
  if (legend.type === 'category') {
76
80
  formattedText = displayDataAsText(entry.value, 'primary')
77
81
  }
@@ -86,32 +90,49 @@ const Legend = forwardRef((props, ref) => {
86
90
  legendLabel = entry.label || entry.value
87
91
  }
88
92
 
93
+ return {
94
+ color: entry.color,
95
+ label: legendLabel,
96
+ disabled: entry.disabled,
97
+ special: entry.hasOwnProperty('special'),
98
+ value: [entry.min, entry.max]
99
+ }
100
+ })
101
+ }
102
+
103
+ const legendList = () => {
104
+ const formattedItems = getFormattedLegendItems()
105
+ let legendItems
106
+
107
+ legendItems = formattedItems.map((item, idx) => {
89
108
  const handleListItemClass = () => {
90
109
  let classes = ['legend-container__li']
91
- if (disabled) classes.push('legend-container__li--disabled')
92
- if (entry.hasOwnProperty('special')) classes.push('legend-container__li--special-class')
93
- return classes
110
+ if (item.disabled) classes.push('legend-container__li--disabled')
111
+ if (item.special) classes.push('legend-container__li--special-class')
112
+ return classes.join(' ')
94
113
  }
95
114
 
96
115
  return (
97
116
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions
98
117
  <li
99
- style={fontSize}
100
- className={handleListItemClass().join(' ')}
118
+ className={handleListItemClass()}
101
119
  key={idx}
102
- title={`Legend item ${legendLabel} - Click to disable`}
103
- onClick={() => {
104
- toggleLegendActive(idx, legendLabel)
105
- }}
120
+ title={`Legend item ${item.label} - Click to disable`}
121
+ onClick={() => toggleLegendActive(idx, item.label)}
106
122
  onKeyDown={e => {
107
123
  if (e.key === 'Enter') {
108
124
  e.preventDefault()
109
- toggleLegendActive(idx, legendLabel)
125
+ toggleLegendActive(idx, item.label)
110
126
  }
111
127
  }}
112
128
  tabIndex={0}
113
129
  >
114
- <LegendCircle viewport={viewport} fill={entry.color} /> <span>{legendLabel}</span>
130
+ <LegendShape
131
+ shape={state.legend.style === 'boxes' ? 'square' : 'circle'}
132
+ viewport={viewport}
133
+ fill={item.color}
134
+ />
135
+ <span>{item.label}</span>
115
136
  </li>
116
137
  )
117
138
  })
@@ -131,13 +152,48 @@ const Legend = forwardRef((props, ref) => {
131
152
 
132
153
  legendItems.push(
133
154
  <>
134
- <li className={`legend-container__li legend-container__li--geo-pattern`} aria-label='You are on a pattern button. We dont support toggling patterns on this legend at the moment, but provide the area as being focusable for congruity.' tabIndex={0}>
155
+ <li
156
+ className={`legend-container__li legend-container__li--geo-pattern`}
157
+ aria-label='You are on a pattern button. We dont support toggling patterns on this legend at the moment, but provide the area as being focusable for congruity.'
158
+ tabIndex={0}
159
+ >
135
160
  <span className='legend-item' style={{ border: 'unset' }}>
136
161
  <svg width={legendSize} height={legendSize}>
137
- {pattern === 'waves' && <PatternWaves id={`${dataKey}--${patternDataIndex}`} height={sizes[size] ?? 10} width={sizes[size] ?? 10} fill={defaultPatternColor} />}
138
- {pattern === 'circles' && <PatternCircles id={`${dataKey}--${patternDataIndex}`} height={sizes[size] ?? 10} width={sizes[size] ?? 10} fill={defaultPatternColor} />}
139
- {pattern === 'lines' && <PatternLines id={`${dataKey}--${patternDataIndex}`} height={sizes[size] ?? 6} width={sizes[size] ?? 10} stroke={defaultPatternColor} strokeWidth={2} orientation={['diagonalRightToLeft']} />}
140
- <circle id={dataKey} fill={`url(#${dataKey}--${patternDataIndex})`} r={legendSize / 2} cx={legendSize / 2} cy={legendSize / 2} stroke='#0000004d' strokeWidth={1} />
162
+ {pattern === 'waves' && (
163
+ <PatternWaves
164
+ id={`${mapId}--${dataKey}--${patternDataIndex}`}
165
+ height={sizes[size] ?? 10}
166
+ width={sizes[size] ?? 10}
167
+ fill={defaultPatternColor}
168
+ />
169
+ )}
170
+ {pattern === 'circles' && (
171
+ <PatternCircles
172
+ id={`${mapId}--${dataKey}--${patternDataIndex}`}
173
+ height={sizes[size] ?? 10}
174
+ width={sizes[size] ?? 10}
175
+ fill={defaultPatternColor}
176
+ />
177
+ )}
178
+ {pattern === 'lines' && (
179
+ <PatternLines
180
+ id={`${mapId}--${dataKey}--${patternDataIndex}`}
181
+ height={sizes[size] ?? 6}
182
+ width={sizes[size] ?? 10}
183
+ stroke={defaultPatternColor}
184
+ strokeWidth={2}
185
+ orientation={['diagonalRightToLeft']}
186
+ />
187
+ )}
188
+ <circle
189
+ id={dataKey}
190
+ fill={`url(#${mapId}--${dataKey}--${patternDataIndex})`}
191
+ r={legendSize / 2}
192
+ cx={legendSize / 2}
193
+ cy={legendSize / 2}
194
+ stroke='#0000004d'
195
+ strokeWidth={1}
196
+ />
141
197
  </svg>
142
198
  </span>
143
199
  <p style={{ lineHeight: '22.4px' }}>{patternData.label || patternData.dataValue || ''}</p>
@@ -149,7 +205,6 @@ const Legend = forwardRef((props, ref) => {
149
205
 
150
206
  return legendItems
151
207
  }
152
-
153
208
  const { legendClasses } = useDataVizClasses(state, viewport)
154
209
 
155
210
  const handleReset = e => {
@@ -164,7 +219,15 @@ const Legend = forwardRef((props, ref) => {
164
219
  }
165
220
  }
166
221
 
167
- const pin = <path className='marker' d='M0,0l-8.8-17.7C-12.1-24.3-7.4-32,0-32h0c7.4,0,12.1,7.7,8.8,14.3L0,0z' strokeWidth={2} stroke={'black'} transform={`scale(0.5)`} />
222
+ const pin = (
223
+ <path
224
+ className='marker'
225
+ d='M0,0l-8.8-17.7C-12.1-24.3-7.4-32,0-32h0c7.4,0,12.1,7.7,8.8,14.3L0,0z'
226
+ strokeWidth={2}
227
+ stroke={'black'}
228
+ transform={`scale(0.5)`}
229
+ />
230
+ )
168
231
 
169
232
  const cityStyleShapes = {
170
233
  pin: pin,
@@ -178,13 +241,18 @@ const Legend = forwardRef((props, ref) => {
178
241
  return (
179
242
  <ErrorBoundary component='Sidebar'>
180
243
  <div className='legends'>
181
- <aside id={skipId || 'legend'} className={legendClasses.aside.join(' ') || ''} role='region' aria-label='Legend' tabIndex={0} ref={ref}>
244
+ <aside
245
+ id={skipId || 'legend'}
246
+ className={legendClasses.aside.join(' ') || ''}
247
+ role='region'
248
+ aria-label='Legend'
249
+ tabIndex={0}
250
+ ref={ref}
251
+ >
182
252
  <section className={legendClasses.section.join(' ') || ''} aria-label='Map Legend'>
183
253
  {legend.title && <h3 className={legendClasses.title.join(' ') || ''}>{parse(legend.title)}</h3>}
184
254
  {legend.dynamicDescription === false && legend.description && (
185
- <p style={fontSize} className={legendClasses.description.join(' ') || ''}>
186
- {parse(legend.description)}
187
- </p>
255
+ <p className={legendClasses.description.join(' ') || ''}>{parse(legend.description)}</p>
188
256
  )}
189
257
  {legend.dynamicDescription === true &&
190
258
  runtimeFilters.map((filter, idx) => {
@@ -195,15 +263,25 @@ const Legend = forwardRef((props, ref) => {
195
263
 
196
264
  if (desc.length > 0) {
197
265
  return (
198
- <p style={fontSize} key={`dynamic-description-${lookupStr}`} className={`dynamic-legend-description-${lookupStr}`}>
266
+ <p key={`dynamic-description-${lookupStr}`} className={`dynamic-legend-description-${lookupStr}`}>
199
267
  {desc}
200
268
  </p>
201
269
  )
202
270
  }
203
271
  return true
204
272
  })}
273
+
274
+ <LegendGradient
275
+ labels={getFormattedLegendItems().map(item => item?.label) ?? []}
276
+ colors={getFormattedLegendItems().map(item => item?.color) ?? []}
277
+ values={getFormattedLegendItems().map(item => item?.value) ?? []}
278
+ dimensions={dimensions}
279
+ currentViewport={currentViewport}
280
+ config={state}
281
+ getTextWidth={getTextWidth}
282
+ />
205
283
  <ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
206
- {legendList()}
284
+ {state.legend.style === 'gradient' ? '' : legendList()}
207
285
  </ul>
208
286
  {(state.visual.additionalCityStyles.some(c => c.label) || state.visual.cityStyleLabel) && (
209
287
  <>
@@ -212,7 +290,10 @@ const Legend = forwardRef((props, ref) => {
212
290
  {state.visual.cityStyleLabel && (
213
291
  <div>
214
292
  <svg>
215
- <Group top={state.visual.cityStyle === 'pin' ? 19 : state.visual.cityStyle === 'triangle' ? 13 : 11} left={10}>
293
+ <Group
294
+ top={state.visual.cityStyle === 'pin' ? 19 : state.visual.cityStyle === 'triangle' ? 13 : 11}
295
+ left={10}
296
+ >
216
297
  {cityStyleShapes[state.visual.cityStyle.toLowerCase()]}
217
298
  </Group>
218
299
  </svg>
@@ -229,7 +310,7 @@ const Legend = forwardRef((props, ref) => {
229
310
  {cityStyleShapes[shape.toLowerCase()]}
230
311
  </Group>
231
312
  </svg>
232
- <p style={fontSize}>{label}</p>
313
+ <p>{label}</p>
233
314
  </div>
234
315
  )
235
316
  )}
@@ -239,7 +320,9 @@ const Legend = forwardRef((props, ref) => {
239
320
  {runtimeLegend.disabledAmt > 0 && <Button onClick={handleReset}>Reset</Button>}
240
321
  </section>
241
322
  </aside>
242
- {state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex && <LegendItemHex state={state} runtimeLegend={runtimeLegend} viewport={viewport} />}
323
+ {state.hexMap.shapeGroups?.length > 0 && state.hexMap.type === 'shapes' && state.general.displayAsHex && (
324
+ <LegendItemHex state={state} runtimeLegend={runtimeLegend} viewport={viewport} />
325
+ )}
243
326
  </div>
244
327
  </ErrorBoundary>
245
328
  )
@@ -17,8 +17,10 @@
17
17
  background-color: #fff;
18
18
  z-index: 6;
19
19
  border-top: $lightGray 1px solid;
20
+
20
21
  @include breakpointClass(md) {
21
- &.bottom {
22
+ &.bottom,
23
+ &.top {
22
24
  border: $lightGray 1px solid;
23
25
  }
24
26
  &.side {
@@ -35,8 +37,12 @@
35
37
  right: 1em;
36
38
 
37
39
  ul.vertical-sorted {
40
+ display: block;
38
41
  column-count: 2;
39
42
  column-fill: balance;
43
+ & > li {
44
+ white-space: nowrap;
45
+ }
40
46
  }
41
47
 
42
48
  ul:not(.vertical-sorted) {
@@ -47,29 +53,17 @@
47
53
  flex-wrap: wrap;
48
54
  }
49
55
  }
56
+ &.no-border {
57
+ border: none;
58
+ }
50
59
 
51
- &.bottom {
60
+ &.bottom,
61
+ &.top {
52
62
  ul.legend-container__ul.vertical-sorted {
53
63
  display: block;
54
64
  column-count: 2;
55
65
  column-fill: balance;
56
66
  }
57
-
58
- ul.legend-container__ul {
59
- display: flex;
60
- flex-direction: row;
61
- flex-wrap: wrap;
62
-
63
- li {
64
- width: 50%;
65
- }
66
- }
67
-
68
- ul.single-row {
69
- display: block;
70
- column-count: initial;
71
- column-fill: auto;
72
- }
73
67
  }
74
68
  }
75
69
 
@@ -103,9 +97,12 @@
103
97
  p {
104
98
  line-height: 1.4em;
105
99
  }
106
- .legend-container__ul {
100
+ .legend-container__ul:not(.single-row) {
107
101
  list-style: none;
108
102
  padding-top: 1em;
103
+ display: grid;
104
+ grid-template-columns: 1fr 1fr;
105
+
109
106
  button {
110
107
  font-size: unset;
111
108
  background: transparent;
@@ -132,6 +129,7 @@
132
129
  transition: 0.1s opacity;
133
130
  display: flex;
134
131
  cursor: pointer;
132
+ white-space: nowrap;
135
133
  flex-grow: 1;
136
134
 
137
135
  &.legend-container__li--disabled {
@@ -139,6 +137,29 @@
139
137
  }
140
138
  }
141
139
  }
140
+ .legend-container__ul.single-row {
141
+ width: 100%;
142
+ list-style: none;
143
+ display: flex;
144
+ flex-direction: row;
145
+ align-items: center;
146
+ justify-content: flex-start;
147
+ flex-wrap: wrap;
148
+
149
+ & > li {
150
+ margin-right: 1em;
151
+ margin-bottom: 1em;
152
+ white-space: nowrap;
153
+ display: flex;
154
+ justify-content: center;
155
+ align-items: center;
156
+ vertical-align: middle;
157
+
158
+ & svg {
159
+ vertical-align: baseline;
160
+ }
161
+ }
162
+ }
142
163
  }
143
164
 
144
165
  .bottom .legend-container__ul--single-column:not(.vertical-sorted) {
@@ -150,6 +171,7 @@
150
171
 
151
172
  .legend-container__li {
152
173
  width: 100%;
174
+ white-space: nowrap;
153
175
  }
154
176
  }
155
177
 
@@ -175,30 +197,8 @@
175
197
  }
176
198
  }
177
199
 
178
- &.bottom.single-row {
179
- width: 100%;
180
- .legend-container ul {
181
- flex-direction: row;
182
- align-items: baseline;
183
- justify-content: flex-start;
184
- flex-wrap: wrap;
185
- li {
186
- justify-items: center;
187
- line-break: loose;
188
- align-items: center;
189
- width: auto;
190
- padding-right: 1em;
191
- padding-bottom: 1em;
192
- display: inline-block;
193
- & > span {
194
- margin: 0 !important;
195
- }
196
- }
197
- }
198
- }
199
-
200
200
  @include breakpointClass(sm) {
201
- .legend-container ul {
201
+ .legend-container ul:not(.single-row) {
202
202
  align-items: flex-start;
203
203
  justify-content: space-between;
204
204
  li {
@@ -0,0 +1,25 @@
1
+ import { useContext } from 'react'
2
+ import LegendShape from '@cdc/core/components/LegendShape'
3
+ import ConfigContext from '../context'
4
+ import Icon from '@cdc/core/components/ui/Icon'
5
+
6
+ const Modal = () => {
7
+ const { applyTooltipsToGeo, capitalize, applyLegendToRow, viewport, type, content } = useContext(ConfigContext)
8
+
9
+ const tooltip = applyTooltipsToGeo(content.geoName, content.keyedData, 'jsx')
10
+
11
+ const legendColors = applyLegendToRow(content.keyedData)
12
+
13
+ return (
14
+ <section
15
+ className={capitalize ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport}
16
+ aria-hidden='true'
17
+ >
18
+ {type === 'data' && <LegendShape fill={legendColors[0]} />}
19
+ <div className='content'>{tooltip}</div>
20
+ <Icon display='close' alt='Close Modal' size={20} color='#000' className='modal-close' />
21
+ </section>
22
+ )
23
+ }
24
+
25
+ export default Modal
@@ -0,0 +1,74 @@
1
+ import React, { useContext } from 'react'
2
+ import ConfigContext from '../../../../context'
3
+ import { MapContext } from '../../../../types/MapContext'
4
+
5
+ interface CountyOutputProps {
6
+ counties: any[]
7
+ scale: number
8
+ geoStrokeColor: string
9
+ tooltipId: string
10
+ path: any
11
+ }
12
+
13
+ const CountyOutput: React.FC<CountyOutputProps> = ({ path, counties, scale, geoStrokeColor, tooltipId }) => {
14
+ const { applyTooltipsToGeo, applyLegendToRow, displayGeoName, state, data, geoClickHandler } = useContext<MapContext>(ConfigContext)
15
+ return (
16
+ <>
17
+ {counties.map(county => {
18
+ // Map the name from the geo data with the appropriate key for the processed data
19
+ const geoKey = county.id
20
+
21
+ if (!geoKey) return null
22
+
23
+ const countyPath = path(county)
24
+
25
+ const geoData = data[county.id]
26
+ let legendColors
27
+
28
+ // Once we receive data for this geographic item, setup variables.
29
+ if (geoData !== undefined) {
30
+ legendColors = applyLegendToRow(geoData)
31
+ }
32
+
33
+ const geoDisplayName = displayGeoName(geoKey)
34
+
35
+ // For some reason, these two geos are breaking the display.
36
+ if (geoDisplayName === 'Franklin City' || geoDisplayName === 'Waynesboro') return null
37
+
38
+ const toolTip = applyTooltipsToGeo(geoDisplayName, geoData)
39
+
40
+ if (legendColors && legendColors[0] !== '#000000') {
41
+ const styles = {
42
+ fill: legendColors[0],
43
+ cursor: 'default',
44
+ '&:hover': {
45
+ fill: legendColors[1]
46
+ },
47
+ '&:active': {
48
+ fill: legendColors[2]
49
+ }
50
+ }
51
+
52
+ // When to add pointer cursor
53
+ if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'hover') {
54
+ styles.cursor = 'pointer'
55
+ }
56
+
57
+ return (
58
+ <g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')} county--${geoData[state.columns.geo.name]}`} style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip}>
59
+ <path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
60
+ </g>
61
+ )
62
+ } else {
63
+ return (
64
+ <g key={`key--${county.id}`} className={`county county--${geoDisplayName.split(' ').join('')}`} style={{ fill: '#e6e6e6' }} data-tooltip-id={`tooltip__${tooltipId}`} data-tooltip-html={toolTip}>
65
+ <path tabIndex={-1} className={`county`} stroke={geoStrokeColor} d={countyPath} strokeWidth={0.75 / scale} />
66
+ </g>
67
+ )
68
+ }
69
+ })}
70
+ </>
71
+ )
72
+ }
73
+
74
+ export default CountyOutput
@@ -0,0 +1,29 @@
1
+ import { useContext } from 'react'
2
+ import { mesh, Topology } from 'topojson-client'
3
+ import ConfigContext from '../../../../context'
4
+
5
+ type StateOutputProps = {
6
+ topoData: Topology
7
+ path: any
8
+ scale: any
9
+ }
10
+
11
+ const StateOutput: React.FC<StateOutputProps> = ({ topoData, path, scale, stateToShow }: StateOutputProps) => {
12
+ const { state } = useContext(ConfigContext)
13
+ if (!topoData?.objects?.states) return null
14
+ let geo = topoData.objects.states.geometries.filter(s => {
15
+ return s.properties.name === state.general.statePicked.stateName
16
+ })
17
+
18
+ const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
19
+
20
+ let stateLines = path(mesh(topoData, geo[0]))
21
+
22
+ return (
23
+ <g key={'single-state'} className='single-state' style={{ fill: '#E6E6E6' }} stroke={geoStrokeColor} strokeWidth={0.95 / scale}>
24
+ <path tabIndex={-1} className='state-path' d={stateLines} />
25
+ </g>
26
+ )
27
+ }
28
+
29
+ export default StateOutput
@@ -0,0 +1,9 @@
1
+ import CountyOutput from './SingleState.CountyOutput'
2
+ import StateOutput from './SingleState.StateOutput'
3
+
4
+ const SingleState = {
5
+ StateOutput,
6
+ CountyOutput
7
+ }
8
+
9
+ export default SingleState
@@ -7,15 +7,16 @@ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
7
7
 
8
8
  const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPattern, territory, ...props }) => {
9
9
  const { state, supportedTerritories } = useContext<MapContext>(ConfigContext)
10
+ const { territoryData, ...otherProps } = props
10
11
 
11
12
  return (
12
- <svg viewBox='0 0 45 28'>
13
- <g {...props} strokeLinejoin='round' tabIndex={-1}>
13
+ <svg viewBox='0 0 45 28' key={territory} className={territory}>
14
+ <g {...otherProps} strokeLinejoin='round' tabIndex={-1}>
14
15
  <path
15
16
  stroke={stroke}
16
17
  strokeWidth={strokeWidth}
17
18
  d='M40,0.5 C41.2426407,0.5 42.3676407,1.00367966 43.1819805,1.81801948 C43.9963203,2.63235931 44.5,3.75735931 44.5,5 L44.5,5 L44.5,23 C44.5,24.2426407 43.9963203,25.3676407 43.1819805,26.1819805 C42.3676407,26.9963203 41.2426407,27.5 40,27.5 L40,27.5 L5,27.5 C3.75735931,27.5 2.63235931,26.9963203 1.81801948,26.1819805 C1.00367966,25.3676407 0.5,24.2426407 0.5,23 L0.5,23 L0.5,5 C0.5,3.75735931 1.00367966,2.63235931 1.81801948,1.81801948 C2.63235931,1.00367966 3.75735931,0.5 5,0.5 L5,0.5 Z'
18
- {...props}
19
+ {...otherProps}
19
20
  />
20
21
  <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: textColor, strokeWidth: 1 }} className='territory-text' paintOrder='stroke'>
21
22
  {label}