Skip to content

Conversation

@dioo1461
Copy link
Contributor

@dioo1461 dioo1461 commented Feb 9, 2025

#️⃣ 연관된 이슈>

📝 작업 내용> 이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

  • Date Picker를 리팩터링했습니다.
  • 이제 다음 상태들을 외부에서 주입받습니다.
    interface commonProps {
      baseDate: Date;
      handleBaseDateChange: (date: Date) => void;
      gotoNextMonth: () => void;
      gotoPrevMonth: () => void;
      trigger?: ReactNode;
    }
    
    // <DatePicker.Select />
    interface selectProps extends commonProps {
      selectedDate: Date | null;
      handleDateSelect: (date: Date | null) => void;
    }
    
    // <DatePicker.Range />
    interface rangeProps extends commonProps {
      highlightRange: HighlightRange;
      setHighlightStart: (date: Date | null) => void;
      setHighlightEnd: (date: Date | null) => void;
    }

🙏 여기는 꼭 봐주세요! > 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

Summary by CodeRabbit

  • New Features

    • Introduced DatePicker.Select and DatePicker.Range components for enhanced date selection options.
    • Added a new DatePickerRange component for managing date ranges with improved context management.
    • Implemented a new useDatePickerRange hook for handling date range selection logic.
    • Added a new useDatePickerSelect hook for managing date selection within the date picker component.
    • Introduced SharedCalendarContext for managing shared calendar information across components.
  • Bug Fixes

    • Simplified date selection logic in the DateCell component for better performance and clarity.
  • Refactor

    • Restructured the date picker components to improve modularity and maintainability.
    • Updated context usage across components for streamlined state management.
  • Documentation

    • Enhanced story definitions for the DatePicker component to improve clarity and usability in demonstrations.

@dioo1461 dioo1461 added the 🖥️ FE Frontend label Feb 9, 2025
@dioo1461 dioo1461 added this to the 2️⃣ 2차 스프린트 milestone Feb 9, 2025
@dioo1461 dioo1461 self-assigned this Feb 9, 2025
@dioo1461 dioo1461 changed the title [FE-Feat] [FE-Feat] Date Picker 리팩터링 Feb 9, 2025
@softeer5th softeer5th deleted a comment from coderabbitai bot Feb 9, 2025
@dioo1461 dioo1461 marked this pull request as ready for review February 10, 2025 01:40
@dioo1461 dioo1461 requested a review from hamo-o as a code owner February 10, 2025 01:40
@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2025

Walkthrough

The changes reorganize and refactor the DatePicker component and its related functionality. The main DatePicker now delegates rendering to its subcomponents DatePicker.Select and DatePicker.Range, with the corresponding stories updated. Obsolete context and hooks (e.g., useDatePicker, useDateSelect) have been removed, and new hooks (useDatePickerRange and useDatePickerSelect) are introduced. Internal names and types have been standardized (e.g., CalendarType → DatePickerType) and import paths adjusted, while table components and the Header have been updated to use the revised context and naming conventions.

Changes

File(s) Change Summary
frontend/src/components/DatePicker/DatePicker.stories.tsx Updated story definitions for DatePicker: meta now references DatePicker.Select, removed argTypes, and added new Default and Range story functions using custom hooks for navigation and highlighting.
frontend/src/components/DatePicker/DatePickerContext.ts
frontend/src/components/DatePicker/context/DatePickerContext.ts
Removed old DatePickerContext file and introduced a new context with DatePickerContextProps in a reorganized path.
frontend/src/components/DatePicker/DatePickerRange.tsx
frontend/src/components/DatePicker/DatePickerSelect.tsx
Added new components DatePickerRange and DatePickerSelect with dedicated interfaces, state management, and context providers for range and select functionalities.
frontend/src/components/DatePicker/Header/index.tsx Updated import paths for DatePickerContext and renamed month navigation methods from goToPrevMonth/goToNextMonth to gotoPrevMonth/gotoNextMonth.
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx
frontend/src/components/DatePicker/Table/Row.tsx
frontend/src/components/DatePicker/Table/TableBody.tsx
Refactored table components to use updated context, renamed CalendarType to DatePickerType, and adjusted props (weekDates instead of week and removed selectedDate) while streamlining date cell click handling.
frontend/src/components/DatePicker/index.tsx Restructured main DatePicker component: removed direct rendering logic (throws error now) and added subcomponents DatePicker.Select and DatePicker.Range; updated related types and interfaces.
frontend/src/hooks/useDatePicker/index.ts
frontend/src/hooks/useDatePicker/useDatePickerRange.ts
frontend/src/hooks/useDatePicker/useDatePickerSelect.ts
frontend/src/hooks/useDatePicker/useDateSelect.ts
frontend/src/hooks/useDatePicker/useHighlightRange.ts
frontend/src/hooks/useDatePicker/useMonthNavigation.ts
Removed deprecated hooks (useDatePicker, useDateSelect); added new hooks (useDatePickerRange, useDatePickerSelect); and updated useHighlightRange and useMonthNavigation (renamed methods and allowed an initialBaseDate parameter).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DatePickerSelect
    participant useDatePickerSelect
    participant useMonthNavigation

    User->>DatePickerSelect: Clicks date cell
    DatePickerSelect->>useDatePickerSelect: onDateCellClick(date)
    useDatePickerSelect->>useMonthNavigation: Validate date & update baseDate
    useMonthNavigation-->>useDatePickerSelect: Return updated calendar dates
    useDatePickerSelect-->>DatePickerSelect: Return selectedDate and highlight info
    DatePickerSelect->>User: Render updated date selection
Loading
sequenceDiagram
    participant User
    participant DatePickerRange
    participant useDatePickerRange
    participant useHighlightRange

    User->>DatePickerRange: Clicks date cell for range selection
    DatePickerRange->>useDatePickerRange: onDateCellClick(date)
    useDatePickerRange->>useHighlightRange: Update highlightRange (start/end)
    useHighlightRange-->>useDatePickerRange: Return updated range data
    DatePickerRange->>User: Render selected date range
Loading

Suggested reviewers

  • hamo-o

Poem

I'm a little bunny on a code crusade,
Hopping through changes that developers made.
New hooks and context light my way,
Date pickers now dance in a fresh new display.
Carrots and code, we'll celebrate today! 🐰

With every line, my joy won't fade.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (11)
frontend/src/components/DatePicker/index.tsx (2)

2-2: Consider importing ReactNode directly from 'react'.
It's unusual to import ReactNode from '@tanstack/react-router'. This might introduce confusion and possible version mismatches.


9-14: Interface looks good.
The new CommonDatePickerProps aligns with a modular approach. Consider adding brief JSDoc comments explaining each prop.

frontend/src/components/DatePicker/context/DatePickerContext.ts (1)

7-14: Unified interface for both select and range modes.
While it’s convenient to have a single interface, consider documenting which properties apply uniquely to “select” vs. “range.”

frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1)

8-8: Consider keeping method names consistent with common patterns.

The method names have been changed from goToPrevMonth/goToNextMonth to gotoPrevMonth/gotoNextMonth. While both are valid, the previous naming convention with "To" is more commonly used in React components.

-  const gotoPrevMonth = () => {
+  const goToPrevMonth = () => {
-  const gotoNextMonth = () => {
+  const goToNextMonth = () => {

Also applies to: 13-13

frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1)

25-44: Consider adding validation for date range boundaries.

The click handling logic could benefit from additional validation to ensure the date range doesn't exceed reasonable boundaries (e.g., maximum range of 1 year).

 const onDateCellClick = (date: Date) => {
   const { start, end } = highlightRange;
+  const MAX_RANGE_DAYS = 365;
+
   if (!start) {
     setHighlightStart(date);
     return;
   }
 
   if (!end) {
     if (date < start) {
       setHighlightStart(date);
     } else if (isSameDate(date, start)) {
       setHighlightStart(null);
     } else {
+      const daysDifference = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
+      if (daysDifference > MAX_RANGE_DAYS) {
+        return;
+      }
       setHighlightEnd(date);
     }
     return;
   }
   setHighlightStart(null);
   setHighlightEnd(null);
 };
frontend/src/components/DatePicker/DatePickerSelect.tsx (1)

35-37: Consider adding ARIA attributes for better accessibility.

The trigger div should have appropriate ARIA attributes to indicate it's an expandable/collapsible element.

-      <div onClick={() => setIsOpen(prev => !prev)}>
+      <div 
+        onClick={() => setIsOpen(prev => !prev)}
+        role="button"
+        aria-expanded={isOpen}
+        aria-haspopup="true"
+      >
frontend/src/hooks/useDatePicker/useDatePickerSelect.ts (1)

46-48: Consider extracting week range calculation to a utility function.

The week range calculation logic could be moved to a separate utility function for better reusability.

+const getWeekRange = (date: Date): [Date, Date] => {
+  const weekDates = formatDateToWeekDates(date);
+  return [weekDates[0], weekDates[weekDates.length - 1]];
+};

-    const weekDates = formatDateToWeekDates(date);
-    const [startOfWeek, endOfWeek] = [weekDates[0], weekDates[weekDates.length - 1]];
+    const [startOfWeek, endOfWeek] = getWeekRange(date);
frontend/src/components/DatePicker/DatePickerRange.tsx (1)

12-12: Rename interface to follow naming convention.

The interface name should be suffixed with Props to maintain consistency with TypeScript naming conventions.

-export interface DatePickerRange extends CommonDatePickerProps {
+export interface DatePickerRangeProps extends CommonDatePickerProps {
frontend/src/components/DatePicker/Table/Row.tsx (1)

17-17: Consider maintaining consistent naming.

While the local rename from weekDates to week minimizes internal changes, it introduces two different names for the same data. Consider using weekDates consistently throughout to improve code clarity.

-const Row = ({ weekDates: week, baseDate }: RowProps) => {
+const Row = ({ weekDates, baseDate }: RowProps) => {
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (2)

47-48: Consider extracting disabled state logic.

The disabled state logic could be moved to a helper function for better reusability and maintainability.

+const isDateCellDisabled = (calendarType: DatePickerType, dateCellType: DateCellType) =>
+  calendarType === 'range' && dateCellType === 'otherMonth';

 const DateCell = ({ ... }: DateCellProps) => {
   ...
-  const isDisabled = calendarType === 'range' && dateCellType === 'otherMonth';
+  const isDisabled = isDateCellDisabled(calendarType, dateCellType);
   ...
 };

77-77: Address TODO: Implement holiday checking.

The TODO comment indicates missing functionality for holiday checking. This is important for proper date cell styling.

Would you like me to help implement the holiday checking functionality? I can suggest an implementation that:

  1. Accepts a list of holiday dates
  2. Checks if a given date is in the holiday list
  3. Applies appropriate styling
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95751d0 and 0be9e47.

📒 Files selected for processing (16)
  • frontend/src/components/DatePicker/DatePicker.stories.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerContext.ts (0 hunks)
  • frontend/src/components/DatePicker/DatePickerRange.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerSelect.tsx (1 hunks)
  • frontend/src/components/DatePicker/Header/index.tsx (3 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (4 hunks)
  • frontend/src/components/DatePicker/Table/Row.tsx (2 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/context/DatePickerContext.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/hooks/useDatePicker/index.ts (0 hunks)
  • frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDatePickerSelect.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDateSelect.ts (0 hunks)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • frontend/src/components/DatePicker/DatePickerContext.ts
  • frontend/src/hooks/useDatePicker/useDateSelect.ts
  • frontend/src/hooks/useDatePicker/index.ts
🧰 Additional context used
📓 Learnings (1)
frontend/src/components/DatePicker/Header/index.tsx (2)
Learnt from: hamo-o
PR: softeer5th/Team4-enDolphin#80
File: frontend/src/components/Calendar/Header/CalendarHeader.tsx:8-24
Timestamp: 2025-02-04T03:20:42.270Z
Learning: The codebase uses `useSafeContext` hook to handle React context error cases by throwing an error with message "🚨 이 컴포넌트는 ContextProvider 내부에서 사용되어야 합니다!" when the context value is null.
Learnt from: hamo-o
PR: softeer5th/Team4-enDolphin#80
File: frontend/src/components/Calendar/Header/CalendarHeader.tsx:8-24
Timestamp: 2025-02-04T03:20:42.270Z
Learning: The codebase uses `useSafeContext` hook to handle React context error cases, making explicit null checks redundant.
🔇 Additional comments (26)
frontend/src/components/DatePicker/index.tsx (4)

4-5: Neat import of subcomponents.
Importing DatePickerRange and DatePickerSelect here neatly organizes the subcomponents.


7-7: Clearer naming.
Renaming to 'DatePickerType' clearly differentiates the component's usage modes.


17-18: Explicit usage guidance.
Throwing an error is a direct way to ensure the user selects the correct subcomponent. This aligns with an "API guard" pattern.


21-22: Attaching subcomponents provides flexibility.
Exposing “Select” and “Range” on the main object is a convenient pattern for multiple variant usage.

frontend/src/components/DatePicker/Table/TableBody.tsx (4)

4-4: Updated import path.
Switching to the new context path is consistent with the refactoring.


8-8: Destructuring context usage.
Pulling out calendarDates and baseDate from the context is clean; consider verifying if any additional props are needed.


14-14: Renamed prop.
Renaming “week” to “weekDates” improves clarity.


20-20: Maintain default export.
Exporting as default is straightforward and matches typical usage patterns.

frontend/src/components/DatePicker/context/DatePickerContext.ts (2)

1-4: Clean imports.
Importing React's createContext and the external types sets a clear foundation for the new context.


16-16: Context creation.
Creating the DatePickerContext with a null default aligns well with a safe context strategy.

frontend/src/hooks/useDatePicker/useHighlightRange.ts (2)

11-12: LGTM! Type annotation improves type safety.

The addition of the HighlightRange type annotation for the prev parameter enhances type safety and code clarity.


17-17: LGTM! Exposing setHighlightRange provides more flexibility.

Including setHighlightRange in the return object gives consumers more control over the highlight range state.

frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1)

5-6: LGTM! Improved initialization flexibility.

The addition of the optional initialBaseDate parameter and its usage in state initialization provides better control over the initial state.

frontend/src/components/DatePicker/DatePicker.stories.tsx (1)

20-32: LGTM! Well-structured stories demonstrating component usage.

The stories effectively demonstrate the usage of both Select and Range variants of the DatePicker component, including state management and event handling.

Also applies to: 34-45

frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1)

20-23: LGTM! Efficient calendar generation with useMemo.

The use of useMemo for calendar generation optimizes performance by preventing unnecessary recalculations.

frontend/src/components/DatePicker/DatePickerSelect.tsx (1)

39-39: 🏁 Script executed:

#!/bin/bash
fd -e css -e scss "DatePickerSelect" 

Length of output: 36

frontend/src/components/DatePicker/Header/index.tsx (1)

12-12: LGTM! Good use of useSafeContext.

The component correctly uses useSafeContext for context error handling, as established in the codebase.

frontend/src/components/DatePicker/DatePickerRange.tsx (2)

24-28: LGTM! Good use of useCallback.

The isDateSelected function is correctly memoized using useCallback with proper dependencies.


46-46: 🏁 Script executed:

#!/bin/bash
fd -e css -e scss "DatePickerRange"

Length of output: 35

frontend/src/components/DatePicker/Table/Row.tsx (4)

5-5: LGTM! Import path restructuring.

The updated import path follows a better organization pattern.


13-13: LGTM! Improved prop naming.

The rename from week to weekDates better describes the data being passed.


18-18: LGTM! Improved state management.

Moving date selection logic to the context aligns with the PR's objective of state injection and improves component modularity.


26-31: LGTM! Simplified DateCell props.

The change improves component modularity by delegating date selection state to the context.

frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (3)

4-4: LGTM! Improved imports organization.

The changes improve code organization by:

  • Centralizing theme variables
  • Using more structured import paths

Also applies to: 7-8


30-39: LGTM! Improved component structure.

The restructuring enhances code readability and aligns with the PR's state injection pattern.


52-53: LGTM! Improved interaction handling.

The changes properly handle disabled state in both visual feedback (cursor) and interaction (click handler).

export default meta;

type Story = StoryObj<typeof DatePicker>;
// export default meta;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate export statement.

Line 16 contains a duplicate export statement that should be removed.

- // export default meta;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// export default meta;

return;
}

const { year, month } = getDateParts(baseDate!);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check for baseDate.

The non-null assertion (!) on baseDate could lead to runtime errors if baseDate is undefined.

-    const { year, month } = getDateParts(baseDate!);
+    const currentDate = baseDate ?? new Date();
+    const { year, month } = getDateParts(currentDate);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { year, month } = getDateParts(baseDate!);
const currentDate = baseDate ?? new Date();
const { year, month } = getDateParts(currentDate);

Comment on lines +103 to +109
const getTodayCellStyle = (calendarType: DatePickerType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
: { };

const getSelectedCellStyle = (calendarType: DatePickerType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
: { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in color variable name.

There's a typo in the color variable name: 'Netural' should be 'Neutral'.

-  ? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
+  ? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] }
-  : { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] };
+  : { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getTodayCellStyle = (calendarType: DatePickerType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
: { };
const getSelectedCellStyle = (calendarType: DatePickerType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
: { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] };
const getTodayCellStyle = (calendarType: DatePickerType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] }
: { };
const getSelectedCellStyle = (calendarType: DatePickerType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
: { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] };

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
frontend/src/components/DatePicker/index.tsx (1)

21-23: Consider using English for error messages.

The error message is currently in Korean. Consider using English to ensure broader accessibility across development teams.

-  throw new Error('!!! DatePicker.Select, DatePicker.Range를 사용해주세요 !!!');
+  throw new Error('!!! Please use DatePicker.Select or DatePicker.Range !!!');
frontend/src/components/DatePicker/DatePicker.stories.tsx (1)

24-24: Consider explicit prop passing instead of spreading.

While spreading props works, explicitly passing them would make the component usage more transparent and maintainable.

// For Default story
-      {...monthNavigation}
+      baseDate={monthNavigation.baseDate}
+      gotoNextMonth={monthNavigation.gotoNextMonth}
+      gotoPrevMonth={monthNavigation.gotoPrevMonth}

// For Range story
-      {...monthNavigation}
-      {...highlightProps}
+      baseDate={monthNavigation.baseDate}
+      gotoNextMonth={monthNavigation.gotoNextMonth}
+      gotoPrevMonth={monthNavigation.gotoPrevMonth}
+      highlightRange={highlightProps.highlightRange}
+      setHighlightStart={highlightProps.setHighlightStart}
+      setHighlightEnd={highlightProps.setHighlightEnd}

Also applies to: 35-36

frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1)

25-44: Add validation for invalid date ranges.

The click handler should validate that the end date isn't more than a reasonable range (e.g., 1 year) from the start date to prevent invalid selections.

  const onDateCellClick = (date: Date) => {
    const { start, end } = highlightRange;
    if (!start) {
      setHighlightStart(date);
      return;
    }
  
    if (!end) {
+     const maxRange = new Date(start);
+     maxRange.setFullYear(maxRange.getFullYear() + 1);
+     
      if (date < start) {
        setHighlightStart(date);
+     } else if (date > maxRange) {
+       // Either show an error or set to max allowed date
+       return;
      } else if (isSameDate(date, start)) {
        setHighlightStart(null);
      } else {
        setHighlightEnd(date);
      }
      return;
    }
    setHighlightStart(null);
    setHighlightEnd(null);
  };
frontend/src/components/DatePicker/DatePickerRange.tsx (2)

11-15: LGTM! Consider adding JSDoc comments.

The interface is well-defined and extends CommonDatePickerProps appropriately with range-specific functionality.

Consider adding JSDoc comments to document the purpose of each prop:

 export interface DatePickerRange extends CommonDatePickerProps {
+  /** The currently selected date range */
   highlightRange: HighlightRange;
+  /** Callback to set the start date of the range */
   setHighlightStart: (date: Date | null) => void;
+  /** Callback to set the end date of the range */
   setHighlightEnd: (date: Date | null) => void;
 }

29-52: Add error boundaries and loading states.

The component should handle potential errors from useDatePickerRange and provide visual feedback during loading.

Consider wrapping the component with an error boundary and adding loading states:

import { ErrorBoundary } from '@/components/ErrorBoundary';

const DatePickerRange = ({ highlightRange, ...props }: DatePickerRange) => {
  // ... existing code ...

  if (calendarDates === undefined) {
    return <div>Loading...</div>;
  }

  return (
    <ErrorBoundary fallback={<div>Error loading date picker</div>}>
      <DatePickerContext.Provider>
        {/* ... existing JSX ... */}
      </DatePickerContext.Provider>
    </ErrorBoundary>
  );
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0be9e47 and d51ece4.

📒 Files selected for processing (10)
  • frontend/src/components/DatePicker/DatePicker.stories.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerRange.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerSelect.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Row.tsx (2 hunks)
  • frontend/src/components/DatePicker/context/DatePickerContext.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDatePickerSelect.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts
  • frontend/src/hooks/useDatePicker/useDatePickerSelect.ts
  • frontend/src/components/DatePicker/context/DatePickerContext.ts
  • frontend/src/components/DatePicker/Table/Row.tsx
🔇 Additional comments (5)
frontend/src/components/DatePicker/index.tsx (1)

7-19: Well-structured type definitions!

The interfaces are well-defined and align perfectly with the PR objectives. The CommonDatePickerProps interface provides a good foundation for both Select and Range variants.

frontend/src/components/DatePicker/DatePicker.stories.tsx (1)

16-39: Well-structured stories demonstrating component usage!

The stories effectively demonstrate both Select and Range variants with proper state management using custom hooks.

frontend/src/components/DatePicker/DatePickerSelect.tsx (1)

39-39: Define containerStyle using CSS modules or styled-components.

The containerStyle className suggests inline styles, which should be moved to a proper styling solution.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check if there's a styles file for the DatePicker
fd -e css -e scss -e styled.ts "DatePicker"

Length of output: 43


Issue: Missing proper styling definition for "containerStyle".

The search for any related styling file (CSS/SCSS/styled-components) for the DatePicker yielded no results. This means that the "containerStyle" class applied in the <div> is not backed by a proper CSS module or styled-component. Please update the code by either defining a CSS module (e.g., DatePickerSelect.module.css) and importing it or by refactoring the style into a styled-component.

frontend/src/components/DatePicker/DatePickerRange.tsx (2)

18-18: Review the isOpen state initialization logic.

The current initialization !props.trigger might lead to unexpected behavior. If trigger is undefined, the picker will always be open by default.

Consider explicitly checking for trigger existence:

-  const [isOpen, setIsOpen] = useState(!props.trigger);
+  const [isOpen, setIsOpen] = useState(props.trigger === undefined);

54-54: LGTM!

The default export is appropriate for this component.

Comment on lines 20 to 23
const calendarDates = useMemo(
() => generateMonthCalendar(baseDate ?? new Date()),
[baseDate],
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider timezone-safe default date handling.

Using new Date() directly might cause timezone-related issues. Consider using a utility function that ensures consistent timezone handling.


🏁 Script executed:

#!/bin/bash
# Check if there's a date utility function in the codebase
rg -l "new Date\(\)" --type ts

Length of output: 384


Timezone Safety Concern in Default Date Initialization

  • In frontend/src/hooks/useDatePicker/useDatePickerRange.ts (lines 20–23), the fallback uses new Date() directly, which can introduce timezone inconsistencies.
  • A search confirmed similar usage in several files, suggesting a systemic reliance on new Date() without any wrapper or utility ensuring consistent, timezone-safe date handling.
  • Consider refactoring this and related instances (e.g., in useCalendar.ts, DateCell.tsx, etc.) to a common utility that guarantees proper timezone adjustments.

Comment on lines 35 to 37
<div onClick={() => setIsOpen(prev => !prev)}>
{props.trigger}
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add accessibility attributes to the trigger.

The click handler div should have proper accessibility attributes for better keyboard navigation and screen reader support.

-      <div onClick={() => setIsOpen(prev => !prev)}>
+      <div
+        role="button"
+        tabIndex={0}
+        aria-expanded={isOpen}
+        aria-haspopup="true"
+        onClick={() => setIsOpen(prev => !prev)}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault();
+            setIsOpen(prev => !prev);
+          }
+        }}
+      >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div onClick={() => setIsOpen(prev => !prev)}>
{props.trigger}
</div>
<div
role="button"
tabIndex={0}
aria-expanded={isOpen}
aria-haspopup="true"
onClick={() => setIsOpen(prev => !prev)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(prev => !prev);
}
}}
>
{props.trigger}
</div>

Comment on lines 38 to 43
{isOpen && (
<div className='containerStyle'>
<Header />
<Table />
</div>
)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add click outside handling for the dropdown.

The date picker should close when clicking outside to follow standard dropdown behavior.

+import { useEffect, useRef } from 'react';
+
 const DatePickerSelect = ({ selectedDate, ...props }: DatePickerSelectProps) => {
   const [isOpen, setIsOpen] = useState(!props.trigger);
+  const containerRef = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    const handleClickOutside = (event: MouseEvent) => {
+      if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
+        setIsOpen(false);
+      }
+    };
+
+    document.addEventListener('mousedown', handleClickOutside);
+    return () => document.removeEventListener('mousedown', handleClickOutside);
+  }, []);

   // ... rest of the component

   return (
     <DatePickerContext.Provider value={...}>
       <div onClick={...}>{props.trigger}</div>
       {isOpen && (
-        <div className='containerStyle'>
+        <div ref={containerRef} className='containerStyle'>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{isOpen && (
<div className='containerStyle'>
<Header />
<Table />
</div>
)}
import { useEffect, useRef } from 'react';
import { useState } from 'react';
// ... other necessary imports
const DatePickerSelect = ({ selectedDate, ...props }: DatePickerSelectProps) => {
const [isOpen, setIsOpen] = useState(!props.trigger);
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
// ... rest of the component
return (
<DatePickerContext.Provider value={/* ... context value ... */}>
<div onClick={/* ... click handler logic ... */}>{props.trigger}</div>
{isOpen && (
<div ref={containerRef} className='containerStyle'>
<Header />
<Table />
</div>
)}
</DatePickerContext.Provider>
);
};
export default DatePickerSelect;

Comment on lines 41 to 43
<div onClick={() => setIsOpen(prev => !prev)}>
{props.trigger}
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add accessibility attributes to the clickable div.

The div needs proper keyboard interaction and ARIA attributes for accessibility.

Apply these changes:

-      <div onClick={() => setIsOpen(prev => !prev)}>
+      <div
+        role="button"
+        tabIndex={0}
+        aria-expanded={isOpen}
+        aria-haspopup="dialog"
+        onClick={() => setIsOpen(prev => !prev)}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault();
+            setIsOpen(prev => !prev);
+          }
+        }}
+      >
         {props.trigger}
       </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div onClick={() => setIsOpen(prev => !prev)}>
{props.trigger}
</div>
<div
role="button"
tabIndex={0}
aria-expanded={isOpen}
aria-haspopup="dialog"
onClick={() => setIsOpen(prev => !prev)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(prev => !prev);
}
}}
>
{props.trigger}
</div>

{props.trigger}
</div>
{isOpen && (
<div className='containerStyle'>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Define or import the containerStyle className.

The className 'containerStyle' appears to be undefined.

Please verify if this style is defined in a CSS module or styled-components. If not, consider:

-        <div className='containerStyle'>
+        <div className={styles.container}>

Run this script to check for style definitions:


🏁 Script executed:

#!/bin/bash
# Search for containerStyle definition in CSS/SCSS files
rg -t css -t scss 'containerStyle'

# Search for style imports in the current file's directory
rg -t typescript -t javascript 'containerStyle' "$(dirname "frontend/src/components/DatePicker/DatePickerRange.tsx")"

Length of output: 247


I'll run a revised script using file globbing to search for any occurrences of “containerStyle” in potential style files and in the component directory. This should help us verify whether the “containerStyle” class is defined somewhere in the codebase.

Once you execute this script, please share the output so we can further verify whether the containerStyle class is defined or imported.


🏁 Script executed:

#!/bin/bash
echo "Searching for containerStyle in .css files:"
find . -type f -iname "*.css" -exec grep -H "containerStyle" {} \;

echo "Searching for containerStyle in .scss files:"
find . -type f -iname "*.scss" -exec grep -H "containerStyle" {} \;

echo "Searching for containerStyle in .tsx and .ts files in the DatePicker directory:"
find frontend/src/components/DatePicker -type f \( -iname "*.ts" -o -iname "*.tsx" \) -exec grep -H "containerStyle" {} \;

Length of output: 912


I'll run a script to check whether the DatePickerRange.tsx file imports styles from index.css.ts (or a similar module) so that the defined style containerStyle can be correctly used.

Please share the output so we can further verify if the style is properly imported.


🏁 Script executed:

#!/bin/bash
echo "Looking for CSS module import in DatePickerRange.tsx:"
rg "index\.css\.ts" frontend/src/components/DatePicker/DatePickerRange.tsx

Length of output: 193


Issue Found: Missing Import for Defined Style

The investigation shows that the style is defined in the file frontend/src/components/DatePicker/index.css.ts (exporting containerStyle), but neither the DatePickerRange.tsx nor the other relevant component files import it. Instead of using a string literal (className='containerStyle'), you need to import the style and apply it correctly.

  • Location: frontend/src/components/DatePicker/DatePickerRange.tsx
  • Suggested Fix:
    • Add an import in DatePickerRange.tsx:
      import { containerStyle } from './index.css.ts';
    • Update the JSX to use the imported style:
      - <div className='containerStyle'>
      + <div className={containerStyle}>

Copy link
Contributor

@hamo-o hamo-o left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다..! (+ 스타일링 피그마랑 동일하게 한번 신경써주세요~!)

Comment on lines 41 to 43
<div onClick={() => setIsOpen(prev => !prev)}>
{props.trigger}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2;
trigger 위치 스타일링이 데이트피커 바로 위에 되어야 할 것 같습니다! 크기 줄어드는 것도 수정해주시면 좋을 것 같아요
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9015a1c 반영했습니다~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3; 트리거 있는 버전 + 공용 컨텍스트 쓰는 버전도 추가해주시면 좋을 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a432435 반영했습니다~

Comment on lines 20 to 22
const calendarDates = useMemo(
() => generateMonthCalendar(baseDate ?? new Date()),
[baseDate],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3;
요게 쓰이는 곳은 한군데밖에 없는 것 같습니다. generateMonthCalendar 함수를 테이블에서 실행해서 컨텍스트에서 관리하는 상태를 줄여볼 수 있을 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11506a8 반영했습니다~

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (2)
frontend/src/components/DatePicker/index.tsx (1)

16-18: Consider using English for error messages.

The error message is currently in Korean. Consider using English for better accessibility across international development teams.

-  throw new Error('!!! DatePicker.Select, DatePicker.Range를 사용해주세요 !!!');
+  throw new Error('!!! Please use DatePicker.Select or DatePicker.Range !!!');
frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1)

16-35: Consider adding comments and descriptive variable names.

While the logic is correct, the code could be more self-documenting. Consider:

  1. Adding comments explaining the different states and their transitions
  2. Using more descriptive variable names for better readability
 const onDateCellClick = (date: Date) => {
   const { start, end } = highlightRange;
+  // If no start date is set, set it to the clicked date
   if (!start) {
     setHighlightStart(date);
     return;
   }
 
+  // If only start date is set, handle the second click
   if (!end) {
+    // If clicked date is before start, update start date
     if (date < start) {
       setHighlightStart(date);
+    // If clicked date is same as start, clear selection
     } else if (isSameDate(date, start)) {
       setHighlightStart(null);
+    // If clicked date is after start, set it as end date
     } else {
       setHighlightEnd(date);
     }
     return;
   }
+  // If both dates are set, clear the selection
   setHighlightStart(null);
   setHighlightEnd(null);
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d51ece4 and f8cafa5.

📒 Files selected for processing (10)
  • frontend/src/components/DatePicker/DatePickerRange.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerSelect.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Row.tsx (2 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/context/DatePickerContext.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/hooks/useDatePicker/useDatePickerRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDatePickerSelect.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts
  • frontend/src/components/DatePicker/context/DatePickerContext.ts
  • frontend/src/hooks/useDatePicker/useDatePickerSelect.ts
  • frontend/src/components/DatePicker/DatePickerRange.tsx
  • frontend/src/components/DatePicker/Table/Row.tsx
🔇 Additional comments (8)
frontend/src/components/DatePicker/Table/Highlight/index.ts (1)

8-11: LGTM!

The HighlightRange interface is well-defined and properly typed for managing date range selection.

frontend/src/components/DatePicker/index.tsx (1)

9-14: LGTM!

The refactoring to a composite component with Select and Range variants improves modularity and reusability. The CommonDatePickerProps interface is well-defined with clear prop types.

Also applies to: 20-21

frontend/src/components/DatePicker/DatePickerSelect.tsx (3)

34-36: Add accessibility attributes to the trigger.

The click handler div should have proper accessibility attributes for better keyboard navigation and screen reader support.


37-42: Add click outside handling for the dropdown.

The date picker should close when clicking outside to follow standard dropdown behavior.


16-46: LGTM!

The component is well-structured with clear separation of concerns:

  • Uses custom hooks for date selection logic
  • Properly manages internal state
  • Provides comprehensive context to child components
frontend/src/components/DatePicker/Table/TableBody.tsx (3)

2-6: LGTM! Clean import organization.

The import changes reflect good separation of concerns with the calendar generation logic moved to a utility function, and the context properly organized in a dedicated context directory.


8-19: LGTM! Clean implementation with improved prop naming.

The component implementation is now more focused with:

  • Simplified context usage
  • Clear separation of calendar generation logic
  • Improved prop naming (weekDates is more descriptive than week)

9-10: Verify baseDate null handling.

The fallback to new Date() when baseDate is null could lead to unexpected behavior if baseDate is undefined in the context. Consider whether this fallback aligns with the expected behavior.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for DatePickerContext provider to understand baseDate initialization
ast-grep --pattern 'const DatePickerContext = createContext<DatePickerContextType>($$$)'

Length of output: 90


I'll now generate additional shell scripts to search for the definition of DatePickerContext and inspect how its value for baseDate is initialized. This will help us determine whether the fallback (new Date()) might be problematic or if the context always supplies a valid value.

Please run the following scripts:

Once we have these results, we can verify how baseDate is initialized and whether the fallback to new Date() is intentional or might lead to unexpected behavior.


🏁 Script executed:

#!/bin/bash
# Look for files with "DatePickerContext" in their name to locate the provider definition
echo "Searching for files containing 'DatePickerContext':"
fd -e ts -e tsx -e js "DatePickerContext" .

# Search within the repository for the provider initialization (using createContext) for DatePickerContext
echo "Searching for DatePickerContext provider definition using createContext:"
rg "createContext.*DatePickerContext" -n .

Length of output: 582


Verified baseDate null handling in TableBody.tsx.

  • The provider file (frontend/src/components/DatePicker/context/DatePickerContext.ts) initializes the context with a default value of null.
  • As a result, if the provider does not supply a specific baseDate, the component’s use of the nullish coalescing operator (baseDate ?? new Date()) will indeed fall back to the current date.
  • This behavior appears intentional: when no explicit baseDate is provided via a wrapping provider, the calendar is generated using today's date.
  • Ensure that this fallback aligns with the overall design expectations—if a strictly controlled date is required, the provider should always supply a valid baseDate.

Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (2)
frontend/src/components/DatePicker/DatePickerSelect.tsx (1)

26-28: Add accessibility attributes to the trigger wrapper.

The clickable div needs proper keyboard interaction and ARIA attributes for accessibility.

-        <div className={triggerWrapperStyle} onClick={() => setIsOpen(true)}>
+        <div
+          className={triggerWrapperStyle}
+          role="button"
+          tabIndex={0}
+          aria-expanded={isOpen}
+          aria-haspopup="dialog"
+          aria-label="Select date"
+          onClick={() => setIsOpen(true)}
+          onKeyDown={(e) => {
+            if (e.key === 'Enter' || e.key === ' ') {
+              e.preventDefault();
+              setIsOpen(true);
+            }
+          }}
+        >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        <div
          className={triggerWrapperStyle}
          role="button"
          tabIndex={0}
          aria-expanded={isOpen}
          aria-haspopup="dialog"
          aria-label="Select date"
          onClick={() => setIsOpen(true)}
          onKeyDown={(e) => {
            if (e.key === 'Enter' || e.key === ' ') {
              e.preventDefault();
              setIsOpen(true);
            }
          }}
        >
          {trigger}
          {isOpen && (
frontend/src/components/DatePicker/DatePickerRange.tsx (1)

30-32: Add accessibility attributes to the trigger wrapper.

The clickable div needs proper keyboard interaction and ARIA attributes for accessibility.

-      <div className={triggerWrapperStyle} onClick={() => setIsOpen(true)}>
+      <div
+        className={triggerWrapperStyle}
+        role="button"
+        tabIndex={0}
+        aria-expanded={isOpen}
+        aria-haspopup="dialog"
+        aria-label="Select date range"
+        onClick={() => setIsOpen(true)}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault();
+            setIsOpen(true);
+          }
+        }}
+      >
🧹 Nitpick comments (13)
frontend/src/components/DatePicker/Table/TableBody.tsx (1)

13-24: Consider optimizing the key generation.

While using toISOString() for key generation is reliable, it might be more efficient to use the week's index or a combination of year/month/weekNumber for the key, especially if the component re-renders frequently.

Consider this alternative approach:

- key={`week-${week[0].toISOString()}`}
+ key={`week-${baseDate.getFullYear()}-${baseDate.getMonth()}-${index}`}

The layout structure using Flex with explicit direction and gap properties is clean and maintainable.

frontend/src/components/DatePicker/index.css.ts (1)

5-18: Consider theming for width property and consistent border usage.
Currently, the width is hardcoded to 286. You might consider using a theme-based variable (just like you're doing for spacing) to maintain consistency. Also, you use vars.color.Ref.Netural[100] inside border: `1px ${...}`, but rely on direct numeric values for other properties. Harmonizing these can help standardize styling across the app.

frontend/src/components/DatePicker/datePicker.stories.css.ts (1)

1-6: Unify sizing with theming.
If your design system uses spacing variables, you might consider referencing them for width and height, just like for gap or padding in other components, to ensure consistency across the codebase.

frontend/src/components/Calendar/context/SharedCalendarContext.ts (1)

10-10: Optionally define a default object.
Using null as the default can be valid, but you could provide a default implementation if you want to avoid null checks elsewhere. A custom hook pattern can also help manage initialization logic.

frontend/src/components/DatePicker/RootContainer.tsx (1)

7-10: Consider using more explicit typing for the ref prop.

While the current implementation works, using RefObject<HTMLDivElement | null> could be more precise as RefObject<HTMLDivElement> since the ref object itself is never null, only its current value can be.

interface RootContainerProps extends PropsWithChildren {
-  ref: RefObject<HTMLDivElement | null>;
+  ref: RefObject<HTMLDivElement>;
  className?: string;
}
frontend/src/hooks/useSharedCalendar.ts (1)

7-20: Consider timezone handling and performance optimizations.

A few suggestions to improve the hook:

  1. Consider using a date library (like date-fns) for consistent timezone handling.
  2. Memoize the returned object to prevent unnecessary re-renders.
export const useSharedCalendar = (): CalendarSharedInfo => {
  const [selected, setSelected] = useState(new Date());

  const handleSelectDate = useCallback((date: Date) => {
    setSelected(date);
  }, []);

+  return useMemo(() => ({
-  return {
    selectedDate: selected,
    selectedWeek: formatDateToWeekDates(selected),
    handleSelectDate,
    today: new Date(),
+  }), [selected, handleSelectDate]);
-  };
};
frontend/src/components/DatePicker/index.tsx (2)

2-2: Consider importing ReactNode from 'react' instead.

The ReactNode type is available directly from 'react', no need to import it from @tanstack/react-router.

-import type { ReactNode } from '@tanstack/react-router';
+import type { ReactNode } from 'react';

17-19: Consider using English for error messages.

For consistency and better international developer experience, error messages should be in English.

const DatePicker = () => {
-  throw new Error('!!! DatePicker.Select, DatePicker.Range를 사용해주세요 !!!');
+  throw new Error('Please use DatePicker.Select or DatePicker.Range instead of DatePicker directly');
};
frontend/src/components/DatePicker/context/DatePickerRangeProvider.tsx (1)

28-42: Consider adding error boundaries.

While the implementation is clean, consider adding error boundaries to handle potential runtime errors gracefully.

+import { ErrorBoundary } from '@/components/ErrorBoundary';

 return (
+  <ErrorBoundary fallback={<div>Failed to load date picker</div>}>
     <DatePickerContext.Provider 
       value={{
         calendarType: 'range',
         onDateCellClick,
         isDateSelected,
         selectedDate: null,
         highlightRange,
         ...props,
       }}
     >
       {children}
     </DatePickerContext.Provider>
+  </ErrorBoundary>
 );
frontend/src/components/DatePicker/Table/Highlight/index.css.ts (1)

8-9: Use CSS variables for dimensions.

Using fixed pixel values makes the component less flexible. Consider using CSS variables from the theme for better maintainability and consistency.

-    width: 32,
-    height: 32,
+    width: vars.spacing[800], // Adjust the spacing token as needed
+    height: vars.spacing[800], // Adjust the spacing token as needed

And similarly for the gap style:

-    height: 32,
+    height: vars.spacing[800], // Adjust the spacing token as needed

Also applies to: 33-33

frontend/src/components/DatePicker/DatePicker.stories.tsx (3)

15-20: Add documentation for story variants.

The meta configuration should include descriptions for each story variant to help other developers understand their purpose and usage.

 const meta = {
   title: 'Calendar/DatePicker',
   component: DatePicker.Select,
+  parameters: {
+    docs: {
+      description: {
+        component: 'A flexible date picker component that supports single date and range selection.',
+      },
+    },
+  },
 } satisfies Meta<typeof DatePicker.Select>;

22-106: Add more story variants for error and disabled states.

Consider adding examples that demonstrate:

  • Error states (e.g., invalid date selection)
  • Disabled states (e.g., disabled dates, disabled trigger)
  • Loading states
  • Different locales

103-103: Remove commented-out code.

Clean up the commented-out Calendar component reference.

-      {/* <Calendar {...props}/> */}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8cafa5 and a432435.

📒 Files selected for processing (17)
  • frontend/src/components/Calendar/context/SharedCalendarContext.ts (1 hunks)
  • frontend/src/components/DatePicker/DatePicker.stories.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerRange.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerSelect.tsx (1 hunks)
  • frontend/src/components/DatePicker/Header/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/RootContainer.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.css.ts (2 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/index.css.ts (2 hunks)
  • frontend/src/components/DatePicker/Table/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/context/DatePickerRangeProvider.tsx (1 hunks)
  • frontend/src/components/DatePicker/context/DatePickerSelectProvider.tsx (1 hunks)
  • frontend/src/components/DatePicker/datePicker.stories.css.ts (1 hunks)
  • frontend/src/components/DatePicker/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/hooks/useSharedCalendar.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/components/DatePicker/Table/index.tsx
🔇 Additional comments (13)
frontend/src/components/DatePicker/Table/TableBody.tsx (2)

2-2: LGTM! Import changes align with refactoring goals.

The updated imports reflect better organization with the context in a dedicated directory and introduce necessary utilities for the refactored component.

Also applies to: 4-4, 6-6


10-11: Verify the null handling approach for baseDate.

While providing a fallback to new Date() is reasonable, consider whether this default behavior should be documented or handled more explicitly, as it could affect the parent component's state management.

Consider:

  1. Adding a comment explaining the fallback behavior
  2. Validating baseDate earlier in the component tree
  3. Documenting this behavior in the component's interface
  const { baseDate } = useSafeContext(DatePickerContext);
+ // Fallback to current date if baseDate is not provided
  const calendarDates = generateMonthCalendar(baseDate ?? new Date());
frontend/src/components/DatePicker/index.css.ts (2)

20-24: Looks good!
The rootContainerStyle is clear and consistent with the theming approach, providing clarity on layout.


26-28: Simple & effective.
Positioning the trigger wrapper as relative gives flexibility for nested absolutely positioned elements in the future. The style definition looks correct.

frontend/src/components/DatePicker/datePicker.stories.css.ts (1)

8-11: Straightforward input styling.
This approach is clear. If you frequently adjust input dimensions, referencing them via theme variables could streamline updates and keep design aligned.

frontend/src/components/Calendar/context/SharedCalendarContext.ts (1)

3-8: Interface is well-defined.
Including all relevant date properties (e.g., selectedDate, selectedWeek) and associated handlers clarifies the intended usage.

frontend/src/components/DatePicker/Header/index.css.ts (1)

3-7: LGTM! Good use of design system tokens.

The change from hardcoded padding to theme-based spacing improves maintainability and ensures visual consistency across the application.

frontend/src/components/DatePicker/index.tsx (1)

9-15: LGTM! Good use of compound components pattern.

The refactoring to use compound components (Select and Range) with shared common props improves the component's flexibility and maintainability.

Also applies to: 21-22

frontend/src/components/DatePicker/Table/index.css.ts (2)

12-18: LGTM! Improved responsive design.

The change from fixed width to 100% enhances the component's flexibility and responsiveness.


20-25: LGTM! Enhanced layout control.

Adding height: '100%' improves layout control and consistency.

frontend/src/components/DatePicker/context/DatePickerSelectProvider.tsx (1)

11-35: LGTM! Clean implementation of the provider component.

The component effectively manages date selection state and provides it through context.

Let's verify the hook implementation:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check if the hook is properly implemented
ast-grep --pattern $'export function useDatePickerSelect($_) {
  $$$
}'

Length of output: 72


I'll now run new scripts to search for the hook definition in alternative forms—both as a function declaration and as an arrow function—to ensure we properly verify its implementation.

Once we see the output from these commands, we can determine whether the hook is implemented as expected.


🏁 Script executed:

#!/bin/bash
# Search for a function declaration of useDatePickerSelect
rg -n "export function useDatePickerSelect" .

echo "-----"

# Search for an arrow function (const-based) declaration of useDatePickerSelect
rg -n "export const useDatePickerSelect" .

Length of output: 210


I'll now run additional scripts to verify that the hook returns both highlightRange and onDateCellClick as they are used in the provider component.


🏁 Script executed:

#!/bin/bash
echo "Searching for 'highlightRange' in the hook implementation:"
rg -n "highlightRange" frontend/src/hooks/useDatePicker/useDatePickerSelect.ts

echo "-----"

echo "Searching for 'onDateCellClick' in the hook implementation:"
rg -n "onDateCellClick" frontend/src/hooks/useDatePicker/useDatePickerSelect.ts

Length of output: 662


Verified: Provider and Hook Implementation Are Consistent

  • The hook useDatePickerSelect is implemented as an arrow function returning both highlightRange and onDateCellClick, as confirmed by the searches.
  • The returned values are correctly deconstructed and passed into the DatePickerContext.Provider in the provider component.
frontend/src/components/DatePicker/context/DatePickerRangeProvider.tsx (1)

22-26: LGTM! Well-optimized date selection check.

Good use of useCallback for memoization of the isDateSelected function.

frontend/src/components/DatePicker/Table/Cell/index.css.ts (1)

13-14: Verify unit handling for numeric values.

The change from string ('24px') to number (24) might affect how the CSS is interpreted. Please verify that vanilla-extract properly handles numeric values for width and height.

✅ Verification successful

🌐 Web query:

How does vanilla-extract handle numeric values for width and height properties?

💡 Result:

Vanilla Extract automatically converts numeric values for dimensional properties like width and height to pixel units (px) by default[1][2]. This behavior applies to most CSS properties requiring units, while unitless properties like flexGrow or opacity accept raw numbers directly[1].

Key Handling Characteristics:

  1. Automatic Pixel Conversion
    Numeric values without explicit units get converted to pixels:

    const box = style({
      width: 100,    // → 100px
      height: 200    // → 200px
    });
  2. Unitless Property Support
    Properties like lineHeight, zIndex, and flexGrow retain raw numbers:

    const flexItem = style({
      flexGrow: 1,
      zIndex: 100
    });
  3. Explicit Unit Overrides
    Use string literals for non-pixel units:

    const container = style({
      width: '50%',
      height: 'calc(100vh - 20px)'
    });

Advanced Use Cases

  • Sprinkles Utility handles responsive numeric values through predefined scales[4]:
    const responsiveWidth = sprinkles({
      width: { mobile: 320, desktop: 1024 } // Converts to px at each breakpoint
    });
  • CSS Variables enable dynamic values when combined with assignInlineVars[7]:
    import { assignInlineVars } from '@vanilla-extract/dynamic';
    
    const dynamicWidth = style({
      vars: {
        '--custom-width': '50vw'
      },
      width: 'var(--custom-width)'
    });

For mixed unit requirements (e.g., viewport units), always use string literals to bypass automatic pixel conversion[1][7].

Citations:


Numeric value handling verified for vanilla-extract

Vanilla Extract automatically converts numeric values for properties like width and height to px. The change from a string ('24px') to a number (24) is correct and will be interpreted as 24px.

import type { DatePickerSelectProps } from '../DatePickerSelect';
import { DatePickerContext } from './DatePickerContext';

interface DatePickerRangeProviderProps extends PropsWithChildren, DatePickerSelectProps {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix interface naming inconsistency.

The interface name DatePickerRangeProviderProps doesn't match the component's purpose (SelectProvider).

-interface DatePickerRangeProviderProps extends PropsWithChildren, DatePickerSelectProps {}
+interface DatePickerSelectProviderProps extends PropsWithChildren, DatePickerSelectProps {}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface DatePickerRangeProviderProps extends PropsWithChildren, DatePickerSelectProps {}
interface DatePickerSelectProviderProps extends PropsWithChildren, DatePickerSelectProps {}

Copy link
Contributor

@hamo-o hamo-o left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@hamo-o hamo-o merged commit 8d9f24f into dev Feb 11, 2025
1 check passed
@hamo-o hamo-o deleted the feature/fe/date-picker-component branch February 11, 2025 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🖥️ FE Frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants