From c4c2c2ae39c9323c924152a5832a17caebd2b724 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 8 Sep 2026 01:33:37 +0200 Subject: [PATCH] =?UTF-8?q?fix(planning):=20la=20grille=20remplit=20l'?= =?UTF-8?q?=C3=A9cran,=20une=20case=20ne=20grandit=20plus=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `table.planning-grid` a `height: 100%` mais des hauteurs de ligne pilotées par le contenu : un créneau avec plusieurs recettes étirait son ``, donc toute la ligne (les autres cases gagnant la même hauteur morte sous `vertical-align: top`), puis la table débordait de `.planning-grid-wrapper` en scroll vertical. - `tbody tr { height: 1px }` : les cinq lignes se partagent la hauteur du wrapper à parts égales (une hauteur de ligne est un minimum que le navigateur étire pour absorber la hauteur restante de la table), au lieu d'être aussi hautes que la colonne la plus chargée. - `.meal-cell__recipes` : `max-height: 8.5rem; overflow-y: auto` — au-delà d'une poignée de recettes la liste scrolle dans la case. Cap en `rem` et pas en `%` : les hauteurs en pourcentage sur un `` ne se résolvent pas de façon fiable selon le moteur. `.add-recipe-btn` (frère flex hors de cette boîte) reste épinglé juste en dessous. - `min-width: 62rem` laissé tel quel : les 7 colonnes ont besoin d'un plancher pour rester lisibles, et le scroll horizontal dessous est couvert par `layout.cy.ts`. Le breakpoint phone (vue `PlanningDayView`) n'est pas concerné. Closes #110 Co-Authored-By: Claude Sonnet 5 --- apps/web/cypress/e2e/planning-page.cy.ts | 43 +++++++++++++++++++ .../web/src/pages/planning/planning-page.scss | 32 ++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/apps/web/cypress/e2e/planning-page.cy.ts b/apps/web/cypress/e2e/planning-page.cy.ts index b3c23df..f63bb63 100644 --- a/apps/web/cypress/e2e/planning-page.cy.ts +++ b/apps/web/cypress/e2e/planning-page.cy.ts @@ -182,6 +182,49 @@ describe("Planning grid", () => { cy.contains("Semaine du 10 au 16 août 2026").should("be.visible"); }); + // Issue #110 — a slot with many recipes used to stretch its ``, and + // with it the whole row and the table, past `.planning-grid-wrapper` + // into a vertical scroll (the other cells of that row just gaining dead + // space). The chip list now scrolls inside its own cell instead. + it("keeps a heavy slot's recipes scrolling inside the cell, not stretching the grid", () => { + cy.intercept("GET", /\/planning\?/, { + statusCode: 200, + body: { + id: 1, + startDate: "2026-08-17T00:00:00.000Z", + finishDate: "2026-08-23T00:00:00.000Z", + items: Array.from({ length: 14 }, (_, i) => ({ + id: i + 1, + weekDay: "lundi", + meal: "dejeuner", + portions: 2, + recipe: { id: i + 1, name: `Recette ${i + 1}` }, + })), + }, + }); + + cy.visit("/"); + cy.get(".recipe-chip").should("have.length", 14); + + // The busy cell's own recipe list is now the scroller: it's capped and + // scrolls its own overflow instead of stretching the `` (and with + // it the whole row, then the table past `.planning-grid-wrapper` into a + // tall vertical scroll). + cy.get(".meal-cell__recipes").should(($lists) => { + const list = [...$lists].find( + (el) => el.querySelectorAll(".recipe-chip").length === 14, + ) as HTMLElement; + expect(list, "the 14-recipe cell's list").to.exist; + expect(getComputedStyle(list).overflowY).to.be.oneOf(["auto", "scroll"]); + expect(list.scrollHeight).to.be.greaterThan(list.clientHeight); + expect(list.clientHeight).to.be.lessThan(200); + }); + + cy.get(".planning-grid-wrapper").should(($w) => { + expect($w[0].scrollHeight).to.be.lessThan($w[0].clientHeight * 1.25); + }); + }); + it("jumps to an arbitrary week by picking a day in the calendar popover", () => { cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null }).as("getPlanning"); diff --git a/apps/web/src/pages/planning/planning-page.scss b/apps/web/src/pages/planning/planning-page.scss index 0534110..b1d7f78 100644 --- a/apps/web/src/pages/planning/planning-page.scss +++ b/apps/web/src/pages/planning/planning-page.scss @@ -84,6 +84,18 @@ table.planning-grid { border-collapse: collapse; table-layout: fixed; + // Share the wrapper's height evenly across the five meal rows instead of + // letting each row size to its own content. A `height` on a table row is + // a minimum the browser then stretches to absorb the table's leftover + // height; an equal tiny value on every row spreads that leftover evenly, + // so the grid genuinely fills the screen rather than being only as tall + // as its busiest column (with dead space under the short ones). The + // per-cell scroll below is what then keeps a heavy slot from pushing its + // row past that share. + tbody tr { + height: 1px; + } + th, td { border: 1px solid var(--color-border); @@ -178,12 +190,32 @@ table.planning-grid { flex-direction: column; gap: var(--space-xs); height: 100%; + // The content must not be what defines the row's height: a slot with + // several recipes would otherwise stretch its ``, and with it the + // whole row (every other day's cell gaining the same dead height under + // `vertical-align: top`), then the table past the wrapper into a + // vertical scroll. `min-height: 0` lets this flex column shrink to its + // share of the row so `.meal-cell__recipes` below can take over the + // scrolling. + min-height: 0; } .meal-cell__recipes { display: flex; flex-direction: column; gap: var(--space-xs); + // A slot only ever holds a handful of recipes in real use; past that the + // list scrolls inside the cell instead of stretching the `` (and + // with it the whole row, then the table past `.planning-grid-wrapper` + // into a vertical scroll — issue #110). A `rem` cap rather than a `%` of + // the cell: percentage heights against a `` don't resolve + // consistently across engines. `flex: 1` still lets the list use the + // row's spare height up to that cap. `.add-recipe-btn` is a flex sibling + // (outside this box), so it stays pinned just below, always reachable. + flex: 1; + min-height: 0; + max-height: 8.5rem; + overflow-y: auto; } .recipe-chip { -- 2.45.2