/
url.ts
87 lines (75 loc) · 2.55 KB
/
url.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { history, document } from 'global';
import qs from 'qs';
import deprecate from 'util-deprecate';
import { StoreSelectionSpecifier, StoreSelection } from '@storybook/client-api';
import { StoryId, ViewMode } from '@storybook/addons';
export function pathToId(path: string) {
const match = (path || '').match(/^\/story\/(.+)/);
if (!match) {
throw new Error(`Invalid path '${path}', must start with '/story/'`);
}
return match[1];
}
// todo add proper types
export const setPath = (selection?: StoreSelection) => {
if (!selection) {
return;
}
const { storyId, viewMode }: { storyId: StoryId; viewMode: ViewMode } = selection;
const { search, hash } = document.location;
const { path, selectedKind, selectedStory, ...rest } = qs.parse(search, {
ignoreQueryPrefix: true,
});
const newPath = `${document.location.pathname}?${qs.stringify({
...rest,
id: storyId,
viewMode,
})}${hash || ''}`;
history.replaceState({}, '', newPath);
};
export const parseQueryParameters = (search: string) => {
const { id } = qs.parse(search, { ignoreQueryPrefix: true });
return id;
};
type ValueOf<T> = T[keyof T];
const isObject = (val: Record<string, any>) =>
val != null && typeof val === 'object' && Array.isArray(val) === false;
const getFirstString = (v: ValueOf<qs.ParsedQs>): string | void => {
if (typeof v === 'string') {
return v;
}
if (Array.isArray(v)) {
return getFirstString(v[0]);
}
if (isObject(v)) {
// @ts-ignore
return getFirstString(Object.values(v));
}
return undefined;
};
const deprecatedLegacyQuery = deprecate(
() => 0,
`URL formats with \`selectedKind\` and \`selectedName\` query parameters are deprecated.
Use \`id=$storyId\` instead.
See https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#new-url-structure`
);
export const getSelectionSpecifierFromPath: () => StoreSelectionSpecifier = () => {
const query = qs.parse(document.location.search, { ignoreQueryPrefix: true });
let viewMode = getFirstString(query.viewMode) as ViewMode;
if (typeof viewMode !== 'string' || !viewMode.match(/docs|story/)) {
viewMode = 'story';
}
const path = getFirstString(query.path);
const storyId = path ? pathToId(path) : getFirstString(query.id);
if (storyId) {
return { storySpecifier: storyId, viewMode };
}
// Legacy URL format
const kind = getFirstString(query.selectedKind);
const name = getFirstString(query.selectedStory);
if (kind && name) {
deprecatedLegacyQuery();
return { storySpecifier: { kind, name }, viewMode };
}
return null;
};