Repository: sparksuite/react-accessible-dropdown-menu-hook (ts)

Issue:
Make the menu close when an item is clicked
Currently the hook only manages closing and opening the menu element when keyboard controls are being utilized. This leaves it up to the developer to implement closing and opening their menu element when an item is clicked. This is suboptimal because they can not properly utilize the `isOpen` boolean value returned by the hook if the developer(s) must rely on a different value for click events.

Due to this some solution needs to be implemented to detect item clicks and close the menu in response.

Pull request:
Rethink behavior on item click
Currently when a menu item is clicked, by default the menu stays open. This behavior is not standard to most menus and should be reserved for when the user indicates they would like the menu to stay open.

This PR makes it so that when an item is clicked, if it is not a link it closes the menu.

closes #70 

Patch:
diff --git a/demo/src/app.tsx b/demo/src/app.tsx
index 60eacec..f4418e9 100644
--- a/demo/src/app.tsx
+++ b/demo/src/app.tsx
@@ -6,7 +6,7 @@ import useDropdownMenu from 'react-accessible-dropdown-menu-hook';
 // Functional component
 const App: React.FC = () => {
 	// Use the Hook
-	const [buttonProps, itemProps, isOpen] = useDropdownMenu(3);
+	const { buttonProps, itemProps, isOpen } = useDropdownMenu(3);
 
 	// Return JSX
 	return (
diff --git a/package.json b/package.json
index 2c29bb3..f7a4f1f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "react-accessible-dropdown-menu-hook",
-	"version": "1.1.1",
+	"version": "2.0.0",
 	"description": "A simple Hook for creating fully accessible dropdown menus in React",
 	"main": "dist/use-dropdown-menu.js",
 	"types": "dist/use-dropdown-menu.d.ts",
diff --git a/src/use-dropdown-menu.ts b/src/use-dropdown-menu.ts
index b650102..671a6e3 100644
--- a/src/use-dropdown-menu.ts
+++ b/src/use-dropdown-menu.ts
@@ -174,5 +174,5 @@ export default function useDropdownMenu(itemCount: number) {
 	}));
 
 	// Return a listener for the button, individual list items, and the state of the menu
-	return [buttonProps, itemProps, isOpen] as const;
+	return { buttonProps, itemProps, isOpen, setIsOpen } as const;
 }


Test patch:
diff --git a/test/puppeteer/demo.test.ts b/test/puppeteer/demo.test.ts
index ba1fd02..1ca21c7 100644
--- a/test/puppeteer/demo.test.ts
+++ b/test/puppeteer/demo.test.ts
@@ -96,7 +96,7 @@ it('reroutes enter presses on menu items as clicks', async () => {
 	await menuOpen();
 
 	// eslint-disable-next-line @typescript-eslint/no-misused-promises
-	page.on('dialog', async dialog => {
+	page.once('dialog', async dialog => {
 		alertAppeared = true;
 		await dialog.dismiss();
 	});
diff --git a/test/test-component.tsx b/test/test-component.tsx
index 2592fff..4a5fdac 100644
--- a/test/test-component.tsx
+++ b/test/test-component.tsx
@@ -4,7 +4,7 @@ import useDropdownMenu from '../src/use-dropdown-menu';
 
 // A mock component for testing the Hook
 const TestComponent: React.FC = () => {
-	const [buttonProps, itemProps, isOpen] = useDropdownMenu(3);
+	const { buttonProps, itemProps, isOpen, setIsOpen } = useDropdownMenu(3);
 
 	return (
 		<React.Fragment>
@@ -13,7 +13,7 @@ const TestComponent: React.FC = () => {
 			</button>
 
 			<div role='menu' id='menu'>
-				<a {...itemProps[0]} onClick={() => null} id='menu-item-1'>
+				<a {...itemProps[0]} onClick={() => setIsOpen(false)} id='menu-item-1'>
 					Item 1
 				</a>
 
diff --git a/test/use-dropdown-menu.test.tsx b/test/use-dropdown-menu.test.tsx
index 806d796..da4079e 100644
--- a/test/use-dropdown-menu.test.tsx
+++ b/test/use-dropdown-menu.test.tsx
@@ -70,6 +70,20 @@ it('sets isOpen to true after pressing space while focused on the menu button',
 	expect(span.text()).toBe('true');
 });
 
+it('sets isOpen to false after clicking a menu item that calls the state change function', () => {
+	const component = mount(<TestComponent />);
+	const button = component.find('#menu-button');
+	const itemWithHandler = component.find('#menu-item-1');
+	const span = component.find('#is-open-indicator');
+
+	button.getDOMNode<HTMLButtonElement>().focus();
+	button.simulate('keydown', { key: 'Enter' });
+
+	itemWithHandler.simulate('click');
+
+	expect(span.text()).toBe('false');
+});
+
 it('moves the focus to the next element in the menu after pressing the down arrow', () => {
 	const component = mount(<TestComponent />);
 	const button = component.find('#menu-button');