diff --git a/packages/autocomplete-js/src/__tests__/detached.test.ts b/packages/autocomplete-js/src/__tests__/detached.test.ts index 430e61dc8..c0cd09780 100644 --- a/packages/autocomplete-js/src/__tests__/detached.test.ts +++ b/packages/autocomplete-js/src/__tests__/detached.test.ts @@ -111,17 +111,30 @@ describe('detached', () => { '.aa-DetachedCancelButton' ); - const bodyClickListener = jest.fn(); - document.querySelector('body').addEventListener('click', bodyClickListener); + // Prevent `onTouchStart` event from closing detached overlay + const windowTouchStartListener = jest.fn(); + window.addEventListener('touchstart', windowTouchStartListener); + + fireEvent( + cancelButton, + new TouchEvent('touchstart', { + bubbles: true, + cancelable: true, + composed: true, + }) + ); - // Close detached overlay - cancelButton.click(); + expect(windowTouchStartListener).toHaveBeenCalledTimes(0); - expect(bodyClickListener).toHaveBeenCalledTimes(0); + window.removeEventListener('touchstart', windowTouchStartListener); + + await waitFor(() => { + expect(document.querySelector('.aa-DetachedOverlay')).toBeInTheDocument(); + expect(document.body).toHaveClass('aa-Detached'); + }); - document - .querySelector('body') - .removeEventListener('click', bodyClickListener); + // Close detached overlay + cancelButton.click(); // The detached overlay should close await waitFor(() => { diff --git a/packages/autocomplete-js/src/createAutocompleteDom.ts b/packages/autocomplete-js/src/createAutocompleteDom.ts index aae8dc7bd..3ce303773 100644 --- a/packages/autocomplete-js/src/createAutocompleteDom.ts +++ b/packages/autocomplete-js/src/createAutocompleteDom.ts @@ -169,8 +169,12 @@ export function createAutocompleteDom({ type: 'button', class: classNames.detachedCancelButton, textContent: translations.detachedCancelButtonText, - onClick(event: MouseEvent) { + // Prevent `onTouchStart` from closing the panel + // since it should be initiated by `onClick` only + onTouchStart(event: TouchEvent) { event.stopPropagation(); + }, + onClick() { autocomplete.setIsOpen(false); setIsModalOpen(false); }, diff --git a/packages/autocomplete-js/src/utils/setProperties.ts b/packages/autocomplete-js/src/utils/setProperties.ts index e0ab31deb..4098b493b 100644 --- a/packages/autocomplete-js/src/utils/setProperties.ts +++ b/packages/autocomplete-js/src/utils/setProperties.ts @@ -1,5 +1,17 @@ /* eslint-disable */ +/** + * Touch-specific event aliases + * + * See https://w3c.github.io/touch-events/#extensions-to-the-globaleventhandlers-mixin + */ +const TOUCH_EVENTS_ALIASES = [ + 'ontouchstart', + 'ontouchend', + 'ontouchmove', + 'ontouchcancel', +]; + /* * Taken from Preact * @@ -50,7 +62,8 @@ export function setProperty(dom: HTMLElement, name: string, value: any) { else if (name[0] === 'o' && name[1] === 'n') { useCapture = name !== (name = name.replace(/Capture$/, '')); nameLower = name.toLowerCase(); - if (nameLower in dom) name = nameLower; + if (nameLower in dom || TOUCH_EVENTS_ALIASES.includes(nameLower)) + name = nameLower; name = name.slice(2); if (!(dom as any)._listeners) (dom as any)._listeners = {};