Skip to content

Dashboard: query-backed filter options — single-select MVP #160

Description

@BorisTyshkevich

Dependencies

This issue depends on:

  1. Saved-query format v2: separate SQL from an extensible Spec document #211
  2. Workbench: add separate SQL and Spec JSON editor modes #212

Dashboard authoring in this issue uses Spec JSON.

It does not add a larger Library edit form.

Problem

Dashboard filters already exist for every {name:Type} parameter referenced by an included query.

They share:

  • persisted values;
  • activation state;
  • validation;
  • relative-time expressions;
  • recent values;
  • Enum-derived suggestions.

What is missing is a way for one saved query to provide the authoritative option list for one existing dashboard filter.

A Filter source is not a Panel:

  • Panel → visual representation of a query result;
  • Filter source → option provider for one parameter;
  • Setup source → hidden setup execution.

Goal

Allow an included saved query to participate in the dashboard as a Filter source.

Example SQL:

SELECT
    Code AS value,
    Name AS label
FROM airline.airports
ORDER BY Name

Spec:

{
  "name": "Airport options",
  "favorite": true,
  "view": "table",
  "panel": {
    "cfg": {
      "type": "table"
    }
  },
  "dashboard": {
    "role": "filter",
    "param": "origin"
  }
}

The dashboard uses the rows to upgrade the existing origin filter into a strict searchable single-select.

Terminology

Panel

A visual representation:

bar | hbar | line | area | pie | table | logs | text | kpi

Panel configuration remains:

{
  "panel": {
    "cfg": {
      "type": "logs"
    }
  }
}

Dashboard role

How the saved query participates:

spec.dashboard.role = 'panel' | 'filter' | 'setup'

This issue implements Panel and Filter-source execution.

Setup execution remains tracked by #175.

Defaults:

missing dashboard
missing dashboard.role

both mean:

{
  "role": "panel"
}

Inclusion

Dashboard inclusion remains:

spec.favorite

Role and inclusion are independent:

  • favorite + Panel → displayed dashboard Panel;
  • favorite + Filter source → filter-bar option source;
  • favorite + Setup → hidden setup source;
  • not favorite → absent from dashboard, configured role retained.

Authoring UX

Spec JSON is authoritative

Configure a Filter source in the workbench Spec editor:

{
  "favorite": true,
  "dashboard": {
    "role": "filter",
    "param": "origin"
  }
}

Do not add to the Library pencil form:

  • Dashboard checkbox;
  • role radio buttons;
  • target-filter combobox;
  • advanced validation form;
  • larger modal editor.

The pencil remains Name/Description only.

The star remains the quick favorite toggle.

Spec completion

At:

{
  "dashboard": {
    "role": ""
  }
}

suggest:

panel
filter
setup

At:

{
  "dashboard": {
    "role": "filter",
    "param": ""
  }
}

suggest parameter names found across saved-query SQL analysis:

origin     used by 3 dashboard panels
year       used by 2 dashboard panels

Free text remains allowed so a Filter source may be authored before its consumer is saved.

Spec diagnostics

Provide path-specific diagnostics:

dashboard.param
Filter sources require a non-empty target parameter.
dashboard.param
No executable dashboard Panel currently uses "origin".
dashboard.param
Two included Filter sources target "origin".
dashboard.role
Unknown role "filters". This query will be excluded from dashboard execution.

Editor diagnostics assist authoring.

Runtime validation remains authoritative.

Library badges

Show role summaries:

★ Airports                [Filter · origin]
★ Authentication errors   [Panel · Logs]
★ Dashboard setup         [Setup]

For Panel queries, include configured or derived visual type when available.

Clicking a badge:

  1. opens or activates the saved query tab;
  2. switches to Spec mode;
  3. focuses or navigates to dashboard or panel.

Workbench role badge

Near the saved-query title, show:

Airports     Filter · origin

Clicking it opens/focuses the relevant Spec path.

Workbench behavior

A Filter source remains a normal query in SQL mode:

  • Run executes it normally;
  • Table / JSON / Panel inspection remains available;
  • existing spec.panel configuration is preserved;
  • while role is Filter, spec.panel is dormant for dashboard rendering.

Changing role never deletes Panel configuration.

Switching back to Panel restores the previous Panel configuration.

Spec validation

A Filter source requires:

  • non-empty dashboard.param;
  • exactly one row-returning statement;
  • no additional statements;
  • no parameters in its own SQL in v1;
  • target parameter used by at least one executable dashboard Panel.

Invalid when:

  • target missing;
  • target unused;
  • SQL empty;
  • zero or multiple statements;
  • statement is not row-returning;
  • SQL declares any {name:Type} parameter;
  • two included Filter sources target the same parameter.

Duplicate targets are configuration errors.

Do not silently select one by Library order.

Unknown fields inside dashboard are preserved.

Unknown non-empty roles are preserved but excluded from execution with a diagnostic.

Dashboard partition

Partition the included snapshot:

  1. role === 'filter' → option-source scheduler; no grid slot;
  2. role === 'setup' → setup scheduler from Dashboard setup sources: run hidden setup queries before filters and panels #175; no grid slot;
  3. missing role or role === 'panel' → ordinary dashboard Panel;
  4. unknown role → excluded with diagnostic.

A Filter source:

  • never renders as a grid Panel;
  • is excluded from “N not shown” counts;
  • contributes to the existing filter bar;
  • is absent when not favorite.

Option result contract

A valid Filter source returns:

  • 1 column → value and label are that column;
  • 2 columns → first is value, second is label;
  • 0 or more than 2 columns → runtime configuration error.

Rules:

  • NULL values are rejected;
  • duplicate values are deduplicated, first row wins;
  • duplicate labels are allowed;
  • empty-string values are valid and distinct from inactive;
  • values remain strings end-to-end;
  • use FORMAT JSONStrings;
  • server cap is 1,000;
  • fetch 1,001 as truncation sentinel;
  • trim to 1,000;
  • truncated lists remain usable.

Truncation message:

Showing first 1,000 options — refine the source query

The source SQL owns ordering, joins, DISTINCT, and cost.

Execution

Dashboard open

  1. analyze and partition included queries;
  2. render ordinary filter controls immediately;
  3. run valid Filter sources under the shared dashboard limiter;
  4. reconcile persisted selections;
  5. run dashboard Panels after reconciliation.

Persisted-value reconciliation:

  • active stored value present → keep active;
  • active stored value absent → deactivate and keep value dormant;
  • never auto-select first option.

Refresh

Dashboard Refresh:

  • supersedes in-flight option requests;
  • reruns all valid Filter sources;
  • reconciles selections;
  • then reruns dashboard Panels.

No option cache in v1.

Filter changes

Changing a filter:

  • updates shared value/activation stores;
  • reruns only affected dashboard Panels;
  • does not rerun Filter-source option queries in v1.

No cascading option graph in this issue.

Control behavior

Use the shared accessible combobox foundation with strict selection.

The control:

  • opens on focus;
  • filters the complete option list;
  • supports keyboard and pointer selection;
  • distinguishes inactive from active empty string;
  • rejects unmatched arbitrary text;
  • never auto-selects.

Inactive labels:

  • All when optional in every affected Panel;
  • Not set when required by at least one affected Panel.

Lifecycle states:

Loading options…
No options
Option query failed · Retry
Showing first 1,000 options

Retry reruns only that source.

On failure:

  • keep ordinary text/suggestion control available;
  • show warning;
  • do not leave a permanently disabled field.

Shared values and recents

Filter-source controls use:

state.varValues
state.filterActive
state.varRecent

Selecting an option persists exactly like existing filters.

Successful Panel executions continue recording bound values.

The Filter-source control displays authoritative source options, not a separate recents section.

Network and cancellation

Each Filter source owns latest-request state:

generation
AbortController
status

Requirements:

  • newer request supersedes older;
  • abort previous request when practical;
  • ignore stale responses;
  • Refresh supersedes all current option requests;
  • dashboard close aborts outstanding requests;
  • option sources and Panels share the same concurrency ceiling.

Reuse the shared latest-request controller if that abstraction has landed.

Files

Expected changes:

  • saved-query/dashboard Spec validators
  • Spec editor completion/diagnostic provider
  • src/core/dashboard.js
  • src/ui/dashboard.js
  • src/ui/saved-history.js
  • src/net/ch-client.js
  • strict combobox support
  • src/styles.css
  • affected tests
  • README
  • CHANGELOG

Do not add a large Library role-edit form.

Tests

Spec authoring

  • role completion offers Panel/Filter/Setup;
  • target completion uses saved-query parameter analysis;
  • free text remains allowed;
  • missing target is an error;
  • unused target is a warning or configured severity;
  • duplicate target is diagnosed;
  • unknown fields survive edits;
  • no role edit runs SQL.

Library/workbench UX

  • Panel, Filter, and Setup badges render;
  • badge click opens Spec mode;
  • pencil remains Name/Description only;
  • star changes favorite only;
  • role and favorite remain independent;
  • workbench role badge reflects current Spec.

Partition and validation

  • Filter sources never create grid slots;
  • non-favorite Filter sources are absent;
  • unknown roles are excluded with diagnostics;
  • duplicate targets execute neither source;
  • parameterized Filter-source SQL is rejected in v1;
  • unrelated Panels continue running.

Result parsing

  • one-column and two-column shapes;
  • NULL rejection;
  • empty-string value;
  • duplicate-value first-wins;
  • exact large UInt64/Decimal strings;
  • server cap and truncation.

Execution

  • open runs options before Panels;
  • roots run in parallel under the shared limiter;
  • no Panel query starts before persisted-selection reconciliation;
  • valid persisted value remains active;
  • stale persisted value becomes inactive;
  • no first-option auto-selection;
  • filter edit reruns affected Panels only;
  • Refresh reruns options then Panels;
  • stale/aborted responses never land.

Control

  • strict mode rejects unmatched text;
  • inactive sentinel differs from active empty string;
  • All versus Not set follows requiredness;
  • failure exposes Retry and fallback ordinary control.

Acceptance criteria

  • Filter sources are configured through spec.dashboard.
  • No expanded Library dashboard form is added.
  • Pencil remains Name/Description only.
  • Star remains favorite only.
  • Library/workbench badges summarize role and navigate to Spec.
  • A favorite Filter source contributes options and no grid Panel.
  • One/two-column results produce strict searchable single-select options.
  • Persisted selections reconcile before Panel execution.
  • Invalid/failed sources degrade to ordinary controls with diagnostics.
  • Values are capped and transported losslessly.
  • Filter changes rerun affected Panels only.
  • No cascading graph, cache, or multiselect is introduced.
  • Unknown Spec fields are preserved.
  • Coverage gates, build, README, and CHANGELOG updates pass.

Non-goals

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions