Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion src/documentation/components/Organisms/Modals.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useDisclosure } from '@chakra-ui/react'
import { Box, useDisclosure } from '@chakra-ui/react'

import { BtnLink, BtnPrimary, BtnSecondary } from '@/molecules'
import {
Modal,
ModalAlertNew,
ModalAlertButtons,
ModalButtons,
ModalCard,
ModalCardContent,
ModalContent,
ModalSimple,
} from '@/organisms/Modals/'
import { ModalMultiple, ModalMultipleProps } from '@/organisms/Modals/ModalMultiple/ModalMultiple'
import { useState } from 'react'
Expand Down Expand Up @@ -302,6 +305,66 @@ export const ModalAlertDemo = ({
)
}

export const ModalSimpleDemo = (): JSX.Element => {
const { isOpen, onOpen, onClose } = useDisclosure()

return (
<>
<BtnPrimary onClick={onOpen}>ModalSimple</BtnPrimary>
<ModalSimple isOpen={isOpen} onClose={onClose}>
<p>{text}</p>
</ModalSimple>
</>
)
}

export const ModalCardDemo = (): JSX.Element => {
const { isOpen, onOpen, onClose } = useDisclosure()

return (
<>
<BtnPrimary onClick={onOpen}>ModalCard</BtnPrimary>
<ModalCard isOpen={isOpen} onClose={onClose}>
<ModalCardContent>
<Box display="flex" flexDirection="column" alignItems="center" textAlign="center">
<Box as="p" fontSize="20px" fontWeight={700} lineHeight="24px" mb="16px">
!Has ganado una nueva medalla!
</Box>
<Box
w="120px"
h="120px"
borderRadius="16px"
bg="#E0EEFA"
mb="16px"
display="flex"
alignItems="center"
justifyContent="center"
fontSize="14px"
fontWeight={700}
>
Medal
</Box>
<Box as="p" fontSize="16px" fontWeight={700} lineHeight="24px" mb="8px">
Blast off
</Box>
<Box as="p" fontSize="16px" lineHeight="24px" color="#808080" mb="16px">
¡Felicitaciones, has comenzado tu viaje de estudio!
</Box>
<BtnLink as="button" onClick={onClose}>
Ver mis medallas
</BtnLink>
</Box>
</ModalCardContent>
<ModalAlertButtons>
<BtnLink as="button" onClick={onClose}>
Entendido
</BtnLink>
</ModalAlertButtons>
</ModalCard>
</>
)
}

export const ModalMultipleDemo = (): JSX.Element => {
const { isOpen, onOpen, onClose } = useDisclosure()
const [type, setType] = useState<ModalMultipleProps['type']>('modal')
Expand Down
73 changes: 73 additions & 0 deletions src/documentation/pages/Organisms/Modals.tsx
Original file line number Diff line number Diff line change
@@ -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 => {
Expand Down Expand Up @@ -52,6 +54,77 @@ export function View(){
</ModalContent>
</Modal>
)
}`}
/>
<MyTitle>Tipo ModalSimple</MyTitle>
<MyText>
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 <strong>X</strong> como
control principal junto con las props base del modal.
</MyText>
<MyText>El componente se importa de la siguiente manera:</MyText>
<Code text="import { ModalSimple } from '@eclass/ui-kit'" />
<ListComponent>
<ModalSimpleDemo />
</ListComponent>
<Code
text={`
import { ModalSimple } from '@eclass/ui-kit'
import { useDisclosure } from '@chakra-ui/react'

export function View(){
const { isOpen, onOpen, onClose } = useDisclosure()

return (
<>
<BtnPrimary onClick={onOpen}>Abrir ModalSimple</BtnPrimary>
<ModalSimple
isOpen={isOpen}
onClose={onClose}
>
<p>Contenido libre del modal...</p>
</ModalSimple>
</>
)
}`}
/>
<MyTitle>Tipo ModalCard</MyTitle>
<MyText>
Es una variante pensada para contenido tipo card, como logros o medallas. No renderiza
iconos ni encabezado por defecto, mantiene la <strong>X</strong> para cerrar y permite
contenido completamente libre. Su ancho está pensado para cards compactas.
</MyText>
<MyText>El componente se importa de la siguiente manera:</MyText>
<Code text="import { ModalCard, ModalCardContent, ModalAlertButtons } from '@eclass/ui-kit'" />
<ListComponent>
<ModalCardDemo />
</ListComponent>
<Code
text={`
import { ModalCard, ModalCardContent, ModalAlertButtons, BtnLink } from '@eclass/ui-kit'
import { useDisclosure } from '@chakra-ui/react'

export function View(){
const { isOpen, onOpen, onClose } = useDisclosure()

return (
<>
<BtnPrimary onClick={onOpen}>Abrir ModalCard</BtnPrimary>
<ModalCard
isOpen={isOpen}
onClose={onClose}
>
<ModalCardContent>
<div>Contenido libre del card...</div>
</ModalCardContent>
<ModalAlertButtons>
<BtnLink as="button" onClick={onClose}>
Entendido
</BtnLink>
</ModalAlertButtons>
</ModalCard>
</>
)
}`}
/>
<MyTitle>Variantes del tipo Modal</MyTitle>
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
31 changes: 31 additions & 0 deletions src/organisms/Modals/Modal/ModalCard.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<ChakraProvider>{ui}</ChakraProvider>)
}

describe('ModalCard Component', () => {
it('renders children content', () => {
renderWithChakra(
<ModalCard isOpen onClose={jest.fn()}>
<div>Card content</div>
</ModalCard>
)

expect(screen.getByText('Card content')).toBeInTheDocument()
expect(screen.getByRole('dialog')).toBeInTheDocument()
})

it('does not render a close button', () => {
renderWithChakra(
<ModalCard isOpen onClose={jest.fn()}>
<div>Card content</div>
</ModalCard>
)

expect(screen.queryByLabelText('Close')).not.toBeInTheDocument()
})
})
52 changes: 52 additions & 0 deletions src/organisms/Modals/Modal/ModalCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<ChakraModal
closeOnOverlayClick={closeOnOverlayClick}
closeOnEsc={closeOnEsc}
isOpen={isOpen}
motionPreset="scale"
onClose={onClose}
scrollBehavior="outside"
blockScrollOnMount={false}
>
<ModalOverlay />
<ModalContent
{...modalConfig.contentProps}
maxW="355px"
minW="355px"
minH="auto"
overflow="hidden"
sx={{
...modalConfig.contentProps.sx,
bgColor: vars('colors-neutral-white'),
maxWidth: '355px',
}}
>
<Box>{children}</Box>
</ModalContent>
</ChakraModal>
)
}

export const ModalCardContent = ({ children }: { children: React.ReactNode }): JSX.Element => {
return <Box p="32px">{children}</Box>
}
61 changes: 61 additions & 0 deletions src/organisms/Modals/Modal/ModalSimple.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<ChakraProvider>{ui}</ChakraProvider>)
}

describe('ModalSimple Component', () => {
it('renders children content', () => {
renderWithChakra(
<ModalSimple isOpen onClose={jest.fn()}>
<div>Simple content</div>
</ModalSimple>
)

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(
<ModalSimple isOpen onClose={onCloseMock}>
<div>Simple content</div>
</ModalSimple>
)

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(
<ModalSimple isOpen onClose={onCloseMock} closeOnEsc={false}>
<div>Simple content</div>
</ModalSimple>
)

await user.keyboard('{Escape}')
expect(onCloseMock).not.toHaveBeenCalled()
})

it('removes inner spacing when withoutMargin is true', () => {
renderWithChakra(
<ModalSimple isOpen onClose={jest.fn()} withoutMargin>
<div>Simple content</div>
</ModalSimple>
)

expect(screen.getByTestId('modal-simple-content')).toBeInTheDocument()
expect(screen.getByText('Simple content')).toBeInTheDocument()
})
})
Loading
Loading