fix(cooking-session): premier passage responsive dédié à l'écran de cuisine (#89)
Some checks failed
Some checks failed
cooking-session-page.scss n'avait encore aucune règle @media. Audit dédié à cet écran (minuteur inexistant sur cette page — rien à faire de ce côté, liste d'étapes, actions) : - Bug d'overflow réel dans l'onglet Recettes : `.cooking-recipes__name` n'avait aucune borne dans sa ligne flex (contrairement au `.recipe-chip__name` équivalent côté planning) — un nom de recette long pouvait repousser le badge de portions et le chevron hors de la case (silencieusement coupés par le `overflow: hidden` de la carte parente). Corrigé avec le même traitement flex:1/min-width:0/ ellipsis que son équivalent. - Cibles tactiles sous le breakpoint phone : les cartes de tâches (`. cooking-task`, l'action principale et répétée de tout l'onglet Plan), les nœuds du stepper, les onglets, la ligne de l'onglet Recettes et les boutons Précédent/Suivant passent tous à --space-md pour dépasser confortablement les 48dp minimums d'Android (l'app est Android-only) même dans le pire cas (une tâche à une seule ligne, sans ustensiles). - Piège rencontré : surcharger `.cooking-task` par un simple sélecteur de classe ne fonctionnait pas silencieusement — la règle globale partagée `label:has(> input[type="checkbox"])` (global.scss) a une spécificité CSS plus élevée qu'il n'y paraît (l'argument de :has() compte comme un élément + un attribut). Corrigé en réutilisant le même niveau d'imbrication que `.cooking-phase__tasks .cooking-task --done` juste au-dessus dans le même fichier, qui contournait déjà ce problème pour la même raison. Couverture Cypress : nouveau describe "Cooking session page (phone)" dans cooking-session-page.cy.ts (2 tests — padding effectivement appliqué + troncature du nom de recette sans pousser le badge hors champ). Suite complète : biome check, tsc -b --force, build, 146/146 e2e, 71/71 composants — tous verts. Closes #89 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
23bc19b482
commit
2b5ae6722a
2 changed files with 143 additions and 0 deletions
|
|
@ -3,6 +3,8 @@
|
|||
// `GET /cooking-session` behavior (including the optimizer) against a real
|
||||
// database.
|
||||
|
||||
import { PHONE_VIEWPORT } from "../support/viewports";
|
||||
|
||||
const authenticatedProfile = {
|
||||
id: 1,
|
||||
firstName: "Alice",
|
||||
|
|
@ -421,3 +423,72 @@ describe("Cooking session page", () => {
|
|||
cy.contains("Impossible de charger").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
// Ticket #89 — this page's first dedicated responsive pass (it previously
|
||||
// had no `@media` rule at all, see cooking-session-page.scss's own header
|
||||
// comment). No layout is restructured on phone (the flex-column shell and
|
||||
// the two horizontally-scrolling rows already degrade safely) — this covers
|
||||
// the two things that actually needed fixing: comfortable touch targets on
|
||||
// the screen's own controls, and a real flex overflow bug in the Recettes
|
||||
// tab (a long recipe name had no upper bound and could crowd its portions
|
||||
// badge/chevron out of the row).
|
||||
describe("Cooking session page (phone)", () => {
|
||||
beforeEach(() => {
|
||||
cy.setViewport("phone");
|
||||
cy.clock(TODAY, ["Date"]);
|
||||
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
|
||||
});
|
||||
|
||||
it("gives the task cards and stepper nodes a taller, more tappable padding than on desktop", () => {
|
||||
cy.intercept("GET", /\/cooking-session\?/, { statusCode: 200, body: planFixture() });
|
||||
cy.visit("/cuisiner?date=2026-08-17");
|
||||
|
||||
cy.get(".cooking-task")
|
||||
.first()
|
||||
.should(($el) => {
|
||||
// --space-md (16px) on phone vs. the shared checkbox-label rule's
|
||||
// own --space-xs (4px) app-wide default — see cooking-session-page
|
||||
// .scss's phone block.
|
||||
expect(getComputedStyle($el[0]).paddingTop).to.equal("16px");
|
||||
});
|
||||
cy.get(".cooking-stepper__node")
|
||||
.first()
|
||||
.should(($el) => {
|
||||
expect(getComputedStyle($el[0]).paddingTop).to.equal("16px");
|
||||
});
|
||||
|
||||
// The document itself never scrolls sideways, same global rule every
|
||||
// other page is held to.
|
||||
cy.document()
|
||||
.its("documentElement.scrollWidth")
|
||||
.should("be.lte", PHONE_VIEWPORT.width + 1);
|
||||
});
|
||||
|
||||
it("truncates a long recipe name in the Recettes tab instead of pushing its portions badge out of view", () => {
|
||||
const longName =
|
||||
"Un très long nom de recette qui ne devrait surtout pas pousser le badge de portions hors de l'écran";
|
||||
cy.intercept("GET", /\/cooking-session\?/, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
startDate: "2026-08-17T00:00:00.000Z",
|
||||
finishDate: "2026-08-23T00:00:00.000Z",
|
||||
recipes: [{ recipeId: 1, name: longName, portions: 4 }],
|
||||
phases: [],
|
||||
},
|
||||
});
|
||||
cy.visit("/cuisiner?date=2026-08-17");
|
||||
cy.contains(".cooking-session-page__tab", "Recettes").click();
|
||||
|
||||
cy.get(".cooking-recipes__name").should(($el) => {
|
||||
const s = getComputedStyle($el[0]);
|
||||
expect(s.textOverflow).to.equal("ellipsis");
|
||||
expect(s.whiteSpace).to.equal("nowrap");
|
||||
// The name's own box is clipped narrower than its full text — proof
|
||||
// the truncation is actually engaged, not just declared.
|
||||
expect($el[0].scrollWidth).to.be.greaterThan($el[0].clientWidth);
|
||||
});
|
||||
// ...and the badge survives, still on screen, not clipped away by the
|
||||
// card's own `overflow: hidden`.
|
||||
cy.contains(".cooking-recipes__portions", "4 portions").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
// on the theme.
|
||||
// =============================================================================
|
||||
|
||||
@use "../../styles/breakpoints" as bp;
|
||||
|
||||
.cooking-session-page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
|
@ -134,12 +136,24 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
// `flex: 1; min-width: 0` + truncation — without it, a long recipe name
|
||||
// has no upper bound in this flex row and can crowd the portions badge
|
||||
// and chevron off to the side (or past the card's own `overflow: hidden`
|
||||
// on `&__item`, silently clipping them) instead of the name itself
|
||||
// truncating. Same fix as `.recipe-chip__name` (planning-page.scss) for
|
||||
// the identical shape of bug.
|
||||
&__name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&__portions {
|
||||
flex-shrink: 0;
|
||||
padding: 0.1rem var(--space-sm);
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--color-surface-alt);
|
||||
|
|
@ -486,3 +500,61 @@
|
|||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Phone (issue #89, first responsive pass) -------------------------------
|
||||
// Nothing here reorganizes the layout — the flex-column shell and the two
|
||||
// horizontally-scrolling rows (`&__tabs`, `.cooking-stepper`, both the same
|
||||
// generic `overflow-x: auto` pattern used throughout the app) already
|
||||
// degrade safely on a narrow screen, and every text block wraps within its
|
||||
// own flex/block container (no fixed widths). What was actually missing:
|
||||
// comfortable touch targets on this screen's own controls — the task cards
|
||||
// especially, since ticking one off is THE primary, repeated, one-handed
|
||||
// action of the whole "Plan" tab. Same audit `planning-page.scss` got in
|
||||
// #88, applied here to this page's own elements.
|
||||
@include bp.phone {
|
||||
// The task card is a shared "selectable card" checkbox label
|
||||
// (global.scss's `label:has(> input[type="checkbox"])`, padding
|
||||
// `--space-xs --space-sm` app-wide — fine with a mouse, too tight to tap
|
||||
// reliably mid-cooking). `--space-md` all around clears Android's 48dp
|
||||
// minimum touch target (this app is Android-only, see the root README)
|
||||
// even in the worst case — a one-line headline with no utensils and no
|
||||
// "Mutualisé" badge (~25px of text): 25 + 2×16 = 57px. Bumped here, not
|
||||
// in the global rule: this page's task cards are the one place site-wide
|
||||
// where quick, repeated tapping actually matters. Scoped under
|
||||
// `.cooking-phase__tasks` (not a bare `.cooking-task` selector) for the
|
||||
// same specificity reason as `&__tasks .cooking-task--done` above: a
|
||||
// plain single class loses to `label:has(> input[type="checkbox"])`'s
|
||||
// own specificity (a `:has()` argument counts its inner element +
|
||||
// attribute), so it silently never applied without this.
|
||||
.cooking-phase__tasks .cooking-task {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
// Same reasoning for the stepper's own nodes: the enlarged 1.75rem
|
||||
// (28px) marker below is this node's tallest element, so `--space-md`
|
||||
// padding puts its total height at 28 + 2×16 = 60px, comfortably past
|
||||
// 48dp rather than landing exactly on it.
|
||||
.cooking-stepper__node {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
.cooking-stepper__marker {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
|
||||
// Tab bar, Recettes summary row, Prev/Next — same treatment, so every
|
||||
// tappable control on this page ends up consistently comfortable rather
|
||||
// than fixing only the one most obviously broken.
|
||||
.cooking-session-page__tab {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
.cooking-recipes__row {
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
.cooking-plan__nav-btn {
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue