@cdc/map 4.25.5-1 → 4.25.6-1

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.
@@ -11,7 +11,6 @@ import {
11
11
  import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
12
12
  import { useDebounce } from 'use-debounce'
13
13
  import _ from 'lodash'
14
- // import ReactTags from 'react-tag-autocomplete'
15
14
  import { Tooltip as ReactTooltip } from 'react-tooltip'
16
15
  import Panels from './Panels'
17
16
  import Layout from '@cdc/core/components/Layout'
@@ -47,8 +46,14 @@ import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/In
47
46
  import useColumnsRequiredChecker from '../../../hooks/useColumnsRequiredChecker'
48
47
  import { addUIDs, HEADER_COLORS } from '../../../helpers'
49
48
  import './editorPanel.styles.css'
49
+ import FootnotesEditor from '@cdc/core/components/EditorPanel/FootnotesEditor'
50
+ import { Datasets } from '@cdc/core/types/DataSet'
50
51
 
51
- const EditorPanel = () => {
52
+ type MapEditorPanelProps = {
53
+ datasets?: Datasets
54
+ }
55
+
56
+ const EditorPanel: React.FC<MapEditorPanelProps> = ({ datasets }) => {
52
57
  const {
53
58
  setParentConfig,
54
59
  isDashboard,
@@ -1099,9 +1104,8 @@ const EditorPanel = () => {
1099
1104
  </span>
1100
1105
  <ul className='geo-buttons d-grid' style={{ gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
1101
1106
  <button
1102
- className={`${
1103
- config.general.geoType === 'us' || config.general.geoType === 'us-county' ? 'active' : ''
1104
- } full-width`}
1107
+ className={`${config.general.geoType === 'us' || config.general.geoType === 'us-county' ? 'active' : ''
1108
+ } full-width`}
1105
1109
  onClick={e => {
1106
1110
  e.preventDefault()
1107
1111
  handleEditorChanges('geoType', 'us')
@@ -2236,7 +2240,7 @@ const EditorPanel = () => {
2236
2240
  checked={legend.singleColumn}
2237
2241
  onChange={event => {
2238
2242
  const _newConfig = _.cloneDeep(config)
2239
- _newConfig.legend.singleColumn = !event.target.checked
2243
+ _newConfig.legend.singleColumn = event.target.checked
2240
2244
  _newConfig.legend.singleRow = false
2241
2245
  _newConfig.legend.verticalSorted = false
2242
2246
 
@@ -2253,7 +2257,7 @@ const EditorPanel = () => {
2253
2257
  checked={legend.singleRow}
2254
2258
  onChange={event => {
2255
2259
  const _newConfig = _.cloneDeep(config)
2256
- _newConfig.legend.singleRow = !event.target.checked
2260
+ _newConfig.legend.singleRow = event.target.checked
2257
2261
  _newConfig.legend.singleColumn = false
2258
2262
  _newConfig.legend.verticalSorted = false
2259
2263
 
@@ -2454,20 +2458,17 @@ const EditorPanel = () => {
2454
2458
  <DynamicDesc value={legend.descriptions[String(activeFilterValueForDescription)]} />
2455
2459
  </label>
2456
2460
  <label>
2457
- <select
2461
+ <Select
2462
+ label='Filter Value'
2458
2463
  value={String(activeFilterValueForDescription)}
2464
+ options={filterValueOptionList.map(arr => ({
2465
+ value: arr,
2466
+ label: displayFilterLegendValue(arr)
2467
+ }))}
2459
2468
  onChange={event => {
2460
2469
  handleEditorChanges('changeActiveFilterValue', event.target.value)
2461
2470
  }}
2462
- >
2463
- {filterValueOptionList.map((arr, i) => {
2464
- return (
2465
- <option value={arr} key={i}>
2466
- {displayFilterLegendValue(arr)}
2467
- </option>
2468
- )
2469
- })}
2470
- </select>
2471
+ />
2471
2472
  </label>
2472
2473
  </React.Fragment>
2473
2474
  )}
@@ -2529,16 +2530,30 @@ const EditorPanel = () => {
2529
2530
  </AccordionItem>
2530
2531
  )}
2531
2532
  {'navigation' !== config.general.type && (
2532
- <AccordionItem>
2533
- {' '}
2534
- {/* Filters */}
2535
- <AccordionItemHeading>
2536
- <AccordionItemButton>Filters</AccordionItemButton>
2537
- </AccordionItemHeading>
2538
- <AccordionItemPanel>
2539
- <VizFilterEditor config={config} updateField={updateField} rawData={config.data} />
2540
- </AccordionItemPanel>
2541
- </AccordionItem>
2533
+ <>
2534
+ <AccordionItem>
2535
+ {/* Filters */}
2536
+ <AccordionItemHeading>
2537
+ <AccordionItemButton>Filters</AccordionItemButton>
2538
+ </AccordionItemHeading>
2539
+ <AccordionItemPanel>
2540
+ <VizFilterEditor
2541
+ config={config}
2542
+ updateField={updateField}
2543
+ rawData={config.data}
2544
+ hasFootnotes={isDashboard}
2545
+ />
2546
+ </AccordionItemPanel>
2547
+ </AccordionItem>
2548
+ <AccordionItem>
2549
+ <AccordionItemHeading>
2550
+ <AccordionItemButton>Footnotes</AccordionItemButton>
2551
+ </AccordionItemHeading>
2552
+ <AccordionItemPanel>
2553
+ <FootnotesEditor config={config} updateField={updateField} datasets={datasets} />
2554
+ </AccordionItemPanel>
2555
+ </AccordionItem>
2556
+ </>
2542
2557
  )}
2543
2558
  {'navigation' !== config.general.type && (
2544
2559
  <AccordionItem>
@@ -2609,6 +2624,35 @@ const EditorPanel = () => {
2609
2624
  </Tooltip>
2610
2625
  </span>
2611
2626
  </label>
2627
+ <label className='checkbox'>
2628
+ <input
2629
+ type='checkbox'
2630
+ checked={config.table.showNonGeoData}
2631
+ onChange={event => {
2632
+ setConfig({
2633
+ ...config,
2634
+ table: {
2635
+ ...config.table,
2636
+ showNonGeoData: event.target.checked
2637
+ }
2638
+ })
2639
+ }}
2640
+ />
2641
+ <span className='edit-label column-heading'>
2642
+ Show Non Geographic Data
2643
+ <Tooltip style={{ textTransform: 'none' }}>
2644
+ <Tooltip.Target>
2645
+ <Icon
2646
+ display='question'
2647
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
2648
+ />
2649
+ </Tooltip.Target>
2650
+ <Tooltip.Content>
2651
+ <p>Show any data not associated with a geographic location</p>
2652
+ </Tooltip.Content>
2653
+ </Tooltip>
2654
+ </span>
2655
+ </label>
2612
2656
  <TextField
2613
2657
  value={table.indexLabel || ''}
2614
2658
  updateField={updateField}
@@ -3066,19 +3110,19 @@ const EditorPanel = () => {
3066
3110
  )}
3067
3111
  {(config.general.geoType === 'world' ||
3068
3112
  (config.general.geoType === 'us' && config.general.type === 'bubble')) && (
3069
- <label className='checkbox'>
3070
- <input
3071
- type='checkbox'
3072
- checked={config.visual.showBubbleZeros}
3073
- onChange={event => {
3074
- const _newConfig = _.cloneDeep(config)
3075
- _newConfig.visual.showBubbleZeros = event.target.checked
3076
- setConfig(_newConfig)
3077
- }}
3078
- />
3079
- <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
3080
- </label>
3081
- )}
3113
+ <label className='checkbox'>
3114
+ <input
3115
+ type='checkbox'
3116
+ checked={config.visual.showBubbleZeros}
3117
+ onChange={event => {
3118
+ const _newConfig = _.cloneDeep(config)
3119
+ _newConfig.visual.showBubbleZeros = event.target.checked
3120
+ setConfig(_newConfig)
3121
+ }}
3122
+ />
3123
+ <span className='edit-label'>Show Data with Zero's on Bubble Map</span>
3124
+ </label>
3125
+ )}
3082
3126
  {(config.general.geoType === 'world' || config.general.geoType === 'single-state') && (
3083
3127
  <label className='checkbox'>
3084
3128
  <input
@@ -3112,42 +3156,42 @@ const EditorPanel = () => {
3112
3156
  {(config.general.geoType === 'us' ||
3113
3157
  config.general.geoType === 'us-county' ||
3114
3158
  config.general.geoType === 'world') && (
3115
- <>
3116
- <label>
3117
- <span className='edit-label'>Default City Style</span>
3118
- <select
3119
- value={config.visual.cityStyle || false}
3120
- onChange={event => {
3121
- handleEditorChanges('handleCityStyle', event.target.value)
3122
- }}
3123
- >
3124
- <option value='circle'>Circle</option>
3125
- <option value='pin'>Pin</option>
3126
- <option value='square'>Square</option>
3127
- <option value='triangle'>Triangle</option>
3128
- <option value='diamond'>Diamond</option>
3129
- <option value='star'>Star</option>
3130
- </select>
3131
- </label>
3132
- <TextField
3133
- value={config.visual.cityStyleLabel}
3134
- section='visual'
3135
- fieldName='cityStyleLabel'
3136
- label='Label (Optional) '
3137
- updateField={updateField}
3138
- tooltip={
3139
- <Tooltip style={{ textTransform: 'none' }}>
3140
- <Tooltip.Target>
3141
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3142
- </Tooltip.Target>
3143
- <Tooltip.Content>
3144
- <p>When a label is provided, the default city style will appear in the legend.</p>
3145
- </Tooltip.Content>
3146
- </Tooltip>
3147
- }
3148
- />
3149
- </>
3150
- )}
3159
+ <>
3160
+ <label>
3161
+ <span className='edit-label'>Default City Style</span>
3162
+ <select
3163
+ value={config.visual.cityStyle || false}
3164
+ onChange={event => {
3165
+ handleEditorChanges('handleCityStyle', event.target.value)
3166
+ }}
3167
+ >
3168
+ <option value='circle'>Circle</option>
3169
+ <option value='pin'>Pin</option>
3170
+ <option value='square'>Square</option>
3171
+ <option value='triangle'>Triangle</option>
3172
+ <option value='diamond'>Diamond</option>
3173
+ <option value='star'>Star</option>
3174
+ </select>
3175
+ </label>
3176
+ <TextField
3177
+ value={config.visual.cityStyleLabel}
3178
+ section='visual'
3179
+ fieldName='cityStyleLabel'
3180
+ label='Label (Optional) '
3181
+ updateField={updateField}
3182
+ tooltip={
3183
+ <Tooltip style={{ textTransform: 'none' }}>
3184
+ <Tooltip.Target>
3185
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
3186
+ </Tooltip.Target>
3187
+ <Tooltip.Content>
3188
+ <p>When a label is provided, the default city style will appear in the legend.</p>
3189
+ </Tooltip.Content>
3190
+ </Tooltip>
3191
+ }
3192
+ />
3193
+ </>
3194
+ )}
3151
3195
  {/* <AdditionalCityStyles /> */}
3152
3196
  <>
3153
3197
  {config.visual.additionalCityStyles.length > 0 &&
@@ -4,6 +4,7 @@
4
4
  .map-container.world aside.side {
5
5
  border-top: 0;
6
6
  }
7
+
7
8
  @include breakpointClass(md) {
8
9
  .map-container.world aside.side {
9
10
  border-top: var(--cool-gray-10) 1px solid;
@@ -19,10 +20,12 @@
19
20
  border-radius: 6px;
20
21
 
21
22
  @include breakpointClass(md) {
23
+
22
24
  &.bottom,
23
25
  &.top {
24
26
  border: var(--cool-gray-10) 1px solid;
25
27
  }
28
+
26
29
  &.side {
27
30
  z-index: 1;
28
31
  box-sizing: content-box;
@@ -40,7 +43,8 @@
40
43
  display: block;
41
44
  column-count: 2;
42
45
  column-fill: balance;
43
- & > li {
46
+
47
+ &>li {
44
48
  white-space: nowrap;
45
49
  }
46
50
  }
@@ -53,6 +57,7 @@
53
57
  flex-wrap: wrap;
54
58
  }
55
59
  }
60
+
56
61
  &.no-border {
57
62
  border: none;
58
63
  }
@@ -84,12 +89,15 @@
84
89
  padding-bottom: 0;
85
90
  display: inline-block;
86
91
  }
92
+
87
93
  .legend-container__description {
88
94
  font-size: var(--legend-description-font-size);
89
95
  }
96
+
90
97
  .legend-container__reset-button {
91
98
  margin-top: 1em;
92
99
  }
100
+
93
101
  p {
94
102
  line-height: 1.4em;
95
103
  }
@@ -99,7 +107,9 @@
99
107
  row-gap: var(--space-between-legend-item-rows);
100
108
  column-gap: var(--space-between-legend-item-columns);
101
109
  }
102
- .legend-container__ul:not(.single-row) {
110
+
111
+
112
+ .legend-container__ul:not(.single-row, .legend-container__ul--single-column) {
103
113
  list-style: none;
104
114
  display: grid !important;
105
115
  grid-template-columns: 1fr;
@@ -114,12 +124,28 @@
114
124
  }
115
125
 
116
126
  &.vertical-sorted {
117
- flex-direction: column;
127
+ // Remove the grid overrides - let the existing column rules handle this
128
+ display: block !important; // Switch from grid to block to enable columns
129
+
130
+ @include breakpoint(md) {
131
+ column-count: 2;
132
+ column-fill: balance;
133
+ }
134
+
135
+ // Add spacing between legend items in vertical sorted mode
136
+ .legend-container__li {
137
+ margin-bottom: var(--space-between-legend-item-rows);
138
+
139
+ &:last-child {
140
+ margin-bottom: 0; // Remove margin from last item
141
+ }
142
+ }
118
143
  }
119
144
 
120
145
  &:not(.vertical-sorted, .legend-container__ul--single-column, .single-row) {
121
146
  width: 100%;
122
147
  }
148
+
123
149
  .legend-container__li {
124
150
  display: flex;
125
151
  align-items: center;
@@ -142,6 +168,7 @@
142
168
  &:last-child {
143
169
  margin-bottom: 0;
144
170
  }
171
+
145
172
  @include breakpoint(md) {
146
173
  white-space: nowrap;
147
174
  }
@@ -149,6 +176,7 @@
149
176
  &.legend-container__li--disabled {
150
177
  opacity: 0.4;
151
178
  }
179
+
152
180
  &.legend-container__li--not-disabled {
153
181
  outline: 1px solid #005ea2;
154
182
  outline-offset: 5px;
@@ -156,6 +184,7 @@
156
184
  }
157
185
  }
158
186
  }
187
+
159
188
  .legend-container__ul.single-row {
160
189
  width: 100%;
161
190
  cursor: pointer;
@@ -166,12 +195,13 @@
166
195
  justify-content: flex-start;
167
196
  flex-wrap: wrap;
168
197
 
169
- & > li {
198
+ &>li {
170
199
  white-space: wrap;
171
200
  display: flex;
172
201
  justify-content: center;
173
202
  align-items: center;
174
203
  vertical-align: middle;
204
+
175
205
  @include breakpoint(md) {
176
206
  white-space: nowrap;
177
207
  }
@@ -181,6 +211,7 @@
181
211
  }
182
212
  }
183
213
  }
214
+
184
215
  .legend-container__ul.patterns-only {
185
216
  margin-top: 10px;
186
217
  }
@@ -203,19 +234,24 @@
203
234
  @include breakpointClass(md) {
204
235
  width: 25%;
205
236
  min-width: 200px;
237
+
206
238
  .legend-section ul {
207
239
  flex-direction: column;
240
+
208
241
  li {
209
242
  width: 100%;
243
+
210
244
  &:nth-last-of-type(-n + 2) {
211
245
  padding-bottom: 1em;
212
246
  }
247
+
213
248
  &:last-child {
214
249
  padding-bottom: 0;
215
250
  }
216
251
  }
217
252
  }
218
253
  }
254
+
219
255
  li {
220
256
  width: 100%;
221
257
  }
@@ -225,6 +261,7 @@
225
261
  .legend-container ul:not(.single-row) {
226
262
  align-items: flex-start;
227
263
  justify-content: space-between;
264
+
228
265
  li {
229
266
  flex-grow: 1;
230
267
  padding-right: 0.5em;
@@ -234,24 +271,29 @@
234
271
 
235
272
  .filters-section {
236
273
  padding: 0 1em 1em;
274
+
237
275
  .heading-3 {
238
276
  font-weight: bold;
239
277
  margin-bottom: 0.5em;
240
278
  }
279
+
241
280
  form {
242
281
  margin-top: 0.5em;
243
282
  line-height: 2em;
244
283
  display: flex;
245
284
  align-items: flex-end;
246
- section + section {
285
+
286
+ section+section {
247
287
  margin-left: 0.75em;
248
288
  }
289
+
249
290
  select {
250
291
  display: block;
251
292
  font-size: 1em;
252
293
  }
253
294
  }
254
295
  }
296
+
255
297
  & .shape-container {
256
298
  &.single-row {
257
299
  display: flex;
@@ -260,26 +302,29 @@
260
302
  justify-content: flex-start;
261
303
  flex-wrap: wrap;
262
304
 
263
- & > div {
305
+ &>div {
264
306
  margin-right: 2em;
265
307
  }
266
- & > div:last-child {
308
+
309
+ &>div:last-child {
267
310
  margin-right: 0;
268
311
  }
269
312
  }
270
313
 
271
- & > div {
314
+ &>div {
272
315
  display: flex;
273
316
  flex-direction: row;
274
317
  }
275
- & div > svg {
318
+
319
+ & div>svg {
276
320
  width: 25px;
277
321
  height: 32px;
278
322
  }
279
- & div > p {
323
+
324
+ & div>p {
280
325
  white-space: nowrap;
281
326
  margin-top: 1px;
282
327
  }
283
328
  }
284
329
  }
285
- }
330
+ }
@@ -23,25 +23,28 @@ import { titleCase, handleMapAriaLabels, getGeoStrokeColor, MAX_ZOOM_LEVEL } fro
23
23
  import { getTopoData, getCurrentTopoYear, isTopoReady } from '../helpers/map'
24
24
  import useGeoClickHandler from '../../../hooks/useGeoClickHandler'
25
25
  import { SVG_WIDTH, SVG_HEIGHT, SVG_PADDING, SVG_VIEWBOX } from '../../../helpers'
26
+ import _ from 'lodash'
27
+ import { getStatePicked } from '../../../helpers/getStatePicked'
26
28
 
27
- const SingleStateMap = () => {
29
+ const SingleStateMap: React.FC = () => {
28
30
  const {
29
31
  config,
30
32
  setSharedFilterValue,
31
33
  isFilterValueSupported,
32
34
  runtimeFilters,
35
+ runtimeData,
33
36
  tooltipId,
34
37
  position,
35
- stateToShow,
36
38
  topoData,
37
39
  scale,
38
- translate,
39
- legendMemo,
40
- legendSpecialClassLastMemo
40
+ translate
41
41
  } = useContext<MapContext>(ConfigContext)
42
42
 
43
43
  const dispatch = useContext(MapDispatchContext)
44
- const { handleMoveEnd, handleZoomIn, handleZoomOut, handleReset, projection, statePicked } = useStateZoom(topoData)
44
+ const { handleMoveEnd, handleZoomIn, handleZoomOut, handleReset, projection } = useStateZoom(topoData)
45
+ const statePicked = getStatePicked(config, runtimeData)
46
+ const stateToShow = topoData?.states?.find(s => s.properties.name === statePicked.stateName)
47
+
45
48
  const { geoClickHandler } = useGeoClickHandler()
46
49
 
47
50
  const cityListProjection = geoAlbersUsaTerritories()
@@ -50,11 +53,6 @@ const SingleStateMap = () => {
50
53
  const geoStrokeColor = getGeoStrokeColor(config)
51
54
  const path = geoPath().projection(projection)
52
55
 
53
- useEffect(() => {
54
- const stateToShow = topoData?.states?.find(s => s.properties.name === config.general.statePicked.stateName)
55
- dispatch({ type: 'SET_STATE_TO_SHOW', payload: stateToShow })
56
- }, [statePicked])
57
-
58
56
  useEffect(() => {
59
57
  let currentYear = getCurrentTopoYear(config, runtimeFilters)
60
58
 
@@ -75,7 +73,7 @@ const SingleStateMap = () => {
75
73
 
76
74
  const checkForNoData = () => {
77
75
  // If no statePicked, return true
78
- if (!config.general.statePicked.fipsCode) return true
76
+ if (!statePicked.fipsCode) return true
79
77
  }
80
78
 
81
79
  // Constructs and displays markup for all geos on the map (except territories right now)
@@ -123,7 +121,7 @@ const SingleStateMap = () => {
123
121
  }
124
122
  return (
125
123
  <ErrorBoundary component='SingleStateMap'>
126
- {statePicked && config.general.allowMapZoom && config.general.statePicked.fipsCode && (
124
+ {statePicked && config.general.allowMapZoom && statePicked.fipsCode && (
127
125
  <svg
128
126
  viewBox={SVG_VIEWBOX}
129
127
  preserveAspectRatio='xMinYMin'
@@ -152,7 +150,7 @@ const SingleStateMap = () => {
152
150
  data={[
153
151
  {
154
152
  states: topoData?.states,
155
- counties: topoData.counties.filter(c => c.id.substring(0, 2) === config.general.statePicked.fipsCode)
153
+ counties: topoData.counties.filter(c => c.id.substring(0, 2) === statePicked.fipsCode)
156
154
  }
157
155
  ]}
158
156
  projection={geoAlbersUsaTerritories}
@@ -184,7 +182,7 @@ const SingleStateMap = () => {
184
182
  </ZoomableGroup>
185
183
  </svg>
186
184
  )}
187
- {statePicked && !config.general.allowMapZoom && config.general.statePicked.fipsCode && (
185
+ {statePicked && !config.general.allowMapZoom && statePicked.fipsCode && (
188
186
  <svg
189
187
  viewBox={SVG_VIEWBOX}
190
188
  preserveAspectRatio='xMinYMin'
@@ -203,7 +201,7 @@ const SingleStateMap = () => {
203
201
  data={[
204
202
  {
205
203
  states: topoData?.states,
206
- counties: topoData.counties.filter(c => c.id.substring(0, 2) === config.general.statePicked.fipsCode)
204
+ counties: topoData.counties.filter(c => c.id.substring(0, 2) === statePicked.fipsCode)
207
205
  }
208
206
  ]}
209
207
  projection={geoAlbersUsaTerritories}
@@ -215,7 +213,7 @@ const SingleStateMap = () => {
215
213
  stateToShow
216
214
  ]}
217
215
  >
218
- {({ features, projection }) => {
216
+ {({ features }) => {
219
217
  return (
220
218
  <g
221
219
  id='mapGroup'
@@ -226,7 +224,7 @@ const SingleStateMap = () => {
226
224
  data-scale=''
227
225
  key='countyMapGroup'
228
226
  >
229
- {constructGeoJsx(features, projection)}
227
+ {constructGeoJsx(features)}
230
228
  </g>
231
229
  )
232
230
  }}
@@ -9,7 +9,6 @@ import {
9
9
 
10
10
  import { SUPPORTED_DC_NAMES, GEO_TYPES, GEOCODE_TYPES } from './constants'
11
11
  import { DataRow, MapConfig } from '../types/MapConfig'
12
- import { memoize } from 'lodash'
13
12
 
14
13
  // Data props
15
14
  const stateKeys = Object.keys(supportedStates)
@@ -65,8 +64,8 @@ const handleUSLocation = (row: DataRow, geoColumn: string, displayAsHex: boolean
65
64
  uid = memoizedFindUID(geoName, 'territory')
66
65
  }
67
66
 
68
- if (!uid) uid = findCityUID(geoName)
69
67
  if (!uid) uid = handleDCDisplay(geoName, displayAsHex)
68
+ if (!uid) uid = findCityUID(geoName)
70
69
 
71
70
  return uid
72
71
  }
@@ -1,6 +1,8 @@
1
1
  import { stateFipsToTwoDigit, supportedCounties } from '../data/supported-geos'
2
2
  import { titleCase } from './titleCase'
3
3
 
4
+ const countyKeySet = new Set(Object.keys(supportedCounties))
5
+
4
6
  export const formatLegendLocation = (key, runtimeLookup) => {
5
7
  let formattedName = ''
6
8
 
@@ -9,8 +11,7 @@ export const formatLegendLocation = (key, runtimeLookup) => {
9
11
  formattedName += stateName
10
12
  }
11
13
 
12
- const countyKeys = Object.keys(supportedCounties)
13
- if (countyKeys.includes(key)) {
14
+ if (countyKeySet.has(key)) {
14
15
  formattedName += ', ' + titleCase(supportedCounties[key])
15
16
  }
16
17
 
@@ -8,7 +8,8 @@ const generateRuntimeData = (
8
8
  configObj: MapConfig,
9
9
  filters: VizFilter[],
10
10
  hash: number,
11
- isCategoryLegend: boolean
11
+ isCategoryLegend: boolean,
12
+ keepNoUidRows = false
12
13
  ): {
13
14
  [uid: string]: DataRow
14
15
  } => {
@@ -23,7 +24,10 @@ const generateRuntimeData = (
23
24
  addUIDs(configObj, configObj.columns.geo.name)
24
25
 
25
26
  configObj.data.forEach((row: DataRow) => {
26
- if (undefined === row.uid) return false // No UID for this row, we can't use for mapping
27
+ if (!row.uid) {
28
+ if (!keepNoUidRows) return false // No UID for this row, we can't use for mapping
29
+ row.uid = row[configObj.columns.geo.name]
30
+ }
27
31
  const configPrimaryName = configObj.columns.primary.name
28
32
  const value = row[configPrimaryName]
29
33
  const categoryLegend = typeof value === 'string' && isCategoryLegend