@cdc/core 4.25.3 → 4.25.6-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 (89) hide show
  1. package/assets/icon-close.svg +1 -1
  2. package/components/Alert/components/Alert.tsx +1 -1
  3. package/components/DataTable/DataTable.tsx +18 -16
  4. package/components/DataTable/DataTableStandAlone.tsx +15 -9
  5. package/components/DataTable/components/CellAnchor.tsx +1 -1
  6. package/components/DataTable/components/ChartHeader.tsx +8 -5
  7. package/components/DataTable/components/DataTableEditorPanel.tsx +25 -3
  8. package/components/DataTable/components/MapHeader.tsx +1 -0
  9. package/components/DataTable/helpers/chartCellMatrix.tsx +14 -10
  10. package/components/DataTable/helpers/getChartCellValue.ts +42 -26
  11. package/components/DataTable/helpers/mapCellMatrix.tsx +25 -7
  12. package/components/DownloadButton.tsx +17 -2
  13. package/components/EditorPanel/DataTableEditor.tsx +1 -1
  14. package/components/EditorPanel/FootnotesEditor.tsx +76 -22
  15. package/components/EditorPanel/Inputs.tsx +12 -4
  16. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +3 -2
  17. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +51 -35
  18. package/components/Filters/Filters.tsx +158 -461
  19. package/components/Filters/components/Dropdown.tsx +39 -0
  20. package/components/Filters/components/Tabs.tsx +82 -0
  21. package/components/Filters/helpers/getChangedFilters.ts +31 -0
  22. package/components/Filters/helpers/getNestedOptions.ts +2 -2
  23. package/components/Filters/helpers/handleSorting.ts +2 -2
  24. package/components/Filters/helpers/tests/getChangedFilters.test.ts +92 -0
  25. package/components/Filters/helpers/tests/getNestedOptions.test.ts +31 -0
  26. package/components/Filters/index.ts +1 -1
  27. package/components/Footnotes/Footnotes.tsx +1 -1
  28. package/components/Footnotes/FootnotesStandAlone.tsx +8 -33
  29. package/components/Layout/components/Visualization/index.tsx +4 -3
  30. package/components/Legend/Legend.Gradient.tsx +68 -24
  31. package/components/MultiSelect/MultiSelect.tsx +3 -6
  32. package/components/MultiSelect/multiselect.styles.css +2 -0
  33. package/components/NestedDropdown/NestedDropdown.tsx +21 -21
  34. package/components/RichTooltip/RichTooltip.tsx +37 -0
  35. package/components/RichTooltip/richTooltip.css +16 -0
  36. package/components/Table/Table.tsx +142 -142
  37. package/components/Table/components/Row.tsx +1 -1
  38. package/components/Table/table.styles.css +10 -0
  39. package/components/_stories/DataTable.stories.tsx +9 -2
  40. package/components/_stories/Table.stories.tsx +1 -1
  41. package/components/_stories/styles.scss +0 -4
  42. package/components/ui/Accordion.jsx +8 -1
  43. package/components/ui/Title/index.tsx +4 -1
  44. package/components/ui/Title/{Title.scss → title.styles.css} +0 -2
  45. package/components/ui/_stories/Colors.stories.mdx +220 -0
  46. package/components/ui/_stories/IconGallery.stories.mdx +14 -0
  47. package/components/ui/_stories/Title.stories.tsx +29 -4
  48. package/components/ui/accordion.styles.css +3 -0
  49. package/data/colorPalettes.js +0 -1
  50. package/dist/cove-main.css +3 -8
  51. package/dist/cove-main.css.map +1 -1
  52. package/helpers/constants.ts +6 -0
  53. package/helpers/cove/accessibility.ts +7 -8
  54. package/helpers/cove/number.ts +5 -3
  55. package/helpers/coveUpdateWorker.ts +9 -1
  56. package/helpers/filterOrderOptions.ts +17 -0
  57. package/helpers/formatConfigBeforeSave.ts +19 -32
  58. package/helpers/isNumber.ts +20 -0
  59. package/helpers/isRightAlignedTableValue.js +1 -1
  60. package/helpers/pivotData.ts +16 -11
  61. package/helpers/tests/pivotData.test.ts +74 -0
  62. package/helpers/updateFieldFactory.ts +1 -0
  63. package/helpers/ver/4.25.3.ts +25 -2
  64. package/helpers/ver/4.25.4.ts +110 -0
  65. package/helpers/ver/4.25.6.ts +36 -0
  66. package/helpers/ver/4.25.7.ts +26 -0
  67. package/helpers/ver/tests/4.25.4.test.ts +89 -0
  68. package/helpers/ver/tests/4.25.6.test.ts +84 -0
  69. package/helpers/viewports.ts +4 -0
  70. package/package.json +7 -6
  71. package/styles/_global-variables.scss +3 -0
  72. package/styles/_global.scss +0 -4
  73. package/styles/_reset.scss +0 -6
  74. package/styles/filters.scss +0 -4
  75. package/styles/v2/main.scss +0 -5
  76. package/types/Axis.ts +2 -0
  77. package/types/DataSet.ts +14 -0
  78. package/types/Footnotes.ts +5 -2
  79. package/types/General.ts +1 -0
  80. package/types/Legend.ts +1 -0
  81. package/types/Table.ts +1 -0
  82. package/types/Visualization.ts +3 -12
  83. package/types/VizFilter.ts +3 -0
  84. package/components/ui/_stories/Colors.stories.tsx +0 -92
  85. package/components/ui/_stories/Icon.stories.tsx +0 -29
  86. package/helpers/cove/fontSettings.ts +0 -2
  87. package/helpers/isNumber.js +0 -24
  88. package/helpers/isNumberLog.js +0 -18
  89. /package/helpers/{fetchRemoteData.js → fetchRemoteData.ts} +0 -0
@@ -1,75 +1,129 @@
1
+ import React, { useState } from 'react'
1
2
  import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
2
3
  import _ from 'lodash'
3
4
  import Footnotes, { Footnote } from '../../types/Footnotes'
4
5
  import { footnotesSymbols } from '../../helpers/footnoteSymbols'
5
6
  import InputSelect from '../inputs/InputSelect'
6
7
  import { TextField } from './Inputs'
8
+ import { Datasets } from '@cdc/core/types/DataSet'
9
+ import DataTransform from '../../helpers/DataTransform'
10
+ import fetchRemoteData from '../../helpers/fetchRemoteData'
11
+ import Loader from '../Loader'
12
+ import { AnyVisualization } from '../../types/Visualization'
7
13
  interface FootnotesEditorProps {
8
- config: Footnotes
9
- updateField: UpdateFieldFunc<Footnote[]>
14
+ config: AnyVisualization
15
+ updateField: UpdateFieldFunc<Footnote[] | Object>
16
+ datasets: Datasets
10
17
  }
11
18
 
12
- const FootnotesEditor: React.FC<FootnotesEditorProps> = ({ config, updateField }) => {
19
+ const FootnotesEditor: React.FC<FootnotesEditorProps> = ({ config, updateField, datasets }) => {
20
+ const footnotesConfig = config.footnotes || {}
21
+ const [errorMessage, setErrorMessage] = useState('')
22
+ const [loadingAPIData, setLoadingAPIData] = useState(false)
23
+ const [datasetsCache, setDatasetsCache] = useState(datasets || {})
24
+ const transform = new DataTransform()
25
+
26
+ const fetchData = async datasetKey => {
27
+ const { data, dataUrl } = datasetsCache[datasetKey]
28
+ if (!dataUrl) return data
29
+ let newData = data
30
+ const noCachedData = dataUrl && !data
31
+ const dataSetChanged = datasetKey !== footnotesConfig.dataKey
32
+ setErrorMessage('')
33
+ if (dataSetChanged || noCachedData) {
34
+ setLoadingAPIData(true)
35
+ try {
36
+ newData = await fetchRemoteData(dataUrl)
37
+ newData = transform.autoStandardize(newData)
38
+ } catch (e) {
39
+ setErrorMessage('There was an issue loading the data source. Please check the datasource URL and try again.')
40
+ }
41
+
42
+ setLoadingAPIData(false)
43
+ }
44
+ return newData
45
+ }
46
+
13
47
  const addStaticFootnote = () => {
14
- const newStaticNotes = [...(config.staticFootnotes || []), { text: 'Add Footnote Text' }]
15
- updateField(null, null, 'staticFootnotes', newStaticNotes)
48
+ const newStaticNotes = [...(footnotesConfig.staticFootnotes || []), { text: 'Add Footnote Text' }]
49
+ updateField('footnotes', null, 'staticFootnotes', newStaticNotes)
16
50
  }
17
51
 
18
52
  const updateStaticFootnote = (footnoteIndex, footnoteUpdate: Footnote) => {
19
- const footnoteCopy = _.cloneDeep(config.staticFootnotes)
53
+ const footnoteCopy = _.cloneDeep(footnotesConfig.staticFootnotes)
20
54
  footnoteCopy[footnoteIndex] = footnoteUpdate
21
- updateField(null, null, 'staticFootnotes', footnoteCopy)
55
+ updateField('footnotes', null, 'staticFootnotes', footnoteCopy)
22
56
  }
23
57
 
24
58
  const deleteStaticFootnote = footnoteIndex => {
25
- const footnoteCopy = _.cloneDeep(config.staticFootnotes)
59
+ const footnoteCopy = _.cloneDeep(footnotesConfig.staticFootnotes)
26
60
  footnoteCopy.splice(footnoteIndex, 1)
27
- updateField(null, null, 'staticFootnotes', footnoteCopy)
61
+ updateField('footnotes', null, 'staticFootnotes', footnoteCopy)
28
62
  }
29
63
 
30
64
  const getOptions = (opts: string[]) => {
31
65
  return [['', '--Select--']].concat(opts.map(key => [key, key]))
32
66
  }
33
67
 
34
- const datasets = config.datasets || {}
68
+ const dataColumns = footnotesConfig.dataKey
69
+ ? getOptions(Object.keys(datasetsCache[footnotesConfig.dataKey]?.data?.[0] || {}))
70
+ : []
71
+ const dataSetOptions = getOptions(Object.keys(datasetsCache))
72
+
73
+ const changeFootnoteDataKey = async value => {
74
+ if (value) {
75
+ if (!datasetsCache[value]) {
76
+ const newData = await fetchData(value)
77
+ setDatasetsCache({ ...datasetsCache, [value]: { ...datasetsCache[value], data: newData } })
78
+ }
79
+ } else {
80
+ updateField('footnotes', null, 'dynamicFootnotes', {})
81
+ }
35
82
 
36
- const dataColumns = config.dataKey ? getOptions(Object.keys(datasets[config.dataKey]?.data?.[0] || {})) : []
37
- const dataSetOptions = getOptions(Object.keys(datasets))
83
+ updateField('footnotes', null, 'dataKey', value)
84
+ }
38
85
  return (
39
86
  <>
87
+ {loadingAPIData && <Loader fullScreen />}
40
88
  <em>Dynamic Footnotes</em>
41
89
  <div className='row border p-2'>
42
90
  <InputSelect
43
91
  label='Select a Footnote Dataset'
44
- value={config.dataKey}
92
+ value={footnotesConfig.dataKey}
45
93
  options={dataSetOptions}
46
94
  fieldName='dataKey'
47
- updateField={updateField}
95
+ updateField={(section, subsection, fieldname, dataKey) => {
96
+ changeFootnoteDataKey(dataKey)
97
+ }}
48
98
  />
99
+ {errorMessage && <p className='text-danger'>{errorMessage}</p>}
49
100
 
50
- {config.dataKey && (
101
+ {footnotesConfig.dataKey && (
51
102
  <div className='p-3'>
52
103
  <InputSelect
53
104
  label='Footnote Symbol Column'
54
- value={config.dynamicFootnotes?.symbolColumn}
105
+ value={footnotesConfig.dynamicFootnotes?.symbolColumn}
55
106
  options={dataColumns}
56
- section='dynamicFootnotes'
107
+ section='footnotes'
108
+ subsection='dynamicFootnotes'
57
109
  fieldName='symbolColumn'
58
110
  updateField={updateField}
59
111
  />
60
112
  <InputSelect
61
113
  label='Footnote Text Column'
62
- value={config.dynamicFootnotes?.textColumn}
114
+ value={footnotesConfig.dynamicFootnotes?.textColumn}
63
115
  options={dataColumns}
64
- section='dynamicFootnotes'
116
+ section='footnotes'
117
+ subsection='dynamicFootnotes'
65
118
  fieldName='textColumn'
66
119
  updateField={updateField}
67
120
  />
68
121
  <InputSelect
69
122
  label='Footnote Order Column'
70
- value={config.dynamicFootnotes?.orderColumn}
123
+ value={footnotesConfig.dynamicFootnotes?.orderColumn}
71
124
  options={dataColumns}
72
- section='dynamicFootnotes'
125
+ section='footnotes'
126
+ subsection='dynamicFootnotes'
73
127
  fieldName='orderColumn'
74
128
  updateField={updateField}
75
129
  />
@@ -81,7 +135,7 @@ const FootnotesEditor: React.FC<FootnotesEditorProps> = ({ config, updateField }
81
135
 
82
136
  <em>Static Footnotes</em>
83
137
 
84
- {config.staticFootnotes?.map((note, index) => (
138
+ {footnotesConfig.staticFootnotes?.map((note, index) => (
85
139
  <div key={index} className='row border p-2'>
86
140
  <div className='col-8'>
87
141
  <InputSelect
@@ -1,6 +1,6 @@
1
1
  import { memo, useEffect, useState } from 'react'
2
2
  import { useDebounce } from 'use-debounce'
3
- import { DROPDOWN_STYLES } from '../Filters/Filters'
3
+ import { DROPDOWN_STYLES } from '../Filters/components/Dropdown'
4
4
 
5
5
  export type Input = {
6
6
  label: string
@@ -118,9 +118,17 @@ const CheckBox = memo((props: CheckboxProps) => {
118
118
  return <></>
119
119
  }
120
120
  return (
121
- <label className='checkbox column-heading'>
121
+ <label
122
+ className='checkbox column-heading'
123
+ onClick={e => {
124
+ if (!['SPAN', 'INPUT'].includes(e.target.nodeName)) {
125
+ e.preventDefault()
126
+ }
127
+ }}
128
+ >
122
129
  <input
123
130
  type='checkbox'
131
+ className='edit-checkbox'
124
132
  name={fieldName}
125
133
  checked={value}
126
134
  onChange={e => {
@@ -161,7 +169,7 @@ const Select = memo((props: SelectProps) => {
161
169
  initial: initialValue,
162
170
  ...attributes
163
171
  } = props
164
- const optionsJsx = options.map((option, index) => {
172
+ const optionsJsx = options?.map((option, index) => {
165
173
  if (typeof option === 'string') {
166
174
  return (
167
175
  <option value={option} key={index}>
@@ -178,7 +186,7 @@ const Select = memo((props: SelectProps) => {
178
186
  })
179
187
 
180
188
  if (initialValue) {
181
- optionsJsx.unshift(
189
+ optionsJsx?.unshift(
182
190
  <option value='' key='initial'>
183
191
  {initialValue}
184
192
  </option>
@@ -1,6 +1,7 @@
1
1
  import _ from 'lodash'
2
2
  import { SubGrouping, VizFilter, OrderBy } from '../../../types/VizFilter'
3
- import { filterOrderOptions, handleSorting } from '../../Filters'
3
+ import { handleSorting } from '../../Filters/helpers/handleSorting'
4
+ import { filterOrderOptions } from '../../../helpers/filterOrderOptions'
4
5
  import FilterOrder from './components/FilterOrder'
5
6
  import { Visualization } from '../../../types/Visualization'
6
7
  import { useMemo } from 'react'
@@ -266,7 +267,7 @@ const NestedDropdownEditor: React.FC<NestedDropdownEditorProps> = ({
266
267
  const orderedSubGroupValues = subGrouping.valuesLookup[groupName].orderedValues
267
268
  return (
268
269
  <div key={`group-subgroup-values-${groupName}-${i}`}>
269
- <span className='font-weight-bold'>{groupName}</span>
270
+ <span className='font-weight-bold fw-bold'>{groupName}</span>
270
271
  <FilterOrder
271
272
  key={`subgroup-values-${groupName}-${i}`}
272
273
  orderedValues={orderedSubGroupValues}
@@ -5,7 +5,9 @@ import { Visualization } from '../../../types/Visualization'
5
5
  import { UpdateFieldFunc } from '../../../types/UpdateFieldFunc'
6
6
  import _ from 'lodash'
7
7
  import { MultiSelectFilter, VizFilter, VizFilterStyle } from '../../../types/VizFilter'
8
- import { filterStyleOptions, handleSorting, filterOrderOptions } from '../../Filters'
8
+ import { handleSorting } from '../../Filters/helpers/handleSorting'
9
+ import { filterOrderOptions } from '../../../helpers/filterOrderOptions'
10
+ import { filterStyleOptions } from '../../Filters'
9
11
  import FieldSetWrapper from '../FieldSetWrapper'
10
12
 
11
13
  import FilterOrder from './components/FilterOrder'
@@ -17,9 +19,10 @@ type VizFilterProps = {
17
19
  config: Visualization
18
20
  updateField: UpdateFieldFunc<string | VizFilter[] | VizFilter>
19
21
  rawData: Object[]
22
+ hasFootnotes?: boolean
20
23
  }
21
24
 
22
- const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawData }) => {
25
+ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawData, hasFootnotes }) => {
23
26
  const openControls = useState({})
24
27
  const dataColumns = useMemo(() => {
25
28
  return _.uniq(_.flatten(rawData?.map(row => Object.keys(row))))
@@ -169,20 +172,6 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
169
172
  options={filterStyleOptions}
170
173
  />
171
174
 
172
- <Select
173
- value={filter.defaultValue}
174
- options={
175
- filter.resetLabel
176
- ? [filter.resetLabel, ...config.filters?.[filterIndex].values]
177
- : config.filters?.[filterIndex].values
178
- }
179
- updateField={(_section, _subSection, _key, value) => {
180
- updateFilterDefaultValue(filterIndex, value)
181
- }}
182
- label='Filter Default Value'
183
- initial='Select'
184
- />
185
-
186
175
  {filter.filterStyle !== 'nested-dropdown' ? (
187
176
  <>
188
177
  <Select
@@ -194,16 +183,21 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
194
183
  initial='- Select Option -'
195
184
  />
196
185
 
197
- <label>
198
- <span className='edit-showDropdown column-heading'>Show Filter Input</span>
199
- <input
200
- type='checkbox'
201
- checked={filter.showDropdown === undefined ? true : filter.showDropdown}
202
- onChange={e => {
203
- updateFilterProp('showDropdown', filterIndex, e.target.checked)
186
+ {filter.columnName && (
187
+ <Select
188
+ value={filter.defaultValue}
189
+ options={
190
+ filter.resetLabel
191
+ ? [filter.resetLabel, ...config.filters?.[filterIndex].values]
192
+ : config.filters?.[filterIndex].values
193
+ }
194
+ updateField={(_section, _subSection, _key, value) => {
195
+ updateFilterDefaultValue(filterIndex, value)
204
196
  }}
197
+ label='Filter Default Value'
198
+ initial='Select'
205
199
  />
206
- </label>
200
+ )}
207
201
 
208
202
  <label>
209
203
  <span className='edit-label column-heading'>Label</span>
@@ -238,17 +232,6 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
238
232
  />
239
233
  )}
240
234
 
241
- <label>
242
- <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
243
- <input
244
- type='text'
245
- value={filter.setByQueryParameter}
246
- onChange={e => {
247
- updateFilterProp('setByQueryParameter', filterIndex, e.target.value)
248
- }}
249
- />
250
- </label>
251
-
252
235
  <Select
253
236
  value={filter.order || 'asc'}
254
237
  fieldName='order'
@@ -276,6 +259,27 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
276
259
  options={dataColumns}
277
260
  />
278
261
  )}
262
+ <label>
263
+ <span className='edit-label column-heading'>Default Value Set By Query String Parameter</span>
264
+ <input
265
+ type='text'
266
+ value={filter.setByQueryParameter}
267
+ onChange={e => {
268
+ updateFilterProp('setByQueryParameter', filterIndex, e.target.value)
269
+ }}
270
+ />
271
+ </label>
272
+
273
+ <label>
274
+ <input
275
+ type='checkbox'
276
+ checked={filter.showDropdown === undefined ? true : filter.showDropdown}
277
+ onChange={e => {
278
+ updateFilterProp('showDropdown', filterIndex, e.target.checked)
279
+ }}
280
+ />
281
+ <span className='edit-showDropdown column-heading'>Show Filter</span>
282
+ </label>
279
283
  </>
280
284
  ) : (
281
285
  <NestedDropdownEditor
@@ -289,6 +293,18 @@ const VizFilterEditor: React.FC<VizFilterProps> = ({ config, updateField, rawDat
289
293
  updateFilterStyle={updateFilterStyle}
290
294
  />
291
295
  )}
296
+ {hasFootnotes && (
297
+ <label>
298
+ <input
299
+ type='checkbox'
300
+ checked={!!filter.filterFootnotes}
301
+ onChange={e => {
302
+ updateFilterProp('filterFootnotes', filterIndex, e.target.checked)
303
+ }}
304
+ />
305
+ <span className='edit-showDropdown column-heading'>Filter Footnotes</span>
306
+ </label>
307
+ )}
292
308
  <label>
293
309
  <span className='edit-label column-heading'>
294
310
  Filter Parents{' '}