Hold'em: every seat on the ring is the same seat, and the ring is measured before it is believed #122
@@ -2460,6 +2460,18 @@ input[type="checkbox"] {
|
||||
margin-bottom: var(--sp-2);
|
||||
}
|
||||
|
||||
/* The two tiers. Upright this is the column it has always been — the wrappers stack in
|
||||
the order they are written and gap exactly as the loose children used to, so nothing
|
||||
about this sheet moves on a phone held the way phones are usually held. They are here
|
||||
for the one held sideways, where the tiers become columns instead of rows. */
|
||||
.seatMenuHand,
|
||||
.seatMenuKeeping {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-2);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* What the group is for, said once, so the tiles under it don't each have to say it.
|
||||
Small and unsaturated on purpose: a caption that competes with the actions has made
|
||||
the sheet busier, which is the thing it is here to undo. */
|
||||
@@ -2473,6 +2485,17 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.seatMenuGrid {
|
||||
/* How narrow a tile in this grid is allowed to get before the row gives up a
|
||||
column. Only read by the sideways layout, where the number of columns is a
|
||||
function of the width there happens to be; upright there are two, always, because
|
||||
a 360px phone has room for exactly two and counting them is not the dealer's job.
|
||||
It lives here rather than there so the one grid of names can raise it.
|
||||
|
||||
7.5rem is not what the words need — the longest of these labels, "Small blind",
|
||||
sets about 75px. It is what makes three columns fit the 397px the housekeeping
|
||||
side has on a 640x360 phone, with the rest going to the tiles rather than to a
|
||||
fourth column too narrow to hold anything. */
|
||||
--tileMin: 7.5rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--sp-2);
|
||||
@@ -5969,6 +5992,141 @@ body.hasActionBar #toastHost {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- A phone on its side: the seat menu goes beside itself ----------
|
||||
|
||||
The block above moved the action bar off the bottom edge because a landscape phone is
|
||||
short of height and swimming in width. The sheets never got the same news. Measured
|
||||
on the fullest menu the tool can build, twelve-handed with somebody waiting:
|
||||
|
||||
640x360 545px of content in a 306px sheet — Cash out…, Move to…, Edit seat…,
|
||||
Small blind, Big blind and Cancel below the fold, Cancel by 224px
|
||||
667x375 545 in 319, Cancel 211px under
|
||||
844x390 529 in 332, Cancel 183px under
|
||||
|
||||
and in all three the sheet was 420px wide in a window 640 to 844 wide. Two hundred
|
||||
and forty-seven pixels of width held empty, on the one screen shape whose whole
|
||||
problem is height. A dealer holding the phone sideways had to scroll a menu to find
|
||||
the way out of it.
|
||||
|
||||
The sheet takes the width instead. The two tiers this menu has been sorted into since
|
||||
round 7 — what you press with cards in the air, and housekeeping between hands — stop
|
||||
being the top and bottom of a column and become the right and left of a row. Which
|
||||
half goes right is not arbitrary: Fold and All-in sit under the same thumb the rail
|
||||
above put Fold, Call and Raise under, so the urgent half of the screen is in the same
|
||||
place whether the dealer is looking at the bar or at a seat.
|
||||
|
||||
Cancel goes under them, in the same column, because the way out belongs at the thumb
|
||||
too — and because a full-width Cancel under both columns is 60px of the 306 there
|
||||
are.
|
||||
|
||||
Scoped on height and width rather than orientation: a short desktop window has the
|
||||
same shortage and the same answer, and a narrow one has neither the width to spend
|
||||
nor a second column to spend it on. */
|
||||
@media (max-height: 560px) and (min-width: 600px) {
|
||||
#seatMenuSheet {
|
||||
/* Not the whole window. A sheet with the scrim showing either side of it still
|
||||
reads as a sheet over a table, and the edge is where a tap dismisses it. */
|
||||
max-width: min(100% - var(--sp-5), 46rem);
|
||||
/* 85vh of a 360px screen is 306px, and this is the one sheet whose content is
|
||||
fixed by the game rather than by the dealer. The seven points buy 25px, which
|
||||
is half a row of tiles. */
|
||||
max-height: 92vh;
|
||||
display: grid;
|
||||
/* Wide enough for Fold and All-in to stay the bars they are, and no wider: the
|
||||
right-hand column is two words, and every pixel over that comes out of the
|
||||
left one, where it is a whole extra column of tiles. Measured at 640x360 —
|
||||
at 11rem the housekeeping grid gets 381px and fits two columns; at 10rem it
|
||||
gets 397 and fits three, which is a row of tiles saved for 16px. */
|
||||
grid-template-columns: minmax(0, 1fr) minmax(8rem, 10rem);
|
||||
grid-template-areas:
|
||||
"header header"
|
||||
"keeping hand"
|
||||
"keeping cancel";
|
||||
align-content: start;
|
||||
/* Nothing grows into the slack. The left column is the taller of the two, so
|
||||
stretching would have left Cancel a 74px button on one phone and a 48px one
|
||||
on the next — the way out should be the same size wherever it is read. */
|
||||
align-items: start;
|
||||
column-gap: var(--sp-3);
|
||||
}
|
||||
|
||||
/* The move picker is a list of chairs and has no hand tier at all, so there is
|
||||
nothing to put in the right-hand column and a column reserved for nothing would
|
||||
narrow the names for no reason. One column, and the chairs spread across it. */
|
||||
#seatMenuSheet:not(:has(.seatMenuHand)) {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-areas:
|
||||
"header"
|
||||
"keeping"
|
||||
"cancel";
|
||||
}
|
||||
|
||||
/* The tiers are the grid's items, not this box's. It keeps its flex column for
|
||||
upright phones and stands aside here. */
|
||||
#seatMenuActions {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.seatMenuHeader {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.seatMenuHand {
|
||||
grid-area: hand;
|
||||
}
|
||||
|
||||
.seatMenuKeeping {
|
||||
grid-area: keeping;
|
||||
}
|
||||
|
||||
#seatMenuCancelBtn {
|
||||
grid-area: cancel;
|
||||
}
|
||||
|
||||
/* As many columns as the width holds, rather than the two a 360px phone has room
|
||||
for. At 640x360 the left-hand column is ~397px, which is three tiles across: the
|
||||
five under "This player" go from three rows to two, and the two under "Give this
|
||||
seat" from two rows to one.
|
||||
|
||||
auto-fill, not auto-fit. auto-fit throws away the tracks nothing sits in, so the
|
||||
two blinds under "Give this seat" stretched to half the sheet each while the five
|
||||
above them were a third — two grids in a column, neither lining up with the
|
||||
other, which reads as two different lists rather than one menu. auto-fill keeps
|
||||
the tracks, so every tile in this sheet is the same width and sits on the same
|
||||
column edges however many there happen to be in a group. */
|
||||
.seatMenuGrid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--tileMin), 1fr));
|
||||
}
|
||||
|
||||
/* And the chairs are allowed to take two lines here. Twelve names three across is
|
||||
four rows, which is the 30px that kept Back off the screen on a 640x360 phone;
|
||||
four across is three rows, and the tile is 136px with 120 for the text, so the
|
||||
longest of these — "Seat 2 — Mike from work", 115px wrapped — goes onto a second
|
||||
line. It costs nothing: two lines of 15px sit inside the 44px floor the tile
|
||||
already has, so the row is the same height it was. */
|
||||
.seatMenuGrid.seatMenuGridNames {
|
||||
--tileMin: 8.25rem;
|
||||
}
|
||||
|
||||
/* Whose menu this is and what they have in front of them, on one line rather than
|
||||
two. A second line of header is height every row of tiles below it pays for, and
|
||||
this header is the part that cannot scroll away. It wraps back to two lines when
|
||||
the name and the stack together need them — the move picker's sentence about
|
||||
where the chips go still takes its own line on every phone here. */
|
||||
.seatMenuHeading {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
column-gap: var(--sp-2);
|
||||
}
|
||||
|
||||
/* The captions lose their top margin: they are the top of their own column now,
|
||||
not a break in the middle of a list. */
|
||||
.seatMenuKeeping > .seatMenuCaption:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Focus is brought to rest where it can be seen ----------
|
||||
|
||||
The ring below was the easy half. The hard half is that the bar is fixed over the
|
||||
|
||||
@@ -178,9 +178,23 @@ const SeatMenu = {
|
||||
// A row of tiles. Two columns on a phone: a seat action's label is a verb and a
|
||||
// short one, and stacking nine of them full width was most of the height the sheet
|
||||
// was spending.
|
||||
grid() {
|
||||
grid(extra) {
|
||||
const el = document.createElement("div");
|
||||
el.className = "seatMenuGrid";
|
||||
el.className = `seatMenuGrid ${extra || ""}`.trim();
|
||||
return el;
|
||||
},
|
||||
|
||||
// The two tiers, as boxes rather than as a run of siblings.
|
||||
//
|
||||
// Upright they are what they always were: one column, in the order they are
|
||||
// written. The wrappers exist for the phone on its side, where the sheet has 306px
|
||||
// of height and 616 of width and the tiers stop being two halves of a column and
|
||||
// become two columns — see the `max-height: 560px` block in holdem.css. That is
|
||||
// only expressible if each tier is one box the layout can address; as five loose
|
||||
// children they can only ever stack.
|
||||
tier(name) {
|
||||
const el = document.createElement("div");
|
||||
el.className = name;
|
||||
return el;
|
||||
},
|
||||
|
||||
@@ -219,7 +233,10 @@ const SeatMenu = {
|
||||
// button already at the thumb end, so it says Back while there is somewhere
|
||||
// to go back to.
|
||||
cancel.textContent = "Back";
|
||||
const grid = this.grid();
|
||||
const keeping = this.tier("seatMenuKeeping");
|
||||
// Chairs, not verbs: "Seat 3 — Alexandra" sets 116px where "Sit out" sets
|
||||
// about 50, so this grid asks for a wider column than the tiles do.
|
||||
const grid = this.grid("seatMenuGridNames");
|
||||
this.destinationsFor(this.seatIndex).forEach(({ player: other, seat }) => {
|
||||
// displayName falls back to "Seat 5" for a chair with nobody in it,
|
||||
// which reads as "Seat 5 — Seat 5". An empty chair says it's empty.
|
||||
@@ -234,7 +251,8 @@ const SeatMenu = {
|
||||
});
|
||||
grid.appendChild(btn);
|
||||
});
|
||||
list.appendChild(grid);
|
||||
keeping.appendChild(grid);
|
||||
list.appendChild(keeping);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -267,18 +285,24 @@ const SeatMenu = {
|
||||
});
|
||||
};
|
||||
|
||||
const hand = this.tier("seatMenuHand");
|
||||
actions.filter(a => a.group === "hand").forEach(action => {
|
||||
const btn = this.button(action.label, `seatMenuBtn ${action.className || ""}`.trim());
|
||||
wire(btn, action);
|
||||
list.appendChild(btn);
|
||||
hand.appendChild(btn);
|
||||
});
|
||||
// An empty tier is left out rather than drawn empty — a folded player in a game
|
||||
// with nobody waiting has nothing in the air to do, and the sideways layout
|
||||
// reads the tier's absence to know it has one column rather than two.
|
||||
if (hand.childElementCount) list.appendChild(hand);
|
||||
|
||||
const keeping = this.tier("seatMenuKeeping");
|
||||
this.GROUPS.forEach(({ name, caption }) => {
|
||||
const inGroup = actions.filter(a => a.group === name);
|
||||
// A caption for nothing is worse than no caption. A group empties often —
|
||||
// past the blinds there is nothing to give a seat but the button.
|
||||
if (inGroup.length === 0) return;
|
||||
list.appendChild(this.caption(caption));
|
||||
keeping.appendChild(this.caption(caption));
|
||||
const grid = this.grid();
|
||||
inGroup.forEach(action => {
|
||||
const btn = this.button(action.label,
|
||||
@@ -287,8 +311,9 @@ const SeatMenu = {
|
||||
wire(btn, action);
|
||||
grid.appendChild(btn);
|
||||
});
|
||||
list.appendChild(grid);
|
||||
keeping.appendChild(grid);
|
||||
});
|
||||
if (keeping.childElementCount) list.appendChild(keeping);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
// The seat menu on a phone held sideways.
|
||||
//
|
||||
// The tool learned two rounds ago that a landscape phone is short of height and
|
||||
// swimming in width: the action bar stops being a bar across the bottom and becomes a
|
||||
// rail down the right. The sheets were never told. Measured on the fullest menu the
|
||||
// tool can build — twelve seated, somebody on the waiting list, blinds still the only
|
||||
// money in:
|
||||
//
|
||||
// 640x360 545px of content in a 306px sheet. Cash out…, Move to…, Edit seat…,
|
||||
// Small blind, Big blind and Cancel all below the fold, Cancel by 224px.
|
||||
// 667x375 545 in 319, Cancel 211px under.
|
||||
// 844x390 529 in 332, Cancel 183px under.
|
||||
//
|
||||
// And on all three the sheet was 420px wide inside a window 640 to 844 wide. Two
|
||||
// hundred and forty-seven pixels of width held empty on the one screen shape whose
|
||||
// whole problem is height, while the dealer scrolled a menu to find the way out of it.
|
||||
//
|
||||
// The two tiers this menu has been sorted into since round 7 — what you press with
|
||||
// cards in the air, and housekeeping between hands — stop being the top and bottom of
|
||||
// a column and become the right and left of a row. Fold and All-in land under the same
|
||||
// thumb the rail puts Fold, Call and Raise under, with Cancel beneath them. Measured
|
||||
// after: 297px in a 298px sheet at 640x360 and at 667x375, 281 in 282 at 844x390.
|
||||
// Nothing below the fold on any of them, and the move picker's twelve chairs and its
|
||||
// Back went from four rows and a scroll to three rows and none.
|
||||
//
|
||||
// Upright nothing moved at all: 545px before, 545px after, the same two columns.
|
||||
const test = require("node:test");
|
||||
const { describe } = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { Betting, HoldemState, SeatMenu, makeTable } = require("./load");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "..", "JoshHeaps.Net");
|
||||
const CSS = fs.readFileSync(path.join(ROOT, "wwwroot", "css", "casino", "holdem.css"), "utf8");
|
||||
|
||||
const ruleFor = selector => {
|
||||
const at = CSS.indexOf(`\n${selector} {`);
|
||||
assert.ok(at >= 0, `there is no \`${selector}\` rule`);
|
||||
return CSS.slice(at, CSS.indexOf("}", at));
|
||||
};
|
||||
|
||||
// Everything inside one @media block, braces balanced, so a test asks what that block
|
||||
// says rather than whether a string appears anywhere in six thousand lines.
|
||||
const SHORT = "@media (max-height: 560px) and (min-width: 600px) {";
|
||||
function shortViewportBlock() {
|
||||
const at = CSS.indexOf(SHORT);
|
||||
assert.ok(at >= 0, "there is no short-viewport block for the sheets");
|
||||
let depth = 0;
|
||||
for (let i = at + SHORT.length - 1; i < CSS.length; i++) {
|
||||
if (CSS[i] === "{") depth++;
|
||||
else if (CSS[i] === "}" && --depth === 0) return CSS.slice(at, i + 1);
|
||||
}
|
||||
assert.fail("the short-viewport block is never closed");
|
||||
}
|
||||
|
||||
// A document small enough to answer what paint() asks of one. It builds elements,
|
||||
// appends them and reads their labels back; nothing here lays anything out, which is
|
||||
// the stylesheet's half of the job and is checked as text below.
|
||||
function fakeDom() {
|
||||
const element = tag => ({
|
||||
tag,
|
||||
className: "",
|
||||
type: "",
|
||||
textContent: "",
|
||||
children: [],
|
||||
appendChild(child) { this.children.push(child); return child; },
|
||||
addEventListener() {},
|
||||
setAttribute() {},
|
||||
focus() {},
|
||||
get childElementCount() { return this.children.length; },
|
||||
set innerHTML(value) { assert.equal(value, "", "only clearing is modelled"); this.children = []; },
|
||||
get innerHTML() { return ""; },
|
||||
});
|
||||
const byId = {};
|
||||
["seatMenuTitle", "seatMenuSubtitle", "seatMenuActions", "seatMenuCancelBtn",
|
||||
"seatMenuOverlay"].forEach(id => { byId[id] = element("div"); });
|
||||
return {
|
||||
byId,
|
||||
doc: {
|
||||
createElement: element,
|
||||
getElementById: id => byId[id] || null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function withDocument(doc, body) {
|
||||
const had = Object.prototype.hasOwnProperty.call(globalThis, "document");
|
||||
const before = globalThis.document;
|
||||
globalThis.document = doc;
|
||||
try { return body(); } finally {
|
||||
if (had) globalThis.document = before; else delete globalThis.document;
|
||||
}
|
||||
}
|
||||
|
||||
// The fullest menu the tool can build, aimed at a live seat that isn't the dealer with
|
||||
// the blinds still the only money in and an override already set.
|
||||
function painted(seatIndex = 3, { move = false, seated = [0, 1, 2, 3, 4], waiting = true } = {}) {
|
||||
const state = makeTable({ seatCount: 9, seated, stack: 500, dealerSeatIndex: 0 });
|
||||
Betting.beginHand(state);
|
||||
state.blindOverride = { sbIndex: 1, bbIndex: 2 };
|
||||
if (waiting) state.waitingList = [{ id: "w1", name: "Nina" }];
|
||||
const { doc, byId } = fakeDom();
|
||||
return withDocument(doc, () => {
|
||||
SeatMenu.state = state;
|
||||
SeatMenu.hooks = {};
|
||||
SeatMenu.seatIndex = seatIndex;
|
||||
SeatMenu.mode = move ? "move" : null;
|
||||
SeatMenu.paint();
|
||||
return { list: byId.seatMenuActions, cancel: byId.seatMenuCancelBtn, state };
|
||||
});
|
||||
}
|
||||
|
||||
const classes = el => el.className.split(/\s+/).filter(Boolean);
|
||||
const labels = el => el.children.map(child => child.textContent);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("the two tiers are boxes, not a run of siblings", () => {
|
||||
// Five loose children can only ever stack. Two boxes can be placed.
|
||||
test("the sheet's actions are exactly the hand and the housekeeping", () => {
|
||||
const { list } = painted();
|
||||
assert.deepEqual(list.children.map(child => child.className),
|
||||
["seatMenuHand", "seatMenuKeeping"]);
|
||||
});
|
||||
|
||||
test("the hand tier holds the bars a dealer presses with cards in the air", () => {
|
||||
const { list } = painted();
|
||||
const hand = list.children[0];
|
||||
assert.deepEqual(labels(hand), ["Fold", "All-in"]);
|
||||
hand.children.forEach(btn => {
|
||||
assert.ok(classes(btn).includes("seatMenuBtn"));
|
||||
assert.ok(!classes(btn).includes("menuTile"),
|
||||
"a bar is not a tile — the whole point of the tier is that it is a bar");
|
||||
});
|
||||
});
|
||||
|
||||
test("and the housekeeping tier holds its captions and grids in order", () => {
|
||||
const { list } = painted();
|
||||
const keeping = list.children[1];
|
||||
assert.deepEqual(keeping.children.map(child => child.className),
|
||||
["seatMenuCaption", "seatMenuGrid", "seatMenuCaption", "seatMenuGrid"]);
|
||||
assert.deepEqual(keeping.children.map(child => child.tag), ["p", "div", "p", "div"]);
|
||||
});
|
||||
|
||||
// The sideways layout reads the tier's absence to know it has one column rather
|
||||
// than two, so an empty tier has to be absent rather than empty.
|
||||
test("a tier with nothing in it is left out rather than drawn empty", () => {
|
||||
// A chair nobody is sitting in, with nobody waiting to take it: there is
|
||||
// nothing at all to do to it while the cards are in the air.
|
||||
const { list } = painted(7, { seated: [0, 1, 2], waiting: false });
|
||||
assert.deepEqual(list.children.map(child => child.className), ["seatMenuKeeping"],
|
||||
"an empty chair still drew a hand tier with nothing in it");
|
||||
});
|
||||
});
|
||||
|
||||
describe("the move picker", () => {
|
||||
test("its chairs go in the housekeeping tier too, so the sheet has one column", () => {
|
||||
const { list } = painted(3, { move: true });
|
||||
assert.deepEqual(list.children.map(child => child.className), ["seatMenuKeeping"]);
|
||||
});
|
||||
|
||||
// "Seat 3 — Alexandra" sets 116px against "Sit out"'s 50, so this grid asks for a
|
||||
// different column from the one the tiles use.
|
||||
test("and they ask for a column of their own width", () => {
|
||||
const { list } = painted(3, { move: true });
|
||||
const grid = list.children[0].children[0];
|
||||
assert.deepEqual(classes(grid), ["seatMenuGrid", "seatMenuGridNames"]);
|
||||
});
|
||||
|
||||
test("the way back out is still the sheet's own foot button", () => {
|
||||
const { cancel } = painted(3, { move: true });
|
||||
assert.equal(cancel.textContent, "Back");
|
||||
assert.equal(painted(3).cancel.textContent, "Cancel");
|
||||
});
|
||||
});
|
||||
|
||||
describe("upright, nothing moved", () => {
|
||||
// The wrappers gap exactly as the loose children used to, so the sheet a dealer
|
||||
// holding a phone the usual way sees is the one it has been since round 7.
|
||||
test("both tiers are the same column the sheet already was", () => {
|
||||
const rule = ruleFor(".seatMenuHand,\n.seatMenuKeeping");
|
||||
assert.match(rule, /flex-direction:\s*column/);
|
||||
assert.match(rule, /gap:\s*var\(--sp-2\)/);
|
||||
assert.match(rule, /min-width:\s*0/);
|
||||
});
|
||||
|
||||
test("and the grid is still two columns wherever the screen is not short", () => {
|
||||
assert.match(ruleFor(".seatMenuGrid"), /grid-template-columns:\s*repeat\(2, minmax\(0, 1fr\)\)/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("what the short screen does instead", () => {
|
||||
const block = shortViewportBlock();
|
||||
|
||||
// Height and width, not orientation: a short desktop window has the same shortage
|
||||
// and the same answer, and a narrow one has neither the width to spend nor a second
|
||||
// column to spend it on.
|
||||
test("it is scoped on the shortage rather than on which way up the phone is", () => {
|
||||
assert.ok(!/orientation/.test(SHORT), "orientation says nothing about how much height there is");
|
||||
});
|
||||
|
||||
test("the sheet takes the width it has been leaving empty", () => {
|
||||
assert.match(block, /max-width:\s*min\(100% - var\(--sp-5\), 46rem\)/);
|
||||
});
|
||||
|
||||
test("the tiers become the two columns, with the way out under the thumb", () => {
|
||||
assert.match(block, /"header\s+header"/);
|
||||
assert.match(block, /"keeping hand"/);
|
||||
assert.match(block, /"keeping cancel"/);
|
||||
});
|
||||
|
||||
test("which means the box that held them stands aside", () => {
|
||||
const at = block.indexOf("#seatMenuActions {");
|
||||
assert.ok(at >= 0, "#seatMenuActions is not addressed here");
|
||||
assert.match(block.slice(at, block.indexOf("}", at)), /display:\s*contents/);
|
||||
});
|
||||
|
||||
// A column reserved for nothing would narrow the names for no reason.
|
||||
test("a sheet with no hand tier is one column, not two", () => {
|
||||
const at = block.indexOf("#seatMenuSheet:not(:has(.seatMenuHand))");
|
||||
assert.ok(at >= 0, "the move picker still reserves a column for a tier it hasn't got");
|
||||
const rule = block.slice(at, block.indexOf("}", at));
|
||||
assert.match(rule, /grid-template-columns:\s*minmax\(0, 1fr\)/);
|
||||
assert.match(rule, /"keeping"/);
|
||||
});
|
||||
|
||||
test("the tiles spread into as many columns as the width holds", () => {
|
||||
assert.match(block, /repeat\(auto-fill, minmax\(var\(--tileMin\), 1fr\)\)/);
|
||||
});
|
||||
|
||||
// auto-fit throws away the tracks nothing sits in, so a group of two stretched to
|
||||
// half the sheet each while the group above it was a third — two grids in a column
|
||||
// lining up with nothing.
|
||||
test("and they line up with each other, group to group", () => {
|
||||
// The comment above the rule says the word it is here to rule out, so the code
|
||||
// is read with the comments stripped out of it.
|
||||
const code = block.replace(/\/\*[\s\S]*?\*\//g, "");
|
||||
assert.ok(!/auto-fit/.test(code),
|
||||
"auto-fit collapses the empty tracks and the grids stop sharing column edges");
|
||||
});
|
||||
|
||||
// The left column is the taller of the two, and stretching left Cancel a 74px
|
||||
// button on one phone and a 48px one on the next.
|
||||
test("nothing grows into the slack the taller column leaves", () => {
|
||||
assert.match(block, /align-items:\s*start/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the numbers the columns are chosen from", () => {
|
||||
// The floor is what the longest of these labels needs, not a round figure that
|
||||
// happened to work on the phone it was measured on.
|
||||
test("a tile's narrowest is a token the grid of names can raise", () => {
|
||||
assert.match(ruleFor(".seatMenuGrid"), /--tileMin:\s*7\.5rem/);
|
||||
const block = shortViewportBlock();
|
||||
const at = block.indexOf(".seatMenuGrid.seatMenuGridNames");
|
||||
assert.ok(at >= 0, "the chairs never ask for more room than a verb");
|
||||
assert.match(block.slice(at, block.indexOf("}", at)), /--tileMin:\s*8\.25rem/);
|
||||
});
|
||||
|
||||
// Two words. Every pixel over that comes out of the left column, where it is a
|
||||
// whole extra column of tiles.
|
||||
test("the hand column is sized for Fold and All-in and no wider", () => {
|
||||
const block = shortViewportBlock();
|
||||
assert.match(block, /grid-template-columns:\s*minmax\(0, 1fr\) minmax\(8rem, 10rem\)/);
|
||||
});
|
||||
|
||||
test("what was measured is written beside the rules", () => {
|
||||
const block = shortViewportBlock();
|
||||
const note = CSS.slice(CSS.indexOf("A phone on its side: the seat menu goes beside itself"),
|
||||
CSS.indexOf(SHORT));
|
||||
assert.match(note, /640x360/);
|
||||
assert.match(note, /545px/);
|
||||
assert.match(note, /224px/);
|
||||
assert.match(block, /10rem it\s+gets 397 and fits three/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user