@cdc/map 4.23.2 → 4.23.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.
@@ -79,6 +79,7 @@ const UsaMap = props => {
79
79
  isFilterValueSupported = true
80
80
  }
81
81
  })
82
+
82
83
  Object.keys(supportedTerritories).forEach(supportedTerritory => {
83
84
  if (supportedTerritories[supportedTerritory].indexOf(setSharedFilterValue.toUpperCase()) !== -1) {
84
85
  isFilterValueSupported = true
@@ -109,11 +110,15 @@ const UsaMap = props => {
109
110
  const territoriesKeys = Object.keys(supportedTerritories) // data will have already mapped abbreviated territories to their full names
110
111
 
111
112
  useEffect(() => {
112
- // Territories need to show up if they're in the data at all, not just if they're "active". That's why this is different from Cities
113
- const territoriesList = territoriesKeys.filter(key => data[key])
114
-
115
- setTerritoriesData(territoriesList)
116
- }, [data])
113
+ if (state.general.territoriesAlwaysShow) {
114
+ // show all Territories whether in the data or not
115
+ setTerritoriesData(territoriesKeys)
116
+ } else {
117
+ // Territories need to show up if they're in the data at all, not just if they're "active". That's why this is different from Cities
118
+ const territoriesList = territoriesKeys.filter(key => data[key])
119
+ setTerritoriesData(territoriesList)
120
+ }
121
+ }, [data, state.general.territoriesAlwaysShow]) // eslint-disable-line
117
122
 
118
123
  const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
119
124
 
@@ -167,8 +172,8 @@ const UsaMap = props => {
167
172
  }
168
173
 
169
174
  return <Shape key={label} label={label} css={styles} text={styles.color} strokeWidth={1.5} textColor={textColor} onClick={() => geoClickHandler(territory, territoryData)}
170
- data-tooltip-id="tooltip"
171
- data-tooltip-html={toolTip}
175
+ data-tooltip-id="tooltip"
176
+ data-tooltip-html={toolTip}
172
177
  />
173
178
  }
174
179
  })
@@ -292,10 +297,10 @@ const UsaMap = props => {
292
297
  return (
293
298
  <g data-name={geoName} key={key}>
294
299
  <g className='geo-group' css={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)}
295
- id={geoName}
296
- data-tooltip-id="tooltip"
297
- data-tooltip-html={tooltip}
298
- >
300
+ id={geoName}
301
+ data-tooltip-id="tooltip"
302
+ data-tooltip-html={tooltip}
303
+ >
299
304
  <path tabIndex={-1} className='single-geo' strokeWidth={1.3} d={path} />
300
305
  {(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
301
306
  </g>
@@ -319,18 +324,18 @@ const UsaMap = props => {
319
324
  // Cities
320
325
  geosJsx.push(
321
326
  <CityList
322
- projection={projection}
323
- key='cities'
324
- data={data}
325
- state={state}
326
- geoClickHandler={geoClickHandler}
327
+ applyLegendToRow={applyLegendToRow}
327
328
  applyTooltipsToGeo={applyTooltipsToGeo}
329
+ data={data}
328
330
  displayGeoName={displayGeoName}
329
- applyLegendToRow={applyLegendToRow}
330
- titleCase={titleCase}
331
- setSharedFilterValue={setSharedFilterValue}
331
+ geoClickHandler={geoClickHandler}
332
332
  isFilterValueSupported={isFilterValueSupported}
333
333
  isGeoCodeMap={state.general.type === 'us-geocode'}
334
+ key='cities'
335
+ projection={projection}
336
+ setSharedFilterValue={setSharedFilterValue}
337
+ state={state}
338
+ titleCase={titleCase}
334
339
  />
335
340
  )
336
341
 
@@ -1,4 +1,4 @@
1
- import { useEffect, memo } from 'react'
1
+ import { memo } from 'react'
2
2
 
3
3
  import { jsx } from '@emotion/react'
4
4
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
@@ -16,7 +16,21 @@ const { features: world } = feature(topoJSON, topoJSON.objects.countries)
16
16
  let projection = geoMercator()
17
17
 
18
18
  const WorldMap = props => {
19
- const { state, applyTooltipsToGeo, data, geoClickHandler, applyLegendToRow, displayGeoName, supportedCountries, setState, setRuntimeData, generateRuntimeData, setFilteredCountryCode, position, setPosition, hasZoom, handleMapAriaLabels } = props
19
+ const {
20
+ state,
21
+ applyTooltipsToGeo,
22
+ data,
23
+ geoClickHandler,
24
+ applyLegendToRow,
25
+ displayGeoName,
26
+ supportedCountries,
27
+ setState, setRuntimeData,
28
+ generateRuntimeData,
29
+ setFilteredCountryCode,
30
+ position, setPosition,
31
+ hasZoom,
32
+ handleMapAriaLabels,
33
+ titleCase } = props
20
34
 
21
35
  // TODO Refactor - state should be set together here to avoid rerenders
22
36
  // Resets to original data & zooms out
@@ -58,6 +72,11 @@ const WorldMap = props => {
58
72
  Reset Filters
59
73
  </button>
60
74
  )}
75
+ {state.general.type === 'world-geocode' && (
76
+ <button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom'>
77
+ Reset Zoom
78
+ </button>
79
+ )}
61
80
  </div>
62
81
  )
63
82
 
@@ -76,7 +95,7 @@ const WorldMap = props => {
76
95
  const geosJsx = geographies.map(({ feature: geo, path }, i) => {
77
96
  const geoKey = geo.properties.iso
78
97
 
79
- if (!geoKey) return
98
+ if (!geoKey) return null;
80
99
 
81
100
  const geoData = data[geoKey]
82
101
 
@@ -104,13 +123,13 @@ const WorldMap = props => {
104
123
 
105
124
  styles = {
106
125
  ...styles,
107
- fill: legendColors[0],
126
+ fill: state.general.type !== 'world-geocode' ? legendColors[0] : '#E6E6E6',
108
127
  cursor: 'default',
109
128
  '&:hover': {
110
- fill: legendColors[1]
129
+ fill: state.general.type !== 'world-geocode' ? legendColors[1] : '#E6E6E6'
111
130
  },
112
131
  '&:active': {
113
- fill: legendColors[2]
132
+ fill: state.general.type !== 'world-geocode' ? legendColors[2] : '#E6E6E6'
114
133
  }
115
134
  }
116
135
 
@@ -136,7 +155,20 @@ const WorldMap = props => {
136
155
  })
137
156
 
138
157
  // Cities
139
- geosJsx.push(<CityList projection={projection} key='cities' data={data} state={state} geoClickHandler={geoClickHandler} applyTooltipsToGeo={applyTooltipsToGeo} displayGeoName={displayGeoName} applyLegendToRow={applyLegendToRow} isGeoCodeMap={state.general.type === 'us-geocode'} />)
158
+ geosJsx.push(
159
+ <CityList
160
+ applyLegendToRow={applyLegendToRow}
161
+ applyTooltipsToGeo={applyTooltipsToGeo}
162
+ data={data}
163
+ displayGeoName={displayGeoName}
164
+ geoClickHandler={geoClickHandler}
165
+ isGeoCodeMap={state.general.type === 'world-geocode'}
166
+ key='cities'
167
+ projection={projection}
168
+ state={state}
169
+ titleCase={titleCase}
170
+ />
171
+ )
140
172
 
141
173
  // Bubbles
142
174
  if (state.general.type === 'bubble') {
@@ -174,7 +206,7 @@ const WorldMap = props => {
174
206
  </ZoomableGroup>
175
207
  </svg>
176
208
  )}
177
- {(state.general.type === 'data' || (state.general.type === 'bubble' && hasZoom)) && <ZoomControls position={position} setPosition={setPosition} setRuntimeData={setRuntimeData} state={state} setState={setState} generateRuntimeData={generateRuntimeData} />}
209
+ {(state.general.type === 'data' || (state.general.type === 'world-geocode' && hasZoom) || (state.general.type === 'bubble' && hasZoom)) && <ZoomControls position={position} setPosition={setPosition} setRuntimeData={setRuntimeData} state={state} setState={setState} generateRuntimeData={generateRuntimeData} />}
178
210
  </ErrorBoundary>
179
211
  )
180
212
  }
@@ -0,0 +1,73 @@
1
+ {
2
+ "type": "Topology",
3
+ "arcs": [
4
+ [
5
+ [
6
+ 33,
7
+ 100
8
+ ],
9
+ [
10
+ -6,
11
+ 9
12
+ ],
13
+ [
14
+ 30,
15
+ 66
16
+ ],
17
+ [
18
+ 102,
19
+ -42
20
+ ],
21
+ [
22
+ -118,
23
+ -133
24
+ ],
25
+ [
26
+ 11,
27
+ 39
28
+ ],
29
+ [
30
+ -52,
31
+ 39
32
+ ],
33
+ [
34
+ 33,
35
+ 22
36
+ ]
37
+ ]
38
+ ],
39
+ "transform": {
40
+ "scale": [
41
+ 0.04505085471698112,
42
+ 0.04496205714285712
43
+ ],
44
+ "translate": [
45
+ -116.2012448,
46
+ 33.3764121
47
+ ]
48
+ },
49
+ "objects": {
50
+ "Untitled layer": {
51
+ "type": "GeometryCollection",
52
+ "geometries": [
53
+ {
54
+ "arcs": [
55
+ [
56
+ 0
57
+ ]
58
+ ],
59
+ "type": "Polygon",
60
+ "properties": {
61
+ "name": "new",
62
+ "styleUrl": "#poly-000000-1200-77-nodesc",
63
+ "fill-opacity": 0.30196078431372547,
64
+ "fill": "#000000",
65
+ "stroke-opacity": 1,
66
+ "stroke": "#000000",
67
+ "stroke-width": 1.2
68
+ }
69
+ }
70
+ ]
71
+ }
72
+ }
73
+ }
@@ -2,6 +2,7 @@ export default {
2
2
  general: {
3
3
  geoBorderColor: 'darkGray',
4
4
  headerColor: 'theme-blue',
5
+ title: '',
5
6
  showTitle: true,
6
7
  showSidebar: true,
7
8
  showDownloadButton: true,
@@ -9,6 +10,7 @@ export default {
9
10
  displayAsHex: false,
10
11
  displayStateLabels: false,
11
12
  territoriesLabel: 'Territories',
13
+ territoriesAlwaysShow: false,
12
14
  language: 'en',
13
15
  geoType: 'single-state',
14
16
  geoLabelOverride: '',
@@ -53,6 +55,7 @@ export default {
53
55
  unified: false,
54
56
  singleColumn: false,
55
57
  singleRow: false,
58
+ showSpecialClassesLast: false,
56
59
  dynamicDescription: false,
57
60
  type: 'equalnumber',
58
61
  numberOfItems: 3,
@@ -121,13 +121,13 @@ export const stateToIso = {
121
121
  }
122
122
 
123
123
  export const stateFipsToTwoDigit = {
124
- ['01']: 'AL',
125
- ['02']: 'AK',
126
- ['04']: 'AZ',
127
- ['05']: 'AR',
128
- ['06']: 'CA',
129
- ['08']: 'CO',
130
- ['09']: 'CT',
124
+ ['01']: 'AL', // eslint-disable-line
125
+ ['02']: 'AK', // eslint-disable-line
126
+ ['04']: 'AZ', // eslint-disable-line
127
+ ['05']: 'AR', // eslint-disable-line
128
+ ['06']: 'CA', // eslint-disable-line
129
+ ['08']: 'CO', // eslint-disable-line
130
+ ['09']: 'CT', // eslint-disable-line
131
131
  10: 'DE',
132
132
  11: 'DC',
133
133
  12: 'FL',
package/src/scss/map.scss CHANGED
@@ -225,6 +225,7 @@ header + .map-container.full-border {
225
225
  position: absolute;
226
226
  top: 10px;
227
227
  right: 10px;
228
+ display: none;
228
229
  }
229
230
 
230
231
  //Region Maps
@@ -310,3 +311,14 @@ header + .map-container.full-border {
310
311
  stroke-linecap: round;
311
312
  pointer-events: none;
312
313
  }
314
+
315
+ canvas {
316
+ width: 100%;
317
+ }
318
+
319
+ #canvas-tooltip {
320
+ position: fixed;
321
+ background-color: white;
322
+ pointer-events: none;
323
+ display: none;
324
+ }
@@ -0,0 +1,19 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react'
2
+ import CdcMap from './../CdcMap'
3
+ import usaConfig from './../../examples/example-city-state.json'
4
+
5
+ // Example: City State Map
6
+ describe('United States Map', () => {
7
+ it('loads', async () => {
8
+ render(<CdcMap config={usaConfig} />)
9
+ // screen.debug()
10
+ })
11
+
12
+ it('accepts title changes', async () => {
13
+ render(<CdcMap config={usaConfig} isEditor />)
14
+ const titleInput = screen.getByTestId('title-input')
15
+ expect(titleInput.value).toBe('Example Data Map with Cities')
16
+ fireEvent.change(titleInput, { target: { value: 'My New Title' } })
17
+ expect(titleInput.value).toBe('My New Title')
18
+ })
19
+ })
package/vite.config.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import GenerateViteConfig from '../../generateViteConfig.js'
2
2
  import { moduleName } from './package.json'
3
3
 
4
- export default GenerateViteConfig(moduleName,null,{
4
+ export default GenerateViteConfig(moduleName, null, {
5
5
  jsxImportSource: '@emotion/react',
6
6
  babel: {
7
- plugins: [ '@emotion/babel-plugin' ],
8
- },
7
+ plugins: ['@emotion/babel-plugin']
8
+ }
9
9
  })
@@ -1,88 +0,0 @@
1
- import { useEffect, useReducer } from 'react'
2
-
3
- // constants
4
- const SEQUENTIAL = 'SEQUENTIAL'
5
- const SEQUENTIAL_REVERSE = 'SEQUENTIAL_REVERSE'
6
- export const GET_PALETTE = 'GET_PALETTE'
7
-
8
- // types & interfaces
9
- interface State {
10
- readonly filteredPallets: string[]
11
- readonly filteredQualitative: string[]
12
- readonly isPaletteReversed: boolean
13
- paletteName: string | undefined
14
- }
15
- interface Action<Palettes> {
16
- type: 'SEQUENTIAL' | 'SEQUENTIAL_REVERSE' | 'GET_PALETTE'
17
- payload: Palettes
18
- paletteName?: string
19
- }
20
-
21
- // create initial state
22
- const initialState: State = {
23
- filteredPallets: [],
24
- isPaletteReversed: false,
25
- filteredQualitative: [],
26
- paletteName: undefined
27
- }
28
-
29
- // create reducer function to handle multiple states & manupilate with each state
30
- function reducer<T>(state: State, action: Action<T>): State {
31
- // <T> refers to generic type
32
- const palletNamesArr: string[] = Object.keys(action.payload) // action.payload === colorPalettes object
33
- let reverseRegex = new RegExp('reverse$') // matches a string that ends in with "reverse".
34
- let qualitativeRegex = new RegExp('^qualitative') //matches any string that starts with "qualitative".
35
- let paletteName: string = ''
36
- switch (action.type) {
37
- case GET_PALETTE:
38
- // this case runs first time when page loads and then every time when color.state changes.It is mounted insude of useEffect on Editors Panel
39
- // action.palletName is a string type and equals to state.color which is inisde Editors Panel.
40
- return { ...state, paletteName: action.paletteName }
41
- case SEQUENTIAL:
42
- paletteName = String(state.paletteName).endsWith('reverse') ? String(state.paletteName).substring(0, state.paletteName.length - 7) : String(state.paletteName)
43
- const qualitative: string[] = palletNamesArr.filter((name: string) => name.match(qualitativeRegex) && !name.match(reverseRegex) && !name.includes('qualitative9'))
44
- const sequential: string[] = palletNamesArr.filter((name: string) => !name.match(qualitativeRegex) && !name.match(reverseRegex))
45
-
46
- return { ...state, filteredPallets: sequential, filteredQualitative: qualitative, paletteName: paletteName, isPaletteReversed: false }
47
-
48
- case SEQUENTIAL_REVERSE:
49
- paletteName = state.paletteName && String(state.paletteName).concat('reverse')
50
- const qualitativeReverse: string[] = palletNamesArr.filter((name: string) => name.match(qualitativeRegex) && name.match(reverseRegex))
51
- const sequentialReverse: string[] = palletNamesArr.filter((name: string) => !name.match(qualitativeRegex) && name.match(reverseRegex))
52
-
53
- return { ...state, filteredQualitative: qualitativeReverse, filteredPallets: sequentialReverse, paletteName: paletteName, isPaletteReversed: true }
54
- default:
55
- return state
56
- }
57
- }
58
-
59
- interface Keyable {
60
- color: string
61
- general: {
62
- palette: {
63
- isReversed: boolean
64
- }
65
- }
66
- }
67
-
68
- export function useColorPalette<T, Y extends Keyable>(colorPalettes: T, configState: Y) {
69
- const [state, dispatch] = useReducer(reducer, initialState)
70
- const { paletteName, isPaletteReversed, filteredPallets, filteredQualitative } = state
71
-
72
- useEffect(() => {
73
- dispatch({ type: SEQUENTIAL, payload: colorPalettes })
74
- }, [])
75
-
76
- useEffect(() => {
77
- if (configState.general.palette.isReversed) {
78
- dispatch({ type: 'SEQUENTIAL_REVERSE', payload: colorPalettes })
79
- }
80
- return () => dispatch({ type: 'SEQUENTIAL', payload: colorPalettes })
81
- }, [configState.general.palette.isReversed, dispatch, colorPalettes])
82
-
83
- useEffect(() => {
84
- if (configState.color) dispatch({ type: GET_PALETTE, payload: colorPalettes, paletteName: configState.color })
85
- }, [dispatch, configState.color])
86
-
87
- return { paletteName, isPaletteReversed, filteredPallets, filteredQualitative, dispatch }
88
- }