Merge pull request #351 from philomena-dev/mouse-move-then-over

Add mouseMoveThenOver, test
This commit is contained in:
liamwhite 2024-08-29 18:39:57 -04:00 committed by GitHub
commit c83b9f1833
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 64 additions and 23 deletions

View file

@ -8,6 +8,7 @@ import { getTermContexts } from './match_query';
import store from './utils/store';
import { TermContext } from './query/lex.ts';
import { $, $$, makeEl, removeEl } from './utils/dom.ts';
import { mouseMoveThenOver } from './utils/events.ts';
type TermSuggestion = {
label: string;
@ -125,38 +126,18 @@ function createItem(list: HTMLUListElement, suggestion: TermSuggestion) {
className: 'autocomplete__item',
});
let ignoreMouseOver = true;
item.textContent = suggestion.label;
item.dataset.value = suggestion.value;
function onItemMouseOver() {
// Prevent selection when mouse entered the element without actually moving.
if (ignoreMouseOver) {
return;
}
mouseMoveThenOver(item, () => {
removeSelected();
item.classList.add('autocomplete__item--selected');
}
item.addEventListener('mouseover', onItemMouseOver);
});
item.addEventListener('mouseout', () => {
removeSelected();
});
item.addEventListener(
'mousemove',
() => {
ignoreMouseOver = false;
onItemMouseOver();
},
{
once: true,
},
);
item.addEventListener('click', () => {
if (!inputField || !item.dataset.value) return;

View file

@ -1,4 +1,4 @@
import { delegate, fire, leftClick, on, PhilomenaAvailableEventsMap } from '../events';
import { delegate, fire, mouseMoveThenOver, leftClick, on, PhilomenaAvailableEventsMap } from '../events';
import { getRandomArrayItem } from '../../../test/randomness';
import { fireEvent } from '@testing-library/dom';
@ -80,6 +80,55 @@ describe('Event utils', () => {
});
});
describe('mouseMoveThenOver', () => {
it('should NOT fire on first mouseover', () => {
const mockButton = document.createElement('button');
const mockHandler = vi.fn();
mouseMoveThenOver(mockButton, mockHandler);
fireEvent.mouseOver(mockButton);
expect(mockHandler).toHaveBeenCalledTimes(0);
});
it('should fire on the first mousemove', () => {
const mockButton = document.createElement('button');
const mockHandler = vi.fn();
mouseMoveThenOver(mockButton, mockHandler);
fireEvent.mouseMove(mockButton);
expect(mockHandler).toHaveBeenCalledTimes(1);
});
it('should fire on subsequent mouseover', () => {
const mockButton = document.createElement('button');
const mockHandler = vi.fn();
mouseMoveThenOver(mockButton, mockHandler);
fireEvent.mouseMove(mockButton);
fireEvent.mouseOver(mockButton);
expect(mockHandler).toHaveBeenCalledTimes(2);
});
it('should NOT fire on subsequent mousemove', () => {
const mockButton = document.createElement('button');
const mockHandler = vi.fn();
mouseMoveThenOver(mockButton, mockHandler);
fireEvent.mouseMove(mockButton);
fireEvent.mouseOver(mockButton);
fireEvent.mouseMove(mockButton);
expect(mockHandler).toHaveBeenCalledTimes(2);
});
});
describe('delegate', () => {
it('should call the native addEventListener method on the element', () => {
const mockElement = document.createElement('div');

View file

@ -43,6 +43,17 @@ export function leftClick<E extends MouseEvent, Target extends EventTarget>(func
};
}
export function mouseMoveThenOver<El extends HTMLElement>(element: El, func: (e: MouseEvent) => void) {
element.addEventListener(
'mousemove',
(event: MouseEvent) => {
func(event);
element.addEventListener('mouseover', func);
},
{ once: true },
);
}
export function delegate<K extends keyof PhilomenaAvailableEventsMap, Target extends Element>(
node: PhilomenaEventElement,
event: K,