@cdc/map 4.22.10 → 4.23.1

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.
Files changed (58) hide show
  1. package/dist/495.js +3 -0
  2. package/dist/703.js +1 -0
  3. package/dist/856.js +3 -0
  4. package/dist/cdcmap.js +724 -120
  5. package/examples/bubble-us.json +362 -362
  6. package/examples/bubble-world.json +426 -426
  7. package/examples/city-state.json +434 -0
  8. package/examples/example-city-state.json +202 -25
  9. package/package.json +3 -4
  10. package/src/CdcMap.js +249 -423
  11. package/src/components/BubbleList.js +198 -240
  12. package/src/components/CityList.js +50 -97
  13. package/src/components/CountyMap.js +511 -600
  14. package/src/components/DataTable.js +223 -230
  15. package/src/components/EditorPanel.js +2395 -2551
  16. package/src/components/Filters.js +114 -0
  17. package/src/components/Geo.js +4 -14
  18. package/src/components/Modal.js +13 -23
  19. package/src/components/NavigationMenu.js +43 -39
  20. package/src/components/Sidebar.js +77 -93
  21. package/src/components/SingleStateMap.js +95 -151
  22. package/src/components/UsaMap.js +165 -214
  23. package/src/components/UsaRegionMap.js +122 -160
  24. package/src/components/WorldMap.js +96 -179
  25. package/src/components/ZoomableGroup.js +6 -26
  26. package/src/context.js +5 -0
  27. package/src/data/initial-state.js +20 -15
  28. package/src/data/supported-geos.js +3201 -3175
  29. package/src/hooks/useActiveElement.js +13 -13
  30. package/src/hooks/useColorPalette.ts +66 -74
  31. package/src/hooks/useZoomPan.js +22 -23
  32. package/src/index.html +32 -29
  33. package/src/scss/datatable.scss +1 -2
  34. package/src/scss/filters.scss +42 -0
  35. package/src/scss/main.scss +4 -3
  36. package/src/scss/sidebar.scss +22 -0
  37. package/examples/private/atsdr.json +0 -439
  38. package/examples/private/atsdr_new.json +0 -436
  39. package/examples/private/bubble.json +0 -285
  40. package/examples/private/city-state.json +0 -428
  41. package/examples/private/cty-issue.json +0 -42768
  42. package/examples/private/default-usa.json +0 -460
  43. package/examples/private/default-world-data.json +0 -1444
  44. package/examples/private/default.json +0 -968
  45. package/examples/private/legend-issue.json +0 -1
  46. package/examples/private/map-rounding-error.json +0 -42759
  47. package/examples/private/map.csv +0 -60
  48. package/examples/private/mdx.json +0 -210
  49. package/examples/private/monkeypox.json +0 -376
  50. package/examples/private/regions.json +0 -52
  51. package/examples/private/valid-data-map.csv +0 -59
  52. package/examples/private/wcmsrd-13881-data.json +0 -2858
  53. package/examples/private/wcmsrd-13881.json +0 -5823
  54. package/examples/private/wcmsrd-14492-data.json +0 -292
  55. package/examples/private/wcmsrd-14492.json +0 -114
  56. package/examples/private/wcmsrd-test.json +0 -268
  57. package/examples/private/world.json +0 -1580
  58. package/examples/private/worldmap.json +0 -1490
@@ -0,0 +1,114 @@
1
+ import React, { useState, useContext } from 'react'
2
+ import Context from './../context'
3
+ import Button from '@cdc/core/components/elements/Button'
4
+
5
+ // TODO: Combine Charts/Maps Filters.js files
6
+ const useFilters = () => {
7
+ const { state: config, setState: setConfig, runtimeFilters, setRuntimeFilters } = useContext(Context)
8
+ const [showApplyButton, setShowApplyButton] = useState(false)
9
+
10
+ const sortAsc = (a, b) => {
11
+ return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
12
+ }
13
+
14
+ const sortDesc = (a, b) => {
15
+ return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
16
+ }
17
+
18
+ const announceChange = text => {}
19
+
20
+ const changeFilterActive = (index, value) => {
21
+ let newFilters = runtimeFilters
22
+ newFilters[index].active = value
23
+ setRuntimeFilters(newFilters)
24
+ setShowApplyButton(true)
25
+ }
26
+
27
+ const handleApplyButton = () => {
28
+ setConfig({ ...config, filters: runtimeFilters })
29
+ setShowApplyButton(false)
30
+ }
31
+
32
+ const handleReset = () => {
33
+ let newFilters = runtimeFilters
34
+
35
+ // reset to first item in values array.
36
+ newFilters.map(filter => {
37
+ filter.active = filter.values[0]
38
+ })
39
+
40
+ setConfig({ ...config, filters: newFilters })
41
+ }
42
+
43
+ return { announceChange, sortAsc, sortDesc, changeFilterActive, showApplyButton, handleReset, handleApplyButton }
44
+ }
45
+
46
+ export const Filters = () => {
47
+ const { runtimeFilters, state: config } = useContext(Context)
48
+ const { handleApplyButton, changeFilterActive, announceChange, sortAsc, sortDesc, showApplyButton, handleReset } = useFilters()
49
+
50
+ const buttonText = 'Apply Filters'
51
+ const resetText = 'Reset All'
52
+
53
+ const { filters } = config
54
+
55
+ if (filters.length === 0) return false
56
+
57
+ const FilterList = ({ filters: runtimeFilters }) => {
58
+ if (runtimeFilters) {
59
+ return runtimeFilters.map((singleFilter, idx) => {
60
+ const values = []
61
+
62
+ if (undefined === singleFilter.active) return null
63
+
64
+ singleFilter.values.forEach((filterOption, idx) => {
65
+ values.push(
66
+ <option key={idx} value={filterOption}>
67
+ {filterOption}
68
+ </option>
69
+ )
70
+ })
71
+
72
+ return (
73
+ <section className='filter-col single-filter' key={idx}>
74
+ {singleFilter.label.length > 0 && <label htmlFor={`filter-${idx}`}>{singleFilter.label}</label>}
75
+ <select
76
+ id={`filter-${idx}`}
77
+ className='filter-select'
78
+ aria-label='select filter'
79
+ value={singleFilter.active}
80
+ onChange={e => {
81
+ changeFilterActive(idx, e.target.value)
82
+ announceChange(`Filter ${singleFilter.label} value has been changed to ${e.target.value}, please reference the data table to see updated values.`)
83
+ }}
84
+ >
85
+ {values}
86
+ </select>
87
+ </section>
88
+ )
89
+ })
90
+ } else {
91
+ return null
92
+ }
93
+ }
94
+
95
+ return (
96
+ <section className={`filters-section`} style={{ display: 'block', width: '100%' }}>
97
+ <h3 className='filters-section__title'>Filters</h3>
98
+ <hr />
99
+ <div className='filters-section__wrapper' style={{ flexWrap: 'wrap', display: 'flex', gap: '7px 15px' }}>
100
+ <FilterList filters={runtimeFilters} />
101
+ <div className='filter-section__buttons' style={{ width: '100%' }}>
102
+ <Button onClick={handleApplyButton} disabled={!showApplyButton} style={{ marginRight: '10px' }}>
103
+ {buttonText}
104
+ </Button>
105
+ <a href='#!' role='button' onClick={handleReset}>
106
+ {resetText}
107
+ </a>
108
+ </div>
109
+ </div>
110
+ </section>
111
+ )
112
+ }
113
+
114
+ export default Filters
@@ -1,19 +1,9 @@
1
- import React, { memo } from 'react';
1
+ import React, { memo } from 'react'
2
2
 
3
- const Geo = ({path, styles, stroke, strokeWidth, ...props}) => {
3
+ const Geo = ({ path, styles, stroke, strokeWidth, ...props }) => {
4
4
  return (
5
- <g
6
- className="geo-group"
7
- css={styles}
8
- {...props}
9
- >
10
- <path
11
- tabIndex={-1}
12
- className='single-geo'
13
- stroke={stroke}
14
- strokeWidth={strokeWidth}
15
- d={path}
16
- />
5
+ <g className='geo-group' css={styles} {...props}>
6
+ <path tabIndex={-1} className='single-geo' stroke={stroke} strokeWidth={strokeWidth} d={path} />
17
7
  </g>
18
8
  )
19
9
  }
@@ -1,32 +1,22 @@
1
- import React from 'react';
2
- import closeIcon from '../images/close.svg?inline';
3
- import LegendCircle from '@cdc/core/components/LegendCircle';
1
+ import React from 'react'
2
+ import closeIcon from '../images/close.svg?inline'
3
+ import LegendCircle from '@cdc/core/components/LegendCircle'
4
4
 
5
5
  //TODO: Where is this being used? Transfer to new Modal component?
6
- const Modal = (props) => {
7
- const {
8
- applyTooltipsToGeo,
9
- content,
10
- capitalize,
11
- applyLegendToRow,
12
- viewport,
13
- type
14
- } = props;
6
+ const Modal = props => {
7
+ const { applyTooltipsToGeo, content, capitalize, applyLegendToRow, viewport, type } = props
15
8
 
16
- const tooltip = applyTooltipsToGeo(content.geoName, content.keyedData, 'jsx');
9
+ const tooltip = applyTooltipsToGeo(content.geoName, content.keyedData, 'jsx')
17
10
 
18
- const legendColors = applyLegendToRow(content.keyedData);
11
+ const legendColors = applyLegendToRow(content.keyedData)
19
12
 
20
13
  return (
21
- <section className={capitalize ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport} aria-hidden="true">
22
- <img src={closeIcon} className="modal-close" alt="Close Modal" />
14
+ <section className={capitalize ? 'modal-content tooltip capitalize ' + viewport : 'modal-content tooltip ' + viewport} aria-hidden='true'>
15
+ <img src={closeIcon} className='modal-close' alt='Close Modal' />
23
16
  {type === 'data' && <LegendCircle fill={legendColors[0]} />}
24
- <div className="content">
25
- {tooltip}
26
- </div>
17
+ <div className='content'>{tooltip}</div>
27
18
  </section>
28
- );
29
- };
19
+ )
20
+ }
30
21
 
31
-
32
- export default Modal;
22
+ export default Modal
@@ -1,67 +1,71 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react'
2
2
 
3
- const NavigationMenu = ({
4
- data, navigationHandler, options, columns, displayGeoName, mapTabbingID
5
- }) => {
6
- const [activeGeo, setActiveGeo] = useState('');
3
+ const NavigationMenu = ({ data, navigationHandler, options, columns, displayGeoName, mapTabbingID }) => {
4
+ const [activeGeo, setActiveGeo] = useState('')
7
5
 
8
- const [dropdownItems, setDropdownItems] = useState({});
6
+ const [dropdownItems, setDropdownItems] = useState({})
9
7
 
10
- const handleSubmit = (event) => {
11
- event.preventDefault();
8
+ const handleSubmit = event => {
9
+ event.preventDefault()
12
10
  if (activeGeo !== '') {
13
- const urlString = data[dropdownItems[activeGeo]][columns.navigate.name];
11
+ const urlString = data[dropdownItems[activeGeo]][columns.navigate.name]
14
12
 
15
- navigationHandler(urlString);
13
+ navigationHandler(urlString)
16
14
  }
17
- };
15
+ }
18
16
 
19
- let navSelect; let
20
- navGo;
17
+ let navSelect
18
+ let navGo
21
19
 
22
20
  switch (options.language) {
23
21
  case 'es':
24
- navSelect = 'Selecciona un Artículo';
25
- navGo = 'Ir';
26
- break;
22
+ navSelect = 'Selecciona un Artículo'
23
+ navGo = 'Ir'
24
+ break
27
25
  default:
28
- navSelect = 'Select an Item';
29
- navGo = 'Go';
26
+ navSelect = 'Select an Item'
27
+ navGo = 'Go'
30
28
  }
31
29
 
32
30
  useEffect(() => {
33
- const sortedOptions = {};
31
+ const sortedOptions = {}
34
32
 
35
- const processedDropdown = {};
33
+ const processedDropdown = {}
36
34
 
37
- Object.keys(data).forEach((val) => {
38
- const fullName = displayGeoName(val);
35
+ Object.keys(data).forEach(val => {
36
+ const fullName = displayGeoName(val)
39
37
 
40
- processedDropdown[fullName] = val;
41
- });
38
+ processedDropdown[fullName] = val
39
+ })
42
40
 
43
- Object.keys(processedDropdown).sort().forEach((key) => {
44
- sortedOptions[key] = processedDropdown[key];
45
- });
41
+ Object.keys(processedDropdown)
42
+ .sort()
43
+ .forEach(key => {
44
+ sortedOptions[key] = processedDropdown[key]
45
+ })
46
46
 
47
- setDropdownItems(sortedOptions);
47
+ setDropdownItems(sortedOptions)
48
48
 
49
- setActiveGeo(Object.keys(sortedOptions)[0]);
50
- }, [data, displayGeoName]);
49
+ setActiveGeo(Object.keys(sortedOptions)[0])
50
+ }, [data, displayGeoName])
51
51
 
52
52
  return (
53
- <section className="navigation-menu">
54
- <form onSubmit={handleSubmit} type="get">
53
+ <section className='navigation-menu'>
54
+ <form onSubmit={handleSubmit} type='get'>
55
55
  <label htmlFor={mapTabbingID.replace('#', '')}>
56
- <div className="select-heading">{navSelect}</div>
57
- <select value={activeGeo} id={mapTabbingID.replace('#', '')} onChange={(e) => setActiveGeo(e.target.value)}>
58
- {Object.keys(dropdownItems).map((key, i) => <option key={key} value={key}>{key}</option>)}
56
+ <div className='select-heading'>{navSelect}</div>
57
+ <select value={activeGeo} id={mapTabbingID.replace('#', '')} onChange={e => setActiveGeo(e.target.value)}>
58
+ {Object.keys(dropdownItems).map((key, i) => (
59
+ <option key={key} value={key}>
60
+ {key}
61
+ </option>
62
+ ))}
59
63
  </select>
60
64
  </label>
61
- <input type="submit" value={navGo} className={`${options.headerColor} btn`} id="cdcnavmap-dropdown-go" />
65
+ <input type='submit' value={navGo} className={`${options.headerColor} btn`} id='cdcnavmap-dropdown-go' />
62
66
  </form>
63
67
  </section>
64
- );
65
- };
68
+ )
69
+ }
66
70
 
67
- export default NavigationMenu;
71
+ export default NavigationMenu
@@ -1,30 +1,17 @@
1
- import React from 'react';
2
- import parse from 'html-react-parser';
1
+ import React from 'react'
2
+ import parse from 'html-react-parser'
3
3
 
4
4
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
5
5
  import LegendCircle from '@cdc/core/components/LegendCircle'
6
6
 
7
- const Sidebar = (props) => {
8
- const {
9
- legend,
10
- runtimeFilters,
11
- columns,
12
- setAccessibleStatus,
13
- changeFilterActive,
14
- resetLegendToggles,
15
- runtimeLegend,
16
- setRuntimeLegend,
17
- prefix,
18
- suffix,
19
- viewport,
20
- displayDataAsText
21
- } = props;
7
+ const Sidebar = props => {
8
+ const { legend, runtimeFilters, columns, setAccessibleStatus, changeFilterActive, resetLegendToggles, runtimeLegend, setRuntimeLegend, prefix, suffix, viewport, displayDataAsText } = props
22
9
 
23
10
  // Toggles if a legend is active and being applied to the map and data table.
24
11
  const toggleLegendActive = (i, legendLabel) => {
25
- const newValue = !runtimeLegend[i].disabled;
12
+ const newValue = !runtimeLegend[i].disabled
26
13
 
27
- runtimeLegend[i].disabled = newValue; // Toggle!
14
+ runtimeLegend[i].disabled = newValue // Toggle!
28
15
 
29
16
  let newLegend = [...runtimeLegend]
30
17
 
@@ -36,127 +23,124 @@ const Sidebar = (props) => {
36
23
 
37
24
  setRuntimeLegend(newLegend)
38
25
 
39
- setAccessibleStatus(`Disabled legend item ${legendLabel ?? ''}. Please reference the data table to see updated values.`);
40
- };
26
+ setAccessibleStatus(`Disabled legend item ${legendLabel ?? ''}. Please reference the data table to see updated values.`)
27
+ }
41
28
 
42
29
  const legendList = runtimeLegend.map((entry, idx) => {
30
+ const entryMax = displayDataAsText(entry.max, 'primary')
43
31
 
44
- const entryMax = displayDataAsText(entry.max, 'primary');
32
+ const entryMin = displayDataAsText(entry.min, 'primary')
45
33
 
46
- const entryMin = displayDataAsText(entry.min, 'primary');
47
-
48
- let formattedText = `${entryMin}${entryMax !== entryMin ? ` - ${entryMax}` : ''}`;
34
+ let formattedText = `${entryMin}${entryMax !== entryMin ? ` - ${entryMax}` : ''}`
49
35
 
50
36
  // If interval, add some formatting
51
37
  if (legend.type === 'equalinterval' && idx !== runtimeLegend.length - 1) {
52
- formattedText = `${entryMin} - < ${entryMax}`;
38
+ formattedText = `${entryMin} - < ${entryMax}`
53
39
  }
54
40
 
55
- const { disabled } = entry;
41
+ const { disabled } = entry
56
42
 
57
43
  if (legend.type === 'category') {
58
- formattedText = displayDataAsText(entry.value, 'primary');
44
+ formattedText = displayDataAsText(entry.value, 'primary')
59
45
  }
60
46
 
61
47
  if (entry.max === 0 && entry.min === 0) {
62
- formattedText = '0';
48
+ formattedText = '0'
63
49
  }
64
50
 
65
- let legendLabel = formattedText;
51
+ let legendLabel = formattedText
66
52
 
67
53
  if (entry.hasOwnProperty('special')) {
68
- legendLabel = entry.label || entry.value;
54
+ legendLabel = entry.label || entry.value
69
55
  }
70
56
 
71
57
  return (
72
- <li
58
+ <li
73
59
  className={disabled ? 'disabled single-legend' : 'single-legend'}
74
60
  key={idx}
75
61
  title={`Legend item ${legendLabel} - Click to disable`}
76
- onClick={() => { toggleLegendActive(idx, legendLabel); } }
77
- >
78
- <LegendCircle fill={entry.color} /> <span className="label">{legendLabel}</span>
62
+ onClick={() => {
63
+ toggleLegendActive(idx, legendLabel)
64
+ }}
65
+ >
66
+ <LegendCircle fill={entry.color} /> <span className='label'>{legendLabel}</span>
79
67
  </li>
80
- );
81
- });
68
+ )
69
+ })
82
70
 
83
71
  const filtersList = runtimeFilters.map((singleFilter, idx) => {
84
- const values = [];
72
+ const values = []
85
73
 
86
- if(undefined === singleFilter.active) return null
74
+ if (undefined === singleFilter.active) return null
87
75
 
88
76
  singleFilter.values.forEach((filterOption, idx) => {
89
- values.push(<option
90
- key={idx}
91
- value={filterOption}
92
- >{filterOption}
93
- </option>);
94
- });
77
+ values.push(
78
+ <option key={idx} value={filterOption}>
79
+ {filterOption}
80
+ </option>
81
+ )
82
+ })
95
83
 
96
84
  return (
97
- <section className="filter-col" key={idx}>
85
+ <section className='filter-col' key={idx}>
98
86
  {singleFilter.label.length > 0 && <label htmlFor={`filter-${idx}`}>{singleFilter.label}</label>}
99
87
  <select
100
88
  id={`filter-${idx}`}
101
- className="filter-select"
102
- aria-label="select filter"
89
+ className='filter-select'
90
+ aria-label='select filter'
103
91
  value={singleFilter.active}
104
- onChange={(val) => {
105
- changeFilterActive(idx, val.target.value);
106
- setAccessibleStatus(`Filter ${singleFilter.label} value has been changed to ${val.target.value}, please reference the data table to see updated values.`);
92
+ onChange={val => {
93
+ changeFilterActive(idx, val.target.value)
94
+ setAccessibleStatus(`Filter ${singleFilter.label} value has been changed to ${val.target.value}, please reference the data table to see updated values.`)
107
95
  }}
108
96
  >
109
97
  {values}
110
98
  </select>
111
99
  </section>
112
- );
113
- });
100
+ )
101
+ })
102
+
103
+ const columnLogic = legend.position === 'side' && legend.singleColumn ? 'single-column' : legend.position === 'bottom' && legend.singleRow ? 'single-row' : ''
104
+
105
+ const classNames = [`${legend.position}`, `${columnLogic}`, `cdcdataviz-sr-focusable`, `${viewport}`]
114
106
 
115
107
  return (
116
- <ErrorBoundary component="Sidebar">
117
- <aside id="legend" className={`${legend.position} ${legend.singleColumn ? 'single-column cdcdataviz-sr-focusable' : 'cdcdataviz-sr-focusable'} ${viewport}`} role="region" aria-label="Legend" tabIndex="0">
118
- <section className="legend-section" aria-label="Map Legend">
119
- {runtimeLegend.disabledAmt > 0 &&
120
- (
108
+ <ErrorBoundary component='Sidebar'>
109
+ <aside id='legend' className={classNames.join(' ')} role='region' aria-label='Legend' tabIndex='0'>
110
+ <section className='legend-section' aria-label='Map Legend'>
111
+ {runtimeLegend.disabledAmt > 0 && (
121
112
  <button
122
- onClick={(e) => {
123
- e.preventDefault();
124
- resetLegendToggles();
125
- setAccessibleStatus('Legend has been reset, please reference the data table to see updated values.');
113
+ onClick={e => {
114
+ e.preventDefault()
115
+ resetLegendToggles()
116
+ setAccessibleStatus('Legend has been reset, please reference the data table to see updated values.')
126
117
  }}
127
- className="clear btn"
128
- >Clear
118
+ className='clear btn'
119
+ >
120
+ Clear
129
121
  </button>
130
122
  )}
131
- {legend.title && <span className="heading-2">{ parse(legend.title) }</span>}
132
- {legend.dynamicDescription === false && legend.description
133
- && <p>{ parse(legend.description) }</p>}
134
- {legend.dynamicDescription === true && runtimeFilters.map((filter, idx) => {
135
- const lookupStr = `${idx},${filter.values.indexOf(String(filter.active))}`;
136
-
137
- // Do we have a custom description for this?
138
- const desc = legend.descriptions[lookupStr] || '';
139
-
140
- if (desc.length > 0) {
141
- return (<p key={`dynamic-description-${lookupStr}`}>{desc}</p>);
142
- }
143
- return true;
144
- })}
145
- <ul className={legend.singleColumn ? 'single-column' : ''} aria-label="Legend items">
146
- {legendList}
147
- </ul>
148
- </section>
149
- {filtersList.length > 0 &&
150
- <section className="filters-section" aria-label="Map Filters">
151
- <span className="heading-3">Filters</span>
152
- <form>
153
- {filtersList}
154
- </form>
123
+ {legend.title && <span className='heading-2'>{parse(legend.title)}</span>}
124
+ {legend.dynamicDescription === false && legend.description && <p>{parse(legend.description)}</p>}
125
+ {legend.dynamicDescription === true &&
126
+ runtimeFilters.map((filter, idx) => {
127
+ const lookupStr = `${idx},${filter.values.indexOf(String(filter.active))}`
128
+
129
+ // Do we have a custom description for this?
130
+ const desc = legend.descriptions[lookupStr] || ''
131
+
132
+ if (desc.length > 0) {
133
+ return <p key={`dynamic-description-${lookupStr}`}>{desc}</p>
134
+ }
135
+ return true
136
+ })}
137
+ <ul className={columnLogic} aria-label='Legend items'>
138
+ {legendList}
139
+ </ul>
155
140
  </section>
156
- }
157
- </aside>
141
+ </aside>
158
142
  </ErrorBoundary>
159
- );
160
- };
143
+ )
144
+ }
161
145
 
162
- export default Sidebar;
146
+ export default Sidebar