@cdc/map 4.22.10-alpha.1 → 4.22.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 (47) hide show
  1. package/dist/cdcmap.js +10 -10
  2. package/examples/private/atsdr.json +19 -29
  3. package/examples/private/atsdr_new.json +1 -1
  4. package/examples/private/bubble.json +282 -284
  5. package/examples/private/city-state.json +427 -427
  6. package/examples/private/city-state2.json +433 -433
  7. package/examples/private/cty-issue.json +42765 -42768
  8. package/examples/private/default-usa.json +2 -5
  9. package/examples/private/default-world-data.json +1443 -1443
  10. package/examples/private/default.json +965 -965
  11. package/examples/private/diff.json +226 -0
  12. package/examples/private/filters.json +1 -0
  13. package/examples/private/legend-issue.json +3271 -1
  14. package/examples/private/map-issue.json +166 -0
  15. package/examples/private/map-rounding-error.json +42756 -42759
  16. package/examples/private/mdx.json +209 -209
  17. package/examples/private/monkeypox.json +375 -375
  18. package/examples/private/regions.json +51 -51
  19. package/examples/private/wcmsrd-13881-data.json +2856 -2856
  20. package/examples/private/wcmsrd-13881.json +5818 -5822
  21. package/examples/private/wcmsrd-14492-data.json +291 -291
  22. package/examples/private/wcmsrd-14492.json +103 -113
  23. package/examples/private/wcmsrd-test.json +264 -267
  24. package/examples/private/world.json +1579 -1579
  25. package/examples/private/worldmap.json +1489 -1489
  26. package/package.json +3 -3
  27. package/src/CdcMap.js +231 -315
  28. package/src/components/BubbleList.js +199 -240
  29. package/src/components/CityList.js +50 -96
  30. package/src/components/CountyMap.js +511 -600
  31. package/src/components/DataTable.js +218 -253
  32. package/src/components/EditorPanel.js +2338 -2551
  33. package/src/components/Geo.js +4 -14
  34. package/src/components/Modal.js +13 -23
  35. package/src/components/NavigationMenu.js +43 -39
  36. package/src/components/Sidebar.js +83 -93
  37. package/src/components/SingleStateMap.js +95 -151
  38. package/src/components/UsaMap.js +165 -214
  39. package/src/components/UsaRegionMap.js +122 -160
  40. package/src/components/WorldMap.js +96 -179
  41. package/src/components/ZoomableGroup.js +6 -26
  42. package/src/data/initial-state.js +1 -0
  43. package/src/hooks/useActiveElement.js +13 -13
  44. package/src/hooks/useColorPalette.ts +66 -74
  45. package/src/hooks/useZoomPan.js +22 -23
  46. package/src/index.html +1 -2
  47. package/src/scss/sidebar.scss +22 -0
@@ -1,367 +1,332 @@
1
- import React, {
2
- useEffect, useState, useMemo, memo, useCallback
3
- } from 'react';
4
- import {
5
- useTable, useSortBy, useResizeColumns, useBlockLayout
6
- } from 'react-table';
7
- import Papa from 'papaparse';
8
- import ExternalIcon from '../images/external-link.svg'; // TODO: Move to Icon component
9
-
10
- import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
11
- import LegendCircle from '@cdc/core/components/LegendCircle';
12
-
13
-
14
- import Loading from '@cdc/core/components/Loading';
15
-
16
- const DataTable = (props) => {
17
- const {
18
- state,
19
- tableTitle,
20
- indexTitle,
21
- mapTitle,
22
- rawData,
23
- showDownloadButton,
24
- runtimeData,
25
- runtimeLegend,
26
- headerColor,
27
- expandDataTable,
28
- columns,
29
- displayDataAsText,
30
- applyLegendToRow,
31
- displayGeoName,
32
- navigationHandler,
33
- viewport,
34
- formatLegendLocation,
35
- tabbingId,
36
- setFilteredCountryCode
37
- } = props;
38
-
39
- const [expanded, setExpanded] = useState(expandDataTable);
40
-
41
- const [accessibilityLabel, setAccessibilityLabel] = useState('');
1
+ import React, { useEffect, useState, useMemo, memo, useCallback } from 'react'
2
+ import { useTable, useSortBy, useResizeColumns, useBlockLayout } from 'react-table'
3
+ import Papa from 'papaparse'
4
+ import ExternalIcon from '../images/external-link.svg' // TODO: Move to Icon component
42
5
 
43
- const [ready, setReady] = useState(false)
6
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
+ import LegendCircle from '@cdc/core/components/LegendCircle'
8
+
9
+ import Loading from '@cdc/core/components/Loading'
10
+
11
+ const DataTable = props => {
12
+ const { state, tableTitle, indexTitle, mapTitle, rawData, showDownloadButton, runtimeData, runtimeLegend, headerColor, expandDataTable, columns, displayDataAsText, applyLegendToRow, displayGeoName, navigationHandler, viewport, formatLegendLocation, tabbingId, setFilteredCountryCode } = props
44
13
 
45
- const fileName = `${mapTitle || 'data-table'}.csv`;
14
+ const [expanded, setExpanded] = useState(expandDataTable)
15
+
16
+ const [accessibilityLabel, setAccessibilityLabel] = useState('')
17
+
18
+ const [ready, setReady] = useState(false)
46
19
 
20
+ const fileName = `${mapTitle || 'data-table'}.csv`
47
21
 
48
22
  // Catch all sorting method used on load by default but also on user click
49
23
  // Having a custom method means we can add in any business logic we want going forward
50
- const customSort = useCallback((a, b) => {
51
- const digitRegex = /\d+/;
24
+ const customSort = useCallback(
25
+ (a, b) => {
26
+ const digitRegex = /\d+/
52
27
 
53
- const hasNumber = (value) => digitRegex.test(value);
28
+ const hasNumber = value => digitRegex.test(value)
54
29
 
55
- // force null and undefined to the bottom
56
- a = a === null || a === undefined ? '' : a;
57
- b = b === null || b === undefined ? '' : b;
30
+ // force null and undefined to the bottom
31
+ a = a === null || a === undefined ? '' : a
32
+ b = b === null || b === undefined ? '' : b
58
33
 
59
- // convert any strings that are actually numbers to proper data type
60
- const aNum = Number(a);
34
+ // convert any strings that are actually numbers to proper data type
35
+ const aNum = Number(a)
61
36
 
62
- if (!Number.isNaN(aNum)) {
63
- a = aNum;
64
- }
37
+ if (!Number.isNaN(aNum)) {
38
+ a = aNum
39
+ }
65
40
 
66
- const bNum = Number(b);
41
+ const bNum = Number(b)
67
42
 
68
- if (!Number.isNaN(bNum)) {
69
- b = bNum;
70
- }
43
+ if (!Number.isNaN(bNum)) {
44
+ b = bNum
45
+ }
71
46
 
72
- // remove iso code prefixes
73
- if (typeof a === 'string') {
74
- a = a.replace('us-', '');
75
- a = displayGeoName(a);
76
- }
47
+ // remove iso code prefixes
48
+ if (typeof a === 'string') {
49
+ a = a.replace('us-', '')
50
+ a = displayGeoName(a)
51
+ }
77
52
 
78
- if (typeof b === 'string') {
79
- b = b.replace('us-', '');
80
- b = displayGeoName(b);
81
- }
53
+ if (typeof b === 'string') {
54
+ b = b.replace('us-', '')
55
+ b = displayGeoName(b)
56
+ }
82
57
 
83
- // force any string values to lowercase
84
- a = typeof a === 'string' ? a.toLowerCase() : a;
85
- b = typeof b === 'string' ? b.toLowerCase() : b;
58
+ // force any string values to lowercase
59
+ a = typeof a === 'string' ? a.toLowerCase() : a
60
+ b = typeof b === 'string' ? b.toLowerCase() : b
86
61
 
87
- // If the string contains a number, remove the text from the value and only sort by the number. Only uses the first number it finds.
88
- if (typeof a === 'string' && hasNumber(a) === true) {
89
- a = a.match(digitRegex)[0];
62
+ // If the string contains a number, remove the text from the value and only sort by the number. Only uses the first number it finds.
63
+ if (typeof a === 'string' && hasNumber(a) === true) {
64
+ a = a.match(digitRegex)[0]
90
65
 
91
- a = Number(a);
92
- }
66
+ a = Number(a)
67
+ }
93
68
 
94
- if (typeof b === 'string' && hasNumber(b) === true) {
95
- b = b.match(digitRegex)[0];
69
+ if (typeof b === 'string' && hasNumber(b) === true) {
70
+ b = b.match(digitRegex)[0]
96
71
 
97
- b = Number(b);
98
- }
72
+ b = Number(b)
73
+ }
99
74
 
100
- // When comparing a number to a string, always send string to bottom
101
- if (typeof a === 'number' && typeof b === 'string') {
102
- return 1;
103
- }
75
+ // When comparing a number to a string, always send string to bottom
76
+ if (typeof a === 'number' && typeof b === 'string') {
77
+ return 1
78
+ }
104
79
 
105
- if (typeof b === 'number' && typeof a === 'string') {
106
- return -1;
107
- }
80
+ if (typeof b === 'number' && typeof a === 'string') {
81
+ return -1
82
+ }
108
83
 
109
- // Return either 1 or -1 to indicate a sort priority
110
- if (a > b) {
111
- return 1;
112
- }
113
- if (a < b) {
114
- return -1;
115
- }
116
- // returning 0, undefined or any falsey value will use subsequent sorts or
117
- // the index as a tiebreaker
118
- return 0;
119
- }, [displayGeoName]);
84
+ // Return either 1 or -1 to indicate a sort priority
85
+ if (a > b) {
86
+ return 1
87
+ }
88
+ if (a < b) {
89
+ return -1
90
+ }
91
+ // returning 0, undefined or any falsey value will use subsequent sorts or
92
+ // the index as a tiebreaker
93
+ return 0
94
+ },
95
+ [displayGeoName]
96
+ )
120
97
 
121
98
  // Optionally wrap cell with anchor if config defines a navigation url
122
- const getCellAnchor = useCallback((markup, row) => {
123
- if (columns.navigate && row[columns.navigate.name]) {
124
- markup = (
125
- <span
126
- onClick={() => navigationHandler(row[columns.navigate.name])}
127
- className="table-link"
128
- title="Click for more information (Opens in a new window)"
129
- role="link"
130
- tabIndex="0"
131
- onKeyDown={(e) => {
132
- if (e.keyCode === 13) {
133
- navigationHandler(row[columns.navigate.name]);
134
- }
135
- }}
136
- >
137
- {markup}
138
- <ExternalIcon className="inline-icon" />
139
- </span>
140
- );
141
- }
99
+ const getCellAnchor = useCallback(
100
+ (markup, row) => {
101
+ if (columns.navigate && row[columns.navigate.name]) {
102
+ markup = (
103
+ <span
104
+ onClick={() => navigationHandler(row[columns.navigate.name])}
105
+ className='table-link'
106
+ title='Click for more information (Opens in a new window)'
107
+ role='link'
108
+ tabIndex='0'
109
+ onKeyDown={e => {
110
+ if (e.keyCode === 13) {
111
+ navigationHandler(row[columns.navigate.name])
112
+ }
113
+ }}
114
+ >
115
+ {markup}
116
+ <ExternalIcon className='inline-icon' />
117
+ </span>
118
+ )
119
+ }
142
120
 
143
- return markup;
144
- }, [columns.navigate, navigationHandler]);
121
+ return markup
122
+ },
123
+ [columns.navigate, navigationHandler]
124
+ )
145
125
 
146
126
  const DownloadButton = memo(() => {
147
- const csvData = Papa.unparse(rawData);
127
+ const csvData = Papa.unparse(rawData)
148
128
 
149
- const blob = new Blob([csvData], {type: "text/csv;charset=utf-8;"});
129
+ const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
150
130
 
151
131
  const saveBlob = () => {
152
132
  //@ts-ignore
153
133
  if (typeof window.navigator.msSaveBlob === 'function') {
154
134
  //@ts-ignore
155
- navigator.msSaveBlob(blob, fileName);
135
+ navigator.msSaveBlob(blob, fileName)
156
136
  }
157
137
  }
158
138
 
159
139
  return (
160
- <a
161
- download={fileName}
162
- type="button"
163
- onClick={saveBlob}
164
- href={URL.createObjectURL(blob)}
165
- aria-label="Download this data in a CSV file format."
166
- className={`${headerColor} btn btn-download no-border`}
167
- id={`${skipId}`}
168
- data-html2canvas-ignore
169
- role="button"
170
- >
171
- Download Data (CSV)
172
- </a>
140
+ <a download={fileName} type='button' onClick={saveBlob} href={URL.createObjectURL(blob)} aria-label='Download this data in a CSV file format.' className={`${headerColor} btn btn-download no-border`} id={`${skipId}`} data-html2canvas-ignore role='button'>
141
+ Download Data (CSV)
142
+ </a>
173
143
  )
174
- }, [rawData]);
144
+ }, [rawData])
175
145
 
176
146
  // Creates columns structure for the table
177
147
  const tableColumns = useMemo(() => {
178
- const newTableColumns = [];
148
+ const newTableColumns = []
179
149
 
180
- Object.keys(columns).forEach((column) => {
150
+ Object.keys(columns).forEach(column => {
181
151
  if (columns[column].dataTable === true && columns[column].name) {
182
152
  const newCol = {
183
153
  Header: columns[column].label ? columns[column].label : columns[column].name,
184
154
  id: column,
185
- accessor: (row) => {
155
+ accessor: row => {
186
156
  if (runtimeData) {
187
- if(state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object'){
188
- for(let i = 0; i < state.legend.specialClasses.length; i++){
189
- if(String(runtimeData[row][state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value){
190
- return state.legend.specialClasses[i].label;
157
+ if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
158
+ for (let i = 0; i < state.legend.specialClasses.length; i++) {
159
+ if (String(runtimeData[row][state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
160
+ return state.legend.specialClasses[i].label
191
161
  }
192
162
  }
193
163
  }
194
- return runtimeData[row][columns[column].name] ?? null;
164
+ return runtimeData[row][columns[column].name] ?? null
195
165
  }
196
166
 
197
- return null;
167
+ return null
198
168
  },
199
169
  sortType: (a, b) => customSort(a.values[column], b.values[column])
200
- };
170
+ }
201
171
 
202
172
  if (column === 'geo') {
203
- newCol.Header = indexTitle || 'Location';
173
+ newCol.Header = indexTitle || 'Location'
204
174
  newCol.Cell = ({ row, value }) => {
205
- const rowObj = runtimeData[row.original];
175
+ const rowObj = runtimeData[row.original]
206
176
 
207
- const legendColor = applyLegendToRow(rowObj);
177
+ const legendColor = applyLegendToRow(rowObj)
208
178
 
209
- if(state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
210
- var labelValue = displayGeoName(row.original);
179
+ if (state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
180
+ var labelValue = displayGeoName(row.original)
211
181
  } else {
212
182
  var labelValue = formatLegendLocation(row.original)
213
183
  }
214
184
 
215
- labelValue = getCellAnchor(labelValue, rowObj);
185
+ labelValue = getCellAnchor(labelValue, rowObj)
216
186
 
217
187
  const cellMarkup = (
218
188
  <>
219
189
  <LegendCircle fill={legendColor[0]} />
220
190
  {labelValue}
221
191
  </>
222
- );
192
+ )
223
193
 
224
- return cellMarkup;
225
- };
194
+ return cellMarkup
195
+ }
226
196
  } else {
227
197
  newCol.Cell = ({ value }) => {
228
- const cellMarkup = displayDataAsText(value, column);
198
+ const cellMarkup = displayDataAsText(value, column)
229
199
 
230
- return (cellMarkup);
231
- };
200
+ return cellMarkup
201
+ }
232
202
  }
233
203
 
234
- newTableColumns.push(newCol);
204
+ newTableColumns.push(newCol)
235
205
  }
236
- });
206
+ })
237
207
 
238
- return newTableColumns;
239
- }, [indexTitle, columns, runtimeData,getCellAnchor,displayDataAsText,applyLegendToRow,customSort,displayGeoName,state.legend.specialClasses]);
208
+ return newTableColumns
209
+ }, [indexTitle, columns, runtimeData, getCellAnchor, displayDataAsText, applyLegendToRow, customSort, displayGeoName, state.legend.specialClasses])
240
210
 
241
211
  const tableData = useMemo(
242
- () => Object.keys(runtimeData).filter((key) => applyLegendToRow(runtimeData[key])).sort((a, b) => customSort(a, b)),
243
- [ runtimeData, applyLegendToRow, customSort]
244
- );
212
+ () =>
213
+ Object.keys(runtimeData)
214
+ .filter(key => applyLegendToRow(runtimeData[key]))
215
+ .sort((a, b) => customSort(a, b)),
216
+ [runtimeData, applyLegendToRow, customSort]
217
+ )
245
218
 
246
219
  // Change accessibility label depending on expanded status
247
220
  useEffect(() => {
248
- const expandedLabel = 'Accessible data table.';
249
- const collapsedLabel = 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.';
221
+ const expandedLabel = 'Accessible data table.'
222
+ const collapsedLabel = 'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
250
223
 
251
224
  if (expanded === true && accessibilityLabel !== expandedLabel) {
252
- setAccessibilityLabel(expandedLabel);
225
+ setAccessibilityLabel(expandedLabel)
253
226
  }
254
227
 
255
228
  if (expanded === false && accessibilityLabel !== collapsedLabel) {
256
- setAccessibilityLabel(collapsedLabel);
229
+ setAccessibilityLabel(collapsedLabel)
257
230
  }
258
231
  // eslint-disable-next-line react-hooks/exhaustive-deps
259
- }, [expanded, applyLegendToRow, customSort]);
232
+ }, [expanded, applyLegendToRow, customSort])
260
233
 
261
234
  const defaultColumn = useMemo(
262
235
  () => ({
263
236
  minWidth: 150,
264
237
  width: 200,
265
- maxWidth: 400,
238
+ maxWidth: 400
266
239
  }),
267
240
  []
268
- );
241
+ )
269
242
 
270
243
  const mapLookup = {
271
244
  'us-county': 'United States County Map',
272
245
  'single-state': 'State Map',
273
- 'us': 'United States Map',
274
- 'world': 'World Map'
246
+ us: 'United States Map',
247
+ world: 'World Map'
275
248
  }
276
249
 
277
- const {
278
- getTableProps,
279
- getTableBodyProps,
280
- headerGroups,
281
- rows,
282
- prepareRow,
283
- } = useTable({ columns: tableColumns, data: tableData, defaultColumn }, useSortBy, useBlockLayout, useResizeColumns);
250
+ const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ columns: tableColumns, data: tableData, defaultColumn }, useSortBy, useBlockLayout, useResizeColumns)
284
251
 
285
- const rand = Math.random().toString(16).substr(2, 8);
252
+ const rand = Math.random().toString(16).substr(2, 8)
286
253
  const skipId = `btn__${rand}`
287
254
 
288
- if(!state.data) return <Loading />
255
+ if (!state.data) return <Loading />
289
256
  return (
290
- <ErrorBoundary component="DataTable">
257
+ <ErrorBoundary component='DataTable'>
291
258
  <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
292
259
  <a id='skip-nav' className='cdcdataviz-sr-only-focusable' href={`#${skipId}`}>
293
260
  Skip Navigation or Skip to Content
294
261
  </a>
295
- <div
296
- className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
297
- onClick={() => { setExpanded(!expanded); }}
298
- tabIndex="0"
299
- onKeyDown={(e) => { if (e.keyCode === 13) { setExpanded(!expanded); } }}
300
- >
301
-
302
- {tableTitle}
303
- </div>
304
- <div
305
- className="table-container"
306
- style={ { maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' } }
307
- >
308
- <table
309
- height={expanded ? null : 0} {...getTableProps()}
310
- aria-live="assertive"
311
- className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
312
- hidden={!expanded}
313
- aria-rowcount={state?.data.length ? state.data.length : '-1' }
262
+ <div
263
+ className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
264
+ onClick={() => {
265
+ setExpanded(!expanded)
266
+ }}
267
+ tabIndex='0'
268
+ onKeyDown={e => {
269
+ if (e.keyCode === 13) {
270
+ setExpanded(!expanded)
271
+ }
272
+ }}
314
273
  >
315
- <caption className='cdcdataviz-sr-only'>{state.dataTable.caption ? state.dataTable.caption : `Datatable showing data for the ${mapLookup[state.general.geoType]} figure.`}</caption>
316
- <thead style={{position: 'sticky', top: 0, zIndex: 999}}>
317
- {headerGroups.map((headerGroup) => (
318
- <tr {...headerGroup.getHeaderGroupProps()}>
319
- {headerGroup.headers.map((column) => (
320
- <th
321
- tabIndex="0"
322
- title={column.Header}
323
- role="columnheader"
324
- scope="col"
325
- {...column.getHeaderProps(column.getSortByToggleProps())}
326
- className={column.isSorted ? column.isSortedDesc ? 'sort sort-desc' : 'sort sort-asc' : 'sort'}
327
- onKeyDown={(e) => { if (e.keyCode === 13) { column.toggleSortBy(); } }}
328
- //aria-sort={column.isSorted ? column.isSortedDesc ? 'descending' : 'ascending' : 'none' }
329
- {...(column.isSorted ? column.isSortedDesc ? { 'aria-sort': 'descending' } : { 'aria-sort': 'ascending' } : null)}
330
-
331
- >
332
- {column.render('Header')}
333
- <button>
334
- <span className="cdcdataviz-sr-only">{`Sort by ${(column.render('Header')).toLowerCase() } in ${ column.isSorted ? column.isSortedDesc ? 'descending' : 'ascending' : 'no'} `} order</span>
335
- </button>
336
- <div {...column.getResizerProps()} className="resizer" />
337
- </th>
338
- ))}
339
- </tr>
340
- ))}
341
- </thead>
342
- <tbody {...getTableBodyProps()}>
343
- {rows.map((row) => {
344
- prepareRow(row);
345
- return (
346
- <tr {...row.getRowProps()} role="row">
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
- )}
274
+ {tableTitle}
275
+ </div>
276
+ <div className='table-container' style={{ maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' }}>
277
+ <table height={expanded ? null : 0} {...getTableProps()} aria-live='assertive' className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'} hidden={!expanded} aria-rowcount={state?.data.length ? state.data.length : '-1'}>
278
+ <caption className='cdcdataviz-sr-only'>{state.dataTable.caption ? state.dataTable.caption : `Datatable showing data for the ${mapLookup[state.general.geoType]} figure.`}</caption>
279
+ <thead style={{ position: 'sticky', top: 0, zIndex: 999 }}>
280
+ {headerGroups.map(headerGroup => (
281
+ <tr {...headerGroup.getHeaderGroupProps()}>
282
+ {headerGroup.headers.map(column => (
283
+ <th
284
+ tabIndex='0'
285
+ title={column.Header}
286
+ role='columnheader'
287
+ scope='col'
288
+ {...column.getHeaderProps(column.getSortByToggleProps())}
289
+ className={column.isSorted ? (column.isSortedDesc ? 'sort sort-desc' : 'sort sort-asc') : 'sort'}
290
+ onKeyDown={e => {
291
+ if (e.keyCode === 13) {
292
+ column.toggleSortBy()
293
+ }
294
+ }}
295
+ //aria-sort={column.isSorted ? column.isSortedDesc ? 'descending' : 'ascending' : 'none' }
296
+ {...(column.isSorted ? (column.isSortedDesc ? { 'aria-sort': 'descending' } : { 'aria-sort': 'ascending' }) : null)}
297
+ >
298
+ {column.render('Header')}
299
+ <button>
300
+ <span className='cdcdataviz-sr-only'>{`Sort by ${column.render('Header').toLowerCase()} in ${column.isSorted ? (column.isSortedDesc ? 'descending' : 'ascending') : 'no'} `} order</span>
301
+ </button>
302
+ <div {...column.getResizerProps()} className='resizer' />
303
+ </th>
304
+ ))}
355
305
  </tr>
356
- );
357
- })}
358
- </tbody>
359
- </table>
360
- </div>
361
- {showDownloadButton === true && <DownloadButton />}
362
- </section>
306
+ ))}
307
+ </thead>
308
+ <tbody {...getTableBodyProps()}>
309
+ {rows.map(row => {
310
+ prepareRow(row)
311
+ return (
312
+ <tr {...row.getRowProps()} role='row'>
313
+ {row.cells.map(cell => {
314
+ return (
315
+ <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)}>
316
+ {cell.render('Cell')}
317
+ </td>
318
+ )
319
+ })}
320
+ </tr>
321
+ )
322
+ })}
323
+ </tbody>
324
+ </table>
325
+ </div>
326
+ {showDownloadButton === true && <DownloadButton />}
327
+ </section>
363
328
  </ErrorBoundary>
364
- );
365
- };
329
+ )
330
+ }
366
331
 
367
- export default DataTable;
332
+ export default DataTable