Skip to content

feat(template-builder): finish the table's query data mode - #164

Merged
roncodes merged 3 commits into
test/coverage-campaignfrom
feature/query-table-data-mode-2f4ab8
Aug 25, 2026
Merged

roncodes merged 3 commits into
test/coverage-campaignfrom
feature/query-table-data-mode-2f4ab8

Conversation

@roncodes

@roncodes roncodes commented Aug 25, 2026 •

Copy link
Copy Markdown
Member

Finishes the table's query data mode in the template builder's properties panel. The mode has been
half-built for a while: setTableDataMode handled 'query' and the manual/variable arms cleared
query_endpoint, query_params and query_response_path, but the toggle offered only two buttons,
nothing ever called it with 'query', and nothing read those three fields. The branch reported
[0,0] in coverage — never evaluated either way.

Deferred out of the coverage sweep (DEFECTS.md #15, Appendix D) pending six decisions. Those are
settled here and written into the code so they are not rediscovered.

The decision that mattered: how this reconciles with __queries__

Saved queries already solve "get rows from the server into a table", so a second mechanism needed a
reason to exist. They stay two things with a stated boundary, and Variable mode remains the usual
answer
:

  • Variable mode — a token resolved from the render context, including the saved TemplateQuery
    records the queries panel manages, which template-builder.js exposes under __queries__.
    Structured, reusable across elements, saved with the template. A saved query is reached here.
  • Query mode — one API path with params, bound to one element. It exists for what the saved-query
    builder cannot express: aggregates, reports, and extension endpoints with no model_type behind
    them.

That boundary is a comment block above the query helpers in properties-panel.js, and the variable
mode hint now points at __queries__ so the structured route is the one people find first.

The rest of the contract

  • Endpoint — a path relative to the Fleetbase API (int/v1/orders); leading slashes stripped.
    Absolute and protocol-relative URLs are rejected, in the panel as you type and again before any
    request fires. The fetch service attaches the session to everything it sends, so allowing
    https://another-host/… would hand those credentials over.
  • Auth — everything goes through the injected fetch service. Nothing new introduced.
  • query_params — [{ key, value }], matching the [] the clearing arms already seeded. Values
    may hold {variable} tokens, resolved downstream at render.
  • query_response_path — a dotted path into the response body; blank means the body is itself the
    array. Every way it can fail is reported by name: a missing segment, a segment that runs into a
    primitive, and a path that lands on something other than an array.
  • Fetching — query mode stores intent, like variable mode; nothing in this addon resolves a data
    source at render time. The one exception is the explicit Test query button, which fetches once
    so the endpoint, params and path can be checked before saving. It never writes the fetched rows
    onto the element — it reports the row count and the keys, and offers to turn those keys into
    columns. Params still holding an unresolved token are left out of that request and named.
  • Loading and error states — the button reports in-flight and disables itself; request failures
    and response-path failures both surface. Results are keyed to the element they ran against, so
    selecting another table does not show it the previous one's results.

Render side

Nothing consumed a query-backed table before, and a variable-backed one was equally invisible: both
drew the same placeholder dashes as an empty manual table. element-renderer now captions a table
that has no rows of its own with where its rows come from — Rows from int/v1/orders,
Rows from {order.items}. It does not fetch; a builder canvas issuing requests per element while you
drag is not what anyone wants.

Scope note

The panel is shared, so the third toggle button changes the Data Source section for every consumer.
That is the intended change, not a side effect.

Tests

~70 new tests. All of them fail against the current code — the mode has no control to drive.

  • properties-panel-test.js — the toggle and what each mode clears; endpoint validation including
    both URL shapes; the params editor; response-path resolution and each of its failure messages;
    the test request (endpoint normalisation, params sent, keyless and token-holding params dropped);
    in-flight state; rejection handling; key discovery and its 20-row scan bound; applying discovered
    columns; result/selection isolation; and every action inert without an onUpdateElement handler.
  • element-renderer-test.js — the source caption across manual, variable and query modes, with and
    without a configured source, and that a table with real rows renders those instead.

DEFECTS.md #15 is updated in place to FIXED with the decisions above, rather than adding a second
entry for a defect the tracker already carries.

One note on the branch itself

The final arm of setTableDataMode is now a plain else rather than else if (mode === 'query').
A third condition carries a false path nothing can reach, which is what left this branch reporting
[0,0] and un-suppressable in the first place — the deferral note called that out specifically. As a
plain else the dead path is gone rather than ignored, and the variable arm that real tests cover
is untouched.

Verified on this base: full suite 5196 passing, 0 failing; element-renderer.js at 100%
statements/branches/functions/lines; lint clean across JS, templates and CSS.

`setTableDataMode` handled a 'query' mode the panel never offered: the toggle
had two buttons, nothing called it with 'query', and the three fields the branch
managed — query_endpoint, query_params, query_response_path — were written only
by the clearing arms of the other two modes and read by nothing. The branch
reported [0,0] in coverage, never evaluated either way.

Built rather than deleted, with the questions it was deferred on settled:

- Reconciliation with __queries__. Saved queries and query mode stay two things
  with a stated boundary, and Variable mode remains the usual answer. A saved
  TemplateQuery is structured, reusable and saved with the template, and is
  reached through Variable mode; query mode is one API path bound to one element,
  for the endpoints the query builder cannot express — aggregates, reports, and
  extension endpoints with no model_type behind them. The boundary is a comment
  above the query helpers, and the variable-mode hint now names __queries__.
- Endpoint: a path relative to the Fleetbase API, leading slashes stripped.
  Absolute and protocol-relative URLs are rejected as typed and again before any
  request — the fetch service attaches the session to everything it sends.
- Auth: the injected fetch service. Nothing new introduced.
- query_params: [{ key, value }], matching the [] the clearing arms seeded.
- query_response_path: a dotted path; blank means the body is the array. Every
  way it can fail to resolve is reported by name.
- Fetching: the mode stores intent, like variable mode. The one exception is an
  explicit Test query button that fetches once so the endpoint, params and path
  can be checked before saving. It never writes the fetched rows onto the
  element — it reports the count and keys, and offers them as columns. Params
  still holding an unresolved token are left out of that request and named.
- Loading and error states: in-flight reporting, request and response-path
  failures, and results keyed to the element they ran against.

element-renderer now captions a table that has no rows of its own with where its
rows come from, so a query- or variable-backed table is no longer
indistinguishable from an empty one on the canvas. It does not fetch.

Adds ~70 tests across both components, and records DEFECTS.md #6 as fixed.
@roncodes
roncodes force-pushed the feature/query-table-data-mode-2f4ab8 branch from a87cc7c to 3564e48 Compare August 25, 2026 08:43
The stamped coverage run named three, all in the new code:

- The @Tracked initializers for _queryTestResult and _queryTestElementUuid never
  ran. A tracked field's initializer is evaluated on first *read*, and every path
  wrote before reading, so the declarations were dead. The three fields collapse
  into one `queryTest` object assigned from the constructor, which removes the
  lazy initializers entirely rather than contriving a read to trigger them.
- `event?.target ? event.target.value : event` in updateQueryParam had a false
  path nothing reaches — only DOM change events get there. Now `event.target.value`,
  matching updateColumnLabel/updateColumnKey/updateRowCell alongside it.
- `if (!element) return` in testQuery was never true: the button it fires from is
  rendered inside `{{#if this.hasSelection}}`. Dropped, with the invariant stated.

No behavioural change, and no test changed.
@roncodes
roncodes merged commit c9bc486 into test/coverage-campaign Aug 25, 2026
@roncodes
roncodes deleted the feature/query-table-data-mode-2f4ab8 branch August 25, 2026 09:46
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