Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"storybook:build": "storybook build",
"clean": "rm -rf storybook-static",
"format": "biome format --write .",
"format:check": "biome format ."
"format:check": "biome format .",
"sync:flow-nodes": "tsx scripts/sync-flow-node-manifests.mts"
},
"dependencies": {
"@mui/material": "^5.18.0",
Expand Down
101 changes: 101 additions & 0 deletions apps/storybook/scripts/sync-flow-node-manifests.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Snapshot Flow Workbench's built-in node definitions for the Flow Node Panels
* pattern page. Apollo can't depend on Flow (Flow depends on Apollo), so the
* page reads a copied JSON snapshot instead of importing `@uipath/flow-core`.
*
* Usage, from the repository root:
* pnpm --filter storybook-app sync:flow-nodes <path-to-flow-workbench>
*
* Reads Flow's built manifest entry, so build it first in the Flow checkout:
* pnpm --filter @uipath/flow-schema build && pnpm --filter @uipath/flow-core build
*
* `--from <module>` reads `ootbNodes`, `inlineAgentNodes` and `categories` from
* another ESM module instead, for a Flow checkout that can't build.
*/

import { execFileSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { parseArgs } from 'node:util';

type Manifest = {
nodeType: string;
version: string;
category?: string;
description?: string;
display?: { label?: string; icon?: string; description?: string };
inputDefaults?: Record<string, unknown>;
form?: unknown;
};

type Category = {
id: string;
name: string;
parentId?: string;
sortOrder?: number;
icon?: string;
};

const { positionals, values } = parseArgs({
allowPositionals: true,
options: { from: { type: 'string' } },
});

const flowRoot = positionals[0];
if (!flowRoot) {
console.error('Usage: sync-flow-node-manifests <path-to-flow-workbench> [--from <module>]');
process.exit(1);
}

const flowDir = resolve(flowRoot);
const entry = resolve(values.from ?? resolve(flowDir, 'packages/flow-core/dist/manifest.js'));
const { ootbNodes, inlineAgentNodes, categories } = (await import(pathToFileURL(entry).href)) as {
ootbNodes: Manifest[];
inlineAgentNodes: Manifest[];
categories: Category[];
};

function toEntry(manifest: Manifest, audience: 'public' | 'inline-agent') {
return {
nodeType: manifest.nodeType,
version: manifest.version,
category: manifest.category ?? null,
label: manifest.display?.label ?? manifest.nodeType,
icon: manifest.display?.icon ?? null,
description: manifest.description ?? manifest.display?.description ?? null,
audience,
inputDefaults: manifest.inputDefaults ?? {},
form: manifest.form ?? null,
};
}

const commit = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: flowDir })
.toString()
.trim();

const snapshot = {
source: {
repository: 'UiPath/flow-workbench',
commit,
generatedAt: new Date().toISOString().slice(0, 10),
},
categories: categories.map(({ id, name, parentId, sortOrder, icon }) => ({
id,
name,
parentId: parentId ?? null,
sortOrder: sortOrder ?? 0,
icon: icon ?? null,
})),
nodes: [
...ootbNodes.map((manifest) => toEntry(manifest, 'public')),
...inlineAgentNodes.map((manifest) => toEntry(manifest, 'inline-agent')),
],
};

const outFile = resolve(
dirname(fileURLToPath(import.meta.url)),
'../src/patterns/flow-node-panels/flow-node-manifests.json'
);
writeFileSync(outFile, `${JSON.stringify(snapshot, null, 2)}\n`);
console.log(`Wrote ${snapshot.nodes.length} Flow nodes (flow-workbench@${commit}) to ${outFile}`);
26 changes: 26 additions & 0 deletions apps/storybook/src/patterns/LayoutPatterns.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { withCanvasProviders } from '@uipath/apollo-react/canvas/storybook-utils';
import { useArgs } from 'storybook/preview-api';
import { FullWorkbenchComposition } from '../../../../packages/apollo-react/src/canvas/stories/templates/Flow.stories';
import { DapLayoutsPage } from './DapLayoutsPage';
import { FlowNodePanelsPage } from './flow-node-panels/FlowNodePanelsPage';
import { FLOW_NODE_PANEL_ITEMS } from './flow-node-panels/flow-node-catalog';

const meta = {
title: 'Apollo Wind/Patterns/Layout Patterns',
Expand All @@ -14,6 +17,29 @@ const meta = {
export default meta;
type Story = StoryObj<typeof meta>;

/**
* Every Flow Workbench node on one canvas; selecting a node opens its right
* panel. The `node` arg (e.g. `&args=node:core-logic-delay`) deep-links to a
* single panel.
*/
export const FlowNodePanels: StoryObj<{ node: string }> = {
name: 'Flow Node Panels',
args: { node: '' },
argTypes: {
node: {
description: 'The node whose right panel is open, by slug.',
control: 'select',
options: ['', ...FLOW_NODE_PANEL_ITEMS.map((item) => item.slug)],
},
},
render: function Render() {
const [{ node }, updateArgs] = useArgs<{ node: string }>();
return (
<FlowNodePanelsPage nodeSlug={node} onNodeSelect={(slug) => updateArgs({ node: slug })} />
);
},
};

export const DapSendEmail: Story = {
name: 'UX DAP - Send email',
render: () => <FullWorkbenchComposition rightPanelVariant="dap" />,
Expand Down
Loading
Loading