-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What's the recommended way to write dynamic stories and yield proper Story
source code?
#13657
Comments
We currently support this in Once that stabilizes, we will try to reconcile the difference between the doc blocks that show up in the docs tab, and addons like Storysource that show up in the addons panel. cc @phated |
Yesterday I had refactored a See commit: UnlyEd/reaflow@72d1ddc Before commit: (using Template) Code: import {
Meta,
Story
} from '@storybook/react/types-6-0';
import React, { useState } from 'react';
import {
EdgeData,
NodeData
} from '../src';
import { Canvas } from '../src/Canvas';
import {
UndoRedoEvent,
useUndo
} from '../src/helpers';
import {
Add,
Arrow,
Edge,
Icon,
Label,
MarkerArrow,
Node,
Port,
Remove
} from '../src/symbols';
export default {
title: 'Demos/Undo Redo',
component: Canvas,
argTypes: {
initialHistory: {
control: {
disable: true // TODO make it readonly when it'll be possible - See https://github.com/storybookjs/storybook/issues/14048
}
}
},
subcomponents: {
Node,
Edge,
MarkerArrow,
Arrow,
Icon,
Label,
Port,
Remove,
Add
}
} as Meta;
type PropsWithChildrenMock = {
initialHistory?: { nodes: NodeData[]; edges: EdgeData[] }[];
};
const Template: Story<PropsWithChildrenMock> = (props) => {
const { initialHistory } = props;
const [nodes, setNodes] = useState<any[]>([
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
},
{
id: '3',
text: 'Node 3'
}
]);
const [edges, setEdges] = useState<any[]>([
{
id: '1-2',
from: '1',
to: '2'
},
{
id: '1-3',
from: '1',
to: '3'
}
]);
const { undo, redo, canUndo, canRedo } = useUndo({
nodes,
edges,
initialHistory,
onUndoRedo: (state: UndoRedoEvent) => {
console.log('Undo / Redo', state);
setEdges(state.edges);
setNodes(state.nodes);
}
});
const addNode = () => {
setNodes([
...nodes,
{
id: `a${Math.random()}`,
text: `Node ${Math.random()}`
}
]);
};
return (
<div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}>
<button
style={{ position: 'absolute', top: 10, left: 10, zIndex: 999 }}
onClick={addNode}
>
Add Nodes
</button>
<button
style={{ position: 'absolute', top: 10, left: 100, zIndex: 999 }}
onClick={undo}
disabled={!canUndo}
>
Undo
</button>
<button
style={{ position: 'absolute', top: 10, left: 160, zIndex: 999 }}
onClick={redo}
disabled={!canRedo}
>
Redo
</button>
<Canvas
nodes={nodes}
edges={edges}
onLayoutChange={layout => console.log('Layout', layout)}
/>
</div>
);
};
export const Simple: Story<PropsWithChildrenMock> = Template.bind({});
Simple.args = {};
export const WithInitialHistory: Story<PropsWithChildrenMock> = Template.bind({});
WithInitialHistory.args = {
initialHistory: [
{
nodes: [],
edges: []
},
{
nodes: [
{
id: '1',
text: 'Node 1'
}
],
edges: []
},
{
nodes: [
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
}
],
edges: [
{
id: '1-2',
from: '1',
to: '2'
}
]
},
{
nodes: [
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
},
{
id: '3',
text: 'Node 3'
}
],
edges: [
{
id: '1-2',
from: '1',
to: '2'
},
{
id: '1-3',
from: '1',
to: '3'
}
]
}
]
};
After commit: (using code duplication, no template) Code: import {
Meta,
Story
} from '@storybook/react/types-6-0';
import React, { useState } from 'react';
import {
EdgeData,
NodeData
} from '../src';
import { Canvas } from '../src/Canvas';
import {
UndoRedoEvent,
useUndo
} from '../src/helpers';
import {
Add,
Arrow,
Edge,
Icon,
Label,
MarkerArrow,
Node,
Port,
Remove
} from '../src/symbols';
export default {
title: 'Demos/Undo Redo',
component: Canvas,
argTypes: {
initialHistory: {
control: {
disable: true // TODO make it readonly when it'll be possible - See https://github.com/storybookjs/storybook/issues/14048
}
}
},
subcomponents: {
Node,
Edge,
MarkerArrow,
Arrow,
Icon,
Label,
Port,
Remove,
Add
}
} as Meta;
type Props = {}
export const Simple: Story<Props> = () => {
const [nodes, setNodes] = useState<any[]>([
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
},
{
id: '3',
text: 'Node 3'
}
]);
const [edges, setEdges] = useState<any[]>([
{
id: '1-2',
from: '1',
to: '2'
},
{
id: '1-3',
from: '1',
to: '3'
}
]);
const { undo, redo, canUndo, canRedo } = useUndo({
nodes,
edges,
onUndoRedo: (state: UndoRedoEvent) => {
console.log('Undo / Redo', state);
setEdges(state.edges);
setNodes(state.nodes);
}
});
const addNode = () => {
setNodes([
...nodes,
{
id: `a${Math.random()}`,
text: `Node ${Math.random()}`
}
]);
};
return (
<div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}>
<button
style={{ position: 'absolute', top: 10, left: 10, zIndex: 999 }}
onClick={addNode}
>
Add Nodes
</button>
<button
style={{ position: 'absolute', top: 10, left: 100, zIndex: 999 }}
onClick={undo}
disabled={!canUndo}
>
Undo
</button>
<button
style={{ position: 'absolute', top: 10, left: 160, zIndex: 999 }}
onClick={redo}
disabled={!canRedo}
>
Redo
</button>
<Canvas
nodes={nodes}
edges={edges}
onLayoutChange={layout => console.log('Layout', layout)}
/>
</div>
);
};
export const WithInitialHistory: Story<Props> = () => {
const initialHistory: { nodes: NodeData[]; edges: EdgeData[] }[] = [
{
nodes: [],
edges: []
},
{
nodes: [
{
id: '1',
text: 'Node 1'
}
],
edges: []
},
{
nodes: [
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
}
],
edges: [
{
id: '1-2',
from: '1',
to: '2'
}
]
},
{
nodes: [
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
},
{
id: '3',
text: 'Node 3'
}
],
edges: [
{
id: '1-2',
from: '1',
to: '2'
},
{
id: '1-3',
from: '1',
to: '3'
}
]
}
];
const [nodes, setNodes] = useState<any[]>([
{
id: '1',
text: 'Node 1'
},
{
id: '2',
text: 'Node 2'
},
{
id: '3',
text: 'Node 3'
}
]);
const [edges, setEdges] = useState<any[]>([
{
id: '1-2',
from: '1',
to: '2'
},
{
id: '1-3',
from: '1',
to: '3'
}
]);
const { undo, redo, canUndo, canRedo } = useUndo({
nodes,
edges,
initialHistory,
onUndoRedo: (state: UndoRedoEvent) => {
console.log('Undo / Redo', state);
setEdges(state.edges);
setNodes(state.nodes);
}
});
const addNode = () => {
setNodes([
...nodes,
{
id: `a${Math.random()}`,
text: `Node ${Math.random()}`
}
]);
};
return (
<div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}>
<button
style={{ position: 'absolute', top: 10, left: 10, zIndex: 999 }}
onClick={addNode}
>
Add Nodes
</button>
<button
style={{ position: 'absolute', top: 10, left: 100, zIndex: 999 }}
onClick={undo}
disabled={!canUndo}
>
Undo
</button>
<button
style={{ position: 'absolute', top: 10, left: 160, zIndex: 999 }}
onClick={redo}
disabled={!canRedo}
>
Redo
</button>
<Canvas
nodes={nodes}
edges={edges}
onLayoutChange={layout => console.log('Layout', layout)}
/>
</div>
);
}; I'm not sure what's the right way to keep the most reusable code possible while providing the best user experience while reading the Story documentation. |
Is there any update for this? Sadly the current state of things makes the storysource addon not as helpful as it could be |
There are many examples around about to write Stories, it's hard for newcomers to understand what is the recommended way, especially when we want to use dynamic stories with
args
.Here is what I'm doing now, I think it's not too bad:
This way is flexible enough for me to add more stories without too much code duplicate.
The problem is the
Story
panel that shows meaningless source code (compared to other ways of writing stories):Demo: https://nrn-v2-mst-aptd-at-lcz-sty-storybook.vercel.app/?path=/story/next-right-now-form-spoilerlink--dynamic-example-with-btn
All stories will show the same source code, and it makes the whole "Story" panel unusable.
Is this a known issue? Is there a workaround?
I tried to think about it, and the goal of the "Story" panel is to show code that can be copied for quick use. Displaying either the above source code, or the whole story file (*.stories.tsx) would be unusable. It'd need to show a code example that infers the
args
, such as:Something like this would give a much better developer experience.
The text was updated successfully, but these errors were encountered: