Skip to content

finding(plugin-detail): the SHARED emptiness authority calls an EMPTY ARRAY a value — the record page paints a blank row for [], the exact defect the authority exists to prevent #8474

Description

@os-justin

Found while implementing objectui#8459 (RelatedList's two emptiness spellings). Filed unassigned, ⛔ not claimed. Out of that card's fence: it is a defect in the shared authority itself, on a different surface (the record page body), not in the related-list grid.

What

hasCellValue — THE definition of emptiness for the record page (objectui#8376, widened by objectui#8394, extracted to packages/plugin-detail/src/emptiness.ts by PR #8457) — opens with:

if (value !== null && typeof value === 'object') return true;

typeof [] === 'object', so an empty array is a VALUE. Nothing further is asked of it.

Why that is wrong here, measured

The object half exists for a good reason, and its docblock states it: an object value is handed to a TYPE-AWARE renderer that knows how to draw it — coordinates, a postal address, select badges. But every example the docblock reasons about is a populated object; [] is never considered. And a type-aware renderer has nothing to draw for it. SelectCellRenderer tests value == null || value === '', which [] passes, and then maps it over zero options.

Measured on origin/main (a407bd6), rendering a real DetailSection with tags: [] on a select field:

FULL TEXT: "SNameWidgetTags"
has em-dash anywhere: false
count of em-dash: 0
TAGS ROW HTML:
<div class="text-xs font-medium text-muted-foreground uppercase tracking-wide">Tags</div>

The label renders; the value area is empty. No No value affordance, no em-dash — the visually blank cell the em-dash exists to prevent.

The three consequences are the same three objectui#8376 names

Because all three of DetailSection's reads share this one function, the empty array reproduces the full objectui#8376 triple:

  • the row paints a blank cell (measured above);
  • it escapes emptyCount, so "Show N empty fields" reads one too low and revealing the empty rows does not reveal this one;
  • canCopy is hasCellValue(value), so the row offers to copy an empty array next to a value it did not draw.

shouldAutoHideEmpty needs only filledCount > 0, so a section whose only non-null value is [] can suppress the all-empty skeleton by itself — the objectui#8376 escalation, unchanged.

PR #8457 does not fix it

The extracted emptiness.ts in the merge queue carries the identical predicate (if (value !== null && typeof value === 'object') return true;), so this survives the extraction verbatim. Its docblock's reader list (DetailSection, HeaderHighlight, DetailView summary chips, HistoryTimeline, RecordMetaFooter) is the blast radius: each of those bands asks the same question and gets the same wrong answer for [].

There is already a measured counter-example in the repo

RelatedList.isValueEmpty — the related-list grid's own predicate, and the convergence target chosen in objectui#8459 — draws the finer line and has for some time:

(Array.isArray(v) && v.length === 0)

objectui#8459 measured both directions end-to-end: an all-[] column is correctly pruned there, and a surviving [] cell draws the placeholder. That card deliberately did not delegate to hasCellValue for exactly this reason, and pins the difference in RelatedList.emptinessAgreement-8459.test.tsx (A SURVIVING EMPTY-ARRAY CELL DRAWS THE EM-DASH), which is red for the hasCellValue shape.

Suggested shape (not settled — measurement should decide)

Narrow the object half from "every non-null object is a value" to "every non-empty object is a value": keep objects and populated arrays as values, and let [] fall through to EMPTY. {} deserves its own measurement rather than being assumed — JsonCellRenderer may well draw {} visibly, in which case it is a value and only the array clause moves.

⚠️ Whatever the shape, the pin must assert the rendered outcome and carry non-regression axes that stay green for populated objects and arrays — a predicate that answers EMPTY for every object would pass a naive absence-only pin.

Dedup

Searched objectstack-ai/objectui via MCP search_issues (REST is 403 from the dev container): RelatedList EmptyValue placeholder em-dash aria-label inconsistent hand-rolled span and RelatedList two emptiness spellings disagree pruneEmpty makeCell. Both returned objectui#8459 as a control hit, so the readings are real. Nothing covers the empty-array hole in the authority: objectui#8376, objectui#8394 and objectui#8350 are all about whitespace-only strings.

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpluginpm:dispatchedpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions