Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Fix legacy story URLs #9545

Merged
merged 1 commit into from Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 12 additions & 4 deletions lib/client-api/src/story_store.ts
Expand Up @@ -280,6 +280,18 @@ export default class StoryStore extends EventEmitter {
}
}, 0);

// Unlike a bunch of deprecated APIs below, these lookup functions
// use the `_data` member, which is the new data structure. They should
// be the preferred way of looking up stories in the future.

getStoriesForKind(kind: string) {
return this.raw().filter(story => story.kind === kind);
}

getRawStory(kind: string, name: string) {
return this.getStoriesForKind(kind).find(s => s.name === name);
}

// OLD apis
getRevision() {
return this._revision;
Expand Down Expand Up @@ -339,10 +351,6 @@ export default class StoryStore extends EventEmitter {
.map(info => info.name);
}

getStoriesForKind(kind: string) {
return this.raw().filter(story => story.kind === kind);
}

getStoryFileName(kind: string) {
const key = toKey(kind);
const storiesKind = this._legacydata[key as string];
Expand Down
2 changes: 1 addition & 1 deletion lib/core/src/client/preview/start.js
Expand Up @@ -311,7 +311,7 @@ export default function start(render, { decorateStory } = {}) {
}

storyStore.on(Events.STORY_INIT, () => {
const { storyId, viewMode } = initializePath();
const { storyId, viewMode } = initializePath(storyStore);
storyStore.setSelection({ storyId, viewMode });
});

Expand Down
23 changes: 19 additions & 4 deletions lib/core/src/client/preview/url.js
Expand Up @@ -22,19 +22,34 @@ export const setPath = ({ storyId, viewMode }) => {
history.replaceState({}, '', newPath);
};

export const getIdFromLegacyQuery = ({ path, selectedKind, selectedStory }) =>
(path && pathToId(path)) || (selectedKind && selectedStory && toId(selectedKind, selectedStory));
export const getIdFromLegacyQuery = ({ path, selectedKind, selectedStory }, storyStore) => {
if (path) {
return pathToId(path);
}
if (selectedKind && selectedStory) {
// Look up the story ID inside the story store, since as of 5.3, the
// Story ID is not necessarily a direct function of its kind/name.
const story = storyStore.getRawStory(selectedKind, selectedStory);
if (story) {
return story.id;
}
// this will preserve existing behavior of showing a "not found" screen,
// but the inputs will be preserved in the query param to help debugging
return toId(selectedKind, selectedStory);
}
return undefined;
};

export const parseQueryParameters = search => {
const { id } = qs.parse(search, { ignoreQueryPrefix: true });
return id;
};

export const initializePath = () => {
export const initializePath = storyStore => {
const query = qs.parse(document.location.search, { ignoreQueryPrefix: true });
let { id: storyId, viewMode } = query; // eslint-disable-line prefer-const
if (!storyId) {
storyId = getIdFromLegacyQuery(query);
storyId = getIdFromLegacyQuery(query, storyStore);
if (storyId) {
setPath({ storyId, viewMode });
}
Expand Down
23 changes: 17 additions & 6 deletions lib/core/src/client/preview/url.test.js
Expand Up @@ -42,16 +42,26 @@ describe('url', () => {
});

describe('getIdFromLegacyQuery', () => {
const store = { getRawStory: () => null };
it('should parse story paths', () => {
expect(getIdFromLegacyQuery({ path: '/story/story--id' })).toBe('story--id');
expect(getIdFromLegacyQuery({ path: '/story/story--id' }, store)).toBe('story--id');
});
it('should parse legacy queries', () => {
it('should use legacy parameters to look up custom story ids', () => {
const customStore = {
getRawStory: () => ({ id: 'custom--id' }),
};
expect(
getIdFromLegacyQuery({ path: null, selectedKind: 'kind', selectedStory: 'story' })
getIdFromLegacyQuery({ selectedKind: 'kind', selectedStory: 'story' }, customStore)
).toBe('custom--id');
});
it('should use fall-back behavior for legacy queries for unknown stories', () => {
expect(
getIdFromLegacyQuery({ path: null, selectedKind: 'kind', selectedStory: 'story' }, store)
).toBe('kind--story');
});

it('should not parse non-queries', () => {
expect(getIdFromLegacyQuery({})).toBeUndefined();
expect(getIdFromLegacyQuery({}, store)).toBeUndefined();
});
});

Expand All @@ -65,14 +75,15 @@ describe('url', () => {
});

describe('initializePath', () => {
const store = { getRawStory: () => null };
it('should handle id queries', () => {
document.location.search = '?id=story--id';
expect(initializePath()).toEqual({ storyId: 'story--id' });
expect(initializePath(store)).toEqual({ storyId: 'story--id' });
expect(history.replaceState).not.toHaveBeenCalled();
});
it('should redirect legacy queries', () => {
document.location.search = '?selectedKind=kind&selectedStory=story';
expect(initializePath()).toEqual({ storyId: 'kind--story' });
expect(initializePath(store)).toEqual({ storyId: 'kind--story' });
expect(history.replaceState).toHaveBeenCalledWith({}, '', 'pathname?id=kind--story');
});
});
Expand Down