[FIX] event header: a lone warning shows its action without scrolling
A four-line warning with its action needed 132 px in the header's side zone, capped at 7.25rem, so « Ouvrir les tables » hid below the fold. A zone holding a single warning now grows to 10rem, five lines plus the action; several warnings keep the lower cap, so the plan loses no more. Checked: 1, 4 and 13 missing places at 1366 and 1024 px, the action inside the visible zone; node 4012, browser 872. --- FR --- [FIX] en-tête : un avis seul montre son action sans défilement Un avis de quatre lignes et son action demandaient 132 px dans la zone latérale de l'en-tête, bornée à 7,25rem : « Ouvrir les tables » restait caché. Une zone qui ne porte qu'un avis monte à 10rem, cinq lignes et l'action ; plusieurs avis gardent la borne basse, le plan ne perd rien de plus. Vérifié : 1, 4 et 13 places manquantes à 1366 et 1024 px, l'action dans la zone visible ; node 4012, navigateur 872. Assisted-by: Claude Opus 5.5
This commit is contained in:
parent
6f7cd3511b
commit
16a7abbedc
2 changed files with 65 additions and 29 deletions
|
|
@ -404,6 +404,17 @@
|
|||
padding-block: 0.125rem;
|
||||
}
|
||||
|
||||
/* Une zone qui ne porte qu'un message — un seul enfant, qui n'en a qu'un
|
||||
lui-même — n'a rien à faire défiler pour en montrer d'autres : son
|
||||
plafond, 10rem, tient entier un avertissement de cinq lignes de texte
|
||||
et son action dessous. Le plafond de plusieurs messages reste celui
|
||||
ci-dessus, et la surface garde sa hauteur devant des avertissements
|
||||
nombreux. Ces enfants viennent d'un autre composant : :global les
|
||||
soustrait à la portée des styles de celui-ci. */
|
||||
.place > .annonces:has(> :global(:only-child > :only-child)) {
|
||||
max-height: 10rem;
|
||||
}
|
||||
|
||||
/* Seules — sans tête, en plein écran, ou devant un support rompu —, les
|
||||
annonces ne prennent jamais plus du quart du cadre, et cèdent d'abord
|
||||
leur hauteur à la surface : leur facteur de rétrécissement, mille fois
|
||||
|
|
|
|||
|
|
@ -133,6 +133,17 @@ async function enManque(m) {
|
|||
flushSync();
|
||||
}
|
||||
|
||||
// L'événement passé en écriture, une table de trois places créée, puis
|
||||
// trois personnes de plus que de places : le manque donné, à chaque tour.
|
||||
async function enManqueDe(m, manque) {
|
||||
await m.seance.passerEnEcriture();
|
||||
await m.seance.executer('creerTable', { forme: 'ronde' });
|
||||
for (let n = 0; n < EVENEMENT.siegesParDefaut + manque; n += 1) {
|
||||
await m.seance.executer('ajouterParticipant', { champs: { nom: `${NOMS[n % NOMS.length]} ${n + 1}` } });
|
||||
}
|
||||
flushSync();
|
||||
}
|
||||
|
||||
// --- Les épreuves -----------------------------------------------------------------
|
||||
|
||||
describe('la bande de mode (§ 8.4)', () => {
|
||||
|
|
@ -436,37 +447,51 @@ describe('les avertissements, en messages d’avis', () => {
|
|||
// Le texte de l'avertissement et son action ne sont jamais rognés : ni
|
||||
// le texte, ni le message, ni aucune boîte qui les porte jusqu'au cadre
|
||||
// ne déborde de sa hauteur, et chacun tient dans la boîte visible de
|
||||
// chaque ascendant qui coupe ce qui le dépasse.
|
||||
// chaque ascendant qui coupe ce qui le dépasse, la zone des annonces la
|
||||
// première. Le manque d'une place, de quatre et de treize : le nombre et
|
||||
// l'accord allongent la phrase.
|
||||
for (const largeur of [1366, 1024]) {
|
||||
test(`à ${largeur} × 700, le texte de PLACES_MANQUANTES et « Ouvrir les tables » se lisent en entier : aucune boîte ne les rogne jusqu’au cadre`, async () => {
|
||||
await fixerCadre(largeur, 700);
|
||||
const m = await neuf();
|
||||
await enManque(m);
|
||||
const message = await attendre(() => {
|
||||
const element = dans(m, '[data-annonces] [data-avertissement="PLACES_MANQUANTES"]');
|
||||
expect(element).not.toBeNull();
|
||||
return element;
|
||||
});
|
||||
const texte = message.querySelector('p');
|
||||
const action = message.querySelector('[data-commande]');
|
||||
expect(texte.textContent).toBe(t('PLACES_MANQUANTES', { placesManquantes: 1 }));
|
||||
expect(action).not.toBeNull();
|
||||
const borne = cadre(m);
|
||||
for (let element = texte; element !== borne.parentElement; element = element.parentElement) {
|
||||
const nom = `${element.tagName.toLowerCase()}${element.hasAttribute('data-annonces') ? '[data-annonces]' : ''}`;
|
||||
expect(element.scrollHeight, `${nom} : hauteur du contenu`).toBeLessThanOrEqual(element.clientHeight + 1);
|
||||
if (element === texte || style(element).overflowY === 'visible') continue;
|
||||
const b = boite(element);
|
||||
const bas = b.top + element.clientTop + element.clientHeight;
|
||||
for (const [lu, porte] of [['texte', texte], ['action', action]]) {
|
||||
expect(boite(porte).top, `${lu} dans ${nom}`).toBeGreaterThanOrEqual(b.top + element.clientTop - 0.5);
|
||||
expect(boite(porte).bottom, `${lu} dans ${nom}`).toBeLessThanOrEqual(bas + 0.5);
|
||||
for (const manque of [1, 4, 13]) {
|
||||
test(`à ${largeur} × 700, il manque ${manque} : le texte de PLACES_MANQUANTES et le bouton « Ouvrir les tables » se lisent en entier, sans défiler`, async () => {
|
||||
await fixerCadre(largeur, 700);
|
||||
const m = await neuf();
|
||||
await enManqueDe(m, manque);
|
||||
const message = await attendre(() => {
|
||||
const element = dans(m, '[data-annonces] [data-avertissement="PLACES_MANQUANTES"]');
|
||||
expect(element).not.toBeNull();
|
||||
return element;
|
||||
});
|
||||
const texte = message.querySelector('p');
|
||||
const action = message.querySelector('button[data-commande]');
|
||||
expect(texte.textContent).toBe(t('PLACES_MANQUANTES', { placesManquantes: manque }));
|
||||
expect(action).not.toBeNull();
|
||||
expect(action.textContent.trim()).toBe(t('communs.remede.ouvrirSection.tables'));
|
||||
// La zone des annonces ne défile pas, et le bouton tient dans sa
|
||||
// boîte visible.
|
||||
const zone = message.closest('[data-annonces]');
|
||||
expect(zone.scrollHeight, 'zone : hauteur du contenu').toBeLessThanOrEqual(zone.clientHeight + 1);
|
||||
expect(zone.scrollTop).toBe(0);
|
||||
const z = boite(zone);
|
||||
const a = boite(action);
|
||||
expect(a.top, 'bouton sous le haut de la zone').toBeGreaterThanOrEqual(z.top + zone.clientTop - 0.5);
|
||||
expect(a.bottom, 'bouton au-dessus du bas visible de la zone').toBeLessThanOrEqual(z.top + zone.clientTop + zone.clientHeight + 0.5);
|
||||
const borne = cadre(m);
|
||||
for (let element = texte; element !== borne.parentElement; element = element.parentElement) {
|
||||
const nom = `${element.tagName.toLowerCase()}${element.hasAttribute('data-annonces') ? '[data-annonces]' : ''}`;
|
||||
expect(element.scrollHeight, `${nom} : hauteur du contenu`).toBeLessThanOrEqual(element.clientHeight + 1);
|
||||
if (element === texte || style(element).overflowY === 'visible') continue;
|
||||
const b = boite(element);
|
||||
const bas = b.top + element.clientTop + element.clientHeight;
|
||||
for (const [lu, porte] of [['texte', texte], ['bouton', action]]) {
|
||||
expect(boite(porte).top, `${lu} dans ${nom}`).toBeGreaterThanOrEqual(b.top + element.clientTop - 0.5);
|
||||
expect(boite(porte).bottom, `${lu} dans ${nom}`).toBeLessThanOrEqual(bas + 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(visible(action)).toBe(true);
|
||||
expect(document.documentElement.scrollHeight).toBeLessThanOrEqual(innerHeight);
|
||||
expect(document.documentElement.scrollWidth).toBeLessThanOrEqual(innerWidth);
|
||||
});
|
||||
expect(visible(action)).toBe(true);
|
||||
expect(document.documentElement.scrollHeight).toBeLessThanOrEqual(innerHeight);
|
||||
expect(document.documentElement.scrollWidth).toBeLessThanOrEqual(innerWidth);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue