Skip to content

The peek is the record page, at a size the reader picks - #35

Open
nguyenngothuong wants to merge 2 commits into
votrongdao:devfrom
diginno-net:feat/record-peek-sizes
Open

The peek is the record page, at a size the reader picks#35
nguyenngothuong wants to merge 2 commits into
votrongdao:devfrom
diginno-net:feat/record-peek-sizes

Conversation

@nguyenngothuong

Copy link
Copy Markdown

Opening a row from a list gave a panel that listed the model’s fields, drew the stages as tags and offered its own Edit. The record page drew the same record from the same model with a different header, a highlight strip, a stage path, four tabs and a different Edit.

Two renderings of one thing drift, and this pair had already started: the peek showed the row the list had fetched, not the record — so a column the list does not select was simply missing from it, with nothing saying so.

What changed

RecordDetail is that rendering, once. RecordScreen is now <Page> around it; the list’s peek is a Drawer around it. A field added to the layout appears in both because there is one place to add it to.

Three sizes on two buttons in the drawer header — the 452px strip it always was, a 920px column, and the whole window — remembered per browser. wide is the size that makes a peek an answer rather than a preview: four sections of two-column fields wrap to a line each in a strip, and the reader scrolls a page that would have fitted on a screen.

Escape steps down through the sizes before it closes. A reader who went full screen and pressed it meant “give me the list back”, and closing instead is not undoable — the peek does not remember which record it held.

At the narrowest size the highlight strip and the stage path are dropped. Both are horizontal by nature and neither survives a 452px column.

The one thing worth your eye

Drawers now portal to the document. That became necessary rather than tidy: the peek draws RecordDetail, which has an Edit that opens a drawer of its own, and rendered in place the edit panel was laid out inside the peek and clipped by it — a control that reads as broken rather than nested.

.shell is the only positioned ancestor and it fills the window, so fixed covers the rectangle absolute did. I believe this moves no pixel for the nine drawers that were already fine, and I checked the ones I could reach — but it is a change to a primitive every screen uses, so it is the part I would review first.

If you would rather the primitive not grow a size prop at all, the alternative I considered was a second component beside Drawer; I did not take it because two things that open over the page are two things to keep accessible, and Drawer already has the Escape and focus handling.

Verified

Driven with Playwright against docker compose up:

452 → 920 → 1440 on a 1440 viewport, Widen disabled at the end
three Escapes to close, one size per press
size survives a reload
nested edit drawer lands at the window edge, not inside the panel

drawerSize has unit tests for the ends and the failure — widen/narrow saturate rather than wrap, an unknown stored value falls back, and a localStorage that throws does not take the record page with it (Safari in private mode throws on setItem).

npm test is 140 passed, with the one failure pre-existing on dev: liveRecords.test.ts asserts 28 Aug 2026 for a T23:18:06Z timestamp, which is 29 August east of UTC. #30 covers the application-side half of that root cause.

npm run lint could not be run — no eslint.config.js (#33).


Signed off under the DCO.

nguyenngothuong and others added 2 commits August 8, 2026 23:13
Opening a row gave a panel that listed the model's fields, drew the stages as tags and
offered its own Edit. The record page drew the same record from the same model with a
different header, a highlight strip, a stage path, four tabs and a different Edit. Two
renderings of one thing drift, and this pair had already started: the peek showed the
*row* the list had fetched, so a column the list does not select was missing from it with
nothing saying so.

`RecordDetail` is that rendering, once. `RecordScreen` is now `<Page>` around it, and the
list's peek is a `Drawer` around it. A field added to the layout appears in both because
there is only one place to add it to.

WHAT THE READER GETS. Three sizes — the 452px strip it always was, a 920px column, and
the whole window — on two buttons in the drawer header, and the choice is remembered per
browser. `wide` is the size that makes a peek an answer rather than a preview: four
sections of two-column fields do not fit in a strip, and in one they wrap to a line each
and the reader scrolls a page that would have fitted on a screen.

Escape steps down through the sizes before it closes, because a reader who went full
screen and pressed it meant "give me the list back", and closing instead is not undoable
— the peek does not remember which record it held.

At the narrowest size the highlight strip and the stage path are dropped. Both are
horizontal by nature and neither survives a 452px column: three highlights become four
wrapped lines, which reads as damage rather than density.

DRAWERS NOW PORTAL TO THE DOCUMENT. That became necessary rather than tidy: the peek
draws `RecordDetail`, which has an Edit that opens a drawer of its own, and rendered in
place the edit panel was laid out inside the peek and clipped by it. `.shell` is the only
positioned ancestor and it fills the window, so `fixed` covers the rectangle `absolute`
did — no pixel moves for the nine drawers that were already fine.

Tests: `drawerSize` is a state machine and a stored preference, so the ends and the
failure are what is pinned — widen and narrow saturate rather than wrap, an unknown
stored value falls back, and a `localStorage` that throws does not take the record page
with it. Safari in private mode throws on `setItem`, and a peek is not worth a blank
screen.

Verified against the running sample with Playwright: 452 → 920 → 1440 on a 1440 viewport,
Widen disabled at the end, three Escapes to close, the size surviving a reload, and the
nested edit drawer landing at the window edge rather than inside the panel.

`npm test` is 140 passed with the one failure that is pre-existing on this commit's
parent — `liveRecords.test.ts` asserts `28 Aug 2026` for a `T23:18:06Z` timestamp, which
is 29 August anywhere east of UTC (votrongdao#30 covers the application-side half of that).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: nguyenngothuong <nguyenthuongtb12@gmail.com>
`EDITABLE` names the four kinds a custom field *may* be declared on, and the Edit button
was offered whenever the record was one of them. Whether the tenant has declared anything
is a different question, and nothing asked it.

So on the seeded tenant — which declares one field, on accounts — Edit is live on every
contact, lead and opportunity, and opens a drawer reading "Nothing has been declared on
contacts" above a Save button offering "0 change(s)".

A reader who presses Edit and is shown an empty form does not conclude that their tenant
has declared no fields. They conclude the editor is broken, and the sentence explaining
otherwise arrives after the click that cost them the trust. This repository already makes
that argument about the Clone button two lines below — disabled, with the reason on it,
rather than live and inert.

The count comes from the schema the edit drawer itself reads, so the button and the panel
behind it cannot disagree about whether there is anything to edit.

Two reasons rather than one, because they are two different facts and only one is fixable
by the person reading it: the kind takes no declared fields at all, or this tenant has not
declared any yet — and the second says where to go and that it takes no deployment, which
is the claim the sample exists to make.

Found by opening Edit on a contact in the peek this branch adds, which is how the promise
came to be made in two places instead of one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: nguyenngothuong <nguyenthuongtb12@gmail.com>
@nguyenngothuong

Copy link
Copy Markdown
Author

Added a second commit, found while testing the first.

e23bfd8Do not offer an Edit that opens an empty form.

EDITABLE names the four kinds a custom field may be declared on, and the button was offered whenever the record was one of them. Whether the tenant has declared anything is a different question and nothing asked it. On the seeded tenant — which declares one field, on accounts — Edit is live on every contact, lead and opportunity, and opens:

Declared fields
Nothing has been declared on contacts. Declare a field in setup and it appears here…
[ Save 0 change(s) ]  [ Cancel ]

A reader who presses Edit and gets an empty form does not conclude that their tenant has declared no fields — they conclude the editor is broken, and the sentence explaining otherwise arrives after the click that already cost the trust. It is the argument this file already makes about Clone two lines below: disabled with the reason on it, rather than live and inert.

The count comes from the same schema the edit drawer reads, so the button and the panel behind it cannot disagree about whether there is anything to edit. Two reasons rather than one, because "this kind takes no declared fields" and "this tenant has declared none yet" are different facts and only the second is fixable by the reader — so that one names Setup and says it takes no deployment, which is the claim the sample exists to make.

Verified: Edit disabled with the reason on a contact, live on an account.

Happy to split this into its own PR if you would rather keep the two apart — it is only here because the peek is what made the promise appear in two places, and reviewing the peek means meeting it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant