Merge pull request #83 from kristianwind/opgavens-tekst-kan-laeses
fix(opgave): teksten kan læses færdig, også på en telefon
This commit is contained in:
@@ -4755,3 +4755,69 @@ test('et indsat billede kan åbnes stort og kopieres', async ({ page, context })
|
|||||||
|
|
||||||
expect(trouble).toEqual([]);
|
expect(trouble).toEqual([]);
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* En opgaves tekst kan læses færdig, også på en telefon.
|
||||||
|
*
|
||||||
|
* Rapporteret fra en telefon, med et billede af en beskrivelse, der manglede sit
|
||||||
|
* første bogstav på hver linje: "hermostat RU0910301440" og "atteries". Det er
|
||||||
|
* ikke manglende tegn — det er et felt, der er rullet til højre. En beskrivelse er
|
||||||
|
* tit et klippet stykke mail med en URL i, et <textarea> ombryder ved mellemrum,
|
||||||
|
* og en URL har ingen.
|
||||||
|
*
|
||||||
|
* Og titlen var et <input>, som ikke kan ombryde overhovedet. "tado°: Your Smart
|
||||||
|
* Thermostat needs new batteries" er en almindelig opgavetitel, og halvdelen af
|
||||||
|
* den stod uden for kanten.
|
||||||
|
*/
|
||||||
|
test('en opgaves tekst kan læses færdig på en telefon', async ({ page }) => {
|
||||||
|
const trouble = watchForTrouble(page);
|
||||||
|
await page.setViewportSize({ width: 390, height: 844 });
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
const langTitel = 'tado°: Your Smart Thermostat needs new batteries';
|
||||||
|
// "i dag" for at den lander på siden, prøven står på — parseren klipper det ud
|
||||||
|
// af titlen igen, så den hedder præcis det, den skal.
|
||||||
|
await page.getByLabel('Ny opgave').fill(`${langTitel} i dag`);
|
||||||
|
await page.getByLabel('Ny opgave').press('Enter');
|
||||||
|
await page.getByText('tado°', { exact: false }).first().click();
|
||||||
|
|
||||||
|
const titel = page.getByLabel('Opgavens tekst');
|
||||||
|
await expect(titel).toBeVisible();
|
||||||
|
await expect(titel).toHaveValue(langTitel);
|
||||||
|
|
||||||
|
// Beskrivelsen: en URL uden et eneste mellemrum at bryde ved.
|
||||||
|
const url = 'https://link.tado.com/emails/8/ttid/1-a37f2c9b4e6d8a1f0c3b5e7d9a2f4c6b8e0d1a3f5c7b9e2d4a6f8c0b3e5d7a9f';
|
||||||
|
const beskrivelse = page.getByLabel('Beskrivelse');
|
||||||
|
await beskrivelse.fill(`Thermostat RU0910301440\nBatteries\n${url}`);
|
||||||
|
await beskrivelse.blur();
|
||||||
|
|
||||||
|
// Målt frem for beskrevet: et felt, der ruller vandret, har en scrollWidth
|
||||||
|
// større end sin clientWidth. Det var dét, der klippede venstre kant.
|
||||||
|
//
|
||||||
|
// Felterne måles gennem deres egne lokatorer og ikke med en forespørgsel på
|
||||||
|
// aria-label: beskrivelsen har et <label for>, ikke et aria-label, så en
|
||||||
|
// forespørgsel fandt ingenting og ville have bestået tomt.
|
||||||
|
const spild = [];
|
||||||
|
for (const [navn, felt] of [
|
||||||
|
['Opgavens tekst', titel],
|
||||||
|
['Beskrivelse', beskrivelse]
|
||||||
|
]) {
|
||||||
|
const m = await felt.evaluate((el) => ({ ind: el.scrollWidth, ude: el.clientWidth }));
|
||||||
|
if (m.ind - m.ude > 1) spild.push(`${navn}: ${m.ind} > ${m.ude}`);
|
||||||
|
}
|
||||||
|
expect(spild).toEqual([]);
|
||||||
|
|
||||||
|
// Beskrivelsen løber ikke over i Chromium, heller ikke uden reglen — det var
|
||||||
|
// Safari, den blev set i, og det er en anden motor. Prøven måler derfor reglen
|
||||||
|
// selv frem for dens virkning. Det er svagere, og det står her, så den næste
|
||||||
|
// ved det: den vogter mod at reglen bliver slettet, ikke mod at den er forkert.
|
||||||
|
await expect(beskrivelse).toHaveCSS('overflow-wrap', 'break-word');
|
||||||
|
|
||||||
|
// Og titlen er blevet høj nok til at rumme sig selv frem for at klippe.
|
||||||
|
const linjer = await titel.evaluate((el) => ({
|
||||||
|
hoejde: el.clientHeight,
|
||||||
|
linje: parseFloat(getComputedStyle(el).lineHeight)
|
||||||
|
}));
|
||||||
|
expect(linjer.hoejde).toBeGreaterThan(linjer.linje * 1.5);
|
||||||
|
|
||||||
|
expect(trouble).toEqual([]);
|
||||||
|
});
|
||||||
|
|||||||
@@ -171,6 +171,32 @@
|
|||||||
* took is said out loud, because a title that edits itself without a word is
|
* took is said out loud, because a title that edits itself without a word is
|
||||||
* worse than one that does nothing.
|
* worse than one that does nothing.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Lader et felt være lige så højt som sin tekst.
|
||||||
|
*
|
||||||
|
* `height: auto` først, og så `scrollHeight`: uden nulstillingen kan feltet kun
|
||||||
|
* vokse, for scrollHeight på et felt, der allerede er for højt, er dets egen
|
||||||
|
* højde.
|
||||||
|
*/
|
||||||
|
function grow(node) {
|
||||||
|
const fit = () => {
|
||||||
|
node.style.height = 'auto';
|
||||||
|
node.style.height = `${node.scrollHeight}px`;
|
||||||
|
};
|
||||||
|
fit();
|
||||||
|
node.addEventListener('input', fit);
|
||||||
|
// Bredden skifter, når ruden gør — og en tekst, der fyldte to linjer, fylder
|
||||||
|
// tre på en smallere skærm uden at der er rørt ved den.
|
||||||
|
const watch = new ResizeObserver(fit);
|
||||||
|
watch.observe(node);
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('input', fit);
|
||||||
|
watch.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function saveContent() {
|
async function saveContent() {
|
||||||
if (!task) return;
|
if (!task) return;
|
||||||
const trimmed = content.trim();
|
const trimmed = content.trim();
|
||||||
@@ -418,13 +444,27 @@
|
|||||||
|
|
||||||
<div class="scroll">
|
<div class="scroll">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input
|
<!-- Et felt frem for en linje. En opgavetitel er tit længere end en telefon
|
||||||
|
er bred — "tado°: Your Smart Thermostat needs new batteries" er en
|
||||||
|
almindelig en — og et <input> kan ikke ombryde: det ruller, og så står
|
||||||
|
halvdelen af overskriften uden for kanten. -->
|
||||||
|
<textarea
|
||||||
class="title"
|
class="title"
|
||||||
|
rows="1"
|
||||||
use:focusOnMount
|
use:focusOnMount
|
||||||
|
use:grow
|
||||||
bind:value={content}
|
bind:value={content}
|
||||||
onblur={saveContent}
|
onblur={saveContent}
|
||||||
|
onkeydown={(e) => {
|
||||||
|
// Retur gemmer frem for at lave en ny linje. En titel er én linje,
|
||||||
|
// uanset hvor mange den fylder på skærmen.
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
aria-label={t('detail.text')}
|
aria-label={t('detail.text')}
|
||||||
/>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -894,6 +934,16 @@
|
|||||||
resize: vertical;
|
resize: vertical;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
/* En beskrivelse er tit et klippet stykke mail med en URL i. Et <textarea>
|
||||||
|
ombryder ved mellemrum, og en URL har ingen — så feltet rullede vandret,
|
||||||
|
og på en telefon stod teksten forskudt med venstre kant klippet af. Det
|
||||||
|
ser ikke ud som rulning; det ser ud som manglende bogstaver.
|
||||||
|
|
||||||
|
`break-word` og ikke `anywhere`: den første bryder kun et ord, der ikke kan
|
||||||
|
stå på en linje alene, mens den anden bryder ved først givne lejlighed og
|
||||||
|
delte "GarageRisteriet" midt i en stavelse. Se h1 på projektets side — den
|
||||||
|
lektion er betalt én gang. */
|
||||||
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The title is the one field that should not look like a field until you are in
|
/* The title is the one field that should not look like a field until you are in
|
||||||
@@ -905,6 +955,10 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
/* Højden sættes af `grow`, så håndtaget ville love noget, det ikke kan
|
||||||
|
holde: trækker man i det, retter det næste bogstav det tilbage. */
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title:hover {
|
.title:hover {
|
||||||
|
|||||||
Reference in New Issue
Block a user