fix(integrationer): en adresse kan læses helt, også på en telefon
Feed'et, mailadressen, krogen, curl-eksemplet, CalDAV-serveren og de to redirect-URI'er lå i skrivebeskyttede <input>. Et input ombryder ikke — det ruller indeni — så på en telefon stoppede adressen midt i kassen, og resten fandtes kun ved at trække inde i feltet. Der var ikke engang et rullepanel at se: feltet så ud som om det indeholdt det, der stod i det. Målt ved 390 px var kassen 306 px og krogens adresse 634. De er nu <output>. Det er stadig et felt, en <label for> kan pege på, så navnene på felterne bliver ved med at høre sammen med dem, og det er tekst, der ombryder. `user-select: all` beholder det ene, inputtet gjorde bedre: ét tryk tager hele adressen. Det samme gjaldt invitationslinket i en delt note, som vises netop når der ikke er nogen post at sende det med — altså når det er det eneste, der findes. Prøven nævner felterne ved navn frem for at forespørge på en klasse. Den, der måler `.field output` og finder nul, består også den dag felterne er lavet om til noget andet klippet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
72637a153a
commit
61f8887319
+38
-7
@@ -4362,19 +4362,25 @@ test('dagens plan står på I dag, og ikke kun som en besked', async ({ page })
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* En kalenderadresse gør ikke siden bredere end telefonen.
|
* Adresserne på integrationssiden kan ses helt på en telefon.
|
||||||
*
|
*
|
||||||
* Adressen på et abonnement er ét ord på et par hundrede tegn uden et mellemrum i,
|
* En URL er ét ord på et par hundrede tegn uden et mellemrum i, og den kan derfor
|
||||||
* og den stod i en liste uden noget, der lod den ombryde. Det, man så, var ikke en
|
* ikke ombryde af sig selv. Det gav to forskellige fejl på den samme side, og
|
||||||
* linje, der løb ud over kanten — det var hele siden, der fik et vandret
|
* prøven her holder øje med dem begge:
|
||||||
* rullepanel, fordi det bredeste element på en side bestemmer sidens bredde. Derfor
|
*
|
||||||
* måler prøven dokumentet og ikke elementet: det er dokumentet, der var galt.
|
* Abonnementets adresse lå i en liste uden noget, der lod den bryde, og det
|
||||||
|
* bredeste element på en side bestemmer sidens bredde — så hele indholdsspalten fik
|
||||||
|
* et vandret rullepanel, ikke bare den ene linje.
|
||||||
|
*
|
||||||
|
* Feed'et, krogen og CalDAV-serveren lå i skrivebeskyttede <input>, som ruller
|
||||||
|
* indeni i stedet for at ombryde. Der var ikke noget rullepanel at se: adressen
|
||||||
|
* stoppede bare midt i kassen, og resten fandtes kun ved at trække inde i feltet.
|
||||||
*
|
*
|
||||||
* Abonnementet kommer fra et svar, prøven selv skriver. Serveren ville skulle hente
|
* Abonnementet kommer fra et svar, prøven selv skriver. Serveren ville skulle hente
|
||||||
* kalenderen for at have et at vise, og en prøve, der går på nettet, er en prøve,
|
* kalenderen for at have et at vise, og en prøve, der går på nettet, er en prøve,
|
||||||
* der fejler en dag af en anden grund end sin egen.
|
* der fejler en dag af en anden grund end sin egen.
|
||||||
*/
|
*/
|
||||||
test('en lang kalenderadresse giver ikke vandret rulning', async ({ page }) => {
|
test('adresserne på integrationssiden kan ses helt på en telefon', async ({ page }) => {
|
||||||
const trouble = watchForTrouble(page);
|
const trouble = watchForTrouble(page);
|
||||||
|
|
||||||
await page.route('**/api/v1/calendar', (route) =>
|
await page.route('**/api/v1/calendar', (route) =>
|
||||||
@@ -4421,5 +4427,30 @@ test('en lang kalenderadresse giver ikke vandret rulning', async ({ page }) => {
|
|||||||
expect(spill.content).toBe(0);
|
expect(spill.content).toBe(0);
|
||||||
expect(spill.past).toBeLessThanOrEqual(0);
|
expect(spill.past).toBeLessThanOrEqual(0);
|
||||||
|
|
||||||
|
// Og adresserne på siden står der helt. De lå i skrivebeskyttede <input>, som
|
||||||
|
// ikke kan ombryde: kassen var 306 px og indholdet 595, så resten af adressen
|
||||||
|
// fandtes kun ved at rulle inde i feltet — hvilket ingen opdager. Et felt, der
|
||||||
|
// er klippet, er et felt, hvor scrollWidth er større end clientWidth, og det er
|
||||||
|
// præcis det, der måles her.
|
||||||
|
//
|
||||||
|
// Felterne står ved navn og ikke som en forespørgsel på en klasse: en prøve, der
|
||||||
|
// måler `.field output` og finder nul, består — også den dag felterne er lavet om
|
||||||
|
// til noget andet klippet. Den skal fejle, hvis et af dem forsvinder.
|
||||||
|
const clipped = await page.evaluate(() =>
|
||||||
|
['feed', 'mail', 'hook', 'hookcurl', 'caldav']
|
||||||
|
.map((id) => {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (!el) return `${id}: findes ikke`;
|
||||||
|
if (el.scrollWidth - el.clientWidth > 1) return `${id}: ${el.scrollWidth} > ${el.clientWidth}`;
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
);
|
||||||
|
expect(clipped).toEqual([]);
|
||||||
|
|
||||||
|
// Selve teksten, ikke kun kassens mål: krogens adresse er den, der skal kunne
|
||||||
|
// læses af, hvis man taster den ind i en genvej på telefonen.
|
||||||
|
await expect(page.locator('#hook')).toHaveText(/^https?:\/\/.+\/inbound\/hook\/.+/);
|
||||||
|
|
||||||
expect(trouble).toEqual([]);
|
expect(trouble).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -151,7 +151,8 @@
|
|||||||
|
|
||||||
.settings :global(.field input),
|
.settings :global(.field input),
|
||||||
.settings :global(.field select),
|
.settings :global(.field select),
|
||||||
.settings :global(.field textarea) {
|
.settings :global(.field textarea),
|
||||||
|
.settings :global(.field output) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--s2) var(--s3);
|
padding: var(--s2) var(--s3);
|
||||||
background: var(--surface-sunken);
|
background: var(--surface-sunken);
|
||||||
@@ -168,6 +169,22 @@
|
|||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Adresserne — feed'et, krogen, CalDAV-serveren — vises i et <output> og ikke i
|
||||||
|
et skrivebeskyttet <input>. Et input kan ikke ombryde: det ruller indeni, så
|
||||||
|
på en telefon står halvdelen af adressen uden for kassen, og der er ingen måde
|
||||||
|
at se resten på. <output> er stadig et felt, en <label for> kan pege på, og det
|
||||||
|
er tekst, der ombryder.
|
||||||
|
|
||||||
|
`user-select: all` er der, fordi det var det eneste, inputtet gjorde bedre: ét
|
||||||
|
tryk tog hele adressen. Uden den skulle man trække hen over fire linjer. */
|
||||||
|
.settings :global(.field output) {
|
||||||
|
display: block;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
user-select: all;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
/* The field error sits under its own input rather than in a summary at the top:
|
/* The field error sits under its own input rather than in a summary at the top:
|
||||||
the server answers with a map of field to message, and putting each one where
|
the server answers with a map of field to message, and putting each one where
|
||||||
it belongs is the whole point of that shape. */
|
it belongs is the whole point of that shape. */
|
||||||
|
|||||||
@@ -460,7 +460,7 @@
|
|||||||
it has to match Google's copy exactly, and the error Google gives
|
it has to match Google's copy exactly, and the error Google gives
|
||||||
when it does not names neither value. Google afviser desuden
|
when it does not names neither value. Google afviser desuden
|
||||||
private IP-adresser over http — det skal være https. -->
|
private IP-adresser over http — det skal være https. -->
|
||||||
<input id="redirect" readonly value={client.redirect_uri} />
|
<output id="redirect">{client.redirect_uri}</output>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onsubmit={saveClient}>
|
<form onsubmit={saveClient}>
|
||||||
@@ -546,7 +546,7 @@
|
|||||||
{#if app.user?.is_admin}
|
{#if app.user?.is_admin}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="calendar-redirect">{t('int.redirectURI')}</label>
|
<label for="calendar-redirect">{t('int.redirectURI')}</label>
|
||||||
<input id="calendar-redirect" class="mono" readonly value={calendar.redirect_uri} />
|
<output id="calendar-redirect" class="mono">{calendar.redirect_uri}</output>
|
||||||
<p class="hint">{t('int.calendarSameClient')}</p>
|
<p class="hint">{t('int.calendarSameClient')}</p>
|
||||||
</div>
|
</div>
|
||||||
{:else if !calendar.has_client}
|
{:else if !calendar.has_client}
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="feed">{t('int.address')}</label>
|
<label for="feed">{t('int.address')}</label>
|
||||||
<input id="feed" class="mono" value={feedURL} readonly />
|
<output id="feed" class="mono">{feedURL}</output>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -681,7 +681,7 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="mail">{t('int.yourAddress')}</label>
|
<label for="mail">{t('int.yourAddress')}</label>
|
||||||
<input id="mail" class="mono" value={mailAddress} readonly />
|
<output id="mail" class="mono">{mailAddress}</output>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -700,7 +700,7 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="hook">{t('int.yourHook')}</label>
|
<label for="hook">{t('int.yourHook')}</label>
|
||||||
<input id="hook" class="mono" value={hookURL} readonly />
|
<output id="hook" class="mono">{hookURL}</output>
|
||||||
<p class="hint">
|
<p class="hint">
|
||||||
{t('int.hookHow')}
|
{t('int.hookHow')}
|
||||||
</p>
|
</p>
|
||||||
@@ -711,8 +711,8 @@
|
|||||||
kroppen bare er den linje, opgaven skal hedde. -->
|
kroppen bare er den linje, opgaven skal hedde. -->
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="hookcurl">{t('int.hookExample')}</label>
|
<label for="hookcurl">{t('int.hookExample')}</label>
|
||||||
<textarea id="hookcurl" class="mono" rows="3" readonly
|
<output id="hookcurl" class="mono"
|
||||||
>{`curl -X POST ${hookURL} \\\n -H "Content-Type: text/plain" \\\n -d "Ring til Anders i morgen p1 #Firma"`}</textarea
|
>{`curl -X POST ${hookURL} \\\n -H "Content-Type: text/plain" \\\n -d "Ring til Anders i morgen p1 #Firma"`}</output
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -732,7 +732,7 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="caldav">{t('int.server')}</label>
|
<label for="caldav">{t('int.server')}</label>
|
||||||
<input id="caldav" class="mono" value={`${location.origin}/caldav/`} readonly />
|
<output id="caldav" class="mono">{`${location.origin}/caldav/`}</output>
|
||||||
<p class="hint">
|
<p class="hint">
|
||||||
{t('int.caldavAuth')}
|
{t('int.caldavAuth')}
|
||||||
</p>
|
</p>
|
||||||
@@ -817,7 +817,7 @@
|
|||||||
color: var(--ink-muted);
|
color: var(--ink-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.setup input[readonly] {
|
.setup output {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-size: var(--text-xs);
|
font-size: var(--text-xs);
|
||||||
color: var(--ink-muted);
|
color: var(--ink-muted);
|
||||||
|
|||||||
@@ -1279,7 +1279,7 @@
|
|||||||
lovet i en besked, der ikke kom af sted. -->
|
lovet i en besked, der ikke kom af sted. -->
|
||||||
<p class="invitelink">
|
<p class="invitelink">
|
||||||
{t('notes.inviteNoMail')}
|
{t('notes.inviteNoMail')}
|
||||||
<input readonly value={shareInviteLink} onclick={(e) => e.target.select()} />
|
<output>{shareInviteLink}</output>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
@@ -2216,11 +2216,23 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invitelink input {
|
/* <output> og ikke et skrivebeskyttet <input>: et input kan ikke ombryde, så et
|
||||||
|
invitationslink stod halvt uden for kassen på en telefon, og resten kunne kun
|
||||||
|
nås ved at rulle inde i feltet. `user-select: all` beholder det, inputtet var
|
||||||
|
god til — ét tryk tager hele linket. */
|
||||||
|
.invitelink output {
|
||||||
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
font-size: var(--text-xs);
|
|
||||||
padding: 2px var(--s1);
|
padding: 2px var(--s1);
|
||||||
|
background: var(--surface-sunken);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
color: var(--ink);
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
user-select: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addshare {
|
.addshare {
|
||||||
|
|||||||
Reference in New Issue
Block a user