diff --git a/components/DropdownLight/DropdownLight.jsx b/components/DropdownLight/DropdownLight.jsx index 0d0f034e5..466c79248 100644 --- a/components/DropdownLight/DropdownLight.jsx +++ b/components/DropdownLight/DropdownLight.jsx @@ -210,12 +210,14 @@ const DropdownLight = ({ const [cursor, setCursor] = useState(-1); const buttonRef = useRef(); const listOptions = useRef(); + const ButtonIsClosedAndFocused = () => + !isOpen && document.activeElement === buttonRef.current; - const downPress = useKeyPress('ArrowDown'); - const upPress = useKeyPress('ArrowUp'); + const downPress = useKeyPress('ArrowDown', ButtonIsClosedAndFocused); + const upPress = useKeyPress('ArrowUp', ButtonIsClosedAndFocused); const enterPress = useKeyPress('Enter'); + const escapePress = useKeyPress('Escape'); const focusedItemIndex = useKeyboardSearchItems(items, cursor, isOpen); - const EscapeKeyPressValue = 'Escape'; const handleToggleDropdown = () => { setIsOpen(!isOpen); @@ -249,19 +251,33 @@ const DropdownLight = ({ } }; - const handleEscPress = ({ key }) => { - if (key === EscapeKeyPressValue) { + useEffect(() => { + if (isOpen && escapePress) { setIsOpen(false); setCursor(-1); buttonRef.current.focus(); } - }; + }, [escapePress, isOpen]); useEffect(() => { + const GetNextCursor = (currentCursor) => + currentCursor < items.length - 1 ? currentCursor + 1 : currentCursor; + if (isOpen && downPress) { - const selectedCursor = cursor < items.length - 1 ? cursor + 1 : cursor; + const selectedCursor = GetNextCursor(cursor); setCursor(selectedCursor); } + + if (downPress && ButtonIsClosedAndFocused()) { + const selectedItemIndex = items.findIndex( + (item) => (item?.label || item) === selectedOptionItem, + ); + const selectedCursor = GetNextCursor(selectedItemIndex); + + if (selectedItemIndex !== selectedCursor) { + selectItem(items[selectedCursor]); + } + } }, [downPress, items, isOpen]); useEffect(() => { @@ -269,24 +285,30 @@ const DropdownLight = ({ const selectedCursor = cursor - 1; setCursor(selectedCursor); } + + if (upPress && ButtonIsClosedAndFocused()) { + const selectedItemIndex = items.findIndex( + (item) => (item?.label || item) === selectedOptionItem, + ); + const previousCursor = + selectedItemIndex > 0 ? selectedItemIndex - 1 : selectedItemIndex; + const selectedCursor = previousCursor; + + if (selectedItemIndex !== selectedCursor) { + selectItem(items[selectedCursor]); + } + } }, [upPress, items, isOpen]); useEffect(() => { window.addEventListener('click', handleClickOutside); - window.addEventListener('keydown', handleEscPress); return () => { window.removeEventListener('click', handleClickOutside); - window.removeEventListener('keydown', handleEscPress); }; }, []); useEffect(() => { - if ( - enterPress && - !isOpen && - document.activeElement === buttonRef.current && - selectedOptionItem - ) { + if (enterPress && ButtonIsClosedAndFocused() && selectedOptionItem) { const selectedItemIndex = items.findIndex( (item) => (item?.label || item) === selectedOptionItem, ); diff --git a/components/DropdownLight/DropdownLight.unit.test.jsx b/components/DropdownLight/DropdownLight.unit.test.jsx index 1c708ace0..d09a56b8f 100644 --- a/components/DropdownLight/DropdownLight.unit.test.jsx +++ b/components/DropdownLight/DropdownLight.unit.test.jsx @@ -171,14 +171,17 @@ describe('', () => { it('should close Dropdown Options when user press Escape', async () => { render(); + const dropdownButton = screen.getByRole('button', { + name: 'abrir lista de itens', + }); + await userEvent.click(dropdownButton); await userEvent.keyboard(ESCAPE_KEY_CODE); expect(screen.queryByRole('list')).not.toBeInTheDocument(); - expect( - screen.getByRole('button', { name: 'abrir lista de itens' }), - ).toBeInTheDocument(); + expect(dropdownButton).toBeInTheDocument(); + expect(dropdownButton).toHaveFocus(); }); it('should allow User to select an option using only keyboard', async () => { @@ -200,9 +203,13 @@ describe('', () => { await userEvent.keyboard(ENTER_KEY_CODE); const input = screen.getByRole('textbox', { hidden: true }); + const dropdownButton = screen.getByRole('button', { + name: 'abrir lista de itens', + }); expect(input.value).toEqual(bananaItemMock); expect(screen.queryByRole('list')).not.toBeInTheDocument(); + expect(dropdownButton).toHaveFocus(); }); it('should allow the User to navigate between items using the keys with the initial letter of each item', async () => { @@ -280,4 +287,36 @@ describe('', () => { expect(selectionItemImage).toBeInTheDocument(); }); + + it('should allow to select an item from the list using the keyboard and without opening the list of options', async () => { + const onChangeMock = jest.fn(); + render(); + + await userEvent.tab(); + + const dropdownButton = screen.getByRole('button', { + name: 'abrir lista de itens', + }); + expect(dropdownButton).toHaveFocus(); + + await userEvent.keyboard(ARROW_DOWN_KEY_CODE); + + expect(onChangeMock).toHaveBeenCalledWith({ + label: 'Lemon', + value: 'Lemon', + }); + + expect(screen.queryByRole('list')).not.toBeInTheDocument(); + expect(dropdownButton).toHaveFocus(); + + await userEvent.keyboard(ARROW_DOWN_KEY_CODE); + + expect(onChangeMock).toHaveBeenCalledWith({ + label: 'Lime', + value: 'Lime', + }); + + expect(screen.queryByRole('list')).not.toBeInTheDocument(); + expect(dropdownButton).toHaveFocus(); + }); }); diff --git a/components/DropdownLight/SubComponents/UseKeyPress.jsx b/components/DropdownLight/SubComponents/UseKeyPress.jsx index 8a7ad4861..b0004255a 100644 --- a/components/DropdownLight/SubComponents/UseKeyPress.jsx +++ b/components/DropdownLight/SubComponents/UseKeyPress.jsx @@ -1,11 +1,14 @@ import { useState, useEffect } from 'react'; -const useKeyPress = (targetKey) => { +const useKeyPress = (targetKey, isPreventDefault = () => false) => { const [keyPressed, setKeyPressed] = useState(false); /* istanbul ignore next */ - const downHandler = ({ key }) => { - if (key === targetKey) { + const downHandler = (event) => { + if (event.key === targetKey) { + if (isPreventDefault()) { + event.preventDefault(); + } setKeyPressed(true); } };