@coreui/react 4.0.0-rc.6 → 4.1.0

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 (30) hide show
  1. package/README.md +157 -0
  2. package/dist/components/accordion/CAccordionCollapse.d.ts +1 -1
  3. package/dist/components/collapse/CCollapse.d.ts +4 -0
  4. package/dist/components/form/CFormCheck.d.ts +4 -0
  5. package/dist/components/form/CFormLabel.d.ts +2 -2
  6. package/dist/components/form/CFormSelect.d.ts +13 -0
  7. package/dist/components/placeholder/CPlaceholder.d.ts +51 -0
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.es.js +281 -177
  10. package/dist/index.es.js.map +1 -1
  11. package/dist/index.js +281 -176
  12. package/dist/index.js.map +1 -1
  13. package/package.json +10 -10
  14. package/src/components/accordion/CAccordionCollapse.tsx +1 -1
  15. package/src/components/collapse/CCollapse.tsx +36 -3
  16. package/src/components/form/CFormCheck.tsx +30 -3
  17. package/src/components/form/CFormLabel.tsx +2 -2
  18. package/src/components/form/CFormSelect.tsx +28 -2
  19. package/src/components/form/__tests__/CFormLabel.spec.tsx +8 -0
  20. package/src/components/form/__tests__/__snapshots__/CFormLabel.spec.tsx.snap +11 -0
  21. package/src/components/grid/CCol.tsx +8 -8
  22. package/src/components/grid/CContainer.tsx +3 -3
  23. package/src/components/grid/CRow.tsx +6 -6
  24. package/src/components/offcanvas/COffcanvas.tsx +15 -2
  25. package/src/components/offcanvas/__tests__/COffcanvas.spec.tsx +1 -1
  26. package/src/components/offcanvas/__tests__/__snapshots__/COffcanvas.spec.tsx.snap +2 -2
  27. package/src/components/placeholder/CPlaceholder.tsx +114 -0
  28. package/src/components/placeholder/__tests__/CPlaceholder.spec.tsx +21 -0
  29. package/src/components/placeholder/__tests__/__snapshots__/CPlaceholder.spec.tsx.snap +17 -0
  30. package/src/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react",
3
- "version": "4.0.0-rc.6",
3
+ "version": "4.1.0",
4
4
  "description": "UI Components Library for React.js",
5
5
  "keywords": [
6
6
  "react",
@@ -26,7 +26,7 @@
26
26
  "main": "dist/index.js",
27
27
  "module": "dist/index.es.js",
28
28
  "jsnext:main": "dist/index.es.js",
29
- "typings": "dist/index.d.ts",
29
+ "types": "dist/index.d.ts",
30
30
  "files": [
31
31
  "dist/",
32
32
  "src/"
@@ -35,24 +35,24 @@
35
35
  "build": "rollup -c"
36
36
  },
37
37
  "devDependencies": {
38
- "@popperjs/core": "^2.10.2",
39
- "@rollup/plugin-commonjs": "^21.0.0",
38
+ "@popperjs/core": "^2.11.0",
39
+ "@rollup/plugin-commonjs": "^21.0.1",
40
40
  "@rollup/plugin-node-resolve": "^13.0.6",
41
41
  "@rollup/plugin-typescript": "^8.3.0",
42
- "@testing-library/jest-dom": "^5.14.1",
42
+ "@testing-library/jest-dom": "^5.15.1",
43
43
  "@testing-library/react": "^12.1.2",
44
- "@types/react": "^17.0.30",
45
- "@types/react-dom": "^17.0.9",
46
- "@types/react-transition-group": "^4.4.3",
44
+ "@types/react": "^17.0.37",
45
+ "@types/react-dom": "^17.0.11",
46
+ "@types/react-transition-group": "^4.4.4",
47
47
  "classnames": "^2.3.1",
48
48
  "prop-types": "^15.7.2",
49
49
  "react": "^17.0.2",
50
50
  "react-dom": "^17.0.2",
51
51
  "react-popper": "^2.2.5",
52
52
  "react-transition-group": "^4.4.2",
53
- "rollup": "^2.58.0",
53
+ "rollup": "^2.60.1",
54
54
  "rollup-plugin-peer-deps-external": "^2.2.4",
55
- "typescript": "^4.4.4"
55
+ "typescript": "^4.5.2"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "react": "^17",
@@ -2,7 +2,7 @@ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { CCollapse, CCollapseProps } from '../collapse/CCollapse'
4
4
 
5
- export const CAccordionCollapse = forwardRef<HTMLDivElement, CCollapseProps>(
5
+ export const CAccordionCollapse = forwardRef<HTMLDivElement, Omit<CCollapseProps, 'horizontal'>>(
6
6
  ({ children, ...props }, ref) => {
7
7
  return (
8
8
  <CCollapse className="accordion-collapse" {...props} ref={ref}>
@@ -10,6 +10,10 @@ export interface CCollapseProps extends HTMLAttributes<HTMLDivElement> {
10
10
  * A string of all className you want applied to the base component.
11
11
  */
12
12
  className?: string
13
+ /**
14
+ * Set horizontal collapsing to transition the width instead of height.
15
+ */
16
+ horizontal?: boolean
13
17
  /**
14
18
  * Callback fired when the component requests to be hidden.
15
19
  */
@@ -25,8 +29,9 @@ export interface CCollapseProps extends HTMLAttributes<HTMLDivElement> {
25
29
  }
26
30
 
27
31
  export const CCollapse = forwardRef<HTMLDivElement, CCollapseProps>(
28
- ({ children, className, onHide, onShow, visible, ...rest }, ref) => {
32
+ ({ children, className, horizontal, onHide, onShow, visible, ...rest }, ref) => {
29
33
  const [height, setHeight] = useState<number>()
34
+ const [width, setWidth] = useState<number>()
30
35
  const collapseRef = useRef<HTMLDivElement>(null)
31
36
  const forkedRef = useForkedRef(ref, collapseRef)
32
37
 
@@ -42,27 +47,53 @@ export const CCollapse = forwardRef<HTMLDivElement, CCollapseProps>(
42
47
 
43
48
  const onEntering = () => {
44
49
  onShow && onShow()
50
+
51
+ if (horizontal) {
52
+ collapseRef.current && setWidth(collapseRef.current.scrollWidth)
53
+ return
54
+ }
45
55
  collapseRef.current && setHeight(collapseRef.current.scrollHeight)
46
56
  }
47
57
 
48
58
  const onEntered = () => {
59
+ if (horizontal) {
60
+ setWidth(0)
61
+ return
62
+ }
49
63
  setHeight(0)
50
64
  }
51
65
 
52
66
  const onExit = () => {
67
+ if (horizontal) {
68
+ collapseRef.current && setWidth(collapseRef.current.scrollWidth)
69
+ return
70
+ }
53
71
  collapseRef.current && setHeight(collapseRef.current.scrollHeight)
54
72
  }
55
73
 
56
74
  const onExiting = () => {
57
75
  onHide && onHide()
76
+ if (horizontal) {
77
+ setWidth(0)
78
+ return
79
+ }
58
80
  setHeight(0)
59
81
  }
60
82
 
61
83
  const onExited = () => {
84
+ if (horizontal) {
85
+ setWidth(0)
86
+ return
87
+ }
62
88
  setHeight(0)
63
89
  }
64
90
 
65
- const _className = classNames(className)
91
+ const _className = classNames(
92
+ {
93
+ 'collapse-horizontal': horizontal,
94
+ },
95
+ className,
96
+ )
66
97
 
67
98
  return (
68
99
  <CSSTransition
@@ -77,10 +108,11 @@ export const CCollapse = forwardRef<HTMLDivElement, CCollapseProps>(
77
108
  {(state) => {
78
109
  const transitionClass = getTransitionClass(state)
79
110
  const currentHeight = height === 0 ? null : { height }
111
+ const currentWidth = width === 0 ? null : { width }
80
112
  return (
81
113
  <div
82
114
  className={classNames(_className, transitionClass)}
83
- style={{ ...currentHeight }}
115
+ style={{ ...currentHeight, ...currentWidth }}
84
116
  {...rest}
85
117
  ref={forkedRef}
86
118
  >
@@ -96,6 +128,7 @@ export const CCollapse = forwardRef<HTMLDivElement, CCollapseProps>(
96
128
  CCollapse.propTypes = {
97
129
  children: PropTypes.node,
98
130
  className: PropTypes.string,
131
+ horizontal: PropTypes.bool,
99
132
  onHide: PropTypes.func,
100
133
  onShow: PropTypes.func,
101
134
  visible: PropTypes.bool,
@@ -1,7 +1,8 @@
1
- import React, { forwardRef, InputHTMLAttributes, ReactNode } from 'react'
1
+ import React, { forwardRef, InputHTMLAttributes, ReactNode, useEffect, useRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
 
5
+ import { useForkedRef } from '../../utils/hooks'
5
6
  import { Colors, Shapes } from '../Types'
6
7
 
7
8
  import { CFormLabel } from './CFormLabel'
@@ -46,6 +47,10 @@ export interface CFormCheckProps extends InputHTMLAttributes<HTMLInputElement> {
46
47
  * The id global attribute defines an identifier (ID) that must be unique in the whole document.
47
48
  */
48
49
  id?: string
50
+ /**
51
+ * Input Checkbox indeterminate Property.
52
+ */
53
+ indeterminate?: boolean
49
54
  /**
50
55
  * Group checkboxes or radios on the same horizontal row by adding.
51
56
  */
@@ -70,9 +75,30 @@ export interface CFormCheckProps extends InputHTMLAttributes<HTMLInputElement> {
70
75
 
71
76
  export const CFormCheck = forwardRef<HTMLInputElement, CFormCheckProps>(
72
77
  (
73
- { className, button, hitArea, id, inline, invalid, label, type = 'checkbox', valid, ...rest },
78
+ {
79
+ className,
80
+ button,
81
+ hitArea,
82
+ id,
83
+ indeterminate,
84
+ inline,
85
+ invalid,
86
+ label,
87
+ type = 'checkbox',
88
+ valid,
89
+ ...rest
90
+ },
74
91
  ref,
75
92
  ) => {
93
+ const inputRef = useRef<HTMLInputElement>(null)
94
+ const forkedRef = useForkedRef(ref, inputRef)
95
+
96
+ useEffect(() => {
97
+ if (inputRef.current && indeterminate) {
98
+ inputRef.current.indeterminate = indeterminate
99
+ }
100
+ }, [indeterminate])
101
+
76
102
  const _className = classNames(
77
103
  'form-check',
78
104
  {
@@ -102,7 +128,7 @@ export const CFormCheck = forwardRef<HTMLInputElement, CFormCheckProps>(
102
128
  )
103
129
 
104
130
  const formControl = () => {
105
- return <input type={type} className={inputClassName} id={id} {...rest} ref={ref} />
131
+ return <input type={type} className={inputClassName} id={id} {...rest} ref={forkedRef} />
106
132
  }
107
133
 
108
134
  const formLabel = () => {
@@ -141,6 +167,7 @@ CFormCheck.propTypes = {
141
167
  className: PropTypes.string,
142
168
  hitArea: PropTypes.oneOf(['full']),
143
169
  id: PropTypes.string,
170
+ indeterminate: PropTypes.bool,
144
171
  inline: PropTypes.bool,
145
172
  invalid: PropTypes.bool,
146
173
  label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
@@ -1,8 +1,8 @@
1
- import React, { forwardRef, HTMLAttributes } from 'react'
1
+ import React, { forwardRef, AllHTMLAttributes } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
 
5
- export interface CFormLabelProps extends HTMLAttributes<HTMLLabelElement> {
5
+ export interface CFormLabelProps extends AllHTMLAttributes<HTMLLabelElement> {
6
6
  /**
7
7
  * A string of all className you want applied to the component.
8
8
  */
@@ -2,6 +2,11 @@ import React, { ChangeEventHandler, forwardRef, InputHTMLAttributes } from 'reac
2
2
  import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
 
5
+ type Option = {
6
+ disabled?: boolean
7
+ label?: string
8
+ value?: string
9
+ }
5
10
  export interface CFormSelectProps extends Omit<InputHTMLAttributes<HTMLSelectElement>, 'size'> {
6
11
  /**
7
12
  * A string of all className you want applied to the component.
@@ -19,6 +24,13 @@ export interface CFormSelectProps extends Omit<InputHTMLAttributes<HTMLSelectEle
19
24
  * Method called immediately after the `value` prop changes.
20
25
  */
21
26
  onChange?: ChangeEventHandler<HTMLSelectElement>
27
+ /**
28
+ * Options list of the select component. Available keys: `label`, `value`, `disabled`.
29
+ * Examples:
30
+ * - `options={[{ value: 'js', label: 'JavaScript' }, { value: 'html', label: 'HTML', disabled: true }]}`
31
+ * - `options={['js', 'html']}`
32
+ */
33
+ options?: Option[] | string[]
22
34
  /**
23
35
  * Size the component small or large.
24
36
  */
@@ -36,7 +48,7 @@ export interface CFormSelectProps extends Omit<InputHTMLAttributes<HTMLSelectEle
36
48
  }
37
49
 
38
50
  export const CFormSelect = forwardRef<HTMLSelectElement, CFormSelectProps>(
39
- ({ children, className, htmlSize, invalid, size, valid, ...rest }, ref) => {
51
+ ({ children, className, htmlSize, invalid, options, size, valid, ...rest }, ref) => {
40
52
  const _className = classNames(
41
53
  'form-select',
42
54
  {
@@ -48,7 +60,20 @@ export const CFormSelect = forwardRef<HTMLSelectElement, CFormSelectProps>(
48
60
  )
49
61
  return (
50
62
  <select className={_className} size={htmlSize} {...rest} ref={ref}>
51
- {children}
63
+ {options
64
+ ? options.map((option, index) => {
65
+ return (
66
+ <option
67
+ {...(typeof option === 'object' &&
68
+ option.disabled && { disabled: option.disabled })}
69
+ {...(typeof option === 'object' && option.value && { value: option.value })}
70
+ key={index}
71
+ >
72
+ {typeof option === 'string' ? option : option.label}
73
+ </option>
74
+ )
75
+ })
76
+ : children}
52
77
  </select>
53
78
  )
54
79
  },
@@ -59,6 +84,7 @@ CFormSelect.propTypes = {
59
84
  className: PropTypes.string,
60
85
  htmlSize: PropTypes.number,
61
86
  invalid: PropTypes.bool,
87
+ options: PropTypes.array,
62
88
  size: PropTypes.oneOf(['sm', 'lg']),
63
89
  valid: PropTypes.bool,
64
90
  }
@@ -15,3 +15,11 @@ test('CFormLabel customize className', async () => {
15
15
  expect(container.firstChild).toHaveClass('form-label')
16
16
  expect(container.firstChild).toHaveTextContent('Test')
17
17
  })
18
+
19
+ test('CFormLabel customize htmlFor', async () => {
20
+ const { container } = render(<CFormLabel htmlFor="bazinga">Test</CFormLabel>)
21
+ expect(container).toMatchSnapshot()
22
+ expect(container.firstChild).toHaveAttribute('for', 'bazinga')
23
+ expect(container.firstChild).toHaveClass('form-label')
24
+ expect(container.firstChild).toHaveTextContent('Test')
25
+ })
@@ -10,6 +10,17 @@ exports[`CFormLabel customize className 1`] = `
10
10
  </div>
11
11
  `;
12
12
 
13
+ exports[`CFormLabel customize htmlFor 1`] = `
14
+ <div>
15
+ <label
16
+ class="form-label"
17
+ for="bazinga"
18
+ >
19
+ Test
20
+ </label>
21
+ </div>
22
+ `;
23
+
13
24
  exports[`loads and displays CFormLabel component 1`] = `
14
25
  <div>
15
26
  <label
@@ -66,7 +66,7 @@ const BREAKPOINTS = [
66
66
 
67
67
  export const CCol = forwardRef<HTMLDivElement, CColProps>(
68
68
  ({ children, className, ...rest }, ref) => {
69
- const repsonsiveCLassNames: string[] = []
69
+ const repsonsiveClassNames: string[] = []
70
70
 
71
71
  BREAKPOINTS.forEach((bp) => {
72
72
  const breakpoint = rest[bp]
@@ -75,34 +75,34 @@ export const CCol = forwardRef<HTMLDivElement, CColProps>(
75
75
  const infix = bp === 'xs' ? '' : `-${bp}`
76
76
 
77
77
  if (typeof breakpoint === 'number' || typeof breakpoint === 'string') {
78
- repsonsiveCLassNames.push(`col${infix}-${breakpoint}`)
78
+ repsonsiveClassNames.push(`col${infix}-${breakpoint}`)
79
79
  }
80
80
 
81
81
  if (typeof breakpoint === 'boolean') {
82
- repsonsiveCLassNames.push(`col${infix}`)
82
+ repsonsiveClassNames.push(`col${infix}`)
83
83
  }
84
84
 
85
85
  if (breakpoint && typeof breakpoint === 'object') {
86
86
  if (typeof breakpoint.span === 'number' || typeof breakpoint.span === 'string') {
87
- repsonsiveCLassNames.push(`col${infix}-${breakpoint.span}`)
87
+ repsonsiveClassNames.push(`col${infix}-${breakpoint.span}`)
88
88
  }
89
89
 
90
90
  if (typeof breakpoint.span === 'boolean') {
91
- repsonsiveCLassNames.push(`col${infix}`)
91
+ repsonsiveClassNames.push(`col${infix}`)
92
92
  }
93
93
 
94
94
  if (typeof breakpoint.order === 'number' || typeof breakpoint.order === 'string') {
95
- repsonsiveCLassNames.push(`order${infix}-${breakpoint.order}`)
95
+ repsonsiveClassNames.push(`order${infix}-${breakpoint.order}`)
96
96
  }
97
97
 
98
98
  if (typeof breakpoint.offset === 'number') {
99
- repsonsiveCLassNames.push(`offset${infix}-${breakpoint.offset}`)
99
+ repsonsiveClassNames.push(`offset${infix}-${breakpoint.offset}`)
100
100
  }
101
101
  }
102
102
  })
103
103
 
104
104
  const _className = classNames(
105
- repsonsiveCLassNames.length ? repsonsiveCLassNames : 'col',
105
+ repsonsiveClassNames.length ? repsonsiveClassNames : 'col',
106
106
  className,
107
107
  )
108
108
 
@@ -44,17 +44,17 @@ const BREAKPOINTS = [
44
44
 
45
45
  export const CContainer = forwardRef<HTMLDivElement, CContainerProps>(
46
46
  ({ children, className, ...rest }, ref) => {
47
- const repsonsiveCLassNames: string[] = []
47
+ const repsonsiveClassNames: string[] = []
48
48
 
49
49
  BREAKPOINTS.forEach((bp) => {
50
50
  const breakpoint = rest[bp]
51
51
  delete rest[bp]
52
52
 
53
- breakpoint && repsonsiveCLassNames.push(`container-${bp}`)
53
+ breakpoint && repsonsiveClassNames.push(`container-${bp}`)
54
54
  })
55
55
 
56
56
  const _className = classNames(
57
- repsonsiveCLassNames.length ? repsonsiveCLassNames : 'container',
57
+ repsonsiveClassNames.length ? repsonsiveClassNames : 'container',
58
58
  className,
59
59
  )
60
60
 
@@ -63,7 +63,7 @@ const BREAKPOINTS = [
63
63
 
64
64
  export const CRow = forwardRef<HTMLDivElement, CRowProps>(
65
65
  ({ children, className, ...rest }, ref) => {
66
- const repsonsiveCLassNames: string[] = []
66
+ const repsonsiveClassNames: string[] = []
67
67
 
68
68
  BREAKPOINTS.forEach((bp) => {
69
69
  const breakpoint = rest[bp]
@@ -73,21 +73,21 @@ export const CRow = forwardRef<HTMLDivElement, CRowProps>(
73
73
 
74
74
  if (typeof breakpoint === 'object') {
75
75
  if (breakpoint.cols) {
76
- repsonsiveCLassNames.push(`row-cols${infix}-${breakpoint.cols}`)
76
+ repsonsiveClassNames.push(`row-cols${infix}-${breakpoint.cols}`)
77
77
  }
78
78
  if (typeof breakpoint.gutter === 'number') {
79
- repsonsiveCLassNames.push(`g${infix}-${breakpoint.gutter}`)
79
+ repsonsiveClassNames.push(`g${infix}-${breakpoint.gutter}`)
80
80
  }
81
81
  if (typeof breakpoint.gutterX === 'number') {
82
- repsonsiveCLassNames.push(`gx${infix}-${breakpoint.gutterX}`)
82
+ repsonsiveClassNames.push(`gx${infix}-${breakpoint.gutterX}`)
83
83
  }
84
84
  if (typeof breakpoint.gutterY === 'number') {
85
- repsonsiveCLassNames.push(`gy${infix}-${breakpoint.gutterY}`)
85
+ repsonsiveClassNames.push(`gy${infix}-${breakpoint.gutterY}`)
86
86
  }
87
87
  }
88
88
  })
89
89
 
90
- const _className = classNames('row', repsonsiveCLassNames, className)
90
+ const _className = classNames('row', repsonsiveClassNames, className)
91
91
 
92
92
  return (
93
93
  <div className={_className} ref={ref}>
@@ -150,8 +150,21 @@ export const COffcanvas = forwardRef<HTMLDivElement, COffcanvasProps>(
150
150
  </Transition>
151
151
  {typeof window !== 'undefined' && portal
152
152
  ? backdrop &&
153
- createPortal(<CBackdrop visible={_visible} onClick={handleDismiss} />, document.body)
154
- : backdrop && <CBackdrop visible={_visible} onClick={handleDismiss} />}
153
+ createPortal(
154
+ <CBackdrop
155
+ className="offcanvas-backdrop"
156
+ onClick={handleDismiss}
157
+ visible={_visible}
158
+ />,
159
+ document.body,
160
+ )
161
+ : backdrop && (
162
+ <CBackdrop
163
+ className="offcanvas-backdrop"
164
+ onClick={handleDismiss}
165
+ visible={_visible}
166
+ />
167
+ )}
155
168
  </>
156
169
  )
157
170
  },
@@ -51,7 +51,7 @@ test('COffcanvas customize and event on click backdrop', async () => {
51
51
  expect(container.firstChild).toHaveClass('show')
52
52
  expect(container.firstChild).toHaveTextContent('Test')
53
53
  expect(onHide).toHaveBeenCalledTimes(0)
54
- const backdrop = document.querySelector('.modal-backdrop')
54
+ const backdrop = document.querySelector('.offcanvas-backdrop')
55
55
  if (backdrop !== null) {
56
56
  fireEvent.click(backdrop)
57
57
  }
@@ -11,7 +11,7 @@ exports[`COffcanvas customize and event on click backdrop 1`] = `
11
11
  Test
12
12
  </div>
13
13
  <div
14
- class="modal-backdrop fade show"
14
+ class="offcanvas-backdrop fade show"
15
15
  />
16
16
  </div>
17
17
  `;
@@ -27,7 +27,7 @@ exports[`COffcanvas customize and event on keypress 1`] = `
27
27
  Test
28
28
  </div>
29
29
  <div
30
- class="modal-backdrop fade show"
30
+ class="offcanvas-backdrop fade show"
31
31
  />
32
32
  </div>
33
33
  `;
@@ -0,0 +1,114 @@
1
+ import React, { ElementType, forwardRef, HTMLAttributes } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import classNames from 'classnames'
4
+
5
+ import { Colors, colorPropType } from '../Types'
6
+
7
+ export interface CPlaceholderProps extends HTMLAttributes<HTMLSpanElement> {
8
+ /**
9
+ * Set animation type to better convey the perception of something being actively loaded.
10
+ */
11
+ animation?: 'glow' | 'wave'
12
+ /**
13
+ * A string of all className you want applied to the component.
14
+ */
15
+ className?: string
16
+ /**
17
+ * Sets the color context of the component to one of CoreUI’s themed colors.
18
+ *
19
+ * @type 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | string
20
+ */
21
+ color?: Colors
22
+ /**
23
+ * Component used for the root node. Either a string to use a HTML element or a component.
24
+ */
25
+ component?: string | ElementType
26
+ /**
27
+ * Size the component extra small, small, or large.
28
+ */
29
+ size?: 'xs' | 'sm' | 'lg'
30
+ /**
31
+ * The number of columns on extra small devices (<576px).
32
+ */
33
+ xs?: number
34
+ /**
35
+ * The number of columns on small devices (<768px).
36
+ */
37
+ sm?: number
38
+ /**
39
+ * The number of columns on medium devices (<992px).
40
+ */
41
+ md?: number
42
+ /**
43
+ * The number of columns on large devices (<1200px).
44
+ */
45
+ lg?: number
46
+ /**
47
+ * The number of columns on X-Large devices (<1400px).
48
+ */
49
+ xl?: number
50
+ /**
51
+ * The number of columns on XX-Large devices (≥1400px).
52
+ */
53
+ xxl?: number
54
+ }
55
+
56
+ const BREAKPOINTS = [
57
+ 'xxl' as const,
58
+ 'xl' as const,
59
+ 'lg' as const,
60
+ 'md' as const,
61
+ 'sm' as const,
62
+ 'xs' as const,
63
+ ]
64
+
65
+ export const CPlaceholder = forwardRef<HTMLSpanElement, CPlaceholderProps>(
66
+ (
67
+ { children, animation, className, color, component: Component = 'span', size, ...rest },
68
+ ref,
69
+ ) => {
70
+ const repsonsiveClassNames: string[] = []
71
+
72
+ BREAKPOINTS.forEach((bp) => {
73
+ const breakpoint = rest[bp]
74
+ delete rest[bp]
75
+
76
+ const infix = bp === 'xs' ? '' : `-${bp}`
77
+
78
+ if (typeof breakpoint === 'number') {
79
+ repsonsiveClassNames.push(`col${infix}-${breakpoint}`)
80
+ }
81
+
82
+ if (typeof breakpoint === 'boolean') {
83
+ repsonsiveClassNames.push(`col${infix}`)
84
+ }
85
+ })
86
+
87
+ const _className = classNames(
88
+ animation ? `placeholder-${animation}` : 'placeholder',
89
+ {
90
+ [`bg-${color}`]: color,
91
+ [`placeholder-${size}`]: size,
92
+ },
93
+ repsonsiveClassNames,
94
+ className,
95
+ )
96
+
97
+ return (
98
+ <Component className={_className} {...rest} ref={ref}>
99
+ {children}
100
+ </Component>
101
+ )
102
+ },
103
+ )
104
+
105
+ CPlaceholder.propTypes = {
106
+ animation: PropTypes.oneOf(['glow', 'wave']),
107
+ children: PropTypes.node,
108
+ className: PropTypes.string,
109
+ color: colorPropType,
110
+ component: PropTypes.elementType,
111
+ size: PropTypes.oneOf(['xs', 'sm', 'lg']),
112
+ }
113
+
114
+ CPlaceholder.displayName = 'CPlaceholder'
@@ -0,0 +1,21 @@
1
+ import * as React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import '@testing-library/jest-dom/extend-expect'
4
+ import { CPlaceholder } from '../../../index'
5
+
6
+ test('loads and displays CPlaceholder component', async () => {
7
+ const { container } = render(<CPlaceholder color="primary" />)
8
+ expect(container).toMatchSnapshot()
9
+ })
10
+
11
+ test('CPlaceholder customize', async () => {
12
+ const { container } = render(
13
+ <CPlaceholder animation="glow" className="bazinga" color="secondary" size="lg" sm={7} />,
14
+ )
15
+ expect(container).toMatchSnapshot()
16
+ expect(container.firstChild).toHaveClass('bazinga')
17
+ expect(container.firstChild).toHaveClass('bg-secondary')
18
+ expect(container.firstChild).toHaveClass('col-sm-7')
19
+ expect(container.firstChild).toHaveClass('placeholder-lg')
20
+ expect(container.firstChild).toHaveClass('placeholder-glow')
21
+ })
@@ -0,0 +1,17 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`CPlaceholder customize 1`] = `
4
+ <div>
5
+ <span
6
+ class="placeholder-glow bg-secondary placeholder-lg col-sm-7 bazinga"
7
+ />
8
+ </div>
9
+ `;
10
+
11
+ exports[`loads and displays CPlaceholder component 1`] = `
12
+ <div>
13
+ <span
14
+ class="placeholder bg-primary"
15
+ />
16
+ </div>
17
+ `;
package/src/index.ts CHANGED
@@ -86,6 +86,7 @@ import { CNavbarText } from './components/navbar/CNavbarText'
86
86
  import { CNavbarToggler } from './components/navbar/CNavbarToggler'
87
87
  import { CPagination } from './components/pagination/CPagination'
88
88
  import { CPaginationItem } from './components/pagination/CPaginationItem'
89
+ import { CPlaceholder } from './components/placeholder/CPlaceholder'
89
90
  import { CPopover } from './components/popover/CPopover'
90
91
  import { CProgress } from './components/progress/CProgress'
91
92
  import { CProgressBar } from './components/progress/CProgressBar'
@@ -206,6 +207,7 @@ export {
206
207
  CNavbarToggler,
207
208
  CPagination,
208
209
  CPaginationItem,
210
+ CPlaceholder,
209
211
  CPopover,
210
212
  CProgress,
211
213
  CProgressBar,