Skip to content

Restore role-aware initial result views when opening saved or shared queries #244

Description

@BorisTyshkevich

Problem

Opening a saved Library query whose effective Dashboard role is filter runs the Filter-owned query but leaves the result drawer on whichever view was already active, usually Table.

That makes Library activation nondeterministic:

previous result view = Table  -> Filter query opens on Table
previous result view = JSON   -> Filter query opens on JSON
previous result view = Panel  -> Filter query opens on Panel

The Filter result is executed correctly and its normalized preview is available, but the user must manually select Filter to see the role-specific representation.

Panel queries already restore their intended launch view from spec.view. Filter queries cannot persist:

{
  "view": "filter"
}

because Filter is intentionally a transient workbench view.

The launch behavior therefore needs a role-aware fallback.


Goal

When a Filter-role query is opened from the Library, start its run in the transient Filter preview.

This must:

  • apply only to Library activation;
  • not persist spec.view: "filter";
  • preserve the user’s ability to switch to Table or JSON;
  • preserve Table/JSON across ordinary reruns after the query is already open;
  • leave existing Panel and queryless-panel behavior unchanged.

Root cause

src/ui/saved-history.js currently derives:

const view = queryView(q);

and launches auto-runnable queries with:

app.actions.run({ view });

For a normal Filter-role query:

queryView(q) === undefined

because Filter is not a persisted view.

run() only changes state.resultView when opts.view is one of:

table
json
panel
filter

When opts.view is undefined, it deliberately preserves the current result view.

That behavior is correct for reruns, but wrong for initial Library activation of a role-specific query.


Required launch precedence

Library activation must resolve the initial result view in this order:

role-specific transient preview
-> persisted spec.view
-> queryless Panel fallback
-> preserve current result view

For this issue:

dashboard.role = filter -> launch view = filter

A future Setup-role implementation may extend the same helper with:

dashboard.role = setup -> launch view = setup

Do not implement Setup behavior in this issue unless the Setup preview already exists.


Implementation

Add a pure helper for the role-owned launch view.

Suggested location:

src/core/result-choice.js

Suggested API:

export function rolePreviewView(spec) {
  switch (effectiveDashboardRole(spec)) {
    case 'filter':
      return 'filter';
    default:
      return null;
  }
}

Alternatively, use an equivalently named helper in the saved-query layer if that avoids a dependency cycle.

In src/ui/saved-history.js, derive:

const persistedView = queryView(q);
const launchView = rolePreviewView(q.spec) || persistedView;

Then update Library activation:

const open = () => {
  app.actions.loadIntoNewTab(q);

  if (isAutoRunnable(q.sql)) {
    app.actions.run({ view: launchView });
  } else if (SAVED_VIEWS.has(launchView)) {
    app.state.resultView.value = launchView;
  } else if (isQuerylessPanel(panel)) {
    app.state.resultView.value = 'panel';
  }
};

The exact helper placement may vary, but the launch precedence must remain explicit and tested.


Role must override dormant Panel state

A Filter-role query may retain dormant Panel configuration and a persisted Panel view so that switching the query back to Panel restores its prior authoring state.

Example:

{
  "view": "panel",
  "dashboard": {
    "role": "filter"
  },
  "panel": {
    "cfg": {
      "type": "kpi"
    }
  }
}

Opening this query from the Library must select:

filter

It must not select:

panel

The implementation must not delete or rewrite the dormant Panel configuration or persisted spec.view.


Rerun behavior

Do not change ordinary run() behavior.

This workflow must remain valid:

  1. Open a Filter-role query from the Library.
  2. The Filter preview opens automatically.
  3. Switch to Table or JSON.
  4. Edit the SQL.
  5. Run again.
  6. Remain on the currently selected Table or JSON view.

Only the initial Library launch supplies:

{ view: 'filter' }

Subsequent normal runs with no explicit launch view continue to preserve state.resultView.

Do not add unconditional logic such as:

if (isFilter) {
  state.resultView.value = 'filter';
}

inside the normal run path.


Error behavior

Filter-role execution errors should remain visible in the Filter preview when the query was launched from the Library.

This includes:

  • static Filter SQL errors;
  • network errors;
  • ClickHouse errors;
  • cancellation;
  • invalid one-row option-bundle shape.

The existing Filter renderer and tab.filterPreview state remain authoritative.

Do not redirect Filter errors to the generic Table error view during Library launch.


Files

Expected modifications:

src/ui/saved-history.js
src/core/result-choice.js

Expected tests:

tests/unit/saved-history.test.js
tests/unit/result-choice.test.js

Modify other focused tests if the current repository structure places Library activation coverage elsewhere.

No schema change.

No saved-query migration.

No new runtime dependency.


Tests

Filter launch

Cover:

  • current result view is Table;
  • current result view is JSON;
  • current result view is Panel;
  • Filter-role query has no persisted view;
  • Filter-role query has dormant spec.view: "panel";
  • Filter-role query has dormant Panel configuration;
  • clicking the Library row calls:
run({ view: 'filter' })
  • the result drawer immediately selects Filter while the request is running;
  • the final normalized preview remains selected after completion;
  • a Filter execution error remains visible in Filter view.

Persistence

Verify:

  • no spec.view: "filter" is written;
  • the saved Library entry remains byte-for-byte unchanged;
  • the tab Spec remains unchanged by launch;
  • dormant spec.view and panel fields remain intact.

Reruns

Verify:

  • after initial Filter launch, switching to Table and running again remains on Table;
  • switching to JSON and running again remains on JSON;
  • selecting Filter again remains possible;
  • the role itself does not force every run back to Filter.

Existing behavior

Verify:

  • ordinary Table query launch is unchanged;
  • ordinary JSON query launch is unchanged;
  • Panel query still restores persisted spec.view: "panel";
  • legacy view: "chart" still maps through the existing Panel compatibility path;
  • queryless text panel still opens Panel;
  • non-auto-runnable DDL behavior remains unchanged;
  • History activation remains unchanged unless it already carries role-aware Library semantics.

Non-goals

Do not include:

  • persisting spec.view: "filter";
  • forcing Filter view on every run;
  • changing Filter execution or normalization;
  • changing Dashboard Filter scheduling;
  • changing the result selector;
  • adding Setup preview behavior before Setup exists;
  • changing History-row semantics;
  • changing Panel launch behavior;
  • changing dormant Panel configuration handling.

Acceptance criteria

  • Clicking an auto-runnable Library query with effective role filter starts its run in the Filter preview.
  • Filter launch behavior is independent of the previously active result view.
  • Filter role takes precedence over dormant persisted spec.view.
  • Dormant Panel configuration remains unchanged.
  • No spec.view: "filter" is persisted.
  • The saved Library entry is not modified by launch.
  • Filter loading state is shown in Filter view.
  • Filter success remains in Filter view.
  • Filter errors remain visible in Filter view.
  • Users can switch to Table or JSON after launch.
  • Subsequent ordinary reruns preserve the user-selected Table, JSON, or Filter view.
  • Existing Panel launch behavior remains unchanged.
  • Existing Table/JSON behavior remains unchanged.
  • Queryless Panel fallback remains unchanged.
  • Unit tests cover launch precedence and rerun preservation.
  • npm test passes.
  • npm run build succeeds.
  • No new runtime dependency is added.

Definition of done

Opening a Filter-role query from the Library always begins in the Filter preview, regardless of the previously active result view.

The preview remains transient, dormant Panel state remains preserved, and normal reruns continue to respect the user’s current Table, JSON, or Filter selection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions