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