diff --git a/packages/orbit-components/src/InputSelect/README.md b/packages/orbit-components/src/InputSelect/README.md index 954de21c19..7ceb120458 100644 --- a/packages/orbit-components/src/InputSelect/README.md +++ b/packages/orbit-components/src/InputSelect/README.md @@ -73,7 +73,7 @@ The table below contains all types of props available in the InputSelect compone | label | `Translation` | | The label for the InputSelect. | | name | `string` | | The name for the InputSelect. | | onBlur | `event => void \| Promise` | | Function for handling onBlur event. | -| onChange | `event => void \| Promise` | | Function for handling onChange event. | +| onChange | `event => void \| Promise` | | Function for handling onChange event on the text input. For option selection change, use `onOptionSelect`. | | onFocus | `event => void \| Promise` | | Function for handling onFocus event. | | onKeyDown | `event => void \| Promise` | | Function for handling onKeyDown event. | | onKeyUp | `event => void \| Promise` | | Function for handling onKeyUp event. | diff --git a/packages/orbit-components/src/InputSelect/__tests__/index.test.tsx b/packages/orbit-components/src/InputSelect/__tests__/index.test.tsx index 595daa4b68..96bdbb01d0 100644 --- a/packages/orbit-components/src/InputSelect/__tests__/index.test.tsx +++ b/packages/orbit-components/src/InputSelect/__tests__/index.test.tsx @@ -120,7 +120,7 @@ describe("InputSelect", () => { // test closing dropdown by ESC fireEvent.keyDown(input, { key: "Escape" }); expect(dropdown).not.toBeInTheDocument(); - expect(onClose).toHaveBeenCalled(); + expect(onClose).toHaveBeenCalledWith(jetLiOption); // test clear of the input by button and reset of filtered options userEvent.tab(); @@ -132,6 +132,7 @@ describe("InputSelect", () => { it("can have a default selected value", () => { const onClose = jest.fn(); + const onOptionSelect = jest.fn(); render( { name={name} defaultSelected={jetLiOption} onClose={onClose} + onOptionSelect={onOptionSelect} />, ); @@ -153,6 +155,13 @@ describe("InputSelect", () => { // Simulate closing to assert the selected value is the default fireEvent.keyDown(input, { key: "Escape" }); expect(onClose).toHaveBeenCalledWith(jetLiOption); + + // Simulate changing the input without selecting anything to assert the previous selected option remains selected + userEvent.type(input, "Random unexisting option"); + fireEvent.keyDown(input, { key: "Escape" }); + expect(onClose).toHaveBeenLastCalledWith(jetLiOption); + expect(onOptionSelect).not.toHaveBeenCalled(); + expect(screen.getByRole("textbox")).toHaveValue(jetLiOption.title); }); it("can have prevSelected defined", () => { diff --git a/packages/orbit-components/src/InputSelect/index.tsx b/packages/orbit-components/src/InputSelect/index.tsx index 8ade84e483..9d576e7728 100644 --- a/packages/orbit-components/src/InputSelect/index.tsx +++ b/packages/orbit-components/src/InputSelect/index.tsx @@ -89,13 +89,6 @@ const InputSelect = React.forwardRef( const { isLargeMobile } = useMediaQuery(); - const handleClose = () => { - if (onClose && isOpened) onClose(selectedOption); - setIsOpened(false); - }; - - useClickOutside(labelRef, handleClose); - const groupedOptions = React.useMemo( () => groupOptions(options, showAll, prevSelected), [options, prevSelected, showAll], @@ -107,6 +100,25 @@ const InputSelect = React.forwardRef( flattened: Option[]; }>(groupedOptions); + const handleClose = (selection?: Option | null) => { + if (!selection) { + if (inputValue !== selectedOption?.title) { + setInputValue(selectedOption?.title || ""); + setResults(groupedOptions); + setActiveIdx(0); + } + } + + if (onClose && isOpened) onClose(selection || selectedOption); + setIsOpened(false); + }; + + const handleCloseClick = () => { + handleClose(); + }; + + useClickOutside(labelRef, handleCloseClick); + const handleFocus = (ev: React.FocusEvent) => { if (onFocus) onFocus(ev); setIsOpened(true); @@ -150,16 +162,18 @@ const InputSelect = React.forwardRef( return; } - if (isOpened && ev.keyCode === KEY_CODE.ESC) setIsOpened(false); + if (isOpened && ev.keyCode === KEY_CODE.ESC) handleClose(); if (isOpened && ev.keyCode === KEY_CODE.ENTER) { ev.preventDefault(); if (results.all.length !== 0) { - setSelectedOption(results.flattened[activeIdx]); - setIsOpened(false); - setInputValue(results.flattened[activeIdx].title); - if (onOptionSelect) onOptionSelect(results.flattened[activeIdx]); + const option = results.flattened[activeIdx]; + + setSelectedOption(option); + setInputValue(option.title); + if (onOptionSelect) onOptionSelect(option); + handleClose(option); } } @@ -214,10 +228,12 @@ const InputSelect = React.forwardRef( ariaControls={isOpened ? dropdownId : undefined} autoComplete="off" ref={ref} + prefix={selectedOption && selectedOption.prefix} suffix={ String(inputValue).length > 1 && ( { + onClick={ev => { + ev.preventDefault(); if (onOptionSelect) onOptionSelect(null); setInputValue(""); setResults(groupedOptions); @@ -262,7 +278,7 @@ const InputSelect = React.forwardRef( if (!isSelected) { setInputValue(title); setSelectedOption(option); - handleClose(); + handleClose(option); } }} /> @@ -315,7 +331,7 @@ const InputSelect = React.forwardRef( if (!isSelected) { setInputValue(title); setSelectedOption(option); - handleClose(); + handleClose(option); } }} /> @@ -357,7 +373,7 @@ const InputSelect = React.forwardRef( if (!isSelected) { setInputValue(title); setSelectedOption(option); - handleClose(); + handleClose(option); } }} /> @@ -406,12 +422,13 @@ const InputSelect = React.forwardRef( role="textbox" placeholder={placeholder} value={inputValue} + prefix={selectedOption && selectedOption.prefix} /> {isOpened && ( 50}> { if (!isLargeMobile) { @@ -429,14 +446,14 @@ const InputSelect = React.forwardRef( {label} - + )} {input} {dropdown} -