@cdc/map 4.23.6 → 4.23.7

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/index.html CHANGED
@@ -16,12 +16,13 @@
16
16
 
17
17
  <body>
18
18
  <!-- DEFAULT EXAMPLES -->
19
- <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div>
19
+ <!-- <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div> -->
20
+ <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div>
20
21
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-geocode.json"></div> -->
21
22
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-usa-regions.json"></div> -->
22
23
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-single-state.json"></div> -->
23
24
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-world.json"></div> -->
24
- <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/bubble-us.json"></div>-->
25
+ <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/bubble-us.json"></div> -->
25
26
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/bubble-world.json"></div> -->
26
27
 
27
28
  <!-- TP4 EXAMPLES -->
@@ -29,15 +30,15 @@
29
30
  <!-- <div class="react-container react-container--maps" data-config="/examples/custom-map-layers.json"></div> -->
30
31
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-stateBAD.json"></div> -->
31
32
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
32
- <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
33
+ <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
33
34
 
34
35
  <!-- TP4 EXAMPLES -->
35
36
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
36
- <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state-no-territories.json"></div> -->
37
+ <div class="react-container react-container--maps" data-config="/examples/example-city-state-no-territories.json"></div>
37
38
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
38
- <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
39
+ <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
39
40
 
40
- <div class="react-container" data-config="/examples/example-hex-map-with-filter.json"></div>
41
+ <!-- <div class="react-container" data-config="/examples/example-hex-map-with-filter.json"></div> -->
41
42
 
42
43
  <noscript>You need to enable JavaScript to run this app.</noscript>
43
44
  <script type="module" src="./src/index.jsx"></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/map",
3
- "version": "4.23.6",
3
+ "version": "4.23.7",
4
4
  "description": "React component for visualizing tabular data on a map of the United States or the world.",
5
5
  "moduleName": "CdcMap",
6
6
  "main": "dist/cdcmap",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "@cdc/core": "^4.23.6",
27
+ "@cdc/core": "^4.23.7",
28
28
  "@emotion/core": "^10.0.28",
29
29
  "@emotion/react": "^11.1.5",
30
30
  "@hello-pangea/dnd": "^16.2.0",
@@ -51,5 +51,5 @@
51
51
  "react": "^18.2.0",
52
52
  "react-dom": "^18.2.0"
53
53
  },
54
- "gitHead": "aaed0388b487adfeb3e7e278b4ce74df09cbaade"
54
+ "gitHead": "6c7ac5215dcf3bc1cc7d199089c8c2e75f53a93e"
55
55
  }
package/src/CdcMap.jsx CHANGED
@@ -51,6 +51,7 @@ import SingleStateMap from './components/SingleStateMap' // Future: Lazy
51
51
  import UsaMap from './components/UsaMap' // Future: Lazy
52
52
  import UsaRegionMap from './components/UsaRegionMap' // Future: Lazy
53
53
  import WorldMap from './components/WorldMap' // Future: Lazy
54
+ import useTooltip from './hooks/useTooltip'
54
55
 
55
56
  // Data props
56
57
  const stateKeys = Object.keys(supportedStates)
@@ -999,81 +1000,6 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
999
1000
  }
1000
1001
  }
1001
1002
 
1002
- const applyTooltipsToGeo = (geoName, row, returnType = 'string') => {
1003
- if (!row) return
1004
- let toolTipText = ''
1005
-
1006
- // Adds geo label, ie State: Georgia
1007
- let stateOrCounty = state.general.geoType === 'us' ? 'State: ' : state.general.geoType === 'us-county' || state.general.geoType === 'single-state' ? 'County: ' : ''
1008
-
1009
- // check the override
1010
- stateOrCounty = state.general.geoLabelOverride !== '' ? state.general.geoLabelOverride + ': ' : stateOrCounty
1011
-
1012
- if (state.general.geoType === 'us-county' && state.general.type !== 'us-geocode') {
1013
- let stateFipsCode = row[state.columns.geo.name].substring(0, 2)
1014
- const stateName = supportedStatesFipsCodes[stateFipsCode]
1015
-
1016
- toolTipText += !state.general.hideGeoColumnInTooltip ? `<strong>Location: ${stateName}</strong><br/>` : `<strong>${stateName}</strong><br/>`
1017
- }
1018
-
1019
- toolTipText += !state.general.hideGeoColumnInTooltip ? `<strong>${stateOrCounty}${displayGeoName(geoName)}</strong>` : `<strong>${displayGeoName(geoName)}</strong>`
1020
-
1021
- if (('data' === state.general.type || state.general.type === 'bubble' || state.general.type === 'us-geocode' || state.general.type === 'world-geocode') && undefined !== row) {
1022
- toolTipText += `<dl>`
1023
-
1024
- Object.keys(state.columns).forEach(columnKey => {
1025
- const column = state.columns[columnKey]
1026
-
1027
- if (true === column.tooltip) {
1028
- let label = column.label?.length > 0 ? column.label : ''
1029
-
1030
- let value
1031
-
1032
- if (state.legend.specialClasses && state.legend.specialClasses.length && typeof state.legend.specialClasses[0] === 'object') {
1033
- // THIS CODE SHOULD NOT ACT ON THE ENTIRE ROW OF KEYS BUT ONLY THE ONE KEY IN THE SPECIAL CLASS
1034
- for (let i = 0; i < state.legend.specialClasses.length; i++) {
1035
- // Special Classes label in HOVERS should only apply to selected special class key
1036
- // - you have to ALSO check that the key matches - putting here otherwise the if stmt too long
1037
- if (column.name === state.legend.specialClasses[i].key) {
1038
- if (String(row[state.legend.specialClasses[i].key]) === state.legend.specialClasses[i].value) {
1039
- value = displayDataAsText(state.legend.specialClasses[i].label, columnKey)
1040
- break
1041
- }
1042
- }
1043
- }
1044
- }
1045
-
1046
- if (!value) {
1047
- value = displayDataAsText(row[column.name], columnKey)
1048
- }
1049
-
1050
- if (0 < value.length) {
1051
- // Only spit out the tooltip if there's a value there
1052
- toolTipText += state.general.hidePrimaryColumnInTooltip ? `<div><dd>${value}</dd></div>` : `<div><dt>${label}</dt><dd>${value}</dd></div>`
1053
- }
1054
- }
1055
- })
1056
- toolTipText += `</dl>`
1057
- }
1058
-
1059
- // We convert the markup into JSX and add a navigation link if it's going into a modal.
1060
- if ('jsx' === returnType) {
1061
- toolTipText = [<div key='modal-content'>{parse(toolTipText)}</div>]
1062
-
1063
- if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
1064
- toolTipText.push(
1065
- // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
1066
- <span className='navigation-link' key='modal-navigation-link' onClick={() => navigationHandler(row[state.columns.navigate.name])}>
1067
- {state.tooltips.linkLabel}
1068
- <ExternalIcon className='inline-icon ml-1' />
1069
- </span>
1070
- )
1071
- }
1072
- }
1073
-
1074
- return toolTipText
1075
- }
1076
-
1077
1003
  // if city has a hyphen then in tooltip it ends up UPPER CASE instead of just regular Upper Case
1078
1004
  // - this function is used to prevent that and instead give the formatting that is wanted
1079
1005
  // Example: Desired city display in tooltip on map: "Inter-Tribal Indian Reservation"
@@ -1173,6 +1099,30 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1173
1099
  }
1174
1100
  }
1175
1101
 
1102
+ // todo: convert to store or context eventually.
1103
+ const { buildTooltip } = useTooltip({ state, displayGeoName, displayDataAsText, supportedStatesFipsCodes })
1104
+
1105
+ const applyTooltipsToGeo = (geoName, row, returnType = 'string') => {
1106
+ let toolTipText = buildTooltip(row, geoName, '')
1107
+
1108
+ // We convert the markup into JSX and add a navigation link if it's going into a modal.
1109
+ if ('jsx' === returnType) {
1110
+ toolTipText = [<div key='modal-content'>{parse(toolTipText)}</div>]
1111
+
1112
+ if (state.columns.hasOwnProperty('navigate') && row[state.columns.navigate.name]) {
1113
+ toolTipText.push(
1114
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
1115
+ <ul className='navigation-link' key='modal-navigation-link' onClick={() => navigationHandler(row[state.columns.navigate.name])}>
1116
+ {state.tooltips.linkLabel}
1117
+ <ExternalIcon className='inline-icon ml-1' />
1118
+ </ul>
1119
+ )
1120
+ }
1121
+ }
1122
+
1123
+ return toolTipText
1124
+ }
1125
+
1176
1126
  const navigationHandler = urlString => {
1177
1127
  // Call custom navigation method if passed
1178
1128
  if (customNavigationHandler) {
@@ -1639,7 +1589,9 @@ const CdcMap = ({ className, config, navigationHandler: customNavigationHandler,
1639
1589
  )}
1640
1590
  {!runtimeData.init && (general.type === 'navigation' || runtimeLegend) && (
1641
1591
  <section className={`cdc-map-inner-container ${currentViewport}`} aria-label={'Map: ' + title} ref={innerContainerRef}>
1642
- {!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && <ReactTooltip id='tooltip' variant='light' float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}`} />}
1592
+ {!window.matchMedia('(any-hover: none)').matches && 'hover' === tooltips.appearanceType && (
1593
+ <ReactTooltip id='tooltip' float={true} className={`${tooltips.capitalizeLabels ? 'capitalize tooltip' : 'tooltip'}`} style={{ background: `rgba(255,255,255, ${state.tooltips.opacity / 100})`, color: 'black' }} />
1594
+ )}
1643
1595
  {title && (
1644
1596
  <header className={general.showTitle === true ? 'visible' : 'hidden'} {...(!general.showTitle || !state.general.title ? { 'aria-hidden': true } : { 'aria-hidden': false })}>
1645
1597
  {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
@@ -9,7 +9,6 @@ import Loading from '@cdc/core/components/Loading'
9
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
10
10
 
11
11
  import topoJSON from '../data/county-map.json'
12
- import { formatPrefix } from 'd3'
13
12
  import useMapLayers from '../hooks/useMapLayers'
14
13
 
15
14
  const sortById = (a, b) => {
@@ -379,8 +378,17 @@ const CountyMap = props => {
379
378
 
380
379
  return (
381
380
  <ErrorBoundary component='CountyMap'>
382
- <canvas ref={canvasRef} aria-label={handleMapAriaLabels(state)} onMouseMove={canvasHover} onClick={canvasClick}></canvas>
383
- <div ref={tooltipRef} id='canvas-tooltip' className='tooltip'></div>
381
+ <canvas
382
+ ref={canvasRef}
383
+ aria-label={handleMapAriaLabels(state)}
384
+ onMouseMove={canvasHover}
385
+ onMouseOut={() => {
386
+ tooltipRef.current.style.display = 'none'
387
+ tooltipRef.current.setAttribute('data-index', null)
388
+ }}
389
+ onClick={canvasClick}
390
+ ></canvas>
391
+ <div ref={tooltipRef} id='canvas-tooltip' className='tooltip' style={{ background: `rgba(255,255,255,${state.tooltips.opacity / 100})` }}></div>
384
392
  <button className={`btn btn--reset`} onClick={onReset} ref={resetButton} tabIndex='0'>
385
393
  Reset Zoom
386
394
  </button>
@@ -1573,7 +1573,7 @@ const EditorPanel = props => {
1573
1573
  handleEditorChanges('displayStateLabels', event.target.checked)
1574
1574
  }}
1575
1575
  />
1576
- <span className='edit-label'>Display state labels</span>
1576
+ <span className='edit-label'>Show state labels</span>
1577
1577
  </label>
1578
1578
  )}
1579
1579
  </AccordionItemPanel>
@@ -1841,7 +1841,7 @@ const EditorPanel = props => {
1841
1841
  editColumn('primary', 'dataTable', event.target.checked)
1842
1842
  }}
1843
1843
  />
1844
- <span className='edit-label'>Display in Data Table</span>
1844
+ <span className='edit-label'>Show in Data Table</span>
1845
1845
  </label>
1846
1846
  </li>
1847
1847
  <li>
@@ -1853,7 +1853,7 @@ const EditorPanel = props => {
1853
1853
  editColumn('primary', 'tooltip', event.target.checked)
1854
1854
  }}
1855
1855
  />
1856
- <span className='edit-label'>Display in Tooltips</span>
1856
+ <span className='edit-label'>Show in Tooltips</span>
1857
1857
  </label>
1858
1858
  </li>
1859
1859
  </ul>
@@ -2068,7 +2068,7 @@ const EditorPanel = props => {
2068
2068
  editColumn(val, 'dataTable', event.target.checked)
2069
2069
  }}
2070
2070
  />
2071
- <span className='edit-label'>Display in Data Table</span>
2071
+ <span className='edit-label'>Show in Data Table</span>
2072
2072
  </label>
2073
2073
  </li>
2074
2074
  <li>
@@ -2080,7 +2080,7 @@ const EditorPanel = props => {
2080
2080
  editColumn(val, 'tooltip', event.target.checked)
2081
2081
  }}
2082
2082
  />
2083
- <span className='edit-label'>Display in Tooltips</span>
2083
+ <span className='edit-label'>Show in Tooltips</span>
2084
2084
  </label>
2085
2085
  </li>
2086
2086
  </ul>
@@ -2829,70 +2829,71 @@ const EditorPanel = props => {
2829
2829
  <span className='edit-label'>Bubble Map has extra border</span>
2830
2830
  </label>
2831
2831
  )}
2832
- {state.general.geoType === 'us' ||
2833
- state.general.geoType === 'us-county' ||
2834
- (state.general.geoType === 'world' && (
2835
- <label>
2836
- <span className='edit-label'>City Style</span>
2837
- <select
2838
- value={state.visual.cityStyle || false}
2839
- onChange={event => {
2840
- handleEditorChanges('handleCityStyle', event.target.value)
2841
- }}
2842
- >
2843
- <option value='circle'>Circle</option>
2844
- <option value='pin'>Pin</option>
2845
- </select>
2846
- </label>
2847
- ))}
2832
+ {(state.general.geoType === 'us' || state.general.geoType === 'us-county' || state.general.geoType === 'world') && (
2833
+ <label>
2834
+ <span className='edit-label'>City Style</span>
2835
+ <select
2836
+ value={state.visual.cityStyle || false}
2837
+ onChange={event => {
2838
+ handleEditorChanges('handleCityStyle', event.target.value)
2839
+ }}
2840
+ >
2841
+ <option value='circle'>Circle</option>
2842
+ <option value='pin'>Pin</option>
2843
+ </select>
2844
+ </label>
2845
+ )}
2846
+ <label htmlFor='opacity'>
2847
+ <TextField type='number' min={0} max={100} value={state.tooltips.opacity ? state.tooltips.opacity : 100} section='tooltips' fieldName='opacity' label='Tooltip Opacity (%)' updateField={updateField} />
2848
+ </label>
2849
+ </AccordionItemPanel>
2850
+ </AccordionItem>
2851
+ <AccordionItem>
2852
+ <AccordionItemHeading>
2853
+ <AccordionItemButton>Custom Map Layers</AccordionItemButton>
2854
+ </AccordionItemHeading>
2855
+ <AccordionItemPanel>
2856
+ {state.map.layers.length === 0 && <p>There are currently no layers.</p>}
2857
+
2858
+ {state.map.layers.map((layer, index) => {
2859
+ return (
2860
+ <>
2861
+ <Accordion allowZeroExpanded>
2862
+ <AccordionItem className='series-item map-layers-list'>
2863
+ <AccordionItemHeading className='series-item__title map-layers-list--title'>
2864
+ <AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
2865
+ </AccordionItemHeading>
2866
+ <AccordionItemPanel>
2867
+ <div className='map-layers-panel'>
2868
+ <label htmlFor='layerName'>Layer Name:</label>
2869
+ <input type='text' name='layerName' value={layer.name} onChange={e => handleMapLayer(e, index, 'name')} />
2870
+ <label htmlFor='layerFilename'>File:</label>
2871
+ <input type='text' name='layerFilename' value={layer.url} onChange={e => handleMapLayer(e, index, 'url')} />
2872
+ <label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
2873
+ <input type='text' name='layerNamespace' value={layer.namespace} onChange={e => handleMapLayer(e, index, 'namespace')} />
2874
+ <label htmlFor='layerFill'>Fill Color:</label>
2875
+ <input type='text' name='layerFill' value={layer.fill} onChange={e => handleMapLayer(e, index, 'fill')} />
2876
+ <label htmlFor='layerFill'>Fill Opacity (%):</label>
2877
+ <input type='number' min={0} max={100} name='layerFill' value={layer.fillOpacity ? layer.fillOpacity * 100 : ''} onChange={e => handleMapLayer(e, index, 'fillOpacity')} />
2878
+ <label htmlFor='layerStroke'>Stroke Color:</label>
2879
+ <input type='text' name='layerStroke' value={layer.stroke} onChange={e => handleMapLayer(e, index, 'stroke')} />
2880
+ <label htmlFor='layerStroke'>Stroke Width:</label>
2881
+ <input type='number' min={0} max={5} name='layerStrokeWidth' value={layer.strokeWidth} onChange={e => handleMapLayer(e, index, 'strokeWidth')} />
2882
+ <label htmlFor='layerTooltip'>Tooltip:</label>
2883
+ <textarea name='layerTooltip' value={layer.tooltip} onChange={e => handleMapLayer(e, index, 'tooltip')}></textarea>
2884
+ <button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
2885
+ </div>
2886
+ </AccordionItemPanel>
2887
+ </AccordionItem>
2888
+ </Accordion>
2889
+ </>
2890
+ )
2891
+ })}
2892
+ <button className={'btn full-width'} onClick={handleAddLayer}>
2893
+ Add Map Layer
2894
+ </button>
2848
2895
  </AccordionItemPanel>
2849
2896
  </AccordionItem>
2850
- {/* <AccordionItem>
2851
- <AccordionItemHeading>
2852
- <AccordionItemButton>Custom Map Layers</AccordionItemButton>
2853
- </AccordionItemHeading>
2854
- <AccordionItemPanel>
2855
- {state.map.layers.length === 0 && <p>There are currently no layers.</p>}
2856
-
2857
- {state.map.layers.map((layer, index) => {
2858
- return (
2859
- <>
2860
- <Accordion allowZeroExpanded>
2861
- <AccordionItem className='series-item map-layers-list'>
2862
- <AccordionItemHeading className='series-item__title map-layers-list--title'>
2863
- <AccordionItemButton>{`Layer ${index + 1}: ${layer.name}`}</AccordionItemButton>
2864
- </AccordionItemHeading>
2865
- <AccordionItemPanel>
2866
- <div className='map-layers-panel'>
2867
- <label htmlFor='layerName'>Layer Name:</label>
2868
- <input type='text' name='layerName' value={layer.name} onChange={e => handleMapLayer(e, index, 'name')} />
2869
- <label htmlFor='layerFilename'>File:</label>
2870
- <input type='text' name='layerFilename' value={layer.url} onChange={e => handleMapLayer(e, index, 'url')} />
2871
- <label htmlFor='layerNamespace'>TOPOJSON Namespace:</label>
2872
- <input type='text' name='layerNamespace' value={layer.namespace} onChange={e => handleMapLayer(e, index, 'namespace')} />
2873
- <label htmlFor='layerFill'>Fill Color:</label>
2874
- <input type='text' name='layerFill' value={layer.fill} onChange={e => handleMapLayer(e, index, 'fill')} />
2875
- <label htmlFor='layerFill'>Fill Opacity (%):</label>
2876
- <input type='number' min={0} max={100} name='layerFill' value={layer.fillOpacity ? layer.fillOpacity * 100 : ''} onChange={e => handleMapLayer(e, index, 'fillOpacity')} />
2877
- <label htmlFor='layerStroke'>Stroke Color:</label>
2878
- <input type='text' name='layerStroke' value={layer.stroke} onChange={e => handleMapLayer(e, index, 'stroke')} />
2879
- <label htmlFor='layerStroke'>Stroke Width:</label>
2880
- <input type='number' min={0} max={5} name='layerStrokeWidth' value={layer.strokeWidth} onChange={e => handleMapLayer(e, index, 'strokeWidth')} />
2881
- <label htmlFor='layerTooltip'>Tooltip:</label>
2882
- <textarea name='layerTooltip' value={layer.tooltip} onChange={e => handleMapLayer(e, index, 'tooltip')}></textarea>
2883
- <button onClick={e => handleRemoveLayer(e, index)}>Remove Layer</button>
2884
- </div>
2885
- </AccordionItemPanel>
2886
- </AccordionItem>
2887
- </Accordion>
2888
- </>
2889
- )
2890
- })}
2891
- <button className={'btn full-width'} onClick={handleAddLayer}>
2892
- Add Map Layer
2893
- </button>
2894
- </AccordionItemPanel>
2895
- </AccordionItem> */}
2896
2897
  </Accordion>
2897
2898
  <AdvancedEditor loadConfig={loadConfig} state={state} convertStateToConfig={convertStateToConfig} />
2898
2899
  </section>
@@ -77,7 +77,8 @@ export default {
77
77
  tooltips: {
78
78
  appearanceType: 'hover',
79
79
  linkLabel: 'Learn More',
80
- capitalizeLabels: true
80
+ capitalizeLabels: true,
81
+ opacity: 90
81
82
  },
82
83
  runtime: {
83
84
  editorErrorMessage: []
@@ -0,0 +1,135 @@
1
+ const useTooltip = props => {
2
+ const { state, displayGeoName, displayDataAsText, supportedStatesFipsCodes } = props
3
+
4
+ /**
5
+ * On county maps there's a need to append the state name
6
+ * @param {String} toolTipText - previous tooltip text to build upon
7
+ * @param {Object} row - row of data to lookup fips code with using the geo column.
8
+ * @returns {String} toolTipText - new toolTipText
9
+ */
10
+ const handleTooltipStateNameColumn = (toolTipText, row) => {
11
+ const { geoType, type, hideGeoColumnInTooltip } = state.general
12
+ if (geoType === 'us-county' && type !== 'us-geocode') {
13
+ let stateFipsCode = row[state.columns.geo.name].substring(0, 2)
14
+ const stateName = supportedStatesFipsCodes[stateFipsCode]
15
+ toolTipText += hideGeoColumnInTooltip ? `<strong>${stateName}</strong><br/>` : `<strong>Location: ${stateName}</strong><br/>`
16
+ }
17
+ return toolTipText
18
+ }
19
+
20
+ /**
21
+ * On county and state maps, adds the ability to hide the geo column name (prefix)
22
+ * @param {String} geoName - feature name
23
+ * @returns {String} text to be appended to toolTipText
24
+ */
25
+ const handleTooltipGeoColumn = geoName => {
26
+ const { hideGeoColumnInTooltip } = state.general
27
+
28
+ const handleTooltipPrefix = toolTipText => {
29
+ const { geoType, geoLabelOverride } = state.general
30
+ switch (geoType) {
31
+ case 'us':
32
+ toolTipText = 'State: '
33
+ break
34
+ case 'us-county':
35
+ toolTipText = 'County: '
36
+ break
37
+ case 'single-state':
38
+ toolTipText = 'County: '
39
+ break
40
+ default:
41
+ toolTipText = ''
42
+ break
43
+ }
44
+
45
+ if (geoLabelOverride) toolTipText = `${geoLabelOverride}: `
46
+
47
+ return toolTipText
48
+ }
49
+
50
+ const prefix = handleTooltipPrefix()
51
+
52
+ if (hideGeoColumnInTooltip) return `<strong>${displayGeoName(geoName)}</strong>`
53
+ return `<p class="tooltip-heading">${prefix}${displayGeoName(geoName)}</p>`
54
+ }
55
+
56
+ /**
57
+ * Handles special class descriptions in tooltips
58
+ */
59
+ const handleTooltipSpecialClassText = (specialClasses, column, row, value, columnKey) => {
60
+ if (specialClasses && specialClasses.length && typeof specialClasses[0] === 'object') {
61
+ for (const specialClass of specialClasses) {
62
+ if (column.name === specialClass.key && String(row[specialClass.key]) === specialClass.value) {
63
+ value = displayDataAsText(specialClass.label, columnKey)
64
+ break
65
+ }
66
+ }
67
+ }
68
+ return value
69
+ }
70
+
71
+ const handleTooltipPrimaryColumn = (tooltipValue, column) => {
72
+ const { hidePrimaryColumnInTooltip } = state.general
73
+ let tooltipPrefix = column.label?.length > 0 ? column.label : ''
74
+ if (hidePrimaryColumnInTooltip || !tooltipPrefix) return `<li class="tooltip-body">${tooltipValue}</li>`
75
+ return `<li class="tooltip-body">${tooltipPrefix}: ${tooltipValue}</li>`
76
+ }
77
+
78
+ /**
79
+ *
80
+ * @param {String} toolTipText - previous tooltipText to build upon
81
+ * @param {Object} row - row of data
82
+ * @returns {String} new tooltipText value
83
+ */
84
+ const handleTooltipColumns = (toolTipText, row) => {
85
+ const tooltipEnabledMaps = ['data', 'bubble', 'us-geocode', 'world-geocode', 'map']
86
+ const {
87
+ general: { type: currentMapType },
88
+ columns,
89
+ legend: { specialClasses }
90
+ } = state
91
+
92
+ if (tooltipEnabledMaps.includes(currentMapType) && undefined !== row) {
93
+ toolTipText += `<ul className="capitalize">`
94
+
95
+ // if tooltips are allowed, loop through each column
96
+ Object.keys(columns).forEach(columnKey => {
97
+ const column = state.columns[columnKey]
98
+
99
+ if (column.tooltip) {
100
+ let tooltipValue = handleTooltipSpecialClassText(specialClasses, column, row, '', columnKey)
101
+
102
+ if (!tooltipValue) {
103
+ tooltipValue = displayDataAsText(row[column.name], columnKey)
104
+ }
105
+
106
+ toolTipText += handleTooltipPrimaryColumn(tooltipValue, column)
107
+ }
108
+ })
109
+ toolTipText += `</ul>`
110
+ }
111
+
112
+ return toolTipText
113
+ }
114
+
115
+ const buildTooltip = (row, geoName, toolTipText = '') => {
116
+ if (!row) return
117
+
118
+ // Handle County Location Columns
119
+ toolTipText += handleTooltipStateNameColumn(toolTipText, row)
120
+
121
+ // Handle Data > Geo Column In tooltips
122
+ toolTipText += handleTooltipGeoColumn(geoName)
123
+
124
+ // Handle Data > Primary Column in tooltips
125
+ toolTipText = handleTooltipColumns(toolTipText, row)
126
+
127
+ return toolTipText
128
+ }
129
+
130
+ return {
131
+ buildTooltip
132
+ }
133
+ }
134
+
135
+ export default useTooltip
@@ -130,13 +130,7 @@
130
130
  .remove-column {
131
131
  font-size: 13px;
132
132
  }
133
- .__react_component_tooltip {
134
- padding: 5px;
135
- max-width: 300px;
136
- }
137
- .tooltip {
138
- position: relative;
139
- }
133
+
140
134
  .helper {
141
135
  position: relative;
142
136
  opacity: 0.7;
@@ -3,6 +3,7 @@
3
3
  @import 'variables';
4
4
  @import 'editor-panel';
5
5
  @import 'filters';
6
+ @import '@cdc/core/styles/v2/components/ui/tooltip';
6
7
 
7
8
  .cdc-map-outer-container {
8
9
  position: relative;
@@ -20,7 +21,6 @@
20
21
  }
21
22
 
22
23
  .cdc-map-inner-container {
23
- @import './tooltips';
24
24
  @import './map';
25
25
  @import './sidebar';
26
26
  @import './datatable';