@cdc/map 4.23.2 → 4.23.4
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 +24661 -24191
- package/examples/custom-map-layers.json +764 -0
- package/examples/default-county.json +169 -155
- package/examples/default-geocode.json +744 -744
- package/examples/default-hex.json +3 -5
- package/examples/example-city-state-no-territories.json +703 -0
- package/examples/example-city-state.json +26 -7
- package/examples/example-city-stateBAD.json +744 -0
- package/examples/gallery/city-state.json +478 -478
- package/examples/testing-layer-2.json +1 -0
- package/examples/testing-layer.json +96 -0
- package/examples/world-geocode-data.json +18 -0
- package/examples/world-geocode.json +108 -0
- package/index.html +35 -29
- package/package.json +6 -3
- package/src/CdcMap.jsx +179 -111
- package/src/components/CityList.jsx +35 -35
- package/src/components/CountyMap.jsx +309 -446
- package/src/components/DataTable.jsx +7 -31
- package/src/components/EditorPanel.jsx +468 -217
- package/src/components/Sidebar.jsx +2 -0
- package/src/components/UsaMap.jsx +34 -23
- package/src/components/WorldMap.jsx +40 -8
- package/src/data/feature-test.json +73 -0
- package/src/data/initial-state.js +10 -3
- package/src/data/supported-geos.js +7 -7
- 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/scss/map.scss +12 -0
- package/src/test/CdcMap.test.jsx +19 -0
- package/vite.config.js +3 -3
- package/src/components/Filters.jsx +0 -113
- package/src/hooks/useColorPalette.ts +0 -88
package/src/CdcMap.jsx
CHANGED
|
@@ -37,7 +37,7 @@ import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
|
37
37
|
|
|
38
38
|
// Child Components
|
|
39
39
|
import ConfigContext from './context'
|
|
40
|
-
import Filters from '
|
|
40
|
+
import Filters, { useFilters } from '@cdc/core/components/Filters'
|
|
41
41
|
import Modal from './components/Modal'
|
|
42
42
|
import Sidebar from './components/Sidebar'
|
|
43
43
|
|
|
@@ -82,13 +82,13 @@ const hashObj = row => {
|
|
|
82
82
|
|
|
83
83
|
return hash
|
|
84
84
|
} catch (e) {
|
|
85
|
-
console.error(e)
|
|
85
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const indexOfIgnoreType = (arr, item) => {
|
|
90
90
|
for (let i = 0; i < arr.length; i++) {
|
|
91
|
-
if (item
|
|
91
|
+
if (item === arr[i]) {
|
|
92
92
|
return i
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -112,7 +112,7 @@ const getUniqueValues = (data, columnName) => {
|
|
|
112
112
|
return Object.keys(result)
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, configUrl, logo = null, setConfig, setSharedFilter, setSharedFilterValue, hostname = 'localhost:8080', link }) => {
|
|
115
|
+
const CdcMap = ({ className, config, navigationHandler: customNavigationHandler, isDashboard = false, isEditor = false, isDebug = false, configUrl, logo = null, setConfig, setSharedFilter, setSharedFilterValue, hostname = 'localhost:8080', link }) => {
|
|
116
116
|
const transform = new DataTransform()
|
|
117
117
|
const [state, setState] = useState({ ...initialState })
|
|
118
118
|
const [loading, setLoading] = useState(true)
|
|
@@ -127,7 +127,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
127
127
|
const [coveLoadedHasRan, setCoveLoadedHasRan] = useState(false)
|
|
128
128
|
const [container, setContainer] = useState()
|
|
129
129
|
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
|
|
130
|
+
const [dimensions, setDimensions] = useState()
|
|
130
131
|
|
|
132
|
+
const { changeFilterActive, handleSorting } = useFilters({ config: state, setConfig: setState })
|
|
131
133
|
let legendMemo = useRef(new Map())
|
|
132
134
|
let innerContainerRef = useRef()
|
|
133
135
|
|
|
@@ -145,9 +147,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
145
147
|
})
|
|
146
148
|
}
|
|
147
149
|
} catch (e) {
|
|
148
|
-
console.error('Failed to set world map zoom.')
|
|
150
|
+
console.error('COVE: Failed to set world map zoom.') // eslint-disable-line
|
|
149
151
|
}
|
|
150
|
-
}, [filteredCountryCode])
|
|
152
|
+
}, [filteredCountryCode]) // eslint-disable-line
|
|
151
153
|
|
|
152
154
|
useEffect(() => {
|
|
153
155
|
setTimeout(() => {
|
|
@@ -159,7 +161,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
159
161
|
setRuntimeData(tmpData)
|
|
160
162
|
}
|
|
161
163
|
}, 100)
|
|
162
|
-
}, [filteredCountryCode])
|
|
164
|
+
}, [filteredCountryCode]) // eslint-disable-line
|
|
163
165
|
|
|
164
166
|
useEffect(() => {
|
|
165
167
|
if (state.mapPosition) {
|
|
@@ -167,16 +169,47 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
167
169
|
}
|
|
168
170
|
}, [state.mapPosition, setPosition])
|
|
169
171
|
|
|
172
|
+
const generateRuntimeLegendHash = () => {
|
|
173
|
+
return hashObj({
|
|
174
|
+
unified: state.legend.unified ?? false,
|
|
175
|
+
equalNumberOptIn: state.general.equalNumberOptIn ?? false,
|
|
176
|
+
specialClassesLast: state.legend.showSpecialClassesLast ?? false,
|
|
177
|
+
color: state.color,
|
|
178
|
+
customColors: state.customColors,
|
|
179
|
+
numberOfItems: state.legend.numberOfItems,
|
|
180
|
+
type: state.legend.type,
|
|
181
|
+
separateZero: state.legend.separateZero ?? false,
|
|
182
|
+
primary: state.columns.primary.name,
|
|
183
|
+
categoryValuesOrder: state.legend.categoryValuesOrder,
|
|
184
|
+
specialClasses: state.legend.specialClasses,
|
|
185
|
+
geoType: state.general.geoType,
|
|
186
|
+
data: state.data,
|
|
187
|
+
...runtimeFilters,
|
|
188
|
+
filters: {
|
|
189
|
+
...state.filters
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
|
|
170
194
|
const resizeObserver = new ResizeObserver(entries => {
|
|
171
195
|
for (let entry of entries) {
|
|
196
|
+
let { width, height } = entry.contentRect
|
|
172
197
|
let newViewport = getViewport(entry.contentRect.width)
|
|
198
|
+
let svgMarginWidth = 32
|
|
199
|
+
let editorWidth = 350
|
|
173
200
|
|
|
174
201
|
setCurrentViewport(newViewport)
|
|
202
|
+
|
|
203
|
+
if (isEditor) {
|
|
204
|
+
width = width - editorWidth
|
|
205
|
+
}
|
|
206
|
+
setDimensions([width, height])
|
|
175
207
|
}
|
|
176
208
|
})
|
|
177
209
|
|
|
178
210
|
// Tag each row with a UID. Helps with filtering/placing geos. Not enumerable so doesn't show up in loops/console logs except when directly addressed ex row.uid
|
|
179
211
|
// We are mutating state in place here (depending on where called) - but it's okay, this isn't used for rerender
|
|
212
|
+
// eslint-disable-next-line
|
|
180
213
|
const addUIDs = useCallback((obj, fromColumn) => {
|
|
181
214
|
obj.data.forEach(row => {
|
|
182
215
|
let uid = null
|
|
@@ -225,6 +258,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
225
258
|
const geoName = row[obj.columns.geo.name]
|
|
226
259
|
|
|
227
260
|
uid = countryKeys.find(key => supportedCountries[key].includes(geoName))
|
|
261
|
+
|
|
262
|
+
// Cities
|
|
263
|
+
if (!uid && 'world-geocode' === state.general.type) {
|
|
264
|
+
uid = cityKeys.find(key => key === geoName?.toUpperCase())
|
|
265
|
+
}
|
|
228
266
|
}
|
|
229
267
|
|
|
230
268
|
// County Check
|
|
@@ -248,9 +286,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
248
286
|
obj.data.fromColumn = fromColumn
|
|
249
287
|
})
|
|
250
288
|
|
|
289
|
+
// eslint-disable-next-line
|
|
251
290
|
const generateRuntimeLegend = useCallback((obj, runtimeData, hash) => {
|
|
252
291
|
const newLegendMemo = new Map() // Reset memoization
|
|
253
|
-
|
|
292
|
+
let primaryCol = obj.columns.primary.name,
|
|
254
293
|
isBubble = obj.general.type === 'bubble',
|
|
255
294
|
categoricalCol = obj.columns.categorical ? obj.columns.categorical.name : undefined,
|
|
256
295
|
type = obj.legend.type,
|
|
@@ -262,6 +301,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
262
301
|
result.fromHash = hash
|
|
263
302
|
}
|
|
264
303
|
|
|
304
|
+
result.runtimeDataHash = runtimeData.fromHash
|
|
305
|
+
|
|
265
306
|
// Unified will based the legend off ALL of the data maps received. Otherwise, it will use
|
|
266
307
|
let dataSet = obj.legend.unified ? obj.data : Object.values(runtimeData)
|
|
267
308
|
|
|
@@ -457,6 +498,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
457
498
|
}
|
|
458
499
|
|
|
459
500
|
legendMemo.current = newLegendMemo
|
|
501
|
+
|
|
502
|
+
result.forEach((row, i) => {
|
|
503
|
+
row.bin = i // set bin number to index
|
|
504
|
+
})
|
|
505
|
+
|
|
460
506
|
return result
|
|
461
507
|
}
|
|
462
508
|
|
|
@@ -536,6 +582,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
536
582
|
let min = removedRows[0][primaryCol],
|
|
537
583
|
max = removedRows[removedRows.length - 1][primaryCol]
|
|
538
584
|
|
|
585
|
+
// eslint-disable-next-line
|
|
539
586
|
removedRows.forEach(row => {
|
|
540
587
|
newLegendMemo.set(hashObj(row), result.length)
|
|
541
588
|
})
|
|
@@ -574,6 +621,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
574
621
|
breaks.unshift(0)
|
|
575
622
|
}
|
|
576
623
|
|
|
624
|
+
// eslint-disable-next-line array-callback-return
|
|
577
625
|
breaks.map((item, index) => {
|
|
578
626
|
const setMin = index => {
|
|
579
627
|
let min = breaks[index]
|
|
@@ -646,7 +694,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
646
694
|
}
|
|
647
695
|
|
|
648
696
|
// Equal Interval
|
|
649
|
-
|
|
650
697
|
if (type === 'equalinterval' && dataSet?.length !== 0) {
|
|
651
698
|
if (!dataSet || dataSet.length === 0) {
|
|
652
699
|
setState({
|
|
@@ -695,11 +742,29 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
695
742
|
})
|
|
696
743
|
|
|
697
744
|
legendMemo.current = newLegendMemo
|
|
745
|
+
|
|
746
|
+
//----------
|
|
747
|
+
// DEV-784
|
|
748
|
+
// before returning the legend result
|
|
749
|
+
// add property for bin number and set to index location
|
|
750
|
+
result.forEach((row, i) => {
|
|
751
|
+
row.bin = i // set bin number to index
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
// Move all special legend items from "Special Classes" to the end of the legend
|
|
755
|
+
if (state.legend.showSpecialClassesLast) {
|
|
756
|
+
let specialRows = result.filter(d => d.special === true)
|
|
757
|
+
let otherRows = result.filter(d => !d.special)
|
|
758
|
+
result = [...otherRows, ...specialRows]
|
|
759
|
+
}
|
|
760
|
+
//-----------
|
|
761
|
+
|
|
698
762
|
return result
|
|
699
763
|
})
|
|
700
764
|
|
|
765
|
+
// eslint-disable-next-line
|
|
701
766
|
const generateRuntimeFilters = useCallback((obj, hash, runtimeFilters) => {
|
|
702
|
-
if (undefined === obj.filters || obj.filters.length === 0) return []
|
|
767
|
+
if (typeof obj === 'undefined' || undefined === obj.filters || obj.filters.length === 0) return []
|
|
703
768
|
|
|
704
769
|
let filters = []
|
|
705
770
|
|
|
@@ -738,10 +803,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
738
803
|
newFilter = {}
|
|
739
804
|
}
|
|
740
805
|
|
|
806
|
+
newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
|
|
741
807
|
newFilter.label = label ?? ''
|
|
742
808
|
newFilter.columnName = columnName
|
|
743
809
|
newFilter.values = values
|
|
744
|
-
newFilter
|
|
810
|
+
handleSorting(newFilter)
|
|
811
|
+
newFilter.active = active ?? values[0] // Default to first found value
|
|
812
|
+
newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
|
|
745
813
|
|
|
746
814
|
filters.push(newFilter)
|
|
747
815
|
})
|
|
@@ -750,21 +818,21 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
750
818
|
})
|
|
751
819
|
|
|
752
820
|
// Calculates what's going to be displayed on the map and data table at render.
|
|
821
|
+
// eslint-disable-next-line
|
|
753
822
|
const generateRuntimeData = useCallback((obj, filters, hash, test) => {
|
|
754
823
|
try {
|
|
755
824
|
const result = {}
|
|
756
825
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
})
|
|
762
|
-
}
|
|
826
|
+
// Adding property this way prevents it from being enumerated
|
|
827
|
+
Object.defineProperty(result, 'fromHash', {
|
|
828
|
+
value: hash
|
|
829
|
+
})
|
|
763
830
|
|
|
831
|
+
addUIDs(obj, obj.columns.geo.name)
|
|
764
832
|
obj.data.forEach(row => {
|
|
765
833
|
if (test) {
|
|
766
|
-
console.log('object', obj)
|
|
767
|
-
console.log('row', row)
|
|
834
|
+
console.log('object', obj) // eslint-disable-line
|
|
835
|
+
console.log('row', row) // eslint-disable-line
|
|
768
836
|
}
|
|
769
837
|
|
|
770
838
|
if (undefined === row.uid) return false // No UID for this row, we can't use for mapping
|
|
@@ -807,7 +875,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
807
875
|
|
|
808
876
|
return result
|
|
809
877
|
} catch (e) {
|
|
810
|
-
console.error(e)
|
|
878
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
811
879
|
}
|
|
812
880
|
})
|
|
813
881
|
|
|
@@ -816,7 +884,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
816
884
|
resizeObserver.observe(node)
|
|
817
885
|
}
|
|
818
886
|
setContainer(node)
|
|
819
|
-
}, [])
|
|
887
|
+
}, []) // eslint-disable-line
|
|
820
888
|
|
|
821
889
|
const mapSvg = useRef(null)
|
|
822
890
|
|
|
@@ -826,33 +894,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
826
894
|
}
|
|
827
895
|
}
|
|
828
896
|
|
|
829
|
-
const changeFilterActive = async (idx, activeValue) => {
|
|
830
|
-
// Reset active legend toggles
|
|
831
|
-
resetLegendToggles()
|
|
832
|
-
|
|
833
|
-
try {
|
|
834
|
-
const isEmpty = obj => {
|
|
835
|
-
return Object.keys(obj).length === 0
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
let filters = [...runtimeFilters]
|
|
839
|
-
|
|
840
|
-
filters[idx] = { ...filters[idx] }
|
|
841
|
-
filters[idx].active = activeValue
|
|
842
|
-
|
|
843
|
-
const newData = generateRuntimeData(state, filters)
|
|
844
|
-
|
|
845
|
-
// throw an error if newData is empty
|
|
846
|
-
if (isEmpty(newData)) throw new Error('Cove Filter Error: No runtime data to set for this filter')
|
|
847
|
-
|
|
848
|
-
// set the runtime filters and data
|
|
849
|
-
setRuntimeData(newData)
|
|
850
|
-
setRuntimeFilters(filters)
|
|
851
|
-
} catch (e) {
|
|
852
|
-
console.error(e.message)
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
897
|
const displayDataAsText = (value, columnName) => {
|
|
857
898
|
if (value === null || value === '' || value === undefined) {
|
|
858
899
|
return ''
|
|
@@ -897,6 +938,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
897
938
|
return formattedValue
|
|
898
939
|
}
|
|
899
940
|
|
|
941
|
+
// this is passed DOWN into the various components
|
|
942
|
+
// then they do a lookup based on the bin number as index into here (TT)
|
|
900
943
|
const applyLegendToRow = rowObj => {
|
|
901
944
|
try {
|
|
902
945
|
if (!rowObj) throw new Error('COVE: No rowObj in applyLegendToRow')
|
|
@@ -911,13 +954,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
911
954
|
if (legendMemo.current.has(hash)) {
|
|
912
955
|
let idx = legendMemo.current.get(hash)
|
|
913
956
|
if (runtimeLegend[idx]?.disabled) return false
|
|
914
|
-
|
|
957
|
+
|
|
958
|
+
// DEV-784 changed to use bin prop to get color instead of idx
|
|
959
|
+
// bc we re-order legend when showSpecialClassesLast is checked
|
|
960
|
+
let legendBinColor = runtimeLegend.find(o => o.bin === idx)?.color
|
|
961
|
+
return generateColorsArray(legendBinColor, runtimeLegend[idx]?.special)
|
|
915
962
|
}
|
|
916
963
|
|
|
917
964
|
// Fail state
|
|
918
965
|
return generateColorsArray()
|
|
919
966
|
} catch (e) {
|
|
920
|
-
console.error(e)
|
|
967
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
921
968
|
}
|
|
922
969
|
}
|
|
923
970
|
|
|
@@ -927,6 +974,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
927
974
|
|
|
928
975
|
// Adds geo label, ie State: Georgia
|
|
929
976
|
let stateOrCounty = state.general.geoType === 'us' ? 'State: ' : state.general.geoType === 'us-county' || state.general.geoType === 'single-state' ? 'County: ' : ''
|
|
977
|
+
|
|
930
978
|
// check the override
|
|
931
979
|
stateOrCounty = state.general.geoLabelOverride !== '' ? state.general.geoLabelOverride + ': ' : stateOrCounty
|
|
932
980
|
|
|
@@ -939,7 +987,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
939
987
|
|
|
940
988
|
toolTipText += !state.general.hideGeoColumnInTooltip ? `<strong>${stateOrCounty}${displayGeoName(geoName)}</strong>` : `<strong>${displayGeoName(geoName)}</strong>`
|
|
941
989
|
|
|
942
|
-
if (('data' === state.general.type || state.general.type === 'bubble' || state.general.type === 'us-geocode') && undefined !== row) {
|
|
990
|
+
if (('data' === state.general.type || state.general.type === 'bubble' || state.general.type === 'us-geocode' || state.general.type === 'world-geocode') && undefined !== row) {
|
|
943
991
|
toolTipText += `<dl>`
|
|
944
992
|
|
|
945
993
|
Object.keys(state.columns).forEach(columnKey => {
|
|
@@ -951,10 +999,15 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
951
999
|
let value
|
|
952
1000
|
|
|
953
1001
|
if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
|
|
1002
|
+
// THIS CODE SHOULD NOT ACT ON THE ENTIRE ROW OF KEYS BUT ONLY THE ONE KEY IN THE SPECIAL CLASS
|
|
954
1003
|
for (let i = 0; i < state.legend.specialClasses.length; i++) {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1004
|
+
// DEV-3303 - Special Classes label in HOVERS should only apply to selected special class key
|
|
1005
|
+
// - you have to ALSO check that the key matches - putting here otherwise the if stmt too long
|
|
1006
|
+
if (columnKey === state.legend.specialClasses[i].key) {
|
|
1007
|
+
if (String(row[state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
|
|
1008
|
+
value = displayDataAsText(state.legend.specialClasses[i].label, columnKey)
|
|
1009
|
+
break
|
|
1010
|
+
}
|
|
958
1011
|
}
|
|
959
1012
|
}
|
|
960
1013
|
}
|
|
@@ -1024,6 +1077,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1024
1077
|
delete legendItem.disabled
|
|
1025
1078
|
})
|
|
1026
1079
|
|
|
1080
|
+
newLegend.runtimeDataHash = runtimeLegend.runtimeDataHash
|
|
1081
|
+
|
|
1027
1082
|
setRuntimeLegend(newLegend)
|
|
1028
1083
|
}
|
|
1029
1084
|
|
|
@@ -1100,6 +1155,17 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1100
1155
|
setSharedFilter(state.uid, value)
|
|
1101
1156
|
}
|
|
1102
1157
|
|
|
1158
|
+
// If world-geocode map zoom to geo point
|
|
1159
|
+
if ('world-geocode' === state.general.type) {
|
|
1160
|
+
let lat = value[state.columns.latitude.name]
|
|
1161
|
+
let long = value[state.columns.longitude.name]
|
|
1162
|
+
|
|
1163
|
+
setState({
|
|
1164
|
+
...state,
|
|
1165
|
+
mapPosition: { coordinates: [long, lat], zoom: 3 }
|
|
1166
|
+
})
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1103
1169
|
// If modals are set or we are on a mobile viewport, display modal
|
|
1104
1170
|
if (window.matchMedia('(any-hover: none)').matches || 'click' === state.tooltips.appearanceType) {
|
|
1105
1171
|
setModal({
|
|
@@ -1131,7 +1197,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1131
1197
|
}
|
|
1132
1198
|
|
|
1133
1199
|
const handleMapAriaLabels = (state = '', testing = false) => {
|
|
1134
|
-
if (testing) console.log(`handleMapAriaLabels Testing On: ${state}`)
|
|
1200
|
+
if (testing) console.log(`handleMapAriaLabels Testing On: ${state}`) // eslint-disable-line
|
|
1135
1201
|
try {
|
|
1136
1202
|
if (!state.general.geoType) throw Error('handleMapAriaLabels: no geoType found in state')
|
|
1137
1203
|
let ariaLabel = ''
|
|
@@ -1162,7 +1228,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1162
1228
|
|
|
1163
1229
|
return ariaLabel
|
|
1164
1230
|
} catch (e) {
|
|
1165
|
-
console.error(e.message)
|
|
1231
|
+
console.error('COVE: ', e.message) // eslint-disable-line
|
|
1166
1232
|
}
|
|
1167
1233
|
}
|
|
1168
1234
|
|
|
@@ -1247,28 +1313,28 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1247
1313
|
// Initial load
|
|
1248
1314
|
useEffect(() => {
|
|
1249
1315
|
init()
|
|
1250
|
-
}, [])
|
|
1316
|
+
}, []) // eslint-disable-line
|
|
1251
1317
|
|
|
1252
1318
|
useEffect(() => {
|
|
1253
1319
|
if (state && !coveLoadedHasRan && container) {
|
|
1254
1320
|
publish('cove_loaded', { config: state })
|
|
1255
1321
|
setCoveLoadedHasRan(true)
|
|
1256
1322
|
}
|
|
1257
|
-
}, [state, container])
|
|
1323
|
+
}, [state, container]) // eslint-disable-line
|
|
1258
1324
|
|
|
1259
1325
|
useEffect(() => {
|
|
1260
1326
|
if (state.data) {
|
|
1261
1327
|
let newData = generateRuntimeData(state)
|
|
1262
1328
|
setRuntimeData(newData)
|
|
1263
1329
|
}
|
|
1264
|
-
}, [state.general.statePicked])
|
|
1330
|
+
}, [state.general.statePicked]) // eslint-disable-line
|
|
1265
1331
|
|
|
1266
1332
|
useEffect(() => {
|
|
1267
1333
|
// When geotype changes - add UID
|
|
1268
1334
|
if (state.data && state.columns.geo.name) {
|
|
1269
1335
|
addUIDs(state, state.columns.geo.name)
|
|
1270
1336
|
}
|
|
1271
|
-
}, [state])
|
|
1337
|
+
}, [state]) // eslint-disable-line
|
|
1272
1338
|
|
|
1273
1339
|
// DEV-769 make "Data Table" both a required field and default value
|
|
1274
1340
|
useEffect(() => {
|
|
@@ -1281,13 +1347,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1281
1347
|
}
|
|
1282
1348
|
})
|
|
1283
1349
|
}
|
|
1284
|
-
}, [state.dataTable])
|
|
1350
|
+
}, [state.dataTable]) // eslint-disable-line
|
|
1285
1351
|
|
|
1286
1352
|
// When geo label override changes
|
|
1287
1353
|
// - redo the tooltips
|
|
1288
1354
|
useEffect(() => {
|
|
1289
1355
|
applyTooltipsToGeo()
|
|
1290
|
-
}, [state.general.geoLabelOverride])
|
|
1356
|
+
}, [state.general.geoLabelOverride]) // eslint-disable-line
|
|
1291
1357
|
|
|
1292
1358
|
useEffect(() => {
|
|
1293
1359
|
// UID
|
|
@@ -1307,76 +1373,57 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1307
1373
|
}
|
|
1308
1374
|
}
|
|
1309
1375
|
|
|
1310
|
-
const hashLegend =
|
|
1311
|
-
color: state.color,
|
|
1312
|
-
customColors: state.customColors,
|
|
1313
|
-
numberOfItems: state.legend.numberOfItems,
|
|
1314
|
-
type: state.legend.type,
|
|
1315
|
-
separateZero: state.legend.separateZero ?? false,
|
|
1316
|
-
categoryValuesOrder: state.legend.categoryValuesOrder,
|
|
1317
|
-
specialClasses: state.legend.specialClasses,
|
|
1318
|
-
geoType: state.general.geoType,
|
|
1319
|
-
data: state.data,
|
|
1320
|
-
...runtimeLegend,
|
|
1321
|
-
...runtimeFilters
|
|
1322
|
-
})
|
|
1376
|
+
const hashLegend = generateRuntimeLegendHash()
|
|
1323
1377
|
|
|
1324
1378
|
const hashData = hashObj({
|
|
1379
|
+
data: state.data,
|
|
1325
1380
|
columns: state.columns,
|
|
1326
1381
|
geoType: state.general.geoType,
|
|
1327
1382
|
type: state.general.type,
|
|
1328
1383
|
geo: state.columns.geo.name,
|
|
1329
1384
|
primary: state.columns.primary.name,
|
|
1330
|
-
data: state.data,
|
|
1331
|
-
...runtimeFilters,
|
|
1332
1385
|
mapPosition: state.mapPosition,
|
|
1333
1386
|
...runtimeFilters
|
|
1334
1387
|
})
|
|
1335
1388
|
|
|
1336
1389
|
// Data
|
|
1337
|
-
let newRuntimeData
|
|
1338
1390
|
if (hashData !== runtimeData.fromHash && state.data?.fromColumn) {
|
|
1339
1391
|
const newRuntimeData = generateRuntimeData(state, filters || runtimeFilters, hashData)
|
|
1340
1392
|
setRuntimeData(newRuntimeData)
|
|
1393
|
+
} else {
|
|
1394
|
+
if (hashLegend !== runtimeLegend.fromHash && undefined === runtimeData.init) {
|
|
1395
|
+
const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
|
|
1396
|
+
setRuntimeLegend(legend)
|
|
1397
|
+
}
|
|
1341
1398
|
}
|
|
1342
|
-
|
|
1343
|
-
// Legend
|
|
1344
|
-
if (hashLegend !== runtimeLegend.fromHash && (undefined === runtimeData.init || newRuntimeData)) {
|
|
1345
|
-
const legend = generateRuntimeLegend(state, newRuntimeData || runtimeData, hashLegend)
|
|
1346
|
-
setRuntimeLegend(legend)
|
|
1347
|
-
}
|
|
1348
|
-
}, [state])
|
|
1399
|
+
}, [state]) // eslint-disable-line
|
|
1349
1400
|
|
|
1350
1401
|
useEffect(() => {
|
|
1351
|
-
const hashLegend =
|
|
1352
|
-
color: state.color,
|
|
1353
|
-
customColors: state.customColors,
|
|
1354
|
-
numberOfItems: state.legend.numberOfItems,
|
|
1355
|
-
type: state.legend.type,
|
|
1356
|
-
separateZero: state.legend.separateZero ?? false,
|
|
1357
|
-
categoryValuesOrder: state.legend.categoryValuesOrder,
|
|
1358
|
-
specialClasses: state.legend.specialClasses,
|
|
1359
|
-
geoType: state.general.geoType,
|
|
1360
|
-
data: state.data
|
|
1361
|
-
})
|
|
1402
|
+
const hashLegend = generateRuntimeLegendHash()
|
|
1362
1403
|
|
|
1363
1404
|
// Legend - Update when runtimeData does
|
|
1364
1405
|
if (hashLegend !== runtimeLegend.fromHash && undefined === runtimeData.init) {
|
|
1365
|
-
const legend = generateRuntimeLegend(state, runtimeData)
|
|
1406
|
+
const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
|
|
1366
1407
|
setRuntimeLegend(legend)
|
|
1367
1408
|
}
|
|
1368
|
-
}, [runtimeData])
|
|
1409
|
+
}, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses]) // eslint-disable-line
|
|
1369
1410
|
|
|
1370
1411
|
if (config) {
|
|
1371
1412
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
1372
1413
|
useEffect(() => {
|
|
1373
1414
|
loadConfig(config)
|
|
1374
|
-
}, [config.data])
|
|
1415
|
+
}, [config.data]) // eslint-disable-line
|
|
1375
1416
|
}
|
|
1376
1417
|
|
|
1377
1418
|
// Destructuring for more readable JSX
|
|
1378
1419
|
const { general, tooltips, dataTable } = state
|
|
1379
|
-
|
|
1420
|
+
let { title, subtext = '' } = general
|
|
1421
|
+
|
|
1422
|
+
// if no title AND in editor then set a default
|
|
1423
|
+
if (isEditor) {
|
|
1424
|
+
if (!title || title === '') title = 'Map Title'
|
|
1425
|
+
}
|
|
1426
|
+
if (!dataTable.title || dataTable.title === '') dataTable.title = 'Data Table'
|
|
1380
1427
|
|
|
1381
1428
|
// Outer container classes
|
|
1382
1429
|
let outerContainerClasses = ['cdc-open-viz-module', 'cdc-map-outer-container', currentViewport]
|
|
@@ -1421,7 +1468,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1421
1468
|
handleMapAriaLabels,
|
|
1422
1469
|
runtimeFilters,
|
|
1423
1470
|
setRuntimeFilters,
|
|
1424
|
-
innerContainerRef
|
|
1471
|
+
innerContainerRef,
|
|
1472
|
+
currentViewport,
|
|
1473
|
+
isDebug
|
|
1425
1474
|
}
|
|
1426
1475
|
|
|
1427
1476
|
if (!mapProps.data || !state.data) return <Loading />
|
|
@@ -1452,16 +1501,35 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1452
1501
|
|
|
1453
1502
|
const tabId = handleMapTabbing()
|
|
1454
1503
|
|
|
1504
|
+
// this only shows in Dashboard config mode and only if Show Table is also set
|
|
1505
|
+
const tableLink = (
|
|
1506
|
+
<a href={`#data-table-${state.general.dataKey}`} className='margin-left-href'>
|
|
1507
|
+
{state.general.dataKey} (Go to Table)
|
|
1508
|
+
</a>
|
|
1509
|
+
)
|
|
1510
|
+
|
|
1455
1511
|
return (
|
|
1456
1512
|
<ConfigContext.Provider value={mapProps}>
|
|
1457
1513
|
<div className={outerContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
|
|
1458
|
-
{isEditor &&
|
|
1514
|
+
{isEditor && (
|
|
1515
|
+
<EditorPanel
|
|
1516
|
+
isDashboard={isDashboard}
|
|
1517
|
+
isDebug={isDebug}
|
|
1518
|
+
state={state}
|
|
1519
|
+
setState={setState}
|
|
1520
|
+
loadConfig={loadConfig}
|
|
1521
|
+
setParentConfig={setConfig}
|
|
1522
|
+
setRuntimeFilters={setRuntimeFilters}
|
|
1523
|
+
runtimeFilters={runtimeFilters}
|
|
1524
|
+
runtimeLegend={runtimeLegend}
|
|
1525
|
+
columnsInData={Object.keys(state.data[0])}
|
|
1526
|
+
changeFilterActive={changeFilterActive}
|
|
1527
|
+
/>
|
|
1528
|
+
)}
|
|
1459
1529
|
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
1460
1530
|
<section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
|
|
1461
|
-
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType &&
|
|
1462
|
-
|
|
1463
|
-
}
|
|
1464
|
-
{state.general.title && (
|
|
1531
|
+
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && <ReactTooltip id='tooltip' variant='light' float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}`} />}
|
|
1532
|
+
{title && (
|
|
1465
1533
|
<header className={general.showTitle === true ? 'visible' : 'hidden'} {...(!general.showTitle || !state.general.title ? { 'aria-hidden': true } : { 'aria-hidden': false })}>
|
|
1466
1534
|
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
|
1467
1535
|
<div role='heading' className={'map-title ' + general.headerColor} tabIndex='0' aria-level='2'>
|
|
@@ -1470,11 +1538,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1470
1538
|
</div>
|
|
1471
1539
|
</header>
|
|
1472
1540
|
)}
|
|
1473
|
-
{general.introText &&
|
|
1474
|
-
<section className='introText'>{parse(general.introText)}</section>
|
|
1475
|
-
}
|
|
1541
|
+
{general.introText && <section className='introText'>{parse(general.introText)}</section>}
|
|
1476
1542
|
|
|
1477
|
-
|
|
1543
|
+
{/* prettier-ignore */}
|
|
1544
|
+
{state?.filters?.length > 0 && <Filters config={state} setConfig={setState} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
|
|
1478
1545
|
|
|
1479
1546
|
<div
|
|
1480
1547
|
role='button'
|
|
@@ -1528,7 +1595,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1528
1595
|
|
|
1529
1596
|
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1530
1597
|
|
|
1531
|
-
{
|
|
1598
|
+
{/* Link */}
|
|
1599
|
+
{isDashboard && config.dataTable.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
|
|
1532
1600
|
|
|
1533
1601
|
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1534
1602
|
|