Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,23 @@ describe('[Autocomplete functionality]', () => {
screen.queryByRole('button', { name: 'test1' }),
).not.toBeInTheDocument();
});
it('should remove last selected item when pressing Backspace with empty filter (multiple)', async () => {
const onChange = jest.fn();
render(
<AutoComplete
value={['1', '2']}
filter=''
setFilter={() => {}}
options={[
{ value: '1', label: 'test1' },
{ value: '2', label: 'test2' },
]}
onChange={onChange}
multiple
/>,
);
const input = screen.getByRole('textbox');
await userEvent.type(input, '{Backspace}');
expect(onChange).toHaveBeenCalledWith(['1']);
});
});
43 changes: 36 additions & 7 deletions packages/fuselage/src/components/AutoComplete/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
ChangeEvent,
ComponentType,
FocusEvent,
KeyboardEvent,
MouseEvent,
ReactNode,
} from 'react';
Expand Down Expand Up @@ -106,7 +107,6 @@ function AutoComplete<TLabel = ReactNode>({
);

useEffect(() => {
// Validates if selected items are still valid after value changes
setSelected((selected) => {
return !selected.every(isSelectedValid(value))
? selected.filter(isSelectedValid(value))
Expand Down Expand Up @@ -163,21 +163,50 @@ function AutoComplete<TLabel = ReactNode>({
);

const firstSelectedIndex = useMemo(
() => options.findIndex((option) => selected[0]?.value === option.value),
[options, selected],
);
() =>
filter
? options.findIndex((option) =>
String(option.label)
.toLowerCase()
.includes(filter.toLowerCase()),
)
: options.findIndex((option) => selected[0]?.value === option.value),
[options, selected, filter],
);

const [cursor, handleKeyDown, , reset, [optionsAreVisible, hide, show]] =
useCursor(firstSelectedIndex, memoizedOptions, handleSelect);

const handleKeyDownWrapper = useStableCallback(
(event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Backspace' && filter === '') {
if (selected.length > 0) {
const lastSelected = selected[selected.length - 1];
const filtered = selected.slice(0, -1);
const filteredValue =
multiple && Array.isArray(value)
? value.filter((item) => item !== lastSelected.value)
: '';
setSelected(filtered);
onChange(filteredValue);
hide();
return;
}
}
handleKeyDown(event);
},
);

const handleOnBlur = useStableCallback(
(event: FocusEvent<HTMLInputElement>) => {
hide();
onBlurAction(event);
},
);

useEffect(reset, [filter, reset]);
useEffect(() => {
reset();
}, [filter, reset]);

return (
<Box
Expand Down Expand Up @@ -206,7 +235,7 @@ function AutoComplete<TLabel = ReactNode>({
)}
onBlur={handleOnBlur}
onFocus={show}
onKeyDown={handleKeyDown}
onKeyDown={handleKeyDownWrapper}
placeholder={
optionsAreVisible === AnimatedVisibility.HIDDEN || !value
? placeholder
Expand Down Expand Up @@ -262,4 +291,4 @@ function AutoComplete<TLabel = ReactNode>({
);
}

export default AutoComplete;
export default AutoComplete;
16 changes: 11 additions & 5 deletions packages/fuselage/src/components/Options/useCursor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useStableCallback } from '@rocket.chat/fuselage-hooks';
import type { KeyboardEvent } from 'react';
import { useState } from 'react';
import { useState, useEffect } from 'react';

import { AnimatedVisibility } from '../AnimatedVisibility';

Expand Down Expand Up @@ -83,10 +83,13 @@ export const useCursor = <
reset: () => void,
visibilityHandler: ReturnType<typeof useVisible>,
] => {
const [cursor, setCursor] = useState(initial);
const [cursor, setCursor] = useState(initial);
useEffect(() => {
setCursor(initial);
}, [initial]);
const visibilityHandler = useVisible();
const [visibility, hide, show] = visibilityHandler;
const reset = useStableCallback(() => setCursor(0));
const reset = useStableCallback(() => setCursor(initial));
const handleKeyUp = useStableCallback((e: KeyboardEvent) => {
const { keyCode } = e;
if (AnimatedVisibility.HIDDEN === visibility && keyCode === keyCodes.TAB) {
Expand Down Expand Up @@ -140,8 +143,11 @@ export const useCursor = <
e.nativeEvent.stopImmediatePropagation(); // TODO
e.stopPropagation();
}
hide();
onChange(options[cursor], visibilityHandler);
const targetIndex = cursor >= 0 ? cursor : 0;
if (options[targetIndex]) {
hide();
onChange(options[targetIndex], visibilityHandler);
}
return;

case keyCodes.ESC:
Expand Down