Skip to content
Open
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
23 changes: 23 additions & 0 deletions src/documentation/pages/Organisms/CourseList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { CourseList } from '@/organisms'
import { dataFake } from '@/organisms/CourseList/utils'

export const ViewCourseList = (): JSX.Element => {
const courseWithCustomClick = {
...dataFake[18],
onClick: (selectedCourse: typeof dataFake[number]) => console.log(selectedCourse),
}

return (
<>
<MyHeading>CourseList</MyHeading>
Expand All @@ -21,6 +26,24 @@ export const ViewCourseList = (): JSX.Element => {
/>
<CourseList courses={[dataFake[18]]} />

<MyTitle>Acción personalizada al seleccionar una caja</MyTitle>
<MyText>
Cada curso puede incluir <code>onClick</code> para ejecutar una acción personalizada al
seleccionar su caja. Cuando se define, recibe el objeto completo del curso y reemplaza la
redirección configurada en <code>action.href</code>.
</MyText>
<Code
text={`const courses = [
{
...course,
onClick: (selectedCourse) => {
console.log(selectedCourse)
},
},
]`}
/>
<CourseList courses={[courseWithCustomClick]} typeBox="TRADITIONAL" />

<MyTitle>Tipos de Caja curso</MyTitle>
<MyText>
Actualmente existen tres formatos en que se muestran las cajas. El tipo que se define es a
Expand Down
34 changes: 29 additions & 5 deletions src/organisms/CourseList/Boxes/BoxImage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as React from 'react'
import { Box, LinkBox, LinkOverlay } from '@chakra-ui/react'

import { vars } from '@theme'
Expand Down Expand Up @@ -29,12 +30,29 @@ export function BoxImage({
size = 'large',
m,
}: ImageBoxProps): JSX.Element {
const hasCustomClick = data?.onClick !== undefined
const isClickable =
hasCustomClick || isCourseActive(data?.action?.enabled ?? false, data?.Profile?.id)
const hasHref = !hasCustomClick && !!data?.action?.href

const handleClick = (event: React.MouseEvent<HTMLElement>): void => {
event.preventDefault()
data?.onClick?.(data)
}

const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
data?.onClick?.(data)
}
}

const boxHeight = {
large: '286px',
small: '197px',
}
return (
<WithRipples enabled={isCourseActive(data?.action?.enabled ?? false, data?.Profile?.id)}>
<WithRipples enabled={isClickable}>
<LinkBox
className="CourseList-ImageBox"
_focusVisible={{
Expand Down Expand Up @@ -63,12 +81,18 @@ export function BoxImage({
},
},
}}
aria-label={hasCustomClick ? title : undefined}
role={hasCustomClick ? 'button' : undefined}
onKeyDown={hasCustomClick ? handleKeyDown : undefined}
>
{!data?.hasFinanzeFreezed &&
isCourseActive(data?.action?.enabled ?? false, data?.Profile?.id) && (
{(hasCustomClick || !data?.hasFinanzeFreezed) &&
isClickable &&
(hasHref || hasCustomClick) && (
<LinkOverlay
href={data?.action?.href}
isExternal={data?.action?.targetBlank}
data-testid="course-link-overlay"
href={hasHref ? data?.action?.href : undefined}
isExternal={hasHref && data?.action?.targetBlank}
onClick={hasCustomClick ? handleClick : undefined}
tabIndex={-1}
/>
)}
Expand Down
29 changes: 23 additions & 6 deletions src/organisms/CourseList/Boxes/BoxTraditional.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,22 @@ interface IBoxTraditional {
}

export function BoxTraditional({ data, modalPaymentText }: IBoxTraditional): JSX.Element {
const isClickable = isCourseActive(data.action?.enabled ?? false, data.Profile?.id)
const hasHref = !!data.action?.href
const hasCustomClick = data.onClick !== undefined
const isClickable =
hasCustomClick || isCourseActive(data.action?.enabled ?? false, data.Profile?.id)
const hasHref = !hasCustomClick && !!data.action?.href

const handleClick = (event: React.MouseEvent<HTMLElement>): void => {
event.preventDefault()
data.onClick?.(data)
}

const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>): void => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
data.onClick?.(data)
}
}

useEnterNavigate()

Expand All @@ -47,18 +61,21 @@ export function BoxTraditional({ data, modalPaymentText }: IBoxTraditional): JSX
boxShadow: `0 0 0 3px ${vars('colors-alert-deepSkyBlue')} inset`,
}}
tabIndex={0}
role={hasHref ? 'link' : undefined}
role={hasHref ? 'link' : hasCustomClick ? 'button' : undefined}
data-href={hasHref ? data.action?.href : undefined}
onKeyDown={hasCustomClick ? handleKeyDown : undefined}
>
<WithRipples enabled={isClickable}>
<Flex direction="column" justify="space-between" h="100%">
<Box className="CourseList-TraditionalBox">
{isClickable && hasHref && (
{isClickable && (hasHref || hasCustomClick) && (
<LinkOverlay
className="course-link-overlay"
data-testid="course-link-overlay"
bg="gray"
href={data.action?.href}
isExternal={data.action?.targetBlank}
href={hasHref ? data.action?.href : undefined}
isExternal={hasHref && data.action?.targetBlank}
onClick={hasCustomClick ? handleClick : undefined}
tabIndex={-1}
/>
)}
Expand Down
50 changes: 50 additions & 0 deletions src/organisms/CourseList/CourseList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ChakraProvider } from '@chakra-ui/react'
import { fireEvent, render, screen } from '@testing-library/react'

import { CourseList } from './CourseList'
import { ExtendAcademicList, WrapperCoursesProps } from './types'
import { dataFake } from './utils'

const course = dataFake[0] as ExtendAcademicList

const renderCourseList = (
courseData: ExtendAcademicList,
typeBox: WrapperCoursesProps['typeBox'] = 'TRADITIONAL'
): ReturnType<typeof render> =>
render(
<ChakraProvider>
<CourseList courses={[courseData]} typeBox={typeBox} />
</ChakraProvider>
)

describe.each(['TRADITIONAL', 'IMAGE_LARGE'] as const)('CourseList %s', (typeBox) => {
it('calls the data onClick from the overlay with the full course and does not redirect', () => {
const onClick = jest.fn()
const courseWithOnClick = { ...course, onClick }
renderCourseList(courseWithOnClick, typeBox)

const overlay = screen.getByTestId('course-link-overlay')

expect(screen.queryByRole('link')).not.toBeInTheDocument()
expect(overlay).not.toHaveAttribute('href')
expect(fireEvent.click(overlay)).toBe(false)
expect(onClick).toHaveBeenCalledTimes(1)
expect(onClick).toHaveBeenCalledWith(courseWithOnClick)
})

it('keeps the redirect href when the course does not provide onClick', () => {
renderCourseList(course, typeBox)

expect(screen.getByTestId('course-link-overlay')).toHaveAttribute('href', course.action?.href)
})

it('calls the custom action when the selectable box is activated with Enter', () => {
const onClick = jest.fn()
const courseWithOnClick = { ...course, onClick }
renderCourseList(courseWithOnClick, typeBox)

fireEvent.keyDown(screen.getByRole('button'), { key: 'Enter' })

expect(onClick).toHaveBeenCalledWith(courseWithOnClick)
})
})
10 changes: 9 additions & 1 deletion src/organisms/CourseList/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { AcademicBox } from '@eclass/api'

export type ExtendAcademicList = AcademicBox & {
export type CourseClickPayload = AcademicBox & {
soonCourse?: {
show?: true
text?: string
}
}

export type ExtendAcademicList = CourseClickPayload & {
/**
* Ejecuta una acción personalizada al seleccionar la caja en lugar de navegar a `action.href`.
* Recibe el objeto completo que se utilizó para renderizar la caja.
*/
onClick?: (course: CourseClickPayload) => void
}

interface PaymentText {
title: string
body: string
Expand Down
Loading