diff --git a/lib/ui/src/core/context.js b/lib/ui/src/core/context.js index 6836cc31248a..3f44544b1cca 100644 --- a/lib/ui/src/core/context.js +++ b/lib/ui/src/core/context.js @@ -75,7 +75,8 @@ export class Provider extends Component { ].map(initModule => initModule(apiData)); // Create our initial state by combining the initial state of all modules, then overlaying any saved state - const state = store.getInitialState(getInitialState(...this.modules.map(m => m.state))); + this.state = store.getInitialState(); + const state = getInitialState(...this.modules.map(m => m.state)); // Get our API by combining the APIs exported by each module const combo = Object.assign({ navigate }, ...this.modules.map(m => m.api)); diff --git a/lib/ui/src/core/init-provider-api.js b/lib/ui/src/core/init-provider-api.js index a76e0a9dc9f9..b3414c4add6c 100644 --- a/lib/ui/src/core/init-provider-api.js +++ b/lib/ui/src/core/init-provider-api.js @@ -1,89 +1,6 @@ -import pick from 'lodash.pick'; -import deprecate from 'util-deprecate'; - -import { create } from '@storybook/theming'; - -const deprecationMessage = (optionsMap, prefix) => - `The options { ${Object.keys(optionsMap).join(', ')} } are deprecated -- use ${ - prefix ? `${prefix}'s` : '' - } { ${Object.values(optionsMap).join(', ')} } instead.`; - -const deprecatedThemeOptions = { - name: 'brandTitle', - url: 'brandUrl', -}; -const applyDeprecatedThemeOptions = deprecate(({ name, url, theme }) => { - const vars = { - brandTitle: name, - brandUrl: url, - brandImage: null, - }; - - return { theme: create(vars, theme) }; -}, deprecationMessage(deprecatedThemeOptions)); -const checkDeprecatedThemeOptions = options => { - if (Object.keys(deprecatedThemeOptions).find(key => !!options[key])) { - return applyDeprecatedThemeOptions(options); - } - return {}; -}; - -const deprecatedLayoutOptions = { - goFullScreen: 'isFullscreen', - showStoriesPanel: 'showNav', - showAddonPanel: 'showPanel', - addonPanelInRight: 'panelPosition', -}; -const applyDeprecatedLayoutOptions = deprecate(options => { - const layoutUpdate = {}; - - ['goFullScreen', 'showStoriesPanel', 'showAddonPanel'].forEach(option => { - if (typeof options[option] !== 'undefined') { - layoutUpdate[deprecatedLayoutOptions[option]] = options[option]; - } - }); - if (options.addonPanelInRight) { - layoutUpdate.panelPosition = 'right'; - } - return layoutUpdate; -}, deprecationMessage(deprecatedLayoutOptions)); -const checkDeprecatedLayoutOptions = options => { - if (Object.keys(deprecatedLayoutOptions).find(key => typeof options[key] !== 'undefined')) { - return applyDeprecatedLayoutOptions(options); - } - return {}; -}; - -export default ({ provider, api, store }) => { +export default ({ provider, api }) => { const providerAPI = { ...api, - - setOptions: options => { - const { layout, ui, selectedPanel } = store.getState(); - - if (options) { - const updatedLayout = { - ...layout, - ...pick(options, Object.keys(layout)), - ...checkDeprecatedLayoutOptions(options), - }; - - const updatedUi = { - ...ui, - ...pick(options, Object.keys(ui)), - ...checkDeprecatedThemeOptions(options), - }; - - store.setState( - { - layout: updatedLayout, - ui: updatedUi, - selectedPanel: options.panel || options.selectedPanel || selectedPanel, - }, - { persistence: 'permanent' } - ); - } - }, }; provider.handleAPI(providerAPI); diff --git a/lib/ui/src/core/initial-state.js b/lib/ui/src/core/initial-state.js index 5ac502039fc2..5eee085d6286 100644 --- a/lib/ui/src/core/initial-state.js +++ b/lib/ui/src/core/initial-state.js @@ -1,21 +1,6 @@ -import { themes } from '@storybook/theming'; - import merge from '../libs/merge'; const initial = { - ui: { - enableShortcuts: true, - sortStoriesByKind: false, - sidebarAnimations: true, - theme: themes.normal, - }, - layout: { - isToolshown: true, - isFullscreen: false, - showPanel: true, - showNav: true, - panelPosition: 'bottom', - }, customQueryParams: {}, storiesConfigured: false, }; diff --git a/lib/ui/src/core/layout.js b/lib/ui/src/core/layout.js index 80a9523899e2..9cc120595464 100644 --- a/lib/ui/src/core/layout.js +++ b/lib/ui/src/core/layout.js @@ -1,3 +1,64 @@ +import pick from 'lodash.pick'; + +import deprecate from 'util-deprecate'; + +import { create, themes } from '@storybook/theming'; +import merge from '../libs/merge'; + +const deprecatedThemeOptions = { + name: 'brandTitle', + url: 'brandUrl', +}; +const deprecatedLayoutOptions = { + goFullScreen: 'isFullscreen', + showStoriesPanel: 'showNav', + showAddonPanel: 'showPanel', + addonPanelInRight: 'panelPosition', +}; + +const deprecationMessage = (optionsMap, prefix) => + `The options { ${Object.keys(optionsMap).join(', ')} } are deprecated -- use ${ + prefix ? `${prefix}'s` : '' + } { ${Object.values(optionsMap).join(', ')} } instead.`; + +const applyDeprecatedThemeOptions = deprecate(({ name, url, theme }) => { + const vars = { + brandTitle: name, + brandUrl: url, + brandImage: null, + }; + + return { theme: create(vars, theme) }; +}, deprecationMessage(deprecatedThemeOptions)); + +const applyDeprecatedLayoutOptions = deprecate(options => { + const layoutUpdate = {}; + + ['goFullScreen', 'showStoriesPanel', 'showAddonPanel'].forEach(option => { + if (typeof options[option] !== 'undefined') { + layoutUpdate[deprecatedLayoutOptions[option]] = options[option]; + } + }); + if (options.addonPanelInRight) { + layoutUpdate.panelPosition = 'right'; + } + return layoutUpdate; +}, deprecationMessage(deprecatedLayoutOptions)); + +const checkDeprecatedThemeOptions = options => { + if (Object.keys(deprecatedThemeOptions).find(key => !!options[key])) { + return applyDeprecatedThemeOptions(options); + } + return {}; +}; + +const checkDeprecatedLayoutOptions = options => { + if (Object.keys(deprecatedLayoutOptions).find(key => typeof options[key] !== 'undefined')) { + return applyDeprecatedLayoutOptions(options); + } + return {}; +}; + export default function({ store }) { const api = { toggleFullscreen(toggled) { @@ -69,7 +130,54 @@ export default function({ store }) { }; }); }, + + setOptions: options => { + const { layout, ui, selectedPanel } = store.getState(); + + if (options) { + const updatedLayout = { + ...layout, + ...pick(options, Object.keys(layout)), + ...checkDeprecatedLayoutOptions(options), + }; + + const updatedUi = { + ...ui, + ...pick(options, Object.keys(ui)), + ...checkDeprecatedThemeOptions(options), + }; + + store.setState( + { + layout: updatedLayout, + ui: updatedUi, + selectedPanel: options.panel || options.selectedPanel || selectedPanel, + }, + { persistence: 'permanent' } + ); + } + }, + }; + + const fromState = pick(store.getState(), 'layout', 'ui', 'selectedPanel'); + const fromRestore = pick(store.getInitialState(), 'layout', 'ui', 'selectedPanel'); + const initial = { + ui: { + enableShortcuts: true, + sortStoriesByKind: false, + sidebarAnimations: true, + theme: themes.normal, + }, + layout: { + isToolshown: true, + isFullscreen: false, + showPanel: true, + showNav: true, + panelPosition: 'bottom', + }, }; - return { api }; + const state = merge({}, fromRestore, fromState, initial); + + return { api, state }; } diff --git a/lib/ui/src/core/store.js b/lib/ui/src/core/store.js index 3d1ce77aacba..4bab42572701 100644 --- a/lib/ui/src/core/store.js +++ b/lib/ui/src/core/store.js @@ -3,8 +3,6 @@ import { localStorage, sessionStorage } from 'global'; import { parse, stringify } from 'telejson'; -import merge from '../libs/merge'; - export const STORAGE_KEY = '@storybook/ui/store'; function get(storage) { @@ -32,11 +30,11 @@ export default class Store { // The assumption is that this will be called once, to initialize the React state // when the module is instanciated - getInitialState(base = {}) { + getInitialState() { // We don't only merge at the very top level (the same way as React setState) // when you set keys, so it makes sense to do the same in combining the two storage modes // Really, you shouldn't store the same key in both places - return merge(base, { ...get(localStorage), ...get(sessionStorage) }); + return { ...get(localStorage), ...get(sessionStorage) }; } getState() {