@cdc/core 4.24.12 → 4.25.2-25

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 (77) hide show
  1. package/components/DataTable/DataTable.tsx +58 -45
  2. package/components/DataTable/DataTableStandAlone.tsx +3 -3
  3. package/components/DataTable/components/ChartHeader.tsx +26 -6
  4. package/components/DataTable/components/ExpandCollapse.tsx +1 -4
  5. package/components/DataTable/components/MapHeader.tsx +5 -1
  6. package/components/DataTable/data-table.css +3 -8
  7. package/components/DataTable/helpers/chartCellMatrix.tsx +14 -5
  8. package/components/DataTable/helpers/customSort.ts +2 -2
  9. package/components/DataTable/helpers/mapCellMatrix.tsx +83 -60
  10. package/components/DataTable/types/TableConfig.ts +0 -1
  11. package/components/EditorPanel/FootnotesEditor.tsx +49 -7
  12. package/components/EditorPanel/Inputs.tsx +4 -0
  13. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -3
  14. package/components/Filters/Filters.tsx +95 -51
  15. package/components/Filters/helpers/filterWrapping.ts +43 -0
  16. package/components/Filters/helpers/handleSorting.ts +6 -0
  17. package/components/Filters/helpers/tests/handleSorting.test.ts +26 -0
  18. package/components/Footnotes/Footnotes.tsx +1 -1
  19. package/components/Layout/components/Visualization/index.tsx +18 -4
  20. package/components/Layout/components/Visualization/visualizations.scss +1 -1
  21. package/components/Legend/Legend.Gradient.tsx +1 -4
  22. package/components/Legend/index.tsx +1 -1
  23. package/components/LegendShape.tsx +2 -3
  24. package/components/MediaControls.jsx +32 -8
  25. package/components/NestedDropdown/NestedDropdown.tsx +25 -17
  26. package/components/NestedDropdown/nesteddropdown.styles.css +13 -7
  27. package/components/Table/Table.tsx +11 -11
  28. package/components/Table/components/Row.tsx +14 -5
  29. package/components/_stories/DataTable.stories.tsx +1 -2
  30. package/components/elements/Button.jsx +38 -19
  31. package/components/elements/Confirm.tsx +45 -0
  32. package/components/elements/Error.tsx +24 -0
  33. package/components/managers/DataDesigner.tsx +198 -143
  34. package/components/ui/Title/Title.scss +12 -5
  35. package/components/ui/Title/index.tsx +1 -1
  36. package/dist/cove-main.css +260 -723
  37. package/dist/cove-main.css.map +1 -1
  38. package/helpers/DataTransform.ts +55 -61
  39. package/helpers/addValuesToFilters.ts +45 -16
  40. package/helpers/cove/accessibility.ts +24 -0
  41. package/helpers/cove/fontSettings.ts +1 -1
  42. package/helpers/cove/number.ts +1 -7
  43. package/helpers/coveUpdateWorker.ts +5 -1
  44. package/helpers/displayDataAsText.ts +64 -0
  45. package/helpers/filterVizData.ts +2 -2
  46. package/helpers/formatConfigBeforeSave.ts +17 -2
  47. package/helpers/isOlderVersion.ts +20 -0
  48. package/helpers/isRightAlignedTableValue.js +14 -0
  49. package/helpers/missingRequiredSections.ts +20 -0
  50. package/helpers/queryStringUtils.ts +7 -0
  51. package/helpers/tests/addValuesToFilters.test.ts +19 -1
  52. package/helpers/useDataVizClasses.ts +8 -4
  53. package/helpers/ver/4.24.10.ts +12 -0
  54. package/helpers/ver/4.24.11.ts +18 -0
  55. package/helpers/ver/4.24.7.ts +19 -1
  56. package/helpers/ver/4.25.1.ts +18 -0
  57. package/package.json +2 -2
  58. package/styles/_button-section.scss +2 -5
  59. package/styles/_global-variables.scss +17 -7
  60. package/styles/_global.scss +8 -12
  61. package/styles/_reset.scss +4 -5
  62. package/styles/_typography.scss +0 -20
  63. package/styles/_variables.scss +0 -3
  64. package/styles/base.scss +44 -5
  65. package/styles/cove-main.scss +1 -1
  66. package/styles/filters.scss +65 -6
  67. package/styles/v2/base/_general.scss +3 -2
  68. package/styles/v2/components/button.scss +0 -1
  69. package/styles/v2/main.scss +3 -4
  70. package/styles/v2/themes/_color-definitions.scss +4 -4
  71. package/styles/v2/utils/index.scss +0 -1
  72. package/types/BoxPlot.ts +1 -0
  73. package/types/Runtime.ts +1 -0
  74. package/types/Table.ts +0 -1
  75. package/types/Version.ts +1 -1
  76. package/types/VizFilter.ts +3 -1
  77. 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(' ')}>
@@ -6,12 +6,13 @@ import Loader from '../Loader'
6
6
 
7
7
  const Options: React.FC<{
8
8
  subOptions: ValueTextPair[]
9
+ handleBlur: React.FocusEventHandler<HTMLLIElement>
9
10
  filterIndex: number
10
11
  label: string
11
12
  handleSubGroupSelect: Function
12
13
  userSelectedLabel: string
13
14
  userSearchTerm: string
14
- }> = ({ subOptions, filterIndex, label, handleSubGroupSelect, userSelectedLabel, userSearchTerm }) => {
15
+ }> = ({ subOptions, handleBlur, filterIndex, label, handleSubGroupSelect, userSelectedLabel, userSearchTerm }) => {
15
16
  const [isTierOneExpanded, setIsTierOneExpanded] = useState(true)
16
17
  const checkMark = <>&#10004;</>
17
18
 
@@ -45,6 +46,7 @@ const Options: React.FC<{
45
46
  tabIndex={0}
46
47
  aria-label={label}
47
48
  onClick={handleGroupClick}
49
+ onBlur={handleBlur}
48
50
  onKeyUp={handleKeyUp}
49
51
  className={`nested-dropdown-group-${filterIndex}`}
50
52
  >
@@ -124,25 +126,22 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
124
126
  }) => {
125
127
  const dropdownId = useId()
126
128
 
127
- const [userSearchTerm, setUserSearchTerm] = useState('')
128
- const [inputValue, setInputValue] = useState('')
129
+ const [userSearchTerm, setUserSearchTerm] = useState(null)
129
130
 
130
- const initialInputValue = useMemo(() => {
131
+ const inputValue = useMemo(() => {
131
132
  // value from props
132
133
  return activeSubGroup ? `${activeGroup} - ${activeSubGroup}` : ''
133
- }, [activeSubGroup])
134
+ }, [activeGroup, activeSubGroup])
134
135
  const [inputHasFocus, setInputHasFocus] = useState(false)
135
136
  const [isListOpened, setIsListOpened] = useState(false)
136
-
137
+ const nestedDropdownRef = useRef(null)
137
138
  const searchInput = useRef(null)
138
139
  const searchDropdown = useRef(null)
139
140
 
140
141
  const chooseSelectedSubGroup = (tierOne: string | number, tierTwo: string | number) => {
141
142
  searchInput.current.focus()
142
- const selectedItemValue = `${tierOne} - ${tierTwo}`
143
- setUserSearchTerm('')
143
+ setUserSearchTerm(null)
144
144
  setIsListOpened(false)
145
- setInputValue(selectedItemValue)
146
145
  handleSelectedItems([String(tierOne), String(tierTwo)])
147
146
  }
148
147
 
@@ -220,34 +219,43 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
220
219
  }
221
220
 
222
221
  const filterOptions = useMemo(() => {
223
- return filterSearchTerm(userSearchTerm, options)
222
+ return filterSearchTerm(userSearchTerm || '', options)
224
223
  }, [userSearchTerm, options])
225
224
 
226
225
  const handleSearchTermChange = e => {
227
226
  const newSearchTerm = e.target.value
228
227
  setIsListOpened(true)
229
228
  setUserSearchTerm(newSearchTerm)
230
- setInputValue(newSearchTerm)
231
229
  }
232
230
 
233
- const handleOnBlur = e => {
231
+ const handleOnBlur = (e: React.FocusEvent<HTMLLIElement, Element>): void => {
234
232
  if (
235
233
  e.relatedTarget === null ||
236
234
  ![
237
235
  `nested-dropdown-${filterIndex}`,
238
236
  `nested-dropdown-group-${filterIndex}`,
239
- `selectable-item-${filterIndex}`
237
+ `selectable-item-${filterIndex}`,
238
+ `main-nested-dropdown-container-${filterIndex}`
240
239
  ].includes(e.relatedTarget.className)
241
240
  ) {
242
241
  setInputHasFocus(false)
243
242
  setIsListOpened(false)
243
+ } else {
244
+ ;(e.relatedTarget as HTMLElement).focus()
244
245
  }
245
246
  }
246
247
 
248
+ function handleBlur(nestedDropdown, handleOnBlur) {
249
+ nestedDropdown?.addEventListener('blur', handleOnBlur)
250
+ }
251
+ handleBlur(searchInput.current, e => handleOnBlur(e))
252
+ handleBlur(searchDropdown.current, e => handleOnBlur(e))
253
+
247
254
  return (
248
255
  <>
249
256
  <div
250
257
  id={dropdownId}
258
+ ref={nestedDropdownRef}
251
259
  className={`nested-dropdown nested-dropdown-${filterIndex} ${isListOpened ? 'open-filter' : ''}`}
252
260
  onKeyUp={handleKeyUp}
253
261
  >
@@ -265,7 +273,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
265
273
  aria-haspopup='true'
266
274
  aria-hidden='false'
267
275
  tabIndex={0}
268
- value={inputValue || initialInputValue}
276
+ value={userSearchTerm !== null ? userSearchTerm : inputValue}
269
277
  onChange={handleSearchTermChange}
270
278
  placeholder={loading ? 'Loading...' : '- Select -'}
271
279
  disabled={loading || !options.length}
@@ -273,7 +281,6 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
273
281
  if (inputHasFocus) setIsListOpened(!isListOpened)
274
282
  }}
275
283
  onFocus={() => setInputHasFocus(true)}
276
- onBlur={e => handleOnBlur(e)}
277
284
  />
278
285
  <span className='list-arrow' aria-hidden={true}>
279
286
  <Icon display='caretDown' />
@@ -287,7 +294,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
287
294
  aria-labelledby='main-nested-dropdown'
288
295
  aria-expanded={isListOpened}
289
296
  ref={searchDropdown}
290
- className={`main-nested-dropdown-container ${isListOpened ? '' : 'hide'}`}
297
+ className={`main-nested-dropdown-container-${filterIndex}${isListOpened ? '' : ' hide'}`}
291
298
  >
292
299
  {filterOptions.length
293
300
  ? filterOptions.map(([group, subgroup], index) => {
@@ -296,6 +303,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
296
303
  return (
297
304
  <Options
298
305
  key={groupTextValue + '_' + index}
306
+ handleBlur={handleOnBlur}
299
307
  subOptions={subgroup}
300
308
  filterIndex={filterIndex}
301
309
  label={groupTextValue}
@@ -303,7 +311,7 @@ const NestedDropdown: React.FC<NestedDropdownProps> = ({
303
311
  chooseSelectedSubGroup(groupValue, subGroupValue)
304
312
  }}
305
313
  userSelectedLabel={activeGroup + activeSubGroup}
306
- userSearchTerm={userSearchTerm}
314
+ userSearchTerm={userSearchTerm || ''}
307
315
  />
308
316
  )
309
317
  })
@@ -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
- .main-nested-dropdown-container,
32
+ [class^='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,13 +70,12 @@
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
  }
70
76
  }
71
77
 
72
- & .main-nested-dropdown-container {
78
+ & [class^='main-nested-dropdown-container-'] {
73
79
  max-height: 375px;
74
80
  overflow-y: scroll;
75
81
  position: absolute;
@@ -1,13 +1,13 @@
1
1
  import { ReactNode } from 'react'
2
2
  import Row from './components/Row'
3
3
  import GroupRow from './components/GroupRow'
4
- import { CellMatrix, GroupCellMatrix } from './types/CellMatrix'
4
+ import { CellMatrix } from './types/CellMatrix'
5
5
  import { RowType } from './types/RowType'
6
6
  import { PreliminaryDataItem } from '@cdc/chart/src/types/ChartConfig'
7
7
  import _ from 'lodash'
8
8
 
9
9
  type TableProps = {
10
- childrenMatrix: CellMatrix | GroupCellMatrix
10
+ childrenMatrix: CellMatrix | Map<string, CellMatrix>
11
11
  noData?: boolean
12
12
  tableName: string
13
13
  caption: string
@@ -22,9 +22,9 @@ 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[]
27
+ rightAlignedCols: object
28
28
  }
29
29
 
30
30
  type Position = 'sticky'
@@ -39,9 +39,9 @@ const Table = ({
39
39
  tableOptions,
40
40
  wrapColumns,
41
41
  hasRowType,
42
- fontSize,
43
42
  viewport,
44
- preliminaryData
43
+ preliminaryData,
44
+ rightAlignedCols
45
45
  }: TableProps) => {
46
46
  const headStyle = stickyHeader ? { position: 'sticky' as Position, top: 0, zIndex: 2 } : {}
47
47
  const isGroupedMatrix = !Array.isArray(childrenMatrix)
@@ -58,9 +58,9 @@ const Table = ({
58
58
  <thead style={headStyle}>{headContent}</thead>
59
59
  <tbody>
60
60
  {isGroupedMatrix
61
- ? Object.keys(childrenMatrix).flatMap(groupName => {
61
+ ? Array.from(childrenMatrix.keys()).flatMap(groupName => {
62
62
  let colSpan = 0
63
- const rows = childrenMatrix[groupName].map((row, i) => {
63
+ const rows = childrenMatrix.get(groupName).map((row, i) => {
64
64
  colSpan = row.length
65
65
  const key = `${tableName}-${groupName}-row-${i}`
66
66
  return (
@@ -71,8 +71,8 @@ const Table = ({
71
71
  childRow={row}
72
72
  wrapColumns={wrapColumns}
73
73
  cellMinWidth={tableOptions.cellMinWidth}
74
- fontSize={fontSize}
75
74
  viewport={viewport}
75
+ rightAlignedCols={rightAlignedCols}
76
76
  />
77
77
  )
78
78
  })
@@ -92,8 +92,8 @@ const Table = ({
92
92
  childRow={childRow}
93
93
  wrapColumns={wrapColumns}
94
94
  cellMinWidth={tableOptions.cellMinWidth}
95
- fontSize={fontSize}
96
95
  viewport={viewport}
96
+ rightAlignedCols={rightAlignedCols}
97
97
  />
98
98
  )
99
99
  } else {
@@ -110,8 +110,8 @@ const Table = ({
110
110
  isTotal={true}
111
111
  wrapColumns={wrapColumns}
112
112
  cellMinWidth={tableOptions.cellMinWidth}
113
- fontSize={fontSize}
114
113
  viewport={viewport}
114
+ rightAlignedCols={rightAlignedCols}
115
115
  />
116
116
  )
117
117
  case RowType.row_group_total:
@@ -125,8 +125,8 @@ const Table = ({
125
125
  childRow={childRowCopy}
126
126
  wrapColumns={wrapColumns}
127
127
  cellMinWidth={tableOptions.cellMinWidth}
128
- fontSize={fontSize}
129
128
  viewport={viewport}
129
+ rightAlignedCols={rightAlignedCols}
130
130
  />
131
131
  )
132
132
  }
@@ -8,18 +8,25 @@ 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[]
14
+ rightAlignedCols: object
15
15
  }
16
16
 
17
17
  const Row: FC<RowProps> = props => {
18
- const { childRow, rowKey, wrapColumns, cellMinWidth = 0, isTotal, fontSize, viewport, preliminaryData } = props
18
+ const {
19
+ childRow,
20
+ rowKey,
21
+ wrapColumns,
22
+ cellMinWidth = 0,
23
+ isTotal,
24
+ viewport,
25
+ preliminaryData,
26
+ rightAlignedCols
27
+ } = props
19
28
  const whiteSpace = wrapColumns ? 'unset' : 'nowrap'
20
29
  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
30
 
24
31
  return (
25
32
  <tr>
@@ -30,11 +37,13 @@ const Row: FC<RowProps> = props => {
30
37
  ) && { color: '#777772' }) ||
31
38
  {}
32
39
 
40
+ const textAlign = rightAlignedCols && rightAlignedCols[i] ? 'right' : ''
41
+
33
42
  return (
34
43
  <Cell
35
44
  ariaLabel={style?.color ? 'suppressed data' : ''}
36
45
  key={rowKey + '__' + i}
37
- style={{ whiteSpace, minWidth, fontSize: cellFontSize, ...style }}
46
+ style={{ whiteSpace, minWidth, textAlign, ...style }}
38
47
  isBold={isTotal}
39
48
  >
40
49
  {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