Skip to content

[WC-3492]: fix(calendar-web): apply custom Header day format to column headers#2312

Open
rahmanunver wants to merge 3 commits into
mainfrom
fix/calendar-custom-header-day-format
Open

[WC-3492]: fix(calendar-web): apply custom Header day format to column headers#2312
rahmanunver wants to merge 3 commits into
mainfrom
fix/calendar-custom-header-day-format

Conversation

@rahmanunver

Copy link
Copy Markdown
Contributor

Pull request type

Bug fix (non-breaking change which fixes an issue)


Description

What: In the Calendar widget's Custom view, the "Header day format" property now applies to the day, week, and month column headers — not just the toolbar title.

Why: A customer (Mendix 10.24.19, Calendar module 2.4.0) set Header day format: EE dd-MM but the day-column header still rendered the default 07 Tue.

react-big-calendar uses two distinct sets of format keys:

  • dayHeaderFormat / monthHeaderFormat — the toolbar navigation title
  • dayFormat (week/day columns) and weekdayFormat (month weekday headers) — the per-column headers, the "07 Tue" text the user actually sees

CalendarPropsBuilder.buildFormats wired customViewHeaderDayFormat only into the toolbar-title keys, so the column headers kept RBC's date-fns default dayFormat: 'dd eee' and the user's pattern was silently dropped.

Fix: buildFormats now also sets formats.dayFormat (day/week columns) and formats.weekdayFormat (month) from the same customViewHeaderDayFormat pattern. RBC exposes a single shared dayFormat for day and week, so when both items set different patterns the week pattern wins (consistent with the existing timeGutterFormat precedence) and a console.warn is logged on collision. The custom work_week view spreads formats into RBC's TimeGrid, so it inherits the new dayFormat automatically.

Added unit tests for both formats, the collision precedence + warning, the no-format regression, and the existing time-gutter wiring (21/21 pass).

What should be covered while testing?

  1. Calendar in Custom view with day and week toolbar items.
  2. Set Header day format = EE dd-MM and Time gutter format = HH:mm.
  3. Day/week column headers should render e.g. Tue 07-07 (not 07 Tue); time gutter shows 24h 14:00.
  4. Switch to month view — weekday headers reflect the pattern.
  5. Regression: with no custom format set, headers fall back to RBC defaults unchanged.

@rahmanunver
rahmanunver requested a review from a team as a code owner July 7, 2026 14:49
@github-actions

This comment has been minimized.

gjulivan
gjulivan previously approved these changes Jul 8, 2026
In Custom view the Header day format was only wired into react-big-calendar's
toolbar-title keys (dayHeaderFormat/monthHeaderFormat), so the day/week/month
column headers still used RBC's default dayFormat (dd eee, rendering "07 Tue")
and the user's pattern was silently dropped.

Wire customViewHeaderDayFormat into RBC's per-column keys: dayFormat (day/week
columns) and weekdayFormat (month weekday headers). RBC shares a single global
dayFormat across day and week, so when both items set different patterns the week
pattern wins (matching the existing timeGutterFormat precedence) and a warning is
logged. Add unit tests covering both formats, the collision precedence, and the
no-format regression.
@rahmanunver
rahmanunver force-pushed the fix/calendar-custom-header-day-format branch from 75753eb to 4daebe8 Compare July 21, 2026 13:37
@github-actions

Copy link
Copy Markdown
Contributor

AI Code Review

⚠️ Approved with suggestions — low-severity items only, safe to merge


What was reviewed

File Change
packages/pluggableWidgets/calendar-web/src/Calendar.tsx Added controlled view state + onView handler; passes activeView to builder
packages/pluggableWidgets/calendar-web/src/helpers/CalendarPropsBuilder.ts build() accepts activeView; buildFormats() resolves dayFormat/weekdayFormat/dayRangeHeaderFormat per active view
packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx New describe block with 6 targeted format tests
packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap Updated to include view="day" on rendered calendar
packages/pluggableWidgets/calendar-web/CHANGELOG.md [Unreleased] entry added under ### Fixed

Skipped (out of scope): dist/, pnpm-lock.yaml

Note: CI check results could not be fetched in this environment — verify checks pass before merging.


Findings

⚠️ Low — Controlled view prop causes extra re-render on first mount

File: packages/pluggableWidgets/calendar-web/src/Calendar.tsx lines 28–34
Problem: activeView starts as undefined, so build() runs with undefined on the first render. RBC fires onView immediately on mount (to report defaultView), which sets activeView and triggers a second render with the correct view. This is two renders before the calendar is stable — harmless but unnecessary.
Fix: Seed the state with the value build() already computes as safeDefaultView so the first render is already correct. One way — read defaultViewCustom/defaultViewStandard from props directly:

const [activeView, setActiveView] = useState<View | undefined>(
    () => (props.view === "custom" ? props.defaultViewCustom : props.defaultViewStandard) as View
);

This is advisory; the current double-render is invisible to users.


⚠️ Low — buildFormats parameter type is duplicated instead of using the imported View

File: packages/pluggableWidgets/calendar-web/src/helpers/CalendarPropsBuilder.ts line 152
Problem: The activeView parameter is typed as the inline union "day" | "week" | "work_week" | "month" | "agenda" rather than the already-imported View type from react-big-calendar. These are equivalent today but will silently diverge if RBC adds a new view.

// current
private buildFormats(_localizer: DateLocalizer, activeView: "day" | "week" | "work_week" | "month" | "agenda"): Formats
// suggested
private buildFormats(_localizer: DateLocalizer, activeView: View): Formats

⚠️ Low — New format tests use manual mock localizer instead of shared mockLocalizer

File: packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx lines 261–268
Problem: The buildFormats helper inside CalendarPropsBuilder column header formats creates a new jest.fn() localizer inline. The existing CalendarPropsBuilder validation describe block already defines a mockLocalizer constant in scope. The duplication is minor but means future changes have two places to update.
Note: Not blocking — each test suite scoping its own mock is also a valid pattern. Just worth noting for consistency.


⚠️ Low — Snapshot test will fail if defaultViewCustom ever changes

File: packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap line 24
Problem: The snapshot now includes view="day" (resolved from safeDefaultView). The customViewProps fixture sets defaultViewCustom: "work_week", but the toolbarItems list is empty — so safeDefaultView falls back to enabledViews[0] which in non-custom mode resolves to "day". This is fragile: the snapshot will break if the fallback logic changes or if a test adds toolbar items to the shared fixture.
Note: The snapshot is a regression guard for this rendered output — acceptable to keep, but worth a comment in the fixture explaining why defaultViewCustom: "work_week" still renders view="day".


Positives

  • The active-view resolution pattern (pass activeView into buildFormats, fall back to safeDefaultView on first render) cleanly solves the shared-key contamination problem without a breaking change to RBC's API surface.
  • The console.warn approach for the missing customViewHeaderDayFormat on day when re-used by dayHeaderFormat is appropriately non-fatal.
  • New tests cover all four distinct code paths: per-view resolution, fallback-to-RBC-default, no-config-at-all, and the pre-existing time-gutter wiring — exactly the right set given how subtle the shared-key semantics are.
  • CHANGELOG entry is present under [Unreleased] with a user-facing description (no implementation details).
  • Narrowing the existing defaultView field type from the manual union to the imported View type is a clean improvement picked up in the same PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants