[WEB-4841] chore: calendar component migration UI to propel#7730
[WEB-4841] chore: calendar component migration UI to propel#7730sriramveeraghanta merged 7 commits intopreviewfrom
Conversation
|
Pull Request Linked with Plane Work Items
Comment Automatically Generated by Plane |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughCalendar UI and DayPicker styles were moved from the UI package into a new Propel calendar module. Web app imports were switched to Changes
Sequence Diagram(s)sequenceDiagram
participant Web as apps/web components
participant Propel as @plane/propel/calendar
participant RDP as react-day-picker
Note over Web,Propel: New import routing
Web->>Propel: import { Calendar, Matcher, DateRange } from "@plane/propel/calendar"
Propel->>RDP: render DayPicker with forwarded props
Propel->>Propel: apply startMonth/endMonth, chevrons, classNames defaults
Propel-->>Web: rendered calendar UI (DayPicker output)
Note over UI,Web: Old flow removed
UI--xWeb: previously exported Calendar (deleted)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
✨ 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. Comment |
There was a problem hiding this comment.
Pull Request Overview
This PR migrates the Calendar component from @plane/ui to @plane/propel package, consolidating dependencies and improving component organization.
- Moved Calendar component from @plane/ui to @plane/propel with react-day-picker dependency
- Updated all import statements across the web app to use the new calendar location
- Added comprehensive Storybook stories for the calendar component
Reviewed Changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/src/index.ts | Removed calendar export from UI package |
| packages/ui/src/calendar.tsx | Deleted old calendar component implementation |
| packages/propel/tsdown.config.ts | Added calendar to build configuration |
| packages/propel/src/calendar/root.tsx | New calendar component implementation in propel |
| packages/propel/src/calendar/index.ts | Calendar component exports and type re-exports |
| packages/propel/src/calendar/calendar.stories.tsx | Comprehensive Storybook stories for calendar |
| packages/propel/package.json | Added calendar export and react-day-picker dependency |
| packages/propel/.storybook/preview.ts | Added calendar styles to Storybook |
| apps/web/package.json | Removed react-day-picker dependency from web app |
| apps/web/core/components/inbox/modals/snooze-issue-modal.tsx | Updated calendar import |
| apps/web/core/components/dropdowns/date.tsx | Updated calendar and types import |
| apps/web/core/components/dropdowns/date-range.tsx | Updated calendar and types import |
| apps/web/core/components/core/filters/date-filter-modal.tsx | Updated calendar import |
| apps/web/app/(all)/layout.tsx | Updated calendar styles import |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/core/components/core/filters/date-filter-modal.tsx (1)
47-50: Bug: date2 watches date1, breaking range validation
date2is incorrectly derived fromwatch("date1"), soisInvalidcan’t detect inverted ranges.Apply this diff:
- const date1 = getDate(watch("date1")); - const date2 = getDate(watch("date1")); + const date1 = getDate(watch("date1")); + const date2 = getDate(watch("date2"));apps/web/core/components/inbox/modals/snooze-issue-modal.tsx (1)
67-73: Fix undefined close() call in onClick.close() is not defined in scope; call the provided handleClose() to dismiss the modal before confirming.
- <Button + <Button variant="primary" onClick={() => { - close(); + handleClose(); onConfirm(date); }} >
🧹 Nitpick comments (10)
packages/propel/package.json (1)
52-52: Version ownership: pin vs. catalogHaving Propel own
react-day-picker@9.5.0is fine. If you prefer centralized versioning like other deps (catalog:), consider moving this to the repo catalog for consistency. Optional.apps/web/core/components/core/filters/date-filter-modal.tsx (1)
97-110: Minor: dedupe repeated Calendar classNamesThe
classNames.rootstring is duplicated. Consider extracting to a constant to keep it DRY.+ const calendarRootClass = "border border-custom-border-200 p-3 rounded-md"; ... <Calendar - classNames={{ root: ` border border-custom-border-200 p-3 rounded-md` }} + classNames={{ root: calendarRootClass }} ... /> ... <Calendar - classNames={{ root: ` border border-custom-border-200 p-3 rounded-md` }} + classNames={{ root: calendarRootClass }} ... />Also applies to: 122-135
apps/web/core/components/inbox/modals/snooze-issue-modal.tsx (1)
51-66: Optional: avoid shadowing and redundant cloning.The onSelect parameter date shadows the state variable date, and selected/defaultMonth re-wrap date with new Date(..) unnecessarily. Not a bug, but minor cleanup possible.
- onSelect={(date) => { - if (!date) return; - setDate(date); + onSelect={(d) => { + if (!d) return; + setDate(d); }} - selected={date ? new Date(date) : undefined} - defaultMonth={date ? new Date(date) : undefined} + selected={date ?? undefined} + defaultMonth={date ?? undefined}packages/propel/src/calendar/root.tsx (2)
21-33: Tighten Chevron override (destructure orientation, clearer props).Readability: destructure orientation directly and pass the rest explicitly.
- components={{ - Chevron: ({ className, ...props }) => ( - <ChevronLeft - className={cn( - "size-4", - { "rotate-180": props.orientation === "right", "-rotate-90": props.orientation === "down" }, - className - )} - {...props} - /> - ), - }} + components={{ + Chevron: ({ className, orientation, ...rest }) => ( + <ChevronLeft + className={cn( + "size-4", + { "rotate-180": orientation === "right", "-rotate-90": orientation === "down" }, + className + )} + {...rest} + /> + ), + }}
19-35: Remove redundant prop and clarify end-of-window var name.
- weekStartsOn is already included via {...props}; the explicit prop is redundant.
- Rename thirtyYearsFromNowFirstDay to thirtyYearsFromNowLastDay to reflect Dec 31.
export const Calendar = ({ className, showOutsideDays = true, ...props }: CalendarProps) => { const currentYear = new Date().getFullYear(); const thirtyYearsAgoFirstDay = new Date(currentYear - 30, 0, 1); - const thirtyYearsFromNowFirstDay = new Date(currentYear + 30, 11, 31); + const thirtyYearsFromNowLastDay = new Date(currentYear + 30, 11, 31); return ( <DayPicker showOutsideDays={showOutsideDays} className={cn("p-3", className)} - weekStartsOn={props.weekStartsOn} components={{ Chevron: ({ className, ...props }) => ( <ChevronLeft className={cn( "size-4", { "rotate-180": props.orientation === "right", "-rotate-90": props.orientation === "down" }, className )} {...props} /> ), }} startMonth={thirtyYearsAgoFirstDay} - endMonth={thirtyYearsFromNowFirstDay} + endMonth={thirtyYearsFromNowLastDay} {...props} /> ); }packages/propel/src/calendar/calendar.stories.tsx (5)
1-4: Import from the public barrel and pull in reusable typesStories should exercise the public API and reuse exported types.
-import { Calendar } from "./root"; +import { Calendar, type DateRange, type Matcher } from ".";
41-44: Use the DateRange type for stronger typingAligns with the Calendar/DayPicker API and simplifies future refactors.
- const [dateRange, setDateRange] = useState<{ from?: Date; to?: Date }>({ + const [dateRange, setDateRange] = useState<DateRange | undefined>({ from: new Date(), to: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days from now });
73-75: Guard onSelect against undefined in multiple modeDayPicker may call onSelect with undefined; keep state consistent.
- onSelect={setDates} + onSelect={(v) => setDates(v ?? [])}
86-90: Type disabledDays for better safety and editor helpAnnotate as Matcher[] to match DayPicker’s matcher API.
- const disabledDays = [ + const disabledDays: Matcher[] = [ { dayOfWeek: [0, 6] }, // Sunday and Saturday new Date(2024, 4, 10), // May 10, 2024 ];
21-143: Optional: make stories time-stableDirect new Date() calls can cause flaky visuals over time. Consider a fixed “today” (e.g., via a const or Storybook global/arg) to stabilize snapshots.
📜 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 sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
apps/web/app/(all)/layout.tsx(1 hunks)apps/web/core/components/core/filters/date-filter-modal.tsx(1 hunks)apps/web/core/components/dropdowns/date-range.tsx(1 hunks)apps/web/core/components/dropdowns/date.tsx(1 hunks)apps/web/core/components/inbox/modals/snooze-issue-modal.tsx(1 hunks)apps/web/package.json(0 hunks)packages/propel/.storybook/preview.ts(1 hunks)packages/propel/package.json(3 hunks)packages/propel/src/calendar/calendar.stories.tsx(1 hunks)packages/propel/src/calendar/index.ts(1 hunks)packages/propel/src/calendar/root.tsx(1 hunks)packages/propel/tsdown.config.ts(1 hunks)packages/ui/src/calendar.tsx(0 hunks)packages/ui/src/index.ts(0 hunks)
💤 Files with no reviewable changes (3)
- packages/ui/src/index.ts
- apps/web/package.json
- packages/ui/src/calendar.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-04T16:22:44.344Z
Learnt from: lifeiscontent
PR: makeplane/plane#7164
File: packages/ui/.storybook/main.ts:24-47
Timestamp: 2025-06-04T16:22:44.344Z
Learning: In packages/ui/.storybook/main.ts, the webpackFinal function intentionally overrides the CSS loader strategy by finding and replacing existing CSS rules. This is a temporary workaround for a known upstream issue in Storybook's CSS handling that has been communicated to the Storybook maintainers. The current implementation should not be changed until the upstream issue is resolved.
Applied to files:
packages/propel/.storybook/preview.ts
🧬 Code graph analysis (1)
packages/propel/src/calendar/calendar.stories.tsx (1)
packages/propel/src/calendar/root.tsx (1)
Calendar(11-38)
🪛 GitHub Check: Build and lint web apps
packages/propel/src/calendar/calendar.stories.tsx
[failure] 5-5:
An interface declaring no members is equivalent to its supertype
⏰ 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). (1)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (12)
apps/web/app/(all)/layout.tsx (1)
8-8: No lingering old stylesheet references; migration complete
Verified there are no imports or files for the old react-day-picker.css and all consumers now import from @plane/propel/styles/react-day-picker.packages/propel/.storybook/preview.ts (1)
3-3: Storybook CSS import is appropriate for local devImporting from src keeps HMR tight for Storybook. No change requested.
packages/propel/package.json (2)
21-34: Exports and copy step verified
Calendar and react-day-picker CSS subpath exports match their dist paths and tsdown copiessrc/stylesas configured, so importing the CSS subpath will work.
41-57: Confirm noreact-day-pickerusage in apps/web
No references found inapps/web/package.jsonor code imports/requires; safe to merge.packages/propel/tsdown.config.ts (2)
7-8: Calendar entry added to build surface—LGTMIncluding
src/calendar/index.tsensures types and JS ship properly.
27-28: Verify CSS export path Ensure that after running the build,react-day-picker.cssis located atpackages/propel/dist/styles/react-day-picker.css.apps/web/core/components/core/filters/date-filter-modal.tsx (1)
8-9: Import migration to Propel calendar looks correctDecouples the app from UI’s calendar. Button import separation is fine.
apps/web/core/components/inbox/modals/snooze-issue-modal.tsx (2)
7-8: Calendar import migration looks good.Importing Calendar from @plane/propel/calendar and keeping Button from @plane/ui aligns with the package split.
1-85: Resolve DayPicker style and dependency checks: CSS import from @plane/propel/styles/react-day-picker is present, no direct react-day-picker imports or Calendar from @plane/ui remain, and the react-day-picker dependency has been moved to packages/propel.packages/propel/src/calendar/index.ts (1)
1-2: Barrel and type re-exports look correct.Re-exporting Calendar and the Matcher/DateRange types from one place simplifies app imports.
apps/web/core/components/dropdowns/date-range.tsx (1)
12-13: Good consolidation of imports via Propel.Pulling Calendar, DateRange, and Matcher from @plane/propel/calendar removes the app’s direct coupling to react-day-picker.
apps/web/core/components/dropdowns/date.tsx (1)
8-9: Import migration looks good.Using Calendar and Matcher from @plane/propel/calendar with ComboDropDown from @plane/ui matches the new split.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/propel/src/calendar/calendar.stories.tsx (4)
7-16: Adopt Storybook “satisfies” typing and drop the local props aliasUsing the v7/v8-recommended “satisfies” pattern improves type safety and eliminates the need for a local CalendarProps alias and explicit generic wiring.
-import { ComponentProps, useState } from "react"; -import type { Meta, StoryObj } from "@storybook/react-vite"; +import { useState } from "react"; +import type { Meta, StoryObj } from "@storybook/react"; -import { Calendar } from "./root"; +import { Calendar } from "."; // mirror consumer import surface -type CalendarProps = ComponentProps<typeof Calendar>; -const meta: Meta<CalendarProps> = { +const meta = { title: "Components/Calendar", component: Calendar, parameters: { layout: "centered", }, - args: { - showOutsideDays: true, - }, -}; +} satisfies Meta<typeof Calendar>; export default meta; -type Story = StoryObj<CalendarProps>; +type Story = StoryObj<typeof meta>; export const Default: Story = { args: {}, - render: (args: CalendarProps) => { + render: (args) => {Also applies to: 19-20, 23-23
3-3: Import from the barrel index instead of './root'Stories should import the public surface to match consumer usage and catch export/packaging issues earlier.
-import { Calendar } from "./root"; +import { Calendar } from ".";
24-24: Stabilize the initial date to avoid flaky visual/snapshot diffsUsing “today” can cause story renders to change daily. Pick a fixed date for determinism in Chromatic/visual tests.
- const [date, setDate] = useState<Date | undefined>(new Date()); + const [date, setDate] = useState<Date | undefined>(new Date(2024, 0, 1));
21-32: Hooks insiderendermay trigger react-hooks lint in some setupsIf lint complains about hooks in inline render functions, move state into a small wrapper component or a
Templatefunction.Example:
const DefaultRender = (args: React.ComponentProps<typeof Calendar>) => { const [date, setDate] = useState<Date | undefined>(new Date(2024, 0, 1)); return ( <div className="p-4"> <Calendar {...args} mode="single" selected={date} onSelect={setDate} className="rounded-md border" /> </div> ); }; export const Default: Story = { render: DefaultRender };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/propel/src/calendar/calendar.stories.tsx(1 hunks)packages/propel/src/calendar/index.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/propel/src/calendar/index.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/propel/src/calendar/calendar.stories.tsx (1)
packages/propel/src/calendar/root.tsx (2)
CalendarProps(9-9)Calendar(11-38)
⏰ 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: Build and lint web apps
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/propel/src/calendar/calendar.stories.tsx (1)
5-5: Resolved lint warning: empty interface replaced with type alias — good fixThis addresses the earlier lint failure. No further action needed here.
…e#7730) * chore: move calendar components and dependencies * chore: update package configurations * chore: calendar import updated * chore: propel config updated * chore: propel calendar code refactor * chore: code refactor * fix: build error
Description
This PR includes the following changes:
Type of Change
Media
Summary by CodeRabbit