Merge pull request '[UI mobile] Liste de courses et catalogue de recettes : renforcement mobile' (#99) from feat/mobile-shopping-recipes into main
Some checks are pending
CI / lint (push) Waiting to run
CI / test (push) Waiting to run
CI / intent-service-test (push) Waiting to run
CI / build (push) Waiting to run
CI / e2e (push) Waiting to run
CI / build-android (push) Blocked by required conditions

This commit is contained in:
kyuno 2026-09-07 17:39:53 +02:00
commit 8d1c4a7f0b
4 changed files with 140 additions and 0 deletions

View file

@ -5,6 +5,8 @@
// The favorite-toggle and delete-recipe journeys moved to recipes.feature —
// this file now only covers catalog browsing/display.
import { PHONE_VIEWPORT } from "../support/viewports";
const authenticatedProfile = {
id: 1,
firstName: "Alice",
@ -460,3 +462,38 @@ describe("Recipe catalog", () => {
);
});
});
// Ticket #90 — `.recipe-table-wrap` had no horizontal-scroll guard at all
// (unlike the planning grid's own wrapper), so a narrow viewport just let
// the browser compress every column instead of scrolling. Below the
// tablet breakpoint (1024px) the master-detail split already collapses to
// one column (recipes.scss's own `bp.tablet-down` rule) and gives the
// table the full page width — still comfortably under `.recipe-table`'s
// own 36rem floor on a phone, which is what this covers.
describe("Recipe catalog (phone)", () => {
beforeEach(() => {
cy.setViewport("phone");
interceptAuth();
});
it("keeps the table on its own horizontal scroll, at full width, instead of squeezing its columns", () => {
cy.intercept("GET", /\/recipes\?/, { statusCode: 200, body: [ratatouille, omelette] });
cy.visit("/recettes");
cy.contains(".recipe-table__name", "Ratatouille").should("be.visible");
cy.contains(".recipe-table__name", "Omelette").should("be.visible");
cy.get(".recipe-table-wrap").should(($el) => {
const s = getComputedStyle($el[0]);
expect(s.overflowX).to.be.oneOf(["auto", "scroll"]);
expect($el[0].scrollWidth).to.be.greaterThan($el[0].clientWidth);
});
// ...and that overflow stays *inside* the wrapper — same global rule
// every other page is held to.
cy.document()
.its("documentElement.scrollWidth")
.should("be.lte", PHONE_VIEWPORT.width + 1);
});
});

View file

@ -0,0 +1,67 @@
// Plain layout-mode spec (not Gherkin) for ShoppingListPage's own
// phone-viewport pass (issue #90) — the actual grouped-list rendering,
// aggregation and week-navigation journey is already covered end-to-end by
// shopping-list.feature; this only exercises the responsive change added
// here. Mocks the API via cy.intercept — this job doesn't run a live
// backend (see .github/workflows/ci.yml).
import { PHONE_VIEWPORT } from "../support/viewports";
const authenticatedProfile = {
id: 1,
firstName: "Alice",
lastName: "Martin",
email: "alice@example.com",
tokenVersion: 0,
houseId: 1,
dietId: null,
};
/** Same minimal-fixture convention as shopping-list.ts's own `buildShoppingListItem` step-def helper — only the fields this page actually reads. */
function shoppingListItem(id: number, ingredientKey: string, icon: string, category: string) {
return {
ingredient: {
id,
key: ingredientKey,
icon,
category,
subcategory: category,
reproducible: false,
allergens: [],
diets: [],
},
quantity: 2,
unit: { id, key: "piece", type: "COUNT", toBaseFactor: 1 },
};
}
describe("Shopping list page (phone)", () => {
beforeEach(() => {
cy.setViewport("phone");
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
});
it("gives each row a taller, more legible padding than on desktop, with no horizontal body scroll", () => {
cy.intercept("GET", /\/shopping-list\?/, {
statusCode: 200,
body: {
startDate: "2026-08-17",
finishDate: "2026-08-23",
items: [shoppingListItem(1, "egg", "EGG", "dairyAndCheese")],
},
}).as("getList");
cy.visit("/liste-de-courses");
cy.wait("@getList");
cy.get(".shopping-list__item").should(($el) => {
// --space-md (16px) on phone vs. the desktop default --space-sm
// (8px) — see shopping-list-page.scss's phone block.
expect(getComputedStyle($el[0]).paddingTop).to.equal("16px");
});
cy.document()
.its("documentElement.scrollWidth")
.should("be.lte", PHONE_VIEWPORT.width + 1);
});
});

View file

@ -260,6 +260,17 @@
height: 100%;
min-height: 0;
overflow-y: auto;
// Issue #90: unlike the planning grid's own wrapper (planning-page.scss),
// this one had no horizontal guard at all — on a narrow viewport the
// table's `width: 100%` below just let the browser compress every column
// (photo/name/allergens/diets) to fit instead of scrolling. Same fix,
// scaled down: 4 narrower columns don't need anywhere near the planning
// grid's 62rem, but they do need *some* floor so the badge columns don't
// get squeezed to the point of wrapping every single pill onto its own
// line. Shared by both the full catalog (`/recettes`) and
// `RecipePickerDialog`'s own instance of this same table/wrapper — fixes
// the identical compression there too, not just the full-page case.
overflow-x: auto;
background: var(--color-surface);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
@ -267,6 +278,7 @@
.recipe-table {
width: 100%;
min-width: 36rem;
border-collapse: collapse;
text-align: left;

View file

@ -5,6 +5,8 @@
// `__status`), a simple grouped list rather than a grid below it.
// =============================================================================
@use "../../styles/breakpoints" as bp;
.shopping-list-page {
height: 100%;
display: flex;
@ -100,3 +102,25 @@
font-variant-numeric: tabular-nums;
}
}
// --- Phone (issue #90) ------------------------------------------------------
// This list has no checkbox/delete button at all — it's deliberately
// read-only (see ShoppingListPage.tsx's own doc comment) — so, unlike the
// planning grid or the cooking-session task cards, there's no *tap* target
// to enlarge here; the chantier's original ticket assumed some existed
// (written before this page's actual code was read). The structure also
// already doesn't overflow: `&-name` is `flex: 1; min-width: 0` and wraps
// rather than pushing `&-quantity` out. What phone-viewport reading
// genuinely rewards here — held in one hand, glanced at repeatedly while
// pushing a cart — is a slightly roomier row and a clearer icon, not a
// structural change.
@include bp.phone {
.shopping-list__item {
padding: var(--space-md);
}
.shopping-list__item-icon svg {
width: 1.5rem;
height: 1.5rem;
}
}