Storybook: Displaying imported HTML in Storysource

Created on 4 Oct 2019  ·  29Comments  ·  Source: storybookjs/storybook

Support request summary

I would like to be able to display imported HTML in the Storysource panel, rather than the contents of the .stories.js file.

Steps to reproduce

  1. Create a new package and create/amend the two files below:

__html/button.html:__

<button>Hello world</button>

__stories/index.stories.js:__

import button from '../html/button.html';

export default {
  title: 'Demo',
};

export const Button = () => button;
  1. Set up Storysource addon as per package README

__.storybook/addons.js__

import '@storybook/addon-storysource/register';

__.storybook/webpack.config.js__

module.exports = function({ config }) {
  config.module.rules.push({
    test: /\.stories\.jsx?$/,
    loaders: [require.resolve('@storybook/source-loader')],
    enforce: 'pre',
  });

  return config;
};

Please specify which version of Storybook and optionally any affected addons that you're running

Expected result

I would like the Storysource panel to display the contents of html/button.html

Actual result

Storysource panel shows contents of stories/index.stories.js

image

Any advice on how to achieve this is greatly appreciated.

Thanks!

storysource feature request question / support todo

All 29 comments

Just a quick follow up to this, in older versions of @storybook/html (v5.1.9) I used to do this with success:

.storybook/config.js

import { withStorySource } from '@storybook/addon-storysource';
addDecorator((storyFn, context) => {
  const source = storyFn()
  return withStorySource(source)(storyFn, context)
})

However with @storybook/[email protected] upwards I get the following error with this technique:

storybook_ui_dll.js:2 TypeError: Cannot read property 'source' of undefined
    at StoryPanel._this.listener (main.8e749501743145db6330.bundle.js:9344)
    at storybook_ui_dll.js:2
    at Array.forEach (<anonymous>)
    at t.value (storybook_ui_dll.js:2)
    at PostmsgTransport.<anonymous> (storybook_ui_dll.js:2)
    at PostmsgTransport.handler (main.8e749501743145db6330.bundle.js:10423)
    at PostmsgTransport.handleEvent (main.8e749501743145db6330.bundle.js:1051

I believe this is the change that has introduced the issue: https://github.com/storybookjs/storybook/pull/7272/files#diff-904dad7f92c7bbfac64aca74960bbf33

@libetl do you have any time to look at @danhead 's error above?

storybook_ui_dll.js:2 TypeError: Cannot read property 'source' of undefined
at StoryPanel._this.listener (main.8e749501743145db6330.bundle.js:9344)
at storybook_ui_dll.js:2
at Array.forEach ()
at t.value (storybook_ui_dll.js:2)
at PostmsgTransport. (storybook_ui_dll.js:2)
at PostmsgTransport.handler (main.8e749501743145db6330.bundle.js:10423)
at PostmsgTransport.handleEvent (main.8e749501743145db6330.bundle.js:1051

@shilman You added a "TODO" label but didn't re-open the issue. It still appears to have problems, which appear to be related to the changes mentioned by @danhead:

https://github.com/storybookjs/storybook/pull/7272/files#diff-904dad7f92c7bbfac64aca74960bbf33

I am trying to use the following:

.addDecorator(withStorySource('my test source'))

(to overwrite the source inserted automatically by register) but nothing changes in the Storysource panel.

I debugged the "storybook/source-loader/set" event parameters, and the source added by register has the following event parameters:

edition: {source: '...', …}, story: {…}, location: {…}

but the events triggered by "withStorysource" have the following event parameters:

source: "my test source", currentLocation: undefined, locationsMap: {…}

I guess that setStorySource() needs updating to work with the new format?

@shilman @igor-dv @libetl

I created a pull request with a fix for the issue I described above:

https://github.com/storybookjs/storybook/pull/9561

If you try to use withStorySource(source) in addDecorator, and the '@storybook/source-loader' is added to the storybook webpack config, with "injectDecorator" set to true, the story decorators are executed first, then overwritten by the decorators injected by the webpack loader... is this expected behaviour? I would expect the injected decorator to load first, followed by the global stories decorator, followed by story-specific changes...

If this is the expected behaviour, how can I override it so that I can overwrite the storysource in specific stories?

@LeeBurton thanks for taking the time to file this pull request.

For 6+, storybook is moving away from addDecorator: https://github.com/storybookjs/storybook/issues/9506 and I just filed a refactor of source-loader here: https://github.com/storybookjs/storybook/pull/9547

Would you like us to work together on adapting your use case to use parameters instead of addDecorator with the new code base?

@shilman ?

An example of using storySource parameters:
https://github.com/storybookjs/storybook/pull/9547/files#diff-f9e4c1787dc63502454a68f7ff88245c

We are using the HTML version to include web components, rather than React, so I guess that we are a little limited with what we can do in the stories.

It would be useful if I could somehow set a parameter within the stories, then access that parameter within a global decorator to emit an event to update the source panel.

Currently, the only solution I have found is to emit the event in the stories. This also solves the problem that the Webpack loader injected decorator story source is overwriting the story decorator source (but only with my pull request changes, otherwise the wrong parameters are provided to emit().

This is causing me real difficulties on my client’s project, and I’m struggling to make progress after trying various solutions for a week 😳

When is version 6 scheduled to be released?

Plus, this should also be tagged as a bug, I guess.

@LeeBurton - did you check the example, it does not use events at all and this makes it to also work for the docs tab (the source-loader events were only working for the StorySource panel):

import button from './button.html';

 export default {
   title: 'Addons/Source loader',
 };

 export const Button = () => button;
 Button.story = {
   parameters: {
     storySource: {
       source: button,
     },
   },
 };

I’ll explain a little more about what I’m trying to achieve, as I’m not sure how this helps with my problem.

The story uses a template string with our custom elements, such as:

<my-grid title=“my title” rows=3> </my-grid>

and I am then populating it with data by setting the “data” attribute.

So I need to document our grid component HTML code, but also the necessary code populating various attributes, including the example JSON data.

The grid component includes pagination. When a paging button is clicked, the next page of data is fetched from the backend API and used to update the “data” attribute. I need to show the developers how the data is changing when events such as paging occurs.

Thanks @LeeBurton I think we can have a 'dynamic' storySource parameter - ie a function that will be called at runtime, so you can entirely customize what source code to display for the developers.

@shilman - any thoughts?

@atanasster @LeeBurton Parameters cascade, so global parameters can be overridden at a story level, just as you have done in your comment above @atanasster . I don't think we need to do anything special to support user configuration. Maybe just better documentation on how to do it.

  1. withSource(...) doesn’t work when the source loader is used in webpack. It injects a decorator which overwrites the value set by the withSource decorator. The same goes for storySource added to a story’s parameters...

  2. How can parameters be updated dynamically within a story?

you can create separate stories for each state that you want to show. in the future we may try to support more interaction scenarios, but we have our hands pretty full with doing a great job of simply showing every state. you can also link between stories with addon-links to fake interactivity.

I greatly appreciate the hard work you guys are doing, but any assistance in achieving my goals would be greatly appreciated. My client is a government department responsible for building web components that various other government departments can use, and we are trying to convince them that Storybook is the best choice for documenting their components for developers in the other departments. If we cannot make progress quickly, they may start looking at other options.

For this particular component, I have a story that uses knobs, and interactions.

I need to be able to show documentation for the current story HTML with the attributes set (which I could get from storyFn() in a decorator) and also include JavaScript code. I am also listening to events within the story, by getting the element in useEffect() using document.querySelector() and then adding event listeners. when the listeners fire, I want to be able to update the documentation. Although, it seems a bit messy having to implement it like this... Are there better ways without cluttering the stories?

Is it not currently possible to update a parameter in stories (that I can access in a decorator)?

As far as I know it's not possible to update parameters dynamically this way.

@atanasster and @ndelangen have been working on powerful addon APIs (shared state hooks) that could make sharing state between stories & addons much easier. It might be fairly easy to implement your use case using that shared state hook, and not have to deal with the entirety of storysource for that purpose.

@LeeBurton as michael mentioned, its not possible to modify parameters dynamically.
However we can add a dynamic property source that would allow you to do pretty much the same thing. The best thing would be if you can join and test the upcoming sb6 alphas and give us feedback on the discord channel until me make it work as best as we can for your use case.

Okay, thanks. Unfortunately, I’m too much under pressure with my client’s project at the moment. If I have more time later, I’d love to help. When is v.6.0 release planned?

When will my bug fix pull request likely be merged to fix the storySource event parameters to the correct format? Currently my workaround won’t work without these changes merged...

@LeeBurton - did you check the example, it does not use events at all and this makes it to also work for the docs tab (the source-loader events were only working for the StorySource panel):

import button from './button.html';

 export default {
   title: 'Addons/Source loader',
 };

 export const Button = () => button;
 Button.story = {
   parameters: {
     storySource: {
       source: button,
     },
   },
 };

@atanasster When I set the storySource.source parameter, it does nothing. It just says “Loading source...” 😳

@LeeBurton - are you trying the PR, its not yet merged.

If yes, please try the html-kitchen sink example https://github.com/storybookjs/storybook/pull/9547/files#diff-f9e4c1787dc63502454a68f7ff88245c

@atanasster I don’t understand what you mean by “are you trying the PR”... which PR? The one you just included?

When are your changes likely to be merged and available?

@LeeBurton - yes i meant the listed PR from above.

That PR is meant for 6.0 and @shilman would know about the merging, but i think he is currently very busy with the 5.3 updates.

I can probably merge/release an alpha today, but I want to do another review & test it first. Juggling a lot.

storySource.source - still seems to have no effect, when can we expect this to work?

Will release this in 6.0-alpha on Thu at earliest, Sat at latest. Currently in a remote area with poor network connection, and unfortunately the release process requires a good connection. Thanks for your patience.

@LeeBurton - did you check the example, it does not use events at all and this makes it to also work for the docs tab (the source-loader events were only working for the StorySource panel):

import button from './button.html';

 export default {
   title: 'Addons/Source loader',
 };

 export const Button = () => button;
 Button.story = {
   parameters: {
     storySource: {
       source: button,
     },
   },
 };

This works great, now could someone say how write a storydocs for a example like that?

@juniovitorino like this?

import button from './button.html';

 export default {
   title: 'Addons/Source loader',
 };

 export const Button = () => button;
 Button.story = {
   parameters: {
     storyDescription: 'some description here',
     storySource: {
       source: button,
     },
   },
 };

@shilman Am I right that the correct format now is

import button from './button.html';

 export default {
   title: 'Addons/Source loader',
 };

 export const Button = () => button;
 Button.parameters = {
   storyDescription: 'some description here',
   storySource: {
     source: button,
   },
};

Also is this documented anywhere?

Was this page helpful?
0 / 5 - 0 ratings