Merge remote-tracking branch 'origin/master' into svelte-support

This commit is contained in:
igor-dv 2018-07-30 23:59:46 +03:00
commit 13d09e6972
39 changed files with 400 additions and 231 deletions

View File

@ -123,7 +123,9 @@ exports[`addon Info should render <Info /> and external markdown 1`] = `
>
<div>
<h1>
function func(x) {return x + 1;}
function func(x) {
return x + 1;
}
</h1>
<h2>
[object Object]
@ -1430,7 +1432,9 @@ containing **bold**, *cursive* text, \`code\` and [a link](https://github.com)"
>
<div>
<h1>
function func(x) {return x + 1;}
function func(x) {
return x + 1;
}
</h1>
<h2>
[object Object]

View File

@ -264,6 +264,23 @@ const value = select(label, options, defaultValue, groupId);
> You can also provide options as an array like this: `['red', 'blue', 'yellow']`
### radio buttons
Allows you to get a value from a list of radio buttons from the user.
```js
import { radios } from '@storybook/addon-knobs';
const options = {
Kiwi: 'kiwi',
Guava: 'guava',
Watermelon: 'watermelon',
};
const = radios(name, options, defaultValue);
```
### files
Allows you to get a value from a file input from the user.

View File

@ -0,0 +1,44 @@
import React from 'react';
import { shallow } from 'enzyme';
import RadioType from '../types/Radio';
describe('Radio', () => {
let knob;
beforeEach(() => {
knob = {
name: 'Color',
value: '#319C16',
options: {
Green: '#319C16',
Red: '#FF2B2B',
},
};
});
describe('displays value of button input', () => {
it('correctly renders labels', () => {
const wrapper = shallow(<RadioType knob={knob} />);
const greenLabel = wrapper.find('label').first();
expect(greenLabel.text()).toEqual('Green');
});
it('sets value on the radio buttons', () => {
const wrapper = shallow(<RadioType knob={knob} />);
const greenInput = wrapper.find('input').first();
expect(greenInput.prop('value')).toEqual('#319C16');
});
it('marks the correct checkbox as checked', () => {
const wrapper = shallow(<RadioType knob={knob} />);
const greenInput = wrapper.find('input').first();
const redInput = wrapper.find('input').last();
expect(greenInput.prop('checked')).toEqual(true);
expect(redInput.prop('checked')).toEqual(false);
});
});
});

View File

@ -0,0 +1,66 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const styles = {
label: {
fontSize: 11,
padding: '5px',
},
};
class RadiosType extends Component {
renderRadioButtonList({ options }) {
if (Array.isArray(options)) {
return options.map(val => this.renderRadioButton(val, val));
}
return Object.keys(options).map(key => this.renderRadioButton(key, options[key]));
}
renderRadioButton(label, value) {
const opts = { label, value };
const { onChange } = this.props;
const { name } = this.props.knob;
const id = `${name}-${opts.value}`;
return (
<div key={id}>
<input
type="radio"
id={id}
name={name}
value={opts.value}
onChange={e => onChange(e.target.value)}
checked={value === this.props.knob.value}
/>
<label style={styles.label} htmlFor={id}>
{label}
</label>
</div>
);
}
render() {
const { knob, onChange } = this.props;
return <div>{this.renderRadioButtonList(knob, onChange)}</div>;
}
}
RadiosType.defaultProps = {
knob: {},
onChange: value => value,
};
RadiosType.propTypes = {
knob: PropTypes.shape({
name: PropTypes.string,
value: PropTypes.string,
options: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
}),
onChange: PropTypes.func,
};
RadiosType.serialize = value => value;
RadiosType.deserialize = value => value;
export default RadiosType;

View File

@ -4,6 +4,7 @@ import ColorType from './Color';
import BooleanType from './Boolean';
import ObjectType from './Object';
import SelectType from './Select';
import RadiosType from './Radio';
import ArrayType from './Array';
import DateType from './Date';
import ButtonType from './Button';
@ -16,6 +17,7 @@ export default {
boolean: BooleanType,
object: ObjectType,
select: SelectType,
radios: RadiosType,
array: ArrayType,
date: DateType,
button: ButtonType,

View File

@ -52,6 +52,10 @@ export function select(name, options, value, groupId) {
return manager.knob(name, { type: 'select', selectV2: true, options, value, groupId });
}
export function radios(name, options, value, groupId) {
return manager.knob(name, { type: 'radios', options, value, groupId });
}
export function array(name, value, separator = ',', groupId) {
return manager.knob(name, { type: 'array', value, separator, groupId });
}

View File

@ -5,7 +5,7 @@ function getConfigPathParts(configPath) {
const resolvedConfigPath = path.resolve(configPath);
if (fs.lstatSync(resolvedConfigPath).isDirectory()) {
return path.join(resolvedConfigPath, 'config.js');
return path.join(resolvedConfigPath, 'config');
}
return resolvedConfigPath;

View File

@ -360,7 +360,7 @@ describe('Viewport/Panel', () => {
});
it('passes the children', () => {
expect(select.props().children).toHaveLength(8);
expect(select.props().children).toHaveLength(15);
});
it('onChange it updates the viewport', () => {

View File

@ -40,6 +40,22 @@ export const INITIAL_VIEWPORTS = {
},
type: 'mobile',
},
iphone8p: {
name: 'iPhone 7 Plus',
styles: {
height: '960px',
width: '540px',
},
type: 'mobile',
},
iphonex: {
name: 'iPhone X',
styles: {
height: '1218px',
width: '563px',
},
type: 'mobile',
},
ipad: {
name: 'iPad',
styles: {
@ -48,6 +64,22 @@ export const INITIAL_VIEWPORTS = {
},
type: 'tablet',
},
ipad10p: {
name: 'iPad Pro 10.5-in',
styles: {
height: '1112px',
width: '834px',
},
type: 'tablet',
},
ipad12p: {
name: 'iPad Pro 12.9-in',
styles: {
height: '1366px',
width: '1024px',
},
type: 'tablet',
},
galaxys5: {
name: 'Galaxy S5',
styles: {
@ -56,6 +88,14 @@ export const INITIAL_VIEWPORTS = {
},
type: 'mobile',
},
galaxys9: {
name: 'Galaxy S9',
styles: {
height: '1480px',
width: '720px',
},
type: 'mobile',
},
nexus5x: {
name: 'Nexus 5X',
styles: {
@ -72,5 +112,21 @@ export const INITIAL_VIEWPORTS = {
},
type: 'mobile',
},
pixel: {
name: 'Pixel',
styles: {
height: '960px',
width: '540px',
},
type: 'mobile',
},
pixelxl: {
name: 'Pixel XL',
styles: {
height: '1280px',
width: '720px',
},
type: 'mobile',
},
};
export const DEFAULT_VIEWPORT = 'responsive';

View File

@ -39,6 +39,6 @@ declare module '@storybook/angular' {
export function setAddon(addon: any): void;
export function addDecorator(decorator: any): IApi;
export function addParameters(parameters: any): IApi;
export function configure(loaders: () => NodeRequire, module: NodeModule): void;
export function configure(loaders: () => void, module: NodeModule): void;
export function getStorybook(): IStoribookSection[];
}

View File

@ -1,6 +1,6 @@
export function storiesOf(kind: string, module: NodeModule): IApi;
export function setAddon(addon: any): void;
export function addDecorator(decorator: any): IApi;
export function configure(loaders: () => NodeRequire, module: NodeModule): void;
export function configure(loaders: () => void, module: NodeModule): void;
export function getStorybook(): IStoribookSection[];
export function clearDecorators(): void;

View File

@ -27,7 +27,7 @@ This will register all the addons and you'll be able to see the actions and note
![Stories without notes](../static/stories-without-notes.png)
Now when you are writing a story it like this and add some notes:
Now when you are writing a story, import it and add some notes:
```js
import { storiesOf } from '@storybook/react';

View File

@ -137,6 +137,7 @@ gumgum:
title: GumGum
description: GumGum (Computer Vision Company) Component library
demo: https://storybook.gumgum.com
source: https://github.com/gumgum/gumdrops
site: https://gumgum.com
lucid-ui:
thumbnail: lucid-ui.png

View File

@ -1,6 +1,6 @@
const path = require('path');
module.exports = baseConfig => {
module.exports = (baseConfig: any) => {
baseConfig.module.rules.push({
test: [/\.stories\.tsx?$/, /index\.ts$/],
loaders: [

View File

@ -70,6 +70,8 @@
"@types/jasminewd2": "^2.0.3",
"@types/jest": "^22.2.3",
"@types/node": "~9.6.20",
"@types/storybook__addon-options": "^3.2.2",
"@types/webpack-env": "^1.13.6",
"babel-core": "^6.26.3",
"global": "^4.3.2",
"jasmine-core": "~3.1.0",

View File

@ -8,6 +8,7 @@ import {
boolean,
array,
select,
radios,
color,
date,
button,
@ -58,6 +59,12 @@ storiesOf('Addon|Knobs', module)
Cherry: 'cherries',
};
const fruit = select('fruit', fruits, 'apples');
const otherFruits = {
Kiwi: 'kiwi',
Guava: 'guava',
Watermelon: 'watermelon',
};
const otherFruit = radios('Other Fruit', otherFruits, 'watermelon');
const price = number('price', 2.25);
const border = color('border', 'deeppink');
@ -72,6 +79,7 @@ storiesOf('Addon|Knobs', module)
name,
stock,
fruit,
otherFruit,
price,
border,
today,

View File

@ -12,6 +12,7 @@
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"../../node_modules/@types",
"node_modules/@types"
],
"lib": [

View File

@ -7,6 +7,7 @@ import {
boolean,
color,
select,
radios,
array,
date,
object,
@ -21,6 +22,13 @@ export default () => {
Cherry: 'cherry',
};
const fruit = select('Fruit', fruits, 'apple');
const otherFruits = {
Kiwi: 'kiwi',
Guava: 'guava',
Watermelon: 'watermelon',
};
const otherFruit = radios('Other Fruit', otherFruits, 'watermelon');
const dollars = number('Dollars', 12.5);
// NOTE: color picker is currently broken
@ -36,7 +44,7 @@ export default () => {
// NOTE: put this last because it currently breaks everything after it :D
const birthday = date('Birthday', new Date('Jan 20 2017'));
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`;
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}. I also enjoy ${otherFruit}.`;
const style = { backgroundColor, ...otherStyles };
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' };

View File

@ -36,6 +36,7 @@ storiesOf('Addons|Knobs', module)
Cherry: 'cherries',
};
const fruit = select('Fruit', fruits, 'apples');
const price = number('Price', 2.25);
const colour = color('Border', 'deeppink');

View File

@ -39,7 +39,7 @@ exports[`Storyshots Addons|Knobs.withKnobs tweaks static values 1`] = `
style="background-color:#dedede;border:2px dashed silver;border-radius:10px;padding:10px"
>
<p>
My name is Storyteller, I'm 70 years old, and my favorite fruit is apple.
My name is Storyteller, I'm 70 years old, and my favorite fruit is apple. I also enjoy watermelon.
</p>
<p>
My birthday is: January 20, 2017
@ -109,6 +109,9 @@ exports[`Storyshots Addons|Knobs.withKnobs tweaks static values organized in gro
<p>
Fruit: apple
</p>
<p>
Other Fruit: watermelon
</p>
<p>
Items:
</p>

View File

@ -10,6 +10,7 @@ import {
boolean,
color,
select,
radios,
array,
date,
button,
@ -44,6 +45,13 @@ storiesOf('Addons|Knobs.withKnobs', module)
Cherry: 'cherry',
};
const fruit = select('Fruit', fruits, 'apple');
const otherFruits = {
Kiwi: 'kiwi',
Guava: 'guava',
Watermelon: 'watermelon',
};
const otherFruit = radios('Other Fruit', otherFruits, 'watermelon');
const dollars = number('Dollars', 12.5, { min: 0, max: 100, step: 0.01 });
const years = number('Years in NY', 9);
@ -63,7 +71,7 @@ storiesOf('Addons|Knobs.withKnobs', module)
const defaultBirthday = new Date('Jan 20 2017 GMT+0');
const birthday = date('Birthday', defaultBirthday);
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`;
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}. I also enjoy ${otherFruit}.`;
const style = { backgroundColor, ...otherStyles };
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' };
@ -96,6 +104,12 @@ storiesOf('Addons|Knobs.withKnobs', module)
Cherry: 'cherry',
};
const otherFruits = {
Kiwi: 'kiwi',
Guava: 'guava',
Watermelon: 'watermelon',
};
// NOTE: the default value must not change - e.g., do not do date('Label', new Date()) or date('Label')
const defaultBirthday = new Date('Jan 20 2017 GMT+0');
@ -114,6 +128,7 @@ storiesOf('Addons|Knobs.withKnobs', module)
// Favorites
const nice = boolean('Nice', true, GROUP_IDS.FAVORITES);
const fruit = select('Fruit', fruits, 'apple', GROUP_IDS.FAVORITES);
const otherFruit = radios('Other Fruit', otherFruits, 'watermelon', GROUP_IDS.FAVORITES);
const items = array('Items', ['Laptop', 'Book', 'Whiskey'], ',', GROUP_IDS.FAVORITES);
// Display
@ -145,6 +160,7 @@ storiesOf('Addons|Knobs.withKnobs', module)
<h1>Favorites</h1>
<p>Catchphrase: {salutation}</p>
<p>Fruit: {fruit}</p>
<p>Other Fruit: {otherFruit}</p>
<p>Items:</p>
<ul>{items.map(item => <li key={`${item}`}>{item}</li>)}</ul>
</div>

View File

@ -114,7 +114,7 @@ exports[`Storyshots Knobs with knobs 1`] = `
allowFontScaling={true}
ellipsizeMode="tail"
>
My name is Storyteller, I'm 70 years old, and my favorite fruit is apple.
My name is Storyteller, I'm 70 years old, and my favorite fruit is apple. I also enjoy watermelon.
</Text>
<Text
accessible={true}

View File

@ -12,6 +12,8 @@ import {
object,
} from '@storybook/addon-knobs/react';
import { radios } from '@storybook/addon-knobs';
export default () => {
const name = text('Name', 'Storyteller');
const age = number('Age', 70, { range: true, min: 0, max: 90, step: 5 });
@ -21,6 +23,13 @@ export default () => {
cherry: 'Cherry',
};
const fruit = select('Fruit', fruits, 'apple');
const otherFruits = {
Kiwi: 'kiwi',
Guava: 'guava',
Watermelon: 'watermelon',
};
const otherFruit = radios('Other Fruit', otherFruits, 'watermelon');
const dollars = number('Dollars', 12.5);
// NOTE: color picker is currently broken
@ -36,7 +45,7 @@ export default () => {
// NOTE: put this last because it currently breaks everything after it :D
const birthday = date('Birthday', new Date('Jan 20 2017'));
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`;
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}. I also enjoy ${otherFruit}.`;
const style = { backgroundColor, ...otherStyles };
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' };

View File

@ -23,6 +23,7 @@ module.exports = {
],
transform: {
'^.+\\.jsx?$': 'babel-jest',
'^.+[/\\\\].storybook[/\\\\]config\\.ts$': '<rootDir>/scripts/jest-ts-babel.js',
'^.+\\.(ts|html)$': '<rootDir>/node_modules/jest-preset-angular/preprocessor.js',
'.*\\.(vue)$': '<rootDir>/node_modules/jest-vue-preprocessor',
'.*\\.(svelte)$': '<rootDir>/node_modules/svelte-jest',

View File

@ -14,7 +14,7 @@ exports[`Storyshots Components|Layout addon panel in right 1`] = `
style="flex:none;position:relative;outline:none;width:250px"
>
<div
class="css-1kbxkw2"
class="css-7r9jvr"
>
<div
class="css-9in0fq"
@ -25,20 +25,10 @@ exports[`Storyshots Components|Layout addon panel in right 1`] = `
Stories
</div>
</div>
<div
class="css-owlojx"
direction="vertical"
>
<div
class="css-19oxd4p"
direction="vertical"
/>
</div>
</div>
</div>
<span
class="Resizer vertical "
style="cursor:col-resize;height:auto;width:10px;z-index:1"
/>
<div
class="Pane vertical Pane2 "
@ -71,7 +61,6 @@ exports[`Storyshots Components|Layout addon panel in right 1`] = `
</div>
<span
class="Resizer vertical "
style="cursor:col-resize;height:100%;width:10px;z-index:1"
/>
<div
class="Pane vertical Pane2 "
@ -80,15 +69,6 @@ exports[`Storyshots Components|Layout addon panel in right 1`] = `
<div
class="css-1929wko"
>
<div
class="css-nq4yle"
direction="vertical"
>
<div
class="css-19oxd4p"
direction="vertical"
/>
</div>
<div
style="position:absolute;height:100%;width:100%;color:white;background:#fc4a1a"
>
@ -119,7 +99,7 @@ exports[`Storyshots Components|Layout default 1`] = `
style="flex:none;position:relative;outline:none;width:250px"
>
<div
class="css-1kbxkw2"
class="css-7r9jvr"
>
<div
class="css-9in0fq"
@ -130,20 +110,10 @@ exports[`Storyshots Components|Layout default 1`] = `
Stories
</div>
</div>
<div
class="css-owlojx"
direction="vertical"
>
<div
class="css-19oxd4p"
direction="vertical"
/>
</div>
</div>
</div>
<span
class="Resizer vertical "
style="cursor:col-resize;height:auto;width:10px;z-index:1"
/>
<div
class="Pane vertical Pane2 "
@ -176,7 +146,6 @@ exports[`Storyshots Components|Layout default 1`] = `
</div>
<span
class="Resizer horizontal "
style="cursor:row-resize;height:10px;width:100%;z-index:1"
/>
<div
class="Pane horizontal Pane2 "
@ -185,15 +154,6 @@ exports[`Storyshots Components|Layout default 1`] = `
<div
class="css-hys0kv"
>
<div
class="css-1fpokv0"
direction="horizontal"
>
<div
class="css-1nvj8jd"
direction="horizontal"
/>
</div>
<div
style="position:absolute;height:100%;width:100%;color:white;background:#fc4a1a"
>
@ -224,7 +184,7 @@ exports[`Storyshots Components|Layout full screen 1`] = `
style="flex:none;position:relative;outline:none;width:1px"
>
<div
class="css-1aqo4nl"
class="css-1yb218t"
>
<div
class="css-9in0fq"
@ -235,20 +195,10 @@ exports[`Storyshots Components|Layout full screen 1`] = `
Stories
</div>
</div>
<div
class="css-owlojx"
direction="vertical"
>
<div
class="css-19oxd4p"
direction="vertical"
/>
</div>
</div>
</div>
<span
class="Resizer vertical disabled"
style="height:auto;width:10px;z-index:1"
/>
<div
class="Pane vertical Pane2 "
@ -281,7 +231,6 @@ exports[`Storyshots Components|Layout full screen 1`] = `
</div>
<span
class="Resizer horizontal disabled"
style="height:10px;width:100%;z-index:1"
/>
<div
class="Pane horizontal Pane2 "
@ -290,15 +239,6 @@ exports[`Storyshots Components|Layout full screen 1`] = `
<div
class="css-1yfjvq7"
>
<div
class="css-1fpokv0"
direction="horizontal"
>
<div
class="css-1nvj8jd"
direction="horizontal"
/>
</div>
<div
style="position:absolute;height:100%;width:100%;color:white;background:#fc4a1a"
>
@ -407,7 +347,7 @@ exports[`Storyshots Components|Layout no addon panel 1`] = `
style="flex:none;position:relative;outline:none;width:250px"
>
<div
class="css-1kbxkw2"
class="css-7r9jvr"
>
<div
class="css-9in0fq"
@ -418,20 +358,10 @@ exports[`Storyshots Components|Layout no addon panel 1`] = `
Stories
</div>
</div>
<div
class="css-owlojx"
direction="vertical"
>
<div
class="css-19oxd4p"
direction="vertical"
/>
</div>
</div>
</div>
<span
class="Resizer vertical "
style="cursor:col-resize;height:auto;width:10px;z-index:1"
/>
<div
class="Pane vertical Pane2 "
@ -464,7 +394,6 @@ exports[`Storyshots Components|Layout no addon panel 1`] = `
</div>
<span
class="Resizer horizontal disabled"
style="height:10px;width:100%;z-index:1"
/>
<div
class="Pane horizontal Pane2 "
@ -473,15 +402,6 @@ exports[`Storyshots Components|Layout no addon panel 1`] = `
<div
class="css-1yfjvq7"
>
<div
class="css-1fpokv0"
direction="horizontal"
>
<div
class="css-1nvj8jd"
direction="horizontal"
/>
</div>
<div
style="position:absolute;height:100%;width:100%;color:white;background:#fc4a1a"
>
@ -512,7 +432,7 @@ exports[`Storyshots Components|Layout no stories panel 1`] = `
style="flex:none;position:relative;outline:none;width:1px"
>
<div
class="css-1aqo4nl"
class="css-1yb218t"
>
<div
class="css-9in0fq"
@ -523,20 +443,10 @@ exports[`Storyshots Components|Layout no stories panel 1`] = `
Stories
</div>
</div>
<div
class="css-owlojx"
direction="vertical"
>
<div
class="css-19oxd4p"
direction="vertical"
/>
</div>
</div>
</div>
<span
class="Resizer vertical disabled"
style="height:auto;width:10px;z-index:1"
/>
<div
class="Pane vertical Pane2 "
@ -569,7 +479,6 @@ exports[`Storyshots Components|Layout no stories panel 1`] = `
</div>
<span
class="Resizer horizontal "
style="cursor:row-resize;height:10px;width:100%;z-index:1"
/>
<div
class="Pane horizontal Pane2 "
@ -578,15 +487,6 @@ exports[`Storyshots Components|Layout no stories panel 1`] = `
<div
class="css-hys0kv"
>
<div
class="css-1fpokv0"
direction="horizontal"
>
<div
class="css-1nvj8jd"
direction="horizontal"
/>
</div>
<div
style="position:absolute;height:100%;width:100%;color:white;background:#fc4a1a"
>

View File

@ -2,15 +2,64 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { localStorage, window } from 'global';
import { injectGlobal } from 'emotion';
import styled from 'react-emotion';
import throttle from 'lodash.throttle';
import SplitPane from 'react-split-pane';
import USplit from './usplit';
import Dimensions from './dimensions';
// eslint-disable-next-line no-unused-expressions
injectGlobal`
.Resizer {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
}
.Resizer.horizontal {
height: 10px;
margin: -5px 0;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
cursor: row-resize;
width: 100%;
}
.Resizer.horizontal::after {
content: '';
display: block;
height: 2px;
width: 20px;
border-top: 1px solid rgba(0,0,0, 0.2);
border-bottom: 1px solid rgba(0,0,0, 0.2);
}
.Resizer.vertical {
width: 10px;
margin: 0 -5px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
cursor: col-resize;
}
.Resizer.vertical::after {
content: '';
display: block;
width: 2px;
height: 20px;
border-left: 1px solid rgba(0,0,0, 0.2);
border-right: 1px solid rgba(0,0,0, 0.2);
}
.Resizer.disabled {
visibility: hidden;
}
`;
const StoriesPanelWrapper = styled('div')(({ showStoriesPanel, storiesPanelOnTop }) => ({
boxSizing: 'border-box',
width: '100%',
height: '100%',
display: showStoriesPanel ? 'flex' : 'none',
@ -42,22 +91,6 @@ const AddonPanelWrapper = styled('div')(
})
);
const resizerCursor = isVert => (isVert ? 'col-resize' : 'row-resize');
const storiesResizerStyle = (showStoriesPanel, storiesPanelOnTop) => ({
cursor: showStoriesPanel ? resizerCursor(!storiesPanelOnTop) : undefined,
height: storiesPanelOnTop ? 10 : 'auto',
width: storiesPanelOnTop ? '100%' : 10,
zIndex: 1,
});
const addonResizerStyle = (showAddonPanel, addonPanelInRight) => ({
cursor: showAddonPanel ? resizerCursor(addonPanelInRight) : undefined,
height: addonPanelInRight ? '100%' : 10,
width: addonPanelInRight ? 10 : '100%',
zIndex: 1,
});
const ContentPanel = styled('div')(
({ addonPanelInRight, storiesPanelOnTop, theme: { layoutMargin } }) => ({
position: 'absolute',
@ -239,7 +272,6 @@ class Layout extends Component {
maxSize={-400}
size={showStoriesPanel ? storiesPanelDefaultSize : 1}
defaultSize={storiesPanelDefaultSize}
resizerStyle={storiesResizerStyle(showStoriesPanel, storiesPanelOnTop)}
onDragStarted={this.onDragStart}
onDragFinished={this.onDragEnd}
onChange={size => this.onResize('storiesPanel', storiesPanelOnTop ? 'top' : 'left', size)}
@ -248,7 +280,6 @@ class Layout extends Component {
<StoriesPanelInner>
<StoriesPanel />
</StoriesPanelInner>
<USplit shift={5} split={storiesSplit} />
</StoriesPanelWrapper>
<SplitPane
split={addonSplit}
@ -258,7 +289,6 @@ class Layout extends Component {
maxSize={-200}
size={showAddonPanel ? addonPanelDefaultSize : 1}
defaultSize={addonPanelDefaultSize}
resizerStyle={addonResizerStyle(showAddonPanel, addonPanelInRight)}
onDragStarted={this.onDragStart}
onDragFinished={this.onDragEnd}
onChange={size => this.onResize('addonPanel', addonPanelInRight ? 'right' : 'down', size)}
@ -281,7 +311,6 @@ class Layout extends Component {
</PreviewWrapper>
</ContentPanel>
<AddonPanelWrapper {...{ showAddonPanel, addonPanelInRight }}>
<USplit shift={-5} split={addonSplit} />
<AddonPanel />
</AddonPanelWrapper>
</SplitPane>

View File

@ -1,79 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
const gripSize = 1;
const splitSize = 10;
const Wrapper = styled('div')(({ shift, direction }) => {
switch (direction) {
case 'horizontal': {
return {
height: splitSize,
marginTop: shift - splitSize / 2,
marginBottom: -shift - splitSize / 2,
position: 'relative',
};
}
case 'vertical':
default: {
return {
width: splitSize,
marginLeft: shift - splitSize / 2,
marginRight: -shift - splitSize / 2,
position: 'relative',
};
}
}
});
const Inner = styled('div')(({ direction, theme }) => {
switch (direction) {
case 'horizontal': {
return {
height: gripSize,
width: 20,
top: splitSize / 2 - (gripSize + 2) / 2,
left: '50%',
position: 'absolute',
borderTop: theme.mainBorder,
borderBottom: theme.mainBorder,
borderTopWidth: 1,
borderBottomWidth: 1,
};
}
case 'vertical':
default: {
return {
width: gripSize,
height: 20,
left: splitSize / 2 - (gripSize + 2) / 2,
top: '50%',
position: 'absolute',
borderLeft: theme.mainBorder,
borderRight: theme.mainBorder,
borderLeftWidth: 1,
borderRightWidth: 1,
};
}
}
});
const USplit = ({ shift, split }) => (
<Wrapper direction={split} shift={shift}>
<Inner direction={split} />
</Wrapper>
);
USplit.propTypes = {
shift: PropTypes.number,
split: PropTypes.oneOf(['vertical', 'horizontal']),
};
USplit.defaultProps = {
shift: 0,
split: 'vertical',
};
export default USplit;

View File

@ -2,8 +2,8 @@ import findCacheDir from 'find-cache-dir';
import { logger } from '@storybook/node-logger';
import { createDefaultWebpackConfig } from './config/defaults/webpack.config';
import devBabelConfig from './config/babel';
import loadBabelConfig from './babel_config';
import loadCustomConfig from './loadCustomWebpackConfig';
import loadCustomBabelConfig from './loadCustomBabelConfig';
import loadCustomWebpackConfig from './loadCustomWebpackConfig';
const noopWrapper = config => config;
@ -14,7 +14,7 @@ function getBabelConfig({
wrapBabelConfig = noopWrapper,
}) {
const defaultConfig = wrapDefaultBabelConfig(defaultBabelConfig);
return wrapBabelConfig(loadBabelConfig(configDir, defaultConfig));
return wrapBabelConfig(loadCustomBabelConfig(configDir, defaultConfig));
}
function mergeConfigs(config, customConfig) {
@ -84,7 +84,7 @@ export default options => {
// Check whether user has a custom webpack config file and
// return the (extended) base configuration if it's not available.
const customConfig = loadCustomConfig(configDir);
const customConfig = loadCustomWebpackConfig(configDir);
if (customConfig === null) {
informAboutCustomConfig(defaultConfigName);

View File

@ -0,0 +1,21 @@
import fs from 'fs';
import { extensions } from 'interpret';
const boost = new Set(['.js', '.jsx', '.ts', '.tsx']);
function sortExtensions() {
return [
...Array.from(boost),
...Object.keys(extensions)
.filter(ext => !boost.has(ext))
.sort((a, b) => a.length - b.length),
];
}
const possibleExtensions = sortExtensions();
export function getInterpretedFile(pathToFile) {
return possibleExtensions
.map(ext => `${pathToFile}${ext}`)
.find(candidate => fs.existsSync(candidate));
}

View File

@ -0,0 +1,37 @@
import mock from 'mock-fs';
import { getInterpretedFile } from './interpret-files';
describe('interpret-files', () => {
it('will interpret file as file.ts when it exists in fs', () => {
mock({
'path/to/file.ts': 'ts file contents',
});
const file = getInterpretedFile('path/to/file');
expect(file).toEqual('path/to/file.ts');
});
it('will interpret file as file.js when both are in fs', () => {
mock({
'path/to/file.js': 'js file contents',
'path/to/file.ts': 'ts file contents',
});
const file = getInterpretedFile('path/to/file');
expect(file).toEqual('path/to/file.js');
});
it('will return undefined if there is no candidate of a file in fs', () => {
mock({
'path/to/file.js': 'js file contents',
});
const file = getInterpretedFile('path/to/file2');
expect(file).toBeUndefined();
});
afterEach(mock.restore);
});

View File

@ -1,7 +1,6 @@
import fs from 'fs';
import path from 'path';
import { logger } from '@storybook/node-logger';
import { getInterpretedFile } from './interpret-files';
export const includePaths = [path.resolve('./')];
@ -41,18 +40,18 @@ export function getEntries(configDir) {
const preview = [require.resolve('./polyfills'), require.resolve('./globals')];
const manager = [require.resolve('./polyfills'), require.resolve('../../client/manager')];
// Check whether a config.js file exists inside the storybook
// Check whether a config.{ext} 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)) {
throw new Error(`=> Create a storybook config file in "${configDir}/config.js".`);
const storybookConfigPath = getInterpretedFile(path.resolve(configDir, 'config'));
if (!storybookConfigPath) {
throw new Error(`=> Create a storybook config file in "${configDir}/config.{ext}".`);
}
preview.push(require.resolve(storybookConfigPath));
// Check whether addons.js file exists inside the storybook.
const storybookCustomAddonsPath = path.resolve(configDir, 'addons.js');
if (fs.existsSync(storybookCustomAddonsPath)) {
// Check whether addons.{ext} file exists inside the storybook.
const storybookCustomAddonsPath = getInterpretedFile(path.resolve(configDir, 'addons'));
if (storybookCustomAddonsPath) {
logger.info('=> Loading custom addons config.');
manager.unshift(storybookCustomAddonsPath);
}

View File

@ -58,6 +58,7 @@ export default configDir => {
}
return a.length - b.length;
});
const customConfigCandidates = ['webpack.config', 'webpackfile']
.map(filename =>
extensions.map(ext => ({

View File

@ -50,6 +50,7 @@
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-jest": "^22.4.4",
"babel-plugin-emotion": "^9.1.2",
"babel-plugin-macros": "^2.2.2",
"babel-plugin-require-context-hook": "^1.0.0",

9
scripts/jest-ts-babel.js Normal file
View File

@ -0,0 +1,9 @@
const tsTransformer = require('jest-preset-angular/preprocessor');
const babelTransformer = require('babel-jest');
module.exports.process = function transform(src, path, config, transformOptions) {
const tsResult = tsTransformer.process(src, path, config, transformOptions);
const jsPath = path.replace('.ts', '.js');
return babelTransformer.process(tsResult, jsPath, config, transformOptions);
};

View File

@ -476,6 +476,14 @@
version "2.53.43"
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz#2de3d718819bc20165754c4a59afb7e9833f6707"
"@types/storybook__addon-options@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@types/storybook__addon-options/-/storybook__addon-options-3.2.2.tgz#f42f81414fa9692cf20d947e9b49c60c4bdfbc4d"
"@types/webpack-env@^1.13.6":
version "1.13.6"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976"
"@vue/component-compiler-utils@^2.0.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.1.0.tgz#8331eadc8acdbc35aace5a61d2153e6f6434bfe2"