fix(tasks): open the task by clicking its text, links carved out
The first cut put an open-button overlay over the row, which intercepted every click on the title text — nine smoke tests (and real users) open a task by clicking its text, and the overlay stole those clicks. Make the body a role=button instead, with each link stopping its own click from bubbling: plain text opens the task, a link opens the page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9d4a13c2c2
commit
4103e1b0ca
@@ -101,9 +101,9 @@ test('en URL i en opgave bliver et klikbart link, og resten åbner opgaven', asy
|
||||
await expect(link).toHaveAttribute('href', 'https://example.com/artikel');
|
||||
await expect(link).toHaveAttribute('rel', /noopener/);
|
||||
|
||||
// Clicking anywhere but the link still opens the task — the open-overlay sits
|
||||
// behind the text, the link above it.
|
||||
await row.getByRole('button', { name: /læs https/ }).click();
|
||||
// Clicking the plain part of the title still opens the task; only the link is
|
||||
// carved out. Aimed at the top-left, over "læs", not the URL in the middle.
|
||||
await row.locator('.content').click({ position: { x: 3, y: 3 } });
|
||||
await expect(page.getByRole('complementary', { name: 'Opgave' })).toBeVisible();
|
||||
|
||||
expect(trouble).toEqual([]);
|
||||
|
||||
@@ -101,25 +101,30 @@
|
||||
</button>
|
||||
|
||||
<!-- A URL pasted into the title or the description is clickable, without the
|
||||
text ever having been anything but plain text. The link cannot live inside
|
||||
the open-button — an <a> in a <button> is invalid — so the whole body is a
|
||||
container and the button is an overlay behind it: plain text (below it)
|
||||
still opens the task, links (raised above it) open the page. -->
|
||||
<div class="body">
|
||||
<!-- Opening the detail drawer is the default. `onedit` is for the callers
|
||||
that want the click to mean something else. -->
|
||||
<button
|
||||
class="open"
|
||||
aria-label={task.content}
|
||||
onclick={() => (onedit ? onedit(task) : app.openDetail(task.id))}
|
||||
></button>
|
||||
|
||||
text ever having been anything but plain text. The link is a real <a>, which
|
||||
an <a> in a <button> may not be — so the body is a role=button rather than a
|
||||
button, and each link stops its click from also opening the task. Clicking
|
||||
any plain text still opens it. -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="body"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => (onedit ? onedit(task) : app.openDetail(task.id))}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onedit ? onedit(task) : app.openDetail(task.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span class="content"
|
||||
>{#each linkify(task.content) as run}{#if run.href}<a
|
||||
class="tlink"
|
||||
href={run.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">{run.text}</a
|
||||
rel="noopener noreferrer"
|
||||
onclick={(e) => e.stopPropagation()}>{run.text}</a
|
||||
>{:else}{run.text}{/if}{/each}</span
|
||||
>
|
||||
|
||||
@@ -129,7 +134,8 @@
|
||||
class="tlink"
|
||||
href={run.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">{run.text}</a
|
||||
rel="noopener noreferrer"
|
||||
onclick={(e) => e.stopPropagation()}>{run.text}</a
|
||||
>{:else}{run.text}{/if}{/each}</span
|
||||
>
|
||||
{/if}
|
||||
@@ -327,31 +333,17 @@
|
||||
}
|
||||
|
||||
.body {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
text-align: left;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* The click target for opening the task, stretched behind everything. Plain
|
||||
text and the meta row sit at the base level, so a click on them lands here;
|
||||
only the links are raised above it. */
|
||||
.open {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.open:focus-visible {
|
||||
.body:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
@@ -362,11 +354,9 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
/* Raised above the open-overlay so the link takes the click, while the rest of
|
||||
the line still opens the task. */
|
||||
/* A real link, so it can be middle-clicked, copied and previewed. Its own click
|
||||
is stopped from bubbling, so opening the page does not also open the task. */
|
||||
.tlink {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
|
||||
Reference in New Issue
Block a user