@cdc/map 4.24.9 → 4.24.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 (34) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcmap.js +26371 -25867
  3. package/examples/private/DEV-9644.json +184 -0
  4. package/package.json +3 -3
  5. package/src/CdcMap.tsx +25 -12
  6. package/src/_stories/CdcMap.stories.tsx +2 -6
  7. package/src/_stories/CdcMapLegend.stories.tsx +86 -0
  8. package/src/_stories/_mock/usa-state-gradient.json +238 -0
  9. package/src/_stories/_mock/wastewater-map.json +208 -0
  10. package/src/components/Annotation/AnnotationDropdown.styles.css +0 -1
  11. package/src/components/CityList.tsx +55 -10
  12. package/src/components/DataTable.tsx +94 -17
  13. package/src/components/EditorPanel/components/EditorPanel.tsx +118 -64
  14. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +27 -23
  15. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +75 -16
  16. package/src/components/Legend/components/Legend.tsx +23 -19
  17. package/src/components/Legend/components/index.scss +23 -10
  18. package/src/components/UsaMap/components/HexIcon.tsx +7 -1
  19. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +56 -11
  20. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +88 -15
  21. package/src/components/UsaMap/components/Territory/TerritoryShape.ts +13 -0
  22. package/src/components/UsaMap/components/UsaMap.County.tsx +49 -48
  23. package/src/components/UsaMap/components/UsaMap.State.tsx +5 -4
  24. package/src/components/UsaMap/helpers/shapes.ts +207 -0
  25. package/src/coreStyles_map.scss +3 -0
  26. package/src/data/initial-state.js +1 -0
  27. package/src/index.jsx +7 -1
  28. package/src/scss/editor-panel.scss +5 -13
  29. package/src/scss/filters.scss +2 -7
  30. package/src/scss/main.scss +4 -9
  31. package/src/scss/map.scss +6 -6
  32. package/src/scss/mixins.scss +47 -0
  33. package/src/types/MapConfig.ts +6 -3
  34. package/src/types/MapContext.ts +0 -1
@@ -1,5 +1,11 @@
1
1
  import { useContext } from 'react'
2
- import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
2
+ import {
3
+ Accordion,
4
+ AccordionItem,
5
+ AccordionItemHeading,
6
+ AccordionItemPanel,
7
+ AccordionItemButton
8
+ } from 'react-accessible-accordion'
3
9
  import ConfigContext from '../../../../context'
4
10
  import { type MapContext } from '../../../../types/MapContext'
5
11
  import Button from '@cdc/core/components/elements/Button'
@@ -41,7 +47,11 @@ const PatternSettings = ({ name }: PanelProps) => {
41
47
  }
42
48
 
43
49
  /** Updates the map pattern at a given index */
44
- const handleUpdateGeoPattern = (value: string, index: number, keyToUpdate: 'dataKey' | 'pattern' | 'dataValue' | 'size' | 'label' | 'color') => {
50
+ const handleUpdateGeoPattern = (
51
+ value: string,
52
+ index: number,
53
+ keyToUpdate: 'dataKey' | 'pattern' | 'dataValue' | 'size' | 'label' | 'color'
54
+ ) => {
45
55
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.states)
46
56
  const updatedPatterns = [...state.map.patterns]
47
57
 
@@ -68,7 +78,9 @@ const PatternSettings = ({ name }: PanelProps) => {
68
78
 
69
79
  // Log a warning if the contrast check fails
70
80
  if (!contrastCheck) {
71
- console.warn(`COVE: pattern contrast check failed on ${geoData?.[state.columns.geo.name]} for ${patternData.dataKey} with:
81
+ console.warn(`COVE: pattern contrast check failed on ${geoData?.[state.columns.geo.name]} for ${
82
+ patternData.dataKey
83
+ } with:
72
84
  pattern color: ${patternColor}
73
85
  contrast: ${getColorContrast(currentFill, patternColor)}
74
86
  `)
@@ -78,7 +90,9 @@ const PatternSettings = ({ name }: PanelProps) => {
78
90
  })
79
91
  })
80
92
 
81
- const editorErrorMessage = updatedPatterns.some(pattern => pattern.contrastCheck === false) ? 'One or more patterns do not pass the WCAG 2.1 contrast ratio of 3:1.' : ''
93
+ const editorErrorMessage = updatedPatterns.some(pattern => pattern.contrastCheck === false)
94
+ ? 'One or more patterns do not pass the WCAG 2.1 contrast ratio of 3:1.'
95
+ : ''
82
96
 
83
97
  // Update the state with the new patterns and error message
84
98
  setState(prevState => ({
@@ -116,7 +130,12 @@ const PatternSettings = ({ name }: PanelProps) => {
116
130
  <AccordionItemButton>{name}</AccordionItemButton>
117
131
  </AccordionItemHeading>
118
132
  <AccordionItemPanel>
119
- {patterns.length > 0 && <Alert type={checkPatternContrasts() ? 'success' : 'danger'} message='Pattern colors must comply with <br /> <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1</a> 3:1 contrast ratio.' />}
133
+ {patterns.length > 0 && (
134
+ <Alert
135
+ type={checkPatternContrasts() ? 'success' : 'danger'}
136
+ message='Pattern colors must comply with <br /> <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1</a> 3:1 contrast ratio.'
137
+ />
138
+ )}
120
139
  <br />
121
140
 
122
141
  {patterns &&
@@ -133,13 +152,26 @@ const PatternSettings = ({ name }: PanelProps) => {
133
152
  <Accordion allowZeroExpanded key={`accordion-pattern--${patternIndex}`}>
134
153
  <AccordionItem>
135
154
  <AccordionItemHeading>
136
- <AccordionItemButton>{pattern.dataKey ? `${pattern.dataKey}: ${pattern.dataValue ?? 'No Value'}` : 'Select Column'}</AccordionItemButton>
155
+ <AccordionItemButton>
156
+ {pattern.dataKey ? `${pattern.dataKey}: ${pattern.dataValue ?? 'No Value'}` : 'Select Column'}
157
+ </AccordionItemButton>
137
158
  </AccordionItemHeading>
138
159
  <AccordionItemPanel>
139
160
  <>
140
- {pattern.contrastCheck ?? true ? <Alert type='success' message='This pattern passes contrast checks' /> : <Alert type='danger' message='Error: <a href="https://webaim.org/resources/contrastchecker/" target="_blank"> Review Color Contrast</a>' />}{' '}
161
+ {pattern.contrastCheck ?? true ? (
162
+ <Alert type='success' message='This pattern passes contrast checks' />
163
+ ) : (
164
+ <Alert
165
+ type='danger'
166
+ message='Error: <a href="https://webaim.org/resources/contrastchecker/" target="_blank"> Review Color Contrast</a>'
167
+ />
168
+ )}{' '}
141
169
  <label htmlFor={`pattern-dataKey--${patternIndex}`}>Data Key:</label>
142
- <select id={`pattern-dataKey--${patternIndex}`} value={pattern.dataKey !== '' ? pattern.dataKey : 'Select'} onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'dataKey')}>
170
+ <select
171
+ id={`pattern-dataKey--${patternIndex}`}
172
+ value={pattern.dataKey !== '' ? pattern.dataKey : 'Select'}
173
+ onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'dataKey')}
174
+ >
143
175
  {/* TODO: sort these? */}
144
176
  {dataKeyOptions.map((d, index) => {
145
177
  return (
@@ -151,14 +183,28 @@ const PatternSettings = ({ name }: PanelProps) => {
151
183
  </select>
152
184
  <label htmlFor={`pattern-dataValue--${patternIndex}`}>
153
185
  Data Value:
154
- <input type='text' onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'dataValue')} id={`pattern-dataValue--${patternIndex}`} value={pattern.dataValue === '' ? '' : pattern.dataValue} />
186
+ <input
187
+ type='text'
188
+ onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'dataValue')}
189
+ id={`pattern-dataValue--${patternIndex}`}
190
+ value={pattern.dataValue === '' ? '' : pattern.dataValue}
191
+ />
155
192
  </label>
156
193
  <label htmlFor={`pattern-label--${patternIndex}`}>
157
194
  Label (optional):
158
- <input type='text' onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'label')} id={`pattern-dataValue--${patternIndex}`} value={pattern.label === '' ? '' : pattern.label} />
195
+ <input
196
+ type='text'
197
+ onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'label')}
198
+ id={`pattern-dataValue--${patternIndex}`}
199
+ value={pattern.label === '' ? '' : pattern.label}
200
+ />
159
201
  </label>
160
202
  <label htmlFor={`pattern-type--${patternIndex}`}>Pattern Type:</label>
161
- <select id={`pattern-type--${patternIndex}`} value={pattern?.pattern} onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'pattern')}>
203
+ <select
204
+ id={`pattern-type--${patternIndex}`}
205
+ value={pattern?.pattern}
206
+ onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'pattern')}
207
+ >
162
208
  {patternTypes.map((patternName, index) => (
163
209
  <option value={patternName} key={index}>
164
210
  {patternName}
@@ -166,7 +212,11 @@ const PatternSettings = ({ name }: PanelProps) => {
166
212
  ))}
167
213
  </select>
168
214
  <label htmlFor={`pattern-size--${patternIndex}`}>Pattern Size:</label>
169
- <select id={`pattern-size--${patternIndex}`} value={pattern?.size} onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'size')}>
215
+ <select
216
+ id={`pattern-size--${patternIndex}`}
217
+ value={pattern?.size}
218
+ onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'size')}
219
+ >
170
220
  {['small', 'medium', 'large'].map((size, index) => (
171
221
  <option value={size} key={index}>
172
222
  {size}
@@ -178,16 +228,25 @@ const PatternSettings = ({ name }: PanelProps) => {
178
228
  Pattern Color
179
229
  <Tooltip style={{ textTransform: 'none' }}>
180
230
  <Tooltip.Target>
181
- <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
231
+ <Icon
232
+ display='question'
233
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
234
+ />
182
235
  </Tooltip.Target>
183
236
  <Tooltip.Content>
184
237
  <p>{`If this setting is used, it is the reponsibility of the visualization author to verify the visualization colors meet WCAG 3:1 contrast ratios.`}</p>
185
238
  </Tooltip.Content>
186
239
  </Tooltip>
187
- <input type='text' value={pattern.color || ''} id='patternColor' name='patternColor' onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'color')} />
240
+ <input
241
+ type='text'
242
+ value={pattern.color || ''}
243
+ id='patternColor'
244
+ name='patternColor'
245
+ onChange={e => handleUpdateGeoPattern(e.target.value, patternIndex, 'color')}
246
+ />
188
247
  </label>
189
248
  </div>
190
- <Button onClick={e => handleRemovePattern(patternIndex)} className='btn btn--remove warn'>
249
+ <Button onClick={e => handleRemovePattern(patternIndex)} className='btn btn-danger'>
191
250
  Remove Pattern
192
251
  </Button>
193
252
  </>
@@ -196,7 +255,7 @@ const PatternSettings = ({ name }: PanelProps) => {
196
255
  </Accordion>
197
256
  )
198
257
  })}
199
- <button className='btn full-width' onClick={handleAddGeoPattern}>
258
+ <button className='btn btn-primary full-width' onClick={handleAddGeoPattern}>
200
259
  Add Geo Pattern
201
260
  </button>
202
261
  </AccordionItemPanel>
@@ -15,18 +15,22 @@ import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
15
15
  import ConfigContext from '../../../context'
16
16
  import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
17
17
  import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
18
- import { type ViewportSize } from '../../../types/MapConfig'
19
18
  import { Group } from '@visx/group'
20
19
  import './index.scss'
20
+ import { ViewportSize } from '@cdc/chart/src/types/ChartConfig'
21
+ import { isMobileHeightViewport } from '@cdc/core/helpers/viewports'
22
+
23
+ const LEGEND_PADDING = 30
21
24
 
22
25
  type LegendProps = {
23
26
  skipId: string
24
- currentViewport: ViewportSize
25
27
  dimensions: DimensionsType
28
+ containerWidthPadding: number
29
+ currentViewport: ViewportSize
26
30
  }
27
31
 
28
32
  const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
29
- const { skipId, currentViewport, dimensions } = props
33
+ const { skipId, dimensions, containerWidthPadding, currentViewport } = props
30
34
 
31
35
  const {
32
36
  // prettier-ignore
@@ -38,7 +42,6 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
38
42
  setRuntimeLegend,
39
43
  state,
40
44
  viewport,
41
- getTextWidth,
42
45
  mapId
43
46
  } = useContext(ConfigContext)
44
47
 
@@ -100,13 +103,14 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
100
103
  })
101
104
  }
102
105
 
103
- const legendList = () => {
104
- const formattedItems = getFormattedLegendItems()
106
+ const legendList = (patternsOnly = false) => {
107
+ const formattedItems = patternsOnly ? [] : getFormattedLegendItems()
108
+ const patternsOnlyFont = isMobileHeightViewport(currentViewport) ? '12px' : '14px'
105
109
  let legendItems
106
110
 
107
111
  legendItems = formattedItems.map((item, idx) => {
108
112
  const handleListItemClass = () => {
109
- let classes = ['legend-container__li']
113
+ let classes = ['legend-container__li', 'd-flex', 'align-items-center']
110
114
  if (item.disabled) classes.push('legend-container__li--disabled')
111
115
  if (item.special) classes.push('legend-container__li--special-class')
112
116
  return classes.join(' ')
@@ -127,11 +131,7 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
127
131
  }}
128
132
  tabIndex={0}
129
133
  >
130
- <LegendShape
131
- shape={state.legend.style === 'boxes' ? 'square' : 'circle'}
132
- viewport={viewport}
133
- fill={item.color}
134
- />
134
+ <LegendShape shape={state.legend.style === 'boxes' ? 'square' : 'circle'} fill={item.color} />
135
135
  <span>{item.label}</span>
136
136
  </li>
137
137
  )
@@ -196,7 +196,9 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
196
196
  />
197
197
  </svg>
198
198
  </span>
199
- <p style={{ lineHeight: '22.4px' }}>{patternData.label || patternData.dataValue || ''}</p>
199
+ <p style={{ lineHeight: '22.4px', fontSize: patternsOnly ? patternsOnlyFont : '16px' }}>
200
+ {patternData.label || patternData.dataValue || ''}
201
+ </p>
200
202
  </li>
201
203
  </>
202
204
  )
@@ -205,6 +207,8 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
205
207
 
206
208
  return legendItems
207
209
  }
210
+ const legendListItems = legendList(state.legend.style === 'gradient')
211
+
208
212
  const { legendClasses } = useDataVizClasses(state, viewport)
209
213
 
210
214
  const handleReset = e => {
@@ -274,15 +278,15 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
274
278
  <LegendGradient
275
279
  labels={getFormattedLegendItems().map(item => item?.label) ?? []}
276
280
  colors={getFormattedLegendItems().map(item => item?.color) ?? []}
277
- values={getFormattedLegendItems().map(item => item?.value) ?? []}
278
281
  dimensions={dimensions}
279
- currentViewport={currentViewport}
282
+ parentPaddingToSubtract={containerWidthPadding + (legend.hideBorder ? 0 : LEGEND_PADDING)}
280
283
  config={state}
281
- getTextWidth={getTextWidth}
282
284
  />
283
- <ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
284
- {state.legend.style === 'gradient' ? '' : legendList()}
285
- </ul>
285
+ {!!legendListItems.length && (
286
+ <ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
287
+ {legendListItems}
288
+ </ul>
289
+ )}
286
290
  {(state.visual.additionalCityStyles.some(c => c.label) || state.visual.cityStyleLabel) && (
287
291
  <>
288
292
  <hr />
@@ -1,5 +1,4 @@
1
- @import '@cdc/core/styles/base';
2
- @import '@cdc/core/styles/heading-colors';
1
+ @import '../../../scss/mixins';
3
2
 
4
3
  .cdc-map-inner-container {
5
4
  .map-container.world aside.side {
@@ -7,7 +6,7 @@
7
6
  }
8
7
  @include breakpointClass(md) {
9
8
  .map-container.world aside.side {
10
- border-top: $lightGray 1px solid;
9
+ border-top: var(--lightGray) 1px solid;
11
10
  position: absolute;
12
11
  box-shadow: rgba(0, 0, 0, 0.2) 0 10px 18px;
13
12
  }
@@ -16,12 +15,12 @@
16
15
  aside {
17
16
  background-color: #fff;
18
17
  z-index: 6;
19
- border-top: $lightGray 1px solid;
18
+ border-top: var(--lightGray) 1px solid;
20
19
 
21
20
  @include breakpointClass(md) {
22
21
  &.bottom,
23
22
  &.top {
24
- border: $lightGray 1px solid;
23
+ border: var(--lightGray) 1px solid;
25
24
  }
26
25
  &.side {
27
26
  z-index: 1;
@@ -32,7 +31,7 @@
32
31
  align-self: flex-start;
33
32
  z-index: 4;
34
33
  right: 1em;
35
- border: $lightGray 1px solid;
34
+ border: var(--lightGray) 1px solid;
36
35
  top: 2em;
37
36
  right: 1em;
38
37
 
@@ -68,8 +67,10 @@
68
67
  }
69
68
 
70
69
  .legend-container {
71
- padding: 1em;
72
70
  position: relative;
71
+ &.legend-padding {
72
+ padding: 15px;
73
+ }
73
74
  .legend-container__title {
74
75
  font-size: 1.3em;
75
76
  padding-bottom: 0;
@@ -101,7 +102,10 @@
101
102
  list-style: none;
102
103
  padding-top: 1em;
103
104
  display: grid;
104
- grid-template-columns: 1fr 1fr;
105
+ grid-template-columns: 1fr;
106
+ @include breakpoint(md) {
107
+ grid-template-columns: 1fr 1fr;
108
+ }
105
109
 
106
110
  button {
107
111
  font-size: unset;
@@ -129,8 +133,11 @@
129
133
  transition: 0.1s opacity;
130
134
  display: flex;
131
135
  cursor: pointer;
132
- white-space: nowrap;
136
+ white-space: wrap;
133
137
  flex-grow: 1;
138
+ @include breakpoint(md) {
139
+ white-space: nowrap;
140
+ }
134
141
 
135
142
  &.legend-container__li--disabled {
136
143
  opacity: 0.4;
@@ -149,17 +156,23 @@
149
156
  & > li {
150
157
  margin-right: 1em;
151
158
  margin-bottom: 1em;
152
- white-space: nowrap;
159
+ white-space: wrap;
153
160
  display: flex;
154
161
  justify-content: center;
155
162
  align-items: center;
156
163
  vertical-align: middle;
164
+ @include breakpoint(md) {
165
+ white-space: nowrap;
166
+ }
157
167
 
158
168
  & svg {
159
169
  vertical-align: baseline;
160
170
  }
161
171
  }
162
172
  }
173
+ .legend-container__ul.patterns-only {
174
+ margin-top: 10px;
175
+ }
163
176
  }
164
177
 
165
178
  .bottom .legend-container__ul--single-column:not(.vertical-sorted) {
@@ -30,7 +30,13 @@ const HexIcon: React.FC<HexIconProps> = props => {
30
30
  )
31
31
  }
32
32
  return (
33
- <Group top={centroid[1] - 5} left={centroid[0] - iconSize} color={textColor} textAnchor='start' key={`hex--${item.key}-${item.value}-${index}`}>
33
+ <Group
34
+ top={centroid[1] - 5}
35
+ left={centroid[0] - iconSize}
36
+ color={textColor}
37
+ textAnchor='start'
38
+ key={`hex--${item.key}-${item.value}-${index}`}
39
+ >
34
40
  {item.shape === 'Arrow Down' && <AiOutlineArrowDown />}
35
41
  {item.shape === 'Arrow Up' && <AiOutlineArrowUp />}
36
42
  {item.shape === 'Arrow Right' && <AiOutlineArrowRight />}
@@ -1,5 +1,5 @@
1
1
  import { useContext } from 'react'
2
- import { geoCentroid, geoPath } from 'd3-geo'
2
+ import { geoCentroid } from 'd3-geo'
3
3
  import ConfigContext from './../../../../context'
4
4
  import { MapContext } from './../../../../types/MapContext'
5
5
  import HexIcon from '../HexIcon'
@@ -31,7 +31,19 @@ const nudges = {
31
31
 
32
32
  // todo: combine hexagonLabel & geoLabel functions
33
33
  // todo: move geoLabel functions outside of components for reusability
34
- const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territory, territoryData, ...props }) => {
34
+ const TerritoryHexagon = ({
35
+ dataTooltipHtml,
36
+ dataTooltipId,
37
+ handleShapeClick,
38
+ label,
39
+ stroke,
40
+ strokeWidth,
41
+ territory,
42
+ territoryData,
43
+ text,
44
+ textColor,
45
+ ...props
46
+ }) => {
35
47
  const { state } = useContext<MapContext>(ConfigContext)
36
48
 
37
49
  const isHex = state.general.displayAsHex
@@ -115,7 +127,17 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
115
127
  let y = state.hexMap.type === 'shapes' ? '30%' : '50%'
116
128
  return (
117
129
  <>
118
- <Text fontSize={14} x={'50%'} y={y} style={{ fill: 'currentColor', stroke: 'initial', fontWeight: 400, opacity: 1, fillOpacity: 1 }} textAnchor='middle' verticalAnchor='middle'>
130
+ <Text
131
+ fontSize={14}
132
+ x={'50%'}
133
+ y={y}
134
+ style={{ fill: 'currentColor', stroke: 'initial', fontWeight: 400, opacity: 1, fillOpacity: 1 }}
135
+ textAnchor='middle'
136
+ verticalAnchor='middle'
137
+ onClick={handleShapeClick}
138
+ data-tooltip-id={dataTooltipId}
139
+ data-tooltip-html={dataTooltipHtml}
140
+ >
119
141
  {abbr.substring(3)}
120
142
  </Text>
121
143
  {state.general.displayAsHex && state.hexMap.type === 'shapes' && getArrowDirection(territoryData, geo, true)}
@@ -127,8 +149,25 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
127
149
 
128
150
  return (
129
151
  <g>
130
- <line x1={centroid[0]} y1={centroid[1]} x2={centroid[0] + dx} y2={centroid[1] + dy} stroke='rgba(0,0,0,.5)' strokeWidth={1} />
131
- <text x={4} strokeWidth='0' fontSize={13} style={{ fill: '#202020' }} alignmentBaseline='middle' transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}>
152
+ <line
153
+ x1={centroid[0]}
154
+ y1={centroid[1]}
155
+ x2={centroid[0] + dx}
156
+ y2={centroid[1] + dy}
157
+ stroke='rgba(0,0,0,.5)'
158
+ strokeWidth={1}
159
+ />
160
+ <text
161
+ x={4}
162
+ strokeWidth='0'
163
+ fontSize={13}
164
+ style={{ fill: '#202020' }}
165
+ alignmentBaseline='middle'
166
+ transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}
167
+ onClick={handleShapeClick}
168
+ data-tooltip-id={dataTooltipId}
169
+ data-tooltip-html={dataTooltipHtml}
170
+ >
132
171
  {abbr.substring(3)}
133
172
  </text>
134
173
  </g>
@@ -136,12 +175,18 @@ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territo
136
175
  }
137
176
 
138
177
  return (
139
- <svg viewBox='0 0 45 51' className='territory-wrapper--hex'>
140
- <g {...props}>
141
- <polygon stroke={stroke} strokeWidth={strokeWidth} points='22 0 44 12.702 44 38.105 22 50.807 0 38.105 0 12.702' />
142
- {state.general.displayAsHex && hexagonLabel(territoryData ? territoryData : geo, stroke, false)}
143
- </g>
144
- </svg>
178
+ territoryData && (
179
+ <svg viewBox='0 0 45 51' className='territory-wrapper--hex'>
180
+ <g {...props} data-tooltip-html={dataTooltipHtml} data-tooltip-id={dataTooltipId} onClick={handleShapeClick}>
181
+ <polygon
182
+ stroke={stroke}
183
+ strokeWidth={strokeWidth}
184
+ points='22 0 44 12.702 44 38.105 22 50.807 0 38.105 0 12.702'
185
+ />
186
+ {state.general.displayAsHex && hexagonLabel(territoryData, stroke, false)}
187
+ </g>
188
+ </svg>
189
+ )
145
190
  )
146
191
  }
147
192
 
@@ -4,21 +4,50 @@ import ConfigContext from './../../../../context'
4
4
  import { type MapContext } from '../../../../types/MapContext'
5
5
  import { patternSizes } from './../../helpers/patternSizes'
6
6
  import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
7
+ import { type TerritoryShape } from './TerritoryShape'
7
8
 
8
- const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPattern, territory, ...props }) => {
9
+ const TerritoryRectangle: React.FC<TerritoryShape> = ({
10
+ dataTooltipId,
11
+ dataTooltipHtml,
12
+ handleShapeClick,
13
+ hasPattern,
14
+ label,
15
+ stroke,
16
+ strokeWidth,
17
+ territory,
18
+ text,
19
+ textColor,
20
+ ...props
21
+ }) => {
9
22
  const { state, supportedTerritories } = useContext<MapContext>(ConfigContext)
10
23
  const { territoryData, ...otherProps } = props
24
+ const rectanglePath =
25
+ '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'
11
26
 
12
27
  return (
13
28
  <svg viewBox='0 0 45 28' key={territory} className={territory}>
14
- <g {...otherProps} strokeLinejoin='round' tabIndex={-1}>
15
- <path
16
- stroke={stroke}
17
- strokeWidth={strokeWidth}
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'
19
- {...otherProps}
20
- />
21
- <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: textColor, strokeWidth: 1 }} className='territory-text' paintOrder='stroke'>
29
+ <g
30
+ {...otherProps}
31
+ strokeLinejoin='round'
32
+ tabIndex={-1}
33
+ onClick={handleShapeClick}
34
+ data-tooltip-id={dataTooltipId}
35
+ data-tooltip-html={dataTooltipHtml}
36
+ >
37
+ <path stroke={stroke} strokeWidth={strokeWidth} d={rectanglePath} {...otherProps} />
38
+ <text
39
+ textAnchor='middle'
40
+ dominantBaseline='middle'
41
+ x='50%'
42
+ y='54%'
43
+ fill={text}
44
+ style={{ stroke: textColor, strokeWidth: 1 }}
45
+ className='territory-text'
46
+ paintOrder='stroke'
47
+ onClick={handleShapeClick}
48
+ data-tooltip-id={dataTooltipId}
49
+ data-tooltip-html={dataTooltipHtml}
50
+ >
22
51
  {label}
23
52
  </text>
24
53
 
@@ -31,18 +60,62 @@ const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPa
31
60
 
32
61
  return (
33
62
  <>
34
- {patternData?.pattern === 'waves' && <PatternWaves id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 10} width={patternSizes[patternData?.size] ?? 10} fill={patternColor} complement />}
35
- {patternData?.pattern === 'circles' && <PatternCircles id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 10} width={patternSizes[patternData?.size] ?? 10} fill={patternColor} complement />}
36
- {patternData?.pattern === 'lines' && <PatternLines id={`territory-${patternData?.dataKey}--${patternIndex}`} height={patternSizes[patternData?.size] ?? 6} width={patternSizes[patternData?.size] ?? 6} stroke={patternColor} strokeWidth={1} orientation={['diagonalRightToLeft']} />}
63
+ {patternData?.pattern === 'waves' && (
64
+ <PatternWaves
65
+ id={`territory-${patternData?.dataKey}--${patternIndex}`}
66
+ height={patternSizes[patternData?.size] ?? 10}
67
+ width={patternSizes[patternData?.size] ?? 10}
68
+ fill={patternColor}
69
+ complement
70
+ />
71
+ )}
72
+ {patternData?.pattern === 'circles' && (
73
+ <PatternCircles
74
+ id={`territory-${patternData?.dataKey}--${patternIndex}`}
75
+ height={patternSizes[patternData?.size] ?? 10}
76
+ width={patternSizes[patternData?.size] ?? 10}
77
+ fill={patternColor}
78
+ complement
79
+ />
80
+ )}
81
+ {patternData?.pattern === 'lines' && (
82
+ <PatternLines
83
+ id={`territory-${patternData?.dataKey}--${patternIndex}`}
84
+ height={patternSizes[patternData?.size] ?? 6}
85
+ width={patternSizes[patternData?.size] ?? 6}
86
+ stroke={patternColor}
87
+ strokeWidth={1}
88
+ orientation={['diagonalRightToLeft']}
89
+ />
90
+ )}
37
91
  <path
38
92
  stroke={stroke}
39
93
  strokeWidth={strokeWidth}
40
- 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'
94
+ d={rectanglePath}
41
95
  fill={`url(#territory-${patternData?.dataKey}--${patternIndex})`}
42
96
  color={patternData ? 'white' : textColor}
43
- className={[`territory-pattern-${patternData.dataKey}`, `territory-pattern-${patternData.dataKey}--${patternData.dataValue}`].join(' ')}
97
+ className={[
98
+ `territory-pattern-${patternData.dataKey}`,
99
+ `territory-pattern-${patternData.dataKey}--${patternData.dataValue}`
100
+ ].join(' ')}
44
101
  />
45
- <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ fill: patternData ? 'white' : 'black', stroke: patternData ? 'black' : textColor, strokeWidth: patternData ? 6 : 0 }} className='territory-text' paint-order='stroke'>
102
+ <text
103
+ textAnchor='middle'
104
+ dominantBaseline='middle'
105
+ x='50%'
106
+ y='54%'
107
+ fill={text}
108
+ style={{
109
+ fill: patternData ? 'white' : 'black',
110
+ stroke: patternData ? 'black' : textColor,
111
+ strokeWidth: patternData ? 6 : 0
112
+ }}
113
+ className='territory-text'
114
+ paint-order='stroke'
115
+ onClick={handleShapeClick}
116
+ data-tooltip-id={dataTooltipId}
117
+ data-tooltip-html={dataTooltipHtml}
118
+ >
46
119
  {label}
47
120
  </text>
48
121
  </>
@@ -0,0 +1,13 @@
1
+ export type TerritoryShape = {
2
+ handleShapeClick: () => void
3
+ dataTooltipHtml: string
4
+ dataTooltipId: string
5
+ hasPattern: boolean
6
+ label: string
7
+ stroke: string
8
+ strokeWidth: number
9
+ territory: string
10
+ territoryData: object
11
+ text: string
12
+ textColor: string
13
+ }