mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-17 05:02:23 +08:00
Merge remote-tracking branch 'origin/master' into svelte-support
This commit is contained in:
commit
13d09e6972
@ -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]
|
||||
|
@ -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.
|
||||
|
44
addons/knobs/src/components/__tests__/RadioButtons.js
Normal file
44
addons/knobs/src/components/__tests__/RadioButtons.js
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
66
addons/knobs/src/components/types/Radio.js
Normal file
66
addons/knobs/src/components/types/Radio.js
Normal 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;
|
@ -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,
|
||||
|
@ -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 });
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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', () => {
|
||||
|
@ -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';
|
||||
|
2
app/angular/index.d.ts
vendored
2
app/angular/index.d.ts
vendored
@ -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[];
|
||||
}
|
||||
|
2
app/angular/src/client/preview/index.d.ts
vendored
2
app/angular/src/client/preview/index.d.ts
vendored
@ -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;
|
||||
|
@ -27,7 +27,7 @@ This will register all the addons and you'll be able to see the actions and note
|
||||
|
||||

|
||||
|
||||
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';
|
||||
|
@ -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
|
||||
|
@ -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: [
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -12,6 +12,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5",
|
||||
"typeRoots": [
|
||||
"../../node_modules/@types",
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
|
@ -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' };
|
||||
|
@ -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');
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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' };
|
||||
|
@ -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',
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
@ -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);
|
||||
|
21
lib/core/src/server/config/interpret-files.js
Normal file
21
lib/core/src/server/config/interpret-files.js
Normal 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));
|
||||
}
|
37
lib/core/src/server/config/interpret-files.test.js
Normal file
37
lib/core/src/server/config/interpret-files.test.js
Normal 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);
|
||||
});
|
@ -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);
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ export default configDir => {
|
||||
}
|
||||
return a.length - b.length;
|
||||
});
|
||||
|
||||
const customConfigCandidates = ['webpack.config', 'webpackfile']
|
||||
.map(filename =>
|
||||
extensions.map(ext => ({
|
||||
|
@ -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
9
scripts/jest-ts-babel.js
Normal 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);
|
||||
};
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user