mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
fix(dropdown): _a2.find is not a function (#3762)
* feat(dropdown): add "should respect closeOnSelect setting of DropdownItem (dynamic)" * chore(changeset): add changeset * fix(dropdown): find is not function error when click dropdown item (#3763) * fix: find is not function error when click dropdown item * fix: find is not function error when click dropdown item * fix: type error * fix: optimization * refactor(dropdown): must have return value * chore(changeset): revise changeset --------- Co-authored-by: winches <329487092@qq.com>
This commit is contained in:
parent
2293884229
commit
8fecb5afa9
6
.changeset/serious-panthers-beg.md
Normal file
6
.changeset/serious-panthers-beg.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@nextui-org/dropdown": patch
|
||||||
|
"@nextui-org/use-aria-menu": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fixed `_a2.find` is not a function (#3761)
|
||||||
@ -796,7 +796,7 @@ describe("Keyboard interactions", () => {
|
|||||||
logSpy.mockRestore();
|
logSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should respect closeOnSelect setting of DropdownItem", async () => {
|
it("should respect closeOnSelect setting of DropdownItem (static)", async () => {
|
||||||
const onOpenChange = jest.fn();
|
const onOpenChange = jest.fn();
|
||||||
const wrapper = render(
|
const wrapper = render(
|
||||||
<Dropdown onOpenChange={onOpenChange}>
|
<Dropdown onOpenChange={onOpenChange}>
|
||||||
@ -831,4 +831,51 @@ describe("Keyboard interactions", () => {
|
|||||||
expect(onOpenChange).toBeCalledTimes(2);
|
expect(onOpenChange).toBeCalledTimes(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should respect closeOnSelect setting of DropdownItem (dynamic)", async () => {
|
||||||
|
const onOpenChange = jest.fn();
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
key: "new",
|
||||||
|
label: "New file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "copy",
|
||||||
|
label: "Copy link",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const wrapper = render(
|
||||||
|
<Dropdown onOpenChange={onOpenChange}>
|
||||||
|
<DropdownTrigger>
|
||||||
|
<Button data-testid="trigger-test">Trigger</Button>
|
||||||
|
</DropdownTrigger>
|
||||||
|
<DropdownMenu aria-label="Actions" items={items}>
|
||||||
|
{(item) => (
|
||||||
|
<DropdownItem key={item.key} closeOnSelect={item.key !== "new"}>
|
||||||
|
{item.label}
|
||||||
|
</DropdownItem>
|
||||||
|
)}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>,
|
||||||
|
);
|
||||||
|
|
||||||
|
let triggerButton = wrapper.getByTestId("trigger-test");
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
triggerButton.click();
|
||||||
|
});
|
||||||
|
expect(onOpenChange).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
let menuItems = wrapper.getAllByRole("menuitem");
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.click(menuItems[0]);
|
||||||
|
expect(onOpenChange).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.click(menuItems[1]);
|
||||||
|
expect(onOpenChange).toBeCalledTimes(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils";
|
|||||||
import {useMemo, useRef} from "react";
|
import {useMemo, useRef} from "react";
|
||||||
import {mergeProps} from "@react-aria/utils";
|
import {mergeProps} from "@react-aria/utils";
|
||||||
import {MenuProps} from "@nextui-org/menu";
|
import {MenuProps} from "@nextui-org/menu";
|
||||||
|
import {CollectionElement} from "@react-types/shared";
|
||||||
|
|
||||||
interface Props extends HTMLNextUIProps<"div"> {
|
interface Props extends HTMLNextUIProps<"div"> {
|
||||||
/**
|
/**
|
||||||
@ -42,6 +43,40 @@ interface Props extends HTMLNextUIProps<"div"> {
|
|||||||
|
|
||||||
export type UseDropdownProps = Props & Omit<PopoverProps, "children" | "color" | "variant">;
|
export type UseDropdownProps = Props & Omit<PopoverProps, "children" | "color" | "variant">;
|
||||||
|
|
||||||
|
const getMenuItem = <T extends object>(props: Partial<MenuProps<T>> | undefined, key: string) => {
|
||||||
|
if (props) {
|
||||||
|
const mergedChildren = Array.isArray(props.children)
|
||||||
|
? props.children
|
||||||
|
: [...(props?.items || [])];
|
||||||
|
|
||||||
|
if (mergedChildren && mergedChildren.length) {
|
||||||
|
const item = ((mergedChildren as CollectionElement<T>[]).find((item) => {
|
||||||
|
if (item.key === key) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}) || {}) as {props: MenuProps};
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCloseOnSelect = <T extends object>(
|
||||||
|
props: Partial<MenuProps<T>> | undefined,
|
||||||
|
key: string,
|
||||||
|
item?: any,
|
||||||
|
) => {
|
||||||
|
const mergedItem = item || getMenuItem(props, key);
|
||||||
|
|
||||||
|
if (mergedItem && mergedItem.props && "closeOnSelect" in mergedItem.props) {
|
||||||
|
return mergedItem.props.closeOnSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
return props?.closeOnSelect;
|
||||||
|
};
|
||||||
|
|
||||||
export function useDropdown(props: UseDropdownProps) {
|
export function useDropdown(props: UseDropdownProps) {
|
||||||
const globalContext = useProviderContext();
|
const globalContext = useProviderContext();
|
||||||
|
|
||||||
@ -152,16 +187,10 @@ export function useDropdown(props: UseDropdownProps) {
|
|||||||
menuProps,
|
menuProps,
|
||||||
closeOnSelect,
|
closeOnSelect,
|
||||||
...mergeProps(props, {
|
...mergeProps(props, {
|
||||||
onAction: (key: any) => {
|
onAction: (key: any, item?: any) => {
|
||||||
// @ts-ignore
|
const closeOnSelect = getCloseOnSelect(props, key, item);
|
||||||
const item = props?.children?.find((item) => item.key === key);
|
|
||||||
|
|
||||||
if (item?.props?.closeOnSelect === false) {
|
onMenuAction(closeOnSelect);
|
||||||
onMenuAction(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onMenuAction(props?.closeOnSelect);
|
|
||||||
},
|
},
|
||||||
onClose: state.close,
|
onClose: state.close,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -94,7 +94,7 @@ export interface AriaMenuItemProps
|
|||||||
* Handler that is called when the user activates the item.
|
* Handler that is called when the user activates the item.
|
||||||
* @deprecated - pass to the menu instead.
|
* @deprecated - pass to the menu instead.
|
||||||
*/
|
*/
|
||||||
onAction?: (key: Key) => void;
|
onAction?: (key: Key, item: any) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The native button click event handler
|
* The native button click event handler
|
||||||
@ -167,11 +167,11 @@ export function useMenuItem<T>(
|
|||||||
|
|
||||||
if (props.onAction) {
|
if (props.onAction) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
props.onAction(key);
|
props.onAction(key, item);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} else if (data.onAction) {
|
} else if (data.onAction) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
data.onAction(key);
|
data.onAction(key, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.target instanceof HTMLAnchorElement) {
|
if (e.target instanceof HTMLAnchorElement) {
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export interface AriaMenuOptions<T> extends Omit<AriaMenuProps<T>, "children">,
|
|||||||
|
|
||||||
interface MenuData {
|
interface MenuData {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
onAction?: (key: Key) => void;
|
onAction?: (key: Key, item: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const menuData = new WeakMap<TreeState<unknown>, MenuData>();
|
export const menuData = new WeakMap<TreeState<unknown>, MenuData>();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user