Skip to content

Commit

Permalink
feat: autocomplete filter without typeahead
Browse files Browse the repository at this point in the history
  • Loading branch information
Gururajj77 committed Jul 2, 2024
1 parent 04d8d1c commit 26972c3
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
65 changes: 65 additions & 0 deletions packages/react/src/components/ComboBox/ComboBox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,4 +423,69 @@ describe('ComboBox', () => {
);
});
});
describe('ComboBox autocomplete', () => {
const items = [
{ id: 'option-1', text: 'Option 1' },
{ id: 'option-2', text: 'Option 2' },
{ id: 'option-3', text: 'Option 3' },
];

const mockProps = {
id: 'test-combobox',
items,
itemToString: (item) => (item ? item.text : ''),
onChange: jest.fn(),
};

it('should respect autocomplete prop', async () => {
render(<ComboBox {...mockProps} autocomplete />);
await waitForPosition();
const inputNode = findInputNode();
expect(inputNode).toHaveAttribute('autocomplete');
});
it('should use autocompleteCustomFilter when autocomplete prop is true', async () => {
render(<ComboBox {...mockProps} autocomplete />);

// Open the dropdown
const input = screen.getByRole('combobox');
fireEvent.click(input);

// Type 'op' which should match all options
await userEvent.type(input, 'op');
expect(screen.getAllByRole('option')).toHaveLength(3);

// Type 'opt' which should still match all options
await userEvent.type(input, 't');
expect(screen.getAllByRole('option')).toHaveLength(3);

// Type 'opti' which should match only 'Option 1'
await userEvent.type(input, 'i');
expect(screen.getAllByRole('option')).toHaveLength(3);
expect(screen.getByText('Option 1')).toBeInTheDocument();
});

it('should use default filter when autocomplete prop is false', async () => {
render(<ComboBox {...mockProps} />);

// Open the dropdown
const input = screen.getByRole('combobox');
fireEvent.click(input);

// Type 'op' which should match all options
await userEvent.type(input, 'op');
expect(screen.getAllByRole('option')).toHaveLength(3);

// Type 'opt' which should still match all options
await userEvent.type(input, 't');
expect(screen.getAllByRole('option')).toHaveLength(3);

// Type 'opti' which should still match all options
await userEvent.type(input, 'i');
expect(screen.getAllByRole('option')).toHaveLength(3);

// Type 'option' which should still match all options
await userEvent.type(input, 'on');
expect(screen.getAllByRole('option')).toHaveLength(3);
});
});
});
20 changes: 20 additions & 0 deletions packages/react/src/components/ComboBox/ComboBox.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { WithLayer } from '../../../.storybook/templates/WithLayer';

import ComboBox from '../ComboBox';
import mdx from './ComboBox.mdx';
import { on } from 'process';

const items = [
{
Expand Down Expand Up @@ -92,6 +93,21 @@ export const AllowCustomValue = (args) => {
</div>
);
};
export const AutocompleteWithTypeahead = (args) => {
return (
<div style={{ width: 300 }}>
<ComboBox
allowCustomValue
autocomplete
onChange={args.onChange}
id="carbon-combobox"
items={['Apple', 'Orange', 'Banana', 'Pineapple', 'Raspberry', 'Lime']}
titleText="ComboBox title"
helperText="Combobox helper text"
/>
</div>
);
};
export const ExperimentalAutoAlign = () => (
<div style={{ width: 400 }}>
<div style={{ height: 300 }}></div>
Expand All @@ -112,6 +128,10 @@ AllowCustomValue.argTypes = {
onChange: { action: 'onChange' },
};

AutocompleteWithTypeahead.argTypes = {
onChange: { action: 'onChange' },
};

export const _WithLayer = () => (
<WithLayer>
{(layer) => (
Expand Down
38 changes: 36 additions & 2 deletions packages/react/src/components/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ export interface ComboBoxProps<ItemType>
*/
autoAlign?: boolean;

/**
* **Experimental**: will enable autcomplete and typeahead for the input field
*/
autocomplete?: boolean;
/**
* An optional className to add to the container node
*/
Expand Down Expand Up @@ -323,6 +327,7 @@ const ComboBox = forwardRef(
['aria-label']: ariaLabel = 'Choose an item',
ariaLabel: deprecatedAriaLabel,
autoAlign = false,
autocomplete = false,
className: containerClassName,
direction = 'bottom',
disabled = false,
Expand Down Expand Up @@ -406,14 +411,32 @@ const ComboBox = forwardRef(
})
);
}
const autocompleteCustomFilter = (menu) => {
if (
!menu ||
typeof menu.item !== 'string' ||
typeof menu.inputValue !== 'string'
) {
return false;
}
const item = menu.item.toLowerCase();
const input = menu.inputValue.toLowerCase();

if (input.length > item.length) {
return false;
}

return input.split('').every((char, index) => item[index] === char);
};
const filterItems = (
items: ItemType[],
itemToString: ItemToStringHandler<ItemType>,
inputValue: string | null
) =>
items.filter((item) =>
shouldFilterItem
autocomplete
? autocompleteCustomFilter({ item: itemToString(item), inputValue })
: shouldFilterItem
? shouldFilterItem({
item,
itemToString,
Expand Down Expand Up @@ -662,7 +685,13 @@ const ComboBox = forwardRef(
'aria-label': deprecatedAriaLabel || ariaLabel,
ref: autoAlign ? refs.setFloating : null,
}),
[autoAlign, deprecatedAriaLabel, ariaLabel]
[
getMenuProps,
deprecatedAriaLabel,
ariaLabel,
autoAlign,
refs.setFloating,
]
);

return (
Expand Down Expand Up @@ -884,6 +913,11 @@ ComboBox.propTypes = {
*/
autoAlign: PropTypes.bool,

/**
* **Experimental**: will enable autcomplete and typeahead for the input field
*/
autocomplete: PropTypes.bool,

/**
* An optional className to add to the container node
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/ListBox/test-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

const prefix = 'cds';
import userEvent from '@testing-library/user-event';
import { act } from 'react';
import { act } from '@testing-library/react';

// Finding nodes in a ListBox
export const findListBoxNode = () => {
Expand Down

0 comments on commit 26972c3

Please sign in to comment.