Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 15 additions & 6 deletions src/components/calendar-picker-view/calendar-picker-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,18 @@ export const CalendarPickerView = forwardRef<
)

// ================================ Refs ================================
const jumpToPage = (page: Page) => {
const next = convertPageToDayjs(page)
if (!props.min && next.isBefore(defaultMin)) {
setDefaultMin(next.date(1))
}
if (!props.max && next.isAfter(defaultMax)) {
setDefaultMax(next.endOf('month'))
}
setCurrent(next)
scrollTo(next)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment on lines +178 to +191
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The jumpToPage function has two issues:

  1. Boundary Clamping: If props.min or props.max are provided, the rendering range is fixed. If the target page is outside these bounds, setCurrent(next) will set an unreachable month, and scrollTo(next) will fail because the corresponding DOM element won't be rendered. The target date should be clamped to the allowed range defined by minDay and maxDay.
  2. Dayjs Rollover Bug: The convertPageToDayjs utility (used here) has a potential bug where it can roll over to the wrong month if today is the 31st (e.g., calling it for February on January 31st might result in March). It's safer to create the dayjs object by setting the date to the 1st before setting the year and month.

Additionally, next.date(1) is redundant if the date is already set to the 1st.

  const jumpToPage = (page: Page) => {
    // Use a safe way to create the dayjs object to avoid rollover issues when today is the 31st
    let next = dayjs().date(1).year(page.year).month(page.month - 1)

    if (next.isBefore(minDay, 'month')) {
      if (props.min) {
        next = minDay.date(1)
      } else {
        setDefaultMin(next)
      }
    } else if (next.isAfter(maxDay, 'month')) {
      if (props.max) {
        next = maxDay.date(1)
      } else {
        setDefaultMax(next.endOf('month'))
      }
    }

    setCurrent(next)
    scrollTo(next)
  }


useImperativeHandle(ref, () => ({
jumpTo: pageOrPageGenerator => {
let page: Page
Expand All @@ -173,14 +185,11 @@ export const CalendarPickerView = forwardRef<
} else {
page = pageOrPageGenerator
}
const next = convertPageToDayjs(page)
setCurrent(next)
scrollTo(next)
jumpToPage(page)
},
jumpToToday: () => {
const next = dayjs().date(1)
setCurrent(next)
scrollTo(next)
const today = dayjs()
jumpToPage({ year: today.year(), month: today.month() + 1 })
},
getDateRange: () => dateRange,
}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,44 @@ describe('Calendar', () => {
expect(container.querySelectorAll(`.${classPrefix}-cell`)).toHaveLength(30)
})

test('jumpTo expands defaultMin/defaultMax when no min/max set', () => {
const App = () => {
const ref = useRef<CalendarPickerViewRef>(null)
return (
<>
<button
onClick={() => {
ref.current?.jumpTo({ year: 2021, month: 1 })
}}
>
jumpTo
</button>
<button
onClick={() => {
ref.current?.jumpTo({ year: 2026, month: 12 })
}}
>
jumpToFuture
</button>
<CalendarPickerView ref={ref} selectionMode='single' />
</>
)
}
const { container, getByText } = render(<App />)

// defaultMin starts at today (2023-05), jumpTo 2021-01 should expand rendering range
fireEvent.click(getByText('jumpTo'))
expect(
container.querySelector('[data-year-month="2021-1"]')
).toBeInTheDocument()

// jumpToFuture 2026-12 should also expand defaultMax
fireEvent.click(getByText('jumpToFuture'))
expect(
container.querySelector('[data-year-month="2026-12"]')
).toBeInTheDocument()
})

test('auto expand month list', () => {
const { container, rerender } = render(
<CalendarPickerView value={new Date(2024, 9, 1)} selectionMode='single' />
Expand Down
Loading