Skip to content

finding(app-shell,react): the drill "Open in list" / target: 'navigate' path sends filter placeholders UNRESOLVED, so one drill has two scopes depending on its target #9022

Description

@os-steve

Found while fixing objectui#8944 (PR #9016). Filed rather than folded in: that card is about how the two filter SOURCES compose, this is about what the composed value still carries. Orthogonal, and this one is not chart-specific.

The defect

Every widget that drills through DrillNavigationContext.openRecordList hands it the widget's own filter composed with the click context — and that value is the raw authored filter, placeholders included. Both in-repo consumers of the handler (useOpenRecordList in app-shell, and OpenInListButton in plugin-dashboard) pass it straight to serializeDrillFilterParams, which writes it into filter[...] URL params verbatim. Nothing resolves {current_quarter_start} or {current_user_id} on the way.

Why it survived: the drawer path does NOT have the bug

The in-place drawer renders object-data-table, whose own fetch calls resolveFilterPlaceholders(schema.filter, filterScope). So a macro authored on the widget is resolved on the drawer path and is not on the navigate path.

⇒ One drill, two different scopes, decided only by drillDown.target. And the same widget's own chart bars are scoped by the resolved value — ObjectChart resolves its query legs through resolveFilterPlaceholders(schema.filter, filterScope) while composing the drill from schema.filter raw — so the drilled list disagrees with the chart the user clicked.

The failure is silent and lands on a literal

A widget filter of { close_date: { $gte: '{current_quarter_start}' } } serializes to filter[close_date][gte]={current_quarter_start}. The bare data surface parses that back as an ordinary string comparand, so the list matches nothing (or the driver refuses the comparand). Nothing errors at the seam that produced it.

Note this direction is NARROW, unlike objectui#8944's widening — but it is the same class: the widget's declared scope silently not being the scope the drilled list runs under.

Not chart-specific

openRecordList is the shared drill escape hatch: the table / pivot / metric widgets reach it through DrillDownDrawer and OpenInListButton too. Any of them carrying a placeholder in filter has the same split.

The decision this needs

Where resolution belongs is the actual question, and it should be answered once rather than per widget:

  • A — at each widget's drill seam: compose from the already-resolved filter the widget queries with. Local, but it is N seams and the next hand-rolled drill panel will forget it again.
  • B — inside the openRecordList contract: resolve once where the value crosses into the host, covering every caller including future ones. Needs the filter-token scope to be reachable there.

B looks right, but the scope plumbing is a real design question, so this is not a mechanical fix.

Acceptance

  • A widget whose filter carries {current_user_id} or a relative-date macro, drilled with target: 'navigate', produces a URL carrying the RESOLVED value.
  • The drawer path keeps resolving exactly once — no double resolution.
  • Pinned for at least one non-chart caller as well, since the contract is shared and a chart-only pin would let the next widget regress silently.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions