Jump to today, and open a period on the dates it actually has
The month can now be reached in one click instead of paging back through the year. The button sits left of the month dropdown and sizes to its label, so Idag and Today both fit. Opening a period showed the wish's first year rather than the occurrence that was clicked: a 2028 week of a yearly wish opened on 2026, and a flexible period opened on its bounding range instead of where the planner put it. The popover now prefills from the clicked band's own dates. Saving a yearly wish keeps its anchor year, since the edited dates describe the recurring pattern and not one occurrence — moving a single year is a separate choice that does not exist yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ei7gCZzdd751uZtPzb1Pep
This commit is contained in:
@@ -134,6 +134,22 @@ vi.mock('@/components/calendar/PeriodPopover', () => ({
|
||||
>
|
||||
save
|
||||
</button>
|
||||
<button
|
||||
data-testid="period-save-once"
|
||||
onClick={() =>
|
||||
void props.onSave({
|
||||
name: 'Edited name',
|
||||
startDate: props.startDate,
|
||||
endDate: props.endDate,
|
||||
parentMode: 'either',
|
||||
parentPersonalDataUuid: null,
|
||||
recurrence: 'once',
|
||||
lastYear: null,
|
||||
} satisfies PeriodPopoverSavePayload)
|
||||
}
|
||||
>
|
||||
save once
|
||||
</button>
|
||||
<button data-testid="period-delete" onClick={() => void props.onDelete()}>
|
||||
delete
|
||||
</button>
|
||||
@@ -309,6 +325,23 @@ describe('Calendar', () => {
|
||||
expect(yearSelect.value).toBe('2035')
|
||||
})
|
||||
|
||||
it('Today button jumps back to the current month/year after navigating away', () => {
|
||||
render(<Calendar />)
|
||||
const title = screen.getByRole('heading', { level: 2 })
|
||||
const monthSelect = title.querySelector('.calendar__month-select') as HTMLSelectElement
|
||||
const yearSelect = title.querySelector('.calendar__year-select') as HTMLSelectElement
|
||||
|
||||
fireEvent.change(monthSelect, { target: { value: '5' } })
|
||||
fireEvent.change(yearSelect, { target: { value: '2035' } })
|
||||
expect(yearSelect.value).toBe('2035')
|
||||
|
||||
fireEvent.click(screen.getByText('Idag'))
|
||||
|
||||
const today = new Date()
|
||||
expect(monthSelect.value).toBe(String(today.getMonth()))
|
||||
expect(yearSelect.value).toBe(String(today.getFullYear()))
|
||||
})
|
||||
|
||||
it('year dropdown is derived from the calendar window\'s minYear/maxYear', () => {
|
||||
vi.mocked(useCalendarDays).mockReturnValue(
|
||||
makeCalendarDaysResult({ data: makeCalendarWindow({ minYear: 2026, maxYear: 2055 }) }),
|
||||
@@ -640,6 +673,58 @@ describe('Calendar', () => {
|
||||
expect(screen.queryByTestId('period-popover')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('saving a later-year occurrence of a yearly wish preserves the original firstYear', async () => {
|
||||
// The wish was first taken in 2026 (the fixture default); the band
|
||||
// clicked here is its 2028 occurrence. Writing `firstYear: 2028` back
|
||||
// would silently drop the 2026/2027 occurrences — see the comment on
|
||||
// `handleSavePeriod` in Calendar.tsx.
|
||||
const wish = makeLeaveWish({ id: 'lw-1', firstYear: 2026, recurrence: 'yearly' })
|
||||
const update = vi.fn().mockResolvedValue(wish)
|
||||
const sync = vi.fn().mockResolvedValue(makePlanSyncResponse())
|
||||
vi.mocked(useSearchParams).mockReturnValue([new URLSearchParams('date=2028-06-15'), mockSetSearchParams])
|
||||
vi.mocked(useLeaveWishes).mockReturnValue(makeLeaveWishesResult({ data: [wish], update }))
|
||||
vi.mocked(usePlanSync).mockReturnValue(makePlanSyncResult({ sync }))
|
||||
vi.mocked(usePlannedDays).mockReturnValue(
|
||||
makePlannedDaysResult({
|
||||
data: [makePlannedLeaveDay({ id: 'pd-1', date: '2028-06-15', wishId: 'lw-1', parentPersonalDataUuid: 'person-1' })],
|
||||
}),
|
||||
)
|
||||
|
||||
render(<Calendar />)
|
||||
fireEvent.click(screen.getAllByTestId(/^calendar-band-/)[0]!)
|
||||
expect(screen.getByTestId('period-popover')).toHaveAttribute('data-start', '2028-06-15')
|
||||
fireEvent.click(screen.getByTestId('period-save'))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(update).toHaveBeenCalledWith('lw-1', expect.objectContaining({ firstYear: 2026 })),
|
||||
)
|
||||
})
|
||||
|
||||
it('switching a later-year occurrence to "once" uses that occurrence\'s year as the single firstYear', async () => {
|
||||
// Unlike the yearly case above, there is exactly one occurrence once
|
||||
// recurrence becomes 'once' — its year is meaningful and must be the
|
||||
// edited/clicked date's year, not the wish's original anchor year.
|
||||
const wish = makeLeaveWish({ id: 'lw-1', firstYear: 2026, recurrence: 'yearly' })
|
||||
const update = vi.fn().mockResolvedValue(wish)
|
||||
const sync = vi.fn().mockResolvedValue(makePlanSyncResponse())
|
||||
vi.mocked(useSearchParams).mockReturnValue([new URLSearchParams('date=2028-06-15'), mockSetSearchParams])
|
||||
vi.mocked(useLeaveWishes).mockReturnValue(makeLeaveWishesResult({ data: [wish], update }))
|
||||
vi.mocked(usePlanSync).mockReturnValue(makePlanSyncResult({ sync }))
|
||||
vi.mocked(usePlannedDays).mockReturnValue(
|
||||
makePlannedDaysResult({
|
||||
data: [makePlannedLeaveDay({ id: 'pd-1', date: '2028-06-15', wishId: 'lw-1', parentPersonalDataUuid: 'person-1' })],
|
||||
}),
|
||||
)
|
||||
|
||||
render(<Calendar />)
|
||||
fireEvent.click(screen.getAllByTestId(/^calendar-band-/)[0]!)
|
||||
fireEvent.click(screen.getByTestId('period-save-once'))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(update).toHaveBeenCalledWith('lw-1', expect.objectContaining({ firstYear: 2028, recurrence: 'once' })),
|
||||
)
|
||||
})
|
||||
|
||||
it('deleting a wish-linked period removes the wish and re-syncs', async () => {
|
||||
const today = todayDateString()
|
||||
const wish = makeLeaveWish({ id: 'lw-1' })
|
||||
|
||||
Reference in New Issue
Block a user