diff --git a/packages/main/src/components/MessageBox/MessageBox.cy.tsx b/packages/main/src/components/MessageBox/MessageBox.cy.tsx new file mode 100644 index 00000000000..e92f0b8c464 --- /dev/null +++ b/packages/main/src/components/MessageBox/MessageBox.cy.tsx @@ -0,0 +1,234 @@ +import addIcon from '@ui5/webcomponents-icons/dist/add.js'; +import { Button, Icon, MessageBoxActions, MessageBoxTypes } from '../..'; +import { MessageBox } from './index'; + +describe('MessageBox', () => { + [ + [MessageBoxTypes.Confirm, MessageBoxActions.OK], + [MessageBoxTypes.Success, MessageBoxActions.OK], + [MessageBoxTypes.Warning, MessageBoxActions.OK], + [MessageBoxTypes.Error, MessageBoxActions.Close], + [MessageBoxTypes.Information, MessageBoxActions.OK] + ].forEach(([type, buttonText]: [MessageBoxTypes, MessageBoxActions]) => { + it(type, () => { + const callback = cy.spy(); + cy.mount( + + My Message Box Content + + ); + cy.findByText(buttonText).click(); + cy.wrap(callback).should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: buttonText + } + }) + ); + }); + }); + + it('Custom Button', () => { + const click = cy.spy().as('onButtonClick'); + const close = cy.spy().as('onMessageBoxClose'); + cy.mount( + + Custom + , + 'Custom Text Action', + MessageBoxActions.OK + ]} + > + My Message Box Content + + ); + + cy.findByText('Cancel').should('be.visible'); + cy.findByText('Custom Text Action').should('be.visible'); + cy.findByText('OK').should('be.visible'); + + cy.findByText('Custom').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledOnce') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: `1: custom action` + } + }) + ); + cy.get('@onButtonClick').should('have.been.calledOnce'); + }); + + it('Confirm - Cancel', () => { + const callback = cy.spy().as('onMessageBoxClose'); + cy.mount( + + Confirm + + ); + + cy.findByText('Cancel').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledOnce') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: MessageBoxActions.Cancel + } + }) + ); + }); + + it('Show', () => { + const callback = cy.spy().as('onMessageBoxClose'); + cy.mount( + + Custom + + ); + + cy.findByText('Yes').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledOnce') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: MessageBoxActions.Yes + } + }) + ); + + cy.findByText('No').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledTwice') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: MessageBoxActions.No + } + }) + ); + }); + + it('Success w/ custom title', () => { + const callback = cy.spy().as('onMessageBoxClose'); + cy.mount( + } + > + Custom Success + + ); + cy.findAllByText('Custom Success').should('have.length', 2); + cy.findByText('OK').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledOnce') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: MessageBoxActions.OK + } + }) + ); + }); + + it('No Title', () => { + const callback = cy.spy().as('onMessageBoxClose'); + cy.mount( + + No Title + + ); + + cy.findByText('Confirmation').should('be.visible'); + }); + + it('Custom Action Text', () => { + const callback = cy.spy().as('onMessageBoxClose'); + cy.mount( + + My Message Box Content + + ); + + cy.findByText(MessageBoxActions.OK).should('be.visible').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledOnce') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: MessageBoxActions.OK + } + }) + ); + cy.findByText('My Custom Action').should('be.visible').click(); + cy.get('@onMessageBoxClose') + .should('have.been.calledTwice') + .should( + 'have.been.calledWith', + Cypress.sinon.match({ + detail: { + action: 'My Custom Action' + } + }) + ); + }); + + it("Don't crash on unknown type", () => { + const callback = cy.spy().as('onMessageBoxClose'); + cy.mount( + // @ts-expect-error: testing an invalid type + + Unknown Type! + + ); + cy.findByText('Unknown Type!').should('be.visible'); + }); + + it('initial focus', () => { + cy.mount( + + Content + + ); + + cy.focused().then(([el]) => { + const focusedElementId = el.id; + cy.findByText('Cancel').should('have.id', focusedElementId); + cy.findByTestId('Dialog').should('have.attr', 'initial-focus', focusedElementId); + }); + }); + + it('display custom header', () => { + cy.mount( + Custom Header}> + Content + + ); + + cy.findByText('Confirmation').should('not.exist'); + cy.findByText('Custom Header').should('be.visible'); + }); +}); diff --git a/packages/main/src/components/MessageBox/MessageBox.stories.mdx b/packages/main/src/components/MessageBox/MessageBox.stories.mdx index ec153ff3951..2f7d4552989 100644 --- a/packages/main/src/components/MessageBox/MessageBox.stories.mdx +++ b/packages/main/src/components/MessageBox/MessageBox.stories.mdx @@ -174,7 +174,14 @@ const MessageBoxComponent = () => { Custom Button] + actions: [ + MessageBoxActions.OK, + 'Custom Action', + MessageBoxActions.Cancel, + + ] }} > {(args) => { diff --git a/packages/main/src/components/MessageBox/MessageBox.test.tsx b/packages/main/src/components/MessageBox/MessageBox.test.tsx deleted file mode 100644 index 0db053efb95..00000000000 --- a/packages/main/src/components/MessageBox/MessageBox.test.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { fireEvent, render, screen } from '@shared/tests'; -import '@ui5/webcomponents-icons/dist/add.js'; -import React from 'react'; -import { Button, Icon, MessageBoxActions, MessageBoxTypes } from '../..'; -import { MessageBox } from './index'; - -const mockActionIds = (element) => { - element.querySelectorAll('ui5-button').forEach((item, index) => { - item.id = `${index}`; - }); -}; - -describe('MessageBox', () => { - test.each([ - [MessageBoxTypes.Confirm, MessageBoxActions.OK], - [MessageBoxTypes.Success, MessageBoxActions.OK], - [MessageBoxTypes.Warning, MessageBoxActions.OK], - [MessageBoxTypes.Error, MessageBoxActions.Close], - [MessageBoxTypes.Information, MessageBoxActions.OK] - ])('%s', (type, buttonText) => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - - My Message Box Content - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - - fireEvent.click(screen.getByText(buttonText)); - - expect(callback.mock.calls[0][0].detail.action).toEqual(buttonText); - unmount(); - }); - - test('Custom Button', () => { - const click = jest.fn(); - const close = jest.fn(); - const { asFragment, getByText, container, rerender } = render( - - Custom - - ]} - > - My Message Box Content - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - - fireEvent.click(getByText('Custom')); - expect(close.mock.calls[0][0].detail.action).toEqual('0: custom action'); - expect(close).toHaveBeenCalledTimes(1); - expect(click).toHaveBeenCalledTimes(1); - - rerender( - - Custom - , - 'Custom Text Action', - MessageBoxActions.OK - ]} - > - My Message Box Content - - ); - - getByText('Cancel'); - getByText('Custom Text Action'); - getByText('OK'); - fireEvent.click(getByText('Custom')); - expect(close.mock.calls[1][0].detail.action).toEqual('1: custom action'); - expect(close).toHaveBeenCalledTimes(2); - expect(click).toHaveBeenCalledTimes(2); - }); - - test('Confirm - Cancel', () => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - - Confirm - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - - fireEvent.click(screen.getByText('Cancel')); - - expect(callback.mock.calls[0][0].detail.action).toEqual(MessageBoxActions.Cancel); - unmount(); - }); - - test('Show', () => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - - Custom - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - - fireEvent.click(screen.getByText('Yes')); - expect(callback.mock.calls[0][0].detail.action).toEqual(MessageBoxActions.Yes); - - fireEvent.click(screen.getByText('No')); - expect(callback.mock.calls).toHaveLength(2); - expect(callback.mock.calls[1][0].detail.action).toEqual(MessageBoxActions.No); - unmount(); - }); - - test('Success w/ custom title', () => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - } - > - Custom Success - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - expect(screen.getAllByText('Custom Success')).toHaveLength(2); - - fireEvent.click(screen.getByText('OK')); - expect(callback.mock.calls[0][0].detail.action).toEqual(MessageBoxActions.OK); - unmount(); - }); - - test('Not open', () => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - - Custom Success - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - unmount(); - }); - - test('No Title', () => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - - No Title - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - - expect(screen.getByText('Confirmation')).toBeInTheDocument(); - unmount(); - }); - - test('Custom Action Text', () => { - const onClose = jest.fn(); - const { asFragment, unmount, container } = render( - - My Message Box Content - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - - const textOK = screen.getByText(MessageBoxActions.OK); - expect(textOK).toBeInTheDocument(); - const customAction = screen.getByText('My Custom Action'); - expect(customAction).toBeInTheDocument(); - - fireEvent.click(textOK); - fireEvent.click(customAction); - - expect(onClose.mock.calls[0][0].detail.action).toEqual(MessageBoxActions.OK); - expect(onClose.mock.calls[1][0].detail.action).toEqual('My Custom Action'); - unmount(); - }); - - test("Don't crash on unknown type", () => { - const callback = jest.fn(); - const { asFragment, unmount, container } = render( - - Unknown Type! - - ); - mockActionIds(container); - expect(asFragment()).toMatchSnapshot(); - unmount(); - }); - - test('Unique ids for actions', () => { - jest.spyOn(console, 'error').mockImplementation(() => {}); - jest.spyOn(console, 'warn').mockImplementation(() => {}); - const { getAllByText } = render( - <> - {new Array(200).fill('howdy').map((_, index) => ( - - Content - - ))} - - ); - const okBtns = getAllByText('OK'); - const okBtnsIds = okBtns.map((btn) => btn.id); - expect(new Set(okBtnsIds).size).toEqual(okBtnsIds.length); - const cancelBtns = getAllByText('Cancel'); - const cancelBtnsIds = cancelBtns.map((btn) => btn.id); - expect(new Set(cancelBtnsIds).size).toEqual(cancelBtnsIds.length); - }); - - test('initial focus', () => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); - jest.spyOn(console, 'error').mockImplementation(() => {}); - const { getByText, getByTestId } = render( - - Content - - ); - const dialogInitialFocus = getByTestId('Dialog').getAttribute('initial-focus'); - const cancelBtnId = getByText('Cancel').getAttribute('id'); - - expect(dialogInitialFocus).toEqual(cancelBtnId); - }); - - test('display custom header', () => { - const { getByText, queryByText } = render( - Custom Header}> - Content - - ); - expect(queryByText('Confirmation')).toBeNull(); - expect(getByText('Custom Header')).toHaveTextContent('Custom Header'); - }); -}); diff --git a/packages/main/src/components/MessageBox/__snapshots__/MessageBox.test.tsx.snap b/packages/main/src/components/MessageBox/__snapshots__/MessageBox.test.tsx.snap deleted file mode 100644 index 716f9e9f426..00000000000 --- a/packages/main/src/components/MessageBox/__snapshots__/MessageBox.test.tsx.snap +++ /dev/null @@ -1,626 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`MessageBox Confirm - Cancel 1`] = ` - - -
-
- - Confirm - -
- - OK - - - Cancel - -
-
-
-`; - -exports[`MessageBox Confirm 1`] = ` - - -
-
- - My Message Box Content - -
- - OK - - - Cancel - -
-
-
-`; - -exports[`MessageBox Custom Action Text 1`] = ` - - -
-
- - My Message Box Content - -
- - OK - - - My Custom Action - -
-
-
-`; - -exports[`MessageBox Custom Button 1`] = ` - - -
-
- - My Message Box Content - -
- - Custom - -
-
-
-`; - -exports[`MessageBox Don't crash on unknown type 1`] = ` - - - - Unknown Type! - -
- - OK - -
-
-
-`; - -exports[`MessageBox Error 1`] = ` - - - - My Message Box Content - -
- - Close - -
-
-
-`; - -exports[`MessageBox Information 1`] = ` - - - - My Message Box Content - -
- - OK - -
-
-
-`; - -exports[`MessageBox No Title 1`] = ` - - -
-
- - No Title - -
- - OK - - - Cancel - -
-
-
-`; - -exports[`MessageBox Not open 1`] = ` - - - - Custom Success - -
- - OK - -
-
-
-`; - -exports[`MessageBox Show 1`] = ` - - -
-
- - Custom - -
- - Yes - - - No - -
-
-
-`; - -exports[`MessageBox Success 1`] = ` - - - - My Message Box Content - -
- - OK - -
-
-
-`; - -exports[`MessageBox Success w/ custom title 1`] = ` - - -
- - - - Custom Success - -
- - Custom Success - -
- - OK - -
-
-
-`; - -exports[`MessageBox Warning 1`] = ` - - - - My Message Box Content - -
- - OK - -
-
-
-`; diff --git a/packages/main/src/components/MessageBox/index.tsx b/packages/main/src/components/MessageBox/index.tsx index f108e0a0d27..f90cdbd29d8 100644 --- a/packages/main/src/components/MessageBox/index.tsx +++ b/packages/main/src/components/MessageBox/index.tsx @@ -1,14 +1,9 @@ 'use client'; import iconSysHelp from '@ui5/webcomponents-icons/dist/sys-help-2.js'; -import { - enrichEventWithDetails, - useI18nBundle, - useIsomorphicId, - useIsomorphicLayoutEffect -} from '@ui5/webcomponents-react-base'; +import { enrichEventWithDetails, useI18nBundle, useIsomorphicId } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; -import React, { cloneElement, forwardRef, isValidElement, ReactNode, useState } from 'react'; +import React, { cloneElement, forwardRef, isValidElement, ReactNode } from 'react'; import { createUseStyles } from 'react-jss'; import { ButtonDesign, MessageBoxActions, MessageBoxTypes, TitleLevel, ValueState } from '../../enums'; import { @@ -29,16 +24,7 @@ import { } from '../../i18n/i18n-defaults'; import { Ui5CustomEvent } from '../../interfaces/Ui5CustomEvent'; import { stopPropagation } from '../../internal/stopPropagation'; -import { - Button, - ButtonPropTypes, - Dialog, - DialogDomRef, - DialogPropTypes, - Icon, - IconPropTypes, - Title -} from '../../webComponents'; +import { Button, ButtonPropTypes, Dialog, DialogDomRef, DialogPropTypes, Icon, Title } from '../../webComponents'; import { Text } from '../Text'; import styles from './MessageBox.jss'; @@ -106,21 +92,11 @@ export interface MessageBoxPropTypes const useStyles = createUseStyles(styles, { name: 'MessageBox' }); -const createUniqueIds = (internalActions): (string | null)[] => { - return internalActions.map((action) => { - if (typeof action === 'string') { - return `${performance.now() + Math.random()}`.split('.')[1]; - } - return null; - }); -}; - const getIcon = (icon, type) => { if (isValidElement(icon)) return icon; - const iconProps = { 'aria-hidden': 'true', accessibleRole: 'presentation' } as IconPropTypes; switch (type) { case MessageBoxTypes.Confirm: - return ; + return