Skip to content

feat(lineage): float kebab on hover, keep status icons visible, enrich name tooltip#1383

Merged
gcko merged 9 commits into
mainfrom
worktree-remove-hide-icon-hover
May 20, 2026
Merged

feat(lineage): float kebab on hover, keep status icons visible, enrich name tooltip#1383
gcko merged 9 commits into
mainfrom
worktree-remove-hide-icon-hover

Conversation

@danyelf
Copy link
Copy Markdown
Contributor

@danyelf danyelf commented May 19, 2026

PR checklist

  • Ensure you have added or ran the appropriate tests for your PR.
  • DCO signed

What type of PR is this?

feat — UX polish on the lineage view

What this PR does / why we need it:

Three related improvements to the lineage view's model nodes:

  1. Stop swapping icons on hover. Previously, hovering a node hid the descriptor + change-status icons and replaced them with an impact-radius + kebab cluster. The motion was distracting and removed information the viewer wanted to keep reading. Now the in-card icons stay put; a small floating toolbar (xyflow NodeToolbar) appears on hover, containing just the kebab. The impact-radius button was redundant with the node's color/border treatment, so it's been dropped from the hover entirely (callback still exists on LineageNode for future menu items). A 500ms hide grace period plus mouse handlers + an invisible bridge on the toolbar bridge the portal gap so the cursor can reach the kebab without losing hover.

  2. Move the hover kebab to the right of the card. The kebab originally appeared above the top-right corner, where it could overlap the model-name tooltip on shorter names. It now floats to the right side of the card, vertically centered, with a left-side hover bridge — leaving the entire top of the card free for the tooltip.

  3. Enrich the model-name tooltip. Was: stg_customers. Now: stg_customers - view / raw_customers - seed / int_orders - table (materialization for models, resource type otherwise). The shared formatNodeTooltip(name, resourceType, materialized) helper lives in lineage/styles.tsx and is reused by NodeView's right-sidebar header so the same label appears anywhere the model name is shown.

LineageCanvas also gained an onNodeContextMenu prop that flows through a memoized nodeTypes wrapper to every node. Note for reviewers: in production the OSS app routes the context menu internally via GraphNodeOss, so this prop is currently only consumed by Storybook stories — the wrapper exists so the kebab can be exercised in dev. It's intentional plumbing for forward use, not dead code, but worth knowing during review.

Which issue(s) this PR fixes:

None linked.

Special notes for your reviewer:

  • Verified end-to-end against recce server on the jaffle-shop-duckdb project (snowflake_dev target), not just Storybook.
  • All 3706 frontend tests pass; biome lint + tsc clean.
  • The LineageNode.test.tsx mock for @xyflow/react had to be extended with NodeToolbar. Two tooltip-format assertions updated from the old name (kind) to the new name - kind shape.

Does this PR introduce a user-facing change?:

Lineage view: hovering a model no longer hides the descriptor + change-status icons. Hover now reveals a small floating toolbar to the right of the node containing the context-menu kebab. The model-name tooltip (and the same field in the node-detail sidebar) now includes the materialization or resource type, e.g. "stg_customers - view".

Generated with Claude Code

danyelf and others added 7 commits May 19, 2026 11:51
…h name tooltip

Previously, hovering a lineage node swapped the descriptor + change-status
icons out for an impact-radius + kebab cluster. The motion was distracting
and hid info the user wanted to keep reading. Now the in-card icons stay
put and a small floating toolbar (xyflow NodeToolbar) appears above the
card's top-right corner on hover with just the kebab. A short hide-delay
plus mouse handlers on the toolbar itself bridge the portal gap so the
cursor can reach the kebab without losing hover.

The model-name tooltip is upgraded from "stg_customers" to
"stg_customers - view" / "raw_customers - seed" / "int_orders - table"
(materialization for models, resource type otherwise) via a shared
formatNodeTooltip helper in lineage/styles.tsx, reused by NodeView's
sidebar header so the same label appears wherever the model name is shown.

LineageCanvas now forwards onNodeContextMenu and onShowImpactRadius
through a memoized wrapper nodeTypes, so Storybook stories (and any
future caller) can populate the toolbar without going through GraphNodeOss.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
The hover kebab and the model-name tooltip both rendered above the node
card, so they could overlap on shorter names. The kebab now floats to the
right side of the card (vertically centered) with a left-side hover bridge,
freeing the entire top of the card for the tooltip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
- Drop dead onShowImpactRadius prop + its wiring through LineageCanvas
  and GraphNodeOss (the icon was removed earlier; the callback no longer
  had a consumer).
- Stabilize the LineageCanvas nodeTypes wrapper with a ref-backed
  callback so an unstable caller-side handler can no longer trigger
  full node remounts when ReactFlow re-registers node types.
- Toolbar now uses MUI theme tokens (background.paper / divider) instead
  of hardcoded hex; MUI 7's CSS-variables mode flips them on .dark.
- formatNodeTooltip drops the trailing "- unknown" suffix when resource
  type is missing.
- NodeView Typography uses component="span" rather than a
  display:inline-block hack to attach the tooltip.
- Add a hover -> kebab -> onContextMenu test for the floating toolbar
  (tagged data-testid="lineage-node-kebab"). Remove obsolete GraphNode
  impact-radius assertions and rewrite "hides resource icon on hover"
  to verify the icon now stays put after hover.
- Extend the hover-hide grace period from 150ms to 500ms so the cursor
  has more leniency to travel from the card to the kebab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
… into worktree-remove-hide-icon-hover

Signed-off-by: Danyel Fisher <danyel@gmail.com>
@danyelf danyelf requested a review from gcko May 20, 2026 08:06
@danyelf danyelf self-assigned this May 20, 2026
danyelf and others added 2 commits May 20, 2026 01:10
…ge comment

- LineageCanvas: move onNodeContextMenu ref sync into useEffect (avoid
  mutating ref during render); give the conditional wrapper component a
  displayName so it shows up in React DevTools.
- LineageNode: expand the hover-bridge comment to explain the asymmetric
  12px-vs-6px sizing — the extra buffer is intentional and gives the user
  a generous target between the card edge and the floating kebab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Danyel Fisher <danyel@gmail.com>
Copy link
Copy Markdown
Contributor

@gcko gcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #1383

SHA 3db06643 · Verdict GO

UX polish on the lineage node card: status icons stay visible on hover, a NodeToolbar-portaled kebab floats to the right with a 500ms grace + 12px bridge, and the title tooltip now appends materialization / resource type via a shared formatNodeTooltip helper. The LineageCanvas onNodeContextMenu plumbing is gated behind a ref-backed wrapper that is only consumed by Storybook today — the production OSS path still routes the kebab through GraphNodeOss.handleContextMenu. Tests (3707 passing), tsc --noEmit, and biome are clean on 3db06643. Verified the security surface (no dangerouslySetInnerHTML, no new I/O, model names rendered as text into MUI Tooltip), correctness on the tooltip fallback chain (materialized || resourceType for models, plain resourceType otherwise, name alone if no resourceType), and that the hover timer cancels on hover-begin and on unmount.

Notes

  1. js/src/components/lineage/__tests__/GraphNode.test.tsx:504-513"shows impact radius icon on hover for modified nodes" survives but no longer asserts anything (the only "assertion" is the trailing comment). The behavior it documents was removed by this PR. Same shape at :491-502 ("shows kebab menu on hover") and :638-654 ("calls showColumnLevelLineage on impact radius click for modified nodes"): they hover, then assert that a mock variable is defined. They pass trivially. Worth deleting in a follow-up so the file stops describing removed behavior.
    Evidence: lines 504-513 contain fireEvent.mouseEnter(...) and a comment, no expect(...) calls. The PR's diff already pruned four such tests around onShowImpactRadius; these two slipped through.
    Pass C.

  2. LineageCanvas.tsx:114-131 — the nodeTypes wrapper pattern is good (useMemo gated on hasContextMenu, ref-backed indirection so callback updates don't re-mount the node-type registry). The inline arrow (event, nodeId) => onNodeContextMenuRef.current?.(event, nodeId) is created on every render of LineageNodeWithContextMenu, which defeats React.memo on LineageNode for the Storybook path. Not an issue in production (LineageView.tsx:406 doesn't pass onNodeContextMenu, so hasContextMenu=false and defaultNodeTypes is used). Mentioning it only so the constraint is on the record if this prop ever ships to production consumers.
    Pass H.

@gcko gcko merged commit 7814b24 into main May 20, 2026
7 checks passed
@gcko gcko deleted the worktree-remove-hide-icon-hover branch May 20, 2026 09:39
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.

2 participants