From 8b2315184239ce2530de6e26e9415f36984f1598 Mon Sep 17 00:00:00 2001 From: Heiko Osigus Date: Tue, 3 Mar 2026 12:42:50 +0100 Subject: [PATCH 1/7] feat: add showMultiDayTimes property to calendar component --- .../calendar-web/src/Calendar.xml | 4 + .../src/__tests__/Calendar.spec.tsx | 183 ++++++++++++++++++ .../__snapshots__/Calendar.spec.tsx.snap | 1 + .../src/helpers/CalendarPropsBuilder.ts | 13 ++ .../calendar-web/typings/CalendarProps.d.ts | 2 + 5 files changed, 203 insertions(+) diff --git a/packages/pluggableWidgets/calendar-web/src/Calendar.xml b/packages/pluggableWidgets/calendar-web/src/Calendar.xml index 25e9204adf..ed16f9e0f0 100644 --- a/packages/pluggableWidgets/calendar-web/src/Calendar.xml +++ b/packages/pluggableWidgets/calendar-web/src/Calendar.xml @@ -124,6 +124,10 @@ Show all events Auto-adjust calendar height to display all events without "more" links + + Show multi-day times + Show start and end times for events that span multiple days in the week and day views instead of placing them in the all-day row + Step Determines the selectable time increments in week and day views diff --git a/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx b/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx index 50c72a9ac1..be430481e9 100644 --- a/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx +++ b/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx @@ -19,6 +19,9 @@ jest.mock("react-big-calendar", () => { resizable, selectable, showAllEvents, + showMultiDayTimes, + min, + max, events, step, timeslots, @@ -34,6 +37,9 @@ jest.mock("react-big-calendar", () => { data-resizable={resizable} data-selectable={selectable} data-show-all-events={showAllEvents} + data-show-multi-day-times={showMultiDayTimes} + data-min={min?.toISOString()} + data-max={max?.toISOString()} data-events-count={events?.length ?? 0} data-step={step} data-timeslots={timeslots} @@ -93,6 +99,7 @@ const customViewProps: CalendarContainerProps = { customViewShowFriday: true, customViewShowSaturday: false, showAllEvents: true, + showMultiDayTimes: true, step: 60, timeslots: 2, toolbarItems: [], @@ -358,3 +365,179 @@ describe("CalendarPropsBuilder column header formats", () => { expect(localizer.format).toHaveBeenCalledWith(expect.any(Date), "HH:mm", "en"); }); }); + +describe("CalendarPropsBuilder showMultiDayTimes", () => { + const mockLocalizer = { + format: jest.fn(), + parse: jest.fn(), + startOfWeek: jest.fn(), + getDay: jest.fn(), + messages: {} + } as any; + + it("passes showMultiDayTimes=true to calendar props", () => { + const props = { ...customViewProps, showMultiDayTimes: true }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + expect(result.showMultiDayTimes).toBe(true); + }); + + it("passes showMultiDayTimes=false to calendar props", () => { + const props = { ...customViewProps, showMultiDayTimes: false }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + expect(result.showMultiDayTimes).toBe(false); + }); +}); + +describe("CalendarPropsBuilder multi-day time formats", () => { + const mockLocalizer = { + format: jest.fn((date: Date, pattern: string, _culture: string) => { + // Simulate locale-aware formatting using the pattern + const hours = date.getHours(); + const minutes = date.getMinutes().toString().padStart(2, "0"); + return `${hours}:${minutes} (${pattern})`; + }), + parse: jest.fn(), + startOfWeek: jest.fn(), + getDay: jest.fn(), + messages: {} + } as any; + + const buildWithTimeFormat = (timeFormatValue: string) => { + const props = { + ...customViewProps, + timeFormat: dynamic(timeFormatValue) + }; + const builder = new CalendarPropsBuilder(props); + return builder.build(mockLocalizer, "en"); + }; + + it("sets eventTimeRangeStartFormat using the configured time pattern", () => { + const result = buildWithTimeFormat("HH:mm"); + const start = new Date("2025-04-28T22:00:00Z"); + const end = new Date("2025-04-29T02:00:00Z"); + + expect(result.formats!.eventTimeRangeStartFormat).toBeDefined(); + const label = (result.formats!.eventTimeRangeStartFormat as Function)({ start, end }, "en", mockLocalizer); + expect(label).toContain("HH:mm"); + expect(label).toMatch(/– $/); + }); + + it("sets eventTimeRangeEndFormat using the configured time pattern", () => { + const result = buildWithTimeFormat("HH:mm"); + const start = new Date("2025-04-28T22:00:00Z"); + const end = new Date("2025-04-29T02:00:00Z"); + + expect(result.formats!.eventTimeRangeEndFormat).toBeDefined(); + const label = (result.formats!.eventTimeRangeEndFormat as Function)({ start, end }, "en", mockLocalizer); + expect(label).toContain("HH:mm"); + expect(label).toMatch(/^ – /); + }); + + it("uses the same pattern for eventTimeRangeFormat, start, and end formats", () => { + const result = buildWithTimeFormat("h:mm a"); + const start = new Date("2025-04-28T22:00:00Z"); + const end = new Date("2025-04-29T02:00:00Z"); + + const rangeLabel = (result.formats!.eventTimeRangeFormat as Function)({ start, end }, "en", mockLocalizer); + const startLabel = (result.formats!.eventTimeRangeStartFormat as Function)({ start, end }, "en", mockLocalizer); + const endLabel = (result.formats!.eventTimeRangeEndFormat as Function)({ start, end }, "en", mockLocalizer); + + // All three should use the same "h:mm a" pattern passed to localizer.format + expect(rangeLabel).toContain("h:mm a"); + expect(startLabel).toContain("h:mm a"); + expect(endLabel).toContain("h:mm a"); + }); + + it("does not set start/end formats when no timeFormat is configured", () => { + const props = { ...customViewProps, timeFormat: undefined }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + + expect(result.formats!.eventTimeRangeStartFormat).toBeUndefined(); + expect(result.formats!.eventTimeRangeEndFormat).toBeUndefined(); + }); +}); + +describe("CalendarPropsBuilder showEventDate hides multi-day formats", () => { + const mockLocalizer = { + format: jest.fn((_date: Date, pattern: string) => `formatted(${pattern})`), + parse: jest.fn(), + startOfWeek: jest.fn(), + getDay: jest.fn(), + messages: {} + } as any; + + it("blanks eventTimeRangeStartFormat when showEventDate=false", () => { + const props = { + ...customViewProps, + showEventDate: dynamic(false), + timeFormat: dynamic("HH:mm") + }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + + const label = (result.formats!.eventTimeRangeStartFormat as Function)( + { start: new Date(), end: new Date() }, + "en", + mockLocalizer + ); + expect(label).toBe(""); + }); + + it("blanks eventTimeRangeEndFormat when showEventDate=false", () => { + const props = { + ...customViewProps, + showEventDate: dynamic(false), + timeFormat: dynamic("HH:mm") + }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + + const label = (result.formats!.eventTimeRangeEndFormat as Function)( + { start: new Date(), end: new Date() }, + "en", + mockLocalizer + ); + expect(label).toBe(""); + }); + + it("blanks eventTimeRangeFormat when showEventDate=false", () => { + const props = { + ...customViewProps, + showEventDate: dynamic(false), + timeFormat: dynamic("HH:mm") + }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + + const label = (result.formats!.eventTimeRangeFormat as Function)( + { start: new Date(), end: new Date() }, + "en", + mockLocalizer + ); + expect(label).toBe(""); + }); + + it("preserves all time range formats when showEventDate=true", () => { + const props = { + ...customViewProps, + showEventDate: dynamic(true), + timeFormat: dynamic("p") + }; + const builder = new CalendarPropsBuilder(props); + const result = builder.build(mockLocalizer, "en"); + + const start = new Date("2025-04-28T22:00:00Z"); + const end = new Date("2025-04-29T02:00:00Z"); + + const rangeLabel = (result.formats!.eventTimeRangeFormat as Function)({ start, end }, "en", mockLocalizer); + const startLabel = (result.formats!.eventTimeRangeStartFormat as Function)({ start, end }, "en", mockLocalizer); + const endLabel = (result.formats!.eventTimeRangeEndFormat as Function)({ start, end }, "en", mockLocalizer); + + expect(rangeLabel).not.toBe(""); + expect(startLabel).not.toBe(""); + expect(endLabel).not.toBe(""); + }); +}); diff --git a/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap b/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap index 422306a487..bbdff94632 100644 --- a/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap +++ b/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap @@ -13,6 +13,7 @@ exports[`Calendar renders correctly with basic props 1`] = ` data-resizable="true" data-selectable="true" data-show-all-events="true" + data-show-multi-day-times="true" data-step="60" data-testid="mock-calendar" data-timeslots="2" diff --git a/packages/pluggableWidgets/calendar-web/src/helpers/CalendarPropsBuilder.ts b/packages/pluggableWidgets/calendar-web/src/helpers/CalendarPropsBuilder.ts index 7f33a8b198..3366272f7a 100644 --- a/packages/pluggableWidgets/calendar-web/src/helpers/CalendarPropsBuilder.ts +++ b/packages/pluggableWidgets/calendar-web/src/helpers/CalendarPropsBuilder.ts @@ -92,6 +92,7 @@ export class CalendarPropsBuilder { startAccessor: (event: CalendarEvent) => event.start, titleAccessor: (event: CalendarEvent) => event.title, showAllEvents: this.props.showAllEvents, + showMultiDayTimes: this.props.showMultiDayTimes, min: this.minTime, max: this.maxTime, step: this.step, @@ -174,6 +175,16 @@ export class CalendarPropsBuilder { culture: string, loc: DateLocalizer ) => `${formatWith(start, culture, loc)} – ${formatWith(end, culture, loc)}`; + formats.eventTimeRangeStartFormat = ( + { start }: { start: Date; end: Date }, + culture: string, + loc: DateLocalizer + ) => `${formatWith(start, culture, loc)} – `; + formats.eventTimeRangeEndFormat = ( + { end }: { start: Date; end: Date }, + culture: string, + loc: DateLocalizer + ) => ` – ${formatWith(end, culture, loc)}`; formats.agendaTimeRangeFormat = ( { start, end }: { start: Date; end: Date }, culture: string, @@ -306,6 +317,8 @@ export class CalendarPropsBuilder { // Ensure showEventDate=false always hides event time ranges if (this.props.showEventDate?.value === false) { formats.eventTimeRangeFormat = () => ""; + formats.eventTimeRangeStartFormat = () => ""; + formats.eventTimeRangeEndFormat = () => ""; } return formats; diff --git a/packages/pluggableWidgets/calendar-web/typings/CalendarProps.d.ts b/packages/pluggableWidgets/calendar-web/typings/CalendarProps.d.ts index cc0721cc46..2beb55f550 100644 --- a/packages/pluggableWidgets/calendar-web/typings/CalendarProps.d.ts +++ b/packages/pluggableWidgets/calendar-web/typings/CalendarProps.d.ts @@ -89,6 +89,7 @@ export interface CalendarContainerProps { minHour: number; maxHour: number; showAllEvents: boolean; + showMultiDayTimes: boolean; step: number; timeslots: number; startDateAttribute?: EditableValue; @@ -144,6 +145,7 @@ export interface CalendarPreviewProps { minHour: number | null; maxHour: number | null; showAllEvents: boolean; + showMultiDayTimes: boolean; step: number | null; timeslots: number | null; startDateAttribute: string; From 787b20a68a0321d57d4ef3eabb4b08dea195df15 Mon Sep 17 00:00:00 2001 From: Illia Obukhau <8282906+iobuhov@users.noreply.github.com> Date: Thu, 7 May 2026 12:47:06 +0200 Subject: [PATCH 2/7] chore(calendar-web): add changelog entry for showMultiDayTimes property Co-Authored-By: Claude Sonnet 4.5 --- packages/pluggableWidgets/calendar-web/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/pluggableWidgets/calendar-web/CHANGELOG.md b/packages/pluggableWidgets/calendar-web/CHANGELOG.md index 9e222df503..4d89e0ef53 100644 --- a/packages/pluggableWidgets/calendar-web/CHANGELOG.md +++ b/packages/pluggableWidgets/calendar-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- We added a `showMultiDayTimes` property to control whether start/end times are displayed for multi-day events in the calendar. + ### Fixed - We fixed an issue in Custom view where the "Header day format" was only applied to the toolbar title and not to the day, week, and month column headers. From 3a964d6acbb4bba273001557fea1b83c970dbd73 Mon Sep 17 00:00:00 2001 From: Illia Obukhau <8282906+iobuhov@users.noreply.github.com> Date: Thu, 7 May 2026 12:57:47 +0200 Subject: [PATCH 3/7] test(calendar-web): update snapshot for min/max date serialization Co-Authored-By: Claude Sonnet 4.5 --- .../src/__tests__/__snapshots__/Calendar.spec.tsx.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap b/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap index bbdff94632..c852584b8d 100644 --- a/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap +++ b/packages/pluggableWidgets/calendar-web/src/__tests__/__snapshots__/Calendar.spec.tsx.snap @@ -10,6 +10,8 @@ exports[`Calendar renders correctly with basic props 1`] = ` data-culture="en-US" data-default-view="day" data-events-count="0" + data-max="2025-04-28T23:59:59.000Z" + data-min="2025-04-28T00:00:00.000Z" data-resizable="true" data-selectable="true" data-show-all-events="true" @@ -19,9 +21,7 @@ exports[`Calendar renders correctly with basic props 1`] = ` data-timeslots="2" formats="[object Object]" localizer="[object Object]" - max="Mon Apr 28 2025 23:59:59 GMT+0000 (Coordinated Universal Time)" messages="[object Object]" - min="Mon Apr 28 2025 00:00:00 GMT+0000 (Coordinated Universal Time)" view="day" views="[object Object]" /> From db32285ac329776125feba65ef5e74299c274246 Mon Sep 17 00:00:00 2001 From: Illia Obukhau <8282906+iobuhov@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:52:49 +0200 Subject: [PATCH 4/7] docs(calendar-web): add openspec change for showMultiDayTimes property Co-Authored-By: Claude Opus 4.8 --- .../add-show-multi-day-times/.openspec.yaml | 2 + .../add-show-multi-day-times/proposal.md | 44 +++++++++++ .../specs/calendar-configuration/spec.md | 73 +++++++++++++++++++ .../changes/add-show-multi-day-times/tasks.md | 28 +++++++ 4 files changed, 147 insertions(+) create mode 100644 packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/.openspec.yaml create mode 100644 packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/proposal.md create mode 100644 packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/specs/calendar-configuration/spec.md create mode 100644 packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md diff --git a/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/.openspec.yaml b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/.openspec.yaml new file mode 100644 index 0000000000..9e5b8a1905 --- /dev/null +++ b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-23 diff --git a/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/proposal.md b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/proposal.md new file mode 100644 index 0000000000..e68b8ed643 --- /dev/null +++ b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/proposal.md @@ -0,0 +1,44 @@ +## Why + +In the Calendar widget's week and day views, `react-big-calendar` places any event that +spans more than one day into the top "all-day" row, hiding its start and end times. For +apps where multi-day events have meaningful times (e.g. a booking that starts at 22:00 on +one day and ends at 02:00 the next), authors need those events rendered as timed blocks in +the time grid, showing their start/end times, instead of being flattened into the all-day +row. + +`react-big-calendar` already exposes a `showMultiDayTimes` flag for exactly this, but the +Calendar widget did not surface it, so authors had no way to opt in. + +## What Changes + +Add a `showMultiDayTimes` boolean property (default `false`) to the Calendar widget and pass +it through to `react-big-calendar`. When enabled, multi-day events render in the time grid +with their start/end times in week and day views. To make those times readable on the +event blocks, the widget also supplies `eventTimeRangeStartFormat` and +`eventTimeRangeEndFormat` derived from the widget's configured time format, and keeps them +consistent with the existing `eventTimeRangeFormat` and `showEventDate` behavior. + +- `src/Calendar.xml` — add `showMultiDayTimes` boolean property (defaultValue `false`) in + the week/day view group, with caption and description. +- `typings/CalendarProps.d.ts` — add `showMultiDayTimes: boolean` to the container props and + `showMultiDayTimes: boolean` to the preview props (generated typings). +- `src/helpers/CalendarPropsBuilder.ts`: + - Pass `showMultiDayTimes: this.props.showMultiDayTimes` through to the calendar props. + - Add `eventTimeRangeStartFormat` (`" – "`) and `eventTimeRangeEndFormat` + (`" – "`) using the same configured time pattern as `eventTimeRangeFormat`. + - When `showEventDate` is `false`, blank out `eventTimeRangeStartFormat` and + `eventTimeRangeEndFormat` alongside the existing `eventTimeRangeFormat`. +- Unit tests + snapshot update for the above. +- User-facing changelog entry. + +## Impact + +- **Consumers:** Purely additive. The default is `false`, which preserves the existing + behavior (multi-day events stay in the all-day row), so existing apps are unaffected. Not + breaking. +- **Must NOT break:** `showEventDate=false` must continue to hide all event time ranges, + including the new start/end formats; existing single-day timed event rendering and the + existing `eventTimeRangeFormat` behavior are unchanged. +- **Users:** Authors can opt in to see start/end times for multi-day events in week and day + views. Version bump deferred to release time per repo convention. diff --git a/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/specs/calendar-configuration/spec.md b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/specs/calendar-configuration/spec.md new file mode 100644 index 0000000000..c98f7a7187 --- /dev/null +++ b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/specs/calendar-configuration/spec.md @@ -0,0 +1,73 @@ +## ADDED Requirements + +### Requirement: Show multi-day times property + +The Calendar widget SHALL expose a `showMultiDayTimes` boolean property (default `false`) +that controls whether events spanning multiple days are rendered as timed blocks (showing +start/end times) in the week and day views instead of being placed in the all-day row. + +#### Scenario: Property is available in Studio Pro + +- **WHEN** a developer configures the Calendar widget in Studio Pro +- **THEN** a "Show multi-day times" boolean property is available, defaulting to off + +#### Scenario: Enabled flag is passed through to the calendar + +- **WHEN** `showMultiDayTimes` is set to `true` +- **THEN** the Calendar passes `showMultiDayTimes=true` to `react-big-calendar`, so multi-day + events render as timed blocks in the week and day views + +#### Scenario: Disabled by default preserves all-day rendering + +- **WHEN** `showMultiDayTimes` is left at its default (`false`) +- **THEN** the Calendar passes `showMultiDayTimes=false`, and multi-day events continue to be + placed in the all-day row (existing behavior is unchanged) + +### Requirement: Multi-day event time-range formats + +When a time format is configured, the Calendar widget SHALL provide +`eventTimeRangeStartFormat` and `eventTimeRangeEndFormat` for `react-big-calendar`, derived +from the same configured time pattern used by `eventTimeRangeFormat`, so the start and end +times of multi-day events are shown in a locale-aware way. + +#### Scenario: Start format renders the start time with a trailing separator + +- **WHEN** a time format (e.g. `HH:mm`) is configured and `eventTimeRangeStartFormat` is + called for an event +- **THEN** it returns the event's start time formatted with that pattern, followed by a + " – " separator + +#### Scenario: End format renders the end time with a leading separator + +- **WHEN** a time format (e.g. `HH:mm`) is configured and `eventTimeRangeEndFormat` is called + for an event +- **THEN** it returns the event's end time formatted with that pattern, preceded by a " – " + separator + +#### Scenario: Start, end, and range formats share the same time pattern + +- **WHEN** a time format (e.g. `h:mm a`) is configured +- **THEN** `eventTimeRangeFormat`, `eventTimeRangeStartFormat`, and `eventTimeRangeEndFormat` + all format times using that same pattern + +#### Scenario: No time format leaves start/end formats unset + +- **WHEN** no time format is configured +- **THEN** the widget does not set `eventTimeRangeStartFormat` or `eventTimeRangeEndFormat` + +### Requirement: Show event date hides multi-day time ranges + +When `showEventDate` is disabled, the Calendar widget SHALL hide all event time-range labels, +including the multi-day start and end formats, so no times are shown on events. + +#### Scenario: showEventDate=false blanks all time-range formats + +- **WHEN** `showEventDate` is `false` (with a time format configured) +- **THEN** `eventTimeRangeFormat`, `eventTimeRangeStartFormat`, and `eventTimeRangeEndFormat` + all return an empty string + +#### Scenario: showEventDate=true preserves all time-range formats + +- **WHEN** `showEventDate` is `true` (with a time format configured) +- **THEN** `eventTimeRangeFormat`, `eventTimeRangeStartFormat`, and `eventTimeRangeEndFormat` + all return a non-empty formatted label diff --git a/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md new file mode 100644 index 0000000000..8e3ea216e5 --- /dev/null +++ b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md @@ -0,0 +1,28 @@ +## 1. Property definition + +- [x] 1.1 Add `showMultiDayTimes` boolean property (defaultValue `false`) with caption and description to `src/Calendar.xml` in the week/day view group +- [x] 1.2 Regenerate typings: `showMultiDayTimes: boolean` in `CalendarContainerProps` and `CalendarPreviewProps` (`typings/CalendarProps.d.ts`) + +## 2. Implementation + +- [x] 2.1 Pass `showMultiDayTimes: this.props.showMultiDayTimes` through to the calendar props in `CalendarPropsBuilder.build` +- [x] 2.2 Add `eventTimeRangeStartFormat` (`" – "`) and `eventTimeRangeEndFormat` (`" – "`) using the same configured time pattern as `eventTimeRangeFormat` +- [x] 2.3 When `showEventDate?.value === false`, blank `eventTimeRangeStartFormat` and `eventTimeRangeEndFormat` alongside the existing `eventTimeRangeFormat` + +## 3. Tests + +- [x] 3.1 Test `showMultiDayTimes=true`/`false` is passed through to calendar props +- [x] 3.2 Test start/end formats use the configured time pattern with the correct leading/trailing separator; and are unset when no time format is configured +- [x] 3.3 Test `showEventDate=false` blanks range/start/end formats; `showEventDate=true` preserves them +- [x] 3.4 Update the Calendar snapshot for the new `data-show-multi-day-times` (and `data-min`/`data-max`) attributes + +## 4. Verification + +- [x] 4.1 `calendar-web` unit suite passes +- [x] 4.2 Add user-facing changelog entry (new `showMultiDayTimes` property) +- [ ] 4.3 `/code-review` before merge; PR ready + +## Notes + +- Change is additive; default `false` preserves existing all-day rendering for multi-day events. +- Version bump deferred to release time per repo convention. From 7070f5e3caca89615132b04c34bfd6d1d846d3cf Mon Sep 17 00:00:00 2001 From: Illia Obukhau <8282906+iobuhov@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:18:36 +0200 Subject: [PATCH 5/7] test(calendar-web): use dynamic.available for timeFormat/showEventDate in specs Co-Authored-By: Claude Opus 4.8 --- .../src/__tests__/Calendar.spec.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx b/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx index be430481e9..2ed0897eb2 100644 --- a/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx +++ b/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx @@ -407,7 +407,7 @@ describe("CalendarPropsBuilder multi-day time formats", () => { const buildWithTimeFormat = (timeFormatValue: string) => { const props = { ...customViewProps, - timeFormat: dynamic(timeFormatValue) + timeFormat: dynamic.available(timeFormatValue) }; const builder = new CalendarPropsBuilder(props); return builder.build(mockLocalizer, "en"); @@ -472,8 +472,8 @@ describe("CalendarPropsBuilder showEventDate hides multi-day formats", () => { it("blanks eventTimeRangeStartFormat when showEventDate=false", () => { const props = { ...customViewProps, - showEventDate: dynamic(false), - timeFormat: dynamic("HH:mm") + showEventDate: dynamic.available(false), + timeFormat: dynamic.available("HH:mm") }; const builder = new CalendarPropsBuilder(props); const result = builder.build(mockLocalizer, "en"); @@ -489,8 +489,8 @@ describe("CalendarPropsBuilder showEventDate hides multi-day formats", () => { it("blanks eventTimeRangeEndFormat when showEventDate=false", () => { const props = { ...customViewProps, - showEventDate: dynamic(false), - timeFormat: dynamic("HH:mm") + showEventDate: dynamic.available(false), + timeFormat: dynamic.available("HH:mm") }; const builder = new CalendarPropsBuilder(props); const result = builder.build(mockLocalizer, "en"); @@ -506,8 +506,8 @@ describe("CalendarPropsBuilder showEventDate hides multi-day formats", () => { it("blanks eventTimeRangeFormat when showEventDate=false", () => { const props = { ...customViewProps, - showEventDate: dynamic(false), - timeFormat: dynamic("HH:mm") + showEventDate: dynamic.available(false), + timeFormat: dynamic.available("HH:mm") }; const builder = new CalendarPropsBuilder(props); const result = builder.build(mockLocalizer, "en"); @@ -523,8 +523,8 @@ describe("CalendarPropsBuilder showEventDate hides multi-day formats", () => { it("preserves all time range formats when showEventDate=true", () => { const props = { ...customViewProps, - showEventDate: dynamic(true), - timeFormat: dynamic("p") + showEventDate: dynamic.available(true), + timeFormat: dynamic.available("p") }; const builder = new CalendarPropsBuilder(props); const result = builder.build(mockLocalizer, "en"); From 91781927e3bf1cc5698ad593b0865aa1cda0c38b Mon Sep 17 00:00:00 2001 From: Illia Obukhau <8282906+iobuhov@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:18:46 +0200 Subject: [PATCH 6/7] chore: set recursiveInstall true in pnpm-workspace for correct monorepo installs Co-Authored-By: Claude Opus 4.8 --- pnpm-workspace.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4c37007e1c..2988723ef3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ +recursiveInstall: true packages: - - "packages/*/*" - - "automation/*" + - "packages/*/*" + - "automation/*" catalog: - rollup: "3.29" + rollup: "3.29" From b296db11c59e362ec175c800cf789bc033181ed9 Mon Sep 17 00:00:00 2001 From: Illia Obukhau <8282906+iobuhov@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:18:51 +0200 Subject: [PATCH 7/7] docs(calendar-web): mark code-review task complete in openspec change Co-Authored-By: Claude Opus 4.8 --- .../openspec/changes/add-show-multi-day-times/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md index 8e3ea216e5..f8d61380c6 100644 --- a/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md +++ b/packages/pluggableWidgets/calendar-web/openspec/changes/add-show-multi-day-times/tasks.md @@ -20,7 +20,7 @@ - [x] 4.1 `calendar-web` unit suite passes - [x] 4.2 Add user-facing changelog entry (new `showMultiDayTimes` property) -- [ ] 4.3 `/code-review` before merge; PR ready +- [x] 4.3 `/code-review` before merge; PR ready (⚠️ Approved with suggestions — low-severity only, no medium/high findings) ## Notes