@coreui/react 5.0.0 → 5.2.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 (114) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/button/CButton.js +5 -1
  3. package/dist/cjs/components/button/CButton.js.map +1 -1
  4. package/dist/cjs/components/dropdown/CDropdown.js +2 -1
  5. package/dist/cjs/components/dropdown/CDropdown.js.map +1 -1
  6. package/dist/cjs/components/dropdown/utils.d.ts +0 -1
  7. package/dist/cjs/components/dropdown/utils.js +0 -13
  8. package/dist/cjs/components/dropdown/utils.js.map +1 -1
  9. package/dist/cjs/components/popover/CPopover.js +47 -40
  10. package/dist/cjs/components/popover/CPopover.js.map +1 -1
  11. package/dist/cjs/components/tabs/CTab.d.ts +12 -0
  12. package/dist/cjs/components/tabs/CTab.js +25 -0
  13. package/dist/cjs/components/tabs/CTab.js.map +1 -0
  14. package/dist/cjs/components/tabs/CTabList.d.ts +16 -0
  15. package/dist/cjs/components/tabs/CTabList.js +54 -0
  16. package/dist/cjs/components/tabs/CTabList.js.map +1 -0
  17. package/dist/cjs/components/tabs/CTabPane.d.ts +6 -0
  18. package/dist/cjs/components/tabs/CTabPane.js +4 -2
  19. package/dist/cjs/components/tabs/CTabPane.js.map +1 -1
  20. package/dist/cjs/components/tabs/CTabPanel.d.ts +28 -0
  21. package/dist/cjs/components/tabs/CTabPanel.js +43 -0
  22. package/dist/cjs/components/tabs/CTabPanel.js.map +1 -0
  23. package/dist/cjs/components/tabs/CTabs.d.ts +22 -0
  24. package/dist/cjs/components/tabs/CTabs.js +28 -0
  25. package/dist/cjs/components/tabs/CTabs.js.map +1 -0
  26. package/dist/cjs/components/tabs/index.d.ts +5 -1
  27. package/dist/cjs/components/tooltip/CTooltip.js +50 -40
  28. package/dist/cjs/components/tooltip/CTooltip.js.map +1 -1
  29. package/dist/cjs/hooks/useForkedRef.d.ts +0 -1
  30. package/dist/cjs/hooks/usePopper.d.ts +1 -0
  31. package/dist/cjs/hooks/usePopper.js +11 -4
  32. package/dist/cjs/hooks/usePopper.js.map +1 -1
  33. package/dist/cjs/index.js +8 -0
  34. package/dist/cjs/index.js.map +1 -1
  35. package/dist/cjs/node_modules/react-transition-group/esm/CSSTransition.js +1 -2
  36. package/dist/cjs/node_modules/react-transition-group/esm/CSSTransition.js.map +1 -1
  37. package/dist/cjs/node_modules/react-transition-group/esm/Transition.js +1 -2
  38. package/dist/cjs/node_modules/react-transition-group/esm/Transition.js.map +1 -1
  39. package/dist/cjs/utils/getNextActiveElement.d.ts +2 -0
  40. package/dist/cjs/utils/getNextActiveElement.js +19 -0
  41. package/dist/cjs/utils/getNextActiveElement.js.map +1 -0
  42. package/dist/cjs/utils/index.d.ts +2 -1
  43. package/dist/esm/components/button/CButton.js +5 -1
  44. package/dist/esm/components/button/CButton.js.map +1 -1
  45. package/dist/esm/components/dropdown/CDropdown.js +2 -1
  46. package/dist/esm/components/dropdown/CDropdown.js.map +1 -1
  47. package/dist/esm/components/dropdown/utils.d.ts +0 -1
  48. package/dist/esm/components/dropdown/utils.js +1 -13
  49. package/dist/esm/components/dropdown/utils.js.map +1 -1
  50. package/dist/esm/components/popover/CPopover.js +47 -40
  51. package/dist/esm/components/popover/CPopover.js.map +1 -1
  52. package/dist/esm/components/tabs/CTab.d.ts +12 -0
  53. package/dist/esm/components/tabs/CTab.js +23 -0
  54. package/dist/esm/components/tabs/CTab.js.map +1 -0
  55. package/dist/esm/components/tabs/CTabList.d.ts +16 -0
  56. package/dist/esm/components/tabs/CTabList.js +52 -0
  57. package/dist/esm/components/tabs/CTabList.js.map +1 -0
  58. package/dist/esm/components/tabs/CTabPane.d.ts +6 -0
  59. package/dist/esm/components/tabs/CTabPane.js +4 -2
  60. package/dist/esm/components/tabs/CTabPane.js.map +1 -1
  61. package/dist/esm/components/tabs/CTabPanel.d.ts +28 -0
  62. package/dist/esm/components/tabs/CTabPanel.js +41 -0
  63. package/dist/esm/components/tabs/CTabPanel.js.map +1 -0
  64. package/dist/esm/components/tabs/CTabs.d.ts +22 -0
  65. package/dist/esm/components/tabs/CTabs.js +25 -0
  66. package/dist/esm/components/tabs/CTabs.js.map +1 -0
  67. package/dist/esm/components/tabs/index.d.ts +5 -1
  68. package/dist/esm/components/tooltip/CTooltip.js +50 -40
  69. package/dist/esm/components/tooltip/CTooltip.js.map +1 -1
  70. package/dist/esm/hooks/useForkedRef.d.ts +0 -1
  71. package/dist/esm/hooks/usePopper.d.ts +1 -0
  72. package/dist/esm/hooks/usePopper.js +11 -4
  73. package/dist/esm/hooks/usePopper.js.map +1 -1
  74. package/dist/esm/index.js +4 -0
  75. package/dist/esm/index.js.map +1 -1
  76. package/dist/esm/node_modules/react-transition-group/esm/CSSTransition.js +1 -2
  77. package/dist/esm/node_modules/react-transition-group/esm/CSSTransition.js.map +1 -1
  78. package/dist/esm/node_modules/react-transition-group/esm/Transition.js +1 -2
  79. package/dist/esm/node_modules/react-transition-group/esm/Transition.js.map +1 -1
  80. package/dist/esm/utils/getNextActiveElement.d.ts +2 -0
  81. package/dist/esm/utils/getNextActiveElement.js +15 -0
  82. package/dist/esm/utils/getNextActiveElement.js.map +1 -0
  83. package/dist/esm/utils/index.d.ts +2 -1
  84. package/package.json +13 -13
  85. package/src/components/button/CButton.tsx +5 -2
  86. package/src/components/button/__tests__/CButton.spec.tsx +1 -1
  87. package/src/components/button-group/__tests__/CButtonGroup.spec.tsx +6 -6
  88. package/src/components/button-group/__tests__/CButtonToolbar.spec.tsx +6 -6
  89. package/src/components/dropdown/CDropdown.tsx +2 -2
  90. package/src/components/dropdown/__tests__/CDropdownToggle.spec.tsx +1 -1
  91. package/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap +1 -1
  92. package/src/components/dropdown/__tests__/__snapshots__/CDropdownToggle.spec.tsx.snap +1 -1
  93. package/src/components/dropdown/utils.ts +0 -22
  94. package/src/components/modal/__tests__/CModal.spec.tsx +1 -1
  95. package/src/components/nav/__tests__/__snapshots__/CNavItem.spec.tsx.snap +12 -8
  96. package/src/components/popover/CPopover.tsx +59 -66
  97. package/src/components/popover/__tests__/CPopover.spec.tsx +2 -2
  98. package/src/components/popover/__tests__/__snapshots__/CPopover.spec.tsx.snap +22 -0
  99. package/src/components/sidebar/__tests__/__snapshots__/CSidebarBrand.spec.tsx.snap +4 -4
  100. package/src/components/sidebar/__tests__/__snapshots__/CSidebarNav.spec.tsx.snap +6 -2
  101. package/src/components/tabs/CTab.tsx +56 -0
  102. package/src/components/tabs/CTabList.tsx +92 -0
  103. package/src/components/tabs/CTabPane.tsx +9 -2
  104. package/src/components/tabs/CTabPanel.tsx +98 -0
  105. package/src/components/tabs/CTabs.tsx +54 -0
  106. package/src/components/tabs/__tests__/__snapshots__/CTabPane.spec.tsx.snap +1 -1
  107. package/src/components/tabs/index.ts +5 -1
  108. package/src/components/toast/__tests__/__snapshots__/CToaster.spec.tsx.snap +1 -1
  109. package/src/components/tooltip/CTooltip.tsx +63 -66
  110. package/src/components/tooltip/__tests__/__snapshots__/CTooltip.spec.tsx.snap +1 -0
  111. package/src/components/widgets/__tests__/__snapshots__/CWidgetStatsC.spec.tsx.snap +1 -1
  112. package/src/hooks/usePopper.ts +15 -5
  113. package/src/utils/getNextActiveElement.ts +23 -0
  114. package/src/utils/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react",
3
- "version": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "description": "UI Components Library for React.js",
5
5
  "keywords": [
6
6
  "react",
@@ -41,31 +41,31 @@
41
41
  "test:update": "jest --coverage --updateSnapshot"
42
42
  },
43
43
  "dependencies": {
44
- "@coreui/coreui": "^5.0.0",
44
+ "@coreui/coreui": "^5.1.0",
45
45
  "@popperjs/core": "^2.11.8",
46
46
  "prop-types": "^15.8.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@rollup/plugin-commonjs": "^25.0.7",
49
+ "@rollup/plugin-commonjs": "^26.0.1",
50
50
  "@rollup/plugin-node-resolve": "^15.2.3",
51
51
  "@rollup/plugin-typescript": "^11.1.6",
52
- "@testing-library/jest-dom": "^6.4.2",
53
- "@testing-library/react": "^14.2.2",
52
+ "@testing-library/jest-dom": "^6.4.6",
53
+ "@testing-library/react": "^16.0.0",
54
54
  "@types/jest": "^29.5.12",
55
- "@types/react": "18.2.73",
56
- "@types/react-dom": "^18.2.22",
55
+ "@types/react": "18.3.3",
56
+ "@types/react-dom": "^18.3.0",
57
57
  "@types/react-transition-group": "^4.4.10",
58
58
  "classnames": "^2.5.1",
59
59
  "cross-env": "^7.0.3",
60
60
  "jest": "^29.7.0",
61
61
  "jest-environment-jsdom": "^29.7.0",
62
- "react": "^18.2.0",
63
- "react-dom": "^18.2.0",
62
+ "react": "^18.3.1",
63
+ "react-dom": "^18.3.1",
64
64
  "react-transition-group": "^4.4.5",
65
- "rollup": "^4.13.1",
66
- "ts-jest": "^29.1.2",
67
- "tslib": "^2.6.2",
68
- "typescript": "^5.4.3"
65
+ "rollup": "^4.18.0",
66
+ "ts-jest": "^29.1.5",
67
+ "tslib": "^2.6.3",
68
+ "typescript": "^5.5.3"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "react": ">=17",
@@ -74,8 +74,11 @@ export const CButton: PolymorphicRefForwardingComponent<'button', CButtonProps>
74
74
  {...(!rest.href && { type: type })}
75
75
  className={classNames(
76
76
  'btn',
77
- variant ? `btn-${variant}-${color}` : `btn-${color}`,
78
- { [`btn-${size}`]: size },
77
+ {
78
+ [`btn-${color}`]: color && !variant,
79
+ [`btn-${variant}-${color}`]: color && variant,
80
+ [`btn-${size}`]: size,
81
+ },
79
82
  shape,
80
83
  className,
81
84
  )}
@@ -4,7 +4,7 @@ import '@testing-library/jest-dom'
4
4
  import { CButton } from '../../../index'
5
5
 
6
6
  test('loads and displays CButton component', async () => {
7
- const { container } = render(<CButton>Test</CButton>)
7
+ const { container } = render(<CButton color="primary">Test</CButton>)
8
8
  expect(container).toMatchSnapshot()
9
9
  })
10
10
 
@@ -11,9 +11,9 @@ test('loads and displays CButtonGroup component', async () => {
11
11
  test('CButtonGroup customize', async () => {
12
12
  const { container } = render(
13
13
  <CButtonGroup className="bazinga" size="lg" vertical={false}>
14
- <CButton>Test A</CButton>
15
- <CButton>Test B</CButton>
16
- <CButton>Test C</CButton>
14
+ <CButton color="primary">Test A</CButton>
15
+ <CButton color="primary">Test B</CButton>
16
+ <CButton color="primary">Test C</CButton>
17
17
  </CButtonGroup>,
18
18
  )
19
19
  expect(container).toMatchSnapshot()
@@ -25,9 +25,9 @@ test('CButtonGroup customize', async () => {
25
25
  test('CButtonGroup customize vertical', async () => {
26
26
  const { container } = render(
27
27
  <CButtonGroup className="bazinga" size="lg" vertical={true}>
28
- <CButton>Test A</CButton>
29
- <CButton>Test B</CButton>
30
- <CButton>Test C</CButton>
28
+ <CButton color="primary">Test A</CButton>
29
+ <CButton color="primary">Test B</CButton>
30
+ <CButton color="primary">Test C</CButton>
31
31
  </CButtonGroup>,
32
32
  )
33
33
  expect(container).toMatchSnapshot()
@@ -12,14 +12,14 @@ test('CButtonToolbar customize', async () => {
12
12
  const { container } = render(
13
13
  <CButtonToolbar className="bazinga" role="group" aria-label="Bazinga">
14
14
  <CButtonGroup role="group">
15
- <CButton>1</CButton>
16
- <CButton>2</CButton>
17
- <CButton>3</CButton>
15
+ <CButton color="primary">1</CButton>
16
+ <CButton color="primary">2</CButton>
17
+ <CButton color="primary">3</CButton>
18
18
  </CButtonGroup>
19
19
  <CButtonGroup role="group">
20
- <CButton>A</CButton>
21
- <CButton>B</CButton>
22
- <CButton>C</CButton>
20
+ <CButton color="primary">A</CButton>
21
+ <CButton color="primary">B</CButton>
22
+ <CButton color="primary">C</CButton>
23
23
  </CButtonGroup>
24
24
  </CButtonToolbar>,
25
25
  )
@@ -15,10 +15,10 @@ import { PolymorphicRefForwardingComponent } from '../../helpers'
15
15
  import { useForkedRef, usePopper } from '../../hooks'
16
16
  import { placementPropType } from '../../props'
17
17
  import type { Placements } from '../../types'
18
- import { isRTL } from '../../utils'
18
+ import { getNextActiveElement, isRTL } from '../../utils'
19
19
 
20
20
  import type { Alignments, Directions } from './types'
21
- import { getNextActiveElement, getPlacement } from './utils'
21
+ import { getPlacement } from './utils'
22
22
 
23
23
  export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIElement> {
24
24
  /**
@@ -10,7 +10,7 @@ test('loads and displays CDropdownToggle component', async () => {
10
10
 
11
11
  test('CDropdownToggle customize', async () => {
12
12
  const { container } = render(
13
- <CDropdownToggle caret={true} split={true} trigger="focus">
13
+ <CDropdownToggle caret={true} color="primary" split={true} trigger="focus">
14
14
  Test
15
15
  </CDropdownToggle>,
16
16
  )
@@ -17,7 +17,7 @@ exports[`CDropdown example 1`] = `
17
17
  >
18
18
  <button
19
19
  aria-expanded="false"
20
- class="btn btn-primary dropdown-toggle"
20
+ class="btn dropdown-toggle"
21
21
  tabindex="0"
22
22
  type="button"
23
23
  >
@@ -20,7 +20,7 @@ exports[`CDropdownToggle customize 1`] = `
20
20
  exports[`loads and displays CDropdownToggle component 1`] = `
21
21
  <div>
22
22
  <button
23
- class="btn btn-primary dropdown-toggle"
23
+ class="btn dropdown-toggle"
24
24
  tabindex="0"
25
25
  type="button"
26
26
  >
@@ -19,28 +19,6 @@ export const getAlignmentClassNames = (alignment: Alignments) => {
19
19
  return classNames
20
20
  }
21
21
 
22
- export const getNextActiveElement = (
23
- list: HTMLElement[],
24
- activeElement: HTMLElement,
25
- shouldGetNext: boolean,
26
- isCycleAllowed: boolean,
27
- ) => {
28
- const listLength = list.length
29
- let index = list.indexOf(activeElement)
30
-
31
- if (index === -1) {
32
- return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
33
- }
34
-
35
- index += shouldGetNext ? 1 : -1
36
-
37
- if (isCycleAllowed) {
38
- index = (index + listLength) % listLength
39
- }
40
-
41
- return list[Math.max(0, Math.min(index, listLength - 1))]
42
- }
43
-
44
22
  export const getPlacement = (
45
23
  placement: Placement,
46
24
  direction: string | undefined,
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react'
2
2
  import { render, fireEvent } from '@testing-library/react'
3
- import '@testing-library/jest-dom/extend-expect'
3
+ import '@testing-library/jest-dom'
4
4
  import { CModal } from '../../../index'
5
5
 
6
6
  test('loads and displays CModal component', async () => {
@@ -2,35 +2,39 @@
2
2
 
3
3
  exports[`CNavItem customize 1`] = `
4
4
  <div>
5
- <li
5
+ <h3
6
6
  class="nav-item bazinga"
7
7
  >
8
- <h3
8
+ <a
9
9
  aria-current="page"
10
+ aria-disabled="true"
10
11
  class="nav-link bazinga active disabled"
11
12
  disabled=""
12
13
  href="/bazinga"
14
+ tabindex="-1"
13
15
  >
14
16
  Test
15
- </h3>
16
- </li>
17
+ </a>
18
+ </h3>
17
19
  </div>
18
20
  `;
19
21
 
20
22
  exports[`CNavItem customize 2`] = `
21
23
  <div>
22
- <li
24
+ <h3
23
25
  class="nav-item bazinga"
24
26
  >
25
- <h3
27
+ <a
26
28
  aria-current="page"
29
+ aria-disabled="true"
27
30
  class="nav-link bazinga active disabled"
28
31
  disabled=""
29
32
  href="/bazinga"
33
+ tabindex="-1"
30
34
  >
31
35
  Test
32
- </h3>
33
- </li>
36
+ </a>
37
+ </h3>
34
38
  </div>
35
39
  `;
36
40
 
@@ -1,13 +1,12 @@
1
1
  import React, { forwardRef, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react'
2
2
  import classNames from 'classnames'
3
3
  import PropTypes from 'prop-types'
4
- import { Transition } from 'react-transition-group'
5
4
 
6
5
  import { CConditionalPortal } from '../conditional-portal'
7
6
  import { useForkedRef, usePopper } from '../../hooks'
8
7
  import { fallbackPlacementsPropType, triggerPropType } from '../../props'
9
8
  import type { Placements, Triggers } from '../../types'
10
- import { getRTLPlacement, getTransitionDurationFromElement } from '../../utils'
9
+ import { executeAfterTransition, getRTLPlacement } from '../../utils'
11
10
 
12
11
  export interface CPopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'content'> {
13
12
  /**
@@ -101,6 +100,7 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
101
100
  const uID = useRef(`popover${Math.floor(Math.random() * 1_000_000)}`)
102
101
 
103
102
  const { initPopper, destroyPopper } = usePopper()
103
+ const [mounted, setMounted] = useState(false)
104
104
  const [_visible, setVisible] = useState(visible)
105
105
 
106
106
  const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay
@@ -133,14 +133,44 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
133
133
  setVisible(visible)
134
134
  }, [visible])
135
135
 
136
- const toggleVisible = (visible: boolean) => {
137
- if (visible) {
138
- setTimeout(() => setVisible(true), _delay.show)
139
- return
136
+ useEffect(() => {
137
+ if (_visible) {
138
+ setMounted(true)
139
+
140
+ if (popoverRef.current) {
141
+ popoverRef.current.classList.remove('fade', 'show')
142
+ destroyPopper()
143
+ }
144
+
145
+ setTimeout(() => {
146
+ if (togglerRef.current && popoverRef.current) {
147
+ if (animation) {
148
+ popoverRef.current.classList.add('fade')
149
+ }
150
+
151
+ initPopper(togglerRef.current, popoverRef.current, popperConfig)
152
+ popoverRef.current.style.removeProperty('display')
153
+ popoverRef.current.classList.add('show')
154
+ onShow && onShow()
155
+ }
156
+ }, _delay.show)
140
157
  }
141
158
 
142
- setTimeout(() => setVisible(false), _delay.hide)
143
- }
159
+ return () => {
160
+ if (popoverRef.current) {
161
+ popoverRef.current.classList.remove('show')
162
+ onHide && onHide()
163
+ executeAfterTransition(() => {
164
+ if (popoverRef.current) {
165
+ popoverRef.current.style.display = 'none'
166
+ }
167
+
168
+ destroyPopper()
169
+ setMounted(false)
170
+ }, popoverRef.current)
171
+ }
172
+ }
173
+ }, [_visible])
144
174
 
145
175
  return (
146
176
  <>
@@ -150,71 +180,34 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
150
180
  }),
151
181
  ref: togglerRef,
152
182
  ...((trigger === 'click' || trigger.includes('click')) && {
153
- onClick: () => toggleVisible(!_visible),
183
+ onClick: () => setVisible(!_visible),
154
184
  }),
155
185
  ...((trigger === 'focus' || trigger.includes('focus')) && {
156
- onFocus: () => toggleVisible(true),
157
- onBlur: () => toggleVisible(false),
186
+ onFocus: () => setVisible(true),
187
+ onBlur: () => setVisible(false),
158
188
  }),
159
189
  ...((trigger === 'hover' || trigger.includes('hover')) && {
160
- onMouseEnter: () => toggleVisible(true),
161
- onMouseLeave: () => toggleVisible(false),
190
+ onMouseEnter: () => setVisible(true),
191
+ onMouseLeave: () => setVisible(false),
162
192
  }),
163
193
  })}
164
194
  <CConditionalPortal container={container} portal={true}>
165
- <Transition
166
- in={_visible}
167
- mountOnEnter
168
- nodeRef={popoverRef}
169
- onEnter={() => {
170
- if (togglerRef.current && popoverRef.current) {
171
- initPopper(togglerRef.current, popoverRef.current, popperConfig)
172
- }
173
-
174
- onShow
175
- }}
176
- onEntering={() => {
177
- if (togglerRef.current && popoverRef.current) {
178
- popoverRef.current.style.display = 'initial'
179
- }
180
- }}
181
- onExit={onHide}
182
- onExited={() => {
183
- destroyPopper()
184
- }}
185
- timeout={{
186
- enter: 0,
187
- exit: popoverRef.current
188
- ? getTransitionDurationFromElement(popoverRef.current) + 50
189
- : 200,
190
- }}
191
- unmountOnExit
192
- >
193
- {(state) => (
194
- <div
195
- className={classNames(
196
- 'popover',
197
- 'bs-popover-auto',
198
- {
199
- fade: animation,
200
- show: state === 'entered',
201
- },
202
- className,
203
- )}
204
- id={uID.current}
205
- ref={forkedRef}
206
- role="tooltip"
207
- style={{
208
- display: 'none',
209
- }}
210
- {...rest}
211
- >
212
- <div className="popover-arrow"></div>
213
- <div className="popover-header">{title}</div>
214
- <div className="popover-body">{content}</div>
215
- </div>
216
- )}
217
- </Transition>
195
+ {mounted && (
196
+ <div
197
+ className={classNames('popover', 'bs-popover-auto', className)}
198
+ id={uID.current}
199
+ ref={forkedRef}
200
+ role="tooltip"
201
+ style={{
202
+ display: 'none',
203
+ }}
204
+ {...rest}
205
+ >
206
+ <div className="popover-arrow"></div>
207
+ <div className="popover-header">{title}</div>
208
+ <div className="popover-body">{content}</div>
209
+ </div>
210
+ )}
218
211
  </CConditionalPortal>
219
212
  </>
220
213
  )
@@ -6,7 +6,7 @@ import { CPopover, CButton } from '../../../index'
6
6
  test('loads and displays CPopover component', async () => {
7
7
  const { container } = render(
8
8
  <CPopover content="A">
9
- <CButton>Test</CButton>
9
+ <CButton color="primary">Test</CButton>
10
10
  </CPopover>,
11
11
  )
12
12
  expect(container).toMatchSnapshot()
@@ -17,7 +17,7 @@ test('CPopover customize', async () => {
17
17
  let arr, element
18
18
  const { container } = render(
19
19
  <CPopover content="content" title="title" trigger="click" placement="right">
20
- <CButton>Test</CButton>
20
+ <CButton color="primary">Test</CButton>
21
21
  </CPopover>,
22
22
  { container: document.body },
23
23
  )
@@ -3,11 +3,33 @@
3
3
  exports[`CPopover customize 1`] = `
4
4
  <body>
5
5
  <button
6
+ aria-describedby="popover744956"
6
7
  class="btn btn-primary"
7
8
  type="button"
8
9
  >
9
10
  Test
10
11
  </button>
12
+ <div
13
+ class="popover bs-popover-auto fade show"
14
+ id="popover744956"
15
+ role="tooltip"
16
+ style="position: absolute; left: 0px; top: 0px; margin: 0px;"
17
+ >
18
+ <div
19
+ class="popover-arrow"
20
+ style="position: absolute;"
21
+ />
22
+ <div
23
+ class="popover-header"
24
+ >
25
+ title
26
+ </div>
27
+ <div
28
+ class="popover-body"
29
+ >
30
+ content
31
+ </div>
32
+ </div>
11
33
  </body>
12
34
  `;
13
35
 
@@ -2,21 +2,21 @@
2
2
 
3
3
  exports[`CSidebarBrand customize 1`] = `
4
4
  <div>
5
- <div
5
+ <a
6
6
  class="sidebar-brand bazinga"
7
7
  >
8
8
  Test
9
- </div>
9
+ </a>
10
10
  </div>
11
11
  `;
12
12
 
13
13
  exports[`loads and displays CSidebarBrand component 1`] = `
14
14
  <div>
15
- <div
15
+ <a
16
16
  class="sidebar-brand"
17
17
  color="primary"
18
18
  >
19
19
  Test
20
- </div>
20
+ </a>
21
21
  </div>
22
22
  `;
@@ -4,7 +4,9 @@ exports[`CSidebarNav customize 1`] = `
4
4
  <div>
5
5
  <ul
6
6
  class="sidebar-nav bazinga"
7
- />
7
+ >
8
+ Test
9
+ </ul>
8
10
  </div>
9
11
  `;
10
12
 
@@ -12,6 +14,8 @@ exports[`loads and displays CSidebarNav component 1`] = `
12
14
  <div>
13
15
  <ul
14
16
  class="sidebar-nav"
15
- />
17
+ >
18
+ Test
19
+ </ul>
16
20
  </div>
17
21
  `;
@@ -0,0 +1,56 @@
1
+ import React, { forwardRef, HTMLAttributes, useContext } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import classNames from 'classnames'
4
+
5
+ import { TabsContext } from './CTabs'
6
+
7
+ export interface CTabProps extends HTMLAttributes<HTMLButtonElement> {
8
+ /**
9
+ * A string of all className you want applied to the base component.
10
+ */
11
+ className?: string
12
+ /**
13
+ * Item key.
14
+ */
15
+ itemKey: number | string
16
+ }
17
+
18
+ export const CTab = forwardRef<HTMLButtonElement, CTabProps>(
19
+ ({ children, className, itemKey, ...rest }, ref) => {
20
+ const { _activeItemKey, setActiveItemKey, id } = useContext(TabsContext)
21
+
22
+ const isActive = () => itemKey === _activeItemKey
23
+
24
+ return (
25
+ <button
26
+ className={classNames(
27
+ 'nav-link',
28
+ {
29
+ active: isActive(),
30
+ },
31
+ className,
32
+ )}
33
+ id={`${id}${itemKey}-tab`}
34
+ onClick={() => setActiveItemKey(itemKey)}
35
+ onFocus={() => setActiveItemKey(itemKey)}
36
+ role="tab"
37
+ tabIndex={isActive() ? 0 : -1}
38
+ type="button"
39
+ aria-controls={`${id}${itemKey}-tab-pane`}
40
+ aria-selected={isActive()}
41
+ ref={ref}
42
+ {...rest}
43
+ >
44
+ {children}
45
+ </button>
46
+ )
47
+ },
48
+ )
49
+
50
+ CTab.propTypes = {
51
+ children: PropTypes.node,
52
+ className: PropTypes.string,
53
+ itemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
54
+ }
55
+
56
+ CTab.displayName = 'CTab'
@@ -0,0 +1,92 @@
1
+ import React, { forwardRef, HTMLAttributes, KeyboardEvent, useRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import classNames from 'classnames'
4
+
5
+ import { useForkedRef } from '../../hooks'
6
+ import { getNextActiveElement } from '../../utils'
7
+
8
+ export interface CTabListProps extends HTMLAttributes<HTMLDivElement> {
9
+ /**
10
+ * A string of all className you want applied to the base component.
11
+ */
12
+ className?: string
13
+ /**
14
+ * Specify a layout type for component.
15
+ */
16
+ layout?: 'fill' | 'justified'
17
+ /**
18
+ * Set the nav variant to tabs or pills.
19
+ */
20
+ variant?: 'pills' | 'tabs' | 'underline' | 'underline-border'
21
+ }
22
+
23
+ export const CTabList = forwardRef<HTMLDivElement, CTabListProps>(
24
+ ({ children, className, layout, variant, ...rest }, ref) => {
25
+ const tabListRef = useRef<HTMLDivElement>(null)
26
+ const forkedRef = useForkedRef(ref, tabListRef)
27
+
28
+ const handleKeydown = (event: KeyboardEvent<HTMLDivElement>) => {
29
+ if (
30
+ tabListRef.current !== null &&
31
+ (event.key === 'ArrowDown' ||
32
+ event.key === 'ArrowUp' ||
33
+ event.key === 'ArrowLeft' ||
34
+ event.key === 'ArrowRight' ||
35
+ event.key === 'Home' ||
36
+ event.key === 'End')
37
+ ) {
38
+ event.preventDefault()
39
+ const target = event.target as HTMLElement
40
+ // eslint-disable-next-line unicorn/prefer-spread
41
+ const items: HTMLElement[] = Array.from(
42
+ tabListRef.current.querySelectorAll('.nav-link:not(.disabled):not(:disabled)'),
43
+ )
44
+
45
+ let nextActiveElement
46
+
47
+ if (event.key === 'Home' || event.key === 'End') {
48
+ nextActiveElement = event.key === 'End' ? items.at(-1) : items[0]
49
+ } else {
50
+ nextActiveElement = getNextActiveElement(
51
+ items,
52
+ target,
53
+ event.key === 'ArrowDown' || event.key === 'ArrowRight',
54
+ true,
55
+ )
56
+ }
57
+
58
+ if (nextActiveElement) {
59
+ nextActiveElement.focus({ preventScroll: true })
60
+ }
61
+ }
62
+ }
63
+
64
+ return (
65
+ <div
66
+ className={classNames(
67
+ 'nav',
68
+ {
69
+ [`nav-${layout}`]: layout,
70
+ [`nav-${variant}`]: variant,
71
+ },
72
+ className,
73
+ )}
74
+ role="tablist"
75
+ onKeyDown={handleKeydown}
76
+ ref={forkedRef}
77
+ {...rest}
78
+ >
79
+ {children}
80
+ </div>
81
+ )
82
+ },
83
+ )
84
+
85
+ CTabList.propTypes = {
86
+ children: PropTypes.node,
87
+ className: PropTypes.string,
88
+ layout: PropTypes.oneOf(['fill', 'justified']),
89
+ variant: PropTypes.oneOf(['pills', 'tabs', 'underline', 'underline-border']),
90
+ }
91
+
92
+ CTabList.displayName = 'CTabList'