-
Notifications
You must be signed in to change notification settings - Fork 10.5k
fix: multiple widgets for Booker
atom
#22925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Hey there and thank you for opening this pull request! 👋🏼 We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted. Details:
|
WalkthroughAdds a React context provider for the Booker zustand store (BookerStoreProvider) plus selector and initializer hooks (useBookerStoreContext, useInitializeBookerStoreContext) and a factory-based store (createBookerStore) while preserving a default useBookerStore. Migrates most components, hooks, platform wrappers, embeds, and tests from the module-level store hook to the new context hook. Centralizes selectedTimeslot flow from AvailableTimeSlots into BookEventForm and EventMeta. Adds test utilities to render components with a mocked BookerStore context. Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ |
Booker
atom
…t tests - Create reusable test utility in test-utils.tsx with comprehensive mock store - Update Booker.test.tsx to use context-based testing approach - Fix DatePicker tests in both bookings and calendars packages - Simulate auto-advance behavior for month navigation tests - All 14 previously failing tests now pass Co-Authored-By: [email protected] <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
packages/features/bookings/Booker/Booker.tsx (1)
231-305
: Missing useMemo dependencies cause stale timeslot and props in BookEventFormEventBooker depends on selectedTimeslot (passed to BookEventForm and used to compute isTimeslotUnavailable), confirmButtonDisabled, customClassNames, isInstantMeeting, and schedule (used in onCancel) but these aren’t in the dependency array. This can keep the form from updating when selecting a different slot and leave other props stale.
Add the missing dependencies:
}, [ bookerFormErrorRef, instantVideoMeetingUrl, bookerState, bookingForm, errors, event, expiryTime, extraOptions, formErrors, handleBookEvent, handleVerifyEmail, key, loadingStates, onGoBackInstantMeeting, renderConfirmNotVerifyEmailButtonCond, seatedEventData, setSeatedEventData, setSelectedTimeslot, isPlatform, shouldRenderCaptcha, isVerificationCodeSending, unavailableTimeSlots, + selectedTimeslot, + confirmButtonDisabled, + customClassNames, + isInstantMeeting, + schedule, ]);
♻️ Duplicate comments (6)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (3)
9-13
: Remove legacy store imports; use only context APIsYou're now using the context-based store. Keep imports from BookerStoreProvider and drop the legacy singleton imports to avoid accidental cross-instance coupling and unused imports.
Apply:
import { BookerStoreProvider, useInitializeBookerStoreContext, useBookerStoreContext, } from "@calcom/features/bookings/Booker/BookerStoreProvider"; -import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store"; +import type { BookerStore } from "@calcom/features/bookings/Booker/store";#!/bin/bash # Verify no lingering legacy singleton imports remain across the repo rg -n --no-heading $'@calcom/features/bookings/Booker/store' | rg -n --no-heading 'useBookerStore\\(|useInitializeBookerStore\\(' || true # Verify all context-based imports are used within a Provider rg -n --no-heading $'useBookerStoreContext\\(|useInitializeBookerStoreContext\\(' -A 2 -B 2 rg -n --no-heading $'BookerStoreProvider'Also applies to: 17-17
182-197
: Double initialization: remove legacy useInitializeBookerStore to prevent cross-widget leaksYou correctly initialize via useInitializeBookerStoreContext here. However, you still call the legacy useInitializeBookerStore above. That reintroduces the global singleton and can leak state between widgets.
Action:
- Delete the legacy initializer block above (Lines 166–181).
- With the import cleanup suggested earlier, this avoids initializing the global store entirely.
Remove the following block (shown for clarity):
useInitializeBookerStore({ ...props, teamMemberEmail, crmAppSlug, crmOwnerRecordType, crmRecordId: props.crmRecordId, eventId: event?.data?.id, rescheduleUid: props.rescheduleUid ?? null, bookingUid: props.bookingUid ?? null, layout: layout, org: props.entity?.orgSlug, username, bookingData, isPlatform: true, allowUpdatingUrlParams, });
72-75
: Stop subscribing to the legacy global store in onBookerStateChange; scope it to the context storeThe effect still uses useBookerStore.subscribe/getState and types getStateValues off the legacy store, which breaks isolation across widgets. Select from the context-scoped store instead.
Follow-up changes:
- Type the selector against the store type.
- Drive the callback using a context selector so it only reacts to this instance’s state.
Add the type import (already suggested in imports comment):
import type { BookerStore } from "@calcom/features/bookings/Booker/store";Update the selector typing:
const getStateValues = useCallback((state: BookerStore): BookerStoreValues => { return Object.fromEntries(Object.entries(state).filter(([_, v]) => typeof v !== "function")) as BookerStoreValues; }, []);Replace the effect with a context-scoped subscription:
const scopedStateValues = useBookerStoreContext(getStateValues); useEffect(() => { if (!onBookerStateChange) return; // Initial call and subsequent debounced updates scoped to this provider debouncedStateChange(scopedStateValues, onBookerStateChange); return () => { debouncedStateChange.cancel(); }; }, [scopedStateValues, onBookerStateChange, debouncedStateChange]);Also applies to: 90-96, 112-129
packages/features/embed/Embed.tsx (2)
14-19
: Don’t mix context-based reader with legacy initializer; remove useInitializeBookerStore importYou’ve added BookerStoreProvider and context hooks (good), but still import the legacy useInitializeBookerStore. This can mutate a different, global store and break isolation.
Apply:
import { BookerStoreProvider, useInitializeBookerStoreContext, useBookerStoreContext, } from "@calcom/features/bookings/Booker/BookerStoreProvider"; -import { useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
268-275
: Initialize via context only (correct)Using useInitializeBookerStoreContext with username/eventSlug/eventId/layout/org/isTeamEvent is the right approach. Remove the legacy initializer call above (Lines 260–267) to avoid double-initialization.
Apply removal inside EmailEmbed:
- useInitializeBookerStore({ - username, - eventSlug: eventType?.slug ?? "", - eventId: eventType?.id, - layout: BookerLayouts.MONTH_VIEW, - org: orgSlug, - isTeamEvent, - });packages/features/bookings/Booker/Booker.tsx (1)
408-414
: Confirm EventMeta accepts and consumes selectedTimeslotYou’re passing selectedTimeslot to EventMeta. Ensure its props include selectedTimeslot?: string | null and that it’s used inside the component as intended.
Run:
#!/bin/bash # Check EventMeta props for `selectedTimeslot` rg -n 'type .*EventMeta.*Props|interface .*EventMeta.*Props' -A15 packages/features/bookings/Booker/components/EventMeta.tsx rg -n --fixed-strings 'selectedTimeslot' packages/features/bookings/Booker/components/EventMeta.tsx
🧹 Nitpick comments (4)
packages/features/bookings/Booker/components/hooks/useBookerTime.ts (1)
9-9
: Simplify the selector: select the primitive directly and drop shallow.You’re selecting a single primitive; the extra array allocation and shallow compare are unnecessary. Zustand’s default equality for primitives is sufficient.
Apply this diff within the current line range:
- const [timezoneFromBookerStore] = useBookerStoreContext((state) => [state.timezone], shallow); + const timezoneFromBookerStore = useBookerStoreContext((state) => state.timezone);And remove the (now unused) import at the top of this file:
// Remove this import import { shallow } from "zustand/shallow";packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)
198-199
: Nit: avoid selecting unused setter to reduce re-render noiseYou select setDayCount but never use it. Select only dayCount.
-const [dayCount] = useBookerStoreContext((state) => [state.dayCount, state.setDayCount], shallow); +const dayCount = useBookerStoreContext((state) => state.dayCount);packages/features/bookings/Booker/Booker.tsx (2)
93-94
: Avoid double subscriptions by selecting value and setter togetherYou’re subscribing twice to the store for selectedDate and setSelectedDate. Combine them into a single selector tuple with shallow to reduce subscriptions.
Apply this diff:
-const selectedDate = useBookerStoreContext((state) => state.selectedDate); -const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate); +const [selectedDate, setSelectedDate] = useBookerStoreContext( + (state) => [state.selectedDate, state.setSelectedDate], + shallow +);
195-198
: Stabilize the time-slot select handler with useCallbackonAvailableTimeSlotSelect is recreated on every render which can cause extra rerenders downstream. Wrap it in useCallback.
Apply this diff:
- const onAvailableTimeSlotSelect = (time: string) => { - setSelectedTimeslot(time); - }; + const onAvailableTimeSlotSelect = useCallback((time: string) => { + setSelectedTimeslot(time); + }, [setSelectedTimeslot]);Add this import at the top (outside the selected range):
import { useCallback, useEffect, useMemo, useRef } from "react";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/features/bookings/Booker/Booker.tsx
(7 hunks)packages/features/bookings/Booker/components/DatePicker.tsx
(2 hunks)packages/features/bookings/Booker/components/hooks/useBookerTime.ts
(1 hunks)packages/features/calendars/DatePicker.tsx
(3 hunks)packages/features/embed/Embed.tsx
(4 hunks)packages/platform/atoms/booker/BookerPlatformWrapper.tsx
(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/features/calendars/DatePicker.tsx
- packages/features/bookings/Booker/components/DatePicker.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
**/*.ts
: For Prisma queries, only select data you need; never useinclude
, always useselect
Ensure thecredential.key
field is never returned from tRPC endpoints or APIs
Files:
packages/features/bookings/Booker/components/hooks/useBookerTime.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()
in hot paths like loops
Files:
packages/features/bookings/Booker/components/hooks/useBookerTime.ts
packages/features/bookings/Booker/Booker.tsx
packages/platform/atoms/booker/BookerPlatformWrapper.tsx
packages/features/embed/Embed.tsx
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Always use
t()
for text localization in frontend code; direct text embedding should trigger a warning
Files:
packages/features/bookings/Booker/Booker.tsx
packages/platform/atoms/booker/BookerPlatformWrapper.tsx
packages/features/embed/Embed.tsx
🧬 Code Graph Analysis (4)
packages/features/bookings/Booker/components/hooks/useBookerTime.ts (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/features/bookings/Booker/Booker.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (2)
packages/features/bookings/Booker/BookerStoreProvider.tsx (3)
useBookerStoreContext
(24-35)useInitializeBookerStoreContext
(37-113)BookerStoreProvider
(15-22)packages/platform/atoms/booker/types.ts (2)
BookerPlatformWrapperAtomPropsForIndividual
(92-96)BookerPlatformWrapperAtomPropsForTeam
(98-103)
packages/features/embed/Embed.tsx (3)
packages/features/bookings/Booker/BookerStoreProvider.tsx (3)
useInitializeBookerStoreContext
(37-113)useBookerStoreContext
(24-35)BookerStoreProvider
(15-22)packages/embeds/embed-core/src/types.ts (1)
BookerLayouts
(6-6)packages/features/embed/types/index.d.ts (1)
EmbedType
(6-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (10)
packages/features/bookings/Booker/components/hooks/useBookerTime.ts (1)
3-3
: Verify BookerStoreProvider wraps all useBookerTime call sitesI ran the repo-wide search you suggested. Summary:
useBookerTime is used across multiple places (examples):
- packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts
- packages/features/embed/Embed.tsx
- packages/features/calendars/weeklyview/components/event/Empty.tsx
- packages/features/calendars/weeklyview/components/currentTime/index.tsx
- packages/features/bookings/components/AvailableTimes.tsx
- packages/features/bookings/Booker/components/EventMeta.tsx
- packages/features/bookings/Booker/utils/event.ts
- packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx
BookerStoreProvider placements found (wrap roots / common wrappers / tests):
- packages/features/embed/Embed.tsx
- packages/platform/atoms/booker/BookerWebWrapper.tsx
- packages/platform/atoms/booker/BookerPlatformWrapper.tsx
- packages/platform/atoms/availability/AvailabilitySettings.tsx (modal)
- tests (e.g. packages/features/calendars/tests/DatePicker.test.tsx)
Items needing explicit attention:
- Files that appear to mix the legacy store API and the new context-based API (please confirm intentional):
- packages/features/bookings/components/event-meta/Occurences.tsx (uses useBookerStore and useBookerTime)
- packages/features/bookings/Booker/components/hooks/useBookings.ts (imports useBookerStore from store)
- Double-check platform atom hooks (e.g. packages/platform/atoms/hooks/useSlots.ts, useHandleBookEvent.ts) are only invoked within components wrapped by BookerStoreProvider in the platform wrappers.
If you'd like, I can run a follow-up script that cross-references each useBookerTime file with known provider-wrapping entrypoints to try to flag any call sites that are likely rendered outside a provider.
packages/platform/atoms/booker/BookerPlatformWrapper.tsx (1)
584-592
: Provider wrapping looks correctWrapping the internal component with BookerStoreProvider at the public boundary ensures per-instance isolation. Good move.
packages/features/embed/Embed.tsx (3)
277-291
: Context-based reads/writes LGTMReading month/selectedDate/selectedDatesAndTimes and writing setSelectedDate/setMonth/setSelectedDatesAndTimes/setSelectedTimeslot/setTimezone via useBookerStoreContext is aligned with the per-instance store design.
702-705
: Scoped selection LGTMSelecting [month, selectedDatesAndTimes] from the context store is correct and avoids cross-widget leakage.
1385-1413
: Correct: wrap the embed dialog tree with BookerStoreProviderThis ensures EmailEmbed and preview consumers have a scoped store instance. Solid.
packages/features/bookings/Booker/Booker.tsx (5)
88-91
: Good use of context selector with shallow equalitySwitching to useBookerStoreContext and selecting [state, setState] with shallow prevents unnecessary rerenders. LGTM.
108-116
: LGTM on store usage for seatedEventData and dayCountConsistent tuple selection with shallow for seatedEventData and dayCount looks correct and performant.
235-235
: Verified — BookEventForm props includetimeslot: string | null
; no change requiredBookEventFormProps declares timeslot as
string | null
and the component guards for a missing timeslot, sotimeslot={selectedTimeslot}
is aligned.
- packages/features/bookings/Booker/components/BookEventForm/BookEventForm.tsx —
type BookEventFormProps
includestimeslot: string | null
(declaration and usage around lines 27/46 and checks at ~94–100).- packages/features/bookings/Booker/Booker.tsx — passing prop at line ~235: timeslot={selectedTimeslot}
Snippet:
timeslot={selectedTimeslot}
495-495
: AvailableTimeSlots: onAvailableTimeSlotSelect is present and required — no change neededAvailableTimeSlotsProps already declares onAvailableTimeSlotSelect: (time: string) => void and the handler in Booker.tsx matches and is passed through, so the original suggestion to make it optional is incorrect.
- packages/features/bookings/Booker/components/AvailableTimeSlots.tsx
- line 52:
onAvailableTimeSlotSelect: (time: string) => void;
- lines 76 / 151 / 167: destructure/use/call the prop
- packages/features/bookings/Booker/Booker.tsx
- line 195:
const onAvailableTimeSlotSelect = (time: string) => { ... }
- line 495:
onAvailableTimeSlotSelect={onAvailableTimeSlotSelect}
Note: The “Timeslot” vs “TimeSlot” naming inconsistency is a minor, non-blocking style issue you may want to standardize later.
Likely an incorrect or invalid review comment.
13-13
: Verified — Booker is always rendered inside BookerStoreProvider (or mocked in tests)I searched the repo and confirmed there are no direct, unwrapped production usages of . Key locations:
- packages/platform/atoms/booker/BookerPlatformWrapper.tsx — BookerPlatformWrapper exports the component wrapped in .
- packages/platform/atoms/booker/BookerWebWrapper.tsx — BookerWebWrapper exports the component wrapped in .
- packages/features/embed/Embed.tsx — EmbedDialog content is wrapped in .
- packages/features/bookings/Booker/tests/test-utils.tsx — tests use renderWithBookerStore which provides BookStoreContext.Provider (mock store) when rendering .
Note: packages/features/bookings/Booker/Booker.tsx exports Booker without embedding a provider (expected). Consumers must continue to use the provided wrappers (or the test helper) when rendering Booker.
No fixes required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (2)
packages/features/troubleshooter/Troubleshooter.tsx (2)
31-33
: CSS variable name typo breaks layout on non-lg breakpointsThe CSS custom property is misspelled as
--troublehooster-meta-width
but referenced as--troubleshooter-meta-width
(and correctly set forlg:
only). This causesgridTemplateColumns
to reference an undefined variable on mobile/tablet, likely breaking the layout.Apply this diff to fix the variable name consistently:
- ? "[--troublehooster-meta-width:0px]" - : "[--troublehooster-meta-width:250px] lg:[--troubleshooter-meta-width:430px]" + ? "[--troubleshooter-meta-width:0px]" + : "[--troubleshooter-meta-width:250px] lg:[--troubleshooter-meta-width:430px]"
57-59
: Invalid Tailwind arbitrary value class: useml-[-1px]
ml[-1px]
is invalid Tailwind syntax; it should beml-[-1px]
. This class currently won’t apply.Apply this diff:
- <div className="ml[-1px] border-subtle sticky top-0 [grid-area:main]"> + <div className="ml-[-1px] border-subtle sticky top-0 [grid-area:main]">
🧹 Nitpick comments (1)
packages/features/troubleshooter/Troubleshooter.tsx (1)
21-23
: Nit: fix hook name typo for consistencyConsider renaming
useInitalizeTroubleshooterStore
touseInitializeTroubleshooterStore
for clarity and consistency across the codebase. If you proceed, update the import on Line 8 and all call sites.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/features/troubleshooter/Troubleshooter.tsx
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Always use
t()
for text localization in frontend code; direct text embedding should trigger a warning
Files:
packages/features/troubleshooter/Troubleshooter.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()
in hot paths like loops
Files:
packages/features/troubleshooter/Troubleshooter.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Atoms E2E Tests
🔇 Additional comments (2)
packages/features/troubleshooter/Troubleshooter.tsx (2)
1-1
: Import of BookerStoreProvider aligns with per-instance store objectiveThis import is correct and consistent with the PR goal to scope Booker store per subtree.
67-71
: Unconditional BookerStoreProvider is OK here — no nested Booker stores foundRepo scan shows Troubleshooter is only mounted from the dedicated page and not inside an existing Booker provider, so the unconditional provider does not create a double-wrap in current usage.
- packages/features/troubleshooter/Troubleshooter.tsx — wraps TroubleshooterComponent with (lines ~66–71).
- apps/web/modules/availability/troubleshoot/troubleshoot-view.tsx — dynamically imports and renders the Troubleshooter (TroubleshooterClientOnly); there is no upstream BookerStoreProvider at that integration point.
- BookerStoreProvider is used in other places (e.g. packages/platform/atoms/booker/BookerWebWrapper.tsx, packages/platform/atoms/booker/BookerPlatformWrapper.tsx) but those files do not render Troubleshooter.
Conclusion: No change required for this PR. If you plan to render Troubleshooter inside an already-wrapped Booker subtree in the future, consider a provider-if-missing / boundary pattern in a follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/features/bookings/Booker/components/Section.tsx (1)
47-54
: Rename local variable to avoid confusion with framer-motion’slayout
propUsing
layout
as a local variable while also setting thelayout
prop on<m.div>
is easy to misread. Rename the local variable for clarity.- const layout = useBookerStoreContext((state) => state.layout); + const currentLayout = useBookerStoreContext((state) => state.layout); @@ - gridClassName = gridAreaClassNameMap[area[layout] || area.default]; + gridClassName = gridAreaClassNameMap[area[currentLayout] || area.default];
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/features/bookings/Booker/components/Section.tsx
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Always use
t()
for text localization in frontend code; direct text embedding should trigger a warning
Files:
packages/features/bookings/Booker/components/Section.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()
in hot paths like loops
Files:
packages/features/bookings/Booker/components/Section.tsx
🧬 Code Graph Analysis (1)
packages/features/bookings/Booker/components/Section.tsx (1)
packages/features/bookings/Booker/BookerStoreProvider.tsx (1)
useBookerStoreContext
(24-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Production builds / Build Web App
- GitHub Check: Production builds / Build Docs
- GitHub Check: Production builds / Build Atoms
- GitHub Check: Production builds / Build API v2
- GitHub Check: Production builds / Build API v1
- GitHub Check: Tests / Unit
- GitHub Check: Type check / check-types
- GitHub Check: Linters / lint
- GitHub Check: Atoms E2E Tests
import { m } from "framer-motion"; | ||
import { forwardRef } from "react"; | ||
|
||
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Context store migration looks good; verify all usages are wrapped in BookerStoreProvider
Switching to useBookerStoreContext
is correct for isolating store instances. However, this hook throws if no provider is present (see BookerStoreProvider.tsx Lines 23-34). Please verify every render path of BookerSection
sits under BookerStoreProvider
to avoid runtime errors in downstream apps, embeds, and examples.
Run this script to surface candidate files that reference BookerSection
without any obvious BookerStoreProvider
import/usage in the same file (heuristic; may include false positives if the provider is higher up the tree):
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find files that import or use BookerSection
files=$(rg -n --glob '!**/node_modules/**' -e 'import\s+{?\s*BookerSection\b' -e '<BookerSection\b' --no-heading | cut -d: -f1 | sort -u)
echo "Files referencing BookerSection:"
echo "$files" | sed 's/^/ - /'
echo
echo "Candidates to review (no explicit BookerStoreProvider reference in the same file):"
while read -r f; do
if [[ -z "$f" ]]; then continue; fi
if rg -q 'BookerStoreProvider|<BookerStoreProvider' "$f"; then
echo "[OK] $f"
else
echo "[CHECK] $f"
fi
done <<< "$files"
Length of output: 301
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find files that reference BookerSection (import, JSX usage, or any mention)
mapfile -t files < <(rg --hidden --glob '!**/node_modules/**' -n -l 'BookerSection' || true)
if [ ${#files[@]} -eq 0 ]; then
echo "No files referencing BookerSection found."
exit 0
fi
echo "Files referencing BookerSection:"
for f in "${files[@]}"; do
echo " - $f"
done
echo
echo "Candidates to review (no explicit BookerStoreProvider reference in the same file):"
for f in "${files[@]}"; do
if rg -n --hidden --glob '!**/node_modules/**' -e 'BookerStoreProvider' -e '<BookerStoreProvider' -- "$f" >/dev/null; then
echo "[OK] $f"
else
echo "[CHECK] $f"
fi
done
Length of output: 211
Run a robust search for BookerSection usages and whether BookerStoreProvider is present
Previous attempt hit a shell /dev/fd issue — run the script below to re-check.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find files that reference BookerSection (import, JSX usage, or any mention)
files=$(rg --hidden --glob '!**/node_modules/**' -n -l 'BookerSection' || true)
if [ -z "$files" ]; then
echo "No files referencing BookerSection found."
exit 0
fi
echo "Files referencing BookerSection:"
echo "$files" | sed 's/^/ - /'
echo
echo "Candidates to review (no explicit BookerStoreProvider reference in the same file):"
echo "$files" | while IFS= read -r f; do
if [ -z "$f" ]; then continue; fi
if rg -n --hidden --glob '!**/node_modules/**' -e 'BookerStoreProvider' -e '<BookerStoreProvider' -- "$f" >/dev/null 2>&1; then
echo "[OK] $f"
else
echo "[CHECK] $f"
fi
done
Length of output: 2407
Wrap or mock BookerSection usages outside BookerStoreProvider — mock needs a fix
I searched the repo for BookerSection usages. Results:
- packages/features/bookings/Booker/Booker.tsx — [OK] (contains BookerStoreProvider)
- packages/features/bookings/Booker/components/Section.tsx — [OK]
- packages/features/bookings/Booker/components/mocks/Section.tsx — [CHECK] (no BookerStoreProvider found)
Action required: update the mock at packages/features/bookings/Booker/components/mocks/Section.tsx to either wrap the mock export with BookerStoreProvider or mock useBookerStoreContext (so the hook cannot throw during tests/renders).
🤖 Prompt for AI Agents
In packages/features/bookings/Booker/components/__mocks__/Section.tsx around
line 5, the mock for BookerSection can call useBookerStoreContext outside a
BookerStoreProvider causing the hook to throw in tests; update the mock to
either wrap the mocked component export with BookerStoreProvider (so consumers
render inside the provider) or replace the mock implementation to stub
useBookerStoreContext (e.g., jest.mock the hook to return a safe default) so the
hook is never invoked against a missing provider during tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested the web app booker, and everything is still working as it should
What does this PR do?
Booker
atom wherein previously we were not able to use multiple instance of theBooker
widget on the same page. If we would use multipleBooker
widgets the state would change in both of them even though one of them would be toggledBooker
atom widget uses a store of its own instead of sharing a global instance that all the widgets can access which was the previous approachVisual Demo:
Screen.Recording.2025-08-11.at.6.14.02.PM.mov
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
For the web app these are the steps:
For platform these are the steps:
booking.tsx
in the examples app and copy the code for Booker atom here and paste it just under to make another instance of the sameBooker
widget appearBooker
atom widgets you need to either comment out the prop allowUpdatingUrlParams or set it to false