diff --git a/desktop/app/src/chrome/DropDownSearchView.tsx b/desktop/app/src/chrome/DropDownSearchView.tsx index 1c505834b..ab9574c29 100644 --- a/desktop/app/src/chrome/DropDownSearchView.tsx +++ b/desktop/app/src/chrome/DropDownSearchView.tsx @@ -71,6 +71,7 @@ function RowComponent(props: { }) { return ( { props.onClick(props.elem.id); }}> @@ -85,15 +86,11 @@ function RowComponent(props: { export default function (props: Props) { const {list, handleNoResults, onSelect, selectedElementID} = props; - const initialElement = list.find((e) => { - return e.id === selectedElementID; - }); + const [filteredElements, setFilteredElements] = useState>([]); - const [searchedValue, setSearchedValue] = useState( - initialElement ? initialElement.label : '', - ); + const [searchedValue, setSearchedValue] = useState(''); const [selectedElement, setSelectedElement] = useState( - initialElement, + undefined, ); const [focussed, setFocus] = useState(false); const wrapperRef = useRef(null); @@ -129,6 +126,16 @@ export default function (props: Props) { [setFocus], ); + // Set the searched value and selectedElement when the selectedElementID changes. + useEffect(() => { + const initialElement = list.find((e) => e.id === selectedElementID); + if (initialElement) { + setSearchedValue(initialElement.label); + } + setSelectedElement(initialElement); + setFocus(false); + }, [selectedElementID, list]); + // Effect to filter items useEffect(() => { if (searchedValue.length > 0) { @@ -195,6 +202,7 @@ export default function (props: Props) { onFocus={onFocusCallBack} value={searchedValue} isValidInput={false} + data-testid={'search-input'} /> @@ -203,14 +211,14 @@ export default function (props: Props) { {filteredElements.map((e, idx) => { return ( - <> + {idx < filteredElements.length - 1 && } - + ); })} diff --git a/desktop/app/src/chrome/__tests__/DropDownSearchView.node.tsx b/desktop/app/src/chrome/__tests__/DropDownSearchView.node.tsx new file mode 100644 index 000000000..2c272b246 --- /dev/null +++ b/desktop/app/src/chrome/__tests__/DropDownSearchView.node.tsx @@ -0,0 +1,173 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import * as React from 'react'; +import {render, fireEvent} from '@testing-library/react'; +import DropDownSearchView from '../DropDownSearchView'; +import {act} from 'react-dom/test-utils'; + +test('Test selected element id is shown as the selected one.', async () => { + const res = render( + , + ); + const searchInput = (await res.findByTestId( + 'search-input', + )) as HTMLInputElement; + expect(searchInput).toBeTruthy(); + expect(searchInput.value).toEqual('label1'); + + act(() => { + searchInput.dispatchEvent(new FocusEvent('focus', {bubbles: true})); + }); + expect(await res.queryByText('label1')).toBeTruthy(); +}); + +test('Test the change of the selectedElementID changes the the selected element in the UI.', async () => { + const res = render( + , + ); + const searchInput = (await res.findByTestId( + 'search-input', + )) as HTMLInputElement; + expect(searchInput).toBeTruthy(); + expect(searchInput.value).toEqual('label1'); + + res.rerender( + , + ); + const searchInputRerendered = (await res.findByTestId( + 'search-input', + )) as HTMLInputElement; + expect(searchInputRerendered).toBeTruthy(); + expect(searchInputRerendered.value).toEqual('label2'); +}); + +test('Test the entire flow and click on the available options.', async () => { + const onSelect = jest.fn(); + const res = render( + , + ); + const searchInput = (await res.findByTestId( + 'search-input', + )) as HTMLInputElement; + expect(searchInput).toBeTruthy(); + expect(searchInput.value).toEqual('label1'); + + act(() => { + searchInput.dispatchEvent(new FocusEvent('focus', {bubbles: true})); + }); + // Right now just the filtered elements will show up + expect(await res.queryByText('label1')).toBeTruthy(); + expect(await res.queryByText('label2')).toBeFalsy(); + expect(await res.queryByText('label3')).toBeFalsy(); + expect(await res.queryByText('label4')).toBeFalsy(); + act(() => { + fireEvent.change(searchInput, {target: {value: ''}}); + }); + // Once the input field is cleared all the available options will show up. + expect(await res.queryByText('label1')).toBeTruthy(); + expect(await res.queryByText('label2')).toBeTruthy(); + expect(await res.queryByText('label3')).toBeTruthy(); + const text4 = await res.queryByText('label4'); + + expect(text4).toBeTruthy(); + + act(() => { + text4?.parentElement?.dispatchEvent( + new MouseEvent('click', {bubbles: true}), + ); + }); + + expect(searchInput.value).toEqual('label4'); + expect(onSelect).toBeCalledTimes(1); + // After onSelect the expanded menu gets closed. + expect(await res.queryByText('label1')).toBeFalsy(); + expect(await res.queryByText('label2')).toBeFalsy(); + expect(await res.queryByText('label3')).toBeFalsy(); + expect(await res.queryByText('label4')).toBeFalsy(); +}); + +test('Test the validation error.', async () => { + const handleNoResults = jest.fn(); + const res = render( + , + ); + const searchInput = (await res.findByTestId( + 'search-input', + )) as HTMLInputElement; + expect(searchInput).toBeTruthy(); + expect(searchInput.value).toEqual(''); + + act(() => { + searchInput.dispatchEvent(new FocusEvent('focus', {bubbles: true})); + }); + // Right now just the filtered elements will show up + expect(await res.queryByText('label1 group')).toBeTruthy(); + expect(await res.queryByText('label2 group')).toBeTruthy(); + expect(await res.queryByText('label3 support')).toBeTruthy(); + expect(await res.queryByText('label4 support')).toBeTruthy(); + + act(() => { + fireEvent.change(searchInput, {target: {value: 'support'}}); + }); + // Only the items which satisfy the search query should be shown + expect(await res.queryByText('label3 support')).toBeTruthy(); + expect(await res.queryByText('label4 support')).toBeTruthy(); + expect(await res.queryByText('label1 group')).toBeFalsy(); + expect(await res.queryByText('label2 group')).toBeFalsy(); + act(() => { + fireEvent.change(searchInput, {target: {value: 'gibberish'}}); + }); + + expect(handleNoResults).toBeCalled(); + expect(await res.queryByText('label3 support')).toBeFalsy(); + expect(await res.queryByText('label4 support')).toBeFalsy(); + expect(await res.queryByText('label1 group')).toBeFalsy(); + expect(await res.queryByText('label2 group')).toBeFalsy(); +});