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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Fix default sorting of docs-only stories #9504

Merged
merged 3 commits into from Jan 17, 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
5 changes: 5 additions & 0 deletions examples/official-storybook/intro.stories.mdx
@@ -0,0 +1,5 @@
<Meta title="About/Intro" />

# Official-storybook

Welcome to `official-storybook`, a collection of test cases and demos for `@storybook/react` and all its addons.
1 change: 1 addition & 0 deletions examples/official-storybook/main.js
@@ -1,5 +1,6 @@
module.exports = {
stories: [
// FIXME: Breaks e2e tests './intro.stories.mdx',
'../../lib/ui/src/**/*.stories.(js|tsx|mdx)',
'../../lib/components/src/**/*.stories.(js|tsx|mdx)',
'./stories/**/*.stories.(js|tsx|mdx)',
Expand Down
10 changes: 6 additions & 4 deletions lib/client-api/src/client_api.test.ts
Expand Up @@ -514,21 +514,23 @@ describe('preview.client_api', () => {
clientApi: { storiesOf, getStorybook },
channel,
} = getContext(undefined);
const module0 = new MockModule();
const module1 = new MockModule();
const module2 = new MockModule();
channel.emit = jest.fn();

expect(getStorybook()).toEqual([]);

storiesOf('kind0', module0).add('story0-docs-only', jest.fn(), { docsOnly: true });
storiesOf('kind1', module1).add('story1', jest.fn());
storiesOf('kind2', module2).add('story2', jest.fn());

// storyStore debounces so we need to wait for the next tick
await new Promise(r => setTimeout(r, 0));

let [event, storiesHash] = channel.emit.mock.calls[0];
let [event, args] = channel.emit.mock.calls[0];
expect(event).toEqual(Events.SET_STORIES);
expect(Object.values(storiesHash.stories).map(v => v.kind)).toEqual(['kind1', 'kind2']);
expect(Object.values(args.stories).map(v => v.kind)).toEqual(['kind0', 'kind1', 'kind2']);
expect(getStorybook().map(story => story.kind)).toEqual(['kind1', 'kind2']);

channel.emit.mockClear();
Expand All @@ -540,10 +542,10 @@ describe('preview.client_api', () => {

await new Promise(r => setTimeout(r, 0));
// eslint-disable-next-line prefer-destructuring
[event, storiesHash] = channel.emit.mock.calls[0];
[event, args] = channel.emit.mock.calls[0];

expect(event).toEqual(Events.SET_STORIES);
expect(Object.values(storiesHash.stories).map(v => v.kind)).toEqual(['kind1', 'kind2']);
expect(Object.values(args.stories).map(v => v.kind)).toEqual(['kind0', 'kind1', 'kind2']);
expect(getStorybook().map(story => story.kind)).toEqual(['kind1', 'kind2']);
});
});
Expand Down
28 changes: 17 additions & 11 deletions lib/client-api/src/story_store.ts
Expand Up @@ -55,6 +55,8 @@ interface StoryOptions {
includeDocsOnly?: boolean;
}

type KindOrder = Record<string, number>;

const isStoryDocsOnly = (parameters?: Parameters) => {
return parameters && parameters.docsOnly;
};
Expand All @@ -81,6 +83,8 @@ export default class StoryStore extends EventEmitter {

_selection: Selection;

_kindOrder: KindOrder;

constructor(params: { channel: Channel }) {
super();

Expand All @@ -90,6 +94,7 @@ export default class StoryStore extends EventEmitter {
this._selection = {} as any;
this._channel = params.channel;
this._error = undefined;
this._kindOrder = {};
}

setChannel = (channel: Channel) => {
Expand Down Expand Up @@ -133,17 +138,11 @@ export default class StoryStore extends EventEmitter {
stable.inplace(stories, sortFn);
} else {
// NOTE: when kinds are HMR'ed they get temporarily removed from the `_data` array
// and thus lose order. However in `_legacydata` they just get zeroed out, meaning
// that the order is preserved. Here we can use this to preserve the load
// order if there is no sort function, although it's a hack.
const kindOrder = Object.values(this._legacydata).reduce(
(acc: Record<string, number>, { kind }: any, idx: number) => {
acc[kind] = idx;
return acc;
},
{}
// and thus lose order. However `_kindOrder` preservers the original load order
stable.inplace(
stories,
(s1, s2) => this._kindOrder[s1[1].kind] - this._kindOrder[s2[1].kind]
);
stable.inplace(stories, (s1, s2) => kindOrder[s1[1].kind] - kindOrder[s2[1].kind]);
}
}
// removes function values from all stories so they are safe to transport over the channel
Expand Down Expand Up @@ -252,11 +251,18 @@ export default class StoryStore extends EventEmitter {
parameters,
};

// LEGACY DATA
// Don't store docs-only stories in legacy data because
// existing clients (at the time?!), e.g. storyshots/chromatic
// are not necessarily equipped to process them
if (!isStoryDocsOnly(parameters)) {
this.addLegacyStory({ kind, name, storyFn, parameters });
}

// Store 1-based order of kind loading to preserve sorting on HMR
if (!this._kindOrder[kind]) {
this._kindOrder[kind] = 1 + Object.keys(this._kindOrder).length;
}

// LET'S SEND IT TO THE MANAGER
this.pushToManager();
}
Expand Down