fix(header): dynamic --hdr-h so a wrapped mobile header can't overlap content
The floating header pads the content below it by a FIXED `--hdr-h` (4.25rem, sized for one row). On mobile the flex-wrap header grows to 2–3 rows, so the content toolbar (sort / count / filter chips) slid up UNDER the header — the top of the page was a pile of overlapping controls. - Header.tsx: measure the real header box with a ResizeObserver and publish its height (offsetTop + offsetHeight + gap) as `--hdr-h` on the content column, so the content pad always tracks the actual (wrapped) header height at any width. Verified live at 440 (mobile) and desktop: the toolbar now sits cleanly below the header, no overlap; desktop unchanged (still ~4rem, one row). Also addresses the S7 /code-review findings: - App.tsx: note that `container-type: inline-size` makes the column a containing block for `position: fixed` descendants too (safe today — floating-ui menus body-portal, toasts are absolute). - SearchBar.tsx: move the responsive-rules comment above `return` and note the rules are on the SHARED search box (every module, not just Plex). - SearchBar.dom.test.tsx: lock the container-query classes (min-w floor + basis-full/order-last) so the wrap-not-collapse fix can't silently regress. FE gate: tsc / knip / lint clean, 218 vitest pass.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
// @vitest-environment jsdom
|
||||
//
|
||||
// E5 S7: locks the container-query classes on the shared search box that fix the narrow-header
|
||||
// collapse — the box must WRAP to its own line (not shrink the input to ~0) when the content column
|
||||
// (@container/appmain) is narrow. jsdom does no layout, but the class presence is the contract these
|
||||
// rules depend on, so a refactor that drops them fails here instead of only on a hand-resized window.
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import SearchBar from "./SearchBar";
|
||||
|
||||
vi.mock("react-i18next", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("react-i18next")>();
|
||||
return { ...actual, useTranslation: () => ({ t: (key: string) => key }) };
|
||||
});
|
||||
|
||||
describe("SearchBar responsive wrapper (E5 S7)", () => {
|
||||
it("carries the container-query classes that make it wrap instead of collapsing", () => {
|
||||
const { container } = render(
|
||||
<SearchBar value="" onChange={() => {}} placeholder="Search…" />,
|
||||
);
|
||||
const cls = (container.firstElementChild as HTMLElement).className;
|
||||
// A usable min-width floor once the column has room → the box wraps via flex-wrap, never ~0:
|
||||
expect(cls).toContain("@min-[420px]/appmain:min-w-[15rem]");
|
||||
// On a narrow column it takes a full line of its own and sorts last (small pills share the row above):
|
||||
expect(cls).toContain("@max-[640px]/appmain:basis-full");
|
||||
expect(cls).toContain("@max-[640px]/appmain:order-last");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user