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).
This commit is contained in:
2026-09-26 20:01:04 +02:00
parent 3e7c79fa69
commit f254588409
5 changed files with 22 additions and 14 deletions
+11 -6
View File
@@ -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]);
});
});
+4 -3
View File
@@ -125,7 +125,7 @@ function Row({
<td
key={col.key}
style={col.width ? { width: col.width } : undefined}
className={`px-2 py-1.5 align-middle ${col.nowrap ? "whitespace-nowrap" : ""} ${alignClass(col.align)}${col.phoneHidden ? " hidden sm:table-cell" : ""}`}
className={`px-2 py-1.5 align-middle ${col.nowrap ? "whitespace-nowrap" : ""} ${alignClass(col.align)}${col.narrowHidden ? " hidden @2xl:table-cell" : ""}`}
>
{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. */}
<div className="max-w-5xl mx-auto">
{/* 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`). */}
<div className="@container max-w-5xl mx-auto">
{/* 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. */}
+2 -2
View File
@@ -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 ?? "",
+1 -1
View File
@@ -214,7 +214,7 @@ export function ColumnHead<T>({
<th
key={col.key}
style={col.width ? { width: col.width } : undefined}
className={`relative font-medium px-2 py-2 whitespace-nowrap bg-bg ${alignClass(col.align)}${col.phoneHidden ? " hidden sm:table-cell" : ""}`}
className={`relative font-medium px-2 py-2 whitespace-nowrap bg-bg ${alignClass(col.align)}${col.narrowHidden ? " hidden @2xl:table-cell" : ""}`}
aria-sort={
col.sortable
? isSorted
+4 -2
View File
@@ -38,8 +38,10 @@ export interface Column<T> {
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<string, string | string[]>;