-
-
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
Is it possible to dynamically generate stories in CSF #9828
Comments
No, you need to use the API for dynamic stories AFAIK |
ok thanks! |
@shilman perhaps you have an example to give us? |
import { storiesOf } from '@storybook/react';
const cases = ['a', 'b', 'c'];
const stories = storiesOf('foo', module);
cases.forEach(label => stories.add(label, () => <>{label}</>)); |
@shilman The storiesof API document that you linked states that it's a legacy API. What is the current way of adding multiple stories programatically? I could not find anything in the csf docs at https://storybook.js.org/docs/react/api/csf |
@matyasf we don't have one. what's your use case? It's very important for us that CSF be statically analyzable, so we're not planning on adding a dynamic story API. One common use case I've seen is combinatorial testing, and we might support that through some other mechanism, such as this proposal. |
We are making a UI library and generating component variations programatically. These are fed to Chromatic which compares them to the last result, this way we can see easily and precisely what visual changes a code change caused. Heres out stories.js: https://github.com/instructure/instructure-ui/pull/399/files#diff-55e315ae08c20edbe6c750231bdfa74a92bdc0752777f9332db54675e17af835 and the variation generator: https://github.com/instructure/instructure-ui/tree/master/packages/ui-component-examples |
Same use case as @matyasf here |
Thanks @matyasf. I believe the combos proposal--possibly with some extra features--should satisfy your use case. We don't plan to formally deprecate |
This might be another example of the benefit of an api. Any suggestions are welcome :D |
@IanEdington Thanks for sharing that! The "CSF-compatible" way to do this would be to write a custom loader for those email files, which should be as simple as the for-loop you describe. We'll create a sample when we deprecate |
@shilman I've read the doc about this proposal, is there an issue to follow implementation? (I couldn't find it) I wonder if it could also be used for color palettes/shades, to present design tokens. |
It's just a proposal, we haven't agreed on anything and haven't started implementing it. However, we are intent on deprecating |
@shilman ok, thanks. I've not used |
@nhoizey look into MDX -- you can embed arbitrary react elements in there to do whatever you want |
@shilman I'm already using MDX, I explained it in another issue, let's continue there: #7671 (comment) |
I've come up with an idea that worked for me quite good when trying to "generate" stories dynamically for an The basic folder structure is as follow:
export const IconDefaultPicker = () => {
const componentOptions = select('component', Object.keys(icons), 'Cat', 'main')
const fontSizeOptions = select(
'fontSize',
['default', 'inherit', 'large', 'medium', 'small'],
'large',
'main',
)
const htmlColorOptions = select(
'htmlColor',
['inherit', 'primary', 'secondary', 'action', 'disabled', 'error'],
'primary',
'main',
)
return (
<SvgIcon
component={icons[componentOptions]}
fontSize={fontSizeOptions}
htmlColor={htmlColorOptions}
/>
)
} |
Just wanted to note I have a use case similar to @IanEdington: we have some components that display some complex data, and we pass that to the component as a plain JS object. These are generated as JSON by a separate system, and we have some JSON "expected" test files for that system to ensure we don't regress anything. We would like to generate a separate story for each JSON file. I can do that with storiesOf now. I skimmed over the proposal linked above, and I think that would also work for our use case (mostly as a degenerate case of a lot of variations of one complex prop). |
Hello, I think the previous approach using knobs was better as it allowed to easily create dynamic stories. For example with knobs it is easy to do something like import React from 'react';
import {storiesOf} from '@storybook/react';
import {componentFactory} from "../factories/componentFactory";
import {text, object} from '@storybook/addon-knobs';
const testConfig = [
{
"type": "DefaultCreateButton",
"storyName": "Contained",
"props": {
"resource": "users"
},
"config": {
"label": "Test",
"variant": "contained"
}
},
{
"type": "DefaultCreateButton",
"storyName": "Text",
"props": {
"resource": "users"
},
"config": {
![Screen Shot 2021-10-09 at 5 02 46 PM](https://user-images.githubusercontent.com/25964088/136676849-6c733ca8-742d-4fb4-be66-739113a8ee47.png)
"label": "Test",
"variant": "text"
}
},
{
"type": "CreateResourceWithRelated",
"storyName": "Primary",
"props": {
'resource': "carrier_bids",
'resourceName': "Carrier Bids",
'relatedResourceName': "Items",
'relationshipField': "carrier_bid_items",
'fields': [
{
source: "id",
"label": "ID"
}
],
'record': {
id: 1,
owner_username: 'test_user',
'carrier_bid_items': []
},
},
"config": {
'resource': 'carrier_bids',
'fieldProps': [
{
label: "Charge Type",
field: "charge_type",
defaultValue: "Line Haul",
size: "small",
variant: "filled"
},
{
label: "Amount",
field: "amount",
defaultValue: 0,
type: "number",
size: "small",
variant: "outlined"
},
{
label: "Currency",
field: "currency",
defaultValue: "USD",
size: "small",
variant: "outlined"
}
]
}
}
]
for (let conf of testConfig) {
const compType = conf.type
const comp = componentFactory({'type': compType})
storiesOf(compType, module)
.add(conf.storyName, (args) => {
let _args = {}
let _config = {}
const configGroupId = 'Config';
const propsGroupId = 'Props';
for (const [k, v] of Object.entries(conf.config)) {
if (typeof v === 'object') {
_config[k] = (object(k, v, configGroupId))
continue
}
_config[k] = (text(k, v, configGroupId))
}
for (const [k, v] of Object.entries(conf.props)) {
if (typeof v === 'object') {
_args[k] = (object(k, v, propsGroupId))
continue
}
_args[k] = (text(k, v, propsGroupId))
}
return comp(React, _config)(_args)
})
} where my components are all defined like this import {CreateButton} from "react-admin"
const DefaultCreateButton = (React, buttonConfig) => {
return (props) => {
console.log(props, buttonConfig)
const to_resource = props.resource
return (
<CreateButton style={{backgroundColor: props.backgroundColor}}
basePath={to_resource}
label={buttonConfig.label}
variant={buttonConfig.variant}/>
)
}
}
export default DefaultCreateButton; My components are factories, so we can define some initial config and then allow parent components to still pass props. Is it possible to do this using the new Controls approach ??? It seems impossible due to inability to do dynamic export in js. |
Adding 2 cents to this. Implemented Storybook in 2 orgs where I've loaded test case data from Google Sheets in so colleagues can see how each row of data appears in a feature mounted in Storybook. Very useful solution for Product Managers to be able to browse their features in states they can maintain in Google Sheets, so for loop wrapping storiesOf has been a good solution. More recently I've done something sort of along the same lines as what @shilman is proposing with Combos, building an intermediate wrapper around args that shunts the values dynamically into Storybook controls, but not ideal - Combos looks like a great solutions to storiesOf. |
I think the combos addon is a great idea. However, I don't think it will work well for our use case, which I'd like to share here. TLDR: We need a way to show extra stories in Chromatic but not in regular storybook. We have 3 different themes. On storybook, only 1 theme is shown at a time and users can toggle between the themes using the theme-switcher addon. On chromatic however, we show all 3 themes on the same page at the same time. The combos addon wouldn't work because it would put everything on the same story/page, which would break things. |
@oriooctopus I'd propose the following workaround for your case: https://www.notion.so/chromatic-ui/Story-tags-586c7d9ff24f4792bc42a7d840b1142b For those special case stories that only show up in chromatic, you'd tag those stories with What do you think? |
@shilman - is there a way to create nested titles for this example? #9828 (comment) |
Is this the best issue to follow for updates on A few words about my use case for this if it helps – roughly what’s described as "JSON fixture" in discussion #18480:
Here’s an example script if this helps. I believe this specific use case might still be possible with a Babel transform or macro generating CSF, but we’ve moved away from Babel for everything else (using instead vanilla TypeScript or ESBuild or SWC). In an ideal world we’d even be able to generate stories at runtime in the browser – make an API request to our pattern library generator, and generate stories based on that. |
@thibaudcolas we're not going to fix this for 7.0. the workaround is to use the following flag in module.exports = {
features: {
storyStoreV7: false
}
} This will use the old, unoptimized way of loading Storybook. So you'll miss out on a lot of the performance optimizations in V7, but you'll still be able to use the |
Storybook use of CSF format it is optimized in v7. storybookjs/storybook#9828 Since we are measuring performance here, I use mustache to generate the test case before running the test / storybook
For @carbon/charts (monorepo), there are five packages: Angular, Vanilla JavaScript, React, Svelte/Sveltekit, and Vue.js. For each environment, there are 179 stories created for the charts using storiesOf() and a big array. There's another 28 for diagrams (applicable for Angular and React) plus another 12 docs. Net/net - over 1000 stories. Here's a link to the Vanilla JS storybook: https://charts.carbondesignsystem.com/?path=/story/docs--welcome. The Welcome page has the links to the others. This was all created in Storybook 5.x. To move to CSF3, I'm guessing we would need to write our own storiesOf API that would output a file per story into a cache folder that would be viewed as a story source in our main.ts. The files would have to be gitignored and we'd need to clean them before each storybook build. I'm open to any solution but the capability of building stories from data is very important especially for large publicly-facing monorepos where Storybook is the documentation. |
Another item I wanted to add here... we have a number of components that are too simplistic to show as separate stories (like lines, or lines with arrows). For those, we want to follow a bunch of them together in an MDX file for documentation purposes. If we had to turn them into stories, they would look like the Diagrams section here which provides very little value: Instead, we want something more like this as an MDX (this example is using storiesOf())... |
OK, I finally have a proposal for deprecating StoriesOf and providing some rough paths forward for users. I've documented this in an RFC, which even includes a working prototype for people to play with: #23177. Feedback is welcome on the RFC discussion or in DM on Discord if you prefer chatting about it http://discord.gg/storybook |
Why: - Webpack + Babel is a pain to maintain and there are nowadays better build tools. Vite had good results in the State of JavaScript 2022. https://2022.stateofjs.com/en-US/libraries/build-tools/ - Frontend directories had to be relocated to accommodate Vite conventions. However, we changed the default root from "." to "web", to maintain the separation between frontend and backend code. - For CSS modules to work in Vite, their file extension had to be changed to "".module.css". - Mocha didn't have good Vite integration, so the test runner was changed to Vitest. This avoids the Babel dependency. - Vite crashes with segmentation fault inside Debian, so the builder's Linux distro was changed to Alpine. - Storybook had to be upgraded for Vite support. Storybook 7 has deprecated the storiesOf function, but we can't migrate away from it because there is not yet any other way to generate stories dynamically. See storybookjs/storybook#9828
Please use the documented experimental indexer API to generate stories dynamically. Closing this issue. Let us know if the indexer API isn't sufficient for your use case. |
Hi,
I have a folder with many icon components and I need to load them dynamically into stories.
is it possible with CSF like it was possible before : in this previous issue
thanks
The text was updated successfully, but these errors were encountered: