Storybook: @storybook/html - can't run JS in story

Created on 17 Aug 2019  路  10Comments  路  Source: storybookjs/storybook

Describe the bug

Can't run JS functions alongside HTML of story.
I need to run a jQuery function once the page loads, and none of the methods (using Event Listeners and DOMContentLoaded) I've discovered seem to work.

To Reproduce

  1. Create a story with the JS before the story is returned:
import { storiesOf } from "@storybook/html";

import "../../../dist/semantic.min.css";
import "../../../dist/semantic.min.js";

import Frontpage from "./Frontpage.html";

storiesOf("Page: Frontpage", module).add("Standard", () => {
  function init() {
    $(".ui.dropdown").dropdown({
      on: "hover"
    });
    console.log("ran function");
  }
  document.addEventListener("DOMContentLoaded", init);
  return Frontpage;
});

or more complex example using a Mutation Observer:


storiesOf("Page: Frontpage", module).add("Standard", () => {
  function pageScript() {
    console.log("Page has changed!");
  }
  document.addEventListener(
    "DOMContentLoaded",
    function() {
      pageScript();
      const callback = function(mutationsList) {
        for (let i = 0, len = mutationsList.length; i < len; i++) {
          if (mutationsList[i].type == "childList") {
            pageScript();
            break;
          }
        }
      };

      const observer = new MutationObserver(callback);
      const config = {
        childList: true,
        subtree: false
      };
      observer.observe(document.getElementById("root"), config);
    },
    false
  );
  return Frontpage;
});

Expected behavior
Runs JS on story load and when content changes.

Code snippets
see above

System:

Environment Info:

System:
OS: macOS 10.14
CPU: (4) x64 Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
Binaries:
Node: 8.12.0 - /usr/local/bin/node
npm: 6.4.1 - /usr/local/bin/npm
Browsers:
Chrome: 76.0.3809.100
Firefox: 61.0.2
Safari: 12.0
npmPackages:
@storybook/addon-a11y: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-actions: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-backgrounds: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-centered: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-events: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-jest: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-knobs: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-links: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-notes: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-options: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-storyshots: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-storysource: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addon-viewport: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/addons: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/core: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/core-events: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/html: 5.2.0-alpha.35 => 5.2.0-alpha.35
@storybook/source-loader: 5.2.0-alpha.35 => 5.2.0-alpha.35

Additional context

  • Using Semantic UI and jQuery.
  • SUI is local, jQuery is NPM module that's globally registered in storybook.config.js.
html question / support

Most helpful comment

This probably means that DOMContentLoaded is already fired at the moment when story function is called. In 5.2.0-beta, you can try using Storybook hooks instead:

import { storiesOf } from "@storybook/html";
import { useEffect } from "@storybook/client-api";

import "../../../dist/semantic.min.css";
import "../../../dist/semantic.min.js";

import Frontpage from "./Frontpage.html";

storiesOf("Page: Frontpage", module).add("Standard", () => {
  useEffect(() => {
    $(".ui.dropdown").dropdown({
      on: "hover"
    });
    console.log("ran function");
  }, []);
  return Frontpage;
});

All 10 comments

@whoisryosuke Super hacky and I'm sure there's a better solution, but have you tried a setTimeout just to see if it works?

@ndelangen we have APIs for registering for channel events. Are they accessible when I'm writing a story?

This probably means that DOMContentLoaded is already fired at the moment when story function is called. In 5.2.0-beta, you can try using Storybook hooks instead:

import { storiesOf } from "@storybook/html";
import { useEffect } from "@storybook/client-api";

import "../../../dist/semantic.min.css";
import "../../../dist/semantic.min.js";

import Frontpage from "./Frontpage.html";

storiesOf("Page: Frontpage", module).add("Standard", () => {
  useEffect(() => {
    $(".ui.dropdown").dropdown({
      on: "hover"
    });
    console.log("ran function");
  }, []);
  return Frontpage;
});

@hypnosphi this is really awesome 鉂わ笍

@shilman unfortunately, it doesn't work yet: the effects are triggered too early. I'll send a PR shortly

Yowza!! I just released https://github.com/storybookjs/storybook/releases/tag/v5.2.0-beta.36 containing PR #7791 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.

This probably means that DOMContentLoaded is already fired at the moment when story function is called. In 5.2.0-beta, you can try using Storybook hooks instead:

Hi @Hypnosphi @ndelangen what's today's state of documentation for Storybook hooks? I see https://storybook.js.org/docs/addons/api/#storybook-hooks exists, but useEffect as shown at https://github.com/storybookjs/storybook/issues/7786#issuecomment-522234154 is nowhere to be seen?

Algolia search on website finds no matches for "useEffect".

I tested it, and it seems to work exactly as needed. How come the docs missing on it?

Unfortunately, the docs only exist in form of inline comments: https://github.com/storybookjs/storybook/blob/next/lib/addons/src/hooks.ts#L279

@lkraav a PR on the docs would be super super useful, maybe you'd be able to contribute?

In a 1 month time horizon yes. Within the next 2 weeks, probably not. Main barrier is learning what the current practices and setup for docs here is, and figuring out what exactly to write. Brainspace is extremely limited due to an ongoing project.

I worked around this simply by using jQuery's document ready function.

$(document).ready(function() {
    ...
});
Was this page helpful?
0 / 5 - 0 ratings

Related issues

maraisr picture maraisr  路  119Comments

joeruello picture joeruello  路  79Comments

EdenTurgeman picture EdenTurgeman  路  81Comments

enagy27 picture enagy27  路  69Comments

firaskrichi picture firaskrichi  路  61Comments