diff --git a/app/angular/src/client/preview/index.js b/app/angular/src/client/preview/index.js index e24954c17850..be920eba40a7 100644 --- a/app/angular/src/client/preview/index.js +++ b/app/angular/src/client/preview/index.js @@ -53,3 +53,5 @@ const renderUI = () => { }; reduxStore.subscribe(renderUI); + +export const forceReRender = () => render(context, true); diff --git a/app/angular/src/client/preview/render.js b/app/angular/src/client/preview/render.js index f7f8e71ba624..d53d95342278 100644 --- a/app/angular/src/client/preview/render.js +++ b/app/angular/src/client/preview/render.js @@ -22,7 +22,7 @@ export function renderException(error) { logger.error(error.stack); } -export function renderMain(data, storyStore) { +export function renderMain(data, storyStore, forceRender) { if (storyStore.size() === 0) return null; const { selectedKind, selectedStory } = data; @@ -39,7 +39,7 @@ export function renderMain(data, storyStore) { // https://github.com/storybooks/react-storybook/issues/116 const reRender = selectedKind !== previousKind || previousStory !== selectedStory; - if (reRender) { + if (reRender || forceRender) { // We need to unmount the existing set of components in the DOM node. // Otherwise, React may not recrease instances for every story run. // This could leads to issues like below: @@ -54,14 +54,14 @@ export function renderMain(data, storyStore) { return renderNgApp(story, context, reRender); } -export default function renderPreview({ reduxStore, storyStore }) { +export default function renderPreview({ reduxStore, storyStore }, forceRender = false) { const state = reduxStore.getState(); if (state.error) { return renderException(state.error); } try { - return renderMain(state, storyStore); + return renderMain(state, storyStore, forceRender); } catch (ex) { return renderException(ex); } diff --git a/app/react/src/client/index.js b/app/react/src/client/index.js index da10136436bc..889ca11bfa29 100644 --- a/app/react/src/client/index.js +++ b/app/react/src/client/index.js @@ -4,7 +4,14 @@ import deprecate from 'util-deprecate'; import { action as deprecatedAction } from '@storybook/addon-actions'; import { linkTo as deprecatedLinkTo } from '@storybook/addon-links'; -export { storiesOf, setAddon, addDecorator, configure, getStorybook } from './preview'; +export { + storiesOf, + setAddon, + addDecorator, + configure, + getStorybook, + forceReRender, +} from './preview'; export const action = deprecate( deprecatedAction, diff --git a/app/react/src/client/preview/index.js b/app/react/src/client/preview/index.js index 32a10109a781..733711486350 100644 --- a/app/react/src/client/preview/index.js +++ b/app/react/src/client/preview/index.js @@ -21,7 +21,9 @@ const isBrowser = !(navigator.userAgent.indexOf('jsdom') > -1); const storyStore = new StoryStore(); +/* eslint-disable no-underscore-dangle */ const reduxStore = createStore(reducer); +/* eslint-enable */ const context = { storyStore, reduxStore }; if (isBrowser) { @@ -54,3 +56,5 @@ const renderUI = () => { }; reduxStore.subscribe(renderUI); + +export const forceReRender = () => render(context, true); diff --git a/app/react/src/client/preview/render.js b/app/react/src/client/preview/render.js index 6ee916ac793e..4f003ce64721 100644 --- a/app/react/src/client/preview/render.js +++ b/app/react/src/client/preview/render.js @@ -39,7 +39,7 @@ export function renderException(error) { logger.error(error.stack); } -export function renderMain(data, storyStore) { +export function renderMain(data, storyStore, forceRender) { if (storyStore.size() === 0) return null; const NoPreview = () =>

No Preview Available!

; @@ -60,6 +60,7 @@ export function renderMain(data, storyStore) { // 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 @@ -109,14 +110,14 @@ export function renderMain(data, storyStore) { return null; } -export default function renderPreview({ reduxStore, storyStore }) { +export default function renderPreview({ reduxStore, storyStore }, forceRender = false) { const state = reduxStore.getState(); if (state.error) { return renderException(state.error); } try { - return renderMain(state, storyStore); + return renderMain(state, storyStore, forceRender); } catch (ex) { return renderException(ex); } diff --git a/app/vue/src/client/preview/index.js b/app/vue/src/client/preview/index.js index 32a10109a781..a3d7bf380ba0 100644 --- a/app/vue/src/client/preview/index.js +++ b/app/vue/src/client/preview/index.js @@ -54,3 +54,5 @@ const renderUI = () => { }; reduxStore.subscribe(renderUI); + +export const forceReRender = () => render(context, true); diff --git a/app/vue/src/client/preview/render.js b/app/vue/src/client/preview/render.js index ae5e4cf16112..caf6a9a8365c 100644 --- a/app/vue/src/client/preview/render.js +++ b/app/vue/src/client/preview/render.js @@ -49,7 +49,7 @@ function renderRoot(options) { app = new Vue(options); } -export function renderMain(data, storyStore) { +export function renderMain(data, storyStore, forceRender) { if (storyStore.size() === 0) return; const { selectedKind, selectedStory } = data; @@ -60,7 +60,7 @@ export function renderMain(data, storyStore) { // 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 - if (selectedKind !== previousKind || previousStory !== selectedStory) { + if (forceRender || selectedKind !== previousKind || previousStory !== selectedStory) { // We need to unmount the existing set of components in the DOM node. // Otherwise, React may not recrease instances for every story run. // This could leads to issues like below: @@ -97,14 +97,14 @@ export function renderMain(data, storyStore) { }); } -export default function renderPreview({ reduxStore, storyStore }) { +export default function renderPreview({ reduxStore, storyStore }, forceRender = false) { const state = reduxStore.getState(); if (state.error) { return renderException(state.error); } try { - return renderMain(state, storyStore); + return renderMain(state, storyStore, forceRender); } catch (ex) { return renderException(ex); } diff --git a/examples/cra-kitchen-sink/src/stories/__snapshots__/force-rerender.stories.storyshot b/examples/cra-kitchen-sink/src/stories/__snapshots__/force-rerender.stories.storyshot new file mode 100644 index 000000000000..fd64ff4e772b --- /dev/null +++ b/examples/cra-kitchen-sink/src/stories/__snapshots__/force-rerender.stories.storyshot @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots Force ReRender button 1`] = ` + +`; diff --git a/examples/cra-kitchen-sink/src/stories/force-rerender.stories.js b/examples/cra-kitchen-sink/src/stories/force-rerender.stories.js new file mode 100644 index 000000000000..92b83adfdcc3 --- /dev/null +++ b/examples/cra-kitchen-sink/src/stories/force-rerender.stories.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { storiesOf, forceReRender } from '@storybook/react'; + +let count = 0; +const increment = () => { + count += 1; + forceReRender(); +}; + +storiesOf('Force ReRender', module).add('button', () => ( + +));