diff --git a/packages/react-core/src/components/Card/__tests__/CardSelectableActions.test.tsx b/packages/react-core/src/components/Card/__tests__/CardSelectableActions.test.tsx new file mode 100644 index 00000000000..f07d0e95c3e --- /dev/null +++ b/packages/react-core/src/components/Card/__tests__/CardSelectableActions.test.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { CardSelectableActions } from '../CardSelectableActions'; + +test('Matches the snapshot', () => { + const { asFragment } = render(Test); + expect(asFragment()).toMatchSnapshot(); +}); + +test('Renders without children', () => { + render(); + + expect(screen.getByTestId('card-selectable-actions')).toBeVisible(); +}); + +test('Renders children', () => { + render(Test); + + expect(screen.getByText('Test')).toBeVisible(); +}); + +test('Renders with class name pf-v5-c-card__selectable-actions', () => { + render(Test); + + expect(screen.getByText('Test')).toHaveClass('pf-v5-c-card__selectable-actions'); +}); + +test('Renders with custom class names provided via prop', () => { + render(Test); + + expect(screen.getByText('Test')).toHaveClass('test-class'); +}); diff --git a/packages/react-core/src/components/Card/__tests__/__snapshots__/CardSelectableActions.test.tsx.snap b/packages/react-core/src/components/Card/__tests__/__snapshots__/CardSelectableActions.test.tsx.snap new file mode 100644 index 00000000000..d3c07ec4969 --- /dev/null +++ b/packages/react-core/src/components/Card/__tests__/__snapshots__/CardSelectableActions.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Matches the snapshot 1`] = ` + +
+ Test +
+
+`; diff --git a/packages/react-integration/cypress/integration/card.spec.ts b/packages/react-integration/cypress/integration/card.spec.ts index b2fc5b76d8c..3d7701566c7 100644 --- a/packages/react-integration/cypress/integration/card.spec.ts +++ b/packages/react-integration/cypress/integration/card.spec.ts @@ -3,8 +3,18 @@ describe('Card Demo Test', () => { cy.visit('http://localhost:3000/card-demo-nav-link'); }); - it('Verify that selectable card can be selected and unselected with keyboard input', () => { - cy.get('#selectableCard').focus(); + it('Verify card with actions', () => { + cy.get('#cardWithActions .pf-v5-c-menu-toggle').then(($menuToggle) => { + cy.wrap($menuToggle).should('not.have.class', 'pf-m-expanded'); + cy.wrap($menuToggle).click(); + cy.wrap($menuToggle).should('have.class', 'pf-m-expanded'); + }); + cy.get('#cardWithActions .pf-v5-c-menu .pf-v5-c-menu__item').first().click(); + cy.get('#cardWithActions .pf-v5-c-menu-toggle').should('not.have.class', 'pf-m-expanded'); + }); + + it('Verify that deprecated selectable card can be selected and unselected with keyboard input', () => { + cy.get('#selectableCardDeprecated').focus(); cy.focused().should('have.class', 'pf-m-selectable'); cy.focused().should('not.have.class', 'pf-m-selected'); cy.focused().type('{enter}'); @@ -29,4 +39,66 @@ describe('Card Demo Test', () => { cy.get('.pf-v5-c-card__header-toggle .pf-v5-c-button').click(); cy.get('#expand-card').should('have.class', 'pf-m-expanded'); }); + + it('Verify new selectable card can be selected', () => { + cy.get('#selectable-card-example-1 #selectable-card-input-1').should('not.be.checked'); + cy.get('#selectable-card-example-2 #selectable-card-input-2').should('not.be.checked'); + cy.get('#selectable-card-example-1').then(($card) => { + cy.wrap($card).click(); + cy.wrap($card).get('#selectable-card-input-1').should('be.checked'); + cy.get('#selectable-card-example-2 #selectable-card-input-2').should('not.be.checked'); + }); + cy.get('#selectable-card-example-2').then(($card) => { + cy.wrap($card).click(); + cy.wrap($card).get('#selectable-card-input-2').should('be.checked'); + cy.get('#selectable-card-example-1 #selectable-card-input-1').should('be.checked'); + }); + }); + + it('Verify new single selectable card can be selected', () => { + cy.get('#single-selectable-card-example-1 #single-selectable-card-input-1').should('not.be.checked'); + cy.get('#single-selectable-card-example-2 #single-selectable-card-input-2').should('not.be.checked'); + cy.get('#single-selectable-card-example-1').then(($card) => { + cy.wrap($card).click(); + cy.wrap($card).get('#single-selectable-card-input-1').should('be.checked'); + cy.get('#single-selectable-card-example-2 #single-selectable-card-input-2').should('not.be.checked'); + }); + cy.get('#single-selectable-card-example-2').then(($card) => { + cy.wrap($card).click(); + cy.wrap($card).get('#single-selectable-card-input-2').should('be.checked'); + cy.get('#single-selectable-card-example-1 #single-selectable-card-input-1').should('not.be.checked'); + }); + }); + + it('Verify clickable only card action is triggered', () => { + cy.get('#clickable-card-drawer').should('not.have.class', 'pf-m-expanded'); + cy.get('#clickable-card-example-1 #clickable-card-input-1').should('not.be.checked'); + cy.get('#clickable-card-example-1').click(); + cy.get('#clickable-card-drawer').should('have.class', 'pf-m-expanded'); + cy.get('#clickable-card-example-1 #clickable-card-input-1').should('be.checked'); + }); + + it('Verify clickable only card link is navigated to', () => { + cy.location('pathname').should('eq', '/card-demo-nav-link'); + cy.get('#clickable-card-example-2').click(); + cy.location('pathname').should('eq', '/button-demo-nav-link'); + cy.go('back'); + }); + + it('Verify clickable and selectable card', () => { + cy.get('#clickable-selectable-card-drawer').should('not.have.class', 'pf-m-expanded'); + cy.get('#clickable-selectable-card-example-1 #clickable-selectable-card-input-1').should('not.be.checked'); + // Clicking outside clickable areas should not change input or trigger action + cy.get('#clickable-selectable-card-example-1').click(); + cy.get('#clickable-selectable-card-drawer').should('not.have.class', 'pf-m-expanded'); + cy.get('#clickable-selectable-card-example-1 #clickable-selectable-card-input-1').should('not.be.checked'); + + // Ciicking input should not trigger action + cy.get('#clickable-selectable-card-example-1 #clickable-selectable-card-input-1').click(); + cy.get('#clickable-selectable-card-example-1 #clickable-selectable-card-input-1').should('be.checked'); + cy.get('#clickable-selectable-card-drawer').should('not.have.class', 'pf-m-expanded'); + + cy.get('#clickable-selectable-card-example-1 .pf-v5-c-button').click(); + cy.get('#clickable-selectable-card-drawer').should('have.class', 'pf-m-expanded'); + }); }); diff --git a/packages/react-integration/demo-app-ts/src/components/demos/CardDemo/CardDemo.tsx b/packages/react-integration/demo-app-ts/src/components/demos/CardDemo/CardDemo.tsx index ef3898d3f24..962f50e2f88 100644 --- a/packages/react-integration/demo-app-ts/src/components/demos/CardDemo/CardDemo.tsx +++ b/packages/react-integration/demo-app-ts/src/components/demos/CardDemo/CardDemo.tsx @@ -1,13 +1,17 @@ import React from 'react'; import { + Brand, + Button, Card, CardTitle, CardHeader, CardBody, CardFooter, CardExpandableContent, - Checkbox, - Brand, + Drawer, + DrawerContent, + DrawerContentBody, + DrawerPanelContent, Dropdown, DropdownItem, DropdownList, @@ -20,7 +24,12 @@ interface CardDemoState { selected: string; isExpanded: boolean; isOpen: boolean; - check1: boolean; + selectableChecked1: boolean; + selectableChecked2: boolean; + drawerIsExpanded: boolean; + selectableClickableChecked: boolean; + selectableClickableSelected: boolean; + selectaleClickableDrawerIsExpanded: boolean; } export class CardDemo extends React.Component { @@ -30,7 +39,12 @@ export class CardDemo extends React.Component { selected: null, isExpanded: false, isOpen: false, - check1: false + selectableChecked1: false, + selectableChecked2: false, + drawerIsExpanded: false, + selectableClickableChecked: false, + selectableClickableSelected: false, + selectaleClickableDrawerIsExpanded: false }; onKeyDown = (event: any) => { @@ -67,14 +81,39 @@ export class CardDemo extends React.Component { }); }; - onClick = (event: any, _checked: boolean) => { - const target = event.target; - const value = target.type === 'checkbox' ? target.checked : target.value; - const name = target.name; - this.setState({ [name]: value }); + onSelectableChange = (event: React.FormEvent, checked: boolean) => { + const name = event.currentTarget.name; + + switch (name) { + case 'selectable-card-input-1': + this.setState({ selectableChecked1: checked }); + break; + case 'selectable-card-input-2': + this.setState({ selectableChecked2: checked }); + break; + } + }; + + onSelectableClickableChange = (_event: React.FormEvent, checked: boolean) => { + this.setState({ selectableClickableChecked: checked }); + }; + + onSelectableClickableClick = () => { + this.setState({ + selectableClickableSelected: !this.state.selectableClickableSelected, + selectaleClickableDrawerIsExpanded: !this.state.selectaleClickableDrawerIsExpanded + }); }; render() { + const { + selectableChecked1, + selectableChecked2, + drawerIsExpanded, + selectableClickableChecked, + selectableClickableSelected, + selectaleClickableDrawerIsExpanded + } = this.state; const dropdownItems = [ Link, Action, @@ -103,74 +142,24 @@ export class CardDemo extends React.Component { > {dropdownItems} - ); return ( - - Header - Body - Footer - -

- - Header - Body - Footer - -

- + + + + Header Body Footer

- - - Header - - Body - Footer - -

- - Header - Body - Footer - - - Header - Body - Footer - - - Header - Body - Footer - - - Header - Body - Footer - - - Header - Body - Footer - - @@ -202,14 +191,143 @@ export class CardDemo extends React.Component { )}

- - - + + + First selectable card - Header - Body - Footer + This card is selectable. + + + + Second selectable card + + This card is selectable. + +

+ + + First single selectable card + + This card is single selectable. + + + Second single selectable card + + This card is single selectable. + +

+
+ + + Clickable card drawer panel + + } + > + + + { + this.setState({ drawerIsExpanded: !drawerIsExpanded }); + }, + selectableActionId: 'clickable-card-input-1', + selectableActionAriaLabelledby: 'clickable-card-example-1', + name: 'clickable-card-example-1' + }} + > + Clickable card with action + + This card performs an action on click. + + + + +
+

+ + + Clickable card with link + + This card can navigate to a link on click. + +

+
+ + + Clickable and selectable card drawer panel + + } + > + + + + + + + + This card performs an action upon clicking the card title and is selectable. + + + + +
); }