Skip to content
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

How to use existing build process and use storybook as lib #5975

Closed
rolandjohann opened this issue Mar 8, 2019 · 35 comments
Closed

How to use existing build process and use storybook as lib #5975

rolandjohann opened this issue Mar 8, 2019 · 35 comments

Comments

@rolandjohann
Copy link

Describe the solution you'd like
I would like to use my existing build process (in this case parcel-bundler) with it's own capabilities (build, serve, hmr) and omit storybook build system, just use it as library.

Describe alternatives you've considered
As my setup involves typescript I started to setup storybook as described at the official docs, but it seems to be wrong to setup a parallel build system just for storybook when I have already everything in place.

Are you able to assist bring the feature to reality?
maybe

@tmeasday
Copy link
Member

tmeasday commented Mar 9, 2019

Hi @rolandjohann! This is something that we've considered in the past:

I think this is something that @shilman might be thinking about as he starts to think about docs mode; not guaranteed to happen but there might be some related refactoring that makes it much easier to do.

Perhaps it makes sense to leave this ticket open to track the idea?

@shilman
Copy link
Member

shilman commented Mar 9, 2019

There's also a node API by @igor-dv , which hasn't been documented and which I'd like to change:

import build from '@storybook/react/standalone';
build({
  mode: 'dev',
  port: 9009,
  configDir: './storybook',
})
  .then(() => console.log('done'))

@rolandjohann
Copy link
Author

Hi together, thanks for quick reply.

@tmeasday thanks for pointing to existing issues. Doing the discussion at this ticket makes sense.

@shilman I played with that API already which triggers webpack anyway. It seems that the code for build and the actual business logic of storybook is not encapsulated in a way to just use storybook. Are there any detailed plans yet to refactor this and where I can help?

@shilman
Copy link
Member

shilman commented Mar 10, 2019

Hi @rolandjohann -- no plans that I know of? Maybe @ndelangen can weigh in here? I'd be open to some refactoring if it can also simplify the default webpack build process AND make it more debuggable. Every time we change anything related to webpack/babel it generates TONS of issues, so I'm hesitant to make changes here unless they are both well-justified and well-tested.

@RPDeshaies
Copy link

Maybe a solution would be to try to externalize a render api that takes an id and a list of stories as a parameter that way anybody can customize it the way they want ?

@tmeasday
Copy link
Member

Hey all, I've been experiementing / thinking about this a little.

I think there are three things here. We can probably achieve all of this of this quite easily and potentially even as part of 5.1 if someone has some time to do some of it.

1. Allow arbitrary preview URL

We should add a new CLI option to start-storybook and build-storybook that sets a arbitrary preview URL. Right now it is hard coded here: https://github.com/storybooks/storybook/blob/next/lib/ui/src/components/preview/preview.js#L305

It probably wouldn't be too hard to pass it into the manager (lib/ui) somehow.

If someone sets such a URL we should also not start the preview webpack process or require a .storybook/config.js.

2. Allowing @storybook/core to be used from inside any build process.

This is actually almost possible already. I tried the following code inside a Meteor process:

import { configure, storiesOf } from '@storybook/react';

configure(() => {
  storiesOf('chapter').add('story', () => <div>Story</div>);
});

And apart from one issue with expecting module to be defined it worked, no problems.

If you add the following code and browse to http://localhost:XYZ/iframe.html?id=chapter--story (where XYZ is the port of your process), it should work fine.

The main thing to change here would be "binding" to a subpath. Right now there is code in the StoryStore that looks for the id query parameter (and some other back-compat ones) and also changes the URL when the manager tells it to change the story. If it could do that on a subpath (like http://localhost:XYZ/storybook?id=chapter--story), it would be great I think.

3. Allowing the preview iframe to not use the story store (i.e @storybook/core)

This part is even easier actually. We probably just need to document and resolve to stick to the post message "channel" API that the manager uses to communicate with the story store.

I've been thinking about how we could make a @storybook/server API that could be used to render server-rendered components (like Rails partials, for instance) inside Storybook's UI. If anyone is keen to see this happen, get in touch and we can talk through how to do it.

The tricky part would be addons here -- if the addon needs code to run inside the preview window (e.g. a11y) then it's not going to work. However a fair few addons don't; they just have a expectations about the parameters that are set for a story. We could document that too.


If anyone is keen to look at 1, 2 or 3 above, let me know. I plan to look at it at some point but I am not sure when.

@RPDeshaies
Copy link

RPDeshaies commented Mar 22, 2019

Number 2 would probably be one of the most flexible solution.

Just adding some documentation and examples and people could use what ever build process or bundler they want with Storybook (parcel, fusebox, etc)

What kind of HTML do you need for number 2 to work and for the storybook UI to render properly ?

apart from one issue with expecting module to be defined it worked, no problems

can that be bypassed ?

@tmeasday
Copy link
Member

tmeasday commented Mar 22, 2019

Hi @RPDeshaies -- these aren't either/ors -- both 1&2 would be required in the simplest case.

It's a good question about HTML. All the storybook view layers (@storybook/react etc) render into a #root element (e.g. https://github.com/storybooks/storybook/blob/next/app/react/src/client/preview/render.js#L7). Refactoring to allow this to be configured also might be a good idea.

can that be bypassed ?

yes, definitely. Arguably this is just a bug in storybook. Don't even worry about this bit, I just noted it here in case someone runs across it.

@RPDeshaies
Copy link

@tmeasday so if someone would want to use storybook with any bundler they would only need to have

  • a html file that includes a javascript file
  • that javascript files that contains
import { configure, storiesOf } from "@storybook/react";
import React from "react";

configure(() => {
  storiesOf("Category 1").add("Test 1", () => <div>Category 1 - Test 1</div>);
  storiesOf("Category 2")
    .add("Test 1", () => <div>Category 2 - Test 1</div>)
    .add("Test 2", () => <div>Category 2 - Test 2</div>);
}, {});

Right ?

So right now, I've done that (with ParcelJS) but the HTML only renders my story and not the whole storybook UI

image

Any ideas why ?

I'm probably missing something simple, but once I can make that work, I could make an example repo that you guys could include in the doc to use StorybookJS with ParcelJS

@tmeasday
Copy link
Member

@RPDeshaies does the above work, e.g. if you browse to ?path=/story/category-1--test-2? If so great!

To make it really work we'd still need to do part (1), which would allow the storybook UI to run and point at that. So the way you would use it would be to start your app as normal (including the part that renders the stories as above) and also start Storybook pointing at your app.

Also I suspect the above code snippet will interact badly with the rest of your app (overwriting routes etc possibly), and we probably want to do part (2) to contain the storybook behaviour in your app to a single route.

Are you interested in working on this? I can help out if you want to have a go at it!

@RPDeshaies
Copy link

does the above work

This url works for example: http://localhost:1234/?selectedKind=Category%202&selectedStory=Test%202

and loads the right story

Also I suspect the above code snippet will interact badly with the rest of your app

I personally think its fine as I think storybook should be rendered in its own app.

That, at least, could be an easy MVP

For example, I've been using React cosmos for months now and I have a very custom build to use it with parcel but when I use it, it's never executed inside the same app context as the rest of my app.

It just uses a similar bundling pattern

Are you interested in working on this?

Maybe ? I dont know anything about the project so I would need some guiding if you have some time

@tmeasday
Copy link
Member

tmeasday commented Mar 25, 2019

I personally think its fine as I think storybook should be rendered in its own app.

That's true, that could be a first step.

Maybe ? I dont know anything about the project so I would need some guiding if you have some time

If you have the time to experiment I can definitely point you in the right direction. What we need to do is:

  1. add a CLI option (to prod and dev). Perhaps --preview-url?

  2. When that option is set, ensure the webpack dev server (in dev) or build (in prod) does not add the preview entry point and associated configs to the webpack setup. I'm not 100% clear on how this is done and there are some layers to it (in here - https://github.com/storybooks/storybook/tree/next/lib/core/src/server) but I'm sure myself and others can help you figure it out if you want to start looking.

  3. Wire the --preview-url option through to the Preview component -
    https://github.com/storybooks/storybook/blob/next/lib/ui/src/components/preview/preview.js#L305

    I'm not sure how best to do this; probably using the webpack DefinePlugin?

Happy to have a chat about this if you want to have a go once you've taken a look around.

@RPDeshaies
Copy link

Alright maybe I don't understand what you want clearly because I thought the goal was to existing build process and use storybook as lib

Wouldn't we just need to export the Preview Component ?

If the goal is to use an existing build process, I don't think updating webpack would help us doing that no ?

@tmeasday
Copy link
Member

tmeasday commented Mar 25, 2019

Sorry if it is unclear. Storybook consists of two parts:

  • the manager which is the UI of storybook itself
  • the preview which is the stories that run via code pulled from your app.

Right now Storybook starts a webpack process for both parts and embeds the preview inside an iframe in the manager (via the Preview component linked above).

What I am proposing is to continue to run the manager via webpack and Storybook's webpack config (this is our code unless you are writing addons).

The tricky part is running your own code in the preview inside webpack if you don't use webpack. So instead, you'll run the preview part of storybook yourself, either in a route inside your own app, or in its own app using which ever build tool you like (as you say).

@RPDeshaies
Copy link

Gotcha. I'll dig inside the code and will come back with either a solution and or questions

@RPDeshaies
Copy link

Questions


Should I branch off master or next in my fork ?


I as able to add the CLI config for the preview url and realized there was already a ignorePreview

Do you think it's OK if I reuse it to make sure the preview mode is disabled when my flag is also passed ?

e.g.

// lib/core/src/server/build-dev.js
export async function buildDev({ packageJson, ...loadOptions }) {
  const cliOptions = await getDevCli(packageJson);
  const hasPreviewUrlCliOption = !!cliOptions.previewUrl;

  await buildDevStandalone({
    ...cliOptions,
    ...loadOptions,
    packageJson,
    configDir: loadOptions.configDir || cliOptions.configDir || './.storybook',
    // IMPORTANT PART 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 
    ignorePreview: loadOptions.ignorePreview || hasPreviewUrlCliOption,
  });
}

I found multiple places where iframe.html is used like

  • addons/storyshots/storyshots-puppeteer/src/url.js
  • lib/core/src/server/preview/iframe-webpack.config.js (I don't think this one is important though since I think it's only used when the preview is turned on, right ?
  • lib/ui/src/components/preview/preview.js (default props)

And that is pretty much it.

Though even though I'm trying to debug/log I don't seem to be able to find where it's ACTUALLY used by the HTML to load the iframe.

Maybe I'm missing something


According to the doc, you need to run yarn boostrap --core followed by yarn dev

yarn dev seems to build all the projects inside the app even though I just need to core really to be rebuilt on file change.

Is there a way to accomplish that ? My computer's RAM and CPU you thank you very much

@tmeasday
Copy link
Member

Should I branch off master or next in my fork ?

Branch off next

Do you think it's OK if I reuse it to make sure the preview mode is disabled when my flag is also passed ?

Seems great, yeah! (Someone else, e.g. @igor-dv could probably say for sure)

addons/storyshots/storyshots-puppeteer/src/url.js

I think eventually we might want to be able to customize this too but not a high priority

I don't think this one is important though since I think it's only used when the preview is turned on, right ?

👍

Though even though I'm trying to debug/log I don't seem to be able to find where it's ACTUALLY used by the HTML to load the iframe.

I found the way the props/components get passed through the UI to be pretty confusing too but I can definitely say for sure that when I changed the default prop in lib/ui/src/components/preview/preview.js it definitely changed what was loaded in the iframe.

What is confusing is that if you say yarn link @storybook/core you'll run into the issue that it uses the DLL that is built by the UI won't get updated as you update the core package. So you won't see changes in a test app. To work around this in the official-storybook in the repo we somehow don't use the DLL. I'm not sure exactly how to set that up for another app.

The TLDR is it's easiest to do any development against the official-storybook.

yarn dev seems to build all the projects inside the app even though I just need to core really to be rebuilt on file change.

Once it has run it shouldn't do much though? I find it takes a little while to get going but then it's OK.

@RPDeshaies
Copy link

Once it has run it shouldn't do much though?
I guess it's ok ;)

The TLDR is it's easiest to do any development against the official-storybook.
Gotcha, it works fine now.

Manager
So, if all the stories are inside the "preview view" managed by another bundler, how is the manager supposed to know the list of stories to display on the left side of the page?

I'm able to load my preview in the iframe now but the manager has no idea what to display (which is normal I guess)

https://gyazo.com/eb1810e7307ebbe74350242198f91cea


Also, do you have any idea or example that you guys are already going on how to pass a CI options down to the UI / component layer?

@tmeasday
Copy link
Member

I'm able to load my preview in the iframe now but the manager has no idea what to display (which is normal I guess)

It looks like maybe the right messages aren't coming over the channel for the storybook. Try watching the console messages at the "debug" level and you'll see what stories are reported by your preview.

Also, do you have any idea or example that you guys are already going on how to pass a CI options down to the UI / component layer?

I'm not aware of it being done (although it might be) already, but I think webpack define plugin is the typical way this is done?

@tmeasday
Copy link
Member

PS feel free to reach out on discord

@RPDeshaies
Copy link

Alright I don't know what I changed but it works now

image

Will create a PR

@RPDeshaies
Copy link

#6323

@stale
Copy link

stale bot commented Apr 17, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Apr 17, 2019
@RPDeshaies
Copy link

PR is still opened and currently in the review process...

@stale stale bot removed the inactive label Apr 17, 2019
@stale
Copy link

stale bot commented May 8, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label May 8, 2019
@shilman shilman removed the inactive label May 8, 2019
@stale
Copy link

stale bot commented May 29, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@shilman
Copy link
Member

shilman commented Jul 1, 2019

Huzzah!! I just released https://github.com/storybookjs/storybook/releases/tag/v5.2.0-alpha.33 containing PR #7235 that references this issue. Upgrade today to try it out!

You can find this prerelease on the @next NPM tag.

Closing this issue. Please re-open if you think there's still more to do.

@shilman
Copy link
Member

shilman commented Jul 2, 2019

Gadzooks!! I just released https://github.com/storybookjs/storybook/releases/tag/v5.2.0-alpha.34 containing PR #7245 that references this issue. Upgrade today to try it out!

You can find this prerelease on the @next NPM tag.

@RPDeshaies
Copy link

@shilman I'm testing the alpha release heavily and it seems like the "zoom" button are throwing an exception and make the page entirely blank.

Does that ring a bell ? Should we open another issue for this ?

Uncaught TypeError: Cannot read property 'body' of null
    at IFrame.setIframeBodyStyle (vendors~main.d85284da259e1b1da394.bundle.js:166262)
    at IFrame.shouldComponentUpdate (vendors~main.d85284da259e1b1da394.bundle.js:166247)
    at checkShouldComponentUpdate (vendors~main.d85284da259e1b1da394.bundle.js:64940)
    at updateClassInstance (vendors~main.d85284da259e1b1da394.bundle.js:65410)
    at updateClassComponent (vendors~main.d85284da259e1b1da394.bundle.js:68418)
    at beginWork (vendors~main.d85284da259e1b1da394.bundle.js:69368)
    at performUnitOfWork (vendors~main.d85284da259e1b1da394.bundle.js:73036)
    at workLoop (vendors~main.d85284da259e1b1da394.bundle.js:73076)
    at HTMLUnknownElement.callCallback (vendors~main.d85284da259e1b1da394.bundle.js:53873)
    at Object.invokeGuardedCallbackDev (vendors~main.d85284da259e1b1da394.bundle.js:53923)

The above error occurred in the <IFrame> component:
    in IFrame (created by ActualPreview)
    in div (created by Context.Consumer)
    in Styled(div) (created by Context.Consumer)
    in div (created by ActualPreview)
    in ActualPreview (created by Context.Consumer)
    in div (created by Context.Consumer)
    in Styled(div) (created by Preview)
    in Provider (created by Preview)
    in Provider (created by Preview)
    in Preview (created by Context.Consumer)
    in ManagerConsumer
    in Unknown (created by Layout)
    in div (created by Context.Consumer)
    in Styled(div) (created by Preview)
    in Preview (created by Layout)
    in div (created by Context.Consumer)
    in Styled(div) (created by Main)
    in div (created by Context.Consumer)
    in Styled(div) (created by Main)
    in Main (created by Layout)
    in Layout (created by Context.Consumer)
    in WithTheme(Layout)
    in Unknown
    in Unknown
    in div (created by Context.Consumer)
    in Styled(div)
    in Unknown
    in Unknown (created by SizeMeRenderer(Component))
    in SizeMeReferenceWrapper (created by SizeMeRenderer(Component))
    in SizeMeRenderer(Component) (created by SizeMe(Component))
    in SizeMe(Component) (created by Manager)
    in ThemeProvider (created by Manager)
    in Manager (created by Context.Consumer)
    in Location (created by QueryLocation)
    in QueryLocation (created by Root)
    in LocationProvider (created by Root)
    in HelmetProvider (created by Root)
    in Root

@shilman
Copy link
Member

shilman commented Jul 4, 2019

@RPDeshaies Thanks for following up on this. Yeah I think it's a new issue!

@Eoksni
Copy link

Eoksni commented Jul 21, 2019

@shilman Thanks for the awesome work, this preview-url feature is amazing! One note though: I couldn't manage to get addons to work in such setup. Not sure if I'm doing something wrong or it is not supported yet (or maybe an unrelated bug).

So in my parcel entrypoint I try to add info addon:

import { configure, addDecorator } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';

function loadStories() {
  require('../../packages/typed-form/**/*.stories.tsx');
}

addDecorator(withInfo as any);
configure(loadStories, module);

And in the storybook in browser it displays loading state for eternity and in the console there is error:

Uncaught Error: Cannot find module './lib/nestedObjectAssign.js'
    at newRequire (storybook.9d40b3f6.js:37)
    at localRequire (storybook.9d40b3f6.js:53)
    at Object.parcelRequire.../../node_modules/nested-object-assign/index.js../package.json (index.js:3)
    at newRequire (storybook.9d40b3f6.js:47)
    at localRequire (storybook.9d40b3f6.js:53)
    at Object.parcelRequire.../../node_modules/@storybook/addon-info/dist/index.js.core-js/modules/es.object.assign (index.js:21)
    at newRequire (storybook.9d40b3f6.js:47)
    at localRequire (storybook.9d40b3f6.js:53)
    at Object.parcelRequire.storybook.tsx.@storybook/react (storybook.tsx:2)
...

localhost-1563670564688.log

Storybook version: 5.2.0-beta.4

If I remove addDecorator(withInfo as any); line, then everything is good.

@shilman
Copy link
Member

shilman commented Jul 21, 2019

@Eoksni that's possible! Mind filing a new issue so we can follow up on that? Thanks!! 🙏

@Eoksni
Copy link

Eoksni commented Jul 21, 2019

@shilman Ok I'll try to reproduce it in a fresh setup and create new issue later today.

@shilman
Copy link
Member

shilman commented Jul 21, 2019

@Eoksni Ideal repro is in the storybook monorepo itself, since that's where we'll be debugging and iterating. TBH your current comment is probably enough 😄

@Eoksni
Copy link

Eoksni commented Jul 21, 2019

@shilman Unfortunately I could not set up storybook monorepo - it failed the bootstrap at the node-gyp stage with some compilation errors, so I created fresh repo here https://github.com/Eoksni/poc-storybook-preview (using @storybook/react@next) and created new issue #7514.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants