Storybook: How to load an external scripts

Created on 26 Jul 2016  路  6Comments  路  Source: storybookjs/storybook

Hello,

Today I'm try to use the react-storybook for building a component thats use the SDK of Facebook. I will try something like this:

componentWillMount() {
  (function(d, s, id){
     var js, fjs = d.getElementsByTagName(s)[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement(s); js.id = id;
     js.src = "//connect.facebook.net/en_US/sdk.js";
     fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));
}

And I did not succeed. The script loads up if I see on the _network of browser_. But it is not accessible through the console or even within React functions.

I believe it can be related to how the encapsulation react-storybook works.

Most helpful comment

Leaving this snippet here for anyone who may have a similar need to load scripts per-story:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Script extends Component {
  static propTypes = {
    src: PropTypes.string.isRequired,
  };

  state = {
    loaded: false,
  };

  componentWillMount() {
    const head = document.querySelector('head');
    const script = document.createElement('script');

    script.async = true;
    script.src = this.props.src;
    script.onload = () => this.setState({
      loaded: true,
    });

    head.appendChild(script);
  }

  render() {
    return this.state.loaded ? this.props.children : null;
  }
}

const loadScriptDecorator = src => story => <Script src={src}>{story()}</Script>;

export default loadScriptDecorator;

All 6 comments

Since we are running inside an inframe, you wouldnt see it directly in the console.
But your other method will see it.

Make sure, we only run a component once. Hooks like componentWillRecieveProps won't work. When you change a story, we will run it from the begining after cleaning the DOM.

@arunoda Nice! Works fine to me :)

What if you need a cdn resource before the stories run/mount?

var App = (function () {
  return function () {
     loadScript('cdn.file.js' , function scriptsDidLoadCallback () {
       storiesOf('Button Test', module).add('', () => (<Button/>));
     });
  }
})();

App();

which assumes the setup is something like this:

```js
function loadScript (file, callback) {
source = document.createElement('script');
source.src = file;
source.async = true;
// ...
source.onload = function () {
callback (); // aka: scriptsDidLoadCallback
};
}

I need something like what @adamellsworth described, but his code didn't seem to work. While I didn't get any errors, my stories weren't actually loaded at runtime.

Leaving this snippet here for anyone who may have a similar need to load scripts per-story:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Script extends Component {
  static propTypes = {
    src: PropTypes.string.isRequired,
  };

  state = {
    loaded: false,
  };

  componentWillMount() {
    const head = document.querySelector('head');
    const script = document.createElement('script');

    script.async = true;
    script.src = this.props.src;
    script.onload = () => this.setState({
      loaded: true,
    });

    head.appendChild(script);
  }

  render() {
    return this.state.loaded ? this.props.children : null;
  }
}

const loadScriptDecorator = src => story => <Script src={src}>{story()}</Script>;

export default loadScriptDecorator;

I created a public decorator to solve it:
https://www.npmjs.com/package/storybook-external-links

It also works for Vue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tirli picture tirli  路  3Comments

wahengchang picture wahengchang  路  3Comments

tlrobinson picture tlrobinson  路  3Comments

ZigGreen picture ZigGreen  路  3Comments

miljan-aleksic picture miljan-aleksic  路  3Comments