-
Notifications
You must be signed in to change notification settings - Fork 0
4 add toast notifications #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…in App and BaggageCard components
…consistency and icon support in App and BaggageCard components
WalkthroughIntroduces 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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.
⛔ Files ignored due to path filters (1)
package-lock.jsonis 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.0dependency 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.successandtoast.errormethods, 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
customToastmethods 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.successwith 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.
| customToast.increaseQuantity(itemName, icon); | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
| 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.
Docstrings generation was requested by @dralan71. * #10 (comment) The following files were modified: * `src/App.tsx`
|
Note Generated docstrings for this pull request at #11 |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
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.
⛔ Files ignored due to path filters (1)
package-lock.jsonis 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 19Verified 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.erroronly surfaced calls insrc/ToastContext.tsx, which is the intended spot for your custom wrapper. All other modules rely oncustomToastas expected.- The
Toastercomponent is imported insrc/App.tsxand 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 || trueAll looks good here.
name: Pull Request
about: Propose changes to improve Pack## Checklist
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-toastand a newToastContexthelper (src/ToastContext.tsx) exposingcustomToastfunctions to keep notifications consistent.Key behavior added:
toast.success(...)calls withcustomToast.*helpersFixes #4
Type of Change
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:
Manual UI checks performed:
Verified
Toasterdisplay and that toasts auto-dismiss (duration set to 2500ms)Automated tests:
src/test/ToastContext.test.tsx) that verifies:customToastfunctions are exported and callabletoast.success()with correct messagesToastercomponent renders in the AppChecklist
Files changed (summary of diffs)
A src/ToastContext.tsx— new centralized toast helpers + stylingA src/test/ToastContext.test.tsx— Vitest tests for toast functionalityM src/App.tsx— usecustomToast, addToasterconfigM src/components/BaggageCard.tsx— usecustomToastin add/remove flowsM README.md— added toast notification documentation and updated tech stackM package.json— addreact-hot-toastM package-lock.json— lockfile updates forreact-hot-toastNotes / Decisions
ToastContextcentralizes toast behavior so future styling or icon logic changes only need a single update.style: { color: '#27ae60' }) so the icon centers correctly and inherits the intended color. This avoids layout shifts introduced by wrapper elements.Next steps (optional)
4-add-toast-notificationsintomainusing this body (I can create it if you want).Toasterrenders and/or thatcustomToastfunctions are callable.Summary by CodeRabbit