feat(storybook): add Flow UX layout patterns - #1082
Conversation
|
Apollo Coded App preview deployments are ready.
|
Dependency License Review
License distribution
Excluded packages
|
📊 Coverage + size by packagePer-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.
"Coverage" is each package's own |
There was a problem hiding this comment.
Pull request overview
Adds Flow UX layout-pattern demonstrations to Storybook, including standalone, Dockview/VS Code, variables, node, takeover, and agent layouts.
Changes:
- Adds reusable Flow workbench and panel compositions.
- Extends sidebar and node-panel APIs; adds Dockview styling and dependency.
- Registers and organizes Apollo Wind layout-pattern stories.
Reviewed changes
Copilot reviewed 13 out of 15 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Summary |
|---|---|
pnpm-lock.yaml |
Locks Dockview dependencies. |
packages/apollo-react/src/canvas/stories/templates/TemplateGuidelines.stories.tsx |
Adds template usage guidance. |
packages/apollo-react/src/canvas/stories/templates/FlowVSCode.stories.tsx |
Adds Dockview layout stories. |
packages/apollo-react/src/canvas/stories/templates/Flow.stories.tsx |
Adds Flow compositions; final comments identify state-sync, provider-isolation, title-bar, accessibility, and synthetic-data issues. |
packages/apollo-react/src/canvas/stories/templates/Flow.stories.css |
Styles Dockview layouts. |
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.types.ts |
Adds drag-handle props; coverage test requested. |
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx |
Forwards drag-handle attributes. |
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.stories.tsx |
Updates panel stories. |
packages/apollo-react/src/canvas/components/Flow.stories.tsx |
Updates canvas story organization. |
packages/apollo-react/src/canvas/components/CanvasTakeoverModal/CanvasTakeoverModal.tsx |
Updates takeover styling. |
packages/apollo-react/src/canvas/components/CanvasLeftSidebar/CanvasLeftSidebar.tsx |
Adds configurable sidebar sizing and headers; coverage tests requested. |
packages/apollo-react/package.json |
Adds Dockview development dependency. |
apps/storybook/src/patterns/LayoutPatterns.stories.tsx |
Registers layout-pattern stories. |
apps/storybook/.storybook/preview.tsx |
Updates Storybook ordering. |
apps/storybook/.storybook/main.ts |
Registers the patterns directory. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (3)
apps/storybook/src/patterns/LayoutPatterns.stories.tsx:28
- This story is named
UX Variables, but it renders the node-properties variant. As a result, selecting this story shows the Send Email properties panel instead of the variables/parameters demonstration; use thevariablesvariant here.
render: () => <FullWorkbenchComposition rightPanelVariant="node" />,
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx:86
- The new
dragHandlePropspath is what enables the standalone bottom-panel reorder behavior, but the existingNodePropertyPaneltests do not verify that native drag attributes and handlers are forwarded to the handle. Add a focused test fordraggable/onDragStartso this integration cannot regress.
{...dragHandleProps}
data-slot="node-property-panel-drag-handle"
className="grid size-8 cursor-grab touch-none place-items-center text-foreground-subtle active:cursor-grabbing"
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx:86
- The new drag handle is a plain
div. The story passes native drag handlers, but the element is not focusable and has no keyboard interaction, so keyboard-only users cannot access the panel reordering demonstrated by this API. Provide a keyboard-operable reorder alternative in addition to the pointer drag behavior.
<div
{...dragHandleProps}
data-slot="node-property-panel-drag-handle"
className="grid size-8 cursor-grab touch-none place-items-center text-foreground-subtle active:cursor-grabbing"
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| showContentHeader = true, | ||
| variant = 'default', | ||
| expandedContentWidth = CANVAS_LEFT_SIDEBAR_WIDTH, |
| */ | ||
| panelTitle?: string; | ||
| /** Native interaction props applied to the dedicated title-bar drag handle. */ | ||
| dragHandleProps?: Omit<HTMLAttributes<HTMLDivElement>, 'className'>; |
| className?: string; | ||
| onClose?: () => void; | ||
| dragHandleProps?: NodePropertyPanelProps['dragHandleProps']; | ||
| }) { | ||
| return ( | ||
| <NodePropertyPanel | ||
| panelTitle="Properties" |
| <button | ||
| key={id as string} | ||
| type="button" | ||
| className="grid size-7 place-items-center rounded-md hover:bg-surface-hover" | ||
| > |
| <Label className="text-xs"> | ||
| {field.label} |
| }, [fitWorkflow, nodesInitialized]); | ||
|
|
||
| const tidy = useCallback(() => { | ||
| setNodes(createFlowGraph(workflowVariant).nodes); |
| const graph = createFlowGraph('forms'); | ||
| setNodes(graph.nodes); | ||
| setEdges(graph.edges); |
| headerActions={<Button size="sm">Run test</Button>} | ||
| > | ||
| <div className="h-full min-h-[480px]"> | ||
| <CanvasViewport /> |
| <span className="truncate rounded bg-brand-subtle px-1.5 py-0.5"> | ||
| anurag.krishna@uipath.com | ||
| </span> |
Storybook visual diffBaseline is the deployed main Storybook, so changes merged to main after this branch was last updated can also appear here. Logs Updated (PT): Aug 26, 2026, 05:31:56 PM |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 15 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (6)
Previously missed (2) — in code that hasn't changed since the last review.
apps/storybook/src/patterns/LayoutPatterns.stories.tsx:28
- The story is named “UX Variables” but renders
rightPanelVariant="node", which shows the Send Email node-properties experience. Use thevariablesvariant here; the separateVariableSelectstory already demonstrates that variant correctly.
render: () => <FullWorkbenchComposition rightPanelVariant="node" />,
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx:84
dragHandlePropsadds a new forwarding contract, butNodePropertyPanel.test.tsxhas no regression test that renders these props and verifies they reach the dedicated handle. Add coverage for the draggable/ARIA or event-handler forwarding used by the new Flow compositions.
{...dragHandleProps}
packages/apollo-react/src/canvas/components/CanvasLeftSidebar/CanvasLeftSidebar.tsx:204
- The new
showContentHeaderandexpandedContentWidthbranches are used by the Agent and sidebar layout stories, butCanvasLeftSidebar.test.tsxcovers only the default header and width. Add assertions for the hidden-header/custom-width contract so these new layout props are protected.
{showContentHeader && (
<header className="flex h-12 shrink-0 items-center justify-between gap-3 px-4">
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.stories.tsx:3172
- These tab values are swapped relative to their labels and the existing
QuickFormPropertiesPanelPreview(branchingfor Branching anderror-handlingfor Error handling). Selecting Branching currently targets the error-handling slot, while Error handling targetsadvanced; align the trigger IDs and the matchingTabsContentIDs so future content appears under the correct tab.
<TabsTrigger value="error-handling" className={TAB_TRIGGER_CLASS}>
Branching
</TabsTrigger>
<TabsTrigger value="advanced" className={TAB_TRIGGER_CLASS}>
Error handling
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.stories.tsx:3007
- Defaulting
onCloseto a no-op makes the standalone Quick Form story render a Close button that does nothing.NodePropertyPanelintentionally only renders that button when a callback exists; leave this optional callback undefined unless the story manages an open state, so the demonstration does not expose an inert control.
onClose = () => {},
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx:86
- This is a plain, non-focusable
div; native/pointer drag props provide no keyboard interaction or accessible drag semantics. In the new standalone and bottom-panel layouts, this is the only way to move/reorder panels, so keyboard and screen-reader users cannot operate that layout. Add a labelled keyboard move operation (or separate accessible move controls) alongside pointer dragging.
<div
{...dragHandleProps}
data-slot="node-property-panel-drag-handle"
className="grid size-8 cursor-grab touch-none place-items-center text-foreground-subtle active:cursor-grabbing"
| onInsertVariable={(variable) => | ||
| onParameterValueChange(parameterKey, variable.value ?? variable.label) | ||
| } |
| collapsedSize={80} | ||
| defaultSize={368} | ||
| minSize={368} |
| collapsedSize={80} | ||
| defaultSize={80} | ||
| minSize={368} |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 16 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (1)
packages/apollo-react/src/canvas/components/CanvasLeftSidebar/CanvasLeftSidebar.tsx:128
- The new
showContentHeaderandexpandedContentWidthbranches are not covered by the existingCanvasLeftSidebartests, which only exercise the default header and width. They are used by the new agent composition, so add assertions that the header/collapse control is omitted when disabled and that the custom width is applied; otherwise those layout regressions will go unnoticed.
showContentHeader = true,
variant = 'default',
expandedContentWidth = CANVAS_LEFT_SIDEBAR_WIDTH,
| {...dragHandleProps} | ||
| data-slot="node-property-panel-drag-handle" | ||
| className="grid size-8 cursor-grab touch-none place-items-center text-foreground-subtle active:cursor-grabbing" |
| layout={layout} | ||
| layoutOptions={panelLayoutOptions} | ||
| onLayoutChange={onLayoutChange} | ||
| onPanelToggle={onPanelToggle} | ||
| onPropertiesClick={onPropertiesClick} |
| api.addPanel({ | ||
| id: 'canvas', | ||
| component: 'canvas', | ||
| title: 'Canvas', | ||
| minimumWidth: 320, | ||
| minimumHeight: 240, | ||
| }); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 16 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (4)
Previously missed (1) — in code that hasn't changed since the last review.
apps/storybook/src/patterns/LayoutPatterns.stories.tsx:27
- This story is named
UX Variables, but it rendersrightPanelVariant="node", which shows the Send Email node panel rather than the unified parameters/variables demonstration. Change the variant tovariablesso the story matches its advertised layout.
render: () => <FullWorkbenchComposition rightPanelVariant="node" />,
packages/apollo-react/src/canvas/components/CanvasLeftSidebar/CanvasLeftSidebar.tsx:148
- The new
expandedContentWidthbranch is not covered byCanvasLeftSidebar.test.tsx, which only asserts the default width. Add a test for a custom width because the Flow agent layout relies on this value for its canvas/sidebar sizing.
style={{
width: isExpanded
? CANVAS_LEFT_SIDEBAR_RAIL_WIDTH + expandedContentWidth
: CANVAS_LEFT_SIDEBAR_COLLAPSED_WIDTH,
packages/apollo-react/src/canvas/components/CanvasLeftSidebar/CanvasLeftSidebar.tsx:203
- The new
showContentHeaderbranch is used by the Flow agent layout but has no test coverage. Add a test asserting that the header and collapse action are omitted when it is false, so this host-specific layout option does not regress.
{showContentHeader && (
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx:85
- This new
dragHandlePropsforwarding is the mechanism used by the panel-drag demonstrations, butNodePropertyPanel.test.tsxdoes not assert that native props or drag events reach the dedicated handle. Add a regression test for the forwardeddata-slothandle and a draggable/drag-event prop.
{...dragHandleProps}
data-slot="node-property-panel-drag-handle"
| InputGroupButton, | ||
| InputGroupInput, | ||
| Label, | ||
| type PanelImperativeHandle, |
| } from '@uipath/apollo-wind'; | ||
| import type { DockviewApi, DockviewReadyEvent, IDockviewPanelProps } from 'dockview-react'; | ||
| import { DockviewReact } from 'dockview-react'; | ||
| import 'dockview-react/dist/styles/dockview.css'; |
| export const Variables: Story = { | ||
| name: 'UX Variables', | ||
| render: () => <FullWorkbenchComposition rightPanelVariant="node" />, | ||
| }; |
| <TabsTrigger value="error-handling" className={TAB_TRIGGER_CLASS}> | ||
| Branching | ||
| </TabsTrigger> | ||
| <TabsTrigger value="advanced" className={TAB_TRIGGER_CLASS}> | ||
| Error handling | ||
| </TabsTrigger> |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 18 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (2)
packages/apollo-react/src/canvas/components/NodePropertyPanel/NodePropertyPanel.tsx:87
- A new public prop (dragHandleProps) is introduced but there is no coverage ensuring the props are actually forwarded to the drag-handle element. Since NodePropertyPanel already has unit tests, adding a small test that asserts an event handler / aria attribute from dragHandleProps is present would prevent regressions.
<div
{...dragHandleProps}
data-slot="node-property-panel-drag-handle"
className="grid size-8 cursor-grab touch-none place-items-center text-foreground-subtle active:cursor-grabbing"
>
packages/apollo-react/src/canvas/components/CanvasLeftSidebar/CanvasLeftSidebar.tsx:56
- CanvasLeftSidebar adds new public props (showContentHeader, expandedContentWidth) but the existing unit tests only validate the default width/header behavior. Adding tests that (1) verifies the header is omitted when showContentHeader={false} and (2) verifies the overall width uses expandedContentWidth would prevent regressions.
bottomItems?: readonly CanvasLeftSidebarItem[];
/** Additional controls rendered before the collapse button in the panel header. */
headerActions?: ReactNode;
/** Whether to render the expanded content-panel header. Defaults to true. */
showContentHeader?: boolean;
/** Docked sidebars are square; floating sidebars use rounded, elevated chrome. */
variant?: 'default' | 'floating';
/** Width of the expanded content panel. Defaults to 288px. */
expandedContentWidth?: number;
| import type { Meta, StoryObj } from '@storybook/react-vite'; | ||
| import { NodePatternComposition } from '../../../../packages/apollo-react/src/canvas/stories/templates/Flow.stories'; | ||
| import { withCanvasProviders } from '../../../../packages/apollo-react/src/canvas/storybook-utils'; |
| import type { Meta, StoryObj } from '@storybook/react-vite'; | ||
| import { | ||
| AgentExperienceComposition, | ||
| FullWorkbenchComposition, | ||
| mapTemplateThemeToChat, | ||
| ResponsiveWorkbenchComposition, | ||
| } from '../../../../packages/apollo-react/src/canvas/stories/templates/Flow.stories'; | ||
| import { withCanvasProviders } from '../../../../packages/apollo-react/src/canvas/storybook-utils'; |
- Render Flow expression fields (Send Email, Variables, Rules) through apollo-wind's PromptEditor so inserted variables are clickable, hoverable, and removable chips instead of raw text - Fix PromptEditor token chip colors (unreadable text on bright background) and truncate long paths with a hover tooltip for detail - Add UX Variable collection pattern: a keyword-only expression field style plus a collection filter panel matching the existing Rule panel's visual conventions - Add UX Responsive behaviors pattern demonstrating sidebar-then-panel auto-collapse as the workbench narrows, with a resizable properties panel (min/max enforced) and a live status readout
e4e0f08 to
af155fe
Compare
| export function DefaultEntryPointIndicator() { | ||
| return ( | ||
| <CanvasTooltip content="Default entry point" placement="bottom"> | ||
| <TriggerBottomAdornment aria-label="Default entry point"> |
| height: 96, | ||
| data: { | ||
| details: { | ||
| tooltip: 'Schedule trigger — default entry point', |
| return tokens; | ||
| } | ||
|
|
||
| /** Inverse of {@link expressionValueToTokens} — rejoins tokens back into a flat expression string. */ |
| <InputGroupAddon align="inline-start"> | ||
| <LockToggleButton locked={locked} onLockedChange={onLockedChange} /> | ||
| {leadingAddon ?? <LockToggleButton locked={locked} onLockedChange={onLockedChange} />} | ||
| </InputGroupAddon> |
Summary
Validation