From f254588409001941cc797b921661bd8574f3e3c2 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 26 Sep 2026 20:01:04 +0200 Subject: [PATCH] fix(playlists): hide '#' and Channel by the table's width, not the window's (E28) Beside the playlist rail an 800 px window left the table 432 px: the fixed columns took all of it and the title column was 0 px wide, its thumbnail drawn over the channel. The table is an @container now and the columns hide under 42rem of its own width (narrowHidden, was phoneHidden). --- frontend/src/components/Playlists.dom.test.tsx | 17 +++++++++++------ frontend/src/components/Playlists.tsx | 7 ++++--- frontend/src/components/playlistColumns.tsx | 4 ++-- frontend/src/components/ui/tableHeader.tsx | 2 +- frontend/src/lib/tableColumns.ts | 6 ++++-- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/Playlists.dom.test.tsx b/frontend/src/components/Playlists.dom.test.tsx index 454c20dd..3d58d980 100644 --- a/frontend/src/components/Playlists.dom.test.tsx +++ b/frontend/src/components/Playlists.dom.test.tsx @@ -558,17 +558,22 @@ describe("Playlists · the header's '…'", () => { }); }); -describe("Playlists · a phone-width table", () => { - it("leaves out '#' and Channel below sm, header and cells alike, so the title keeps a width", async () => { +describe("Playlists · a narrow table", () => { + it("leaves out '#' and Channel while the TABLE is narrow, header and cells alike, so the title keeps a width", async () => { mount({ 1: [vid("A", 0)] }); await screen.findByRole("button", { name: "card.play — Video A" }); - const phoneHidden = (el: Element) => - el.classList.contains("hidden") && el.classList.contains("sm:table-cell"); + // By the table's own width (its @container), not the viewport's: beside the rail a desktop + // window left the table 432 px and the title 0. + expect(document.querySelector("table")!.parentElement!.classList.contains("@container")).toBe( + true, + ); + const narrowHidden = (el: Element) => + el.classList.contains("hidden") && el.classList.contains("@2xl:table-cell"); const heads = [...document.querySelectorAll("thead th")]; const cells = [...document.querySelectorAll("tbody tr:first-child td")]; // Columns: grip, #, title, channel, length, "…". - expect(heads.map(phoneHidden)).toEqual([false, true, false, true, false, false]); - expect(cells.map(phoneHidden)).toEqual([false, true, false, true, false, false]); + expect(heads.map(narrowHidden)).toEqual([false, true, false, true, false, false]); + expect(cells.map(narrowHidden)).toEqual([false, true, false, true, false, false]); }); }); diff --git a/frontend/src/components/Playlists.tsx b/frontend/src/components/Playlists.tsx index cf97a3c0..fe74bd26 100644 --- a/frontend/src/components/Playlists.tsx +++ b/frontend/src/components/Playlists.tsx @@ -125,7 +125,7 @@ function Row({ {col.key === GRIP_KEY ? ( // The one cell the column defs can't render: the handle needs this row's sortable @@ -907,8 +907,9 @@ export default function Playlists() { // drag; now that the keyboard can, they ARE the instructions for the feature. accessibility={dndA11y} > - {/* Not clipped, so the header's filter popovers can overflow the table box. */} -
+ {/* Not clipped, so the header's filter popovers can overflow the table box. A container, + so a narrow column hides by the table's width, not the window's (`narrowHidden`). */} +
{/* table-fixed, not auto: every column but the title has a declared width, so the title absorbs the rest and its `truncate` works. Auto layout sizes columns from min-content, and a nowrap title cell's min-content is the entire title. */} diff --git a/frontend/src/components/playlistColumns.tsx b/frontend/src/components/playlistColumns.tsx index e62aa90c..7e0c09ac 100644 --- a/frontend/src/components/playlistColumns.tsx +++ b/frontend/src/components/playlistColumns.tsx @@ -38,7 +38,7 @@ export function playlistColumns({ nowrap: true, hideInCard: true, // The position number is the first thing a phone row can spare; the order still shows. - phoneHidden: true, + narrowHidden: true, // Sorting by "#" ascending is the stored order itself — that is what makes it a truthful // default for the header arrow, and a one-click way back from any other sort. sortable: true, @@ -89,7 +89,7 @@ export function playlistColumns({ key: "channel", header: t("playlists.colChannel"), width: "12rem", - phoneHidden: true, + narrowHidden: true, nowrap: true, sortable: true, sortValue: (v) => v.channel_title ?? "", diff --git a/frontend/src/components/ui/tableHeader.tsx b/frontend/src/components/ui/tableHeader.tsx index 1106aca4..4b037af4 100644 --- a/frontend/src/components/ui/tableHeader.tsx +++ b/frontend/src/components/ui/tableHeader.tsx @@ -214,7 +214,7 @@ export function ColumnHead({ { hideInCard?: boolean; cardLabel?: boolean; // A column a table WITHOUT the card fallback (the playlist's drag-reorderable hybrid) leaves out - // below `sm`: on a phone its fixed widths alone overran the screen and left the title no width. - phoneHidden?: boolean; + // while the TABLE is narrower than 42rem: its fixed widths alone overran the box and left the title + // no width. The table's own width, not the viewport's (an `@container` round it): beside the + // playlist rail a desktop window gave the table 432 px, and the title column was 0 px wide. + narrowHidden?: boolean; } export type FilterMap = Record;