Skip to content

Commit d0cdebd

Browse files
dbacomputerclaude
andcommitted
docs(storybook): adornment slot Option A/B comparison with full header context
- Add Option A (inside border) vs Option B (on border) placement diagrams to both BaseNode V2 and LoopNode V2 anatomy pages - LoopNode V2 diagrams use full interactive header mocks (iteration picker + Sequential badge + real adornment icons) so header crowding is visible - Pros/cons bullet lists per option; two-point trade-off callout covering canvas density and the topRight badge / header crowding tension - Default Option B anatomy demo to All-selected state - Switch Demo page to Option B pill (LoopExecutionCanvasNodePill) and rename story to "Demo - Option B" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0063013 commit d0cdebd

2 files changed

Lines changed: 263 additions & 100 deletions

File tree

packages/apollo-react/src/canvas/components/BaseNode/BaseNodeV2.stories.tsx

Lines changed: 119 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -671,73 +671,138 @@ function AnatomyStory() {
671671
<div>
672672
<h2 className="text-base font-semibold">Adornment Slots</h2>
673673
<p className="mt-1 text-sm text-foreground-muted">
674-
Four 20×20 px slots, inset 6 px from each corner. Each slot runs its priority chain and
675-
renders the first matching condition.
674+
Four 20×20 px slots at each corner. Each slot runs its priority chain and renders the
675+
first matching condition. Two placement options are under consideration.
676676
</p>
677677
</div>
678678

679-
{/* Diagram */}
680-
<div className="flex justify-center rounded-xl border border-border bg-surface px-12 py-10">
681-
<div className="flex items-center gap-10">
682-
{/* Left labels */}
683-
<div className="flex flex-col gap-10">
684-
{SLOT_DOCS.filter((_, i) => i % 2 === 0).map(({ slot, dot, rule }) => (
685-
<div key={slot} className="flex items-center justify-end gap-2.5">
686-
<div className="text-right">
687-
<div className="font-mono text-xs font-semibold">{slot}</div>
688-
<div className="text-[11px] text-foreground-muted">{rule}</div>
689-
</div>
690-
<div className="flex items-center gap-1">
691-
<div className="h-px w-6 bg-border" />
692-
<div className={`h-2.5 w-2.5 shrink-0 rounded-full ${dot}`} />
679+
{/* Option A / Option B comparison */}
680+
<div className="flex flex-col gap-6">
681+
682+
{/* Option A — inside border */}
683+
<div className="flex flex-col gap-4 rounded-xl border border-border bg-surface p-6">
684+
<div>
685+
<h3 className="text-sm font-semibold">Option A — Inside border</h3>
686+
<ul className="mt-1.5 space-y-1 text-[12px]">
687+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-emerald-500"></span><span className="text-foreground-muted">Never overflows node bounds — safe for dense canvas layouts where nodes sit close together.</span></li>
688+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-emerald-500"></span><span className="text-foreground-muted">Predictable hit area — all interactions stay within the node boundary.</span></li>
689+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-red-400"></span><span className="text-foreground-muted">Competes with node content near corners, reducing usable space.</span></li>
690+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-red-400"></span><span className="text-foreground-muted">Badge can visually merge with the node background, reducing contrast.</span></li>
691+
</ul>
692+
</div>
693+
<div className="flex flex-1 items-center justify-center py-6">
694+
<div className="flex items-center gap-10">
695+
{/* Left labels */}
696+
<div className="flex flex-col gap-10">
697+
{SLOT_DOCS.filter((_, i) => i % 2 === 0).map(({ slot, dot, rule }) => (
698+
<div key={slot} className="flex items-center justify-end gap-2.5">
699+
<div className="text-right">
700+
<div className="font-mono text-xs font-semibold">{slot}</div>
701+
<div className="text-[11px] text-foreground-muted">{rule}</div>
702+
</div>
703+
<div className="flex items-center gap-1">
704+
<div className="h-px w-6 bg-border" />
705+
<div className={`h-2.5 w-2.5 shrink-0 rounded-full ${dot}`} />
706+
</div>
707+
</div>
708+
))}
709+
</div>
710+
{/* Node mock — inset 6px */}
711+
<div className="relative shrink-0" style={{ width: 96, height: 96 }}>
712+
<div className="flex h-full w-full items-center justify-center border border-border bg-surface-overlay" style={{ borderRadius: 32 }}>
713+
<div className="flex items-center justify-center bg-surface" style={{ width: 80, height: 80, borderRadius: 24 }}>
714+
<CanvasIcon icon="agent" size={40} color="var(--color-foreground-de-emp)" />
715+
</div>
693716
</div>
717+
<div className="absolute flex items-center justify-center" style={{ top: 6, left: 6, width: 20, height: 20 }}><BreakpointIndicator /></div>
718+
<div className="absolute flex items-center justify-center" style={{ top: 6, right: 6, width: 20, height: 20 }}><ExecutionStatusIcon status="Completed" /></div>
719+
<div className="absolute flex items-center justify-center" style={{ bottom: 6, left: 6, width: 20, height: 20 }}><ExecutionStartPointIndicator /></div>
720+
<div className="absolute" style={{ bottom: 6, right: 6, width: 20, height: 20 }}><LoopCountPill count={3} /></div>
694721
</div>
695-
))}
696-
</div>
697-
698-
{/* Node mock at 96×96 — actual CSS from BaseNode */}
699-
<div className="relative shrink-0" style={{ width: 96, height: 96 }}>
700-
<div
701-
className="flex h-full w-full items-center justify-center border border-border bg-surface-overlay"
702-
style={{ borderRadius: 32 }}
703-
>
704-
<div
705-
className="flex items-center justify-center bg-surface"
706-
style={{ width: 80, height: 80, borderRadius: 24 }}
707-
>
708-
<CanvasIcon icon="agent" size={40} color="var(--color-foreground-de-emp)" />
722+
{/* Right labels */}
723+
<div className="flex flex-col gap-10">
724+
{SLOT_DOCS.filter((_, i) => i % 2 !== 0).map(({ slot, dot, rule }) => (
725+
<div key={slot} className="flex items-center gap-2.5">
726+
<div className="flex items-center gap-1">
727+
<div className={`h-2.5 w-2.5 shrink-0 rounded-full ${dot}`} />
728+
<div className="h-px w-6 bg-border" />
729+
</div>
730+
<div>
731+
<div className="font-mono text-xs font-semibold">{slot}</div>
732+
<div className="text-[11px] text-foreground-muted">{rule}</div>
733+
</div>
734+
</div>
735+
))}
709736
</div>
710737
</div>
711-
<div className="absolute flex items-center justify-center" style={{ top: 6, left: 6, width: 20, height: 20 }}>
712-
<BreakpointIndicator />
713-
</div>
714-
<div className="absolute flex items-center justify-center" style={{ top: 6, right: 6, width: 20, height: 20 }}>
715-
<ExecutionStatusIcon status="Completed" />
716-
</div>
717-
<div className="absolute flex items-center justify-center" style={{ bottom: 6, left: 6, width: 20, height: 20 }}>
718-
<ExecutionStartPointIndicator />
719-
</div>
720-
<div className="absolute" style={{ bottom: 6, right: 6, width: 20, height: 20 }}>
721-
<LoopCountPill count={3} />
722-
</div>
723738
</div>
739+
</div>
724740

725-
{/* Right labels */}
726-
<div className="flex flex-col gap-10">
727-
{SLOT_DOCS.filter((_, i) => i % 2 !== 0).map(({ slot, dot, rule }) => (
728-
<div key={slot} className="flex items-center gap-2.5">
729-
<div className="flex items-center gap-1">
730-
<div className={`h-2.5 w-2.5 shrink-0 rounded-full ${dot}`} />
731-
<div className="h-px w-6 bg-border" />
732-
</div>
733-
<div>
734-
<div className="font-mono text-xs font-semibold">{slot}</div>
735-
<div className="text-[11px] text-foreground-muted">{rule}</div>
741+
{/* Option B — on border (50/50) */}
742+
<div className="flex flex-col gap-4 rounded-xl border border-border bg-surface p-6">
743+
<div>
744+
<h3 className="text-sm font-semibold">Option B — On border</h3>
745+
<ul className="mt-1.5 space-y-1 text-[12px]">
746+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-emerald-500"></span><span className="text-foreground-muted">Familiar notification badge pattern — immediately recognisable to most users.</span></li>
747+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-emerald-500"></span><span className="text-foreground-muted">Pops clearly against both the node background and the canvas — stronger contrast.</span></li>
748+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-red-400"></span><span className="text-foreground-muted">Extends slightly outside node bounds — may overlap adjacent nodes or edges at tight spacing.</span></li>
749+
<li className="flex items-start gap-1.5"><span className="mt-0.5 text-red-400"></span><span className="text-foreground-muted">Canvas layout needs to account for the overflow margin around each node.</span></li>
750+
</ul>
751+
</div>
752+
<div className="flex flex-1 items-center justify-center py-6">
753+
<div className="flex items-center gap-10">
754+
{/* Left labels */}
755+
<div className="flex flex-col gap-10">
756+
{SLOT_DOCS.filter((_, i) => i % 2 === 0).map(({ slot, dot, rule }) => (
757+
<div key={slot} className="flex items-center justify-end gap-2.5">
758+
<div className="text-right">
759+
<div className="font-mono text-xs font-semibold">{slot}</div>
760+
<div className="text-[11px] text-foreground-muted">{rule}</div>
761+
</div>
762+
<div className="flex items-center gap-1">
763+
<div className="h-px w-6 bg-border" />
764+
<div className={`h-2.5 w-2.5 shrink-0 rounded-full ${dot}`} />
765+
</div>
766+
</div>
767+
))}
768+
</div>
769+
{/* Node mock — on border (-10px = -½ × 20px slot size) */}
770+
<div className="relative shrink-0" style={{ width: 96, height: 96 }}>
771+
<div className="flex h-full w-full items-center justify-center border border-border bg-surface-overlay" style={{ borderRadius: 32 }}>
772+
<div className="flex items-center justify-center bg-surface" style={{ width: 80, height: 80, borderRadius: 24 }}>
773+
<CanvasIcon icon="agent" size={40} color="var(--color-foreground-de-emp)" />
774+
</div>
736775
</div>
776+
<div className="absolute flex items-center justify-center" style={{ top: -1, left: -1, width: 20, height: 20 }}><BreakpointIndicator /></div>
777+
<div className="absolute flex items-center justify-center" style={{ top: -1, right: -1, width: 20, height: 20 }}><ExecutionStatusIcon status="Completed" /></div>
778+
<div className="absolute flex items-center justify-center" style={{ bottom: -1, left: -1, width: 20, height: 20 }}><ExecutionStartPointIndicator /></div>
779+
<div className="absolute" style={{ bottom: -1, right: -1, width: 20, height: 20 }}><LoopCountPill count={3} /></div>
737780
</div>
738-
))}
781+
{/* Right labels */}
782+
<div className="flex flex-col gap-10">
783+
{SLOT_DOCS.filter((_, i) => i % 2 !== 0).map(({ slot, dot, rule }) => (
784+
<div key={slot} className="flex items-center gap-2.5">
785+
<div className="flex items-center gap-1">
786+
<div className={`h-2.5 w-2.5 shrink-0 rounded-full ${dot}`} />
787+
<div className="h-px w-6 bg-border" />
788+
</div>
789+
<div>
790+
<div className="font-mono text-xs font-semibold">{slot}</div>
791+
<div className="text-[11px] text-foreground-muted">{rule}</div>
792+
</div>
793+
</div>
794+
))}
795+
</div>
796+
</div>
739797
</div>
740798
</div>
799+
800+
</div>
801+
802+
{/* Key trade-off callout */}
803+
<div className="rounded-xl border border-border bg-surface-overlay px-5 py-4 text-[12px] text-foreground-muted">
804+
<span className="font-semibold text-foreground">Key trade-off — </span>
805+
Option A is the safer choice when canvas nodes are densely packed, since badges never stray outside the node boundary. Option B is more visually distinct and follows a pattern users already recognise from notification systems, but requires the canvas layout to reserve a small overflow margin around each node to avoid clipping into neighbours.
741806
</div>
742807

743808
{/* Reference table */}

0 commit comments

Comments
 (0)