#679 day view without sidebar on mobile - look only

This commit is contained in:
lethemanh
2026-04-06 09:34:43 +07:00
parent fbb31e4a80
commit aa96a39b28
21 changed files with 664 additions and 117 deletions
@@ -0,0 +1,180 @@
import { CalendarApi } from '@fullcalendar/core'
import {
act,
render,
screen,
fireEvent,
waitFor,
RenderResult
} from '@testing-library/react'
import React from 'react'
import { DatePickerMobile } from '@/components/Menubar/components/DatePickerMobile'
jest.mock('twake-i18n', () => ({
useI18n: (): { t: (key: string) => string; lang: string } => ({
t: (key: string) => key,
lang: 'en'
})
}))
jest.mock('@linagora/twake-mui', () => {
const actual = jest.requireActual<typeof import('@linagora/twake-mui')>(
'@linagora/twake-mui'
)
return {
...actual,
useTheme: (): { palette: { grey: { 500: string } } } => ({
palette: { grey: { 500: '#9e9e9e' } }
})
}
})
const scrollIntoViewMock = jest.fn()
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView
beforeAll(() => {
window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock
})
afterAll(() => {
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView
})
function makeCalendarApi(initialDate: Date): CalendarApi & {
getDate: jest.Mock
gotoDate: jest.Mock
} {
let _date = initialDate
const mockApi = {
getDate: jest.fn(() => _date),
gotoDate: jest.fn((d: Date) => {
_date = d
})
}
return mockApi as unknown as CalendarApi & typeof mockApi
}
describe('DatePickerMobile', () => {
beforeEach(() => {
jest.clearAllMocks()
scrollIntoViewMock.mockClear()
})
const setup = async (
initialDate: Date
): Promise<
RenderResult & { calendarApi: CalendarApi; onDateChange: jest.Mock }
> => {
const calendarApi = makeCalendarApi(initialDate)
const onDateChange = jest.fn()
const renderResult = await act(() =>
render(
<DatePickerMobile
calendarRef={{ current: calendarApi }}
currentDate={initialDate}
onDateChange={onDateChange}
/>
)
)
return { ...renderResult, calendarApi, onDateChange }
}
const expectNavigatedDate = (
calendarApi: CalendarApi,
expected: { day: number; month: number; year: number }
): void => {
// eslint-disable-next-line @typescript-eslint/unbound-method
const gotoDateMock = calendarApi.gotoDate as jest.Mock<void, [Date]>
const navigatedDate: Date = gotoDateMock.mock.calls[0][0]
expect(navigatedDate.getDate()).toBe(expected.day)
expect(navigatedDate.getMonth()).toBe(expected.month)
expect(navigatedDate.getFullYear()).toBe(expected.year)
}
const testMonthNavigation = async (
initialDate: Date,
monthToClick: number,
expected: { day: number; month: number; year: number }
): Promise<void> => {
const { calendarApi } = await setup(initialDate)
const monthButton = screen.getByText(`months.short.${monthToClick}`)
act(() => {
fireEvent.click(monthButton)
})
await waitFor(() => {
// eslint-disable-next-line @typescript-eslint/unbound-method
expect(calendarApi.gotoDate).toHaveBeenCalled()
})
expectNavigatedDate(calendarApi, expected)
}
it('calls calendarApi.gotoDate and onDateChange with the selected date', async () => {
const initialDate = new Date(2025, 5, 15) // June 15, 2025
const { calendarApi, onDateChange } = await setup(initialDate)
const day20 = screen.getByRole('gridcell', { name: '20' })
act(() => {
fireEvent.click(day20)
})
await waitFor(() => {
// eslint-disable-next-line @typescript-eslint/unbound-method
expect(calendarApi.gotoDate).toHaveBeenCalled()
})
expectNavigatedDate(calendarApi, { day: 20, month: 5, year: 2025 })
// eslint-disable-next-line @typescript-eslint/unbound-method
const gotoDateMock = calendarApi.gotoDate as jest.Mock<void, [Date]>
expect(onDateChange).toHaveBeenCalledWith(gotoDateMock.mock.calls[0][0])
})
it('navigates to the clicked month while keeping the current day', async () => {
await testMonthNavigation(
new Date(2025, 5, 15), // June 15, 2025
8,
{ day: 15, month: 8, year: 2025 }
)
})
it('clamps the day to the last valid day when switching to a shorter month', async () => {
await testMonthNavigation(
new Date(2025, 0, 31), // Jan 31, 2025
1,
{ day: 28, month: 1, year: 2025 }
)
})
it('scrollIntoView is called on the correct month button when currentDate changes to another month', async () => {
const initialDate = new Date(2025, 5, 15) // June 15, 2025 (month index 5)
const { rerender, calendarApi, onDateChange } = await setup(initialDate)
// Simulate parent updating currentDate to August (e.g. user picked a day in August)
const newDate = new Date(2025, 7, 5) // August 5, 2025 (month index 7)
act(() => {
rerender(
<DatePickerMobile
calendarRef={{ current: calendarApi }}
currentDate={newDate}
onDateChange={onDateChange}
/>
)
})
await waitFor(() => {
// scrollIntoView should have been called at least once (initial mount + rerender)
expect(scrollIntoViewMock).toHaveBeenCalledWith({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
})
})
// The last call should correspond to August (index 7) button being highlighted
const augustButton = screen.getByText('months.short.7')
expect(augustButton).toBeInTheDocument()
})
})