Skip to content

Commit

Permalink
fix(InputSelect): close events and invalid typing
Browse files Browse the repository at this point in the history
  • Loading branch information
DSil committed Jul 25, 2023
1 parent 6f888f6 commit ce9051a
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 21 deletions.
2 changes: 1 addition & 1 deletion packages/orbit-components/src/InputSelect/README.md
Expand Up @@ -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. |
Expand Down
Expand Up @@ -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();
Expand All @@ -132,6 +132,7 @@ describe("InputSelect", () => {

it("can have a default selected value", () => {
const onClose = jest.fn();
const onOptionSelect = jest.fn();

render(
<InputSelect
Expand All @@ -141,6 +142,7 @@ describe("InputSelect", () => {
name={name}
defaultSelected={jetLiOption}
onClose={onClose}
onOptionSelect={onOptionSelect}
/>,
);

Expand All @@ -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", () => {
Expand Down
55 changes: 36 additions & 19 deletions packages/orbit-components/src/InputSelect/index.tsx
Expand Up @@ -89,13 +89,6 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(

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],
Expand All @@ -107,6 +100,25 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
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<HTMLInputElement>) => {
if (onFocus) onFocus(ev);
setIsOpened(true);
Expand Down Expand Up @@ -150,16 +162,18 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
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);
}
}

Expand Down Expand Up @@ -214,10 +228,12 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
ariaControls={isOpened ? dropdownId : undefined}
autoComplete="off"
ref={ref}
prefix={selectedOption && selectedOption.prefix}
suffix={
String(inputValue).length > 1 && (
<StyledCloseButton
onClick={() => {
onClick={ev => {
ev.preventDefault();
if (onOptionSelect) onOptionSelect(null);
setInputValue("");
setResults(groupedOptions);
Expand Down Expand Up @@ -262,7 +278,7 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
if (!isSelected) {
setInputValue(title);
setSelectedOption(option);
handleClose();
handleClose(option);
}
}}
/>
Expand Down Expand Up @@ -315,7 +331,7 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
if (!isSelected) {
setInputValue(title);
setSelectedOption(option);
handleClose();
handleClose(option);
}
}}
/>
Expand Down Expand Up @@ -357,7 +373,7 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
if (!isSelected) {
setInputValue(title);
setSelectedOption(option);
handleClose();
handleClose(option);
}
}}
/>
Expand Down Expand Up @@ -406,12 +422,13 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
role="textbox"
placeholder={placeholder}
value={inputValue}
prefix={selectedOption && selectedOption.prefix}
/>
{isOpened && (
<StyledModalWrapper $maxHeight={maxHeight} isScrolled={isScrolled && topOffset > 50}>
<Modal
labelClose={labelClose}
onClose={handleClose}
onClose={handleCloseClick}
fixedFooter
onScroll={ev => {
if (!isLargeMobile) {
Expand All @@ -429,14 +446,14 @@ const InputSelect = React.forwardRef<HTMLInputElement, Props>(
<Box>
<Heading type="title2">{label}</Heading>
</Box>
<ModalCloseButton onClick={handleClose} title="Close" />
<ModalCloseButton onClick={handleCloseClick} title={labelClose} />
</Stack>
)}
{input}
</StyledModalHeader>
<StyledModalSection>{dropdown}</StyledModalSection>
<ModalFooter flex="100%">
<Button type="secondary" fullWidth onClick={handleClose}>
<Button type="secondary" fullWidth onClick={handleCloseClick}>
{labelClose}
</Button>
</ModalFooter>
Expand Down

0 comments on commit ce9051a

Please sign in to comment.