@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
package/src/CdcMap.jsx
CHANGED
|
@@ -8,6 +8,7 @@ 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
|
|
|
@@ -34,15 +35,15 @@ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
|
34
35
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
35
36
|
import Loading from '@cdc/core/components/Loading'
|
|
36
37
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
38
|
+
import DataTable from '@cdc/core/components/DataTable' // Future: Lazy
|
|
37
39
|
|
|
38
40
|
// Child Components
|
|
39
41
|
import ConfigContext from './context'
|
|
40
|
-
import Filters from '
|
|
42
|
+
import Filters, { useFilters } from '@cdc/core/components/Filters'
|
|
41
43
|
import Modal from './components/Modal'
|
|
42
44
|
import Sidebar from './components/Sidebar'
|
|
43
45
|
|
|
44
46
|
import CountyMap from './components/CountyMap' // Future: Lazy
|
|
45
|
-
import DataTable from './components/DataTable' // Future: Lazy
|
|
46
47
|
import EditorPanel from './components/EditorPanel' // Future: Lazy
|
|
47
48
|
import NavigationMenu from './components/NavigationMenu' // Future: Lazy
|
|
48
49
|
import SingleStateMap from './components/SingleStateMap' // Future: Lazy
|
|
@@ -82,7 +83,7 @@ const hashObj = row => {
|
|
|
82
83
|
|
|
83
84
|
return hash
|
|
84
85
|
} catch (e) {
|
|
85
|
-
console.error(e)
|
|
86
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -112,7 +113,7 @@ const getUniqueValues = (data, columnName) => {
|
|
|
112
113
|
return Object.keys(result)
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, configUrl, logo = null, setConfig, setSharedFilter, setSharedFilterValue, hostname = 'localhost:8080', link }) => {
|
|
116
|
+
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, isDebug = false, configUrl, logo = null, setConfig, setSharedFilter, setSharedFilterValue, hostname = 'localhost:8080', link }) => {
|
|
116
117
|
const transform = new DataTransform()
|
|
117
118
|
const [state, setState] = useState({ ...initialState })
|
|
118
119
|
const [loading, setLoading] = useState(true)
|
|
@@ -127,10 +128,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
127
128
|
const [coveLoadedHasRan, setCoveLoadedHasRan] = useState(false)
|
|
128
129
|
const [container, setContainer] = useState()
|
|
129
130
|
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
|
|
131
|
+
const [dimensions, setDimensions] = useState()
|
|
130
132
|
|
|
133
|
+
const { changeFilterActive, handleSorting } = useFilters({ config: state, setConfig: setState })
|
|
131
134
|
let legendMemo = useRef(new Map())
|
|
132
135
|
let innerContainerRef = useRef()
|
|
133
136
|
|
|
137
|
+
if (isDebug) console.log('CdcMap state=', state) // eslint-disable-line
|
|
138
|
+
|
|
134
139
|
useEffect(() => {
|
|
135
140
|
try {
|
|
136
141
|
if (filteredCountryCode) {
|
|
@@ -145,7 +150,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
145
150
|
})
|
|
146
151
|
}
|
|
147
152
|
} catch (e) {
|
|
148
|
-
console.error('Failed to set world map zoom.')
|
|
153
|
+
console.error('COVE: Failed to set world map zoom.') // eslint-disable-line
|
|
149
154
|
}
|
|
150
155
|
}, [filteredCountryCode]) // eslint-disable-line
|
|
151
156
|
|
|
@@ -169,6 +174,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
169
174
|
|
|
170
175
|
const generateRuntimeLegendHash = () => {
|
|
171
176
|
return hashObj({
|
|
177
|
+
unified: state.legend.unified ?? false,
|
|
178
|
+
equalNumberOptIn: state.general.equalNumberOptIn ?? false,
|
|
179
|
+
specialClassesLast: state.legend.showSpecialClassesLast ?? false,
|
|
172
180
|
color: state.color,
|
|
173
181
|
customColors: state.customColors,
|
|
174
182
|
numberOfItems: state.legend.numberOfItems,
|
|
@@ -179,15 +187,25 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
179
187
|
specialClasses: state.legend.specialClasses,
|
|
180
188
|
geoType: state.general.geoType,
|
|
181
189
|
data: state.data,
|
|
182
|
-
...runtimeFilters
|
|
190
|
+
...runtimeFilters,
|
|
191
|
+
filters: {
|
|
192
|
+
...state.filters
|
|
193
|
+
}
|
|
183
194
|
})
|
|
184
195
|
}
|
|
185
196
|
|
|
186
197
|
const resizeObserver = new ResizeObserver(entries => {
|
|
187
198
|
for (let entry of entries) {
|
|
199
|
+
let { width, height } = entry.contentRect
|
|
188
200
|
let newViewport = getViewport(entry.contentRect.width)
|
|
201
|
+
let editorWidth = 350
|
|
189
202
|
|
|
190
203
|
setCurrentViewport(newViewport)
|
|
204
|
+
|
|
205
|
+
if (isEditor) {
|
|
206
|
+
width = width - editorWidth
|
|
207
|
+
}
|
|
208
|
+
setDimensions([width, height])
|
|
191
209
|
}
|
|
192
210
|
})
|
|
193
211
|
|
|
@@ -285,6 +303,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
285
303
|
result.fromHash = hash
|
|
286
304
|
}
|
|
287
305
|
|
|
306
|
+
result.runtimeDataHash = runtimeData.fromHash
|
|
307
|
+
|
|
288
308
|
// Unified will based the legend off ALL of the data maps received. Otherwise, it will use
|
|
289
309
|
let dataSet = obj.legend.unified ? obj.data : Object.values(runtimeData)
|
|
290
310
|
|
|
@@ -752,9 +772,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
752
772
|
|
|
753
773
|
if (hash) filters.fromHash = hash
|
|
754
774
|
|
|
755
|
-
obj?.filters.forEach(({ columnName, label, active, values }, idx) => {
|
|
756
|
-
if (undefined === columnName) return
|
|
757
|
-
|
|
775
|
+
obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type }, idx) => {
|
|
758
776
|
let newFilter = runtimeFilters[idx]
|
|
759
777
|
|
|
760
778
|
const sortAsc = (a, b) => {
|
|
@@ -765,30 +783,41 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
765
783
|
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
766
784
|
}
|
|
767
785
|
|
|
768
|
-
|
|
786
|
+
if (type !== 'url') {
|
|
787
|
+
values = getUniqueValues(state.data, columnName)
|
|
769
788
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
789
|
+
if (obj.filters[idx].order === 'asc') {
|
|
790
|
+
values = values.sort(sortAsc)
|
|
791
|
+
}
|
|
773
792
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
793
|
+
if (obj.filters[idx].order === 'desc') {
|
|
794
|
+
values = values.sort(sortDesc)
|
|
795
|
+
}
|
|
777
796
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
797
|
+
if (obj.filters[idx].order === 'cust') {
|
|
798
|
+
if (obj.filters[idx]?.values.length > 0) {
|
|
799
|
+
values = obj.filters[idx].values
|
|
800
|
+
}
|
|
781
801
|
}
|
|
802
|
+
} else {
|
|
803
|
+
values = values
|
|
782
804
|
}
|
|
783
805
|
|
|
784
806
|
if (undefined === newFilter) {
|
|
785
807
|
newFilter = {}
|
|
786
808
|
}
|
|
787
809
|
|
|
810
|
+
newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
|
|
811
|
+
newFilter.type = type
|
|
788
812
|
newFilter.label = label ?? ''
|
|
789
813
|
newFilter.columnName = columnName
|
|
814
|
+
newFilter.orderedValues = orderedValues
|
|
815
|
+
newFilter.queryParameter = queryParameter
|
|
816
|
+
newFilter.labels = labels
|
|
790
817
|
newFilter.values = values
|
|
791
|
-
newFilter
|
|
818
|
+
handleSorting(newFilter)
|
|
819
|
+
newFilter.active = active ?? values[0] // Default to first found value
|
|
820
|
+
newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
|
|
792
821
|
|
|
793
822
|
filters.push(newFilter)
|
|
794
823
|
})
|
|
@@ -810,8 +839,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
810
839
|
addUIDs(obj, obj.columns.geo.name)
|
|
811
840
|
obj.data.forEach(row => {
|
|
812
841
|
if (test) {
|
|
813
|
-
console.log('object', obj)
|
|
814
|
-
console.log('row', row)
|
|
842
|
+
console.log('object', obj) // eslint-disable-line
|
|
843
|
+
console.log('row', row) // eslint-disable-line
|
|
815
844
|
}
|
|
816
845
|
|
|
817
846
|
if (undefined === row.uid) return false // No UID for this row, we can't use for mapping
|
|
@@ -841,8 +870,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
841
870
|
// Filters
|
|
842
871
|
if (filters?.length) {
|
|
843
872
|
for (let i = 0; i < filters.length; i++) {
|
|
844
|
-
const { columnName, active } = filters[i]
|
|
845
|
-
if (String(row[columnName]) !== String(active)) return false // Bail out, not part of filter
|
|
873
|
+
const { columnName, active, type } = filters[i]
|
|
874
|
+
if (type !== 'url' && String(row[columnName]) !== String(active)) return false // Bail out, not part of filter
|
|
846
875
|
}
|
|
847
876
|
}
|
|
848
877
|
|
|
@@ -854,7 +883,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
854
883
|
|
|
855
884
|
return result
|
|
856
885
|
} catch (e) {
|
|
857
|
-
console.error(e)
|
|
886
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
858
887
|
}
|
|
859
888
|
})
|
|
860
889
|
|
|
@@ -873,38 +902,12 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
873
902
|
}
|
|
874
903
|
}
|
|
875
904
|
|
|
876
|
-
const changeFilterActive = async (idx, activeValue) => {
|
|
877
|
-
// Reset active legend toggles
|
|
878
|
-
resetLegendToggles()
|
|
879
|
-
|
|
880
|
-
try {
|
|
881
|
-
const isEmpty = obj => {
|
|
882
|
-
return Object.keys(obj).length === 0
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
let filters = [...runtimeFilters]
|
|
886
|
-
|
|
887
|
-
filters[idx] = { ...filters[idx] }
|
|
888
|
-
filters[idx].active = activeValue
|
|
889
|
-
|
|
890
|
-
const newData = generateRuntimeData(state, filters)
|
|
891
|
-
|
|
892
|
-
// throw an error if newData is empty
|
|
893
|
-
if (isEmpty(newData)) throw new Error('Cove Filter Error: No runtime data to set for this filter')
|
|
894
|
-
|
|
895
|
-
// set the runtime filters and data
|
|
896
|
-
setRuntimeData(newData)
|
|
897
|
-
setRuntimeFilters(filters)
|
|
898
|
-
} catch (e) {
|
|
899
|
-
console.error(e.message)
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
|
|
903
905
|
const displayDataAsText = (value, columnName) => {
|
|
904
906
|
if (value === null || value === '' || value === undefined) {
|
|
905
907
|
return ''
|
|
906
908
|
}
|
|
907
909
|
|
|
910
|
+
// if string of letters like 'Home' then dont need to format as a number
|
|
908
911
|
if (typeof value === 'string' && value.length > 0 && state.legend.type === 'equalnumber') {
|
|
909
912
|
return value
|
|
910
913
|
}
|
|
@@ -913,6 +916,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
913
916
|
|
|
914
917
|
let columnObj = state.columns[columnName]
|
|
915
918
|
|
|
919
|
+
if (columnObj === undefined) {
|
|
920
|
+
// then use left axis config
|
|
921
|
+
columnObj = state.columns.primary
|
|
922
|
+
// NOTE: Left Value Axis uses different names
|
|
923
|
+
// so map them below so the code below works
|
|
924
|
+
// - copy commas to useCommas to work below
|
|
925
|
+
columnObj['useCommas'] = columnObj.commas
|
|
926
|
+
// - copy roundTo to roundToPlace to work below
|
|
927
|
+
columnObj['roundToPlace'] = columnObj.roundTo ? columnObj.roundTo : ''
|
|
928
|
+
}
|
|
929
|
+
|
|
916
930
|
if (columnObj) {
|
|
917
931
|
// If value is a number, apply specific formattings
|
|
918
932
|
if (Number(value)) {
|
|
@@ -970,7 +984,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
970
984
|
// Fail state
|
|
971
985
|
return generateColorsArray()
|
|
972
986
|
} catch (e) {
|
|
973
|
-
console.error(e)
|
|
987
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
974
988
|
}
|
|
975
989
|
}
|
|
976
990
|
|
|
@@ -1000,16 +1014,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1000
1014
|
const column = state.columns[columnKey]
|
|
1001
1015
|
|
|
1002
1016
|
if (true === column.tooltip) {
|
|
1003
|
-
let label = column.label
|
|
1017
|
+
let label = column.label?.length > 0 ? column.label : ''
|
|
1004
1018
|
|
|
1005
1019
|
let value
|
|
1006
1020
|
|
|
1007
1021
|
if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
|
|
1008
1022
|
// THIS CODE SHOULD NOT ACT ON THE ENTIRE ROW OF KEYS BUT ONLY THE ONE KEY IN THE SPECIAL CLASS
|
|
1009
1023
|
for (let i = 0; i < state.legend.specialClasses.length; i++) {
|
|
1010
|
-
//
|
|
1024
|
+
// Special Classes label in HOVERS should only apply to selected special class key
|
|
1011
1025
|
// - you have to ALSO check that the key matches - putting here otherwise the if stmt too long
|
|
1012
|
-
if (
|
|
1026
|
+
if (column.name === state.legend.specialClasses[i].key) {
|
|
1013
1027
|
if (String(row[state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
|
|
1014
1028
|
value = displayDataAsText(state.legend.specialClasses[i].label, columnKey)
|
|
1015
1029
|
break
|
|
@@ -1053,25 +1067,29 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1053
1067
|
// - this function is used to prevent that and instead give the formatting that is wanted
|
|
1054
1068
|
// Example: Desired city display in tooltip on map: "Inter-Tribal Indian Reservation"
|
|
1055
1069
|
const titleCase = string => {
|
|
1056
|
-
//
|
|
1057
|
-
if (string
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
.split(' ')
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1070
|
+
// guard clause else error in editor
|
|
1071
|
+
if (!string) return
|
|
1072
|
+
if (string !== undefined) {
|
|
1073
|
+
// if hyphen found, then split, uppercase each word, and put back together
|
|
1074
|
+
if (string.includes('–') || string.includes('-')) {
|
|
1075
|
+
let dashSplit = string.includes('–') ? string.split('–') : string.split('-') // determine hyphen or en dash to split on
|
|
1076
|
+
let splitCharacter = string.includes('–') ? '–' : '-' // print hyphen or en dash later on.
|
|
1077
|
+
let frontSplit = dashSplit[0]
|
|
1078
|
+
.split(' ')
|
|
1079
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
1080
|
+
.join(' ')
|
|
1081
|
+
let backSplit = dashSplit[1]
|
|
1082
|
+
.split(' ')
|
|
1083
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
1084
|
+
.join(' ')
|
|
1085
|
+
return frontSplit + splitCharacter + backSplit
|
|
1086
|
+
} else {
|
|
1087
|
+
// just return with each word uppercase
|
|
1088
|
+
return string
|
|
1089
|
+
.split(' ')
|
|
1090
|
+
.map(word => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase())
|
|
1091
|
+
.join(' ')
|
|
1092
|
+
}
|
|
1075
1093
|
}
|
|
1076
1094
|
}
|
|
1077
1095
|
|
|
@@ -1083,6 +1101,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1083
1101
|
delete legendItem.disabled
|
|
1084
1102
|
})
|
|
1085
1103
|
|
|
1104
|
+
newLegend.runtimeDataHash = runtimeLegend.runtimeDataHash
|
|
1105
|
+
|
|
1086
1106
|
setRuntimeLegend(newLegend)
|
|
1087
1107
|
}
|
|
1088
1108
|
|
|
@@ -1201,7 +1221,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1201
1221
|
}
|
|
1202
1222
|
|
|
1203
1223
|
const handleMapAriaLabels = (state = '', testing = false) => {
|
|
1204
|
-
if (testing) console.log(`handleMapAriaLabels Testing On: ${state}`)
|
|
1224
|
+
if (testing) console.log(`handleMapAriaLabels Testing On: ${state}`) // eslint-disable-line
|
|
1205
1225
|
try {
|
|
1206
1226
|
if (!state.general.geoType) throw Error('handleMapAriaLabels: no geoType found in state')
|
|
1207
1227
|
let ariaLabel = ''
|
|
@@ -1232,7 +1252,68 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1232
1252
|
|
|
1233
1253
|
return ariaLabel
|
|
1234
1254
|
} catch (e) {
|
|
1235
|
-
console.error(e.message)
|
|
1255
|
+
console.error('COVE: ', e.message) // eslint-disable-line
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const reloadURLData = async () => {
|
|
1260
|
+
if (state.dataUrl) {
|
|
1261
|
+
const dataUrl = new URL(state.runtimeDataUrl || state.dataUrl)
|
|
1262
|
+
let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
1263
|
+
|
|
1264
|
+
let isUpdateNeeded = false
|
|
1265
|
+
state.filters.forEach(filter => {
|
|
1266
|
+
if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
|
|
1267
|
+
qsParams[filter.queryParameter] = filter.active
|
|
1268
|
+
isUpdateNeeded = true
|
|
1269
|
+
}
|
|
1270
|
+
})
|
|
1271
|
+
|
|
1272
|
+
if (!isUpdateNeeded) return
|
|
1273
|
+
|
|
1274
|
+
let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
|
|
1275
|
+
.map((param, i) => {
|
|
1276
|
+
let qs = i === 0 ? '?' : '&'
|
|
1277
|
+
qs += param + '='
|
|
1278
|
+
qs += qsParams[param]
|
|
1279
|
+
return qs
|
|
1280
|
+
})
|
|
1281
|
+
.join('')}`
|
|
1282
|
+
|
|
1283
|
+
let data
|
|
1284
|
+
|
|
1285
|
+
try {
|
|
1286
|
+
const regex = /(?:\.([^.]+))?$/
|
|
1287
|
+
|
|
1288
|
+
const ext = regex.exec(dataUrl.pathname)[1]
|
|
1289
|
+
if ('csv' === ext) {
|
|
1290
|
+
data = await fetch(dataUrlFinal)
|
|
1291
|
+
.then(response => response.text())
|
|
1292
|
+
.then(responseText => {
|
|
1293
|
+
const parsedCsv = Papa.parse(responseText, {
|
|
1294
|
+
header: true,
|
|
1295
|
+
dynamicTyping: true,
|
|
1296
|
+
skipEmptyLines: true
|
|
1297
|
+
})
|
|
1298
|
+
return parsedCsv.data
|
|
1299
|
+
})
|
|
1300
|
+
} else if ('json' === ext) {
|
|
1301
|
+
data = await fetch(dataUrlFinal).then(response => response.json())
|
|
1302
|
+
} else {
|
|
1303
|
+
data = []
|
|
1304
|
+
}
|
|
1305
|
+
} catch (e) {
|
|
1306
|
+
console.error(`Cannot parse URL: ${dataUrlFinal}`) // eslint-disable-line
|
|
1307
|
+
console.log(e) // eslint-disable-line
|
|
1308
|
+
data = []
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
if (state.dataDescription) {
|
|
1312
|
+
data = transform.autoStandardize(data)
|
|
1313
|
+
data = transform.developerStandardize(data, state.dataDescription)
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
setState({ ...state, runtimeDataUrl: dataUrlFinal, data })
|
|
1236
1317
|
}
|
|
1237
1318
|
}
|
|
1238
1319
|
|
|
@@ -1246,8 +1327,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1246
1327
|
...configObj
|
|
1247
1328
|
}
|
|
1248
1329
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1330
|
+
const urlFilters = newState.filters ? (newState.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
|
|
1331
|
+
|
|
1332
|
+
if (newState.dataUrl && !urlFilters) {
|
|
1251
1333
|
if (newState.dataUrl[0] === '/') {
|
|
1252
1334
|
newState.dataUrl = 'http://' + hostname + newState.dataUrl
|
|
1253
1335
|
}
|
|
@@ -1286,8 +1368,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1286
1368
|
addUIDs(newState, newState.columns.geo.name || newState.columns.geo.fips)
|
|
1287
1369
|
}
|
|
1288
1370
|
|
|
1289
|
-
if (newState.
|
|
1290
|
-
newState.
|
|
1371
|
+
if (newState.table.forceDisplay === undefined) {
|
|
1372
|
+
newState.table.forceDisplay = !isDashboard
|
|
1291
1373
|
}
|
|
1292
1374
|
|
|
1293
1375
|
validateFipsCodeLength(newState)
|
|
@@ -1342,16 +1424,16 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1342
1424
|
|
|
1343
1425
|
// DEV-769 make "Data Table" both a required field and default value
|
|
1344
1426
|
useEffect(() => {
|
|
1345
|
-
if (state.
|
|
1427
|
+
if (state.table?.label === '' || state.table?.label === undefined) {
|
|
1346
1428
|
setState({
|
|
1347
1429
|
...state,
|
|
1348
|
-
|
|
1349
|
-
...state.
|
|
1430
|
+
table: {
|
|
1431
|
+
...state.table,
|
|
1350
1432
|
title: 'Data Table'
|
|
1351
1433
|
}
|
|
1352
1434
|
})
|
|
1353
1435
|
}
|
|
1354
|
-
}, [state.
|
|
1436
|
+
}, [state.table]) // eslint-disable-line
|
|
1355
1437
|
|
|
1356
1438
|
// When geo label override changes
|
|
1357
1439
|
// - redo the tooltips
|
|
@@ -1391,16 +1473,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1391
1473
|
})
|
|
1392
1474
|
|
|
1393
1475
|
// Data
|
|
1394
|
-
let newRuntimeData
|
|
1395
1476
|
if (hashData !== runtimeData.fromHash && state.data?.fromColumn) {
|
|
1396
1477
|
const newRuntimeData = generateRuntimeData(state, filters || runtimeFilters, hashData)
|
|
1397
1478
|
setRuntimeData(newRuntimeData)
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
setRuntimeLegend(legend)
|
|
1479
|
+
} else {
|
|
1480
|
+
if (hashLegend !== runtimeLegend.fromHash && undefined === runtimeData.init) {
|
|
1481
|
+
const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
|
|
1482
|
+
setRuntimeLegend(legend)
|
|
1483
|
+
}
|
|
1404
1484
|
}
|
|
1405
1485
|
}, [state]) // eslint-disable-line
|
|
1406
1486
|
|
|
@@ -1412,7 +1492,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1412
1492
|
const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
|
|
1413
1493
|
setRuntimeLegend(legend)
|
|
1414
1494
|
}
|
|
1415
|
-
}, [runtimeData]) // eslint-disable-line
|
|
1495
|
+
}, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses]) // eslint-disable-line
|
|
1496
|
+
|
|
1497
|
+
useEffect(() => {
|
|
1498
|
+
reloadURLData()
|
|
1499
|
+
}, [JSON.stringify(state.filters)])
|
|
1416
1500
|
|
|
1417
1501
|
if (config) {
|
|
1418
1502
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
@@ -1422,14 +1506,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1422
1506
|
}
|
|
1423
1507
|
|
|
1424
1508
|
// Destructuring for more readable JSX
|
|
1425
|
-
const { general, tooltips,
|
|
1509
|
+
const { general, tooltips, table } = state
|
|
1426
1510
|
let { title, subtext = '' } = general
|
|
1427
1511
|
|
|
1428
1512
|
// if no title AND in editor then set a default
|
|
1429
1513
|
if (isEditor) {
|
|
1430
1514
|
if (!title || title === '') title = 'Map Title'
|
|
1431
1515
|
}
|
|
1432
|
-
if (!
|
|
1516
|
+
if (!table.label || table.label === '') table.label = 'Data Table'
|
|
1433
1517
|
|
|
1434
1518
|
// Outer container classes
|
|
1435
1519
|
let outerContainerClasses = ['cdc-open-viz-module', 'cdc-map-outer-container', currentViewport]
|
|
@@ -1475,12 +1559,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1475
1559
|
runtimeFilters,
|
|
1476
1560
|
setRuntimeFilters,
|
|
1477
1561
|
innerContainerRef,
|
|
1478
|
-
currentViewport
|
|
1562
|
+
currentViewport,
|
|
1563
|
+
isDebug
|
|
1479
1564
|
}
|
|
1480
1565
|
|
|
1481
1566
|
if (!mapProps.data || !state.data) return <Loading />
|
|
1482
1567
|
|
|
1483
|
-
const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true ===
|
|
1568
|
+
const hasDataTable = state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading
|
|
1484
1569
|
|
|
1485
1570
|
const handleMapTabbing = () => {
|
|
1486
1571
|
let tabbingID
|
|
@@ -1506,12 +1591,20 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1506
1591
|
|
|
1507
1592
|
const tabId = handleMapTabbing()
|
|
1508
1593
|
|
|
1594
|
+
// this only shows in Dashboard config mode and only if Show Table is also set
|
|
1595
|
+
const tableLink = (
|
|
1596
|
+
<a href={`#data-table-${state.general.dataKey}`} className='margin-left-href'>
|
|
1597
|
+
{state.general.dataKey} (Go to Table)
|
|
1598
|
+
</a>
|
|
1599
|
+
)
|
|
1600
|
+
|
|
1509
1601
|
return (
|
|
1510
1602
|
<ConfigContext.Provider value={mapProps}>
|
|
1511
1603
|
<div className={outerContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
|
|
1512
1604
|
{isEditor && (
|
|
1513
1605
|
<EditorPanel
|
|
1514
1606
|
isDashboard={isDashboard}
|
|
1607
|
+
isDebug={isDebug}
|
|
1515
1608
|
state={state}
|
|
1516
1609
|
setState={setState}
|
|
1517
1610
|
loadConfig={loadConfig}
|
|
@@ -1537,7 +1630,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1537
1630
|
)}
|
|
1538
1631
|
{general.introText && <section className='introText'>{parse(general.introText)}</section>}
|
|
1539
1632
|
|
|
1540
|
-
|
|
1633
|
+
{/* prettier-ignore */}
|
|
1634
|
+
{state?.filters?.length > 0 && <Filters config={state} setConfig={setState} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
|
|
1541
1635
|
|
|
1542
1636
|
<div
|
|
1543
1637
|
role='button'
|
|
@@ -1591,7 +1685,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1591
1685
|
|
|
1592
1686
|
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1593
1687
|
|
|
1594
|
-
{
|
|
1688
|
+
{/* Link */}
|
|
1689
|
+
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
|
|
1595
1690
|
|
|
1596
1691
|
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1597
1692
|
|
|
@@ -1600,12 +1695,12 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1600
1695
|
{state.general.showDownloadPdfButton && <CoveMediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
|
|
1601
1696
|
</CoveMediaControls.Section>
|
|
1602
1697
|
|
|
1603
|
-
{state.runtime.editorErrorMessage.length === 0 && true ===
|
|
1698
|
+
{state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1604
1699
|
<DataTable
|
|
1605
|
-
|
|
1700
|
+
config={state}
|
|
1606
1701
|
rawData={state.data}
|
|
1607
1702
|
navigationHandler={navigationHandler}
|
|
1608
|
-
expandDataTable={general.expandDataTable}
|
|
1703
|
+
expandDataTable={general.expandDataTable ? general.expandDataTable : table.expanded ? table.expanded : false}
|
|
1609
1704
|
headerColor={general.headerColor}
|
|
1610
1705
|
columns={state.columns}
|
|
1611
1706
|
showDownloadButton={general.showDownloadButton}
|
|
@@ -1614,9 +1709,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1614
1709
|
displayDataAsText={displayDataAsText}
|
|
1615
1710
|
displayGeoName={displayGeoName}
|
|
1616
1711
|
applyLegendToRow={applyLegendToRow}
|
|
1617
|
-
tableTitle={
|
|
1618
|
-
indexTitle={
|
|
1619
|
-
|
|
1712
|
+
tableTitle={table.label}
|
|
1713
|
+
indexTitle={table.indexLabel}
|
|
1714
|
+
vizTitle={general.title}
|
|
1620
1715
|
viewport={currentViewport}
|
|
1621
1716
|
formatLegendLocation={formatLegendLocation}
|
|
1622
1717
|
setFilteredCountryCode={setFilteredCountryCode}
|
|
@@ -1626,6 +1721,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1626
1721
|
innerContainerRef={innerContainerRef}
|
|
1627
1722
|
outerContainerRef={outerContainerRef}
|
|
1628
1723
|
imageRef={imageId}
|
|
1724
|
+
isDebug={isDebug}
|
|
1629
1725
|
/>
|
|
1630
1726
|
)}
|
|
1631
1727
|
|