fix: Esc key down doesn't close searchbox (#1544)

* fix: Esckey on ListBoxSearch doesn't close the searchbox
This commit is contained in:
Donya MashaallahPoor
2024-06-13 13:58:23 +02:00
committed by GitHub
parent 7fd2fcc97f
commit d6cb5aa122
5 changed files with 91 additions and 19 deletions

View File

@@ -192,6 +192,7 @@ export default function ListBoxPopover({
<div ref={moreAlignTo} />
<Grid item ref={searchContainerRef}>
<ListBoxSearch
popoverOpen={open}
styles={styles}
visible
model={model}

View File

@@ -59,6 +59,7 @@ const StyledOutlinedInput = styled(OutlinedInput, {
});
export default function ListBoxSearch({
popoverOpen,
selections,
selectionState,
model,
@@ -75,7 +76,6 @@ export default function ListBoxSearch({
const { translator } = useContext(InstanceContext);
const [value, setValue] = useState('');
const [wildcardOn, setWildcardOn] = useState(false);
const inputRef = useRef();
const { getStoreValue, setStoreValue } = useDataStore(model);
@@ -171,6 +171,9 @@ export default function ListBoxSearch({
case 'Escape': {
focusRow(container);
cancel();
if (popoverOpen) {
return undefined;
}
break;
}
case 'Tab': {

View File

@@ -20,16 +20,21 @@ const styles = { content: {}, header: {}, selections: {}, search: {}, background
const keyboard = {};
let selectionState;
const hide = jest.fn().mockReturnValue(true);
let open = false;
const testRender = (model) =>
create(
<InstanceContext.Provider value={{ translator: { get: () => 'Search' } }}>
<ListBoxSearch
popoverOpen={open}
styles={styles}
selections={selections}
model={model}
keyboard={keyboard}
selectionState={selectionState}
wildCardSearch
hide={hide}
/>
</InstanceContext.Provider>
);
@@ -56,6 +61,7 @@ describe('<ListBoxSearch />', () => {
keyboard.innerTabStops = true;
keyboard.outerTabStops = !keyboard.innerTabStops;
open = false;
model = {
searchListObjectFor: jest.fn().mockResolvedValue(true),
@@ -205,7 +211,7 @@ describe('<ListBoxSearch />', () => {
expect(type.props.value).toBe('no matches generated by this string');
});
test('should call `cancel` on `Escape`', async () => {
test('should call `cancel` on `Escape` and stopPropagation', async () => {
const testRenderer = testRender(model);
const testInstance = testRenderer.root;
const type = testInstance.findByType(OutlinedInput);
@@ -216,12 +222,34 @@ describe('<ListBoxSearch />', () => {
await act(async () => {
await type.props.onKeyDown({ ...keyEventDefaults, key: 'Escape' });
});
// expect(selections.isActive).toHaveBeenCalledTimes(1);
expect(selections.cancel).toHaveBeenCalledTimes(1);
expect(model.abortListObjectSearch).not.toHaveBeenCalled();
expect(selections.cancel).toHaveBeenCalled();
expect(keyEventDefaults.stopPropagation).toHaveBeenCalled();
expect(type.props.value).toBe('foo'); // text is not reset in the test since "deactivated" is not triggered on cancel
});
test('should call `cancel` on `Escape` and `return` and propagate the event', async () => {
open = true;
const testRenderer = create(
<InstanceContext.Provider value={{ translator: { get: () => 'Search' } }}>
<ListBoxSearch
popoverOpen={open}
selectionState={selectionState}
styles={styles}
selections={selections}
model={model}
keyboard={keyboard}
/>
</InstanceContext.Provider>
);
const testInstance = testRenderer.root;
const type = testInstance.findByType(OutlinedInput);
await act(async () => {
await type.props.onKeyDown({ ...keyEventDefaults, key: 'Escape' });
});
expect(keyEventDefaults.stopPropagation).not.toHaveBeenCalled();
});
test('should abort after performing a search and then removing the text', async () => {
const testRenderer = testRender(model);
const testInstance = testRenderer.root;

View File

@@ -20,6 +20,18 @@ describe('keyboard navigation', () => {
stopPropagation: jest.fn(),
};
const eventCancel = {
nativeEvent: { keyCode: KEYS.ESCAPE },
preventDefault: jest.fn(),
stopPropagation: jest.fn(),
};
const eventConfirm = {
nativeEvent: { keyCode: KEYS.ENTER },
preventDefault: jest.fn(),
stopPropagation: jest.fn(),
};
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
@@ -74,24 +86,48 @@ describe('keyboard navigation', () => {
expect(event.stopPropagation).toHaveBeenCalledTimes(1);
});
test('confirm selections with Enter', () => {
const eventConfirm = {
nativeEvent: { keyCode: KEYS.ENTER },
preventDefault: jest.fn(),
stopPropagation: jest.fn(),
};
test('confirm selections with Enter and stopPropagation', () => {
handleKeyDownForRow(eventConfirm);
expect(actions.confirm).toHaveBeenCalledTimes(1);
expect(actions.confirm).toHaveBeenCalled();
expect(eventConfirm.stopPropagation).toHaveBeenCalled();
});
test('cancel selections with Escape', () => {
const eventCancel = {
nativeEvent: { keyCode: KEYS.ESCAPE },
preventDefault: jest.fn(),
stopPropagation: jest.fn(),
};
test('confirm selections with Enter and propagate the event', () => {
isModal = undefined;
handleKeyDownForRow = getRowsKeyboardNavigation({
select: actions.select,
cancel: actions.cancel,
confirm: actions.confirm,
setScrollPosition,
focusListItems,
keyboard,
isModal,
});
handleKeyDownForRow(eventConfirm);
expect(actions.confirm).toHaveBeenCalled();
expect(eventConfirm.stopPropagation).not.toHaveBeenCalled();
});
test('cancel selections with Escape and stopPropagation', () => {
handleKeyDownForRow(eventCancel);
expect(actions.cancel).toHaveBeenCalledTimes(1);
expect(actions.cancel).toHaveBeenCalled();
expect(eventCancel.stopPropagation).toHaveBeenCalled();
});
test('cancel selections with Escape and propagate the event', () => {
isModal = undefined;
handleKeyDownForRow = getRowsKeyboardNavigation({
select: actions.select,
cancel: actions.cancel,
confirm: actions.confirm,
setScrollPosition,
focusListItems,
keyboard,
isModal,
});
handleKeyDownForRow(eventCancel);
expect(actions.cancel).toHaveBeenCalled();
expect(eventCancel.stopPropagation).not.toHaveBeenCalled();
});
test('arrow up should move focus upwards', () => {

View File

@@ -119,6 +119,10 @@ export default function getRowsKeyboardNavigation({
}
break;
case KEYS.ESCAPE:
if (typeof isModal === 'undefined') {
cancel();
return;
}
if (isModal()) {
cancel();
} else {