Skip to content

Conversation

@tonyb650
Copy link
Contributor

@tonyb650 tonyb650 commented Oct 24, 2025

Description

Before (2 Issues):

Issue 1
Calendar size was inconsistent. As the user changes the month displayed, the number of weeks visible would change depending on the particular month.
Issue 2
Choosing an input from 'popover' style input fields would not trigger the closure of the popover in question. This applied to the "Budget", "Drawing Date", and "Exchange Date" inputs.

After:

These fixes were applied to both the CreateGroup page and the EditGroup page.
Issue 1
Calendar size was made consistent by applying the fixedweeks attribute to the<DayPicker/> within the <Calendar/> component.
Issue 2
For each input field, a state variable was added to track the open state of the associated popover. For each field, the onSelect script now includes an update to 'setOpen(false)` to close the popover.

Closes #559

Additional information

The array of Price Ranges / Budget Options was extracted to a separate file to reduce repetition.

Screenshots

Before, number of weeks not 'fixed':

image

After, number of weeks 'fixed':

image

Pre-submission checklist

  • Code builds and passes locally
  • PR title follows Conventional Commit format (e.g. test #001: created unit test for __ component)
  • Request reviews from the Peer Code Reviewers and Senior+ Code Reviewers groups
  • Thread has been created in Discord and PR is linked in gis-code-questions

Summary by CodeRabbit

  • New Features

    • Enhanced budget and date selection with improved popover controls for better user experience.
  • Bug Fixes

    • Fixed calendar display consistency across all months.
  • Tests

    • Expanded test coverage for budget selection, drawing date, and exchange date inputs.

✏️ Tip: You can customize this high-level summary in your review settings.

@tonyb650 tonyb650 requested review from a team and shashilo and removed request for a team October 24, 2025 17:29
@vercel
Copy link

vercel bot commented Oct 24, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
elecretanta Ready Ready Preview Comment Nov 20, 2025 11:35pm
elecretanta-storybook Ready Ready Preview Comment Nov 20, 2025 11:35pm
elecretanta-unit-test Ready Ready Preview Comment Nov 20, 2025 11:35pm

@shashilo shashilo requested review from a team and shashilo and removed request for a team and shashilo October 24, 2025 17:29
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR improves the UX for calendar and popover interactions in the gift exchange group creation and editing flows. The changes fix calendar size inconsistency across months and implement auto-closing behavior for popover inputs when selections are made.

Key Changes:

  • Added fixedWeeks prop to the Calendar component to maintain consistent calendar height across all months
  • Implemented controlled popover state management for Budget, Drawing Date, and Exchange Date inputs to enable auto-closing on selection
  • Extracted budget options to a shared constants file (BUDGET_OPTIONS) to reduce code duplication

Reviewed Changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
jest.setup.js Minor formatting fix (added blank line)
constants/exchangeGroupOptions.ts New file containing shared budget options array
components/Calendar/calendar.tsx Added fixedWeeks prop to maintain consistent calendar height
components/Calendar/calendar.test.tsx Added test for fixed weeks behavior (has implementation bug)
app/gift-exchanges/[id]/edit/page.tsx Refactored to use shared budget options and added controlled popover state (has critical React Hooks violation)
app/gift-exchanges/[id]/edit/page.test.tsx Added tests for popover auto-close behavior and improved test organization
app/create-group-page/page.tsx Refactored to use shared budget options and added controlled popover state (has critical React Hooks violation and date validation inconsistency)
app/create-group-page/page.test.tsx Added tests for popover auto-close behavior

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

className="h-9"
/>
<CommandList>
<CommandEmpty>No framework found.</CommandEmpty>
Copy link

Copilot AI Nov 13, 2025

Choose a reason for hiding this comment

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

The error message in CommandEmpty is misleading for the budget selection context. "No framework found." should be updated to something more appropriate like "No budget options found." or "No price range found."

Suggested change
<CommandEmpty>No framework found.</CommandEmpty>
<CommandEmpty>No budget options found.</CommandEmpty>

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@suzuhe481 suzuhe481 left a comment

Choose a reason for hiding this comment

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

LGTM!

@suzuhe481
Copy link
Contributor

This would be for a separate ticket, but did you spot any major differences in the forms used in app/gift-exchanges/[id]/edit/page.tsx and app/create-group-page/page.tsx? If they're mostly the same, we could extract this form into a separate component in the future.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

The changes refactor budget selection across group creation and editing pages to use a centralized BUDGET_OPTIONS constant, replace hard-coded price ranges with controlled Popover components for budget and date selection, add the fixedWeeks prop to Calendar for consistent sizing, and expand test coverage for these UI patterns.

Changes

Cohort / File(s) Change Summary
Page Refactoring
app/create-group-page/page.tsx, app/gift-exchanges/[id]/edit/page.tsx
Centralize budget options via BUDGET_OPTIONS import; replace hard-coded price ranges with controlled Popovers for budget, drawing date, and exchange date fields; introduce budgetOpen, drawingDateOpen, exchangeDateOpen state; add data-testid attributes; update validation message for budget field.
Page Tests
app/create-group-page/page.test.tsx, app/gift-exchanges/[id]/edit/page.test.tsx
Add user-event based tests for budget popover interaction, drawing date calendar selection, and exchange date calendar selection; mock next/navigation and ResizeObserver; use fake timers for calendar testing; assert popovers close after selection and options render per BUDGET_OPTIONS.
Calendar Component
components/Calendar/calendar.tsx
Add fixedWeeks prop to DayPicker component to ensure consistent calendar structure across month navigation.
Calendar Tests
components/Calendar/calendar.test.tsx
New test suite verifying Calendar renders >31 grid cells and maintains consistent cell count across 12 months of navigation.
Constants
constants/exchangeGroupOptions.ts
New constant file exporting BUDGET_OPTIONS array with label and value pairs for $10–$100 budget ranges in $10 increments, with as const assertion.
Setup
jest.setup.js
Add trailing newline to end-of-file.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as Page
    participant Form as Form State
    participant Popover as Popover/Calendar

    User->>UI: Click budget/date button
    UI->>Popover: Open popover (set open state)
    Popover-->>User: Display options/calendar

    User->>Popover: Select option/date
    Popover->>Form: Update form value
    Popover->>UI: Close popover (set open state false)
    UI-->>User: Display selected value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Page component files (app/create-group-page/page.tsx, app/gift-exchanges/[id]/edit/page.tsx): Require careful review of controlled Popover state management, onOpenChange handlers, and form.setValue patterns; verify consistency between budget and date field implementations.
  • Test setup and mocks (app/gift-exchanges/[id]/edit/page.test.tsx): MockResizeObserver and global fetch mock setup should be verified for test isolation.
  • Calendar fixedWeeks prop: Verify this resolves the calendar sizing inconsistency mentioned in issue #559 and doesn't introduce unintended layout effects.

Poem

🐰✨ With budget buttons now so neat,
And popovers that know when to retreat,
Our calendars stand tall and true,
The same size through and through!
No more popups that overstay,
Clean selections light the way! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main changes: improving UX for calendars and popovers in CreateGroup and EditGroup pages, with a direct reference to issue #559.
Linked Issues check ✅ Passed The PR successfully addresses both objectives from issue #559: fixedWeeks prop ensures consistent calendar size, and popover state management ensures popovers close after selection.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue: calendar fixes, popover state management, budget options extraction, and test additions for these features.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tony/559-bug-create-group-UX-issues

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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

♻️ Duplicate comments (2)
app/create-group-page/page.tsx (1)

182-252: Budget popover implementation is clean; adjust CommandEmpty copy

The budget field now:

  • Uses a controlled Popover with budgetOpen/setBudgetOpen.
  • Renders options from centralized BUDGET_OPTIONS.
  • Closes the popover on selection via setBudgetOpen(false).
  • Shows the selected label or a fallback “Select a price range”.

This satisfies the “close on selection” UX requirement and de-duplicates price ranges.

One small nit: CommandEmpty still says “No framework found.”, which is misleading in this context. Consider updating the copy, e.g.:

-                            <CommandList>
-                              <CommandEmpty>No framework found.</CommandEmpty>
+                            <CommandList>
+                              <CommandEmpty>No budget options found.</CommandEmpty>
components/Calendar/calendar.test.tsx (1)

6-19: Avoid forEach with async user-event; convert test to async + for loop

The test intent is solid, but Array.from({ length: 12 }).forEach(async () => { ... }) inside a non‑async it means Jest won’t wait for the async clicks/expectations. This can lead to flaky or misleading results.

Consider rewriting as an async test with a simple loop:

-  it('displays the same number of weeks/rows for every month of the year', () => {
+  it('displays the same number of weeks/rows for every month of the year', async () => {
     render(<Calendar initialFocus />);

-    const user = userEvent.setup();
+    const user = userEvent.setup();
     const daysDisplayedCount = screen.getAllByRole('gridcell').length;
     expect(daysDisplayedCount).toBeGreaterThan(31);

-    Array.from({ length: 12 }).forEach(async () => {
-      const nextMonthButton = screen.getByLabelText('Go to next month');
-      await user.click(nextMonthButton);
-
-      expect(screen.getAllByRole('gridcell')).toHaveLength(daysDisplayedCount);
-    });
+    for (let i = 0; i < 12; i++) {
+      const nextMonthButton = screen.getByLabelText('Go to next month');
+      await user.click(nextMonthButton);
+      expect(screen.getAllByRole('gridcell')).toHaveLength(daysDisplayedCount);
+    }
   });
🧹 Nitpick comments (8)
components/Calendar/calendar.tsx (1)

31-31: Hard-coding fixedWeeks makes calendar height consistent for all usages

Adding fixedWeeks achieves the UX goal of a stable calendar grid across months for every consumer of Calendar. If you ever need a non‑fixed calendar elsewhere, you’ll have to adjust this component or add a prop to make fixedWeeks configurable, but for this PR’s objective the change looks correct.

app/create-group-page/page.test.tsx (2)

10-12: Strengthen useRouter mock to be closer to Next’s API

Right now useRouter returns {}, which works because these tests don’t hit router.push, but it’s brittle if future tests exercise form submission.

Consider returning an object with the methods the page uses, e.g.:

-jest.mock('next/navigation', () => ({
-  useRouter: () => {},
-}));
+jest.mock('next/navigation', () => ({
+  useRouter: () => ({
+    push: jest.fn(),
+    prefetch: jest.fn(),
+  }),
+}));

This keeps tests closer to runtime behavior and avoids surprises if the component starts calling router methods during render or submit.


89-147: Popover and calendar interaction tests are solid; tweak scrollIntoView mocking

These new tests nicely exercise the budget and date popover behavior and align with the new data-testid hooks and BUDGET_OPTIONS usage. One small improvement: instead of assigning directly to Element.prototype.scrollIntoView, use a spy so jest.restoreAllMocks() actually restores it:

  describe('Budget input', () => {
-    beforeEach(() => {
-      Element.prototype.scrollIntoView = jest.fn();
-    });
+    beforeEach(() => {
+      jest
+        .spyOn(Element.prototype, 'scrollIntoView')
+        .mockImplementation(() => {});
+    });

     afterEach(() => {
       jest.restoreAllMocks();
     });

This avoids leaving a mutated prototype behind and makes the intent clearer.

app/create-group-page/page.tsx (1)

310-360: Guard giftDrawingDate when computing disabled dates for exchange calendar

The exchange date field correctly:

  • Uses a controlled Popover with exchangeDateOpen.
  • Writes via field.onChange and closes on selection.
  • Attempts to enforce exchange_date > drawing_date with disabled={[{ before: addDays(new Date(giftDrawingDate), 1) }]}.

However, when giftDrawingDate is still unset, new Date(giftDrawingDate) will be an invalid date, which then gets passed into addDays. While most libraries tolerate this by propagating an “Invalid Date”, it’s safer and clearer to guard:

-                          <Calendar
-                            mode="single"
-                            selected={field.value}
-                            onSelect={(inputValue) => {
-                              field.onChange(inputValue);
-                              setExchangeDateOpen(false);
-                            }}
-                            disabled={[
-                              { before: addDays(new Date(giftDrawingDate), 1) },
-                            ]}
-                            initialFocus
-                          />
+                          <Calendar
+                            mode="single"
+                            selected={field.value}
+                            onSelect={(inputValue) => {
+                              field.onChange(inputValue);
+                              setExchangeDateOpen(false);
+                            }}
+                            disabled={
+                              giftDrawingDate
+                                ? [{ before: addDays(giftDrawingDate, 1) }]
+                                : undefined
+                            }
+                            initialFocus
+                          />

This keeps the intent explicit and avoids passing invalid dates into the calendar library.

app/gift-exchanges/[id]/edit/page.tsx (2)

239-312: Budget popover integrates well; consider leveraging field.onChange / setValue options

The budget popover wiring correctly:

  • Shows the selected label via BUDGET_OPTIONS.
  • Updates the form’s budget value.
  • Closes on selection via setBudgetOpen(false).

One small refinement: form.setValue('budget', budgetOption.value) does not mark the field as touched/dirty or trigger validation by default in react-hook-form. If you want inline validation and dirtiness tracking to behave like a regular input, consider:

- onSelect={() => {
-   form.setValue('budget', budgetOption.value);
-   setBudgetOpen(false);
- }}
+ onSelect={() => {
+   field.onChange(budgetOption.value);
+   // or, if you prefer setValue:
+   // form.setValue('budget', budgetOption.value, {
+   //   shouldDirty: true,
+   //   shouldTouch: true,
+   //   shouldValidate: true,
+   // });
+   setBudgetOpen(false);
+}}

This keeps UX/validation behavior more consistent with other fields.


368-420: Guard exchange_date disabled boundary when drawing date is unset

The intent to constrain exchange_date via:

disabled={[{ before: addDays(new Date(giftDrawingDate), 1) }]}

lines up with the schema rule exchange_date > drawing_date, but there are two edge cases:

  1. When giftDrawingDate is initially undefined (before the user picks a drawing date), new Date(giftDrawingDate) produces an invalid Date, which can lead to unclear behavior in the calendar’s disabled matcher.
  2. If users open the exchange-date picker before setting drawing_date, they can currently pick any date; the refine will catch it on submit, but the UI won’t guide them.

A small guard makes the behavior clearer and avoids relying on an invalid Date:

- disabled={[
-   { before: addDays(new Date(giftDrawingDate), 1) },
- ]}
+ disabled={
+   giftDrawingDate
+     ? [{ before: addDays(giftDrawingDate, 1) }]
+     : [] // or e.g. [{ before: addDays(new Date(), 1) }] if you want at least "tomorrow"
+ }

This keeps the disabled logic well-defined and tightly aligned with the validation rule.

app/gift-exchanges/[id]/edit/page.test.tsx (2)

18-25: Type-safe ResizeObserver mock

The MockResizeObserver class is fine for JSDOM, but assigning it directly:

global.ResizeObserver = MockResizeObserver;

can be rejected by TypeScript in stricter setups because the constructor signature may not match the DOM ResizeObserver type.

To avoid type friction, it’s safer to either cast or align the constructor signature, e.g.:

-class MockResizeObserver {
+class MockResizeObserver {
   observe() {}
   unobserve() {}
   disconnect() {}
 }
 
-global.ResizeObserver = MockResizeObserver;
+global.ResizeObserver = MockResizeObserver as unknown as typeof ResizeObserver;

This keeps runtime behavior the same while satisfying TS.


84-133: Popover-close interaction tests align with UX goals (minor naming nit)

The budget, drawing-date, and exchange-date tests:

  • Render EditGroupPage with mocks in place.
  • Open each popover via data-testid triggers.
  • Assert the correct number of options / grid cells.
  • Click an option/day and assert that options or gridcells disappear, confirming close-on-select behavior.

This matches the PR’s UX objective and should catch regressions.

Minor readability nit: in the “Exchange date calendar input” test, the trigger is stored in drawingDateTriggerButton:

const drawingDateTriggerButton = screen.getByTestId(
  'exchange-date-button',
);

Renaming this variable to exchangeDateTriggerButton would better reflect its purpose.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec8e025 and 44495ec.

📒 Files selected for processing (8)
  • app/create-group-page/page.test.tsx (2 hunks)
  • app/create-group-page/page.tsx (4 hunks)
  • app/gift-exchanges/[id]/edit/page.test.tsx (1 hunks)
  • app/gift-exchanges/[id]/edit/page.tsx (4 hunks)
  • components/Calendar/calendar.test.tsx (1 hunks)
  • components/Calendar/calendar.tsx (1 hunks)
  • constants/exchangeGroupOptions.ts (1 hunks)
  • jest.setup.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
app/gift-exchanges/[id]/edit/page.test.tsx (2)
app/gift-exchanges/[id]/edit/page.tsx (1)
  • EditGroupPage (72-440)
constants/exchangeGroupOptions.ts (1)
  • BUDGET_OPTIONS (4-14)
app/create-group-page/page.test.tsx (2)
app/create-group-page/page.tsx (1)
  • CreateGroupPage (70-374)
constants/exchangeGroupOptions.ts (1)
  • BUDGET_OPTIONS (4-14)
app/gift-exchanges/[id]/edit/page.tsx (2)
lib/utils.ts (1)
  • cn (13-15)
constants/exchangeGroupOptions.ts (1)
  • BUDGET_OPTIONS (4-14)
app/create-group-page/page.tsx (2)
lib/utils.ts (1)
  • cn (13-15)
constants/exchangeGroupOptions.ts (1)
  • BUDGET_OPTIONS (4-14)
🔇 Additional comments (8)
jest.setup.js (1)

4-4: Jest DOM setup looks good

The @testing-library/jest-dom import is appropriate for the RTL tests in this PR; no changes needed.

constants/exchangeGroupOptions.ts (1)

4-14: Centralized BUDGET_OPTIONS constant is clear and reusable

Extracting the budget ranges into a shared BUDGET_OPTIONS array with as const improves consistency between the create/edit pages and tests; this looks good as-is.

app/create-group-page/page.tsx (2)

42-76: Top-level popover state and budget validation align with UX goals

Importing useState/BUDGET_OPTIONS and introducing budgetOpen, drawingDateOpen, and exchangeDateOpen at the component level fixes the previous “hooks inside render” issue and gives you precise control over when each popover opens/closes. Updating the budget required_error message to “Please select a budget.” also matches the current field semantics.


255-305: Drawing date calendar popover behavior matches the requirements

For the drawing date field, the controlled Popover plus Calendar:

  • Disables past dates via disabled={[{ before: new Date() }]}.
  • Writes the selected date into the form with field.onChange.
  • Closes the popover on selection with setDrawingDateOpen(false).
  • Provides a clear formatted label or “Pick a date” placeholder.

This is a good fit for both the UX objective (closing on selection) and preventing invalid past drawing dates.

app/gift-exchanges/[id]/edit/page.tsx (3)

46-66: Schema + centralized budget options look solid

Importing BUDGET_OPTIONS and using a clearer required_error plus the group_image refine keeps validation messages consistent and expresses constraints explicitly. No issues from a correctness perspective.


72-80: Good fix for popover open state (no hooks-in-render issues anymore)

Defining budgetOpen, drawingDateOpen, and exchangeDateOpen at the top level and wiring them into the popovers resolves the earlier “hooks inside render” concern while making the open/close behavior explicit and predictable.

Also applies to: 173-173


315-365: Drawing date calendar popover behavior matches requirements

The drawing date field:

  • Uses controlled drawingDateOpen state.
  • Disables past dates via disabled={[{ before: new Date() }]}.
  • Closes the popover on date selection (setDrawingDateOpen(false)).
  • Feeds the selected date through field.onChange for RHF/zod.

This aligns well with the UX goal of closing the date picker after selection and preventing past dates.

app/gift-exchanges/[id]/edit/page.test.tsx (1)

50-82: Calendar disabled-date test provides good coverage

The Calendar component suite with fixed currentDate, fake timers, and checks for past/today/tomorrow being disabled/enabled exercises the core disabled matcher logic well and stays decoupled from the page component. This is a solid unit-style test for the calendar itself.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create Group has UX issues

3 participants