@cdc/core 4.23.4 → 4.23.6

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.
@@ -1,4 +1,5 @@
1
- import React, { useState, useEffect } from 'react'
1
+ import React, { useState, useEffect, useRef } from 'react'
2
+ import { useId } from 'react'
2
3
 
3
4
  // CDC
4
5
  import Button from '@cdc/core/components/elements/Button'
@@ -72,7 +73,9 @@ export const useFilters = props => {
72
73
 
73
74
  const changeFilterActive = (index, value) => {
74
75
  let newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
76
+
75
77
  newFilters[index].active = value
78
+ setConfig({ ...visualizationConfig })
76
79
 
77
80
  // If this is a button filter type show the button.
78
81
  if (visualizationConfig.filterBehavior === 'Apply Button') {
@@ -183,6 +186,8 @@ const Filters = props => {
183
186
  const { config: visualizationConfig, filteredData, dimensions } = props
184
187
  const { filters, type, general, theme, filterBehavior } = visualizationConfig
185
188
  const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
189
+ const [selectedFilter, setSelectedFilter] = useState('')
190
+ const id = useId()
186
191
 
187
192
  // useFilters hook provides data and logic for handling various filter functions
188
193
  // prettier-ignore
@@ -205,6 +210,13 @@ const Filters = props => {
205
210
  }
206
211
  }, [dimensions])
207
212
 
213
+ useEffect(() => {
214
+ if (selectedFilter) {
215
+ let el = document.getElementById(selectedFilter.id)
216
+ if (el) el.focus()
217
+ }
218
+ }, [changeFilterActive, selectedFilter])
219
+
208
220
  const Filters = props => props.children
209
221
 
210
222
  const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : theme]
@@ -241,10 +253,24 @@ const Filters = props => {
241
253
  const { filter: singleFilter, index: outerIndex } = props
242
254
  return (
243
255
  <section className='single-filters__tab-bar'>
244
- {singleFilter.values.map(filter => {
256
+ {singleFilter.values.map((filter, index) => {
245
257
  const buttonClassList = ['button__tab-bar', singleFilter.active === filter ? 'button__tab-bar--active' : '']
246
258
  return (
247
- <button className={buttonClassList.join(' ')} key={filter} onClick={e => changeFilterActive(outerIndex, filter)}>
259
+ <button
260
+ id={`${filter}-${outerIndex}-${index}-${id}`}
261
+ className={buttonClassList.join(' ')}
262
+ key={filter}
263
+ onClick={e => {
264
+ changeFilterActive(outerIndex, filter)
265
+ setSelectedFilter(e.target)
266
+ }}
267
+ onKeyDown={e => {
268
+ if (e.keyCode === 13) {
269
+ changeFilterActive(outerIndex, filter)
270
+ setSelectedFilter(e.target)
271
+ }
272
+ }}
273
+ >
248
274
  {filter}
249
275
  </button>
250
276
  )
@@ -286,6 +312,8 @@ const Filters = props => {
286
312
  delete filtersToLoop.fromHash
287
313
 
288
314
  return filtersToLoop.map((singleFilter, outerIndex) => {
315
+ if(singleFilter.showDropdown === false) return
316
+
289
317
  const values = []
290
318
  const pillValues = []
291
319
  const tabValues = []
@@ -300,8 +328,22 @@ const Filters = props => {
300
328
  const tabClassList = ['tab', active === filterOption && 'tab--active', theme && theme]
301
329
 
302
330
  pillValues.push(
303
- <div className='pill__wrapper'>
304
- <button className={pillClassList.join(' ')} onClick={e => changeFilterActive(outerIndex, filterOption)} name={label}>
331
+ <div className='pill__wrapper' key={`pill-${index}`}>
332
+ <button
333
+ id={`${filterOption}-${outerIndex}-${index}-${id}`}
334
+ className={pillClassList.join(' ')}
335
+ onKeyDown={e => {
336
+ if (e.keyCode === 13) {
337
+ changeFilterActive(outerIndex, filterOption)
338
+ setSelectedFilter(e.target)
339
+ }
340
+ }}
341
+ onClick={e => {
342
+ changeFilterActive(outerIndex, filterOption)
343
+ setSelectedFilter(e.target)
344
+ }}
345
+ name={label}
346
+ >
305
347
  {filterOption}
306
348
  </button>
307
349
  </div>
@@ -309,12 +351,25 @@ const Filters = props => {
309
351
 
310
352
  values.push(
311
353
  <option key={index} value={filterOption}>
312
- {filterOption}
354
+ {singleFilter.labels && singleFilter.labels[filterOption] ? singleFilter.labels[filterOption] : filterOption}
313
355
  </option>
314
356
  )
315
357
 
316
358
  tabValues.push(
317
- <button className={tabClassList.join(' ')} onClick={e => changeFilterActive(outerIndex, filterOption)}>
359
+ <button
360
+ id={`${filterOption}-${outerIndex}-${index}-${id}`}
361
+ className={tabClassList.join(' ')}
362
+ onClick={e => {
363
+ changeFilterActive(outerIndex, filterOption)
364
+ setSelectedFilter(e.target)
365
+ }}
366
+ onKeyDown={e => {
367
+ if (e.keyCode === 13) {
368
+ changeFilterActive(outerIndex, filterOption)
369
+ setSelectedFilter(e.target)
370
+ }
371
+ }}
372
+ >
318
373
  {filterOption}
319
374
  </button>
320
375
  )
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
 
3
- export default function LegendCircle({ fill }) {
3
+ export default function LegendCircle({ fill, borderColor }) {
4
4
  const styles = {
5
5
  marginRight: '5px',
6
6
  borderRadius: '300px',
@@ -8,7 +8,7 @@ export default function LegendCircle({ fill }) {
8
8
  display: 'inline-block',
9
9
  height: '1em',
10
10
  width: '1em',
11
- border: 'rgba(0,0,0,.3) 1px solid',
11
+ border: borderColor ? `${borderColor} 1px solid` : 'rgba(0,0,0,.3) 1px solid',
12
12
  backgroundColor: fill
13
13
  }
14
14
 
@@ -134,11 +134,11 @@ const Section = ({ children, classes }) => {
134
134
  )
135
135
  }
136
136
 
137
- const CoveMediaControls = () => null
137
+ const MediaControls = () => null
138
138
 
139
- CoveMediaControls.Section = Section
140
- CoveMediaControls.Link = Link
141
- CoveMediaControls.Button = Button
142
- CoveMediaControls.generateMedia = generateMedia
139
+ MediaControls.Section = Section
140
+ MediaControls.Link = Link
141
+ MediaControls.Button = Button
142
+ MediaControls.generateMedia = generateMedia
143
143
 
144
- export default CoveMediaControls
144
+ export default MediaControls
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+ import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
3
+ import { Draggable } from '@hello-pangea/dnd'
4
+ import Icon from '@cdc/core/components/ui/Icon'
5
+
6
+ const RepeatableGroupSection = props => {
7
+ return <div className='repeatable-group'>{props.children}</div>
8
+ }
9
+
10
+ export const RepeatableGroupItem = props => {
11
+ const { item, index, title } = props
12
+
13
+ return (
14
+ <Draggable key={item} draggableId={`draggable-repeatable-item-${item}`} index={index}>
15
+ {(provided, snapshot) => (
16
+ <div key={index} className={snapshot.isDragging ? 'currently-dragging' : ''} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
17
+ <Accordion allowZeroExpanded>
18
+ <AccordionItem className='series-item series-item--chart'>
19
+ <AccordionItemHeading className='series-item__title'>
20
+ <AccordionItemButton className={''}>
21
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
22
+ Title Section
23
+ </AccordionItemButton>
24
+ </AccordionItemHeading>
25
+ <AccordionItemPanel>panel section to be built</AccordionItemPanel>
26
+ </AccordionItem>
27
+ </Accordion>
28
+ </div>
29
+ )}
30
+ </Draggable>
31
+ )
32
+ }
33
+
34
+ export const RepeatableGroup = {
35
+ Section: RepeatableGroupSection,
36
+ Item: RepeatableGroupItem
37
+ }
@@ -33,7 +33,7 @@ const InputSelect = memo(({ label, value, options, fieldName, section = null, su
33
33
  }
34
34
 
35
35
  return (
36
- <label>
36
+ <label style={{ width: '100%', display: 'block' }}>
37
37
  {label && <span className='edit-label cove-input__label'>{label}</span>}
38
38
  <select
39
39
  className={required && !value ? 'warning' : ''}
@@ -21,6 +21,7 @@ const Accordion = ({ children }) => {
21
21
  <AccordionItem className='cove-accordion__item' key={index}>
22
22
  <AccordionItemHeading className='cove-accordion__heading'>
23
23
  <AccordionItemButton className='cove-accordion__button'>
24
+ {section.props.icon}
24
25
  {section.props.title}
25
26
  {section.props.tooltipText ? (
26
27
  <Tooltip position='bottom'>
@@ -43,6 +44,8 @@ const Accordion = ({ children }) => {
43
44
  Accordion.Section = AccordionSection
44
45
 
45
46
  Accordion.Section.propTypes = {
47
+ /* Icon for the accordion label */
48
+ icon: PropTypes.node,
46
49
  /* Title for the accordion label*/
47
50
  title: PropTypes.string,
48
51
  /* Tooltip for the accordion label*/
@@ -51,16 +51,16 @@ export const colorPalettes3 = {
51
51
  'monochrome-4': ['#C2C0FC', '#6a3d9a'],
52
52
  'monochrome-5': ['#fedab8', '#bf5b17'],
53
53
  'cool-1': ['#b2df8a', '#1f78b4'],
54
- 'cool-2': ['#a6cee3', '#33A02C'],
54
+ 'cool-2': ['#a6cee3', '#72D66B'],
55
55
  'cool-3': ['#C2C0FC', '#386cb0'],
56
- 'cool-4': ['#33A02c', '#6a3d9a'],
56
+ 'cool-4': ['#72D66B', '#6a3d9a'],
57
57
  'cool-5': ['#a6cee3', '#6a3d9a'],
58
58
  'warm-1': ['#e31a1c', '#fedab8'],
59
59
  'complementary-1': ['#1f78b4', '#e6ab02'],
60
60
  'complementary-2': ['#1f78b4', '#ff7f00'],
61
61
  'complementary-3': ['#6a3d9a', '#ff7f00'],
62
62
  'complementary-4': ['#6a3d9a', '#e6ab02'],
63
- 'complementary-5': ['#e31a90', '#1b9e77']
63
+ 'complementary-5': ['#df168c', '#1EB386']
64
64
  }
65
65
 
66
66
  export const colorPalettesChart = updatePaletteNames(colorPalettes2)
@@ -11,109 +11,109 @@ export class DataTransform {
11
11
 
12
12
  //Performs standardizations that can be completed automatically without use input
13
13
  autoStandardize(data) {
14
- const errorsFound = [];
14
+ const errorsFound = []
15
15
 
16
16
  // Empty data
17
17
  if (0 === data.length) {
18
- errorsFound.push(this.constants.errorMessageEmptyData);
18
+ errorsFound.push(this.constants.errorMessageEmptyData)
19
19
  }
20
20
 
21
21
  // Does it have the correct data structure?
22
22
  if (!data.filter || data.filter(row => typeof row !== 'object').length > 0) {
23
- errorsFound.push(this.constants.errorMessageFormat);
23
+ errorsFound.push(this.constants.errorMessageFormat)
24
24
  }
25
25
 
26
26
  if (errorsFound.length > 0) {
27
- console.error(errorsFound);
28
- return undefined;
27
+ console.error(errorsFound)
28
+ return undefined
29
29
  }
30
30
 
31
31
  //Convert array of arrays, to array of objects
32
32
  if (data.filter(row => row.constructor !== Object).length > 0) {
33
- let standardizedData = [];
33
+ let standardizedData = []
34
34
  for (let row = 1; row < data.length; row++) {
35
- let standardizedRow = {};
35
+ let standardizedRow = {}
36
36
  data[row].forEach((datum, col) => {
37
- standardizedRow[data[0][col]] = datum;
37
+ standardizedRow[data[0][col]] = datum
38
38
  })
39
- standardizedData.push(standardizedRow);
39
+ standardizedData.push(standardizedRow)
40
40
  }
41
- data = standardizedData;
41
+ data = standardizedData
42
42
  }
43
43
 
44
- return data;
44
+ return data
45
45
  }
46
46
 
47
47
  //Performs standardizations based on developer provided description of the data
48
48
  developerStandardize(data, description) {
49
49
  //Validate the description object
50
50
  if (!description) {
51
- return undefined;
51
+ return undefined
52
52
  }
53
53
 
54
54
  if (description.horizontal === undefined || description.series === undefined) {
55
- return undefined;
55
+ return undefined
56
56
  }
57
57
 
58
58
  if (description.series === true && description.horizontal === false && description.singleRow === undefined) {
59
- return undefined;
59
+ return undefined
60
60
  }
61
61
 
62
62
  if (description.horizontal === true) {
63
63
  if (description.series === true) {
64
64
  if (!description.seriesKey) {
65
- return undefined;
65
+ return undefined
66
66
  }
67
67
 
68
- let standardizedMapped = {};
69
- let standardized = [];
68
+ let standardizedMapped = {}
69
+ let standardized = []
70
70
  data.forEach(row => {
71
- let nonNumericKeys = [];
71
+ let nonNumericKeys = []
72
72
  Object.keys(row).forEach(key => {
73
73
  if (key !== description.seriesKey && isNaN(parseFloat(row[key]))) {
74
- nonNumericKeys.push(key);
74
+ nonNumericKeys.push(key)
75
75
  }
76
76
  })
77
77
 
78
78
  Object.keys(row).forEach(key => {
79
79
  if (key !== description.seriesKey && nonNumericKeys.indexOf(key) === -1) {
80
- let uniqueKey = key + '|' + nonNumericKeys.map(nonNumericKey => nonNumericKey + '=' + row[nonNumericKey]);
80
+ let uniqueKey = key + '|' + nonNumericKeys.map(nonNumericKey => nonNumericKey + '=' + row[nonNumericKey])
81
81
  if (!standardizedMapped[uniqueKey]) {
82
- standardizedMapped[uniqueKey] = { [row[description.seriesKey]]: row[key], key };
82
+ standardizedMapped[uniqueKey] = { [row[description.seriesKey]]: row[key], key }
83
83
  nonNumericKeys.forEach(nonNumericKey => {
84
- standardizedMapped[uniqueKey][nonNumericKey] = row[nonNumericKey];
84
+ standardizedMapped[uniqueKey][nonNumericKey] = row[nonNumericKey]
85
85
  })
86
86
  }
87
- standardizedMapped[uniqueKey][row[description.seriesKey]] = row[key];
87
+ standardizedMapped[uniqueKey][row[description.seriesKey]] = row[key]
88
88
  }
89
89
  })
90
90
  })
91
91
 
92
92
  Object.keys(standardizedMapped).forEach(key => {
93
- standardized.push(standardizedMapped[key]);
93
+ standardized.push(standardizedMapped[key])
94
94
  })
95
95
 
96
- return standardized;
96
+ return standardized
97
97
  } else {
98
- let standardized = [];
98
+ let standardized = []
99
99
 
100
100
  data.forEach(row => {
101
- let nonNumericKeys = [];
101
+ let nonNumericKeys = []
102
102
  Object.keys(row).forEach(key => {
103
103
  if (isNaN(parseFloat(row[key]))) {
104
- nonNumericKeys.push(key);
104
+ nonNumericKeys.push(key)
105
105
  }
106
106
  })
107
107
 
108
108
  Object.keys(row).forEach(key => {
109
109
  if (nonNumericKeys.indexOf(key) === -1) {
110
- let newRow = { key, value: row[key] };
110
+ let newRow = { key, value: row[key] }
111
111
 
112
112
  nonNumericKeys.forEach(nonNumericKey => {
113
- newRow[nonNumericKey] = row[nonNumericKey];
113
+ newRow[nonNumericKey] = row[nonNumericKey]
114
114
  })
115
115
 
116
- standardized.push(newRow);
116
+ standardized.push(newRow)
117
117
  }
118
118
  })
119
119
  })
@@ -122,43 +122,43 @@ export class DataTransform {
122
122
  }
123
123
  } else if (description.series === true && description.singleRow === false) {
124
124
  if (description.seriesKey !== undefined && description.xKey !== undefined && (description.valueKey !== undefined || (description.valueKeys !== undefined && description.valueKeys.length > 0))) {
125
- if(description.valueKeys !== undefined){
126
- let standardizedMapped = {};
127
- let standardized = [];
128
- let valueKeys = description.valueKeys;
129
- if(description.ignoredKeys && description.ignoredKeys.length > 0){
130
- valueKeys = valueKeys.concat(description.ignoredKeys);
125
+ if (description.valueKeys !== undefined) {
126
+ let standardizedMapped = {}
127
+ let standardized = []
128
+ let valueKeys = description.valueKeys
129
+ if (description.ignoredKeys && description.ignoredKeys.length > 0) {
130
+ valueKeys = valueKeys.concat(description.ignoredKeys)
131
131
  }
132
132
 
133
133
  data.forEach(row => {
134
134
  valueKeys.forEach(valueKey => {
135
- let extraKeys = [];
136
- let uniqueKey = row[description.xKey] + '|' + valueKey;
135
+ let extraKeys = []
136
+ let uniqueKey = row[description.xKey] + '|' + valueKey
137
137
  Object.keys(row).forEach(key => {
138
138
  if (key !== description.xKey && key !== description.seriesKey && valueKeys.indexOf(key) === -1) {
139
- uniqueKey += '|' + key + '=' + row[key];
140
- extraKeys.push(key);
139
+ uniqueKey += '|' + key + '=' + row[key]
140
+ extraKeys.push(key)
141
141
  }
142
142
  })
143
143
 
144
- if(!standardizedMapped[uniqueKey]){
145
- standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey], '**Numeric Value Property**': valueKey };
144
+ if (!standardizedMapped[uniqueKey]) {
145
+ standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey], '**Numeric Value Property**': valueKey }
146
146
  extraKeys.forEach(key => {
147
- standardizedMapped[uniqueKey][key] = row[key];
147
+ standardizedMapped[uniqueKey][key] = row[key]
148
148
  })
149
149
  }
150
150
 
151
- standardizedMapped[uniqueKey][row[description.seriesKey]] = row[valueKey];
152
- });
151
+ standardizedMapped[uniqueKey][row[description.seriesKey]] = row[valueKey]
152
+ })
153
153
  })
154
154
 
155
155
  Object.keys(standardizedMapped).forEach(key => {
156
- if(!description.ignoredKeys || description.ignoredKeys.indexOf(standardizedMapped[key]['**Numeric Value Property**']) === -1){
157
- standardized.push(standardizedMapped[key]);
156
+ if (!description.ignoredKeys || description.ignoredKeys.indexOf(standardizedMapped[key]['**Numeric Value Property**']) === -1) {
157
+ standardized.push(standardizedMapped[key])
158
158
  }
159
159
  })
160
160
 
161
- return standardized;
161
+ return standardized
162
162
  } else {
163
163
  let standardizedMapped = {}
164
164
  let standardized = []
@@ -190,14 +190,14 @@ export class DataTransform {
190
190
  return standardized
191
191
  }
192
192
  } else {
193
- return undefined;
193
+ return undefined
194
194
  }
195
195
  }
196
196
 
197
- return data;
197
+ return data
198
198
  }
199
199
 
200
- /**
200
+ /**
201
201
  * cleanData
202
202
  *
203
203
  // This cleans a data set by:
@@ -207,21 +207,21 @@ export class DataTransform {
207
207
  *
208
208
  * Inputs: data as array, excludeKey indicates which key to use to NOT clean
209
209
  * Example: "Date" should not be cleaned if part of the data
210
- *
210
+ *
211
211
  * Output: returns the cleanedData
212
- *
212
+ *
213
213
  * Set testing = true if you need to see before and after data
214
- *
214
+ *
215
215
  */
216
- cleanData (data, excludeKey, testing = false) {
216
+ cleanData(data, excludeKey, testing = false) {
217
217
  let cleanedupData = []
218
218
  if (testing) console.log('## Data to clean=', data)
219
219
  if (excludeKey === undefined) {
220
220
  console.log('COVE: cleanData excludeKey undefined')
221
- return data // because no excludeKey
221
+ return data // because no excludeKey
222
222
  }
223
223
  data.forEach(function (d, i) {
224
- if (testing) console.log("clean", i, " d", d);
224
+ if (testing) console.log('clean', i, ' d', d)
225
225
  let cleaned = {}
226
226
  Object.keys(d).forEach(function (key) {
227
227
  if (key === excludeKey) {
@@ -229,27 +229,27 @@ export class DataTransform {
229
229
  cleaned[key] = d[key]
230
230
  } else {
231
231
  // remove comma and dollar signs
232
- if (testing) console.log("typeof d[key] is ", typeof d[key]);
233
- let tmp = "";
232
+ if (testing) console.log('typeof d[key] is ', typeof d[key])
233
+ let tmp = ''
234
234
  if (typeof d[key] === 'string') {
235
235
  tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,\$]/g, '') : ''
236
236
  } else {
237
- tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
237
+ tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
238
238
  }
239
239
  if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
240
240
  cleaned[key] = tmp
241
- } else { cleaned[key] = '' }
241
+ } else {
242
+ cleaned[key] = ''
243
+ }
242
244
  // if you get here, then return nothing to skip bad data point
243
245
  }
244
246
  })
245
- if (testing) console.log("cleaned=", cleaned)
247
+ if (testing) console.log('cleaned=', cleaned)
246
248
  cleanedupData.push(cleaned)
247
249
  })
248
250
  if (testing) console.log('## cleanedData =', cleanedupData)
249
251
  return cleanedupData
250
252
  }
251
-
252
-
253
253
  }
254
254
 
255
255
  export default DataTransform
@@ -0,0 +1,9 @@
1
+ import { timeFormat, timeParse } from 'd3-time-format'
2
+
3
+ export function formatDate(format = undefined, date) {
4
+ return timeFormat(format)(date)
5
+ }
6
+
7
+ export function parseDate(format = undefined, dateString) {
8
+ return timeParse(format)(dateString) || new Date()
9
+ }