@cdc/map 4.23.4 → 4.23.6
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 +25668 -29239
- package/examples/custom-map-layers.json +10 -2
- package/examples/example-city-state.json +39 -3
- package/examples/new-cities.json +656 -0
- package/examples/testing-layer-2.json +61 -1
- package/examples/testing-layer.json +1 -5
- package/index.html +2 -2
- package/package.json +3 -3
- package/src/CdcMap.jsx +180 -67
- package/src/components/CountyMap.jsx +15 -12
- package/src/components/DataTable.jsx +190 -223
- package/src/components/EditorPanel.jsx +1270 -1226
- package/src/components/UsaMap.jsx +7 -1
- package/src/data/initial-state.js +11 -6
- package/src/data/supported-cities.csv +165 -0
- package/src/data/supported-geos.js +14 -0
- package/src/hooks/useMapLayers.jsx +11 -75
|
@@ -66,11 +66,7 @@
|
|
|
66
66
|
"properties": {
|
|
67
67
|
"name": "Polygon 2",
|
|
68
68
|
"styleUrl": "#poly-000000-1200-77-nodesc",
|
|
69
|
-
"fill-opacity": 0.30196078431372547
|
|
70
|
-
"fill": "blue",
|
|
71
|
-
"stroke-opacity": 1,
|
|
72
|
-
"stroke": "red",
|
|
73
|
-
"stroke-width": 5
|
|
69
|
+
"fill-opacity": 0.30196078431372547
|
|
74
70
|
}
|
|
75
71
|
},
|
|
76
72
|
{
|
package/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
<body>
|
|
18
18
|
<!-- DEFAULT EXAMPLES -->
|
|
19
|
-
|
|
19
|
+
<div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div>
|
|
20
20
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-geocode.json"></div> -->
|
|
21
21
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div> -->
|
|
22
22
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div> -->
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
|
|
33
33
|
|
|
34
34
|
<!-- TP4 EXAMPLES -->
|
|
35
|
-
<div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div>
|
|
35
|
+
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
|
|
36
36
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state-no-territories.json"></div> -->
|
|
37
37
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
|
|
38
38
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/map",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.6",
|
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@cdc/core": "^4.23.
|
|
27
|
+
"@cdc/core": "^4.23.6",
|
|
28
28
|
"@emotion/core": "^10.0.28",
|
|
29
29
|
"@emotion/react": "^11.1.5",
|
|
30
30
|
"@hello-pangea/dnd": "^16.2.0",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"react": "^18.2.0",
|
|
52
52
|
"react-dom": "^18.2.0"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "aaed0388b487adfeb3e7e278b4ce74df09cbaade"
|
|
55
55
|
}
|
package/src/CdcMap.jsx
CHANGED
|
@@ -8,11 +8,13 @@ import ResizeObserver from 'resize-observer-polyfill'
|
|
|
8
8
|
// Third party
|
|
9
9
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
10
10
|
import chroma from 'chroma-js'
|
|
11
|
+
import Papa from 'papaparse'
|
|
11
12
|
import parse from 'html-react-parser'
|
|
12
13
|
import 'react-tooltip/dist/react-tooltip.css'
|
|
13
14
|
|
|
14
15
|
// Helpers
|
|
15
16
|
import { publish } from '@cdc/core/helpers/events'
|
|
17
|
+
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
16
18
|
|
|
17
19
|
// Data
|
|
18
20
|
import { countryCoordinates } from './data/country-coordinates'
|
|
@@ -29,11 +31,12 @@ import './scss/btn.scss'
|
|
|
29
31
|
|
|
30
32
|
// Core
|
|
31
33
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
32
|
-
import
|
|
34
|
+
import MediaControls from '@cdc/core/components/MediaControls'
|
|
33
35
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
34
36
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
35
37
|
import Loading from '@cdc/core/components/Loading'
|
|
36
38
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
39
|
+
import DataTable from '@cdc/core/components/DataTable' // Future: Lazy
|
|
37
40
|
|
|
38
41
|
// Child Components
|
|
39
42
|
import ConfigContext from './context'
|
|
@@ -42,7 +45,6 @@ import Modal from './components/Modal'
|
|
|
42
45
|
import Sidebar from './components/Sidebar'
|
|
43
46
|
|
|
44
47
|
import CountyMap from './components/CountyMap' // Future: Lazy
|
|
45
|
-
import DataTable from './components/DataTable' // Future: Lazy
|
|
46
48
|
import EditorPanel from './components/EditorPanel' // Future: Lazy
|
|
47
49
|
import NavigationMenu from './components/NavigationMenu' // Future: Lazy
|
|
48
50
|
import SingleStateMap from './components/SingleStateMap' // Future: Lazy
|
|
@@ -133,6 +135,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
133
135
|
let legendMemo = useRef(new Map())
|
|
134
136
|
let innerContainerRef = useRef()
|
|
135
137
|
|
|
138
|
+
if (isDebug) console.log('CdcMap state=', state) // eslint-disable-line
|
|
139
|
+
|
|
136
140
|
useEffect(() => {
|
|
137
141
|
try {
|
|
138
142
|
if (filteredCountryCode) {
|
|
@@ -195,7 +199,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
195
199
|
for (let entry of entries) {
|
|
196
200
|
let { width, height } = entry.contentRect
|
|
197
201
|
let newViewport = getViewport(entry.contentRect.width)
|
|
198
|
-
let svgMarginWidth = 32
|
|
199
202
|
let editorWidth = 350
|
|
200
203
|
|
|
201
204
|
setCurrentViewport(newViewport)
|
|
@@ -499,10 +502,19 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
499
502
|
|
|
500
503
|
legendMemo.current = newLegendMemo
|
|
501
504
|
|
|
505
|
+
// before returning the legend result
|
|
506
|
+
// add property for bin number and set to index location
|
|
502
507
|
result.forEach((row, i) => {
|
|
503
508
|
row.bin = i // set bin number to index
|
|
504
509
|
})
|
|
505
510
|
|
|
511
|
+
// Move all special legend items from "Special Classes" to the end of the legend
|
|
512
|
+
if (state.legend.showSpecialClassesLast) {
|
|
513
|
+
let specialRows = result.filter(d => d.special === true)
|
|
514
|
+
let otherRows = result.filter(d => !d.special)
|
|
515
|
+
result = [...otherRows, ...specialRows]
|
|
516
|
+
}
|
|
517
|
+
|
|
506
518
|
return result
|
|
507
519
|
}
|
|
508
520
|
|
|
@@ -770,9 +782,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
770
782
|
|
|
771
783
|
if (hash) filters.fromHash = hash
|
|
772
784
|
|
|
773
|
-
obj?.filters.forEach(({ columnName, label, active, values }, idx) => {
|
|
774
|
-
if (undefined === columnName) return
|
|
775
|
-
|
|
785
|
+
obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown }, idx) => {
|
|
776
786
|
let newFilter = runtimeFilters[idx]
|
|
777
787
|
|
|
778
788
|
const sortAsc = (a, b) => {
|
|
@@ -783,20 +793,24 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
783
793
|
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
784
794
|
}
|
|
785
795
|
|
|
786
|
-
|
|
796
|
+
if (type !== 'url') {
|
|
797
|
+
values = getUniqueValues(state.data, columnName)
|
|
787
798
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
799
|
+
if (obj.filters[idx].order === 'asc') {
|
|
800
|
+
values = values.sort(sortAsc)
|
|
801
|
+
}
|
|
791
802
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
803
|
+
if (obj.filters[idx].order === 'desc') {
|
|
804
|
+
values = values.sort(sortDesc)
|
|
805
|
+
}
|
|
795
806
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
807
|
+
if (obj.filters[idx].order === 'cust') {
|
|
808
|
+
if (obj.filters[idx]?.values.length > 0) {
|
|
809
|
+
values = obj.filters[idx].values
|
|
810
|
+
}
|
|
799
811
|
}
|
|
812
|
+
} else {
|
|
813
|
+
values = values
|
|
800
814
|
}
|
|
801
815
|
|
|
802
816
|
if (undefined === newFilter) {
|
|
@@ -804,12 +818,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
804
818
|
}
|
|
805
819
|
|
|
806
820
|
newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
|
|
821
|
+
newFilter.type = type
|
|
807
822
|
newFilter.label = label ?? ''
|
|
808
823
|
newFilter.columnName = columnName
|
|
824
|
+
newFilter.orderedValues = orderedValues
|
|
825
|
+
newFilter.queryParameter = queryParameter
|
|
826
|
+
newFilter.labels = labels
|
|
809
827
|
newFilter.values = values
|
|
810
828
|
handleSorting(newFilter)
|
|
811
829
|
newFilter.active = active ?? values[0] // Default to first found value
|
|
812
830
|
newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
|
|
831
|
+
newFilter.showDropdown = showDropdown
|
|
813
832
|
|
|
814
833
|
filters.push(newFilter)
|
|
815
834
|
})
|
|
@@ -862,8 +881,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
862
881
|
// Filters
|
|
863
882
|
if (filters?.length) {
|
|
864
883
|
for (let i = 0; i < filters.length; i++) {
|
|
865
|
-
const { columnName, active } = filters[i]
|
|
866
|
-
if (String(row[columnName]) !== String(active)) return false // Bail out, not part of filter
|
|
884
|
+
const { columnName, active, type } = filters[i]
|
|
885
|
+
if (type !== 'url' && String(row[columnName]) !== String(active)) return false // Bail out, not part of filter
|
|
867
886
|
}
|
|
868
887
|
}
|
|
869
888
|
|
|
@@ -899,6 +918,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
899
918
|
return ''
|
|
900
919
|
}
|
|
901
920
|
|
|
921
|
+
// if string of letters like 'Home' then dont need to format as a number
|
|
902
922
|
if (typeof value === 'string' && value.length > 0 && state.legend.type === 'equalnumber') {
|
|
903
923
|
return value
|
|
904
924
|
}
|
|
@@ -907,6 +927,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
907
927
|
|
|
908
928
|
let columnObj = state.columns[columnName]
|
|
909
929
|
|
|
930
|
+
if (columnObj === undefined) {
|
|
931
|
+
// then use left axis config
|
|
932
|
+
columnObj = state.columns.primary
|
|
933
|
+
// NOTE: Left Value Axis uses different names
|
|
934
|
+
// so map them below so the code below works
|
|
935
|
+
// - copy commas to useCommas to work below
|
|
936
|
+
columnObj['useCommas'] = columnObj.commas
|
|
937
|
+
// - copy roundTo to roundToPlace to work below
|
|
938
|
+
columnObj['roundToPlace'] = columnObj.roundTo ? columnObj.roundTo : ''
|
|
939
|
+
}
|
|
940
|
+
|
|
910
941
|
if (columnObj) {
|
|
911
942
|
// If value is a number, apply specific formattings
|
|
912
943
|
if (Number(value)) {
|
|
@@ -939,11 +970,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
939
970
|
}
|
|
940
971
|
|
|
941
972
|
// this is passed DOWN into the various components
|
|
942
|
-
// then they do a lookup based on the bin number as index into here
|
|
973
|
+
// then they do a lookup based on the bin number as index into here
|
|
943
974
|
const applyLegendToRow = rowObj => {
|
|
944
975
|
try {
|
|
945
976
|
if (!rowObj) throw new Error('COVE: No rowObj in applyLegendToRow')
|
|
946
|
-
// Navigation
|
|
977
|
+
// Navigation mapchanged
|
|
947
978
|
if ('navigation' === state.general.type) {
|
|
948
979
|
let mapColorPalette = colorPalettes[state.color] || colorPalettes['bluegreenreverse']
|
|
949
980
|
return generateColorsArray(mapColorPalette[3])
|
|
@@ -955,7 +986,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
955
986
|
let idx = legendMemo.current.get(hash)
|
|
956
987
|
if (runtimeLegend[idx]?.disabled) return false
|
|
957
988
|
|
|
958
|
-
//
|
|
989
|
+
// changed to use bin prop to get color instead of idx
|
|
959
990
|
// bc we re-order legend when showSpecialClassesLast is checked
|
|
960
991
|
let legendBinColor = runtimeLegend.find(o => o.bin === idx)?.color
|
|
961
992
|
return generateColorsArray(legendBinColor, runtimeLegend[idx]?.special)
|
|
@@ -994,16 +1025,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
994
1025
|
const column = state.columns[columnKey]
|
|
995
1026
|
|
|
996
1027
|
if (true === column.tooltip) {
|
|
997
|
-
let label = column.label
|
|
1028
|
+
let label = column.label?.length > 0 ? column.label : ''
|
|
998
1029
|
|
|
999
1030
|
let value
|
|
1000
1031
|
|
|
1001
1032
|
if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
|
|
1002
1033
|
// THIS CODE SHOULD NOT ACT ON THE ENTIRE ROW OF KEYS BUT ONLY THE ONE KEY IN THE SPECIAL CLASS
|
|
1003
1034
|
for (let i = 0; i < state.legend.specialClasses.length; i++) {
|
|
1004
|
-
//
|
|
1035
|
+
// Special Classes label in HOVERS should only apply to selected special class key
|
|
1005
1036
|
// - you have to ALSO check that the key matches - putting here otherwise the if stmt too long
|
|
1006
|
-
if (
|
|
1037
|
+
if (column.name === state.legend.specialClasses[i].key) {
|
|
1007
1038
|
if (String(row[state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
|
|
1008
1039
|
value = displayDataAsText(state.legend.specialClasses[i].label, columnKey)
|
|
1009
1040
|
break
|
|
@@ -1047,25 +1078,29 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1047
1078
|
// - this function is used to prevent that and instead give the formatting that is wanted
|
|
1048
1079
|
// Example: Desired city display in tooltip on map: "Inter-Tribal Indian Reservation"
|
|
1049
1080
|
const titleCase = string => {
|
|
1050
|
-
//
|
|
1051
|
-
if (string
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
.split(' ')
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1081
|
+
// guard clause else error in editor
|
|
1082
|
+
if (!string) return
|
|
1083
|
+
if (string !== undefined) {
|
|
1084
|
+
// if hyphen found, then split, uppercase each word, and put back together
|
|
1085
|
+
if (string.includes('–') || string.includes('-')) {
|
|
1086
|
+
let dashSplit = string.includes('–') ? string.split('–') : string.split('-') // determine hyphen or en dash to split on
|
|
1087
|
+
let splitCharacter = string.includes('–') ? '–' : '-' // print hyphen or en dash later on.
|
|
1088
|
+
let frontSplit = dashSplit[0]
|
|
1089
|
+
.split(' ')
|
|
1090
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
1091
|
+
.join(' ')
|
|
1092
|
+
let backSplit = dashSplit[1]
|
|
1093
|
+
.split(' ')
|
|
1094
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
1095
|
+
.join(' ')
|
|
1096
|
+
return frontSplit + splitCharacter + backSplit
|
|
1097
|
+
} else {
|
|
1098
|
+
// just return with each word uppercase
|
|
1099
|
+
return string
|
|
1100
|
+
.split(' ')
|
|
1101
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
1102
|
+
.join(' ')
|
|
1103
|
+
}
|
|
1069
1104
|
}
|
|
1070
1105
|
}
|
|
1071
1106
|
|
|
@@ -1129,7 +1164,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1129
1164
|
if (true === Object.keys(dict).includes(value)) {
|
|
1130
1165
|
value = dict[value]
|
|
1131
1166
|
}
|
|
1132
|
-
|
|
1167
|
+
|
|
1168
|
+
// if you get here and it's 2 letters then DONT titleCase state abbreviations like "AL"
|
|
1169
|
+
if (value.length === 2) {
|
|
1170
|
+
return value
|
|
1171
|
+
} else {
|
|
1172
|
+
return titleCase(value)
|
|
1173
|
+
}
|
|
1133
1174
|
}
|
|
1134
1175
|
|
|
1135
1176
|
const navigationHandler = urlString => {
|
|
@@ -1232,6 +1273,67 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1232
1273
|
}
|
|
1233
1274
|
}
|
|
1234
1275
|
|
|
1276
|
+
const reloadURLData = async () => {
|
|
1277
|
+
if (state.dataUrl) {
|
|
1278
|
+
const dataUrl = new URL(state.runtimeDataUrl || state.dataUrl)
|
|
1279
|
+
let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
1280
|
+
|
|
1281
|
+
let isUpdateNeeded = false
|
|
1282
|
+
state.filters.forEach(filter => {
|
|
1283
|
+
if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
|
|
1284
|
+
qsParams[filter.queryParameter] = filter.active
|
|
1285
|
+
isUpdateNeeded = true
|
|
1286
|
+
}
|
|
1287
|
+
})
|
|
1288
|
+
|
|
1289
|
+
if (!isUpdateNeeded) return
|
|
1290
|
+
|
|
1291
|
+
let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
|
|
1292
|
+
.map((param, i) => {
|
|
1293
|
+
let qs = i === 0 ? '?' : '&'
|
|
1294
|
+
qs += param + '='
|
|
1295
|
+
qs += qsParams[param]
|
|
1296
|
+
return qs
|
|
1297
|
+
})
|
|
1298
|
+
.join('')}`
|
|
1299
|
+
|
|
1300
|
+
let data
|
|
1301
|
+
|
|
1302
|
+
try {
|
|
1303
|
+
const regex = /(?:\.([^.]+))?$/
|
|
1304
|
+
|
|
1305
|
+
const ext = regex.exec(dataUrl.pathname)[1]
|
|
1306
|
+
if ('csv' === ext) {
|
|
1307
|
+
data = await fetch(dataUrlFinal)
|
|
1308
|
+
.then(response => response.text())
|
|
1309
|
+
.then(responseText => {
|
|
1310
|
+
const parsedCsv = Papa.parse(responseText, {
|
|
1311
|
+
header: true,
|
|
1312
|
+
dynamicTyping: true,
|
|
1313
|
+
skipEmptyLines: true
|
|
1314
|
+
})
|
|
1315
|
+
return parsedCsv.data
|
|
1316
|
+
})
|
|
1317
|
+
} else if ('json' === ext) {
|
|
1318
|
+
data = await fetch(dataUrlFinal).then(response => response.json())
|
|
1319
|
+
} else {
|
|
1320
|
+
data = []
|
|
1321
|
+
}
|
|
1322
|
+
} catch (e) {
|
|
1323
|
+
console.error(`Cannot parse URL: ${dataUrlFinal}`) // eslint-disable-line
|
|
1324
|
+
console.log(e) // eslint-disable-line
|
|
1325
|
+
data = []
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (state.dataDescription) {
|
|
1329
|
+
data = transform.autoStandardize(data)
|
|
1330
|
+
data = transform.developerStandardize(data, state.dataDescription)
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
setState({ ...state, runtimeDataUrl: dataUrlFinal, data })
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1235
1337
|
const loadConfig = async configObj => {
|
|
1236
1338
|
// Set loading flag
|
|
1237
1339
|
if (!loading) setLoading(true)
|
|
@@ -1242,8 +1344,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1242
1344
|
...configObj
|
|
1243
1345
|
}
|
|
1244
1346
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1347
|
+
const urlFilters = newState.filters ? (newState.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
|
|
1348
|
+
|
|
1349
|
+
if (newState.dataUrl && !urlFilters) {
|
|
1247
1350
|
if (newState.dataUrl[0] === '/') {
|
|
1248
1351
|
newState.dataUrl = 'http://' + hostname + newState.dataUrl
|
|
1249
1352
|
}
|
|
@@ -1282,12 +1385,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1282
1385
|
addUIDs(newState, newState.columns.geo.name || newState.columns.geo.fips)
|
|
1283
1386
|
}
|
|
1284
1387
|
|
|
1285
|
-
if (newState.
|
|
1286
|
-
newState.
|
|
1388
|
+
if (newState.table.forceDisplay === undefined) {
|
|
1389
|
+
newState.table.forceDisplay = !isDashboard
|
|
1287
1390
|
}
|
|
1288
1391
|
|
|
1289
1392
|
validateFipsCodeLength(newState)
|
|
1290
|
-
|
|
1393
|
+
|
|
1394
|
+
// add ability to rename state properties over time.
|
|
1395
|
+
const processedConfig = { ...(await coveUpdateWorker(newState)) }
|
|
1396
|
+
|
|
1397
|
+
setState(processedConfig)
|
|
1291
1398
|
setLoading(false)
|
|
1292
1399
|
}
|
|
1293
1400
|
|
|
@@ -1338,16 +1445,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1338
1445
|
|
|
1339
1446
|
// DEV-769 make "Data Table" both a required field and default value
|
|
1340
1447
|
useEffect(() => {
|
|
1341
|
-
if (state.
|
|
1448
|
+
if (state.table?.label === '' || state.table?.label === undefined) {
|
|
1342
1449
|
setState({
|
|
1343
1450
|
...state,
|
|
1344
|
-
|
|
1345
|
-
...state.
|
|
1451
|
+
table: {
|
|
1452
|
+
...state.table,
|
|
1346
1453
|
title: 'Data Table'
|
|
1347
1454
|
}
|
|
1348
1455
|
})
|
|
1349
1456
|
}
|
|
1350
|
-
}, [state.
|
|
1457
|
+
}, [state.table]) // eslint-disable-line
|
|
1351
1458
|
|
|
1352
1459
|
// When geo label override changes
|
|
1353
1460
|
// - redo the tooltips
|
|
@@ -1408,6 +1515,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1408
1515
|
}
|
|
1409
1516
|
}, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses]) // eslint-disable-line
|
|
1410
1517
|
|
|
1518
|
+
useEffect(() => {
|
|
1519
|
+
reloadURLData()
|
|
1520
|
+
}, [JSON.stringify(state.filters)])
|
|
1521
|
+
|
|
1411
1522
|
if (config) {
|
|
1412
1523
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
1413
1524
|
useEffect(() => {
|
|
@@ -1416,14 +1527,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1416
1527
|
}
|
|
1417
1528
|
|
|
1418
1529
|
// Destructuring for more readable JSX
|
|
1419
|
-
const { general, tooltips,
|
|
1530
|
+
const { general, tooltips, table } = state
|
|
1420
1531
|
let { title, subtext = '' } = general
|
|
1421
1532
|
|
|
1422
1533
|
// if no title AND in editor then set a default
|
|
1423
1534
|
if (isEditor) {
|
|
1424
1535
|
if (!title || title === '') title = 'Map Title'
|
|
1425
1536
|
}
|
|
1426
|
-
if (!
|
|
1537
|
+
if (!table.label || table.label === '') table.label = 'Data Table'
|
|
1427
1538
|
|
|
1428
1539
|
// Outer container classes
|
|
1429
1540
|
let outerContainerClasses = ['cdc-open-viz-module', 'cdc-map-outer-container', currentViewport]
|
|
@@ -1475,7 +1586,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1475
1586
|
|
|
1476
1587
|
if (!mapProps.data || !state.data) return <Loading />
|
|
1477
1588
|
|
|
1478
|
-
const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true ===
|
|
1589
|
+
const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading
|
|
1479
1590
|
|
|
1480
1591
|
const handleMapTabbing = () => {
|
|
1481
1592
|
let tabbingID
|
|
@@ -1596,32 +1707,33 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1596
1707
|
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1597
1708
|
|
|
1598
1709
|
{/* Link */}
|
|
1599
|
-
{isDashboard && config.
|
|
1710
|
+
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
|
|
1600
1711
|
|
|
1601
1712
|
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1602
1713
|
|
|
1603
|
-
<
|
|
1604
|
-
{state.general.showDownloadImgButton && <
|
|
1605
|
-
{state.general.showDownloadPdfButton && <
|
|
1606
|
-
</
|
|
1714
|
+
<MediaControls.Section classes={['download-buttons']}>
|
|
1715
|
+
{state.general.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
|
|
1716
|
+
{state.general.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
|
|
1717
|
+
</MediaControls.Section>
|
|
1607
1718
|
|
|
1608
|
-
{state.runtime.editorErrorMessage.length === 0 && true ===
|
|
1719
|
+
{state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1609
1720
|
<DataTable
|
|
1610
|
-
|
|
1721
|
+
config={state}
|
|
1611
1722
|
rawData={state.data}
|
|
1612
1723
|
navigationHandler={navigationHandler}
|
|
1613
|
-
expandDataTable={general.expandDataTable}
|
|
1724
|
+
expandDataTable={general.expandDataTable ? general.expandDataTable : table.expanded ? table.expanded : false}
|
|
1614
1725
|
headerColor={general.headerColor}
|
|
1615
1726
|
columns={state.columns}
|
|
1616
1727
|
showDownloadButton={general.showDownloadButton}
|
|
1728
|
+
showFullGeoNameInCSV={table.showFullGeoNameInCSV}
|
|
1617
1729
|
runtimeLegend={runtimeLegend}
|
|
1618
1730
|
runtimeData={runtimeData}
|
|
1619
1731
|
displayDataAsText={displayDataAsText}
|
|
1620
1732
|
displayGeoName={displayGeoName}
|
|
1621
1733
|
applyLegendToRow={applyLegendToRow}
|
|
1622
|
-
tableTitle={
|
|
1623
|
-
indexTitle={
|
|
1624
|
-
|
|
1734
|
+
tableTitle={table.label}
|
|
1735
|
+
indexTitle={table.indexLabel}
|
|
1736
|
+
vizTitle={general.title}
|
|
1625
1737
|
viewport={currentViewport}
|
|
1626
1738
|
formatLegendLocation={formatLegendLocation}
|
|
1627
1739
|
setFilteredCountryCode={setFilteredCountryCode}
|
|
@@ -1631,6 +1743,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1631
1743
|
innerContainerRef={innerContainerRef}
|
|
1632
1744
|
outerContainerRef={outerContainerRef}
|
|
1633
1745
|
imageRef={imageId}
|
|
1746
|
+
isDebug={isDebug}
|
|
1634
1747
|
/>
|
|
1635
1748
|
)}
|
|
1636
1749
|
|
|
@@ -44,9 +44,6 @@ states.forEach(state => {
|
|
|
44
44
|
countyIndecies[state.id] = [minIndex, maxIndex]
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
// CREATE STATE LINES
|
|
48
|
-
const projection = geoAlbersUsaTerritories()
|
|
49
|
-
|
|
50
47
|
// Ensures county map is only rerendered when it needs to (when one of the variables below is updated)
|
|
51
48
|
function CountyMapChecks(prevState, nextState) {
|
|
52
49
|
const equalNumberOptIn = prevState.state.general.equalNumberOptIn && nextState.state.general.equalNumberOptIn
|
|
@@ -62,6 +59,9 @@ function CountyMapChecks(prevState, nextState) {
|
|
|
62
59
|
const CountyMap = props => {
|
|
63
60
|
const { state, runtimeLegend, applyTooltipsToGeo, data, geoClickHandler, applyLegendToRow, displayGeoName, containerEl, handleMapAriaLabels } = props
|
|
64
61
|
|
|
62
|
+
// CREATE STATE LINES
|
|
63
|
+
const projection = geoAlbersUsaTerritories()
|
|
64
|
+
|
|
65
65
|
const [focus, setFocus] = useState({})
|
|
66
66
|
|
|
67
67
|
const pathGenerator = geoPath().projection(geoAlbersUsaTerritories())
|
|
@@ -160,7 +160,7 @@ const CountyMap = props => {
|
|
|
160
160
|
if (isNaN(currentTooltipIndex) || !geoContains(mapData[currentTooltipIndex], pointCoordinates)) {
|
|
161
161
|
const context = canvas.getContext('2d')
|
|
162
162
|
const path = geoPath(projection, context)
|
|
163
|
-
if (!isNaN(currentTooltipIndex)) {
|
|
163
|
+
if (!isNaN(currentTooltipIndex) && applyLegendToRow(data[mapData[currentTooltipIndex].id])) {
|
|
164
164
|
context.fillStyle = applyLegendToRow(data[mapData[currentTooltipIndex].id])[0]
|
|
165
165
|
context.strokeStyle = geoStrokeColor
|
|
166
166
|
context.lineWidth = lineWidth
|
|
@@ -193,13 +193,16 @@ const CountyMap = props => {
|
|
|
193
193
|
|
|
194
194
|
// If the hovered county is found, show the tooltip for that county, otherwise hide the tooltip
|
|
195
195
|
if (county && data[county.id]) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
if (applyLegendToRow(data[county.id])) {
|
|
197
|
+
context.globalAlpha = 1
|
|
198
|
+
context.fillStyle = applyLegendToRow(data[county.id])[1]
|
|
199
|
+
context.strokeStyle = geoStrokeColor
|
|
200
|
+
context.lineWidth = lineWidth
|
|
201
|
+
context.beginPath()
|
|
202
|
+
path(mapData[countyIndex])
|
|
203
|
+
context.fill()
|
|
204
|
+
context.stroke()
|
|
205
|
+
}
|
|
203
206
|
|
|
204
207
|
tooltipRef.current.style.display = 'block'
|
|
205
208
|
tooltipRef.current.style.top = e.clientY + 'px'
|
|
@@ -235,7 +238,7 @@ const CountyMap = props => {
|
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
240
|
|
|
238
|
-
if (hoveredGeo) {
|
|
241
|
+
if (hoveredGeo && applyLegendToRow(hoveredGeo)) {
|
|
239
242
|
tooltipRef.current.style.display = 'block'
|
|
240
243
|
tooltipRef.current.style.top = e.clientY + 'px'
|
|
241
244
|
tooltipRef.current.style.left = e.clientX + 'px'
|