diff --git a/.eslintignore b/.eslintignore index 235d8b73c41..cdfefe2893d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,6 @@ # Javascript builds node_modules dist -__tests__ thirdparty tsc_out .out diff --git a/.eslintrc.json b/.eslintrc.json index b78d47c5ef8..3a119092f7d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -127,6 +127,15 @@ "rules": { "patternfly-react/no-anonymous-functions": "off" } + }, + { + "files": ["**/*.test.[jt]s?(x)"], + "extends": ["plugin:testing-library/react"], + "rules": { + "testing-library/no-node-access": "off", + "react/jsx-key": "off", + "no-console": "off" + } } ] } diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index 2f400db2655..d943b7a14e5 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -26,7 +26,7 @@ jobs: node_modules **/node_modules ~/.cache/Cypress - key: ${{ runner.os }}-yarn-14-${{ secrets.CACHE_VERSION }}-${{ hashFiles('yarn.lock') }} + key: ${{ runner.os }}-yarn-14-${{ secrets.CACHE_VERSION }}-${{ hashFiles('yarn.lock', 'packages/*/package.json') }} - run: yarn install --frozen-lockfile if: steps.yarn-cache.outputs.cache-hit != 'true' - uses: actions/cache@v2 diff --git a/jest.config.js b/jest.config.js index 620b84b3b63..10cbcfd3396 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,7 +10,7 @@ module.exports = { ], roots: ['/packages'], transform: { - '^.+\\.[jt]sx?$': 'babel-jest', + '^.+\\.m?[jt]sx?$': 'babel-jest', '^.+\\.svg$': 'jest-transform-stub' }, setupFilesAfterEnv: ['/packages/testSetup.ts'], diff --git a/package.json b/package.json index 45f13ae3a79..ac9ce6ef184 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "14.5.1", - "@types/jest": "29.5.5", + "@types/jest": "29.5.8", "@types/react": "^18", "@types/react-dom": "^18", "@typescript-eslint/eslint-plugin": "^5.59.2", @@ -50,6 +50,7 @@ "eslint-plugin-markdown": "^3.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.32.2", + "eslint-plugin-testing-library": "^6.1.0", "fs-extra": "^11.1.1", "glob": "^9.3.0", "husky": "^4.3.0", @@ -64,7 +65,8 @@ "react-dom": "^18", "surge": "^0.23.1", "ts-patch": "^2.1.0", - "typescript": "^4.7.4" + "typescript": "^4.7.4", + "ts-node": "^10.9.1" }, "scripts": { "build": "yarn clean && yarn build:generate && yarn build:esm && yarn build:cjs && yarn build:subpaths && yarn build:single:packages", @@ -84,6 +86,7 @@ "lint:md": "yarn eslint packages --ext md --no-eslintrc --config .eslintrc-md.json --cache", "lint:ts": "yarn lint packages/*/src", "lint:versions": "node scripts/verifyPatternflyVersions.js", + "lint:tests": "yarn lint packages/*/src/components/*/__tests__/*.test.*", "prepare": "ts-patch install -s", "serve:docs": "yarn workspace @patternfly/react-docs serve", "serve:integration": "lerna run serve:demo-app", diff --git a/packages/react-charts/CHANGELOG.md b/packages/react-charts/CHANGELOG.md index 4f123e2e6ff..7a547260034 100644 --- a/packages/react-charts/CHANGELOG.md +++ b/packages/react-charts/CHANGELOG.md @@ -25,6 +25,14 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @patternfly/react-charts +# 7.2.0-prerelease.7 (2023-11-10) + +**Note:** Version bump only for package @patternfly/react-charts + +# 7.2.0-prerelease.6 (2023-11-03) + +**Note:** Version bump only for package @patternfly/react-charts + # 7.2.0-prerelease.5 (2023-10-26) **Note:** Version bump only for package @patternfly/react-charts diff --git a/packages/react-code-editor/CHANGELOG.md b/packages/react-code-editor/CHANGELOG.md index 568e555f137..e3bfeff2b75 100644 --- a/packages/react-code-editor/CHANGELOG.md +++ b/packages/react-code-editor/CHANGELOG.md @@ -51,6 +51,54 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @patternfly/react-code-editor +# [5.2.0-prerelease.31](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.30...@patternfly/react-code-editor@5.2.0-prerelease.31) (2023-11-17) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.30](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.29...@patternfly/react-code-editor@5.2.0-prerelease.30) (2023-11-16) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.29](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.28...@patternfly/react-code-editor@5.2.0-prerelease.29) (2023-11-14) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.28](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.27...@patternfly/react-code-editor@5.2.0-prerelease.28) (2023-11-14) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.27](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.26...@patternfly/react-code-editor@5.2.0-prerelease.27) (2023-11-14) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.26](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.25...@patternfly/react-code-editor@5.2.0-prerelease.26) (2023-11-13) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.25](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.24...@patternfly/react-code-editor@5.2.0-prerelease.25) (2023-11-10) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.24](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.23...@patternfly/react-code-editor@5.2.0-prerelease.24) (2023-11-03) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.23](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.22...@patternfly/react-code-editor@5.2.0-prerelease.23) (2023-11-03) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.22](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.21...@patternfly/react-code-editor@5.2.0-prerelease.22) (2023-11-02) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.21](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.20...@patternfly/react-code-editor@5.2.0-prerelease.21) (2023-11-02) + +**Note:** Version bump only for package @patternfly/react-code-editor + +# [5.2.0-prerelease.20](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.19...@patternfly/react-code-editor@5.2.0-prerelease.20) (2023-11-02) + +**Note:** Version bump only for package @patternfly/react-code-editor + # [5.2.0-prerelease.19](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@5.2.0-prerelease.18...@patternfly/react-code-editor@5.2.0-prerelease.19) (2023-10-30) **Note:** Version bump only for package @patternfly/react-code-editor diff --git a/packages/react-core/CHANGELOG.md b/packages/react-core/CHANGELOG.md index 57b121f3334..6e3ba63ceed 100644 --- a/packages/react-core/CHANGELOG.md +++ b/packages/react-core/CHANGELOG.md @@ -76,6 +76,76 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **Drawer:** Added start and end to position props, updated resizing to work with RTL ([#9627](https://github.com/patternfly/patternfly-react/issues/9627)) ([e12b872](https://github.com/patternfly/patternfly-react/commit/e12b8726b9ebc2fec921b7316a270a9c09decf96)) +# [5.2.0-prerelease.31](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.30...@patternfly/react-core@5.2.0-prerelease.31) (2023-11-17) + +**Note:** Version bump only for package @patternfly/react-core + +# [5.2.0-prerelease.30](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.29...@patternfly/react-core@5.2.0-prerelease.30) (2023-11-16) + +### Bug Fixes + +- **Chip:** update tooltip vis when chip updates ([#9819](https://github.com/patternfly/patternfly-react/issues/9819)) ([18c140a](https://github.com/patternfly/patternfly-react/commit/18c140a2adcc1833e4b095755aa7495d1d88a177)) + +# [5.2.0-prerelease.29](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.28...@patternfly/react-core@5.2.0-prerelease.29) (2023-11-14) + +**Note:** Version bump only for package @patternfly/react-core + +# [5.2.0-prerelease.28](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.27...@patternfly/react-core@5.2.0-prerelease.28) (2023-11-14) + +**Note:** Version bump only for package @patternfly/react-core + +# [5.2.0-prerelease.27](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.26...@patternfly/react-core@5.2.0-prerelease.27) (2023-11-14) + +**Note:** Version bump only for package @patternfly/react-core + +# [5.2.0-prerelease.26](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.25...@patternfly/react-core@5.2.0-prerelease.26) (2023-11-13) + +### Bug Fixes + +- **DatePicker:** updated logic for parsing and focus management ([#9794](https://github.com/patternfly/patternfly-react/issues/9794)) ([a12cf31](https://github.com/patternfly/patternfly-react/commit/a12cf313f0924a61a8cf20ee78b4b27290b9c05f)) + +# [5.2.0-prerelease.25](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.24...@patternfly/react-core@5.2.0-prerelease.25) (2023-11-10) + +**Note:** Version bump only for package @patternfly/react-core + +# [5.2.0-prerelease.24](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.23...@patternfly/react-core@5.2.0-prerelease.24) (2023-11-03) + +### Bug Fixes + +- **ClipboardCopy:** kepp caret position ([#9772](https://github.com/patternfly/patternfly-react/issues/9772)) ([981361e](https://github.com/patternfly/patternfly-react/commit/981361e0de354bbe06c4a799b6e7c92228dab51f)) + +### Features + +- **Datalist:** implement full page Basic demo to match HTML [#9048](https://github.com/patternfly/patternfly-react/issues/9048) ([#9087](https://github.com/patternfly/patternfly-react/issues/9087)) ([ab18059](https://github.com/patternfly/patternfly-react/commit/ab1805993ed5e2b5ed3d2f1be4131e035152b468)) + +# [5.2.0-prerelease.23](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.22...@patternfly/react-core@5.2.0-prerelease.23) (2023-11-03) + +**Note:** Version bump only for package @patternfly/react-core + +# [5.2.0-prerelease.22](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.21...@patternfly/react-core@5.2.0-prerelease.22) (2023-11-02) + +### Bug Fixes + +- **TreeView:** define button type ([#9770](https://github.com/patternfly/patternfly-react/issues/9770)) ([a772c6d](https://github.com/patternfly/patternfly-react/commit/a772c6d67bfa4154979ee14a34ec954406503109)) + +# [5.2.0-prerelease.21](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.20...@patternfly/react-core@5.2.0-prerelease.21) (2023-11-02) + +### Bug Fixes + +- **Notification Drawer:** Added screen reader text for notification drawer item read state ([#9569](https://github.com/patternfly/patternfly-react/issues/9569)) ([bc19f91](https://github.com/patternfly/patternfly-react/commit/bc19f9148e872fb62d8cd2bf644a771d9c5650d7)) +- **Text input:** Added aria-expanded ([#9705](https://github.com/patternfly/patternfly-react/issues/9705)) ([7f6a62c](https://github.com/patternfly/patternfly-react/commit/7f6a62c1a3f860a8759c63cde17da6b763dd7b48)) +- **Wizard:** onStepChange - skip isDisabled & isHidden ([#9748](https://github.com/patternfly/patternfly-react/issues/9748)) ([4d4d623](https://github.com/patternfly/patternfly-react/commit/4d4d623d294bbc7821c6dc9156980f2b8c609c96)) + +### Features + +- **Wizard:** ability to add props to WizardFooter buttons ([#9709](https://github.com/patternfly/patternfly-react/issues/9709)) ([9f21cee](https://github.com/patternfly/patternfly-react/commit/9f21cee5438c8ec4d0e2c5c938f9fffda013757b)) + +# [5.2.0-prerelease.20](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.19...@patternfly/react-core@5.2.0-prerelease.20) (2023-11-02) + +### Bug Fixes + +- **RTL:** added right-to-left page demo ([#9694](https://github.com/patternfly/patternfly-react/issues/9694)) ([d7308ae](https://github.com/patternfly/patternfly-react/commit/d7308aebec1e3ff22df548b5613f058c9ba35262)) + # [5.2.0-prerelease.19](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@5.2.0-prerelease.18...@patternfly/react-core@5.2.0-prerelease.19) (2023-10-30) **Note:** Version bump only for package @patternfly/react-core diff --git a/packages/react-core/src/components/AboutModal/__tests__/AboutModalBox.test.tsx b/packages/react-core/src/components/AboutModal/__tests__/AboutModalBox.test.tsx index 6c1c8b0aaf0..9587eb1a531 100644 --- a/packages/react-core/src/components/AboutModal/__tests__/AboutModalBox.test.tsx +++ b/packages/react-core/src/components/AboutModal/__tests__/AboutModalBox.test.tsx @@ -3,10 +3,6 @@ import { render } from '@testing-library/react'; import { AboutModalBox } from '../AboutModalBox'; test('AboutModalBox Test', () => { - const { asFragment } = render( - - This is a AboutModalBox - - ); + const { asFragment } = render(This is a AboutModalBox); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/Accordion/__tests__/AccordionItem.test.tsx b/packages/react-core/src/components/Accordion/__tests__/AccordionItem.test.tsx index d2a6c8ff540..ca18a80cf88 100644 --- a/packages/react-core/src/components/Accordion/__tests__/AccordionItem.test.tsx +++ b/packages/react-core/src/components/Accordion/__tests__/AccordionItem.test.tsx @@ -11,8 +11,6 @@ test('Renders children', () => { }); test('Matches the snapshot', () => { - const { asFragment } = render( - Test - ); + const { asFragment } = render(Test); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/Alert/__tests__/AlertGroup.test.tsx b/packages/react-core/src/components/Alert/__tests__/AlertGroup.test.tsx index a2d6186f28d..09f37026d10 100644 --- a/packages/react-core/src/components/Alert/__tests__/AlertGroup.test.tsx +++ b/packages/react-core/src/components/Alert/__tests__/AlertGroup.test.tsx @@ -19,7 +19,7 @@ test('Alert Group renders without children', () => { }); test('Alert Group works with n children', () => { - const { asFragment } = render( + render( diff --git a/packages/react-core/src/components/BackToTop/__tests__/BackToTop.test.tsx b/packages/react-core/src/components/BackToTop/__tests__/BackToTop.test.tsx index 438bae01e72..61ba74b68c6 100644 --- a/packages/react-core/src/components/BackToTop/__tests__/BackToTop.test.tsx +++ b/packages/react-core/src/components/BackToTop/__tests__/BackToTop.test.tsx @@ -115,10 +115,11 @@ test('Clicking backToTop scrolls back to top of the element passed via scrollabl const user = userEvent.setup(); const wrapper = document.getElementById('backToTopWrapper'); fireEvent.scroll(wrapper as HTMLElement, { target: { scrollY: 401 } }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion wrapper!.scrollTo = jest.fn(); await user.click(screen.getByRole(`button`).parentElement as Element); - expect(wrapper?.scrollTo).toBeCalledTimes(1); + expect(wrapper?.scrollTo).toHaveBeenCalledTimes(1); }); test('Passes correct text content to button child component', () => { diff --git a/packages/react-core/src/components/Button/Button.tsx b/packages/react-core/src/components/Button/Button.tsx index 53863058344..4f0a834ceb6 100644 --- a/packages/react-core/src/components/Button/Button.tsx +++ b/packages/react-core/src/components/Button/Button.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import styles from '@patternfly/react-styles/css/components/Button/button'; import { css } from '@patternfly/react-styles'; import { Spinner, spinnerSize } from '../Spinner'; -import { useOUIAProps, OUIAProps } from '../../helpers'; +import { useOUIAProps, OUIAProps } from '../../helpers/OUIA/ouia'; import { Badge } from '../Badge'; export enum ButtonVariant { diff --git a/packages/react-core/src/components/Button/__tests__/Button.test.tsx b/packages/react-core/src/components/Button/__tests__/Button.test.tsx index cbd758c1c83..a9d57d538f3 100644 --- a/packages/react-core/src/components/Button/__tests__/Button.test.tsx +++ b/packages/react-core/src/components/Button/__tests__/Button.test.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import CartArrowDownIcon from '@patternfly/react-icons/dist/esm/icons/cart-arrow-down-icon'; import { Button, ButtonVariant } from '../Button'; Object.values(ButtonVariant).forEach((variant) => { diff --git a/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx b/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx index 4b70968618e..f94e4af16a0 100644 --- a/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx +++ b/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx @@ -57,7 +57,9 @@ test('Next year dates have correct year in aria label', () => { }); test('InlineProps render correct wrapper component and attributes', () => { - render(Title, ariaLabelledby: "hi"}} />); + render( + Title, ariaLabelledby: 'hi' }} /> + ); const article = screen.getByRole('article'); expect(article).toHaveAttribute('aria-labelledby', 'hi'); diff --git a/packages/react-core/src/components/Card/__tests__/CardBody.test.tsx b/packages/react-core/src/components/Card/__tests__/CardBody.test.tsx index b4f77d82415..140f3b25a9e 100644 --- a/packages/react-core/src/components/Card/__tests__/CardBody.test.tsx +++ b/packages/react-core/src/components/Card/__tests__/CardBody.test.tsx @@ -30,7 +30,7 @@ describe('CardBody', () => { test('allows passing in a React Component as the component', () => { const Component = () =>
im a div
; - render(); + render(); expect(screen.getByText('im a div')).toBeInTheDocument(); }); diff --git a/packages/react-core/src/components/Card/__tests__/CardFooter.test.tsx b/packages/react-core/src/components/Card/__tests__/CardFooter.test.tsx index c48d145920c..19c7db7eab2 100644 --- a/packages/react-core/src/components/Card/__tests__/CardFooter.test.tsx +++ b/packages/react-core/src/components/Card/__tests__/CardFooter.test.tsx @@ -27,7 +27,7 @@ describe('CardFooter', () => { test('allows passing in a React Component as the component', () => { const Component = () =>
im a div
; - render(); + render(); expect(screen.getByText('im a div')).toBeInTheDocument(); }); }); diff --git a/packages/react-core/src/components/Chip/Chip.tsx b/packages/react-core/src/components/Chip/Chip.tsx index 52c6c62d518..12ab24e665c 100644 --- a/packages/react-core/src/components/Chip/Chip.tsx +++ b/packages/react-core/src/components/Chip/Chip.tsx @@ -81,6 +81,17 @@ class Chip extends React.Component { }); } + componentDidUpdate(_prevProps: ChipProps, prevState: ChipState) { + const nextIsTooltipVisible = Boolean( + this.span.current && this.span.current.offsetWidth < this.span.current.scrollWidth + ); + if (prevState.isTooltipVisible !== nextIsTooltipVisible) { + this.setState({ + isTooltipVisible: nextIsTooltipVisible + }); + } + } + setChipStyle = () => ({ [cssChipTextMaxWidth.name]: this.props.textMaxWidth }); diff --git a/packages/react-core/src/components/Chip/__tests__/ChipGroup.test.tsx b/packages/react-core/src/components/Chip/__tests__/ChipGroup.test.tsx index 807944710f1..148f985133e 100644 --- a/packages/react-core/src/components/Chip/__tests__/ChipGroup.test.tsx +++ b/packages/react-core/src/components/Chip/__tests__/ChipGroup.test.tsx @@ -115,7 +115,7 @@ test('chip group with closeBtnAriaLabel', () => { ); expect(screen.getByLabelText('close button aria label')).toBeInTheDocument(); - expect(screen.getByLabelText('close button aria label')).toHaveAccessibleName("close button aria label category"); + expect(screen.getByLabelText('close button aria label')).toHaveAccessibleName('close button aria label category'); }); test('chip group onClick', async () => { @@ -169,8 +169,6 @@ test('chip group expanded', async () => { }); test('overflow chip does not render by default when < 4 children are passed', async () => { - const user = userEvent.setup(); - render( 1 @@ -183,8 +181,6 @@ test('overflow chip does not render by default when < 4 children are passed', as }); test('overflow chip collapsed by default', async () => { - const user = userEvent.setup(); - render( 1 diff --git a/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx b/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx index b4338b36ce7..142fb4bed37 100644 --- a/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx +++ b/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx @@ -10,7 +10,7 @@ import { ClipboardCopyToggle } from './ClipboardCopyToggle'; import { ClipboardCopyExpanded } from './ClipboardCopyExpanded'; import { getOUIAProps, OUIAProps } from '../../helpers'; -export const clipboardCopyFunc = (event: React.ClipboardEvent, text?: React.ReactNode) => { +export const clipboardCopyFunc = (event: React.ClipboardEvent, text?: string) => { navigator.clipboard.writeText(text.toString()); }; @@ -21,12 +21,13 @@ export enum ClipboardCopyVariant { } export interface ClipboardCopyState { - text: string | number; + text: string; expanded: boolean; copied: boolean; + textWhenExpanded: string; } -export interface ClipboardCopyProps extends Omit, 'onChange'>, OUIAProps { +export interface ClipboardCopyProps extends Omit, 'onChange' | 'children'>, OUIAProps { /** Additional classes added to the clipboard copy container. */ className?: string; /** Tooltip message to display when hover the copy button */ @@ -70,12 +71,12 @@ export interface ClipboardCopyProps extends Omit /** Delay in ms before the tooltip appears. */ entryDelay?: number; /** A function that is triggered on clicking the copy button. */ - onCopy?: (event: React.ClipboardEvent, text?: React.ReactNode) => void; + onCopy?: (event: React.ClipboardEvent, text?: string) => void; /** A function that is triggered on changing the text. */ - onChange?: (event: React.FormEvent, text?: string | number) => void; + onChange?: (event: React.FormEvent, text?: string) => void; /** The text which is copied. */ - children: React.ReactNode; - /** Additional actions for inline-compact clipboard copy. Should be wrapped with ClipboardCopyAction. */ + children: string; + /** Additional actions for inline clipboard copy. Should be wrapped with ClipboardCopyAction. */ additionalActions?: React.ReactNode; /** Value to overwrite the randomly generated data-ouia-component-id.*/ ouiaId?: number | string; @@ -88,12 +89,12 @@ class ClipboardCopy extends React.Component { if (prevProps.children !== this.props.children) { - this.setState({ text: this.props.children as string | number }); + this.setState({ text: this.props.children as string }); } }; @@ -136,11 +137,16 @@ class ClipboardCopy extends React.Component { + updateText = (event: React.FormEvent, text: string) => { this.setState({ text }); this.props.onChange(event, text); }; + updateTextWhenExpanded = (event: React.FormEvent, text: string) => { + this.setState({ textWhenExpanded: text }); + this.props.onChange(event, text); + }; + render = () => { const { /* eslint-disable @typescript-eslint/no-unused-vars */ @@ -229,7 +235,14 @@ class ClipboardCopy extends React.Component { + this.expandContent(_event); + if (this.state.expanded) { + this.setState({ text: this.state.textWhenExpanded }); + } else { + this.setState({ textWhenExpanded: this.state.text }); + } + }} id={`${toggleIdPrefix}${id}`} textId={`${textIdPrefix}${id}`} contentId={`${contentIdPrefix}${id}`} @@ -239,7 +252,7 @@ class ClipboardCopy extends React.Component { - onCopy(event, this.state.text); + onCopy(event, this.state.expanded ? this.state.textWhenExpanded : this.state.text); this.setState({ copied: true }); }} onTooltipHidden={() => this.setState({ copied: false })} @@ -266,7 +279,7 @@ class ClipboardCopy extends React.Component {this.state.text} diff --git a/packages/react-core/src/components/ClipboardCopy/ClipboardCopyExpanded.tsx b/packages/react-core/src/components/ClipboardCopy/ClipboardCopyExpanded.tsx index 8a82c3e0001..0c805495078 100644 --- a/packages/react-core/src/components/ClipboardCopy/ClipboardCopyExpanded.tsx +++ b/packages/react-core/src/components/ClipboardCopy/ClipboardCopyExpanded.tsx @@ -6,7 +6,6 @@ import { PickOptional } from '../../helpers/typeUtils'; export interface ClipboardCopyExpandedProps extends Omit { className?: string; - children: React.ReactNode; onChange?: (e: React.FormEvent, text: string) => void; isReadOnly?: boolean; isCode?: boolean; diff --git a/packages/react-core/src/components/ClipboardCopy/__tests__/ClipboardCopyToggle.test.tsx b/packages/react-core/src/components/ClipboardCopy/__tests__/ClipboardCopyToggle.test.tsx index bb6a297c470..49bc2b64c1b 100644 --- a/packages/react-core/src/components/ClipboardCopy/__tests__/ClipboardCopyToggle.test.tsx +++ b/packages/react-core/src/components/ClipboardCopy/__tests__/ClipboardCopyToggle.test.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { screen, render } from '@testing-library/react'; import { ClipboardCopyToggle } from '../ClipboardCopyToggle'; -import styles from '@patternfly/react-styles/css/components/ClipboardCopy/clipboard-copy'; import userEvent from '@testing-library/user-event'; const onClickMock = jest.fn(); diff --git a/packages/react-core/src/components/DataList/DataList.tsx b/packages/react-core/src/components/DataList/DataList.tsx index 5b2a82a738c..817fb1c6875 100644 --- a/packages/react-core/src/components/DataList/DataList.tsx +++ b/packages/react-core/src/components/DataList/DataList.tsx @@ -73,6 +73,7 @@ class DataList extends React.Component { const { className, children, + 'aria-label': ariaLabel, onSelectDataListItem, selectedDataListItemId, isCompact, @@ -106,6 +107,7 @@ class DataList extends React.Component { )} style={props.style} role="list" + aria-label={ariaLabel} {...props} ref={this.ref} > diff --git a/packages/react-core/src/components/DataList/DataListAction.tsx b/packages/react-core/src/components/DataList/DataListAction.tsx index 807bf915691..ea38de0e3e7 100644 --- a/packages/react-core/src/components/DataList/DataListAction.tsx +++ b/packages/react-core/src/components/DataList/DataListAction.tsx @@ -35,8 +35,8 @@ export const DataListAction: React.FunctionComponent = ({ id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, + /* eslint-disable @typescript-eslint/no-unused-vars */ isPlainButtonAction, - /* eslint-enable @typescript-eslint/no-unused-vars */ ...props }: DataListActionProps) => (
diff --git a/packages/react-core/src/components/DataList/__tests__/DataList.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataList.test.tsx index a7a5bc878c1..f28b8cd6f2c 100644 --- a/packages/react-core/src/components/DataList/__tests__/DataList.test.tsx +++ b/packages/react-core/src/components/DataList/__tests__/DataList.test.tsx @@ -5,346 +5,182 @@ import userEvent from '@testing-library/user-event'; import { DataList } from '../DataList'; import { DataListItem } from '../DataListItem'; -import { DataListAction } from '../DataListAction'; -import { DataListCell } from '../DataListCell'; -import { DataListToggle } from '../DataListToggle'; -import { DataListItemCells } from '../DataListItemCells'; import { DataListItemRow } from '../DataListItemRow'; -import { DataListContent } from '../DataListContent'; -import { Button } from '../../Button'; -import { Dropdown, DropdownList, DropdownItem } from '../../Dropdown'; -import { MenuToggle } from '../../MenuToggle'; -import styles from '@patternfly/react-styles/css/components/DataList/data-list'; -describe('DataList', () => { - test('List default', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; - test('List compact', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test('Renders to match snapshot', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); - describe('DataList variants', () => { - ['none', 'always', 'sm', 'md', 'lg', 'xl', '2xl'].forEach((oneBreakpoint) => { - test(`Breakpoint - ${oneBreakpoint}`, () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); - }); - }); +test(`Renders with default class ${styles.dataList}`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.dataList); +}); - test('List draggable', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders with custom class when className is passed`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass('custom'); +}); - test('List', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders with spread props`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveAttribute('id', 'test'); +}); - test('List renders with a hidden input to improve a11y when selectableRow is passed', () => { - render( - {}}> - - -

Test

-
-
-
- ); +test(`Renders with aria-label when aria-label is passed`, () => { + render(test); + expect(screen.getByText('test')).toHaveAccessibleName('list'); +}); - const selectableInput = screen.getByRole('radio', { hidden: true }); +test(`Renders ${styles.modifiers.compact} when isCompact = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.compact); +}); - expect(selectableInput).toBeInTheDocument(); +['nowrap', 'truncate', 'breakWord'].forEach((wrap) => { + test(`Renders with class ${styles.modifiers[wrap]} when wrapModifier = ${wrap} is pased`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers[wrap]); }); +}); - test('List does not render with a hidden input to improve a11y when selectableRow is not passed', () => { - render( - - - -

Test

-
-
-
- ); - - const selectableInput = screen.queryByRole('radio', { hidden: true }); - - expect(selectableInput).not.toBeInTheDocument(); +const gridBreakpointClasses = { + none: styles.modifiers.gridNone, + always: 'pf-m-grid', + sm: styles.modifiers.gridSm, + md: styles.modifiers.gridMd, + lg: styles.modifiers.gridLg, + xl: styles.modifiers.gridXl, + '2xl': styles.modifiers.grid_2xl +}; + +['none', 'always', 'sm', 'md', 'lg', 'xl', '2xl'].forEach((oneBreakpoint) => { + test(`Has breakpoint - ${oneBreakpoint} when gridBreakpoint is pased`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(gridBreakpointClasses[oneBreakpoint]); }); +}); - test('List calls selectableRow.onChange when the selectable input changes', async () => { - const mock = jest.fn(); - const user = userEvent.setup(); - - render( - - - -

Test

-
-
-
- ); - - const selectableInput = screen.getByRole('radio', { hidden: true }); - await user.click(selectableInput); +test(`Renders default class ${styles.dataList}`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.dataList); +}); - expect(mock).toHaveBeenCalled(); - }); +test('Renders custom class when passed', () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass('data-list-custom'); +}); - test('List does not call selectableRow.onChange when the selectable input is not changed', () => { - const mock = jest.fn(); +test('Renders with a hidden input to improve a11y when onSelectableRowChange is passed', () => { + render( + {}}> + + +

Test

+
+
+
+ ); - render( - - - -

Test

-
-
-
- ); + const selectableInput = screen.getByRole('radio', { hidden: true }); - expect(mock).not.toHaveBeenCalled(); - }); - - test('Item applies selectableInputAriaLabel to the hidden input', () => { - render( - {}}> - - -

Test

-
-
-
- ); + expect(selectableInput).toBeInTheDocument(); +}); - const selectableInput = screen.getByRole('radio', { hidden: true }); +test('Does not render with a hidden input to improve a11y when onSelectableRowChange is not passed', () => { + render( + + + +

Test

+
+
+
+ ); - expect(selectableInput).toHaveAccessibleName('Data list item label test'); - }); + const selectableInput = screen.queryByRole('radio', { hidden: true }); - test('Item defaults to labelling its input using its aria-labelledby prop', () => { - render( - {}}> - -

Test cell content

-
-
- ); + expect(selectableInput).not.toBeInTheDocument(); +}); - const selectableInput = screen.getByRole('radio', { hidden: true }); +test('Calls onSelectableRowChange when the selectable input changes', async () => { + const mock = jest.fn(); + const user = userEvent.setup(); - expect(selectableInput).toHaveAccessibleName('Test cell content'); - }); + render( + + + +

Test

+
+
+
+ ); - test('Item prioritizes selectableInputAriaLabel over aria-labelledby prop', () => { - render( - {}}> - -

Test cell content

-
-
- ); + const selectableInput = screen.getByRole('radio', { hidden: true }); + await user.click(selectableInput); - const selectableInput = screen.getByRole('radio', { hidden: true }); + expect(mock).toHaveBeenCalled(); +}); - expect(selectableInput).toHaveAccessibleName('Data list item label test'); - }); +test('Does not call onSelectableRowChange when the selectable input is not changed', () => { + const mock = jest.fn(); - test('Item default', () => { - const { asFragment } = render( - - test + render( + + + +

Test

+
- ); - expect(asFragment()).toMatchSnapshot(); - }); +
+ ); - test('Item expanded', () => { - render( - - test - - ); - expect(screen.getByRole('listitem')).toHaveClass(`${styles.dataListItem} ${styles.modifiers.expanded}`); - }); + expect(mock).not.toHaveBeenCalled(); +}); - test('Item', () => { - const { asFragment } = render( - - test +test('Applies selectableInputAriaLabel to the hidden input', () => { + render( + {}}> + + +

Test

+
- ); - expect(asFragment()).toMatchSnapshot(); - }); - - test('item row default', () => { - const { asFragment } = render(test); - expect(asFragment()).toMatchSnapshot(); - }); - - test('Cell default', () => { - const { asFragment } = render(Secondary); - expect(asFragment()).toMatchSnapshot(); - }); - - test('Cells', () => { - const { asFragment } = render( - - Primary Id - , - - Primary Id 2 - - ]} - /> - ); - expect(asFragment()).toMatchSnapshot(); - }); - - test('Cell with width modifier', () => { - [ - { width: 1, class: '' }, - { width: 2, class: 'pf-m-flex-2' }, - { width: 3, class: 'pf-m-flex-3' }, - { width: 4, class: 'pf-m-flex-4' }, - { width: 5, class: 'pf-m-flex-5' } - ].forEach((testCase, index) => { - const testId = `cell-test-id-${index}`; - - render( - - Primary Id - - ); - - const dataListCell = screen.getByTestId(testId); - - testCase.class === '' - ? expect(dataListCell).toHaveClass(styles.dataListCell) - : expect(dataListCell).toHaveClass(`${styles.dataListCell} ${testCase.class}`); - }); - }); +
+ ); - test('Cell with text modifiers', () => { - [ - { wrapModifier: null as any, class: '' }, - { wrapModifier: 'breakWord', class: 'pf-m-break-word' }, - { wrapModifier: 'nowrap', class: 'pf-m-nowrap' }, - { wrapModifier: 'truncate', class: 'pf-m-truncate' } - ].forEach((testCase, index) => { - const testId = `cell-test-id-${index}`; - - render( - - Primary Id - - ); - - const dataListCell = screen.getByTestId(testId); - - testCase.class === '' - ? expect(dataListCell).toHaveClass(styles.dataListCell) - : expect(dataListCell).toHaveClass(`${styles.dataListCell} ${testCase.class}`); - }); - }); - - test('Toggle default with aria label', () => { - render(); + const selectableInput = screen.getByRole('radio', { hidden: true }); - expect(screen.getByRole('button')).not.toHaveAttribute('aria-labelledby'); - expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Toggle details for'); - expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'false'); - }); + expect(selectableInput).toHaveAccessibleName('Data list item label test'); +}); - test('Toggle expanded', () => { - render(); - expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true'); - }); +test('Defaults to labelling its input using its aria-labelledby prop', () => { + render( + {}}> + +

Test cell content

+
+
+ ); - test('DataListAction dropdown', () => { - const { asFragment } = render( - - }> - - - action-1 - - , - - action-2 - - - - - ); - expect(asFragment()).toMatchSnapshot(); - }); + const selectableInput = screen.getByRole('radio', { hidden: true }); - test('DataListAction button', () => { - const { asFragment } = render( - - - - ); - expect(asFragment()).toMatchSnapshot(); - }); - - test('DataListAction visibility - show button when lg', () => { - render( - - - - ); - - expect(screen.getByRole('button').parentElement).toHaveClass('pf-m-hidden'); - expect(screen.getByRole('button').parentElement).toHaveClass('pf-m-visible-on-lg'); - }); + expect(selectableInput).toHaveAccessibleName('Test cell content'); +}); - test('DataListAction visibility - hide button on 2xl', () => { - render( - - - - ); - - expect(screen.getByRole('button').parentElement).toHaveClass('pf-m-hidden-on-2xl'); - }); +test('Prioritizes selectableInputAriaLabel over aria-labelledby prop', () => { + render( + {}}> + +

Test cell content

+
+
+ ); - test('DataListContent', () => { - const { asFragment } = render( test); - expect(asFragment()).toMatchSnapshot(); - }); + const selectableInput = screen.getByRole('radio', { hidden: true }); - test('DataListContent hasNoPadding', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); + expect(selectableInput).toHaveAccessibleName('Data list item label test'); }); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListAction.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListAction.test.tsx new file mode 100644 index 00000000000..38745263caa --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListAction.test.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListAction } from '../DataListAction'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render( + + test + + ); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default class ${styles.dataListItemAction}`, () => { + render( + + test + + ); + expect(screen.getByText('test')).toHaveClass(styles.dataListItemAction, { exact: true }); +}); + +test(`Renders with custom class when className is passed`, () => { + render( + + test + + ); + expect(screen.getByText('test')).toHaveClass('test-class'); +}); + +test(`Renders with spread props`, () => { + render( + + test + + ); + expect(screen.getByText('test')).toHaveAttribute('dir', 'rtl'); +}); + +test(`Renders with class ${styles.dataListAction} when isPlainButtonAction = true`, () => { + render( + + test + + ); + expect(screen.getByText('test')).toHaveClass(styles.dataListAction); +}); + +['hidden', 'visible'].forEach((vis) => { + const visMod = vis as 'hidden' | 'visible'; + test(`Has visibility - ${vis} for every breakpoint`, () => { + render( + + test + + ); + + if (visMod === 'hidden') { + expect(screen.getByText('test')).toHaveClass(styles.modifiers[`${visMod}`]); + } + expect(screen.getByText('test')).toHaveClass(styles.modifiers[`${visMod}OnSm`]); + expect(screen.getByText('test')).toHaveClass(styles.modifiers[`${visMod}OnMd`]); + expect(screen.getByText('test')).toHaveClass(styles.modifiers[`${visMod}OnLg`]); + expect(screen.getByText('test')).toHaveClass(styles.modifiers[`${visMod}OnXl`]); + expect(screen.getByText('test')).toHaveClass(styles.modifiers[`${visMod}On_2xl`]); + }); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListCell.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListCell.test.tsx new file mode 100644 index 00000000000..6e5954384fa --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListCell.test.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListCell } from '../DataListCell'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Cell renders to match snapshot', () => { + const { asFragment } = render(Secondary); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders default class ${styles.dataListCell}`, () => { + render( + + Primary Id + + ); + expect(screen.getByTestId('test')).toHaveClass(styles.dataListCell, { exact: true }); +}); + +test(`Renders custom class when className is passed`, () => { + render( + + Primary Id + + ); + expect(screen.getByTestId('test')).toHaveClass('test-class'); +}); + +test(`Renders with spread props`, () => { + render( + + Primary Id + + ); + expect(screen.getByText('Primary Id')).toHaveAttribute('id', 'test'); +}); + +test('Renders width modifier when width is passed', () => { + [ + { width: 1, class: '' }, + { width: 2, class: styles.modifiers.flex_2 }, + { width: 3, class: styles.modifiers.flex_3 }, + { width: 4, class: styles.modifiers.flex_4 }, + { width: 5, class: styles.modifiers.flex_5 } + ].forEach((testCase, index) => { + const testId = `cell-test-id-${index}`; + + render( + + Primary Id + + ); + + const dataListCell = screen.getByTestId(testId); + + testCase.class === '' + ? expect(dataListCell).not.toHaveClass( + styles.modifiers.flex_2, + styles.modifiers.flex_3, + styles.modifiers.flex_4, + styles.modifiers.flex_5 + ) + : expect(dataListCell).toHaveClass(`pf-v5-c-data-list__cell ${testCase.class}`, { exact: true }); + }); +}); + +test('Has text wrap modifiers when wrapModifier is passed', () => { + [ + { wrapModifier: null as any, class: '' }, + { wrapModifier: 'breakWord', class: styles.modifiers.breakWord }, + { wrapModifier: 'nowrap', class: styles.modifiers.nowrap }, + { wrapModifier: 'truncate', class: styles.modifiers.truncate } + ].forEach((testCase, index) => { + const testId = `cell-test-id-${index}`; + + render( + + Primary Id + + ); + + const dataListCell = screen.getByTestId(testId); + + testCase.class === '' + ? expect(dataListCell).not.toHaveClass( + styles.modifiers.breakWord, + styles.modifiers.nowrap, + styles.modifiers.truncate + ) + : expect(dataListCell).toHaveClass(`${testCase.class}`); + }); +}); + +test(`Renders with class ${styles.modifiers.alignRight} when alignRight is passed`, () => { + render( + + Primary Id + + ); + expect(screen.getByTestId('test')).toHaveClass(styles.modifiers.alignRight); +}); + +test(`Renders with class ${styles.modifiers.icon} when isIcon is passed`, () => { + render( + + Primary Id + + ); + expect(screen.getByTestId('test')).toHaveClass(styles.modifiers.icon); +}); + +test(`Renders with class ${styles.modifiers.noFill} when isFilled = false`, () => { + render( + + Primary Id + + ); + expect(screen.getByTestId('test')).toHaveClass(styles.modifiers.noFill); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListCheck.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListCheck.test.tsx index 9e69ff734e8..fa1424c746f 100644 --- a/packages/react-core/src/components/DataList/__tests__/DataListCheck.test.tsx +++ b/packages/react-core/src/components/DataList/__tests__/DataListCheck.test.tsx @@ -3,6 +3,11 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { DataListCheck } from '../DataListCheck'; +test(`Renders with spread props`, () => { + render(); + expect(screen.getByRole('checkbox')).toHaveAttribute('id', 'test'); +}); + it('does not throw a "A component is changing an uncontrolled input of type checkbox to be controlled" error when changed if using isChecked', async () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); const user = userEvent.setup(); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListContent.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListContent.test.tsx new file mode 100644 index 00000000000..912bb48f0de --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListContent.test.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListContent } from '../DataListContent'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render( test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default class ${styles.dataListExpandableContent}`, () => { + render( + + test + + ); + expect(screen.getByTestId('test-id')).toHaveClass(styles.dataListExpandableContent, { exact: true }); +}); + +test(`Renders with default class ${styles.dataListExpandableContentBody}`, () => { + render( + + test + + ); + expect(screen.getByText('test')).toHaveClass(styles.dataListExpandableContentBody, { exact: true }); +}); + +test(`Renders with custom class when className is passed`, () => { + render( + + test + + ); + expect(screen.getByTestId('test-id')).toHaveClass('test-class'); +}); + +test(`Renders with id when id is passed`, () => { + render( + + test + + ); + expect(screen.getByTestId('test-id')).toHaveAttribute('id', 'idProp'); +}); + +test(`Renders with spread props`, () => { + render( + + test + + ); + expect(screen.getByTestId('test-id')).toHaveAttribute('dir', 'rtl'); +}); + +test(`Renders with class ${styles.modifiers.noPadding} when hasNoPadding is passed`, () => { + render( + + ); + expect(screen.getByText('test')).toHaveClass(styles.modifiers.noPadding); +}); + +test(`Renders with hidden property when isHidden is passed`, () => { + render( + + test + + ); + expect(screen.getByTestId('test-id')).toHaveAttribute('hidden'); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListItem.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListItem.test.tsx new file mode 100644 index 00000000000..54f358c8e83 --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListItem.test.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListItem } from '../DataListItem'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render( + + test + + ); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default class ${styles.dataListItem}`, () => { + render(test); + expect(screen.getByRole('listitem')).toHaveClass(styles.dataListItem, { exact: true }); +}); + +test('Renders with custom class name', () => { + render( + + test + + ); + expect(screen.getByRole('listitem')).toHaveClass('data-list-item-custom'); +}); + +test('Renders with spread props', () => { + render( + + test + + ); + expect(screen.getByRole('listitem')).toHaveAttribute('dir', 'rtl'); +}); + +test(`Renders class ${styles.modifiers.expanded} when isExpanded is passed`, () => { + render( + + test + + ); + expect(screen.getByRole('listitem')).toHaveClass(styles.modifiers.expanded); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListItemCells.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListItemCells.test.tsx new file mode 100644 index 00000000000..e8419f2b361 --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListItemCells.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListItemCells } from '../DataListItemCells'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Cells renders to match snapshot', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default class ${styles.dataListItemContent}`, () => { + render(); + expect(screen.getByText('test')).toHaveClass(styles.dataListItemContent, { exact: true }); +}); + +test(`Renders with custom class when className is passed`, () => { + render(); + expect(screen.getByText('test')).toHaveClass('custom'); +}); + +test(`Renders with spread props`, () => { + render(); + expect(screen.getByText('test')).toHaveAttribute('dir', 'rtl'); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListItemRow.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListItemRow.test.tsx new file mode 100644 index 00000000000..74ec2e6fafc --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListItemRow.test.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListItemRow } from '../DataListItemRow'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Item row renders to match snapshot', () => { + const { asFragment } = render(test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default class ${styles.dataListItemRow}`, () => { + render(test); + expect(screen.getByTestId('test')).toHaveClass(styles.dataListItemRow, { exact: true }); +}); + +test(`Renders with custom class when className is passed`, () => { + render( + + test + + ); + expect(screen.getByTestId('test')).toHaveClass('custom'); +}); + +test(`Renders with spread props`, () => { + render( + + test + + ); + expect(screen.getByTestId('test')).toHaveAttribute('dir', 'rtl'); +}); + +['nowrap', 'truncate', 'breakWord'].forEach((wrap) => { + test(`Renders with class ${styles.modifiers[wrap]} when wrapModifier = ${wrap} is pased`, () => { + render( + + test + + ); + expect(screen.getByTestId('test')).toHaveClass(styles.modifiers[wrap]); + }); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/DataListToggle.test.tsx b/packages/react-core/src/components/DataList/__tests__/DataListToggle.test.tsx new file mode 100644 index 00000000000..8f7f8b4cd9b --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/DataListToggle.test.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DataListToggle } from '../DataListToggle'; + +import styles from '@patternfly/react-styles/css/components/DataList/data-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders with default class ${styles.dataListToggle}`, () => { + render(); + expect(screen.getByRole('button').parentElement).toHaveClass(styles.dataListToggle); +}); + +test(`Renders with default class ${styles.dataListItemControl}`, () => { + render(); + expect(screen.getByRole('button').parentElement?.parentElement).toHaveClass(styles.dataListItemControl, { + exact: true + }); +}); + +test(`Renders with custom class when className is passed`, () => { + render(); + expect(screen.getByRole('button').parentElement?.parentElement).toHaveClass('custom'); +}); + +test(`Renders with spread props`, () => { + render(); + expect(screen.getByRole('button').parentElement?.parentElement).toHaveAttribute('dir', 'rtl'); +}); + +test(`Renders with spread buttonProps`, () => { + render(); + expect(screen.getByRole('button')).toHaveAttribute('disabled'); +}); + +test('Renders aria-labelledby by default when no custom aria-label is passed', () => { + render(); + + expect(screen.getByRole('button')).toHaveAttribute('aria-labelledby', 'row ex-toggle2'); +}); + +test('Does not render aria-labelledby when custom aria-label is passed', () => { + render(); + + expect(screen.getByRole('button')).not.toHaveAttribute('aria-labelledby'); + expect(screen.getByRole('button')).toHaveAccessibleName('Toggle details for'); +}); + +test(`Renders with ariaControls when ariaControls is passed`, () => { + render(); + expect(screen.getByRole('button')).toHaveAttribute('aria-controls', 'control'); +}); + +test('Renders with aria-expanded when isExpanded = true', () => { + render(); + + expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true'); +}); diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataList.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataList.test.tsx.snap index ee9d1720bb9..c8bebb7725c 100644 --- a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataList.test.tsx.snap +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataList.test.tsx.snap @@ -1,332 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DataList Cell default 1`] = ` - -
- Secondary -
-
-`; - -exports[`DataList Cells 1`] = ` - -
-
- Primary Id -
-
- Primary Id 2 -
-
-
-`; - -exports[`DataList DataList variants Breakpoint - 2xl 1`] = ` - -
    - -`; - -exports[`DataList DataList variants Breakpoint - always 1`] = ` - -
      - -`; - -exports[`DataList DataList variants Breakpoint - lg 1`] = ` - -
        - -`; - -exports[`DataList DataList variants Breakpoint - md 1`] = ` - -
          - -`; - -exports[`DataList DataList variants Breakpoint - none 1`] = ` - -
            - -`; - -exports[`DataList DataList variants Breakpoint - sm 1`] = ` - -
              - -`; - -exports[`DataList DataList variants Breakpoint - xl 1`] = ` - -
                - -`; - -exports[`DataList DataListAction button 1`] = ` - -
                - -
                -
                -`; - -exports[`DataList DataListAction dropdown 1`] = ` - -
                -
                - -
                -
                - -
                -
                -
                -
                -
                -`; - -exports[`DataList DataListContent 1`] = ` - -
                -
                - test -
                -
                -
                -`; - -exports[`DataList DataListContent hasNoPadding 1`] = ` - - - -`; - -exports[`DataList Item 1`] = ` - -
              • - -`; - -exports[`DataList Item default 1`] = ` - -
              • - -`; - -exports[`DataList List 1`] = ` - -
                  - -`; - -exports[`DataList List compact 1`] = ` +exports[`Renders to match snapshot 1`] = `
                    - -`; - -exports[`DataList List default 1`] = ` - -
                      `; - -exports[`DataList List draggable 1`] = ` - -
                        - -`; - -exports[`DataList item row default 1`] = ` - -
                        - -`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListAction.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListAction.test.tsx.snap new file mode 100644 index 00000000000..cf61bf5eabf --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListAction.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        + test +
                        +
                        +`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListCell.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListCell.test.tsx.snap new file mode 100644 index 00000000000..8dfb9393730 --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListCell.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Cell renders to match snapshot 1`] = ` + +
                        + Secondary +
                        +
                        +`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListContent.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListContent.test.tsx.snap new file mode 100644 index 00000000000..15736a7aa11 --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListContent.test.tsx.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        +
                        + test +
                        +
                        +
                        +`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItem.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItem.test.tsx.snap new file mode 100644 index 00000000000..1f0da0b84eb --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItem.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                      • + +`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItemCells.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItemCells.test.tsx.snap new file mode 100644 index 00000000000..05d99f283e7 --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItemCells.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Cells renders to match snapshot 1`] = ` + +
                        + +`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItemRow.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItemRow.test.tsx.snap new file mode 100644 index 00000000000..bacaa0a7320 --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListItemRow.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Item row renders to match snapshot 1`] = ` + +
                        + +`; diff --git a/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListToggle.test.tsx.snap b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListToggle.test.tsx.snap new file mode 100644 index 00000000000..cd3276482cf --- /dev/null +++ b/packages/react-core/src/components/DataList/__tests__/__snapshots__/DataListToggle.test.tsx.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        +
                        + +
                        +
                        +
                        +`; diff --git a/packages/react-core/src/components/DataList/examples/DataListCheckboxes.tsx b/packages/react-core/src/components/DataList/examples/DataListCheckboxes.tsx index c4eb52d718c..8f3ca384ddd 100644 --- a/packages/react-core/src/components/DataList/examples/DataListCheckboxes.tsx +++ b/packages/react-core/src/components/DataList/examples/DataListCheckboxes.tsx @@ -166,8 +166,8 @@ export const DataListCheckboxes: React.FunctionComponent = () => { @@ -192,7 +192,7 @@ export const DataListCheckboxes: React.FunctionComponent = () => { @@ -231,8 +231,8 @@ export const DataListCheckboxes: React.FunctionComponent = () => { diff --git a/packages/react-core/src/components/DataList/examples/DataListMixedExpandable.tsx b/packages/react-core/src/components/DataList/examples/DataListMixedExpandable.tsx index 87648fdc0ed..7f6a33c6b9b 100644 --- a/packages/react-core/src/components/DataList/examples/DataListMixedExpandable.tsx +++ b/packages/react-core/src/components/DataList/examples/DataListMixedExpandable.tsx @@ -162,7 +162,7 @@ export const DataListMixedExpandable: React.FunctionComponent = () => { ]} /> val.split('-').length === 3 && new Date(`${val}T00:00:00`), + dateParse = (val: string) => (val.split('-').length === 3 ? new Date(`${val}T00:00:00`) : new Date(undefined)), isDisabled = false, placeholder = 'YYYY-MM-DD', value: valueProp = '', @@ -230,7 +230,7 @@ const DatePickerBase = ( return (
                        - + { test('Date picker with multiple validators does not show invalid icon on valid input', async () => { const user = userEvent.setup(); - const rangeValidator = (date: Date) => { - return ''; - }; + const rangeValidator = (_date: Date) => ''; - const rangeValidatorB = (date: Date) => { - return ''; - }; + const rangeValidatorB = (_date: Date) => ''; render(); @@ -119,7 +115,7 @@ test('Does not render emptyDateText when requiredDateOptions.isRequired is false await user.click(screen.getByRole('textbox')); await user.click(document.body); - expect(screen.queryByText('Date cannot be blank')).not.toBeInTheDocument; + expect(screen.queryByText('Date cannot be blank')).not.toBeInTheDocument(); }); test('Renders text input as invalid on blur when requiredDateOptions.isRequired is true', async () => { diff --git a/packages/react-core/src/components/DatePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap b/packages/react-core/src/components/DatePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap index ca761a7a7ef..6091153f111 100644 --- a/packages/react-core/src/components/DatePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap +++ b/packages/react-core/src/components/DatePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap @@ -13,7 +13,7 @@ exports[`With popover opened 1`] = ` class="pf-v5-c-input-group" >
                        { - test('default', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); - test('1 col on all breakpoints', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); +import styles from '@patternfly/react-styles/css/components/DescriptionList/description-list'; - test('2 col on all breakpoints', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test('Renders to match snapshot', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); - test('3 col on all breakpoints', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders default class ${styles.descriptionList}`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.descriptionList, { exact: true }); +}); - test('Horizontal Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test('Renders custom className', () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass('custom'); +}); - test('Compact Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); +Object.values(['1Col', '2Col', '3Col']).forEach((_col) => { + const col = _col as '1Col' | '2Col' | '3Col'; + test(`Renders ${col} on all breakpoints`, () => { + render( + + ); + expect(screen.getByLabelText('list')).toHaveClass( + styles.modifiers[col], + styles.modifiers[`${col}OnSm`], + styles.modifiers[`${col}OnMd`], + styles.modifiers[`${col}OnLg`], + styles.modifiers[`${col}OnXl`], + styles.modifiers[`${col}On_2xl`] + ); }); +}); - test('Compact Horizontal Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.horizontal} when isHorizontal = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.horizontal); +}); - test('Fluid Horizontal Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.compact} when isCompact = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.compact); +}); + +test(`Renders ${styles.modifiers.horizontal} and ${styles.modifiers.fluid} when isFluid = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.fluid, styles.modifiers.horizontal); +}); - test('alignment breakpoints', () => { - const { asFragment } = render( +Object.values(['horizontal', 'vertical']).forEach((_align) => { + const align = _align as 'horizontal' | 'vertical'; + test(`Renders ${align} on all breakpoints`, () => { + render( ); - expect(asFragment()).toMatchSnapshot(); + expect(screen.getByLabelText('list')).toHaveClass( + styles.modifiers[`${align}OnSm`], + styles.modifiers[`${align}OnMd`], + styles.modifiers[`${align}OnLg`], + styles.modifiers[`${align}OnXl`], + styles.modifiers[`${align}On_2xl`] + ); }); +}); - test('Auto Column Widths Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.autoColumnWidths} when isAutoColumnWidths = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.autoColumnWidths); +}); - test('Inline Grid Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.inlineGrid} when isInlineGrid = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.inlineGrid); +}); - test('Auto fit Description List', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.autoFit} when isAutoFit = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.autoFit); +}); - test('Auto fit with responsive grid Description List', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.fillColumns} when isFillColumns = true`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.fillColumns); +}); - test('Term default', () => { - const { asFragment } = render( - - test - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.displayLg} when displaySize = lg`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.displayLg); +}); - test('Term helper text', () => { - const { asFragment } = render( - - test - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test(`Renders ${styles.modifiers.display_2xl} when displaySize = 2xl`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveClass(styles.modifiers.display_2xl); +}); - test('Group', () => { - const { asFragment } = render( - - test - - ); - expect(asFragment()).toMatchSnapshot(); +test(`Renders style when isHorizontal and horizontalTermWidthModifier is set`, () => { + render( + + ); + expect(screen.getByLabelText('list')).toHaveStyle({ + '--pf-v5-c-description-list--m-horizontal__term--width': '12ch', + '--pf-v5-c-description-list--m-horizontal__term--width-on-sm': '15ch', + '--pf-v5-c-description-list--m-horizontal__term--width-on-md': '20ch', + '--pf-v5-c-description-list--m-horizontal__term--width-on-lg': '28ch', + '--pf-v5-c-description-list--m-horizontal__term--width-on-xl': '30ch', + '--pf-v5-c-description-list--m-horizontal__term--width-on-2xl': '35ch' }); +}); - test('Description', () => { - const { asFragment } = render( - - test - - ); - expect(asFragment()).toMatchSnapshot(); +test(`Renders style when termWidth is set`, () => { + render(); + expect(screen.getByLabelText('list')).toHaveStyle({ + '--pf-v5-c-description-list__term--width': '30px' }); }); + +test(`Renders style when isAutoFit and horizontalTermWidthModifier is set`, () => { + render( + + ); + expect(screen.getByLabelText('list')).toHaveAttribute( + 'style', + '--pf-v5-c-description-list--GridTemplateColumns--min: 50px; --pf-v5-c-description-list--GridTemplateColumns--min-on-sm: 50px; --pf-v5-c-description-list--GridTemplateColumns--min-on-md: 100px; --pf-v5-c-description-list--GridTemplateColumns--min-on-lg: 150px; --pf-v5-c-description-list--GridTemplateColumns--min-on-xl: 200px; --pf-v5-c-description-list--GridTemplateColumns--min-on-2xl: 300px;' + ); +}); diff --git a/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListDescription.test.tsx b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListDescription.test.tsx new file mode 100644 index 00000000000..65367307167 --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListDescription.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DescriptionListDescription } from '../DescriptionListDescription'; + +import styles from '@patternfly/react-styles/css/components/DescriptionList/description-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render(test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders default class ${styles.descriptionListDescription}`, () => { + render(test); + expect(screen.getByText('test').parentElement).toHaveClass(styles.descriptionListDescription, { exact: true }); +}); + +test('Renders custom className', () => { + render(test); + expect(screen.getByText('test').parentElement).toHaveClass('custom'); +}); + +test('Renders spread props', () => { + render(test); + expect(screen.getByText('test').parentElement).toHaveAttribute('id', 'id'); +}); diff --git a/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListGroup.test.tsx b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListGroup.test.tsx new file mode 100644 index 00000000000..53e84b545ff --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListGroup.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DescriptionListGroup } from '../DescriptionListGroup'; + +import styles from '@patternfly/react-styles/css/components/DescriptionList/description-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render(test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders default class ${styles.descriptionListGroup}`, () => { + render(test); + expect(screen.getByText('test')).toHaveClass(styles.descriptionListGroup, { exact: true }); +}); + +test('Renders custom className', () => { + render(test); + expect(screen.getByText('test')).toHaveClass('custom'); +}); + +test('Renders spread props', () => { + render(test); + expect(screen.getByText('test')).toHaveAttribute('id', 'id'); +}); diff --git a/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListHelpTextButton.test.tsx b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListHelpTextButton.test.tsx new file mode 100644 index 00000000000..bb49c585450 --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListHelpTextButton.test.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DescriptionListTermHelpTextButton } from '../DescriptionListTermHelpTextButton'; + +import styles from '@patternfly/react-styles/css/components/DescriptionList/description-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render(test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders default class ${styles.descriptionListText}`, () => { + render(test); + expect(screen.getByText('test')).toHaveClass(`${styles.descriptionListText} ${styles.modifiers.helpText}`, { + exact: true + }); +}); + +test('Renders custom className', () => { + render(test); + expect(screen.getByText('test')).toHaveClass('custom'); +}); + +test('Renders spread props', () => { + render(test); + expect(screen.getByText('test')).toHaveAttribute('id', 'id'); +}); diff --git a/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListTerm.test.tsx b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListTerm.test.tsx new file mode 100644 index 00000000000..840db5199ea --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListTerm.test.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DescriptionListTerm } from '../DescriptionListTerm'; + +import styles from '@patternfly/react-styles/css/components/DescriptionList/description-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render(test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders default class ${styles.descriptionListTerm}`, () => { + render(test); + expect(screen.getByText('test').parentElement).toHaveClass(styles.descriptionListTerm, { exact: true }); +}); + +test(`Renders default class ${styles.descriptionListText}`, () => { + render(test); + expect(screen.getByText('test')).toHaveClass(styles.descriptionListText, { exact: true }); +}); + +test('Renders custom className', () => { + render(test); + expect(screen.getByText('test').parentElement).toHaveClass('custom'); +}); + +test('Renders icon', () => { + render(icon
                        }>test); + expect(screen.getByText('icon').parentElement).toHaveClass(styles.descriptionListTermIcon); +}); + +test('Renders spread props', () => { + render(test); + expect(screen.getByText('test').parentElement).toHaveAttribute('id', 'id'); +}); diff --git a/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListTermHelpText.test.tsx b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListTermHelpText.test.tsx new file mode 100644 index 00000000000..1bd9779d379 --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/DescriptionListTermHelpText.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { DescriptionListTermHelpText } from '../DescriptionListTermHelpText'; + +import styles from '@patternfly/react-styles/css/components/DescriptionList/description-list'; + +test('Renders to match snapshot', () => { + const { asFragment } = render(test); + expect(asFragment()).toMatchSnapshot(); +}); + +test(`Renders default class ${styles.descriptionListTerm}`, () => { + render(test); + expect(screen.getByText('test')).toHaveClass(styles.descriptionListTerm, { exact: true }); +}); + +test('Renders custom className', () => { + render(test); + expect(screen.getByText('test')).toHaveClass('custom'); +}); + +test('Renders spread props', () => { + render(test); + expect(screen.getByText('test')).toHaveAttribute('id', 'id'); +}); diff --git a/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionList.test.tsx.snap b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionList.test.tsx.snap index b9bde9f6974..e59055aa1af 100644 --- a/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionList.test.tsx.snap +++ b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionList.test.tsx.snap @@ -1,162 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Description List 1 col on all breakpoints 1`] = ` - -
                        - -`; - -exports[`Description List 2 col on all breakpoints 1`] = ` - -
                        - -`; - -exports[`Description List 3 col on all breakpoints 1`] = ` - -
                        - -`; - -exports[`Description List Auto Column Widths Description List 1`] = ` - -
                        - -`; - -exports[`Description List Auto fit Description List 1`] = ` - -
                        - -`; - -exports[`Description List Auto fit with responsive grid Description List 1`] = ` - -
                        - -`; - -exports[`Description List Compact Description List 1`] = ` - -
                        - -`; - -exports[`Description List Compact Horizontal Description List 1`] = ` - -
                        - -`; - -exports[`Description List Description 1`] = ` - -
                        -
                        - test -
                        -
                        -
                        -`; - -exports[`Description List Fluid Horizontal Description List 1`] = ` - -
                        - -`; - -exports[`Description List Group 1`] = ` - -
                        - test -
                        -
                        -`; - -exports[`Description List Horizontal Description List 1`] = ` - -
                        - -`; - -exports[`Description List Inline Grid Description List 1`] = ` - -
                        - -`; - -exports[`Description List Term default 1`] = ` - -
                        - - test - -
                        -
                        -`; - -exports[`Description List Term helper text 1`] = ` - -
                        - - test - -
                        -
                        -`; - -exports[`Description List alignment breakpoints 1`] = ` - -
                        - -`; - -exports[`Description List default 1`] = ` +exports[`Renders to match snapshot 1`] = `
                        +
                        +
                        + test +
                        +
                        + +`; diff --git a/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListGroup.test.tsx.snap b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListGroup.test.tsx.snap new file mode 100644 index 00000000000..73b0656b755 --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListGroup.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        + test +
                        +
                        +`; diff --git a/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListHelpTextButton.test.tsx.snap b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListHelpTextButton.test.tsx.snap new file mode 100644 index 00000000000..1d85b2b7303 --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListHelpTextButton.test.tsx.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + + + test + + +`; diff --git a/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListTerm.test.tsx.snap b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListTerm.test.tsx.snap new file mode 100644 index 00000000000..a99897481de --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListTerm.test.tsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        + + test + +
                        +
                        +`; diff --git a/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListTermHelpText.test.tsx.snap b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListTermHelpText.test.tsx.snap new file mode 100644 index 00000000000..883c9a40ad7 --- /dev/null +++ b/packages/react-core/src/components/DescriptionList/__tests__/__snapshots__/DescriptionListTermHelpText.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        + test +
                        +
                        +`; diff --git a/packages/react-core/src/components/Divider/__tests__/Divider.test.tsx b/packages/react-core/src/components/Divider/__tests__/Divider.test.tsx index 6b940d6841c..680e164373b 100644 --- a/packages/react-core/src/components/Divider/__tests__/Divider.test.tsx +++ b/packages/react-core/src/components/Divider/__tests__/Divider.test.tsx @@ -1,34 +1,98 @@ -import { Divider } from '../Divider'; -import { Flex, FlexItem } from '../../../layouts/Flex'; import * as React from 'react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import { Divider } from '../Divider'; +import styles from '@patternfly/react-styles/css/components/Divider/divider'; -test('divider using hr', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); +test(`Renders with only the class name ${styles.divider} by default`, () => { + render(); + expect(screen.getByRole('separator')).toHaveClass(styles.divider, { exact: true }); }); -test('divider using li', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); +test('Renders with horizontal rule by default', () => { + render(); + expect(screen.getByRole('separator')).toContainHTML('
                        '); }); -test('divider using div', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); +test('Renders with element passed to component prop', () => { + render(); + expect(screen.getByRole('separator')?.tagName).toBe('LI'); +}); + +test('Test default orientation (vertical divider)', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-vertical'); +}); + +test('Test sm orientation', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-horizontal-on-sm'); +}); + +test('Test md orientation', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-vertical-on-md'); +}); + +test('Test lg orientation', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-horizontal-on-lg'); +}); + +test('Test xl orientation', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-vertical-on-xl'); +}); + +test('Test 2xl orientation', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-horizontal-on-2xl'); +}); + +test('Test default inset', () => { + render(); + expect(screen.getByRole('separator')).toHaveClass('pf-m-inset-none'); }); -test('vertical divider', () => { - const { asFragment } = render( - - first item +test(`Test all insets`, () => { + const insetValues = Object.values([ + 'insetNone', + 'insetXs', + 'insetSm', + 'insetMd', + 'insetLg', + 'insetXl', + 'inset2xl', + 'inset3xl' + ] as ['insetNone', 'insetXs', 'insetSm', 'insetMd', 'insetLg', 'insetXl', 'inset2xl', 'inset3xl']); + + insetValues.forEach((insetValue) => { + render( - second item - - ); + ); + }); + const modifiers = ['none', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl']; + + modifiers.forEach((modifier, index) => { + const smClass = `pf-m-inset-${modifier}-on-sm`; + const mdClass = `pf-m-inset-${modifier}-on-md`; + const lgClass = `pf-m-inset-${modifier}-on-lg`; + const xlClass = `pf-m-inset-${modifier}-on-xl`; + const xl2Class = `pf-m-inset-${modifier}-on-2xl`; + + expect(screen.getAllByRole('separator')[index]).toHaveClass(smClass, mdClass, lgClass, xlClass, xl2Class); + }); +}); + +test('Matches the snapshot', () => { + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/Divider/__tests__/__snapshots__/Divider.test.tsx.snap b/packages/react-core/src/components/Divider/__tests__/__snapshots__/Divider.test.tsx.snap index 03b490d6b5b..1821213154b 100644 --- a/packages/react-core/src/components/Divider/__tests__/__snapshots__/Divider.test.tsx.snap +++ b/packages/react-core/src/components/Divider/__tests__/__snapshots__/Divider.test.tsx.snap @@ -1,49 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`divider using div 1`] = ` - - }>help test text); + expect(screen.getByText('test').parentElement).toHaveClass(styles.helperTextItemIcon); +}); + +test('Renders default icon when hasIcon = true and icon is not passed', () => { + render(help test text); + expect(screen.getByText('help test text').parentElement?.querySelector('span')).toHaveClass( + styles.helperTextItemIcon + ); +}); + +test('Renders custom icon when icon is passed and hasIcon = true', () => { + render( + test
                        }> + help test text + + ); + expect(screen.getByText('test').parentElement).toHaveClass(styles.helperTextItemIcon); +}); + +test('Renders dynamic helper text', () => { + render(help test text); + expect(screen.getByText('help test text').parentElement).toHaveClass(styles.modifiers.dynamic); + expect(screen.getByText('help test text').querySelector('span')).toHaveClass('pf-v5-screen-reader'); +}); + +test('Renders custom screenreader text when isDynamic = true and screenReaderText is passed', () => { + render( + + help test text + + ); + expect(screen.getByText('help test text').querySelector('span')).toHaveTextContent('sr test'); +}); diff --git a/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperText.test.tsx.snap b/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperText.test.tsx.snap index bc7fa094120..80d9d88b05f 100644 --- a/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperText.test.tsx.snap +++ b/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperText.test.tsx.snap @@ -1,230 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`HelperText default helper text variant applies successfully 1`] = ` +exports[`Renders to match snapshot 1`] = `
                        - - default help test text - -
                        -
                        -`; - -exports[`HelperText dynamic helper text renders successfully 1`] = ` - -
                        -
                        - - - help test text - - : default status; - - -
                        -
                        -
                        -`; - -exports[`HelperText helper text block renders successfully 1`] = ` - -
                        -
                        - - help test text 1 - -
                        -
                        - - help test text 2 - -
                        -
                        - - help test text 3 - -
                        -
                        -
                        -`; - -exports[`HelperText icon helper text renders properly 1`] = ` - -
                        -
                        - - - help test text - -
                        -
                        -
                        -`; - -exports[`HelperText indeterminate helper text variant applies successfully 1`] = ` - -
                        - - indeterminate help test text - -
                        -
                        -`; - -exports[`HelperText invalid helper text variant applies successfully 1`] = ` - -
                        - - invalid help test text - -
                        -
                        -`; - -exports[`HelperText simple helper text renders successfully 1`] = ` - -
                        -
                        - - help test text - -
                        -
                        -
                        -`; - -exports[`HelperText success helper text variant applies successfully 1`] = ` - -
                        - - success help test text - -
                        -
                        -`; - -exports[`HelperText variant comonent helper text renders properly 1`] = ` - -
                          -
                        • - - help test text 1 - -
                        • -
                        • - - help test text 2 - -
                        • -
                        -
                        -`; - -exports[`HelperText warning helper text variant applies successfully 1`] = ` - -
                        - - warning help test text - -
                        + />
                        `; diff --git a/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperTextItem.test.tsx.snap b/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperTextItem.test.tsx.snap new file mode 100644 index 00000000000..a06fcc63aaa --- /dev/null +++ b/packages/react-core/src/components/HelperText/__tests__/__snapshots__/HelperTextItem.test.tsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Renders to match snapshot 1`] = ` + +
                        + + help test text + +
                        +
                        +`; diff --git a/packages/react-core/src/components/Icon/__tests__/Icon.test.tsx b/packages/react-core/src/components/Icon/__tests__/Icon.test.tsx index b2f2e67de4f..54b56a0ea81 100644 --- a/packages/react-core/src/components/Icon/__tests__/Icon.test.tsx +++ b/packages/react-core/src/components/Icon/__tests__/Icon.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { render, screen } from '@testing-library/react'; import { Icon } from '../Icon'; -import { CheckIcon } from '@patternfly/react-icons'; +import CheckIcon from '@patternfly/react-icons/dist/esm/icons/check-icon'; import styles from '@patternfly/react-styles/css/components/Icon/icon'; test('renders basic icon successfully', () => { @@ -14,7 +14,7 @@ test('renders basic icon successfully', () => { }); test('checks basic icon structure', () => { - const { asFragment } = render( + render( diff --git a/packages/react-core/src/components/InputGroup/__tests__/InputGroup.test.tsx b/packages/react-core/src/components/InputGroup/__tests__/InputGroup.test.tsx index dd35dd426f7..0c9b1534937 100644 --- a/packages/react-core/src/components/InputGroup/__tests__/InputGroup.test.tsx +++ b/packages/react-core/src/components/InputGroup/__tests__/InputGroup.test.tsx @@ -23,7 +23,7 @@ describe('InputGroup', () => {
                        ); - const formControl = screen.getByLabelText("User password"); - expect(formControl).not.toHaveAttribute("aria-describedby"); + const formControl = screen.getByLabelText('User password'); + expect(formControl).not.toHaveAttribute('aria-describedby'); }); }); diff --git a/packages/react-core/src/components/Masthead/__tests__/Masthead.test.tsx b/packages/react-core/src/components/Masthead/__tests__/Masthead.test.tsx index ef95c453749..df7b7cc1a35 100644 --- a/packages/react-core/src/components/Masthead/__tests__/Masthead.test.tsx +++ b/packages/react-core/src/components/Masthead/__tests__/Masthead.test.tsx @@ -62,7 +62,7 @@ describe('Masthead', () => { 'insetXl', 'inset2xl', 'inset3xl' - ]).forEach(inset => { + ]).forEach((inset) => { test(`verify ${inset} inset breakpoints`, () => { const { asFragment } = render( test diff --git a/packages/react-core/src/components/Menu/__tests__/Menu.test.tsx b/packages/react-core/src/components/Menu/__tests__/Menu.test.tsx index 83627d08d58..b230896c249 100644 --- a/packages/react-core/src/components/Menu/__tests__/Menu.test.tsx +++ b/packages/react-core/src/components/Menu/__tests__/Menu.test.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; -import userEvent from '@testing-library/user-event'; import { Menu } from '../Menu'; import { MenuItem } from '../MenuItem'; @@ -54,7 +53,7 @@ describe('Menu', () => { describe('with hasCheckbox', () => { test('should render Menu with checkbox items', () => { - const { asFragment } = render( + render( @@ -67,7 +66,7 @@ describe('Menu', () => { ); const checkbox1 = screen.getAllByRole('checkbox')[0]; expect(checkbox1).not.toBeChecked(); - expect(screen.getByText("Checkbox 1")).toBeInTheDocument(); + expect(screen.getByText('Checkbox 1')).toBeInTheDocument(); }); }); }); diff --git a/packages/react-core/src/components/MenuToggle/__tests__/MenuToggle.test.tsx b/packages/react-core/src/components/MenuToggle/__tests__/MenuToggle.test.tsx index 55b75090b57..b56971f6ffd 100644 --- a/packages/react-core/src/components/MenuToggle/__tests__/MenuToggle.test.tsx +++ b/packages/react-core/src/components/MenuToggle/__tests__/MenuToggle.test.tsx @@ -5,7 +5,7 @@ import { Badge } from '../../Badge'; import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon'; import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; import userEvent from '@testing-library/user-event'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; describe('menu toggle', () => { test('renders successfully', () => { @@ -61,21 +61,23 @@ describe('menu toggle', () => { const mockClick = jest.fn(); const user = userEvent.setup(); - render( - 10 selected - - ] - }} - aria-label="Menu toggle with checkbox split button and text" - />); + render( + + 10 selected + + ] + }} + aria-label="Menu toggle with checkbox split button and text" + /> + ); await user.click(screen.getByRole(`button`) as Element); expect(mockClick).toHaveBeenCalled(); diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatus.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatus.test.tsx index f3c18f5e1ad..1c7db785c4b 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatus.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatus.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { MultipleFileUploadStatus } from '../MultipleFileUploadStatus'; import InProgressIcon from '@patternfly/react-icons/dist/esm/icons/in-progress-icon'; diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx index 7f59631f14c..e0e03319fb4 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx @@ -48,7 +48,7 @@ describe('MultipleFileUploadStatusItem', () => { fileName="testCustomFileName.txt" fileSize={42} progressId="test-progress-id" - progressAriaLiveMessage={loadPercentage => `test message ${loadPercentage}`} + progressAriaLiveMessage={(loadPercentage) => `test message ${loadPercentage}`} /> ); diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitle.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitle.test.tsx index dce9b52bb21..c75908c453f 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitle.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitle.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { MultipleFileUploadTitle } from '../MultipleFileUploadTitle'; describe('MultipleFileUploadTitle', () => { diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleIcon.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleIcon.test.tsx index 192dfd8e49b..6a8c81d7a52 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleIcon.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleIcon.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { MultipleFileUploadTitleIcon } from '../MultipleFileUploadTitleIcon'; describe('MultipleFileUploadTitleIcon', () => { diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleText.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleText.test.tsx index ec683fc5199..5fb79577157 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleText.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleText.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { MultipleFileUploadTitleText } from '../MultipleFileUploadTitleText'; describe('MultipleFileUploadTitleText', () => { diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleTextSeparator.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleTextSeparator.test.tsx index b9db99b59be..c2f8fd39cf2 100644 --- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleTextSeparator.test.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleTextSeparator.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { MultipleFileUploadTitleTextSeparator } from '../MultipleFileUploadTitleTextSeparator'; describe('MultipleFileUploadTitleTextSeparator', () => { diff --git a/packages/react-core/src/components/Nav/__tests__/Nav.test.tsx b/packages/react-core/src/components/Nav/__tests__/Nav.test.tsx index 7af2ea1ab5e..cfe972719eb 100644 --- a/packages/react-core/src/components/Nav/__tests__/Nav.test.tsx +++ b/packages/react-core/src/components/Nav/__tests__/Nav.test.tsx @@ -30,7 +30,7 @@ describe('Nav', () => { const { asFragment } = renderNav(
                      • ); diff --git a/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerGroup.test.tsx b/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerGroup.test.tsx index 9db5ee24483..64e6514124d 100644 --- a/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerGroup.test.tsx +++ b/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerGroup.test.tsx @@ -12,7 +12,9 @@ describe('NotificationDrawerGroup', () => { }); test('renders correct heading level', () => { - const { asFragment } = render(); + const { asFragment } = render( + + ); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItem.test.tsx b/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItem.test.tsx index 88685ae0149..2f184432325 100644 --- a/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItem.test.tsx +++ b/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItem.test.tsx @@ -21,8 +21,19 @@ describe('NotificationDrawerListItem', () => { expect(screen.getByRole('listitem')).toHaveClass('pf-m-hoverable'); }); - test('drawer list item with isRead applied', () => { + test('drawer list item with isRead applied and screen reader text is set to read', () => { render(); expect(screen.getByRole('listitem')).toHaveClass('pf-m-read'); + expect(screen.getByRole('listitem')).toContainHTML('read'); + }); + + test('drawer list item has screen reader text set to unread by default', () => { + render(); + expect(screen.getByRole('listitem')).toContainHTML('unread'); + }); + + test('drawer list item screen reader textcan be customized', () => { + render(); + expect(screen.getByRole('listitem')).toContainHTML('was read'); }); }); diff --git a/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItemHeader.test.tsx b/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItemHeader.test.tsx index 40060a4f504..c164a3ee93e 100644 --- a/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItemHeader.test.tsx +++ b/packages/react-core/src/components/NotificationDrawer/__tests__/NotificationDrawerListItemHeader.test.tsx @@ -13,7 +13,7 @@ describe('NotificationDrawerListItemHeader', () => { }); test('renders with correct heading level', () => { - const { asFragment } = render(); + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerHeader.test.tsx.snap b/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerHeader.test.tsx.snap index 97afe7f43bb..a5fa337375c 100644 --- a/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerHeader.test.tsx.snap +++ b/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerHeader.test.tsx.snap @@ -15,6 +15,7 @@ exports[`NotificationDrawerHeader drawer header with count applied 1`] = ` Notifications 2 unread @@ -38,6 +39,7 @@ exports[`NotificationDrawerHeader drawer header with custom unread text applied Notifications 2 unread alerts diff --git a/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerListItem.test.tsx.snap b/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerListItem.test.tsx.snap index 42ca7cf796d..b2eb9ebf325 100644 --- a/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerListItem.test.tsx.snap +++ b/packages/react-core/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawerListItem.test.tsx.snap @@ -5,6 +5,12 @@ exports[`NotificationDrawerListItem renders with PatternFly Core styles 1`] = `
                      • + > + + unread + +
                      • `; diff --git a/packages/react-core/src/components/NumberInput/__tests__/NumberInput.test.tsx b/packages/react-core/src/components/NumberInput/__tests__/NumberInput.test.tsx index 84006073ef0..7506929d2d0 100644 --- a/packages/react-core/src/components/NumberInput/__tests__/NumberInput.test.tsx +++ b/packages/react-core/src/components/NumberInput/__tests__/NumberInput.test.tsx @@ -212,13 +212,13 @@ describe('numberInput', () => { expect(input).toHaveDisplayValue(''); }); - test('does not throw an error if onChange is passed via inputProps as well as the onChange prop', () => { + test('does not throw an error if onChange is passed via inputProps as well as the onChange prop', async () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); const NumberInputWrapper = () => { const [value, setValue] = React.useState(0); - const onChange = event => setValue(event.currentTarget.value); - const inputProps = { onChange: onChange }; + const onChange = (event) => setValue(event.currentTarget.value); + const inputProps = { onChange }; return ; }; @@ -226,20 +226,20 @@ describe('numberInput', () => { render(); const input = screen.getByRole('spinbutton'); - userEvent.type(input, '0'); + await userEvent.type(input, '0'); expect(consoleSpy).not.toHaveBeenCalled(); }); test('input is read only if onChange not passed ', () => { - render(); + render(); const input = screen.getByLabelText('readonly input'); expect(input).toHaveAttribute('readOnly'); }); test('input is not read only if onChange passed ', () => { const onChangeMock = jest.fn(); - render(); + render(); const input = screen.getByLabelText('not readonly input'); expect(input).not.toHaveAttribute('readOnly'); }); diff --git a/packages/react-core/src/components/Pagination/__tests__/Pagination.test.tsx b/packages/react-core/src/components/Pagination/__tests__/Pagination.test.tsx index ad6cdc3100e..111618eb136 100644 --- a/packages/react-core/src/components/Pagination/__tests__/Pagination.test.tsx +++ b/packages/react-core/src/components/Pagination/__tests__/Pagination.test.tsx @@ -340,10 +340,12 @@ describe('Pagination', () => { 'insetLg', 'insetXl', 'inset2xl' - ]).forEach(inset => { + ]).forEach((inset) => { test(`verify ${inset} inset breakpoints`, () => { const { asFragment } = render( - test + + test + ); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/Pagination/examples/PaginationSticky.tsx b/packages/react-core/src/components/Pagination/examples/PaginationSticky.tsx index d44f130352d..7298b129a51 100644 --- a/packages/react-core/src/components/Pagination/examples/PaginationSticky.tsx +++ b/packages/react-core/src/components/Pagination/examples/PaginationSticky.tsx @@ -27,8 +27,8 @@ export const PaginationSticky: React.FunctionComponent = () => { const buildCards = () => { const numberOfCards = (page - 1) * perPage + perPage - 1 >= itemCount ? itemCount - (page - 1) * perPage : perPage; - return Array.apply(0, Array(numberOfCards)).map((x, i) => ( - + return Array.from({ length: numberOfCards }).map((_value, index) => ( + This is a card diff --git a/packages/react-core/src/components/Panel/__tests__/Panel.test.tsx b/packages/react-core/src/components/Panel/__tests__/Panel.test.tsx index e117286864f..e3b535658cd 100644 --- a/packages/react-core/src/components/Panel/__tests__/Panel.test.tsx +++ b/packages/react-core/src/components/Panel/__tests__/Panel.test.tsx @@ -85,9 +85,9 @@ test('Renders with ref', async () => { render(); await user.click(document.body); - expect(screen.getByText('Last click was in panel: false')).toBeVisible; + expect(screen.getByText('Last click was in panel: false')).toBeVisible(); await user.click(screen.getByText('Main content')); - expect(screen.getByText('Last click was in panel: true')).toBeVisible; + expect(screen.getByText('Last click was in panel: true')).toBeVisible(); }); test('Renders with the inherited element props spread to the component', () => { diff --git a/packages/react-core/src/components/Popover/__tests__/Popover.test.tsx b/packages/react-core/src/components/Popover/__tests__/Popover.test.tsx index fff0765e7b8..e24cf0f7404 100644 --- a/packages/react-core/src/components/Popover/__tests__/Popover.test.tsx +++ b/packages/react-core/src/components/Popover/__tests__/Popover.test.tsx @@ -70,7 +70,7 @@ test('popover can close from content (uncontrolled)', () => { aria-label="Popover with button in the body that can close it" isVisible headerContent={
                        Popover header
                        } - bodyContent={hide => ( + bodyContent={(hide) => (
                        All the content props (headerContent, bodyContent, footerContent) can take a function which the Popover diff --git a/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx b/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx index 1d666155b1a..24194cb6e7b 100644 --- a/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx +++ b/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx @@ -49,7 +49,7 @@ test('value scaled between minValue and maxValue', () => { }); describe('Progress size', () => { - Object.keys(ProgressSize).forEach(oneSize => { + Object.keys(ProgressSize).forEach((oneSize) => { test(oneSize, () => { const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); @@ -58,7 +58,7 @@ describe('Progress size', () => { }); describe('Progress variant', () => { - Object.keys(ProgressVariant).forEach(oneVariant => { + Object.keys(ProgressVariant).forEach((oneVariant) => { test(oneVariant, () => { const { asFragment } = render( @@ -69,7 +69,7 @@ describe('Progress variant', () => { }); describe('Progress measure location', () => { - Object.keys(ProgressMeasureLocation).forEach(oneLocation => { + Object.keys(ProgressMeasureLocation).forEach((oneLocation) => { test(oneLocation, () => { const { asFragment } = render( @@ -99,10 +99,10 @@ test('progress component generates console warning when no accessible name is pr }); test('Does not render helper text by default', () => { - render(); + render(); expect(screen.queryByText('Test helper text')).not.toBeInTheDocument(); -}) +}); test('Renders passed helper text', () => { render(); diff --git a/packages/react-core/src/components/ProgressStepper/__tests__/ProgressStep.test.tsx b/packages/react-core/src/components/ProgressStepper/__tests__/ProgressStep.test.tsx index a493d3d2d5a..1fb64297dee 100644 --- a/packages/react-core/src/components/ProgressStepper/__tests__/ProgressStep.test.tsx +++ b/packages/react-core/src/components/ProgressStepper/__tests__/ProgressStep.test.tsx @@ -150,7 +150,7 @@ test('Does not renders with aria-labelledBy by default on Component element', () test('Renders with an accessible name that matches children', () => { render( -
                        Popover content
                        } id="test-id" titleId="title-id"> +
                        Popover content
                        } id="test-id" titleId="title-id"> Test
                        ); diff --git a/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx b/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx index df074772b64..b60eb19d9a8 100644 --- a/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx +++ b/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx @@ -6,10 +6,12 @@ import userEvent from '@testing-library/user-event'; import { SearchInput } from '../SearchInput'; import { FormGroup } from '../../Form'; import { Button } from '../../Button'; -import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons'; +import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon'; import badgeStyles from '@patternfly/react-styles/css/components/Badge/badge'; import textInputGroupStyles from '@patternfly/react-styles/css/components/TextInputGroup/text-input-group'; +jest.mock('../../../helpers/OUIA/ouia'); + const props = { onChange: jest.fn(), value: 'test input', @@ -80,9 +82,9 @@ describe('SearchInput', () => { }); test('hide clear button', () => { - const { onClear, ...testProps } = props; - - render(); + render( + + ); expect(screen.queryByRole('button', { name: 'Reset' })).not.toBeInTheDocument(); }); @@ -146,9 +148,7 @@ describe('SearchInput', () => { test('advanced search with custom attributes and appendTo="inline', async () => { const user = userEvent.setup(); - const { container } = render( - - ); + render(); await user.click(screen.getByRole('button', { name: 'Open advanced search' })); @@ -158,7 +158,7 @@ describe('SearchInput', () => { test('advanced search with custom attributes and appendTo external DOM element', async () => { const user = userEvent.setup(); - const { container } = render( + render( ); @@ -252,9 +252,6 @@ test('toggleAriaLabel is applied to the expandable toggle', () => { }); test('Utilities are rendered when areUtilitiesDisplayed is set', () => { - render( - - ); + render(); expect(screen.getByLabelText('test-util-display')).toBeVisible(); }); - diff --git a/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap b/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap index 24719463b95..2a687388f8f 100644 --- a/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap +++ b/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap @@ -52,9 +52,6 @@ exports[`SearchInput advanced search 1`] = ` aria-disabled="false" aria-label="Reset" class="pf-v5-c-button pf-m-plain" - data-ouia-component-id="OUIA-Generated-Button-plain-17" - data-ouia-component-type="PF5/Button" - data-ouia-safe="true" type="button" > Link @@ -463,9 +433,6 @@ exports[`SearchInput advanced search with custom attributes 1`] = ` ) : ( @@ -197,7 +198,7 @@ const TreeViewListItemBase: React.FunctionComponent = ({
                      • @@ -230,6 +231,7 @@ const TreeViewListItemBase: React.FunctionComponent = ({ }} {...(hasCheckbox && { htmlFor: randomId })} {...((hasCheckbox || (isSelectable && children)) && { id: `label-${randomId}` })} + {...(Component === 'button' && { type: 'button' })} > {children && renderToggle(randomId)} diff --git a/packages/react-core/src/components/TreeView/__tests__/TreeView.test.tsx b/packages/react-core/src/components/TreeView/__tests__/TreeView.test.tsx index 9b893398742..cfdb202370f 100644 --- a/packages/react-core/src/components/TreeView/__tests__/TreeView.test.tsx +++ b/packages/react-core/src/components/TreeView/__tests__/TreeView.test.tsx @@ -1,243 +1,316 @@ import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { TreeView } from '../TreeView'; -import { Button } from '@patternfly/react-core'; -import { FolderIcon, FolderOpenIcon } from '@patternfly/react-icons'; -import { TreeViewSearch } from '../TreeViewSearch'; - -const options = [ - { - name: 'ApplicationLauncher', - id: 'AppLaunch', - children: [ - { - name: 'Application 1', - id: 'App1', - children: [ - { name: 'Settings', id: 'App1Settings' }, - { name: 'Current', id: 'App1Current' } - ] - }, - { - name: 'Application 2', - id: 'App2', - children: [ - { name: 'Settings', id: 'App2Settings' }, - { - name: 'Loader', - id: 'App2Loader', - children: [ - { name: 'Loading App 1', id: 'LoadApp1' }, - { name: 'Loading App 2', id: 'LoadApp2' }, - { name: 'Loading App 3', id: 'LoadApp3' } - ] - } - ] - } - ], - defaultExpanded: true - }, - { - name: 'Cost Management', - id: 'Cost', - children: [ - { - name: 'Application 3', - id: 'App3', - children: [ - { name: 'Settings', id: 'App3Settings' }, - { name: 'Current', id: 'App3Current' } - ] - } - ] - }, - { - name: 'Sources', - id: 'Sources', - children: [{ name: 'Application 4', id: 'App4', children: [{ name: 'Settings', id: 'App4Settings' }] }] - }, - { - name: 'Really really really long folder name that overflows the container it is in', - id: 'Long', - children: [{ name: 'Application 5', id: 'App5' }] - } -]; - -const flagOptions = [ - { - name: 'ApplicationLauncher', - id: 'AppLaunch', - hasCheckbox: true, - icon: , - expandedIcon: , - children: [ - { - name: 'Application 1', - id: 'App1', - children: [ - { name: 'Settings', id: 'App1Settings' }, - { name: 'Current', id: 'App1Current' } - ] - }, - { - name: 'Application 2', - id: 'App2', - hasBadge: true, - children: [ - { name: 'Settings', id: 'App2Settings', hasCheckbox: true }, - { - name: 'Loader', - id: 'App2Loader', - children: [ - { name: 'Loading App 1', id: 'LoadApp1' }, - { name: 'Loading App 2', id: 'LoadApp2' }, - { name: 'Loading App 3', id: 'LoadApp3' } - ] - } - ] - } - ], - defaultExpanded: true - }, - { - name: 'Cost Management', - id: 'Cost', - hasBadge: true, - action: ( - - ), - children: [ - { - name: 'Application 3', - id: 'App3', - children: [ - { name: 'Settings', id: 'App3Settings' }, - { name: 'Current', id: 'App3Current' } - ] - } - ] - }, - { - name: 'Sources', - id: 'Sources', - children: [{ name: 'Application 4', id: 'App4', children: [{ name: 'Settings', id: 'App4Settings' }] }] - }, - { - name: 'Really really really long folder name that overflows the container it is in', - id: 'Long', - children: [{ name: 'Application 5', id: 'App5' }] - } -]; - -const active = [ - { - name: 'Application 1', - id: 'App1', - children: [ - { name: 'Settings', id: 'App1Settings' }, - { name: 'Current', id: 'App1Current' } - ] - } -]; - -describe('tree view', () => { - test('renders basic successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); - test('calls onExpand and onCollapse appropriately', () => { - const onExpand = jest.fn(); - const onCollapse = jest.fn(); - const { asFragment } = render( - - ); - expect(onExpand).not.toHaveBeenCalled(); - expect(onCollapse).not.toHaveBeenCalled(); - expect(screen.queryByText('Application 3')).toBeNull(); - expect(screen.getByText('Cost Management')).toBeInTheDocument(); - fireEvent( - screen.getByText('Cost Management'), - new MouseEvent('click', { - bubbles: true, - cancelable: true - }) - ); - expect(onExpand).toHaveBeenCalled(); - expect(onCollapse).not.toHaveBeenCalled(); - expect(screen.getByText('Application 3')).toBeInTheDocument(); - fireEvent( - screen.getByText('Cost Management'), - new MouseEvent('click', { - bubbles: true, - cancelable: true - }) - ); - expect(onCollapse).toHaveBeenCalled(); - expect(screen.queryByText('Application 3')).toBeNull(); - }); +jest.mock('../TreeViewList', () => ({ + TreeViewList: ({ children, isNested, toolbar }) => ( +
                        +

                        {`TreeViewList isNested: ${isNested}`}

                        +

                        {`TreeViewList toolbar: ${toolbar}`}

                        +
                        {children}
                        +
                        + ) +})); +jest.mock('../TreeViewListItem', () => ({ + TreeViewListItem: ({ + action, + activeItems, + badgeProps, + checkProps, + children, + compareItems, + customBadgeContent, + defaultExpanded, + expandedIcon, + hasBadge, + hasCheckbox, + icon, + id, + isCompact, + isExpanded, + isSelectable, + itemData, + name, + onCheck, + onSelect, + onExpand, + onCollapse, + parentItem, + title, + useMemo + }) => ( +
                        +

                        {`TreeViewListItem action: ${action}`}

                        +
                        {activeItems && activeItems[0].name}
                        +

                        {`TreeViewListItem badgeProps: ${badgeProps?.id}`}

                        +

                        {`TreeViewListItem checkProps: ${checkProps?.checked}`}

                        +

                        {`TreeViewListItem customBadgeContent: ${customBadgeContent}`}

                        +

                        {`TreeViewListItem defaultExpanded: ${defaultExpanded}`}

                        +

                        {`TreeViewListItem expandedIcon: ${expandedIcon}`}

                        +

                        {`TreeViewListItem hasBadge: ${hasBadge}`}

                        +

                        {`TreeViewListItem hasCheckbox: ${hasCheckbox}`}

                        +

                        {`TreeViewListItem icon: ${icon}`}

                        +

                        {`TreeViewListItem id: ${id}`}

                        +

                        {`TreeViewListItem isCompact: ${isCompact}`}

                        +

                        {`TreeViewListItem isExpanded: ${isExpanded}`}

                        +

                        {`TreeViewListItem isSelectable: ${isSelectable}`}

                        +

                        {`TreeViewListItem itemData: ${itemData.name}`}

                        +

                        {`TreeViewListItem name: ${name}`}

                        +

                        {`TreeViewListItem parentItem: ${parentItem?.name}`}

                        +

                        {`TreeViewListItem title: ${title}`}

                        +

                        {`TreeViewListItem useMemo: ${useMemo}`}

                        + + + + + +
                        {children}
                        +
                        + ) +})); +jest.mock('../TreeViewRoot', () => ({ + TreeViewRoot: ({ children, hasCheckboxes, hasGuides, hasSelectableNodes, variant, className }) => ( +
                        +

                        {`TreeViewRoot hasCheckboxes: ${hasCheckboxes}`}

                        +

                        {`TreeViewRoot hasGuides: ${hasGuides}`}

                        +

                        {`TreeViewRoot hasSelectableNodes: ${hasSelectableNodes}`}

                        +

                        {`TreeViewRoot variant: ${variant}`}

                        +

                        {`TreeViewRoot className: ${className}`}

                        +
                        {children}
                        +
                        + ) +})); - test('renders active successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); - }); +const basicData = { + name: 'Basic data name' +}; - test('renders search successfully', () => { - const { asFragment } = render( - - ); - expect(asFragment()).toMatchSnapshot(); - }); +test('Passes hasSelectableNodes to TreeViewRoot', () => { + render(); + + expect(screen.getByText('TreeViewRoot hasSelectableNodes: true')).toBeVisible(); +}); +test('Passes hasCheckboxes to TreeViewRoot', () => { + render(); + + expect(screen.getByText('TreeViewRoot hasCheckboxes: true')).toBeVisible(); +}); +test('Passes hasGuides to TreeViewRoot', () => { + render(); + + expect(screen.getByText('TreeViewRoot hasGuides: true')).toBeVisible(); +}); +test('Passes variant to TreeViewRoot', () => { + render(); + + expect(screen.getByText('TreeViewRoot variant: compact')).toBeVisible(); +}); +test('Passes className to TreeViewRoot', () => { + render(); + + expect(screen.getByText('TreeViewRoot className: test-class')).toBeVisible(); +}); +test('Passes data as children TreeViewRoot', () => { + render(); + + expect(screen.getByTestId('TreeViewRoot-children')).toContainHTML('TreeViewListItem name: Basic data name'); +}); +test('Does not render TreeViewRoot when parentItem is passed', () => { + render(); + + expect(screen.queryByTestId('TreeViewRoot-mock')).not.toBeInTheDocument(); +}); + +test('Passes isNested to TreeViewList', () => { + render(); + + expect(screen.getByText('TreeViewList isNested: true')).toBeVisible(); +}); +test('Passes toolbar to TreeViewList', () => { + render(); + + expect(screen.getByText('TreeViewList toolbar: Toolbar content')).toBeVisible(); +}); +test('Passes data as children TreeViewList', () => { + render(); - test('renders toolbar successfully', () => { - const { asFragment } = render( - test
                      • } /> - ); - expect(asFragment()).toMatchSnapshot(); + expect(screen.getByTestId('TreeViewList-children')).toContainHTML('TreeViewListItem name: Basic data name'); +}); + +test('Passes data action to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem action: Item action')).toBeVisible(); +}); +test('Passes activeItems to TreeViewListItem', () => { + render(); + + expect(screen.getByTestId('TreeViewListItem-activeItems')).toHaveTextContent('Active item name'); +}); +test('Passes data badgeProps to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem badgeProps: test-id')).toBeVisible(); +}); +test('Passes data checkProps to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem checkProps: true')).toBeVisible(); +}); +test('Passes data customBadgeContent to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem customBadgeContent: Custom badge')).toBeVisible(); +}); +test('Passes data defaultExpanded to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem defaultExpanded: true')).toBeVisible(); +}); +test('Passes defaultAllExpanded to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem defaultExpanded: true')).toBeVisible(); +}); +test('Passes data expandedIcon to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem expandedIcon: Expanded icon')).toBeVisible(); +}); +test('Passes expandedIcon to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem expandedIcon: Expanded icon')).toBeVisible(); +}); +test('Passes data hasBadge to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem hasBadge: true')).toBeVisible(); +}); +test('Passes hasBadges to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem hasBadge: true')).toBeVisible(); +}); +test('Passes data hasCheckbox to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem hasCheckbox: true')).toBeVisible(); +}); +test('Passes hasCheckboxes to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem hasCheckbox: true')).toBeVisible(); +}); +test('Passes data icon to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem icon: Icon content')).toBeVisible(); +}); +test('Passes icon to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem icon: Icon content')).toBeVisible(); +}); +test('Passes data id to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem id: test-id')).toBeVisible(); +}); + +['default', 'compact', 'compactNoBackground'].forEach((variant) => { + test(`Passes isCompact to TreeViewListItem when variant=${variant}`, () => { + render(); + + expect(screen.getByText(`TreeViewListItem isCompact: ${variant === 'default' ? 'false' : 'true'}`)).toBeVisible(); }); +}); + +test('Passes allExpanded to TreeViewListItem isExpanded prop', () => { + render(); + + expect(screen.getByText('TreeViewListItem isExpanded: true')).toBeVisible(); +}); +test('Passes hasSelectableNodes to TreeViewListItem isSelectable prop', () => { + render(); + + expect(screen.getByText('TreeViewListItem isSelectable: true')).toBeVisible(); +}); +test('Passes data to TreeViewListItem itemData prop', () => { + render(); + + expect(screen.getByText('TreeViewListItem itemData: itemData name')).toBeVisible(); +}); +test('Passes data.name to TreeViewListItem name prop', () => { + render(); - test('renders checkboxes successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); + expect(screen.getByText('TreeViewListItem name: Basic data name')).toBeVisible(); +}); +test('Passes parentItem to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem parentItem: Parent name')).toBeVisible(); +}); +test('Passes data.title to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem title: Basic data title')).toBeVisible(); +}); +test('Passes useMemo to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem useMemo: true')).toBeVisible(); +}); +test('Passes data.children to TreeViewListItem', () => { + render(); + + expect(screen.getByText('TreeViewListItem name: Child 1')).toBeVisible(); +}); + +describe('Passes callback props to TreeViewListItem', () => { + const user = userEvent.setup(); + const callbackMock = jest.fn(); + + test('Passes compareItems', async () => { + render(); + + await user.click(screen.getByRole('button', { name: 'compareItems clicker' })); + + expect(callbackMock).toHaveBeenCalledTimes(1); }); + test('Passes onCheck', async () => { + render(); + + await user.click(screen.getByRole('button', { name: 'onCheck clicker' })); - test('renders icons successfully', () => { - const { asFragment } = render( - } - expandedIcon={} - /> - ); - expect(asFragment()).toMatchSnapshot(); + expect(callbackMock).toHaveBeenCalledTimes(1); }); + test('Passes onSelect', async () => { + render(); - test('renders badges successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); + await user.click(screen.getByRole('button', { name: 'onSelect clicker' })); + + expect(callbackMock).toHaveBeenCalledTimes(1); }); + test('Passes onExpand', async () => { + render(); + + await user.click(screen.getByRole('button', { name: 'onExpand clicker' })); - test('renders individual flag options successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); + expect(callbackMock).toHaveBeenCalledTimes(1); }); -}); + test('Passes onCollapse', async () => { + render(); -test('renders guides successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); -}); + await user.click(screen.getByRole('button', { name: 'onCollapse clicker' })); -test('renders compact successfully', () => { - const { asFragment } = render(); - expect(asFragment()).toMatchSnapshot(); + expect(callbackMock).toHaveBeenCalledTimes(1); + }); }); -test('renders compact no background successfully', () => { - const { asFragment } = render(); +test('Matches snapshot', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/TreeView/__tests__/TreeViewList.test.tsx b/packages/react-core/src/components/TreeView/__tests__/TreeViewList.test.tsx new file mode 100644 index 00000000000..496d8cc6b08 --- /dev/null +++ b/packages/react-core/src/components/TreeView/__tests__/TreeViewList.test.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { TreeViewList } from '../TreeViewList'; +import styles from '@patternfly/react-styles/css/components/TreeView/tree-view'; + +test(`Renders with class ${styles.treeView}__list by default`, () => { + render(Content); + + expect(screen.getByRole('tree')).toHaveClass(`${styles.treeView}__list`, { exact: true }); +}); + +test(`Renders with role="tree" by default`, () => { + render(Content); + + expect(screen.getByRole('tree')).toHaveTextContent('Content'); +}); + +test(`Renders with role="group" when isNested is true`, () => { + render(Content); + + expect(screen.getByRole('group')).toHaveTextContent('Content'); +}); + +test(`Spreads additional props`, () => { + render(Content); + + expect(screen.getByRole('tree')).toHaveAttribute('id', 'test-id'); +}); + +test(`Renders toolbar content when toolbar prop is passed`, () => { + render(Content); + + expect(screen.getByText('Toolbar content')).toBeInTheDocument(); +}); + +test(`Does not render toolbar content when toolbar prop is not passed`, () => { + render(Content); + + expect(screen.queryByRole('separator')).not.toBeInTheDocument(); +}); + +test('Matches snapshot by default', () => { + const { asFragment } = render(Content); + + expect(asFragment()).toMatchSnapshot(); +}); + +test('Matches snapshot when toolbar is passed', () => { + const { asFragment } = render(Toolbar content
                        }>Content); + + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-core/src/components/TreeView/__tests__/TreeViewListItem.test.tsx b/packages/react-core/src/components/TreeView/__tests__/TreeViewListItem.test.tsx new file mode 100644 index 00000000000..2451c90c041 --- /dev/null +++ b/packages/react-core/src/components/TreeView/__tests__/TreeViewListItem.test.tsx @@ -0,0 +1,708 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { TreeViewListItem } from '../TreeViewListItem'; +import { TreeView } from '../TreeView'; +import userEvent from '@testing-library/user-event'; +import styles from '@patternfly/react-styles/css/components/TreeView/tree-view'; + +const requiredProps = { + name: 'Item name', + title: 'Item title' +}; + +test(`Renders without children`, () => { + render( +
                        + +
                        + ); + + expect(screen.getByTestId('container').firstChild).toBeVisible(); +}); + +test(`Does not render children by default`, () => { + render(Content); + + expect(screen.queryByText('Content')).not.toBeInTheDocument(); +}); + +test(`Renders children if defaultExpanded is true`, () => { + render( + + Content + + ); + + expect(screen.getByText('Content')).toBeVisible(); +}); + +test(`Renders children if isExpanded is true`, () => { + render( + + Content + + ); + + expect(screen.getByText('Content')).toBeVisible(); +}); + +test(`Renders children when toggle is clicked`, async () => { + const user = userEvent.setup(); + render(Content); + + await user.click(screen.getByRole('button')); + + expect(screen.getByText('Content')).toBeVisible(); +}); + +test(`Renders with class ${styles.treeViewListItem} and aria-expanded=false by default`, () => { + render(); + + expect(screen.getByRole('treeitem')).toHaveClass(styles.treeViewListItem, { exact: true }); + expect(screen.getByRole('treeitem')).toHaveAttribute('aria-expanded', 'false'); +}); + +test(`Renders with class ${styles.modifiers.expanded} and aria-expanded=true when defaultExpanded is true`, () => { + render(); + + expect(screen.getByRole('treeitem')).toHaveClass(styles.modifiers.expanded); + expect(screen.getByRole('treeitem')).toHaveAttribute('aria-expanded', 'true'); +}); + +test(`Renders with class ${styles.modifiers.expanded} and aria-expanded=true when isExpanded is true`, () => { + render(); + + expect(screen.getByRole('treeitem')).toHaveClass(styles.modifiers.expanded); + expect(screen.getByRole('treeitem')).toHaveAttribute('aria-expanded', 'true'); +}); + +test('Renders with id when passed', () => { + render(); + + expect(screen.getByRole('treeitem')).toHaveAttribute('id', 'test-id'); +}); + +test('Does not render expandable toggle if children are not passed', () => { + render(); + + expect(screen.queryByText(requiredProps.name)?.previousSibling).not.toBeInTheDocument(); +}); + +test(`Renders expandable toggle with class ${styles.treeViewNodeToggle} if children are passed`, () => { + render(Content); + + expect(screen.getByText(requiredProps.name).previousElementSibling).toHaveClass(styles.treeViewNodeToggle); +}); + +test(`Renders expandable toggle without aria-labelledby as span by default`, () => { + render(Content); + const toggle = screen.getByText(requiredProps.name).previousElementSibling; + + expect(toggle?.tagName).toBe('SPAN'); + expect(toggle).not.toHaveAccessibleName(); +}); + +test(`Renders expandable toggle as button with aria-labelledby when hasCheckbox is passed`, () => { + render( + + Content + + ); + const toggle = screen.getByRole('button'); + + expect(toggle).toHaveClass(styles.treeViewNodeToggle); + expect(toggle).toHaveAccessibleName(requiredProps.name); +}); + +test(`Renders expandable toggle as button with aria-labelledby when isSelectable is passed`, () => { + render( + + Content + + ); + const toggle = screen.getByText(requiredProps.name).previousElementSibling; + + expect(toggle?.tagName).toBe('BUTTON'); + expect(toggle).toHaveAccessibleName(requiredProps.name); +}); + +test(`Renders name prop with class ${styles.treeViewNodeText}`, () => { + render(); + + expect(screen.getByText(requiredProps.name)).toHaveClass(styles.treeViewNodeText); +}); + +test(`Renders name prop in span by default`, () => { + render(); + + expect(screen.getByText(requiredProps.name).tagName).toBe('SPAN'); +}); + +test(`Renders name prop in button when isSelectable is passed`, () => { + render(); + + expect(screen.getByText(requiredProps.name).tagName).toBe('BUTTON'); +}); + +test('Does not render title prop by default', () => { + render(); + + expect(screen.queryByText(requiredProps.title)).not.toBeInTheDocument(); +}); + +test(`Renders title prop with class ${styles.treeViewNodeTitle} when isCompact is passed`, () => { + render(); + + expect(screen.getByText(requiredProps.title)).toHaveClass(styles.treeViewNodeTitle); +}); + +test(`Does not render name in ${styles.treeViewNodeContent} wrapper by default`, () => { + render(); + + expect(screen.getByText(requiredProps.name).parentElement).not.toHaveClass(styles.treeViewNodeContent); +}); + +test(`Renders name and title in ${styles.treeViewNodeContent} wrapper when isCompact is passed`, () => { + render(); + + expect(screen.getByText(requiredProps.name).parentElement).toHaveClass(styles.treeViewNodeContent); + expect(screen.getByText(requiredProps.title).parentElement).toHaveClass(styles.treeViewNodeContent); +}); + +test('Does not render checkbox by default', () => { + render(); + + expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); +}); + +test('Renders checkbox when hasCheckbox is passed', () => { + render(); + + expect(screen.getByRole('checkbox')).toBeInTheDocument(); +}); + +test('Passes checkProps to checkbox', () => { + render(); + + // Want to check that checkProps.checked as well as additional spread props are passed + expect(screen.getByRole('checkbox')).toBeChecked(); + expect(screen.getByRole('checkbox')).toHaveClass('test-class'); +}); + +test('Does not render icon by default', () => { + render(Content); + + // + expect(screen.getByText(requiredProps.name).previousElementSibling).not.toHaveClass(styles.treeViewNodeIcon); +}); + +test(`Renders icon with class ${styles.treeViewNodeIcon} when prop is passed`, () => { + render( + + Content + + ); + + expect(screen.getByText('Icon content')).toHaveClass(styles.treeViewNodeIcon); + // Check that previous test isn't a false positive + expect(screen.getByText(requiredProps.name).previousElementSibling).toHaveClass(styles.treeViewNodeIcon); +}); + +test('Does not render expandedIcon if list item is not expanded', () => { + render( + + Content + + ); + + expect(screen.queryByText('Expanded icon content')).not.toBeInTheDocument(); +}); + +test('Renders expandedIcon instead of icon if list item is expanded', () => { + render( + + Content + + ); + + expect(screen.getByText('Expanded icon content')).toBeInTheDocument(); + expect(screen.queryByText('Icon content')).not.toBeInTheDocument(); +}); + +test('Does not render badge by default', () => { + render(); + + expect(screen.queryByText(requiredProps.name)?.nextElementSibling).not.toBeInTheDocument(); +}); + +test('Does not render badge if hasBadge is passed but children and customBadgeContent are not passed', () => { + render(); + + expect(screen.queryByText(requiredProps.name)?.nextElementSibling).not.toBeInTheDocument(); +}); + +test('Renders badge with count by default when hasBadge and children are passed', () => { + render( + + + + ); + + expect(screen.getByText(requiredProps.name).nextElementSibling).toHaveTextContent('2'); +}); + +test('Renders badge with customBadgeContent when hasBadge and children are passed', () => { + render( + + + + ); + + expect(screen.getByText(requiredProps.name).nextElementSibling).toHaveTextContent('Custom badge'); +}); + +test('Renders badge with customBadgeContent when hasBadge is passed and children are not passed', () => { + render(); + + expect(screen.getByText(requiredProps.name).nextElementSibling).toHaveTextContent('Custom badge'); +}); + +test(`Renders badge with class ${styles.treeViewNodeCount} when hasBadge and children passed`, () => { + render( + + + + ); + + expect(screen.getByText('2').parentElement).toHaveClass(styles.treeViewNodeCount); +}); + +test(`Renders badge with class ${styles.treeViewNodeCount} when hasBadge is passed and children are not passed`, () => { + render(); + + expect(screen.getByText('Custom badge').parentElement).toHaveClass(styles.treeViewNodeCount); +}); + +test('Passes badgeProps when hasBadge and children are passed', () => { + render( + + + + ); + + expect(screen.getByText('2')).toHaveClass('test-class'); +}); + +test('Passes badgeProps when hasBadge and customBadgeContent are passed', () => { + render( + + ); + + expect(screen.getByText('Badge content')).toHaveClass('test-class'); +}); + +test(`Renders ${styles.treeViewNode} element as button by default`, () => { + render(); + + expect(screen.getByRole('button')).toHaveClass(styles.treeViewNode); +}); + +test(`Renders ${styles.treeViewNode} element as label when hasCheckbox is passed`, () => { + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode?.tagName).toBe('LABEL'); +}); + +test(`Renders ${styles.treeViewNode} element as div when isSelectable is passed`, () => { + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode?.tagName).toBe('DIV'); +}); + +test(`Does not render ${styles.treeViewNode} element with for or id attributes by default`, () => { + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).not.toHaveAttribute('for'); + expect(treeViewNode).not.toHaveAttribute('id'); +}); + +test(`Renders ${styles.treeViewNode} element with for and id attributes when hasCheckbox is passed`, () => { + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveAttribute('for'); + expect(treeViewNode).toHaveAttribute('id'); +}); + +test(`Renders ${styles.treeViewNode} element with id attribute when isSelectable and children are passed`, () => { + render( + + Content + + ); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveAttribute('id'); +}); + +test(`Does not render ${styles.treeViewNode} element with additional classes by default`, () => { + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveClass(styles.treeViewNode, { exact: true }); +}); + +test(`Renders ${styles.treeViewNode} element with ${styles.modifiers.selectable} class when hasCheckbox and children are passed`, () => { + render( + + Content + + ); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveClass(styles.modifiers.selectable); +}); + +test(`Renders ${styles.treeViewNode} element with ${styles.modifiers.selectable} class when isSelectable and children are passed`, () => { + render( + + Content + + ); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveClass(styles.modifiers.selectable); +}); + +test(`Does not render ${styles.treeViewNode} element with ${styles.modifiers.selectable} if children are not passed`, () => { + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).not.toHaveClass(styles.modifiers.selectable); +}); + +test(`Renders ${styles.treeViewNode} element with ${styles.modifiers.current} class when isSelectable and activeItems are passed`, () => { + render( + true} isSelectable activeItems={[{ name: 'Active item' }]} {...requiredProps}> + Content + + ); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveClass(styles.modifiers.current); +}); + +test(`Renders ${styles.treeViewNode} element with ${styles.modifiers.current} class when children are not passed`, () => { + render( true} activeItems={[{ name: 'Active item' }]} {...requiredProps} />); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).toHaveClass(styles.modifiers.current); +}); + +test(`Does not render ${styles.treeViewNode} element with ${styles.modifiers.current} class when children are passed`, () => { + render( + true} activeItems={[{ name: 'Active item' }]} {...requiredProps}> + Content + + ); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + + expect(treeViewNode).not.toHaveClass(styles.modifiers.current); +}); + +describe('compareItems callback', () => { + const compareItemsMock = jest.fn(); + const activeItems = [{ name: 'Active item' }]; + const itemData = { name: 'Item data' }; + + test('Not called by default', () => { + render(Content); + + expect(compareItemsMock).not.toHaveBeenCalled(); + }); + + test('Called when isSelectable and activeItems are passed', () => { + render( + + Content + + ); + + expect(compareItemsMock).toHaveBeenCalledTimes(1); + expect(compareItemsMock).toHaveBeenCalledWith( + expect.objectContaining(activeItems[0]), + expect.objectContaining(itemData) + ); + }); + + test('Not called when isSelectable is passed but activeItems is not passed', () => { + render( + + Content + + ); + + expect(compareItemsMock).not.toHaveBeenCalled(); + }); + + test('Called when children and activeItems are passed', () => { + render( + + ); + + expect(compareItemsMock).toHaveBeenCalledTimes(1); + expect(compareItemsMock).toHaveBeenCalledWith( + expect.objectContaining(activeItems[0]), + expect.objectContaining(itemData) + ); + }); +}); + +test('Does not call onCheck by default', async () => { + const user = userEvent.setup(); + const onCheckMock = jest.fn(); + + render(); + + await user.click(screen.getByRole('checkbox')); + + expect(onCheckMock).not.toHaveBeenCalled(); +}); + +test('Calls onCheck callback when checkbox is clicked', async () => { + const user = userEvent.setup(); + const onCheckMock = jest.fn(); + + render(); + + await user.click(screen.getByRole('checkbox')); + + expect(onCheckMock).toHaveBeenCalledTimes(1); + expect(onCheckMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test('Does not call onSelect by default', async () => { + const user = userEvent.setup(); + const onSelectMock = jest.fn(); + + render(); + + await user.click(screen.getByRole('button')); + + expect(onSelectMock).not.toHaveBeenCalled(); +}); + +test(`Calls onSelect when ${styles.treeViewNode} is clicked`, async () => { + const user = userEvent.setup(); + const onSelectMock = jest.fn(); + + render(); + + await user.click(screen.getByRole('button')); + + expect(onSelectMock).toHaveBeenCalledTimes(1); + expect(onSelectMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test('Does not call onSelect when hasCheckbox is passed', async () => { + const user = userEvent.setup(); + const onSelectMock = jest.fn(); + + render(); + + const treeViewNode = screen.getByRole('treeitem').querySelector(`.${styles.treeViewNode}`); + await user.click(treeViewNode as Element); + + expect(onSelectMock).not.toHaveBeenCalled(); +}); + +test('Does not call onExpand by default', async () => { + const user = userEvent.setup(); + const onExpandMock = jest.fn(); + + render(Content); + + await user.click(screen.getByRole('button')); + + expect(onExpandMock).not.toHaveBeenCalled(); +}); + +test(`Calls onExpand when ${styles.treeViewNode} is collapsed and clicked`, async () => { + const user = userEvent.setup(); + const onExpandMock = jest.fn(); + + render( + + Content + + ); + + await user.click(screen.getByRole('button')); + + expect(onExpandMock).toHaveBeenCalledTimes(1); + expect(onExpandMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test(`Calls onExpand when ${styles.treeViewNodeToggle} is clicked and isSelectable is passed`, async () => { + const user = userEvent.setup(); + const onExpandMock = jest.fn(); + + render( + + Content + + ); + + const toggle = screen.getByText(requiredProps.name).previousElementSibling; + await user.click(toggle as Element); + + expect(onExpandMock).toHaveBeenCalledTimes(1); + expect(onExpandMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test(`Calls onExpand when ${styles.treeViewNodeToggle} is clicked and hasCheckbox is passed`, async () => { + const user = userEvent.setup(); + const onExpandMock = jest.fn(); + + render( + + Content + + ); + + const toggle = screen.getByText(requiredProps.name).previousElementSibling?.previousElementSibling; + await user.click(toggle as Element); + + expect(onExpandMock).toHaveBeenCalledTimes(1); + expect(onExpandMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test('Does not call onCollapse by default', async () => { + const user = userEvent.setup(); + const onCollapseMock = jest.fn(); + + render( + + Content + + ); + + await user.click(screen.getByRole('button')); + + expect(onCollapseMock).not.toHaveBeenCalled(); +}); + +test(`Calls onCollapse when ${styles.treeViewNode} is expanded and clicked`, async () => { + const user = userEvent.setup(); + const onCollapseMock = jest.fn(); + + render( + + Content + + ); + + await user.click(screen.getByRole('button')); + + expect(onCollapseMock).toHaveBeenCalledTimes(1); + expect(onCollapseMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test(`Calls onCollapse when ${styles.treeViewNodeToggle} is clicked and isSelectable is passed`, async () => { + const user = userEvent.setup(); + const onCollapseMock = jest.fn(); + + render( + + Content + + ); + + const toggle = screen.getByText(requiredProps.name).previousElementSibling; + await user.click(toggle as Element); + + expect(onCollapseMock).toHaveBeenCalledTimes(1); + expect(onCollapseMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test(`Calls onCollapse when ${styles.treeViewNodeToggle} is clicked and hasCheckbox is passed`, async () => { + const user = userEvent.setup(); + const onCollapseMock = jest.fn(); + + render( + + Content + + ); + + const toggle = screen.getByText(requiredProps.name).previousElementSibling?.previousElementSibling; + await user.click(toggle as Element); + + expect(onCollapseMock).toHaveBeenCalledTimes(1); + expect(onCollapseMock).toHaveBeenCalledWith(expect.any(Object), undefined, undefined); +}); + +test(`Does not render ${styles.treeViewAction} element by default`, () => { + render(); + + expect(screen.queryByRole('treeitem')?.querySelector(`.${styles.treeViewAction}`)).not.toBeInTheDocument(); +}); + +test(`Renders action with ${styles.treeViewAction} class when action is passed`, () => { + render(); + + expect(screen.getByText('Action content')).toHaveClass(styles.treeViewAction); +}); + +test('Matches snapshot without children', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); +}); + +test('Matches snapshot with children', () => { + const { asFragment } = render(Content); + + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-core/src/components/TreeView/__tests__/TreeViewRoot.test.tsx b/packages/react-core/src/components/TreeView/__tests__/TreeViewRoot.test.tsx new file mode 100644 index 00000000000..097bf3d77fa --- /dev/null +++ b/packages/react-core/src/components/TreeView/__tests__/TreeViewRoot.test.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { TreeViewRoot } from '../TreeViewRoot'; +import styles from '@patternfly/react-styles/css/components/TreeView/tree-view'; + +test('Renders children', () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content')).toBeVisible(); +}); + +test(`Renders with class ${styles.treeView} by default`, () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content').parentElement).toHaveClass(styles.treeView, { exact: true }); +}); + +test(`Renders with custom class when className is passed`, () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content').parentElement).toHaveClass('test-class'); +}); + +test(`Renders with class ${styles.modifiers.guides} when hasGuides is passed`, () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content').parentElement).toHaveClass(styles.modifiers.guides); +}); + +test(`Does not render with additional classes when variant="default"`, () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content').parentElement).toHaveClass(styles.treeView, { exact: true }); +}); + +test(`Renders with class ${styles.modifiers.compact} when variant="compact"`, () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content').parentElement).toHaveClass(styles.modifiers.compact); +}); + +test(`Renders with classes ${styles.modifiers.compact} and ${styles.modifiers.noBackground} when variant="compactNoBackground"`, () => { + render( + +
                        Content
                        +
                        + ); + + expect(screen.getByText('Content').parentElement).toHaveClass( + styles.modifiers.compact, + styles.modifiers.noBackground + ); +}); + +test('Matches snapshot', () => { + const { asFragment } = render( + +
                        Content
                        +
                        + ); + + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-core/src/components/TreeView/__tests__/TreeViewSearch.test.tsx b/packages/react-core/src/components/TreeView/__tests__/TreeViewSearch.test.tsx new file mode 100644 index 00000000000..049a4d49a4c --- /dev/null +++ b/packages/react-core/src/components/TreeView/__tests__/TreeViewSearch.test.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { TreeViewSearch } from '../TreeViewSearch'; +import userEvent from '@testing-library/user-event'; +import styles from '@patternfly/react-styles/css/components/TreeView/tree-view'; + +test(`Renders with ${styles.treeViewSearch} by default`, () => { + render(); + + expect(screen.getByRole('searchbox').parentElement?.parentElement).toHaveClass(styles.treeViewSearch, { + exact: true + }); +}); + +test(`Renders with custom class when className is passed`, () => { + render(); + + expect(screen.getByRole('searchbox').parentElement?.parentElement).toHaveClass('test-class'); +}); + +test(`Renders with id when passed`, () => { + render(); + + expect(screen.getByRole('searchbox')).toHaveAttribute('id', 'test-id'); +}); + +test(`Does not render with id when it is not passed`, () => { + render(); + + expect(screen.getByRole('searchbox')).not.toHaveAttribute('id'); +}); + +test(`Renders with name when passed`, () => { + render(); + + expect(screen.getByRole('searchbox')).toHaveAttribute('name', 'testName'); +}); + +test(`Does not render with name when it is not passed`, () => { + render(); + + expect(screen.getByRole('searchbox')).not.toHaveAttribute('name'); +}); + +test(`Renders with aria-label when passed`, () => { + render(); + + expect(screen.getByRole('searchbox')).toHaveAccessibleName('test label'); +}); + +test(`Does not render with aria-label when it is not passed`, () => { + render(); + + expect(screen.getByRole('searchbox')).not.toHaveAccessibleName(); +}); + +test(`Spreads additional props`, () => { + render(); + + expect(screen.getByRole('searchbox')).toHaveAttribute('data-testprop', 'test-value'); +}); + +test(`Calls onSearch when input is typed into`, async () => { + const user = userEvent.setup(); + const onSearchMock = jest.fn(); + render(); + + await user.type(screen.getByRole('searchbox'), 'a'); + + expect(onSearchMock).toHaveBeenCalledTimes(1); +}); + +test(`Does not call onSearch when input is not typed into`, async () => { + const user = userEvent.setup(); + const onSearchMock = jest.fn(); + + render( + <> + + + + ); + + await user.type(screen.getByRole('textbox'), 'a'); + + expect(onSearchMock).not.toHaveBeenCalled(); +}); + +test('Matches snapshot', () => { + const { asFragment } = render(); + + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-core/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap b/packages/react-core/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap index 3da0823e398..960322b8685 100644 --- a/packages/react-core/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap +++ b/packages/react-core/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap @@ -1,3366 +1,122 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders compact no background successfully 1`] = ` +exports[`Matches snapshot 1`] = `
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`renders compact successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`renders guides successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders active successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders badges successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders basic successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders checkboxes successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders icons successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders individual flag options successfully 1`] = ` - -
                        -
                          -
                        • -
                          - -
                          -
                            -
                          • -
                            - -
                            -
                          • -
                          • -
                            - -
                            -
                          • -
                          -
                        • -
                        • -
                          - -
                          - -
                          -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        • -
                          - -
                          -
                        • -
                        -
                        -
                        -`; - -exports[`tree view renders search successfully 1`] = ` - -