UNPKG

8.38 kBJavaScriptView Raw
1const _excluded = ["full", "panel", "nav", "shortcuts", "addonPanel", "tabs", "addons", "panelRight", "stories", "selectedKind", "selectedStory", "path"],
2 _excluded2 = ["store", "navigate", "state", "provider", "fullAPI"];
3import "core-js/modules/es.array.reduce.js";
4
5function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
6
7import { once } from '@storybook/client-logger';
8import { NAVIGATE_URL, STORY_ARGS_UPDATED, SET_CURRENT_STORY, GLOBALS_UPDATED, UPDATE_QUERY_PARAMS } from '@storybook/core-events';
9import { queryFromLocation, buildArgsParam } from '@storybook/router';
10import { toId, sanitize } from '@storybook/csf';
11import deepEqual from 'fast-deep-equal';
12import global from 'global';
13import dedent from 'ts-dedent';
14import { isStory } from '../lib/stories';
15const {
16 window: globalWindow
17} = global;
18
19const parseBoolean = value => {
20 if (value === 'true' || value === '1') return true;
21 if (value === 'false' || value === '0') return false;
22 return undefined;
23}; // Initialize the state based on the URL.
24// NOTE:
25// Although we don't change the URL when you change the state, we do support setting initial state
26// via the following URL parameters:
27// - full: 0/1 -- show fullscreen
28// - panel: bottom/right/0 -- set addons panel position (or hide)
29// - nav: 0/1 -- show or hide the story list
30//
31// We also support legacy URLs from storybook <5
32
33
34let prevParams;
35
36const initialUrlSupport = ({
37 state: {
38 location,
39 path,
40 viewMode,
41 storyId: storyIdFromUrl
42 },
43 singleStory
44}) => {
45 const _queryFromLocation = queryFromLocation(location),
46 {
47 full,
48 panel,
49 nav,
50 shortcuts,
51 addonPanel,
52 tabs,
53 addons,
54 // deprecated
55 panelRight,
56 // deprecated
57 stories,
58 // deprecated
59 selectedKind,
60 // deprecated
61 selectedStory // the rest gets passed to the iframe
62
63 } = _queryFromLocation,
64 otherParams = _objectWithoutPropertiesLoose(_queryFromLocation, _excluded);
65
66 const layout = {
67 isFullscreen: parseBoolean(full),
68 showNav: !singleStory && parseBoolean(nav),
69 showPanel: parseBoolean(panel),
70 panelPosition: ['right', 'bottom'].includes(panel) ? panel : undefined,
71 showTabs: parseBoolean(tabs)
72 };
73 const ui = {
74 enableShortcuts: parseBoolean(shortcuts)
75 };
76 const selectedPanel = addonPanel || undefined; // @deprecated Superceded by `panel=false`, to be removed in 7.0
77
78 if (addons === '0') {
79 once.warn(dedent`
80 The 'addons' query param is deprecated and will be removed in Storybook 7.0. Use 'panel=false' instead.
81
82 More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-layout-url-params
83 `);
84 layout.showPanel = false;
85 } // @deprecated Superceded by `panel=right`, to be removed in 7.0
86
87
88 if (panelRight === '1') {
89 once.warn(dedent`
90 The 'panelRight' query param is deprecated and will be removed in Storybook 7.0. Use 'panel=right' instead.
91
92 More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-layout-url-params
93 `);
94 layout.panelPosition = 'right';
95 } // @deprecated Superceded by `nav=false`, to be removed in 7.0
96
97
98 if (stories === '0') {
99 once.warn(dedent`
100 The 'stories' query param is deprecated and will be removed in Storybook 7.0. Use 'nav=false' instead.
101
102 More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-layout-url-params
103 `);
104 layout.showNav = false;
105 } // @deprecated To be removed in 7.0
106 // If the user hasn't set the storyId on the URL, we support legacy URLs (selectedKind/selectedStory)
107 // NOTE: this "storyId" can just be a prefix of a storyId, really it is a storyIdSpecifier.
108
109
110 let storyId = storyIdFromUrl;
111
112 if (!storyId && selectedKind) {
113 once.warn(dedent`
114 The 'selectedKind' and 'selectedStory' query params are deprecated and will be removed in Storybook 7.0. Use 'path' instead.
115
116 More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-layout-url-params
117 `);
118 storyId = selectedStory ? toId(selectedKind, selectedStory) : sanitize(selectedKind);
119 } // Avoid returning a new object each time if no params actually changed.
120
121
122 const customQueryParams = deepEqual(prevParams, otherParams) ? prevParams : otherParams;
123 prevParams = customQueryParams;
124 return {
125 viewMode,
126 layout,
127 ui,
128 selectedPanel,
129 location,
130 path,
131 customQueryParams,
132 storyId
133 };
134};
135
136export const init = _ref => {
137 let {
138 store,
139 navigate,
140 state,
141 provider,
142 fullAPI
143 } = _ref,
144 rest = _objectWithoutPropertiesLoose(_ref, _excluded2);
145
146 const navigateTo = (path, queryParams = {}, options = {}) => {
147 const params = Object.entries(queryParams).filter(([, v]) => v).sort(([a], [b]) => a < b ? -1 : 1).map(([k, v]) => `${k}=${v}`);
148 const to = [path, ...params].join('&');
149 return navigate(to, options);
150 };
151
152 const api = {
153 getQueryParam(key) {
154 const {
155 customQueryParams
156 } = store.getState();
157 return customQueryParams ? customQueryParams[key] : undefined;
158 },
159
160 getUrlState() {
161 const {
162 path,
163 customQueryParams,
164 storyId,
165 url,
166 viewMode
167 } = store.getState();
168 return {
169 path,
170 queryParams: customQueryParams,
171 storyId,
172 url,
173 viewMode
174 };
175 },
176
177 setQueryParams(input) {
178 const {
179 customQueryParams
180 } = store.getState();
181 const queryParams = {};
182 const update = Object.assign({}, customQueryParams, Object.entries(input).reduce((acc, [key, value]) => {
183 if (value !== null) {
184 acc[key] = value;
185 }
186
187 return acc;
188 }, queryParams));
189
190 if (!deepEqual(customQueryParams, update)) {
191 store.setState({
192 customQueryParams: update
193 });
194 fullAPI.emit(UPDATE_QUERY_PARAMS, update);
195 }
196 },
197
198 navigateUrl(url, options) {
199 navigate(url, Object.assign({}, options, {
200 plain: true
201 }));
202 }
203
204 };
205
206 const initModule = () => {
207 // Sets `args` parameter in URL, omitting any args that have their initial value or cannot be unserialized safely.
208 const updateArgsParam = () => {
209 const {
210 path,
211 queryParams,
212 viewMode
213 } = fullAPI.getUrlState();
214 if (viewMode !== 'story') return;
215 const currentStory = fullAPI.getCurrentStoryData();
216 if (!isStory(currentStory)) return;
217 const {
218 args,
219 initialArgs
220 } = currentStory;
221 const argsString = buildArgsParam(initialArgs, args);
222 navigateTo(path, Object.assign({}, queryParams, {
223 args: argsString
224 }), {
225 replace: true
226 });
227 api.setQueryParams({
228 args: argsString
229 });
230 };
231
232 fullAPI.on(SET_CURRENT_STORY, () => updateArgsParam());
233 let handleOrId;
234 fullAPI.on(STORY_ARGS_UPDATED, () => {
235 if ('requestIdleCallback' in globalWindow) {
236 if (handleOrId) globalWindow.cancelIdleCallback(handleOrId);
237 handleOrId = globalWindow.requestIdleCallback(updateArgsParam, {
238 timeout: 1000
239 });
240 } else {
241 if (handleOrId) clearTimeout(handleOrId);
242 setTimeout(updateArgsParam, 100);
243 }
244 });
245 fullAPI.on(GLOBALS_UPDATED, ({
246 globals,
247 initialGlobals
248 }) => {
249 const {
250 path,
251 queryParams
252 } = fullAPI.getUrlState();
253 const globalsString = buildArgsParam(initialGlobals, globals);
254 navigateTo(path, Object.assign({}, queryParams, {
255 globals: globalsString
256 }), {
257 replace: true
258 });
259 api.setQueryParams({
260 globals: globalsString
261 });
262 });
263 fullAPI.on(NAVIGATE_URL, (url, options) => {
264 fullAPI.navigateUrl(url, options);
265 });
266
267 if (fullAPI.showReleaseNotesOnLaunch()) {
268 navigate('/settings/release-notes');
269 }
270 };
271
272 return {
273 api,
274 state: initialUrlSupport(Object.assign({
275 store,
276 navigate,
277 state,
278 provider,
279 fullAPI
280 }, rest)),
281 init: initModule
282 };
283};
\No newline at end of file