From 362a339e79ea30fd84833172fdbd07e91962d05b Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 28 Feb 2019 00:07:36 +0100 Subject: [PATCH 01/11] FIX the themeing flash & persist the theme into localstorage to prevent flashing upon reloading --- examples/official-storybook/config.js | 2 +- .../__snapshots__/storyshots.test.js.snap | 262 +++++++++++++++++- lib/ui/src/core/context.js | 14 +- lib/ui/src/core/init-provider-api.js | 13 +- 4 files changed, 282 insertions(+), 9 deletions(-) diff --git a/examples/official-storybook/config.js b/examples/official-storybook/config.js index 53b804623eea..45abe778d94a 100644 --- a/examples/official-storybook/config.js +++ b/examples/official-storybook/config.js @@ -51,7 +51,7 @@ addParameters({ options: { hierarchySeparator: /\/|\./, hierarchyRootSeparator: '|', - // theme: themes.dark, + theme: themes.dark, }, viewports: { ...INITIAL_VIEWPORTS, diff --git a/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap b/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap index a2c80ea1e626..8d0e28f56907 100644 --- a/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap +++ b/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap @@ -2638,7 +2638,267 @@ exports[`Storyshots Core|Parameters passed to story 1`] = ` Parameters are { "options": { "hierarchyRootSeparator": "|", - "hierarchySeparator": {} + "hierarchySeparator": {}, + "theme": { + "base": "dark", + "color": { + "primary": "#FF4785", + "secondary": "#1EA7FD", + "tertiary": "#FAFBFC", + "ancillary": "#22a699", + "orange": "#FC521F", + "gold": "#FFAE00", + "green": "#66BF3C", + "seafoam": "#37D5D3", + "purple": "#6F2CAC", + "ultraviolet": "#2A0481", + "lightest": "#FFFFFF", + "lighter": "#F8F8F8", + "light": "#F3F3F3", + "mediumlight": "#EEEEEE", + "medium": "#DDDDDD", + "mediumdark": "#999999", + "dark": "#666666", + "darker": "#444444", + "darkest": "#333333", + "border": "rgba(0,0,0,.1)", + "positive": "#66BF3C", + "negative": "#FF4400", + "warning": "#E69D00", + "defaultText": "#FFFFFF", + "inverseText": "#333333" + }, + "background": { + "app": "#2f2f2f", + "content": "#333", + "hoverable": "rgba(250,250,252,.1)", + "positive": "#E1FFD4", + "negative": "#FEDED2", + "warning": "#FFF5CF" + }, + "typography": { + "fonts": { + "base": "\\"Nunito Sans\\", -apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Helvetica Neue\\", Helvetica, Arial, sans-serif", + "mono": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace" + }, + "weight": { + "regular": 400, + "bold": 700, + "black": 900 + }, + "size": { + "s1": 12, + "s2": 14, + "s3": 16, + "m1": 20, + "m2": 24, + "m3": 28, + "l1": 32, + "l2": 40, + "l3": 48, + "code": 90 + } + }, + "animation": { + "rotate360": { + "name": "animation-u07e3c", + "styles": "@keyframes animation-u07e3c{\\n\\tfrom {\\n\\t\\ttransform: rotate(0deg);\\n\\t}\\n\\tto {\\n\\t\\ttransform: rotate(360deg);\\n\\t}\\n}", + "anim": 1 + }, + "glow": { + "name": "animation-r0iffl", + "styles": "@keyframes animation-r0iffl{\\n 0%, 100% { opacity: 1; }\\n 50% { opacity: .4; }\\n}", + "anim": 1 + }, + "float": { + "name": "animation-6tolu8", + "styles": "@keyframes animation-6tolu8{\\n 0% { transform: translateY(1px); }\\n 25% { transform: translateY(0px); }\\n 50% { transform: translateY(-3px); }\\n 100% { transform: translateY(1px); }\\n}", + "anim": 1 + }, + "jiggle": { + "name": "animation-ynpq7w", + "styles": "@keyframes animation-ynpq7w{\\n 0%, 100% { transform:translate3d(0,0,0); }\\n 12.5%, 62.5% { transform:translate3d(-4px,0,0); }\\n 37.5%, 87.5% { transform: translate3d(4px,0,0); }\\n}", + "anim": 1 + }, + "inlineGlow": { + "name": "zv3h0s", + "styles": "\\n animation: animation-r0iffl 1.5s ease-in-out infinite;\\n background: rgba(0,0,0,.1);\\n color: transparent;\\n cursor: progress;\\n", + "next": { + "name": "animation-r0iffl", + "styles": "@keyframes animation-r0iffl{\\n 0%, 100% { opacity: 1; }\\n 50% { opacity: .4; }\\n}" + } + }, + "hoverable": { + "name": "wpaw6f", + "styles": "\\n transition: all 150ms ease-out;\\n transform: translate3d(0, 0, 0);\\n\\n &:hover {\\n transform: translate3d(0, -2px, 0);\\n }\\n\\n &:active {\\n transform: translate3d(0, 0, 0);\\n }\\n" + } + }, + "easing": { + "rubber": "cubic-bezier(0.175, 0.885, 0.335, 1.05)" + }, + "input": { + "border": "rgba(0,0,0,.3)", + "background": "#3f3f3f", + "color": "#FFFFFF", + "borderRadius": 4 + }, + "layoutMargin": 10, + "appBorderColor": "rgba(255,255,255,.1)", + "appBorderRadius": 4, + "barTextColor": "#999999", + "barSelectedColor": "#1EA7FD", + "barBg": "#333333", + "brand": {}, + "code": { + "token": { + "fontFamily": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace", + "WebkitFontSmoothing": "antialiased", + "&.comment": { + "color": "#7C7C7C", + "fontStyle": "italic" + }, + "&.prolog": { + "color": "#7C7C7C", + "fontStyle": "italic" + }, + "&.doctype": { + "color": "#7C7C7C", + "fontStyle": "italic" + }, + "&.cdata": { + "color": "#7C7C7C", + "fontStyle": "italic" + }, + "&.string": { + "color": "#92C379" + }, + "&.punctuation": { + "color": "#EDEDED" + }, + "&.operator": { + "color": "#EDEDED" + }, + "&.url": { + "color": "#C6C5FE" + }, + "&.symbol": { + "color": "#C6C5FE" + }, + "&.number": { + "color": "#C6C5FE" + }, + "&.boolean": { + "color": "#C6C5FE" + }, + "&.variable": { + "color": "#C6C5FE" + }, + "&.constant": { + "color": "#C6C5FE" + }, + "&.inserted": { + "color": "#C6C5FE" + }, + "&.atrule": { + "color": "#B474DD" + }, + "&.keyword": { + "color": "#B474DD" + }, + "&.attr-value": { + "color": "#B474DD" + }, + "&.function": { + "color": "#EDEDED" + }, + "&.deleted": { + "color": "#9a050f" + }, + "&.important": { + "fontWeight": "bold" + }, + "&.bold": { + "fontWeight": "bold" + }, + "&.italic": { + "fontStyle": "italic" + }, + "&.class-name": { + "color": "#FFFFB6" + }, + "&.tag": { + "color": "#A8FF60" + }, + "&.selector": { + "color": "#A8FF60" + }, + "&.attr-name": { + "color": "#96CBFE" + }, + "&.property": { + "color": "#96CBFE" + }, + "&.regex": { + "color": "#96CBFE" + }, + "&.entity": { + "color": "#96CBFE" + }, + "&.directive.tag .tag": { + "background": "#ffff00", + "color": "#EDEDED" + } + }, + "language-json .token.boolean": { + "color": "#B474DD" + }, + "language-json .token.number": { + "color": "#B474DD" + }, + "language-json .token.property": { + "color": "#FFFFB6" + }, + "namespace": { + "opacity": 0.7 + } + }, + "addonActionsTheme": { + "BASE_FONT_FAMILY": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace", + "BASE_FONT_SIZE": 13, + "BASE_LINE_HEIGHT": "18px", + "BASE_BACKGROUND_COLOR": "transparent", + "BASE_COLOR": "#FFFFFF", + "OBJECT_NAME_COLOR": "rgb(227, 110, 236)", + "OBJECT_VALUE_NULL_COLOR": "rgb(127, 127, 127)", + "OBJECT_VALUE_UNDEFINED_COLOR": "rgb(127, 127, 127)", + "OBJECT_VALUE_REGEXP_COLOR": "rgb(233, 63, 59)", + "OBJECT_VALUE_STRING_COLOR": "rgb(233, 63, 59)", + "OBJECT_VALUE_SYMBOL_COLOR": "rgb(233, 63, 59)", + "OBJECT_VALUE_NUMBER_COLOR": "hsl(252, 100%, 75%)", + "OBJECT_VALUE_BOOLEAN_COLOR": "hsl(252, 100%, 75%)", + "OBJECT_VALUE_FUNCTION_KEYWORD_COLOR": "rgb(242, 85, 217)", + "HTML_TAG_COLOR": "rgb(93, 176, 215)", + "HTML_TAGNAME_COLOR": "rgb(93, 176, 215)", + "HTML_TAGNAME_TEXT_TRANSFORM": "lowercase", + "HTML_ATTRIBUTE_NAME_COLOR": "rgb(155, 187, 220)", + "HTML_ATTRIBUTE_VALUE_COLOR": "rgb(242, 151, 102)", + "HTML_COMMENT_COLOR": "rgb(137, 137, 137)", + "HTML_DOCTYPE_COLOR": "rgb(192, 192, 192)", + "ARROW_COLOR": "rgba(255,255,255,0.3)", + "ARROW_MARGIN_RIGHT": 4, + "ARROW_FONT_SIZE": 8, + "TREENODE_FONT_FAMILY": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace", + "TREENODE_FONT_SIZE": 13, + "TREENODE_LINE_HEIGHT": "18px", + "TREENODE_PADDING_LEFT": 12, + "TABLE_BORDER_COLOR": "rgb(85, 85, 85)", + "TABLE_TH_BACKGROUND_COLOR": "rgb(44, 44, 44)", + "TABLE_TH_HOVER_COLOR": "rgb(48, 48, 48)", + "TABLE_SORT_ICON_COLOR": "black", + "TABLE_DATA_BACKGROUND_IMAGE": "linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 50%, rgba(51, 139, 255, 0.0980392) 50%, rgba(51, 139, 255, 0.0980392))", + "TABLE_DATA_BACKGROUND_SIZE": "128px 32px" + } + } }, "a11y": { "configure": {}, diff --git a/lib/ui/src/core/context.js b/lib/ui/src/core/context.js index 7e03f6a0e8f4..63a7130d0366 100644 --- a/lib/ui/src/core/context.js +++ b/lib/ui/src/core/context.js @@ -90,7 +90,9 @@ export class Provider extends Component { api.on(SET_STORIES, data => { api.setStories(data.stories); - const options = api.getParameters(storyId, 'options'); + const options = storyId + ? api.getParameters(storyId, 'options') + : api.getParameters(Object.keys(data.stories)[0], 'options'); api.setOptions(options); }); @@ -101,7 +103,15 @@ export class Provider extends Component { api.selectStory(kind, story, rest); }); - this.state = state; + // TODO: cleanup into the module + // TODO: animation isn't serializing succesfully, should use telejson + const { + theme: { animation, ...rest }, + // eslint-disable-next-line react/destructuring-assignment + } = this.state.ui; + this.state = Object.assign({}, state, { + ui: { ...state.ui, theme: { ...state.ui.theme, ...rest } }, + }); this.api = api; } diff --git a/lib/ui/src/core/init-provider-api.js b/lib/ui/src/core/init-provider-api.js index f0267136cc62..a76e0a9dc9f9 100644 --- a/lib/ui/src/core/init-provider-api.js +++ b/lib/ui/src/core/init-provider-api.js @@ -74,11 +74,14 @@ export default ({ provider, api, store }) => { ...checkDeprecatedThemeOptions(options), }; - store.setState({ - layout: updatedLayout, - ui: updatedUi, - selectedPanel: options.panel || options.selectedPanel || selectedPanel, - }); + store.setState( + { + layout: updatedLayout, + ui: updatedUi, + selectedPanel: options.panel || options.selectedPanel || selectedPanel, + }, + { persistence: 'permanent' } + ); } }, }; From 2eed5f7c4e3d4cc4ff88f516b6c0e159d2196c04 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 28 Feb 2019 14:31:42 +0100 Subject: [PATCH 02/11] REMOVE options from snapshot --- .../stories/core/parameters.stories.js | 2 +- .../__snapshots__/storyshots.test.js.snap | 264 ------------------ 2 files changed, 1 insertion(+), 265 deletions(-) diff --git a/examples/official-storybook/stories/core/parameters.stories.js b/examples/official-storybook/stories/core/parameters.stories.js index 41b1e09052ef..b5ab588400a5 100644 --- a/examples/official-storybook/stories/core/parameters.stories.js +++ b/examples/official-storybook/stories/core/parameters.stories.js @@ -17,7 +17,7 @@ export default { // Given we sort of control the props, should we export a prop type? export const passed = ({ // eslint-disable-next-line react/prop-types - parameters, + parameters: { options, ...parameters }, }) =>
Parameters are {JSON.stringify(parameters, null, 2)}
; passed.title = 'passed to story'; passed.parameters = { storyParameter: 'storyParameter' }; diff --git a/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap b/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap index e5694b9332c5..500c98c03e64 100644 --- a/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap +++ b/examples/official-storybook/tests/__snapshots__/storyshots.test.js.snap @@ -5178,270 +5178,6 @@ exports[`Storyshots Core|Events Force re-render 1`] = ` exports[`Storyshots Core|Parameters passed to story 1`] = `
   Parameters are {
-  "options": {
-    "hierarchyRootSeparator": "|",
-    "hierarchySeparator": {},
-    "theme": {
-      "base": "dark",
-      "color": {
-        "primary": "#FF4785",
-        "secondary": "#1EA7FD",
-        "tertiary": "#FAFBFC",
-        "ancillary": "#22a699",
-        "orange": "#FC521F",
-        "gold": "#FFAE00",
-        "green": "#66BF3C",
-        "seafoam": "#37D5D3",
-        "purple": "#6F2CAC",
-        "ultraviolet": "#2A0481",
-        "lightest": "#FFFFFF",
-        "lighter": "#F8F8F8",
-        "light": "#F3F3F3",
-        "mediumlight": "#EEEEEE",
-        "medium": "#DDDDDD",
-        "mediumdark": "#999999",
-        "dark": "#666666",
-        "darker": "#444444",
-        "darkest": "#333333",
-        "border": "rgba(0,0,0,.1)",
-        "positive": "#66BF3C",
-        "negative": "#FF4400",
-        "warning": "#E69D00",
-        "defaultText": "#FFFFFF",
-        "inverseText": "#333333"
-      },
-      "background": {
-        "app": "#2f2f2f",
-        "content": "#333",
-        "hoverable": "rgba(250,250,252,.1)",
-        "positive": "#E1FFD4",
-        "negative": "#FEDED2",
-        "warning": "#FFF5CF"
-      },
-      "typography": {
-        "fonts": {
-          "base": "\\"Nunito Sans\\", -apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Helvetica Neue\\", Helvetica, Arial, sans-serif",
-          "mono": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace"
-        },
-        "weight": {
-          "regular": 400,
-          "bold": 700,
-          "black": 900
-        },
-        "size": {
-          "s1": 12,
-          "s2": 14,
-          "s3": 16,
-          "m1": 20,
-          "m2": 24,
-          "m3": 28,
-          "l1": 32,
-          "l2": 40,
-          "l3": 48,
-          "code": 90
-        }
-      },
-      "animation": {
-        "rotate360": {
-          "name": "animation-u07e3c",
-          "styles": "@keyframes animation-u07e3c{\\n\\tfrom {\\n\\t\\ttransform: rotate(0deg);\\n\\t}\\n\\tto {\\n\\t\\ttransform: rotate(360deg);\\n\\t}\\n}",
-          "anim": 1
-        },
-        "glow": {
-          "name": "animation-r0iffl",
-          "styles": "@keyframes animation-r0iffl{\\n  0%, 100% { opacity: 1; }\\n  50% { opacity: .4; }\\n}",
-          "anim": 1
-        },
-        "float": {
-          "name": "animation-6tolu8",
-          "styles": "@keyframes animation-6tolu8{\\n  0% { transform: translateY(1px); }\\n  25% { transform: translateY(0px); }\\n  50% { transform: translateY(-3px); }\\n  100% { transform: translateY(1px); }\\n}",
-          "anim": 1
-        },
-        "jiggle": {
-          "name": "animation-ynpq7w",
-          "styles": "@keyframes animation-ynpq7w{\\n  0%, 100% { transform:translate3d(0,0,0); }\\n  12.5%, 62.5% { transform:translate3d(-4px,0,0); }\\n  37.5%, 87.5% {  transform: translate3d(4px,0,0);  }\\n}",
-          "anim": 1
-        },
-        "inlineGlow": {
-          "name": "zv3h0s",
-          "styles": "\\n  animation: animation-r0iffl 1.5s ease-in-out infinite;\\n  background: rgba(0,0,0,.1);\\n  color: transparent;\\n  cursor: progress;\\n",
-          "next": {
-            "name": "animation-r0iffl",
-            "styles": "@keyframes animation-r0iffl{\\n  0%, 100% { opacity: 1; }\\n  50% { opacity: .4; }\\n}"
-          }
-        },
-        "hoverable": {
-          "name": "wpaw6f",
-          "styles": "\\n  transition: all 150ms ease-out;\\n  transform: translate3d(0, 0, 0);\\n\\n  &:hover {\\n    transform: translate3d(0, -2px, 0);\\n  }\\n\\n  &:active {\\n    transform: translate3d(0, 0, 0);\\n  }\\n"
-        }
-      },
-      "easing": {
-        "rubber": "cubic-bezier(0.175, 0.885, 0.335, 1.05)"
-      },
-      "input": {
-        "border": "rgba(0,0,0,.3)",
-        "background": "#3f3f3f",
-        "color": "#FFFFFF",
-        "borderRadius": 4
-      },
-      "layoutMargin": 10,
-      "appBorderColor": "rgba(255,255,255,.1)",
-      "appBorderRadius": 4,
-      "barTextColor": "#999999",
-      "barSelectedColor": "#1EA7FD",
-      "barBg": "#333333",
-      "brand": {},
-      "code": {
-        "token": {
-          "fontFamily": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace",
-          "WebkitFontSmoothing": "antialiased",
-          "&.comment": {
-            "color": "#7C7C7C",
-            "fontStyle": "italic"
-          },
-          "&.prolog": {
-            "color": "#7C7C7C",
-            "fontStyle": "italic"
-          },
-          "&.doctype": {
-            "color": "#7C7C7C",
-            "fontStyle": "italic"
-          },
-          "&.cdata": {
-            "color": "#7C7C7C",
-            "fontStyle": "italic"
-          },
-          "&.string": {
-            "color": "#92C379"
-          },
-          "&.punctuation": {
-            "color": "#EDEDED"
-          },
-          "&.operator": {
-            "color": "#EDEDED"
-          },
-          "&.url": {
-            "color": "#C6C5FE"
-          },
-          "&.symbol": {
-            "color": "#C6C5FE"
-          },
-          "&.number": {
-            "color": "#C6C5FE"
-          },
-          "&.boolean": {
-            "color": "#C6C5FE"
-          },
-          "&.variable": {
-            "color": "#C6C5FE"
-          },
-          "&.constant": {
-            "color": "#C6C5FE"
-          },
-          "&.inserted": {
-            "color": "#C6C5FE"
-          },
-          "&.atrule": {
-            "color": "#B474DD"
-          },
-          "&.keyword": {
-            "color": "#B474DD"
-          },
-          "&.attr-value": {
-            "color": "#B474DD"
-          },
-          "&.function": {
-            "color": "#EDEDED"
-          },
-          "&.deleted": {
-            "color": "#9a050f"
-          },
-          "&.important": {
-            "fontWeight": "bold"
-          },
-          "&.bold": {
-            "fontWeight": "bold"
-          },
-          "&.italic": {
-            "fontStyle": "italic"
-          },
-          "&.class-name": {
-            "color": "#FFFFB6"
-          },
-          "&.tag": {
-            "color": "#A8FF60"
-          },
-          "&.selector": {
-            "color": "#A8FF60"
-          },
-          "&.attr-name": {
-            "color": "#96CBFE"
-          },
-          "&.property": {
-            "color": "#96CBFE"
-          },
-          "&.regex": {
-            "color": "#96CBFE"
-          },
-          "&.entity": {
-            "color": "#96CBFE"
-          },
-          "&.directive.tag .tag": {
-            "background": "#ffff00",
-            "color": "#EDEDED"
-          }
-        },
-        "language-json .token.boolean": {
-          "color": "#B474DD"
-        },
-        "language-json .token.number": {
-          "color": "#B474DD"
-        },
-        "language-json .token.property": {
-          "color": "#FFFFB6"
-        },
-        "namespace": {
-          "opacity": 0.7
-        }
-      },
-      "addonActionsTheme": {
-        "BASE_FONT_FAMILY": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace",
-        "BASE_FONT_SIZE": 13,
-        "BASE_LINE_HEIGHT": "18px",
-        "BASE_BACKGROUND_COLOR": "transparent",
-        "BASE_COLOR": "#FFFFFF",
-        "OBJECT_NAME_COLOR": "rgb(227, 110, 236)",
-        "OBJECT_VALUE_NULL_COLOR": "rgb(127, 127, 127)",
-        "OBJECT_VALUE_UNDEFINED_COLOR": "rgb(127, 127, 127)",
-        "OBJECT_VALUE_REGEXP_COLOR": "rgb(233, 63, 59)",
-        "OBJECT_VALUE_STRING_COLOR": "rgb(233, 63, 59)",
-        "OBJECT_VALUE_SYMBOL_COLOR": "rgb(233, 63, 59)",
-        "OBJECT_VALUE_NUMBER_COLOR": "hsl(252, 100%, 75%)",
-        "OBJECT_VALUE_BOOLEAN_COLOR": "hsl(252, 100%, 75%)",
-        "OBJECT_VALUE_FUNCTION_KEYWORD_COLOR": "rgb(242, 85, 217)",
-        "HTML_TAG_COLOR": "rgb(93, 176, 215)",
-        "HTML_TAGNAME_COLOR": "rgb(93, 176, 215)",
-        "HTML_TAGNAME_TEXT_TRANSFORM": "lowercase",
-        "HTML_ATTRIBUTE_NAME_COLOR": "rgb(155, 187, 220)",
-        "HTML_ATTRIBUTE_VALUE_COLOR": "rgb(242, 151, 102)",
-        "HTML_COMMENT_COLOR": "rgb(137, 137, 137)",
-        "HTML_DOCTYPE_COLOR": "rgb(192, 192, 192)",
-        "ARROW_COLOR": "rgba(255,255,255,0.3)",
-        "ARROW_MARGIN_RIGHT": 4,
-        "ARROW_FONT_SIZE": 8,
-        "TREENODE_FONT_FAMILY": "\\"Operator Mono\\", \\"Fira Code Retina\\", \\"Fira Code\\", \\"FiraCode-Retina\\", \\"Andale Mono\\", \\"Lucida Console\\", Consolas, Monaco, monospace",
-        "TREENODE_FONT_SIZE": 13,
-        "TREENODE_LINE_HEIGHT": "18px",
-        "TREENODE_PADDING_LEFT": 12,
-        "TABLE_BORDER_COLOR": "rgb(85, 85, 85)",
-        "TABLE_TH_BACKGROUND_COLOR": "rgb(44, 44, 44)",
-        "TABLE_TH_HOVER_COLOR": "rgb(48, 48, 48)",
-        "TABLE_SORT_ICON_COLOR": "black",
-        "TABLE_DATA_BACKGROUND_IMAGE": "linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 50%, rgba(51, 139, 255, 0.0980392) 50%, rgba(51, 139, 255, 0.0980392))",
-        "TABLE_DATA_BACKGROUND_SIZE": "128px 32px"
-      }
-    }
-  },
   "a11y": {
     "configure": {},
     "options": {

From 9a5ccca75b6d70f6ef934b44b14fb8a58a20226b Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Thu, 28 Feb 2019 14:51:04 +0100
Subject: [PATCH 03/11] FIX problem with loading empty localStorage to theme

---
 lib/ui/src/core/context.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/ui/src/core/context.js b/lib/ui/src/core/context.js
index 63a7130d0366..e7e94faa0ae3 100644
--- a/lib/ui/src/core/context.js
+++ b/lib/ui/src/core/context.js
@@ -108,7 +108,7 @@ export class Provider extends Component {
     const {
       theme: { animation, ...rest },
       // eslint-disable-next-line react/destructuring-assignment
-    } = this.state.ui;
+    } = this.state.ui || { theme: {} };
     this.state = Object.assign({}, state, {
       ui: { ...state.ui, theme: { ...state.ui.theme, ...rest } },
     });

From 1ecf313c5cf518660c6ccf54ef5a5b45ac620bed Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Thu, 28 Feb 2019 22:55:48 +0100
Subject: [PATCH 04/11] CHANGE the maxDepth of stringify in postMessage channel
 as part of the theme was getting lost ADD telejson as stringifier & parser
 for persistence CLEANUP state initialization CHANGE initial state for
 shortcuts so it's not dependent on this.getState() DEDUPLICATE merge util
 function FIX unnecessary messages about transition & brand missing in theme

---
 lib/channel-postmessage/src/index.ts |  2 +-
 lib/theming/src/ensure.ts            |  4 +++-
 lib/ui/package.json                  |  1 +
 lib/ui/src/core/context.js           | 15 ++++-----------
 lib/ui/src/core/shortcuts.js         |  3 ++-
 lib/ui/src/core/store.js             | 11 +++++++----
 lib/ui/src/core/stories.js           | 22 +---------------------
 7 files changed, 19 insertions(+), 39 deletions(-)

diff --git a/lib/channel-postmessage/src/index.ts b/lib/channel-postmessage/src/index.ts
index 25d672a6e238..b870100cd0e6 100644
--- a/lib/channel-postmessage/src/index.ts
+++ b/lib/channel-postmessage/src/index.ts
@@ -56,7 +56,7 @@ export class PostmsgTransport {
       });
     }
 
-    const data = stringify({ key: KEY, event }, { maxDepth: 10 });
+    const data = stringify({ key: KEY, event }, { maxDepth: 15 });
 
     // TODO: investigate http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage
     // might replace '*' with document.location ?
diff --git a/lib/theming/src/ensure.ts b/lib/theming/src/ensure.ts
index c206a73b0c9a..a485932140e2 100644
--- a/lib/theming/src/ensure.ts
+++ b/lib/theming/src/ensure.ts
@@ -9,6 +9,8 @@ import isEqual from 'lodash.isequal';
 import light from './themes/light';
 import { Theme } from './base';
 
+const base = { ...light, animation: undefined, brand: undefined };
+
 // merge with concatenating arrays, but no duplicates
 const merge = (a: any, b: any) =>
   mergeWith({}, a, b, (objValue: any, srcValue: any) => {
@@ -32,7 +34,7 @@ export const ensure = (input: any): Theme => {
   if (!input) {
     return light;
   } else {
-    const missing = deletedDiff(light, input);
+    const missing = deletedDiff(base, input);
     if (Object.keys(missing).length) {
       logger.warn(
         stripIndent`
diff --git a/lib/ui/package.json b/lib/ui/package.json
index cdd4c4679711..1bb5dd7c0f4e 100644
--- a/lib/ui/package.json
+++ b/lib/ui/package.json
@@ -47,6 +47,7 @@
     "react-resize-detector": "^3.2.1",
     "recompose": "^0.30.0",
     "semver": "^5.6.0",
+    "telejson": "^2.1.1",
     "util-deprecate": "^1.0.2"
   },
   "devDependencies": {
diff --git a/lib/ui/src/core/context.js b/lib/ui/src/core/context.js
index e7e94faa0ae3..6836cc31248a 100644
--- a/lib/ui/src/core/context.js
+++ b/lib/ui/src/core/context.js
@@ -50,7 +50,6 @@ export class Provider extends Component {
     // Initialize the state to be the initial (persisted) state of the store.
     // This gives the modules the chance to read the persisted state, apply their defaults
     // and override if necessary
-    this.state = store.getInitialState();
 
     const apiData = {
       navigate,
@@ -62,6 +61,8 @@ export class Provider extends Component {
       storyId,
     };
 
+    this.state = {};
+
     this.modules = [
       initChannel,
       initAddons,
@@ -74,7 +75,7 @@ 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 = getInitialState(...this.modules.map(m => m.state));
+    const state = store.getInitialState(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));
@@ -103,15 +104,7 @@ export class Provider extends Component {
       api.selectStory(kind, story, rest);
     });
 
-    // TODO: cleanup into the module
-    // TODO: animation isn't serializing succesfully, should use telejson
-    const {
-      theme: { animation, ...rest },
-      // eslint-disable-next-line react/destructuring-assignment
-    } = this.state.ui || { theme: {} };
-    this.state = Object.assign({}, state, {
-      ui: { ...state.ui, theme: { ...state.ui.theme, ...rest } },
-    });
+    this.state = state;
     this.api = api;
   }
 
diff --git a/lib/ui/src/core/shortcuts.js b/lib/ui/src/core/shortcuts.js
index 9c07925da757..3cdab8d48107 100644
--- a/lib/ui/src/core/shortcuts.js
+++ b/lib/ui/src/core/shortcuts.js
@@ -1,6 +1,7 @@
 import { navigator, document } from 'global';
 import { PREVIEW_KEYDOWN } from '@storybook/core-events';
 
+import getInitialState from './initial-state';
 import { shortcutMatchesShortcut, eventToShortcut } from '../libs/shortcut';
 
 export const isMacLike = () =>
@@ -31,7 +32,7 @@ export default function initShortcuts({ store }) {
   const api = {
     // Getting and setting shortcuts
     getShortcutKeys() {
-      return store.getState().shortcuts;
+      return store.getState().shortcuts || getInitialState().shortcuts;
     },
     async setShortcuts(shortcuts) {
       await store.setState({ shortcuts }, { persistence: 'permanent' });
diff --git a/lib/ui/src/core/store.js b/lib/ui/src/core/store.js
index c87ada758e15..3d1ce77aacba 100644
--- a/lib/ui/src/core/store.js
+++ b/lib/ui/src/core/store.js
@@ -1,16 +1,19 @@
 // TODO -- make this TS?
 
 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) {
   const serialized = storage.getItem(STORAGE_KEY);
-  return serialized ? JSON.parse(serialized) : {};
+  return serialized ? parse(serialized) : {};
 }
 
 function set(storage, value) {
-  storage.setItem(STORAGE_KEY, JSON.stringify(value));
+  storage.setItem(STORAGE_KEY, stringify(value, { maxDepth: 50 }));
 }
 
 function update(storage, patch) {
@@ -29,11 +32,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() {
+  getInitialState(base = {}) {
     // 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 { ...get(localStorage), ...get(sessionStorage) };
+    return merge(base, { ...get(localStorage), ...get(sessionStorage) });
   }
 
   getState() {
diff --git a/lib/ui/src/core/stories.js b/lib/ui/src/core/stories.js
index 8eb896bf6dfa..654f10cf0e79 100644
--- a/lib/ui/src/core/stories.js
+++ b/lib/ui/src/core/stories.js
@@ -1,26 +1,6 @@
-import mergeWith from 'lodash.mergewith';
-import isEqual from 'lodash.isequal';
 import { toId, sanitize } from '@storybook/router/utils';
 
-const merge = (a, b) =>
-  mergeWith({}, a, b, (objValue, srcValue) => {
-    if (Array.isArray(srcValue) && Array.isArray(objValue)) {
-      srcValue.forEach(s => {
-        const existing = objValue.find(o => o === s || isEqual(o, s));
-        if (!existing) {
-          objValue.push(s);
-        }
-      });
-
-      return objValue;
-    }
-    if (Array.isArray(objValue)) {
-      // eslint-disable-next-line no-console
-      console.log('the types mismatch, picking', objValue);
-      return objValue;
-    }
-    return undefined;
-  });
+import merge from '../libs/merge';
 
 const initStoriesApi = ({
   store,

From ef1f79c3ddec6d980b5113e139e78c5789cf9aea Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Thu, 28 Feb 2019 22:58:37 +0100
Subject: [PATCH 05/11] REMOVE dark theme

---
 examples/official-storybook/config.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/examples/official-storybook/config.js b/examples/official-storybook/config.js
index 45abe778d94a..f394a8dab04b 100644
--- a/examples/official-storybook/config.js
+++ b/examples/official-storybook/config.js
@@ -51,7 +51,6 @@ addParameters({
   options: {
     hierarchySeparator: /\/|\./,
     hierarchyRootSeparator: '|',
-    theme: themes.dark,
   },
   viewports: {
     ...INITIAL_VIEWPORTS,

From d9bb177884c1881ccb9d7f8afeebd54c4892df5c Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Thu, 28 Feb 2019 23:17:08 +0100
Subject: [PATCH 06/11] FIX typescript issue

---
 lib/theming/src/ensure.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/theming/src/ensure.ts b/lib/theming/src/ensure.ts
index a485932140e2..70fe47718127 100644
--- a/lib/theming/src/ensure.ts
+++ b/lib/theming/src/ensure.ts
@@ -9,7 +9,11 @@ import isEqual from 'lodash.isequal';
 import light from './themes/light';
 import { Theme } from './base';
 
-const base = { ...light, animation: undefined, brand: undefined };
+const base = {
+  ...light,
+  animation: {},
+  brand: {},
+};
 
 // merge with concatenating arrays, but no duplicates
 const merge = (a: any, b: any) =>

From 16ff72322ae67e96dbd8a99569bdab564775eb67 Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Fri, 1 Mar 2019 12:52:01 +0100
Subject: [PATCH 07/11] IMPROVE the default size of panels when switching
 bottom and right (minimal panelsize & maximum main size)

---
 lib/ui/src/components/layout/container.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lib/ui/src/components/layout/container.js b/lib/ui/src/components/layout/container.js
index be43e8248087..33f23593ea94 100644
--- a/lib/ui/src/components/layout/container.js
+++ b/lib/ui/src/components/layout/container.js
@@ -270,7 +270,7 @@ class Layout extends Component {
   constructor(props) {
     super(props);
 
-    const { options } = this.props;
+    const { bounds, options } = props;
 
     const { resizerNav, resizerPanel } = persistance.get();
 
@@ -278,7 +278,10 @@ class Layout extends Component {
       isDragging: false,
       resizerNav: resizerNav || { x: 200, y: 0 },
       resizerPanel:
-        resizerPanel || (options.panelPosition === 'bottom' ? { x: 0, y: 400 } : { x: 400, y: 0 }),
+        resizerPanel ||
+        (options.panelPosition === 'bottom'
+          ? { x: 0, y: bounds.height - 400 }
+          : { x: bounds.width - 400, y: 0 }),
     };
   }
 
@@ -323,7 +326,7 @@ class Layout extends Component {
       }
     }
     if (isPanelRight && !isPanelHidden) {
-      if (bounds.width - 200 < panelX) {
+      if (bounds.width - 200 < panelX || panelX === 0) {
         mutation.resizerPanel = {
           x: bounds.width - 200,
           y: 0,

From 7622f72ae37c960a1f9562e521867e7c9219d1a5 Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Fri, 1 Mar 2019 15:58:50 +0100
Subject: [PATCH 08/11] MOVE setOptions to layout module REMOVE merge from
 initial state MOVE initial state of layout & ui to layout module ADD merge of
 initial, restored to layout module

---
 lib/ui/src/core/context.js           |   4 +-
 lib/ui/src/core/init-provider-api.js |  85 +--------------------
 lib/ui/src/core/initial-state.js     |  15 ----
 lib/ui/src/core/layout.js            | 110 ++++++++++++++++++++++++++-
 lib/ui/src/core/store.js             |   6 +-
 5 files changed, 114 insertions(+), 106 deletions(-)

diff --git a/lib/ui/src/core/context.js b/lib/ui/src/core/context.js
index 6836cc31248a..04e13d1d6305 100644
--- a/lib/ui/src/core/context.js
+++ b/lib/ui/src/core/context.js
@@ -61,7 +61,7 @@ export class Provider extends Component {
       storyId,
     };
 
-    this.state = {};
+    this.state = store.getInitialState();
 
     this.modules = [
       initChannel,
@@ -75,7 +75,7 @@ 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)));
+    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() {

From f03d7e903e27e2bee767bdb2e58f896130f5905d Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Fri, 1 Mar 2019 17:10:10 +0100
Subject: [PATCH 09/11] FIX theming bug

---
 lib/ui/src/core/layout.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/ui/src/core/layout.js b/lib/ui/src/core/layout.js
index 9cc120595464..d6bf8ef8ce9f 100644
--- a/lib/ui/src/core/layout.js
+++ b/lib/ui/src/core/layout.js
@@ -160,7 +160,7 @@ export default function({ store }) {
   };
 
   const fromState = pick(store.getState(), 'layout', 'ui', 'selectedPanel');
-  const fromRestore = pick(store.getInitialState(), 'layout', 'ui', 'selectedPanel');
+
   const initial = {
     ui: {
       enableShortcuts: true,
@@ -177,7 +177,7 @@ export default function({ store }) {
     },
   };
 
-  const state = merge({}, fromRestore, fromState, initial);
+  const state = merge(fromState, initial);
 
   return { api, state };
 }

From 81770ccf14207c5a6d1b8d535a8c65fa1b01379d Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Fri, 1 Mar 2019 17:14:06 +0100
Subject: [PATCH 10/11] FIX selectedPanel not being stored to persisted store

---
 lib/ui/src/core/addons.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/lib/ui/src/core/addons.js b/lib/ui/src/core/addons.js
index 996233d5467f..b34d1314230c 100644
--- a/lib/ui/src/core/addons.js
+++ b/lib/ui/src/core/addons.js
@@ -22,14 +22,18 @@ export default ({ provider, store }) => {
       return ensurePanel(api.getPanels(), selectedPanel, selectedPanel);
     },
     setSelectedPanel: panelName => {
-      store.setState({ selectedPanel: panelName });
+      store.setState({ selectedPanel: panelName }, { persistence: 'session' });
     },
   };
 
   return {
     api,
     state: {
-      selectedPanel: ensurePanel(api.getPanels()),
+      selectedPanel: ensurePanel(
+        api.getPanels(),
+        store.getState().selectedPanel,
+        store.getState().selectedPanel
+      ),
     },
   };
 };

From 6489d0547f5622d8c3c0c650515a7b9de8095902 Mon Sep 17 00:00:00 2001
From: Norbert de Langen 
Date: Sat, 2 Mar 2019 18:24:05 +0100
Subject: [PATCH 11/11] CLEANUP

---
 lib/ui/src/core/addons.js    | 6 +-----
 lib/ui/src/core/shortcuts.js | 3 +--
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/lib/ui/src/core/addons.js b/lib/ui/src/core/addons.js
index b34d1314230c..7ebc7cc5ac4a 100644
--- a/lib/ui/src/core/addons.js
+++ b/lib/ui/src/core/addons.js
@@ -29,11 +29,7 @@ export default ({ provider, store }) => {
   return {
     api,
     state: {
-      selectedPanel: ensurePanel(
-        api.getPanels(),
-        store.getState().selectedPanel,
-        store.getState().selectedPanel
-      ),
+      selectedPanel: ensurePanel(api.getPanels(), store.getState().selectedPanel),
     },
   };
 };
diff --git a/lib/ui/src/core/shortcuts.js b/lib/ui/src/core/shortcuts.js
index 3cdab8d48107..9c07925da757 100644
--- a/lib/ui/src/core/shortcuts.js
+++ b/lib/ui/src/core/shortcuts.js
@@ -1,7 +1,6 @@
 import { navigator, document } from 'global';
 import { PREVIEW_KEYDOWN } from '@storybook/core-events';
 
-import getInitialState from './initial-state';
 import { shortcutMatchesShortcut, eventToShortcut } from '../libs/shortcut';
 
 export const isMacLike = () =>
@@ -32,7 +31,7 @@ export default function initShortcuts({ store }) {
   const api = {
     // Getting and setting shortcuts
     getShortcutKeys() {
-      return store.getState().shortcuts || getInitialState().shortcuts;
+      return store.getState().shortcuts;
     },
     async setShortcuts(shortcuts) {
       await store.setState({ shortcuts }, { persistence: 'permanent' });