diff --git a/docs/API.md b/docs/API.md
index 88d43d57..0a9a1949 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -47,6 +47,7 @@ inputProps | object | {} | Props to be applied directly to the input. `onBlur`,
isInvalid | boolean | false | Adds the `is-invalid` classname to the `form-control`. Only affects Bootstrap 4.
isLoading | boolean | false | Indicate whether an asynchronous data fetch is happening.
isValid | boolean | false | Adds the `is-valid` classname to the `form-control`. Only affects Bootstrap 4.
+keepOpen | boolean \| function | `false` | Allows selecting of multiple values from the menu at once.
labelKey | string \| function | `'label'` | See full documentation in the [Rendering section](Rendering.md#labelkey-string--function).
onChange | function | | Invoked when the set of selections changes (ie: an item is added or removed). For consistency, `selected` is always an array of selections, even if multi-selection is not enabled.
`(selected: Array
onInputChange | function | | Invoked when the input value changes. Receives the string value of the input (`text`), as well as the original event.
`(text: string, event: Event) => void`
diff --git a/src/components/Typeahead/Typeahead.stories.tsx b/src/components/Typeahead/Typeahead.stories.tsx
index 4fc4765d..e7a2f84d 100644
--- a/src/components/Typeahead/Typeahead.stories.tsx
+++ b/src/components/Typeahead/Typeahead.stories.tsx
@@ -95,6 +95,13 @@ AllowNew.args = {
allowNew: true,
};
+export const KeepOpen = Template.bind({});
+KeepOpen.args = {
+ ...defaultProps,
+ multiple: true,
+ keepOpen: true,
+};
+
export const CustomInput = Template.bind({});
CustomInput.args = {
...defaultProps,
diff --git a/src/components/Typeahead/Typeahead.test.tsx b/src/components/Typeahead/Typeahead.test.tsx
index 03e4cb0c..2f6d0c4d 100644
--- a/src/components/Typeahead/Typeahead.test.tsx
+++ b/src/components/Typeahead/Typeahead.test.tsx
@@ -1,4 +1,4 @@
-import React, { createRef, forwardRef } from 'react';
+import React, { createRef, forwardRef, useState } from 'react';
import TypeaheadComponent, { TypeaheadComponentProps } from './Typeahead';
import Typeahead, {
@@ -30,6 +30,7 @@ import {
} from '../../tests/helpers';
import states from '../../tests/data';
+import { Option } from '../../types';
const ID = 'rbt-id';
@@ -1202,6 +1203,96 @@ describe('', () => {
expect(items[0]).toHaveTextContent(`${newSelectionPrefix}${value}`);
});
});
+
+ describe('keepOpen behaviour', () => {
+ it('should not affect single selection mode', async () => {
+ const user = userEvent.setup();
+ render();
+ getInput().focus();
+ const menu = await findMenu();
+ expect(menu).toBeInTheDocument();
+ await user.keyboard('{ArrowDown}{Enter}');
+ expect(menu).not.toBeInTheDocument();
+ });
+
+ it('should default to false', async () => {
+ const user = userEvent.setup();
+ render();
+ getInput().focus();
+ const menu = await findMenu();
+ expect(menu).toBeInTheDocument();
+ await user.keyboard('{ArrowDown}{Enter}');
+ expect(menu).not.toBeInTheDocument();
+ });
+
+ it('should keep the menu open after selection', async () => {
+ const user = userEvent.setup();
+ render();
+ getInput().focus();
+ const menu = await findMenu();
+ expect(menu).toBeInTheDocument();
+ await user.keyboard('{ArrowDown}{Enter}');
+ expect(menu).toBeInTheDocument();
+ });
+
+ it('should close the menu if keepOpen function returns false', async () => {
+ const user = userEvent.setup();
+ const ctrlPressed = false;
+ render( ctrlPressed} />);
+ getInput().focus();
+ const menu = await findMenu();
+ expect(menu).toBeInTheDocument();
+ await user.keyboard('{ArrowDown}{Enter}');
+ expect(menu).not.toBeInTheDocument();
+ });
+
+ it('should keep the menu open if keepOpen function returns true', async () => {
+ const user = userEvent.setup();
+ const ctrlPressed = true;
+ render( ctrlPressed} />);
+ getInput().focus();
+ const menu = await findMenu();
+ expect(menu).toBeInTheDocument();
+ await user.keyboard('{ArrowDown}{Enter}');
+ expect(menu).toBeInTheDocument();
+ });
+
+ it('should retain the search input after selection', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const search = 'Ala';
+ const input = getInput();
+ await user.type(input, search);
+
+ const menu = await findMenu();
+ expect(menu).toBeInTheDocument();
+ await user.keyboard('{ArrowDown}{Enter}');
+ expect(input).toHaveValue(search);
+ });
+
+ it('should reset hint and active item after selection', async () => {
+ const user = userEvent.setup();
+ const KeepOpenAndSelect = () => {
+ const [selected, setSelected] = useState