diff --git a/packages/react-templates/src/components/Select/__tests__/SimpleSelect.test.tsx b/packages/react-templates/src/components/Select/__tests__/SimpleSelect.test.tsx
index ef02b577870..fea5d0f5c3b 100644
--- a/packages/react-templates/src/components/Select/__tests__/SimpleSelect.test.tsx
+++ b/packages/react-templates/src/components/Select/__tests__/SimpleSelect.test.tsx
@@ -3,7 +3,7 @@ import { render, screen, waitForElementToBeRemoved } from '@testing-library/reac
import userEvent from '@testing-library/user-event';
import { SimpleSelect } from '../SimpleSelect';
-test('renders checkbox select with options', async () => {
+test('renders simple select with options', async () => {
const initialOptions = [
{ content: 'Option 1', value: 'option1' },
{ content: 'Option 2', value: 'option2' },
@@ -232,8 +232,6 @@ test('Matches snapshot', async () => {
const user = userEvent.setup();
- render();
-
const { asFragment } = render();
const toggle = screen.getByRole('button', { name: 'Select' });
diff --git a/packages/react-templates/src/components/Select/__tests__/TypeaheadSelect.test.tsx b/packages/react-templates/src/components/Select/__tests__/TypeaheadSelect.test.tsx
new file mode 100644
index 00000000000..0d6b937c3d7
--- /dev/null
+++ b/packages/react-templates/src/components/Select/__tests__/TypeaheadSelect.test.tsx
@@ -0,0 +1,408 @@
+import * as React from 'react';
+import { render, screen, waitForElementToBeRemoved } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { TypeaheadSelect } from '../TypeaheadSelect';
+import styles from '@patternfly/react-styles/css/components/Menu/menu';
+
+test('renders typeahead select with options', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ await user.click(toggle);
+
+ const option1 = screen.getByText('Option 1');
+ const option2 = screen.getByText('Option 2');
+ const option3 = screen.getByText('Option 3');
+
+ expect(option1).toBeInTheDocument();
+ expect(option2).toBeInTheDocument();
+ expect(option3).toBeInTheDocument();
+});
+
+test('selects options when clicked', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+
+ expect(option1).not.toHaveClass(styles.modifiers.selected);
+
+ await user.click(option1);
+
+ expect(option1).toHaveClass(styles.modifiers.selected);
+});
+
+test('calls the onSelect callback with the selected value when an option is selected', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+ const onSelectMock = jest.fn();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ await user.click(toggle);
+
+ expect(onSelectMock).not.toHaveBeenCalled();
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+
+ await user.click(option1);
+
+ expect(onSelectMock).toHaveBeenCalledTimes(1);
+ expect(onSelectMock).toHaveBeenCalledWith(expect.anything(), 'option1');
+});
+
+test('deselects options when clear button is clicked', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+ await user.click(option1);
+ expect(option1).toHaveClass(styles.modifiers.selected);
+
+ const input = screen.getByRole('combobox');
+ expect(input).toHaveValue('Option 1');
+
+ const clearButton = screen.getByRole('button', { name: 'Clear input value' });
+ await user.click(clearButton);
+ expect(input).toHaveValue('');
+
+ await user.click(toggle);
+ expect(screen.getByRole('option', { name: 'Option 1' })).not.toHaveClass(styles.modifiers.selected);
+});
+
+test('toggles the select menu when the toggle button is clicked', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ await user.click(toggle);
+
+ const menu = screen.getByRole('listbox');
+ expect(menu).toBeInTheDocument();
+
+ await user.click(toggle);
+
+ await waitForElementToBeRemoved(menu);
+
+ expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
+});
+
+test('calls the onToggle callback when the toggle is clicked', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+ const onToggleMock = jest.fn();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ expect(onToggleMock).not.toHaveBeenCalled();
+
+ await user.click(toggle);
+
+ expect(onToggleMock).toHaveBeenCalledTimes(1);
+ expect(onToggleMock).toHaveBeenCalledWith(true);
+
+ await user.click(toggle);
+
+ expect(onToggleMock).toHaveBeenCalledTimes(2);
+ expect(onToggleMock).toHaveBeenCalledWith(false);
+});
+
+test('Passes toggleWidth', () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ expect(toggle.parentElement).toHaveAttribute('style', 'width: 500px;');
+});
+
+test('Passes additional toggleProps', () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ expect(toggle.parentElement).toHaveAttribute('id', 'toggle');
+});
+
+test('passes custom placeholder text', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ render();
+
+ const input = screen.getByRole('combobox');
+
+ expect(input).toHaveAttribute('placeholder', 'custom');
+});
+
+test('displays noOptionsFoundMessage when filter returns no results', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ const input = screen.getByRole('combobox');
+
+ expect(input).toHaveAttribute('placeholder', 'Select an option');
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+ const option2 = screen.getByRole('option', { name: 'Option 2' });
+ const option3 = screen.getByRole('option', { name: 'Option 3' });
+
+ expect(option1).toBeInTheDocument();
+ expect(option2).toBeInTheDocument();
+ expect(option3).toBeInTheDocument();
+
+ await user.type(input, '9');
+ expect(input).toHaveValue('9');
+
+ expect(option1).not.toBeInTheDocument();
+ expect(option2).not.toBeInTheDocument();
+ expect(option3).not.toBeInTheDocument();
+ expect(screen.getByRole('option', { name: 'No results found for "9"' })).toBeInTheDocument();
+});
+
+test('displays custom noOptionsFoundMessage', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ const input = screen.getByRole('combobox');
+
+ expect(input).toHaveAttribute('placeholder', 'Select an option');
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+ const option2 = screen.getByRole('option', { name: 'Option 2' });
+ const option3 = screen.getByRole('option', { name: 'Option 3' });
+
+ expect(option1).toBeInTheDocument();
+ expect(option2).toBeInTheDocument();
+ expect(option3).toBeInTheDocument();
+
+ await user.type(input, '9');
+ expect(input).toHaveValue('9');
+
+ expect(option1).not.toBeInTheDocument();
+ expect(option2).not.toBeInTheDocument();
+ expect(option3).not.toBeInTheDocument();
+ expect(screen.getByRole('option', { name: 'custom' })).toBeInTheDocument();
+});
+
+test('disables the select when isDisabled prop is true', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ expect(toggle.parentElement).toHaveClass(styles.modifiers.disabled);
+
+ await user.click(toggle);
+
+ expect(screen.queryByRole('menu')).not.toBeInTheDocument();
+});
+
+test('passes other SelectOption props to the SelectOption component', async () => {
+ const initialOptions = [{ content: 'Option 1', value: 'option1', isDisabled: true }];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+ expect(option1).toBeDisabled();
+});
+
+test('displays the option in input when option is selected', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ const input = screen.getByRole('combobox');
+
+ expect(input).toHaveAttribute('placeholder', 'Select an option');
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+
+ await user.click(option1);
+
+ expect(input).toHaveValue('Option 1');
+
+ const clearButton = screen.getByRole('button', { name: 'Clear input value' });
+ await user.click(clearButton);
+
+ expect(input).toHaveValue('');
+});
+
+test('typing in input filters options', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ const input = screen.getByRole('combobox');
+
+ expect(input).toHaveAttribute('placeholder', 'Select an option');
+
+ await user.click(toggle);
+
+ const option1 = screen.getByRole('option', { name: 'Option 1' });
+ const option2 = screen.getByRole('option', { name: 'Option 2' });
+ const option3 = screen.getByRole('option', { name: 'Option 3' });
+
+ expect(option1).toBeInTheDocument();
+ expect(option2).toBeInTheDocument();
+ expect(option3).toBeInTheDocument();
+
+ await user.type(input, '1');
+ expect(input).toHaveValue('1');
+
+ expect(option1).toBeInTheDocument();
+ expect(option2).not.toBeInTheDocument();
+ expect(option3).not.toBeInTheDocument();
+});
+
+test('typing in input triggers onInputChange callback', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+ const onInputChangeMock = jest.fn();
+
+ render();
+
+ const input = screen.getByRole('combobox');
+
+ expect(input).toHaveAttribute('placeholder', 'Select an option');
+
+ await user.type(input, '1');
+
+ expect(input).toHaveValue('1');
+ expect(onInputChangeMock).toHaveBeenCalledTimes(1);
+ expect(onInputChangeMock).toHaveBeenCalledWith('1');
+});
+
+test('Matches snapshot', async () => {
+ const initialOptions = [
+ { content: 'Option 1', value: 'option1' },
+ { content: 'Option 2', value: 'option2' },
+ { content: 'Option 3', value: 'option3' }
+ ];
+
+ const user = userEvent.setup();
+
+ const { asFragment } = render();
+
+ const toggle = screen.getByRole('button', { name: 'Typeahead menu toggle' });
+ await user.click(toggle);
+
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-templates/src/components/Select/__tests__/__snapshots__/SimpleSelect.test.tsx.snap b/packages/react-templates/src/components/Select/__tests__/__snapshots__/SimpleSelect.test.tsx.snap
index 40749c6d866..574bc65e10c 100644
--- a/packages/react-templates/src/components/Select/__tests__/__snapshots__/SimpleSelect.test.tsx.snap
+++ b/packages/react-templates/src/components/Select/__tests__/__snapshots__/SimpleSelect.test.tsx.snap
@@ -5,7 +5,7 @@ exports[`Matches snapshot 1`] = `