@cdc/map 2.6.4 → 4.22.10-alpha.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.
Files changed (49) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcmap.js +22 -16
  3. package/examples/default-county.json +64 -12
  4. package/examples/default-geocode.json +746 -0
  5. package/examples/default-hex.json +3 -1
  6. package/examples/example-city-state.json +10 -1
  7. package/examples/gallery/categorical-qualitative.json +797 -0
  8. package/examples/gallery/categorical-scale-based.json +739 -0
  9. package/examples/gallery/city-state.json +479 -0
  10. package/examples/gallery/county.json +22731 -0
  11. package/examples/gallery/equal-interval.json +1027 -0
  12. package/examples/gallery/equal-number.json +1027 -0
  13. package/examples/gallery/filterable.json +909 -0
  14. package/examples/gallery/hex-filtered.json +420 -0
  15. package/examples/gallery/hex.json +413 -0
  16. package/examples/gallery/single-state.json +21402 -0
  17. package/examples/gallery/world.json +1592 -0
  18. package/examples/private/city-state.json +428 -0
  19. package/examples/private/city-state2.json +434 -0
  20. package/examples/private/cty-issue.json +42768 -0
  21. package/examples/private/default-usa.json +460 -0
  22. package/examples/private/legend-issue.json +1 -0
  23. package/examples/private/map-rounding-error.json +42759 -0
  24. package/examples/private/monkeypox.json +376 -0
  25. package/examples/private/valid-data-map.csv +59 -0
  26. package/examples/private/wcmsrd-14492-data.json +292 -0
  27. package/examples/private/wcmsrd-14492.json +114 -0
  28. package/package.json +3 -3
  29. package/src/CdcMap.js +1370 -1322
  30. package/src/components/BubbleList.js +9 -5
  31. package/src/components/CityList.js +63 -19
  32. package/src/components/CountyMap.js +101 -42
  33. package/src/components/DataTable.js +18 -15
  34. package/src/components/EditorPanel.js +319 -149
  35. package/src/components/Modal.js +2 -1
  36. package/src/components/NavigationMenu.js +4 -3
  37. package/src/components/Sidebar.js +14 -19
  38. package/src/components/SingleStateMap.js +177 -248
  39. package/src/components/UsaMap.js +83 -30
  40. package/src/components/UsaRegionMap.js +3 -2
  41. package/src/components/WorldMap.js +8 -2
  42. package/src/data/{dfc-map.json → county-map.json} +0 -0
  43. package/src/data/initial-state.js +5 -2
  44. package/src/data/supported-geos.js +10 -0
  45. package/src/index.html +4 -8
  46. package/src/scss/editor-panel.scss +2 -2
  47. package/src/scss/main.scss +1 -6
  48. package/src/scss/map.scss +18 -0
  49. package/src/scss/sidebar.scss +2 -1
@@ -2,6 +2,7 @@ import React, {memo, useState, useEffect} from 'react'
2
2
  import { scaleLinear } from 'd3-scale';
3
3
  import { countryCoordinates } from '../data/country-coordinates';
4
4
  import stateCoordinates from '../data/state-coordinates';
5
+ import ReactTooltip from 'react-tooltip'
5
6
 
6
7
  export const BubbleList = (
7
8
  {
@@ -15,6 +16,9 @@ export const BubbleList = (
15
16
  displayGeoName
16
17
  }) => {
17
18
 
19
+ useEffect(() => {
20
+ ReactTooltip.hide()
21
+ }, [runtimeData]);
18
22
 
19
23
  const maxDataValue = Math.max(...dataImport.map(d => d[state.columns.primary.name]))
20
24
  const hasBubblesWithZeroOnMap = state.visual.showBubbleZeros ? 0 : 1;
@@ -56,7 +60,7 @@ export const BubbleList = (
56
60
  key={`circle-${countryName.replace(' ', '')}`}
57
61
  data-tip={toolTip}
58
62
  data-for="tooltip"
59
- className="bubble"
63
+ className={`bubble country--${countryName}`}
60
64
  cx={ Number(projection(coordinates[1], coordinates[0])[0]) || 0 } // || 0 handles error on loads where the data isn't ready
61
65
  cy={ Number(projection(coordinates[1], coordinates[0])[1]) || 0 }
62
66
  r={ Number(size(country[primaryKey])) }
@@ -197,9 +201,9 @@ export const BubbleList = (
197
201
  data-tip={toolTip}
198
202
  data-for="tooltip"
199
203
  className="bubble"
200
- cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
201
- cy={projection(coordinates)[1] || 0}
202
- r={Number(size(item[primaryKey]) + 1)}
204
+ cx={ projection(coordinates)[0] || 0 } // || 0 handles error on loads where the data isn't ready
205
+ cy={ projection(coordinates)[1] || 0 }
206
+ r={ Number(size(item[primaryKey])) + 1 }
203
207
  fill={"transparent"}
204
208
  stroke={"white"}
205
209
  strokeWidth={.5}
@@ -237,4 +241,4 @@ export const BubbleList = (
237
241
  return bubbles;
238
242
  }
239
243
  }
240
- export default memo(BubbleList)
244
+ export default BubbleList
@@ -3,6 +3,7 @@ import React, { useState, useEffect, useContext } from 'react';
3
3
  import { jsx } from '@emotion/react'
4
4
  import { supportedCities } from '../data/supported-geos';
5
5
  import { scaleLinear } from 'd3-scale';
6
+ import ReactTooltip from 'react-tooltip';
6
7
 
7
8
  const CityList = (({
8
9
  data,
@@ -12,19 +13,33 @@ const CityList = (({
12
13
  displayGeoName,
13
14
  applyLegendToRow,
14
15
  projection,
15
- titleCase
16
+ titleCase,
17
+ setSharedFilterValue,
18
+ isFilterValueSupported,
19
+ isGeoCodeMap
16
20
  }) => {
21
+
17
22
  const [citiesData, setCitiesData] = useState({});
18
23
 
19
24
  useEffect(() => {
20
- const citiesList = Object.keys(data).filter((item) => Object.keys(supportedCities).includes(item));
21
-
22
- const citiesDictionary = {};
23
-
24
- citiesList.map((city) => citiesDictionary[city] = data[city]);
25
+ ReactTooltip.rebuild()
26
+ });
25
27
 
26
- setCitiesData(citiesDictionary);
27
- }, [data]);
28
+ useEffect(() => {
29
+ if(!isGeoCodeMap) {
30
+ const citiesList = Object.keys(data).filter((item) => Object.keys(supportedCities).includes(item));
31
+
32
+ const citiesDictionary = {};
33
+
34
+ citiesList.map((city) => citiesDictionary[city] = data[city]);
35
+
36
+ setCitiesData(citiesDictionary);
37
+ } else {
38
+ const citiesDictionary = {};
39
+ state.data.map(city => citiesDictionary[city[state.columns.geo.name]] = city)
40
+ setCitiesData(citiesDictionary);
41
+ }
42
+ }, [data, state.data]);
28
43
 
29
44
  if (state.general.type === 'bubble') {
30
45
  const maxDataValue = Math.max(...state.data.map(d => d[state.columns.primary.name]))
@@ -37,13 +52,17 @@ const CityList = (({
37
52
  .range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
38
53
 
39
54
  }
55
+ let cityList = isGeoCodeMap ? Object.keys(citiesData).filter((c) => undefined !== c) : Object.keys(citiesData).filter((c) => undefined !== data[c]);
56
+ if(!cityList) return true;
40
57
 
41
- const cityList = Object.keys(citiesData).filter((c) => undefined !== data[c]);
42
-
58
+ // Cities output
43
59
  const cities = cityList.map((city, i) => {
44
- const cityDisplayName = titleCase( displayGeoName(city) );
45
60
 
46
- const legendColors = applyLegendToRow(data[city]);
61
+ const geoData = isGeoCodeMap ? state.data.filter(item => city === item[state.columns.geo.name])[0] : data[city];
62
+
63
+ const cityDisplayName = isGeoCodeMap ? city : titleCase( displayGeoName(city) );
64
+
65
+ const legendColors = (isGeoCodeMap && geoData) ? applyLegendToRow(geoData) : data[city] ? applyLegendToRow(data[city]) : false;
47
66
 
48
67
  if (legendColors === false) {
49
68
  return true;
@@ -51,8 +70,8 @@ const CityList = (({
51
70
 
52
71
  const styles = {
53
72
  fill: legendColors[0],
54
- outline: 0,
55
- stroke: 'rgba(0, 0, 0, 0.4)',
73
+ opacity: setSharedFilterValue && isFilterValueSupported && data[city][state.columns.geo.name] !== setSharedFilterValue ? .5 : 1,
74
+ stroke: setSharedFilterValue && isFilterValueSupported && data[city][state.columns.geo.name] === setSharedFilterValue ? 'rgba(0, 0, 0, 1)' : 'rgba(0, 0, 0, 0.4)',
56
75
  '&:hover': {
57
76
  fill: legendColors[1],
58
77
  outline: 0
@@ -63,16 +82,16 @@ const CityList = (({
63
82
  }
64
83
  };
65
84
 
66
- const geoData = data[city];
85
+
67
86
 
68
87
  const toolTip = applyTooltipsToGeo(cityDisplayName, data[city]);
69
88
 
70
89
  // If we need to add a cursor pointer
71
- if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'click') {
90
+ if ((state.columns.navigate && (geoData?.[state.columns.navigate.name] && geoData[state.columns.navigate.name]) ) || state.tooltips.appearanceType === 'click') {
72
91
  styles.cursor = 'pointer'
73
92
  }
74
93
 
75
- const radius = state.general.geoType === 'us' ? 8 : 4;
94
+ const radius = state.general.geoType === 'us' && !isGeoCodeMap ? 8 : isGeoCodeMap ? 2 : 4;
76
95
 
77
96
  const additionalProps = {
78
97
  fillOpacity: state.general.type === 'bubble' ? .4 : 1
@@ -91,7 +110,31 @@ const CityList = (({
91
110
  />
92
111
  );
93
112
 
94
- let transform = `translate(${projection(supportedCities[city])})`
113
+ const pin = (
114
+ <path
115
+ className="marker"
116
+ 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"
117
+ title="Click for more information"
118
+ onClick={() => geoClickHandler(cityDisplayName, geoData)}
119
+ data-tip={toolTip}
120
+ data-for="tooltip"
121
+ strokeWidth={2}
122
+ stroke={'black'}
123
+ {...additionalProps}
124
+ >
125
+ </path>
126
+ );
127
+
128
+ let transform = '';
129
+
130
+ if (!isGeoCodeMap) {
131
+ transform = `translate(${projection(supportedCities[city])})`
132
+ }
133
+
134
+ if (isGeoCodeMap) {
135
+ let coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
136
+ transform = `translate(${projection(coords)})`
137
+ }
95
138
 
96
139
  return (
97
140
  <g
@@ -100,7 +143,8 @@ const CityList = (({
100
143
  css={styles}
101
144
  className="geo-point"
102
145
  >
103
- {circle}
146
+ {state.visual.cityStyle === 'circle' && circle }
147
+ {state.visual.cityStyle === 'pin' && pin }
104
148
  </g>
105
149
  );
106
150
  });
@@ -8,8 +8,9 @@ import { feature, mesh } from 'topojson-client';
8
8
  import { CustomProjection } from '@visx/geo';
9
9
  import colorPalettes from '../../../core/data/colorPalettes'
10
10
  import { geoAlbersUsaTerritories } from 'd3-composite-projections';
11
- import testJSON from '../data/dfc-map.json';
11
+ import testJSON from '../data/county-map.json';
12
12
  import { abbrs } from '../data/abbreviations';
13
+ import CityList from './CityList';
13
14
 
14
15
  const offsets = {
15
16
  Vermont: [50, -8],
@@ -44,31 +45,21 @@ const STATE_INACTIVE_FILL = '#F4F7FA';
44
45
  const projection = geoAlbersUsaTerritories().translate([WIDTH / 2, HEIGHT / 2]);
45
46
  const path = geoPath().projection(projection);
46
47
  const stateLines = path(mesh(testJSON, testJSON.objects.states));
48
+ const countyLines = path(mesh(testJSON, testJSON.objects.counties));
47
49
 
48
- const nudges = {
49
- 'US-FL': [15, 3],
50
- 'US-AK': [0, -8],
51
- 'US-CA': [-10, 0],
52
- 'US-NY': [5, 0],
53
- 'US-MI': [13, 20],
54
- 'US-LA': [-10, -3],
55
- 'US-HI': [-10, 10],
56
- 'US-ID': [0, 10],
57
- 'US-WV': [-2, 2],
58
- };
59
50
 
60
51
  function CountyMapChecks(prevState, nextState) {
52
+ const equalNumberOptIn = prevState.state.general.equalNumberOptIn && nextState.state.general.equalNumberOptIn;
61
53
  const equalColumnName = prevState.state.general.type && nextState.state.general.type;
62
54
  const equalNavColumn = prevState.state.columns.navigate && nextState.state.columns.navigate;
63
55
  const equalLegend = prevState.runtimeLegend === nextState.runtimeLegend;
64
56
  const equalBorderColors = prevState.state.general.geoBorderColor === nextState.state.general.geoBorderColor; // update when geoborder color changes
65
57
  const equalMapColors = prevState.state.color === nextState.state.color; // update when map colors change
66
58
  const equalData = prevState.data === nextState.data; // update when data changes
67
- return equalMapColors && equalData && equalBorderColors && equalLegend && equalColumnName && equalNavColumn ? true : false;
59
+ return equalMapColors && equalData && equalBorderColors && equalLegend && equalColumnName && equalNavColumn && equalNumberOptIn ? true : false;
68
60
  }
69
61
 
70
62
  const CountyMap = (props) => {
71
-
72
63
  let mapData = states.concat(counties);
73
64
 
74
65
  const {
@@ -80,6 +71,10 @@ const CountyMap = (props) => {
80
71
  displayGeoName,
81
72
  rebuildTooltips,
82
73
  containerEl,
74
+ handleMapAriaLabels,
75
+ titleCase,
76
+ setSharedFilterValue,
77
+ isFilterValueSupported
83
78
  } = props;
84
79
 
85
80
  useEffect(() => {
@@ -214,27 +209,42 @@ const CountyMap = (props) => {
214
209
  };
215
210
 
216
211
  const onReset = (e) => {
217
- e.preventDefault();
218
- const svg = document.querySelector('.svg-container')
219
212
 
220
- svg.setAttribute('data-scaleZoom', 0)
213
+ if (state.general.type !== 'us-geocode') {
214
+ e.preventDefault();
215
+ const svg = document.querySelector('.svg-container')
221
216
 
217
+ svg.setAttribute('data-scaleZoom', 0)
222
218
 
223
- const allStates = document.querySelectorAll('.state path');
224
- const allCounties = document.querySelectorAll('.county path');
225
219
 
226
- stateLinesPath.current.setAttribute('stroke', geoStrokeColor);
227
- stateLinesPath.current.setAttribute('stroke-width', startingLineWidth);
220
+ const allStates = document.querySelectorAll('.state path');
221
+ const allCounties = document.querySelectorAll('.county path');
222
+
223
+ stateLinesPath.current.setAttribute('stroke', geoStrokeColor);
224
+ stateLinesPath.current.setAttribute('stroke-width', startingLineWidth);
228
225
 
229
- let otherStates = document.querySelectorAll(`.state--inactive`);
230
- otherStates.forEach((el) => (el.style.display = 'none'));
231
- allCounties.forEach((el) => (el.style.strokeWidth = 0.85));
232
- allStates.forEach((state) => state.setAttribute('stroke-width', .75 / .85 ));
226
+ let otherStates = document.querySelectorAll(`.state--inactive`);
227
+ otherStates.forEach((el) => (el.style.display = 'none'));
228
+ allCounties.forEach((el) => (el.style.strokeWidth = 0.85));
229
+ allStates.forEach((state) => state.setAttribute('stroke-width', .75 / .85));
233
230
 
234
- mapGroup.current.setAttribute('transform', `translate(${[0, 0]}) scale(${0.85})`);
231
+ mapGroup.current.setAttribute('transform', `translate(${[0, 0]}) scale(${0.85})`);
235
232
 
236
- // reset button
237
- resetButton.current.style.display = 'none';
233
+ // reset button
234
+ resetButton.current.style.display = 'none';
235
+ } else {
236
+ const svg = document.querySelector('.svg-container')
237
+ const allStates = document.querySelectorAll('.state');
238
+ document.querySelector('#focusedBorder path').style.stroke = 'none';
239
+ allStates.forEach(item => item.classList.remove('state--inactive'))
240
+ //document.querySelectorAll('.state path').forEach(item => item.style.fill = 'rgb(244, 247, 250)')
241
+ document.querySelectorAll('.state').forEach(item => item.style.display = 'block')
242
+ stateLinesPath.current.setAttribute('stroke', geoStrokeColor);
243
+ stateLinesPath.current.setAttribute('stroke-width', startingLineWidth);
244
+ svg.setAttribute('data-scaleZoom', 0)
245
+ mapGroup.current.setAttribute('transform', `translate(${[0, 0]}) scale(${0.85})`);
246
+ resetButton.current.style.display = 'none';
247
+ }
238
248
  };
239
249
 
240
250
  function setStateLeave() {
@@ -260,11 +270,11 @@ const CountyMap = (props) => {
260
270
  focusedBorderPath.current.setAttribute('d', focusedStateLine);
261
271
  focusedBorderPath.current.setAttribute('stroke', '#000');
262
272
 
263
- if(scale) {
264
- allStates.forEach( state => state.setAttribute('stroke-width', 0.75 / scale))
265
- focusedBorderPath.current.setAttribute('stroke-width', 0.75 / scale );
273
+ if (scale) {
274
+ allStates.forEach(state => state.setAttribute('stroke-width', 0.75 / scale))
275
+ focusedBorderPath.current.setAttribute('stroke-width', 0.75 / scale);
266
276
  }
267
-
277
+
268
278
  }
269
279
 
270
280
  const StateLines = memo(({ stateLines, lineWidth, geoStrokeColor }) => {
@@ -423,6 +433,12 @@ const CountyMap = (props) => {
423
433
  return output;
424
434
  });
425
435
 
436
+ const GeoCodeCountyLines = memo(() => {
437
+ return (
438
+ <path d={countyLines} className="county-borders" style={{ stroke: geoStrokeColor}} />
439
+ )
440
+ })
441
+
426
442
  const StateOutput = memo(({ geographies, states }) => {
427
443
  let output = [];
428
444
  output.push(
@@ -445,13 +461,25 @@ const CountyMap = (props) => {
445
461
 
446
462
  const geoDisplayName = displayGeoName(geoKey);
447
463
 
448
- let stateStyles = {
449
- cursor: 'default',
450
- stroke: STATE_BORDER,
451
- strokeWidth: 0.75 / scale,
452
- display: !focusedState ? 'none' : focusedState && focusedState !== geo.id ? 'block' : 'none',
453
- fill: focusedState && focusedState !== geo.id ? STATE_INACTIVE_FILL : 'none',
454
- };
464
+ let stateStyles = {}
465
+
466
+ if (state.general.type !== 'us-geocode') {
467
+ stateStyles = {
468
+ cursor: 'default',
469
+ stroke: STATE_BORDER,
470
+ strokeWidth: 0.75 / scale,
471
+ display: !focusedState ? 'none' : focusedState && focusedState !== geo.id ? 'block' : 'none',
472
+ fill: focusedState && focusedState !== geo.id ? STATE_INACTIVE_FILL : 'none',
473
+ };
474
+ } else {
475
+ stateStyles = {
476
+ cursor: 'default',
477
+ stroke: STATE_BORDER,
478
+ strokeWidth: 0.75 / scale,
479
+ display: 'block',
480
+ fill: '#f4f7fa',
481
+ };
482
+ }
455
483
 
456
484
  let stateSelectedStyles = {
457
485
  fillOpacity: 1,
@@ -502,7 +530,14 @@ const CountyMap = (props) => {
502
530
  const states = geographies.slice(0, 56);
503
531
  const counties = geographies.slice(56);
504
532
  let geosJsx = [];
505
- geosJsx.push(<CountyOutput geographies={geographies} counties={counties} key="county-key" />);
533
+ {
534
+ 'us-geocode' !== state.general.type &&
535
+ geosJsx.push(<CountyOutput geographies={geographies} counties={counties} key="county-key" />);
536
+ }
537
+ {
538
+ 'us-geocode' === state.general.type &&
539
+ geosJsx.push(<GeoCodeCountyLines />);
540
+ }
506
541
  geosJsx.push(<StateOutput geographies={geographies} states={states} key="state-key" />);
507
542
  geosJsx.push(
508
543
  <StateLines
@@ -513,12 +548,34 @@ const CountyMap = (props) => {
513
548
  />
514
549
  );
515
550
  geosJsx.push(<FocusedStateBorder key="focused-border-key" />);
551
+ geosJsx.push(<CityList
552
+ projection={projection}
553
+ key="cities"
554
+ data={data}
555
+ state={state}
556
+ geoClickHandler={geoClickHandler}
557
+ applyTooltipsToGeo={applyTooltipsToGeo}
558
+ displayGeoName={displayGeoName}
559
+ applyLegendToRow={applyLegendToRow}
560
+ titleCase={titleCase}
561
+ setSharedFilterValue={setSharedFilterValue}
562
+ isFilterValueSupported={isFilterValueSupported}
563
+ isGeoCodeMap={state.general.type === 'us-geocode'}
564
+ />)
516
565
  return geosJsx;
517
566
  };
518
567
  if(!data) <Loading />
519
568
  return (
520
569
  <ErrorBoundary component='CountyMap'>
521
- <svg viewBox={`0 0 ${WIDTH} ${HEIGHT}`} preserveAspectRatio='xMinYMin' className='svg-container' data-scale={scale ? scale : ''} data-translate={translate ? translate : ''}>
570
+ <svg
571
+ viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
572
+ preserveAspectRatio='xMinYMin'
573
+ className='svg-container'
574
+ data-scale={scale ? scale : ''}
575
+ data-translate={translate ? translate : ''}
576
+ role="img"
577
+ aria-label={handleMapAriaLabels(state)}
578
+ >
522
579
  <rect
523
580
  className='background center-container ocean'
524
581
  width={WIDTH}
@@ -541,12 +598,14 @@ const CountyMap = (props) => {
541
598
  transform={`translate(${translate}) scale(${scale})`}
542
599
  key='countyMapGroup'
543
600
  >
544
- {constructGeoJsx(features, geoAlbersUsaTerritories)}
601
+ {constructGeoJsx(features, projection)}
545
602
  </g>
546
603
  );
547
604
  }}
548
605
  </CustomProjection>
549
606
  </svg>
607
+
608
+ {/* TODO: Refactor to COVE button */}
550
609
  <button className={`btn btn--reset`} onClick={onReset} ref={resetButton} style={{ display: 'none' }} tabIndex="0">
551
610
  Reset Zoom
552
611
  </button>
@@ -5,7 +5,7 @@ import {
5
5
  useTable, useSortBy, useResizeColumns, useBlockLayout
6
6
  } from 'react-table';
7
7
  import Papa from 'papaparse';
8
- import ExternalIcon from '../images/external-link.svg';
8
+ import ExternalIcon from '../images/external-link.svg'; // TODO: Move to Icon component
9
9
 
10
10
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
11
11
  import LegendCircle from '@cdc/core/components/LegendCircle';
@@ -206,7 +206,7 @@ const DataTable = (props) => {
206
206
 
207
207
  const legendColor = applyLegendToRow(rowObj);
208
208
 
209
- if(state.general.geoType !== 'us-county') {
209
+ if(state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
210
210
  var labelValue = displayGeoName(row.original);
211
211
  } else {
212
212
  var labelValue = formatLegendLocation(row.original)
@@ -288,7 +288,7 @@ const DataTable = (props) => {
288
288
  if(!state.data) return <Loading />
289
289
  return (
290
290
  <ErrorBoundary component="DataTable">
291
- <section id={state.general.title ? `dataTableSection__${state.general.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
291
+ <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
292
292
  <a id='skip-nav' className='cdcdataviz-sr-only-focusable' href={`#${skipId}`}>
293
293
  Skip Navigation or Skip to Content
294
294
  </a>
@@ -298,17 +298,17 @@ const DataTable = (props) => {
298
298
  tabIndex="0"
299
299
  onKeyDown={(e) => { if (e.keyCode === 13) { setExpanded(!expanded); } }}
300
300
  >
301
-
301
+
302
302
  {tableTitle}
303
303
  </div>
304
- <div
304
+ <div
305
305
  className="table-container"
306
- style={ { maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' } }
306
+ style={ { maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' } }
307
307
  >
308
308
  <table
309
- height={expanded ? null : 0} {...getTableProps()}
310
- aria-live="assertive"
311
- className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
309
+ height={expanded ? null : 0} {...getTableProps()}
310
+ aria-live="assertive"
311
+ className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
312
312
  hidden={!expanded}
313
313
  aria-rowcount={state?.data.length ? state.data.length : '-1' }
314
314
  >
@@ -317,7 +317,7 @@ const DataTable = (props) => {
317
317
  {headerGroups.map((headerGroup) => (
318
318
  <tr {...headerGroup.getHeaderGroupProps()}>
319
319
  {headerGroup.headers.map((column) => (
320
- <th
320
+ <th
321
321
  tabIndex="0"
322
322
  title={column.Header}
323
323
  role="columnheader"
@@ -344,11 +344,14 @@ const DataTable = (props) => {
344
344
  prepareRow(row);
345
345
  return (
346
346
  <tr {...row.getRowProps()} role="row">
347
- {row.cells.map((cell) => (
348
- <td tabIndex="0" {...cell.getCellProps()} role="gridcell" onClick={ (e) => (state.general.type === 'bubble' && state.general.allowMapZoom && state.general.geoType === 'world') ? setFilteredCountryCode(cell.row.original) : true }>
349
- {cell.render('Cell')}
350
- </td>
351
- ))}
347
+ {row.cells.map((cell) => {
348
+ return (
349
+ <td tabIndex="0" {...cell.getCellProps()} role="gridcell" onClick={ (e) => (state.general.type === 'bubble' && state.general.allowMapZoom && state.general.geoType === 'world') ? setFilteredCountryCode(cell.row.original) : true }>
350
+ {cell.render('Cell')}
351
+ </td>
352
+ )
353
+ }
354
+ )}
352
355
  </tr>
353
356
  );
354
357
  })}