@cdc/map 4.24.2 → 4.24.3
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 +27001 -26354
- 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/usa-special-class-legend.json +501 -0
- package/index.html +4 -3
- package/package.json +3 -3
- package/src/CdcMap.tsx +60 -21
- package/src/components/BubbleList.jsx +9 -1
- package/src/components/CityList.jsx +60 -8
- package/src/components/DataTable.jsx +7 -7
- package/src/components/EditorPanel/components/EditorPanel.tsx +180 -44
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +16 -1
- package/src/components/Legend/components/Legend.tsx +67 -13
- 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 +10 -21
- package/src/components/UsaMap/components/UsaMap.Region.tsx +11 -37
- package/src/components/UsaMap/components/UsaMap.State.tsx +61 -59
- package/src/components/UsaMap/helpers/patternSizes.tsx +5 -0
- package/src/components/WorldMap/components/WorldMap.jsx +3 -1
- package/src/data/initial-state.js +3 -0
- package/src/data/supported-geos.js +1 -1
- package/src/scss/editor-panel.scss +3 -0
- package/src/scss/main.scss +2 -1
- package/src/types/MapConfig.ts +7 -0
package/src/CdcMap.tsx
CHANGED
|
@@ -15,12 +15,13 @@ import 'react-tooltip/dist/react-tooltip.css'
|
|
|
15
15
|
// Helpers
|
|
16
16
|
import { publish } from '@cdc/core/helpers/events'
|
|
17
17
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
18
|
+
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
18
19
|
import Title from '@cdc/core/components/ui/Title'
|
|
19
20
|
|
|
20
21
|
// Data
|
|
21
22
|
import { countryCoordinates } from './data/country-coordinates'
|
|
22
23
|
import { supportedStates, supportedTerritories, supportedCountries, supportedCounties, supportedCities, supportedStatesFipsCodes, stateFipsToTwoDigit, supportedRegions } from './data/supported-geos'
|
|
23
|
-
import colorPalettes from '
|
|
24
|
+
import colorPalettes from '@cdc/core/data/colorPalettes'
|
|
24
25
|
import initialState from './data/initial-state'
|
|
25
26
|
|
|
26
27
|
// Assets
|
|
@@ -37,6 +38,7 @@ import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
|
37
38
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
38
39
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
39
40
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
41
|
+
import isDomainExternal from '@cdc/core/helpers/isDomainExternal'
|
|
40
42
|
import Loading from '@cdc/core/components/Loading'
|
|
41
43
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
42
44
|
import DataTable from '@cdc/core/components/DataTable' // Future: Lazy
|
|
@@ -53,6 +55,7 @@ import UsaMap from './components/UsaMap' // Future: Lazy
|
|
|
53
55
|
import WorldMap from './components/WorldMap' // Future: Lazy
|
|
54
56
|
import useTooltip from './hooks/useTooltip'
|
|
55
57
|
import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
58
|
+
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
56
59
|
|
|
57
60
|
// Data props
|
|
58
61
|
const stateKeys = Object.keys(supportedStates)
|
|
@@ -132,9 +135,11 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
132
135
|
const [container, setContainer] = useState()
|
|
133
136
|
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`) // eslint-disable-line
|
|
134
137
|
const [dimensions, setDimensions] = useState()
|
|
138
|
+
const legendRef = useRef(null)
|
|
135
139
|
|
|
136
140
|
const { changeFilterActive, handleSorting } = useFilters({ config: state, setConfig: setState })
|
|
137
141
|
let legendMemo = useRef(new Map())
|
|
142
|
+
let legendSpecialClassLastMemo = useRef(new Map())
|
|
138
143
|
let innerContainerRef = useRef()
|
|
139
144
|
|
|
140
145
|
if (isDebug) console.log('CdcMap state=', state) // eslint-disable-line
|
|
@@ -303,6 +308,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
303
308
|
// eslint-disable-next-line
|
|
304
309
|
const generateRuntimeLegend = useCallback((obj, runtimeData, hash) => {
|
|
305
310
|
const newLegendMemo = new Map() // Reset memoization
|
|
311
|
+
const newLegendSpecialClassLastMemo = new Map() // Reset bin memoization
|
|
306
312
|
let primaryCol = obj.columns.primary.name,
|
|
307
313
|
isBubble = obj.general.type === 'bubble',
|
|
308
314
|
categoricalCol = obj.columns.categorical ? obj.columns.categorical.name : undefined,
|
|
@@ -524,6 +530,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
524
530
|
result = [...otherRows, ...specialRows]
|
|
525
531
|
}
|
|
526
532
|
|
|
533
|
+
const assignSpecialClassLastIndex = (value, key) => {
|
|
534
|
+
const newIndex = result.findIndex(d => d.bin === value)
|
|
535
|
+
newLegendSpecialClassLastMemo.set(key, newIndex)
|
|
536
|
+
}
|
|
537
|
+
newLegendMemo.forEach(assignSpecialClassLastIndex)
|
|
538
|
+
legendSpecialClassLastMemo.current = newLegendSpecialClassLastMemo
|
|
539
|
+
|
|
527
540
|
return result
|
|
528
541
|
}
|
|
529
542
|
|
|
@@ -775,6 +788,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
775
788
|
}
|
|
776
789
|
//-----------
|
|
777
790
|
|
|
791
|
+
const assignSpecialClassLastIndex = (value, key) => {
|
|
792
|
+
const newIndex = result.findIndex(d => d.bin === value)
|
|
793
|
+
newLegendSpecialClassLastMemo.set(key, newIndex)
|
|
794
|
+
}
|
|
795
|
+
newLegendMemo.forEach(assignSpecialClassLastIndex)
|
|
796
|
+
legendSpecialClassLastMemo.current = newLegendSpecialClassLastMemo
|
|
797
|
+
|
|
778
798
|
return result
|
|
779
799
|
})
|
|
780
800
|
|
|
@@ -786,7 +806,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
786
806
|
|
|
787
807
|
if (hash) filters.fromHash = hash
|
|
788
808
|
|
|
789
|
-
obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown }, idx) => {
|
|
809
|
+
obj?.filters.forEach(({ columnName, label, labels, queryParameter, orderedValues, active, values, type, showDropdown, setByQueryParameter }, idx) => {
|
|
790
810
|
let newFilter = runtimeFilters[idx]
|
|
791
811
|
|
|
792
812
|
const sortAsc = (a, b) => {
|
|
@@ -829,6 +849,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
829
849
|
newFilter.queryParameter = queryParameter
|
|
830
850
|
newFilter.labels = labels
|
|
831
851
|
newFilter.values = values
|
|
852
|
+
newFilter.setByQueryParameter = setByQueryParameter
|
|
832
853
|
handleSorting(newFilter)
|
|
833
854
|
newFilter.active = active ?? values[0] // Default to first found value
|
|
834
855
|
newFilter.filterStyle = obj.filters[idx].filterStyle ? obj.filters[idx].filterStyle : 'dropdown'
|
|
@@ -988,7 +1009,13 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
988
1009
|
|
|
989
1010
|
if (legendMemo.current.has(hash)) {
|
|
990
1011
|
let idx = legendMemo.current.get(hash)
|
|
991
|
-
|
|
1012
|
+
let disabledIdx = idx
|
|
1013
|
+
|
|
1014
|
+
if (state.legend.showSpecialClassesLast) {
|
|
1015
|
+
disabledIdx = legendSpecialClassLastMemo.current.get(hash)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
if (runtimeLegend[disabledIdx]?.disabled) return false
|
|
992
1019
|
|
|
993
1020
|
// changed to use bin prop to get color instead of idx
|
|
994
1021
|
// bc we re-order legend when showSpecialClassesLast is checked
|
|
@@ -1135,11 +1162,19 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1135
1162
|
|
|
1136
1163
|
if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
|
|
1137
1164
|
toolTipText.push(
|
|
1138
|
-
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
|
1139
|
-
<
|
|
1165
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions,jsx-a11y/anchor-is-valid
|
|
1166
|
+
<a
|
|
1167
|
+
href='#'
|
|
1168
|
+
className='navigation-link'
|
|
1169
|
+
key='modal-navigation-link'
|
|
1170
|
+
onClick={e => {
|
|
1171
|
+
e.preventDefault()
|
|
1172
|
+
navigationHandler(row[state.columns.navigate.name])
|
|
1173
|
+
}}
|
|
1174
|
+
>
|
|
1140
1175
|
{state.tooltips.linkLabel}
|
|
1141
|
-
<ExternalIcon className='inline-icon ml-1' />
|
|
1142
|
-
</
|
|
1176
|
+
{isDomainExternal(row[state.columns.navigate.name]) && <ExternalIcon className='inline-icon ml-1' />}
|
|
1177
|
+
</a>
|
|
1143
1178
|
)
|
|
1144
1179
|
}
|
|
1145
1180
|
}
|
|
@@ -1305,8 +1340,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1305
1340
|
data = transform.developerStandardize(data, state.dataDescription)
|
|
1306
1341
|
}
|
|
1307
1342
|
|
|
1308
|
-
console.log('data', data)
|
|
1309
|
-
|
|
1310
1343
|
setState({ ...state, runtimeDataUrl: dataUrlFinal, data })
|
|
1311
1344
|
}
|
|
1312
1345
|
}
|
|
@@ -1449,6 +1482,12 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1449
1482
|
filters = generateRuntimeFilters(state, hashFilters, runtimeFilters)
|
|
1450
1483
|
|
|
1451
1484
|
if (filters) {
|
|
1485
|
+
filters.forEach((filter, index) => {
|
|
1486
|
+
const queryStringFilterValue = getQueryStringFilterValue(filter)
|
|
1487
|
+
if (queryStringFilterValue) {
|
|
1488
|
+
filters[index].active = queryStringFilterValue
|
|
1489
|
+
}
|
|
1490
|
+
})
|
|
1452
1491
|
setRuntimeFilters(filters)
|
|
1453
1492
|
}
|
|
1454
1493
|
}
|
|
@@ -1582,21 +1621,21 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1582
1621
|
|
|
1583
1622
|
// 1) skip to legend
|
|
1584
1623
|
if (general.showSidebar) {
|
|
1585
|
-
tabbingID = '
|
|
1624
|
+
tabbingID = 'legend'
|
|
1586
1625
|
}
|
|
1587
1626
|
|
|
1588
1627
|
// 2) skip to data table if it exists and not a navigation map
|
|
1589
1628
|
if (hasDataTable && !general.showSidebar) {
|
|
1590
|
-
tabbingID =
|
|
1629
|
+
tabbingID = `dataTableSection__${Date.now()}`
|
|
1591
1630
|
}
|
|
1592
1631
|
|
|
1593
1632
|
// 3) if it's a navigation map skip to the dropdown.
|
|
1594
1633
|
if (state.general.type === 'navigation') {
|
|
1595
|
-
tabbingID =
|
|
1634
|
+
tabbingID = `dropdown-${Date.now()}`
|
|
1596
1635
|
}
|
|
1597
1636
|
|
|
1598
1637
|
// 4) handle other options
|
|
1599
|
-
return tabbingID || '
|
|
1638
|
+
return tabbingID || '!'
|
|
1600
1639
|
}
|
|
1601
1640
|
|
|
1602
1641
|
const tabId = handleMapTabbing()
|
|
@@ -1614,9 +1653,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1614
1653
|
{isEditor && <EditorPanel />}
|
|
1615
1654
|
{!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
|
|
1616
1655
|
<section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
|
|
1617
|
-
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
|
|
1618
|
-
<ReactTooltip id='tooltip' float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}`} style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }} />
|
|
1619
|
-
)}
|
|
1620
1656
|
{/* prettier-ignore */}
|
|
1621
1657
|
<Title
|
|
1622
1658
|
title={title}
|
|
@@ -1624,16 +1660,15 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1624
1660
|
config={config}
|
|
1625
1661
|
classes={['map-title', general.showTitle === true ? 'visible' : 'hidden', `${general.headerColor}`]}
|
|
1626
1662
|
/>
|
|
1627
|
-
<
|
|
1628
|
-
|
|
1629
|
-
</a>
|
|
1663
|
+
<SkipTo skipId={tabId} skipMessage='Skip Over Map Container' />
|
|
1664
|
+
|
|
1630
1665
|
{general.introText && <section className='introText'>{parse(general.introText)}</section>}
|
|
1631
1666
|
|
|
1632
1667
|
{/* prettier-ignore */}
|
|
1633
1668
|
{state?.filters?.length > 0 && <Filters config={state} setConfig={setState} filteredData={runtimeFilters} setFilteredData={setRuntimeFilters} dimensions={dimensions} />}
|
|
1634
1669
|
|
|
1635
1670
|
<div
|
|
1636
|
-
role='
|
|
1671
|
+
role='region'
|
|
1637
1672
|
tabIndex='0'
|
|
1638
1673
|
className={mapContainerClasses.join(' ')}
|
|
1639
1674
|
onClick={e => closeModal(e)}
|
|
@@ -1658,7 +1693,7 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1658
1693
|
)}
|
|
1659
1694
|
</section>
|
|
1660
1695
|
|
|
1661
|
-
{general.showSidebar && 'navigation' !== general.type && <Legend />}
|
|
1696
|
+
{general.showSidebar && 'navigation' !== general.type && <Legend ref={legendRef} />}
|
|
1662
1697
|
</div>
|
|
1663
1698
|
|
|
1664
1699
|
{'navigation' === general.type && <NavigationMenu mapTabbingID={tabId} displayGeoName={displayGeoName} data={runtimeData} options={general} columns={state.columns} navigationHandler={val => navigationHandler(val)} />}
|
|
@@ -1712,6 +1747,10 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
|
|
|
1712
1747
|
<div aria-live='assertive' className='cdcdataviz-sr-only'>
|
|
1713
1748
|
{accessibleStatus}
|
|
1714
1749
|
</div>
|
|
1750
|
+
|
|
1751
|
+
{!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
|
|
1752
|
+
<ReactTooltip id='tooltip' float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}`} style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }} />
|
|
1753
|
+
)}
|
|
1715
1754
|
</div>
|
|
1716
1755
|
</ConfigContext.Provider>
|
|
1717
1756
|
)
|
|
@@ -39,6 +39,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
39
39
|
const circle = (
|
|
40
40
|
<>
|
|
41
41
|
<circle
|
|
42
|
+
tabIndex={-1}
|
|
42
43
|
key={`circle-${countryName.replace(' ', '')}`}
|
|
43
44
|
className={`bubble country--${countryName}`}
|
|
44
45
|
cx={Number(projection(coordinates[1], coordinates[0])[0]) || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -67,6 +68,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
67
68
|
|
|
68
69
|
{state.visual.extraBubbleBorder && (
|
|
69
70
|
<circle
|
|
71
|
+
tabIndex={-1}
|
|
70
72
|
key={`circle-${countryName.replace(' ', '')}`}
|
|
71
73
|
className='bubble'
|
|
72
74
|
cx={Number(projection(coordinates[1], coordinates[0])[0]) || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -95,7 +97,11 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
95
97
|
</>
|
|
96
98
|
)
|
|
97
99
|
|
|
98
|
-
return
|
|
100
|
+
return (
|
|
101
|
+
<g key={`group-${countryName.replace(' ', '')}`} tabIndex={-1}>
|
|
102
|
+
{circle}
|
|
103
|
+
</g>
|
|
104
|
+
)
|
|
99
105
|
})
|
|
100
106
|
return countries
|
|
101
107
|
}
|
|
@@ -132,6 +138,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
132
138
|
const circle = (
|
|
133
139
|
<>
|
|
134
140
|
<circle
|
|
141
|
+
tabIndex={-1}
|
|
135
142
|
key={`circle-${stateName.replace(' ', '')}`}
|
|
136
143
|
className='bubble'
|
|
137
144
|
cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -159,6 +166,7 @@ export const BubbleList = ({ data: dataImport, state, projection, applyLegendToR
|
|
|
159
166
|
/>
|
|
160
167
|
{state.visual.extraBubbleBorder && (
|
|
161
168
|
<circle
|
|
169
|
+
tabIndex={-1}
|
|
162
170
|
key={`circle-${stateName.replace(' ', '')}`}
|
|
163
171
|
className='bubble'
|
|
164
172
|
cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
|
|
@@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'
|
|
|
3
3
|
import { jsx } from '@emotion/react'
|
|
4
4
|
import { supportedCities } from '../data/supported-geos'
|
|
5
5
|
import { scaleLinear } from 'd3-scale'
|
|
6
|
+
import { GlyphStar, GlyphTriangle, GlyphDiamond, GlyphSquare, GlyphCircle } from '@visx/glyph'
|
|
6
7
|
|
|
7
8
|
const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeoName, applyLegendToRow, projection, titleCase, setSharedFilterValue, isFilterValueSupported }) => {
|
|
8
9
|
const [citiesData, setCitiesData] = useState({})
|
|
@@ -60,18 +61,18 @@ const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeo
|
|
|
60
61
|
fillOpacity: state.general.type === 'bubble' ? 0.4 : 1
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
const circle = <circle cx={0} cy={0} r={state.general.type === 'bubble' ? size(geoData[state.columns.primary.name]) : radius} title='Click for more information' onClick={() => geoClickHandler(cityDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} {...additionalProps} />
|
|
64
|
-
|
|
65
64
|
const pin = (
|
|
66
65
|
<path
|
|
67
66
|
className='marker'
|
|
68
67
|
d='M0,0l-8.8-17.7C-12.1-24.3-7.4-32,0-32h0c7.4,0,12.1,7.7,8.8,14.3L0,0z'
|
|
69
|
-
title='
|
|
68
|
+
title='Select for more information'
|
|
70
69
|
onClick={() => geoClickHandler(cityDisplayName, geoData)}
|
|
71
|
-
strokeWidth={2}
|
|
72
|
-
stroke={'black'}
|
|
73
70
|
data-tooltip-id='tooltip'
|
|
74
71
|
data-tooltip-html={toolTip}
|
|
72
|
+
transform={`scale(${radius / 9})`}
|
|
73
|
+
stroke={state.general.geoBorderColor === 'sameAsBackground' ? '#ffffff' : '#000000'}
|
|
74
|
+
strokeWidth={'2px'}
|
|
75
|
+
tabIndex='-1'
|
|
75
76
|
{...additionalProps}
|
|
76
77
|
/>
|
|
77
78
|
)
|
|
@@ -114,10 +115,61 @@ const CityList = ({ data, state, geoClickHandler, applyTooltipsToGeo, displayGeo
|
|
|
114
115
|
styles.cursor = 'pointer'
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
const shapeProps = {
|
|
119
|
+
onClick: () => geoClickHandler(cityDisplayName, geoData),
|
|
120
|
+
size: state.general.type === 'bubble' ? size(geoData[state.columns.primary.name]) : radius * 30,
|
|
121
|
+
title: 'Select for more information',
|
|
122
|
+
'data-tooltip-id': 'tooltip',
|
|
123
|
+
'data-tooltip-html': toolTip,
|
|
124
|
+
stroke: state.general.geoBorderColor === 'sameAsBackground' ? '#ffffff' : '#000000',
|
|
125
|
+
strokeWidth: '2px',
|
|
126
|
+
tabIndex: -1,
|
|
127
|
+
...additionalProps
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const cityStyleShapes = {
|
|
131
|
+
circle: <GlyphCircle {...shapeProps} />,
|
|
132
|
+
pin: pin,
|
|
133
|
+
square: <GlyphSquare {...shapeProps} />,
|
|
134
|
+
diamond: <GlyphDiamond {...shapeProps} />,
|
|
135
|
+
star: <GlyphStar {...shapeProps} />,
|
|
136
|
+
triangle: <GlyphTriangle {...shapeProps} />
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { additionalCityStyles } = state.visual || []
|
|
140
|
+
const cityStyle = Object.values(data)
|
|
141
|
+
.filter(d => additionalCityStyles.some(style => String(d[style.column]) === String(style.value)))
|
|
142
|
+
.map(d => {
|
|
143
|
+
const conditionsMatched = additionalCityStyles.find(style => String(d[style.column]) === String(style.value))
|
|
144
|
+
return { ...conditionsMatched, ...d }
|
|
145
|
+
})
|
|
146
|
+
.find(item => {
|
|
147
|
+
return Object.keys(item).find(key => item[key] === city)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
if (cityStyle !== undefined && cityStyle.shape) {
|
|
151
|
+
if (!geoData?.[state.columns.longitude.name] && !geoData?.[state.columns.latitude.name] && city && supportedCities[city.toUpperCase()]) {
|
|
152
|
+
let translate = `translate(${projection(supportedCities[city.toUpperCase()])})`
|
|
153
|
+
return (
|
|
154
|
+
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
155
|
+
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
156
|
+
</g>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (geoData?.[state.columns.longitude.name] && geoData?.[state.columns.latitude.name]) {
|
|
161
|
+
const coords = [Number(geoData?.[state.columns.longitude.name]), Number(geoData?.[state.columns.latitude.name])]
|
|
162
|
+
let translate = `translate(${projection(coords)})`
|
|
163
|
+
return (
|
|
164
|
+
<g key={i} transform={translate} style={styles} className='geo-point' tabIndex={-1}>
|
|
165
|
+
{cityStyleShapes[cityStyle.shape.toLowerCase()]}
|
|
166
|
+
</g>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
117
170
|
return (
|
|
118
|
-
<g key={i} transform={transform} style={styles} className='geo-point'>
|
|
119
|
-
{state.visual.cityStyle
|
|
120
|
-
{state.visual.cityStyle === 'pin' && pin}
|
|
171
|
+
<g key={i} transform={transform} style={styles} className='geo-point' tabIndex={-1}>
|
|
172
|
+
{cityStyleShapes[state.visual.cityStyle.toLowerCase()]}
|
|
121
173
|
</g>
|
|
122
174
|
)
|
|
123
175
|
})
|
|
@@ -7,6 +7,7 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
7
7
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
8
8
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
9
9
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
10
|
+
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
10
11
|
|
|
11
12
|
import Loading from '@cdc/core/components/Loading'
|
|
12
13
|
|
|
@@ -193,9 +194,7 @@ const DataTable = props => {
|
|
|
193
194
|
{state.general.showDownloadButton && <DownloadButton />}
|
|
194
195
|
</MediaControls.Section>
|
|
195
196
|
<section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
|
|
196
|
-
<
|
|
197
|
-
Skip Navigation or Skip to Content
|
|
198
|
-
</a>
|
|
197
|
+
<SkipTo skipId={skipId} skipMessage='Skip Data Table' />
|
|
199
198
|
<div
|
|
200
199
|
className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|
|
201
200
|
onClick={() => {
|
|
@@ -229,7 +228,7 @@ const DataTable = props => {
|
|
|
229
228
|
return (
|
|
230
229
|
<th
|
|
231
230
|
key={`col-header-${column}`}
|
|
232
|
-
tabIndex=
|
|
231
|
+
tabIndex={0}
|
|
233
232
|
title={text}
|
|
234
233
|
role='columnheader'
|
|
235
234
|
scope='col'
|
|
@@ -245,9 +244,7 @@ const DataTable = props => {
|
|
|
245
244
|
{...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
|
|
246
245
|
>
|
|
247
246
|
{text}
|
|
248
|
-
<
|
|
249
|
-
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} `} order</span>
|
|
250
|
-
</button>
|
|
247
|
+
<span className='cdcdataviz-sr-only'>{`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} order`}</span>
|
|
251
248
|
</th>
|
|
252
249
|
)
|
|
253
250
|
})}
|
|
@@ -298,6 +295,9 @@ const DataTable = props => {
|
|
|
298
295
|
</table>
|
|
299
296
|
</div>
|
|
300
297
|
</section>
|
|
298
|
+
<div id={skipId} className='cdcdataviz-sr-only'>
|
|
299
|
+
Skipped data table.
|
|
300
|
+
</div>
|
|
301
301
|
</ErrorBoundary>
|
|
302
302
|
)
|
|
303
303
|
}
|