@cdc/core 4.24.12-2 → 4.25.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 (63) hide show
  1. package/components/DataTable/DataTable.tsx +26 -36
  2. package/components/DataTable/components/ChartHeader.tsx +3 -2
  3. package/components/DataTable/components/ExpandCollapse.tsx +1 -4
  4. package/components/DataTable/data-table.css +2 -7
  5. package/components/DataTable/helpers/customSort.ts +2 -2
  6. package/components/DataTable/helpers/mapCellMatrix.tsx +83 -60
  7. package/components/DataTable/types/TableConfig.ts +0 -1
  8. package/components/EditorPanel/FootnotesEditor.tsx +49 -7
  9. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +3 -2
  10. package/components/Filters/Filters.tsx +16 -9
  11. package/components/Footnotes/Footnotes.tsx +1 -1
  12. package/components/Layout/components/Visualization/index.tsx +18 -4
  13. package/components/Layout/components/Visualization/visualizations.scss +1 -1
  14. package/components/Legend/Legend.Gradient.tsx +1 -4
  15. package/components/Legend/index.tsx +1 -1
  16. package/components/LegendShape.tsx +2 -3
  17. package/components/MediaControls.jsx +32 -8
  18. package/components/NestedDropdown/NestedDropdown.tsx +7 -12
  19. package/components/NestedDropdown/nesteddropdown.styles.css +11 -5
  20. package/components/Table/Table.tsx +0 -6
  21. package/components/Table/components/Row.tsx +2 -5
  22. package/components/_stories/DataTable.stories.tsx +1 -2
  23. package/components/elements/Button.jsx +38 -19
  24. package/components/elements/Confirm.tsx +45 -0
  25. package/components/elements/Error.tsx +24 -0
  26. package/components/managers/DataDesigner.tsx +198 -143
  27. package/components/ui/Title/Title.scss +12 -5
  28. package/components/ui/Title/index.tsx +1 -1
  29. package/dist/cove-main.css +77 -591
  30. package/dist/cove-main.css.map +1 -1
  31. package/helpers/DataTransform.ts +55 -63
  32. package/helpers/addValuesToFilters.ts +24 -9
  33. package/helpers/cove/accessibility.ts +24 -0
  34. package/helpers/cove/fontSettings.ts +1 -1
  35. package/helpers/cove/number.ts +1 -7
  36. package/helpers/coveUpdateWorker.ts +5 -1
  37. package/helpers/displayDataAsText.ts +64 -0
  38. package/helpers/formatConfigBeforeSave.ts +1 -1
  39. package/helpers/isOlderVersion.ts +20 -0
  40. package/helpers/missingRequiredSections.ts +20 -0
  41. package/helpers/tests/addValuesToFilters.test.ts +13 -0
  42. package/helpers/useDataVizClasses.ts +8 -4
  43. package/helpers/ver/4.24.11.ts +18 -0
  44. package/helpers/ver/4.25.1.ts +18 -0
  45. package/package.json +2 -2
  46. package/styles/_button-section.scss +2 -5
  47. package/styles/_global-variables.scss +17 -7
  48. package/styles/_global.scss +8 -12
  49. package/styles/_reset.scss +4 -5
  50. package/styles/_typography.scss +0 -20
  51. package/styles/_variables.scss +0 -3
  52. package/styles/base.scss +44 -5
  53. package/styles/cove-main.scss +1 -1
  54. package/styles/filters.scss +5 -6
  55. package/styles/v2/base/_general.scss +3 -2
  56. package/styles/v2/components/button.scss +0 -1
  57. package/styles/v2/main.scss +3 -4
  58. package/styles/v2/utils/index.scss +0 -1
  59. package/types/BoxPlot.ts +1 -0
  60. package/types/Runtime.ts +1 -0
  61. package/types/Table.ts +0 -1
  62. package/types/Version.ts +1 -1
  63. package/styles/v2/utils/_spacers.scss +0 -31
@@ -42,13 +42,30 @@ const generateMedia = (state, type, elementToCapture) => {
42
42
  // Apparently some packages use state.title where others use state.general.title
43
43
  const handleFileName = state => {
44
44
  // dashboard titles
45
- if (state?.dashboard?.title) return state.dashboard.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
45
+ if (state?.dashboard?.title)
46
+ return (
47
+ state.dashboard.title.replace(/\s+/g, '-').toLowerCase() +
48
+ '-' +
49
+ date.getDate() +
50
+ date.getMonth() +
51
+ date.getFullYear()
52
+ )
46
53
 
47
54
  // map titles
48
- if (state?.general?.title) return state.general.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
55
+ if (state?.general?.title)
56
+ return (
57
+ state.general.title.replace(/\s+/g, '-').toLowerCase() +
58
+ '-' +
59
+ date.getDate() +
60
+ date.getMonth() +
61
+ date.getFullYear()
62
+ )
49
63
 
50
64
  // chart titles
51
- if (state?.title) return state.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
65
+ if (state?.title)
66
+ return (
67
+ state.title.replace(/\s+/g, '-').toLowerCase() + '-' + date.getDate() + date.getMonth() + date.getFullYear()
68
+ )
52
69
 
53
70
  return 'no-title'
54
71
  }
@@ -59,7 +76,10 @@ const generateMedia = (state, type, elementToCapture) => {
59
76
 
60
77
  switch (type) {
61
78
  case 'image':
62
- html2canvas(baseSvg, {ignoreElements: el => el.className?.indexOf && el.className.search(/download-buttons|download-links|data-table-container/) !== -1}).then(canvas => {
79
+ html2canvas(baseSvg, {
80
+ ignoreElements: el =>
81
+ el.className?.indexOf && el.className.search(/download-buttons|download-links|data-table-container/) !== -1
82
+ }).then(canvas => {
63
83
  saveImageAs(canvas.toDataURL(), filename + '.png')
64
84
  })
65
85
  return
@@ -98,9 +118,14 @@ const handleTheme = state => {
98
118
 
99
119
  // Download CSV
100
120
  const Button = ({ state, text, type, title, elementToCapture }) => {
101
- const buttonClasses = ['btn', 'btn-download', `${handleTheme(state)}`]
121
+ const buttonClasses = ['btn', 'btn-primary']
102
122
  return (
103
- <button className={buttonClasses.join(' ')} title={title} onClick={() => generateMedia(state, type, elementToCapture)} style={{ lineHeight: '1.4em' }}>
123
+ <button
124
+ className={buttonClasses.join(' ')}
125
+ title={title}
126
+ onClick={() => generateMedia(state, type, elementToCapture)}
127
+ style={{ lineHeight: '1.4em' }}
128
+ >
104
129
  {buttonText[type]}
105
130
  </button>
106
131
  )
@@ -108,7 +133,7 @@ const Button = ({ state, text, type, title, elementToCapture }) => {
108
133
 
109
134
  // Link to CSV/JSON data
110
135
  const Link = ({ config, dashboardDataConfig }) => {
111
- let dataConfig = dashboardDataConfig || config;
136
+ let dataConfig = dashboardDataConfig || config
112
137
  // Handles Maps & Charts
113
138
  if (dataConfig.dataFileSourceType === 'url' && dataConfig.dataFileName && config.table.showDownloadUrl) {
114
139
  return (
@@ -126,7 +151,6 @@ const Link = ({ config, dashboardDataConfig }) => {
126
151
  ) : null
127
152
  }
128
153
 
129
- // TODO: convert to standardized COVE section
130
154
  const Section = ({ children, classes }) => {
131
155
  return (
132
156
  <section className={classes.join(' ')}>
@@ -124,25 +124,21 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
124
124
  }) => {
125
125
  const dropdownId = useId()
126
126
 
127
- const [userSearchTerm, setUserSearchTerm] = useState('')
128
- const [inputValue, setInputValue] = useState('')
127
+ const [userSearchTerm, setUserSearchTerm] = useState(null)
129
128
 
130
- const initialInputValue = useMemo(() => {
129
+ const inputValue = useMemo(() => {
131
130
  // value from props
132
131
  return activeSubGroup ? `${activeGroup} - ${activeSubGroup}` : ''
133
- }, [activeSubGroup])
132
+ }, [activeGroup, activeSubGroup])
134
133
  const [inputHasFocus, setInputHasFocus] = useState(false)
135
134
  const [isListOpened, setIsListOpened] = useState(false)
136
-
137
135
  const searchInput = useRef(null)
138
136
  const searchDropdown = useRef(null)
139
137
 
140
138
  const chooseSelectedSubGroup = (tierOne: string | number, tierTwo: string | number) => {
141
139
  searchInput.current.focus()
142
- const selectedItemValue = `${tierOne} - ${tierTwo}`
143
- setUserSearchTerm('')
140
+ setUserSearchTerm(null)
144
141
  setIsListOpened(false)
145
- setInputValue(selectedItemValue)
146
142
  handleSelectedItems([String(tierOne), String(tierTwo)])
147
143
  }
148
144
 
@@ -220,14 +216,13 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
220
216
  }
221
217
 
222
218
  const filterOptions = useMemo(() => {
223
- return filterSearchTerm(userSearchTerm, options)
219
+ return filterSearchTerm(userSearchTerm || '', options)
224
220
  }, [userSearchTerm, options])
225
221
 
226
222
  const handleSearchTermChange = e => {
227
223
  const newSearchTerm = e.target.value
228
224
  setIsListOpened(true)
229
225
  setUserSearchTerm(newSearchTerm)
230
- setInputValue(newSearchTerm)
231
226
  }
232
227
 
233
228
  const handleOnBlur = e => {
@@ -265,7 +260,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
265
260
  aria-haspopup='true'
266
261
  aria-hidden='false'
267
262
  tabIndex={0}
268
- value={inputValue || initialInputValue}
263
+ value={userSearchTerm !== null ? userSearchTerm : inputValue}
269
264
  onChange={handleSearchTermChange}
270
265
  placeholder={loading ? 'Loading...' : '- Select -'}
271
266
  disabled={loading || !options.length}
@@ -303,7 +298,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
303
298
  chooseSelectedSubGroup(groupValue, subGroupValue)
304
299
  }}
305
300
  userSelectedLabel={activeGroup + activeSubGroup}
306
- userSearchTerm={userSearchTerm}
301
+ userSearchTerm={userSearchTerm || ''}
307
302
  />
308
303
  )
309
304
  })
@@ -10,23 +10,31 @@
10
10
  list-style: none;
11
11
  }
12
12
 
13
+ * {
14
+ font-family: var(--app-font-secondary) !important;
15
+ font-size: var(--filter-select-font-size) !important;
16
+ }
17
+
13
18
  .search-input {
19
+ color: var(--cool-gray-90);
20
+ font-weight: 300;
21
+
14
22
  border: none;
15
23
  position: relative;
16
24
  display: inline-block;
17
25
  width: 100%;
18
26
  padding: 0;
19
27
  &::placeholder {
20
- color: var(--darkGray);
28
+ color: var(--cool-gray-90);
21
29
  }
22
30
  }
23
31
 
24
32
  .main-nested-dropdown-container,
25
33
  .nested-dropdown-input-container {
26
- border: 1px solid var(--lightGray);
34
+ border: 1px solid var(--cool-gray-10);
27
35
  min-width: 200px;
28
36
  cursor: pointer;
29
- padding: 0.375rem 0.75rem;
37
+ padding: 0.4rem 0.75rem;
30
38
  font-size: 1em;
31
39
  }
32
40
 
@@ -53,7 +61,6 @@
53
61
  border-radius: 0.25rem;
54
62
  position: relative;
55
63
  & > span.list-arrow {
56
- color: var(--mediumGray);
57
64
  position: absolute;
58
65
  top: 9px;
59
66
  right: 1px;
@@ -63,7 +70,6 @@
63
70
  &.disabled {
64
71
  background-color: var(--lightestGray);
65
72
  & > :is(input) {
66
- color: var(--darkGray);
67
73
  background-color: var(--lightestGray);
68
74
  }
69
75
  }
@@ -22,7 +22,6 @@ type TableProps = {
22
22
  }
23
23
  wrapColumns?: boolean
24
24
  hasRowType?: boolean // if it has row type then the first column is the row type which will explain how to render the row
25
- fontSize: 'small' | 'medium' | 'large'
26
25
  viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
27
26
  preliminaryData?: PreliminaryDataItem[]
28
27
  }
@@ -39,7 +38,6 @@ const Table = ({
39
38
  tableOptions,
40
39
  wrapColumns,
41
40
  hasRowType,
42
- fontSize,
43
41
  viewport,
44
42
  preliminaryData
45
43
  }: TableProps) => {
@@ -71,7 +69,6 @@ const Table = ({
71
69
  childRow={row}
72
70
  wrapColumns={wrapColumns}
73
71
  cellMinWidth={tableOptions.cellMinWidth}
74
- fontSize={fontSize}
75
72
  viewport={viewport}
76
73
  />
77
74
  )
@@ -92,7 +89,6 @@ const Table = ({
92
89
  childRow={childRow}
93
90
  wrapColumns={wrapColumns}
94
91
  cellMinWidth={tableOptions.cellMinWidth}
95
- fontSize={fontSize}
96
92
  viewport={viewport}
97
93
  />
98
94
  )
@@ -110,7 +106,6 @@ const Table = ({
110
106
  isTotal={true}
111
107
  wrapColumns={wrapColumns}
112
108
  cellMinWidth={tableOptions.cellMinWidth}
113
- fontSize={fontSize}
114
109
  viewport={viewport}
115
110
  />
116
111
  )
@@ -125,7 +120,6 @@ const Table = ({
125
120
  childRow={childRowCopy}
126
121
  wrapColumns={wrapColumns}
127
122
  cellMinWidth={tableOptions.cellMinWidth}
128
- fontSize={fontSize}
129
123
  viewport={viewport}
130
124
  />
131
125
  )
@@ -8,18 +8,15 @@ type RowProps = {
8
8
  wrapColumns: boolean
9
9
  isTotal?: boolean
10
10
  cellMinWidth?: number
11
- fontSize: 'small' | 'medium' | 'large'
12
11
  viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
13
12
  style?: object
14
13
  preliminaryData?: PreliminaryDataItem[]
15
14
  }
16
15
 
17
16
  const Row: FC<RowProps> = props => {
18
- const { childRow, rowKey, wrapColumns, cellMinWidth = 0, isTotal, fontSize, viewport, preliminaryData } = props
17
+ const { childRow, rowKey, wrapColumns, cellMinWidth = 0, isTotal, viewport, preliminaryData } = props
19
18
  const whiteSpace = wrapColumns ? 'unset' : 'nowrap'
20
19
  const minWidth = cellMinWidth + 'px'
21
- const fontSizes = { small: 16, medium: 18, large: 20 }
22
- const cellFontSize = ['xs', 'xxs'].includes(viewport) ? '12px' : `${fontSizes[fontSize]}px`
23
20
 
24
21
  return (
25
22
  <tr>
@@ -34,7 +31,7 @@ const Row: FC<RowProps> = props => {
34
31
  <Cell
35
32
  ariaLabel={style?.color ? 'suppressed data' : ''}
36
33
  key={rowKey + '__' + i}
37
- style={{ whiteSpace, minWidth, fontSize: cellFontSize, ...style }}
34
+ style={{ whiteSpace, minWidth, ...style }}
38
35
  isBold={isTotal}
39
36
  >
40
37
  {child}
@@ -45,8 +45,7 @@ export const CityState: Story = {
45
45
  tabbingId: '#asdf',
46
46
  columns: CityStateExample.columns,
47
47
  applyLegendToRow: () => ['#000'],
48
- displayGeoName,
49
- displayDataAsText: d => d
48
+ displayGeoName
50
49
  }
51
50
  }
52
51
 
@@ -5,25 +5,41 @@ import LoadSpin from '../ui/LoadSpin'
5
5
 
6
6
  import '../../styles/v2/components/button.scss'
7
7
 
8
- const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false, loadingText = 'Loading...', flexCenter, active = false, onClick, children, ...attributes }) => {
8
+ const Button = ({
9
+ style,
10
+ role,
11
+ hoverStyle = {},
12
+ fluid = false,
13
+ loading = false,
14
+ loadingText = 'Loading...',
15
+ flexCenter,
16
+ active = false,
17
+ onClick,
18
+ children,
19
+ ...attributes
20
+ }) => {
9
21
  const buttonRef = useRef(null)
10
22
 
11
- const [ buttonState, setButtonState ] = useState('out')
12
- const [ customStyles, setCustomStyles ] = useState({ ...style })
13
- const [ childrenWidth, setChildrenWidth ] = useState()
14
- const [ loadtextWidth, setLoadtextWidth ] = useState()
23
+ const [buttonState, setButtonState] = useState('out')
24
+ const [customStyles, setCustomStyles] = useState({ ...style })
25
+ const [childrenWidth, setChildrenWidth] = useState()
26
+ const [loadtextWidth, setLoadtextWidth] = useState()
15
27
 
16
28
  const attributesObj = {
17
29
  ...attributes,
18
30
  style: customStyles,
19
- className: 'cove-button' + (flexCenter || 'loader' === role ? ' cove-button--flex-center' : '') + (fluid ? ' fluid' : '') + (loading ? ' loading' : '') + (attributes.className ? ' ' + attributes.className : ''),
31
+ className:
32
+ 'cove-button' +
33
+ (flexCenter || 'loader' === role ? ' cove-button--flex-center' : '') +
34
+ (fluid ? ' fluid' : '') +
35
+ (loading ? ' loading' : '') +
36
+ (attributes.className ? ' ' + attributes.className : ''),
20
37
  onMouseOver: () => setButtonState('in'),
21
38
  onMouseOut: () => setButtonState('out'),
22
39
  onFocus: () => setButtonState('in'),
23
40
  onBlur: () => setButtonState('out')
24
41
  }
25
42
 
26
-
27
43
  useEffect(() => {
28
44
  if ('loader' === role && buttonRef.current) {
29
45
  //Create ghost object and text nodes for children
@@ -54,9 +70,8 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
54
70
  buttonRef.current.parentNode.removeChild(ghostSpan)
55
71
  buttonRef.current.parentNode.removeChild(ghostLoaderSpan)
56
72
  }
57
- return () => {
58
- }
59
- }, [ buttonRef, children, loadingText, role ])
73
+ return () => {}
74
+ }, [buttonRef, children, loadingText, role])
60
75
 
61
76
  useEffect(() => {
62
77
  //Adjust button styles depending on cursor, focus, and active, states
@@ -64,9 +79,10 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
64
79
 
65
80
  // If button state is out, check if its 'active'; we want to keep hover styles applied to 'active' buttons
66
81
  if (buttonState === 'out')
67
- // If button state is out, and not 'active', reset display styles back to default
68
- if (!active) setCustomStyles({ ...style })
69
- }, [ buttonState, active, style ])
82
+ if (!active)
83
+ // If button state is out, and not 'active', reset display styles back to default
84
+ setCustomStyles({ ...style })
85
+ }, [buttonState, active, style])
70
86
 
71
87
  return (
72
88
  <button
@@ -82,16 +98,19 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
82
98
  <>
83
99
  {'loader' === role && (
84
100
  <>
85
- <span className="cove-button__text" style={loading ? { width: loadtextWidth + 'px' } : { width: childrenWidth + 'px' }}>
86
- <div className="cove-button__text--loading" style={loading ? { opacity: 1 } : null}>
101
+ <span
102
+ className='cove-button__text'
103
+ style={loading ? { width: loadtextWidth + 'px' } : { width: childrenWidth + 'px' }}
104
+ >
105
+ <div className='cove-button__text--loading' style={loading ? { opacity: 1 } : null}>
87
106
  {loadingText}
88
107
  </div>
89
- <div className="cove-button__text--children" style={loading ? { opacity: 0 } : null}>
108
+ <div className='cove-button__text--children' style={loading ? { opacity: 0 } : null}>
90
109
  {children}
91
110
  </div>
92
111
  </span>
93
- <div className="cove-button__load-spin" style={loading ? { width: '28px', opacity: 1 } : null}>
94
- <LoadSpin className="ml-1" size={20}/>
112
+ <div className='cove-button__load-spin' style={loading ? { width: '28px', opacity: 1 } : null}>
113
+ <LoadSpin className='ms-1' size={20} />
95
114
  </div>
96
115
  </>
97
116
  )}
@@ -104,7 +123,7 @@ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false,
104
123
 
105
124
  Button.propTypes = {
106
125
  /** Specify special role type for button */
107
- role: PropTypes.oneOf([ 'loader' ]),
126
+ role: PropTypes.oneOf(['loader']),
108
127
  /** Provide object with styles that overwrite base styles when hovered */
109
128
  hoverStyle: PropTypes.object,
110
129
  /** Enables button to stretch to the full width of the content */
@@ -0,0 +1,45 @@
1
+ import Button from './Button.jsx'
2
+ import React from 'react'
3
+ import { missingRequiredSections } from '../../helpers/missingRequiredSections.js'
4
+ const Confirm = props => {
5
+ const { updateConfig, config } = props
6
+ const confirmDone = e => {
7
+ if (e) {
8
+ e.preventDefault()
9
+ }
10
+
11
+ let newConfig = { ...config }
12
+ delete newConfig.newViz
13
+
14
+ updateConfig(newConfig)
15
+ }
16
+
17
+ const styles: React.CSSProperties = {
18
+ position: 'relative',
19
+ height: '100vh',
20
+ width: '100%',
21
+ display: 'flex',
22
+ justifyContent: 'center',
23
+ alignItems: 'center',
24
+ gridArea: 'content'
25
+ }
26
+
27
+ return (
28
+ <section className='waiting' style={styles}>
29
+ <section className='waiting-container'>
30
+ <h3>Finish Configuring</h3>
31
+ <p>Set all required options to the left and confirm below to display a preview of the chart.</p>
32
+ <Button
33
+ className='btn btn-primary'
34
+ style={{ margin: '1em auto' }}
35
+ disabled={missingRequiredSections(config)}
36
+ onClick={e => confirmDone(e)}
37
+ >
38
+ I'm Done
39
+ </Button>
40
+ </section>
41
+ </section>
42
+ )
43
+ }
44
+
45
+ export default Confirm
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+ const Error = ({ errorMessage }) => {
3
+ const styles: React.CSSProperties = {
4
+ position: 'absolute',
5
+ background: 'white',
6
+ zIndex: '999',
7
+ height: '100vh',
8
+ width: '100%',
9
+ display: 'flex',
10
+ justifyContent: 'center',
11
+ alignItems: 'center',
12
+ gridArea: 'content'
13
+ }
14
+ return (
15
+ <section className='waiting' style={styles}>
16
+ <section className='waiting-container'>
17
+ <h3>Error With Configuration</h3>
18
+ <p>{errorMessage}</p>
19
+ </section>
20
+ </section>
21
+ )
22
+ }
23
+
24
+ export default Error