Skip to content

Conversation

@dralan71
Copy link
Owner

@dralan71 dralan71 commented Aug 22, 2025

name: Pull Request
about: Propose changes to improve Pack## Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation as needed
    title: 'Add toast notifications and central toast config (issue Add toast notifications for item and baggage actions #4)'
    labels: ''
    assignees: ''

Description

Please include a summary of the change and which issue is fixed.
Also include relevant motivation and context.

This PR adds a centralized, themed toast notification system and integrates it across the app to provide clear user feedback for important actions. It uses react-hot-toast and a new ToastContext helper (src/ToastContext.tsx) exposing customToast functions to keep notifications consistent.

Key behavior added:

  • Toast notifications for item addition and removal
  • Toast notifications for baggage addition and deletion
  • Toast notifications for CSV import/export and clearing data
  • A consistent dark-themed toast style that fits the current app UI
  • Dynamic icons for item-related toasts (item's icon shown in toast), colored to match the success color
  • Replaced ad-hoc toast.success(...) calls with customToast.* helpers

Fixes #4

Type of Change

  • Bug fix (non-breaking change)
  • New feature (non-breaking change)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

  • Environment: macOS, Chrome, Node 18+, Vite dev server

  • Test steps / commands:

    • Install and run:

      npm install
      npm run dev
    • Manual UI checks performed:

      • Add baggage → verifies suitcase toast appears
      • Add item (quick add and custom add) → verifies item icon is shown and toast appears
      • Increase item quantity → verifies toast uses same item icon
      • Delete item → verifies trash icon toast appears
      • Delete baggage → verifies trash icon toast appears (with nickname when present)
      • Export CSV / Import CSV → verifies success toasts
      • Clear all data → verifies confirmation flow and success toast
    • Verified Toaster display and that toasts auto-dismiss (duration set to 2500ms)

Automated tests:

  • Added Vitest test (src/test/ToastContext.test.tsx) that verifies:
    • All customToast functions are exported and callable
    • Toast functions properly call the underlying toast.success() with correct messages
    • Toaster component renders in the App

Checklist

  • My code follows the project’s style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation as needed

Files changed (summary of diffs)

  • A src/ToastContext.tsx — new centralized toast helpers + styling
  • A src/test/ToastContext.test.tsx — Vitest tests for toast functionality
  • M src/App.tsx — use customToast, add Toaster config
  • M src/components/BaggageCard.tsx — use customToast in add/remove flows
  • M README.md — added toast notification documentation and updated tech stack
  • M package.json — add react-hot-toast
  • M package-lock.json — lockfile updates for react-hot-toast

Notes / Decisions

  • ToastContext centralizes toast behavior so future styling or icon logic changes only need a single update.
  • For dynamic item icons we pass props to the icon component (e.g., style: { color: '#27ae60' }) so the icon centers correctly and inherits the intended color. This avoids layout shifts introduced by wrapper elements.
  • Added comprehensive Vitest test coverage for toast functionality including mocking and component rendering verification.
  • Updated README.md with toast notification documentation, tech stack updates, and folder structure changes.

Next steps (optional)

  • Create a GitHub PR from branch 4-add-toast-notifications into main using this body (I can create it if you want).
  • Add a small Vitest test ensuring Toaster renders and/or that customToast functions are callable.
  • Add a short README or CHANGELOG note describing the notification feature.

Summary by CodeRabbit

  • New Features
    • Added dark-themed toast notifications across the app (top-right, auto-dismiss in ~2.5s) for actions like adding/removing items, quantity changes, baggage add/remove, and CSV import/export.
  • Documentation
    • Updated README with tech stack (React 19 + TypeScript), new Toast Notifications usage details, scripts (including tests), and revised folder structure.
  • Tests
    • Added tests and mocks to validate toast behaviors and Toaster integration.
  • Chores
    • Introduced a new dependency for toast notifications to support the feature.

@dralan71 dralan71 linked an issue Aug 22, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 22, 2025

Walkthrough

Introduces a toast notification system using react-hot-toast, adds ToastContext utilities, integrates toasts into App and BaggageCard actions, updates README and package.json, and adds tests and mocks validating toast APIs and Toaster integration.

Changes

Cohort / File(s) Summary
Toast infrastructure
src/ToastContext.tsx
New toast utility with shared config, icon mapping, and convenience methods for success/error and domain actions (items, baggage, CSV, clear). Exports toastConfig and customToast.
App integration
src/App.tsx
Adds Toaster at root with position=top-right and duration=2500ms; calls customToast.* after baggage add/delete/clear and CSV import/export.
Item-level toasts
src/components/BaggageCard.tsx
Triggers toasts on item add, quantity increase, and removal via customToast.
Documentation
README.md
Describes toast behavior, updates tech stack to include React 19.x, TypeScript, React Hot Toast; notes new test directory and ToastContext; adds usage notes and scripts.
Tests and mocks
src/test/App.test.tsx, src/test/ToastContext.test.tsx
Adds mocks for react-hot-toast, papaparse, and matchMedia; tests customToast API and verifies Toaster renders in App.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant UI as UI (App/BaggageCard)
  participant TC as ToastContext.customToast
  participant RHT as react-hot-toast Toaster

  U->>UI: Add/Remove Item
  UI->>TC: addItem/increaseQuantity/removeItem(...)
  TC-->>RHT: toast.success(..., options)

  U->>UI: Add/Delete Baggage
  UI->>TC: addBaggage/deleteBaggage(...)
  TC-->>RHT: toast.success(...)

  U->>UI: Import/Export CSV / Clear All
  UI->>TC: importCSV/exportCSV/clearAll()
  TC-->>RHT: toast.success(...)
  note over RHT: Auto-dismiss ~2500ms\nposition: top-right
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Design and integrate a toast notification system into the UI (#4)
Trigger toasts for item addition/removal; baggage addition/removal (#4)
Identify and notify other significant actions (CSV ops, clear all); auto-dismiss behavior (#4)
Test notification functionality for reliability (#4)

Poem

A bunny taps code with a soft little boast,
“Items and bags now come with a toast!” 🥂
A chime when they’re added, a cheer when they’re gone,
In 2.5 seconds, the glow hops on.
I nibble on carrots, then raise my glass—
To tidy toasts that vanish fast! 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 4-add-toast-notifications

🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/test/App.test.tsx (1)

196-214: localStorage.setItem is not a spy — assertion may fail.

In JSDOM, localStorage methods aren’t spies by default. Wrap with a spy before the action to avoid “received value must be a mock or spy function.”

Apply this diff:

-import { describe, it, expect, vi, beforeEach } from 'vitest'
+import { describe, it, expect, vi, beforeEach } from 'vitest'
@@
   it('persists data to localStorage', async () => {
     const user = userEvent.setup()
     render(<App />)
+    // Spy before any persistence occurs
+    const setItemSpy = vi.spyOn(Storage.prototype, 'setItem')
@@
     // Add a baggage
     await user.click(screen.getByText('CARRY ON'))
@@
     // Wait for localStorage to be called
     await waitFor(() => {
-      expect(localStorage.setItem).toHaveBeenCalledWith(
+      expect(setItemSpy).toHaveBeenCalledWith(
         'luggage-tracker-data',
         expect.any(String)
       )
     })
+    setItemSpy.mockRestore()
   })
🧹 Nitpick comments (9)
README.md (1)

101-114: Tighten copy for the Toast Notifications section.

Minor phrasing polish for clarity and consistency; no behavior change.

Apply this diff:

- PackTracker provides visual feedback through toast notifications for all major actions:
+ PackTracker provides visual feedback through toast notifications for major actions:

- - **Adding items**: Shows the item's icon and confirmation message
- - **Increasing quantity**: Displays the item icon with quantity update confirmation
- - **Removing items**: Shows trash icon with removal confirmation
- - **Adding baggage**: Displays suitcase icon with bag type confirmation
- - **Deleting baggage**: Shows trash icon with deletion confirmation
- - **CSV operations**: Success confirmations for import/export
- - **Data clearing**: Confirmation when all data is cleared
+ - **Adding items**: Shows the item’s icon with a confirmation message
+ - **Increasing quantity**: Shows the item’s icon with a quantity-increased message
+ - **Removing items**: Shows a trash icon with a removal confirmation
+ - **Adding baggage**: Shows a suitcase icon with the bag type
+ - **Deleting baggage**: Shows a trash icon with a deletion confirmation
+ - **CSV operations**: Confirms successful import/export
+ - **Data clearing**: Confirms when all data is cleared

- Toasts appear in the top-right corner with a dark theme, auto-dismiss after 2.5 seconds, and use contextual icons for better user experience.
+ Toasts appear in the top-right corner with a dark theme, auto-dismiss after 2.5 seconds, and use contextual icons for clarity.
src/ToastContext.tsx (6)

7-32: Reduce duplication and centralize theme tokens.

The success and error styles duplicate most of base styles. Centralize shared tokens to keep the theme consistent and simplify future tweaks.

Apply this diff near the top (after imports) and refactor toastConfig:

 import { toast } from "react-hot-toast";
 import { PiTrash, PiSuitcase, PiCheckCircle, PiXCircle } from "react-icons/pi";
 import { iconMap } from "./iconMap";
 import React from "react";
 
+// Theme tokens
+const SUCCESS_COLOR = '#27ae60';
+const ERROR_COLOR = '#e74c3c';
+const BASE_BG = '#2a2a2a';
+const BASE_FG = '#fff';
+const BASE_BORDER = '#444';
+const SUCCESS_BORDER = '#555';
+const ICON_SIZE = 18;
 
 // Custom toast configuration with dark theme
 export const toastConfig = {
   style: {
-    borderRadius: '10px',
-    background: '#2a2a2a',
-    color: '#fff',
-    border: '1px solid #444',
-    fontSize: '14px',
-    padding: '12px 16px',
+    borderRadius: '10px',
+    background: BASE_BG,
+    color: BASE_FG,
+    border: `1px solid ${BASE_BORDER}`,
+    fontSize: '14px',
+    padding: '12px 16px',
   },
   success: {
     style: {
-      borderRadius: '10px',
-      background: '#2a2a2a',
-      color: '#fff',
-      border: '1px solid #555',
+      borderRadius: '10px',
+      background: BASE_BG,
+      color: BASE_FG,
+      border: `1px solid ${SUCCESS_BORDER}`,
     },
   },
   error: {
     style: {
-      borderRadius: '10px',
-      background: '#2a2a2a',
-      color: '#fff',
-      border: '1px solid #e74c3c',
+      borderRadius: '10px',
+      background: BASE_BG,
+      color: BASE_FG,
+      border: `1px solid ${ERROR_COLOR}`,
     },
   },
 };

34-44: Make icon helper reusable for different colors and set a consistent size.

Hardcoding green in the helper limits reuse. Allow a color param and set a consistent size so icons don’t fluctuate with surrounding font size.

Apply this diff:

-const getStyledIconComponent = (iconName: string) => {
+const getStyledIconComponent = (iconName: string, color: string = SUCCESS_COLOR) => {
   const IconComponent = iconMap[iconName] || iconMap["PiCube"];
   
   // React icons accept standard SVG props including style and color
   return React.createElement(IconComponent, {
-    style: { color: '#27ae60' },
+    style: { color },
+    size: ICON_SIZE,
     // Alternative: you can also use the color prop directly
     // color: '#27ae60'
   } as React.ComponentProps<typeof IconComponent>);
 };

Also consider exporting this helper if other components need consistent inline icons.


48-53: Use shared tokens and consistent sizes for all inline icons.

Align all icons with the theme tokens and set a shared size to avoid minor layout shifts.

Apply this diff:

-    return toast.success(message, {
+    return toast.success(message, {
       ...toastConfig.success,
-      icon: icon || <PiCheckCircle style={{ color: '#27ae60' }} />,
+      icon: icon || <PiCheckCircle style={{ color: SUCCESS_COLOR }} size={ICON_SIZE} />,
     });
@@
-    return toast.error(message, {
+    return toast.error(message, {
       ...toastConfig.error,
-      icon: <PiXCircle style={{ color: '#e74c3c' }} />,
+      icon: <PiXCircle style={{ color: ERROR_COLOR }} size={ICON_SIZE} />,
     });
@@
-    const styledIcon = getStyledIconComponent(itemIcon);
+    const styledIcon = getStyledIconComponent(itemIcon, SUCCESS_COLOR);
@@
-    const styledIcon = getStyledIconComponent(itemIcon);
+    const styledIcon = getStyledIconComponent(itemIcon, SUCCESS_COLOR);
@@
-      icon: <PiCheckCircle style={{ color: '#27ae60' }} />,
+      icon: <PiCheckCircle style={{ color: SUCCESS_COLOR }} size={ICON_SIZE} />,
@@
-      icon: <PiCheckCircle style={{ color: '#27ae60' }} />,
+      icon: <PiCheckCircle style={{ color: SUCCESS_COLOR }} size={ICON_SIZE} />,

Also applies to: 62-68, 70-76, 78-83, 106-111, 113-118


85-90: Replace only-first hyphen replacement with a global replacement.

replace("-", " ") changes only the first occurrence. Use replaceAll or a regex to be robust.

Apply this diff:

-  addBaggage: (baggageType: string) => {
-    return toast.success(`Added new baggage: ${baggageType.replace("-", " ")}`, {
+  addBaggage: (baggageType: string) => {
+    const humanized = baggageType.replace(/-/g, " ");
+    return toast.success(`Added new baggage: ${humanized}`, {
       ...toastConfig.success,
-      icon: <PiSuitcase style={{ color: '#27ae60' }} />,
+      icon: <PiSuitcase style={{ color: SUCCESS_COLOR }} size={ICON_SIZE} />,
     });
   },

92-97: Type the deleteBaggage parameter to align with domain types.

Small type safety improvement; narrows the input to exactly what’s used.

Apply this diff:

+import type { Baggage } from "./types";
@@
-  deleteBaggage: (baggage?: { nickname?: string }) => {
+  deleteBaggage: (baggage?: Pick<Baggage, 'nickname'>) => {
     return toast.success(`Baggage deleted${baggage?.nickname ? `: ${baggage.nickname}` : ""}`, {
       ...toastConfig.success,
-      icon: <PiTrash style={{ color: '#e74c3c' }} />,
+      icon: <PiTrash style={{ color: ERROR_COLOR }} size={ICON_SIZE} />,
     });
   },

6-6: Optional: Consider renaming the file or exposing a real React context.

This module is a helper, not a context provider. Either:

  • Rename to toast.ts/notifications.ts to reduce cognitive friction, or
  • Export a React Context with value { customToast, toastConfig } so theming/position can be swapped via Provider.

I can wire a minimal that passes toastOptions and position down, keeping App.tsx lean. Want a patch?

src/components/BaggageCard.tsx (1)

195-203: Prefer stable keys over array indexes for default items.

Using indexes can cause unnecessary re-mounts if the array changes. Item names (or another stable id) are better keys.

Apply this diff:

-                    {defaultItems.map((item, index) => (
+                    {defaultItems.map((item) => (
-                      <button
-                        key={index}
+                      <button
+                        key={item.name}
                         onClick={() => addItem(item.name, item.icon)}
                         className="default-item-btn"
                       >
src/test/App.test.tsx (1)

21-28: react-hot-toast mock — LGTM; consider asserting calls.

Good isolation. To increase signal, add at least one assertion that toast.success was called after an action (e.g., adding baggage).

Apply this minimal diff:

+import { toast } from 'react-hot-toast'
@@
   it('can add a new baggage', async () => {
     const user = userEvent.setup()
     render(<App />)
@@
     const carryOnButton = screen.getByText('CARRY ON')
     await user.click(carryOnButton)
@@
     expect(screen.queryByText('No baggage added yet. Start by adding your first bag!')).not.toBeInTheDocument()
+    // Verify toast fired
+    expect(toast.success).toHaveBeenCalledWith(
+      'Added new baggage: carry on',
+      expect.any(Object)
+    )
   })

Note: If wording differs, adjust the expected string to match customToast.addBaggage().

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b581451 and d649e21.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • README.md (5 hunks)
  • package.json (1 hunks)
  • src/App.tsx (8 hunks)
  • src/ToastContext.tsx (1 hunks)
  • src/components/BaggageCard.tsx (4 hunks)
  • src/test/App.test.tsx (2 hunks)
  • src/test/ToastContext.test.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/test/ToastContext.test.tsx (1)
src/ToastContext.tsx (1)
  • customToast (47-119)
src/App.tsx (1)
src/ToastContext.tsx (2)
  • customToast (47-119)
  • toastConfig (7-32)
src/components/BaggageCard.tsx (1)
src/ToastContext.tsx (1)
  • customToast (47-119)
src/ToastContext.tsx (1)
src/iconMap.ts (1)
  • iconMap (23-46)
🪛 LanguageTool
README.md

[grammar] ~24-~24: There might be a mistake here.
Context: ...- Vite - React Hot Toast (notifications) - Ionicons (icon library) - PapaParse (CSV...

(QB_NEW_EN)


[grammar] ~105-~105: There might be a mistake here.
Context: ...the item's icon and confirmation message - Increasing quantity: Displays the item...

(QB_NEW_EN)


[grammar] ~106-~106: There might be a mistake here.
Context: ...m icon with quantity update confirmation - Removing items: Shows trash icon with ...

(QB_NEW_EN)


[grammar] ~107-~107: There might be a mistake here.
Context: ...ows trash icon with removal confirmation - Adding baggage: Displays suitcase icon...

(QB_NEW_EN)


[grammar] ~108-~108: There might be a mistake here.
Context: ...suitcase icon with bag type confirmation - Deleting baggage: Shows trash icon wit...

(QB_NEW_EN)


[grammar] ~109-~109: There might be a mistake here.
Context: ...ws trash icon with deletion confirmation - CSV operations: Success confirmations ...

(QB_NEW_EN)


[grammar] ~110-~110: There might be a mistake here.
Context: ... Success confirmations for import/export - Data clearing: Confirmation when all d...

(QB_NEW_EN)

🔇 Additional comments (15)
package.json (1)

27-27: Toaster configuration verified; please confirm React 19 compatibility

  • Confirmed in src/App.tsx (around lines 294–298) that the <Toaster> component is imported and rendered with
    • position="top-right"
    • toastOptions={{ duration: 2500, … }}
  • Addition of the react-hot-toast@^2.6.0 dependency is appropriate for the new feature.
  • Next step: In your SSR/Strict Mode React 19 environment, ensure there are no peer-dependency warnings or runtime issues with v2.6.x. If you encounter any compatibility warnings, consider pinning to a specific patch version or adding a note in the README.
src/components/BaggageCard.tsx (1)

89-97: Deletion toast placement — LGTM.

Finding the item before filtering ensures you still have the name for the toast post-update. Clean and side-effect-safe.

src/test/App.test.tsx (2)

6-19: matchMedia mock — LGTM.

Solid, browser-like stub that prevents layout-query branches from crashing tests.


52-57: window.confirm mock/reset — LGTM.

Clear, deterministic flow for destructive actions; resetting to true by default avoids brittle tests.

Also applies to: 65-67

src/App.tsx (7)

2-2: LGTM! Clean integration of toast notification system.

The imports are well-organized and the integration with the custom toast context follows the established patterns.

Also applies to: 14-14


118-118: LGTM! Toast notification for baggage addition.

The toast is triggered after the baggage is successfully added to the state, providing good user feedback.


145-145: LGTM! Toast notification for baggage deletion.

The toast is appropriately triggered after successful deletion and includes the baggage reference for personalized feedback.


158-158: LGTM! Toast notification for clearing all data.

The toast provides clear confirmation that all luggage data has been cleared.


206-209: LGTM! Improved CSV export with cleanup and toast notification.

Good additions:

  • Proper DOM cleanup by removing the temporary anchor element
  • URL cleanup with revokeObjectURL() to prevent memory leaks
  • User feedback with toast notification

256-256: LGTM! Toast notification for CSV import.

The toast is triggered after successful CSV parsing and data import, providing appropriate user feedback.


294-300: LGTM! Well-configured Toaster component.

The Toaster configuration is appropriate:

  • Top-right positioning for non-intrusive notifications
  • 2500ms duration aligns with the PR objectives for auto-dismiss
  • Proper integration with the custom toast styling
src/test/ToastContext.test.tsx (4)

6-12: LGTM! Proper mocking setup for react-hot-toast.

The mock implementation correctly provides spies for toast.success and toast.error methods, and a simple test component for the Toaster. This enables proper testing of toast functionality without actual toast behavior.


19-30: LGTM! Comprehensive API validation test.

The test properly validates that all expected customToast methods are exported and callable. The dynamic import approach is appropriate for testing module exports.


32-53: LGTM! Thorough testing of toast message content.

The test validates that:

  • Toast methods call the underlying toast.success with correct messages
  • Message formatting works correctly (e.g., hyphen replacement in "carry-on" → "carry on")
  • The toast configuration object is passed as expected

56-61: LGTM! Simple but effective Toaster integration test.

The test confirms that the Toaster component is properly rendered in the App component, validating the integration between the toast system and the main application.

Comment on lines +58 to 59
customToast.increaseQuantity(itemName, icon);
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use the persisted item icon for the toast when increasing quantity.

If the caller passes an incorrect icon (or icon mapping changes), the toast should still show the icon stored on the existing item.

Apply this diff:

-      customToast.increaseQuantity(itemName, icon);
+      const persistedIcon = updatedItems[existingItemIndex].icon;
+      customToast.increaseQuantity(itemName, persistedIcon);
📝 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
customToast.increaseQuantity(itemName, icon);
} else {
const persistedIcon = updatedItems[existingItemIndex].icon;
customToast.increaseQuantity(itemName, persistedIcon);
} else {
🤖 Prompt for AI Agents
In src/components/BaggageCard.tsx around lines 58-59, the call to
customToast.increaseQuantity currently uses the icon passed by the caller which
may be stale or incorrect; change it to use the persisted item icon from the
component's source of truth (e.g., look up the existing item in
props/state/store by itemName or id and pass that item's icon) so the toast
always shows the stored icon; ensure you handle the case where the lookup fails
by falling back to the passed icon or a default.

coderabbitai bot added a commit that referenced this pull request Aug 22, 2025
Docstrings generation was requested by @dralan71.

* #10 (comment)

The following files were modified:

* `src/App.tsx`
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 22, 2025

Note

Generated docstrings for this pull request at #11

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/App.tsx (1)

248-251: Normalize default icon name on CSV import.

“cube” doesn’t match your iconMap keys (“PiCube”). Today it falls back, but round-tripping CSV will preserve the wrong value. Normalize to “PiCube”.

Apply this diff:

-              icon: row.itemIcon || "cube",
+              icon: row.itemIcon || "PiCube",
-              packed: row.packed === "true",
+              packed: (row.packed || "").toLowerCase() === "true",
♻️ Duplicate comments (1)
src/components/BaggageCard.tsx (1)

58-59: Use the persisted item icon when increasing quantity.

Avoids stale/incorrect icon if the caller-provided icon is wrong or outdated.

Apply this diff:

-      customToast.increaseQuantity(itemName, icon);
+      const persistedIcon = updatedItems[existingItemIndex].icon || icon;
+      customToast.increaseQuantity(itemName, persistedIcon);
🧹 Nitpick comments (13)
package.json (1)

22-22: Consider moving gh-pages to devDependencies.

gh-pages is only used by the deploy script and doesn’t need to ship in production installs.

Apply this diff:

   "dependencies": {
-    "gh-pages": "^6.3.0",
     "ionicons": "^8.0.13",
     "papaparse": "^5.5.3",
     "react": "^19.1.0",
     "react-dom": "^19.1.0",
     "react-hot-toast": "^2.6.0",
     "react-icons": "^5.5.0"
   },
   "devDependencies": {
+    "gh-pages": "^6.3.0",
src/ToastContext.tsx (2)

35-44: Drop the unnecessary props cast and return a typed React element.

The explicit cast isn’t needed and can hide type issues. Also, returning a typed JSX element improves readability.

Apply this diff:

-const getStyledIconComponent = (iconName: string) => {
-  const IconComponent = iconMap[iconName] || iconMap["PiCube"];
-  
-  // React icons accept standard SVG props including style and color
-  return React.createElement(IconComponent, {
-    style: { color: '#27ae60' },
-    // Alternative: you can also use the color prop directly
-    // color: '#27ae60'
-  } as React.ComponentProps<typeof IconComponent>);
-};
+const getStyledIconComponent = (iconName: string): React.ReactElement => {
+  const IconComponent = iconMap[iconName] || iconMap["PiCube"];
+  return <IconComponent style={{ color: "#27ae60" }} />;
+};

85-89: Replace single-hyphen replace with replaceAll for robustness.

Handles cases with more than one hyphen in the type string.

Apply this diff:

-    return toast.success(`Added new baggage: ${baggageType.replace("-", " ")}`, {
+    return toast.success(`Added new baggage: ${baggageType.replaceAll("-", " ")}`, {
src/components/BaggageCard.tsx (1)

195-199: Avoid using array index as React key for defaultItems.

Use a stable key (e.g., item.name) to prevent unnecessary re-renders if the list changes.

Apply this diff:

-                    {defaultItems.map((item, index) => (
+                    {defaultItems.map((item) => (
-                      <button
-                        key={index}
+                      <button
+                        key={item.name}
                         onClick={() => addItem(item.name, item.icon)}
                         className="default-item-btn"
                       >
README.md (1)

24-24: Use the official package name casing.

For consistency with package.json and ecosystem docs, prefer “react-hot-toast”.

Apply this diff:

-- React Hot Toast (notifications)
+- react-hot-toast (notifications)
src/App.tsx (1)

211-218: Surface CSV parse errors to users via a toast.

Leverage customToast.error for immediate feedback if Papa reports errors.

Apply this minimal diff:

     Papa.parse(file, {
       header: true,
-      complete: (results) => {
+      complete: (results) => {
         interface CSVRow {
           baggageId?: string;
           itemIcon?: string;
           baggageType?: string;
           baggageNickname?: string;
           itemName?: string;
           quantity?: string;
           packed?: string;
         }
+        if (Array.isArray(results.errors) && results.errors.length > 0) {
+          customToast.error("CSV contained parse errors. Some rows may be skipped.");
+        }

I can extend this to show the first error message/details if you want.

Also applies to: 255-258

src/test/ToastContext.test.tsx (5)

3-3: Avoid relying on hoisting: import App after mocking react-hot-toast.

App is imported before the vi.mock for react-hot-toast. While Vitest hoists mocks, moving App import after the mock (or dynamically importing inside the test) avoids subtle ordering pitfalls.

Apply this diff to remove the early import:

-import App from '../App';

5-12: Strengthen the Toaster mock to capture props for config assertions.

Turning Toaster into a vi.fn lets you assert that App renders it with expected props (position: 'top-right', duration: 2500), aligning with the PR objective.

-vi.mock('react-hot-toast', () => ({
-  toast: {
-    success: vi.fn(),
-    error: vi.fn(),
-  },
-  Toaster: () => <div data-testid="toaster">Toaster</div>,
-}));
+vi.mock('react-hot-toast', () => {
+  const toasterMock = vi.fn((props: any) => <div data-testid="toaster">Toaster</div>);
+  return {
+    toast: {
+      success: vi.fn(),
+      error: vi.fn(),
+    },
+    Toaster: toasterMock,
+  };
+});

19-30: Expand API surface assertions to include success/error helpers.

customToast also exposes success and error; adding checks improves guardrails if these wrappers change.

   it('should export customToast functions', async () => {
     const { customToast } = await import('../ToastContext');
     expect(customToast).toBeDefined();
+    expect(typeof customToast.success).toBe('function');
+    expect(typeof customToast.error).toBe('function');
     expect(typeof customToast.addItem).toBe('function');
     expect(typeof customToast.increaseQuantity).toBe('function');
     expect(typeof customToast.removeItem).toBe('function');
     expect(typeof customToast.addBaggage).toBe('function');
     expect(typeof customToast.deleteBaggage).toBe('function');
     expect(typeof customToast.clearAll).toBe('function');
     expect(typeof customToast.exportCSV).toBe('function');
     expect(typeof customToast.importCSV).toBe('function');
   });

32-53: Assert option icons exist and avoid regressions in error path.

Given the icon-heavy UX, assert that each toast call supplies an icon, and that error isn’t triggered during happy-path.

   it('should call toast.success when customToast functions are used', async () => {
     const { toast } = await import('react-hot-toast');
     const { customToast } = await import('../ToastContext');
     
     customToast.addItem('Test Item', 'PiCube');
     expect(toast.success).toHaveBeenCalledWith(
       "Added item: 'Test Item'",
       expect.any(Object)
     );
+    // Icon present
+    expect((toast.success as any).mock.calls.at(-1)?.[1]?.icon).toBeDefined();

     customToast.addBaggage('carry-on');
     expect(toast.success).toHaveBeenCalledWith(
       'Added new baggage: carry on',
       expect.any(Object)
     );
+    expect((toast.success as any).mock.calls.at(-1)?.[1]?.icon).toBeDefined();

     customToast.removeItem('Test Item');
     expect(toast.success).toHaveBeenCalledWith(
       "Removed item: 'Test Item'",
       expect.any(Object)
     );
+    expect((toast.success as any).mock.calls.at(-1)?.[1]?.icon).toBeDefined();
+
+    // Happy path shouldn't call error
+    expect(toast.error).not.toHaveBeenCalled();
   });

Optional: add a separate test covering increaseQuantity, deleteBaggage (with nickname), clearAll, exportCSV, and importCSV to fully exercise wrappers. I can draft it if you want.


56-61: Import App dynamically in this test to guarantee mock order and assert Toaster config.

Dynamic import ensures the mock is applied before App is evaluated. You can also assert Toaster props since it’s now a vi.fn.

-describe('App with Toaster', () => {
-  it('should render Toaster component', () => {
-    render(<App />);
-    expect(screen.getByTestId('toaster')).toBeInTheDocument();
-  });
-});
+describe('App with Toaster', () => {
+  it('should render Toaster component', async () => {
+    const { default: App } = await import('../App');
+    render(<App />);
+    expect(screen.getByTestId('toaster')).toBeInTheDocument();
+    // Optional: verify config
+    const { Toaster } = await import('react-hot-toast');
+    expect((Toaster as any).mock.calls[0][0]).toMatchObject({
+      position: 'top-right',
+      toastOptions: expect.objectContaining({ duration: 2500 }),
+    });
+  });
+});
src/test/App.test.tsx (2)

21-29: Capture Toaster props to assert configuration (position/duration).

Mirroring the suggestion in ToastContext tests, making Toaster a vi.fn enables verifying the PR’s top-right and 2500ms settings from App.

-vi.mock('react-hot-toast', () => ({
-  toast: {
-    success: vi.fn(),
-    error: vi.fn(),
-  },
-  Toaster: () => <div data-testid="toaster">Toaster</div>,
-}));
+vi.mock('react-hot-toast', () => {
+  const toasterMock = vi.fn((props: any) => <div data-testid="toaster">Toaster</div>);
+  return {
+    toast: {
+      success: vi.fn(),
+      error: vi.fn(),
+    },
+    Toaster: toasterMock,
+  };
+});

Follow-up: In one of the existing tests that renders , assert:

  • Toaster was rendered once.
  • Props include position: 'top-right' and toastOptions.duration === 2500.

I can add the exact assertion snippet on request.


52-56: Make window.confirm override configurable and restore after tests.

Setting configurable: true avoids descriptor errors across environments. Also consider restoring the original after the suite to prevent leakage.

 Object.defineProperty(window, 'confirm', {
-  writable: true,
-  value: vi.fn(),
+  writable: true,
+  configurable: true,
+  value: vi.fn(),
 });

Optional pattern:

const originalConfirm = window.confirm;
beforeEach(() => { window.confirm = vi.fn(() => true); });
afterAll(() => { window.confirm = originalConfirm; });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b581451 and 9c3f43c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • README.md (5 hunks)
  • package.json (1 hunks)
  • src/App.tsx (8 hunks)
  • src/ToastContext.tsx (1 hunks)
  • src/components/BaggageCard.tsx (4 hunks)
  • src/test/App.test.tsx (2 hunks)
  • src/test/ToastContext.test.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/test/ToastContext.test.tsx (1)
src/ToastContext.tsx (1)
  • customToast (47-119)
src/components/BaggageCard.tsx (1)
src/ToastContext.tsx (1)
  • customToast (47-119)
src/ToastContext.tsx (1)
src/iconMap.ts (1)
  • iconMap (23-46)
src/App.tsx (1)
src/ToastContext.tsx (2)
  • customToast (47-119)
  • toastConfig (7-32)
🪛 LanguageTool
README.md

[grammar] ~22-~22: There might be a mistake here.
Context: ...## Tech Stack - React 19.x + TypeScript - Vite - React Hot Toast (notifications) -...

(QB_NEW_EN)


[grammar] ~23-~23: There might be a mistake here.
Context: ... Stack - React 19.x + TypeScript - Vite - React Hot Toast (notifications) - Ionico...

(QB_NEW_EN)


[grammar] ~24-~24: There might be a mistake here.
Context: ...- Vite - React Hot Toast (notifications) - Ionicons (icon library) - PapaParse (CSV...

(QB_NEW_EN)


[grammar] ~105-~105: There might be a mistake here.
Context: ...the item's icon and confirmation message - Increasing quantity: Displays the item...

(QB_NEW_EN)


[grammar] ~106-~106: There might be a mistake here.
Context: ...m icon with quantity update confirmation - Removing items: Shows trash icon with ...

(QB_NEW_EN)


[grammar] ~107-~107: There might be a mistake here.
Context: ...ows trash icon with removal confirmation - Adding baggage: Displays suitcase icon...

(QB_NEW_EN)


[grammar] ~108-~108: There might be a mistake here.
Context: ...suitcase icon with bag type confirmation - Deleting baggage: Shows trash icon wit...

(QB_NEW_EN)


[grammar] ~109-~109: There might be a mistake here.
Context: ...ws trash icon with deletion confirmation - CSV operations: Success confirmations ...

(QB_NEW_EN)


[grammar] ~110-~110: There might be a mistake here.
Context: ... Success confirmations for import/export - Data clearing: Confirmation when all d...

(QB_NEW_EN)

🔇 Additional comments (6)
package.json (1)

27-27: Dependency addition looks good.

Adding react-hot-toast ^2.6.0 aligns with the new toast system introduced in this PR.

src/ToastContext.tsx (1)

6-32: Shared dark theme config is clean and reusable.

Nice centralization of base, success, and error variants for consistent styling.

README.md (1)

13-14: Great UX doc for Toast Notifications.

Clear, user-centric explanation of when and how toasts appear.

src/App.tsx (1)

2-2: react-hot-toast v2.6.0 is fully compatible with React 19

Verified that:

  • package.json declares
    • react @ ^19.1.0
    • react-dom @ ^19.1.0
    • react-hot-toast @ ^2.6.0
  • react-hot-toast@2.6.0 peerDependencies require react and react-dom ≥ 16, so React 19 satisfies this with no expected runtime warnings.

No changes necessary.

src/test/App.test.tsx (2)

6-19: matchMedia mock looks good and avoids JSDOM pitfalls.

Solid, comprehensive mock including deprecated listeners for broad compatibility.


64-67: Verification complete – no direct toast. calls outside of your ToastContext wrapper*

  • The grep for toast.success/toast.error only surfaced calls in src/ToastContext.tsx, which is the intended spot for your custom wrapper. All other modules rely on customToast as expected.
  • The Toaster component is imported in src/App.tsx and rendered once at the root (line 294), with no duplicates elsewhere.

You can tighten your verification script accordingly:

#!/bin/bash
# Verify no direct toast.success/error calls outside tests or your ToastContext wrapper
rg -nP \
  --glob '!src/test/**' \
  --glob '!**/*.spec.*' \
  --glob '!**/*.test.*' \
  --glob '!src/ToastContext.tsx' \
  '\btoast\.(success|error)\s*\(' src || true

# Ensure Toaster is only rendered once in App.tsx
rg -nP --glob '!src/test/**' '\b<Toaster\b' src/App.tsx || true

All looks good here.

@dralan71 dralan71 merged commit b48b966 into main Aug 22, 2025
2 checks passed
This was referenced Feb 8, 2026
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.

Add toast notifications for item and baggage actions

1 participant