fix(mobile): the project header wraps instead of crushing the title
On a phone, a project called "Skovvænget" rendered as a vertical column of single letters while the buttons ran off the right edge. It looked like a broken font. The header is a flex row of a title and four controls, and 390px does not have room for them. Nothing wrapped, so the heading was shrunk to its min-content — which with `overflow-wrap: anywhere` is one character. Adding Historik this evening is what tipped it over. It wraps now, and the title keeps a floor of 8ch so flex stops squeezing at the point where wrapping is the better answer. Kommende had the same shape and gets the same fix. Every other smoke test runs at desktop width, which is exactly why this shipped. There is one at 390 now: it measures the heading's box — wide enough to hold a word, short enough not to be a column of letters — and checks the page cannot be scrolled sideways. Also stops an empty project saying both "Uden sektion" and "Ingenting her endnu": that label is a heading for the tasks outside a section, and with no sections there is nothing for them to be outside of. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b0ab6ee5c0
commit
5a9d7a98e9
@@ -804,6 +804,46 @@ test('en rolle kan rettes uden at fjerne personen', async ({ browser, page }) =>
|
||||
expect(trouble).toEqual([]);
|
||||
});
|
||||
|
||||
/**
|
||||
* The project page on a phone.
|
||||
*
|
||||
* Every other test here runs at desktop width, and this is what that misses: the
|
||||
* header is a flex row of a title and four controls, and 390px does not have room
|
||||
* for them. Without wrapping, the heading was squeezed to its min-content — which
|
||||
* with `overflow-wrap: anywhere` is one character — so a project called
|
||||
* "Skovvænget" rendered as a vertical column of letters while the buttons ran off
|
||||
* the right edge. It looked like a broken font.
|
||||
*/
|
||||
test('projektsiden er brugbar på en telefon', async ({ page }) => {
|
||||
const trouble = watchForTrouble(page);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto('/');
|
||||
|
||||
// The sidebar is a drawer at this width, so the project is reached through it.
|
||||
await page.getByRole('button', { name: 'Vis menu' }).click();
|
||||
const sidebar = page.getByRole('navigation', { name: 'Hovedmenu' });
|
||||
await sidebar.getByRole('button', { name: 'Nyt projekt' }).click();
|
||||
await sidebar.getByLabel('Projektnavn').fill('Skovvænget');
|
||||
await sidebar.getByLabel('Projektnavn').press('Enter');
|
||||
|
||||
const heading = page.getByRole('heading', { name: 'Skovvænget' });
|
||||
await expect(heading).toBeVisible();
|
||||
|
||||
const box = await heading.boundingBox();
|
||||
// Wide enough to hold the word, and short enough not to be a column of
|
||||
// letters. One line of "Skovvænget" at --text-2xl is nowhere near 200px tall.
|
||||
expect(box.width, 'overskriften er klemt sammen').toBeGreaterThan(120);
|
||||
expect(box.height, 'overskriften er brudt op i én bogstavkolonne').toBeLessThan(120);
|
||||
|
||||
// And nothing sticks out past the right edge of the page.
|
||||
const overflow = await page.evaluate(
|
||||
() => document.documentElement.scrollWidth - document.documentElement.clientWidth
|
||||
);
|
||||
expect(overflow, 'siden kan scrolles vandret').toBeLessThanOrEqual(1);
|
||||
|
||||
expect(trouble).toEqual([]);
|
||||
});
|
||||
|
||||
/**
|
||||
* The MCP connector address must not fall through to the app shell.
|
||||
*
|
||||
|
||||
@@ -548,7 +548,10 @@
|
||||
ondrop={(e) => onSectionDrop(e, '')}
|
||||
>
|
||||
<TaskList tasks={unsectioned} projectId={project.id} sectionId="" {canEdit} />
|
||||
{#if !unsectioned.length}
|
||||
<!-- Only once there are sections to be "without": on a project with none,
|
||||
the label is a heading for the only list there is, and the empty
|
||||
state below already says the project is empty. -->
|
||||
{#if sections.length && !unsectioned.length}
|
||||
<p class="empty">Uden sektion</p>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -664,17 +667,28 @@
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
/* Wraps, and the title keeps a floor.
|
||||
*
|
||||
* Without both, a phone squeezes the heading to its min-content — which with
|
||||
* `overflow-wrap: anywhere` is *one character*, so "Skovvænget" came out as a
|
||||
* vertical column of letters while the buttons ran off the right edge. Four
|
||||
* controls beside a title is more than 390px has, so they belong on the next
|
||||
* line rather than in a race for the same one.
|
||||
*/
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s4);
|
||||
gap: var(--s3);
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: var(--s5);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--text-2xl);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
/* 8ch rather than 0: the basis is what stops flex shrinking it past the
|
||||
point where the wrapping below is the better answer. */
|
||||
flex: 1 1 8ch;
|
||||
min-width: 8ch;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@@ -694,8 +708,8 @@
|
||||
|
||||
/* Matches the heading it stands in for, so the swap does not move the page. */
|
||||
.title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex: 1 1 8ch;
|
||||
min-width: 8ch;
|
||||
font-size: var(--text-2xl);
|
||||
font-weight: 560;
|
||||
letter-spacing: -0.015em;
|
||||
|
||||
@@ -135,17 +135,21 @@
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
/* Wraps, for the same reason the project header does: a title and a switcher
|
||||
are more than a phone's width, and the switcher belongs underneath rather
|
||||
than squeezing the heading. */
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s4);
|
||||
gap: var(--s3);
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: var(--s5);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--text-2xl);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex: 1 1 8ch;
|
||||
min-width: 8ch;
|
||||
}
|
||||
|
||||
.views {
|
||||
|
||||
Reference in New Issue
Block a user