diff --git a/.changeset/odd-tomatoes-call.md b/.changeset/odd-tomatoes-call.md
new file mode 100644
index 0000000000..3fc6077312
--- /dev/null
+++ b/.changeset/odd-tomatoes-call.md
@@ -0,0 +1,5 @@
+---
+"@nextui-org/select": patch
+---
+
+Fix the label placement when the `Select` has a `placeholder` or `description`.
diff --git a/.changeset/wild-jobs-explain.md b/.changeset/wild-jobs-explain.md
new file mode 100644
index 0000000000..4ae1929636
--- /dev/null
+++ b/.changeset/wild-jobs-explain.md
@@ -0,0 +1,5 @@
+---
+"@nextui-org/use-image": patch
+---
+
+fix Image ReferenceError in SSR
\ No newline at end of file
diff --git a/apps/docs/content/docs/guide/routing.mdx b/apps/docs/content/docs/guide/routing.mdx
index ece0f57349..92044633c8 100644
--- a/apps/docs/content/docs/guide/routing.mdx
+++ b/apps/docs/content/docs/guide/routing.mdx
@@ -269,7 +269,7 @@ function RootRoute() {
return (
router.navigate({ to, ...options })}
- useHref={(to) => router.buildLocation(to).href}
+ useHref={(to) => router.buildLocation({ to }).href}
>
{/* You app here... */}
diff --git a/packages/components/select/__tests__/select.test.tsx b/packages/components/select/__tests__/select.test.tsx
index 6776344eeb..655b06ce81 100644
--- a/packages/components/select/__tests__/select.test.tsx
+++ b/packages/components/select/__tests__/select.test.tsx
@@ -723,11 +723,12 @@ describe("Select", () => {
expect(onChange).toHaveBeenCalledTimes(1);
});
- it("should place the label outside when labelPlacement is outside", () => {
+ it("should place the label outside when labelPlacement is outside and isMultiline enabled", () => {
const labelContent = "Favorite Animal Label";
render(
+
+
With placeholder and description
+
+
+
+
+
+
);
diff --git a/packages/core/react/README.md b/packages/core/react/README.md
index 48cba90b64..242f0440c2 100644
--- a/packages/core/react/README.md
+++ b/packages/core/react/README.md
@@ -39,7 +39,7 @@ Visit [https://storybook.nextui.org](https://storybook.nextui.org/) to view the
Canary versions are available after every merge into `canary` branch. You can install the packages with the tag `canary` in npm to use the latest changes before the next production release.
- [Documentation](https://canary.nextui.org/docs)
-- [Storybook](https://canary-storybook.nextui.org)
+- [Storybook](https://canary-sb.nextui.org)
## Community
diff --git a/packages/hooks/use-image/__tests__/use-image.test.tsx b/packages/hooks/use-image/__tests__/use-image.test.tsx
index 7f76d4a7cd..f69de371f6 100644
--- a/packages/hooks/use-image/__tests__/use-image.test.tsx
+++ b/packages/hooks/use-image/__tests__/use-image.test.tsx
@@ -1,4 +1,4 @@
-import {renderHook} from "@testing-library/react-hooks";
+import {renderHook, waitFor} from "@testing-library/react";
import {mocks} from "@nextui-org/test-utils";
import {useImage} from "../src";
@@ -14,31 +14,24 @@ describe("use-image hook", () => {
});
it("can handle missing src", () => {
- const rendered = renderHook(() => useImage({}));
+ const {result} = renderHook(() => useImage({}));
- expect(rendered.result.current).toEqual("pending");
+ expect(result.current).toEqual("pending");
});
it("can handle loading image", async () => {
- const rendered = renderHook(() => useImage({src: "/test.png"}));
+ const {result} = renderHook(() => useImage({src: "/test.png"}));
- expect(rendered.result.current).toEqual("loading");
+ expect(result.current).toEqual("loading");
mockImage.simulate("loaded");
- await rendered.waitForValueToChange(() => rendered.result.current === "loaded");
+ await waitFor(() => expect(result.current).toBe("loaded"));
});
it("can handle error image", async () => {
mockImage.simulate("error");
- const rendered = renderHook(() => useImage({src: "/test.png"}));
+ const {result} = renderHook(() => useImage({src: "/test.png"}));
- expect(rendered.result.current).toEqual("loading");
- await rendered.waitForValueToChange(() => rendered.result.current === "failed");
- });
-
- it("can handle cached image", async () => {
- mockImage.simulate("loaded");
- const rendered = renderHook(() => useImage({src: "/test.png"}));
-
- expect(rendered.result.current).toEqual("loaded");
+ expect(result.current).toEqual("loading");
+ await waitFor(() => expect(result.current).toBe("failed"));
});
});
diff --git a/packages/hooks/use-image/src/index.ts b/packages/hooks/use-image/src/index.ts
index 531b2b98ee..d0a75b67cb 100644
--- a/packages/hooks/use-image/src/index.ts
+++ b/packages/hooks/use-image/src/index.ts
@@ -1,9 +1,10 @@
/**
* Part of this code is taken from @chakra-ui/react package ❤️
*/
-import type {ImgHTMLAttributes, MutableRefObject, SyntheticEvent} from "react";
-import {useEffect, useRef, useState} from "react";
+import type {ImgHTMLAttributes, SyntheticEvent} from "react";
+
+import {useCallback, useEffect, useRef, useState} from "react";
import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect";
type NativeImageProps = ImgHTMLAttributes;
@@ -46,7 +47,6 @@ type Status = "loading" | "failed" | "pending" | "loaded";
export type FallbackStrategy = "onError" | "beforeLoadOrError";
type ImageEvent = SyntheticEvent;
-
/**
* React hook that loads an image in the browser,
* and lets us know the `status` so we can show image
@@ -63,40 +63,44 @@ type ImageEvent = SyntheticEvent;
* }
* ```
*/
+
export function useImage(props: UseImageProps = {}) {
const {loading, src, srcSet, onLoad, onError, crossOrigin, sizes, ignoreFallback} = props;
+ const [status, setStatus] = useState("pending");
+
+ useEffect(() => {
+ setStatus(src ? "loading" : "pending");
+ }, [src]);
+
const imageRef = useRef();
- const firstMount = useRef(true);
- const [status, setStatus] = useState(() => setImageAndGetInitialStatus(props, imageRef));
- useSafeLayoutEffect(() => {
- if (firstMount.current) {
- firstMount.current = false;
+ const load = useCallback(() => {
+ if (!src) return;
- return;
- }
+ flush();
- setStatus(setImageAndGetInitialStatus(props, imageRef));
+ const img = new Image();
- return () => {
- flush();
- };
- }, [src, crossOrigin, srcSet, sizes, loading]);
+ img.src = src;
+ if (crossOrigin) img.crossOrigin = crossOrigin;
+ if (srcSet) img.srcset = srcSet;
+ if (sizes) img.sizes = sizes;
+ if (loading) img.loading = loading;
- useEffect(() => {
- if (!imageRef.current) return;
- imageRef.current.onload = (event) => {
+ img.onload = (event) => {
flush();
setStatus("loaded");
onLoad?.(event as unknown as ImageEvent);
};
- imageRef.current.onerror = (error) => {
+ img.onerror = (error) => {
flush();
setStatus("failed");
onError?.(error as any);
};
- }, [imageRef.current]);
+
+ imageRef.current = img;
+ }, [src, crossOrigin, srcSet, sizes, onLoad, onError, loading]);
const flush = () => {
if (imageRef.current) {
@@ -106,40 +110,25 @@ export function useImage(props: UseImageProps = {}) {
}
};
+ useSafeLayoutEffect(() => {
+ /**
+ * If user opts out of the fallback/placeholder
+ * logic, let's bail out.
+ */
+ if (ignoreFallback) return undefined;
+
+ if (status === "loading") {
+ load();
+ }
+
+ return () => {
+ flush();
+ };
+ }, [status, load, ignoreFallback]);
+
/**
* If user opts out of the fallback/placeholder
* logic, let's just return 'loaded'
*/
return ignoreFallback ? "loaded" : status;
}
-
-function setImageAndGetInitialStatus(
- props: UseImageProps,
- imageRef: MutableRefObject,
-): Status {
- const {loading, src, srcSet, crossOrigin, sizes, ignoreFallback} = props;
-
- if (!src) return "pending";
- if (ignoreFallback) return "loaded";
-
- const img = new Image();
-
- img.src = src;
- if (crossOrigin) img.crossOrigin = crossOrigin;
- if (srcSet) img.srcset = srcSet;
- if (sizes) img.sizes = sizes;
- if (loading) img.loading = loading;
-
- imageRef.current = img;
- if (img.complete && img.naturalWidth) {
- return "loaded";
- }
-
- return "loading";
-}
-
-export const shouldShowFallbackImage = (status: Status, fallbackStrategy: FallbackStrategy) =>
- (status !== "loaded" && fallbackStrategy === "beforeLoadOrError") ||
- (status === "failed" && fallbackStrategy === "onError");
-
-export type UseImageReturn = ReturnType;