Skip to content

Commit

Permalink
fix(dropdown): _a2.find is not a function (#3762)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
wingkwong and winchesHe authored Sep 15, 2024
1 parent 2293884 commit 8fecb5a
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .changeset/serious-panthers-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@nextui-org/dropdown": patch
"@nextui-org/use-aria-menu": patch
---

fixed `_a2.find` is not a function (#3761)
49 changes: 48 additions & 1 deletion packages/components/dropdown/__tests__/dropdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ describe("Keyboard interactions", () => {
logSpy.mockRestore();
});

it("should respect closeOnSelect setting of DropdownItem", async () => {
it("should respect closeOnSelect setting of DropdownItem (static)", async () => {
const onOpenChange = jest.fn();
const wrapper = render(
<Dropdown onOpenChange={onOpenChange}>
Expand Down Expand Up @@ -831,4 +831,51 @@ describe("Keyboard interactions", () => {
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);
});
});
});
47 changes: 38 additions & 9 deletions packages/components/dropdown/src/use-dropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils";
import {useMemo, useRef} from "react";
import {mergeProps} from "@react-aria/utils";
import {MenuProps} from "@nextui-org/menu";
import {CollectionElement} from "@react-types/shared";

interface Props extends HTMLNextUIProps<"div"> {
/**
Expand Down Expand Up @@ -42,6 +43,40 @@ interface Props extends HTMLNextUIProps<"div"> {

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) {
const globalContext = useProviderContext();

Expand Down Expand Up @@ -152,16 +187,10 @@ export function useDropdown(props: UseDropdownProps) {
menuProps,
closeOnSelect,
...mergeProps(props, {
onAction: (key: any) => {
// @ts-ignore
const item = props?.children?.find((item) => item.key === key);

if (item?.props?.closeOnSelect === false) {
onMenuAction(false);
onAction: (key: any, item?: any) => {
const closeOnSelect = getCloseOnSelect(props, key, item);

return;
}
onMenuAction(props?.closeOnSelect);
onMenuAction(closeOnSelect);
},
onClose: state.close,
}),
Expand Down
6 changes: 3 additions & 3 deletions packages/hooks/use-aria-menu/src/use-menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export interface AriaMenuItemProps
* Handler that is called when the user activates the item.
* @deprecated - pass to the menu instead.
*/
onAction?: (key: Key) => void;
onAction?: (key: Key, item: any) => void;

/**
* The native button click event handler
Expand Down Expand Up @@ -167,11 +167,11 @@ export function useMenuItem<T>(

if (props.onAction) {
// @ts-ignore
props.onAction(key);
props.onAction(key, item);
// @ts-ignore
} else if (data.onAction) {
// @ts-ignore
data.onAction(key);
data.onAction(key, item);
}

if (e.target instanceof HTMLAnchorElement) {
Expand Down
2 changes: 1 addition & 1 deletion packages/hooks/use-aria-menu/src/use-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface AriaMenuOptions<T> extends Omit<AriaMenuProps<T>, "children">,

interface MenuData {
onClose?: () => void;
onAction?: (key: Key) => void;
onAction?: (key: Key, item: any) => void;
}

export const menuData = new WeakMap<TreeState<unknown>, MenuData>();
Expand Down

0 comments on commit 8fecb5a

Please sign in to comment.