Skip to content

Commit c2b0405

Browse files
renemadsenclaude
andcommitted
feat(calendar): preserve navigated week on view switch + chevron parity
When the user navigates forward in week view and then switches to "Tidsplan" (schedule) or "Dag" (day), currentDate previously reset to today — losing their navigation context. Now it snaps to Monday of the currently-viewed week instead, so day-view lands on that Monday and schedule-view shows the same week. Also fixes the chevron asymmetry: schedule view's chevron now advances 7 days per click (was 1 day), matching week view; day view stays at 1 day. Tooltips updated to match: only day view says "Previous/Next day"; week and schedule both say "Previous/Next week". Adds H1/H2/H3 Playwright tests in r/calendar-ui-enhancements covering the three behaviours, plus getCalendarHeaderDateText helper. Renames the now-misleading navigateScheduleByDays helper to navigateScheduleByWeeks and updates the F1/F2 call sites in calendar-resize. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0b5638f commit c2b0405

5 files changed

Lines changed: 135 additions & 23 deletions

File tree

eform-client/playwright/e2e/plugins/backend-configuration-pn/r/calendar-resize.spec.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,11 @@ test.describe.serial('Calendar event resize', () => {
442442
await createSimpleEvent(page, calendarPage, title, 6);
443443

444444
await calendarPage.switchToScheduleView();
445-
// Container resets currentDate to today on week→schedule swap
446-
// (calendar-container.component.ts:436). The chevron in schedule
447-
// view advances by 1 DAY per click, not 1 week — so we need 7
448-
// clicks to land in the same week as the event (week +1).
449-
await calendarPage.navigateScheduleByDays(7);
445+
// After the view-switch fix, the schedule view preserves the
446+
// navigated week (no longer snaps currentDate back to today) and
447+
// the chevron advances 7 days per click. Advance one week to
448+
// land in the same week as the event (week +1).
449+
await calendarPage.navigateScheduleByWeeks(1);
450450

451451
const row = calendarPage.findScheduleItem(title);
452452
await expect(row).toBeVisible();
@@ -469,9 +469,10 @@ test.describe.serial('Calendar event resize', () => {
469469
await createSimpleEvent(page, calendarPage, title, 6, 14);
470470

471471
await calendarPage.switchToScheduleView();
472-
// View swap resets the date to today; advance 7 days (1 chevron =
473-
// 1 day in schedule view) to land in week +1 where the event lives.
474-
await calendarPage.navigateScheduleByDays(7);
472+
// Schedule view preserves the navigated week post-fix and the
473+
// chevron advances 7 days per click. Advance one week to land
474+
// in week +1 where the event lives.
475+
await calendarPage.navigateScheduleByWeeks(1);
475476
await calendarPage.findScheduleItem(title).click();
476477
await page.locator('app-task-preview-modal').waitFor({ state: 'visible', timeout: 10000 });
477478

eform-client/playwright/e2e/plugins/backend-configuration-pn/r/calendar-ui-enhancements.page.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,20 @@ export class CalendarUiEnhancementsPage {
285285
}
286286
}
287287

288+
/**
289+
* Read the calendar-header's rendered date string (the `displayDate`
290+
* getter output from calendar-header.component.ts). Format is locale-
291+
* dependent and view-dependent:
292+
* - day / schedule: "{Weekday}, {D}. {Month} {YYYY}" (long form)
293+
* - week: "{Month} {YYYY}"
294+
* The text is rendered in `<span class="date-range">{{ displayDate }}</span>`
295+
* inside `app-calendar-header`. Returns the trimmed visible text.
296+
*/
297+
async getCalendarHeaderDateText(): Promise<string> {
298+
const span = this.page.locator('app-calendar-header .date-range').first();
299+
return ((await span.textContent()) ?? '').trim();
300+
}
301+
288302
// ----- Resize gesture ----------------------------------------------------
289303

290304
/**
@@ -503,14 +517,12 @@ export class CalendarUiEnhancementsPage {
503517
}
504518

505519
/**
506-
* Advance the schedule (list) / day view by `days` days. The chevron
507-
* step in non-week views is 1 day per click (see
508-
* calendar-container.component.ts:421), so a 1-week advance needs 7
509-
* clicks. Each click awaits the /tasks/week POST so navigation is
510-
* deterministic.
520+
* Click the chevron-right N times in schedule view; advances N weeks
521+
* (post the view-switch fix that made schedule chevron step 7 days).
522+
* Each click awaits the /tasks/week POST so navigation is deterministic.
511523
*/
512-
async navigateScheduleByDays(days: number): Promise<void> {
513-
for (let i = 0; i < days; i++) {
524+
async navigateScheduleByWeeks(weeks: number): Promise<void> {
525+
for (let i = 0; i < weeks; i++) {
514526
await this.navigateToNextWeek();
515527
}
516528
}

eform-client/playwright/e2e/plugins/backend-configuration-pn/r/calendar-ui-enhancements.spec.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,4 +544,100 @@ test.describe.serial('Calendar UI enhancements', () => {
544544
await calendarPage.assertHeaderStaysSticky('max');
545545
});
546546
});
547+
548+
// =======================================================================
549+
// H. View-switch preserves the navigated week (regression for the
550+
// currentDate-reset removal). Switching from week → day or week →
551+
// schedule used to snap currentDate back to today; it now snaps to
552+
// Monday of the currently-viewed week. Schedule-view chevron also
553+
// advances 7 days per click (was 1).
554+
// =======================================================================
555+
test.describe('Calendar — view-switch preserves navigated week', () => {
556+
// Test-side helpers (kept inline — test-only date math)
557+
// Local-tz Monday of the current week. Mirrors getMondayOfWeek in
558+
// calendar-container.component.ts so the tests reason in the same
559+
// timezone as the running app.
560+
function mondayOfThisWeekLocal(): Date {
561+
const d = new Date();
562+
d.setHours(0, 0, 0, 0);
563+
const dow = d.getDay(); // 0=Sun … 6=Sat
564+
const diff = dow === 0 ? -6 : 1 - dow;
565+
d.setDate(d.getDate() + diff);
566+
return d;
567+
}
568+
function addDays(d: Date, n: number): Date {
569+
const out = new Date(d);
570+
out.setDate(out.getDate() + n);
571+
return out;
572+
}
573+
// Mirror calendar-header.component.ts displayDate format for day/schedule views.
574+
// The Playwright runtime is da-DK (matches the dev environment).
575+
function formatLongDate(d: Date, locale = 'da-DK'): string {
576+
const formatted = d.toLocaleDateString(locale, {
577+
weekday: 'long', day: 'numeric', month: 'long', year: 'numeric',
578+
});
579+
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
580+
}
581+
582+
test('H1: schedule view preserves the navigated week (not today)', async ({ page }) => {
583+
const calendarPage = new CalendarUiEnhancementsPage(page);
584+
await page.locator('app-calendar-week-grid').waitFor({ state: 'visible', timeout: 10000 });
585+
586+
// Navigate two weeks forward in week view
587+
await calendarPage.navigateToNextWeek();
588+
await calendarPage.navigateToNextWeek();
589+
590+
// Switch to schedule (Tidsplan)
591+
await calendarPage.switchToScheduleView();
592+
593+
// Header should now show the Monday of (today's-week + 14d), in long format
594+
const expected = formatLongDate(addDays(mondayOfThisWeekLocal(), 14));
595+
const actual = await calendarPage.getCalendarHeaderDateText();
596+
expect(actual).toBe(expected);
597+
});
598+
599+
test('H2: day view lands on Monday of the navigated week', async ({ page }) => {
600+
const calendarPage = new CalendarUiEnhancementsPage(page);
601+
await page.locator('app-calendar-week-grid').waitFor({ state: 'visible', timeout: 10000 });
602+
603+
await calendarPage.navigateToNextWeek();
604+
await calendarPage.navigateToNextWeek();
605+
606+
await calendarPage.switchToDayView();
607+
608+
const expectedMonday = addDays(mondayOfThisWeekLocal(), 14);
609+
const expectedHeader = formatLongDate(expectedMonday);
610+
const actualHeader = await calendarPage.getCalendarHeaderDateText();
611+
expect(actualHeader).toBe(expectedHeader);
612+
613+
// The single day-grid column header should also show that Monday's date.
614+
// Selector skips the time-axis column. The grid label format is
615+
// "weekdayShort. dd/mm" (e.g. "man. 11/05"), so the dd/mm pair is a
616+
// tighter anchor than the bare day number — a neighbour-week off-by-one
617+
// would not collide.
618+
const dayCol = page
619+
.locator('app-calendar-week-grid .mat-mdc-header-cell:not(.mat-column-time-axis)')
620+
.first();
621+
const colText = (await dayCol.innerText()).trim();
622+
const dd = String(expectedMonday.getDate()).padStart(2, '0');
623+
const mm = String(expectedMonday.getMonth() + 1).padStart(2, '0');
624+
expect(colText).toContain(`${dd}/${mm}`);
625+
});
626+
627+
test('H3: schedule chevron advances one week per click', async ({ page }) => {
628+
const calendarPage = new CalendarUiEnhancementsPage(page);
629+
await page.locator('app-calendar-week-grid').waitFor({ state: 'visible', timeout: 10000 });
630+
631+
// Get into schedule view at this week's Monday (no navigation yet)
632+
await calendarPage.switchToScheduleView();
633+
const before = await calendarPage.getCalendarHeaderDateText();
634+
expect(before).toBe(formatLongDate(mondayOfThisWeekLocal()));
635+
636+
// One chevron click should advance 7 days (NOT 1 day, post-fix)
637+
await calendarPage.navigateToNextWeek();
638+
639+
const after = await calendarPage.getCalendarHeaderDateText();
640+
expect(after).toBe(formatLongDate(addDays(mondayOfThisWeekLocal(), 7)));
641+
});
642+
});
547643
});

eform-client/src/app/plugins/modules/backend-configuration-pn/modules/calendar/components/calendar-container/calendar-container.component.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,9 @@ export class CalendarContainerComponent implements OnInit, OnDestroy {
417417

418418
onNavigate(direction: -1 | 1) {
419419
const d = new Date(this.currentDate);
420-
// Week view shifts 7 days at a time; day and list views shift 1 day.
421-
const step = this.viewMode === 'week' ? 7 : 1;
420+
// Day view advances by one day; week and schedule (list) views both show a
421+
// full week of data, so the chevron advances by one full week in those.
422+
const step = this.viewMode === 'day' ? 1 : 7;
422423
d.setDate(d.getDate() + direction * step);
423424
this.stateService.updateCurrentDate(this.toLocalDateString(d));
424425
this.loadTasks();
@@ -430,11 +431,13 @@ export class CalendarContainerComponent implements OnInit, OnDestroy {
430431
}
431432

432433
onViewModeChange(viewMode: 'week' | 'day' | 'schedule') {
433-
// Switching into day or list view should always land on today, so users
434-
// don't find themselves in a single-day view of some unrelated week.
435-
// Week→week keeps the currently-viewed week.
434+
// Switching out of week view: snap currentDate to Monday of the
435+
// currently-viewed week so day-view lands on the first day of that
436+
// week and schedule-view shows that same week — preserving the
437+
// user's navigation context instead of jumping back to today.
436438
if (viewMode !== 'week' && this.viewMode === 'week') {
437-
this.stateService.updateCurrentDate(this.toLocalDateString(new Date()));
439+
const monday = this.getMondayOfWeek(new Date(this.currentDate));
440+
this.stateService.updateCurrentDate(this.toLocalDateString(monday));
438441
}
439442
this.stateService.updateViewMode(viewMode);
440443
this.loadTasks();

eform-client/src/app/plugins/modules/backend-configuration-pn/modules/calendar/components/calendar-header/calendar-header.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ export class CalendarHeaderComponent implements OnInit {
7070
}
7171

7272
get prevTooltipKey(): string {
73-
return this.viewMode === 'week' ? 'Previous week' : 'Previous day';
73+
return this.viewMode === 'day' ? 'Previous day' : 'Previous week';
7474
}
7575

7676
get nextTooltipKey(): string {
77-
return this.viewMode === 'week' ? 'Next week' : 'Next day';
77+
return this.viewMode === 'day' ? 'Next day' : 'Next week';
7878
}
7979

8080
private getMondayOfWeek(d: Date): Date {

0 commit comments

Comments
 (0)