diff --git a/src/documentation/components/Organisms/Modals.tsx b/src/documentation/components/Organisms/Modals.tsx index 19cf917a5..3432f21f3 100644 --- a/src/documentation/components/Organisms/Modals.tsx +++ b/src/documentation/components/Organisms/Modals.tsx @@ -1,4 +1,4 @@ -import { useDisclosure } from '@chakra-ui/react' +import { Box, useDisclosure } from '@chakra-ui/react' import { BtnLink, BtnPrimary, BtnSecondary } from '@/molecules' import { @@ -6,7 +6,10 @@ import { ModalAlertNew, ModalAlertButtons, ModalButtons, + ModalCard, + ModalCardContent, ModalContent, + ModalSimple, } from '@/organisms/Modals/' import { ModalMultiple, ModalMultipleProps } from '@/organisms/Modals/ModalMultiple/ModalMultiple' import { useState } from 'react' @@ -302,6 +305,66 @@ export const ModalAlertDemo = ({ ) } +export const ModalSimpleDemo = (): JSX.Element => { + const { isOpen, onOpen, onClose } = useDisclosure() + + return ( + <> + ModalSimple + +

{text}

+
+ + ) +} + +export const ModalCardDemo = (): JSX.Element => { + const { isOpen, onOpen, onClose } = useDisclosure() + + return ( + <> + ModalCard + + + + + !Has ganado una nueva medalla! + + + Medal + + + Blast off + + + ¡Felicitaciones, has comenzado tu viaje de estudio! + + + Ver mis medallas + + + + + + Entendido + + + + + ) +} + export const ModalMultipleDemo = (): JSX.Element => { const { isOpen, onOpen, onClose } = useDisclosure() const [type, setType] = useState('modal') diff --git a/src/documentation/pages/Organisms/Modals.tsx b/src/documentation/pages/Organisms/Modals.tsx index ba5ab639c..77719b0d0 100644 --- a/src/documentation/pages/Organisms/Modals.tsx +++ b/src/documentation/pages/Organisms/Modals.tsx @@ -1,8 +1,10 @@ import { MyHeading, MyText, MyTitle, Code, ListComponent } from '@/documentation/components' import { ModalAlertDemo, + ModalCardDemo, ModalDemo, ModalMultipleDemo, + ModalSimpleDemo, } from '@/documentation/components/Organisms/Modals' export const ViewModals = (): JSX.Element => { @@ -52,6 +54,77 @@ export function View(){ ) +}`} + /> + Tipo ModalSimple + + Es una variante mínima del modal para casos donde solo necesitas contenido libre y la acción + de cierre. No renderiza la banda azul ni título, y mantiene la X como + control principal junto con las props base del modal. + + El componente se importa de la siguiente manera: + + + + + + Abrir ModalSimple + +

Contenido libre del modal...

+
+ + ) +}`} + /> + Tipo ModalCard + + Es una variante pensada para contenido tipo card, como logros o medallas. No renderiza + iconos ni encabezado por defecto, mantiene la X para cerrar y permite + contenido completamente libre. Su ancho está pensado para cards compactas. + + El componente se importa de la siguiente manera: + + + + + + Abrir ModalCard + + +
Contenido libre del card...
+
+ + + Entendido + + +
+ + ) }`} /> Variantes del tipo Modal diff --git a/src/index.ts b/src/index.ts index 4913aecc1..310caf1f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,9 @@ export { ModalContent, ModalAlertButtons, ModalMultiple, + ModalSimple, + ModalCard, + ModalCardContent, } from './organisms/Modals' export type { ModalMultipleProps } from './organisms/Modals' export { ModalAlert } from './organisms/ModalAlert' diff --git a/src/organisms/Modals/Modal/ModalCard.test.tsx b/src/organisms/Modals/Modal/ModalCard.test.tsx new file mode 100644 index 000000000..eb33b572c --- /dev/null +++ b/src/organisms/Modals/Modal/ModalCard.test.tsx @@ -0,0 +1,31 @@ +import { ChakraProvider } from '@chakra-ui/react' +import { render, screen } from '@testing-library/react' + +import { ModalCard } from './ModalCard' + +const renderWithChakra = (ui: React.ReactElement): any => { + return render({ui}) +} + +describe('ModalCard Component', () => { + it('renders children content', () => { + renderWithChakra( + +
Card content
+
+ ) + + expect(screen.getByText('Card content')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() + }) + + it('does not render a close button', () => { + renderWithChakra( + +
Card content
+
+ ) + + expect(screen.queryByLabelText('Close')).not.toBeInTheDocument() + }) +}) diff --git a/src/organisms/Modals/Modal/ModalCard.tsx b/src/organisms/Modals/Modal/ModalCard.tsx new file mode 100644 index 000000000..8089349f3 --- /dev/null +++ b/src/organisms/Modals/Modal/ModalCard.tsx @@ -0,0 +1,52 @@ +import { Modal as ChakraModal, ModalOverlay, ModalContent, Box } from '@chakra-ui/react' + +import { vars } from '@/theme' +import { IModalCard } from '../types' +import { useModalConfig } from './useModalConfig' + +export const ModalCard = ({ + children, + isOpen, + onClose, + closeOnOverlayClick = true, + closeOnEsc = true, +}: IModalCard): JSX.Element => { + const modalConfig = useModalConfig({ + closeOnOverlayClick, + scrollBehavior: 'outside', + fixedButtons: false, + withoutMargin: false, + }) + + return ( + + + + {children} + + + ) +} + +export const ModalCardContent = ({ children }: { children: React.ReactNode }): JSX.Element => { + return {children} +} diff --git a/src/organisms/Modals/Modal/ModalSimple.test.tsx b/src/organisms/Modals/Modal/ModalSimple.test.tsx new file mode 100644 index 000000000..7b34209e5 --- /dev/null +++ b/src/organisms/Modals/Modal/ModalSimple.test.tsx @@ -0,0 +1,61 @@ +import { ChakraProvider } from '@chakra-ui/react' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' + +import { ModalSimple } from './ModalSimple' + +const renderWithChakra = (ui: React.ReactElement): any => { + return render({ui}) +} + +describe('ModalSimple Component', () => { + it('renders children content', () => { + renderWithChakra( + +
Simple content
+
+ ) + + expect(screen.getByText('Simple content')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() + }) + + it('calls onClose when close button is clicked', async () => { + const user = userEvent.setup() + const onCloseMock = jest.fn() + + renderWithChakra( + +
Simple content
+
+ ) + + await user.click(screen.getByLabelText('Close')) + expect(onCloseMock).toHaveBeenCalledTimes(1) + }) + + it('does not call onClose when Escape key is pressed and closeOnEsc is false', async () => { + const user = userEvent.setup() + const onCloseMock = jest.fn() + + renderWithChakra( + +
Simple content
+
+ ) + + await user.keyboard('{Escape}') + expect(onCloseMock).not.toHaveBeenCalled() + }) + + it('removes inner spacing when withoutMargin is true', () => { + renderWithChakra( + +
Simple content
+
+ ) + + expect(screen.getByTestId('modal-simple-content')).toBeInTheDocument() + expect(screen.getByText('Simple content')).toBeInTheDocument() + }) +}) diff --git a/src/organisms/Modals/Modal/ModalSimple.tsx b/src/organisms/Modals/Modal/ModalSimple.tsx new file mode 100644 index 000000000..ace6b56da --- /dev/null +++ b/src/organisms/Modals/Modal/ModalSimple.tsx @@ -0,0 +1,70 @@ +import { + Modal as ChakraModal, + ModalOverlay, + ModalContent, + ModalCloseButton, + Box, +} from '@chakra-ui/react' + +import { vars } from '@/theme' +import { IModalSimple } from '../types' +import { useModalConfig } from './useModalConfig' + +export const ModalSimple = ({ + children, + isOpen, + onClose, + closeOnOverlayClick = true, + closeOnEsc, + withoutMargin = false, +}: IModalSimple): JSX.Element => { + const modalConfig = useModalConfig({ + closeOnOverlayClick, + scrollBehavior: 'outside', + fixedButtons: false, + withoutMargin, + }) + + return ( + + + + + + {children} + + + + ) +} diff --git a/src/organisms/Modals/index.ts b/src/organisms/Modals/index.ts index b6340e6ed..a21d327cf 100644 --- a/src/organisms/Modals/index.ts +++ b/src/organisms/Modals/index.ts @@ -1,4 +1,6 @@ export { Modal } from './Modal/Modal' +export { ModalCard, ModalCardContent } from './Modal/ModalCard' +export { ModalSimple } from './Modal/ModalSimple' export { ModalButtons, ModalContent } from './Modal/ModalButtons' export { ModalAlertNew, ModalAlertButtons } from './ModalAlert/ModalAlert' export { ModalMultiple } from './ModalMultiple/ModalMultiple' diff --git a/src/organisms/Modals/types.d.ts b/src/organisms/Modals/types.d.ts index 952bf808b..70784020f 100644 --- a/src/organisms/Modals/types.d.ts +++ b/src/organisms/Modals/types.d.ts @@ -16,6 +16,23 @@ export interface IModal { autoFocus?: boolean } +export interface IModalSimple { + children: React.ReactNode + closeOnEsc?: boolean + closeOnOverlayClick?: boolean + isOpen: boolean + onClose: () => void + withoutMargin?: boolean +} + +export interface IModalCard { + children: React.ReactNode + closeOnEsc?: boolean + closeOnOverlayClick?: boolean + isOpen: boolean + onClose: () => void +} + export interface IModalContentBase { children: React.ReactNode closeOnOverlayClick: IModal['closeOnOverlayClick']