@cdc/map 4.22.11 → 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/{private/city-state2.json → city-state.json} +2 -2
- package/examples/example-city-state.json +202 -25
- package/package.json +3 -4
- package/src/CdcMap.js +142 -232
- package/src/components/BubbleList.js +2 -3
- package/src/components/CityList.js +1 -2
- package/src/components/DataTable.js +31 -3
- package/src/components/EditorPanel.js +73 -16
- package/src/components/Filters.js +114 -0
- package/src/components/Sidebar.js +0 -6
- package/src/components/UsaMap.js +4 -4
- package/src/context.js +5 -0
- package/src/data/initial-state.js +19 -15
- package/src/data/supported-geos.js +3201 -3175
- package/src/index.html +32 -28
- package/src/scss/datatable.scss +1 -2
- package/src/scss/filters.scss +42 -0
- package/src/scss/main.scss +4 -3
- package/examples/private/atsdr.json +0 -429
- package/examples/private/atsdr_new.json +0 -436
- package/examples/private/bubble.json +0 -283
- package/examples/private/city-state.json +0 -428
- package/examples/private/cty-issue.json +0 -42765
- package/examples/private/default-usa.json +0 -457
- package/examples/private/default-world-data.json +0 -1444
- package/examples/private/default.json +0 -968
- package/examples/private/diff.json +0 -226
- package/examples/private/filters.json +0 -1
- package/examples/private/legend-issue.json +0 -3271
- package/examples/private/map-issue.json +0 -166
- package/examples/private/map-rounding-error.json +0 -42756
- 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 -5819
- package/examples/private/wcmsrd-14492-data.json +0 -292
- package/examples/private/wcmsrd-14492.json +0 -104
- package/examples/private/wcmsrd-test.json +0 -265
- package/examples/private/world.json +0 -1580
- package/examples/private/worldmap.json +0 -1490
package/src/CdcMap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef,
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react'
|
|
2
2
|
import * as d3 from 'd3'
|
|
3
3
|
|
|
4
4
|
// IE11
|
|
@@ -10,9 +10,6 @@ import ResizeObserver from 'resize-observer-polyfill'
|
|
|
10
10
|
import ReactTooltip from 'react-tooltip'
|
|
11
11
|
import chroma from 'chroma-js'
|
|
12
12
|
import parse from 'html-react-parser'
|
|
13
|
-
import html2pdf from 'html2pdf.js'
|
|
14
|
-
import html2canvas from 'html2canvas'
|
|
15
|
-
import Canvg from 'canvg'
|
|
16
13
|
|
|
17
14
|
// Data
|
|
18
15
|
import colorPalettes from '../../core/data/colorPalettes'
|
|
@@ -20,23 +17,18 @@ import ExternalIcon from './images/external-link.svg'
|
|
|
20
17
|
import { supportedStates, supportedTerritories, supportedCountries, supportedCounties, supportedCities, supportedStatesFipsCodes, stateFipsToTwoDigit, supportedRegions } from './data/supported-geos'
|
|
21
18
|
import initialState from './data/initial-state'
|
|
22
19
|
import { countryCoordinates } from './data/country-coordinates'
|
|
20
|
+
import CoveMediaControls from '@cdc/core/helpers/CoveMediaControls'
|
|
23
21
|
|
|
24
22
|
// Sass
|
|
25
23
|
import './scss/main.scss'
|
|
26
24
|
import './scss/btn.scss'
|
|
27
25
|
|
|
28
|
-
// Images
|
|
29
|
-
// TODO: Move to Icon component
|
|
30
|
-
import DownloadImg from './images/icon-download-img.svg'
|
|
31
|
-
import DownloadPdf from './images/icon-download-pdf.svg'
|
|
32
|
-
|
|
33
26
|
// Core
|
|
34
27
|
import Loading from '@cdc/core/components/Loading'
|
|
35
28
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
36
29
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
37
30
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
38
31
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
39
|
-
import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
40
32
|
|
|
41
33
|
// Child Components
|
|
42
34
|
import Sidebar from './components/Sidebar'
|
|
@@ -49,6 +41,8 @@ import DataTable from './components/DataTable' // Future: Lazy
|
|
|
49
41
|
import NavigationMenu from './components/NavigationMenu' // Future: Lazy
|
|
50
42
|
import WorldMap from './components/WorldMap' // Future: Lazy
|
|
51
43
|
import SingleStateMap from './components/SingleStateMap' // Future: Lazy
|
|
44
|
+
import Filters from './components/Filters'
|
|
45
|
+
import Context from './context'
|
|
52
46
|
|
|
53
47
|
import { publish } from '@cdc/core/helpers/events'
|
|
54
48
|
|
|
@@ -106,7 +100,6 @@ const getUniqueValues = (data, columnName) => {
|
|
|
106
100
|
}
|
|
107
101
|
|
|
108
102
|
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, configUrl, logo = null, setConfig, setSharedFilter, setSharedFilterValue, hostname = 'localhost:8080', link }) => {
|
|
109
|
-
const [showLoadingMessage, setShowLoadingMessage] = useState(false)
|
|
110
103
|
const transform = new DataTransform()
|
|
111
104
|
const [state, setState] = useState({ ...initialState })
|
|
112
105
|
const [loading, setLoading] = useState(true)
|
|
@@ -120,13 +113,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
120
113
|
const [position, setPosition] = useState(state.mapPosition)
|
|
121
114
|
const [coveLoadedHasRan, setCoveLoadedHasRan] = useState(false)
|
|
122
115
|
const [container, setContainer] = useState()
|
|
116
|
+
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
|
|
123
117
|
|
|
124
118
|
let legendMemo = useRef(new Map())
|
|
119
|
+
let innerContainerRef = useRef()
|
|
125
120
|
|
|
126
121
|
useEffect(() => {
|
|
127
122
|
try {
|
|
128
123
|
if (filteredCountryCode) {
|
|
129
|
-
const filteredCountryObj = runtimeData[filteredCountryCode]
|
|
130
124
|
const coordinates = countryCoordinates[filteredCountryCode]
|
|
131
125
|
const long = coordinates[1]
|
|
132
126
|
const lat = coordinates[0]
|
|
@@ -160,13 +154,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
160
154
|
}
|
|
161
155
|
}, [state.mapPosition, setPosition])
|
|
162
156
|
|
|
163
|
-
const setZoom = reversedCoordinates => {
|
|
164
|
-
setState({
|
|
165
|
-
...state,
|
|
166
|
-
mapPosition: { coordinates: reversedCoordinates, zoom: 3 }
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
|
|
170
157
|
const resizeObserver = new ResizeObserver(entries => {
|
|
171
158
|
for (let entry of entries) {
|
|
172
159
|
let newViewport = getViewport(entry.contentRect.width)
|
|
@@ -251,7 +238,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
251
238
|
const generateRuntimeLegend = useCallback((obj, runtimeData, hash) => {
|
|
252
239
|
const newLegendMemo = new Map() // Reset memoization
|
|
253
240
|
const primaryCol = obj.columns.primary.name,
|
|
254
|
-
isData = obj.general.type === 'data',
|
|
255
241
|
isBubble = obj.general.type === 'bubble',
|
|
256
242
|
categoricalCol = obj.columns.categorical ? obj.columns.categorical.name : undefined,
|
|
257
243
|
type = obj.legend.type,
|
|
@@ -410,7 +396,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
410
396
|
// Apply custom sorting or regular sorting
|
|
411
397
|
let configuredOrder = obj.legend.categoryValuesOrder ?? []
|
|
412
398
|
|
|
413
|
-
|
|
414
399
|
if (configuredOrder.length) {
|
|
415
400
|
sorted.sort((a, b) => {
|
|
416
401
|
return configuredOrder.indexOf(a) - configuredOrder.indexOf(b)
|
|
@@ -558,7 +543,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
558
543
|
|
|
559
544
|
breaks.map((item, index) => {
|
|
560
545
|
const setMin = index => {
|
|
561
|
-
//debugger;
|
|
562
546
|
let min = breaks[index]
|
|
563
547
|
|
|
564
548
|
// if first break is a seperated zero, min is zero
|
|
@@ -809,101 +793,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
809
793
|
}
|
|
810
794
|
}
|
|
811
795
|
|
|
812
|
-
const saveImageAs = (uri, filename) => {
|
|
813
|
-
const ie = navigator.userAgent.match(/MSIE\s([\d.]+)/)
|
|
814
|
-
const ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/)
|
|
815
|
-
const ieEdge = navigator.userAgent.match(/Edge/g)
|
|
816
|
-
const ieVer = ie ? ie[1] : ie11 ? 11 : ieEdge ? 12 : -1
|
|
817
|
-
|
|
818
|
-
if (ieVer > -1) {
|
|
819
|
-
const fileAsBlob = new Blob([uri], {
|
|
820
|
-
type: 'image/png'
|
|
821
|
-
})
|
|
822
|
-
window.navigator.msSaveBlob(fileAsBlob, filename)
|
|
823
|
-
} else {
|
|
824
|
-
const link = document.createElement('a')
|
|
825
|
-
if (typeof link.download === 'string') {
|
|
826
|
-
link.href = uri
|
|
827
|
-
link.download = filename
|
|
828
|
-
link.onclick = e => document.body.removeChild(e.target)
|
|
829
|
-
document.body.appendChild(link)
|
|
830
|
-
link.click()
|
|
831
|
-
} else {
|
|
832
|
-
window.open(uri)
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
const generateMedia = (target, type) => {
|
|
838
|
-
// Convert SVG to canvas
|
|
839
|
-
const baseSvg = mapSvg.current.querySelector('.rsm-svg')
|
|
840
|
-
|
|
841
|
-
const ratio = baseSvg.getBoundingClientRect().height / baseSvg.getBoundingClientRect().width
|
|
842
|
-
const calcHeight = ratio * 1440
|
|
843
|
-
const xmlSerializer = new XMLSerializer()
|
|
844
|
-
const svgStr = xmlSerializer.serializeToString(baseSvg)
|
|
845
|
-
const options = { log: false, ignoreMouse: true }
|
|
846
|
-
const canvas = document.createElement('canvas')
|
|
847
|
-
const ctx = canvas.getContext('2d')
|
|
848
|
-
ctx.canvas.width = 1440
|
|
849
|
-
ctx.canvas.height = calcHeight
|
|
850
|
-
const canvg = Canvg.fromString(ctx, svgStr, options)
|
|
851
|
-
canvg.start()
|
|
852
|
-
|
|
853
|
-
// Generate DOM <img> from svg data
|
|
854
|
-
const generatedImage = document.createElement('img')
|
|
855
|
-
generatedImage.src = canvas.toDataURL('image/png')
|
|
856
|
-
generatedImage.style.width = '100%'
|
|
857
|
-
generatedImage.style.height = 'auto'
|
|
858
|
-
|
|
859
|
-
baseSvg.style.display = 'none' // Hide default SVG during media generation
|
|
860
|
-
baseSvg.parentNode.insertBefore(generatedImage, baseSvg.nextSibling) // Insert png generated from canvas of svg
|
|
861
|
-
|
|
862
|
-
// Construct filename with timestamp
|
|
863
|
-
const date = new Date()
|
|
864
|
-
const filename = state.general.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
|
|
865
|
-
|
|
866
|
-
switch (type) {
|
|
867
|
-
case 'image':
|
|
868
|
-
return html2canvas(target, {
|
|
869
|
-
allowTaint: true,
|
|
870
|
-
backgroundColor: '#ffffff',
|
|
871
|
-
width: 1440,
|
|
872
|
-
windowWidth: 1440,
|
|
873
|
-
scale: 1,
|
|
874
|
-
logging: false
|
|
875
|
-
})
|
|
876
|
-
.then(canvas => {
|
|
877
|
-
saveImageAs(canvas.toDataURL(), filename + '.png')
|
|
878
|
-
})
|
|
879
|
-
.then(() => {
|
|
880
|
-
generatedImage.remove() // Remove generated png
|
|
881
|
-
baseSvg.style.display = null // Re-display initial svg map
|
|
882
|
-
})
|
|
883
|
-
case 'pdf':
|
|
884
|
-
let opt = {
|
|
885
|
-
margin: 0.2,
|
|
886
|
-
filename: filename + '.pdf',
|
|
887
|
-
image: { type: 'png' },
|
|
888
|
-
html2canvas: { scale: 2, logging: false },
|
|
889
|
-
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
html2pdf()
|
|
893
|
-
.set(opt)
|
|
894
|
-
.from(target)
|
|
895
|
-
.save()
|
|
896
|
-
.then(() => {
|
|
897
|
-
generatedImage.remove() // Remove generated png
|
|
898
|
-
baseSvg.style.display = null // Re-display initial svg map
|
|
899
|
-
})
|
|
900
|
-
break
|
|
901
|
-
default:
|
|
902
|
-
console.warn("generateMedia param 2 type must be 'image' or 'pdf'")
|
|
903
|
-
break
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
796
|
const changeFilterActive = async (idx, activeValue) => {
|
|
908
797
|
// Reset active legend toggles
|
|
909
798
|
resetLegendToggles()
|
|
@@ -1066,10 +955,26 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1066
955
|
}
|
|
1067
956
|
|
|
1068
957
|
const titleCase = string => {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
.
|
|
958
|
+
// if hyphen found, then split, uppercase each word, and put back together
|
|
959
|
+
if (string.includes('–') || string.includes('-')) {
|
|
960
|
+
let dashSplit = string.includes('–') ? string.split('–') : string.split('-') // determine hyphen or en dash to split on
|
|
961
|
+
let splitCharacter = string.includes('–') ? '–' : '-' // print hyphen or en dash later on.
|
|
962
|
+
let frontSplit = dashSplit[0]
|
|
963
|
+
.split(' ')
|
|
964
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
965
|
+
.join(' ')
|
|
966
|
+
let backSplit = dashSplit[1]
|
|
967
|
+
.split(' ')
|
|
968
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
969
|
+
.join(' ')
|
|
970
|
+
return frontSplit + splitCharacter + backSplit
|
|
971
|
+
} else {
|
|
972
|
+
// just return with each word upper cased
|
|
973
|
+
return string
|
|
974
|
+
.split(' ')
|
|
975
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
976
|
+
.join(' ')
|
|
977
|
+
}
|
|
1073
978
|
}
|
|
1074
979
|
|
|
1075
980
|
// This resets all active legend toggles.
|
|
@@ -1102,7 +1007,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1102
1007
|
// Attempts to find the corresponding value
|
|
1103
1008
|
const displayGeoName = key => {
|
|
1104
1009
|
let value = key
|
|
1105
|
-
|
|
1106
1010
|
// Map to first item in values array which is the preferred label
|
|
1107
1011
|
if (stateKeys.includes(value)) {
|
|
1108
1012
|
value = titleCase(supportedStates[key][0])
|
|
@@ -1240,8 +1144,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1240
1144
|
}
|
|
1241
1145
|
|
|
1242
1146
|
// handle urls with spaces in the name.
|
|
1243
|
-
if (newState.dataUrl) newState.dataUrl = encodeURI(`${newState.dataUrl}
|
|
1244
|
-
let newData = await fetchRemoteData(newState.dataUrl)
|
|
1147
|
+
if (newState.dataUrl) newState.dataUrl = encodeURI(`${newState.dataUrl}`)
|
|
1148
|
+
let newData = await fetchRemoteData(newState.dataUrl, 'map')
|
|
1245
1149
|
|
|
1246
1150
|
if (newData && newState.dataDescription) {
|
|
1247
1151
|
newData = transform.autoStandardize(newData)
|
|
@@ -1356,7 +1260,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1356
1260
|
specialClasses: state.legend.specialClasses,
|
|
1357
1261
|
geoType: state.general.geoType,
|
|
1358
1262
|
data: state.data,
|
|
1359
|
-
...runtimeLegend
|
|
1263
|
+
...runtimeLegend,
|
|
1264
|
+
...runtimeFilters
|
|
1360
1265
|
})
|
|
1361
1266
|
|
|
1362
1267
|
const hashData = hashObj({
|
|
@@ -1367,7 +1272,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1367
1272
|
primary: state.columns.primary.name,
|
|
1368
1273
|
data: state.data,
|
|
1369
1274
|
...runtimeFilters,
|
|
1370
|
-
mapPosition: state.mapPosition
|
|
1275
|
+
mapPosition: state.mapPosition,
|
|
1276
|
+
...runtimeFilters
|
|
1371
1277
|
})
|
|
1372
1278
|
|
|
1373
1279
|
// Data
|
|
@@ -1455,7 +1361,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1455
1361
|
setPosition,
|
|
1456
1362
|
setSharedFilterValue,
|
|
1457
1363
|
hasZoom: state.general.allowMapZoom,
|
|
1458
|
-
handleMapAriaLabels
|
|
1364
|
+
handleMapAriaLabels,
|
|
1365
|
+
runtimeFilters,
|
|
1366
|
+
setRuntimeFilters,
|
|
1367
|
+
innerContainerRef
|
|
1459
1368
|
}
|
|
1460
1369
|
|
|
1461
1370
|
if (!mapProps.data || !state.data) return <Loading />
|
|
@@ -1487,123 +1396,124 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1487
1396
|
const tabId = handleMapTabbing()
|
|
1488
1397
|
|
|
1489
1398
|
return (
|
|
1490
|
-
<
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
<
|
|
1498
|
-
<
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
</header>
|
|
1502
|
-
)}
|
|
1503
|
-
|
|
1504
|
-
<div>{general.introText && <section className='introText'>{parse(general.introText)}</section>}</div>
|
|
1505
|
-
|
|
1506
|
-
<section
|
|
1507
|
-
role='button'
|
|
1508
|
-
tabIndex='0'
|
|
1509
|
-
className={mapContainerClasses.join(' ')}
|
|
1510
|
-
onClick={e => closeModal(e)}
|
|
1511
|
-
onKeyDown={e => {
|
|
1512
|
-
if (e.keyCode === 13) {
|
|
1513
|
-
closeModal(e)
|
|
1514
|
-
}
|
|
1515
|
-
}}
|
|
1516
|
-
>
|
|
1517
|
-
{general.showDownloadMediaButton === true && (
|
|
1518
|
-
<div className='map-downloads' data-html2canvas-ignore>
|
|
1519
|
-
<div className='map-downloads__ui btn-group'>
|
|
1520
|
-
<button className='btn' title='Download Map as Image' onClick={() => generateMedia(outerContainerRef.current, 'image')}>
|
|
1521
|
-
<DownloadImg className='btn__icon' title='Download Map as Image' />
|
|
1522
|
-
</button>
|
|
1523
|
-
<button className='btn' title='Download Map as PDF' onClick={() => generateMedia(outerContainerRef.current, 'pdf')}>
|
|
1524
|
-
<DownloadPdf className='btn__icon' title='Download Map as PDF' />
|
|
1525
|
-
</button>
|
|
1399
|
+
<Context.Provider value={mapProps}>
|
|
1400
|
+
<div className={outerContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
|
|
1401
|
+
{isEditor && <EditorPanel isDashboard={isDashboard} state={state} setState={setState} loadConfig={loadConfig} setParentConfig={setConfig} setRuntimeFilters={setRuntimeFilters} runtimeFilters={runtimeFilters} runtimeLegend={runtimeLegend} columnsInData={Object.keys(state.data[0])} />}
|
|
1402
|
+
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
1403
|
+
<section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
|
|
1404
|
+
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && <ReactTooltip id='tooltip' place='right' type='light' html={true} className={tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'} />}
|
|
1405
|
+
{state.general.title && (
|
|
1406
|
+
<header className={general.showTitle === true ? 'visible' : 'hidden'} {...(!general.showTitle || !state.general.title ? { 'aria-hidden': true } : { 'aria-hidden': false })}>
|
|
1407
|
+
<div role='heading' className={'map-title ' + general.headerColor} tabIndex='0' aria-level='2'>
|
|
1408
|
+
<sup>{general.superTitle}</sup>
|
|
1409
|
+
<div>{parse(title)}</div>
|
|
1526
1410
|
</div>
|
|
1527
|
-
</
|
|
1411
|
+
</header>
|
|
1528
1412
|
)}
|
|
1529
1413
|
|
|
1530
|
-
<
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
<
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1414
|
+
<div>{general.introText && <section className='introText'>{parse(general.introText)}</section>}</div>
|
|
1415
|
+
|
|
1416
|
+
<Filters />
|
|
1417
|
+
|
|
1418
|
+
<div
|
|
1419
|
+
role='button'
|
|
1420
|
+
tabIndex='0'
|
|
1421
|
+
className={mapContainerClasses.join(' ')}
|
|
1422
|
+
onClick={e => closeModal(e)}
|
|
1423
|
+
onKeyDown={e => {
|
|
1424
|
+
if (e.keyCode === 13) {
|
|
1425
|
+
closeModal(e)
|
|
1426
|
+
}
|
|
1427
|
+
}}
|
|
1428
|
+
>
|
|
1429
|
+
<a id='skip-geo-container' className='cdcdataviz-sr-only-focusable' href={tabId}>
|
|
1430
|
+
Skip Over Map Container
|
|
1431
|
+
</a>
|
|
1432
|
+
|
|
1433
|
+
<section className='geography-container outline-none' ref={mapSvg} tabIndex='0'>
|
|
1434
|
+
{currentViewport && (
|
|
1435
|
+
<section className='geography-container' ref={mapSvg}>
|
|
1436
|
+
{modal && <Modal type={general.type} viewport={currentViewport} applyTooltipsToGeo={applyTooltipsToGeo} applyLegendToRow={applyLegendToRow} capitalize={state.tooltips.capitalizeLabels} content={modal} />}
|
|
1437
|
+
{'single-state' === general.geoType && <SingleStateMap supportedTerritories={supportedTerritories} {...mapProps} />}
|
|
1438
|
+
{'us' === general.geoType && 'us-geocode' !== state.general.type && <UsaMap supportedTerritories={supportedTerritories} {...mapProps} />}
|
|
1439
|
+
{'us-region' === general.geoType && <UsaRegionMap supportedTerritories={supportedTerritories} {...mapProps} />}
|
|
1440
|
+
{'world' === general.geoType && <WorldMap supportedCountries={supportedCountries} {...mapProps} />}
|
|
1441
|
+
{'us-county' === general.geoType && <CountyMap supportedCountries={supportedCountries} {...mapProps} />}
|
|
1442
|
+
{'data' === general.type && logo && <img src={logo} alt='' className='map-logo' />}
|
|
1443
|
+
</section>
|
|
1444
|
+
)}
|
|
1445
|
+
</section>
|
|
1446
|
+
|
|
1447
|
+
{general.showSidebar && 'navigation' !== general.type && (
|
|
1448
|
+
<Sidebar
|
|
1449
|
+
viewport={currentViewport}
|
|
1450
|
+
legend={state.legend}
|
|
1451
|
+
runtimeLegend={runtimeLegend}
|
|
1452
|
+
setRuntimeLegend={setRuntimeLegend}
|
|
1453
|
+
runtimeFilters={runtimeFilters}
|
|
1454
|
+
columns={state.columns}
|
|
1455
|
+
sharing={state.sharing}
|
|
1456
|
+
prefix={state.columns.primary.prefix}
|
|
1457
|
+
suffix={state.columns.primary.suffix}
|
|
1458
|
+
setState={setState}
|
|
1459
|
+
resetLegendToggles={resetLegendToggles}
|
|
1460
|
+
changeFilterActive={changeFilterActive}
|
|
1461
|
+
setAccessibleStatus={setAccessibleStatus}
|
|
1462
|
+
displayDataAsText={displayDataAsText}
|
|
1463
|
+
/>
|
|
1545
1464
|
)}
|
|
1546
|
-
</
|
|
1465
|
+
</div>
|
|
1547
1466
|
|
|
1548
|
-
{
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1467
|
+
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1468
|
+
|
|
1469
|
+
{link && link}
|
|
1470
|
+
|
|
1471
|
+
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1472
|
+
|
|
1473
|
+
<CoveMediaControls.Section classes={['download-buttons']}>
|
|
1474
|
+
{state.general.showDownloadImgButton && <CoveMediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
|
|
1475
|
+
{state.general.showDownloadPdfButton && <CoveMediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
|
|
1476
|
+
</CoveMediaControls.Section>
|
|
1477
|
+
|
|
1478
|
+
{state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1479
|
+
<DataTable
|
|
1480
|
+
state={state}
|
|
1481
|
+
rawData={state.data}
|
|
1482
|
+
navigationHandler={navigationHandler}
|
|
1483
|
+
expandDataTable={general.expandDataTable}
|
|
1484
|
+
headerColor={general.headerColor}
|
|
1555
1485
|
columns={state.columns}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
setState={setState}
|
|
1560
|
-
resetLegendToggles={resetLegendToggles}
|
|
1561
|
-
changeFilterActive={changeFilterActive}
|
|
1562
|
-
setAccessibleStatus={setAccessibleStatus}
|
|
1486
|
+
showDownloadButton={general.showDownloadButton}
|
|
1487
|
+
runtimeLegend={runtimeLegend}
|
|
1488
|
+
runtimeData={runtimeData}
|
|
1563
1489
|
displayDataAsText={displayDataAsText}
|
|
1490
|
+
displayGeoName={displayGeoName}
|
|
1491
|
+
applyLegendToRow={applyLegendToRow}
|
|
1492
|
+
tableTitle={dataTable.title}
|
|
1493
|
+
indexTitle={dataTable.indexLabel}
|
|
1494
|
+
mapTitle={general.title}
|
|
1495
|
+
viewport={currentViewport}
|
|
1496
|
+
formatLegendLocation={formatLegendLocation}
|
|
1497
|
+
setFilteredCountryCode={setFilteredCountryCode}
|
|
1498
|
+
tabbingId={tabId}
|
|
1499
|
+
showDownloadImgButton={state.general.showDownloadImgButton}
|
|
1500
|
+
showDownloadPdfButton={state.general.showDownloadPdfButton}
|
|
1501
|
+
innerContainerRef={innerContainerRef}
|
|
1502
|
+
outerContainerRef={outerContainerRef}
|
|
1503
|
+
imageRef={imageId}
|
|
1564
1504
|
/>
|
|
1565
1505
|
)}
|
|
1506
|
+
|
|
1507
|
+
{general.footnotes && <section className='footnotes'>{parse(general.footnotes)}</section>}
|
|
1566
1508
|
</section>
|
|
1509
|
+
)}
|
|
1567
1510
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1573
|
-
|
|
1574
|
-
{state.runtime.editorErrorMessage.length === 0 && true === dataTable.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1575
|
-
<DataTable
|
|
1576
|
-
state={state}
|
|
1577
|
-
rawData={state.data}
|
|
1578
|
-
navigationHandler={navigationHandler}
|
|
1579
|
-
expandDataTable={general.expandDataTable}
|
|
1580
|
-
headerColor={general.headerColor}
|
|
1581
|
-
columns={state.columns}
|
|
1582
|
-
showDownloadButton={general.showDownloadButton}
|
|
1583
|
-
runtimeLegend={runtimeLegend}
|
|
1584
|
-
runtimeData={runtimeData}
|
|
1585
|
-
displayDataAsText={displayDataAsText}
|
|
1586
|
-
displayGeoName={displayGeoName}
|
|
1587
|
-
applyLegendToRow={applyLegendToRow}
|
|
1588
|
-
tableTitle={dataTable.title}
|
|
1589
|
-
indexTitle={dataTable.indexLabel}
|
|
1590
|
-
mapTitle={general.title}
|
|
1591
|
-
viewport={currentViewport}
|
|
1592
|
-
formatLegendLocation={formatLegendLocation}
|
|
1593
|
-
setFilteredCountryCode={setFilteredCountryCode}
|
|
1594
|
-
tabbingId={tabId}
|
|
1595
|
-
/>
|
|
1596
|
-
)}
|
|
1597
|
-
|
|
1598
|
-
{general.footnotes && <section className='footnotes'>{parse(general.footnotes)}</section>}
|
|
1599
|
-
</section>
|
|
1600
|
-
)}
|
|
1601
|
-
|
|
1602
|
-
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
1603
|
-
{accessibleStatus}
|
|
1511
|
+
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
1512
|
+
{accessibleStatus}
|
|
1513
|
+
</div>
|
|
1604
1514
|
</div>
|
|
1605
|
-
</
|
|
1515
|
+
</Context.Provider>
|
|
1606
1516
|
)
|
|
1607
1517
|
}
|
|
1608
1518
|
|
|
1609
|
-
export default
|
|
1519
|
+
export default CdcMap
|
|
@@ -16,7 +16,6 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
16
16
|
if (!sortedRuntimeData) return
|
|
17
17
|
|
|
18
18
|
const clickTolerance = 10
|
|
19
|
-
|
|
20
19
|
// Set bubble sizes
|
|
21
20
|
var size = scaleLinear().domain([hasBubblesWithZeroOnMap, maxDataValue]).range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
|
|
22
21
|
|
|
@@ -34,7 +33,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
34
33
|
const legendColors = applyLegendToRow(country)
|
|
35
34
|
|
|
36
35
|
let primaryKey = state.columns.primary.name
|
|
37
|
-
if ((Math.floor(Number(
|
|
36
|
+
if ((Math.floor(Number(country[primaryKey])) === 0 || country[primaryKey] === '') && !state.visual.showBubbleZeros) return
|
|
38
37
|
|
|
39
38
|
let transform = `translate(${projection([coordinates[1], coordinates[0]])})`
|
|
40
39
|
|
|
@@ -117,7 +116,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
117
116
|
if (item[primaryKey] === null) item[primaryKey] = ''
|
|
118
117
|
|
|
119
118
|
// Return if hiding zeros on the map
|
|
120
|
-
if ((Math.floor(Number(
|
|
119
|
+
if ((Math.floor(Number(item[primaryKey])) === 0 || item[primaryKey] === '') && !state.visual.showBubbleZeros) return
|
|
121
120
|
|
|
122
121
|
if (!stateData) return true
|
|
123
122
|
let longitude = Number(stateData.Longitude)
|
|
@@ -42,7 +42,6 @@ const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeo
|
|
|
42
42
|
// Cities output
|
|
43
43
|
const cities = cityList.map((city, i) => {
|
|
44
44
|
const geoData = isGeoCodeMap ? state.data.filter(item => city === item[state.columns.geo.name])[0] : data[city]
|
|
45
|
-
|
|
46
45
|
const cityDisplayName = isGeoCodeMap ? city : titleCase(displayGeoName(city))
|
|
47
46
|
|
|
48
47
|
const legendColors = isGeoCodeMap && geoData ? applyLegendToRow(geoData) : data[city] ? applyLegendToRow(data[city]) : false
|
|
@@ -72,7 +71,7 @@ const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeo
|
|
|
72
71
|
styles.cursor = 'pointer'
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
const radius = state.general.geoType === 'us' && !isGeoCodeMap ? 8 : isGeoCodeMap ?
|
|
74
|
+
const radius = state.general.geoType === 'us' && !isGeoCodeMap ? 8 : isGeoCodeMap ? state.visual.geoCodeCircleSize : 4
|
|
76
75
|
|
|
77
76
|
const additionalProps = {
|
|
78
77
|
fillOpacity: state.general.type === 'bubble' ? 0.4 : 1
|
|
@@ -5,11 +5,36 @@ import ExternalIcon from '../images/external-link.svg' // TODO: Move to Icon com
|
|
|
5
5
|
|
|
6
6
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
7
7
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
8
|
+
import CoveMediaControls from '@cdc/core/helpers/CoveMediaControls'
|
|
8
9
|
|
|
9
10
|
import Loading from '@cdc/core/components/Loading'
|
|
10
11
|
|
|
11
12
|
const DataTable = props => {
|
|
12
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
state,
|
|
15
|
+
tableTitle,
|
|
16
|
+
indexTitle,
|
|
17
|
+
mapTitle,
|
|
18
|
+
rawData,
|
|
19
|
+
showDownloadImgButton,
|
|
20
|
+
showDownloadPdfButton,
|
|
21
|
+
showDownloadButton,
|
|
22
|
+
runtimeData,
|
|
23
|
+
runtimeLegend,
|
|
24
|
+
headerColor,
|
|
25
|
+
expandDataTable,
|
|
26
|
+
columns,
|
|
27
|
+
displayDataAsText,
|
|
28
|
+
applyLegendToRow,
|
|
29
|
+
displayGeoName,
|
|
30
|
+
navigationHandler,
|
|
31
|
+
viewport,
|
|
32
|
+
formatLegendLocation,
|
|
33
|
+
tabbingId,
|
|
34
|
+
setFilteredCountryCode,
|
|
35
|
+
innerContainerRef,
|
|
36
|
+
imageRef
|
|
37
|
+
} = props
|
|
13
38
|
|
|
14
39
|
const [expanded, setExpanded] = useState(expandDataTable)
|
|
15
40
|
|
|
@@ -137,7 +162,7 @@ const DataTable = props => {
|
|
|
137
162
|
}
|
|
138
163
|
|
|
139
164
|
return (
|
|
140
|
-
<a download={fileName} type='button' onClick={saveBlob} href={URL.createObjectURL(blob)} aria-label='Download this data in a CSV file format.' className={`${headerColor}
|
|
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'>
|
|
141
166
|
Download Data (CSV)
|
|
142
167
|
</a>
|
|
143
168
|
)
|
|
@@ -255,6 +280,10 @@ const DataTable = props => {
|
|
|
255
280
|
if (!state.data) return <Loading />
|
|
256
281
|
return (
|
|
257
282
|
<ErrorBoundary component='DataTable'>
|
|
283
|
+
<CoveMediaControls.Section classes={['download-links']}>
|
|
284
|
+
<CoveMediaControls.Link config={state} />
|
|
285
|
+
{state.general.showDownloadButton && <DownloadButton />}
|
|
286
|
+
</CoveMediaControls.Section>
|
|
258
287
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
259
288
|
<a id='skip-nav' className='cdcdataviz-sr-only-focusable' href={`#${skipId}`}>
|
|
260
289
|
Skip Navigation or Skip to Content
|
|
@@ -323,7 +352,6 @@ const DataTable = props => {
|
|
|
323
352
|
</tbody>
|
|
324
353
|
</table>
|
|
325
354
|
</div>
|
|
326
|
-
{showDownloadButton === true && <DownloadButton />}
|
|
327
355
|
</section>
|
|
328
356
|
</ErrorBoundary>
|
|
329
357
|
)
|