Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/13303-withholding-copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@object-ui/react": patch
---

The console line for a visibility predicate that cannot be evaluated, on a gate that withholds what it gates, no longer says "a navigation entry" was withheld. The same line is printed for a navigation entry, a `page:tabs` item and a page node, so it now says "an element whose visibility predicate cannot be evaluated is WITHHELD". The verdict, the line's opening, and when it is printed are unchanged (objectui#13303).
Original file line number Diff line number Diff line change
Expand Up @@ -771,12 +771,13 @@ const ENABLEMENT_TAIL =

/** The shipped visibility copy, which must not move. */
// The `visibleWhen` leg fails CLOSED since objectui#13299, so a declared
// predicate that faults prints the `'withholding'` consequence.
// predicate that faults prints the `'withholding'` consequence, which names
// the withheld thing as "an element", not a navigation entry (objectui#13303).
const VISIBILITY_TAIL =
'The entry was treated as its safe default, which on THIS gate is the one\n' +
'that BITES: a navigation entry whose predicate cannot be evaluated is\n' +
'WITHHELD - not drawn - until it can be, so a missing entry may be this\n' +
'fault rather than a gate that said no.\n' +
'The element was treated as its safe default, which on THIS gate is the\n' +
'one that BITES: an element whose visibility predicate cannot be evaluated\n' +
'is WITHHELD - not drawn - until it can be, so a missing element may be\n' +
'this fault rather than a gate that said no.\n' +
'Page-component predicates bind `record` (the row on a record page),\n' +
'`current_user`, and page state as `page.<var>`. Check those roots and the\n' +
'CEL syntax.';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
* Rows:
* (a) a throwing `visibleWhen` is not rendered, and two renders log ONE
* warning — in the production branch (one engine call, `onFault`) and in
* the development branch (`throwOnError`);
* the development branch (`throwOnError`); that line names no navigation
* surface (objectui#13303);
* (b) every other leg with a faulting declared predicate is not rendered:
* `visible`, `visibleOn` (SHOW legs, negated) and `hidden`, `hiddenOn`
* (HIDE legs, not negated — their fail-soft `true` already hid);
Expand Down Expand Up @@ -59,6 +60,13 @@ const orChain = (n: number) => Array.from({ length: n }, (_, i) => `${i} == ${i}
/** The line the shared reporter prints for the fail-closed SHOW legs. */
const WITHHELD = 'WITHHELD';

/**
* The navigation surface's nouns. The `'withholding'` copy was written for the
* app-shell navigation gate and told a page-node author that "a navigation
* entry" was withheld (objectui#13303); a page node's line names neither.
*/
const NAVIGATION_SURFACE = /navigation|\bentry\b/i;

type WarnSpy = { mock: { calls: unknown[][] } };
const spyWarn = () => vi.spyOn(console, 'warn').mockImplementation(() => {});
const reports = (warn: WarnSpy): string[] =>
Expand Down Expand Up @@ -179,6 +187,7 @@ describe('objectui#13299 (a) — a faulting `visibleWhen` withholds its node, re
expect(lines[0]).toContain('visibleWhen');
expect(lines[0]).toContain(FAULT);
expect(lines[0]).toContain(WITHHELD);
expect(lines[0]).not.toMatch(NAVIGATION_SURFACE);
});
});
});
Expand Down
40 changes: 27 additions & 13 deletions packages/react/src/utils/visibilityDiagnostic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,24 @@ const SCOPE_TIER_ADVICE: Record<PredicateScopeTier, string> = {
* gains a case to answer. That is a type-level change only: no runtime
* signature moved, and every value that was accepted before still is.
*
* `'withholding'` - the app-shell's navigation `visible` gate (objectui#12700).
* A visibility predicate on a navigation entry, but one whose FAULT is answered
* fail-CLOSED: the shell withholds an entry whose gate it cannot evaluate (most
* often a `current_user.can(...)` asked before the permissions payload is
* there) instead of drawing it for everyone. So the default BITES, and the
* sentence `'visibility'` prints - "reads on screen exactly like one that said
* yes" - would be the opposite of what the member sees. Same opening line as
* `'visibility'`, for the same reason `'concealment'` keeps it.
* `'withholding'` - a visibility predicate whose FAULT is answered
* fail-CLOSED: the element it gates is withheld - not drawn - instead of being
* drawn for everyone. Three gates report through it:
*
* - the app-shell's navigation `visible` gate (objectui#12700), whose usual
* fault is a `current_user.can(...)` asked before the permissions payload is
* there;
* - the `page:tabs` item `visibleWhen` gate (objectui#13286);
* - a page node's `visibleWhen` / `visible` / `visibleOn` (objectui#13299), for
* a DECLARED predicate - a blank gate still means "no gate" and still prints
* `'visibility'`.
*
* So the default BITES, and the sentence `'visibility'` prints - "reads on
* screen exactly like one that said yes" - would be the opposite of what the
* author sees. The paragraph calls the withheld thing "an element" because one
* paragraph serves all three, and a noun for one surface is false on the other
* two (objectui#13303). Same opening line as `'visibility'`, for the same
* reason `'concealment'` keeps it.
*/
export type PredicateGateKind = 'visibility' | 'enablement' | 'concealment' | 'withholding';

Expand Down Expand Up @@ -276,14 +286,18 @@ const GATE_KIND_COPY: Record<PredicateGateKind, { prefix: string; consequence: s
'in the renderer: the block is missing because the predicate above could\n' +
'not be evaluated.\n',
},
// The app-shell navigation gate (objectui#12700): fail-CLOSED on a fault.
// The fail-CLOSED visibility gates: the app-shell navigation `visible` gate
// (objectui#12700), the `page:tabs` item gate (objectui#13286) and a page
// node's SHOW legs (objectui#13299). One paragraph serves all three, so it
// names what is withheld as "an element", never as one surface's thing
// (objectui#13303).
withholding: {
prefix: UNRESOLVABLE_VISIBILITY_PREFIX,
consequence:
'The entry was treated as its safe default, which on THIS gate is the one\n' +
'that BITES: a navigation entry whose predicate cannot be evaluated is\n' +
'WITHHELD - not drawn - until it can be, so a missing entry may be this\n' +
'fault rather than a gate that said no.\n',
'The element was treated as its safe default, which on THIS gate is the\n' +
'one that BITES: an element whose visibility predicate cannot be evaluated\n' +
'is WITHHELD - not drawn - until it can be, so a missing element may be\n' +
'this fault rather than a gate that said no.\n',
},
enablement: {
prefix: UNRESOLVABLE_ENABLEMENT_PREFIX,
Expand Down
Loading