@cdc/map 4.23.3 → 4.23.5
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/cdcmap.js +25301 -29100
- package/examples/custom-map-layers.json +764 -0
- package/examples/default-county.json +169 -155
- package/examples/example-city-state.json +34 -12
- package/examples/testing-layer-2.json +1 -0
- package/examples/testing-layer.json +96 -0
- package/index.html +6 -5
- package/package.json +3 -3
- package/src/CdcMap.jsx +201 -105
- package/src/components/CountyMap.jsx +31 -6
- package/src/components/DataTable.jsx +185 -218
- package/src/components/EditorPanel.jsx +293 -162
- package/src/components/UsaMap.jsx +17 -11
- package/src/data/initial-state.js +16 -8
- package/src/hooks/useMapLayers.jsx +243 -0
- package/src/index.jsx +4 -8
- package/src/scss/editor-panel.scss +97 -97
- package/src/scss/filters.scss +0 -2
- package/src/scss/main.scss +25 -26
- package/src/components/Filters.jsx +0 -113
|
@@ -10,6 +10,7 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
|
10
10
|
|
|
11
11
|
import topoJSON from '../data/county-map.json'
|
|
12
12
|
import { formatPrefix } from 'd3'
|
|
13
|
+
import useMapLayers from '../hooks/useMapLayers'
|
|
13
14
|
|
|
14
15
|
const sortById = (a, b) => {
|
|
15
16
|
if (a.id < b.id) return -1
|
|
@@ -63,6 +64,10 @@ const CountyMap = props => {
|
|
|
63
64
|
|
|
64
65
|
const [focus, setFocus] = useState({})
|
|
65
66
|
|
|
67
|
+
const pathGenerator = geoPath().projection(geoAlbersUsaTerritories())
|
|
68
|
+
|
|
69
|
+
const { featureArray } = useMapLayers(state, '', pathGenerator, false)
|
|
70
|
+
|
|
66
71
|
useEffect(() => {
|
|
67
72
|
if (containerEl) {
|
|
68
73
|
if (containerEl.className.indexOf('loaded') === -1) {
|
|
@@ -216,6 +221,9 @@ const CountyMap = props => {
|
|
|
216
221
|
}
|
|
217
222
|
}
|
|
218
223
|
|
|
224
|
+
// todo: current item is a custom map layer
|
|
225
|
+
// if(currentItem === customMapLayer) show layer.tooltip
|
|
226
|
+
|
|
219
227
|
let hoveredGeo
|
|
220
228
|
let hoveredGeoIndex
|
|
221
229
|
for (let i = 0; i < runtimeKeys.length; i++) {
|
|
@@ -227,7 +235,7 @@ const CountyMap = props => {
|
|
|
227
235
|
}
|
|
228
236
|
}
|
|
229
237
|
|
|
230
|
-
if (hoveredGeo) {
|
|
238
|
+
if (hoveredGeo && applyLegendToRow(hoveredGeo)) {
|
|
231
239
|
tooltipRef.current.style.display = 'block'
|
|
232
240
|
tooltipRef.current.style.top = e.clientY + 'px'
|
|
233
241
|
tooltipRef.current.style.left = e.clientX + 'px'
|
|
@@ -309,6 +317,20 @@ const CountyMap = props => {
|
|
|
309
317
|
context.stroke()
|
|
310
318
|
}
|
|
311
319
|
|
|
320
|
+
// add in custom map layers
|
|
321
|
+
if (featureArray.length > 0) {
|
|
322
|
+
featureArray.map(layer => {
|
|
323
|
+
context.beginPath()
|
|
324
|
+
path(layer)
|
|
325
|
+
context.fillStyle = layer.properties.fill
|
|
326
|
+
context.globalAlpha = layer.properties['fill-opacity']
|
|
327
|
+
context.strokeStyle = layer.properties['stroke']
|
|
328
|
+
context.lineWidth = layer.properties['stroke-width']
|
|
329
|
+
context.fill()
|
|
330
|
+
context.stroke()
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
|
|
312
334
|
if (state.general.type === 'us-geocode') {
|
|
313
335
|
context.strokeStyle = 'black'
|
|
314
336
|
const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
|
|
@@ -317,11 +339,14 @@ const CountyMap = props => {
|
|
|
317
339
|
const pixelCoords = projection([data[key][state.columns.longitude.name], data[key][state.columns.latitude.name]])
|
|
318
340
|
|
|
319
341
|
if (pixelCoords) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
342
|
+
const legendValues = data[key] !== undefined ? applyLegendToRow(data[key]) : false
|
|
343
|
+
if (legendValues) {
|
|
344
|
+
context.fillStyle = legendValues[0]
|
|
345
|
+
context.beginPath()
|
|
346
|
+
context.arc(pixelCoords[0], pixelCoords[1], geoRadius, 0, 2 * Math.PI)
|
|
347
|
+
context.fill()
|
|
348
|
+
context.stroke()
|
|
349
|
+
}
|
|
325
350
|
}
|
|
326
351
|
})
|
|
327
352
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useEffect, useState,
|
|
2
|
-
|
|
1
|
+
import React, { useEffect, useState, memo } from 'react'
|
|
2
|
+
|
|
3
3
|
import Papa from 'papaparse'
|
|
4
4
|
import ExternalIcon from '../images/external-link.svg' // TODO: Move to Icon component
|
|
5
5
|
import Icon from '@cdc/core/components/ui/Icon'
|
|
@@ -15,6 +15,7 @@ const DataTable = props => {
|
|
|
15
15
|
const { state, tableTitle, indexTitle, mapTitle, rawData, runtimeData, headerColor, expandDataTable, columns, displayDataAsText, applyLegendToRow, displayGeoName, navigationHandler, viewport, formatLegendLocation, tabbingId, setFilteredCountryCode } = props
|
|
16
16
|
|
|
17
17
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
18
|
+
const [sortBy, setSortBy] = useState({ column: 'geo', asc: false })
|
|
18
19
|
|
|
19
20
|
const [accessibilityLabel, setAccessibilityLabel] = useState('')
|
|
20
21
|
|
|
@@ -22,110 +23,124 @@ const DataTable = props => {
|
|
|
22
23
|
|
|
23
24
|
// Catch all sorting method used on load by default but also on user click
|
|
24
25
|
// Having a custom method means we can add in any business logic we want going forward
|
|
25
|
-
const customSort =
|
|
26
|
-
|
|
27
|
-
const digitRegex = /\d+/
|
|
26
|
+
const customSort = (a, b) => {
|
|
27
|
+
const digitRegex = /\d+/
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
const hasNumber = value => digitRegex.test(value)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
// force null and undefined to the bottom
|
|
32
|
+
a = a === null || a === undefined ? '' : a
|
|
33
|
+
b = b === null || b === undefined ? '' : b
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// convert any strings that are actually numbers to proper data type
|
|
36
|
+
const aNum = Number(a)
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (!Number.isNaN(aNum)) {
|
|
39
|
+
a = aNum
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const bNum = Number(b)
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
if (!Number.isNaN(bNum)) {
|
|
45
|
+
b = bNum
|
|
46
|
+
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
// remove iso code prefixes
|
|
49
|
+
if (typeof a === 'string') {
|
|
50
|
+
a = a.replace('us-', '')
|
|
51
|
+
a = displayGeoName(a)
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
if (typeof b === 'string') {
|
|
55
|
+
b = b.replace('us-', '')
|
|
56
|
+
b = displayGeoName(b)
|
|
57
|
+
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// force any string values to lowercase
|
|
60
|
+
a = typeof a === 'string' ? a.toLowerCase() : a
|
|
61
|
+
b = typeof b === 'string' ? b.toLowerCase() : b
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// 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.
|
|
64
|
+
if (typeof a === 'string' && hasNumber(a) === true) {
|
|
65
|
+
a = a.match(digitRegex)[0]
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
a = Number(a)
|
|
68
|
+
}
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
if (typeof b === 'string' && hasNumber(b) === true) {
|
|
71
|
+
b = b.match(digitRegex)[0]
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
b = Number(b)
|
|
74
|
+
}
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
// When comparing a number to a string, always send string to bottom
|
|
77
|
+
if (typeof a === 'number' && typeof b === 'string') {
|
|
78
|
+
return 1
|
|
79
|
+
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if (typeof b === 'number' && typeof a === 'string') {
|
|
82
|
+
return -1
|
|
83
|
+
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
[displayGeoName]
|
|
97
|
-
)
|
|
85
|
+
// Return either 1 or -1 to indicate a sort priority
|
|
86
|
+
if (a > b) {
|
|
87
|
+
return 1
|
|
88
|
+
}
|
|
89
|
+
if (a < b) {
|
|
90
|
+
return -1
|
|
91
|
+
}
|
|
92
|
+
// returning 0, undefined or any falsey value will use subsequent sorts or
|
|
93
|
+
// the index as a tiebreaker
|
|
94
|
+
return 0
|
|
95
|
+
}
|
|
98
96
|
|
|
99
97
|
// Optionally wrap cell with anchor if config defines a navigation url
|
|
100
|
-
const getCellAnchor =
|
|
101
|
-
(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
98
|
+
const getCellAnchor = (markup, row) => {
|
|
99
|
+
if (columns.navigate && row[columns.navigate.name]) {
|
|
100
|
+
markup = (
|
|
101
|
+
<span
|
|
102
|
+
onClick={() => navigationHandler(row[columns.navigate.name])}
|
|
103
|
+
className='table-link'
|
|
104
|
+
title='Click for more information (Opens in a new window)'
|
|
105
|
+
role='link'
|
|
106
|
+
tabIndex='0'
|
|
107
|
+
onKeyDown={e => {
|
|
108
|
+
if (e.keyCode === 13) {
|
|
109
|
+
navigationHandler(row[columns.navigate.name])
|
|
110
|
+
}
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{markup}
|
|
114
|
+
<ExternalIcon className='inline-icon' />
|
|
115
|
+
</span>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
121
118
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
)
|
|
119
|
+
return markup
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const rand = Math.random().toString(16).substr(2, 8)
|
|
123
|
+
const skipId = `btn__${rand}`
|
|
124
|
+
|
|
125
|
+
const mapLookup = {
|
|
126
|
+
'us-county': 'United States County Map',
|
|
127
|
+
'single-state': 'State Map',
|
|
128
|
+
us: 'United States Map',
|
|
129
|
+
world: 'World Map'
|
|
130
|
+
}
|
|
126
131
|
|
|
127
132
|
const DownloadButton = memo(() => {
|
|
128
|
-
|
|
133
|
+
let csvData
|
|
134
|
+
if (state.general.type === 'bubble') {
|
|
135
|
+
// Just Unparse
|
|
136
|
+
csvData = Papa.unparse(rawData)
|
|
137
|
+
} else if (state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
|
|
138
|
+
// Unparse + Add column for full Geo name
|
|
139
|
+
csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: displayGeoName(row[state.columns.geo.name]), ...row })))
|
|
140
|
+
} else {
|
|
141
|
+
// Unparse + Add column for full Geo name
|
|
142
|
+
csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: formatLegendLocation(row[state.columns.geo.name]), ...row })))
|
|
143
|
+
}
|
|
129
144
|
|
|
130
145
|
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
|
|
131
146
|
|
|
@@ -144,80 +159,6 @@ const DataTable = props => {
|
|
|
144
159
|
)
|
|
145
160
|
}, [rawData])
|
|
146
161
|
|
|
147
|
-
// Creates columns structure for the table
|
|
148
|
-
const tableColumns = useMemo(() => {
|
|
149
|
-
const newTableColumns = []
|
|
150
|
-
|
|
151
|
-
Object.keys(columns).forEach(column => {
|
|
152
|
-
if (columns[column].dataTable === true && columns[column].name) {
|
|
153
|
-
const newCol = {
|
|
154
|
-
Header: columns[column].label ? columns[column].label : columns[column].name,
|
|
155
|
-
id: column,
|
|
156
|
-
accessor: row => {
|
|
157
|
-
if (runtimeData) {
|
|
158
|
-
if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
|
|
159
|
-
for (let i = 0; i < state.legend.specialClasses.length; i++) {
|
|
160
|
-
if (String(runtimeData[row][state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
|
|
161
|
-
return state.legend.specialClasses[i].label
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return runtimeData[row][columns[column].name] ?? null
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return null
|
|
169
|
-
},
|
|
170
|
-
sortType: (a, b) => customSort(a.values[column], b.values[column])
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (column === 'geo') {
|
|
174
|
-
newCol.Header = indexTitle || 'Location'
|
|
175
|
-
newCol.Cell = ({ row, value }) => {
|
|
176
|
-
const rowObj = runtimeData[row.original]
|
|
177
|
-
|
|
178
|
-
const legendColor = applyLegendToRow(rowObj)
|
|
179
|
-
|
|
180
|
-
var labelValue
|
|
181
|
-
if (state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
|
|
182
|
-
labelValue = displayGeoName(row.original)
|
|
183
|
-
} else {
|
|
184
|
-
labelValue = formatLegendLocation(row.original)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
labelValue = getCellAnchor(labelValue, rowObj)
|
|
188
|
-
|
|
189
|
-
const cellMarkup = (
|
|
190
|
-
<>
|
|
191
|
-
<LegendCircle fill={legendColor[0]} />
|
|
192
|
-
{labelValue}
|
|
193
|
-
</>
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
return cellMarkup
|
|
197
|
-
}
|
|
198
|
-
} else {
|
|
199
|
-
newCol.Cell = ({ value }) => {
|
|
200
|
-
const cellMarkup = displayDataAsText(value, column)
|
|
201
|
-
|
|
202
|
-
return cellMarkup
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
newTableColumns.push(newCol)
|
|
207
|
-
}
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
return newTableColumns
|
|
211
|
-
}, [indexTitle, columns, runtimeData, getCellAnchor, displayDataAsText, applyLegendToRow, customSort, displayGeoName, state.legend.specialClasses]) // eslint-disable-line
|
|
212
|
-
|
|
213
|
-
const tableData = useMemo(
|
|
214
|
-
() =>
|
|
215
|
-
Object.keys(runtimeData)
|
|
216
|
-
.filter(key => applyLegendToRow(runtimeData[key]))
|
|
217
|
-
.sort((a, b) => customSort(a, b)),
|
|
218
|
-
[runtimeData, applyLegendToRow, customSort]
|
|
219
|
-
)
|
|
220
|
-
|
|
221
162
|
// Change accessibility label depending on expanded status
|
|
222
163
|
useEffect(() => {
|
|
223
164
|
const expandedLabel = 'Accessible data table.'
|
|
@@ -231,30 +172,20 @@ const DataTable = props => {
|
|
|
231
172
|
setAccessibilityLabel(collapsedLabel)
|
|
232
173
|
}
|
|
233
174
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
234
|
-
}, [expanded
|
|
235
|
-
|
|
236
|
-
const defaultColumn = useMemo(
|
|
237
|
-
() => ({
|
|
238
|
-
minWidth: 150,
|
|
239
|
-
width: 200,
|
|
240
|
-
maxWidth: 400
|
|
241
|
-
}),
|
|
242
|
-
[]
|
|
243
|
-
)
|
|
175
|
+
}, [expanded])
|
|
244
176
|
|
|
245
|
-
|
|
246
|
-
'us-county': 'United States County Map',
|
|
247
|
-
'single-state': 'State Map',
|
|
248
|
-
us: 'United States Map',
|
|
249
|
-
world: 'World Map'
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ columns: tableColumns, data: tableData, defaultColumn }, useSortBy, useBlockLayout, useResizeColumns)
|
|
177
|
+
if (!state.data) return <Loading />
|
|
253
178
|
|
|
254
|
-
const
|
|
255
|
-
|
|
179
|
+
const rows = Object.keys(runtimeData)
|
|
180
|
+
.filter(row => applyLegendToRow(runtimeData[row]))
|
|
181
|
+
.sort((a, b) => {
|
|
182
|
+
const sortVal = customSort(runtimeData[a][state.columns[sortBy.column].name], runtimeData[b][state.columns[sortBy.column].name])
|
|
183
|
+
if (!sortBy.asc) return sortVal
|
|
184
|
+
if (sortVal === 0) return 0
|
|
185
|
+
if (sortVal < 0) return 1
|
|
186
|
+
return -1
|
|
187
|
+
})
|
|
256
188
|
|
|
257
|
-
if (!state.data) return <Loading />
|
|
258
189
|
return (
|
|
259
190
|
<ErrorBoundary component='DataTable'>
|
|
260
191
|
<CoveMediaControls.Section classes={['download-links']}>
|
|
@@ -281,49 +212,85 @@ const DataTable = props => {
|
|
|
281
212
|
{tableTitle}
|
|
282
213
|
</div>
|
|
283
214
|
<div className='table-container' style={{ maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' }}>
|
|
284
|
-
<table height={expanded ? null : 0}
|
|
215
|
+
<table height={expanded ? null : 0} role='table' aria-live='assertive' className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'} hidden={!expanded} aria-rowcount={state?.data.length ? state.data.length : '-1'}>
|
|
285
216
|
<caption className='cdcdataviz-sr-only'>{state.dataTable.caption ? state.dataTable.caption : `Datatable showing data for the ${mapLookup[state.general.geoType]} figure.`}</caption>
|
|
286
217
|
<thead style={{ position: 'sticky', top: 0, zIndex: 999 }}>
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
218
|
+
<tr>
|
|
219
|
+
{Object.keys(columns)
|
|
220
|
+
.filter(column => columns[column].dataTable === true && columns[column].name)
|
|
221
|
+
.map(column => {
|
|
222
|
+
let text
|
|
223
|
+
if (column !== 'geo') {
|
|
224
|
+
text = columns[column].label ? columns[column].label : columns[column].name
|
|
225
|
+
} else {
|
|
226
|
+
text = indexTitle || 'Location'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<th
|
|
231
|
+
key={`col-header-${column}`}
|
|
232
|
+
tabIndex='0'
|
|
233
|
+
title={text}
|
|
234
|
+
role='columnheader'
|
|
235
|
+
scope='col'
|
|
236
|
+
onClick={() => {
|
|
237
|
+
setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
|
|
238
|
+
}}
|
|
239
|
+
onKeyDown={e => {
|
|
240
|
+
if (e.keyCode === 13) {
|
|
241
|
+
setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
|
|
242
|
+
}
|
|
243
|
+
}}
|
|
244
|
+
className={sortBy.column === column ? (sortBy.asc ? 'sort sort-asc' : 'sort sort-desc') : 'sort'}
|
|
245
|
+
{...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
|
|
246
|
+
>
|
|
247
|
+
{text}
|
|
248
|
+
<button>
|
|
249
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
|
|
250
|
+
</button>
|
|
251
|
+
</th>
|
|
252
|
+
)
|
|
253
|
+
})}
|
|
254
|
+
</tr>
|
|
314
255
|
</thead>
|
|
315
|
-
<tbody
|
|
256
|
+
<tbody>
|
|
316
257
|
{rows.map(row => {
|
|
317
|
-
prepareRow(row)
|
|
318
258
|
return (
|
|
319
|
-
<tr
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
259
|
+
<tr role='row'>
|
|
260
|
+
{Object.keys(columns)
|
|
261
|
+
.filter(column => columns[column].dataTable === true && columns[column].name)
|
|
262
|
+
.map(column => {
|
|
263
|
+
let cellValue
|
|
264
|
+
|
|
265
|
+
if (column === 'geo') {
|
|
266
|
+
const rowObj = runtimeData[row]
|
|
267
|
+
const legendColor = applyLegendToRow(rowObj)
|
|
268
|
+
|
|
269
|
+
var labelValue
|
|
270
|
+
if (state.general.geoType !== 'us-county' || state.general.type === 'us-geocode') {
|
|
271
|
+
labelValue = displayGeoName(row)
|
|
272
|
+
} else {
|
|
273
|
+
labelValue = formatLegendLocation(row)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
labelValue = getCellAnchor(labelValue, rowObj)
|
|
277
|
+
|
|
278
|
+
cellValue = (
|
|
279
|
+
<>
|
|
280
|
+
<LegendCircle fill={legendColor[0]} />
|
|
281
|
+
{labelValue}
|
|
282
|
+
</>
|
|
283
|
+
)
|
|
284
|
+
} else {
|
|
285
|
+
cellValue = displayDataAsText(runtimeData[row][state.columns[column].name], column)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<td tabIndex='0' role='gridcell' onClick={e => (state.general.type === 'bubble' && state.general.allowMapZoom && state.general.geoType === 'world' ? setFilteredCountryCode(row) : true)}>
|
|
290
|
+
{cellValue}
|
|
291
|
+
</td>
|
|
292
|
+
)
|
|
293
|
+
})}
|
|
327
294
|
</tr>
|
|
328
295
|
)
|
|
329
296
|
})}
|