Skip to content

Conversation

@Arian94
Copy link

@Arian94 Arian94 commented Feb 2, 2025

Recreates the #3664 PR (fixes #3640).

Summary by CodeRabbit

  • New Features

    • Added a new prop to the Image component that allows displaying a custom loading image while the main image is loading.
    • Enhanced the Image component to support additional styling options for loading and fallback states.
    • Introduced new wrapper style slots for loading and fallback image states.
  • Documentation

    • Updated documentation to explain the new loading image feature, clarify loading and fallback behaviors, and provide new usage examples and API details.
  • Tests

    • Expanded test coverage to verify image loading, custom loading image display, and fallback behavior.
  • Chores

    • Updated Storybook stories and documentation demos to showcase the new loading image functionality.

@Arian94 Arian94 requested a review from jrgarciadev as a code owner February 2, 2025 07:07
@changeset-bot
Copy link

changeset-bot bot commented Feb 2, 2025

🦋 Changeset detected

Latest commit: b0cf753

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@heroui/image Patch
@heroui/theme Patch
@heroui/react Patch
@heroui/autocomplete Patch
@heroui/checkbox Patch
@heroui/date-input Patch
@heroui/date-picker Patch
@heroui/form Patch
@heroui/input-otp Patch
@heroui/input Patch
@heroui/number-input Patch
@heroui/radio Patch
@heroui/select Patch
@heroui/table Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Feb 2, 2025

Someone is attempting to deploy a commit to the HeroUI Inc Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2025

Warning

Rate limit exceeded

@Arian94 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 1 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c9a3df6 and b0cf753.

📒 Files selected for processing (1)
  • packages/components/image/__tests__/image.test.tsx (3 hunks)

Walkthrough

A new loadingSrc prop has been introduced to the Image component, allowing a custom image to be displayed during loading. The implementation updates component logic, documentation, stories, and tests to distinguish between loading and error (fallback) states, and provides new demo and documentation content for this feature.

Changes

Files/Groups Change Summary
packages/components/image/src/image.tsx, packages/components/image/src/use-image.ts Added loadingSrc prop, updated logic to distinguish loading and error states, adjusted wrapper conditions.
packages/components/image/stories/image.stories.tsx Added/renamed stories to demonstrate loadingSrc, fallback, and their combinations.
apps/docs/content/docs/components/image.mdx Updated documentation: added "Custom Loading Image" section, clarified fallback, updated API table.
apps/docs/content/components/image/customLoading.raw.jsx, customLoading.ts Added new demo component and export for custom loading image usage.
apps/docs/content/components/image/index.ts Registered the new custom loading demo in the image content exports.
apps/docs/content/components/image/fallback.raw.jsx Updated fallback demo to clarify fallback behavior and styling.
packages/components/image/__tests__/image.test.tsx Added and updated tests for loading and fallback image scenarios.
.changeset/brave-tools-mate.md Added changeset documenting the new loadingSrc prop and classNames extension.
packages/core/theme/src/components/image.ts Added new empty slots for loading and fallback image wrappers in theme variants.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ImageComponent
    participant useImageHook

    User->>ImageComponent: Render with src, loadingSrc, fallbackSrc
    ImageComponent->>useImageHook: Initialize with props
    useImageHook->>ImageComponent: Returns state (loading, error, loaded)
    alt While loading
        ImageComponent->>User: Display loadingSrc image
    else If error
        ImageComponent->>User: Display fallbackSrc image
    else When loaded
        ImageComponent->>User: Display main src image
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Add ability to distinguish between loading and error states in Image component (#3640)
Add new prop for loading image and update logic in use-image.ts (#3640)

Suggested reviewers

  • wingkwong
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 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.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

Documentation and Community

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

Copy link
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

🧹 Nitpick comments (5)
apps/docs/content/components/image/customLoading.raw.jsx (1)

8-9: Consider using local assets for demo reliability.

The demo uses external services for both the placeholder and the delayed image:

  • via.placeholder.com for loading state
  • requestly.io for simulating delay

This could make the demo unreliable if these services are down.

Consider using local assets hosted within the project for both the placeholder and the main image. For delay simulation, consider using a local development proxy or built-in delay mechanism.

packages/components/image/stories/image.stories.tsx (2)

125-125: Consider more reliable delay simulation method.

Multiple stories use requestly.io for delay simulation which could make the stories unreliable in offline environments or if the service is down.

Consider using React Suspense or a local delay mechanism for more reliable story demonstrations.

Also applies to: 146-147


165-177: Add story description for CustomLoadingAndFallback.

The new story demonstrates an important use case but lacks a description of what it's showcasing.

Add a JSDoc comment explaining the purpose of this story and when to use both loadingSrc and fallbackSrc together.

packages/components/image/src/use-image.ts (1)

27-30: Enhance loadingSrc prop documentation.

The current documentation is minimal. Consider adding:

  • Usage example
  • Default value
  • Note about interaction with disableSkeleton
 /**
  * A loading image.
+ * @example
+ * ```jsx
+ * <Image loadingSrc="/loading.gif" src="/main.jpg" />
+ * ```
+ * @default undefined
+ * @note When provided, disables the default skeleton loader
  */
apps/docs/content/docs/components/image.mdx (1)

165-169: API Enhancement: loadingSrc Prop Entry
The addition of the loadingSrc prop within the API table is well-documented. Its type, description, and default value are clearly stated, providing comprehensive guidance for developers on how to use this new feature. Consider verifying whether the default value ("-") accurately represents the intended default state (e.g., an empty string might be more conventional) if applicable.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92281a6 and ba8eeb3.

📒 Files selected for processing (6)
  • apps/docs/content/components/image/customLoading.raw.jsx (1 hunks)
  • apps/docs/content/components/image/customLoading.ts (1 hunks)
  • apps/docs/content/docs/components/image.mdx (2 hunks)
  • packages/components/image/src/image.tsx (2 hunks)
  • packages/components/image/src/use-image.ts (7 hunks)
  • packages/components/image/stories/image.stories.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/docs/content/components/image/customLoading.ts
🔇 Additional comments (4)
packages/components/image/src/image.tsx (1)

16-16: LGTM! Clean integration of loadingSrc.

The loadingSrc prop is well-integrated into the existing component structure, maintaining consistency with the current implementation pattern.

Also applies to: 49-49

packages/components/image/src/use-image.ts (1)

140-142: LGTM! Clear separation of loading states.

The loading state logic is well-organized with clear separation between:

  • Loading state with custom image
  • Error state with fallback
  • Default skeleton loader
apps/docs/content/docs/components/image.mdx (2)

66-73: Documentation Clarity: "Custom Loading Image" Section
The new section clearly explains the purpose of the loadingSrc prop and how it allows a custom loading image to be displayed while the main image is still loading. The use of a dedicated code demo and a note about the simulated network delay is consistent with other sections in the document.


172-176: Improved Clarity: fallbackSrc Prop Description Update
The updated description for the fallbackSrc prop now specifies that it is used when the main image fails to load or encounters an error, which is clearer than the previous explanation. This update helps avoid ambiguity about the prop’s behavior.

Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

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

please include tests

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

🧹 Nitpick comments (1)
apps/docs/content/docs/components/image.mdx (1)

57-60: Add "the" before "Image component" for better readability.

The sentence should read "The Image component has a built-in skeleton animation..."

-Image component has a built-in `skeleton` animation to indicate the image is loading in case the `loadingSrc` is not defined.
+The Image component has a built-in `skeleton` animation to indicate the image is loading in case the `loadingSrc` is not defined.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~58-~58: You might be missing the article “the” here.
Context: ...d} /> ### Animated (Skeleton) Loading Image component has a built-in skeleton ani...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba8eeb3 and 33053b4.

📒 Files selected for processing (4)
  • apps/docs/content/components/image/customLoading.raw.jsx (1 hunks)
  • apps/docs/content/components/image/fallback.raw.jsx (1 hunks)
  • apps/docs/content/components/image/index.ts (2 hunks)
  • apps/docs/content/docs/components/image.mdx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/docs/content/components/image/customLoading.raw.jsx
🧰 Additional context used
🪛 LanguageTool
apps/docs/content/docs/components/image.mdx

[uncategorized] ~58-~58: You might be missing the article “the” here.
Context: ...d} /> ### Animated (Skeleton) Loading Image component has a built-in skeleton ani...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🔇 Additional comments (5)
apps/docs/content/components/image/fallback.raw.jsx (1)

9-9: LGTM! Good example for demonstrating fallback behavior.

The intentionally invalid image source effectively demonstrates the fallback functionality.

apps/docs/content/components/image/index.ts (1)

5-5: LGTM! Clean integration of the new custom loading example.

The changes follow the existing pattern for importing and exporting examples.

Also applies to: 14-14

apps/docs/content/docs/components/image.mdx (3)

65-71: LGTM! Clear documentation for the new custom loading feature.

The new section effectively explains the loadingSrc prop with a practical example and network simulation note.


83-84: LGTM! Helpful note about combining loading and fallback states.

Good addition clarifying that both states can be handled simultaneously.


164-169: LGTM! Well-documented API properties.

The new loadingSrc prop and updated fallbackSrc description are clear and accurate.

Also applies to: 173-173

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

🧹 Nitpick comments (3)
packages/components/image/__tests__/image.test.tsx (3)

30-39: Enhance loading state test coverage.

The test could be improved in several ways:

  1. Add cleanup to prevent test pollution
  2. Add explicit null check instead of using non-null assertion
  3. Test the transition from loading to loaded state

Consider this improved version:

 test("renders loading source while loading the image.", async () => {
   const wrapper = render(<Image loadingSrc={loadingSrc} src={src} />);
   const imageParent = wrapper.getByRole("img").parentElement;
 
-  expect(imageParent?.getAttribute("data-testid")).toEqual("heroUI/image_parent");
+  expect(imageParent).not.toBeNull();
+  expect(imageParent!.getAttribute("data-testid")).toEqual("heroUI/image_parent");
 
   const computedStyle = window.getComputedStyle(imageParent!);
 
   expect(computedStyle.backgroundImage).toBe(`url(${loadingSrc})`);
+
+  // Test transition to loaded state
+  fireEvent.load(wrapper.getByRole("img"));
+  await waitFor(() => {
+    const updatedStyle = window.getComputedStyle(imageParent!);
+    expect(updatedStyle.backgroundImage).toBe(`url(${src})`);
+  });
+
+  // Cleanup
+  wrapper.unmount();
 });

41-55: Optimize error handling test.

The test has some potential improvements:

  1. The 6-second timeout seems excessive for a unit test
  2. Missing cleanup
  3. Could use more specific error simulation

Consider this improved version:

 test("renders fallback source if src is wrong or not found.", async () => {
   const onError = jest.fn();
   const wrapper = render(
     <Image alt="test" fallbackSrc={fallbackSrc} src="wrong-src-address" onError={onError} />,
   );
   const imageParent = wrapper.getByRole("img").parentElement;
 
-  fireEvent.error(wrapper.getByRole("img"));
+  // Simulate specific error type
+  fireEvent.error(wrapper.getByRole("img"), {
+    target: { error: new Error('Failed to load image') }
+  });
 
-  await waitFor(() => expect(onError).toHaveBeenCalled(), {timeout: 5_000});
+  await waitFor(() => expect(onError).toHaveBeenCalled(), {timeout: 1000});
   expect(imageParent?.getAttribute("data-testid")).toEqual("heroUI/image_parent");
 
   const computedStyle = window.getComputedStyle(imageParent!);
 
   expect(computedStyle.backgroundImage).toBe(`url(${fallbackSrc})`);
-}, 6_000);
+
+  // Cleanup
+  wrapper.unmount();
+}, 2000);

57-61: Add cleanup to prevent test pollution.

The test is well-structured but should include cleanup to prevent potential test pollution.

 test("renders image if there is no loading or fallback behavior defined", async () => {
   const wrapper = render(<Image src={src} />);
 
   expect(wrapper.getByRole("img")).toHaveAttribute("src", src);
+  wrapper.unmount();
 });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33053b4 and d763e24.

📒 Files selected for processing (2)
  • packages/components/image/__tests__/image.test.tsx (2 hunks)
  • packages/components/image/src/image.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/components/image/src/image.tsx
🔇 Additional comments (1)
packages/components/image/__tests__/image.test.tsx (1)

8-8: Consider using an absolute URL for loadingSrc.

Using a relative path /images/local-image-small.jpg might cause issues in different test environments. Consider using an absolute URL (like the placeholder URLs used for src and fallbackSrc) to ensure consistent test behavior across environments.

@Arian94
Copy link
Author

Arian94 commented Feb 4, 2025

please include tests

I couldn't. The second test needs the onError handler to get called but it doesn't. Don't know how to make it work.

@wingkwong
Copy link
Member

you may take a look at packages/hooks/use-image/__tests__/use-image.test.tsx

@Arian94
Copy link
Author

Arian94 commented Feb 8, 2025

you may take a look at packages/hooks/use-image/__tests__/use-image.test.tsx

done.

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d763e24 and de08d98.

📒 Files selected for processing (1)
  • packages/components/image/__tests__/image.test.tsx (4 hunks)
🔇 Additional comments (3)
packages/components/image/__tests__/image.test.tsx (3)

50-71: LGTM! Well-structured test for loading state.

The test thoroughly verifies both the loading state and the final loaded state, including:

  • Loading image display
  • onLoad callback
  • Proper cleanup

110-115: LGTM! Clear test description update.

The test description now accurately reflects the expanded scope including loading behavior.


33-33: LGTM! Consistent cleanup practice.

Good addition of wrapper.unmount() calls across all test cases to prevent memory leaks and test interference.

Also applies to: 47-47, 70-70, 107-107, 114-114, 121-121, 129-129, 143-143, 164-164

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

🧹 Nitpick comments (2)
packages/components/image/__tests__/image.test.tsx (2)

30-38: Optimize cleanup code performance.

The cleanup code is good but can be optimized by avoiding the delete operator.

   afterAll(() => {
-    delete window.Image.prototype._onload;
+    window.Image.prototype._onload = undefined;
     Object.defineProperty(window.Image.prototype, "onload", {
       value: null,
       writable: true,
       configurable: true,
     });
   });
🧰 Tools
🪛 Biome (1.9.4)

[error] 32-32: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


84-130: Enhance error handling test.

The test case is good but can be improved in two areas:

  1. Performance optimization for cleanup
  2. More robust error simulation
   const cleanup = () => {
-    delete window.Image.prototype._onerror;
+    window.Image.prototype._onerror = undefined;
     Object.defineProperty(window.Image.prototype, "onerror", {
       value: null,
       writable: true,
       configurable: true,
     });
   };

   act(() => {
-    imageOnerror();
+    imageOnerror(new Event('error'));
   });
🧰 Tools
🪛 Biome (1.9.4)

[error] 103-103: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de08d98 and ef99401.

📒 Files selected for processing (1)
  • packages/components/image/__tests__/image.test.tsx (4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/components/image/__tests__/image.test.tsx

[error] 32-32: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 103-103: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🔇 Additional comments (4)
packages/components/image/__tests__/image.test.tsx (4)

7-8: LGTM! Constants are well-defined and descriptive.

The constants are appropriately named and their values are clear and meaningful.


13-28: LGTM! Image onload tracking setup is well-implemented.

The setup code properly tracks the image onload event by modifying the Image prototype, which is essential for testing loading states.


61-82: LGTM! Loading state test is comprehensive.

The test case thoroughly verifies:

  • Initial loading image display
  • Proper loading state transition
  • onLoad callback invocation
  • Cleanup after test completion

132-137: LGTM! Test cases have proper cleanup.

All test cases now include proper cleanup by unmounting components after test completion.

Also applies to: 139-187

@vercel
Copy link

vercel bot commented Mar 2, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
heroui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 5, 2025 4:39am
heroui-sb ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 5, 2025 4:39am

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 2, 2025

Open in StackBlitz

@heroui/accordion

npm i https://pkg.pr.new/@heroui/accordion@4783

@heroui/alert

npm i https://pkg.pr.new/@heroui/alert@4783

@heroui/autocomplete

npm i https://pkg.pr.new/@heroui/autocomplete@4783

@heroui/avatar

npm i https://pkg.pr.new/@heroui/avatar@4783

@heroui/badge

npm i https://pkg.pr.new/@heroui/badge@4783

@heroui/breadcrumbs

npm i https://pkg.pr.new/@heroui/breadcrumbs@4783

@heroui/button

npm i https://pkg.pr.new/@heroui/button@4783

@heroui/calendar

npm i https://pkg.pr.new/@heroui/calendar@4783

@heroui/card

npm i https://pkg.pr.new/@heroui/card@4783

@heroui/checkbox

npm i https://pkg.pr.new/@heroui/checkbox@4783

@heroui/chip

npm i https://pkg.pr.new/@heroui/chip@4783

@heroui/code

npm i https://pkg.pr.new/@heroui/code@4783

@heroui/date-input

npm i https://pkg.pr.new/@heroui/date-input@4783

@heroui/date-picker

npm i https://pkg.pr.new/@heroui/date-picker@4783

@heroui/divider

npm i https://pkg.pr.new/@heroui/divider@4783

@heroui/drawer

npm i https://pkg.pr.new/@heroui/drawer@4783

@heroui/dropdown

npm i https://pkg.pr.new/@heroui/dropdown@4783

@heroui/form

npm i https://pkg.pr.new/@heroui/form@4783

@heroui/image

npm i https://pkg.pr.new/@heroui/image@4783

@heroui/input

npm i https://pkg.pr.new/@heroui/input@4783

@heroui/input-otp

npm i https://pkg.pr.new/@heroui/input-otp@4783

@heroui/kbd

npm i https://pkg.pr.new/@heroui/kbd@4783

@heroui/link

npm i https://pkg.pr.new/@heroui/link@4783

@heroui/listbox

npm i https://pkg.pr.new/@heroui/listbox@4783

@heroui/menu

npm i https://pkg.pr.new/@heroui/menu@4783

@heroui/modal

npm i https://pkg.pr.new/@heroui/modal@4783

@heroui/navbar

npm i https://pkg.pr.new/@heroui/navbar@4783

@heroui/number-input

npm i https://pkg.pr.new/@heroui/number-input@4783

@heroui/pagination

npm i https://pkg.pr.new/@heroui/pagination@4783

@heroui/popover

npm i https://pkg.pr.new/@heroui/popover@4783

@heroui/progress

npm i https://pkg.pr.new/@heroui/progress@4783

@heroui/radio

npm i https://pkg.pr.new/@heroui/radio@4783

@heroui/ripple

npm i https://pkg.pr.new/@heroui/ripple@4783

@heroui/scroll-shadow

npm i https://pkg.pr.new/@heroui/scroll-shadow@4783

@heroui/select

npm i https://pkg.pr.new/@heroui/select@4783

@heroui/skeleton

npm i https://pkg.pr.new/@heroui/skeleton@4783

@heroui/slider

npm i https://pkg.pr.new/@heroui/slider@4783

@heroui/snippet

npm i https://pkg.pr.new/@heroui/snippet@4783

@heroui/spacer

npm i https://pkg.pr.new/@heroui/spacer@4783

@heroui/spinner

npm i https://pkg.pr.new/@heroui/spinner@4783

@heroui/switch

npm i https://pkg.pr.new/@heroui/switch@4783

@heroui/table

npm i https://pkg.pr.new/@heroui/table@4783

@heroui/tabs

npm i https://pkg.pr.new/@heroui/tabs@4783

@heroui/toast

npm i https://pkg.pr.new/@heroui/toast@4783

@heroui/tooltip

npm i https://pkg.pr.new/@heroui/tooltip@4783

@heroui/user

npm i https://pkg.pr.new/@heroui/user@4783

@heroui/react

npm i https://pkg.pr.new/@heroui/react@4783

@heroui/system

npm i https://pkg.pr.new/@heroui/system@4783

@heroui/system-rsc

npm i https://pkg.pr.new/@heroui/system-rsc@4783

@heroui/theme

npm i https://pkg.pr.new/@heroui/theme@4783

@heroui/use-aria-accordion

npm i https://pkg.pr.new/@heroui/use-aria-accordion@4783

@heroui/use-aria-accordion-item

npm i https://pkg.pr.new/@heroui/use-aria-accordion-item@4783

@heroui/use-aria-button

npm i https://pkg.pr.new/@heroui/use-aria-button@4783

@heroui/use-aria-link

npm i https://pkg.pr.new/@heroui/use-aria-link@4783

@heroui/use-aria-modal-overlay

npm i https://pkg.pr.new/@heroui/use-aria-modal-overlay@4783

@heroui/use-aria-multiselect

npm i https://pkg.pr.new/@heroui/use-aria-multiselect@4783

@heroui/use-callback-ref

npm i https://pkg.pr.new/@heroui/use-callback-ref@4783

@heroui/use-clipboard

npm i https://pkg.pr.new/@heroui/use-clipboard@4783

@heroui/use-data-scroll-overflow

npm i https://pkg.pr.new/@heroui/use-data-scroll-overflow@4783

@heroui/use-disclosure

npm i https://pkg.pr.new/@heroui/use-disclosure@4783

@heroui/use-draggable

npm i https://pkg.pr.new/@heroui/use-draggable@4783

@heroui/use-image

npm i https://pkg.pr.new/@heroui/use-image@4783

@heroui/use-infinite-scroll

npm i https://pkg.pr.new/@heroui/use-infinite-scroll@4783

@heroui/use-intersection-observer

npm i https://pkg.pr.new/@heroui/use-intersection-observer@4783

@heroui/use-is-mobile

npm i https://pkg.pr.new/@heroui/use-is-mobile@4783

@heroui/use-is-mounted

npm i https://pkg.pr.new/@heroui/use-is-mounted@4783

@heroui/use-measure

npm i https://pkg.pr.new/@heroui/use-measure@4783

@heroui/use-pagination

npm i https://pkg.pr.new/@heroui/use-pagination@4783

@heroui/use-real-shape

npm i https://pkg.pr.new/@heroui/use-real-shape@4783

@heroui/use-ref-state

npm i https://pkg.pr.new/@heroui/use-ref-state@4783

@heroui/use-resize

npm i https://pkg.pr.new/@heroui/use-resize@4783

@heroui/use-safe-layout-effect

npm i https://pkg.pr.new/@heroui/use-safe-layout-effect@4783

@heroui/use-scroll-position

npm i https://pkg.pr.new/@heroui/use-scroll-position@4783

@heroui/use-ssr

npm i https://pkg.pr.new/@heroui/use-ssr@4783

@heroui/use-theme

npm i https://pkg.pr.new/@heroui/use-theme@4783

@heroui/use-update-effect

npm i https://pkg.pr.new/@heroui/use-update-effect@4783

@heroui/aria-utils

npm i https://pkg.pr.new/@heroui/aria-utils@4783

@heroui/dom-animation

npm i https://pkg.pr.new/@heroui/dom-animation@4783

@heroui/framer-utils

npm i https://pkg.pr.new/@heroui/framer-utils@4783

@heroui/react-rsc-utils

npm i https://pkg.pr.new/@heroui/react-rsc-utils@4783

@heroui/react-utils

npm i https://pkg.pr.new/@heroui/react-utils@4783

@heroui/shared-icons

npm i https://pkg.pr.new/@heroui/shared-icons@4783

@heroui/shared-utils

npm i https://pkg.pr.new/@heroui/shared-utils@4783

@heroui/stories-utils

npm i https://pkg.pr.new/@heroui/stories-utils@4783

@heroui/test-utils

npm i https://pkg.pr.new/@heroui/test-utils@4783

commit: e3fbbda

Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

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

please add changeset

@Arian94 Arian94 requested a review from wingkwong July 4, 2025 13:53
Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

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

not looking great

image

@wingkwong wingkwong removed this from the v2.8.0 milestone Jul 8, 2025
@wingkwong wingkwong removed their assignment Jul 8, 2025
@Arian94 Arian94 requested a review from wingkwong July 8, 2025 17:01
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

🧹 Nitpick comments (1)
apps/docs/content/docs/components/image.mdx (1)

101-102: Fix grammatical error in slots description.

There's a duplicate "the" in the fallbackImgWrapper description.

Apply this diff to fix the grammar:

-- **fallbackImgWrapper**: Applies styles to the the "wrapper" only in the failed state.
+- **fallbackImgWrapper**: Applies styles to the "wrapper" only in the failed state.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3fbbda and 02252a1.

📒 Files selected for processing (8)
  • apps/docs/content/components/image/customLoading.raw.jsx (1 hunks)
  • apps/docs/content/components/image/fallback.raw.jsx (1 hunks)
  • apps/docs/content/docs/components/image.mdx (4 hunks)
  • packages/components/image/__tests__/image.test.tsx (2 hunks)
  • packages/components/image/src/image.tsx (2 hunks)
  • packages/components/image/src/use-image.ts (7 hunks)
  • packages/components/image/stories/image.stories.tsx (2 hunks)
  • packages/core/theme/src/components/image.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/components/image/src/image.tsx
  • apps/docs/content/components/image/fallback.raw.jsx
  • apps/docs/content/components/image/customLoading.raw.jsx
  • packages/components/image/stories/image.stories.tsx
  • packages/components/image/src/use-image.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/components/image/__tests__/image.test.tsx

[error] 32-32: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 107-107: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🪛 LanguageTool
apps/docs/content/docs/components/image.mdx

[grammar] ~59-~59: Use proper spacing conventions.
Context: ...in case the loadingSrc is not defined. > Note: The URL uses `https://app.requ...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~63-~63: Use proper spacing conventions.
Context: ...ly.io/delayto simulate a slow network. ### Custom Loading Image You can use thel...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~65-~65: Use proper spacing conventions.
Context: ... slow network. ### Custom Loading Image You can use the loadingSrc prop to dis...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~67-~67: Use proper spacing conventions.
Context: ...mage provided in src is still loading. > Note: The URL uses `https://app.requ...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~71-~71: Use proper spacing conventions.
Context: ...ly.io/delayto simulate a slow network. ### Image with fallback You can use thefa...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~73-~73: Use proper spacing conventions.
Context: ...a slow network. ### Image with fallback You can use the fallbackSrc prop to di...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~75-~75: Use proper spacing conventions.
Context: ...prop to display a fallback image when: - ThefallbackSrc` prop is provided. - Th...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~77-~77: Use proper spacing conventions.
Context: ...: - The fallbackSrc prop is provided. - The image provided in src fails to loa...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~78-~78: Use proper spacing conventions.
Context: ...e image provided in src fails to load. - The image provided in src is not found...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~79-~79: Use proper spacing conventions.
Context: ...he image provided in src is not found. > Note: You can have both loadingSrc a...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~83-~83: Use proper spacing conventions.
Context: ...while loading and handling image errors. ### With Next.js Image Next.js provides an ...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)


[grammar] ~101-~101: There might be a problem here.
Context: ...the "wrapper" only in the loading state. - fallbackImgWrapper: Applies styles to the the "wrapper" onl...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~102-~102: Avoid repeating words in a sentence.
Context: ...fallbackImgWrapper**: Applies styles to the the "wrapper" only in the failed state....

(QB_NEW_EN_OTHER_ERROR_IDS_000021)


[grammar] ~102-~102: Use proper spacing conventions.
Context: ... the "wrapper" only in the failed state. ## API ### Image Props <APITable data={...

(QB_NEW_EN_OTHER_ERROR_IDS_000007)

🔇 Additional comments (8)
packages/core/theme/src/components/image.ts (1)

40-41: LGTM! Clean addition of new theme slots.

The new loadingImgWrapper and fallbackImgWrapper slots follow the established pattern for theme slots. Empty strings are correct here since these slots will be populated with custom classes via the classNames prop at runtime.

packages/components/image/__tests__/image.test.tsx (3)

7-8: LGTM! Well-defined test constants.

The test constants are properly defined and will help maintain consistency across tests.


59-86: LGTM! Comprehensive test for loading state.

The test properly verifies that:

  1. Loading image is displayed as background during loading
  2. Custom classes are applied correctly
  3. Loading background is removed after load
  4. onLoad callback is triggered

141-145: LGTM! Good test for baseline behavior.

This test ensures the component works correctly when neither loading nor fallback behavior is defined.

apps/docs/content/docs/components/image.mdx (4)

59-72: LGTM! Clear documentation of new loading behavior.

The documentation properly explains when the built-in skeleton is shown vs when custom loading images are used, and provides a helpful code demo.


75-84: LGTM! Improved clarity on fallback behavior.

The documentation now clearly distinguishes between loading state (loadingSrc) and error state (fallbackSrc), removing previous confusion about when fallback applies.


166-176: LGTM! Comprehensive API documentation.

The new loadingSrc prop is well-documented with clear description and proper typing. The updated fallbackSrc description also provides better clarity.


204-204: LGTM! Updated classNames type includes new slots.

The classNames prop type correctly includes the new loadingImgWrapper and fallbackImgWrapper slots.

Comment on lines 13 to 38
beforeAll(() => {
function trackImageOnload() {
Object.defineProperty(window.Image.prototype, "onload", {
get() {
return this._onload;
},
set(fn) {
imageOnload = fn;
this._onload = fn;
},
configurable: true,
});
}

trackImageOnload();
});

afterAll(() => {
// Restore original Image prototype
delete window.Image.prototype._onload;
Object.defineProperty(window.Image.prototype, "onload", {
value: null,
writable: true,
configurable: true,
});
});
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

Optimize cleanup by avoiding delete operator.

The global setup and teardown approach is excellent for consistency. However, the delete operator can impact performance.

Apply this diff to optimize the cleanup:

-    delete window.Image.prototype._onload;
+    window.Image.prototype._onload = undefined;

Also consider setting the descriptor to undefined instead of null:

-      value: null,
+      value: undefined,
📝 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
beforeAll(() => {
function trackImageOnload() {
Object.defineProperty(window.Image.prototype, "onload", {
get() {
return this._onload;
},
set(fn) {
imageOnload = fn;
this._onload = fn;
},
configurable: true,
});
}
trackImageOnload();
});
afterAll(() => {
// Restore original Image prototype
delete window.Image.prototype._onload;
Object.defineProperty(window.Image.prototype, "onload", {
value: null,
writable: true,
configurable: true,
});
});
beforeAll(() => {
function trackImageOnload() {
Object.defineProperty(window.Image.prototype, "onload", {
get() {
return this._onload;
},
set(fn) {
imageOnload = fn;
this._onload = fn;
},
configurable: true,
});
}
trackImageOnload();
});
afterAll(() => {
// Restore original Image prototype
- delete window.Image.prototype._onload;
+ window.Image.prototype._onload = undefined;
Object.defineProperty(window.Image.prototype, "onload", {
- value: null,
+ value: undefined,
writable: true,
configurable: true,
});
});
🧰 Tools
🪛 Biome (1.9.4)

[error] 32-32: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🤖 Prompt for AI Agents
In packages/components/image/__tests__/image.test.tsx around lines 13 to 38,
avoid using the delete operator in the afterAll cleanup to improve performance.
Instead of deleting window.Image.prototype._onload, set it to undefined. Also,
when redefining the onload property descriptor, set its value to undefined
rather than null to better reset the property.

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.

[Feature Request] Make a Distinction Between Loading and Error States in Image Component

2 participants