Skip to content

feat(lineage): add materialization badge for model nodes#1237

Open
danyelf wants to merge 3 commits intomainfrom
feature/materialization-badge
Open

feat(lineage): add materialization badge for model nodes#1237
danyelf wants to merge 3 commits intomainfrom
feature/materialization-badge

Conversation

@danyelf
Copy link
Contributor

@danyelf danyelf commented Mar 24, 2026

Show the materialization strategy (table, view, incremental, ephemeral,
materialized_view) on model nodes instead of the generic resource type
icon. Each materialization type gets a distinct icon from the cube family:

  • table: solid cube (reuses existing model icon)
  • view: eye icon
  • incremental: 2/3 solid + 1/3 dashed cube
  • ephemeral: fully dashed cube
  • materialized_view: solid cube with small eye overlay

The badge appears in both the canvas graph nodes and the sidebar detail
view. Non-model nodes continue to show the resource type tag.

Also adds ResourceTypeTag stories and MaterializationTag stories, and
updates LineageCanvas/NodeView fixtures with realistic materialization
data across all dbt layers (staging=view, intermediate=ephemeral,
fact=incremental, dimension=table, mart=materialized_view).

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

image image

  Show the materialization strategy (table, view, incremental, ephemeral,
  materialized_view) on model nodes instead of the generic resource type
  icon. Each materialization type gets a distinct icon from the cube family:

  - table: solid cube (reuses existing model icon)
  - view: eye icon
  - incremental: 2/3 solid + 1/3 dashed cube
  - ephemeral: fully dashed cube
  - materialized_view: solid cube with small eye overlay

  The badge appears in both the canvas graph nodes and the sidebar detail
  view. Non-model nodes continue to show the resource type tag.

  Also adds ResourceTypeTag stories and MaterializationTag stories, and
  updates LineageCanvas/NodeView fixtures with realistic materialization
  data across all dbt layers (staging=view, intermediate=ephemeral,
  fact=incremental, dimension=table, mart=materialized_view).

  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Signed-off-by: Danyel Fisher <danyel@gmail.com>
@danyelf
Copy link
Contributor Author

danyelf commented Mar 24, 2026

Code Review — PR 1237

Summary

Adds materialization badges (table, view, incremental, ephemeral, materialized_view) to model nodes in the lineage graph and sidebar. Clean implementation with proper fallback to ResourceTypeTag for non-model nodes. Type checks and lint pass.

Findings

[Critical] SVG clipPath ID collision in IconIncremental

File: js/packages/ui/src/components/lineage/styles.tsx:356-359
Issue: The IconIncremental component uses hardcoded id="incremental-bottom" and id="incremental-top" for its <clipPath> definitions. When multiple incremental model nodes are rendered simultaneously in a lineage graph (which is the common case — e.g., the largeGraph fixture has 18 fact nodes all using incremental), every instance shares the same DOM IDs. The clipPath="url(#incremental-bottom)" reference resolves to the first matching element in the document, so all but the first icon may render incorrectly depending on browser behavior.
Suggestion: Use React's useId() hook to generate unique IDs per instance. Since IconComponent is a plain function component, switching to useId() is the cleanest approach:

const IconIncremental: IconComponent = (props) => {
  const id = useId();
  const bottomId = `inc-bottom-${id}`;
  const topId = `inc-top-${id}`;
  // use bottomId/topId in clipPath defs and references
};

Alternatively, use a CSS clip-path: inset() or <mask> to avoid IDs entirely.

[Warning] Dangling JSDoc comment in primitives.ts

File: js/packages/ui/src/primitives.ts:67-72
Issue: The "Resource type tag" JSDoc comment block sits above a second JSDoc comment ("Materialization tag"), and neither is attached to a declaration — they both float above the single export { ... } block. The first JSDoc is orphaned and misleading.
Suggestion: Merge into a single JSDoc block, or remove both since the export names are self-documenting.

Verdict

⚠️ Issues found — the clipPath ID collision is a real rendering bug that will manifest whenever multiple incremental models appear in the same graph. The JSDoc nit is cosmetic. Overall the architecture is solid: clean separation of MaterializationTag vs ResourceTypeTag, proper fallback logic, good Storybook coverage.

Hardcoded clipPath IDs caused collisions when multiple incremental
model nodes rendered in the same graph. Use React useId() to generate
unique IDs per instance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
@danyelf danyelf requested a review from gcko March 24, 2026 05:25
@danyelf danyelf self-assigned this Mar 24, 2026
…NodeTag

Materialization is only meaningful for model resources, so these two
concepts belong in a single component. NodeTag takes resourceType and
optional materialized — when resourceType is "model" with a
materialization, it shows the materialization icon/label; otherwise it
shows the resource type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
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