@cdc/core 1.1.4 → 4.22.11

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 (138) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1 -1
  3. package/assets/filtered-text.svg +1 -0
  4. package/assets/icon-caret-down.svg +3 -0
  5. package/assets/icon-caret-filled-down.svg +3 -0
  6. package/assets/icon-caret-filled-up.svg +3 -0
  7. package/assets/icon-caret-up.svg +3 -0
  8. package/assets/icon-chart-bar-paired.svg +15 -0
  9. package/assets/icon-chart-bar-stacked.svg +10 -0
  10. package/assets/icon-chart-bar.svg +3 -0
  11. package/assets/icon-chart-line.svg +3 -0
  12. package/assets/icon-chart-pie.svg +3 -0
  13. package/assets/icon-check.svg +3 -0
  14. package/assets/icon-close.svg +3 -1
  15. package/assets/icon-code.svg +2 -2
  16. package/assets/icon-dashboard.svg +34 -0
  17. package/assets/icon-databite.svg +3 -0
  18. package/assets/icon-edit.svg +3 -0
  19. package/assets/icon-file-upload.svg +3 -0
  20. package/assets/icon-filter-bars.svg +5 -0
  21. package/assets/icon-gear.svg +6 -0
  22. package/assets/icon-grid.svg +2 -3
  23. package/assets/icon-info.svg +1 -1
  24. package/assets/icon-link.svg +3 -0
  25. package/assets/{alabama-graphic.svg → icon-map-alabama.svg} +2 -2
  26. package/assets/icon-map-usa.svg +3 -0
  27. package/assets/icon-map-world.svg +3 -0
  28. package/assets/icon-minus.svg +3 -0
  29. package/assets/icon-move.svg +8 -0
  30. package/assets/icon-plus.svg +3 -0
  31. package/assets/icon-question-circle.svg +3 -0
  32. package/assets/icon-tools.svg +8 -0
  33. package/assets/icon-upload.svg +3 -0
  34. package/assets/icon-warning-circle.svg +3 -0
  35. package/assets/{icon-warning.svg → icon-warning-triangle.svg} +1 -1
  36. package/components/AdvancedEditor.js +52 -67
  37. package/components/{ErrorBoundary.js → ErrorBoundary.jsx} +10 -11
  38. package/components/GlobalContext.jsx +2 -6
  39. package/components/{LegendCircle.js → LegendCircle.jsx} +4 -4
  40. package/components/Loading.jsx +17 -0
  41. package/components/Waiting.jsx +20 -0
  42. package/components/elements/Button.jsx +111 -3
  43. package/components/elements/Card.jsx +13 -0
  44. package/components/inputs/InputCheckbox.jsx +43 -38
  45. package/components/inputs/InputGroup.jsx +71 -0
  46. package/components/inputs/InputSelect.jsx +28 -24
  47. package/components/inputs/InputText.jsx +20 -37
  48. package/components/inputs/InputToggle.jsx +39 -43
  49. package/components/managers/DataDesigner.jsx +194 -0
  50. package/components/ui/Accordion.jsx +18 -30
  51. package/components/ui/Icon.jsx +59 -20
  52. package/components/ui/LoadSpin.jsx +19 -0
  53. package/components/ui/Modal.jsx +40 -39
  54. package/components/ui/Overlay.jsx +15 -24
  55. package/components/ui/OverlayFrame.jsx +1 -5
  56. package/components/ui/Tooltip.jsx +20 -31
  57. package/data/colorPalettes.js +36 -227
  58. package/data/dataDesignerTables.js +148 -0
  59. package/data/themes.js +13 -13
  60. package/helpers/DataTransform.js +162 -0
  61. package/helpers/cacheBustingString.js +5 -0
  62. package/helpers/events.js +5 -6
  63. package/helpers/fetchRemoteData.js +41 -0
  64. package/helpers/getViewport.js +15 -15
  65. package/helpers/numberFromString.js +7 -7
  66. package/helpers/updatePaletteNames.js +15 -17
  67. package/helpers/useDataVizClasses.js +40 -0
  68. package/helpers/validateFipsCodeLength.js +41 -56
  69. package/package.json +3 -2
  70. package/styles/_data-table.scss +45 -40
  71. package/styles/_global.scss +41 -22
  72. package/styles/_mixins.scss +12 -12
  73. package/styles/_reset.scss +95 -16
  74. package/styles/_variables.scss +5 -5
  75. package/styles/base.scss +104 -37
  76. package/styles/heading-colors.scss +6 -2
  77. package/styles/loading.scss +62 -60
  78. package/styles/v2/base/_file-selector.scss +20 -0
  79. package/styles/v2/base/_general.scss +9 -22
  80. package/styles/v2/base/_heading.scss +20 -0
  81. package/styles/v2/base/_reset.scss +4 -3
  82. package/styles/v2/base/index.scss +7 -3
  83. package/styles/v2/components/accordion.scss +13 -13
  84. package/styles/v2/components/button.scss +29 -68
  85. package/styles/v2/components/card.scss +7 -0
  86. package/styles/v2/components/data-designer.scss +101 -0
  87. package/styles/v2/components/editor.scss +52 -50
  88. package/styles/v2/components/guidance-block.scss +74 -0
  89. package/styles/v2/components/icon.scss +0 -4
  90. package/styles/v2/components/input/_input-check-radio.scss +97 -0
  91. package/styles/v2/components/input/_input-group.scss +74 -0
  92. package/styles/v2/components/input/_input-slider.scss +183 -0
  93. package/styles/v2/components/input/_input.scss +66 -0
  94. package/styles/v2/components/input/index.scss +7 -0
  95. package/styles/v2/components/loadspin.scss +100 -0
  96. package/styles/v2/components/modal.scss +15 -8
  97. package/styles/v2/components/overlay.scss +6 -4
  98. package/styles/v2/layout/_alert.scss +15 -14
  99. package/styles/v2/layout/_component.scss +8 -1
  100. package/styles/v2/layout/_data-table.scss +78 -156
  101. package/styles/v2/layout/_progression.scss +12 -8
  102. package/styles/v2/layout/index.scss +5 -7
  103. package/styles/v2/main.scss +52 -2
  104. package/styles/v2/themes/_color-definitions.scss +103 -20
  105. package/styles/v2/themes/index.scss +1 -1
  106. package/styles/v2/utils/_align.scss +17 -0
  107. package/styles/v2/utils/_animations.scss +2 -2
  108. package/styles/v2/utils/_breakpoints.scss +59 -0
  109. package/styles/v2/utils/_grid.scss +47 -0
  110. package/styles/v2/utils/_mixins.scss +0 -16
  111. package/styles/v2/utils/_spacers.scss +31 -0
  112. package/styles/v2/utils/_variables.scss +7 -0
  113. package/styles/v2/utils/index.scss +9 -4
  114. package/styles/waiting.scss +22 -23
  115. package/assets/asc.svg +0 -1
  116. package/assets/chart-bar-solid.svg +0 -1
  117. package/assets/chart-line-solid.svg +0 -1
  118. package/assets/chart-pie-solid.svg +0 -1
  119. package/assets/check.svg +0 -3
  120. package/assets/dashboard.svg +0 -11
  121. package/assets/data-bite-graphic.svg +0 -3
  122. package/assets/desc.svg +0 -1
  123. package/assets/file-upload-solid.svg +0 -1
  124. package/assets/horizontal-stacked-bar.svg +0 -1
  125. package/assets/link.svg +0 -1
  126. package/assets/minus.svg +0 -1
  127. package/assets/paired-bar.svg +0 -11
  128. package/assets/plus.svg +0 -1
  129. package/assets/question-circle.svg +0 -1
  130. package/assets/upload-solid.svg +0 -1
  131. package/assets/usa-graphic.svg +0 -3
  132. package/assets/world-graphic.svg +0 -3
  133. package/components/DataTransform.js +0 -162
  134. package/components/Loading.js +0 -15
  135. package/components/Waiting.js +0 -11
  136. package/styles/v2/components/input.scss +0 -372
  137. package/styles/v2/layout/_header.scss +0 -13
  138. package/styles/v2/layout/_link.scss +0 -46
@@ -1,74 +1,59 @@
1
1
  import React, { useState, useEffect } from 'react'
2
2
  import MapIcon from '../assets/map-folded.svg'
3
- import ChartIcon from '../assets/chart-bar-solid.svg';
4
- import MarkupIncludeIcon from '../assets/icon-code.svg';
5
-
3
+ import ChartIcon from '../assets/icon-chart-bar.svg'
4
+ import MarkupIncludeIcon from '../assets/icon-code.svg'
6
5
 
7
6
  export const AdvancedEditor = ({ loadConfig, state, convertStateToConfig }) => {
8
- const [advancedToggle, setAdvancedToggle] = useState(false);
9
- const [configTextboxValue, setConfigTextbox] = useState({});
10
-
11
-
12
- useEffect(() => {
13
- const parsedData = convertStateToConfig();
14
- const formattedData = JSON.stringify(parsedData, undefined, 2);
15
-
16
- setConfigTextbox(formattedData);
17
- }, [state]);
18
-
19
- const typeLookup = {
20
- 'chart': ['Charts', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
21
- 'map': ['Maps', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html', <MapIcon />],
22
- 'markup-include': ['Markup Include', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/Markup-Include.html', <MarkupIncludeIcon /> ]
23
- }
24
-
25
- if(!state.type) return;
26
- return (
27
- <>
28
- <a
29
- href={typeLookup[state.type][1]}
30
- target='_blank'
31
- rel='noopener noreferrer'
32
- className='guidance-link'
33
- >
34
- {typeLookup[state.type][2]}
35
- <div>
36
- <span className='heading-3'>Get Help with { typeLookup[state.type][0] }</span>
37
- <p>Examples and documentation</p>
38
- </div>
39
- </a>
40
- <div className='advanced'>
41
- <span className='advanced-toggle-link' onClick={() => setAdvancedToggle(!advancedToggle)}>
42
- <span>{advancedToggle ? `— ` : `+ `}</span>Advanced Options
43
- </span>
44
- {advancedToggle && (
45
- <React.Fragment>
46
- <section className='error-box py-2 px-3 my-2'>
47
- <div>
48
- <strong className='pt-1'>Warning</strong>
49
- <p>This can cause serious errors in your visualization.</p>
50
- </div>
51
- </section>
52
- <p className='pb-2'>
53
- This tool displays the actual <acronym title='JavaScript Object Notation'>JSON</acronym> configuration{' '}
54
- that is generated by this
55
- editor and allows you to edit properties directly and apply them.
56
- </p>
57
- <textarea
58
- value={configTextboxValue}
59
- onChange={(event) => setConfigTextbox(event.target.value)}
60
- />
61
- <button
62
- className='btn full-width'
63
- onClick={() => loadConfig(JSON.parse(configTextboxValue))}
64
- >
65
- Apply
66
- </button>
67
- </React.Fragment>
68
- )}
69
- </div>
70
- </>
71
- )
7
+ const [advancedToggle, setAdvancedToggle] = useState(false)
8
+ const [configTextboxValue, setConfigTextbox] = useState({})
9
+
10
+ useEffect(() => {
11
+ const parsedData = convertStateToConfig()
12
+ const formattedData = JSON.stringify(parsedData, undefined, 2)
13
+
14
+ setConfigTextbox(formattedData)
15
+ }, [state])
16
+
17
+ const typeLookup = {
18
+ chart: ['Charts', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html', <ChartIcon />],
19
+ map: ['Maps', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/data-map.html', <MapIcon />],
20
+ 'markup-include': ['Markup Include', 'https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/Markup-Include.html', <MarkupIncludeIcon />]
21
+ }
22
+
23
+ if (!state.type) return
24
+ return (
25
+ <>
26
+ <a href={typeLookup[state.type][1]} target='_blank' rel='noopener noreferrer' className='guidance-link'>
27
+ {typeLookup[state.type][2]}
28
+ <div>
29
+ <span className='heading-3'>Get Help with {typeLookup[state.type][0]}</span>
30
+ <p>Examples and documentation</p>
31
+ </div>
32
+ </a>
33
+ <div className='advanced'>
34
+ <span className='advanced-toggle-link' onClick={() => setAdvancedToggle(!advancedToggle)}>
35
+ <span>{advancedToggle ? `— ` : `+ `}</span>Advanced Options
36
+ </span>
37
+ {advancedToggle && (
38
+ <React.Fragment>
39
+ <section className='error-box py-2 px-3 my-2'>
40
+ <div>
41
+ <strong className='pt-1'>Warning</strong>
42
+ <p>This can cause serious errors in your visualization.</p>
43
+ </div>
44
+ </section>
45
+ <p className='pb-2'>
46
+ This tool displays the actual <acronym title='JavaScript Object Notation'>JSON</acronym> configuration that is generated by this editor and allows you to edit properties directly and apply them.
47
+ </p>
48
+ <textarea value={configTextboxValue} onChange={event => setConfigTextbox(event.target.value)} />
49
+ <button className='btn full-width' onClick={() => loadConfig(JSON.parse(configTextboxValue))}>
50
+ Apply
51
+ </button>
52
+ </React.Fragment>
53
+ )}
54
+ </div>
55
+ </>
56
+ )
72
57
  }
73
58
 
74
59
  export default AdvancedEditor
@@ -1,33 +1,32 @@
1
- import React from 'react';
1
+ import React from 'react'
2
2
 
3
3
  class ErrorBoundary extends React.Component {
4
4
  constructor(props) {
5
- super(props);
6
- this.state = { hasError: false };
7
-
5
+ super(props)
6
+ this.state = { hasError: false }
8
7
  }
9
8
 
10
9
  static getDerivedStateFromError(error) {
11
10
  // Update state so the next render will show the fallback UI.
12
- return { hasError: true };
11
+ return { hasError: true }
13
12
  }
14
13
 
15
14
  componentDidCatch(error, errorInfo) {
16
15
  // You can also log the error to an error reporting service
17
- console.warn(error, errorInfo);
16
+ console.warn(error, errorInfo)
18
17
  }
19
18
 
20
19
  render() {
21
20
  if (this.state.hasError) {
22
- if(this.props.component) {
23
- return <h1>Something went wrong with component {this.props.component}.</h1>;
21
+ if (this.props.component) {
22
+ return <h1>Something went wrong with component {this.props.component}.</h1>
24
23
  }
25
24
  // You can render any custom fallback UI
26
- return <h1>Something went wrong.</h1>;
25
+ return <h1>Something went wrong.</h1>
27
26
  }
28
27
 
29
- return this.props.children;
28
+ return this.props.children
30
29
  }
31
30
  }
32
31
 
33
- export default ErrorBoundary;
32
+ export default ErrorBoundary
@@ -4,7 +4,7 @@ export const GlobalContext = createContext({})
4
4
  export const useGlobalContext = () => useContext(GlobalContext)
5
5
 
6
6
  export const GlobalContextProvider = ({ children }) => {
7
- const [ globalContextData, setGlobalContextData ] = useState({})
7
+ const [globalContextData, setGlobalContextData] = useState({})
8
8
 
9
9
  const openOverlay = (obj, disableBgClose = false) => {
10
10
  let payload = { object: obj, show: true, disableBgClose: disableBgClose }
@@ -33,9 +33,5 @@ export const GlobalContextProvider = ({ children }) => {
33
33
  }
34
34
  }
35
35
 
36
- return (
37
- <GlobalContext.Provider value={globalSettings}>
38
- {children}
39
- </GlobalContext.Provider>
40
- )
36
+ return <GlobalContext.Provider value={globalSettings}>{children}</GlobalContext.Provider>
41
37
  }
@@ -1,6 +1,6 @@
1
- import React from 'react';
1
+ import React from 'react'
2
2
 
3
- export default function LegendCircle({fill}) {
3
+ export default function LegendCircle({ fill }) {
4
4
  const styles = {
5
5
  marginRight: '5px',
6
6
  borderRadius: '300px',
@@ -12,5 +12,5 @@ export default function LegendCircle({fill}) {
12
12
  backgroundColor: fill
13
13
  }
14
14
 
15
- return <span className="legend-item" style={styles} />
16
- }
15
+ return <span className='legend-item' style={styles} />
16
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react'
2
+ import '../styles/loading.scss'
3
+
4
+ export default function Loading({ viewport = 'lg' }) {
5
+ return (
6
+ <section className='loading' aria-live='assertive'>
7
+ <span className='sr-only' style={{ display: 'none' }}>
8
+ Content is loading.
9
+ </span>
10
+ <div className={`la-ball-beat la-dark ${viewport}`}>
11
+ <div />
12
+ <div />
13
+ <div />
14
+ </div>
15
+ </section>
16
+ )
17
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import '../styles/waiting.scss'
3
+
4
+ export default ({ requiredColumns, className }) => (
5
+ <section className={className}>
6
+ <section className='waiting-container'>
7
+ <h3>Configuration Required</h3>
8
+ <p>
9
+ Set{' '}
10
+ {requiredColumns.map((col, i) => (
11
+ <span key={`missing-column-${i}`}>
12
+ <strong>{col}</strong>
13
+ {i + 1 < requiredColumns.length && ', '}
14
+ </span>
15
+ ))}{' '}
16
+ columns in editor.
17
+ </p>
18
+ </section>
19
+ </section>
20
+ )
@@ -1,12 +1,120 @@
1
- import React from 'react'
1
+ import React, { useState, useEffect, useRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import LoadSpin from '../ui/LoadSpin'
2
5
 
3
6
  import '../../styles/v2/components/button.scss'
4
7
 
5
- const Button = ({fluid, children, onClick, className, ...attributes}) => {
8
+ const Button = ({ style, role, hoverStyle = {}, fluid = false, loading = false, loadingText = 'Loading...', flexCenter, active = false, onClick, children, ...attributes }) => {
9
+ const buttonRef = useRef(null)
10
+
11
+ const [buttonState, setButtonState] = useState('out')
12
+ const [customStyles, setCustomStyles] = useState({ ...style })
13
+ const [childrenWidth, setChildrenWidth] = useState()
14
+ const [loadtextWidth, setLoadtextWidth] = useState()
15
+
16
+ const attributesObj = {
17
+ ...attributes,
18
+ style: customStyles,
19
+ className: 'cove-button' + (flexCenter || 'loader' === role ? ' cove-button--flex-center' : '') + (fluid ? ' fluid' : '') + (loading ? ' loading' : '') + (attributes.className ? ' ' + attributes.className : ''),
20
+ onMouseOver: () => setButtonState('in'),
21
+ onMouseOut: () => setButtonState('out'),
22
+ onFocus: () => setButtonState('in'),
23
+ onBlur: () => setButtonState('out')
24
+ }
25
+
26
+ useEffect(() => {
27
+ if ('loader' === role && buttonRef.current) {
28
+ //Create ghost object and text nodes for children
29
+ const ghostSpan = document.createElement('span')
30
+ const ghostContent = document.createTextNode(children)
31
+ ghostSpan.style.opacity = '0'
32
+ ghostSpan.style.visibility = 'hidden'
33
+
34
+ //Create ghost object and text nodes for loading value
35
+ const ghostLoaderSpan = document.createElement('span')
36
+ const ghostLoaderContent = document.createTextNode(loadingText)
37
+ ghostLoaderSpan.style.opacity = '0'
38
+ ghostLoaderSpan.style.visibility = 'hidden'
39
+
40
+ //Append data to ghost objects
41
+ ghostSpan.appendChild(ghostContent)
42
+ ghostLoaderSpan.appendChild(ghostLoaderContent)
43
+
44
+ //Append objects to document
45
+ buttonRef.current.parentNode.insertBefore(ghostSpan, buttonRef.current)
46
+ buttonRef.current.parentNode.insertBefore(ghostLoaderSpan, buttonRef.current)
47
+
48
+ //Register ghost width values in state
49
+ setChildrenWidth(ghostSpan.offsetWidth + 2) //Toss in a 2px to account for subpixel offset
50
+ setLoadtextWidth(ghostLoaderSpan.offsetWidth + 2) //Toss in a 2px to account for subpixel offset
51
+
52
+ //Remove ghost objects form document
53
+ buttonRef.current.parentNode.removeChild(ghostSpan)
54
+ buttonRef.current.parentNode.removeChild(ghostLoaderSpan)
55
+ }
56
+ }, [])
57
+
58
+ useEffect(() => {
59
+ //Adjust button styles depending on cursor, focus, and active, states
60
+ return buttonState === 'in'
61
+ ? setCustomStyles(stateStyles => ({ ...stateStyles, ...hoverStyle }))
62
+ : buttonState === 'out'
63
+ ? active //If button state is out, check if its 'active'; we want to keep hover styles applied to 'active' buttons
64
+ ? null //Button is active, so leave the 'hover' styles in place
65
+ : setCustomStyles({ ...style }) //Button is not 'active', so reset display styles back to default
66
+ : false //Button state is neither 'in' nor 'out' - do nothing
67
+ }, [buttonState, active])
6
68
 
7
69
  return (
8
- <button className={`cove-button${fluid ? ' fluid' : ''}${className ? className : ''}`} {...attributes} onClick={(e) => {e.preventDefault(); onClick();}}>{children}</button>
70
+ <button
71
+ {...attributesObj}
72
+ onClick={e => {
73
+ e.preventDefault()
74
+ return loading || onClick()
75
+ }}
76
+ disabled={loading || attributesObj.disabled}
77
+ ref={buttonRef}
78
+ >
79
+ {children && (
80
+ <>
81
+ {'loader' === role && (
82
+ <>
83
+ <span className='cove-button__text' style={loading ? { width: loadtextWidth + 'px' } : { width: childrenWidth + 'px' }}>
84
+ <div className='cove-button__text--loading' style={loading ? { opacity: 1 } : null}>
85
+ {loadingText}
86
+ </div>
87
+ <div className='cove-button__text--children' style={loading ? { opacity: 0 } : null}>
88
+ {children}
89
+ </div>
90
+ </span>
91
+ <div className='cove-button__load-spin' style={loading ? { width: '28px', opacity: 1 } : null}>
92
+ <LoadSpin className='ml-1' size={20} />
93
+ </div>
94
+ </>
95
+ )}
96
+ {role !== 'loader' && children}
97
+ </>
98
+ )}
99
+ </button>
9
100
  )
10
101
  }
11
102
 
103
+ Button.propTypes = {
104
+ /** Specify special role type for button */
105
+ role: PropTypes.oneOf(['loader']),
106
+ /** Provide object with styles that overwrite base styles when hovered */
107
+ hoverStyle: PropTypes.object,
108
+ /** Enables button to stretch to the full width of the content */
109
+ fluid: PropTypes.bool,
110
+ /** Displays loading spinner on button while condition is true **/
111
+ loading: PropTypes.bool,
112
+ /** Set text to appear during loading animation **/
113
+ loadingText: PropTypes.string,
114
+ /** Displays button as flex and centers all direct children nodes. Useful for aligning icons and text **/
115
+ flexCenter: PropTypes.bool,
116
+ /** When value condition is true, retains any custom, inline `style={}` defined **/
117
+ active: PropTypes.bool
118
+ }
119
+
12
120
  export default Button
@@ -0,0 +1,13 @@
1
+ import React from 'react'
2
+
3
+ import '../../styles/v2/components/card.scss'
4
+
5
+ const Card = ({ children, className, ...attributes }) => {
6
+ return (
7
+ <div className={`cove-card${className ? ' ' + className : ''}`} {...attributes}>
8
+ {children}
9
+ </div>
10
+ )
11
+ }
12
+
13
+ export default Card
@@ -1,12 +1,13 @@
1
1
  import React, { useState, useEffect, memo } from 'react'
2
+ import PropTypes from 'prop-types'
2
3
 
3
- import Check from '../../assets/check.svg'
4
- import '../../styles/v2/components/input.scss'
4
+ import Check from '../../assets/icon-check.svg'
5
+ import '../../styles/v2/components/input/index.scss'
5
6
 
6
- const InputCheckbox = memo((
7
- {
7
+ const InputCheckbox = memo(
8
+ ({
8
9
  label,
9
- size = 'medium',
10
+ size = 'small',
10
11
  activeColor = null,
11
12
  activeCheckColor = null,
12
13
  section = null,
@@ -15,45 +16,49 @@ const InputCheckbox = memo((
15
16
  updateField,
16
17
  value: stateValue,
17
18
 
18
- i = null, min = null, max = null,
19
+ i = null,
20
+ min = null,
21
+ max = null,
19
22
  ...attributes
20
- }
21
- ) => {
23
+ }) => {
24
+ const [value, setValue] = useState(stateValue)
22
25
 
23
- const [ value, setValue ] = useState(stateValue)
26
+ let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
27
+ if (fieldName === 'border') {
28
+ console.table({ fieldName, value, stateValue })
29
+ }
24
30
 
25
- let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
26
- if(fieldName === 'border') {
27
- console.table({fieldName, value, stateValue})
28
-
29
- }
31
+ useEffect(() => {
32
+ if (stateValue !== undefined && stateValue !== value) {
33
+ setValue(stateValue)
34
+ }
35
+ }, [stateValue])
30
36
 
31
- useEffect(() => {
32
- if (stateValue !== undefined && stateValue !== value) {
33
- setValue(stateValue)
34
- }
35
- }, [ stateValue ])
37
+ useEffect(() => {
38
+ if (stateValue !== undefined && stateValue !== value && updateField) {
39
+ updateField(section, subsection, fieldName, value, i)
40
+ }
41
+ }, [value])
36
42
 
37
- useEffect(() => {
38
- if (stateValue !== undefined && stateValue !== value && updateField) {
39
- updateField(section, subsection, fieldName, value, i)
40
- }
41
- }, [ value ])
42
-
43
- return (
44
- <div className="input-group">
45
- {label && <label>{label}</label>}
46
- <div
47
- className={'cove-input__checkbox' + (size === 'small' ? '--small' : size === 'large' ? '--large' : '') + (value ? ' active' : '')}
48
- onClick={() => setValue(!value)}>
49
- <div className={`cove-input__checkbox-box${activeColor ? ' custom-color' : ''}`}
50
- style={value && activeColor ? { backgroundColor: activeColor } : null}>
51
- <Check className="cove-input__checkbox-check" style={{fill: activeCheckColor || '#025eaa'}}/>
43
+ return (
44
+ <div className='input-group'>
45
+ {label && <label>{label}</label>}
46
+ <div className={'cove-input__checkbox' + (size === 'small' ? '--small' : size === 'large' ? '--large' : '') + (value ? ' active' : '')} onClick={() => setValue(!value)}>
47
+ <div className={`cove-input__checkbox-box${activeColor ? ' custom-color' : ''}`} style={value && activeColor ? { backgroundColor: activeColor } : null}>
48
+ <Check className='cove-input__checkbox-check' style={{ fill: activeCheckColor || '#025eaa' }} />
49
+ </div>
50
+ <input className='cove-input--hidden' type='checkbox' name={name} checked={value || false} readOnly />
52
51
  </div>
53
- <input className="cove-input--hidden" type="checkbox" name={name} checked={value || false} readOnly/>
54
52
  </div>
55
- </div>
56
- )
57
- })
53
+ )
54
+ }
55
+ )
56
+
57
+ InputCheckbox.propTypes = {
58
+ label: PropTypes.string,
59
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
60
+ activeColor: PropTypes.string,
61
+ activeCheckColor: PropTypes.string
62
+ }
58
63
 
59
64
  export default InputCheckbox
@@ -0,0 +1,71 @@
1
+ import React, { useState, useEffect, useLayoutEffect, useRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ const InputGroup = ({ label, flow, children, clear, className, style, ...attributes }) => {
5
+ const [styles, setStyles] = useState({})
6
+ const [labelWidth, setLabelWidth] = useState(null)
7
+
8
+ const groupLabelRef = useRef(null)
9
+ const groupLabel = (
10
+ <div className='cove-input-group__label' ref={groupLabelRef}>
11
+ {label}
12
+ </div>
13
+ )
14
+
15
+ useLayoutEffect(() => {
16
+ if (!clear) return
17
+ if (!groupLabelRef.current) return
18
+ setLabelWidth(groupLabelRef.current.offsetWidth)
19
+ }, [clear, groupLabelRef])
20
+
21
+ useLayoutEffect(() => {
22
+ if (!clear) return
23
+ if ('left' === flow) {
24
+ setStyles(() => ({ paddingLeft: labelWidth + 'px' }))
25
+ }
26
+ if ('right' === flow) {
27
+ setStyles(() => ({ paddingRight: labelWidth + 'px' }))
28
+ }
29
+ }, [clear, flow, labelWidth])
30
+
31
+ return (
32
+ <div className={`cove-input-group${clear ? ' clear' : ''}${className ? ' ' + className : ''}`} flow={flow} {...attributes}>
33
+ {label && flow ? (
34
+ <>
35
+ {'left' === flow && (
36
+ <>
37
+ {' '}
38
+ {groupLabel}
39
+ {children}{' '}
40
+ </>
41
+ )}
42
+ {'right' === flow && (
43
+ <>
44
+ {' '}
45
+ {children}
46
+ {groupLabel}{' '}
47
+ </>
48
+ )}
49
+ {'center' === flow && children.length > 1 && (
50
+ <>
51
+ {' '}
52
+ {children[0]}
53
+ {groupLabel}
54
+ {children[1]}{' '}
55
+ </>
56
+ )}
57
+ </>
58
+ ) : (
59
+ children
60
+ )}
61
+ </div>
62
+ )
63
+ }
64
+
65
+ InputGroup.propTypes = {
66
+ /* Text to display for the input group */
67
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
68
+ flow: PropTypes.oneOf(['left', 'center', 'right'])
69
+ }
70
+
71
+ export default InputGroup
@@ -1,45 +1,49 @@
1
1
  import React, { memo } from 'react'
2
2
 
3
- import '../../styles/v2/components/input.scss'
4
-
5
- const InputSelect = memo((
6
- {
7
- label,
8
- value,
9
- options,
10
- fieldName,
11
- section = null,
12
- subsection = null,
13
- required = false,
14
- updateField,
15
- initial: initialValue,
16
- ...attributes
17
- }
18
- ) => {
3
+ import '../../styles/v2/components/input/index.scss'
19
4
 
5
+ const InputSelect = memo(({ label, value, options, fieldName, section = null, subsection = null, required = false, updateField, initial: initialValue, ...attributes }) => {
20
6
  let optionsJsx = ''
21
7
 
22
8
  if (Array.isArray(options)) {
23
9
  //Handle basic array
24
- optionsJsx = options.map(optionName => <option value={optionName} key={optionName}>{optionName}</option>)
10
+ optionsJsx = options.map(optionName => (
11
+ <option value={optionName} key={optionName}>
12
+ {optionName}
13
+ </option>
14
+ ))
25
15
  } else {
26
16
  //Handle object with value/name pairs
27
17
  optionsJsx = []
28
- for (const [ optionValue, optionName ] of Object.entries(options)) {
29
- optionsJsx.push(<option value={optionValue} key={optionValue}>{optionName}</option>)
18
+ for (const [optionValue, optionName] of Object.entries(options)) {
19
+ optionsJsx.push(
20
+ <option value={optionValue} key={optionValue}>
21
+ {optionName}
22
+ </option>
23
+ )
30
24
  }
31
25
  }
32
26
 
33
27
  if (initialValue) {
34
- optionsJsx.unshift(<option value="" key="initial">{initialValue}</option>)
28
+ optionsJsx.unshift(
29
+ <option value='' key='initial'>
30
+ {initialValue}
31
+ </option>
32
+ )
35
33
  }
36
34
 
37
35
  return (
38
36
  <label>
39
- {label && <span className="edit-label">{label}</span>}
40
- <select className={required && !value ? 'warning' : ''} name={fieldName} value={value} onChange={(event) => {
41
- updateField(section, subsection, fieldName, event.target.value)
42
- }} {...attributes}>
37
+ {label && <span className='edit-label cove-input__label'>{label}</span>}
38
+ <select
39
+ className={required && !value ? 'warning' : ''}
40
+ name={fieldName}
41
+ value={value}
42
+ onChange={event => {
43
+ updateField(section, subsection, fieldName, event.target.value)
44
+ }}
45
+ {...attributes}
46
+ >
43
47
  {optionsJsx}
44
48
  </select>
45
49
  </label>