From a4e6e78fe72e335887000302d605040d48bb579f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 01:31:31 -0400 Subject: [PATCH 1/3] feat(adapters): explore CTE/model view toggle + tests.html unit-test viewer via shared test-card partial (#102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dag.html gains a two-arm view toggle: the lineage view (boot) and the highlighted model's CTE DAG, rendered by the same vendored Cytoscape + dagre engine (templates/explore-cte.js). The carrier's new cte_dags map ships one DagPayload per CTE-bearing compiled model — the exact role-classified, join-typed graph the report renders (build_payload reuse seam, parsed once). An uncompiled model ships no entry and renders the labeled fail-open degraded view, never an error; PreflightError stays four variants. The lineage instance is never rebuilt, so its pan/zoom/highlight persist across toggles (local state, same page). tests.html now serves the unit-test viewer through a SHARED askama partial (templates/partials/test-card.html) extracted from report.html output-preservingly (the report goldens stay byte-identical) and filled client-side by templates/explore-tests.js from the embedded cute-dbt-data payload — no jQuery, no DataTables, no graph engine on the page; createElement+textContent only. Co-Authored-By: Claude Fable 5 --- examples/explore/dag.html | 330 +++++++++++++++++++++++++- examples/explore/tests.html | 372 ++++++++++++++++++++++++++++-- src/adapters/asset_embed.rs | 76 ++++++ src/adapters/explore.rs | 212 ++++++++++++++++- src/adapters/render.rs | 6 +- templates/explore-cte.js | 274 ++++++++++++++++++++++ templates/explore-dag.html | 55 ++++- templates/explore-lineage.js | 12 + templates/explore-tests.html | 62 ++++- templates/explore-tests.js | 267 +++++++++++++++++++++ templates/partials/test-card.html | 39 ++++ templates/report.html | 48 +--- 12 files changed, 1682 insertions(+), 71 deletions(-) create mode 100644 templates/explore-cte.js create mode 100644 templates/explore-tests.js create mode 100644 templates/partials/test-card.html diff --git a/examples/explore/dag.html b/examples/explore/dag.html index e0c1b1fa..a88973c9 100644 --- a/examples/explore/dag.html +++ b/examples/explore/dag.html @@ -256,6 +256,21 @@ .lineage-legend { color: #555; font-size: 0.85rem; margin-top: 0.5rem; } .lineage-legend .legend-chip { display: inline-block; padding: 0 0.4rem; border: 1.5px solid #0072B2; border-radius: 4px; background: #e8f1f8; color: #1c1c1f; } .lineage-legend .legend-chip.notcompiled { border-color: #b07400; border-style: dashed; background: #f4f4f5; } +/* cute-dbt#102 — the CTE ⇄ model view toggle + the CTE view host. */ +.view-toggle { display: inline-flex; border: 1px solid #c5c5cc; border-radius: 6px; overflow: hidden; } +.view-toggle button { border: none; border-radius: 0; margin: 0; background: #fff; color: #1c1c1f; padding: 0.25rem 0.7rem; font-size: 0.85rem; cursor: pointer; } +.view-toggle button + button { border-left: 1px solid #c5c5cc; } +.view-toggle button.is-active { background: #e8f1f8; font-weight: 600; } +.view-toggle button:disabled { color: #999; background: #f4f4f5; cursor: not-allowed; } +.cte-canvas { height: min(70vh, 40rem); border: 1px solid #c5c5cc; border-radius: 6px; background: #fcfcfd; } +.cte-canvas:focus { outline: 2px solid #0072B2; outline-offset: 2px; } +.cte-view-title { color: #555; font-size: 0.9rem; margin: 0 0 0.4rem; } +.cte-degraded, .cte-view-empty { color: #555; font-style: italic; } +.cte-legend { color: #555; font-size: 0.85rem; margin-top: 0.5rem; } +.cte-legend .legend-chip { display: inline-block; padding: 0 0.4rem; border-radius: 4px; color: #1c1c1f; } +.cte-legend .legend-chip.final { border: 1.5px solid #0072B2; background: #e8f1f8; } +.cte-legend .legend-chip.import { border: 1.5px solid #1f8a5b; background: #eef7ee; } +.cte-legend .legend-chip.transform { border: 1.5px solid #6b6b76; background: #f4f4f5; } @@ -279,9 +294,16 @@

Model lineage

-

click or search-select highlights a model’s lineage · Space commits focus on the highlighted model

+ +
+ + +
+

click or search-select highlights a model’s lineage · Space commits focus · a highlighted model unlocks its CTE view

+
+
@@ -289,9 +311,23 @@

Model lineage

not compiled — a dashed node has no compiled SQL in this manifest (dbt parse); run dbt compile to fill it in. The explorer renders it anyway (fail-open).
+
+ + - + + \ No newline at end of file diff --git a/examples/explore/tests.html b/examples/explore/tests.html index 2d1e5572..08bf4a63 100644 --- a/examples/explore/tests.html +++ b/examples/explore/tests.html @@ -246,11 +246,39 @@ .model-path { color: #555; font-size: 0.85rem; margin: 0 0 0.4rem; } .not-compiled-badge { font-size: 0.7em; vertical-align: middle; padding: 0.1rem 0.45rem; border: 1.5px dashed #b07400; border-radius: 4px; background: #f4f4f5; color: #1c1c1f; } .explore-tests { margin-bottom: 0.5rem; } -.explore-test .test-name { font-weight: 600; } .explore-test .test-shape { color: #555; font-size: 0.85rem; } .explore-test .test-description { color: #555; font-size: 0.9rem; margin: 0.1rem 0 0; } .no-tests { color: #555; font-style: italic; } .explore-empty { color: #555; font-style: italic; } +/* cute-dbt#102 — explore-local styling for the SHARED test-card partial + (report.html styles the same markup through its chassis CSS, which + this light page deliberately does not embed). */ +.explore-viewer { border-top: 1px solid #ddd; padding-top: 0.75rem; margin-top: 0.75rem; } +.explore-viewer > h2 { margin-bottom: 0.4rem; } +.test-card { border: 1px solid #c5c5cc; border-radius: 6px; padding: 0.75rem 1rem; background: #fcfcfd; } +.test-card-head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; } +.test-select-field label { font-weight: 600; margin-right: 0.4rem; } +/* Explore is full-manifest by design: there is no updated/context split, + so the partial's scope toggle is inert chrome here — hidden, not + forked out of the shared markup. */ +.test-mode-toggle { display: none; } +.test-badges .tag-badge { display: inline-block; font-size: 0.75rem; color: #1c1c1f; border: 1px solid #c5c5cc; border-radius: 4px; padding: 0 0.35rem; background: #f4f4f5; margin-right: 0.25rem; } +.test-description { color: #333; margin: 0.5rem 0 0; } +.test-defined-in { color: #555; font-size: 0.85rem; margin: 0.3rem 0 0; } +.panel-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 0.75rem; } +.panel { flex: 1 1 22rem; min-width: 0; border: 1px solid #c5c5cc; border-radius: 6px; padding: 0.6rem 0.8rem; background: #fcfcfd; } +.panel-header { display: flex; align-items: baseline; gap: 0.6rem; } +.panel-header h2 { font-size: 1.1rem; margin: 0 0 0.4rem; } +.table-header { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; } +.table-title { margin: 0.4rem 0 0.2rem; font-size: 0.95rem; } +.row-count-badge, .format-badge, .this-badge { font-size: 0.75rem; color: #555; border: 1px solid #c5c5cc; border-radius: 4px; padding: 0 0.35rem; background: #f4f4f5; } +.fixture-provenance { font-size: 0.8rem; color: #555; } +table.given-table, table.expected-table { font-size: 0.85rem; margin: 0.3rem 0 0.6rem; } +.cell-null { color: #888; font-style: italic; } +.empty-hint, .given-empty { color: #555; font-style: italic; } +.sql-block { font-size: 0.85rem; } +.test-jump { background: none; border: none; padding: 0; color: #1d7484; font: inherit; font-weight: 600; cursor: pointer; text-decoration: underline; } +.test-jump:hover, .test-jump:focus { color: #144f5a; } @@ -267,6 +295,47 @@

Unit tests

+
+

Unit-test viewer

+
+
+
+
+ + +
+
+ + +
+
+
+ +
+
+
+ +
+ +
+ +
+

Given

+
+
+
+ +
+
+

Expected

+ 0 rows +
+
+
+ +
+
+

dim_conditions

models/marts/analytics/dim_conditions.sql

@@ -299,8 +368,9 @@

dim_patients

dim_payers

models/marts/core/dim_payers.sql

@@ -94,5 +138,14 @@

Model lineage

Banner-pin + end-of-file sentinel tests guard the include. #} {{ explore_lineage_js|safe }} + diff --git a/templates/explore-lineage.js b/templates/explore-lineage.js index 8909b49e..59add19b 100644 --- a/templates/explore-lineage.js +++ b/templates/explore-lineage.js @@ -157,9 +157,20 @@ // ---- HIGHLIGHT (click / search-select; in-place, never a commit) -------- var highlighted = null; + // cute-dbt#102 — page-local highlight observer hook: the CTE-view + // engine (explore-cte.js) gates + retargets its view off this event. + // It is NOT the external-drive signal: data-selected-model stays + // Space-commit-only (the one write site below). + function notifyHighlight(id) { + document.dispatchEvent( + new CustomEvent("cute-explore-highlight", { detail: { id: id } }) + ); + } + function clearHighlight() { cy.elements().removeClass("dim sel trace"); highlighted = null; + notifyHighlight(null); } function highlightNode(node) { @@ -171,6 +182,7 @@ node.removeClass("dim").addClass("sel"); }); highlighted = node; + notifyHighlight(node.id()); } // ---- FOCUS COMMIT (Space only — the one selectedModel write site) ------- diff --git a/templates/explore-tests.html b/templates/explore-tests.html index 9f8fac00..3cecadb1 100644 --- a/templates/explore-tests.html +++ b/templates/explore-tests.html @@ -9,10 +9,11 @@ {{ sakura_css|safe }} @@ -45,14 +74,25 @@

Unit tests

{% if models.is_empty() %}

No models in this manifest.

{% endif %} +{# cute-dbt#102 — the unit-test viewer: the SHARED test-card partial + (templates/partials/test-card.html — the exact markup report.html + renders) plus the explore-tests.js engine below, which fills it from + the embedded cute-dbt-data payload. Hidden when the manifest carries + no unit tests (the index's empty states still tell the story). #} +
+

Unit-test viewer

+{% include "partials/test-card.html" %} +
{% for m in models %}

{{ m.name }}{% if m.not_compiled %} not compiled{% endif %}

{% match m.path %}{% when Some with (p) %}

{{ p }}

{% when None %}{% endmatch %}{% if m.tests.is_empty() %}

0 unit tests wired

{% else %}