diff --git a/.changeset/lazy-buttons-exercise.md b/.changeset/lazy-buttons-exercise.md
new file mode 100644
index 0000000000..5cdc25373d
--- /dev/null
+++ b/.changeset/lazy-buttons-exercise.md
@@ -0,0 +1,7 @@
+---
+"@nextui-org/listbox": patch
+"@nextui-org/menu": patch
+"@nextui-org/theme": patch
+---
+
+Add truncate class to the list item to avoid overflow the wrapper
diff --git a/packages/components/listbox/__tests__/listbox.test.tsx b/packages/components/listbox/__tests__/listbox.test.tsx
index 36bc7ebca3..60614536bf 100644
--- a/packages/components/listbox/__tests__/listbox.test.tsx
+++ b/packages/components/listbox/__tests__/listbox.test.tsx
@@ -273,4 +273,30 @@ describe("Listbox", () => {
expect(checkmark1).toBeFalsy();
});
+
+ it("should truncate the text if the child is not a string", () => {
+ const wrapper = render(
+
+ New file
+ ,
+ );
+
+ const menuItem = wrapper.getByText("New file");
+
+ expect(menuItem).toHaveProperty("className", expect.stringContaining("truncate"));
+ });
+
+ it("should not truncate the text if the child is a string", () => {
+ const wrapper = render(
+
+
+ New file
+
+ ,
+ );
+
+ const menuItem = wrapper.getByText("New file").parentElement;
+
+ expect(menuItem).not.toHaveProperty("className", expect.stringContaining("truncate"));
+ });
});
diff --git a/packages/components/listbox/src/use-listbox-item.ts b/packages/components/listbox/src/use-listbox-item.ts
index f4e0781644..37103f2107 100644
--- a/packages/components/listbox/src/use-listbox-item.ts
+++ b/packages/components/listbox/src/use-listbox-item.ts
@@ -99,8 +99,10 @@ export function useListboxItem(originalProps: UseListboxItemPr
...variantProps,
isDisabled,
disableAnimation,
+ hasTitleTextChild: typeof rendered === "string",
+ hasDescriptionTextChild: typeof description === "string",
}),
- [objectToDeps(variantProps), isDisabled, disableAnimation],
+ [objectToDeps(variantProps), isDisabled, disableAnimation, rendered, description],
);
const baseStyles = clsx(classNames?.base, className);
diff --git a/packages/components/menu/__tests__/menu.test.tsx b/packages/components/menu/__tests__/menu.test.tsx
index 98530665ee..d8a9a53ddd 100644
--- a/packages/components/menu/__tests__/menu.test.tsx
+++ b/packages/components/menu/__tests__/menu.test.tsx
@@ -343,4 +343,30 @@ describe("Menu", () => {
expect(onPress).toHaveBeenCalledTimes(1);
expect(onClick).toHaveBeenCalledTimes(1);
});
+
+ it("should truncate the text if the child is not a string", () => {
+ const wrapper = render(
+ ,
+ );
+
+ const menuItem = wrapper.getByText("New file");
+
+ expect(menuItem).toHaveProperty("className", expect.stringContaining("truncate"));
+ });
+
+ it("should not truncate the text if the child is a string", () => {
+ const wrapper = render(
+ ,
+ );
+
+ const menuItem = wrapper.getByText("New file").parentElement;
+
+ expect(menuItem).not.toHaveProperty("className", expect.stringContaining("truncate"));
+ });
});
diff --git a/packages/components/menu/src/menu.tsx b/packages/components/menu/src/menu.tsx
index 318b86baa1..a4658b4f28 100644
--- a/packages/components/menu/src/menu.tsx
+++ b/packages/components/menu/src/menu.tsx
@@ -71,7 +71,10 @@ function Menu(props: Props, ref: ForwardedRef = Props & {ref?: Ref};
+export type MenuProps = Omit<
+ Props,
+ "hasChildItems" | "hasTitleTextChild" | "hasDescriptionTextChild"
+> & {ref?: Ref};
// forwardRef doesn't support generic parameters, so cast the result to the correct type
export default forwardRef(Menu) as (props: MenuProps) => ReactElement;
diff --git a/packages/components/menu/src/use-menu-item.ts b/packages/components/menu/src/use-menu-item.ts
index 5a4806ce74..4d40050d00 100644
--- a/packages/components/menu/src/use-menu-item.ts
+++ b/packages/components/menu/src/use-menu-item.ts
@@ -130,8 +130,10 @@ export function useMenuItem(originalProps: UseMenuItemProps
...variantProps,
isDisabled,
disableAnimation,
+ hasTitleTextChild: typeof rendered === "string",
+ hasDescriptionTextChild: typeof description === "string",
}),
- [objectToDeps(variantProps), isDisabled, disableAnimation],
+ [objectToDeps(variantProps), isDisabled, disableAnimation, rendered, description],
);
const baseStyles = clsx(classNames?.base, className);
diff --git a/packages/core/theme/src/components/menu.ts b/packages/core/theme/src/components/menu.ts
index b5abf8f83e..2e4fb7bb4b 100644
--- a/packages/core/theme/src/components/menu.ts
+++ b/packages/core/theme/src/components/menu.ts
@@ -11,7 +11,7 @@ import {dataFocusVisibleClasses} from "../utils";
*/
const menu = tv({
slots: {
- base: "w-full relative flex flex-col gap-1 p-1",
+ base: "w-full relative flex flex-col gap-1 p-1 overflow-hidden",
list: "w-full flex flex-col gap-0.5 outline-none",
emptyContent: [
"h-10",
@@ -144,6 +144,17 @@ const menuItem = tv({
base: "data-[hover=true]:transition-colors",
},
},
+ // If the child isn't a string, the truncate such as `overflow, white-space, text-overflow` css won't be extended to the child, so we remove the truncate class here
+ hasTitleTextChild: {
+ true: {
+ title: "truncate",
+ },
+ },
+ hasDescriptionTextChild: {
+ true: {
+ description: "truncate",
+ },
+ },
},
defaultVariants: {
variant: "solid",