From 965a36ee523095573996c6a54bff6ecebd755ddb Mon Sep 17 00:00:00 2001 From: Pritesh Nandgaonkar Date: Tue, 2 Jun 2020 04:45:58 -0700 Subject: [PATCH] Fix the broken deeplink of support form Summary: I just noticed that the deeplink of the support form didn't select the right group. It got broken after the recent design change of the support form. This diff fixes that issue. I have also added unit tests. Reviewed By: passy Differential Revision: D21817153 fbshipit-source-id: 06298b3b60cfc1bd77bea6c1f902b983474808dc --- desktop/app/src/chrome/DropDownSearchView.tsx | 26 ++- .../__tests__/DropDownSearchView.node.tsx | 173 ++++++++++++++++++ 2 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 desktop/app/src/chrome/__tests__/DropDownSearchView.node.tsx 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(); +});