diff --git a/apps/meteor/client/components/GenericModal/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx index 4eddfaad90618..aa1f2b5febc11 100644 --- a/apps/meteor/client/components/GenericModal/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -9,7 +9,7 @@ import type { RequiredModalProps } from './withDoNotAskAgain'; import { withDoNotAskAgain } from './withDoNotAskAgain'; import { modalStore } from '../../providers/ModalProvider/ModalStore'; -type VariantType = 'danger' | 'warning' | 'info' | 'success'; +type VariantType = 'danger' | 'warning' | 'info' | 'success' | 'upsell'; type GenericModalProps = RequiredModalProps & { variant?: VariantType; @@ -38,6 +38,7 @@ const getButtonProps = (variant: VariantType): ComponentProps => case 'danger': return { danger: true }; case 'warning': + case 'upsell': return { primary: true }; default: return {}; @@ -83,6 +84,8 @@ const GenericModal = ({ const dismissedRef = useRef(true); + const taglineColor = variant === 'upsell' ? 'annotation' : undefined; + const handleConfirm = useEffectEvent(() => { dismissedRef.current = false; onConfirm?.(); @@ -118,7 +121,7 @@ const GenericModal = ({ {renderIcon(icon, variant)} - {tagline && {tagline}} + {tagline && {tagline}} {title ?? t('Are_you_sure')} {onClose && } diff --git a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.spec.tsx b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.spec.tsx new file mode 100644 index 0000000000000..58daefb184689 --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.spec.tsx @@ -0,0 +1,70 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { composeStories } from '@storybook/react'; +import { render, screen } from '@testing-library/react'; +import { axe } from 'jest-axe'; + +import GenericUpsellModal from './GenericUpsellModal'; +import * as stories from './GenericUpsellModal.stories'; + +const appRoot = mockAppRoot() + .withTranslations('en', 'core', { + Premium_capability: 'Premium capability', + Cancel: 'Cancel', + Upgrade: 'Upgrade', + }) + .build(); + +describe('GenericUpsellModal', () => { + const defaultProps = { + title: 'Test Title', + img: 'test-image.png', + onClose: jest.fn(), + }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); + + test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const { baseElement } = render(); + expect(baseElement).toMatchSnapshot(); + }); + + test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); + + it('should render basic properties', () => { + const props = { + ...defaultProps, + subtitle: 'Test Subtitle', + description: 'Test Description', + onCancel: jest.fn(), + onConfirm: jest.fn(), + }; + render(, { wrapper: appRoot }); + + expect(screen.getByText('Test Title')).toBeInTheDocument(); + expect(screen.getByText('Test Subtitle')).toBeInTheDocument(); + expect(screen.getByText('Test Description')).toBeInTheDocument(); + }); + + it('should render with default confirm and cancel buttons', () => { + render(, { wrapper: appRoot }); + + expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Upgrade' })).toBeInTheDocument(); + }); + + it('should render with default tagline', () => { + render(, { wrapper: appRoot }); + + expect(screen.getByText('Premium capability')).toBeInTheDocument(); + }); +}); diff --git a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.stories.tsx b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.stories.tsx new file mode 100644 index 0000000000000..3a391ff2f714f --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.stories.tsx @@ -0,0 +1,52 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, StoryObj } from '@storybook/react'; + +import GenericUpsellModal from '.'; + +const meta = { + title: 'Components/GenericUpsellModal', + component: GenericUpsellModal, + args: { + onClose: action('onClose'), + img: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z', + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: 'This is the title', + subtitle: 'This is a subtitle', + description: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + onCancel: action('onCancel'), + onConfirm: action('onConfirm'), + }, +}; + +export const WithCustomTagLine: Story = { + args: { + ...Default.args, + tagline: 'Exclusive', + }, +}; + +export const WithCustomButtons: Story = { + args: { + ...Default.args, + cancelText: 'Learn More', + confirmText: 'Contact Sales', + }, +}; + +export const WithAnnotation: Story = { + args: { + ...Default.args, + annotation: 'This is an annotation.', + }, +}; diff --git a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx index b4b0ad23b1732..a6f4993cd074b 100644 --- a/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx +++ b/apps/meteor/client/components/GenericUpsellModal/GenericUpsellModal.tsx @@ -1,82 +1,40 @@ -import { Box, Button, Modal } from '@rocket.chat/fuselage'; -import type { Keys as IconName } from '@rocket.chat/icons'; -import type { ReactNode, ReactElement, ComponentProps } from 'react'; +import { Box, Modal } from '@rocket.chat/fuselage'; +import type { ReactElement, ComponentProps } from 'react'; import { useTranslation } from 'react-i18next'; -type GenericUpsellModalProps = { - children?: ReactNode; - tagline?: ReactNode; - cancelText?: ReactNode; - confirmText?: ReactNode; - title: string | ReactElement; +import GenericModal from '../GenericModal'; + +type GenericUpsellModalProps = Omit, 'variant' | 'children' | 'onClose' | 'onDismiss'> & { subtitle?: string | ReactElement; description?: string | ReactElement; - icon?: IconName; img: ComponentProps['src']; - onCancel?: () => void; onClose: () => void; onConfirm?: () => void; - annotation?: ReactNode; -} & ComponentProps; +}; -const GenericUpsellModal = ({ - tagline, - title, - subtitle, - img, - cancelText, - confirmText, - icon, - description, - onClose, - onCancel, - onConfirm, - annotation, - ...props -}: GenericUpsellModalProps) => { +const GenericUpsellModal = ({ tagline, subtitle, img, description, confirmText, icon = null, ...props }: GenericUpsellModalProps) => { const { t } = useTranslation(); return ( - - - {icon && } - - {tagline ?? t('Premium_capability')} - {title} - - - - - - {subtitle && ( - - {subtitle} - - )} - {description && ( - - {description} - - )} - - - {annotation && {annotation}} - {(onCancel || onConfirm) && ( - - {onCancel && ( - - )} - {onConfirm && ( - - )} - - )} - - + + + {subtitle && ( + + {subtitle} + + )} + {description && ( + + {description} + + )} + ); }; diff --git a/apps/meteor/client/components/GenericUpsellModal/__snapshots__/GenericUpsellModal.spec.tsx.snap b/apps/meteor/client/components/GenericUpsellModal/__snapshots__/GenericUpsellModal.spec.tsx.snap new file mode 100644 index 0000000000000..61a7ec051890c --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal/__snapshots__/GenericUpsellModal.spec.tsx.snap @@ -0,0 +1,446 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`GenericUpsellModal renders Default without crashing 1`] = ` + +
+ +
+
+
+
+
+ Premium capability +
+

+ This is the title +

+
+ +
+
+
+
+
+ +
+

+ This is a subtitle +

+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +
+
+
+ +
+
+
+ +`; + +exports[`GenericUpsellModal renders WithAnnotation without crashing 1`] = ` + +
+ +
+
+
+
+
+ Premium capability +
+

+ This is the title +

+
+ +
+
+
+
+
+ +
+

+ This is a subtitle +

+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +
+
+
+ +
+
+
+ +`; + +exports[`GenericUpsellModal renders WithCustomButtons without crashing 1`] = ` + +
+ +
+
+
+
+
+ Premium capability +
+

+ This is the title +

+
+ +
+
+
+
+
+ +
+

+ This is a subtitle +

+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +
+
+
+ +
+
+
+ +`; + +exports[`GenericUpsellModal renders WithCustomTagLine without crashing 1`] = ` + +
+ +
+
+
+
+
+ Exclusive +
+

+ This is the title +

+
+ +
+
+
+
+
+ +
+

+ This is a subtitle +

+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +
+
+
+ +
+
+
+ +`; diff --git a/apps/meteor/client/components/UpsellModal.stories.tsx b/apps/meteor/client/components/UpsellModal.stories.tsx deleted file mode 100644 index dfa5ea15b56a1..0000000000000 --- a/apps/meteor/client/components/UpsellModal.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import type { Meta, StoryFn } from '@storybook/react'; - -import GenericUpsellModal from './GenericUpsellModal'; - -export default { - title: 'Components/GenericUpsellModal', - component: GenericUpsellModal, -} satisfies Meta; - -export const Example: StoryFn = () => ( - -);