Skip to content

Commit

Permalink
fix(combobox): correct selection when filtered items
Browse files Browse the repository at this point in the history
  • Loading branch information
tay1orjones committed Jun 20, 2024
1 parent a8f9d2e commit 91126d8
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 13 deletions.
44 changes: 42 additions & 2 deletions packages/react/src/components/ComboBox/ComboBox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
assertMenuClosed,
generateItems,
generateGenericItem,
cognateItems,
} from '../ListBox/test-helpers';
import ComboBox from '../ComboBox';
import { act } from 'react-dom/test-utils';
Expand Down Expand Up @@ -63,7 +64,7 @@ describe('ComboBox', () => {
}
});

it.only('should call `onChange` with the proper item when `shouldFilterItem` is provided', async () => {
it('should call `onChange` with the proper item when `shouldFilterItem` is provided', async () => {
const filterItems = (menu) => {
return menu?.item?.label
?.toLowerCase()
Expand All @@ -89,7 +90,45 @@ describe('ComboBox', () => {
});
});

it('capture filter text events', async () => {
it('should select the correct item from the filtered list after text input on click', async () => {
const user = userEvent.setup();

render(<ComboBox {...mockProps} items={cognateItems} />);

await user.type(findInputNode(), 'struct');

await user.click(screen.getAllByRole('option')[1]);

expect(mockProps.onChange).toHaveBeenCalledTimes(1);
expect(mockProps.onChange).toHaveBeenCalledWith({
selectedItem: {
id: 'construct',
text: 'Construct',
},
});
});

it('should select the correct item from the filtered list after text input on [Enter]', async () => {
const user = userEvent.setup();

render(<ComboBox {...mockProps} items={cognateItems} />);

await user.type(findInputNode(), 'struct');

await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('[Enter]');

expect(mockProps.onChange).toHaveBeenCalledTimes(1);
expect(mockProps.onChange).toHaveBeenCalledWith({
selectedItem: {
id: 'construct',
text: 'Construct',
},
});
});

it('capture filter text event onInputChange', async () => {
const onInputChange = jest.fn();
render(<ComboBox {...mockProps} onInputChange={onInputChange} />);

Expand Down Expand Up @@ -129,6 +168,7 @@ describe('ComboBox', () => {
expect(mockProps.onChange).toHaveBeenCalledWith({
selectedItem: mockProps.items[1],
});
expect(screen.getByRole('combobox')).toHaveDisplayValue('Item 1');
});

it('should not let the user select an option by clicking on the disabled option node', async () => {
Expand Down
19 changes: 8 additions & 11 deletions packages/react/src/components/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ const {
FunctionToggleMenu,
ToggleButtonClick,
ItemMouseMove,
ItemClick,
InputKeyDownArrowUp,
InputKeyDownArrowDown,
MenuMouseLeave,
Expand Down Expand Up @@ -467,12 +466,6 @@ const ComboBox = forwardRef(
case ItemMouseMove:
return { ...changes, highlightedIndex: state.highlightedIndex };

case ItemClick:
if (onChange) {
onChange({ selectedItem: changes.selectedItem });
}
return changes;

default:
return changes;
}
Expand Down Expand Up @@ -563,9 +556,9 @@ const ComboBox = forwardRef(
setHighlightedIndex(indexToHighlight(inputValue));
},

// onSelectedItemChange({ selectedItem }) {
// onChange({ selectedItem });
// },
onSelectedItemChange({ selectedItem }) {
onChange({ selectedItem });
},

initialSelectedItem: initialSelectedItem,
inputId: id,
Expand Down Expand Up @@ -670,7 +663,11 @@ const ComboBox = forwardRef(
toggleMenu();

if (highlightedIndex !== -1) {
selectItem(items[highlightedIndex]);
selectItem(
filterItems(items, itemToString, inputValue)[
highlightedIndex
]
);
}

event.preventDownshiftDefault = true;
Expand Down
42 changes: 42 additions & 0 deletions packages/react/src/components/ListBox/test-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,45 @@ export const generateItems = (amount, generator) =>
.map((_, i) => generator(i));

export const customItemToString = ({ field }) => field;

/**
* This object contains two sets of three items that share the same root
* word in different portions of the string (beginning, middle, end):
*
* - 'struct'
* - 'port'
*
* Separated by a disabled item, these derivative words are helpful when
* testing fuzzy search functions and components that do substring filtering.
*/
export const cognateItems = [
{
id: 'structure',
text: 'Structure',
},
{
id: 'construct',
text: 'Construct',
},
{
id: 'instruction',
text: 'Instruction',
},
{
id: 'disabled-item',
text: 'A disabled item',
disabled: true,
},
{
id: 'transport',
text: 'Transport',
},
{
id: 'portable',
text: 'Portable',
},
{
id: 'import',
text: 'Import',
},
];

0 comments on commit 91126d8

Please sign in to comment.