Skip to content

fix(apollo-react): edge labels lose background and can render under crossing lines#955

Draft
dbacomputer wants to merge 2 commits into
mainfrom
add-label-background-color
Draft

fix(apollo-react): edge labels lose background and can render under crossing lines#955
dbacomputer wants to merge 2 commits into
mainfrom
add-label-background-color

Conversation

@dbacomputer

@dbacomputer dbacomputer commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two bugs in CanvasEdge/SequenceEdge labels, plus a new Storybook reference page documenting label behavior across themes and edge cases.

Bug 1 — line renders over the label

EdgeLabel rendered as a raw foreignObject inside its own edge's <g>, so it competed in the same per-edge z-index/DOM-order stack as every other edge's stroke. An unselected edge crossing near a label could paint its line over that label.

Fix: portal the label through xyflow's EdgeLabelRenderer instead (the pattern StageEdge/EdgeToolbar already use), which always paints after every edge's own <svg>, regardless of z-index or array order.

Bug 2 — label background can render fully transparent

background: var(--canvas-background) had no fallback value. That variable only resolves under a themed ancestor (body.light/.future-*/.vertex/.canvas per canvas/styles/variables.css); a host that mounts the canvas without one (e.g. a shadow-DOM host) got a transparent label with illegible text over the line.

Fix: added a --color-background fallback to both EdgeLabel.tsx and StageEdge.tsx's StageEdgeLabel.

New: Components/Edges/EdgeLabels Storybook page

Consolidates label documentation that was previously scattered across CanvasEdge.stories.tsx and SequenceEdge.stories.tsx (and, in one spot, stale — the old SequenceEdge EdgeLabels story still claimed labels render via "SVG foreignObject") into one dedicated reference page:

  • Orientation, Diff States — baseline label behavior
  • Crossing Labeled Edges — regression coverage for Bug 1
  • Themes — the same label across all 9 canvas themes side by side
  • Missing Theme Fallback — reproduces Bug 2's scenario directly (simulates an unthemed ancestor via CSS custom-property initial), and transparently documents the one remaining gap the fallback doesn't cover (no themed ancestor at all → background still transparent)
  • Overflow — long label text and short-edge crowding (known, undocumented limitations, not fixed in this PR)
  • Execution Status, Bent Path, Read Only — confirms the label composes correctly with execution-status coloring, multi-segment waypoint routing, and readonly mode
  • No Inline Editing — open question for the team, not resolved in this PR. Node labels support double-click-to-edit (BaseNode/NodeLabel's EditableLabel); edge labels don't (EdgeLabel is pointer-events: none, CanvasEdgeData has no onLabelChange). Clicking the label also passes through to the edge underneath and selects it instead, since the label isn't a hit target — the same root cause as the missing editing support, not a separate bug. The story lays out both paths forward:
    • Keep pure display (no change): clicking continues to select the edge; no new interaction model or engineering risk; renaming still requires going through whatever set data.label upstream.
    • Add inline editing for parity with node labels: needs an onLabelChange callback on CanvasEdgeData, pointer-events: auto on the label (likely gated on a handler being provided), an explicit decision on click semantics (does a single click still select the edge, does double-click enter edit mode like NodeLabel), an inline textarea reusing that pattern, and handling for empty labels / Escape-to-cancel / Enter-to-commit.

Out of scope (tracked as a follow-up)

SequenceEdge.stories.tsx's ExecutionStates/ValidationStates stories duplicate content already on CanvasEdge.stories.tsx (SequenceEdge is a thin preset over CanvasEdge, so the underlying status-to-color logic is identical). Left alone here to keep this PR focused; will be addressed separately.

Test plan

  • Full canvas test suite passes (1713 tests, 92 files)
  • EdgeLabel.test.tsx updated for the new EdgeLabelRenderer-based markup
  • Clean typecheck, clean lint, no em-dash violations
  • Verified visually in Storybook: crossing edges no longer obscure labels, background fallback works across themes, no regression on existing label stories (HandleRouting, etc.)

🤖 Generated with Claude Code

…nder crossing lines

Two bugs in CanvasEdge/SequenceEdge labels, plus a new Storybook reference
page documenting label behavior across themes and edge cases.

Bug 1 — line renders over the label:
EdgeLabel rendered as a raw foreignObject inside its own edge's <g>, so it
competed in the same per-edge z-index/DOM-order stack as every other edge's
stroke. An unselected edge crossing near a label could paint its line over
that label. Fixed by portaling the label through xyflow's EdgeLabelRenderer
instead (the pattern StageEdge/EdgeToolbar already use), which always
paints after every edge's own <svg>, regardless of z-index or array order.

Bug 2 — label background can render fully transparent:
`background: var(--canvas-background)` had no fallback value. That variable
only resolves under a themed ancestor (body.light/.future-*/.vertex/.canvas
per canvas/styles/variables.css); a host that mounts the canvas without one
(e.g. a shadow-DOM host) got a transparent label with illegible text over
the line. Added a `--color-background` fallback to both EdgeLabel.tsx and
StageEdge.tsx's StageEdgeLabel.

New: Components/Edges/EdgeLabels Storybook page
Consolidates label documentation that was previously scattered (and, in one
spot, stale — SequenceEdge.stories.tsx's old EdgeLabels story still claimed
labels render via "SVG foreignObject") into one dedicated reference page:
- Orientation, Diff States: baseline label behavior
- Crossing Labeled Edges: regression coverage for Bug 1
- Themes: the same label across all 9 canvas themes side by side
- Missing Theme Fallback: reproduces Bug 2's scenario directly (simulates
  an unthemed ancestor via CSS custom-property `initial`), and documents
  the one remaining gap the fallback doesn't cover (no themed ancestor at
  all, background still transparent)
- Overflow: long label text and short-edge crowding (known, undocumented
  limitations, not fixed here)
- Execution Status, Bent Path, Read Only: label composes correctly with
  execution-status coloring, multi-segment waypoint routing, and readonly
  mode

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 22, 2026 20:36
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (PT)
apollo-design 🟢 Ready Preview, Logs Jul 22, 2026, 02:03:25 PM
apollo-docs 🟢 Ready Preview, Logs Jul 22, 2026, 02:03:25 PM
apollo-landing 🟢 Ready Preview, Logs Jul 22, 2026, 02:03:25 PM
apollo-vertex 🟢 Ready Preview, Logs Jul 22, 2026, 02:03:25 PM

@github-actions

Copy link
Copy Markdown
Contributor

Dependency License Review

  • 1950 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1720
ISC 89
Apache-2.0 55
BSD-3-Clause 27
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
MIT-0 3
CC0-1.0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.2.4 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes two edge-label rendering issues in the apollo-react canvas: (1) labels being obscured by crossing edge strokes due to per-edge SVG stacking order, and (2) label backgrounds becoming transparent when canvas theme variables aren’t present. It also consolidates label behavior guidance into a new Storybook reference page.

Changes:

  • Portal EdgeLabel through xyflow’s EdgeLabelRenderer to ensure labels paint above all edge SVG strokes.
  • Add CSS variable fallbacks for label backgrounds (and hover background for StageEdgeLabel) to avoid transparent labels in unthemed/partially themed hosts.
  • Replace scattered/partially stale label Storybook content with a dedicated Components/Edges/EdgeLabels reference page.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/apollo-react/src/canvas/components/StageNode/StageEdge.tsx Adds background/hover background fallbacks for stage edge labels.
packages/apollo-react/src/canvas/components/Edges/shared/primitives/EdgeLabel.tsx Switches label rendering to EdgeLabelRenderer portal and adds background fallback styling via Tailwind classes.
packages/apollo-react/src/canvas/components/Edges/shared/primitives/EdgeLabel.test.tsx Updates unit tests to reflect portal-based markup and class-based styling.
packages/apollo-react/src/canvas/components/Edges/SequenceEdge.stories.tsx Removes the old label story and points readers to the new reference page.
packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx New consolidated Storybook page documenting label behavior across edge cases and themes.

Comment on lines 22 to 25
&:hover {
background: var(--canvas-background-hover);
background: var(--canvas-background-hover, var(--color-background-hover));
border-color: var(--canvas-border-hover);
}
Comment on lines +12 to +13
// Falls back to --color-background when a host doesn't import canvas/styles/variables.css
// (e.g. a shadow-DOM host), so the label never renders with a transparent background.
Comment on lines +385 to +392
* Simulates a host that does not theme its canvas ancestor. `--canvas-background`
* and `--color-background` are set to `initial` (the CSS-spec guaranteed-invalid
* value) on a wrapping div, which is what actually happens when a host mounts
* the canvas outside any `body.<theme>` / `.future-*` / `.vertex` / `.canvas`
* ancestor (for example, a shadow-DOM host). Column 2 shows the fix's fallback
* resolving; column 3 is the known remaining gap: when neither variable
* resolves, the label background is transparent.
*/
Comment on lines +420 to +423
<div style={{ ...columnStyle, '--canvas-background': 'initial' } as CSSProperties}>
<p style={{ fontSize: 13, margin: 0 }}>
--canvas-background unset (variables.css not imported): falls back to --color-background.
</p>
Comment on lines +433 to +436
...columnStyle,
'--canvas-background': 'initial',
'--color-background': 'initial',
} as CSSProperties
Comment on lines +439 to +442
<p style={{ fontSize: 13, margin: 0 }}>
Both unset, no themed ancestor at all: background is transparent. Known gap, not covered
by the current fallback chain.
</p>
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage + size by package

Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.

Package Coverage New-line coverage Packed (gzip) Unpacked vs main
@uipath/apollo-core 9.0% 43.84 MB 57.45 MB ±0
@uipath/apollo-react 37.1% 2.2% (2/90) 7.40 MB 28.28 MB +55 B
@uipath/apollo-wind 41.0% 397.6 KB 2.58 MB +6 B
@uipath/ap-chat 85.8% 43.43 MB 55.92 MB ±0

"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.

Copilot AI review requested due to automatic review settings July 22, 2026 20:42
@github-actions github-actions Bot added size:XXL 1,000+ changed lines. and removed size:XL 500-999 changed lines. labels Jul 22, 2026
@dbacomputer
dbacomputer force-pushed the add-label-background-color branch from 44a5899 to 69aed8c Compare July 22, 2026 20:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (3)

packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx:392

  • The MissingThemeFallback story comment says setting custom props to initial is a "guaranteed-invalid value", but initial is a valid CSS-wide keyword and will not trigger var() fallback. The comment should describe the actual mechanism used to force fallback/unresolved variables.
/**
 * Simulates a host that does not theme its canvas ancestor. `--canvas-background`
 * and `--color-background` are set to `initial` (the CSS-spec guaranteed-invalid
 * value) on a wrapping div, which is what actually happens when a host mounts
 * the canvas outside any `body.<theme>` / `.future-*` / `.vertex` / `.canvas`
 * ancestor (for example, a shadow-DOM host). Column 2 shows the fix's fallback
 * resolving; column 3 is the known remaining gap: when neither variable
 * resolves, the label background is transparent.
 */

packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx:423

  • Setting --canvas-background: initial does not simulate an unresolved/missing variable for var() fallback (it makes the resolved value the CSS keyword initial, which yields a transparent background and skips the fallback). Use a missing var() reference to make the custom property invalid at computed-value time so the fallback to --color-background is exercised.
      <div style={{ ...columnStyle, '--canvas-background': 'initial' } as CSSProperties}>
        <p style={{ fontSize: 13, margin: 0 }}>
          --canvas-background unset (variables.css not imported): falls back to --color-background.
        </p>

packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx:436

  • Similarly, setting both variables to initial will not demonstrate the "neither resolves" scenario; it sets both to a valid keyword and prevents var() fallback. For the intended demo, both variables should be forced invalid (via a missing var()) so the label background becomes transparent due to an unresolved chain.
            ...columnStyle,
            '--canvas-background': 'initial',
            '--color-background': 'initial',
          } as CSSProperties

Comment on lines +314 to +330
/**
* The same labeled edge under every canvas theme. Each cell forces its theme
* class on a wrapping div (the same selectors variables.css matches on
* `body.<theme>`), independent of the global Storybook theme toolbar, so all
* nine render simultaneously for comparison.
*/
const THEME_CLASSES = [
'light',
'dark',
'light-hc',
'dark-hc',
'future-light',
'future-dark',
'wireframe',
'vertex',
'canvas',
] as const;
Comment on lines +372 to +382
export const Themes: Story = {
render: () => <ThemesStory />,
parameters: {
docs: {
description: {
story:
'The same labeled edge rendered under all nine canvas themes at once, to compare label contrast against each theme\'s background and border colors.',
},
},
},
};
Copilot AI review requested due to automatic review settings July 22, 2026 20:54
…view

Node labels support double-click-to-edit (BaseNode/NodeLabel's
EditableLabel), but edge labels do not. EdgeLabel is pointer-events: none
and CanvasEdgeData has no onLabelChange field, so there's no way to rename
a label from the canvas. Clicking the label also passes straight through
to the edge underneath and selects the whole edge, since the label isn't a
hit target itself, the same root cause as the missing editing support:
enabling pointer events on the label to support editing also changes what
clicking it does today.

Adds a story that surfaces both observations together and lays out two
paths forward (keep labels pure-display vs. add inline editing, with the
concrete changes each would require) as an open question for design/
product review, rather than a decision made here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dbacomputer
dbacomputer force-pushed the add-label-background-color branch from 69aed8c to e81a659 Compare July 22, 2026 20:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines 12 to +13
color: var(--canvas-foreground);
background: var(--canvas-background);
background: var(--canvas-background, var(--color-background));
Copilot AI review requested due to automatic review settings July 22, 2026 21:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx:425

  • The story comment says setting custom properties to initial is “CSS-spec guaranteed-invalid”, but initial is a valid keyword for background/border and won’t exercise the var(--canvas-background, var(--color-background)) fallback chain. Consider describing (and using) an invalid-at-computed-value setup instead (e.g. a self-referential var() cycle) so the story matches what the component actually does.
 * Simulates a host that does not theme its canvas ancestor. `--canvas-background`
 * and `--color-background` are set to `initial` (the CSS-spec guaranteed-invalid
 * value) on a wrapping div, which is what actually happens when a host mounts
 * the canvas outside any `body.<theme>` / `.future-*` / `.vertex` / `.canvas`
 * ancestor (for example, a shadow-DOM host). Column 2 shows the fix's fallback

packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx:459

  • Setting --canvas-background: 'initial' here won’t trigger the fallback to --color-background because the variable is still defined (and resolves to the valid initial keyword). Use an invalid-at-computed-value value (e.g. a self-referential var) so this column actually demonstrates the fallback behavior described in the text.
      <div style={{ ...columnStyle, '--canvas-background': 'initial' } as CSSProperties}>
        <p style={{ fontSize: 13, margin: 0 }}>
          --canvas-background unset (variables.css not imported): falls back to --color-background.
        </p>

packages/apollo-react/src/canvas/components/Edges/EdgeLabel.stories.tsx:472

  • Same issue as the prior column: initial doesn’t simulate “unset”. If the goal is to show the remaining gap when neither --canvas-background nor --color-background resolve, make both variables invalid at computed-value time (self-referential) so the label’s background computation actually ends up transparent.
          {
            ...columnStyle,
            '--canvas-background': 'initial',
            '--color-background': 'initial',
          } as CSSProperties

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:apollo-react size:XXL 1,000+ changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants