@cdc/map 4.23.3 → 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/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 './components/Filters'
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,7 +82,7 @@ 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
 
@@ -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,7 +147,7 @@ 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
152
  }, [filteredCountryCode]) // eslint-disable-line
151
153
 
@@ -169,6 +171,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
169
171
 
170
172
  const generateRuntimeLegendHash = () => {
171
173
  return hashObj({
174
+ unified: state.legend.unified ?? false,
175
+ equalNumberOptIn: state.general.equalNumberOptIn ?? false,
176
+ specialClassesLast: state.legend.showSpecialClassesLast ?? false,
172
177
  color: state.color,
173
178
  customColors: state.customColors,
174
179
  numberOfItems: state.legend.numberOfItems,
@@ -179,15 +184,26 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
179
184
  specialClasses: state.legend.specialClasses,
180
185
  geoType: state.general.geoType,
181
186
  data: state.data,
182
- ...runtimeFilters
187
+ ...runtimeFilters,
188
+ filters: {
189
+ ...state.filters
190
+ }
183
191
  })
184
192
  }
185
193
 
186
194
  const resizeObserver = new ResizeObserver(entries => {
187
195
  for (let entry of entries) {
196
+ let { width, height } = entry.contentRect
188
197
  let newViewport = getViewport(entry.contentRect.width)
198
+ let svgMarginWidth = 32
199
+ let editorWidth = 350
189
200
 
190
201
  setCurrentViewport(newViewport)
202
+
203
+ if (isEditor) {
204
+ width = width - editorWidth
205
+ }
206
+ setDimensions([width, height])
191
207
  }
192
208
  })
193
209
 
@@ -285,6 +301,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
285
301
  result.fromHash = hash
286
302
  }
287
303
 
304
+ result.runtimeDataHash = runtimeData.fromHash
305
+
288
306
  // Unified will based the legend off ALL of the data maps received. Otherwise, it will use
289
307
  let dataSet = obj.legend.unified ? obj.data : Object.values(runtimeData)
290
308
 
@@ -785,10 +803,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
785
803
  newFilter = {}
786
804
  }
787
805
 
806
+ newFilter.order = obj.filters[idx].order ? obj.filters[idx].order : 'asc'
788
807
  newFilter.label = label ?? ''
789
808
  newFilter.columnName = columnName
790
809
  newFilter.values = values
791
- newFilter.active = active || values[0] // Default to first found value
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'
792
813
 
793
814
  filters.push(newFilter)
794
815
  })
@@ -810,8 +831,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
810
831
  addUIDs(obj, obj.columns.geo.name)
811
832
  obj.data.forEach(row => {
812
833
  if (test) {
813
- console.log('object', obj)
814
- console.log('row', row)
834
+ console.log('object', obj) // eslint-disable-line
835
+ console.log('row', row) // eslint-disable-line
815
836
  }
816
837
 
817
838
  if (undefined === row.uid) return false // No UID for this row, we can't use for mapping
@@ -854,7 +875,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
854
875
 
855
876
  return result
856
877
  } catch (e) {
857
- console.error(e)
878
+ console.error('COVE: ', e) // eslint-disable-line
858
879
  }
859
880
  })
860
881
 
@@ -873,33 +894,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
873
894
  }
874
895
  }
875
896
 
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
897
  const displayDataAsText = (value, columnName) => {
904
898
  if (value === null || value === '' || value === undefined) {
905
899
  return ''
@@ -970,7 +964,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
970
964
  // Fail state
971
965
  return generateColorsArray()
972
966
  } catch (e) {
973
- console.error(e)
967
+ console.error('COVE: ', e) // eslint-disable-line
974
968
  }
975
969
  }
976
970
 
@@ -1083,6 +1077,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1083
1077
  delete legendItem.disabled
1084
1078
  })
1085
1079
 
1080
+ newLegend.runtimeDataHash = runtimeLegend.runtimeDataHash
1081
+
1086
1082
  setRuntimeLegend(newLegend)
1087
1083
  }
1088
1084
 
@@ -1201,7 +1197,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1201
1197
  }
1202
1198
 
1203
1199
  const handleMapAriaLabels = (state = '', testing = false) => {
1204
- if (testing) console.log(`handleMapAriaLabels Testing On: ${state}`)
1200
+ if (testing) console.log(`handleMapAriaLabels Testing On: ${state}`) // eslint-disable-line
1205
1201
  try {
1206
1202
  if (!state.general.geoType) throw Error('handleMapAriaLabels: no geoType found in state')
1207
1203
  let ariaLabel = ''
@@ -1232,7 +1228,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1232
1228
 
1233
1229
  return ariaLabel
1234
1230
  } catch (e) {
1235
- console.error(e.message)
1231
+ console.error('COVE: ', e.message) // eslint-disable-line
1236
1232
  }
1237
1233
  }
1238
1234
 
@@ -1391,16 +1387,14 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1391
1387
  })
1392
1388
 
1393
1389
  // Data
1394
- let newRuntimeData
1395
1390
  if (hashData !== runtimeData.fromHash && state.data?.fromColumn) {
1396
1391
  const newRuntimeData = generateRuntimeData(state, filters || runtimeFilters, hashData)
1397
1392
  setRuntimeData(newRuntimeData)
1398
- }
1399
-
1400
- // Legend
1401
- if (hashLegend !== runtimeLegend.fromHash && (undefined === runtimeData.init || newRuntimeData)) {
1402
- const legend = generateRuntimeLegend(state, newRuntimeData || runtimeData, hashLegend)
1403
- setRuntimeLegend(legend)
1393
+ } else {
1394
+ if (hashLegend !== runtimeLegend.fromHash && undefined === runtimeData.init) {
1395
+ const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
1396
+ setRuntimeLegend(legend)
1397
+ }
1404
1398
  }
1405
1399
  }, [state]) // eslint-disable-line
1406
1400
 
@@ -1412,7 +1406,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1412
1406
  const legend = generateRuntimeLegend(state, runtimeData, hashLegend)
1413
1407
  setRuntimeLegend(legend)
1414
1408
  }
1415
- }, [runtimeData]) // eslint-disable-line
1409
+ }, [runtimeData, state.legend.unified, state.legend.showSpecialClassesLast, state.legend.separateZero, state.general.equalNumberOptIn, state.legend.numberOfItems, state.legend.specialClasses]) // eslint-disable-line
1416
1410
 
1417
1411
  if (config) {
1418
1412
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -1475,7 +1469,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1475
1469
  runtimeFilters,
1476
1470
  setRuntimeFilters,
1477
1471
  innerContainerRef,
1478
- currentViewport
1472
+ currentViewport,
1473
+ isDebug
1479
1474
  }
1480
1475
 
1481
1476
  if (!mapProps.data || !state.data) return <Loading />
@@ -1506,12 +1501,20 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1506
1501
 
1507
1502
  const tabId = handleMapTabbing()
1508
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
+
1509
1511
  return (
1510
1512
  <ConfigContext.Provider value={mapProps}>
1511
1513
  <div className={outerContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
1512
1514
  {isEditor && (
1513
1515
  <EditorPanel
1514
1516
  isDashboard={isDashboard}
1517
+ isDebug={isDebug}
1515
1518
  state={state}
1516
1519
  setState={setState}
1517
1520
  loadConfig={loadConfig}
@@ -1537,7 +1540,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1537
1540
  )}
1538
1541
  {general.introText && <section className='introText'>{parse(general.introText)}</section>}
1539
1542
 
1540
- <Filters />
1543
+ {/* prettier-ignore */}
1544
+ {state?.filters?.length > 0 && <Filters config={state} setConfig={setState} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
1541
1545
 
1542
1546
  <div
1543
1547
  role='button'
@@ -1591,7 +1595,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1591
1595
 
1592
1596
  {'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
1593
1597
 
1594
- {link && link}
1598
+ {/* Link */}
1599
+ {isDashboard && config.dataTable.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
1595
1600
 
1596
1601
  {subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
1597
1602
 
@@ -10,6 +10,7 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
10
10
 
11
11
  import topoJSON from '../data/county-map.json'
12
12
  import { formatPrefix } from 'd3'
13
+ import useMapLayers from '../hooks/useMapLayers'
13
14
 
14
15
  const sortById = (a, b) => {
15
16
  if (a.id < b.id) return -1
@@ -63,6 +64,10 @@ const CountyMap = props => {
63
64
 
64
65
  const [focus, setFocus] = useState({})
65
66
 
67
+ const pathGenerator = geoPath().projection(geoAlbersUsaTerritories())
68
+
69
+ const { featureArray } = useMapLayers(state, '', pathGenerator, false)
70
+
66
71
  useEffect(() => {
67
72
  if (containerEl) {
68
73
  if (containerEl.className.indexOf('loaded') === -1) {
@@ -216,6 +221,9 @@ const CountyMap = props => {
216
221
  }
217
222
  }
218
223
 
224
+ // todo: current item is a custom map layer
225
+ // if(currentItem === customMapLayer) show layer.tooltip
226
+
219
227
  let hoveredGeo
220
228
  let hoveredGeoIndex
221
229
  for (let i = 0; i < runtimeKeys.length; i++) {
@@ -309,6 +317,20 @@ const CountyMap = props => {
309
317
  context.stroke()
310
318
  }
311
319
 
320
+ // add in custom map layers
321
+ if (featureArray.length > 0) {
322
+ featureArray.map(layer => {
323
+ context.beginPath()
324
+ path(layer)
325
+ context.fillStyle = layer.properties.fill
326
+ context.globalAlpha = layer.properties['fill-opacity']
327
+ context.strokeStyle = layer.properties['stroke']
328
+ context.lineWidth = layer.properties['stroke-width']
329
+ context.fill()
330
+ context.stroke()
331
+ })
332
+ }
333
+
312
334
  if (state.general.type === 'us-geocode') {
313
335
  context.strokeStyle = 'black'
314
336
  const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
@@ -317,11 +339,14 @@ const CountyMap = props => {
317
339
  const pixelCoords = projection([data[key][state.columns.longitude.name], data[key][state.columns.latitude.name]])
318
340
 
319
341
  if (pixelCoords) {
320
- context.fillStyle = data[key] !== undefined ? applyLegendToRow(data[key])[0] : '#EEE'
321
- context.beginPath()
322
- context.arc(pixelCoords[0], pixelCoords[1], geoRadius, 0, 2 * Math.PI)
323
- context.fill()
324
- context.stroke()
342
+ const legendValues = data[key] !== undefined ? applyLegendToRow(data[key]) : false
343
+ if (legendValues) {
344
+ context.fillStyle = legendValues[0]
345
+ context.beginPath()
346
+ context.arc(pixelCoords[0], pixelCoords[1], geoRadius, 0, 2 * Math.PI)
347
+ context.fill()
348
+ context.stroke()
349
+ }
325
350
  }
326
351
  })
327
352
  }