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
-
-
-
-
-`;
-
-exports[`MessageBox Confirm 1`] = `
-
-
-
-
- My Message Box Content
-
-
-
-
-`;
-
-exports[`MessageBox Custom Action Text 1`] = `
-
-
-
-
- My Message Box Content
-
-
-
-
-`;
-
-exports[`MessageBox Custom Button 1`] = `
-
-
-
-
- My Message Box Content
-
-
-
-
-`;
-
-exports[`MessageBox Don't crash on unknown type 1`] = `
-
-
-
- Unknown Type!
-
-
-
-
-`;
-
-exports[`MessageBox Error 1`] = `
-
-
-
- My Message Box Content
-
-
-
-
-`;
-
-exports[`MessageBox Information 1`] = `
-
-
-
- My Message Box Content
-
-
-
-
-`;
-
-exports[`MessageBox No Title 1`] = `
-
-
-
-
- No Title
-
-
-
-
-`;
-
-exports[`MessageBox Not open 1`] = `
-
-
-
- Custom Success
-
-
-
-
-`;
-
-exports[`MessageBox Show 1`] = `
-
-
-
-
- Custom
-
-
-
-
-`;
-
-exports[`MessageBox Success 1`] = `
-
-
-
- My Message Box Content
-
-
-
-
-`;
-
-exports[`MessageBox Success w/ custom title 1`] = `
-
-
-
-
- Custom Success
-
-
-
-
-`;
-
-exports[`MessageBox Warning 1`] = `
-
-
-
- My Message Box Content
-
-
-
-
-`;
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 ;
default:
return null;
}
@@ -215,37 +191,28 @@ const MessageBox = forwardRef((props, ref) =>
onClose(enrichEventWithDetails(e, { action }));
};
- const messageBoxClassNames = clsx(classes.messageBox, className);
+ const messageBoxId = useIsomorphicId();
const internalActions = getActions(actions, type);
- const [uniqueIds, setUniqueIds] = useState(() => createUniqueIds(internalActions));
- useIsomorphicLayoutEffect(() => {
- setUniqueIds(createUniqueIds(internalActions));
- }, [internalActions.length]);
-
const getInitialFocus = () => {
- const indexOfInitialFocus = internalActions.indexOf(initialFocus);
- const actionToFocus = internalActions[indexOfInitialFocus] as string;
+ const actionToFocus = internalActions.find((action) => action === initialFocus);
if (typeof actionToFocus === 'string') {
- return `${actionToFocus}-${uniqueIds[indexOfInitialFocus]}`;
+ return `${messageBoxId}-action-${actionToFocus}`;
}
return initialFocus;
};
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
+ // @ts-expect-error: footer, headerText and onAfterClose are already omitted via prop types
const { footer: _0, headerText: _1, onAfterClose: _2, ...restWithoutOmitted } = rest;
const iconToRender = getIcon(icon, type);
const needsCustomHeader = !props.header && !!iconToRender;
- const messageBoxId = useIsomorphicId();
-
return (