Merge branch 'master' into update-dev-deps

This commit is contained in:
Filipp Riabchun 2018-03-28 04:44:34 +03:00 committed by GitHub
commit 5058250b4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 2446 additions and 23 deletions

View File

@ -35,6 +35,7 @@ jobs:
- node_modules
- examples/angular-cli/node_modules
- examples/cra-kitchen-sink/node_modules
- examples/mithril-kitchen-sink/node_modules
- examples/official-storybook/node_modules
- examples/polymer-cli/node_modules
- examples/vue-kitchen-sink/node_modules
@ -100,6 +101,11 @@ jobs:
command: |
cd examples/official-storybook
yarn build-storybook
- run:
name: "Build mithril kitchen-sink"
command: |
cd examples/mithril-kitchen-sink
yarn build-storybook
- run:
name: "Visually test storybook"
command: |
@ -148,6 +154,11 @@ jobs:
command: |
cd examples/official-storybook
yarn storybook --smoke-test
- run:
name: "Run mithril kitchen-sink (smoke test)"
command: |
cd examples/mithril-kitchen-sink
yarn storybook --smoke-test
react-native:
<<: *defaults
steps:

View File

@ -17,6 +17,7 @@
'app: react-native': ["app/react-native/**"]
'app: react': ["app/react/**"]
'app: vue': ["app/vue/**"]
'app: mithril': ["app/mithril/**"]
'babel / webpack': ["webpack", "babel"]
'cli': ["lib/cli/**"]
'compatibility with other tools': []

View File

@ -1,19 +1,19 @@
## Addon / Framework Support Table
| |[React](app/react)|[React Native](app/react-native)|[Vue](app/vue)|[Angular](app/angular)| [Polymer](app/polymer)|
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|[a11y](addons/a11y) |+| | | | |
|[actions](addons/actions) |+|+|+|+|+|
|[background](addons/background) |+| | | | |
|[centered](addons/centered) |+| |+| | |
|[events](addons/events) |+| | | | |
|[graphql](addons/graphql) |+| | | | |
|[info](addons/info) |+| | | | |
|[jest](addons/jest) |+| | | | |
|[knobs](addons/knobs) |+|+|+|+|+|
|[links](addons/links) |+|+|+|+|+|
|[notes](addons/notes) |+| |+|+|+|
|[options](addons/options) |+|+|+|+|+|
|[storyshots](addons/storyshots) |+|+|+|+| |
|[storysource](addons/storysource)|+| |+|+|+|
|[viewport](addons/viewport) |+| |+|+|+|
| |[React](app/react)|[React Native](app/react-native)|[Vue](app/vue)|[Angular](app/angular)| [Polymer](app/polymer)| [Mithril](app/mithril)|
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|
|[a11y](addons/a11y) |+| | | | | |
|[actions](addons/actions) |+|+|+|+|+|+|
|[background](addons/background) |+| | | | |+|
|[centered](addons/centered) |+| |+| | |+|
|[events](addons/events) |+| | | | | |
|[graphql](addons/graphql) |+| | | | | |
|[info](addons/info) |+| | | | | |
|[jest](addons/jest) |+| | | | | |
|[knobs](addons/knobs) |+|+|+|+|+|+|
|[links](addons/links) |+|+|+|+|+|+|
|[notes](addons/notes) |+| |+|+|+|+|
|[options](addons/options) |+|+|+|+|+|+|
|[storyshots](addons/storyshots) |+|+|+|+| | |
|[storysource](addons/storysource)|+| |+|+|+|+|
|[viewport](addons/viewport) |+| |+|+|+|+|

View File

@ -72,6 +72,7 @@ For additional help, join us [in our Slack](https://now-examples-slackin-rrirkqo
- [Vue](app/vue)
- [Angular](app/angular)
- [Polymer](app/polymer) <sup>alpha</sup>
- [Mithril](app/mithril) <sup>alpha</sup>
### Sub Projects
@ -107,6 +108,7 @@ See [Addon / Framework Support Table](ADDONS_SUPPORT.md)
- [Vue](https://storybooks-vue.netlify.com/)
- [Angular](https://storybooks-angular.netlify.com/)
- [Polymer](https://storybooks-polymer.netlify.com/)
- [Mithril](https://storybooks-mithril.netlify.com/)
### 3.4
- [React Official](https://release-3-4--storybooks-official.netlify.com)

View File

@ -69,3 +69,10 @@ storiesOf("Button", module)
.addDecorator(backgrounds)
.add("with text", () => <button>Click me</button>);
```
> In the case of Mithril, use these imports:
>
> ```js
> import { storiesOf } from '@storybook/mithril';
> import backgrounds from "@storybook/addon-backgrounds/mithril";
> ```

View File

@ -0,0 +1 @@
module.exports = require('./dist/mithril');

View File

@ -0,0 +1,39 @@
/** @jsx m */
// eslint-disable-next-line import/no-extraneous-dependencies
import m from 'mithril';
import addons from '@storybook/addons';
export class BackgroundDecorator {
constructor(vnode) {
this.props = vnode.attrs;
const { channel, story } = vnode.attrs;
// A channel is explicitly passed in for testing
if (channel) {
this.channel = channel;
} else {
this.channel = addons.getChannel();
}
this.story = story();
}
oncreate() {
this.channel.emit('background-set', this.props.backgrounds);
}
onremove() {
this.channel.emit('background-unset');
}
view() {
return m(this.story);
}
}
export default backgrounds => story => ({
view: () => <BackgroundDecorator story={story} backgrounds={backgrounds} />,
});

View File

@ -49,11 +49,29 @@ storiesOf('MyComponent', module)
.add('without props', () => ({
components: { MyComponent },
template: '<my-component />'
})
}))
.add('with some props', () => ({
components: { MyComponent },
template: '<my-component text="The Comp"/>'
});
}));
```
example for Mithril:
```js
import { storiesOf } from '@storybook/mithril';
import centered from '@storybook/addon-centered/mithril';
import MyComponent from '../Component';
storiesOf('MyComponent', module)
.addDecorator(centered)
.add('without props', () => ({
view: () => <MyComponent />
}))
.add('with some props', () => ({
view: () => <MyComponent text="The Comp"/>
}));
```
Also, you can also add this decorator globally
@ -84,6 +102,19 @@ configure(function () {
}, module);
```
example for Mithril:
```js
import { configure, addDecorator } from '@storybook/mithril';
import centered from '@storybook/addon-centered/mithril';
addDecorator(centered);
configure(function () {
//...
}, module);
```
#### As an extension
##### 1 - Configure the extension

View File

@ -0,0 +1 @@
module.exports = require('./dist/mithril');

View File

@ -0,0 +1,30 @@
/** @jsx m */
// eslint-disable-next-line import/no-extraneous-dependencies
import m from 'mithril';
const style = {
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'auto',
};
const innerStyle = {
margin: 'auto',
};
export default function(storyFn) {
return {
view: () => (
<div style={style}>
<div style={innerStyle}>{m(storyFn())}</div>
</div>
),
};
}

View File

@ -118,6 +118,13 @@ stories.add('as dynamic variables', () => {
> import { storiesOf } from '@storybook/angular';
> import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/angular';
> ```
>
> In the case of Mithril, use these imports:
>
> ```js
> import { storiesOf } from '@storybook/mithril';
> import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/mithril';
> ```
You can see your Knobs in a Storybook panel as shown below.

1
addons/knobs/mithril.js Normal file
View File

@ -0,0 +1 @@
module.exports = require('./dist/mithril');

View File

@ -0,0 +1,68 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import m from 'mithril';
export default class WrapStory {
constructor(vnode) {
this.knobChanged = this.knobChanged.bind(this);
this.knobClicked = this.knobClicked.bind(this);
this.resetKnobs = this.resetKnobs.bind(this);
this.setPaneKnobs = this.setPaneKnobs.bind(this);
this.props = vnode.attrs;
this.storyContent = vnode.attrs.initialContent;
}
oncreate() {
// Watch for changes in knob editor.
this.props.channel.on('addon:knobs:knobChange', this.knobChanged);
// Watch for clicks in knob editor.
this.props.channel.on('addon:knobs:knobClick', this.knobClicked);
// Watch for the reset event and reset knobs.
this.props.channel.on('addon:knobs:reset', this.resetKnobs);
// Watch for any change in the knobStore and set the panel again for those
// changes.
this.props.knobStore.subscribe(this.setPaneKnobs);
// Set knobs in the panel for the first time.
this.setPaneKnobs();
}
onremove() {
this.props.channel.removeListener('addon:knobs:knobChange', this.knobChanged);
this.props.channel.removeListener('addon:knobs:knobClick', this.knobClicked);
this.props.channel.removeListener('addon:knobs:reset', this.resetKnobs);
this.props.knobStore.unsubscribe(this.setPaneKnobs);
}
setPaneKnobs(timestamp = +new Date()) {
const { channel, knobStore } = this.props;
channel.emit('addon:knobs:setKnobs', { knobs: knobStore.getAll(), timestamp });
}
knobChanged(change) {
const { name, value } = change;
const { knobStore, storyFn, context } = this.props;
// Update the related knob and it's value.
const knobOptions = knobStore.get(name);
knobOptions.value = value;
knobStore.markAllUnused();
this.storyContent = storyFn(context);
m.redraw();
}
knobClicked(clicked) {
const knobOptions = this.props.knobStore.get(clicked.name);
knobOptions.callback();
}
resetKnobs() {
const { knobStore, storyFn, context } = this.props;
knobStore.reset();
this.storyContent = storyFn(context);
m.redraw();
this.setPaneKnobs(false);
}
view() {
return m(this.storyContent);
}
}

View File

@ -0,0 +1,49 @@
/** @jsx m */
// eslint-disable-next-line import/no-extraneous-dependencies
import m from 'mithril';
import addons from '@storybook/addons';
import WrapStory from './WrapStory';
import {
knob,
text,
boolean,
number,
color,
object,
array,
date,
select,
selectV2,
button,
manager,
} from '../base';
export { knob, text, boolean, number, color, object, array, date, select, selectV2, button };
export const mithrilHandler = (channel, knobStore) => getStory => context => {
const initialContent = getStory(context);
const props = { context, storyFn: getStory, channel, knobStore, initialContent };
return {
view: () => <WrapStory {...props} />,
};
};
function wrapperKnobs(options) {
const channel = addons.getChannel();
manager.setChannel(channel);
if (options) channel.emit('addon:knobs:setOptions', options);
return mithrilHandler(channel, manager.knobStore);
}
export function withKnobs(storyFn, context) {
return wrapperKnobs()(storyFn)(context);
}
export function withKnobsOptions(options = {}) {
return (storyFn, context) => wrapperKnobs(options)(storyFn)(context);
}

2
app/mithril/.npmignore Normal file
View File

@ -0,0 +1,2 @@
docs
.babelrc

33
app/mithril/README.md Normal file
View File

@ -0,0 +1,33 @@
# Storybook for Mithril
[![Build Status on CircleCI](https://circleci.com/gh/storybooks/storybook.svg?style=shield)](https://circleci.com/gh/storybooks/storybook)
[![CodeFactor](https://www.codefactor.io/repository/github/storybooks/storybook/badge)](https://www.codefactor.io/repository/github/storybooks/storybook)
[![Known Vulnerabilities](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847/badge.svg)](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847)
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)](https://now-examples-slackin-rrirkqohko.now.sh/)
[![Backers on Open Collective](https://opencollective.com/storybook/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/storybook/sponsors/badge.svg)](#sponsors)
* * *
Storybook for Mithril is a UI development environment for your Mithril components.
With it, you can visualize different states of your UI components and develop them interactively.
![Storybook Screenshot](docs/demo.gif)
Storybook runs outside of your app.
So you can develop UI components in isolation without worrying about app specific dependencies and requirements.
## Getting Started
```sh
npm i -g @storybook/cli
cd my-mithril-app
getstorybook
```
For more information visit: [storybook.js.org](https://storybook.js.org)
* * *
Storybook also comes with a lot of [addons](https://storybook.js.org/addons/introduction) and a great API to customize as you wish.
You can also build a [static version](https://storybook.js.org/basics/exporting-storybook) of your storybook and deploy it anywhere you want.

3
app/mithril/bin/build.js Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../dist/server/build');

3
app/mithril/bin/index.js Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../dist/server');

BIN
app/mithril/docs/demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

67
app/mithril/package.json Normal file
View File

@ -0,0 +1,67 @@
{
"name": "@storybook/mithril",
"version": "3.4.0-rc.3",
"description": "Storybook for Mithril: Develop Mithril Component in isolation.",
"homepage": "https://github.com/storybooks/storybook/tree/master/app/mithril",
"bugs": {
"url": "https://github.com/storybooks/storybook/issues"
},
"license": "MIT",
"main": "dist/client/index.js",
"jsnext:main": "src/client/index.js",
"bin": {
"build-storybook": "./bin/build.js",
"start-storybook": "./bin/index.js",
"storybook-server": "./bin/index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/storybooks/storybook.git"
},
"scripts": {
"dev": "cross-env DEV_BUILD=1 nodemon --watch ./src --exec \"yarn prepare\"",
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "3.4.0-rc.3",
"@storybook/channel-postmessage": "3.4.0-rc.3",
"@storybook/client-logger": "3.4.0-rc.3",
"@storybook/core": "3.4.0-rc.3",
"@storybook/node-logger": "3.4.0-rc.3",
"@storybook/ui": "3.4.0-rc.3",
"airbnb-js-shims": "^1.4.1",
"babel-loader": "^7.1.4",
"babel-plugin-macros": "^2.2.0",
"babel-plugin-transform-regenerator": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"babel-preset-minify": "^0.3.0",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"case-sensitive-paths-webpack-plugin": "^2.1.2",
"common-tags": "^1.7.2",
"core-js": "^2.5.4",
"dotenv-webpack": "^1.5.5",
"find-cache-dir": "^1.0.0",
"global": "^4.3.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.1.0",
"json5": "^0.5.1",
"markdown-loader": "^2.0.2",
"react": "^16.2.0",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.2.0",
"util-deprecate": "^1.0.2",
"webpack": "^4.3.0",
"webpack-hot-middleware": "^2.21.2"
},
"devDependencies": {
"mithril": "^1.1.6",
"nodemon": "^1.17.2"
},
"peerDependencies": {
"babel-core": "^6.26.0 || ^7.0.0-0",
"babel-runtime": ">=6.0.0",
"mithril": "^1.1.6"
}
}

View File

@ -0,0 +1,8 @@
export {
storiesOf,
setAddon,
addDecorator,
configure,
getStorybook,
forceReRender,
} from './preview';

View File

@ -0,0 +1,45 @@
/** @jsx m */
import m from 'mithril';
const mainStyle = {
position: 'fixed',
top: 0,
bottom: 0,
left: 0,
right: 0,
padding: 20,
backgroundColor: 'rgb(187, 49, 49)',
color: '#FFF',
WebkitFontSmoothing: 'antialiased',
};
const headingStyle = {
fontSize: 20,
fontWeight: 600,
letterSpacing: 0.2,
margin: '10px 0',
fontFamily: `
-apple-system, ".SFNSText-Regular", "San Francisco", Roboto, "Segoe UI",
"Helvetica Neue", "Lucida Grande", sans-serif
`,
};
const codeStyle = {
fontSize: 14,
width: '100vw',
overflow: 'auto',
};
const ErrorDisplay = {
view: vnode => (
<div style={mainStyle}>
<div style={headingStyle}>{vnode.attrs.error.message}</div>
<pre style={codeStyle}>
<code>{vnode.attrs.error.stack}</code>
</pre>
</div>
),
};
export default ErrorDisplay;

View File

@ -0,0 +1,17 @@
import { start } from '@storybook/core/client';
import render from './render';
const { clientApi, configApi, forceReRender } = start(render);
export const {
storiesOf,
setAddon,
addDecorator,
addParameters,
clearDecorators,
getStorybook,
} = clientApi;
export const { configure } = configApi;
export { forceReRender };

View File

@ -0,0 +1,93 @@
/* global document */
/** @jsx m */
import m from 'mithril';
import { stripIndents } from 'common-tags';
import { logger } from '@storybook/client-logger';
import ErrorDisplay from './error_display';
// check whether we're running on node/browser
const isBrowser = typeof window !== 'undefined';
let rootEl = null;
let previousKind = '';
let previousStory = '';
if (isBrowser) {
rootEl = document.getElementById('root');
}
export function renderError(error) {
const properError = new Error(error.title);
properError.stack = error.description;
const redBox = <ErrorDisplay error={properError} />;
m.mount(rootEl, { view: () => redBox });
}
export function renderException(error) {
// We always need to render redbox in the mainPage if we get an error.
// Since this is an error, this affects to the main page as well.
const realError = new Error(error.message);
realError.stack = error.stack;
const redBox = <ErrorDisplay error={realError} />;
m.mount(rootEl, { view: () => redBox });
// Log the stack to the console. So, user could check the source code.
logger.error(error.stack);
}
export function renderMain(data, storyStore, forceRender) {
if (storyStore.size() === 0) return;
const NoPreview = { view: () => <p>No Preview Available!</p> };
const noPreview = <NoPreview />;
const { selectedKind, selectedStory } = data;
const story = storyStore.getStory(selectedKind, selectedStory);
if (!story) {
m.mount(rootEl, { view: () => noPreview });
return;
}
if (!forceRender && selectedKind === previousKind && previousStory === selectedStory) {
return;
}
previousKind = selectedKind;
previousStory = selectedStory;
const context = {
kind: selectedKind,
story: selectedStory,
};
const element = story(context);
if (!element) {
const error = {
title: `Expecting a Mithril element from the story: "${selectedStory}" of "${selectedKind}".`,
description: stripIndents`
Did you forget to return the Mithril element from the story?
Use "() => MyComp" or "() => { return MyComp; }" when defining the story.
`,
};
renderError(error);
return;
}
m.mount(rootEl, { view: () => m(element) });
}
export default function renderPreview({ reduxStore, storyStore }, forceRender = false) {
const state = reduxStore.getState();
if (state.error) {
return renderException(state.error);
}
try {
return renderMain(state, storyStore, forceRender);
} catch (ex) {
return renderException(ex);
}
}

View File

@ -0,0 +1,68 @@
import fs from 'fs';
import path from 'path';
import JSON5 from 'json5';
import { logger } from '@storybook/node-logger';
import defaultConfig from './config/babel';
function removeReactHmre(presets) {
const index = presets.indexOf('react-hmre');
if (index > -1) {
presets.splice(index, 1);
}
}
// Tries to load a .babelrc and returns the parsed object if successful
function loadFromPath(babelConfigPath) {
let config;
if (fs.existsSync(babelConfigPath)) {
const content = fs.readFileSync(babelConfigPath, 'utf-8');
try {
config = JSON5.parse(content);
config.babelrc = false;
logger.info('=> Loading custom .babelrc');
} catch (e) {
logger.error(`=> Error parsing .babelrc file: ${e.message}`);
throw e;
}
}
if (!config) return null;
// Remove react-hmre preset.
// It causes issues with react-storybook.
// We don't really need it.
// Earlier, we fix this by running storybook in the production mode.
// But, that hide some useful debug messages.
if (config.presets) {
removeReactHmre(config.presets);
}
if (config.env && config.env.development && config.env.development.presets) {
removeReactHmre(config.env.development.presets);
}
return config;
}
export default function(configDir) {
let babelConfig = loadFromPath(path.resolve(configDir, '.babelrc'));
let inConfigDir = true;
if (!babelConfig) {
babelConfig = loadFromPath('.babelrc');
inConfigDir = false;
}
if (babelConfig) {
// If the custom config uses babel's `extends` clause, then replace it with
// an absolute path. `extends` will not work unless we do this.
if (babelConfig.extends) {
babelConfig.extends = inConfigDir
? path.resolve(configDir, babelConfig.extends)
: path.resolve(babelConfig.extends);
}
}
const finalConfig = babelConfig || defaultConfig;
return finalConfig;
}

View File

@ -0,0 +1,84 @@
import loadBabelConfig from './babel_config';
// eslint-disable-next-line global-require
jest.mock('fs', () => require('../../../../__mocks__/fs'));
jest.mock('path', () => ({
resolve: () => '.babelrc',
}));
const setup = ({ files }) => {
// eslint-disable-next-line no-underscore-dangle, global-require
require('fs').__setMockFiles(files);
};
describe('babel_config', () => {
// As the 'fs' is going to be mocked, let's call require.resolve
// so the require.cache has the correct route to the file.
// In fact let's use it in the tests :)
it('should return the config with the extra plugins when `plugins` is an array.', () => {
setup({
files: {
'.babelrc': `{
"presets": [
"env",
"foo-preset"
],
"plugins": [
"foo-plugin"
]
}`,
},
});
const config = loadBabelConfig('.foo');
expect(config).toEqual({
babelrc: false,
plugins: ['foo-plugin'],
presets: ['env', 'foo-preset'],
});
});
it('should return the config with the extra plugins when `plugins` is not an array.', () => {
setup({
files: {
'.babelrc': `{
"presets": [
"env",
"foo-preset"
],
"plugins": "bar-plugin"
}`,
},
});
const config = loadBabelConfig('.bar');
expect(config).toEqual({
babelrc: false,
plugins: 'bar-plugin',
presets: ['env', 'foo-preset'],
});
});
it('should return the config only with the extra plugins when `plugins` is not present.', () => {
// Mock a `.babelrc` config file with no plugins key.
setup({
files: {
'.babelrc': `{
"presets": [
"env",
"foo-preset"
]
}`,
},
});
const config = loadBabelConfig('.biz');
expect(config).toEqual({
babelrc: false,
presets: ['env', 'foo-preset'],
});
});
});

12
app/mithril/src/server/build.js Executable file
View File

@ -0,0 +1,12 @@
import { buildStatic } from '@storybook/core/server';
import path from 'path';
import packageJson from '../../package.json';
import getBaseConfig from './config/webpack.config.prod';
import loadConfig from './config';
buildStatic({
packageJson,
getBaseConfig,
loadConfig,
defaultFavIcon: path.resolve(__dirname, 'public/favicon.ico'),
});

View File

@ -0,0 +1,86 @@
/* eslint-disable global-require, import/no-dynamic-require */
import fs from 'fs';
import path from 'path';
import findCacheDir from 'find-cache-dir';
import { logger } from '@storybook/node-logger';
import { createDefaultWebpackConfig } from '@storybook/core/server';
import loadBabelConfig from './babel_config';
// `baseConfig` is a webpack configuration bundled with storybook.
// Storybook will look in the `configDir` directory
// (inside working directory) if a config path is not provided.
export default function(configType, baseConfig, configDir) {
const config = baseConfig;
const babelConfig = loadBabelConfig(configDir);
config.module.rules[0].query = {
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables a cache directory for faster-rebuilds
// `find-cache-dir` will create the cache directory under the node_modules directory.
cacheDirectory: findCacheDir({ name: 'react-storybook' }),
...babelConfig,
};
// Check whether a config.js file exists inside the storybook
// config directory and throw an error if it's not.
const storybookConfigPath = path.resolve(configDir, 'config.js');
if (!fs.existsSync(storybookConfigPath)) {
const err = new Error(`=> Create a storybook config file in "${configDir}/config.js".`);
throw err;
}
config.entry.preview.push(require.resolve(storybookConfigPath));
// Check whether addons.js file exists inside the storybook.
// Load the default addons.js file if it's missing.
// Insert it after polyfills.js, but before client/manager.
const storybookCustomAddonsPath = path.resolve(configDir, 'addons.js');
if (fs.existsSync(storybookCustomAddonsPath)) {
logger.info('=> Loading custom addons config.');
config.entry.manager.splice(1, 0, storybookCustomAddonsPath);
}
const defaultConfig = createDefaultWebpackConfig(config);
// Check whether user has a custom webpack config file and
// return the (extended) base configuration if it's not available.
const customConfigPath = path.resolve(configDir, 'webpack.config.js');
if (!fs.existsSync(customConfigPath)) {
logger.info('=> Using default webpack setup.');
return defaultConfig;
}
const customConfig = require(customConfigPath);
if (typeof customConfig === 'function') {
logger.info('=> Loading custom webpack config (full-control mode).');
return customConfig(config, configType, defaultConfig);
}
logger.info('=> Loading custom webpack config (extending mode).');
return {
...customConfig,
// We'll always load our configurations after the custom config.
// So, we'll always load the stuff we need.
...config,
// Override with custom devtool if provided
devtool: customConfig.devtool || config.devtool,
// We need to use our and custom plugins.
plugins: [...config.plugins, ...(customConfig.plugins || [])],
module: {
...config.module,
// We need to use our and custom rules.
...customConfig.module,
rules: [
...config.module.rules,
...((customConfig.module && customConfig.module.rules) || []),
],
},
resolve: {
...config.resolve,
...customConfig.resolve,
alias: {
...config.alias,
...(customConfig.resolve && customConfig.resolve.alias),
},
},
};
}

View File

@ -0,0 +1,28 @@
module.exports = {
// Don't try to find .babelrc because we want to force this configuration.
babelrc: false,
presets: [
[
require.resolve('babel-preset-env'),
{
targets: {
browsers: ['last 2 versions', 'safari >= 7'],
},
modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false,
},
],
require.resolve('babel-preset-stage-0'),
],
plugins: [
require.resolve('babel-plugin-macros'),
require.resolve('babel-plugin-transform-regenerator'),
[
require.resolve('babel-plugin-transform-runtime'),
{
helpers: true,
polyfill: true,
regenerator: true,
},
],
],
};

View File

@ -0,0 +1,28 @@
module.exports = {
// Don't try to find .babelrc because we want to force this configuration.
babelrc: false,
presets: [
[
require.resolve('babel-preset-env'),
{
targets: {
browsers: ['last 2 versions', 'safari >= 7'],
},
modules: false,
},
],
require.resolve('babel-preset-stage-0'),
require.resolve('babel-preset-minify'),
],
plugins: [
require.resolve('babel-plugin-transform-regenerator'),
[
require.resolve('babel-plugin-transform-runtime'),
{
helpers: true,
polyfill: true,
regenerator: true,
},
],
],
};

View File

@ -0,0 +1,4 @@
/* globals window */
window.STORYBOOK_REACT_CLASSES = {};
window.STORYBOOK_ENV = 'mithril';

View File

@ -0,0 +1,3 @@
import 'core-js/es6/symbol';
import 'core-js/fn/array/iterator';
import 'airbnb-js-shims';

View File

@ -0,0 +1,35 @@
import path from 'path';
export const includePaths = [path.resolve('./')];
export const excludePaths = [path.resolve('node_modules')];
export const nodeModulesPaths = path.resolve('./node_modules');
export const nodePaths = (process.env.NODE_PATH || '')
.split(process.platform === 'win32' ? ';' : ':')
.filter(Boolean)
.map(p => path.resolve('./', p));
// Load environment variables starts with STORYBOOK_ to the client side.
export function loadEnv(options = {}) {
const defaultNodeEnv = options.production ? 'production' : 'development';
const env = {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || defaultNodeEnv),
// This is to support CRA's public folder feature.
// In production we set this to dot(.) to allow the browser to access these assests
// even when deployed inside a subpath. (like in GitHub pages)
// In development this is just empty as we always serves from the root.
PUBLIC_URL: JSON.stringify(options.production ? '.' : ''),
};
Object.keys(process.env)
.filter(name => /^STORYBOOK_/.test(name))
.forEach(name => {
env[name] = JSON.stringify(process.env[name]);
});
return {
'process.env': env,
};
}

View File

@ -0,0 +1,94 @@
import path from 'path';
import webpack from 'webpack';
import Dotenv from 'dotenv-webpack';
import InterpolateHtmlPlugin from 'react-dev-utils/InterpolateHtmlPlugin';
import WatchMissingNodeModulesPlugin from 'react-dev-utils/WatchMissingNodeModulesPlugin';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { managerPath } from '@storybook/core/server';
import { includePaths, excludePaths, nodeModulesPaths, loadEnv, nodePaths } from './utils';
import babelLoaderConfig from './babel';
import { getPreviewHeadHtml, getManagerHeadHtml } from '../utils';
import { version } from '../../../package.json';
export default function(configDir) {
const config = {
mode: 'development',
devtool: 'cheap-module-source-map',
entry: {
manager: [require.resolve('./polyfills'), managerPath],
preview: [
require.resolve('./polyfills'),
require.resolve('./globals'),
`${require.resolve('webpack-hot-middleware/client')}?reload=true`,
],
},
output: {
path: path.join(__dirname, 'dist'),
filename: 'static/[name].bundle.js',
publicPath: '/',
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
chunks: ['manager'],
data: {
managerHead: getManagerHeadHtml(configDir),
version,
},
template: require.resolve('../index.html.ejs'),
}),
new HtmlWebpackPlugin({
filename: 'iframe.html',
excludeChunks: ['manager'],
data: {
previewHead: getPreviewHeadHtml(configDir),
},
template: require.resolve('../iframe.html.ejs'),
}),
new InterpolateHtmlPlugin(process.env),
new webpack.DefinePlugin(loadEnv()),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new WatchMissingNodeModulesPlugin(nodeModulesPaths),
new webpack.ProgressPlugin(),
new Dotenv({ silent: true }),
],
module: {
rules: [
{
test: /\.jsx?$/,
loader: require.resolve('babel-loader'),
query: babelLoaderConfig,
include: includePaths,
exclude: excludePaths,
},
{
test: /\.md$/,
use: [
{
loader: require.resolve('html-loader'),
},
{
loader: require.resolve('markdown-loader'),
},
],
},
],
},
resolve: {
// Since we ship with json-loader always, it's better to move extensions to here
// from the default config.
extensions: ['.js', '.json', '.jsx'],
// Add support to NODE_PATH. With this we could avoid relative path imports.
// Based on this CRA feature: https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules'].concat(nodePaths),
},
performance: {
hints: false,
},
};
return config;
}

View File

@ -0,0 +1,86 @@
import webpack from 'webpack';
import Dotenv from 'dotenv-webpack';
import InterpolateHtmlPlugin from 'react-dev-utils/InterpolateHtmlPlugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { managerPath } from '@storybook/core/server';
import babelLoaderConfig from './babel.prod';
import { includePaths, excludePaths, loadEnv, nodePaths } from './utils';
import { getPreviewHeadHtml, getManagerHeadHtml } from '../utils';
import { version } from '../../../package.json';
export default function(configDir) {
const entries = {
preview: [require.resolve('./polyfills'), require.resolve('./globals')],
manager: [require.resolve('./polyfills'), managerPath],
};
const config = {
mode: 'production',
bail: true,
devtool: '#cheap-module-source-map',
entry: entries,
output: {
filename: 'static/[name].[chunkhash].bundle.js',
// Here we set the publicPath to ''.
// This allows us to deploy storybook into subpaths like GitHub pages.
// This works with css and image loaders too.
// This is working for storybook since, we don't use pushState urls and
// relative URLs works always.
publicPath: '',
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
chunks: ['manager'],
data: {
managerHead: getManagerHeadHtml(configDir),
version,
},
template: require.resolve('../index.html.ejs'),
}),
new HtmlWebpackPlugin({
filename: 'iframe.html',
excludeChunks: ['manager'],
data: {
previewHead: getPreviewHeadHtml(configDir),
},
template: require.resolve('../iframe.html.ejs'),
}),
new InterpolateHtmlPlugin(process.env),
new webpack.DefinePlugin(loadEnv({ production: true })),
new Dotenv({ silent: true }),
],
module: {
rules: [
{
test: /\.jsx?$/,
loader: require.resolve('babel-loader'),
query: babelLoaderConfig,
include: includePaths,
exclude: excludePaths,
},
{
test: /\.md$/,
use: [
{
loader: require.resolve('html-loader'),
},
{
loader: require.resolve('markdown-loader'),
},
],
},
],
},
resolve: {
// Since we ship with json-loader always, it's better to move extensions to here
// from the default config.
extensions: ['.js', '.json', '.jsx'],
// Add support to NODE_PATH. With this we could avoid relative path imports.
// Based on this CRA feature: https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules'].concat(nodePaths),
},
};
return config;
}

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<base target="_parent">
<title>Storybook</title>
<%= htmlWebpackPlugin.options.data.previewHead %>
</head>
<body>
<div id="root"></div>
<div id="error-display"></div>
</body>
</html>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="storybook-version" content="<%= htmlWebpackPlugin.options.data.version %>">
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<title>Storybook</title>
<style>
/*
When resizing panels, the drag event breaks if the cursor
moves over the iframe. Add the 'dragging' class to the body
at drag start and remove it when the drag ends.
*/
.dragging iframe {
pointer-events: none;
}
/* Styling the fuzzy search box placeholders */
.searchBox::-webkit-input-placeholder { /* Chrome/Opera/Safari */
color: #ddd;
font-size: 16px;
}
.searchBox::-moz-placeholder { /* Firefox 19+ */
color: #ddd;
font-size: 16px;
}
.searchBox:focus{
border-color: #EEE !important;
}
.btn:hover{
background-color: #eee
}
</style>
<%= htmlWebpackPlugin.options.data.managerHead %>
</head>
<body style="margin: 0;">
<div id="root"></div>
</body>
</html>

12
app/mithril/src/server/index.js Executable file
View File

@ -0,0 +1,12 @@
import { buildDev } from '@storybook/core/server';
import path from 'path';
import packageJson from '../../package.json';
import getBaseConfig from './config/webpack.config';
import loadConfig from './config';
buildDev({
packageJson,
getBaseConfig,
loadConfig,
defaultFavIcon: path.resolve(__dirname, 'public/favicon.ico'),
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,30 @@
import path from 'path';
import fs from 'fs';
import deprecate from 'util-deprecate';
const fallbackHeadUsage = deprecate(() => {},
'Usage of head.html has been deprecated. Please rename head.html to preview-head.html');
export function getPreviewHeadHtml(configDirPath) {
const headHtmlPath = path.resolve(configDirPath, 'preview-head.html');
const fallbackHtmlPath = path.resolve(configDirPath, 'head.html');
let headHtml = '';
if (fs.existsSync(headHtmlPath)) {
headHtml = fs.readFileSync(headHtmlPath, 'utf8');
} else if (fs.existsSync(fallbackHtmlPath)) {
headHtml = fs.readFileSync(fallbackHtmlPath, 'utf8');
fallbackHeadUsage();
}
return headHtml;
}
export function getManagerHeadHtml(configDirPath) {
const scriptPath = path.resolve(configDirPath, 'manager-head.html');
let scriptHtml = '';
if (fs.existsSync(scriptPath)) {
scriptHtml = fs.readFileSync(scriptPath, 'utf8');
}
return scriptHtml;
}

View File

@ -0,0 +1,69 @@
import { getPreviewHeadHtml, getManagerHeadHtml } from './utils';
// eslint-disable-next-line global-require
jest.mock('fs', () => require('../../../../__mocks__/fs'));
jest.mock('path', () => ({
resolve: (a, p) => p,
}));
const setup = ({ files }) => {
// eslint-disable-next-line no-underscore-dangle, global-require
require('fs').__setMockFiles(files);
};
const HEAD_HTML_CONTENTS = 'UNITTEST_HEAD_HTML_CONTENTS';
describe('getPreviewHeadHtml', () => {
it('returns an empty string without head.html present', () => {
setup({
files: {},
});
const result = getPreviewHeadHtml('first');
expect(result).toEqual('');
});
it('return contents of head.html when present', () => {
setup({
files: {
'head.html': HEAD_HTML_CONTENTS,
},
});
const result = getPreviewHeadHtml('second');
expect(result).toEqual(HEAD_HTML_CONTENTS);
});
it('returns contents of preview-head.html when present', () => {
setup({
files: {
'preview-head.html': HEAD_HTML_CONTENTS,
},
});
const result = getPreviewHeadHtml('second');
expect(result).toEqual(HEAD_HTML_CONTENTS);
});
});
describe('getManagerHeadHtml', () => {
it('returns an empty string without manager-head.html present', () => {
setup({
files: {},
});
const result = getManagerHeadHtml('first');
expect(result).toEqual('');
});
it('returns contents of manager-head.html when present', () => {
setup({
files: {
'manager-head.html': HEAD_HTML_CONTENTS,
},
});
const result = getManagerHeadHtml('second');
expect(result).toEqual(HEAD_HTML_CONTENTS);
});
});

View File

@ -12,6 +12,7 @@ module.exports = {
'/basics/guide-react/',
'/basics/guide-vue/',
'/basics/guide-angular/',
'/basics/guide-mithril/',
'/basics/writing-stories/',
'/basics/exporting-storybook/',
'/basics/faq/',

View File

@ -0,0 +1,108 @@
---
id: 'guide-mithril'
title: 'Storybook for Mithril'
---
You may have tried to use our quick start guide to setup your project for Storybook. If you want to set up Storybook manually, this is the guide for you.
> This will also help you to understand how Storybook works.
## Starter Guide Mithril
Storybook has its own Webpack setup and a dev server.
In this guide, we will set up Storybook for your Mithril project.
## Table of contents
- [Add @storybook/mithril](#add-storybookmithril)
- [Add mithril and babel-core](#add-mithril-and-babel-core)
- [Create the config file](#create-the-config-file)
- [Write your stories](#write-your-stories)
- [Run your Storybook](#run-your-storybook)
## Add @storybook/mithril
First of all, you need to add `@storybook/mithril` to your project. To do that, simply run:
```sh
npm i --save-dev @storybook/mithril
```
## Add mithril and babel-core
Make sure that you have `mithril` and `babel-core` in your dependencies as well because we list these as a peerDependency:
```sh
npm i --save mithril
npm i --save-dev babel-core
```
Then add the following NPM script to your package json in order to start the storybook later in this guide:
```json
{
"scripts": {
"storybook": "start-storybook -p 9001 -c .storybook"
}
}
```
## Create the config file
Storybook can be configured in several different ways.
Thats why we need a config directory. We've added a `-c` option to the above NPM script mentioning `.storybook` as the config directory.
For the basic Storybook configuration file, you don't need to do much, but simply tell Storybook where to find stories.
To do that, simply create a file at `.storybook/config.js` with the following content:
```js
import { configure } from '@storybook/mithril';
function loadStories() {
require('../stories/index.js');
// You can require as many stories as you need.
}
configure(loadStories, module);
```
That'll load stories in `../stories/index.js`.
## Write your stories
Now you can write some stories inside the `../stories/index.js` file, like this:
```js
/** @jsx m */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { action } from '@storybook/addon-actions';
import Button from '../components/Button';
storiesOf('Button', module)
.add('with text', () => ({
view: () => <Button onclick={action('clicked')}>Hello Button</Button>
}))
.add('with some emoji', () => ({
view: () => <Button onclick={action('clicked')}><span role="img" aria-label="so cool">😀 😎 👍 💯</span></Button>
}));
```
Story is a single state of your component. In the above case, there are two stories for the native button component:
1. with text
2. with some emoji
## Run your Storybook
Now everything is ready. Simply run your storybook with:
```sh
npm run storybook
```
Now you can change components and write stories whenever you need to.

View File

@ -10,6 +10,7 @@ title: 'Live Examples'
- [Vue](https://storybooks-vue.netlify.com/)
- [Angular](https://storybooks-angular.netlify.com/)
- [Polymer](https://storybooks-polymer.netlify.com/)
- [Mithril](https://storybooks-mithril.netlify.com/)
### 3.4
- [React Official](https://release-3-4--storybooks-official.netlify.com)

View File

@ -3,7 +3,7 @@ id: 'quick-start-guide'
title: 'Quick Start Guide'
---
Storybook is very easy to use. You can use it with any kind of React or Vue or Angular project.
Storybook is very easy to use. You can use it with any kind of React or Vue or Angular or Mithril project.
Follow these steps to get started with Storybook.
```sh
@ -23,4 +23,4 @@ Then you can access your storybook from the browser.
* * *
To learn more about what `getstorybook` command does, have a look at our [Start Guide for React](/basics/guide-react/) or [Start Guide for Vue](/basics/guide-vue/) or [Start Guide for Angular](/basics/guide-angular/).
To learn more about what `getstorybook` command does, have a look at our [Start Guide for React](/basics/guide-react/) or [Start Guide for Vue](/basics/guide-vue/) or [Start Guide for Angular](/basics/guide-angular/) or [Start Guide for Mithril](/basics/guide-mithril/).

View File

@ -8,3 +8,4 @@ Storybook supports multiple UI libraries, the manual setup for each is different
- [Storybook for React](/basics/guide-react/)
- [Storybook for Vue](/basics/guide-vue/)
- [Storybook for Angular](/basics/guide-angular/)
- [Storybook for Mithril](/basics/guide-mithril/)

View File

@ -0,0 +1,5 @@
{
"plugins": [
"transform-react-jsx"
]
}

View File

@ -0,0 +1,15 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules
# testing
coverage
# production
build
# misc
.DS_Store
.env
npm-debug.log

View File

@ -0,0 +1,8 @@
import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-notes/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-viewport/register';
import '@storybook/addon-options/register';
import '@storybook/addon-backgrounds/register';

View File

@ -0,0 +1,13 @@
import { configure } from '@storybook/mithril';
import { setOptions } from '@storybook/addon-options';
setOptions({
hierarchyRootSeparator: /\|/,
});
function loadStories() {
const req = require.context('../src/stories', true, /\.stories\.js$/);
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

View File

@ -0,0 +1,12 @@
const path = require('path');
module.exports = (storybookBaseConfig, configType, defaultConfig) => {
defaultConfig.module.rules.push({
test: [/\.stories\.js$/],
loaders: [require.resolve('@storybook/addon-storysource/loader')],
include: [path.resolve(__dirname, '../src')],
enforce: 'pre',
});
return defaultConfig;
};

View File

@ -0,0 +1,3 @@
# Storybook Demo
This is a demo app to test Mithril integration with Storybook. Run `npm install` or `yarn install` to sync Storybook module with the source code and run `npm run storybook` or `yarn storybook` to start the Storybook.

View File

@ -0,0 +1,29 @@
{
"name": "mithril-example",
"version": "3.4.0-rc.3",
"private": true,
"scripts": {
"build-storybook": "build-storybook",
"storybook": "start-storybook -p 9007"
},
"dependencies": {
"mithril": "^1.1.6"
},
"devDependencies": {
"@storybook/addon-actions": "3.4.0-rc.3",
"@storybook/addon-backgrounds": "3.4.0-rc.3",
"@storybook/addon-centered": "3.4.0-rc.3",
"@storybook/addon-knobs": "3.4.0-rc.3",
"@storybook/addon-links": "3.4.0-rc.3",
"@storybook/addon-notes": "3.4.0-rc.3",
"@storybook/addon-options": "3.4.0-rc.3",
"@storybook/addon-storyshots": "3.4.0-rc.3",
"@storybook/addon-storysource": "3.4.0-rc.3",
"@storybook/addon-viewport": "3.4.0-rc.3",
"@storybook/addons": "3.4.0-rc.3",
"@storybook/mithril": "3.4.0-rc.3",
"babel-core": "^6.26.0",
"babel-plugin-transform-react-jsx": "^6.24.1",
"webpack": "^4.3.0"
}
}

View File

@ -0,0 +1,12 @@
import m from 'mithril';
const BaseButton = {
view: ({ attrs }) =>
m(
'button',
{ disabled: attrs.disabled, onclick: attrs.onclick, style: attrs.style },
attrs.label
),
};
export default BaseButton;

View File

@ -0,0 +1,23 @@
/** @jsx m */
import m from 'mithril';
const style = {
border: '1px solid #eee',
borderRadius: '3px',
backgroundColor: '#FFFFFF',
cursor: 'pointer',
fontSize: '15px',
padding: '3px 10px',
margin: '10px',
};
const Button = {
view: vnode => (
<button style={style} {...vnode.attrs}>
{vnode.children}
</button>
),
};
export default Button;

View File

@ -0,0 +1,137 @@
/** @jsx m */
import m from 'mithril';
const Main = {
view: vnode => (
<article
style={{
margin: '15px',
maxWidth: '600px',
lineHeight: 1.4,
fontFamily: '"Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif',
}}
>
{vnode.children}
</article>
),
};
const Title = {
view: vnode => <h1>{vnode.children}</h1>,
};
const Note = {
view: vnode => (
<p
style={{
opacity: 0.5,
}}
>
{vnode.children}
</p>
),
};
const InlineCode = {
view: vnode => (
<code
style={{
fontSize: '15px',
fontWeight: 600,
padding: '2px 5px',
border: '1px solid #eae9e9',
borderRadius: '4px',
backgroundColor: '#f3f2f2',
color: '#3a3a3a',
}}
>
{vnode.children}
</code>
),
};
const Link = {
view: vnode => (
<a
style={{
color: '#1474f3',
textDecoration: 'none',
borderBottom: '1px solid #1474f3',
paddingBottom: '2px',
}}
{...vnode.attrs}
>
{vnode.children}
</a>
),
};
const NavButton = {
view: vnode => (
<button
style={{
borderTop: 'none',
borderRight: 'none',
borderLeft: 'none',
backgroundColor: 'transparent',
padding: 0,
cursor: 'pointer',
font: 'inherit',
}}
{...vnode.attrs}
>
{vnode.children}
</button>
),
};
const Welcome = {
view: vnode => (
<Main>
<Title>Welcome to storybook</Title>
<p>This is a UI component dev environment for your app.</p>
<p>
We've added some basic stories inside the <InlineCode>src/stories</InlineCode> directory.
<br />
A story is a single state of one or more UI components. You can have as many stories as you
want.
<br />
(Basically a story is like a visual test case.)
</p>
<p>
See these sample <NavButton onclick={vnode.attrs.showApp}>stories</NavButton> for a
component called <InlineCode>Button</InlineCode>
.
</p>
<p>
Just like that, you can add your own components as stories.
<br />
You can also edit those components and see changes right away.
<br />
(Try editing the <InlineCode>Button</InlineCode> stories located at{' '}
<InlineCode>src/stories/index.js</InlineCode>.)
</p>
<p>
Usually we create stories with smaller UI components in the app.<br />
Have a look at the{' '}
<Link
href="https://storybook.js.org/basics/writing-stories"
target="_blank"
rel="noopener noreferrer"
>
Writing Stories
</Link>{' '}
section in our documentation.
</p>
<Note>
<b>NOTE:</b>
<br />
Have a look at the <InlineCode>.storybook/webpack.config.js</InlineCode> to add webpack
loaders and plugins you are using in this project.
</Note>
</Main>
),
};
export default Welcome;

View File

@ -0,0 +1,24 @@
/** @jsx m */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { action } from '@storybook/addon-actions';
import Button from '../Button';
storiesOf('Addons|Actions', module)
.add('Action only', () => ({
view: () => <Button onclick={action('logo1')}>Click me to log the action</Button>,
}))
.add('Action and method', () => ({
view: () => (
<Button
onclick={e => {
e.preventDefault();
action('log2')(e.target);
}}
>
Click me to log the action
</Button>
),
}));

View File

@ -0,0 +1,22 @@
/** @jsx m */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import backgrounds from '@storybook/addon-backgrounds/mithril';
import BaseButton from '../BaseButton';
storiesOf('Addons|Backgrounds', module)
.addDecorator(
backgrounds([
{ name: 'twitter', value: '#00aced' },
{ name: 'facebook', value: '#3b5998', default: true },
])
)
.add('story 1', () => ({
view: () => <BaseButton label="You should be able to switch backgrounds for this story" />,
}))
.add('story 2', () => ({
view: () => <BaseButton label="This one too!" />,
}));

View File

@ -0,0 +1,13 @@
/** @jsx m */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import Centered from '@storybook/addon-centered/mithril';
import Button from '../Button';
storiesOf('Addons|Centered', module)
.addDecorator(Centered)
.add('button', () => ({
view: () => <Button>A button</Button>,
}));

View File

@ -0,0 +1,71 @@
/** @jsx m */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { action } from '@storybook/addon-actions';
import {
withKnobs,
text,
number,
boolean,
array,
select,
color,
date,
button,
} from '@storybook/addon-knobs/mithril';
storiesOf('Addons|Knobs', module)
.addDecorator(withKnobs)
.add('Simple', () => {
const name = text('Name', 'John Doe');
const age = number('Age', 44);
const content = `I am ${name} and I'm ${age} years old.`;
return {
view: () => <div>{content}</div>,
};
})
.add('All knobs', () => {
const name = text('Name', 'Jane');
const stock = number('Stock', 20, {
range: true,
min: 0,
max: 30,
step: 5,
});
const fruits = {
apples: 'Apple',
bananas: 'Banana',
cherries: 'Cherry',
};
const fruit = select('Fruit', fruits, 'apple');
const price = number('Price', 2.25);
const colour = color('Border', 'deeppink');
const today = date('Today', new Date('Jan 20 2017 GMT+0'));
const items = array('Items', ['Laptop', 'Book', 'Whiskey']);
const nice = boolean('Nice', true);
const stockMessage = stock
? `I have a stock of ${stock} ${fruit}, costing &dollar;${price} each.`
: `I'm out of ${fruit}${nice ? ', Sorry!' : '.'}`;
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' };
button('Arbitrary action', action('You clicked it!'));
return {
view: () => (
<div style={`border:2px dotted ${colour}; padding: 8px 22px; border-radius: 8px`}>
<h1>My name is {name},</h1>
<h3>today is {new Date(today).toLocaleDateString('en-US', dateOptions)}</h3>
<p>{stockMessage}</p>
<p>Also, I have:</p>
<ul>{items.map(item => `<li key=${item}>${item}</li>`).join('')}</ul>
<p>{salutation}</p>
</div>
),
};
});

View File

@ -0,0 +1,11 @@
/** @jsx m */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { linkTo } from '@storybook/addon-links';
import Button from '../Button';
storiesOf('Addons|Links', module).add('Go to welcome', () => ({
view: () => <Button onclick={linkTo('Welcome')}>This buttons links to Welcome</Button>,
}));

View File

@ -0,0 +1,48 @@
/** @jsx m */
/* eslint-disable jsx-a11y/accessible-emoji */
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { withNotes } from '@storybook/addon-notes';
storiesOf('Addons|Notes', module)
.add(
'Simple note',
withNotes({ text: 'My notes on some bold text' })(() => ({
view: () => (
<p>
<strong>
Etiam vulputate elit eu venenatis eleifend. Duis nec lectus augue. Morbi egestas diam
sed vulputate mollis. Fusce egestas pretium vehicula. Integer sed neque diam. Donec
consectetur velit vitae enim varius, ut placerat arcu imperdiet. Praesent sed faucibus
arcu. Nullam sit amet nibh a enim eleifend rhoncus. Donec pretium elementum leo at
fermentum. Nulla sollicitudin, mauris quis semper tempus, sem metus tristique diam,
efficitur pulvinar mi urna id urna.
</strong>
</p>
),
}))
)
.add(
'Note with HTML',
withNotes({
text: `
<h2>My notes on emojies</h2>
<em>It's not all that important to be honest, but..</em>
Emojis are great, I love emojis, in fact I like using them in my Component notes too! 😇
`,
})(() => ({
view: () => (
<p>
<span>🤔😳😯😮</span>
<br />
<span>😄😩😓😱</span>
<br />
<span>🤓😑😶😊</span>
</p>
),
}))
);

View File

@ -0,0 +1,25 @@
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import Button from '../Button';
import Welcome from '../Welcome';
storiesOf('Welcome', module).add('to Storybook', () => ({
view: () => m(Welcome, { showApp: linkTo('Button') }),
}));
storiesOf('Button', module)
.add('with text', () => ({
view: () => m(Button, { onclick: action('clicked') }, 'Hello Button'),
}))
.add('with some emoji', () => ({
view: () =>
m(
Button,
{ onclick: action('clicked') },
m('span', { role: 'img', ariaLabel: 'so cool' }, '😀 😎 👍 💯')
),
}));

View File

@ -0,0 +1 @@
../../mithril-kitchen-sink/storybook-static

View File

@ -16,6 +16,14 @@ exports[`Storyshots App|acceptance cra-kitchen-sink 1`] = `
/>
`;
exports[`Storyshots App|acceptance mithril-kitchen-sink 1`] = `
<iframe
src="/mithril-kitchen-sink/index.html"
style="border:0;position:absolute;top:0;left:0;width:100vw;height:100vh"
title="mithril-kitchen-sink"
/>
`;
exports[`Storyshots App|acceptance polymer-cli 1`] = `
<iframe
src="/polymer-cli/index.html"

View File

@ -16,7 +16,13 @@ const style = {
height: '100vh',
};
['cra-kitchen-sink', 'vue-kitchen-sink', 'angular-cli', 'polymer-cli'].forEach(name => {
[
'cra-kitchen-sink',
'vue-kitchen-sink',
'angular-cli',
'polymer-cli',
'mithril-kitchen-sink',
].forEach(name => {
chapter.add(
name,
withNotes(`You must build the storybook for the ${name} example for this story to work.`)(

View File

@ -17,6 +17,7 @@ import updateOrganisationsGenerator from '../generators/UPDATE_PACKAGE_ORGANIZAT
import vueGenerator from '../generators/VUE';
import polymerGenerator from '../generators/POLYMER';
import webpackReactGenerator from '../generators/WEBPACK_REACT';
import mithrilGenerator from '../generators/MITHRIL';
const logger = console;
@ -157,6 +158,11 @@ const runGenerator = () => {
.then(commandLog('Adding storybook support to your "Polymer" app'))
.then(end);
case types.MITHRIL:
return mithrilGenerator()
.then(commandLog('Adding storybook support to your "Mithril" app'))
.then(end);
default:
paddedLog(`We couldn't detect your project type. (code: ${projectType})`);
paddedLog(

View File

@ -0,0 +1,66 @@
import path from 'path';
import fs from 'fs';
import JSON5 from 'json5';
import mergeDirs from 'merge-dirs';
import { getVersions, getPackageJson, writePackageJson } from '../../lib/helpers';
export default async () => {
const [
storybookVersion,
actionsVersion,
linksVersion,
addonsVersion,
babelCoreVersion,
babelPluginTransformReactJsxVersion,
] = await getVersions(
'@storybook/mithril',
'@storybook/addon-actions',
'@storybook/addon-links',
'@storybook/addons',
'babel-core',
'babel-plugin-transform-react-jsx'
);
mergeDirs(path.resolve(__dirname, 'template/'), '.', 'overwrite');
const packageJson = getPackageJson();
packageJson.dependencies = packageJson.dependencies || {};
packageJson.devDependencies = packageJson.devDependencies || {};
packageJson.devDependencies['@storybook/addon-actions'] = actionsVersion;
packageJson.devDependencies['@storybook/addon-links'] = linksVersion;
packageJson.devDependencies['@storybook/addons'] = addonsVersion;
packageJson.devDependencies['@storybook/mithril'] = storybookVersion;
// create or update .babelrc
let babelrc = null;
if (fs.existsSync('.babelrc')) {
const babelrcContent = fs.readFileSync('.babelrc', 'utf8');
babelrc = JSON5.parse(babelrcContent);
babelrc.plugins = babelrc.plugins || [];
if (babelrc.plugins.indexOf('babel-plugin-transform-react-jsx') < 0) {
babelrc.plugins.push('transform-react-jsx');
packageJson.devDependencies[
'babel-plugin-transform-react-jsx'
] = babelPluginTransformReactJsxVersion;
}
} else {
babelrc = {
plugins: ['transform-react-jsx'],
};
packageJson.devDependencies['babel-core'] = babelCoreVersion;
packageJson.devDependencies[
'babel-plugin-transform-react-jsx'
] = babelPluginTransformReactJsxVersion;
}
fs.writeFileSync('.babelrc', JSON.stringify(babelrc, null, 2), 'utf8');
packageJson.scripts = packageJson.scripts || {};
packageJson.scripts.storybook = 'start-storybook -p 6006';
packageJson.scripts['build-storybook'] = 'build-storybook';
writePackageJson(packageJson);
};

View File

@ -0,0 +1,2 @@
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';

View File

@ -0,0 +1,9 @@
import { configure } from '@storybook/mithril';
// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /.stories.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

View File

@ -0,0 +1,23 @@
/** @jsx m */
import m from 'mithril';
const style = {
border: '1px solid #eee',
borderRadius: '3px',
backgroundColor: '#FFFFFF',
cursor: 'pointer',
fontSize: '15px',
padding: '3px 10px',
margin: '10px',
};
const Button = {
view: vnode => (
<button style={style} {...vnode.attrs}>
{vnode.children}
</button>
),
};
export default Button;

View File

@ -0,0 +1,178 @@
/** @jsx m */
import m from 'mithril';
import { linkTo, hrefTo } from '@storybook/addon-links';
const Main = {
view: vnode => (
<article
style={{
margin: '15px',
maxWidth: '600px',
lineHeight: 1.4,
fontFamily: '"Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif',
}}
>
{vnode.children}
</article>
),
};
const Title = {
view: vnode => <h1>{vnode.children}</h1>,
};
const Note = {
view: vnode => (
<p
style={{
opacity: 0.5,
}}
>
{vnode.children}
</p>
),
};
const InlineCode = {
view: vnode => (
<code
style={{
fontSize: '15px',
fontWeight: 600,
padding: '2px 5px',
border: '1px solid #eae9e9',
borderRadius: '4px',
backgroundColor: '#f3f2f2',
color: '#3a3a3a',
}}
>
{vnode.children}
</code>
),
};
const Link = {
view: vnode => (
<a
style={{
color: '#1474f3',
textDecoration: 'none',
borderBottom: '1px solid #1474f3',
paddingBottom: '2px',
}}
{...vnode.attrs}
>
{vnode.children}
</a>
),
};
const NavButton = {
view: vnode => (
<button
style={{
borderTop: 'none',
borderRight: 'none',
borderLeft: 'none',
backgroundColor: 'transparent',
padding: 0,
cursor: 'pointer',
font: 'inherit',
}}
{...vnode.attrs}
>
{vnode.children}
</button>
),
};
const StoryLink = {
oninit: vnode => {
// eslint-disable-next-line no-param-reassign
vnode.state.href = '/';
// eslint-disable-next-line no-param-reassign
vnode.state.onclick = () => {
linkTo(vnode.attrs.kind, vnode.attrs.story)();
return false;
};
StoryLink.updateHref(vnode);
},
updateHref: async vnode => {
const href = await hrefTo(vnode.attrs.kind, vnode.attrs.story);
// eslint-disable-next-line no-param-reassign
vnode.state.href = href;
m.redraw();
},
view: vnode => (
<a
href={vnode.state.href}
style={{
color: '#1474f3',
textDecoration: 'none',
borderBottom: '1px solid #1474f3',
paddingBottom: '2px',
}}
onClick={vnode.state.onclick}
>
{vnode.children}
</a>
),
};
const Welcome = {
view: vnode => (
<Main>
<Title>Welcome to storybook</Title>
<p>This is a UI component dev environment for your app.</p>
<p>
We've added some basic stories inside the <InlineCode>src/stories</InlineCode> directory.
<br />
A story is a single state of one or more UI components. You can have as many stories as you
want.
<br />
(Basically a story is like a visual test case.)
</p>
<p>
See these sample{' '}
{vnode.attrs.showApp ? (
<NavButton onclick={vnode.attrs.showApp}>stories</NavButton>
) : (
<StoryLink kind={vnode.attrs.showKind} story={vnode.attrs.showStory}>
stories
</StoryLink>
)}{' '}
for a component called <InlineCode>Button</InlineCode>
.
</p>
<p>
Just like that, you can add your own components as stories.
<br />
You can also edit those components and see changes right away.
<br />
(Try editing the <InlineCode>Button</InlineCode> stories located at{' '}
<InlineCode>src/stories/index.js</InlineCode>.)
</p>
<p>
Usually we create stories with smaller UI components in the app.<br />
Have a look at the{' '}
<Link
href="https://storybook.js.org/basics/writing-stories"
target="_blank"
rel="noopener noreferrer"
>
Writing Stories
</Link>{' '}
section in our documentation.
</p>
<Note>
<b>NOTE:</b>
<br />
Have a look at the <InlineCode>.storybook/webpack.config.js</InlineCode> to add webpack
loaders and plugins you are using in this project.
</Note>
</Main>
),
};
export default Welcome;

View File

@ -0,0 +1,25 @@
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import Button from './Button';
import Welcome from './Welcome';
storiesOf('Welcome', module).add('to Storybook', () => ({
view: () => m(Welcome, { showApp: linkTo('Button') }),
}));
storiesOf('Button', module)
.add('with text', () => ({
view: () => m(Button, { onclick: action('clicked') }, 'Hello Button'),
}))
.add('with some emoji', () => ({
view: () =>
m(
Button,
{ onclick: action('clicked') },
m('span', { role: 'img', ariaLabel: 'so cool' }, '😀 😎 👍 💯')
),
}));

View File

@ -76,6 +76,14 @@ function detectFramework(dependencies) {
) {
return types.POLYMER;
}
if (
(dependencies.dependencies && dependencies.dependencies.mithril) ||
(dependencies.devDependencies && dependencies.devDependencies.mithril)
) {
return types.MITHRIL;
}
return false;
}
@ -85,7 +93,7 @@ function isStorybookInstalled(dependencies, force) {
}
if (!force && dependencies.devDependencies) {
const supportedFrameworks = ['react', 'react-native', 'vue', 'angular', 'polymer'];
const supportedFrameworks = ['react', 'react-native', 'vue', 'angular', 'polymer', 'mithril'];
if (
supportedFrameworks.reduce(
(storybookPresent, framework) =>

View File

@ -13,4 +13,5 @@ export default {
ALREADY_HAS_STORYBOOK: 'ALREADY_HAS_STORYBOOK',
UPDATE_PACKAGE_ORGANIZATIONS: 'UPDATE_PACKAGE_ORGANIZATIONS',
POLYMER: 'POLYMER',
MITHRIL: 'MITHRIL',
};

View File

@ -56,6 +56,7 @@
"@storybook/channel-postmessage": "3.4.0-rc.3",
"@storybook/channel-websocket": "3.4.0-rc.3",
"@storybook/channels": "3.4.0-rc.3",
"@storybook/mithril": "3.4.0-rc.3",
"@storybook/polymer": "3.4.0-rc.3",
"@storybook/react": "3.4.0-rc.3",
"@storybook/react-native": "3.4.0-rc.3",

View File

@ -0,0 +1,14 @@
{
"name": "mithril-fixture",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack -p dist"
},
"dependencies": {
"mithril": "^1.1.6"
},
"devDependencies": {
}
}

View File

@ -0,0 +1,5 @@
{
"plugins": [
"transform-react-jsx"
]
}

View File

@ -0,0 +1,2 @@
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';

View File

@ -0,0 +1,9 @@
import { configure } from '@storybook/mithril';
// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /.stories.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

View File

@ -0,0 +1,22 @@
{
"name": "mithril-fixture",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack -p dist",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"dependencies": {
"mithril": "^1.1.6"
},
"devDependencies": {
"@storybook/addon-actions": "^3.4.0-rc.3",
"@storybook/addon-links": "^3.4.0-rc.3",
"@storybook/addons": "^3.4.0-rc.3",
"@storybook/mithril": "3.4.0-rc.3",
"babel-core": "^6.26.0",
"babel-plugin-transform-react-jsx": "^6.24.1"
}
}

View File

@ -0,0 +1,23 @@
/** @jsx m */
import m from 'mithril';
const style = {
border: '1px solid #eee',
borderRadius: '3px',
backgroundColor: '#FFFFFF',
cursor: 'pointer',
fontSize: '15px',
padding: '3px 10px',
margin: '10px',
};
const Button = {
view: vnode => (
<button style={style} {...vnode.attrs}>
{vnode.children}
</button>
),
};
export default Button;

View File

@ -0,0 +1,178 @@
/** @jsx m */
import m from 'mithril';
import { linkTo, hrefTo } from '@storybook/addon-links';
const Main = {
view: vnode => (
<article
style={{
margin: '15px',
maxWidth: '600px',
lineHeight: 1.4,
fontFamily: '"Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif',
}}
>
{vnode.children}
</article>
),
};
const Title = {
view: vnode => <h1>{vnode.children}</h1>,
};
const Note = {
view: vnode => (
<p
style={{
opacity: 0.5,
}}
>
{vnode.children}
</p>
),
};
const InlineCode = {
view: vnode => (
<code
style={{
fontSize: '15px',
fontWeight: 600,
padding: '2px 5px',
border: '1px solid #eae9e9',
borderRadius: '4px',
backgroundColor: '#f3f2f2',
color: '#3a3a3a',
}}
>
{vnode.children}
</code>
),
};
const Link = {
view: vnode => (
<a
style={{
color: '#1474f3',
textDecoration: 'none',
borderBottom: '1px solid #1474f3',
paddingBottom: '2px',
}}
{...vnode.attrs}
>
{vnode.children}
</a>
),
};
const NavButton = {
view: vnode => (
<button
style={{
borderTop: 'none',
borderRight: 'none',
borderLeft: 'none',
backgroundColor: 'transparent',
padding: 0,
cursor: 'pointer',
font: 'inherit',
}}
{...vnode.attrs}
>
{vnode.children}
</button>
),
};
const StoryLink = {
oninit: vnode => {
// eslint-disable-next-line no-param-reassign
vnode.state.href = '/';
// eslint-disable-next-line no-param-reassign
vnode.state.onclick = () => {
linkTo(vnode.attrs.kind, vnode.attrs.story)();
return false;
};
StoryLink.updateHref(vnode);
},
updateHref: async vnode => {
const href = await hrefTo(vnode.attrs.kind, vnode.attrs.story);
// eslint-disable-next-line no-param-reassign
vnode.state.href = href;
m.redraw();
},
view: vnode => (
<a
href={vnode.state.href}
style={{
color: '#1474f3',
textDecoration: 'none',
borderBottom: '1px solid #1474f3',
paddingBottom: '2px',
}}
onClick={vnode.state.onclick}
>
{vnode.children}
</a>
),
};
const Welcome = {
view: vnode => (
<Main>
<Title>Welcome to storybook</Title>
<p>This is a UI component dev environment for your app.</p>
<p>
We've added some basic stories inside the <InlineCode>src/stories</InlineCode> directory.
<br />
A story is a single state of one or more UI components. You can have as many stories as you
want.
<br />
(Basically a story is like a visual test case.)
</p>
<p>
See these sample{' '}
{vnode.attrs.showApp ? (
<NavButton onclick={vnode.attrs.showApp}>stories</NavButton>
) : (
<StoryLink kind={vnode.attrs.showKind} story={vnode.attrs.showStory}>
stories
</StoryLink>
)}{' '}
for a component called <InlineCode>Button</InlineCode>
.
</p>
<p>
Just like that, you can add your own components as stories.
<br />
You can also edit those components and see changes right away.
<br />
(Try editing the <InlineCode>Button</InlineCode> stories located at{' '}
<InlineCode>src/stories/index.js</InlineCode>.)
</p>
<p>
Usually we create stories with smaller UI components in the app.<br />
Have a look at the{' '}
<Link
href="https://storybook.js.org/basics/writing-stories"
target="_blank"
rel="noopener noreferrer"
>
Writing Stories
</Link>{' '}
section in our documentation.
</p>
<Note>
<b>NOTE:</b>
<br />
Have a look at the <InlineCode>.storybook/webpack.config.js</InlineCode> to add webpack
loaders and plugins you are using in this project.
</Note>
</Main>
),
};
export default Welcome;

View File

@ -0,0 +1,25 @@
import m from 'mithril';
import { storiesOf } from '@storybook/mithril';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import Button from './Button';
import Welcome from './Welcome';
storiesOf('Welcome', module).add('to Storybook', () => ({
view: () => m(Welcome, { showApp: linkTo('Button') }),
}));
storiesOf('Button', module)
.add('with text', () => ({
view: () => m(Button, { onclick: action('clicked') }, 'Hello Button'),
}))
.add('with some emoji', () => ({
view: () =>
m(
Button,
{ onclick: action('clicked') },
m('span', { role: 'img', ariaLabel: 'so cool' }, '😀 😎 👍 💯')
),
}));

View File

@ -147,6 +147,7 @@
"lib/*",
"examples/angular-cli",
"examples/cra-kitchen-sink",
"examples/mithril-kitchen-sink",
"examples/polymer-cli",
"examples/vue-kitchen-sink",
"examples/official-storybook",

View File

@ -42,6 +42,13 @@ elif [ "$BUILD_CONTEXT" = "POLYMER" ]; then
yarn build-storybook
mv storybook-static ../../netlify-build
popd
elif [ "$BUILD_CONTEXT" = "MITHRIL" ]; then
echo "netlify-build Mithril examples"
pushd examples/mithril-kitchen-sink
yarn
yarn build-storybook
mv storybook-static ../../netlify-build
popd
elif [ "$BUILD_CONTEXT" = "OFFICIAL" ]; then
echo "netlify-build official examples"
pushd examples/official-storybook

View File

@ -10618,6 +10618,10 @@ mississippi@^2.0.0:
stream-each "^1.1.0"
through2 "^2.0.0"
mithril@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/mithril/-/mithril-1.1.6.tgz#bd2cc0de3d3c86076a6a7a30367a601a1bd108f3"
mixin-deep@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.0.tgz#47a8732ba97799457c8c1eca28f95132d7e8150a"