@cdc/map 4.24.2 → 4.24.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 +33089 -32197
- package/examples/508.json +548 -0
- package/examples/default-county.json +0 -28
- package/examples/default-hex.json +110 -13
- package/examples/default-usa.json +69 -28
- package/examples/example-city-state.json +51 -17
- package/examples/hex-colors.json +507 -0
- package/examples/usa-special-class-legend.json +501 -0
- package/index.html +10 -9
- package/package.json +3 -3
- package/src/CdcMap.tsx +200 -125
- package/src/components/BubbleList.jsx +16 -6
- package/src/components/CityList.jsx +64 -12
- package/src/components/DataTable.jsx +7 -7
- package/src/components/EditorPanel/components/EditorPanel.tsx +1457 -1367
- package/src/components/EditorPanel/components/Error.tsx +12 -0
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +16 -1
- package/src/components/EditorPanel/components/Panel.PatternSettings-style.css +5 -0
- package/src/components/EditorPanel/components/Panel.PatternSettings.tsx +18 -1
- package/src/components/Legend/components/Legend.tsx +80 -15
- package/src/components/Legend/components/LegendItem.Hex.tsx +1 -1
- package/src/components/Legend/components/index.scss +31 -5
- package/src/components/UsaMap/components/HexIcon.tsx +41 -0
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +38 -19
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +11 -22
- package/src/components/UsaMap/components/UsaMap.County.tsx +7 -4
- package/src/components/UsaMap/components/UsaMap.Region.tsx +13 -38
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +5 -3
- package/src/components/UsaMap/components/UsaMap.State.tsx +77 -60
- package/src/components/UsaMap/helpers/patternSizes.tsx +5 -0
- package/src/components/WorldMap/components/WorldMap.jsx +20 -3
- package/src/data/initial-state.js +3 -0
- package/src/data/supported-geos.js +2 -1
- package/src/hooks/useMapLayers.tsx +2 -2
- package/src/scss/editor-panel.scss +4 -12
- package/src/scss/main.scss +3 -1
- package/src/scss/map.scss +1 -1
- package/src/types/MapConfig.ts +7 -0
package/src/CdcMap.tsx
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef, useCallback } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback, useId } from 'react'
|
|
2
2
|
import * as d3 from 'd3'
|
|
3
|
+
import Layout from '@cdc/core/components/Layout'
|
|
4
|
+
import Waiting from '@cdc/core/components/Waiting'
|
|
5
|
+
import Error from './components/EditorPanel/components/Error'
|
|
3
6
|
|
|
4
7
|
// IE11
|
|
5
8
|
import 'whatwg-fetch'
|
|
@@ -15,12 +18,13 @@ import 'react-tooltip/dist/react-tooltip.css'
|
|
|
15
18
|
// Helpers
|
|
16
19
|
import { publish } from '@cdc/core/helpers/events'
|
|
17
20
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
21
|
+
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
18
22
|
import Title from '@cdc/core/components/ui/Title'
|
|
19
23
|
|
|
20
24
|
// Data
|
|
21
25
|
import { countryCoordinates } from './data/country-coordinates'
|
|
22
26
|
import { supportedStates, supportedTerritories, supportedCountries, supportedCounties, supportedCities, supportedStatesFipsCodes, stateFipsToTwoDigit, supportedRegions } from './data/supported-geos'
|
|
23
|
-
import colorPalettes from '
|
|
27
|
+
import colorPalettes from '@cdc/core/data/colorPalettes'
|
|
24
28
|
import initialState from './data/initial-state'
|
|
25
29
|
|
|
26
30
|
// Assets
|
|
@@ -37,6 +41,7 @@ import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
|
37
41
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
38
42
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
39
43
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
44
|
+
import isDomainExternal from '@cdc/core/helpers/isDomainExternal'
|
|
40
45
|
import Loading from '@cdc/core/components/Loading'
|
|
41
46
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
42
47
|
import DataTable from '@cdc/core/components/DataTable' // Future: Lazy
|
|
@@ -53,6 +58,7 @@ import UsaMap from './components/UsaMap' // Future: Lazy
|
|
|
53
58
|
import WorldMap from './components/WorldMap' // Future: Lazy
|
|
54
59
|
import useTooltip from './hooks/useTooltip'
|
|
55
60
|
import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
61
|
+
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
56
62
|
|
|
57
63
|
// Data props
|
|
58
64
|
const stateKeys = Object.keys(supportedStates)
|
|
@@ -120,6 +126,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
120
126
|
const transform = new DataTransform()
|
|
121
127
|
const [state, setState] = useState({ ...initialState })
|
|
122
128
|
const [loading, setLoading] = useState(true)
|
|
129
|
+
const [displayPanel, setDisplayPanel] = useState(true)
|
|
123
130
|
const [currentViewport, setCurrentViewport] = useState()
|
|
124
131
|
const [runtimeFilters, setRuntimeFilters] = useState([])
|
|
125
132
|
const [runtimeLegend, setRuntimeLegend] = useState([])
|
|
@@ -132,12 +139,49 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
132
139
|
const [container, setContainer] = useState()
|
|
133
140
|
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
|
|
134
141
|
const [dimensions, setDimensions] = useState()
|
|
142
|
+
const [requiredColumns, setRequiredColumns] = useState(null) // Simple state so we know if we need more information before parsing the map
|
|
143
|
+
|
|
144
|
+
const legendRef = useRef(null)
|
|
145
|
+
const legendId = useId()
|
|
146
|
+
const tooltipId = useId()
|
|
135
147
|
|
|
136
148
|
const { changeFilterActive, handleSorting } = useFilters({ config: state, setConfig: setState })
|
|
137
149
|
let legendMemo = useRef(new Map())
|
|
150
|
+
let legendSpecialClassLastMemo = useRef(new Map())
|
|
138
151
|
let innerContainerRef = useRef()
|
|
139
152
|
|
|
140
|
-
if (isDebug) console.log('CdcMap state=', state) // eslint-disable-line
|
|
153
|
+
if (isDebug) console.log('CdcMap state=', state) // <eslint-disable-line></eslint-disable-line>
|
|
154
|
+
|
|
155
|
+
const columnsRequiredChecker = useCallback(() => {
|
|
156
|
+
let columnList = []
|
|
157
|
+
|
|
158
|
+
// Geo is always required
|
|
159
|
+
if ('' === state.columns.geo.name) {
|
|
160
|
+
columnList.push('Geography')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Primary is required if we're on a data map or a point map
|
|
164
|
+
if ('navigation' !== state.general.type && '' === state.columns.primary.name) {
|
|
165
|
+
columnList.push('Primary')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Navigate is required for navigation maps
|
|
169
|
+
if ('navigation' === state.general.type && ('' === state.columns.navigate.name || undefined === state.columns.navigate)) {
|
|
170
|
+
columnList.push('Navigation')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && '' === state.columns.latitude.name) {
|
|
174
|
+
columnList.push('Latitude')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (('us-geocode' === state.general.type || 'world-geocode' === state.general.type) && '' === state.columns.longitude.name) {
|
|
178
|
+
columnList.push('Longitude')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (columnList.length === 0) columnList = null
|
|
182
|
+
|
|
183
|
+
setRequiredColumns(columnList)
|
|
184
|
+
}, [state.columns, state.general.type])
|
|
141
185
|
|
|
142
186
|
useEffect(() => {
|
|
143
187
|
try {
|
|
@@ -303,6 +347,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
303
347
|
// eslint-disable-next-line
|
|
304
348
|
const generateRuntimeLegend = useCallback((obj, runtimeData, hash) => {
|
|
305
349
|
const newLegendMemo = new Map() // Reset memoization
|
|
350
|
+
const newLegendSpecialClassLastMemo = new Map() // Reset bin memoization
|
|
306
351
|
let primaryCol = obj.columns.primary.name,
|
|
307
352
|
isBubble = obj.general.type === 'bubble',
|
|
308
353
|
categoricalCol = obj.columns.categorical ? obj.columns.categorical.name : undefined,
|
|
@@ -524,6 +569,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
524
569
|
result = [...otherRows, ...specialRows]
|
|
525
570
|
}
|
|
526
571
|
|
|
572
|
+
const assignSpecialClassLastIndex = (value, key) => {
|
|
573
|
+
const newIndex = result.findIndex(d => d.bin === value)
|
|
574
|
+
newLegendSpecialClassLastMemo.set(key, newIndex)
|
|
575
|
+
}
|
|
576
|
+
newLegendMemo.forEach(assignSpecialClassLastIndex)
|
|
577
|
+
legendSpecialClassLastMemo.current = newLegendSpecialClassLastMemo
|
|
578
|
+
|
|
527
579
|
return result
|
|
528
580
|
}
|
|
529
581
|
|
|
@@ -775,6 +827,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
775
827
|
}
|
|
776
828
|
//-----------
|
|
777
829
|
|
|
830
|
+
const assignSpecialClassLastIndex = (value, key) => {
|
|
831
|
+
const newIndex = result.findIndex(d => d.bin === value)
|
|
832
|
+
newLegendSpecialClassLastMemo.set(key, newIndex)
|
|
833
|
+
}
|
|
834
|
+
newLegendMemo.forEach(assignSpecialClassLastIndex)
|
|
835
|
+
legendSpecialClassLastMemo.current = newLegendSpecialClassLastMemo
|
|
836
|
+
|
|
778
837
|
return result
|
|
779
838
|
})
|
|
780
839
|
|
|
@@ -786,7 +845,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
786
845
|
|
|
787
846
|
if (hash) filters.fromHash = hash
|
|
788
847
|
|
|
789
|
-
obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown }, idx) => {
|
|
848
|
+
obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown, setByQueryParameter }, idx) => {
|
|
790
849
|
let newFilter = runtimeFilters[idx]
|
|
791
850
|
|
|
792
851
|
const sortAsc = (a, b) => {
|
|
@@ -829,6 +888,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
829
888
|
newFilter.queryParameter = queryParameter
|
|
830
889
|
newFilter.labels = labels
|
|
831
890
|
newFilter.values = values
|
|
891
|
+
newFilter.setByQueryParameter = setByQueryParameter
|
|
832
892
|
handleSorting(newFilter)
|
|
833
893
|
newFilter.active = active ?? values[0] // Default to first found value
|
|
834
894
|
newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
|
|
@@ -988,7 +1048,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
988
1048
|
|
|
989
1049
|
if (legendMemo.current.has(hash)) {
|
|
990
1050
|
let idx = legendMemo.current.get(hash)
|
|
991
|
-
|
|
1051
|
+
let disabledIdx = idx
|
|
1052
|
+
|
|
1053
|
+
if (state.legend.showSpecialClassesLast) {
|
|
1054
|
+
disabledIdx = legendSpecialClassLastMemo.current.get(hash)
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
if (runtimeLegend[disabledIdx]?.disabled) return false
|
|
992
1058
|
|
|
993
1059
|
// changed to use bin prop to get color instead of idx
|
|
994
1060
|
// bc we re-order legend when showSpecialClassesLast is checked
|
|
@@ -1135,11 +1201,19 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1135
1201
|
|
|
1136
1202
|
if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
|
|
1137
1203
|
toolTipText.push(
|
|
1138
|
-
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
|
1139
|
-
<
|
|
1204
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions,jsx-a11y/anchor-is-valid
|
|
1205
|
+
<a
|
|
1206
|
+
href='#'
|
|
1207
|
+
className='navigation-link'
|
|
1208
|
+
key='modal-navigation-link'
|
|
1209
|
+
onClick={e => {
|
|
1210
|
+
e.preventDefault()
|
|
1211
|
+
navigationHandler(row[state.columns.navigate.name])
|
|
1212
|
+
}}
|
|
1213
|
+
>
|
|
1140
1214
|
{state.tooltips.linkLabel}
|
|
1141
|
-
<ExternalIcon className='inline-icon ml-1' />
|
|
1142
|
-
</
|
|
1215
|
+
{isDomainExternal(row[state.columns.navigate.name]) && <ExternalIcon className='inline-icon ml-1' />}
|
|
1216
|
+
</a>
|
|
1143
1217
|
)
|
|
1144
1218
|
}
|
|
1145
1219
|
}
|
|
@@ -1305,8 +1379,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1305
1379
|
data = transform.developerStandardize(data, state.dataDescription)
|
|
1306
1380
|
}
|
|
1307
1381
|
|
|
1308
|
-
console.log('data', data)
|
|
1309
|
-
|
|
1310
1382
|
setState({ ...state, runtimeDataUrl: dataUrlFinal, data })
|
|
1311
1383
|
}
|
|
1312
1384
|
}
|
|
@@ -1449,6 +1521,12 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1449
1521
|
filters = generateRuntimeFilters(state, hashFilters, runtimeFilters)
|
|
1450
1522
|
|
|
1451
1523
|
if (filters) {
|
|
1524
|
+
filters.forEach((filter, index) => {
|
|
1525
|
+
const queryStringFilterValue = getQueryStringFilterValue(filter)
|
|
1526
|
+
if (queryStringFilterValue) {
|
|
1527
|
+
filters[index].active = queryStringFilterValue
|
|
1528
|
+
}
|
|
1529
|
+
})
|
|
1452
1530
|
setRuntimeFilters(filters)
|
|
1453
1531
|
}
|
|
1454
1532
|
}
|
|
@@ -1508,13 +1586,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1508
1586
|
}
|
|
1509
1587
|
if (!table.label || table.label === '') table.label = 'Data Table'
|
|
1510
1588
|
|
|
1511
|
-
// Outer container classes
|
|
1512
|
-
let outerContainerClasses = ['cdc-open-viz-module', 'cdc-map-outer-container', currentViewport]
|
|
1513
|
-
|
|
1514
|
-
if (className) {
|
|
1515
|
-
outerContainerClasses.push(className)
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
1589
|
// Map container classes
|
|
1519
1590
|
let mapContainerClasses = ['map-container', state.legend.position, state.general.type, state.general.geoType, 'outline-none']
|
|
1520
1591
|
|
|
@@ -1570,7 +1641,8 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1570
1641
|
supportedTerritories,
|
|
1571
1642
|
titleCase,
|
|
1572
1643
|
type: general.type,
|
|
1573
|
-
viewport: currentViewport
|
|
1644
|
+
viewport: currentViewport,
|
|
1645
|
+
tooltipId
|
|
1574
1646
|
}
|
|
1575
1647
|
|
|
1576
1648
|
if (!mapProps.data || !state.data) return <></>
|
|
@@ -1582,21 +1654,21 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1582
1654
|
|
|
1583
1655
|
// 1) skip to legend
|
|
1584
1656
|
if (general.showSidebar) {
|
|
1585
|
-
tabbingID =
|
|
1657
|
+
tabbingID = legendId
|
|
1586
1658
|
}
|
|
1587
1659
|
|
|
1588
1660
|
// 2) skip to data table if it exists and not a navigation map
|
|
1589
1661
|
if (hasDataTable && !general.showSidebar) {
|
|
1590
|
-
tabbingID =
|
|
1662
|
+
tabbingID = `dataTableSection__${Date.now()}`
|
|
1591
1663
|
}
|
|
1592
1664
|
|
|
1593
1665
|
// 3) if it's a navigation map skip to the dropdown.
|
|
1594
1666
|
if (state.general.type === 'navigation') {
|
|
1595
|
-
tabbingID =
|
|
1667
|
+
tabbingID = `dropdown-${Date.now()}`
|
|
1596
1668
|
}
|
|
1597
1669
|
|
|
1598
1670
|
// 4) handle other options
|
|
1599
|
-
return tabbingID || '
|
|
1671
|
+
return tabbingID || '!'
|
|
1600
1672
|
}
|
|
1601
1673
|
|
|
1602
1674
|
const tabId = handleMapTabbing()
|
|
@@ -1610,109 +1682,112 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1610
1682
|
|
|
1611
1683
|
return (
|
|
1612
1684
|
<ConfigContext.Provider value={mapProps}>
|
|
1613
|
-
<
|
|
1614
|
-
{isEditor && <EditorPanel />}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
</a>
|
|
1630
|
-
{general.introText && <section className='introText'>{parse(general.introText)}</section>}
|
|
1631
|
-
|
|
1632
|
-
{/* prettier-ignore */}
|
|
1633
|
-
{state?.filters?.length > 0 && <Filters config={state} setConfig={setState} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
|
|
1634
|
-
|
|
1635
|
-
<div
|
|
1636
|
-
role='button'
|
|
1637
|
-
tabIndex='0'
|
|
1638
|
-
className={mapContainerClasses.join(' ')}
|
|
1639
|
-
onClick={e => closeModal(e)}
|
|
1640
|
-
onKeyDown={e => {
|
|
1641
|
-
if (e.keyCode === 13) {
|
|
1642
|
-
closeModal(e)
|
|
1643
|
-
}
|
|
1644
|
-
}}
|
|
1645
|
-
>
|
|
1646
|
-
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
|
1647
|
-
<section className='outline-none geography-container' ref={mapSvg} tabIndex='0' style={{ width: '100%' }}>
|
|
1648
|
-
{currentViewport && (
|
|
1649
|
-
<>
|
|
1650
|
-
{modal && <Modal />}
|
|
1651
|
-
{'single-state' === geoType && <UsaMap.SingleState />}
|
|
1652
|
-
{'us' === geoType && 'us-geocode' !== state.general.type && <UsaMap.State />}
|
|
1653
|
-
{'us-region' === geoType && <UsaMap.Region />}
|
|
1654
|
-
{'us-county' === geoType && <UsaMap.County />}
|
|
1655
|
-
{'world' === geoType && <WorldMap />}
|
|
1656
|
-
{'data' === general.type && logo && <img src={logo} alt='' className='map-logo' />}
|
|
1657
|
-
</>
|
|
1658
|
-
)}
|
|
1659
|
-
</section>
|
|
1660
|
-
|
|
1661
|
-
{general.showSidebar && 'navigation' !== general.type && <Legend />}
|
|
1662
|
-
</div>
|
|
1663
|
-
|
|
1664
|
-
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1665
|
-
|
|
1666
|
-
{/* Link */}
|
|
1667
|
-
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
|
|
1668
|
-
|
|
1669
|
-
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1670
|
-
|
|
1671
|
-
<MediaControls.Section classes={['download-buttons']}>
|
|
1672
|
-
{state.general.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
|
|
1673
|
-
{state.general.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
|
|
1674
|
-
</MediaControls.Section>
|
|
1675
|
-
|
|
1676
|
-
{state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1677
|
-
<DataTable
|
|
1678
|
-
config={state}
|
|
1679
|
-
rawData={state.data}
|
|
1680
|
-
navigationHandler={navigationHandler}
|
|
1681
|
-
expandDataTable={general.expandDataTable ? general.expandDataTable : table.expanded ? table.expanded : false}
|
|
1682
|
-
headerColor={general.headerColor}
|
|
1683
|
-
columns={state.columns}
|
|
1684
|
-
showDownloadButton={general.showDownloadButton}
|
|
1685
|
-
showFullGeoNameInCSV={table.showFullGeoNameInCSV}
|
|
1686
|
-
runtimeLegend={runtimeLegend}
|
|
1687
|
-
runtimeData={runtimeData}
|
|
1688
|
-
displayDataAsText={displayDataAsText}
|
|
1689
|
-
displayGeoName={displayGeoName}
|
|
1690
|
-
applyLegendToRow={applyLegendToRow}
|
|
1691
|
-
tableTitle={table.label}
|
|
1692
|
-
indexTitle={table.indexLabel}
|
|
1693
|
-
vizTitle={general.title}
|
|
1694
|
-
viewport={currentViewport}
|
|
1695
|
-
formatLegendLocation={formatLegendLocation}
|
|
1696
|
-
setFilteredCountryCode={setFilteredCountryCode}
|
|
1697
|
-
tabbingId={tabId}
|
|
1698
|
-
showDownloadImgButton={state.general.showDownloadImgButton}
|
|
1699
|
-
showDownloadPdfButton={state.general.showDownloadPdfButton}
|
|
1700
|
-
innerContainerRef={innerContainerRef}
|
|
1701
|
-
outerContainerRef={outerContainerRef}
|
|
1702
|
-
imageRef={imageId}
|
|
1703
|
-
isDebug={isDebug}
|
|
1704
|
-
wrapColumns={table.wrapColumns}
|
|
1685
|
+
<Layout.VisualizationWrapper config={state} isEditor={isEditor} ref={outerContainerRef} imageId={imageId} showEditorPanel={state.showEditorPanel}>
|
|
1686
|
+
{isEditor && <EditorPanel columnsRequiredChecker={columnsRequiredChecker} />}
|
|
1687
|
+
<Layout.Responsive isEditor={isEditor}>
|
|
1688
|
+
{state?.runtime?.editorErrorMessage.length > 0 && <Error state={state} />}
|
|
1689
|
+
{requiredColumns && <Waiting requiredColumns={requiredColumns} className={displayPanel ? `waiting` : `waiting collapsed`} />}
|
|
1690
|
+
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
1691
|
+
<section className={`cove-component__content cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
|
|
1692
|
+
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
|
|
1693
|
+
<ReactTooltip id={`tooltip__${tooltipId}`} float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip tooltip-test' : 'tooltip tooltip-test'}`} style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }} />
|
|
1694
|
+
)}
|
|
1695
|
+
{/* prettier-ignore */}
|
|
1696
|
+
<Title
|
|
1697
|
+
title={title}
|
|
1698
|
+
superTitle={general.superTitle}
|
|
1699
|
+
config={config}
|
|
1700
|
+
classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${general.headerColor}`]}
|
|
1705
1701
|
/>
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1702
|
+
<SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
|
|
1703
|
+
|
|
1704
|
+
{general.introText && <section className='introText'>{parse(general.introText)}</section>}
|
|
1705
|
+
|
|
1706
|
+
{/* prettier-ignore */}
|
|
1707
|
+
{state?.filters?.length > 0 && <Filters config={state} setConfig={setState} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
|
|
1708
|
+
|
|
1709
|
+
<div
|
|
1710
|
+
role='region'
|
|
1711
|
+
tabIndex='0'
|
|
1712
|
+
className={mapContainerClasses.join(' ')}
|
|
1713
|
+
onClick={e => closeModal(e)}
|
|
1714
|
+
onKeyDown={e => {
|
|
1715
|
+
if (e.keyCode === 13) {
|
|
1716
|
+
closeModal(e)
|
|
1717
|
+
}
|
|
1718
|
+
}}
|
|
1719
|
+
>
|
|
1720
|
+
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
|
1721
|
+
<section className='outline-none geography-container' ref={mapSvg} tabIndex='0' style={{ width: '100%' }}>
|
|
1722
|
+
{currentViewport && (
|
|
1723
|
+
<>
|
|
1724
|
+
{modal && <Modal />}
|
|
1725
|
+
{'single-state' === geoType && <UsaMap.SingleState />}
|
|
1726
|
+
{'us' === geoType && 'us-geocode' !== state.general.type && <UsaMap.State />}
|
|
1727
|
+
{'us-region' === geoType && <UsaMap.Region />}
|
|
1728
|
+
{'us-county' === geoType && <UsaMap.County />}
|
|
1729
|
+
{'world' === geoType && <WorldMap />}
|
|
1730
|
+
{'data' === general.type && logo && <img src={logo} alt='' className='map-logo' />}
|
|
1731
|
+
</>
|
|
1732
|
+
)}
|
|
1733
|
+
</section>
|
|
1734
|
+
|
|
1735
|
+
{general.showSidebar && 'navigation' !== general.type && <Legend ref={legendRef} skipId={tabId} />}
|
|
1736
|
+
</div>
|
|
1737
|
+
|
|
1738
|
+
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
1739
|
+
|
|
1740
|
+
{/* Link */}
|
|
1741
|
+
{isDashboard && config.table?.forceDisplay && config.table.showDataTableLink ? tableLink : link && link}
|
|
1742
|
+
|
|
1743
|
+
{subtext.length > 0 && <p className='subtext'>{parse(subtext)}</p>}
|
|
1744
|
+
|
|
1745
|
+
<MediaControls.Section classes={['download-buttons']}>
|
|
1746
|
+
{state.general.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={state} elementToCapture={imageId} />}
|
|
1747
|
+
{state.general.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={state} elementToCapture={imageId} />}
|
|
1748
|
+
</MediaControls.Section>
|
|
1749
|
+
|
|
1750
|
+
{state.runtime.editorErrorMessage.length === 0 && true === table.forceDisplay && general.type !== 'navigation' && false === loading && (
|
|
1751
|
+
<DataTable
|
|
1752
|
+
config={state}
|
|
1753
|
+
rawData={state.data}
|
|
1754
|
+
navigationHandler={navigationHandler}
|
|
1755
|
+
expandDataTable={table.expanded}
|
|
1756
|
+
headerColor={general.headerColor}
|
|
1757
|
+
columns={state.columns}
|
|
1758
|
+
showDownloadButton={general.showDownloadButton}
|
|
1759
|
+
showFullGeoNameInCSV={table.showFullGeoNameInCSV}
|
|
1760
|
+
runtimeLegend={runtimeLegend}
|
|
1761
|
+
runtimeData={runtimeData}
|
|
1762
|
+
displayDataAsText={displayDataAsText}
|
|
1763
|
+
displayGeoName={displayGeoName}
|
|
1764
|
+
applyLegendToRow={applyLegendToRow}
|
|
1765
|
+
tableTitle={table.label}
|
|
1766
|
+
indexTitle={table.indexLabel}
|
|
1767
|
+
vizTitle={general.title}
|
|
1768
|
+
viewport={currentViewport}
|
|
1769
|
+
formatLegendLocation={formatLegendLocation}
|
|
1770
|
+
setFilteredCountryCode={setFilteredCountryCode}
|
|
1771
|
+
tabbingId={tabId}
|
|
1772
|
+
showDownloadImgButton={state.general.showDownloadImgButton}
|
|
1773
|
+
showDownloadPdfButton={state.general.showDownloadPdfButton}
|
|
1774
|
+
innerContainerRef={innerContainerRef}
|
|
1775
|
+
outerContainerRef={outerContainerRef}
|
|
1776
|
+
imageRef={imageId}
|
|
1777
|
+
isDebug={isDebug}
|
|
1778
|
+
wrapColumns={table.wrapColumns}
|
|
1779
|
+
/>
|
|
1780
|
+
)}
|
|
1781
|
+
|
|
1782
|
+
{general.footnotes && <section className='footnotes'>{parse(general.footnotes)}</section>}
|
|
1783
|
+
</section>
|
|
1784
|
+
)}
|
|
1785
|
+
|
|
1786
|
+
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
1787
|
+
{accessibleStatus}
|
|
1788
|
+
</div>
|
|
1789
|
+
</Layout.Responsive>
|
|
1790
|
+
</Layout.VisualizationWrapper>
|
|
1716
1791
|
</ConfigContext.Provider>
|
|
1717
1792
|
)
|
|
1718
1793
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import React, { useEffect } from 'react'
|
|
1
|
+
import React, { useEffect, useContext } from 'react'
|
|
2
2
|
import { scaleLinear } from 'd3-scale'
|
|
3
3
|
import { countryCoordinates } from '../data/country-coordinates'
|
|
4
4
|
import stateCoordinates from '../data/state-coordinates'
|
|
5
|
+
import ConfigContext from './../../src/context'
|
|
5
6
|
|
|
6
7
|
export const BubbleList = ({ data: dataImport, state, projection, applyLegendToRow, applyTooltipsToGeo, handleCircleClick, runtimeData, displayGeoName }) => {
|
|
7
8
|
const maxDataValue = Math.max(...dataImport.map(d => d[state.columns.primary.name]))
|
|
9
|
+
const { tooltipId } = useContext(ConfigContext)
|
|
8
10
|
const hasBubblesWithZeroOnMap = state.visual.showBubbleZeros ? 0 : 1
|
|
9
11
|
// sort runtime data. Smaller bubbles should appear on top.
|
|
10
12
|
const sortedRuntimeData = Object.values(runtimeData).sort((a, b) => (a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1))
|
|
@@ -39,6 +41,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
39
41
|
const circle = (
|
|
40
42
|
<>
|
|
41
43
|
<circle
|
|
44
|
+
tabIndex={-1}
|
|
42
45
|
key={`circle-${countryName.replace(' ', '')}`}
|
|
43
46
|
className={`bubble country--${countryName}`}
|
|
44
47
|
cx={Number(projection(coordinates[1], coordinates[0])[0]) || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -61,12 +64,13 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
61
64
|
}}
|
|
62
65
|
transform={transform}
|
|
63
66
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
64
|
-
data-tooltip-id=
|
|
67
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
65
68
|
data-tooltip-html={toolTip}
|
|
66
69
|
/>
|
|
67
70
|
|
|
68
71
|
{state.visual.extraBubbleBorder && (
|
|
69
72
|
<circle
|
|
73
|
+
tabIndex={-1}
|
|
70
74
|
key={`circle-${countryName.replace(' ', '')}`}
|
|
71
75
|
className='bubble'
|
|
72
76
|
cx={Number(projection(coordinates[1], coordinates[0])[0]) || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -88,14 +92,18 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
88
92
|
}}
|
|
89
93
|
transform={transform}
|
|
90
94
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
91
|
-
data-tooltip-id=
|
|
95
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
92
96
|
data-tooltip-html={toolTip}
|
|
93
97
|
/>
|
|
94
98
|
)}
|
|
95
99
|
</>
|
|
96
100
|
)
|
|
97
101
|
|
|
98
|
-
return
|
|
102
|
+
return (
|
|
103
|
+
<g key={`group-${countryName.replace(' ', '')}`} tabIndex={-1}>
|
|
104
|
+
{circle}
|
|
105
|
+
</g>
|
|
106
|
+
)
|
|
99
107
|
})
|
|
100
108
|
return countries
|
|
101
109
|
}
|
|
@@ -132,6 +140,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
132
140
|
const circle = (
|
|
133
141
|
<>
|
|
134
142
|
<circle
|
|
143
|
+
tabIndex={-1}
|
|
135
144
|
key={`circle-${stateName.replace(' ', '')}`}
|
|
136
145
|
className='bubble'
|
|
137
146
|
cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -154,11 +163,12 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
154
163
|
}}
|
|
155
164
|
transform={transform}
|
|
156
165
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
157
|
-
data-tooltip-id=
|
|
166
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
158
167
|
data-tooltip-html={toolTip}
|
|
159
168
|
/>
|
|
160
169
|
{state.visual.extraBubbleBorder && (
|
|
161
170
|
<circle
|
|
171
|
+
tabIndex={-1}
|
|
162
172
|
key={`circle-${stateName.replace(' ', '')}`}
|
|
163
173
|
className='bubble'
|
|
164
174
|
cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -181,7 +191,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
181
191
|
}}
|
|
182
192
|
transform={transform}
|
|
183
193
|
style={{ transition: 'all .25s ease-in-out', cursor: 'pointer' }}
|
|
184
|
-
data-tooltip-id=
|
|
194
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
185
195
|
data-tooltip-html={toolTip}
|
|
186
196
|
/>
|
|
187
197
|
)}
|