storybook/app/vue/src/client/preview/client_api.js

120 lines
3.1 KiB
JavaScript

/* eslint no-underscore-dangle: 0 */
export default class ClientApi {
constructor({ channel, storyStore }) {
// channel can be null when running in node
// always check whether channel is available
this._channel = channel;
this._storyStore = storyStore;
this._addons = {};
this._globalDecorators = [];
}
setAddon(addon) {
this._addons = {
...this._addons,
...addon,
};
}
addDecorator(decorator) {
this._globalDecorators.push(decorator);
}
clearDecorators() {
this._globalDecorators = [];
}
storiesOf(kind, m) {
if (!kind && typeof kind !== 'string') {
throw new Error('Invalid or missing kind provided for stories, should be a string');
}
if (!m) {
// eslint-disable-next-line no-console
console.warn(
`Missing 'module' parameter for story with a kind of '${kind}'. It will break your HMR`
);
}
if (m && m.hot) {
m.hot.dispose(() => {
this._storyStore.removeStoryKind(kind);
});
}
const localDecorators = [];
const api = {
kind,
};
// apply addons
Object.keys(this._addons).forEach(name => {
const addon = this._addons[name];
api[name] = (...args) => {
addon.apply(api, args);
return api;
};
});
const createWrapperComponent = Target => ({
functional: true,
render(h, c) {
return h(Target, c.data, c.children);
},
});
api.add = (storyName, getStory) => {
if (typeof storyName !== 'string') {
throw new Error(`Invalid or missing storyName provided for a "${kind}" story.`);
}
if (this._storyStore.hasStory(kind, storyName)) {
throw new Error(`Story of "${kind}" named "${storyName}" already exists`);
}
// Wrap the getStory function with each decorator. The first
// decorator will wrap the story function. The second will
// wrap the first decorator and so on.
const decorators = [...localDecorators, ...this._globalDecorators];
const getDecoratedStory = decorators.reduce(
(decorated, decorator) => context => {
const story = () => decorated(context);
const decoratedStory = decorator(story, context);
decoratedStory.components = decoratedStory.components || {};
decoratedStory.components.story = createWrapperComponent(story());
return decoratedStory;
},
getStory
);
const fileName = m ? m.filename : null;
// Add the fully decorated getStory function.
this._storyStore.addStory(kind, storyName, getDecoratedStory, fileName);
return api;
};
api.addDecorator = decorator => {
localDecorators.push(decorator);
return api;
};
return api;
}
getStorybook() {
return this._storyStore.getStoryKinds().map(kind => {
const fileName = this._storyStore.getStoryFileName(kind);
const stories = this._storyStore.getStories(kind).map(name => {
const render = this._storyStore.getStory(kind, name);
return { name, render };
});
return { kind, fileName, stories };
});
}
}