@cdc/map 4.24.9 → 4.24.10
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/LICENSE +201 -0
- package/dist/cdcmap.js +22964 -22605
- package/package.json +3 -3
- package/src/CdcMap.tsx +14 -8
- package/src/_stories/CdcMap.stories.tsx +2 -6
- package/src/components/Annotation/AnnotationDropdown.styles.css +0 -1
- package/src/components/CityList.tsx +55 -10
- package/src/components/DataTable.tsx +94 -17
- package/src/components/EditorPanel/components/EditorPanel.tsx +81 -17
- package/src/components/Legend/components/Legend.tsx +0 -2
- package/src/components/Legend/components/index.scss +18 -12
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +2 -2
- package/src/components/UsaMap/components/UsaMap.County.tsx +48 -47
- package/src/components/UsaMap/components/UsaMap.State.tsx +2 -1
- package/src/components/UsaMap/helpers/shapes.ts +206 -0
- package/src/coreStyles_map.scss +3 -0
- package/src/data/initial-state.js +1 -0
- package/src/index.jsx +7 -1
- package/src/scss/editor-panel.scss +5 -10
- package/src/scss/main.scss +4 -5
- package/src/scss/map.scss +6 -2
- package/src/scss/mixins.scss +47 -0
- package/src/types/MapConfig.ts +5 -2
- package/src/types/MapContext.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/map",
|
|
3
|
-
"version": "4.24.
|
|
3
|
+
"version": "4.24.10",
|
|
4
4
|
"description": "React component for visualizing tabular data on a map of the United States or the world.",
|
|
5
5
|
"moduleName": "CdcMap",
|
|
6
6
|
"main": "dist/cdcmap",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"license": "Apache-2.0",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@cdc/core": "^4.24.
|
|
28
|
+
"@cdc/core": "^4.24.10",
|
|
29
29
|
"@emotion/core": "^10.0.28",
|
|
30
30
|
"@emotion/react": "^11.1.5",
|
|
31
31
|
"@hello-pangea/dnd": "^16.2.0",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"react": "^18.2.0",
|
|
53
53
|
"react-dom": "^18.2.0"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "a4d88d1bc91f596e1b0307d8e25c57ad8c668b75"
|
|
56
56
|
}
|
package/src/CdcMap.tsx
CHANGED
|
@@ -30,7 +30,6 @@ import { publish } from '@cdc/core/helpers/events'
|
|
|
30
30
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
31
31
|
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
32
32
|
import Title from '@cdc/core/components/ui/Title'
|
|
33
|
-
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
34
33
|
|
|
35
34
|
// Data
|
|
36
35
|
import { countryCoordinates } from './data/country-coordinates'
|
|
@@ -137,7 +136,8 @@ const CdcMap = ({
|
|
|
137
136
|
const legendRef = useRef(null)
|
|
138
137
|
const tooltipRef = useRef(null)
|
|
139
138
|
const legendId = useId()
|
|
140
|
-
|
|
139
|
+
// create random tooltipId
|
|
140
|
+
const tooltipId = `${Math.random().toString(16).slice(-4)}`
|
|
141
141
|
const mapId = useId()
|
|
142
142
|
|
|
143
143
|
const { changeFilterActive, handleSorting } = useFilters({ config: state, setConfig: setState })
|
|
@@ -1462,6 +1462,11 @@ const CdcMap = ({
|
|
|
1462
1462
|
if (newData) {
|
|
1463
1463
|
newState.data = newData
|
|
1464
1464
|
}
|
|
1465
|
+
} else if (newState.formattedData) {
|
|
1466
|
+
newState.data = newState.formattedData
|
|
1467
|
+
} else if (newState.dataDescription) {
|
|
1468
|
+
newState.data = transform.autoStandardize(newState.data)
|
|
1469
|
+
newState.data = transform.developerStandardize(newState.data, newState.dataDescription)
|
|
1465
1470
|
}
|
|
1466
1471
|
|
|
1467
1472
|
// This code goes through and adds the defaults for every property declaring in the initial state at the top.
|
|
@@ -1493,8 +1498,10 @@ const CdcMap = ({
|
|
|
1493
1498
|
// add ability to rename state properties over time.
|
|
1494
1499
|
const processedConfig = { ...coveUpdateWorker(newState) }
|
|
1495
1500
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1501
|
+
setTimeout(() => {
|
|
1502
|
+
setState(processedConfig)
|
|
1503
|
+
setLoading(false)
|
|
1504
|
+
}, 10)
|
|
1498
1505
|
}
|
|
1499
1506
|
|
|
1500
1507
|
const init = async () => {
|
|
@@ -1722,7 +1729,6 @@ const CdcMap = ({
|
|
|
1722
1729
|
tooltipRef,
|
|
1723
1730
|
topoData,
|
|
1724
1731
|
setTopoData,
|
|
1725
|
-
getTextWidth,
|
|
1726
1732
|
mapId
|
|
1727
1733
|
}
|
|
1728
1734
|
|
|
@@ -1839,7 +1845,9 @@ const CdcMap = ({
|
|
|
1839
1845
|
{'us-region' === geoType && <UsaMap.Region />}
|
|
1840
1846
|
{'us-county' === geoType && <UsaMap.County />}
|
|
1841
1847
|
{'world' === geoType && <WorldMap />}
|
|
1842
|
-
{'data' === general.type && logo &&
|
|
1848
|
+
{'data' === general.type && logo && (
|
|
1849
|
+
<img src={logo} alt='' className='map-logo' style={{ maxWidth: '50px' }} />
|
|
1850
|
+
)}
|
|
1843
1851
|
</>
|
|
1844
1852
|
)}
|
|
1845
1853
|
</section>
|
|
@@ -1923,8 +1931,6 @@ const CdcMap = ({
|
|
|
1923
1931
|
|
|
1924
1932
|
{state.annotations.length > 0 && <Annotation.Dropdown />}
|
|
1925
1933
|
|
|
1926
|
-
{state.annotations.length > 0 && <Annotation.Dropdown />}
|
|
1927
|
-
|
|
1928
1934
|
{general.footnotes && <section className='footnotes'>{parse(general.footnotes)}</section>}
|
|
1929
1935
|
</section>
|
|
1930
1936
|
)}
|
|
@@ -30,7 +30,8 @@ export const Equal_Number_Map: Story = {
|
|
|
30
30
|
|
|
31
31
|
export const Scale_Based: Story = {
|
|
32
32
|
args: {
|
|
33
|
-
configUrl:
|
|
33
|
+
configUrl:
|
|
34
|
+
'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/examples/Scale-Based-Categorical-Map-With-Special-Classes.json'
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
export const Qualitative: Story = {
|
|
@@ -86,11 +87,6 @@ export const Custom_Map_Layers: Story = {
|
|
|
86
87
|
configUrl: 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/examples/custom-layer-map.json'
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
|
-
export const Geocode: Story = {
|
|
90
|
-
args: {
|
|
91
|
-
configUrl: 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/examples/example-u-s-geo-code-dat.json'
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
90
|
|
|
95
91
|
export const Single_State_With_Filters: Story = {
|
|
96
92
|
args: {
|
|
@@ -8,7 +8,18 @@ import { getFilterControllingStatePicked } from './UsaMap/helpers/map'
|
|
|
8
8
|
|
|
9
9
|
import ConfigContext from '../context'
|
|
10
10
|
|
|
11
|
-
const CityList = ({
|
|
11
|
+
const CityList = ({
|
|
12
|
+
data,
|
|
13
|
+
geoClickHandler,
|
|
14
|
+
applyTooltipsToGeo,
|
|
15
|
+
displayGeoName,
|
|
16
|
+
applyLegendToRow,
|
|
17
|
+
titleCase,
|
|
18
|
+
setSharedFilterValue,
|
|
19
|
+
isFilterValueSupported,
|
|
20
|
+
tooltipId,
|
|
21
|
+
projection
|
|
22
|
+
}) => {
|
|
12
23
|
const [citiesData, setCitiesData] = useState({})
|
|
13
24
|
const { scale, state, topoData, runtimeData, position } = useContext(ConfigContext)
|
|
14
25
|
if (!projection) return
|
|
@@ -28,7 +39,9 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
28
39
|
|
|
29
40
|
if (state.general.type === 'bubble') {
|
|
30
41
|
const maxDataValue = Math.max(...(data ? Object.keys(data).map(key => data[key][state.columns.primary.name]) : [0]))
|
|
31
|
-
const sortedRuntimeData = Object.values(data).sort((a, b) =>
|
|
42
|
+
const sortedRuntimeData = Object.values(data).sort((a, b) =>
|
|
43
|
+
a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1
|
|
44
|
+
)
|
|
32
45
|
if (!sortedRuntimeData) return
|
|
33
46
|
|
|
34
47
|
// Set bubble sizes
|
|
@@ -84,7 +97,12 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
84
97
|
|
|
85
98
|
let transform = ''
|
|
86
99
|
|
|
87
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
!geoData?.[state.columns.longitude.name] &&
|
|
102
|
+
!geoData?.[state.columns.latitude.name] &&
|
|
103
|
+
city &&
|
|
104
|
+
supportedCities[city.toUpperCase()]
|
|
105
|
+
) {
|
|
88
106
|
transform = `translate(${projection(supportedCities[city.toUpperCase()])})`
|
|
89
107
|
}
|
|
90
108
|
|
|
@@ -96,7 +114,11 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
96
114
|
needsPointer = true
|
|
97
115
|
}
|
|
98
116
|
|
|
99
|
-
if (
|
|
117
|
+
if (
|
|
118
|
+
geoData?.[state.columns.longitude.name] &&
|
|
119
|
+
geoData?.[state.columns.latitude.name] &&
|
|
120
|
+
state.general.geoType === 'single-state'
|
|
121
|
+
) {
|
|
100
122
|
const statePicked = getFilterControllingStatePicked(state, runtimeData)
|
|
101
123
|
const _statePickedData = topoData?.states?.find(s => s.properties.name === statePicked)
|
|
102
124
|
// SVG ITEMS
|
|
@@ -112,8 +134,9 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
112
134
|
_statePickedData
|
|
113
135
|
)
|
|
114
136
|
let coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
transform = `translate(${newProjection(coords)}) scale(${
|
|
138
|
+
state.visual.geoCodeCircleSize / (position.zoom > 1 ? position.zoom : 1)
|
|
139
|
+
})`
|
|
117
140
|
needsPointer = true
|
|
118
141
|
}
|
|
119
142
|
|
|
@@ -123,8 +146,20 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
123
146
|
|
|
124
147
|
const styles = {
|
|
125
148
|
fill: legendColors[0],
|
|
126
|
-
opacity:
|
|
127
|
-
|
|
149
|
+
opacity:
|
|
150
|
+
setSharedFilterValue &&
|
|
151
|
+
isFilterValueSupported &&
|
|
152
|
+
data[city] &&
|
|
153
|
+
data[city][state.columns.geo.name] !== setSharedFilterValue
|
|
154
|
+
? 0.5
|
|
155
|
+
: 1,
|
|
156
|
+
stroke:
|
|
157
|
+
setSharedFilterValue &&
|
|
158
|
+
isFilterValueSupported &&
|
|
159
|
+
data[city] &&
|
|
160
|
+
data[city][state.columns.geo.name] === setSharedFilterValue
|
|
161
|
+
? 'rgba(0, 0, 0, 1)'
|
|
162
|
+
: 'rgba(0, 0, 0, 0.4)',
|
|
128
163
|
'&:hover': {
|
|
129
164
|
fill: legendColors[1],
|
|
130
165
|
outline: 0
|
|
@@ -137,7 +172,10 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
137
172
|
}
|
|
138
173
|
|
|
139
174
|
// If we need to add a cursor pointer
|
|
140
|
-
if (
|
|
175
|
+
if (
|
|
176
|
+
(state.columns.navigate && geoData?.[state.columns.navigate.name] && geoData[state.columns.navigate.name]) ||
|
|
177
|
+
state.tooltips.appearanceType === 'click'
|
|
178
|
+
) {
|
|
141
179
|
styles.cursor = 'pointer'
|
|
142
180
|
}
|
|
143
181
|
|
|
@@ -174,8 +212,14 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
174
212
|
})
|
|
175
213
|
|
|
176
214
|
if (cityStyle !== undefined && cityStyle.shape) {
|
|
177
|
-
if (
|
|
215
|
+
if (
|
|
216
|
+
!geoData?.[state.columns.longitude.name] &&
|
|
217
|
+
!geoData?.[state.columns.latitude.name] &&
|
|
218
|
+
city &&
|
|
219
|
+
supportedCities[city.toUpperCase()]
|
|
220
|
+
) {
|
|
178
221
|
let translate = `translate(${projection(supportedCities[city.toUpperCase()])})`
|
|
222
|
+
|
|
179
223
|
return (
|
|
180
224
|
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
181
225
|
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
@@ -186,6 +230,7 @@ const CityList = ({ data, geoClickHandler, applyTooltipsToGeo, displayGeoName, a
|
|
|
186
230
|
if (geoData?.[state.columns.longitude.name] && geoData?.[state.columns.latitude.name]) {
|
|
187
231
|
const coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
|
|
188
232
|
let translate = `translate(${projection(coords)})`
|
|
233
|
+
console.log(translate, 'translate')
|
|
189
234
|
return (
|
|
190
235
|
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
191
236
|
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
@@ -13,7 +13,25 @@ import Loading from '@cdc/core/components/Loading'
|
|
|
13
13
|
|
|
14
14
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
15
15
|
const DataTable = props => {
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
state,
|
|
18
|
+
tableTitle,
|
|
19
|
+
indexTitle,
|
|
20
|
+
mapTitle,
|
|
21
|
+
rawData,
|
|
22
|
+
runtimeData,
|
|
23
|
+
headerColor,
|
|
24
|
+
expandDataTable,
|
|
25
|
+
columns,
|
|
26
|
+
displayDataAsText,
|
|
27
|
+
applyLegendToRow,
|
|
28
|
+
displayGeoName,
|
|
29
|
+
navigationHandler,
|
|
30
|
+
viewport,
|
|
31
|
+
formatLegendLocation,
|
|
32
|
+
tabbingId,
|
|
33
|
+
setFilteredCountryCode
|
|
34
|
+
} = props
|
|
17
35
|
|
|
18
36
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
19
37
|
const [sortBy, setSortBy] = useState({ column: 'geo', asc: false })
|
|
@@ -140,7 +158,9 @@ const DataTable = props => {
|
|
|
140
158
|
csvData = Papa.unparse(rawData.map(row => ({ FullGeoName: displayGeoName(row[state.columns.geo.name]), ...row })))
|
|
141
159
|
} else {
|
|
142
160
|
// Unparse + Add column for full Geo name
|
|
143
|
-
csvData = Papa.unparse(
|
|
161
|
+
csvData = Papa.unparse(
|
|
162
|
+
rawData.map(row => ({ FullGeoName: formatLegendLocation(row[state.columns.geo.name]), ...row }))
|
|
163
|
+
)
|
|
144
164
|
}
|
|
145
165
|
|
|
146
166
|
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
|
|
@@ -154,16 +174,36 @@ const DataTable = props => {
|
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
return (
|
|
157
|
-
<a
|
|
177
|
+
<a
|
|
178
|
+
download={fileName}
|
|
179
|
+
type='button'
|
|
180
|
+
onClick={saveBlob}
|
|
181
|
+
href={URL.createObjectURL(blob)}
|
|
182
|
+
aria-label='Download this data in a CSV file format.'
|
|
183
|
+
className={`${headerColor} no-border`}
|
|
184
|
+
id={`${skipId}`}
|
|
185
|
+
data-html2canvas-ignore
|
|
186
|
+
role='button'
|
|
187
|
+
>
|
|
158
188
|
Download Data (CSV)
|
|
159
189
|
</a>
|
|
160
190
|
)
|
|
161
191
|
}, [rawData, state.table])
|
|
162
192
|
|
|
193
|
+
const TableMediaControls = ({ belowTable }) => {
|
|
194
|
+
return (
|
|
195
|
+
<MediaControls.Section classes={['download-links'] + (belowTable ? 'below-table' : '')}>
|
|
196
|
+
<MediaControls.Link config={state} />
|
|
197
|
+
{state.general.showDownloadButton && <DownloadButton />}
|
|
198
|
+
</MediaControls.Section>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
163
202
|
// Change accessibility label depending on expanded status
|
|
164
203
|
useEffect(() => {
|
|
165
204
|
const expandedLabel = 'Accessible data table.'
|
|
166
|
-
const collapsedLabel =
|
|
205
|
+
const collapsedLabel =
|
|
206
|
+
'Accessible data table. This table is currently collapsed visually but can still be read using a screen reader.'
|
|
167
207
|
|
|
168
208
|
if (expanded === true && accessibilityLabel !== expandedLabel) {
|
|
169
209
|
setAccessibilityLabel(expandedLabel)
|
|
@@ -180,7 +220,10 @@ const DataTable = props => {
|
|
|
180
220
|
const rows = Object.keys(runtimeData)
|
|
181
221
|
.filter(row => applyLegendToRow(runtimeData[row]))
|
|
182
222
|
.sort((a, b) => {
|
|
183
|
-
const sortVal = customSort(
|
|
223
|
+
const sortVal = customSort(
|
|
224
|
+
runtimeData[a][state.columns[sortBy.column].name],
|
|
225
|
+
runtimeData[b][state.columns[sortBy.column].name]
|
|
226
|
+
)
|
|
184
227
|
if (!sortBy.asc) return sortVal
|
|
185
228
|
if (sortVal === 0) return 0
|
|
186
229
|
if (sortVal < 0) return 1
|
|
@@ -189,11 +232,12 @@ const DataTable = props => {
|
|
|
189
232
|
|
|
190
233
|
return (
|
|
191
234
|
<ErrorBoundary component='DataTable'>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
|
|
196
|
-
|
|
235
|
+
{!state.table.showDownloadLinkBelow && <TableMediaControls />}
|
|
236
|
+
<section
|
|
237
|
+
id={tabbingId.replace('#', '')}
|
|
238
|
+
className={`data-table-container ${viewport}`}
|
|
239
|
+
aria-label={accessibilityLabel}
|
|
240
|
+
>
|
|
197
241
|
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
198
242
|
<div
|
|
199
243
|
className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
@@ -210,9 +254,23 @@ const DataTable = props => {
|
|
|
210
254
|
<Icon display={expanded ? 'minus' : 'plus'} base />
|
|
211
255
|
{tableTitle}
|
|
212
256
|
</div>
|
|
213
|
-
<div
|
|
214
|
-
|
|
215
|
-
|
|
257
|
+
<div
|
|
258
|
+
className='table-container'
|
|
259
|
+
style={{ maxHeight: state.dataTable.limitHeight && `${state.dataTable.height}px`, overflowY: 'scroll' }}
|
|
260
|
+
>
|
|
261
|
+
<table
|
|
262
|
+
height={expanded ? null : 0}
|
|
263
|
+
role='table'
|
|
264
|
+
aria-live='assertive'
|
|
265
|
+
className={expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
|
|
266
|
+
hidden={!expanded}
|
|
267
|
+
aria-rowcount={state?.data.length ? state.data.length : '-1'}
|
|
268
|
+
>
|
|
269
|
+
<caption className='cdcdataviz-sr-only'>
|
|
270
|
+
{state.dataTable.caption
|
|
271
|
+
? state.dataTable.caption
|
|
272
|
+
: `Datatable showing data for the ${mapLookup[state.general.geoType]} figure.`}
|
|
273
|
+
</caption>
|
|
216
274
|
<thead style={{ position: 'sticky', top: 0, zIndex: 999 }}>
|
|
217
275
|
<tr>
|
|
218
276
|
{Object.keys(columns)
|
|
@@ -240,11 +298,19 @@ const DataTable = props => {
|
|
|
240
298
|
setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
|
|
241
299
|
}
|
|
242
300
|
}}
|
|
243
|
-
className={
|
|
244
|
-
|
|
301
|
+
className={
|
|
302
|
+
sortBy.column === column ? (sortBy.asc ? 'sort sort-asc' : 'sort sort-desc') : 'sort'
|
|
303
|
+
}
|
|
304
|
+
{...(sortBy.column === column
|
|
305
|
+
? sortBy.asc
|
|
306
|
+
? { 'aria-sort': 'ascending' }
|
|
307
|
+
: { 'aria-sort': 'descending' }
|
|
308
|
+
: null)}
|
|
245
309
|
>
|
|
246
310
|
{text}
|
|
247
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
|
|
311
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${
|
|
312
|
+
sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
|
|
313
|
+
} order`}</span>
|
|
248
314
|
</th>
|
|
249
315
|
)
|
|
250
316
|
})}
|
|
@@ -283,7 +349,17 @@ const DataTable = props => {
|
|
|
283
349
|
}
|
|
284
350
|
|
|
285
351
|
return (
|
|
286
|
-
<td
|
|
352
|
+
<td
|
|
353
|
+
tabIndex='0'
|
|
354
|
+
role='gridcell'
|
|
355
|
+
onClick={e =>
|
|
356
|
+
state.general.type === 'bubble' &&
|
|
357
|
+
state.general.allowMapZoom &&
|
|
358
|
+
state.general.geoType === 'world'
|
|
359
|
+
? setFilteredCountryCode(row)
|
|
360
|
+
: true
|
|
361
|
+
}
|
|
362
|
+
>
|
|
287
363
|
{cellValue}
|
|
288
364
|
</td>
|
|
289
365
|
)
|
|
@@ -295,6 +371,7 @@ const DataTable = props => {
|
|
|
295
371
|
</table>
|
|
296
372
|
</div>
|
|
297
373
|
</section>
|
|
374
|
+
{state.table.showDownloadLinkBelow && <TableMediaControls belowTable={true} />}
|
|
298
375
|
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
299
376
|
Skipped data table.
|
|
300
377
|
</div>
|
|
@@ -541,6 +541,15 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
541
541
|
}
|
|
542
542
|
})
|
|
543
543
|
break
|
|
544
|
+
case 'toggleDownloadLinkBelow':
|
|
545
|
+
setState({
|
|
546
|
+
...state,
|
|
547
|
+
table: {
|
|
548
|
+
...state.table,
|
|
549
|
+
showDownloadLinkBelow: !state.table.showDownloadLinkBelow
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
break
|
|
544
553
|
case 'toggleDownloadPdfButton':
|
|
545
554
|
setState({
|
|
546
555
|
...state,
|
|
@@ -1153,12 +1162,20 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1153
1162
|
function filterColorPalettes() {
|
|
1154
1163
|
let sequential = []
|
|
1155
1164
|
let nonSequential = []
|
|
1165
|
+
let accessibleColors = []
|
|
1156
1166
|
for (let paletteName in colorPalettes) {
|
|
1157
1167
|
if (!isReversed) {
|
|
1158
1168
|
if (paletteName.includes('qualitative') && !paletteName.endsWith('reverse')) {
|
|
1159
1169
|
nonSequential.push(paletteName)
|
|
1160
1170
|
}
|
|
1161
|
-
if (
|
|
1171
|
+
if (paletteName.includes('colorblindsafe') && !paletteName.endsWith('reverse')) {
|
|
1172
|
+
accessibleColors.push(paletteName)
|
|
1173
|
+
}
|
|
1174
|
+
if (
|
|
1175
|
+
!paletteName.includes('qualitative') &&
|
|
1176
|
+
!paletteName.includes('colorblindsafe') &&
|
|
1177
|
+
!paletteName.endsWith('reverse')
|
|
1178
|
+
) {
|
|
1162
1179
|
sequential.push(paletteName)
|
|
1163
1180
|
}
|
|
1164
1181
|
}
|
|
@@ -1166,15 +1183,22 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
1166
1183
|
if (paletteName.includes('qualitative') && paletteName.endsWith('reverse')) {
|
|
1167
1184
|
nonSequential.push(paletteName)
|
|
1168
1185
|
}
|
|
1169
|
-
if (
|
|
1186
|
+
if (paletteName.includes('colorblindsafe') && paletteName.endsWith('reverse')) {
|
|
1187
|
+
accessibleColors.push(paletteName)
|
|
1188
|
+
}
|
|
1189
|
+
if (
|
|
1190
|
+
!paletteName.includes('qualitative') &&
|
|
1191
|
+
!paletteName.includes('colorblindsafe') &&
|
|
1192
|
+
paletteName.endsWith('reverse')
|
|
1193
|
+
) {
|
|
1170
1194
|
sequential.push(paletteName)
|
|
1171
1195
|
}
|
|
1172
1196
|
}
|
|
1173
1197
|
}
|
|
1174
1198
|
|
|
1175
|
-
return [sequential, nonSequential]
|
|
1199
|
+
return [sequential, nonSequential, accessibleColors]
|
|
1176
1200
|
}
|
|
1177
|
-
const [sequential, nonSequential] = filterColorPalettes()
|
|
1201
|
+
const [sequential, nonSequential, accessibleColors] = filterColorPalettes()
|
|
1178
1202
|
|
|
1179
1203
|
useEffect(() => {
|
|
1180
1204
|
let paletteName = ''
|
|
@@ -3084,6 +3108,16 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3084
3108
|
/>
|
|
3085
3109
|
<span className='edit-label'>Enable Image Download</span>
|
|
3086
3110
|
</label>
|
|
3111
|
+
<label className='checkbox'>
|
|
3112
|
+
<input
|
|
3113
|
+
type='checkbox'
|
|
3114
|
+
checked={state.table.showDownloadLinkBelow}
|
|
3115
|
+
onChange={event => {
|
|
3116
|
+
handleEditorChanges('toggleDownloadLinkBelow', event.target.checked)
|
|
3117
|
+
}}
|
|
3118
|
+
/>
|
|
3119
|
+
<span className='edit-label'>Show Download Link Below Table</span>
|
|
3120
|
+
</label>
|
|
3087
3121
|
{/* <label className='checkbox'>
|
|
3088
3122
|
<input
|
|
3089
3123
|
type='checkbox'
|
|
@@ -3286,6 +3320,41 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3286
3320
|
)
|
|
3287
3321
|
})}
|
|
3288
3322
|
</ul>
|
|
3323
|
+
<span>Colorblind Safe</span>
|
|
3324
|
+
<ul className='color-palette'>
|
|
3325
|
+
{accessibleColors.map(palette => {
|
|
3326
|
+
const colorOne = {
|
|
3327
|
+
backgroundColor: colorPalettes[palette][2]
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
const colorTwo = {
|
|
3331
|
+
backgroundColor: colorPalettes[palette][4]
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
const colorThree = {
|
|
3335
|
+
backgroundColor: colorPalettes[palette][6]
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
// hide palettes with too few colors for region maps
|
|
3339
|
+
if (colorPalettes[palette].length <= 8 && state.general.geoType === 'us-region') {
|
|
3340
|
+
return ''
|
|
3341
|
+
}
|
|
3342
|
+
return (
|
|
3343
|
+
<li
|
|
3344
|
+
title={palette}
|
|
3345
|
+
key={palette}
|
|
3346
|
+
onClick={() => {
|
|
3347
|
+
handleEditorChanges('color', palette)
|
|
3348
|
+
}}
|
|
3349
|
+
className={state.color === palette ? 'selected' : ''}
|
|
3350
|
+
>
|
|
3351
|
+
<span style={colorOne}></span>
|
|
3352
|
+
<span style={colorTwo}></span>
|
|
3353
|
+
<span style={colorThree}></span>
|
|
3354
|
+
</li>
|
|
3355
|
+
)
|
|
3356
|
+
})}
|
|
3357
|
+
</ul>
|
|
3289
3358
|
<label>
|
|
3290
3359
|
Geocode Settings
|
|
3291
3360
|
<TextField
|
|
@@ -3370,14 +3439,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3370
3439
|
>
|
|
3371
3440
|
<option value='circle'>Circle</option>
|
|
3372
3441
|
<option value='pin'>Pin</option>
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
<option value='diamond'>Diamond</option>
|
|
3378
|
-
<option value='star'>Star</option>
|
|
3379
|
-
</>
|
|
3380
|
-
)}
|
|
3442
|
+
<option value='square'>Square</option>
|
|
3443
|
+
<option value='triangle'>Triangle</option>
|
|
3444
|
+
<option value='diamond'>Diamond</option>
|
|
3445
|
+
<option value='star'>Star</option>
|
|
3381
3446
|
</select>
|
|
3382
3447
|
</label>
|
|
3383
3448
|
<TextField
|
|
@@ -3461,11 +3526,10 @@ const EditorPanel = ({ columnsRequiredChecker }) => {
|
|
|
3461
3526
|
</div>
|
|
3462
3527
|
)
|
|
3463
3528
|
})}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
)}
|
|
3529
|
+
|
|
3530
|
+
<button type='button' onClick={() => editCityStyles('add', 0, '', '')} className='btn full-width'>
|
|
3531
|
+
Add city style
|
|
3532
|
+
</button>
|
|
3469
3533
|
</>
|
|
3470
3534
|
<label htmlFor='opacity'>
|
|
3471
3535
|
<TextField
|
|
@@ -38,7 +38,6 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
38
38
|
setRuntimeLegend,
|
|
39
39
|
state,
|
|
40
40
|
viewport,
|
|
41
|
-
getTextWidth,
|
|
42
41
|
mapId
|
|
43
42
|
} = useContext(ConfigContext)
|
|
44
43
|
|
|
@@ -278,7 +277,6 @@ const Legend = forwardRef<HTMLDivElement, LegendProps>((props, ref) => {
|
|
|
278
277
|
dimensions={dimensions}
|
|
279
278
|
currentViewport={currentViewport}
|
|
280
279
|
config={state}
|
|
281
|
-
getTextWidth={getTextWidth}
|
|
282
280
|
/>
|
|
283
281
|
<ul className={legendClasses.ul.join(' ') || ''} aria-label='Legend items'>
|
|
284
282
|
{state.legend.style === 'gradient' ? '' : legendList()}
|