From 8bc26cf966d8681fd9da7aabcd194b23114821d5 Mon Sep 17 00:00:00 2001 From: Eduard Predescu Date: Wed, 19 Feb 2025 18:42:25 +0200 Subject: [PATCH 1/2] fix(Calendar): properly calculate focus on paged navigation false --- .../src/Calendar/CalendarCellTrigger.vue | 18 ++++++++++++++- .../RangeCalendarCellTrigger.vue | 22 +++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/radix-vue/src/Calendar/CalendarCellTrigger.vue b/packages/radix-vue/src/Calendar/CalendarCellTrigger.vue index 71d9f0ba9..440a81678 100644 --- a/packages/radix-vue/src/Calendar/CalendarCellTrigger.vue +++ b/packages/radix-vue/src/Calendar/CalendarCellTrigger.vue @@ -9,7 +9,7 @@ import { } from '@internationalized/date' import { computed, nextTick } from 'vue' import { useKbd } from '@/shared' -import { toDate } from '@/date' +import { getDaysInMonth, toDate } from '@/date' export interface CalendarCellTriggerProps extends PrimitiveProps { /** The date value provided to the cell trigger */ @@ -130,6 +130,14 @@ function handleArrowKey(e: KeyboardEvent) { const newCollectionItems: HTMLElement[] = parentElement ? Array.from(parentElement.querySelectorAll(SELECTOR)) : [] + if (!rootContext.pagedNavigation.value) { + // Placeholder is set to first month of the new page + const numberOfDays = getDaysInMonth(rootContext.placeholder.value) + newCollectionItems[ + numberOfDays - Math.abs(newIndex) + ].focus() + return + } newCollectionItems[ newCollectionItems.length - Math.abs(newIndex) ].focus() @@ -145,6 +153,14 @@ function handleArrowKey(e: KeyboardEvent) { const newCollectionItems: HTMLElement[] = parentElement ? Array.from(parentElement.querySelectorAll(SELECTOR)) : [] + + if (!rootContext.pagedNavigation.value) { + // Placeholder is set to first month of the new page + const numberOfDays = getDaysInMonth(rootContext.placeholder.value.add({ months: rootContext.numberOfMonths.value - 1 })) + newCollectionItems[newCollectionItems.length - numberOfDays + newIndex - allCollectionItems.length].focus() + return + } + newCollectionItems[newIndex - allCollectionItems.length].focus() }) } diff --git a/packages/radix-vue/src/RangeCalendar/RangeCalendarCellTrigger.vue b/packages/radix-vue/src/RangeCalendar/RangeCalendarCellTrigger.vue index 8411d823b..e15916f8c 100644 --- a/packages/radix-vue/src/RangeCalendar/RangeCalendarCellTrigger.vue +++ b/packages/radix-vue/src/RangeCalendar/RangeCalendarCellTrigger.vue @@ -9,7 +9,7 @@ import { } from '@internationalized/date' import { computed, nextTick } from 'vue' import { useKbd } from '@/shared' -import { isBetweenInclusive, toDate } from '@/date' +import { getDaysInMonth, isBetweenInclusive, toDate } from '@/date' export interface RangeCalendarCellTriggerProps extends PrimitiveProps { day: DateValue @@ -169,7 +169,17 @@ function handleArrowKey(e: KeyboardEvent) { const newCollectionItems: HTMLElement[] = parentElement ? Array.from(parentElement.querySelectorAll(SELECTOR)) : [] - newCollectionItems[newCollectionItems.length - Math.abs(newIndex)].focus() + if (!rootContext.pagedNavigation.value) { + // Placeholder is set to first month of the new page + const numberOfDays = getDaysInMonth(rootContext.placeholder.value) + newCollectionItems[ + numberOfDays - Math.abs(newIndex) + ].focus() + return + } + newCollectionItems[ + newCollectionItems.length - Math.abs(newIndex) + ].focus() }) return } @@ -182,6 +192,14 @@ function handleArrowKey(e: KeyboardEvent) { const newCollectionItems: HTMLElement[] = parentElement ? Array.from(parentElement.querySelectorAll(SELECTOR)) : [] + + if (!rootContext.pagedNavigation.value) { + // Placeholder is set to first month of the new page + const numberOfDays = getDaysInMonth(rootContext.placeholder.value.add({ months: rootContext.numberOfMonths.value - 1 })) + newCollectionItems[newCollectionItems.length - numberOfDays + newIndex - allCollectionItems.length].focus() + return + } + newCollectionItems[newIndex - allCollectionItems.length].focus() }) } From 074eee52a27b853dc1c9dcf333f72a71041c5c9c Mon Sep 17 00:00:00 2001 From: zernonia Date: Thu, 20 Feb 2025 11:08:08 +0800 Subject: [PATCH 2/2] test: include test for numberOfMonths --- .../radix-vue/src/Calendar/Calendar.test.ts | 40 +++++++++ .../src/RangeCalendar/RangeCalendar.test.ts | 83 +++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/packages/radix-vue/src/Calendar/Calendar.test.ts b/packages/radix-vue/src/Calendar/Calendar.test.ts index 66cc4b795..9a4272249 100644 --- a/packages/radix-vue/src/Calendar/Calendar.test.ts +++ b/packages/radix-vue/src/Calendar/Calendar.test.ts @@ -241,6 +241,46 @@ describe('calendar', async () => { expect(firstMonthDay).not.toBeInTheDocument() }) + it('properly handles multiple months correctly', async () => { + const { getByTestId, calendar, user } = setup({ + calendarProps: { + modelValue: calendarDateTime, + numberOfMonths: 2, + }, + }) + + const selectedDay = getSelectedDay(calendar) + expect(selectedDay).toHaveTextContent(String(calendarDateTime.day)) + + const heading = getByTestId('heading') + expect(heading).toHaveTextContent('January - February 1980') + + const firstMonthDayDateStr = calendarDateTime.set({ day: 12 }).toString() + const firstMonthDay = getByTestId('date-1-12') + expect(firstMonthDay).toHaveTextContent('12') + expect(firstMonthDay).toHaveAttribute('data-value', firstMonthDayDateStr) + + const secondMonthDay = getByTestId('date-2-15') + const secondMonthDayDateStr = calendarDateTime.set({ day: 15, month: 2 }).toString() + expect(secondMonthDay).toHaveTextContent('15') + expect(secondMonthDay).toHaveAttribute('data-value', secondMonthDayDateStr) + + const prevButton = getByTestId('prev-button') + const nextButton = getByTestId('next-button') + + await user.click(nextButton) + expect(heading).toHaveTextContent('February - March 1980') + expect(firstMonthDay).not.toBeInTheDocument() + + await user.click(prevButton) + expect(heading).toHaveTextContent('January - February 1980') + expect(firstMonthDay).not.toBeInTheDocument() + + await user.click(prevButton) + expect(heading).toHaveTextContent('December 1979 - January 1980') + expect(firstMonthDay).not.toBeInTheDocument() + }) + it('properly handles `pagedNavigation` with multiple months', async () => { const { getByTestId, calendar, user } = setup({ calendarProps: { diff --git a/packages/radix-vue/src/RangeCalendar/RangeCalendar.test.ts b/packages/radix-vue/src/RangeCalendar/RangeCalendar.test.ts index e6ae938dc..88c26cbd5 100644 --- a/packages/radix-vue/src/RangeCalendar/RangeCalendar.test.ts +++ b/packages/radix-vue/src/RangeCalendar/RangeCalendar.test.ts @@ -426,4 +426,87 @@ describe('rangeCalendar', () => { expect(weekdayEl).toHaveTextContent(weekday) } }) + + it('properly handles multiple months correctly', async () => { + const { getByTestId, calendar, user } = setup({ + calendarProps: { + modelValue: calendarDateRange, + numberOfMonths: 2, + }, + }) + + const selectedDays = getSelectedDays(calendar) + expect(selectedDays.at(0)).toHaveTextContent(String(calendarDateRange.start.day)) + expect(selectedDays.at(-1)).toHaveTextContent(String(calendarDateRange.end.day)) + + const heading = getByTestId('heading') + expect(heading).toHaveTextContent('January - February 1980') + + const firstMonthDayDateStr = calendarDateRange.start.set({ day: 12 }).toString() + const firstMonthDay = getByTestId('date-1-12') + expect(firstMonthDay).toHaveTextContent('12') + expect(firstMonthDay).toHaveAttribute('data-value', firstMonthDayDateStr) + + const secondMonthDay = getByTestId('date-2-15') + const secondMonthDayDateStr = calendarDateRange.start.set({ day: 15, month: 2 }).toString() + expect(secondMonthDay).toHaveTextContent('15') + expect(secondMonthDay).toHaveAttribute('data-value', secondMonthDayDateStr) + + const prevButton = getByTestId('prev-button') + const nextButton = getByTestId('next-button') + + await user.click(nextButton) + expect(heading).toHaveTextContent('February - March 1980') + expect(firstMonthDay).not.toBeInTheDocument() + + await user.click(prevButton) + expect(heading).toHaveTextContent('January - February 1980') + expect(firstMonthDay).not.toBeInTheDocument() + + await user.click(prevButton) + expect(heading).toHaveTextContent('December 1979 - January 1980') + expect(firstMonthDay).not.toBeInTheDocument() + }) + + it('properly handles `pagedNavigation` with multiple months', async () => { + const { getByTestId, calendar, user } = setup({ + calendarProps: { + modelValue: calendarDateRange, + numberOfMonths: 2, + pagedNavigation: true, + }, + }) + + const selectedDays = getSelectedDays(calendar) + expect(selectedDays.at(0)).toHaveTextContent(String(calendarDateRange.start.day)) + expect(selectedDays.at(-1)).toHaveTextContent(String(calendarDateRange.end.day)) + + const heading = getByTestId('heading') + expect(heading).toHaveTextContent('January - February 1980') + + const firstMonthDayDateStr = calendarDateRange.start.set({ day: 12 }).toString() + const firstMonthDay = getByTestId('date-1-12') + expect(firstMonthDay).toHaveTextContent('12') + expect(firstMonthDay).toHaveAttribute('data-value', firstMonthDayDateStr) + + const secondMonthDay = getByTestId('date-2-15') + const secondMonthDayDateStr = calendarDateRange.start.set({ day: 15, month: 2 }).toString() + expect(secondMonthDay).toHaveTextContent('15') + expect(secondMonthDay).toHaveAttribute('data-value', secondMonthDayDateStr) + + const prevButton = getByTestId('prev-button') + const nextButton = getByTestId('next-button') + + await user.click(nextButton) + expect(heading).toHaveTextContent('March - April 1980') + expect(firstMonthDay).not.toBeInTheDocument() + + await user.click(prevButton) + expect(heading).toHaveTextContent('January - February 1980') + expect(firstMonthDay).not.toBeInTheDocument() + + await user.click(prevButton) + expect(heading).toHaveTextContent('November - December 1979') + expect(firstMonthDay).not.toBeInTheDocument() + }) })