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( + + 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/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",