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

@storybook/html: Can't run JavaScript in story #4337

Closed
boyum opened this issue Oct 10, 2018 · 6 comments
Closed

@storybook/html: Can't run JavaScript in story #4337

boyum opened this issue Oct 10, 2018 · 6 comments

Comments

@boyum
Copy link

boyum commented Oct 10, 2018

Possible bug

I'm trying out @storybook/html for the first time and have laid out a few components. A few of them need JavaScript to work properly, but I can't get the scripts to run.

Bear in mind, this is vanilla ES5 Javascript, I'm not using web components of any kind. To add my components to the storybook, do as follows:

storiesOf('MyComponent', module)
	.add('MyState', () => `
		<button class="js-toggle">Toggle</button>
		<script>
			function init() { 
				var button = document.querySelector('.js-toggle');
	 			button.addEventListener('click', doSomething); 
			} 
			document.addEventListener('DOMContentLoaded', init);
		</script>`);

Even simple things as console.log() won't work, am I doing anything wrong?

Versions

  • @storybook/html 4.0.0-alpha.23
  • @storybook/addon-notes 4.0.0-alpha.23
  • @storybook/addon-viewport 4.0.0-alpha.23
  • @storybook/addon-a11y 4.0.0-alpha.23
@igor-dv
Copy link
Member

igor-dv commented Oct 10, 2018

We are appending the html nodes into the DOM without any tools/libs (like jQuiery) that's why the script is not really evaluated.

@Hypnosphi, WDYT about forcing evaluation in the render function?

@Hypnosphi
Copy link
Member

Yes, we use innerHTML which doesn't evaluate scripts. But you can rewrite the example like that:

storiesOf('MyComponent', module)
  .add('MyState', () => {
    function init() { 
      var button = document.querySelector('.js-toggle');
      button.addEventListener('click', doSomething); 
    } 
    document.addEventListener('DOMContentLoaded', init);
    return '<button class="js-toggle">Toggle</button>';
  });

There are still at least two problems with this, and forcing script evaluation wouldn't help with them. When you move to other story, event listener isn't removed. And when you move back to this story (or to another similar one), it isn't added to the freshly rendered instance of button, because DOMContentLoaded event won't trigger more than once.

The most correct approach would be to use Events.REGISTER_SUBSCRIPTION, but unfortunately right now it's kinda internal API, so we still need to find a convenient way to use it externally:

import { storiesOf } from '@storybook/html';
import addons from '@storybook/addons';
import CoreEvents from '@storybook/core-events';

// `subscribe` will be called whenever you switch to the decorated story. extracting it to a top-level function is crucial here. If you inline it in `addDecorator`, it will resubscribe on each rerender (which can happen quite often e.g. if you use knobs)
function subscribe() {
  const channel = addons.getChannel();
  const toggle = document.querySelector('.js-toggle');
  // doSomething should be a variable so that it could be reused below in removeEventListener
  document.addEventListener(toggle, 'click', doSomething);
  // `unsubscribe` will be called whenever you leave the decorated story. It should be used for cleanup
  return function unsubscribe() => {
    document.removeEventListener(toggle, 'click', doSomething);
  };
};

storiesOf('MyComponent', module)
  .addDecorator(story => {
    addons.getChannel().emit(CoreEvents.REGISTER_SUBSCRIPTION, subscribe));
    return story();
  })
  .add('MyState', () => '<button class="js-toggle">Toggle</button>');

If the doSomething is just logging the action via addon-actions, you can do it like that (see https://github.com/storybooks/storybook/tree/master/addons/actions#withactions-decorator)

import { storiesOf } from '@storybook/html';
import { withActions } from '@storybook/addon-actions';

storiesOf('MyComponent', module)
  .addDecorator(withActions({
    // Will log "Toggle clicked" in addon-actions panel whenever Toggle button is clicked
    'click .js-toggle': 'Toggle clicked'
  }))
  .add('MyState', () => '<button class="js-toggle">Toggle</button>');

We probably should extract this from 'addon-actions` to allow arbitrary handlers, not just action loggers

@silentHoo
Copy link

Thank you @Hypnosphi, but it seems to only work when I add a setTimeout around the .emit(..). If I don't add it, I can't get any elements:

storiesOf('MyComponent', module)
  .addDecorator(story => {
    setTimeout(_ => addons.getChannel().emit(CoreEvents.REGISTER_SUBSCRIPTION, subscribe)));
    return story();
  })
  .add('MyState', () => '<button class="js-toggle">Toggle</button>');

@Hypnosphi
Copy link
Member

Oh I see. You can use event delegation then:

const handler = e => {
  if (e.target.classList.has('js-toggle')) {
    doSomething(e);
  }
}

function subscribe() {
  const channel = addons.getChannel();
  const toggle = document.querySelector('.js-toggle');
  
  document.addEventListener('click', handler);

  return function unsubscribe() => {
    document.removeEventListener('click', handler);
  };
};

@stale
Copy link

stale bot commented Nov 16, 2018

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 Nov 16, 2018
@stale
Copy link

stale bot commented Dec 16, 2018

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

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

4 participants