示例#1
0
  return decorateStory => {
    if (cache) {
      return cache;
    }
    let channel = null;
    if (isBrowser) {
      try {
        channel = addons.getChannel();
      } catch (e) {
        channel = createChannel({ page: 'preview' });
        addons.setChannel(channel);
      }
    }

    const storyStore = new StoryStore({ channel });
    const clientApi = new ClientApi({ storyStore, decorateStory });
    const { clearDecorators } = clientApi;
    const configApi = new ConfigApi({ clearDecorators, storyStore, channel, clientApi });

    return {
      configApi,
      storyStore,
      channel,
      clientApi,
      showMain,
      showError,
      showException,
    };
  };
示例#2
0
    return () => {
      let webUrl = null;
      let channel = null;

      try {
        channel = addons.getChannel();
      } catch (e) {
        // getChannel throws if the channel is not defined,
        // which is fine in this case (we will define it below)
      }

      if (params.resetStorybook || !channel) {
        const host = params.host || parse(NativeModules.SourceCode.scriptURL).hostname;
        const port = params.port !== false ? `:${params.port || 7007}` : '';

        const query = params.query || '';
        const secured = params.secured;
        const websocketType = secured ? 'wss' : 'ws';
        const httpType = secured ? 'https' : 'http';

        const url = `${websocketType}://${host}${port}/${query}`;
        webUrl = `${httpType}://${host}${port}`;
        channel = createChannel({ url });
        addons.setChannel(channel);
      }
      channel.on('getStories', () => this._sendSetStories());
      channel.on('setCurrentStory', d => this._selectStory(d));
      this._events.on('setCurrentStory', d => this._selectStory(d));
      this._sendSetStories();
      this._sendGetCurrentStory();

      // finally return the preview component
      return params.onDeviceUI ? (
        <OnDeviceUI stories={this._stories} events={this._events} url={webUrl} />
      ) : (
        <StoryView url={webUrl} events={this._events} />
      );
    };
示例#3
0
export default function start(render, { decorateStory } = {}) {
  // check whether we're running on node/browser
  const isBrowser =
    navigator &&
    navigator.userAgent &&
    navigator.userAgent !== 'storyshots' &&
    !(navigator.userAgent.indexOf('Node.js') > -1) &&
    !(navigator.userAgent.indexOf('jsdom') > -1);

  const storyStore = new StoryStore();
  const reduxStore = createStore(reducer);
  const context = {
    storyStore,
    reduxStore,
    decorateStory,
    showMain,
    showError,
    showException,
  };

  const clientApi = new ClientApi(context);

  let channel;
  if (isBrowser) {
    // setup preview channel
    channel = createChannel({ page: 'preview' });
    channel.on(Events.SET_CURRENT_STORY, data => {
      reduxStore.dispatch(Actions.selectStory(data.kind, data.story));
    });
    addons.setChannel(channel);
    Object.assign(context, { channel });

    syncUrlWithStore(reduxStore);

    // Handle keyboard shortcuts
    window.onkeydown = handleKeyboardShortcuts(channel);
  }

  // Provide access to external scripts if `window` is defined.
  // NOTE this is different to isBrowser, primarily for the JSDOM use case
  if (typeof window !== 'undefined') {
    window.__STORYBOOK_CLIENT_API__ = clientApi;
    window.__STORYBOOK_ADDONS_CHANNEL__ = channel; // may not be defined
  }

  const { clearDecorators } = clientApi;
  const configApi = new ConfigApi({ clearDecorators, ...context });

  let previousKind = '';
  let previousStory = '';
  let previousRevision = -1;

  const renderMain = forceRender => {
    if (storyStore.size() === 0) {
      showNopreview();
      return;
    }

    const { selectedKind, selectedStory } = reduxStore.getState();

    const revision = storyStore.getRevision();
    const story = storyStore.getStoryWithContext(selectedKind, selectedStory);
    if (!story) {
      showNopreview();
      return;
    }

    // Render story only if selectedKind or selectedStory has changed.
    // renderMain() gets executed after each action. Actions will cause the whole
    // story to re-render without this check.
    //    https://github.com/storybooks/react-storybook/issues/116
    // However, we do want the story to re-render if the store itself has changed
    // (which happens at the moment when HMR occurs)
    if (
      !forceRender &&
      revision === previousRevision &&
      selectedKind === previousKind &&
      previousStory === selectedStory
    ) {
      return;
    }

    if (!forceRender) {
      // Scroll to top of the page when changing story
      document.documentElement.scrollTop = 0;
    }
    previousRevision = revision;
    previousKind = selectedKind;
    previousStory = selectedStory;

    render({
      ...context,
      story,
      selectedKind,
      selectedStory,
      forceRender,
    });
  };

  // initialize the UI
  const renderUI = forceRender => {
    if (isBrowser) {
      const { error } = reduxStore.getState();
      if (error) {
        showException(error);
        return;
      }
      try {
        renderMain(forceRender);
        addons.getChannel().emit(Events.STORY_RENDERED);
      } catch (ex) {
        showException(ex);
      }
    }
  };

  const forceReRender = () => renderUI(true);
  if (isBrowser) {
    channel.on(Events.FORCE_RE_RENDER, forceReRender);
  }
  renderUI();
  reduxStore.subscribe(renderUI);

  return { context, clientApi, configApi, forceReRender };
}