Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: stop touchstart event propagation if coming from cancel button in detached mode #924

Merged
merged 12 commits into from
Mar 31, 2022
29 changes: 21 additions & 8 deletions packages/autocomplete-js/src/__tests__/detached.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
6 changes: 5 additions & 1 deletion packages/autocomplete-js/src/createAutocompleteDom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,12 @@ export function createAutocompleteDom<TItem extends BaseItem>({
type: 'button',
sarahdayan marked this conversation as resolved.
Show resolved Hide resolved
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);
},
Expand Down
15 changes: 14 additions & 1 deletion packages/autocomplete-js/src/utils/setProperties.ts
Original file line number Diff line number Diff line change
@@ -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
*
Expand Down Expand Up @@ -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 = {};
Expand Down