diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index f623800d..00000000
--- a/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/dist/**
-.eslintrc.js
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 2f74cc21..00000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Copyright (c) 2013-present, creativeLabs Lukasz Holeczek.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-'use strict'
-
-module.exports = {
- root: true, // So parent files don't get applied
- env: {
- es6: true,
- browser: true,
- node: true,
- },
- extends: [
- 'plugin:react/recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:prettier/recommended',
- 'plugin:unicorn/recommended',
- ],
- parser: '@typescript-eslint/parser',
- parserOptions: {
- ecmaVersion: 2020,
- sourceType: 'module',
- ecmaFeatures: {
- jsx: true,
- },
- },
- plugins: ['@typescript-eslint', 'react', 'react-hooks'],
- settings: {
- react: {
- pragma: 'React',
- version: 'detect',
- },
- },
- rules: {
- 'unicorn/filename-case': 'off',
- 'unicorn/no-array-for-each': 'off',
- 'unicorn/no-null': 'off',
- 'unicorn/prefer-dom-node-append': 'off',
- 'unicorn/prefer-export-from': 'off',
- 'unicorn/prefer-query-selector': 'off',
- 'unicorn/prevent-abbreviations': 'off',
- },
- overrides: [
- {
- files: ['packages/docs/build/**'],
- env: {
- browser: false,
- node: true,
- },
- parserOptions: {
- sourceType: 'script',
- },
- rules: {
- '@typescript-eslint/no-var-requires': 'off',
- 'no-console': 'off',
- 'unicorn/prefer-module': 'off',
- 'unicorn/prefer-top-level-await': 'off',
- },
- },
- {
- files: ['packages/docs/**'],
- rules: {
- '@typescript-eslint/no-var-requires': 'off',
- 'unicorn/prefer-module': 'off',
- },
- },
- ],
-}
diff --git a/.prettierrc.js b/.prettierrc.js
deleted file mode 100644
index 415ca057..00000000
--- a/.prettierrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports = {
- semi: false,
- trailingComma: "all",
- singleQuote: true,
- printWidth: 100,
- tabWidth: 2
-};
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 94e4f4d1..fbb053e0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 creativeLabs Łukasz Holeczek
+Copyright (c) 2025 creativeLabs Łukasz Holeczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index f1d28a27..f147887e 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,8 @@
·
Request feature
·
+ Roadmap
+ ·
Blog
@@ -46,7 +48,7 @@
Several quick start options are available:
-- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.4.1.zip)
+- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.7.1.zip)
- Clone the repo: `git clone https://github.com/coreui/coreui-react.git`
- Install with [npm](https://www.npmjs.com/): `npm install @coreui/react`
- Install with [yarn](https://yarnpkg.com/): `yarn add @coreui/react`
@@ -101,6 +103,7 @@ import "bootstrap/dist/css/bootstrap.min.css";
- [React Accordion](https://coreui.io/react/docs/components/accordion/)
- [React Alert](https://coreui.io/react/docs/components/alert/)
+- [React Autocomplete](https://coreui.io/react/docs/forms/autocomplete/) **PRO**
- [React Avatar](https://coreui.io/react/docs/components/avatar/)
- [React Badge](https://coreui.io/react/docs/components/badge/)
- [React Breadcrumb](https://coreui.io/react/docs/components/breadcrumb/)
@@ -134,7 +137,8 @@ import "bootstrap/dist/css/bootstrap.min.css";
- [React Progress](https://coreui.io/react/docs/components/progress/)
- [React Radio](https://coreui.io/react/docs/forms/radio/)
- [React Range](https://coreui.io/react/docs/forms/range/)
-- [React Rating](https://coreui.io/react/docs/forms/rating/)
+- [React Range Slider](https://coreui.io/react/docs/forms/range-slider/) **PRO**
+- [React Rating](https://coreui.io/react/docs/forms/rating/) **PRO**
- [React Select](https://coreui.io/react/docs/forms/select/)
- [React Sidebar](https://coreui.io/react/docs/components/sidebar/)
- [React Smart Pagination](https://coreui.io/react/docs/components/smart-pagination/) **PRO**
@@ -179,24 +183,25 @@ CoreUI supports most popular frameworks.
Fully featured, out-of-the-box, templates for your application based on CoreUI.
-- [Angular Admin Template](https://coreui.io/angular)
-- [Bootstrap Admin Template](https://coreui.io/)
-- [React Admin Template](https://coreui.io/react)
-- [Vue Admin Template](https://coreui.io/vue)
+- [Angular Admin Templates](https://coreui.io/themes-templates/admin-dashboard/angular/)
+- [Bootstrap Admin Templates](https://coreui.io/themes-templates/admin-dashboard/bootstrap/)
+- [Next.js Admin Templates](https://coreui.io/themes-templates/admin-dashboard/next-js/)
+- [React Admin Templates](https://coreui.io/themes-templates/admin-dashboard/react/)
+- [Vue Admin Templates](https://coreui.io/themes-templates/admin-dashboard/vue/)
## Contributing
-Please read through our [contributing guidelines](https://github.com/coreui/coreui-react/blob/v4/.github/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development.
+Please read through our [contributing guidelines](https://github.com/coreui/coreui-react/blob/.github/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development.
-Editor preferences are available in the [editor config](https://github.com/coreui/coreui-react/blob/v4/.editorconfig) for easy use in common text editors. Read more and download plugins at .
+Editor preferences are available in the [editor config](https://github.com/coreui/coreui-react/blob/.editorconfig) for easy use in common text editors. Read more and download plugins at .
## Community
Stay up to date on the development of CoreUI and reach out to the community with these helpful resources.
-- Read and subscribe to [The Official CoreUI Blog](https://coreui.io/blog/).
-
-You can also follow [@core_ui on Twitter](https://twitter.com/core_ui).
+- Read and subscribe to [The Official CoreUI Blog](https://coreui.io/blog).
+- Follow [@core_ui on Twitter](https://x.com/core_ui).
+- Discuss, ask questions, and more on [the community Discord](https://discord.gg/pQRWe5XdGm).
## Versioning
@@ -227,4 +232,4 @@ CoreUI is an MIT-licensed open source project and is completely free to use. How
## Copyright and license
-Copyright 2024 creativeLabs Łukasz Holeczek. Code released under the [MIT License](https://github.com/coreui/coreui-react/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
\ No newline at end of file
+Copyright 2025 creativeLabs Łukasz Holeczek. Code released under the [MIT License](https://github.com/coreui/coreui-react/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
\ No newline at end of file
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 00000000..2498120d
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,104 @@
+import eslint from '@eslint/js'
+import tsParser from '@typescript-eslint/parser'
+import eslintPluginUnicorn from 'eslint-plugin-unicorn'
+import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
+import eslintPluginReact from 'eslint-plugin-react'
+import eslintPluginReactHooks from 'eslint-plugin-react-hooks'
+import globals from 'globals'
+import typescriptEslint from 'typescript-eslint'
+
+export default typescriptEslint.config(
+ { ignores: ['**/*.d.ts', '**/coverage', '**/dist', 'eslint.config.mjs'] },
+ {
+ extends: [
+ eslint.configs.recommended,
+ ...typescriptEslint.configs.recommended,
+ eslintPluginUnicorn.configs['flat/recommended'],
+ eslintPluginReact.configs.flat.recommended,
+ eslintPluginReact.configs.flat['jsx-runtime'],
+ ],
+ plugins: {
+ 'react-hooks': eslintPluginReactHooks,
+ },
+ files: ['packages/**/src/**/*.{js,ts,tsx}'],
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.node,
+ },
+ parser: tsParser,
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+ settings: {
+ react: {
+ pragma: 'React',
+ version: 'detect',
+ },
+ },
+ rules: {
+ ...eslintPluginReactHooks.configs.recommended.rules,
+ 'no-console': 'off',
+ 'no-debugger': 'off',
+ 'unicorn/filename-case': 'off',
+ 'unicorn/no-array-for-each': 'off',
+ 'unicorn/no-null': 'off',
+ 'unicorn/prefer-dom-node-append': 'off',
+ 'unicorn/prefer-export-from': 'off',
+ 'unicorn/prefer-query-selector': 'off',
+ 'unicorn/prevent-abbreviations': 'off',
+ 'vue/require-default-prop': 'off',
+ },
+ },
+ {
+ files: ['**/*.mjs'],
+ languageOptions: {
+ globals: {
+ ...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
+ ...globals.node,
+ },
+
+ ecmaVersion: 5,
+ sourceType: 'module',
+ },
+ },
+ {
+ files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
+ languageOptions: {
+ globals: {
+ ...globals.jest,
+ },
+ },
+ },
+ {
+ files: ['packages/docs/build/**'],
+ languageOptions: {
+ globals: {
+ ...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
+ ...globals.node,
+ },
+
+ ecmaVersion: 5,
+ sourceType: 'commonjs',
+ },
+ rules: {
+ '@typescript-eslint/no-var-requires': 'off',
+ 'no-console': 'off',
+ 'unicorn/prefer-module': 'off',
+ 'unicorn/prefer-top-level-await': 'off',
+ },
+ },
+ {
+ files: ['packages/docs/**'],
+ rules: {
+ '@typescript-eslint/no-var-requires': 'off',
+ 'unicorn/prefer-module': 'off',
+ },
+ },
+ eslintPluginPrettierRecommended,
+)
diff --git a/lerna.json b/lerna.json
index 6fea4400..d86538ff 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,6 +1,6 @@
{
"npmClient": "yarn",
"packages": ["packages/*"],
- "version": "5.4.1",
+ "version": "5.7.1",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
diff --git a/package.json b/package.json
index 91b0b416..b7e32b5f 100644
--- a/package.json
+++ b/package.json
@@ -22,17 +22,18 @@
"test:update": "npm-run-all charts:test:update icons:test:update lib:test:update"
},
"devDependencies": {
- "@typescript-eslint/eslint-plugin": "^8.18.1",
- "@typescript-eslint/parser": "^8.18.1",
- "eslint": "8.57.0",
- "eslint-config-prettier": "^9.1.0",
- "eslint-plugin-prettier": "^5.2.1",
- "eslint-plugin-react": "^7.37.2",
- "eslint-plugin-react-hooks": "^5.1.0",
- "eslint-plugin-unicorn": "^56.0.1",
- "lerna": "^8.1.9",
+ "@typescript-eslint/parser": "^8.36.0",
+ "eslint": "^9.31.0",
+ "eslint-config-prettier": "^10.1.5",
+ "eslint-plugin-prettier": "^5.5.1",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-unicorn": "^59.0.1",
+ "globals": "^16.3.0",
+ "lerna": "^8.2.3",
"npm-run-all": "^4.1.5",
- "prettier": "^3.4.2"
+ "prettier": "^3.6.2",
+ "typescript-eslint": "^8.36.0"
},
"overrides": {
"gatsby-remark-external-links": {
diff --git a/packages/coreui-react/LICENSE b/packages/coreui-react/LICENSE
index 94e4f4d1..fbb053e0 100644
--- a/packages/coreui-react/LICENSE
+++ b/packages/coreui-react/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 creativeLabs Łukasz Holeczek
+Copyright (c) 2025 creativeLabs Łukasz Holeczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/packages/coreui-react/README.md b/packages/coreui-react/README.md
index 0dec4d28..190eafc0 100644
--- a/packages/coreui-react/README.md
+++ b/packages/coreui-react/README.md
@@ -46,7 +46,7 @@
Several quick start options are available:
-- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.4.1.zip)
+- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.7.1.zip)
- Clone the repo: `git clone https://github.com/coreui/coreui-react.git`
- Install with [npm](https://www.npmjs.com/): `npm install @coreui/react`
- Install with [yarn](https://yarnpkg.com/): `yarn add @coreui/react`
@@ -259,4 +259,4 @@ Thanks to all the backers and sponsors! Support this project by [becoming a back
## Copyright and license
-Copyright 2024 creativeLabs Łukasz Holeczek. Code released under the [MIT License](https://github.com/coreui/coreui-react/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
+Copyright 2025 creativeLabs Łukasz Holeczek. Code released under the [MIT License](https://github.com/coreui/coreui-react/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
diff --git a/packages/coreui-react/package.json b/packages/coreui-react/package.json
index a878d9d0..4e62fee7 100644
--- a/packages/coreui-react/package.json
+++ b/packages/coreui-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@coreui/react",
- "version": "5.4.1",
+ "version": "5.7.1",
"description": "UI Components Library for React.js",
"keywords": [
"react",
@@ -41,19 +41,21 @@
"test:update": "jest --coverage --updateSnapshot"
},
"dependencies": {
- "@coreui/coreui": "^5.2.0",
+ "@coreui/coreui": "^5.4.1",
"@popperjs/core": "^2.11.8",
"prop-types": "^15.8.1"
},
"devDependencies": {
- "@rollup/plugin-commonjs": "^28.0.2",
- "@rollup/plugin-node-resolve": "^16.0.0",
- "@rollup/plugin-typescript": "^12.1.2",
+ "@rollup/plugin-commonjs": "^28.0.6",
+ "@rollup/plugin-node-resolve": "^16.0.1",
+ "@rollup/plugin-typescript": "^12.1.4",
+ "@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
- "@testing-library/react": "^16.1.0",
+ "@testing-library/react": "^16.3.0",
"@types/jest": "^29.5.14",
- "@types/react": "18.3.17",
- "@types/react-dom": "^18.3.5",
+ "@types/prop-types": "15.7.15",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
"@types/react-transition-group": "^4.4.12",
"classnames": "^2.5.1",
"cross-env": "^7.0.3",
@@ -62,10 +64,10 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-transition-group": "^4.4.5",
- "rollup": "^4.28.1",
- "ts-jest": "^29.2.5",
+ "rollup": "^4.45.0",
+ "ts-jest": "^29.4.0",
"tslib": "^2.8.1",
- "typescript": "^5.7.2"
+ "typescript": "^5.8.3"
},
"peerDependencies": {
"react": ">=17",
diff --git a/packages/coreui-react/src/components/accordion/CAccordion.tsx b/packages/coreui-react/src/components/accordion/CAccordion.tsx
index 9fe4c634..a3a275ae 100644
--- a/packages/coreui-react/src/components/accordion/CAccordion.tsx
+++ b/packages/coreui-react/src/components/accordion/CAccordion.tsx
@@ -1,7 +1,9 @@
-import React, { createContext, forwardRef, HTMLAttributes, useState } from 'react'
+import React, { forwardRef, HTMLAttributes, useState } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
+import { CAccordionContext } from './CAccordionContext'
+
export interface CAccordionProps extends HTMLAttributes {
/**
* The active item key.
@@ -21,14 +23,6 @@ export interface CAccordionProps extends HTMLAttributes {
flush?: boolean
}
-export interface CAccordionContextProps {
- _activeItemKey?: number | string
- alwaysOpen?: boolean
- setActiveKey: React.Dispatch>
-}
-
-export const CAccordionContext = createContext({} as CAccordionContextProps)
-
export const CAccordion = forwardRef(
({ children, activeItemKey, alwaysOpen = false, className, flush, ...rest }, ref) => {
const [_activeItemKey, setActiveKey] = useState(activeItemKey)
@@ -48,8 +42,8 @@ export const CAccordion = forwardRef(
)
CAccordion.propTypes = {
- alwaysOpen: PropTypes.bool,
activeItemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ alwaysOpen: PropTypes.bool,
children: PropTypes.node,
className: PropTypes.string,
flush: PropTypes.bool,
diff --git a/packages/coreui-react/src/components/accordion/CAccordionBody.tsx b/packages/coreui-react/src/components/accordion/CAccordionBody.tsx
index 532f3ff6..d98e8c43 100644
--- a/packages/coreui-react/src/components/accordion/CAccordionBody.tsx
+++ b/packages/coreui-react/src/components/accordion/CAccordionBody.tsx
@@ -2,9 +2,8 @@ import React, { forwardRef, HTMLAttributes, useContext } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
-import { CAccordionItemContext } from './CAccordionItem'
-
import { CCollapse } from './../collapse/CCollapse'
+import { CAccordionItemContext } from './CAccordionItemContext'
export interface CAccordionBodyProps extends HTMLAttributes {
/**
@@ -15,16 +14,16 @@ export interface CAccordionBodyProps extends HTMLAttributes {
export const CAccordionBody = forwardRef(
({ children, className, ...rest }, ref) => {
- const { visible } = useContext(CAccordionItemContext)
+ const { id, visible } = useContext(CAccordionItemContext)
return (
-
+
{children}
)
- },
+ }
)
CAccordionBody.propTypes = {
diff --git a/packages/coreui-react/src/components/accordion/CAccordionButton.tsx b/packages/coreui-react/src/components/accordion/CAccordionButton.tsx
index 2a54e345..b5e9ca4c 100644
--- a/packages/coreui-react/src/components/accordion/CAccordionButton.tsx
+++ b/packages/coreui-react/src/components/accordion/CAccordionButton.tsx
@@ -2,7 +2,7 @@ import React, { forwardRef, HTMLAttributes, useContext } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
-import { CAccordionItemContext } from './CAccordionItem'
+import { CAccordionItemContext } from './CAccordionItemContext'
export interface CAccordionButtonProps extends HTMLAttributes {
/**
@@ -28,7 +28,7 @@ export const CAccordionButton = forwardRef
)
- },
+ }
)
CAccordionButton.propTypes = {
diff --git a/packages/coreui-react/src/components/accordion/CAccordionContext.ts b/packages/coreui-react/src/components/accordion/CAccordionContext.ts
new file mode 100644
index 00000000..4b5aa46f
--- /dev/null
+++ b/packages/coreui-react/src/components/accordion/CAccordionContext.ts
@@ -0,0 +1,9 @@
+import { createContext } from 'react'
+
+export interface CAccordionContextProps {
+ _activeItemKey?: number | string
+ alwaysOpen?: boolean
+ setActiveKey: React.Dispatch>
+}
+
+export const CAccordionContext = createContext({} as CAccordionContextProps)
diff --git a/packages/coreui-react/src/components/accordion/CAccordionItem.tsx b/packages/coreui-react/src/components/accordion/CAccordionItem.tsx
index b87c1509..f69fe197 100644
--- a/packages/coreui-react/src/components/accordion/CAccordionItem.tsx
+++ b/packages/coreui-react/src/components/accordion/CAccordionItem.tsx
@@ -1,5 +1,4 @@
import React, {
- createContext,
forwardRef,
HTMLAttributes,
useContext,
@@ -11,17 +10,14 @@ import React, {
import PropTypes from 'prop-types'
import classNames from 'classnames'
-import { CAccordionContext } from './CAccordion'
-
-export interface CAccordionItemContextProps {
- id: string
- setVisible: (a: boolean) => void
- visible?: boolean
-}
-
-export const CAccordionItemContext = createContext({} as CAccordionItemContextProps)
+import { CAccordionContext } from './CAccordionContext'
+import { CAccordionItemContext } from './CAccordionItemContext'
export interface CAccordionItemProps extends HTMLAttributes {
+ /**
+ * The id global attribute defines an identifier (ID) that must be unique in the whole document.
+ */
+ id?: string
/**
* A string of all className you want applied to the base component.
*/
@@ -33,9 +29,10 @@ export interface CAccordionItemProps extends HTMLAttributes {
}
export const CAccordionItem = forwardRef(
- ({ children, className, itemKey, ...rest }, ref) => {
- const id = useId()
- const _itemKey = useRef(itemKey ?? id)
+ ({ children, className, id, itemKey, ...rest }, ref) => {
+ const uniqueId = useId()
+ const _id = id ?? uniqueId
+ const _itemKey = useRef(itemKey ?? _id)
const { _activeItemKey, alwaysOpen, setActiveKey } = useContext(CAccordionContext)
const [visible, setVisible] = useState(Boolean(_activeItemKey === _itemKey.current))
@@ -52,12 +49,12 @@ export const CAccordionItem = forwardRef(
return (
-
+
{children}
)
- },
+ }
)
CAccordionItem.propTypes = {
diff --git a/packages/coreui-react/src/components/accordion/CAccordionItemContext.ts b/packages/coreui-react/src/components/accordion/CAccordionItemContext.ts
new file mode 100644
index 00000000..bbdeaf74
--- /dev/null
+++ b/packages/coreui-react/src/components/accordion/CAccordionItemContext.ts
@@ -0,0 +1,9 @@
+import { createContext } from 'react'
+
+export interface CAccordionItemContextProps {
+ id: string
+ setVisible: (a: boolean) => void
+ visible?: boolean
+}
+
+export const CAccordionItemContext = createContext({} as CAccordionItemContextProps)
diff --git a/packages/coreui-react/src/components/accordion/__tests__/CAccordion.spec.tsx b/packages/coreui-react/src/components/accordion/__tests__/CAccordion.spec.tsx
index 761cd0ea..e1c8e342 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/CAccordion.spec.tsx
+++ b/packages/coreui-react/src/components/accordion/__tests__/CAccordion.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAccordion } from '../../../index'
+import { CAccordion } from '../index'
test('loads and displays CAccordion component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/accordion/__tests__/CAccordionBody.spec.tsx b/packages/coreui-react/src/components/accordion/__tests__/CAccordionBody.spec.tsx
index 519f0f13..795dc1d6 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/CAccordionBody.spec.tsx
+++ b/packages/coreui-react/src/components/accordion/__tests__/CAccordionBody.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAccordionBody } from '../../../index'
+import { CAccordionBody } from '../index'
test('loads and displays CAccordionBody component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/accordion/__tests__/CAccordionButton.spec.tsx b/packages/coreui-react/src/components/accordion/__tests__/CAccordionButton.spec.tsx
index 1d40548c..265e06bb 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/CAccordionButton.spec.tsx
+++ b/packages/coreui-react/src/components/accordion/__tests__/CAccordionButton.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAccordionButton } from '../../../index'
+import { CAccordionButton } from '../index'
test('loads and displays CAccordionButton component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/accordion/__tests__/CAccordionHeader.spec.tsx b/packages/coreui-react/src/components/accordion/__tests__/CAccordionHeader.spec.tsx
index ac833186..44458eae 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/CAccordionHeader.spec.tsx
+++ b/packages/coreui-react/src/components/accordion/__tests__/CAccordionHeader.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAccordionHeader } from '../../../index'
+import { CAccordionHeader } from '../index'
test('loads and displays CAccordionHeader component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/accordion/__tests__/CAccordionItem.spec.tsx b/packages/coreui-react/src/components/accordion/__tests__/CAccordionItem.spec.tsx
index 10f693f3..d32330c5 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/CAccordionItem.spec.tsx
+++ b/packages/coreui-react/src/components/accordion/__tests__/CAccordionItem.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAccordionItem } from '../../../index'
+import { CAccordionItem } from '../index'
test('loads and displays CAccordionItem component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap b/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap
index b8bd2e1c..dae014eb 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap
+++ b/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap
@@ -3,7 +3,6 @@
exports[`CAccordionButton customize 1`] = `
@@ -15,7 +14,6 @@ exports[`CAccordionButton customize 1`] = `
exports[`loads and displays CAccordionButton component 1`] = `
diff --git a/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap b/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap
index dfea0a55..7491d9d3 100644
--- a/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap
+++ b/packages/coreui-react/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap
@@ -6,7 +6,6 @@ exports[`CAccordionHeader customize 1`] = `
class="accordion-header bazinga"
>
@@ -22,7 +21,6 @@ exports[`loads and displays CAccordionHeader component 1`] = `
class="accordion-header"
>
diff --git a/packages/coreui-react/src/components/alert/__tests__/CAlert.spec.tsx b/packages/coreui-react/src/components/alert/__tests__/CAlert.spec.tsx
index a9581d0a..00e33441 100644
--- a/packages/coreui-react/src/components/alert/__tests__/CAlert.spec.tsx
+++ b/packages/coreui-react/src/components/alert/__tests__/CAlert.spec.tsx
@@ -1,41 +1,62 @@
import * as React from 'react'
-import { render, fireEvent } from '@testing-library/react'
+import { act, render, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAlert } from '../../../index'
+import { CAlert } from '../index'
test('loads and displays CAlert component', async () => {
- const { container } = render(Test )
- expect(container).toMatchSnapshot()
+ let container: HTMLElement
+ await act(async () => {
+ const renderResult = render(Test )
+ container = renderResult.container
+ })
+ expect(container!).toMatchSnapshot()
})
test('CAlert customize', async () => {
- const { container } = render(
-
- Test
- ,
- )
- expect(container).toMatchSnapshot()
- expect(container.firstChild).toHaveClass('bazinga')
- expect(container.firstChild).toHaveClass('bg-secondary')
- expect(container.firstChild).toHaveClass('text-white')
- expect(container.firstChild).toHaveClass('alert-dismissible')
+ let container: HTMLElement
+ await act(async () => {
+ const renderResult = render(
+
+ Test
+
+ )
+ container = renderResult.container
+ })
+ expect(container!).toMatchSnapshot()
+ expect(container!.firstChild).toHaveClass('bazinga')
+ expect(container!.firstChild).toHaveClass('bg-secondary')
+ expect(container!.firstChild).toHaveClass('text-white')
+ expect(container!.firstChild).toHaveClass('alert-dismissible')
})
test('CAlert click close button', async () => {
jest.useFakeTimers()
+
const onClose = jest.fn()
render(
Test
- ,
+
)
+
expect(onClose).toHaveBeenCalledTimes(0)
+
const btn = document.querySelector('.btn-close')
+
if (btn !== null) {
- fireEvent.click(btn)
+ act(() => {
+ fireEvent.click(btn)
+ jest.runAllTimers()
+ })
}
+
expect(onClose).toHaveBeenCalledTimes(1)
- jest.runAllTimers()
- expect(onClose).toHaveBeenCalledTimes(1)
+
jest.useRealTimers()
})
diff --git a/packages/coreui-react/src/components/alert/__tests__/CAlertHeading.spec.tsx b/packages/coreui-react/src/components/alert/__tests__/CAlertHeading.spec.tsx
index f7308616..cbe90bf8 100644
--- a/packages/coreui-react/src/components/alert/__tests__/CAlertHeading.spec.tsx
+++ b/packages/coreui-react/src/components/alert/__tests__/CAlertHeading.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAlertHeading } from '../../../index'
+import { CAlertHeading } from '../index'
test('loads and displays CAlertHeading component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/alert/__tests__/CAlertLink.spec.tsx b/packages/coreui-react/src/components/alert/__tests__/CAlertLink.spec.tsx
index 47dc0afe..25c6bc04 100644
--- a/packages/coreui-react/src/components/alert/__tests__/CAlertLink.spec.tsx
+++ b/packages/coreui-react/src/components/alert/__tests__/CAlertLink.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAlertLink } from '../../../index'
+import { CAlertLink } from '../index'
test('loads and displays CAlertLink component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/avatar/__tests__/CAvatar.spec.tsx b/packages/coreui-react/src/components/avatar/__tests__/CAvatar.spec.tsx
index 3201afe4..2cc0b298 100644
--- a/packages/coreui-react/src/components/avatar/__tests__/CAvatar.spec.tsx
+++ b/packages/coreui-react/src/components/avatar/__tests__/CAvatar.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CAvatar } from '../../../index'
+import { CAvatar } from '../index'
test('loads and displays CAvatar component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/backdrop/__tests__/CBackdrop.spec.tsx b/packages/coreui-react/src/components/backdrop/__tests__/CBackdrop.spec.tsx
index a5cc0290..60cab767 100644
--- a/packages/coreui-react/src/components/backdrop/__tests__/CBackdrop.spec.tsx
+++ b/packages/coreui-react/src/components/backdrop/__tests__/CBackdrop.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CBackdrop } from '../../../index'
+import { CBackdrop } from '../index'
test('loads and displays CBackdrop component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/badge/__tests__/CBadge.spec.tsx b/packages/coreui-react/src/components/badge/__tests__/CBadge.spec.tsx
index 2f8a19dd..1473d6f7 100644
--- a/packages/coreui-react/src/components/badge/__tests__/CBadge.spec.tsx
+++ b/packages/coreui-react/src/components/badge/__tests__/CBadge.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CBadge } from '../../../index'
+import { CBadge } from '../index'
test('loads and displays CBadge component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumb.spec.tsx b/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumb.spec.tsx
index 396a3ce7..9254313c 100644
--- a/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumb.spec.tsx
+++ b/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumb.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CBreadcrumb, CBreadcrumbItem } from '../../../index'
+import { CBreadcrumb, CBreadcrumbItem } from '../index'
test('loads and displays CBreadcrumb component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumbItem.spec.tsx b/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumbItem.spec.tsx
index 08c3c874..fff97974 100644
--- a/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumbItem.spec.tsx
+++ b/packages/coreui-react/src/components/breadcrumb/__tests__/CBreadcrumbItem.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CBreadcrumbItem } from '../../../index'
+import { CBreadcrumbItem } from '../index'
test('loads and displays CBreadcrumbItem component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/button-group/__tests__/CButtonGroup.spec.tsx b/packages/coreui-react/src/components/button-group/__tests__/CButtonGroup.spec.tsx
index 84671690..b7bd6195 100644
--- a/packages/coreui-react/src/components/button-group/__tests__/CButtonGroup.spec.tsx
+++ b/packages/coreui-react/src/components/button-group/__tests__/CButtonGroup.spec.tsx
@@ -1,7 +1,8 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CButtonGroup, CButton } from '../../../index'
+import { CButton } from '../../button/CButton'
+import { CButtonGroup } from '../index'
test('loads and displays CButtonGroup component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/button-group/__tests__/CButtonToolbar.spec.tsx b/packages/coreui-react/src/components/button-group/__tests__/CButtonToolbar.spec.tsx
index f90c74cc..fd7706b8 100644
--- a/packages/coreui-react/src/components/button-group/__tests__/CButtonToolbar.spec.tsx
+++ b/packages/coreui-react/src/components/button-group/__tests__/CButtonToolbar.spec.tsx
@@ -1,7 +1,8 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CButtonToolbar, CButtonGroup, CButton } from '../../../index'
+import { CButton } from '../../button/CButton'
+import { CButtonToolbar, CButtonGroup } from '../index'
test('loads and displays CButtonToolbar component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/button/CButton.tsx b/packages/coreui-react/src/components/button/CButton.tsx
index f8f5e17a..cbb003d7 100644
--- a/packages/coreui-react/src/components/button/CButton.tsx
+++ b/packages/coreui-react/src/components/button/CButton.tsx
@@ -66,7 +66,7 @@ export const CButton: PolymorphicRefForwardingComponent<'button', CButtonProps>
>(
(
{ children, as = 'button', className, color, shape, size, type = 'button', variant, ...rest },
- ref,
+ ref
) => {
return (
className={classNames(
'btn',
{
- [`btn-${color}`]: color && !variant,
- [`btn-${variant}-${color}`]: color && variant,
+ [`btn-${variant}-${color}`]: variant && color,
+ [`btn-${variant}`]: variant && !color,
+ [`btn-${color}`]: !variant && color,
[`btn-${size}`]: size,
},
shape,
- className,
+ className
)}
{...rest}
ref={ref}
@@ -88,7 +89,7 @@ export const CButton: PolymorphicRefForwardingComponent<'button', CButtonProps>
{children}
)
- },
+ }
)
CButton.propTypes = {
diff --git a/packages/coreui-react/src/components/button/__tests__/CButton.spec.tsx b/packages/coreui-react/src/components/button/__tests__/CButton.spec.tsx
index 27f3f8ce..2c8211c2 100644
--- a/packages/coreui-react/src/components/button/__tests__/CButton.spec.tsx
+++ b/packages/coreui-react/src/components/button/__tests__/CButton.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CButton } from '../../../index'
+import { CButton } from '../index'
test('loads and displays CButton component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/button/__tests__/CButtonClose.spec.tsx b/packages/coreui-react/src/components/button/__tests__/CButtonClose.spec.tsx
deleted file mode 100644
index 64bb591f..00000000
--- a/packages/coreui-react/src/components/button/__tests__/CButtonClose.spec.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as React from 'react'
-import { render } from '@testing-library/react'
-import '@testing-library/jest-dom'
-import { CCloseButton } from '../../../index'
-
-test('loads and displays CCloseButton component', async () => {
- const { container } = render(Test )
- expect(container).toMatchSnapshot()
-})
-
-test('CCloseButton customize', async () => {
- const { container } = render(
-
- Test
- ,
- )
- expect(container).toMatchSnapshot()
- expect(container.firstChild).toHaveClass('bazinga')
- expect(container.firstChild).toHaveClass('btn')
- expect(container.firstChild).toHaveClass('btn-close')
- expect(container.firstChild).toHaveClass('btn-close-white')
- expect(container.firstChild).toHaveAttribute('disabled')
-})
diff --git a/packages/coreui-react/src/components/button/__tests__/__snapshots__/CButtonClose.spec.tsx.snap b/packages/coreui-react/src/components/button/__tests__/__snapshots__/CButtonClose.spec.tsx.snap
deleted file mode 100644
index 092b4a2d..00000000
--- a/packages/coreui-react/src/components/button/__tests__/__snapshots__/CButtonClose.spec.tsx.snap
+++ /dev/null
@@ -1,26 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`CCloseButton customize 1`] = `
-
-
- Test
-
-
-`;
-
-exports[`loads and displays CCloseButton component 1`] = `
-
-
- Test
-
-
-`;
diff --git a/packages/coreui-react/src/components/callout/__tests__/CCallout.spec.tsx b/packages/coreui-react/src/components/callout/__tests__/CCallout.spec.tsx
index 86f63675..637727c7 100644
--- a/packages/coreui-react/src/components/callout/__tests__/CCallout.spec.tsx
+++ b/packages/coreui-react/src/components/callout/__tests__/CCallout.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCallout } from '../../../index'
+import { CCallout } from '../index'
test('loads and displays CCallout component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCard.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCard.spec.tsx
index c22e6080..6b4f9b11 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCard.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCard.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCard } from '../../../index'
+import { CCard } from '../index'
test('loads and displays CCard component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardBody.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardBody.spec.tsx
index 8b79b9f5..0bf1ccd9 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardBody.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardBody.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardBody } from '../../../index'
+import { CCardBody } from '../index'
test('loads and displays CCardBody component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardFooter.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardFooter.spec.tsx
index 0b47f51f..991e3c7c 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardFooter.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardFooter.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardFooter } from '../../../index'
+import { CCardFooter } from '../index'
test('loads and displays CCardFooter component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardGroup.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardGroup.spec.tsx
index a0228126..239c1fd9 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardGroup.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardGroup.spec.tsx
@@ -12,7 +12,7 @@ import {
CCardTitle,
CCardText,
CCardGroup,
-} from '../../../index'
+} from '../index'
test('loads and displays CCardGroup component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardHeader.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardHeader.spec.tsx
index ffb43bab..0477b503 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardHeader.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardHeader.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardHeader } from '../../../index'
+import { CCardHeader } from '../index'
test('loads and displays CCardHeader component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardImage.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardImage.spec.tsx
index 88c20fd1..e1214093 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardImage.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardImage.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardImage } from '../../../index'
+import { CCardImage } from '../index'
test('loads and displays CCardImage component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardImageOverlay.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardImageOverlay.spec.tsx
index d161d9c2..6574856b 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardImageOverlay.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardImageOverlay.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardImageOverlay } from '../../../index'
+import { CCardImageOverlay } from '../index'
test('loads and displays CCardImageOverlay component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardLink.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardLink.spec.tsx
index 2251ac29..2373537f 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardLink.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardLink.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardLink } from '../../../index'
+import { CCardLink } from '../index'
test('loads and displays CCardLink component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardSubtitle.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardSubtitle.spec.tsx
index a44165f4..8a23f9c8 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardSubtitle.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardSubtitle.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardSubtitle } from '../../../index'
+import { CCardSubtitle } from '../index'
test('loads and displays CCardSubtitle component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardText.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardText.spec.tsx
index 06d2ca3a..3cbb4ab2 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardText.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardText.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardText } from '../../../index'
+import { CCardText } from '../index'
test('loads and displays CCardText component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/card/__tests__/CCardTitle.spec.tsx b/packages/coreui-react/src/components/card/__tests__/CCardTitle.spec.tsx
index ada47122..9733a7f4 100644
--- a/packages/coreui-react/src/components/card/__tests__/CCardTitle.spec.tsx
+++ b/packages/coreui-react/src/components/card/__tests__/CCardTitle.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCardTitle } from '../../../index'
+import { CCardTitle } from '../index'
test('loads and displays CCardTitle component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/carousel/CCarousel.tsx b/packages/coreui-react/src/components/carousel/CCarousel.tsx
index 4e7e95ca..3d3186d3 100644
--- a/packages/coreui-react/src/components/carousel/CCarousel.tsx
+++ b/packages/coreui-react/src/components/carousel/CCarousel.tsx
@@ -1,6 +1,5 @@
import React, {
Children,
- createContext,
forwardRef,
HTMLAttributes,
TouchEvent,
@@ -14,6 +13,8 @@ import classNames from 'classnames'
import { isInViewport } from '../../utils'
import { useForkedRef } from '../../hooks'
+import { CCarouselContext } from './CCarouselContext'
+
export interface CCarouselProps extends HTMLAttributes {
/**
* index of the active item.
@@ -71,13 +72,6 @@ interface DataType {
timeout?: null | ReturnType
}
-export interface ContextProps {
- setAnimating: (a: boolean) => void
- setCustomInterval: (a: boolean | number) => void
-}
-
-export const CCarouselContext = createContext({} as ContextProps)
-
export const CCarousel = forwardRef(
(
{
@@ -96,7 +90,7 @@ export const CCarousel = forwardRef(
wrap = true,
...rest
},
- ref,
+ ref
) => {
const carouselRef = useRef(null)
const forkedRef = useForkedRef(ref, carouselRef)
@@ -141,7 +135,7 @@ export const CCarousel = forwardRef(
if (typeof interval === 'number') {
data.timeout = setTimeout(
() => nextItemWhenVisible(),
- typeof customInterval === 'number' ? customInterval : interval,
+ typeof customInterval === 'number' ? customInterval : interval
)
}
}
@@ -228,7 +222,7 @@ export const CCarousel = forwardRef(
{
'carousel-fade': transition === 'crossfade',
},
- className,
+ className
)}
{...(dark && { 'data-coreui-theme': 'dark' })}
onMouseEnter={_pause}
@@ -288,7 +282,7 @@ export const CCarousel = forwardRef(
)
- },
+ }
)
CCarousel.propTypes = {
diff --git a/packages/coreui-react/src/components/carousel/CCarouselCaption.tsx b/packages/coreui-react/src/components/carousel/CCarouselCaption.tsx
index 6e1cd5d1..353a6b1b 100644
--- a/packages/coreui-react/src/components/carousel/CCarouselCaption.tsx
+++ b/packages/coreui-react/src/components/carousel/CCarouselCaption.tsx
@@ -12,7 +12,7 @@ export interface CCarouselCaptionProps extends HTMLAttributes {
export const CCarouselCaption = forwardRef(
({ className, ...rest }, ref) => {
return
- },
+ }
)
CCarouselCaption.propTypes = {
diff --git a/packages/coreui-react/src/components/carousel/CCarouselContext.ts b/packages/coreui-react/src/components/carousel/CCarouselContext.ts
new file mode 100644
index 00000000..6e21932b
--- /dev/null
+++ b/packages/coreui-react/src/components/carousel/CCarouselContext.ts
@@ -0,0 +1,8 @@
+import { createContext } from 'react'
+
+export interface CCarouselContextProps {
+ setAnimating: (a: boolean) => void
+ setCustomInterval: (a: boolean | number) => void
+}
+
+export const CCarouselContext = createContext({} as CCarouselContextProps)
\ No newline at end of file
diff --git a/packages/coreui-react/src/components/carousel/CCarouselItem.tsx b/packages/coreui-react/src/components/carousel/CCarouselItem.tsx
index 3b8744cc..90f4f6ca 100644
--- a/packages/coreui-react/src/components/carousel/CCarouselItem.tsx
+++ b/packages/coreui-react/src/components/carousel/CCarouselItem.tsx
@@ -3,7 +3,9 @@ import PropTypes from 'prop-types'
import classNames from 'classnames'
import { useForkedRef } from '../../hooks'
-import { CCarouselContext } from './CCarousel'
+
+import { CCarouselContext } from './CCarouselContext'
+
export interface CCarouselItemProps extends HTMLAttributes {
/**
* @ignore
@@ -29,7 +31,7 @@ export const CCarouselItem = forwardRef(
const carouselItemRef = useRef(null)
const forkedRef = useForkedRef(ref, carouselItemRef)
- const prevActive = useRef()
+ const prevActive = useRef(undefined)
const [directionClassName, setDirectionClassName] = useState()
const [orderClassName, setOrderClassName] = useState()
const [activeClassName, setActiveClassName] = useState(active && 'active')
@@ -101,7 +103,7 @@ export const CCarouselItem = forwardRef(
activeClassName,
directionClassName,
orderClassName,
- className,
+ className
)}
ref={forkedRef}
{...rest}
@@ -109,7 +111,7 @@ export const CCarouselItem = forwardRef(
{children}
)
- },
+ }
)
CCarouselItem.propTypes = {
diff --git a/packages/coreui-react/src/components/carousel/__tests__/CCarousel.spec.tsx b/packages/coreui-react/src/components/carousel/__tests__/CCarousel.spec.tsx
index 4abb647b..c681e5b7 100644
--- a/packages/coreui-react/src/components/carousel/__tests__/CCarousel.spec.tsx
+++ b/packages/coreui-react/src/components/carousel/__tests__/CCarousel.spec.tsx
@@ -1,8 +1,7 @@
import React from 'react'
-import { render, fireEvent } from '@testing-library/react'
-import { getByText } from '@testing-library/dom'
+import { render, fireEvent, getByText } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCarousel, CCarouselCaption, CCarouselItem } from '../../../index'
+import { CCarousel, CCarouselCaption, CCarouselItem } from '../index'
test('loads and displays CCarousel component', async () => {
const { container } = render(
diff --git a/packages/coreui-react/src/components/close-button/__tests__/CCloseButton.spec.tsx b/packages/coreui-react/src/components/close-button/__tests__/CCloseButton.spec.tsx
index a70c90ea..3eb0dbc0 100644
--- a/packages/coreui-react/src/components/close-button/__tests__/CCloseButton.spec.tsx
+++ b/packages/coreui-react/src/components/close-button/__tests__/CCloseButton.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCloseButton } from '../../../index'
+import { CCloseButton } from '../index'
test('loads and displays CCloseButton component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/collapse/__tests__/CCollapse.spec.tsx b/packages/coreui-react/src/components/collapse/__tests__/CCollapse.spec.tsx
index 7d701afe..faee720d 100644
--- a/packages/coreui-react/src/components/collapse/__tests__/CCollapse.spec.tsx
+++ b/packages/coreui-react/src/components/collapse/__tests__/CCollapse.spec.tsx
@@ -1,7 +1,7 @@
import React from 'react'
-import { render, screen } from '@testing-library/react'
+import { render, screen, act } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CCollapse } from '../../../index'
+import { CCollapse } from '../index'
test('loads and displays CCollapse component', async () => {
const { container } = render(Test )
@@ -15,26 +15,45 @@ test('CCollapse customize', async () => {
})
test('CCollapse use case test', async () => {
+ jest.useFakeTimers()
+
const { rerender } = render(Test )
+
expect(screen.getByText('Test')).toHaveClass('collapse')
expect(screen.getByText('Test')).not.toHaveClass('show')
expect(screen.getByText('Test')).not.toHaveClass('collapsing')
- rerender(Test )
+
+ act(() => {
+ rerender(Test )
+ })
+
expect(screen.getByText('Test')).not.toHaveClass('collapse')
expect(screen.getByText('Test')).not.toHaveClass('show')
expect(screen.getByText('Test')).toHaveClass('collapsing')
- await new Promise((r) => setTimeout(r, 1000))
+
+ act(() => {
+ jest.runAllTimers()
+ })
+
expect(screen.getByText('Test')).toHaveClass('collapse')
expect(screen.getByText('Test')).toHaveClass('show')
expect(screen.getByText('Test')).not.toHaveClass('collapsing')
- rerender(Test )
+
+ act(() => {
+ rerender(Test )
+ })
+
expect(screen.getByText('Test')).not.toHaveClass('collapse')
expect(screen.getByText('Test')).not.toHaveClass('show')
expect(screen.getByText('Test')).toHaveClass('collapsing')
- await new Promise((r) => setTimeout(r, 1000))
+
+ act(() => {
+ jest.runAllTimers()
+ })
+
expect(screen.getByText('Test')).toHaveClass('collapse')
expect(screen.getByText('Test')).not.toHaveClass('show')
expect(screen.getByText('Test')).not.toHaveClass('collapsing')
- jest.runAllTimers()
+
jest.useRealTimers()
})
diff --git a/packages/coreui-react/src/components/dropdown/CDropdown.tsx b/packages/coreui-react/src/components/dropdown/CDropdown.tsx
index 076c1812..2d8a7bbd 100644
--- a/packages/coreui-react/src/components/dropdown/CDropdown.tsx
+++ b/packages/coreui-react/src/components/dropdown/CDropdown.tsx
@@ -1,15 +1,18 @@
import React, {
- createContext,
ElementType,
forwardRef,
HTMLAttributes,
- RefObject,
+ useCallback,
useEffect,
+ useMemo,
useRef,
useState,
} from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
+import type { Options } from '@popperjs/core'
+
+import { CDropdownContext } from './CDropdownContext'
import { PolymorphicRefForwardingComponent } from '../../helpers'
import { useForkedRef, usePopper } from '../../hooks'
@@ -22,91 +25,152 @@ import { getPlacement } from './utils'
export interface CDropdownProps extends HTMLAttributes {
/**
- * Set aligment of dropdown menu.
+ * Specifies the alignment of the React Dropdown Menu within this React Dropdown.
+ *
+ * @example
+ * // Align dropdown menu to the end on large devices, otherwise start
+ *
+ * Toggle dropdown
+ *
+ * Action
+ * Another Action
+ *
+ *
*
* @type 'start' | 'end' | { xs: 'start' | 'end' } | { sm: 'start' | 'end' } | { md: 'start' | 'end' } | { lg: 'start' | 'end' } | { xl: 'start' | 'end'} | { xxl: 'start' | 'end'}
*/
alignment?: Alignments
+
/**
- * Component used for the root node. Either a string to use a HTML element or a component.
+ * Determines the root node component (native HTML element or a custom React component) for the React Dropdown.
*/
as?: ElementType
+
/**
- * Configure the auto close behavior of the dropdown:
- * - `true` - the dropdown will be closed by clicking outside or inside the dropdown menu.
- * - `false` - the dropdown will be closed by clicking the toggle button and manually calling hide or toggle method. (Also will not be closed by pressing esc key)
- * - `'inside'` - the dropdown will be closed (only) by clicking inside the dropdown menu.
- * - `'outside'` - the dropdown will be closed (only) by clicking outside the dropdown menu.
+ * Configures automatic closing behavior for the React Dropdown:
+ * - `true` - Close on clicks inside or outside of the React Dropdown Menu.
+ * - `false` - Disable auto-close; manually call `hide` or `toggle` (also not closed by `Escape`).
+ * - `'inside'` - Close only when clicking inside the React Dropdown Menu.
+ * - `'outside'` - Close only when clicking outside the React Dropdown Menu.
+ *
+ * @example
+ * // Close only when user clicks outside of the menu
+ *
*/
autoClose?: 'inside' | 'outside' | boolean
+
/**
- * A string of all className you want applied to the base component.
+ * Adds custom classes to the React Dropdown root element.
*/
className?: string
+
/**
- * Appends the react dropdown menu to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`.
+ * Appends the React Dropdown Menu to a specific element. You can pass an HTML element or a function returning an element. Defaults to `document.body`.
+ *
+ * @example
+ * // Append the menu to a custom container
+ * const myContainer = document.getElementById('my-container')
+ *
+ *
*
* @since 4.11.0
*/
container?: DocumentFragment | Element | (() => DocumentFragment | Element | null) | null
+
/**
- * Sets a darker color scheme to match a dark navbar.
+ * Applies a darker color scheme to the React Dropdown Menu, often used within dark navbars.
*/
dark?: boolean
+
/**
- * Sets a specified direction and location of the dropdown menu.
+ * Specifies the direction of the React Dropdown.
*/
direction?: 'center' | 'dropup' | 'dropup-center' | 'dropend' | 'dropstart'
+
/**
- * Offset of the dropdown menu relative to its target.
+ * Defines x and y offsets ([x, y]) for the React Dropdown Menu relative to its target.
+ *
+ * @example
+ * // Offset the menu 10px in X and 5px in Y direction
+ *
+ * ...
+ *
*/
offset?: [number, number]
+
/**
- * Callback fired when the component requests to be hidden.
+ * Callback fired right before the React Dropdown becomes hidden.
*
* @since 4.9.0
*/
onHide?: () => void
+
/**
- * Callback fired when the component requests to be shown.
+ * Callback fired immediately after the React Dropdown is displayed.
*/
onShow?: () => void
+
/**
- * Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property.
+ * Determines the placement of the React Dropdown Menu after Popper.js modifiers.
*
- * @type 'auto' | 'top-end' | 'top' | 'top-start' | 'bottom-end' | 'bottom' | 'bottom-start' | 'right-start' | 'right' | 'right-end' | 'left-start' | 'left' | 'left-end'
+ * @type 'auto' | 'auto-start' | 'auto-end' | 'top-end' | 'top' | 'top-start' | 'bottom-end' | 'bottom' | 'bottom-start' | 'right-start' | 'right' | 'right-end' | 'left-start' | 'left' | 'left-end'
*/
placement?: Placements
+
/**
- * If you want to disable dynamic positioning set this property to `true`.
+ * Enables or disables dynamic positioning via Popper.js for the React Dropdown Menu.
*/
popper?: boolean
+
/**
- * Generates dropdown menu using createPortal.
+ * Provides a custom Popper.js configuration or a function that returns a modified Popper.js configuration for advanced positioning of the React Dropdown Menu. [Read more](https://popper.js.org/docs/v2/constructors/#options)
+ *
+ * @example
+ * // Providing a custom popper config
+ * ...
+ *
+ * @since 5.5.0
+ */
+ popperConfig?: Partial | ((defaultPopperConfig: Partial) => Partial)
+
+ /**
+ * Renders the React Dropdown Menu using a React Portal, allowing it to escape the DOM hierarchy for improved positioning.
*
* @since 4.8.0
*/
portal?: boolean
+
/**
- * Set the dropdown variant to an btn-group, dropdown, input-group, and nav-item.
+ * Defines the visual variant of the React Dropdown
*/
variant?: 'btn-group' | 'dropdown' | 'input-group' | 'nav-item'
+
/**
- * Toggle the visibility of dropdown menu component.
+ * Controls the visibility of the React Dropdown Menu:
+ * - `true` - Visible
+ * - `false` - Hidden
+ *
+ * @example
+ * // Programmatically manage the dropdown visibility
+ * const [visible, setVisible] = useState(false)
+ *
+ *
+ * ...
+ *
+ *
*/
visible?: boolean
}
-interface ContextProps extends CDropdownProps {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- dropdownToggleRef: RefObject
- dropdownMenuRef: RefObject
- setVisible: React.Dispatch>
- portal: boolean
-}
-
-export const CDropdownContext = createContext({} as ContextProps)
-
export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> = forwardRef<
HTMLDivElement | HTMLLIElement,
CDropdownProps
@@ -126,79 +190,105 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
onShow,
placement = 'bottom-start',
popper = true,
+ popperConfig,
portal = false,
variant = 'btn-group',
visible = false,
...rest
},
- ref,
+ ref
) => {
const dropdownRef = useRef(null)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const dropdownToggleRef = useRef(null)
const dropdownMenuRef = useRef(null)
const forkedRef = useForkedRef(ref, dropdownRef)
+ const [dropdownToggleElement, setDropdownToggleElement] = useState(null)
const [_visible, setVisible] = useState(visible)
const { initPopper, destroyPopper } = usePopper()
- const Component = variant === 'nav-item' ? 'li' : as
-
- // Disable popper if responsive aligment is set.
- if (typeof alignment === 'object') {
- popper = false
- }
+ const dropdownToggleRef = useCallback((node: HTMLElement | null) => {
+ if (node) {
+ setDropdownToggleElement(node)
+ }
+ }, [])
- const contextValues = {
- alignment,
- container,
- dark,
- dropdownToggleRef,
- dropdownMenuRef,
- popper,
- portal,
- variant,
- visible: _visible,
- setVisible,
- }
+ const allowPopperUse = popper && typeof alignment !== 'object'
+ const Component = variant === 'nav-item' ? 'li' : as
- const popperConfig = {
- modifiers: [
- {
- name: 'offset',
- options: {
- offset: offset,
+ const computedPopperConfig: Partial = useMemo(() => {
+ const defaultPopperConfig = {
+ modifiers: [
+ {
+ name: 'offset',
+ options: {
+ offset,
+ },
},
- },
- ],
- placement: getPlacement(placement, direction, alignment, isRTL(dropdownMenuRef.current)),
- }
+ ],
+ placement: getPlacement(placement, direction, alignment, isRTL(dropdownMenuRef.current)),
+ }
+
+ return {
+ ...defaultPopperConfig,
+ ...(typeof popperConfig === 'function' ? popperConfig(defaultPopperConfig) : popperConfig),
+ }
+ }, [offset, placement, direction, alignment, popperConfig])
useEffect(() => {
- setVisible(visible)
+ if (visible) {
+ handleShow()
+ } else {
+ handleHide()
+ }
}, [visible])
useEffect(() => {
- if (_visible && dropdownToggleRef.current && dropdownMenuRef.current) {
- dropdownToggleRef.current.focus()
- popper && initPopper(dropdownToggleRef.current, dropdownMenuRef.current, popperConfig)
+ const toggleElement = dropdownToggleElement
+ const menuElement = dropdownMenuRef.current
+ if (allowPopperUse && menuElement && toggleElement && _visible) {
+ initPopper(toggleElement, menuElement, computedPopperConfig)
+ }
+ }, [dropdownToggleElement])
+
+ const handleShow = () => {
+ const toggleElement = dropdownToggleElement
+ const menuElement = dropdownMenuRef.current
+
+ if (toggleElement && menuElement) {
+ setVisible(true)
+
+ if (allowPopperUse) {
+ initPopper(toggleElement, menuElement, computedPopperConfig)
+ }
+
+ toggleElement.focus()
+ toggleElement.addEventListener('keydown', handleKeydown)
+ menuElement.addEventListener('keydown', handleKeydown)
+
window.addEventListener('mouseup', handleMouseUp)
window.addEventListener('keyup', handleKeyup)
- dropdownToggleRef.current.addEventListener('keydown', handleKeydown)
- dropdownMenuRef.current.addEventListener('keydown', handleKeydown)
- onShow && onShow()
+
+ onShow?.()
}
+ }
- return () => {
- popper && destroyPopper()
- window.removeEventListener('mouseup', handleMouseUp)
- window.removeEventListener('keyup', handleKeyup)
- dropdownToggleRef.current &&
- dropdownToggleRef.current.removeEventListener('keydown', handleKeydown)
- dropdownMenuRef.current &&
- dropdownMenuRef.current.removeEventListener('keydown', handleKeydown)
- onHide && onHide()
+ const handleHide = () => {
+ setVisible(false)
+
+ const toggleElement = dropdownToggleElement
+ const menuElement = dropdownMenuRef.current
+
+ if (allowPopperUse) {
+ destroyPopper()
}
- }, [_visible])
+
+ toggleElement?.removeEventListener('keydown', handleKeydown)
+ menuElement?.removeEventListener('keydown', handleKeydown)
+
+ window.removeEventListener('mouseup', handleMouseUp)
+ window.removeEventListener('keyup', handleKeyup)
+
+ onHide?.()
+ }
const handleKeydown = (event: KeyboardEvent) => {
if (
@@ -209,7 +299,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
event.preventDefault()
const target = event.target as HTMLElement
const items: HTMLElement[] = Array.from(
- dropdownMenuRef.current.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)'),
+ dropdownMenuRef.current.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)')
)
getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
}
@@ -221,16 +311,16 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
}
if (event.key === 'Escape') {
- setVisible(false)
+ handleHide()
}
}
const handleMouseUp = (event: Event) => {
- if (!dropdownToggleRef.current || !dropdownMenuRef.current) {
+ if (!dropdownToggleElement || !dropdownMenuRef.current) {
return
}
- if (dropdownToggleRef.current.contains(event.target as HTMLElement)) {
+ if (dropdownToggleElement.contains(event.target as HTMLElement)) {
return
}
@@ -239,11 +329,25 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
(autoClose === 'inside' && dropdownMenuRef.current.contains(event.target as HTMLElement)) ||
(autoClose === 'outside' && !dropdownMenuRef.current.contains(event.target as HTMLElement))
) {
- setTimeout(() => setVisible(false), 1)
+ setTimeout(() => handleHide(), 1)
return
}
}
+ const contextValues = {
+ alignment,
+ container,
+ dark,
+ dropdownMenuRef,
+ dropdownToggleRef,
+ handleHide,
+ handleShow,
+ popper: allowPopperUse,
+ portal,
+ variant,
+ visible: _visible,
+ }
+
return (
{variant === 'input-group' ? (
@@ -258,7 +362,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
[`${direction}`]:
direction && direction !== 'center' && direction !== 'dropup-center',
},
- className,
+ className
)}
{...rest}
ref={forkedRef}
@@ -268,7 +372,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
)}
)
- },
+ }
)
const alignmentDirection = PropTypes.oneOf(['start', 'end'])
@@ -297,6 +401,7 @@ CDropdown.propTypes = {
onShow: PropTypes.func,
placement: placementPropType,
popper: PropTypes.bool,
+ popperConfig: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
portal: PropTypes.bool,
variant: PropTypes.oneOf(['btn-group', 'dropdown', 'input-group', 'nav-item']),
visible: PropTypes.bool,
diff --git a/packages/coreui-react/src/components/dropdown/CDropdownContext.ts b/packages/coreui-react/src/components/dropdown/CDropdownContext.ts
new file mode 100644
index 00000000..d46b69d2
--- /dev/null
+++ b/packages/coreui-react/src/components/dropdown/CDropdownContext.ts
@@ -0,0 +1,18 @@
+import { createContext, RefObject } from 'react'
+import { Alignments } from './types'
+
+export interface CDropdownContextProps {
+ alignment?: Alignments
+ container?: DocumentFragment | Element | (() => DocumentFragment | Element | null) | null
+ dark?: boolean
+ dropdownMenuRef: RefObject
+ dropdownToggleRef: (node: HTMLElement | null) => void
+ handleHide?: () => void
+ handleShow?: () => void
+ popper?: boolean
+ portal?: boolean
+ variant?: 'btn-group' | 'dropdown' | 'input-group' | 'nav-item'
+ visible?: boolean
+}
+
+export const CDropdownContext = createContext({} as CDropdownContextProps)
diff --git a/packages/coreui-react/src/components/dropdown/CDropdownDivider.tsx b/packages/coreui-react/src/components/dropdown/CDropdownDivider.tsx
index c9fa58de..57b86e1e 100644
--- a/packages/coreui-react/src/components/dropdown/CDropdownDivider.tsx
+++ b/packages/coreui-react/src/components/dropdown/CDropdownDivider.tsx
@@ -12,7 +12,7 @@ export interface CDropdownDividerProps extends HTMLAttributes {
export const CDropdownDivider = forwardRef(
({ className, ...rest }, ref) => {
return
- },
+ }
)
CDropdownDivider.propTypes = {
diff --git a/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx b/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx
index 0315a0eb..89e53673 100644
--- a/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx
+++ b/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx
@@ -23,7 +23,7 @@ export const CDropdownHeader: PolymorphicRefForwardingComponent<'h6', CDropdownH
{children}
)
- },
+ }
)
CDropdownHeader.propTypes = {
diff --git a/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx b/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx
index 5b22bd40..bc4ae0bb 100644
--- a/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx
+++ b/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx
@@ -25,7 +25,7 @@ export const CDropdownItemPlain: PolymorphicRefForwardingComponent<
{children}
)
- },
+ }
)
CDropdownItemPlain.propTypes = {
diff --git a/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx b/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx
index 35a4e98a..59442d63 100644
--- a/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx
+++ b/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx
@@ -2,8 +2,8 @@ import React, { ElementType, forwardRef, HTMLAttributes, useContext } from 'reac
import PropTypes from 'prop-types'
import classNames from 'classnames'
-import { CDropdownContext } from './CDropdown'
import { CConditionalPortal } from '../conditional-portal'
+import { CDropdownContext } from './CDropdownContext'
import { PolymorphicRefForwardingComponent } from '../../helpers'
import { useForkedRef } from '../../hooks'
@@ -38,11 +38,10 @@ export const CDropdownMenu: PolymorphicRefForwardingComponent<'ul', CDropdownMen
show: visible,
},
alignment && getAlignmentClassNames(alignment),
- className,
+ className
)}
ref={forkedRef}
role="menu"
- aria-hidden={!visible}
{...(!popper && { 'data-coreui-popper': 'static' })}
{...(dark && { 'data-coreui-theme': 'dark' })}
{...rest}
@@ -58,7 +57,7 @@ export const CDropdownMenu: PolymorphicRefForwardingComponent<'ul', CDropdownMen
)
- },
+ }
)
CDropdownMenu.propTypes = {
diff --git a/packages/coreui-react/src/components/dropdown/CDropdownToggle.tsx b/packages/coreui-react/src/components/dropdown/CDropdownToggle.tsx
index 5689c0f4..def263e5 100644
--- a/packages/coreui-react/src/components/dropdown/CDropdownToggle.tsx
+++ b/packages/coreui-react/src/components/dropdown/CDropdownToggle.tsx
@@ -3,8 +3,7 @@ import PropTypes from 'prop-types'
import classNames from 'classnames'
import { CButton, CButtonProps } from '../button/CButton'
-
-import { CDropdownContext } from './CDropdown'
+import { CDropdownContext } from './CDropdownContext'
import { triggerPropType } from '../../props'
import type { Triggers } from '../../types'
@@ -46,18 +45,24 @@ export const CDropdownToggle: FC = ({
trigger = 'click',
...rest
}) => {
- const { dropdownToggleRef, variant, visible, setVisible } = useContext(CDropdownContext)
+ const { dropdownToggleRef, handleHide, handleShow, variant, visible } =
+ useContext(CDropdownContext)
const triggers = {
...((trigger === 'click' || trigger.includes('click')) && {
onClick: (event: React.MouseEvent) => {
event.preventDefault()
- setVisible(!visible)
+
+ if (visible) {
+ handleHide?.()
+ } else {
+ handleShow?.()
+ }
},
}),
...((trigger === 'focus' || trigger.includes('focus')) && {
- onFocus: () => setVisible(true),
- onBlur: () => setVisible(false),
+ onFocus: () => handleShow?.(),
+ onBlur: () => handleHide?.(),
}),
}
@@ -69,42 +74,38 @@ export const CDropdownToggle: FC = ({
'dropdown-toggle-split': split,
show: visible,
},
- className,
+ className
),
'aria-expanded': visible,
...(!rest.disabled && { ...triggers }),
}
- const Toggler = () => {
- if (custom && React.isValidElement(children)) {
- return (
- <>
- {React.cloneElement(children as React.ReactElement, {
- 'aria-expanded': visible,
- ...(!rest.disabled && { ...triggers }),
- ref: dropdownToggleRef,
- })}
- >
- )
- }
-
- if (variant === 'nav-item' && navLink) {
- return (
-
- {children}
-
- )
- }
+ if (custom && React.isValidElement(children)) {
+ return (
+ <>
+ {React.cloneElement(children as React.ReactElement, {
+ 'aria-expanded': visible,
+ ...(!rest.disabled && { ...triggers }),
+ ref: dropdownToggleRef,
+ })}
+ >
+ )
+ }
+ if (variant === 'nav-item' && navLink) {
return (
-
+
{children}
- {split && Toggle Dropdown }
-
+
)
}
- return
+ return (
+
+ {children}
+ {split && Toggle Dropdown }
+
+ )
}
CDropdownToggle.propTypes = {
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdown.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdown.spec.tsx
index d45f8c9d..12b6e3cd 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdown.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdown.spec.tsx
@@ -1,5 +1,5 @@
import * as React from 'react'
-import { render, screen, fireEvent } from '@testing-library/react'
+import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import '@testing-library/jest-dom'
import {
CDropdown,
@@ -9,7 +9,7 @@ import {
CDropdownItemPlain,
CDropdownHeader,
CDropdownDivider,
-} from '../../../index'
+} from '../index'
test('loads and displays CDropdown component', async () => {
const { container } = render(Test )
@@ -52,26 +52,50 @@ test('CDropdown customize', async () => {
// jest.useRealTimers()
// })
-test('CDropdown click', async () => {
+test('CDropdown opens on toggle click and closes on clicking outside', async () => {
render(
-
- Test
-
- A
- B
-
- ,
+
+ {/* External element to simulate clicking outside the dropdown */}
+
External Area
+
+ {/* The dropdown component */}
+
+ Test
+
+ A
+ B
+
+
+
,
)
- expect(screen.getByText('Test')).not.toHaveClass('show')
- const el = screen.getByText('Test')
- if (el !== null) {
- fireEvent.click(el) //click on element
- }
- jest.runAllTimers()
- expect(screen.getByText('Test').closest('div')).toHaveClass('show')
- fireEvent.mouseUp(document.body) //click outside
- await new Promise((r) => setTimeout(r, 1000))
- expect(screen.getByText('Test').closest('div')).not.toHaveClass('show')
+
+ // Ensure the dropdown is initially closed
+ const toggleButton = screen.getByText('Test')
+ expect(toggleButton).toBeInTheDocument()
+
+ // Assuming the 'show' class is applied to the CDropdownMenu
+ const dropdownMenu = screen.getByRole('menu', { hidden: true }) // Adjust role if different
+ expect(dropdownMenu).not.toHaveClass('show')
+
+ // Click on the toggle to open the dropdown
+ fireEvent.click(toggleButton)
+
+ // Wait for the dropdown menu to become visible
+ await waitFor(() => {
+ const openedMenu = screen.getByRole('menu') // Adjust role if different
+ expect(openedMenu).toBeVisible()
+ expect(openedMenu).toHaveClass('show')
+ })
+
+ // Click outside the dropdown to close it
+ const externalArea = screen.getByTestId('external-area')
+ fireEvent.mouseUp(externalArea)
+
+ // Wait for the dropdown menu to be hidden
+ await waitFor(() => {
+ const closedMenu = screen.getByRole('menu', { hidden: true }) // Adjust role if different
+ expect(closedMenu).not.toHaveClass('show')
+ })
})
test('CDropdown example', async () => {
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownDivider.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownDivider.spec.tsx
index bc5f6667..98b9de48 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownDivider.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownDivider.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CDropdownDivider } from '../../../index'
+import { CDropdownDivider } from '../index'
test('loads and displays CDropdownDivider component', async () => {
const { container } = render( )
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownHeader.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownHeader.spec.tsx
index fe11ca28..602b8d26 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownHeader.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownHeader.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CDropdownHeader } from '../../../index'
+import { CDropdownHeader } from '../index'
test('loads and displays CDropdownHeader component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItem.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItem.spec.tsx
index 88091d6f..f484cc2f 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItem.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItem.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CDropdownItem } from '../../../index'
+import { CDropdownItem } from '../index'
test('loads and displays CDropdownItem component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItemPlain.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItemPlain.spec.tsx
index 8407502c..b1aafd07 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItemPlain.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownItemPlain.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CDropdownItemPlain } from '../../../index'
+import { CDropdownItemPlain } from '../index'
test('loads and displays CDropdownItemPlain component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownMenu.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownMenu.spec.tsx
index 73300334..8b630833 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownMenu.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownMenu.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CDropdown, CDropdownMenu } from '../../../index'
+import { CDropdown, CDropdownMenu } from '../index'
test('loads and displays CDropdownMenu component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownToggle.spec.tsx b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownToggle.spec.tsx
index 5d9db7e7..341c1048 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/CDropdownToggle.spec.tsx
+++ b/packages/coreui-react/src/components/dropdown/__tests__/CDropdownToggle.spec.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'
-import { CDropdownToggle } from '../../../index'
+import { CDropdownToggle } from '../index'
test('loads and displays CDropdownToggle component', async () => {
const { container } = render(Test )
diff --git a/packages/coreui-react/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap b/packages/coreui-react/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap
index f6741bfd..ed01b1d9 100644
--- a/packages/coreui-react/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap
+++ b/packages/coreui-react/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap
@@ -24,7 +24,6 @@ exports[`CDropdown example 1`] = `
Test