Skip to content

Commit 6e6059d

Browse files
committed
history: use double-click for opening links
1 parent 820a253 commit 6e6059d

File tree

5 files changed

+53
-33
lines changed

5 files changed

+53
-33
lines changed

special-pages/pages/history/app/components/Item.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
text-decoration: none;
107107
color: inherit;
108108
line-height: var(--row-height);
109+
pointer-events: none;
109110
}
110111

111112
.domain {

special-pages/pages/history/app/global/hooks/useAuxClickHandler.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export function useAuxClickHandler() {
1111
const dispatch = useHistoryServiceDispatch();
1212
useEffect(() => {
1313
const handleAuxClick = (event) => {
14-
const anchor = /** @type {HTMLButtonElement|null} */ (event.target.closest('a[href][data-url]'));
14+
const row = /** @type {HTMLDivElement|null} */ (event.target.closest('[aria-selected]'));
15+
const anchor = /** @type {HTMLAnchorElement|null} */ (row?.querySelector('a[href][data-url]'));
1516
const url = anchor?.dataset.url;
1617
if (anchor && url && event.button === 1) {
1718
event.preventDefault();
@@ -24,5 +25,5 @@ export function useAuxClickHandler() {
2425
return () => {
2526
document.removeEventListener('auxclick', handleAuxClick);
2627
};
27-
}, []);
28+
}, [platformName, dispatch]);
2829
}

special-pages/pages/history/app/global/hooks/useLinkClickHandler.js

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,56 @@ export function useLinkClickHandler() {
1616
const dispatch = useHistoryServiceDispatch();
1717
useEffect(() => {
1818
/**
19-
* Handles click events on the document, intercepting interactions with anchor elements
20-
* that specify both `href` and `data-url` attributes.
19+
* Handles double-click events, and tries to open a link.
2120
*
2221
* @param {MouseEvent} event - The mouse event triggered by a click.
2322
* @returns {void} - No return value.
2423
*/
25-
function clickHandler(event) {
26-
if (!(event.target instanceof Element)) return;
27-
const anchor = /** @type {HTMLAnchorElement|null} */ (event.target.closest('a[href][data-url]'));
28-
if (anchor) {
29-
const url = anchor.dataset.url;
30-
if (!url) return;
24+
function dblClickHandler(event) {
25+
const url = closestUrl(event);
26+
if (url) {
3127
event.preventDefault();
3228
event.stopImmediatePropagation();
3329
const target = eventToTarget(event, platformName);
3430
dispatch({ kind: 'open-url', url, target });
3531
}
3632
}
3733

38-
document.addEventListener('click', clickHandler);
34+
/**
35+
* Handles keydown events, specifically for Space or Enter keys, on anchor links.
36+
*
37+
* @param {KeyboardEvent} event - The keyboard event triggered by a keydown action.
38+
* @returns {void} - No return value.
39+
*/
40+
function keydownHandler(event) {
41+
if (event.key !== 'Enter' && event.key !== ' ') return;
42+
const url = closestUrl(event);
43+
if (url) {
44+
event.preventDefault();
45+
event.stopImmediatePropagation();
46+
const target = eventToTarget(event, platformName);
47+
dispatch({ kind: 'open-url', url, target });
48+
}
49+
}
50+
51+
document.addEventListener('keydown', keydownHandler);
52+
document.addEventListener('dblclick', dblClickHandler);
53+
3954
return () => {
40-
document.removeEventListener('click', clickHandler);
55+
document.removeEventListener('dblclick', dblClickHandler);
56+
document.removeEventListener('keydown', keydownHandler);
4157
};
42-
}, []);
58+
}, [platformName, dispatch]);
59+
}
60+
61+
/**
62+
* @param {KeyboardEvent|MouseEvent} event
63+
* @return {string|null}
64+
*/
65+
function closestUrl(event) {
66+
if (!(event.target instanceof Element)) return null;
67+
const row = /** @type {HTMLDivElement|null} */ (event.target.closest('[aria-selected]'));
68+
const anchor = /** @type {HTMLAnchorElement|null} */ (row?.querySelector('a[href][data-url]'));
69+
const url = anchor?.dataset.url;
70+
return url || null;
4371
}

special-pages/pages/history/integration-tests/history.page.js

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,12 @@ export class HistoryTestPage {
174174
}
175175

176176
async opensLinks() {
177-
const { page } = this;
178-
const link = page.locator('a[href][data-url]').nth(0);
179-
await link.click();
180-
await link.click({ modifiers: ['Meta'] });
181-
await link.click({ modifiers: ['Shift'] });
182-
await link.click({ button: 'middle' });
177+
const row = this.main().locator('[aria-selected]').nth(0);
178+
await row.dblclick();
179+
await row.dblclick({ modifiers: ['Meta'] });
180+
await row.dblclick({ modifiers: ['Shift'] });
181+
182+
await row.locator('a').click({ button: 'middle', force: true });
183183
await this._opensMainLink();
184184
}
185185
async _opensMainLink() {
@@ -302,19 +302,9 @@ export class HistoryTestPage {
302302
* @param {import('../types/history.ts').DeleteRangeResponse} resp
303303
*/
304304
async menuForHistoryEntry(nth, resp) {
305-
const { page } = this;
306-
307-
const cleanup = this._withDialogHandling(resp);
308-
// console.log(data[0].title);
309305
const data = generateSampleData({ count: this.entries, offset: 0 });
310306
const nthItem = data[nth];
311-
const row = page.getByText(nthItem.title);
312-
await row.hover();
313-
await page.locator(`[data-action="entries_menu"][value=${nthItem.id}]`).click();
314-
315-
const calls = await this.mocks.waitForCallCount({ method: 'entries_menu', count: 1 });
316-
expect(calls[0].payload.params).toStrictEqual({ ids: [nthItem.id] });
317-
cleanup();
307+
await this.menuForMultipleHistoryEntries(nth, [nthItem.id], resp);
318308
}
319309

320310
/**
@@ -329,7 +319,7 @@ export class HistoryTestPage {
329319
// console.log(data[0].title);
330320
const data = generateSampleData({ count: this.entries, offset: 0 });
331321
const nthItem = data[nth];
332-
const row = page.getByText(nthItem.title);
322+
const row = this.main().locator(`[data-history-entry=${nthItem.id}]`);
333323
await row.hover();
334324
await page.locator(`[data-action="entries_menu"][value=${nthItem.id}]`).click();
335325

special-pages/shared/handlers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/**
2-
* @param {MouseEvent} event
2+
* @param {MouseEvent|KeyboardEvent} event
33
* @param {ImportMeta['platform']} platformName
44
* @return {'new-tab' | 'new-window' | 'same-tab'}
55
*/
66
export function eventToTarget(event, platformName) {
77
const isControlClick = platformName === 'macos' ? event.metaKey : event.ctrlKey;
88
if (isControlClick) {
99
return 'new-tab';
10-
} else if (event.shiftKey || event.button === 1 /* middle click */) {
10+
} else if (event.shiftKey || ('button' in event && event.button === 1) /* middle click */) {
1111
return 'new-window';
1212
}
1313
return 'same-tab';

0 commit comments

Comments
 (0)