mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-07 07:21:17 +08:00
Merge branch 'master' into a11y#3641
This commit is contained in:
commit
5d0ed30854
47
CHANGELOG.md
47
CHANGELOG.md
@ -1,3 +1,50 @@
|
|||||||
|
# 4.0.0-alpha.14
|
||||||
|
|
||||||
|
2018-July-11
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
- Upgrade universal-dotenv to fix core-js dependency [#3874](https://github.com/storybooks/storybook/pull/3874)
|
||||||
|
|
||||||
|
# 4.0.0-alpha.13
|
||||||
|
|
||||||
|
2018-July-09
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
- Refactor addon-jest to use a parameter-based pattern [#3678](https://github.com/storybooks/storybook/pull/3678)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
- Upgrade universal-dotenv to fix babel-runtime [#3863](https://github.com/storybooks/storybook/pull/3863)
|
||||||
|
|
||||||
|
#### Maintenance
|
||||||
|
|
||||||
|
- Added a test for parameter combination [#3844](https://github.com/storybooks/storybook/pull/3844)
|
||||||
|
|
||||||
|
# 4.0.0-alpha.12
|
||||||
|
|
||||||
|
2018-July-03
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
- Fix non-polyfilled themed UI components [#3829](https://github.com/storybooks/storybook/pull/3829)
|
||||||
|
|
||||||
|
# 4.0.0-alpha.11
|
||||||
|
|
||||||
|
2018-July-02
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
- Storybook UI theming [#3628](https://github.com/storybooks/storybook/pull/3628)
|
||||||
|
- Replaced 'dotenv-webpack' with 'universal-dotenv' to support multiple dot env files (like CRA) [#3744](https://github.com/storybooks/storybook/pull/3744)
|
||||||
|
- Support other type of webpack configs [#3785](https://github.com/storybooks/storybook/pull/3785)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
- Marko: fix welcome component [#3796](https://github.com/storybooks/storybook/pull/3796)
|
||||||
|
- Addon-a11y: Run analysis on demand [#3690](https://github.com/storybooks/storybook/pull/3690)
|
||||||
|
|
||||||
# 4.0.0-alpha.10
|
# 4.0.0-alpha.10
|
||||||
|
|
||||||
2018-June-21
|
2018-June-21
|
||||||
|
@ -331,7 +331,7 @@ yarn bootstrap --reset --core
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
# publish and tag the release
|
# publish and tag the release
|
||||||
npm run publish -- --concurrency 1 --npm-tag=alpha --force-publish=*
|
npm run publish:alpha
|
||||||
|
|
||||||
# update the release page
|
# update the release page
|
||||||
open https://github.com/storybooks/storybook/releases
|
open https://github.com/storybooks/storybook/releases
|
||||||
@ -355,7 +355,7 @@ git commit -m "Changelog for vX.Y"
|
|||||||
yarn bootstrap --reset --core
|
yarn bootstrap --reset --core
|
||||||
|
|
||||||
# publish and tag the release
|
# publish and tag the release
|
||||||
npm run publish -- --concurrency 1 --force-publish=*
|
npm run publish
|
||||||
|
|
||||||
# update the release page
|
# update the release page
|
||||||
open https://github.com/storybooks/storybook/releases
|
open https://github.com/storybooks/storybook/releases
|
||||||
|
@ -67,6 +67,24 @@ storiesOf('button', module)
|
|||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to add a11y globally to your stories, you can use the global Storybook decorator in your *.storybook/config.js* file:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { configure, addDecorator } from '@storybook/react';
|
||||||
|
import { checkA11y } from '@storybook/addon-a11y';
|
||||||
|
|
||||||
|
// pick all stories.js files within the src/ folder
|
||||||
|
const req = require.context('../src', true, /stories\.js$/);
|
||||||
|
|
||||||
|
addDecorator(checkA11y);
|
||||||
|
|
||||||
|
function loadStories() {
|
||||||
|
req.keys().forEach(filename => req(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(loadStories, module);
|
||||||
|
```
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
* Make UI accessibile
|
* Make UI accessibile
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-a11y",
|
"name": "@storybook/addon-a11y",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "a11y addon for storybook",
|
"description": "a11y addon for storybook",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"a11y",
|
"a11y",
|
||||||
@ -25,13 +25,12 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/client-logger": "4.0.0-alpha.10",
|
"@storybook/client-logger": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"axe-core": "^3.0.3",
|
"axe-core": "^3.0.3",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3",
|
||||||
|
@ -1,52 +1,76 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import addons from '@storybook/addons';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
import { CHECK_EVENT_ID } from '../shared';
|
import { STORY_RENDERED } from '@storybook/core-events';
|
||||||
|
import { ActionBar, ActionButton } from '@storybook/components';
|
||||||
|
|
||||||
|
import { CHECK_EVENT_ID, RERUN_EVENT_ID, REQUEST_CHECK_EVENT_ID } from '../shared';
|
||||||
|
|
||||||
import Tabs from './Tabs';
|
import Tabs from './Tabs';
|
||||||
import Report from './Report';
|
import Report from './Report';
|
||||||
|
|
||||||
const Passes = styled('span')({
|
const Passes = styled('span')(({ theme }) => ({
|
||||||
color: '#0D6731',
|
color: theme.successColor,
|
||||||
});
|
}));
|
||||||
|
|
||||||
const Violations = styled('span')({
|
const Violations = styled('span')(({ theme }) => ({
|
||||||
color: '#AC2300',
|
color: theme.failColor,
|
||||||
});
|
}));
|
||||||
|
|
||||||
class Panel extends Component {
|
class Panel extends Component {
|
||||||
constructor(props, ...args) {
|
static propTypes = {
|
||||||
super(props, ...args);
|
active: PropTypes.bool.isRequired,
|
||||||
this.state = {
|
channel: PropTypes.shape({
|
||||||
|
on: PropTypes.func,
|
||||||
|
emit: PropTypes.func,
|
||||||
|
removeListener: PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
passes: [],
|
passes: [],
|
||||||
violations: [],
|
violations: [],
|
||||||
};
|
};
|
||||||
this.channel = addons.getChannel();
|
|
||||||
|
|
||||||
this.onUpdate = this.onUpdate.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.channel.on(CHECK_EVENT_ID, this.onUpdate);
|
this.props.channel.on(CHECK_EVENT_ID, this.onUpdate);
|
||||||
|
this.props.channel.on(STORY_RENDERED, this.requestCheck);
|
||||||
|
this.props.channel.on(RERUN_EVENT_ID, this.requestCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (!prevProps.active && this.props.active) {
|
||||||
|
this.requestCheck();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.channel.removeListener(CHECK_EVENT_ID, this.onUpdate);
|
this.props.channel.removeListener(CHECK_EVENT_ID, this.onUpdate);
|
||||||
|
this.props.channel.removeListener(STORY_RENDERED, this.requestCheck);
|
||||||
|
this.props.channel.removeListener(RERUN_EVENT_ID, this.requestCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate({ passes, violations }) {
|
onUpdate = ({ passes, violations }) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
passes,
|
passes,
|
||||||
violations,
|
violations,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
requestCheck = () => {
|
||||||
|
if (this.props.active) {
|
||||||
|
this.props.channel.emit(REQUEST_CHECK_EVENT_ID);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { passes, violations } = this.state;
|
const { passes, violations } = this.state;
|
||||||
|
const { active } = this.props;
|
||||||
|
|
||||||
return (
|
return active ? (
|
||||||
|
<div>
|
||||||
<Tabs
|
<Tabs
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
@ -59,7 +83,11 @@ class Panel extends Component {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
<ActionBar>
|
||||||
|
<ActionButton onClick={this.requestCheck}>RERUN TEST</ActionButton>
|
||||||
|
</ActionBar>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,18 +3,18 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
const Wrapper = styled('div')({
|
const Wrapper = styled('div')(({ theme }) => ({
|
||||||
backgroundColor: 'rgb(234, 234, 234)',
|
backgroundColor: theme.barFill,
|
||||||
padding: '12px',
|
padding: '12px',
|
||||||
marginBottom: '10px',
|
marginBottom: '10px',
|
||||||
});
|
}));
|
||||||
const Help = styled('p')({
|
const Help = styled('p')({
|
||||||
margin: '0 0 12px',
|
margin: '0 0 12px',
|
||||||
});
|
});
|
||||||
const Link = styled('a')({
|
const Link = styled('a')({
|
||||||
marginTop: '12px',
|
marginTop: '12px',
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
color: 'rgb(130, 130, 130)',
|
color: 'inherit',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,19 +8,28 @@ import Info from './Info';
|
|||||||
import Tags from './Tags';
|
import Tags from './Tags';
|
||||||
import Elements from './Elements';
|
import Elements from './Elements';
|
||||||
|
|
||||||
const Wrapper = styled('div')({
|
const Wrapper = styled('div')(({ theme }) => ({
|
||||||
padding: '0 14px',
|
padding: '0 14px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
borderBottom: '1px solid rgb(234, 234, 234)',
|
borderBottom: theme.mainBorder,
|
||||||
});
|
}));
|
||||||
|
|
||||||
const HeaderBar = styled('button')({
|
const HeaderBar = styled('button')(({ theme }) => ({
|
||||||
padding: '12px 0px',
|
padding: '12px 0px',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
border: 0,
|
border: 0,
|
||||||
background: 'none',
|
background: 'none',
|
||||||
});
|
color: 'inherit',
|
||||||
|
|
||||||
|
borderTop: '3px solid transparent',
|
||||||
|
borderBottom: '3px solid transparent',
|
||||||
|
|
||||||
|
'&:focus': {
|
||||||
|
outline: '0 none',
|
||||||
|
borderBottom: `3px solid ${theme.highlightColor}`,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
class Item extends Component {
|
class Item extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
const RerunButton = styled('button')({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
border: 'none',
|
|
||||||
borderTop: 'solid 1px rgba(0, 0, 0, 0.2)',
|
|
||||||
borderLeft: 'solid 1px rgba(0, 0, 0, 0.2)',
|
|
||||||
background: 'rgba(255, 255, 255, 0.5)',
|
|
||||||
padding: '5px 10px',
|
|
||||||
borderRadius: '4px 0 0 0',
|
|
||||||
color: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
});
|
|
||||||
|
|
||||||
export default RerunButton;
|
|
@ -9,14 +9,12 @@ const Wrapper = styled('div')({
|
|||||||
margin: '12px 0',
|
margin: '12px 0',
|
||||||
});
|
});
|
||||||
|
|
||||||
const Item = styled('div')({
|
const Item = styled('div')(({ theme }) => ({
|
||||||
margin: '0 6px',
|
margin: '0 6px',
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
border: '1px solid rgb(234, 234, 234)',
|
border: theme.mainBorder,
|
||||||
borderRadius: '2px',
|
borderRadius: theme.mainBorderRadius,
|
||||||
color: 'rgb(130, 130, 130)',
|
}));
|
||||||
fontSize: '12px',
|
|
||||||
});
|
|
||||||
|
|
||||||
function Tags({ tags }) {
|
function Tags({ tags }) {
|
||||||
return <Wrapper>{tags.map(tag => <Item key={tag}>{tag}</Item>)}</Wrapper>;
|
return <Wrapper>{tags.map(tag => <Item key={tag}>{tag}</Item>)}</Wrapper>;
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import addons from '@storybook/addons';
|
|
||||||
import { Placeholder } from '@storybook/components';
|
import { Placeholder } from '@storybook/components';
|
||||||
|
|
||||||
import { RERUN_EVENT_ID } from '../../shared';
|
|
||||||
|
|
||||||
import RerunButton from './RerunButton';
|
|
||||||
import Item from './Item';
|
import Item from './Item';
|
||||||
|
|
||||||
function onRerunClick() {
|
|
||||||
const channel = addons.getChannel();
|
|
||||||
channel.emit(RERUN_EVENT_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Report = ({ items, empty, passes }) => (
|
const Report = ({ items, empty, passes }) => (
|
||||||
<div>
|
<div>
|
||||||
{items.length ? (
|
{items.length ? (
|
||||||
@ -20,7 +11,6 @@ const Report = ({ items, empty, passes }) => (
|
|||||||
) : (
|
) : (
|
||||||
<Placeholder>{empty}</Placeholder>
|
<Placeholder>{empty}</Placeholder>
|
||||||
)}
|
)}
|
||||||
<RerunButton onClick={onRerunClick}>Re-run tests</RerunButton>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6,13 +6,14 @@ import styled from 'react-emotion';
|
|||||||
const Container = styled('div')({
|
const Container = styled('div')({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
minHeight: '100%',
|
||||||
});
|
});
|
||||||
|
|
||||||
const List = styled('div')({
|
const List = styled('div')(({ theme }) => ({
|
||||||
borderBottom: '1px solid rgb(234, 234, 234)',
|
borderBottom: theme.mainBorder,
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
});
|
}));
|
||||||
|
|
||||||
const Item = styled('button')(
|
const Item = styled('button')(
|
||||||
({ active }) =>
|
({ active }) =>
|
||||||
@ -22,9 +23,7 @@ const Item = styled('button')(
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
{
|
({ theme }) => ({
|
||||||
color: 'rgb(68, 68, 68)',
|
|
||||||
fontSize: '11px',
|
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
padding: '10px 15px',
|
padding: '10px 15px',
|
||||||
@ -33,9 +32,16 @@ const Item = styled('button')(
|
|||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
|
borderTop: '3px solid transparent',
|
||||||
|
borderBottom: '3px solid transparent',
|
||||||
background: 'none',
|
background: 'none',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}
|
|
||||||
|
'&:focus': {
|
||||||
|
outline: '0 none',
|
||||||
|
borderBottom: `3px solid ${theme.highlightColor}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
class Tabs extends Component {
|
class Tabs extends Component {
|
||||||
|
@ -4,7 +4,7 @@ import addons from '@storybook/addons';
|
|||||||
import Events from '@storybook/core-events';
|
import Events from '@storybook/core-events';
|
||||||
import { logger } from '@storybook/client-logger';
|
import { logger } from '@storybook/client-logger';
|
||||||
|
|
||||||
import { CHECK_EVENT_ID, RERUN_EVENT_ID } from './shared';
|
import { CHECK_EVENT_ID, REQUEST_CHECK_EVENT_ID } from './shared';
|
||||||
|
|
||||||
let axeOptions = {};
|
let axeOptions = {};
|
||||||
|
|
||||||
@ -23,11 +23,10 @@ const runA11yCheck = () => {
|
|||||||
|
|
||||||
const a11ySubscription = () => {
|
const a11ySubscription = () => {
|
||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
channel.on(Events.STORY_RENDERED, runA11yCheck);
|
channel.on(REQUEST_CHECK_EVENT_ID, runA11yCheck);
|
||||||
channel.on(RERUN_EVENT_ID, runA11yCheck);
|
|
||||||
return () => {
|
return () => {
|
||||||
channel.removeListener(Events.STORY_RENDERED, runA11yCheck);
|
channel.removeListener(REQUEST_CHECK_EVENT_ID, runA11yCheck);
|
||||||
channel.removeListener(RERUN_EVENT_ID, runA11yCheck);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@ import Panel from './components/Panel';
|
|||||||
import { ADDON_ID, PANEL_ID } from './shared';
|
import { ADDON_ID, PANEL_ID } from './shared';
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
addons.register(ADDON_ID, () => {
|
addons.register(ADDON_ID, api => {
|
||||||
|
const channel = addons.getChannel();
|
||||||
addons.addPanel(PANEL_ID, {
|
addons.addPanel(PANEL_ID, {
|
||||||
title: 'Accessibility',
|
title: 'Accessibility',
|
||||||
render() {
|
// eslint-disable-next-line react/prop-types
|
||||||
return <Panel />;
|
render: ({ active }) => <Panel channel={channel} api={api} active={active} />,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,6 @@ const ADDON_ID = '@storybook/addon-a11y';
|
|||||||
const PANEL_ID = `${ADDON_ID}/panel`;
|
const PANEL_ID = `${ADDON_ID}/panel`;
|
||||||
const CHECK_EVENT_ID = `${ADDON_ID}/check`;
|
const CHECK_EVENT_ID = `${ADDON_ID}/check`;
|
||||||
const RERUN_EVENT_ID = `${ADDON_ID}/rerun`;
|
const RERUN_EVENT_ID = `${ADDON_ID}/rerun`;
|
||||||
|
const REQUEST_CHECK_EVENT_ID = `${ADDON_ID}/request-check`;
|
||||||
|
|
||||||
export { ADDON_ID, PANEL_ID, CHECK_EVENT_ID, RERUN_EVENT_ID };
|
export { ADDON_ID, PANEL_ID, CHECK_EVENT_ID, RERUN_EVENT_ID, REQUEST_CHECK_EVENT_ID };
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-actions",
|
"name": "@storybook/addon-actions",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Action Logger addon for storybook",
|
"description": "Action Logger addon for storybook",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"storybook"
|
"storybook"
|
||||||
@ -20,12 +20,12 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"deep-equal": "^1.0.1",
|
"deep-equal": "^1.0.1",
|
||||||
"emotion": "^9.1.3",
|
"emotion-theming": "^9.1.2",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"make-error": "^1.3.4",
|
"make-error": "^1.3.4",
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import Inspector from 'react-inspector';
|
import Inspector from 'react-inspector';
|
||||||
import { Actions, Action, Button, Wrapper, InspectorContainer, Countwrap, Counter } from './style';
|
import { withTheme } from 'emotion-theming';
|
||||||
|
|
||||||
class ActionLogger extends Component {
|
import { ActionBar, ActionButton } from '@storybook/components';
|
||||||
getActionData() {
|
|
||||||
return this.props.actions.map(action => this.renderAction(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderAction(action) {
|
import { Actions, Action, Wrapper, InspectorContainer, Countwrap, Counter } from './style';
|
||||||
const counter = <Counter>{action.count}</Counter>;
|
|
||||||
return (
|
const ActionLogger = withTheme(({ actions, onClear, theme }) => (
|
||||||
|
<Wrapper>
|
||||||
|
<Actions>
|
||||||
|
{actions.map(action => (
|
||||||
<Action key={action.id}>
|
<Action key={action.id}>
|
||||||
<Countwrap>{action.count > 1 && counter}</Countwrap>
|
<Countwrap>{action.count > 1 && <Counter>{action.count}</Counter>}</Countwrap>
|
||||||
<InspectorContainer>
|
<InspectorContainer>
|
||||||
<Inspector
|
<Inspector
|
||||||
|
theme={theme.addonActionsTheme || 'chromeLight'}
|
||||||
sortObjectKeys
|
sortObjectKeys
|
||||||
showNonenumerable={false}
|
showNonenumerable={false}
|
||||||
name={action.data.name}
|
name={action.data.name}
|
||||||
@ -22,27 +23,26 @@ class ActionLogger extends Component {
|
|||||||
/>
|
/>
|
||||||
</InspectorContainer>
|
</InspectorContainer>
|
||||||
</Action>
|
</Action>
|
||||||
);
|
))}
|
||||||
}
|
</Actions>
|
||||||
|
|
||||||
render() {
|
<ActionBar>
|
||||||
return (
|
<ActionButton onClick={onClear}>CLEAR</ActionButton>
|
||||||
<Wrapper>
|
</ActionBar>
|
||||||
<Actions>{this.getActionData()}</Actions>
|
|
||||||
<Button onClick={this.props.onClear}>Clear</Button>
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ActionLogger.propTypes = {
|
ActionLogger.propTypes = {
|
||||||
onClear: PropTypes.func,
|
onClear: PropTypes.func.isRequired,
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
actions: PropTypes.arrayOf(
|
||||||
actions: PropTypes.array,
|
PropTypes.shape({
|
||||||
};
|
count: PropTypes.node,
|
||||||
ActionLogger.defaultProps = {
|
data: PropTypes.shape({
|
||||||
onClear: () => {},
|
name: PropTypes.node.isRequired,
|
||||||
actions: [],
|
args: PropTypes.any,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ActionLogger;
|
export default ActionLogger;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
import { Button as BaseButton } from '@storybook/components';
|
|
||||||
|
|
||||||
export const Actions = styled('pre')({
|
export const Actions = styled('pre')({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -12,24 +11,12 @@ export const Actions = styled('pre')({
|
|||||||
export const Action = styled('div')({
|
export const Action = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
padding: '3px 3px 3px 0',
|
padding: '3px 3px 3px 0',
|
||||||
borderLeft: '5px solid white',
|
borderLeft: '5px solid transparent',
|
||||||
borderBottom: '1px solid #fafafa',
|
borderBottom: '1px solid transparent',
|
||||||
transition: 'all 0.1s',
|
transition: 'all 0.1s',
|
||||||
alignItems: 'start',
|
alignItems: 'start',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Button = styled(BaseButton)({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
borderRadius: '4px 0 0 0',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: 1,
|
|
||||||
paddingTop: 5,
|
|
||||||
paddingBottom: 5,
|
|
||||||
border: '0 none',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Counter = styled('div')({
|
export const Counter = styled('div')({
|
||||||
margin: '0 5px 0 5px',
|
margin: '0 5px 0 5px',
|
||||||
backgroundColor: '#777777',
|
backgroundColor: '#777777',
|
||||||
@ -51,4 +38,5 @@ export const Wrapper = styled('div')({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
minHeight: '100%',
|
||||||
});
|
});
|
||||||
|
@ -56,21 +56,25 @@ export default class ActionLogger extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { active } = this.props;
|
||||||
const props = {
|
const props = {
|
||||||
actions: this.state.actions,
|
actions: this.state.actions,
|
||||||
onClear: () => this.clearActions(),
|
onClear: () => this.clearActions(),
|
||||||
};
|
};
|
||||||
return <ActionLoggerComponent {...props} />;
|
return active ? <ActionLoggerComponent {...props} /> : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionLogger.propTypes = {
|
ActionLogger.propTypes = {
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
active: PropTypes.bool.isRequired,
|
||||||
channel: PropTypes.object,
|
channel: PropTypes.shape({
|
||||||
|
emit: PropTypes.func,
|
||||||
|
on: PropTypes.func,
|
||||||
|
removeListener: PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
api: PropTypes.shape({
|
api: PropTypes.shape({
|
||||||
onStory: PropTypes.func.isRequired,
|
onStory: PropTypes.func,
|
||||||
|
getQueryParam: PropTypes.func,
|
||||||
|
setQueryParams: PropTypes.func,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
};
|
};
|
||||||
ActionLogger.defaultProps = {
|
|
||||||
channel: {},
|
|
||||||
};
|
|
||||||
|
@ -8,7 +8,8 @@ export function register() {
|
|||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
addons.addPanel(PANEL_ID, {
|
addons.addPanel(PANEL_ID, {
|
||||||
title: 'Action Logger',
|
title: 'Action Logger',
|
||||||
render: () => <ActionLogger channel={channel} api={api} />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <ActionLogger channel={channel} api={api} active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-backgrounds",
|
"name": "@storybook/addon-backgrounds",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "A storybook addon to show different backgrounds for your preview",
|
"description": "A storybook addon to show different backgrounds for your preview",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"addon",
|
"addon",
|
||||||
@ -24,10 +24,9 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3",
|
||||||
|
@ -16,14 +16,13 @@ const Title = styled('h5')({
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Pre = styled('pre')({
|
const Pre = styled('pre')(({ theme }) => ({
|
||||||
padding: '30px',
|
padding: '30px',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
background: 'rgba(19,19,19,0.9)',
|
background: theme.fillColor,
|
||||||
color: 'rgba(255,255,255,0.95)',
|
|
||||||
marginTop: '15px',
|
marginTop: '15px',
|
||||||
lineHeight: '1.75em',
|
lineHeight: '1.75em',
|
||||||
});
|
}));
|
||||||
|
|
||||||
const List = styled('div')({
|
const List = styled('div')({
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
@ -47,13 +46,13 @@ const defaultBackground = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const instructionsHtml = `
|
const instructionsHtml = `
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from '@storybook/react';
|
||||||
import { withBackgrounds } from "@storybook/addon-backgrounds";
|
import { withBackgrounds } from '@storybook/addon-backgrounds';
|
||||||
|
|
||||||
storiesOf("First Component", module)
|
storiesOf('First Component', module)
|
||||||
.addDecorator(withBackgrounds([
|
.addDecorator(withBackgrounds([
|
||||||
{ name: "twitter", value: "#00aced" },
|
{ name: 'twitter', value: '#00aced' },
|
||||||
{ name: "facebook", value: "#3b5998" },
|
{ name: 'facebook', value: '#3b5998" },
|
||||||
]))
|
]))
|
||||||
.add("First Button", () => <button>Click me</button>);
|
.add("First Button", () => <button>Click me</button>);
|
||||||
`.trim();
|
`.trim();
|
||||||
@ -130,8 +129,11 @@ export default class BackgroundPanel extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { active } = this.props;
|
||||||
const backgrounds = [...this.state.backgrounds];
|
const backgrounds = [...this.state.backgrounds];
|
||||||
|
if (!active) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!backgrounds.length) return <Instructions />;
|
if (!backgrounds.length) return <Instructions />;
|
||||||
|
|
||||||
const hasDefault = backgrounds.filter(x => x.default).length;
|
const hasDefault = backgrounds.filter(x => x.default).length;
|
||||||
@ -149,6 +151,7 @@ export default class BackgroundPanel extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BackgroundPanel.propTypes = {
|
BackgroundPanel.propTypes = {
|
||||||
|
active: PropTypes.bool.isRequired,
|
||||||
api: PropTypes.shape({
|
api: PropTypes.shape({
|
||||||
getQueryParam: PropTypes.func,
|
getQueryParam: PropTypes.func,
|
||||||
setQueryParams: PropTypes.func,
|
setQueryParams: PropTypes.func,
|
||||||
|
@ -3,24 +3,26 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
const Button = styled('button')({
|
const Button = styled('button')(({ theme }) => ({
|
||||||
listStyle: 'none',
|
listStyle: 'none',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: theme.barFill,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
border: '1px solid rgba(0,0,0,0.1)',
|
border: theme.mainBorder,
|
||||||
borderRadius: 4,
|
borderRadius: theme.mainBorderRadius,
|
||||||
|
color: 'inherit',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
width: 175,
|
width: 175,
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
wordWrap: 'break-word',
|
wordWrap: 'break-word',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
});
|
overflow: 'hidden',
|
||||||
const Block = styled('div')(({ bg }) => ({
|
}));
|
||||||
|
|
||||||
|
const Block = styled('div')(({ bg, theme }) => ({
|
||||||
height: 80,
|
height: 80,
|
||||||
borderRadius: '4px 4px 0 0',
|
|
||||||
transition: 'opacity 0.25s ease-in-out',
|
transition: 'opacity 0.25s ease-in-out',
|
||||||
borderBottom: '1px solid rgba(0,0,0,0.1)',
|
borderBottom: theme.mainBorder,
|
||||||
background: bg,
|
background: bg,
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
|
@ -30,26 +30,26 @@ jest.mock('global', () => ({
|
|||||||
|
|
||||||
describe('Background Panel', () => {
|
describe('Background Panel', () => {
|
||||||
it('should exist', () => {
|
it('should exist', () => {
|
||||||
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} />);
|
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} active />);
|
||||||
|
|
||||||
expect(backgroundPanel).toBeDefined();
|
expect(backgroundPanel).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have a default background value of transparent', () => {
|
it('should have a default background value of transparent', () => {
|
||||||
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} />);
|
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} active />);
|
||||||
|
|
||||||
expect(backgroundPanel.state().backgrounds).toHaveLength(0);
|
expect(backgroundPanel.state().backgrounds).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show setup instructions if no colors provided', () => {
|
it('should show setup instructions if no colors provided', () => {
|
||||||
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} />);
|
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} active />);
|
||||||
|
|
||||||
expect(backgroundPanel.html().match(/Setup Instructions/gim).length).toBeGreaterThan(0);
|
expect(backgroundPanel.html().match(/Setup Instructions/gim).length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the query string', () => {
|
it('should set the query string', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />);
|
||||||
SpiedChannel.emit(Events.SET, backgrounds);
|
SpiedChannel.emit(Events.SET, backgrounds);
|
||||||
|
|
||||||
expect(mockedApi.getQueryParam).toBeCalledWith('background');
|
expect(mockedApi.getQueryParam).toBeCalledWith('background');
|
||||||
@ -57,7 +57,7 @@ describe('Background Panel', () => {
|
|||||||
|
|
||||||
it('should not unset the query string', () => {
|
it('should not unset the query string', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />);
|
||||||
SpiedChannel.emit(Events.UNSET, []);
|
SpiedChannel.emit(Events.UNSET, []);
|
||||||
|
|
||||||
expect(mockedApi.setQueryParams).not.toHaveBeenCalled();
|
expect(mockedApi.setQueryParams).not.toHaveBeenCalled();
|
||||||
@ -65,7 +65,9 @@ describe('Background Panel', () => {
|
|||||||
|
|
||||||
it('should accept colors through channel and render the correct swatches with a default swatch', () => {
|
it('should accept colors through channel and render the correct swatches with a default swatch', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
const backgroundPanel = mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
const backgroundPanel = mount(
|
||||||
|
<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />
|
||||||
|
);
|
||||||
SpiedChannel.emit(Events.SET, backgrounds);
|
SpiedChannel.emit(Events.SET, backgrounds);
|
||||||
|
|
||||||
expect(backgroundPanel.state('backgrounds')).toEqual(backgrounds);
|
expect(backgroundPanel.state('backgrounds')).toEqual(backgrounds);
|
||||||
@ -73,7 +75,9 @@ describe('Background Panel', () => {
|
|||||||
|
|
||||||
it('should allow setting a default swatch', () => {
|
it('should allow setting a default swatch', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
const backgroundPanel = mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
const backgroundPanel = mount(
|
||||||
|
<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />
|
||||||
|
);
|
||||||
const [head, ...tail] = backgrounds;
|
const [head, ...tail] = backgrounds;
|
||||||
const localBgs = [{ ...head, default: true }, ...tail];
|
const localBgs = [{ ...head, default: true }, ...tail];
|
||||||
SpiedChannel.emit(Events.SET, localBgs);
|
SpiedChannel.emit(Events.SET, localBgs);
|
||||||
@ -88,7 +92,9 @@ describe('Background Panel', () => {
|
|||||||
|
|
||||||
it('should allow the default swatch become the background color', () => {
|
it('should allow the default swatch become the background color', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
const backgroundPanel = mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
const backgroundPanel = mount(
|
||||||
|
<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />
|
||||||
|
);
|
||||||
const [head, second, ...tail] = backgrounds;
|
const [head, second, ...tail] = backgrounds;
|
||||||
const localBgs = [head, { ...second, default: true }, ...tail];
|
const localBgs = [head, { ...second, default: true }, ...tail];
|
||||||
SpiedChannel.on('background', bg => {
|
SpiedChannel.on('background', bg => {
|
||||||
@ -106,7 +112,9 @@ describe('Background Panel', () => {
|
|||||||
|
|
||||||
it('should unset all swatches on receiving the background-unset message', () => {
|
it('should unset all swatches on receiving the background-unset message', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
const backgroundPanel = mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
const backgroundPanel = mount(
|
||||||
|
<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />
|
||||||
|
);
|
||||||
SpiedChannel.emit(Events.SET, backgrounds);
|
SpiedChannel.emit(Events.SET, backgrounds);
|
||||||
|
|
||||||
expect(backgroundPanel.state('backgrounds')).toEqual(backgrounds);
|
expect(backgroundPanel.state('backgrounds')).toEqual(backgrounds);
|
||||||
@ -118,7 +126,9 @@ describe('Background Panel', () => {
|
|||||||
|
|
||||||
it('should set iframe background', () => {
|
it('should set iframe background', () => {
|
||||||
const SpiedChannel = new EventEmitter();
|
const SpiedChannel = new EventEmitter();
|
||||||
const backgroundPanel = mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
|
const backgroundPanel = mount(
|
||||||
|
<BackgroundPanel channel={SpiedChannel} api={mockedApi} active />
|
||||||
|
);
|
||||||
backgroundPanel.setState({ backgrounds }); // force re-render
|
backgroundPanel.setState({ backgrounds }); // force re-render
|
||||||
|
|
||||||
backgroundPanel
|
backgroundPanel
|
||||||
|
@ -15,6 +15,7 @@ export const withBackgrounds = makeDecorator({
|
|||||||
name: 'backgrounds',
|
name: 'backgrounds',
|
||||||
parameterName: 'backgrounds',
|
parameterName: 'backgrounds',
|
||||||
skipIfNoParametersOrOptions: true,
|
skipIfNoParametersOrOptions: true,
|
||||||
|
allowDeprecatedUsage: true,
|
||||||
wrapper: (getStory, context, { options, parameters }) => {
|
wrapper: (getStory, context, { options, parameters }) => {
|
||||||
const backgrounds = parameters || options;
|
const backgrounds = parameters || options;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ addons.register(ADDON_ID, api => {
|
|||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
addons.addPanel(PANEL_ID, {
|
addons.addPanel(PANEL_ID, {
|
||||||
title: 'Backgrounds',
|
title: 'Backgrounds',
|
||||||
render: () => <BackgroundPanel channel={channel} api={api} />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <BackgroundPanel channel={channel} api={api} active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-centered",
|
"name": "@storybook/addon-centered",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook decorator to center components",
|
"description": "Storybook decorator to center components",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Muhammed Thanish <mnmtanish@gmail.com>",
|
"author": "Muhammed Thanish <mnmtanish@gmail.com>",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-events",
|
"name": "@storybook/addon-events",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Add events to your Storybook stories.",
|
"description": "Add events to your Storybook stories.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"addon",
|
"addon",
|
||||||
@ -19,10 +19,9 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"format-json": "^1.0.3",
|
"format-json": "^1.0.3",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3",
|
||||||
|
@ -10,10 +10,12 @@ const Wrapper = styled('div')({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
padding: '10px',
|
padding: '10px',
|
||||||
|
minHeight: '100%',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Events extends Component {
|
export default class Events extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
active: PropTypes.bool.isRequired,
|
||||||
channel: PropTypes.shape({
|
channel: PropTypes.shape({
|
||||||
on: PropTypes.func,
|
on: PropTypes.func,
|
||||||
emit: PropTypes.func,
|
emit: PropTypes.func,
|
||||||
@ -43,10 +45,11 @@ export default class Events extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { events } = this.state;
|
const { events } = this.state;
|
||||||
return (
|
const { active } = this.props;
|
||||||
|
return active ? (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{events.map(event => <Event key={event.name} {...event} onEmit={this.onEmit} />)}
|
{events.map(event => <Event key={event.name} {...event} onEmit={this.onEmit} />)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import { ADDON_ID, PANEL_ID } from './constants';
|
|||||||
|
|
||||||
export function register() {
|
export function register() {
|
||||||
addons.register(ADDON_ID, () => {
|
addons.register(ADDON_ID, () => {
|
||||||
|
const channel = addons.getChannel();
|
||||||
addons.addPanel(PANEL_ID, {
|
addons.addPanel(PANEL_ID, {
|
||||||
title: 'Events',
|
title: 'Events',
|
||||||
render: () => <Panel channel={addons.getChannel()} />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <Panel channel={channel} active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-graphql",
|
"name": "@storybook/addon-graphql",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook addon to display the GraphiQL IDE",
|
"description": "Storybook addon to display the GraphiQL IDE",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"storybook"
|
"storybook"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
[](https://now-examples-slackin-rrirkqohko.now.sh/)
|
[](https://now-examples-slackin-rrirkqohko.now.sh/)
|
||||||
[](#backers) [](#sponsors)
|
[](#backers) [](#sponsors)
|
||||||
|
|
||||||
* * *
|
---
|
||||||
|
|
||||||
Storybook Info Addon will show additional information for your stories in [Storybook](https://storybook.js.org).
|
Storybook Info Addon will show additional information for your stories in [Storybook](https://storybook.js.org).
|
||||||
Useful when you want to display usage or other types of documentation alongside your story.
|
Useful when you want to display usage or other types of documentation alongside your story.
|
||||||
@ -25,6 +25,7 @@ npm i -D @storybook/addon-info
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
Then, add `withInfo` as a decarator to your book of stories.
|
Then, add `withInfo` as a decarator to your book of stories.
|
||||||
It is possible to add `info` by default to all or a subsection of stories by using a global or story decorator.
|
It is possible to add `info` by default to all or a subsection of stories by using a global or story decorator.
|
||||||
|
|
||||||
@ -33,7 +34,9 @@ It is important to declare this decorator as **the first decorator**, otherwise
|
|||||||
```js
|
```js
|
||||||
addDecorator(withInfo); // Globally in your .storybook/config.js.
|
addDecorator(withInfo); // Globally in your .storybook/config.js.
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```js
|
```js
|
||||||
storiesOf('Component', module)
|
storiesOf('Component', module)
|
||||||
.addDecorator(withInfo) // At your stories directly.
|
.addDecorator(withInfo) // At your stories directly.
|
||||||
@ -53,12 +56,13 @@ storiesOf('Component', module)
|
|||||||
.addParameters({
|
.addParameters({
|
||||||
info: {
|
info: {
|
||||||
// Your settings
|
// Your settings
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.add('with some emoji', () => <Component/>);
|
.add('with some emoji', () => <Component />);
|
||||||
```
|
```
|
||||||
|
|
||||||
...or for each story individually:
|
...or for each story individually:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { storiesOf } from '@storybook/react';
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
@ -67,12 +71,12 @@ import Component from './Component';
|
|||||||
storiesOf('Component', module)
|
storiesOf('Component', module)
|
||||||
.add(
|
.add(
|
||||||
'with some emoji',
|
'with some emoji',
|
||||||
() => <Component emoji/>,
|
() => <Component emoji />,
|
||||||
{ info : { inline: false, header: false } } // Make your component render inline with the additional info
|
{ info: { inline: true, header: false } } // Make your component render inline with the additional info
|
||||||
)
|
)
|
||||||
.add(
|
.add(
|
||||||
'with no emoji',
|
'with no emoji',
|
||||||
() => <Component/>,
|
() => <Component />,
|
||||||
{ info: '☹️ no emojis' } // Add additional info text directly
|
{ info: '☹️ no emojis' } // Add additional info text directly
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
@ -86,41 +90,36 @@ import Component from './Component';
|
|||||||
|
|
||||||
storiesOf('Component', module)
|
storiesOf('Component', module)
|
||||||
.addParameters({
|
.addParameters({
|
||||||
info: { // Make a default for all stories in this book,
|
info: {
|
||||||
|
// Make a default for all stories in this book,
|
||||||
inline: true, // where the components are inlined
|
inline: true, // where the components are inlined
|
||||||
styles: {
|
styles: {
|
||||||
header: {
|
header: {
|
||||||
h1: {
|
h1: {
|
||||||
color: 'red' // and the headers of the sections are red.
|
color: 'red', // and the headers of the sections are red.
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.add(
|
.add('green version', () => <Component green />, {
|
||||||
'green version',
|
|
||||||
() => <Component green/>,
|
|
||||||
{
|
|
||||||
info: {
|
info: {
|
||||||
styles: stylesheet => ({ // Setting the style with a function
|
styles: stylesheet => ({
|
||||||
|
// Setting the style with a function
|
||||||
...stylesheet,
|
...stylesheet,
|
||||||
header: {
|
header: {
|
||||||
...stylesheet.header,
|
...stylesheet.header,
|
||||||
h1: {
|
h1: {
|
||||||
...stylesheet.header.h1,
|
...stylesheet.header.h1,
|
||||||
color: 'green' // Still inlined but with green headers!
|
color: 'green', // Still inlined but with green headers!
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
.add('something else', () => <Component different />, {
|
||||||
})
|
info: 'This story has additional text added to the info!', // Still inlined and with red headers!
|
||||||
.add(
|
});
|
||||||
'something else',
|
|
||||||
() => <Component different/>,
|
|
||||||
{
|
|
||||||
info: "This story has additional text added to the info!" // Still inlined and with red headers!
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It is also possible to disable the `info` addon entirely.
|
It is also possible to disable the `info` addon entirely.
|
||||||
@ -135,16 +134,12 @@ Depending on the scope at which you want to disable the addon, pass the followin
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Markdown
|
## Markdown
|
||||||
|
|
||||||
The `info` addon also supports markdown.
|
The `info` addon also supports markdown.
|
||||||
To use markdown as additional textual documentation for your stories, either pass it directly as a String to the `info` parameters, or use the `text` option.
|
To use markdown as additional textual documentation for your stories, either pass it directly as a String to the `info` parameters, or use the `text` option.
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
storiesOf('Button', module)
|
storiesOf('Button', module).add('Button Component', () => <Button />, {
|
||||||
.add(
|
|
||||||
'Button Component',
|
|
||||||
() => <Button />,
|
|
||||||
{
|
|
||||||
info: {
|
info: {
|
||||||
text: `
|
text: `
|
||||||
description or documentation about my component, supports markdown
|
description or documentation about my component, supports markdown
|
||||||
@ -152,10 +147,9 @@ storiesOf('Button', module)
|
|||||||
~~~js
|
~~~js
|
||||||
<Button>Click Here</Button>
|
<Button>Click Here</Button>
|
||||||
~~~
|
~~~
|
||||||
`
|
`,
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setting Global Options
|
## Setting Global Options
|
||||||
@ -166,9 +160,11 @@ To configure default options for all usage of the info addon, pass a option obje
|
|||||||
// config.js
|
// config.js
|
||||||
import { withInfo } from '@storybook/addon-info';
|
import { withInfo } from '@storybook/addon-info';
|
||||||
|
|
||||||
addDecorator(withInfo({
|
addDecorator(
|
||||||
|
withInfo({
|
||||||
header: false, // Global configuration for the info addon across all of your stories.
|
header: false, // Global configuration for the info addon across all of your stories.
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Configuration parameters can be set at 3 different locations: passed as default options along the `addDecorator` call, passed as an object of parameters to a book of stories to the `addParameters` call, and passed as direct parameters to each individual story.
|
Configuration parameters can be set at 3 different locations: passed as default options along the `addDecorator` call, passed as an object of parameters to a book of stories to the `addParameters` call, and passed as direct parameters to each individual story.
|
||||||
@ -274,40 +270,40 @@ Example:
|
|||||||
```js
|
```js
|
||||||
// button.js
|
// button.js
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react'
|
import React from 'react';
|
||||||
|
|
||||||
const paddingStyles = {
|
const paddingStyles = {
|
||||||
small: '4px 8px',
|
small: '4px 8px',
|
||||||
medium: '8px 16px'
|
medium: '8px 16px',
|
||||||
}
|
};
|
||||||
|
|
||||||
const Button = ({
|
const Button = ({
|
||||||
size,
|
size,
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
/** The size of the button */
|
/** The size of the button */
|
||||||
size: 'small' | 'medium'
|
size: 'small' | 'medium',
|
||||||
}) => {
|
}) => {
|
||||||
const style = {
|
const style = {
|
||||||
padding: paddingStyles[size] || ''
|
padding: paddingStyles[size] || '',
|
||||||
}
|
};
|
||||||
return <button style={style} {...rest} />
|
return <button style={style} {...rest} />;
|
||||||
}
|
};
|
||||||
Button.defaultProps = {
|
Button.defaultProps = {
|
||||||
size: 'medium'
|
size: 'medium',
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Button
|
export default Button;
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// stories.js
|
// stories.js
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
|
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from '@storybook/react';
|
||||||
import { withInfo } from "@storybook/addon-info";
|
import Button from './button';
|
||||||
import Button from "./button";
|
|
||||||
|
|
||||||
const Red = props => <span style={{ color: "red" }} {...props} />;
|
const Red = props => <span style={{ color: 'red' }} {...props} />;
|
||||||
|
|
||||||
const TableComponent = ({ propDefinitions }) => {
|
const TableComponent = ({ propDefinitions }) => {
|
||||||
const props = propDefinitions.map(
|
const props = propDefinitions.map(
|
||||||
@ -341,12 +337,11 @@ const TableComponent = ({ propDefinitions }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Button", module).add(
|
storiesOf('Button', module).add('with text', () => <Button>Hello Button</Button>, {
|
||||||
"with text",
|
info: {
|
||||||
withInfo({
|
TableComponent,
|
||||||
TableComponent
|
},
|
||||||
})(() => <Button>Hello Button</Button>)
|
});
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### React Docgen Integration
|
### React Docgen Integration
|
||||||
@ -359,10 +354,11 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
/** Button component description */
|
/** Button component description */
|
||||||
const DocgenButton = ({ disabled, label, style, onClick }) =>
|
const DocgenButton = ({ disabled, label, style, onClick }) => (
|
||||||
<button disabled={disabled} style={style} onClick={onClick}>
|
<button disabled={disabled} style={style} onClick={onClick}>
|
||||||
{label}
|
{label}
|
||||||
</button>;
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
DocgenButton.defaultProps = {
|
DocgenButton.defaultProps = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-info",
|
"name": "@storybook/addon-info",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "A Storybook addon to show additional information for your stories.",
|
"description": "A Storybook addon to show additional information for your stories.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -13,12 +13,11 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/client-logger": "4.0.0-alpha.10",
|
"@storybook/client-logger": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"core-js": "2.5.7",
|
"core-js": "2.5.7",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"marksy": "^6.0.3",
|
"marksy": "^6.0.3",
|
||||||
"nested-object-assign": "^1.0.1",
|
"nested-object-assign": "^1.0.1",
|
||||||
|
@ -33,6 +33,9 @@ exports[`addon Info should render <Info /> and external markdown 1`] = `
|
|||||||
-webkit-align-self: flex-start;
|
-webkit-align-self: flex-start;
|
||||||
-ms-flex-item-align: start;
|
-ms-flex-item-align: start;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
|
-webkit-flex-shrink: 0;
|
||||||
|
-ms-flex-negative: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emotion-1:hover {
|
.emotion-1:hover {
|
||||||
@ -1335,6 +1338,9 @@ exports[`addon Info should render <Info /> and markdown 1`] = `
|
|||||||
-webkit-align-self: flex-start;
|
-webkit-align-self: flex-start;
|
||||||
-ms-flex-item-align: start;
|
-ms-flex-item-align: start;
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
|
-webkit-flex-shrink: 0;
|
||||||
|
-ms-flex-negative: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emotion-1:hover {
|
.emotion-1:hover {
|
||||||
|
@ -12,6 +12,7 @@ const Button = styled('button')(
|
|||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
padding: '3px 10px',
|
padding: '3px 10px',
|
||||||
alignSelf: 'flex-start',
|
alignSelf: 'flex-start',
|
||||||
|
flexShrink: 0,
|
||||||
|
|
||||||
':hover': {
|
':hover': {
|
||||||
backgroundColor: '#f4f7fa',
|
backgroundColor: '#f4f7fa',
|
||||||
|
@ -87,6 +87,7 @@ function addInfo(storyFn, context, infoOptions) {
|
|||||||
export const withInfo = makeDecorator({
|
export const withInfo = makeDecorator({
|
||||||
name: 'withInfo',
|
name: 'withInfo',
|
||||||
parameterName: 'info',
|
parameterName: 'info',
|
||||||
|
allowDeprecatedUsage: true,
|
||||||
wrapper: (getStory, context, { options, parameters }) => {
|
wrapper: (getStory, context, { options, parameters }) => {
|
||||||
const storyOptions = parameters || options;
|
const storyOptions = parameters || options;
|
||||||
const infoOptions = typeof storyOptions === 'string' ? { text: storyOptions } : storyOptions;
|
const infoOptions = typeof storyOptions === 'string' ? { text: storyOptions } : storyOptions;
|
||||||
|
@ -31,16 +31,18 @@ When running **Jest**, be sure to save the results in a json file:
|
|||||||
```
|
```
|
||||||
|
|
||||||
You may want to add it the result file to `.gitignore`, since it's a generated file:
|
You may want to add it the result file to `.gitignore`, since it's a generated file:
|
||||||
|
|
||||||
```
|
```
|
||||||
jest-test-results.json
|
jest-test-results.json
|
||||||
```
|
```
|
||||||
|
|
||||||
But much like lockfiles and snapshots checking-in generated files can have certain advantages as well. It's up to you.
|
But much like lockfiles and snapshots checking-in generated files can have certain advantages as well. It's up to you.
|
||||||
We recommend to **do** check in the test results file so starting storybook from an clean git clone doesn't require running all tests first,
|
We recommend to **do** check in the test results file so starting storybook from an clean git clone doesn't require running all tests first,
|
||||||
but this can mean you'll experience merge conflicts on this file in the future. (*re-generating this file is super easy though, just like lockfiles and snapshots*)
|
but this can mean you'll experience merge conflicts on this file in the future. (_re-generating this file is super easy though, just like lockfiles and snapshots_)
|
||||||
|
|
||||||
## Generating the test results
|
## Generating the test results
|
||||||
|
|
||||||
You need to make sure the generated test-results file exists before you start storybook.
|
You need to make sure the generated test-restuls file exists before you start storybook.
|
||||||
During development you will likely start jest in watch-mode
|
During development you will likely start jest in watch-mode
|
||||||
and so the json file will be re-generated every time code or tests change.
|
and so the json file will be re-generated every time code or tests change.
|
||||||
|
|
||||||
@ -53,6 +55,7 @@ This change will then be HMR (hot module reloaded) using webpack and displayed b
|
|||||||
If you want to pre-run jest automaticly during development or a static build,
|
If you want to pre-run jest automaticly during development or a static build,
|
||||||
you may need to consider that if your tests fail, the script receives a non-0 exit code and will exit.
|
you may need to consider that if your tests fail, the script receives a non-0 exit code and will exit.
|
||||||
You could create a `prebuild:storybook` npm script, which will never fail by appending `|| true`:
|
You could create a `prebuild:storybook` npm script, which will never fail by appending `|| true`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test:generate-output": "jest --json --outputFile=.jest-test-results.json || true",
|
"test:generate-output": "jest --json --outputFile=.jest-test-results.json || true",
|
||||||
@ -83,40 +86,57 @@ import results from '../.jest-test-results.json';
|
|||||||
import { withTests } from '@storybook/addon-jest';
|
import { withTests } from '@storybook/addon-jest';
|
||||||
|
|
||||||
storiesOf('MyComponent', module)
|
storiesOf('MyComponent', module)
|
||||||
.addDecorator(withTests({ results })('MyComponent', 'MyOtherComponent'))
|
.addDecorator(withTests({ results }))
|
||||||
.add('This story shows test results from MyComponent.test.js and MyOtherComponent.test.js', () => (
|
.add(
|
||||||
<div>Jest results in storybook</div>
|
'This story shows test results from MyComponent.test.js and MyOtherComponent.test.js',
|
||||||
));
|
() => <div>Jest results in storybook</div>,
|
||||||
|
{
|
||||||
|
jest: ['MyComponent.test.js', 'MyOtherComponent.test.js'],
|
||||||
|
}
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in order to avoid importing `.jest-test-results.json` in each story, you can create a simple file `withTests.js`:
|
Or in order to avoid importing `.jest-test-results.json` in each story, simply add the decorator in your `.storybook/config.js` and results will display for stories that you have set the `jest` parameter on:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import results from '../.jest-test-results.json';
|
import { addDecorator } from '@storybook/react'; // <- or your view layer
|
||||||
import { withTests } from '@storybook/addon-jest';
|
import { withTests } from '@storybook/addon-jest';
|
||||||
|
|
||||||
export default withTests({
|
import results from '../.jest-test-results.json';
|
||||||
|
|
||||||
|
addDecorator(
|
||||||
|
withTests({
|
||||||
results,
|
results,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Then in your story:
|
Then in your story:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// import your file
|
|
||||||
import withTests from '.withTests';
|
|
||||||
|
|
||||||
storiesOf('MyComponent', module)
|
storiesOf('MyComponent', module)
|
||||||
.addDecorator(withTests('MyComponent', 'MyOtherComponent'))
|
// Use .addParameters if you want the same tests displayed for all stories of the component
|
||||||
.add('This story shows test results from MyComponent.test.js and MyOtherComponent.test.js', () => (
|
.addParameters({ jest: ['MyComponent', 'MyOtherComponent'] })
|
||||||
<div>Jest results in storybook</div>
|
.add(
|
||||||
));
|
'This story shows test results from MyComponent.test.js and MyOtherComponent.test.js',
|
||||||
|
() => <div>Jest results in storybook</div>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disabling
|
||||||
|
|
||||||
|
You can disable the addon for a single story by setting the `jest` parameter to `{disable: true}`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
storiesOf('MyComponent', module).add('Story', () => <div>Jest results disabled here</div>, {
|
||||||
|
jest: { disable: true },
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### withTests(options)
|
### withTests(options)
|
||||||
|
|
||||||
- **options.results**: OBJECT jest output results. *mandatory*
|
- **options.results**: OBJECT jest output results. _mandatory_
|
||||||
- **filesExt**: STRING test file extention. *optional*. This allow you to write "MyComponent" and not "MyComponent.test.js". It will be used as regex to find your file results. Default value is `((\\.specs?)|(\\.tests?))?(\\.js)?$`. That mean it will match: MyComponent.js, MyComponent.test.js, MyComponent.tests.js, MyComponent.spec.js, MyComponent.specs.js...
|
- **filesExt**: STRING test file extention. _optional_. This allow you to write "MyComponent" and not "MyComponent.test.js". It will be used as regex to find your file results. Default value is `((\\.specs?)|(\\.tests?))?(\\.js)?$`. That mean it will match: MyComponent.js, MyComponent.test.js, MyComponent.tests.js, MyComponent.spec.js, MyComponent.specs.js...
|
||||||
|
|
||||||
## Usage with Angular
|
## Usage with Angular
|
||||||
|
|
||||||
@ -124,7 +144,7 @@ Assuming that you have created a test files `my.component.spec.ts` and `my-other
|
|||||||
|
|
||||||
Configure Jest with [jest-preset-angular](https://www.npmjs.com/package/jest-preset-angular)
|
Configure Jest with [jest-preset-angular](https://www.npmjs.com/package/jest-preset-angular)
|
||||||
|
|
||||||
In project`s `typings.d.ts` add
|
In project`s`typings.d.ts` add
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
declare module '*.json' {
|
declare module '*.json' {
|
||||||
@ -133,29 +153,31 @@ declare module '*.json' {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Create a simple file `withTests.ts`:
|
In your `.storybook/config.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import * as results from '../.jest-test-results.json';
|
import { addDecorator } from '@storybook/angular';
|
||||||
import { withTests } from '@storybook/addon-jest';
|
import { withTests } from '@storybook/addon-jest';
|
||||||
|
|
||||||
export const wTests = withTests({
|
import * as results from '../.jest-test-results.json';
|
||||||
|
|
||||||
|
addDecorator(
|
||||||
|
withTests({
|
||||||
results,
|
results,
|
||||||
filesExt: '((\\.specs?)|(\\.tests?))?(\\.ts)?$'
|
filesExt: '((\\.specs?)|(\\.tests?))?(\\.ts)?$',
|
||||||
});
|
})
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Then in your story:
|
Then in your story:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// import your file
|
|
||||||
import wTests from '.withTests';
|
|
||||||
|
|
||||||
storiesOf('MyComponent', module)
|
storiesOf('MyComponent', module)
|
||||||
.addDecorator(wTests('my.component', 'my-other.component'))
|
.addParameters({ jest: ['my.component', 'my-other.component'] })
|
||||||
.add('This story shows test results from my.component.spec.ts and my-other.component.spec.ts', () => (
|
.add(
|
||||||
<div>Jest results in storybook</div>
|
'This story shows test results from my.component.spec.ts and my-other.component.spec.ts',
|
||||||
));
|
() => <div>Jest results in storybook</div>
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Example [here](https://github.com/storybooks/storybook/tree/master/examples/angular-cli)
|
##### Example [here](https://github.com/storybooks/storybook/tree/master/examples/angular-cli)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-jest",
|
"name": "@storybook/addon-jest",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "React storybook addon that show component jest report",
|
"description": "React storybook addon that show component jest report",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"addon",
|
"addon",
|
||||||
@ -25,13 +25,13 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
"react-emotion": "^9.1.3"
|
"react-emotion": "^9.1.3",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*"
|
"react": "*"
|
||||||
|
@ -2,8 +2,6 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
import { baseFonts } from '@storybook/components';
|
|
||||||
|
|
||||||
import Indicator from './Indicator';
|
import Indicator from './Indicator';
|
||||||
import Result, { FailedResult } from './Result';
|
import Result, { FailedResult } from './Result';
|
||||||
import provideJestResult from '../hoc/provideJestResult';
|
import provideJestResult from '../hoc/provideJestResult';
|
||||||
@ -25,7 +23,6 @@ const Item = styled('li')({
|
|||||||
const NoTests = styled('div')({
|
const NoTests = styled('div')({
|
||||||
padding: '10px 20px',
|
padding: '10px 20px',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
...baseFonts,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const FileTitle = styled('h2')({
|
const FileTitle = styled('h2')({
|
||||||
@ -139,7 +136,6 @@ const Content = styled(({ tests, className }) => (
|
|||||||
))({
|
))({
|
||||||
padding: '10px 20px',
|
padding: '10px 20px',
|
||||||
flex: '1 1 0%',
|
flex: '1 1 0%',
|
||||||
...baseFonts,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Panel = ({ tests }) =>
|
const Panel = ({ tests }) =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const provideTests = Component => {
|
const provideTests = Component =>
|
||||||
class TestProvider extends React.Component {
|
class TestProvider extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
channel: PropTypes.shape({
|
channel: PropTypes.shape({
|
||||||
@ -11,21 +11,22 @@ const provideTests = Component => {
|
|||||||
api: PropTypes.shape({
|
api: PropTypes.shape({
|
||||||
onStory: PropTypes.func,
|
onStory: PropTypes.func,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
active: PropTypes.bool,
|
||||||
|
};
|
||||||
|
static defaultProps = {
|
||||||
|
active: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
state = {};
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {};
|
|
||||||
this.onAddTests = this.onAddTests.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.stopListeningOnStory = this.props.api.onStory(() => {
|
const { channel, api } = this.props;
|
||||||
|
|
||||||
|
this.stopListeningOnStory = api.onStory(() => {
|
||||||
this.onAddTests({});
|
this.onAddTests({});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.channel.on('storybook/tests/add_tests', this.onAddTests);
|
channel.on('storybook/tests/add_tests', this.onAddTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -35,16 +36,14 @@ const provideTests = Component => {
|
|||||||
this.props.channel.removeListener('storybook/tests/add_tests', this.onAddTests);
|
this.props.channel.removeListener('storybook/tests/add_tests', this.onAddTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddTests({ kind, storyName, tests }) {
|
onAddTests = ({ kind, storyName, tests }) => {
|
||||||
this.setState({ kind, storyName, tests });
|
this.setState({ kind, storyName, tests });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Component {...this.state} />;
|
const { active } = this.props;
|
||||||
|
return active ? <Component {...this.state} /> : null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return TestProvider;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default provideTests;
|
export default provideTests;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import addons from '@storybook/addons';
|
import addons from '@storybook/addons';
|
||||||
|
import deprecate from 'util-deprecate';
|
||||||
|
|
||||||
const findTestResults = (testFiles, jestTestResults, jestTestFilesExt) =>
|
const findTestResults = (testFiles, jestTestResults, jestTestFilesExt) =>
|
||||||
testFiles.map(name => {
|
Array.from(testFiles).map(name => {
|
||||||
if (jestTestResults && jestTestResults.testResults) {
|
if (jestTestResults && jestTestResults.testResults) {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
@ -27,9 +28,27 @@ export const withTests = userOptions => {
|
|||||||
};
|
};
|
||||||
const options = Object.assign({}, defaultOptions, userOptions);
|
const options = Object.assign({}, defaultOptions, userOptions);
|
||||||
|
|
||||||
return (...testFiles) => (storyFn, { kind, story }) => {
|
return (...args) => {
|
||||||
emitAddTests({ kind, story, testFiles, options });
|
if (typeof args[0] === 'string') {
|
||||||
|
return deprecate((story, { kind }) => {
|
||||||
|
emitAddTests({ kind, story, testFiles: args, options });
|
||||||
|
|
||||||
return storyFn();
|
return story();
|
||||||
|
}, 'Passing component filenames to the `@storybook/addon-jest` via `withTests` is deprecated. Instead, use the `jest` story parameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
const [
|
||||||
|
story,
|
||||||
|
{
|
||||||
|
kind,
|
||||||
|
parameters: { jest: testFiles },
|
||||||
|
},
|
||||||
|
] = args;
|
||||||
|
|
||||||
|
if (testFiles && !testFiles.disable) {
|
||||||
|
emitAddTests({ kind, story, testFiles, options });
|
||||||
|
}
|
||||||
|
|
||||||
|
return story();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -4,11 +4,11 @@ import addons from '@storybook/addons';
|
|||||||
import PanelTitle from './components/PanelTitle';
|
import PanelTitle from './components/PanelTitle';
|
||||||
import Panel from './components/Panel';
|
import Panel from './components/Panel';
|
||||||
|
|
||||||
// Register the addon with a unique name.
|
|
||||||
addons.register('storybook/tests', api => {
|
addons.register('storybook/tests', api => {
|
||||||
// Also need to set a unique name to the panel.
|
const channel = addons.getChannel();
|
||||||
addons.addPanel('storybook/tests/panel', {
|
addons.addPanel('storybook/tests/panel', {
|
||||||
title: <PanelTitle channel={addons.getChannel()} api={api} />,
|
title: <PanelTitle channel={addons.getChannel()} api={api} />,
|
||||||
render: () => <Panel channel={addons.getChannel()} api={api} />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <Panel channel={channel} api={api} active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-knobs",
|
"name": "@storybook/addon-knobs",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook Addon Prop Editor Component",
|
"description": "Storybook Addon Prop Editor Component",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -13,22 +13,20 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"deep-equal": "^1.0.1",
|
"copy-to-clipboard": "^3.0.8",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
|
"fast-deep-equal": "^2.0.1",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"insert-css": "^2.0.0",
|
|
||||||
"lodash.debounce": "^4.0.8",
|
|
||||||
"moment": "^2.22.2",
|
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
|
"qs": "^6.5.2",
|
||||||
"react-color": "^2.14.1",
|
"react-color": "^2.14.1",
|
||||||
"react-datetime": "^2.14.0",
|
"react-datetime": "^2.14.0",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3",
|
||||||
"react-lifecycles-compat": "^3.0.4",
|
"react-lifecycles-compat": "^3.0.4",
|
||||||
"react-textarea-autosize": "^6.1.0",
|
|
||||||
"util-deprecate": "^1.0.2"
|
"util-deprecate": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* eslint no-underscore-dangle: 0 */
|
/* eslint no-underscore-dangle: 0 */
|
||||||
import deepEqual from 'deep-equal';
|
import deepEqual from 'fast-deep-equal';
|
||||||
import escape from 'escape-html';
|
import escape from 'escape-html';
|
||||||
|
|
||||||
import KnobStore from './KnobStore';
|
import KnobStore from './KnobStore';
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
const callArg = fn => fn();
|
||||||
|
const callAll = fns => fns.forEach(callArg);
|
||||||
|
|
||||||
export default class KnobStore {
|
export default class KnobStore {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.store = {};
|
this.store = {};
|
||||||
@ -12,7 +15,12 @@ export default class KnobStore {
|
|||||||
this.store[key] = value;
|
this.store[key] = value;
|
||||||
this.store[key].used = true;
|
this.store[key].used = true;
|
||||||
this.store[key].groupId = value.groupId;
|
this.store[key].groupId = value.groupId;
|
||||||
this.callbacks.forEach(cb => cb());
|
|
||||||
|
// debounce the execution of the callbacks for 50 milliseconds
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
}
|
||||||
|
this.timer = setTimeout(callAll, 50, this.callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(key) {
|
get(key) {
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
import { Placeholder } from '@storybook/components';
|
|
||||||
|
|
||||||
const Wrapper = styled('div')({
|
|
||||||
flex: '1 1 auto',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
background: 'white',
|
|
||||||
borderRadius: 4,
|
|
||||||
border: 'solid 1px rgb(236, 236, 236)',
|
|
||||||
width: '100%',
|
|
||||||
});
|
|
||||||
|
|
||||||
const Bar = styled('div')({
|
|
||||||
display: 'flex',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderBottom: 'solid 1px #eaeaea',
|
|
||||||
});
|
|
||||||
|
|
||||||
const Content = styled('div')({
|
|
||||||
flex: '1 1 0',
|
|
||||||
display: 'flex',
|
|
||||||
overflow: 'auto',
|
|
||||||
});
|
|
||||||
|
|
||||||
const Tab = styled('button')(({ active }) => ({
|
|
||||||
fontSize: 11,
|
|
||||||
letterSpacing: '1px',
|
|
||||||
padding: '10px 15px',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
transition: 'opacity 0.3s',
|
|
||||||
opacity: active ? 1 : 0.5,
|
|
||||||
maxHeight: 60,
|
|
||||||
overflow: 'hidden',
|
|
||||||
cursor: 'pointer',
|
|
||||||
background: 'transparent',
|
|
||||||
border: 'none',
|
|
||||||
}));
|
|
||||||
|
|
||||||
class GroupTabs extends Component {
|
|
||||||
renderTab(name, group) {
|
|
||||||
const onClick = e => {
|
|
||||||
e.preventDefault();
|
|
||||||
this.props.onGroupSelect(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
let { title } = group;
|
|
||||||
if (typeof title === 'function') {
|
|
||||||
title = title();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tab
|
|
||||||
type="button"
|
|
||||||
key={name}
|
|
||||||
onClick={onClick}
|
|
||||||
role="tab"
|
|
||||||
active={this.props.selectedGroup === name}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Tab>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const entries = this.props.groups ? Object.entries(this.props.groups) : null;
|
|
||||||
|
|
||||||
return entries && entries.length ? (
|
|
||||||
<Wrapper>
|
|
||||||
<Bar role="tablist">{entries.map(([key, value]) => this.renderTab(key, value))}</Bar>
|
|
||||||
<Content>
|
|
||||||
{entries.map(([key, value]) => {
|
|
||||||
const groupStyle = { display: 'none' };
|
|
||||||
if (key === this.props.selectedGroup) {
|
|
||||||
Object.assign(groupStyle, { flex: 1, display: 'flex' });
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div key={key} style={groupStyle}>
|
|
||||||
{value.render()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Content>
|
|
||||||
</Wrapper>
|
|
||||||
) : (
|
|
||||||
<Placeholder>no groups available</Placeholder>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupTabs.defaultProps = {
|
|
||||||
groups: {},
|
|
||||||
onGroupSelect: () => {},
|
|
||||||
selectedGroup: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
GroupTabs.propTypes = {
|
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
|
||||||
groups: PropTypes.object,
|
|
||||||
onGroupSelect: PropTypes.func,
|
|
||||||
selectedGroup: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GroupTabs;
|
|
@ -1,12 +1,14 @@
|
|||||||
import React from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import qs from 'qs';
|
||||||
|
import { document } from 'global';
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
import copy from 'copy-to-clipboard';
|
||||||
|
|
||||||
|
import { Placeholder, TabWrapper, TabsState, ActionBar, ActionButton } from '@storybook/components';
|
||||||
|
|
||||||
import { Placeholder } from '@storybook/components';
|
|
||||||
import GroupTabs from './GroupTabs';
|
|
||||||
import PropForm from './PropForm';
|
|
||||||
import Types from './types';
|
import Types from './types';
|
||||||
|
import PropForm from './PropForm';
|
||||||
|
|
||||||
const getTimestamp = () => +new Date();
|
const getTimestamp = () => +new Date();
|
||||||
|
|
||||||
@ -16,49 +18,21 @@ const PanelWrapper = styled('div')({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
});
|
});
|
||||||
|
|
||||||
const PanelInner = styled('div')({
|
export default class Panel extends PureComponent {
|
||||||
padding: '5px',
|
|
||||||
width: 'auto',
|
|
||||||
position: 'relative',
|
|
||||||
});
|
|
||||||
|
|
||||||
const ResetButton = styled('button')({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 11,
|
|
||||||
right: 10,
|
|
||||||
border: 'none',
|
|
||||||
borderTop: 'solid 1px rgba(0, 0, 0, 0.2)',
|
|
||||||
borderLeft: 'solid 1px rgba(0, 0, 0, 0.2)',
|
|
||||||
background: 'rgba(255, 255, 255, 0.5)',
|
|
||||||
padding: '5px 10px',
|
|
||||||
borderRadius: '4px 0 0 0',
|
|
||||||
color: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
outline: 'none',
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class Panel extends React.Component {
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.state = { knobs: {} };
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
this.setKnobs = this.setKnobs.bind(this);
|
|
||||||
this.reset = this.reset.bind(this);
|
|
||||||
this.setOptions = this.setOptions.bind(this);
|
|
||||||
this.onGroupSelect = this.onGroupSelect.bind(this);
|
|
||||||
|
|
||||||
this.state = { knobs: {}, groupId: DEFAULT_GROUP_ID };
|
|
||||||
this.options = {};
|
this.options = {};
|
||||||
|
|
||||||
this.lastEdit = getTimestamp();
|
this.lastEdit = getTimestamp();
|
||||||
this.loadedFromUrl = false;
|
this.loadedFromUrl = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.channel.on('addon:knobs:setKnobs', this.setKnobs);
|
this.props.channel.on('addon:knobs:setKnobs', this.setKnobs);
|
||||||
this.props.channel.on('addon:knobs:setOptions', this.setOptions);
|
this.props.channel.on('addon:knobs:setOptions', this.setOptions);
|
||||||
|
|
||||||
this.stopListeningOnStory = this.props.api.onStory(() => {
|
this.stopListeningOnStory = this.props.api.onStory(() => {
|
||||||
this.setState({ knobs: [], groupId: DEFAULT_GROUP_ID });
|
this.setState({ knobs: {} });
|
||||||
this.props.channel.emit('addon:knobs:reset');
|
this.props.channel.emit('addon:knobs:reset');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -68,15 +42,11 @@ export default class Panel extends React.Component {
|
|||||||
this.stopListeningOnStory();
|
this.stopListeningOnStory();
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupSelect(name) {
|
setOptions = (options = { timestamps: false }) => {
|
||||||
this.setState({ groupId: name });
|
|
||||||
}
|
|
||||||
|
|
||||||
setOptions(options = { timestamps: false }) {
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
};
|
||||||
|
|
||||||
setKnobs({ knobs, timestamp }) {
|
setKnobs = ({ knobs, timestamp }) => {
|
||||||
const queryParams = {};
|
const queryParams = {};
|
||||||
const { api, channel } = this.props;
|
const { api, channel } = this.props;
|
||||||
|
|
||||||
@ -86,7 +56,6 @@ export default class Panel extends React.Component {
|
|||||||
// For the first time, get values from the URL and set them.
|
// For the first time, get values from the URL and set them.
|
||||||
if (!this.loadedFromUrl) {
|
if (!this.loadedFromUrl) {
|
||||||
const urlValue = api.getQueryParam(`knob-${name}`);
|
const urlValue = api.getQueryParam(`knob-${name}`);
|
||||||
|
|
||||||
if (urlValue !== undefined) {
|
if (urlValue !== undefined) {
|
||||||
// If the knob value present in url
|
// If the knob value present in url
|
||||||
knob.value = Types[knob.type].deserialize(urlValue);
|
knob.value = Types[knob.type].deserialize(urlValue);
|
||||||
@ -94,48 +63,63 @@ export default class Panel extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queryParams[`knob-${name}`] = Types[knob.type].serialize(knob.value);
|
// set all knobsquery params to be deleted from URL
|
||||||
|
queryParams[`knob-${name}`] = null;
|
||||||
});
|
});
|
||||||
this.loadedFromUrl = true;
|
|
||||||
api.setQueryParams(queryParams);
|
api.setQueryParams(queryParams);
|
||||||
this.setState({ knobs });
|
this.setState({ knobs });
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
this.loadedFromUrl = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reset = () => {
|
||||||
this.props.channel.emit('addon:knobs:reset');
|
this.props.channel.emit('addon:knobs:reset');
|
||||||
}
|
};
|
||||||
|
|
||||||
emitChange(changedKnob) {
|
copy = () => {
|
||||||
this.props.channel.emit('addon:knobs:knobChange', changedKnob);
|
const { location } = document;
|
||||||
}
|
const query = qs.parse(location.search.replace('?', ''));
|
||||||
|
|
||||||
handleChange(changedKnob) {
|
|
||||||
this.lastEdit = getTimestamp();
|
|
||||||
const { api } = this.props;
|
|
||||||
const { knobs } = this.state;
|
const { knobs } = this.state;
|
||||||
const { name, type, value } = changedKnob;
|
|
||||||
|
Object.entries(knobs).forEach(([name, knob]) => {
|
||||||
|
query[`knob-${name}`] = Types[knob.type].serialize(knob.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
copy(`${location.origin + location.pathname}?${qs.stringify(query)}`);
|
||||||
|
|
||||||
|
// TODO: show some notification of this
|
||||||
|
};
|
||||||
|
|
||||||
|
emitChange = changedKnob => {
|
||||||
|
this.props.channel.emit('addon:knobs:knobChange', changedKnob);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = changedKnob => {
|
||||||
|
this.lastEdit = getTimestamp();
|
||||||
|
const { knobs } = this.state;
|
||||||
|
const { name } = changedKnob;
|
||||||
const newKnobs = { ...knobs };
|
const newKnobs = { ...knobs };
|
||||||
newKnobs[name] = {
|
newKnobs[name] = {
|
||||||
...newKnobs[name],
|
...newKnobs[name],
|
||||||
...changedKnob,
|
...changedKnob,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState({ knobs: newKnobs });
|
|
||||||
|
|
||||||
const queryParams = {};
|
|
||||||
queryParams[`knob-${name}`] = Types[type].serialize(value);
|
|
||||||
|
|
||||||
api.setQueryParams(queryParams);
|
|
||||||
this.setState({ knobs: newKnobs }, this.emitChange(changedKnob));
|
this.setState({ knobs: newKnobs }, this.emitChange(changedKnob));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleClick(knob) {
|
handleClick = knob => {
|
||||||
this.props.channel.emit('addon:knobs:knobClick', knob);
|
this.props.channel.emit('addon:knobs:knobClick', knob);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knobs, groupId } = this.state;
|
const { knobs } = this.state;
|
||||||
|
const { active } = this.props;
|
||||||
|
|
||||||
|
if (!active) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const groups = {};
|
const groups = {};
|
||||||
const groupIds = [];
|
const groupIds = [];
|
||||||
@ -146,20 +130,23 @@ export default class Panel extends React.Component {
|
|||||||
const knobKeyGroupId = knobs[key].groupId;
|
const knobKeyGroupId = knobs[key].groupId;
|
||||||
groupIds.push(knobKeyGroupId);
|
groupIds.push(knobKeyGroupId);
|
||||||
groups[knobKeyGroupId] = {
|
groups[knobKeyGroupId] = {
|
||||||
render: () => <div id={knobKeyGroupId}>{knobKeyGroupId}</div>,
|
render: ({ active: groupActive, selected }) => (
|
||||||
|
<TabWrapper active={groupActive || selected === DEFAULT_GROUP_ID}>
|
||||||
|
<PropForm
|
||||||
|
knobs={knobsArray.filter(knob => knob.groupId === knobKeyGroupId)}
|
||||||
|
onFieldChange={this.handleChange}
|
||||||
|
onFieldClick={this.handleClick}
|
||||||
|
/>
|
||||||
|
</TabWrapper>
|
||||||
|
),
|
||||||
title: knobKeyGroupId,
|
title: knobKeyGroupId,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (groupIds.length > 0) {
|
|
||||||
groups[DEFAULT_GROUP_ID] = {
|
groups[DEFAULT_GROUP_ID] = {
|
||||||
render: () => <div id={DEFAULT_GROUP_ID}>{DEFAULT_GROUP_ID}</div>,
|
render: () => null,
|
||||||
title: DEFAULT_GROUP_ID,
|
title: DEFAULT_GROUP_ID,
|
||||||
};
|
};
|
||||||
if (groupId !== DEFAULT_GROUP_ID) {
|
|
||||||
knobsArray = knobsArray.filter(key => knobs[key].groupId === groupId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
knobsArray = knobsArray.map(key => knobs[key]);
|
knobsArray = knobsArray.map(key => knobs[key]);
|
||||||
|
|
||||||
@ -169,33 +156,38 @@ export default class Panel extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelWrapper>
|
<PanelWrapper>
|
||||||
{groupIds.length > 0 && (
|
{groupIds.length > 0 ? (
|
||||||
<GroupTabs
|
<TabsState>
|
||||||
groups={groups}
|
{Object.entries(groups).map(([k, v]) => (
|
||||||
onGroupSelect={this.onGroupSelect}
|
<div id={k} title={v.title}>
|
||||||
selectedGroup={this.state.groupId}
|
{v.render}
|
||||||
/>
|
</div>
|
||||||
)}
|
))}
|
||||||
<PanelInner>
|
</TabsState>
|
||||||
|
) : (
|
||||||
<PropForm
|
<PropForm
|
||||||
knobs={knobsArray}
|
knobs={knobsArray}
|
||||||
onFieldChange={this.handleChange}
|
onFieldChange={this.handleChange}
|
||||||
onFieldClick={this.handleClick}
|
onFieldClick={this.handleClick}
|
||||||
/>
|
/>
|
||||||
</PanelInner>
|
)}
|
||||||
<ResetButton onClick={this.reset}>RESET</ResetButton>
|
<ActionBar>
|
||||||
|
<ActionButton onClick={this.copy}>COPY</ActionButton>
|
||||||
|
<ActionButton onClick={this.reset}>RESET</ActionButton>
|
||||||
|
</ActionBar>
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Panel.propTypes = {
|
Panel.propTypes = {
|
||||||
|
active: PropTypes.bool.isRequired,
|
||||||
|
onReset: PropTypes.object, // eslint-disable-line
|
||||||
channel: PropTypes.shape({
|
channel: PropTypes.shape({
|
||||||
emit: PropTypes.func,
|
emit: PropTypes.func,
|
||||||
on: PropTypes.func,
|
on: PropTypes.func,
|
||||||
removeListener: PropTypes.func,
|
removeListener: PropTypes.func,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
onReset: PropTypes.object, // eslint-disable-line
|
|
||||||
api: PropTypes.shape({
|
api: PropTypes.shape({
|
||||||
onStory: PropTypes.func,
|
onStory: PropTypes.func,
|
||||||
getQueryParam: PropTypes.func,
|
getQueryParam: PropTypes.func,
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
import TypeMap from './types';
|
|
||||||
|
|
||||||
const InvalidType = () => <span>Invalid Type</span>;
|
|
||||||
|
|
||||||
const Field = styled('div')({
|
|
||||||
display: 'table-row',
|
|
||||||
padding: '5px',
|
|
||||||
});
|
|
||||||
const Label = styled('label')({
|
|
||||||
display: 'table-cell',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
verticalAlign: 'top',
|
|
||||||
paddingRight: 5,
|
|
||||||
paddingTop: 5,
|
|
||||||
textAlign: 'right',
|
|
||||||
width: 80,
|
|
||||||
fontSize: 12,
|
|
||||||
color: 'rgb(68, 68, 68)',
|
|
||||||
fontWeight: 600,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function PropField({ onChange, onClick, knob }) {
|
|
||||||
const InputType = TypeMap[knob.type] || InvalidType;
|
|
||||||
return (
|
|
||||||
<Field>
|
|
||||||
<Label htmlFor={knob.name}>{!knob.hideLabel && `${knob.name}`}</Label>
|
|
||||||
<InputType knob={knob} onChange={onChange} onClick={onClick} />
|
|
||||||
</Field>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropField.propTypes = {
|
|
||||||
knob: PropTypes.shape({
|
|
||||||
name: PropTypes.string,
|
|
||||||
value: PropTypes.any,
|
|
||||||
}).isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
@ -1,19 +1,19 @@
|
|||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
import PropField from './PropField';
|
import { Field } from '@storybook/components';
|
||||||
|
import TypeMap from './types';
|
||||||
|
|
||||||
const Form = styled('form')({
|
const Form = styled('form')({
|
||||||
display: 'table',
|
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderCollapse: 'separate',
|
|
||||||
borderSpacing: '5px',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class propForm extends React.Component {
|
const InvalidType = () => <span>Invalid Type</span>;
|
||||||
|
|
||||||
|
export default class PropForm extends Component {
|
||||||
makeChangeHandler(name, type) {
|
makeChangeHandler(name, type) {
|
||||||
return value => {
|
return value => {
|
||||||
const change = { name, type, value };
|
const change = { name, type, value };
|
||||||
@ -28,16 +28,12 @@ export default class propForm extends React.Component {
|
|||||||
<Form>
|
<Form>
|
||||||
{knobs.map(knob => {
|
{knobs.map(knob => {
|
||||||
const changeHandler = this.makeChangeHandler(knob.name, knob.type);
|
const changeHandler = this.makeChangeHandler(knob.name, knob.type);
|
||||||
|
const InputType = TypeMap[knob.type] || InvalidType;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PropField
|
<Field key={knob.name} label={!knob.hideLabel && `${knob.name}`}>
|
||||||
key={knob.name}
|
<InputType knob={knob} onChange={changeHandler} onClick={this.props.onFieldClick} />
|
||||||
name={knob.name}
|
</Field>
|
||||||
type={knob.type}
|
|
||||||
value={knob.value}
|
|
||||||
knob={knob}
|
|
||||||
onChange={changeHandler}
|
|
||||||
onClick={this.props.onFieldClick}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Form>
|
</Form>
|
||||||
@ -45,19 +41,15 @@ export default class propForm extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
propForm.displayName = 'propForm';
|
PropForm.displayName = 'PropForm';
|
||||||
|
|
||||||
propForm.defaultProps = {
|
PropForm.propTypes = {
|
||||||
knobs: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
propForm.propTypes = {
|
|
||||||
knobs: PropTypes.arrayOf(
|
knobs: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
})
|
})
|
||||||
),
|
).isRequired,
|
||||||
onFieldChange: PropTypes.func.isRequired,
|
onFieldChange: PropTypes.func.isRequired,
|
||||||
onFieldClick: PropTypes.func.isRequired,
|
onFieldClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import GroupTabs from '../GroupTabs';
|
|
||||||
|
|
||||||
describe('GroupTabs', () => {
|
|
||||||
test('should render only the selected group with display set other than "none"', () => {
|
|
||||||
const groups = {
|
|
||||||
test1: {
|
|
||||||
render() {
|
|
||||||
return <div id="test1">TEST 1</div>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
test2: {
|
|
||||||
render() {
|
|
||||||
return <div id="test2">TEST 2</div>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const onGroupSelect = () => 'onGroupSelect';
|
|
||||||
|
|
||||||
const wrapper = shallow(
|
|
||||||
<GroupTabs groups={groups} onGroupSelect={onGroupSelect} selectedGroup="test2" />
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.find('#test1').parent()).toHaveStyle('display', 'none');
|
|
||||||
expect(wrapper.find('#test2').parent()).not.toHaveStyle('display', 'none');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should set onGroupSelected as onClick handlers of tabs', () => {
|
|
||||||
const groups = {
|
|
||||||
test1: {
|
|
||||||
title: 'test 1',
|
|
||||||
render() {
|
|
||||||
return <div>TEST 1</div>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const onGroupSelect = jest.fn();
|
|
||||||
const preventDefault = jest.fn();
|
|
||||||
const wrapper = shallow(
|
|
||||||
<GroupTabs groups={groups} onGroupSelect={onGroupSelect} selectedGroup="test1" />
|
|
||||||
);
|
|
||||||
wrapper
|
|
||||||
.find('Styled(button)')
|
|
||||||
.dive()
|
|
||||||
.simulate('click', { preventDefault });
|
|
||||||
|
|
||||||
expect(onGroupSelect).toHaveBeenCalled();
|
|
||||||
expect(preventDefault).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when no groups are given', () => {
|
|
||||||
test('should render "no groups available"', () => {
|
|
||||||
const groups = {};
|
|
||||||
const onGroupSelect = () => 'onGroupSelect';
|
|
||||||
const wrapper = shallow(<GroupTabs groups={groups} onGroupSelect={onGroupSelect} />);
|
|
||||||
|
|
||||||
expect(wrapper.contains('no groups available')).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -6,14 +6,14 @@ describe('Panel', () => {
|
|||||||
it('should subscribe to setKnobs event of channel', () => {
|
it('should subscribe to setKnobs event of channel', () => {
|
||||||
const testChannel = { on: jest.fn() };
|
const testChannel = { on: jest.fn() };
|
||||||
const testApi = { onStory: jest.fn() };
|
const testApi = { onStory: jest.fn() };
|
||||||
shallow(<Panel channel={testChannel} api={testApi} />);
|
shallow(<Panel channel={testChannel} api={testApi} active />);
|
||||||
expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function));
|
expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should subscribe to onStory event', () => {
|
it('should subscribe to onStory event', () => {
|
||||||
const testChannel = { on: jest.fn() };
|
const testChannel = { on: jest.fn() };
|
||||||
const testApi = { onStory: jest.fn() };
|
const testApi = { onStory: jest.fn() };
|
||||||
shallow(<Panel channel={testChannel} api={testApi} />);
|
shallow(<Panel channel={testChannel} api={testApi} active />);
|
||||||
|
|
||||||
expect(testApi.onStory).toHaveBeenCalled();
|
expect(testApi.onStory).toHaveBeenCalled();
|
||||||
expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function));
|
expect(testChannel.on).toHaveBeenCalledWith('addon:knobs:setKnobs', jasmine.any(Function));
|
||||||
@ -41,7 +41,7 @@ describe('Panel', () => {
|
|||||||
onStory: jest.fn(),
|
onStory: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
shallow(<Panel channel={testChannel} api={testApi} />);
|
shallow(<Panel channel={testChannel} api={testApi} active />);
|
||||||
const setKnobsHandler = handlers['addon:knobs:setKnobs'];
|
const setKnobsHandler = handlers['addon:knobs:setKnobs'];
|
||||||
|
|
||||||
const knobs = {
|
const knobs = {
|
||||||
@ -67,7 +67,7 @@ describe('Panel', () => {
|
|||||||
expect(testChannel.emit).toHaveBeenCalledWith(e, knobFromUrl);
|
expect(testChannel.emit).toHaveBeenCalledWith(e, knobFromUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set query params when url params are already read', () => {
|
it('should remove query params when url params are already read', () => {
|
||||||
const handlers = {};
|
const handlers = {};
|
||||||
|
|
||||||
const testChannel = {
|
const testChannel = {
|
||||||
@ -88,7 +88,7 @@ describe('Panel', () => {
|
|||||||
onStory: jest.fn(),
|
onStory: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<Panel channel={testChannel} api={testApi} />);
|
const wrapper = shallow(<Panel channel={testChannel} api={testApi} active />);
|
||||||
const setKnobsHandler = handlers['addon:knobs:setKnobs'];
|
const setKnobsHandler = handlers['addon:knobs:setKnobs'];
|
||||||
|
|
||||||
const knobs = {
|
const knobs = {
|
||||||
@ -109,8 +109,8 @@ describe('Panel', () => {
|
|||||||
|
|
||||||
setKnobsHandler({ knobs, timestamp: +new Date() });
|
setKnobsHandler({ knobs, timestamp: +new Date() });
|
||||||
const knobFromStory = {
|
const knobFromStory = {
|
||||||
'knob-foo': knobs.foo.value,
|
'knob-foo': null,
|
||||||
'knob-baz': knobs.baz.value,
|
'knob-baz': null,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(testApi.setQueryParams).toHaveBeenCalledWith(knobFromStory);
|
expect(testApi.setQueryParams).toHaveBeenCalledWith(knobFromStory);
|
||||||
@ -130,7 +130,7 @@ describe('Panel', () => {
|
|||||||
onStory: jest.fn(),
|
onStory: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<Panel channel={testChannel} api={testApi} />);
|
const wrapper = shallow(<Panel channel={testChannel} api={testApi} active />);
|
||||||
|
|
||||||
const testChangedKnob = {
|
const testChangedKnob = {
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
@ -140,8 +140,8 @@ describe('Panel', () => {
|
|||||||
wrapper.instance().handleChange(testChangedKnob);
|
wrapper.instance().handleChange(testChangedKnob);
|
||||||
expect(testChannel.emit).toHaveBeenCalledWith('addon:knobs:knobChange', testChangedKnob);
|
expect(testChannel.emit).toHaveBeenCalledWith('addon:knobs:knobChange', testChangedKnob);
|
||||||
|
|
||||||
const paramsChange = { 'knob-foo': 'changed text' };
|
// const paramsChange = { 'knob-foo': 'changed text' };
|
||||||
expect(testApi.setQueryParams).toHaveBeenCalledWith(paramsChange);
|
// expect(testApi.setQueryParams).toHaveBeenCalledWith(paramsChange);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,24 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
import Textarea from 'react-textarea-autosize';
|
import { Textarea } from '@storybook/components';
|
||||||
import debounce from 'lodash.debounce';
|
|
||||||
|
|
||||||
const StyledTextarea = styled(Textarea)({
|
|
||||||
display: 'table-cell',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
height: '26px',
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '100%',
|
|
||||||
outline: 'none',
|
|
||||||
border: '1px solid #f7f4f4',
|
|
||||||
borderRadius: 2,
|
|
||||||
fontSize: 11,
|
|
||||||
padding: '5px',
|
|
||||||
color: '#555',
|
|
||||||
});
|
|
||||||
|
|
||||||
function formatArray(value, separator) {
|
function formatArray(value, separator) {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
@ -28,34 +11,21 @@ function formatArray(value, separator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ArrayType extends React.Component {
|
class ArrayType extends React.Component {
|
||||||
constructor(props, context) {
|
shouldComponentUpdate(nextProps) {
|
||||||
super(props, context);
|
return nextProps.knob.value !== this.props.knob.value;
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: props.knob.value.join(props.knob.separator),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onChange = debounce(this.props.onChange, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.onChange.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = e => {
|
handleChange = e => {
|
||||||
const { knob } = this.props;
|
const { knob } = this.props;
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
const newVal = formatArray(value, knob.separator);
|
const newVal = formatArray(value, knob.separator);
|
||||||
|
this.props.onChange(newVal);
|
||||||
this.setState({ value });
|
|
||||||
this.onChange(newVal);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knob } = this.props;
|
const { knob } = this.props;
|
||||||
const { value } = this.state;
|
|
||||||
|
|
||||||
return <StyledTextarea id={knob.name} value={value} onChange={this.handleChange} />;
|
return <Textarea id={knob.name} value={knob.value} onChange={this.handleChange} size="flex" />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,18 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import { Button } from '@storybook/components';
|
||||||
|
|
||||||
const Button = styled('button')({
|
|
||||||
height: '26px',
|
|
||||||
});
|
|
||||||
|
|
||||||
const ButtonType = ({ knob, onClick }) => (
|
const ButtonType = ({ knob, onClick }) => (
|
||||||
<Button type="button" id={knob.name} onClick={() => onClick(knob)}>
|
<Button type="button" onClick={() => onClick(knob)}>
|
||||||
{knob.name}
|
{knob.name}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
ButtonType.defaultProps = {
|
|
||||||
knob: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
ButtonType.propTypes = {
|
ButtonType.propTypes = {
|
||||||
knob: PropTypes.shape({
|
knob: PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
}),
|
}).isRequired,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,22 +4,15 @@ import React from 'react';
|
|||||||
import { SketchPicker } from 'react-color';
|
import { SketchPicker } from 'react-color';
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
import debounce from 'lodash.debounce';
|
|
||||||
|
|
||||||
const SwatchButton = styled('button')({
|
import { Button } from '@storybook/components';
|
||||||
background: '#fff',
|
|
||||||
borderRadius: '1px',
|
|
||||||
border: '1px solid rgb(247, 244, 244)',
|
|
||||||
display: 'inline-block',
|
|
||||||
cursor: 'pointer',
|
|
||||||
width: '100%',
|
|
||||||
padding: 0,
|
|
||||||
});
|
|
||||||
const Swatch = styled('div')({
|
const Swatch = styled('div')({
|
||||||
width: 'auto',
|
position: 'absolute',
|
||||||
height: '20px',
|
top: 0,
|
||||||
borderRadius: '2px',
|
bottom: 0,
|
||||||
margin: 5,
|
right: 3,
|
||||||
|
width: 28,
|
||||||
});
|
});
|
||||||
const Popover = styled('div')({
|
const Popover = styled('div')({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -27,23 +20,18 @@ const Popover = styled('div')({
|
|||||||
});
|
});
|
||||||
|
|
||||||
class ColorType extends React.Component {
|
class ColorType extends React.Component {
|
||||||
constructor(props, context) {
|
state = {
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
displayColorPicker: false,
|
displayColorPicker: false,
|
||||||
value: props.knob.value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onChange = debounce(props.onChange, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.addEventListener('mousedown', this.handleWindowMouseDown);
|
document.addEventListener('mousedown', this.handleWindowMouseDown);
|
||||||
}
|
}
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return nextProps.knob.value !== this.props.knob.value;
|
||||||
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener('mousedown', this.handleWindowMouseDown);
|
document.removeEventListener('mousedown', this.handleWindowMouseDown);
|
||||||
this.onChange.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowMouseDown = e => {
|
handleWindowMouseDown = e => {
|
||||||
@ -62,35 +50,30 @@ class ColorType extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleChange = color => {
|
handleChange = color => {
|
||||||
this.setState({
|
this.props.onChange(`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`);
|
||||||
value: color,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.onChange(`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knob } = this.props;
|
const { knob } = this.props;
|
||||||
const { displayColorPicker, value } = this.state;
|
const { displayColorPicker } = this.state;
|
||||||
const colorStyle = {
|
const colorStyle = {
|
||||||
background: knob.value,
|
background: knob.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={knob.name}>
|
<Button type="button" onClick={this.handleClick} size="flex">
|
||||||
<SwatchButton type="button" onClick={this.handleClick}>
|
{knob.value}
|
||||||
<Swatch style={colorStyle} />
|
<Swatch style={colorStyle} />
|
||||||
</SwatchButton>
|
|
||||||
{displayColorPicker ? (
|
{displayColorPicker ? (
|
||||||
<Popover
|
<Popover
|
||||||
innerRef={e => {
|
innerRef={e => {
|
||||||
this.popover = e;
|
this.popover = e;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SketchPicker color={value} onChange={this.handleChange} />
|
<SketchPicker color={knob.value} onChange={this.handleChange} />
|
||||||
</Popover>
|
</Popover>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,43 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
import Datetime from 'react-datetime';
|
import Datetime from 'react-datetime';
|
||||||
import insertCss from 'insert-css';
|
|
||||||
import debounce from 'lodash.debounce';
|
|
||||||
import style from './styles';
|
import style from './styles';
|
||||||
|
|
||||||
const customStyle = `
|
const DateInput = styled(Datetime)(style);
|
||||||
.rdt input {
|
|
||||||
outline: 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #f7f4f4;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 5px;
|
|
||||||
color: #555;
|
|
||||||
display: table-cell;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
insertCss(style);
|
|
||||||
insertCss(customStyle);
|
|
||||||
|
|
||||||
class DateType extends React.Component {
|
class DateType extends React.Component {
|
||||||
constructor(props, context) {
|
shouldComponentUpdate(nextProps) {
|
||||||
super(props, context);
|
return nextProps.knob.value !== this.props.knob.value;
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: props.knob.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onChange = debounce(props.onChange, 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = date => {
|
handleChange = date => {
|
||||||
const value = date.valueOf();
|
const value = date.valueOf();
|
||||||
this.setState({ value });
|
this.props.onChange(value);
|
||||||
|
|
||||||
this.onChange(value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knob } = this.props;
|
const { knob } = this.props;
|
||||||
const { value } = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<DateInput
|
||||||
<Datetime
|
value={knob.value ? new Date(knob.value) : null}
|
||||||
id={knob.name}
|
|
||||||
value={value ? new Date(value) : null}
|
|
||||||
type="date"
|
type="date"
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
|
size="flex"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DateType.defaultProps = {
|
|
||||||
knob: {},
|
|
||||||
onChange: value => value,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateType.propTypes = {
|
DateType.propTypes = {
|
||||||
knob: PropTypes.shape({
|
knob: PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
value: PropTypes.number,
|
value: PropTypes.number,
|
||||||
}),
|
}).isRequired,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
DateType.serialize = value => String(value);
|
DateType.serialize = value => String(value);
|
||||||
|
@ -1,219 +1,202 @@
|
|||||||
export default `
|
import { Input } from '@storybook/components';
|
||||||
.rdt {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.rdtPicker {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
width: 200px;
|
|
||||||
padding: 4px;
|
|
||||||
margin-top: 1px;
|
|
||||||
z-index: 99999 !important;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,.1);
|
|
||||||
border: 1px solid #f9f9f9;
|
|
||||||
}
|
|
||||||
.rdtOpen .rdtPicker {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.rdtStatic .rdtPicker {
|
|
||||||
box-shadow: none;
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rdtPicker .rdtTimeToggle {
|
export default ({ theme, size }) => ({
|
||||||
text-align: center;
|
...Input.sizes({ size, theme }),
|
||||||
font-size:11px;
|
'&.rdt': {
|
||||||
}
|
position: 'relative',
|
||||||
|
},
|
||||||
|
'& > input': {
|
||||||
|
width: '100%',
|
||||||
|
height: 32,
|
||||||
|
...Input.styles({ theme }),
|
||||||
|
...Input.alignment({ theme }),
|
||||||
|
},
|
||||||
|
'& .rdtPicker': {
|
||||||
|
display: 'none',
|
||||||
|
position: 'absolute',
|
||||||
|
width: 200,
|
||||||
|
padding: 4,
|
||||||
|
marginTop: 1,
|
||||||
|
zIndex: 99999,
|
||||||
|
background: theme.barFill,
|
||||||
|
},
|
||||||
|
'&.rdtOpen .rdtPicker': {
|
||||||
|
display: 'block',
|
||||||
|
},
|
||||||
|
'&.rdt .rdtPicker': {
|
||||||
|
boxShadow: 'none',
|
||||||
|
position: 'static',
|
||||||
|
},
|
||||||
|
|
||||||
.rdtPicker table {
|
'& .rdtPicker .rdtTimeToggle': {
|
||||||
width: 100%;
|
textAlign: 'center',
|
||||||
margin: 0;
|
fontSize: 11,
|
||||||
}
|
},
|
||||||
.rdtPicker td,
|
|
||||||
.rdtPicker th {
|
|
||||||
text-align: center;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
.rdtPicker td {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtDay:hover,
|
|
||||||
.rdtPicker td.rdtHour:hover,
|
|
||||||
.rdtPicker td.rdtMinute:hover,
|
|
||||||
.rdtPicker td.rdtSecond:hover,
|
|
||||||
.rdtPicker .rdtTimeToggle:hover {
|
|
||||||
background: #eeeeee;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtOld,
|
|
||||||
.rdtPicker td.rdtNew {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtToday {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtToday:before {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
border-left: 7px solid transparent;
|
|
||||||
border-bottom: 7px solid #428bca;
|
|
||||||
border-top-color: rgba(0, 0, 0, 0.2);
|
|
||||||
position: absolute;
|
|
||||||
bottom: 4px;
|
|
||||||
right: 4px;
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtActive,
|
|
||||||
.rdtPicker td.rdtActive:hover {
|
|
||||||
background-color: #428bca;
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtActive.rdtToday:before {
|
|
||||||
border-bottom-color: #fff;
|
|
||||||
}
|
|
||||||
.rdtPicker td.rdtDisabled,
|
|
||||||
.rdtPicker td.rdtDisabled:hover {
|
|
||||||
background: none;
|
|
||||||
color: #999999;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rdtPicker td span.rdtOld {
|
'& .rdtPicker table': {
|
||||||
color: #999999;
|
width: '100%',
|
||||||
}
|
margin: 0,
|
||||||
.rdtPicker td span.rdtDisabled,
|
},
|
||||||
.rdtPicker td span.rdtDisabled:hover {
|
'& .rdtPicker td, & .rdtPicker th': {
|
||||||
background: none;
|
textAlign: 'center',
|
||||||
color: #999999;
|
height: 32,
|
||||||
cursor: not-allowed;
|
boxSizing: 'border-box',
|
||||||
}
|
},
|
||||||
.rdtPicker th {
|
'& .rdtPicker td': {
|
||||||
border-bottom: 1px solid #f9f9f9;
|
cursor: 'pointer',
|
||||||
}
|
},
|
||||||
.rdtPicker .dow {
|
'& .rdtPicker td.rdtDay:hover, & .rdtPicker td.rdtHour:hover, & .rdtPicker td.rdtMinute:hover, & .rdtPicker td.rdtSecond:hover, & .rdtPicker .rdtTimeToggle:hover': {
|
||||||
width: 14.2857%;
|
color: theme.highlightColor,
|
||||||
font-size: 11px;
|
textDecoration: 'underline',
|
||||||
border-bottom: none;
|
cursor: 'pointer',
|
||||||
}
|
},
|
||||||
.rdtPicker th.rdtSwitch {
|
'& .rdtPicker td.rdtOld, & .rdtPicker td.rdtNew': {
|
||||||
width: 100px;
|
color: '#999999',
|
||||||
font-size: 11px;
|
},
|
||||||
}
|
'& .rdtPicker td.rdtToday': {
|
||||||
.rdtPicker th.rdtNext,
|
position: 'relative',
|
||||||
.rdtPicker th.rdtPrev {
|
},
|
||||||
font-size: 11px;
|
'& .rdtPicker td.rdtToday:before': {
|
||||||
vertical-align: top;
|
content: '""',
|
||||||
}
|
display: 'inline-block',
|
||||||
|
borderLeft: '7px solid transparent',
|
||||||
|
borderBottom: `7px solid ${theme.highlightColor}`,
|
||||||
|
borderTopColor: 'rgba(0, 0, 0, 0.2)',
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 4,
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
'& .rdtPicker td.rdtActive, & .rdtPicker td.rdtActive:hover': {
|
||||||
|
backgroundColor: theme.highlightColor,
|
||||||
|
color: '#fff',
|
||||||
|
textShadow:
|
||||||
|
'0 -1px 0 rgba(0,0,0,0.25), 0 1px 0 rgba(0,0,0,0.25), -1px 0 0 rgba(0,0,0,0.25), 1px 0 0 rgba(0,0,0,0.25)',
|
||||||
|
},
|
||||||
|
'& .rdtPicker td.rdtActive.rdtToday:before': {
|
||||||
|
borderBottomColor: '#fff',
|
||||||
|
},
|
||||||
|
'& .rdtPicker td.rdtDisabled, & .rdtPicker td.rdtDisabled:hover': {
|
||||||
|
background: 'none',
|
||||||
|
color: '#999999',
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
},
|
||||||
|
|
||||||
.rdtPrev span,
|
'& .rdtPicker td span.rdtOld': {
|
||||||
.rdtNext span {
|
color: '#999999',
|
||||||
display: block;
|
},
|
||||||
-webkit-touch-callout: none; /* iOS Safari */
|
'& .rdtPicker td span.rdtDisabled, & .rdtPicker td span.rdtDisabled:hover': {
|
||||||
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
background: 'none',
|
||||||
-khtml-user-select: none; /* Konqueror */
|
color: '#999999',
|
||||||
-moz-user-select: none; /* Firefox */
|
cursor: 'not-allowed',
|
||||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
},
|
||||||
user-select: none;
|
'& .rdtPicker th': {
|
||||||
}
|
borderBottom: `1px solid ${theme.highlightColor}`,
|
||||||
|
},
|
||||||
|
'& .rdtPicker .dow': {
|
||||||
|
width: '14.2857%',
|
||||||
|
fontSize: 11,
|
||||||
|
borderBottom: 'none',
|
||||||
|
},
|
||||||
|
'& .rdtPicker th.rdtSwitch': {
|
||||||
|
width: 100,
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
|
'& .rdtPicker th.rdtNext, & .rdtPicker th.rdtPrev': {
|
||||||
|
fontSize: 11,
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
},
|
||||||
|
|
||||||
.rdtPicker th.rdtDisabled,
|
'& .rdtPrev span, & .rdtNext span': {
|
||||||
.rdtPicker th.rdtDisabled:hover {
|
display: 'block',
|
||||||
background: none;
|
userSelect: 'none',
|
||||||
color: #999999;
|
},
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rdtPicker thead tr:first-child th {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rdtPicker thead tr:first-child th:hover {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rdtPicker tfoot {
|
'& .rdtPicker th.rdtDisabled, & .rdtPicker th.rdtDisabled:hover': {
|
||||||
border-top: 1px solid #f9f9f9;
|
background: 'none',
|
||||||
}
|
color: '#999999',
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
},
|
||||||
|
'& .rdtPicker thead tr:first-child th': {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
'& .rdtPicker thead tr:first-child th:hover': {
|
||||||
|
color: theme.highlightColor,
|
||||||
|
},
|
||||||
|
|
||||||
.rdtPicker button {
|
'& .rdtPicker tfoot': {
|
||||||
border: none;
|
borderTop: '1px solid #f9f9f9',
|
||||||
background: none;
|
},
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rdtPicker button:hover {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rdtPicker thead button {
|
'& .rdtPicker button': {
|
||||||
width: 100%;
|
border: 'none',
|
||||||
height: 100%;
|
background: 'none',
|
||||||
}
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
'& .rdtPicker button:hover': {
|
||||||
|
color: theme.highlightColor,
|
||||||
|
},
|
||||||
|
|
||||||
td.rdtMonth,
|
'& .rdtPicker thead button': {
|
||||||
td.rdtYear {
|
width: '100%',
|
||||||
height: 50px;
|
height: '100%',
|
||||||
width: 25%;
|
},
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
td.rdtMonth:hover,
|
|
||||||
td.rdtYear:hover {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.rdtDay {
|
'& td.rdtMonth, & td.rdtYear': {
|
||||||
font-size: 11px
|
height: 50,
|
||||||
}
|
width: '25%',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
'& td.rdtMonth:hover, & td.rdtYear:hover': {
|
||||||
|
color: theme.highlightColor,
|
||||||
|
},
|
||||||
|
|
||||||
.rdtCounters {
|
'& td.rdtDay': {
|
||||||
display: inline-block;
|
fontSize: 11,
|
||||||
}
|
},
|
||||||
|
|
||||||
.rdtCounters > div {
|
'& .rdtCounters': {
|
||||||
float: left;
|
display: 'inline-block',
|
||||||
}
|
},
|
||||||
|
|
||||||
.rdtCounter {
|
'& .rdtCounters > div': {
|
||||||
height: 100px;
|
float: 'left',
|
||||||
}
|
},
|
||||||
|
|
||||||
.rdtCounter {
|
'& .rdtCounter': {
|
||||||
width: 40px;
|
height: 100,
|
||||||
}
|
width: 40,
|
||||||
|
},
|
||||||
|
|
||||||
.rdtCounterSeparator {
|
'& .rdtCounterSeparator': {
|
||||||
line-height: 100px;
|
lineHeight: '100px',
|
||||||
}
|
},
|
||||||
|
|
||||||
.rdtCounter .rdtBtn {
|
'& .rdtCounter .rdtBtn': {
|
||||||
height: 40%;
|
height: '40%',
|
||||||
line-height: 40px;
|
lineHeight: '40px',
|
||||||
cursor: pointer;
|
cursor: 'pointer',
|
||||||
display: block;
|
display: 'block',
|
||||||
font-size: 11px;
|
fontSize: 11,
|
||||||
|
|
||||||
-webkit-touch-callout: none; /* iOS Safari */
|
userSelect: 'none',
|
||||||
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
},
|
||||||
-khtml-user-select: none; /* Konqueror */
|
'& .rdtCounter .rdtBtn:hover': {
|
||||||
-moz-user-select: none; /* Firefox */
|
color: theme.highlightColor,
|
||||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
},
|
||||||
user-select: none;
|
'& .rdtCounter .rdtCount': {
|
||||||
}
|
height: '20%',
|
||||||
.rdtCounter .rdtBtn:hover {
|
fontSize: 11,
|
||||||
background: #eee;
|
},
|
||||||
}
|
|
||||||
.rdtCounter .rdtCount {
|
|
||||||
height: 20%;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rdtMilli {
|
'& .rdtMilli': {
|
||||||
vertical-align: middle;
|
verticalSlign: 'middle',
|
||||||
padding-left: 8px;
|
paddingLeft: 8,
|
||||||
width: 48px;
|
width: 48,
|
||||||
}
|
},
|
||||||
|
|
||||||
.rdtMilli input {
|
'& .rdtMilli input': {
|
||||||
width: 100%;
|
width: '100%',
|
||||||
font-size: 11px;
|
fontSize: 11,
|
||||||
margin-top: 37px;
|
marginTop: 37,
|
||||||
}
|
},
|
||||||
`;
|
});
|
||||||
|
@ -1,22 +1,12 @@
|
|||||||
import { FileReader } from 'global';
|
import { FileReader } from 'global';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
const Input = styled('input')({
|
import { Input } from '@storybook/components';
|
||||||
display: 'table-cell',
|
|
||||||
boxSizing: 'border-box',
|
const FileInput = styled(Input)({
|
||||||
verticalAlign: 'middle',
|
paddingTop: 4,
|
||||||
height: '26px',
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '100%',
|
|
||||||
outline: 'none',
|
|
||||||
border: '1px solid #f7f4f4',
|
|
||||||
borderRadius: 2,
|
|
||||||
fontSize: 11,
|
|
||||||
padding: '5px',
|
|
||||||
color: '#555',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function fileReaderPromise(file) {
|
function fileReaderPromise(file) {
|
||||||
@ -28,12 +18,12 @@ function fileReaderPromise(file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FilesType = ({ knob, onChange }) => (
|
const FilesType = ({ knob, onChange }) => (
|
||||||
<Input
|
<FileInput
|
||||||
id={knob.name}
|
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
multiple
|
||||||
onChange={e => Promise.all(Array.from(e.target.files).map(fileReaderPromise)).then(onChange)}
|
onChange={e => Promise.all(Array.from(e.target.files).map(fileReaderPromise)).then(onChange)}
|
||||||
accept={knob.accept}
|
accept={knob.accept}
|
||||||
|
size="flex"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
import debounce from 'lodash.debounce';
|
|
||||||
|
import { Input } from '@storybook/components';
|
||||||
|
|
||||||
const base = {
|
const base = {
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
@ -15,11 +16,6 @@ const base = {
|
|||||||
color: '#444',
|
color: '#444',
|
||||||
};
|
};
|
||||||
|
|
||||||
const TextInput = styled('input')(base, {
|
|
||||||
display: 'table-cell',
|
|
||||||
width: '100%',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
});
|
|
||||||
const RangeInput = styled('input')(base, {
|
const RangeInput = styled('input')(base, {
|
||||||
display: 'table-cell',
|
display: 'table-cell',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
@ -37,78 +33,62 @@ const RangeWrapper = styled('div')({
|
|||||||
});
|
});
|
||||||
|
|
||||||
class NumberType extends React.Component {
|
class NumberType extends React.Component {
|
||||||
constructor(props) {
|
shouldComponentUpdate(nextProps) {
|
||||||
super(props);
|
return nextProps.knob.value !== this.props.knob.value;
|
||||||
let { value } = props.knob;
|
|
||||||
if (value === null || value === undefined) {
|
|
||||||
value = '';
|
|
||||||
}
|
|
||||||
this.state = { value };
|
|
||||||
|
|
||||||
this.onChange = debounce(props.onChange, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.onChange.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = event => {
|
handleChange = event => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
|
|
||||||
this.setState({ value });
|
|
||||||
|
|
||||||
let parsedValue = Number(value);
|
let parsedValue = Number(value);
|
||||||
|
|
||||||
if (Number.isNaN(parsedValue) || value === '') {
|
if (Number.isNaN(parsedValue) || value === '') {
|
||||||
parsedValue = null;
|
parsedValue = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onChange(parsedValue);
|
this.props.onChange(parsedValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knob } = this.props;
|
const { knob } = this.props;
|
||||||
const { value } = this.state;
|
|
||||||
|
|
||||||
return knob.range ? (
|
return knob.range ? (
|
||||||
<RangeWrapper>
|
<RangeWrapper>
|
||||||
<RangeLabel>{knob.min}</RangeLabel>
|
<RangeLabel>{knob.min}</RangeLabel>
|
||||||
<RangeInput
|
<RangeInput
|
||||||
id={knob.name}
|
value={knob.value}
|
||||||
value={value}
|
|
||||||
type="range"
|
type="range"
|
||||||
min={knob.min}
|
min={knob.min}
|
||||||
max={knob.max}
|
max={knob.max}
|
||||||
step={knob.step}
|
step={knob.step}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
/>
|
/>
|
||||||
<RangeLabel>{`${value} / ${knob.max}`}</RangeLabel>
|
<RangeLabel>{`${knob.value} / ${knob.max}`}</RangeLabel>
|
||||||
</RangeWrapper>
|
</RangeWrapper>
|
||||||
) : (
|
) : (
|
||||||
<TextInput
|
<Input
|
||||||
id={knob.name}
|
value={knob.value}
|
||||||
value={value}
|
|
||||||
type="number"
|
type="number"
|
||||||
min={knob.min}
|
min={knob.min}
|
||||||
max={knob.max}
|
max={knob.max}
|
||||||
step={knob.step}
|
step={knob.step}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
|
size="flex"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberType.defaultProps = {
|
|
||||||
knob: {},
|
|
||||||
onChange: value => value,
|
|
||||||
};
|
|
||||||
|
|
||||||
NumberType.propTypes = {
|
NumberType.propTypes = {
|
||||||
knob: PropTypes.shape({
|
knob: PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
value: PropTypes.number,
|
value: PropTypes.number,
|
||||||
}),
|
range: PropTypes.bool,
|
||||||
onChange: PropTypes.func,
|
min: PropTypes.number,
|
||||||
|
max: PropTypes.number,
|
||||||
|
step: PropTypes.number,
|
||||||
|
}).isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
NumberType.serialize = value => (value === null || value === undefined ? '' : String(value));
|
NumberType.serialize = value => (value === null || value === undefined ? '' : String(value));
|
||||||
|
@ -1,58 +1,38 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import styled from 'react-emotion';
|
import PropTypes from 'prop-types';
|
||||||
|
import deepEqual from 'fast-deep-equal';
|
||||||
import Textarea from 'react-textarea-autosize';
|
import { polyfill } from 'react-lifecycles-compat';
|
||||||
import debounce from 'lodash.debounce';
|
import { Textarea } from '@storybook/components';
|
||||||
|
|
||||||
const StyledTextarea = styled(Textarea)({
|
|
||||||
display: 'table-cell',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
width: '100%',
|
|
||||||
outline: 'none',
|
|
||||||
border: '1px solid #f7f4f4',
|
|
||||||
borderRadius: 2,
|
|
||||||
fontSize: 11,
|
|
||||||
padding: '5px',
|
|
||||||
color: '#555',
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
});
|
|
||||||
|
|
||||||
class ObjectType extends Component {
|
class ObjectType extends Component {
|
||||||
constructor(props, context) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
super(props, context);
|
if (!state || !deepEqual(props.knob.value, state.json)) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.state = {
|
return {
|
||||||
value: JSON.stringify(props.knob.value, null, 2),
|
value: JSON.stringify(props.knob.value, null, 2),
|
||||||
failed: false,
|
failed: false,
|
||||||
|
json: props.knob.value,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.state = {
|
return { value: 'Object cannot be stringified', failed: true };
|
||||||
// if it can't be JSON stringified, it's probably some weird stuff
|
|
||||||
value: 'Default object cannot not be JSON stringified',
|
|
||||||
failed: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onChange = debounce(props.onChange, 200);
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
componentWillUnmount() {
|
|
||||||
this.onChange.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = e => {
|
handleChange = e => {
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(e.target.value.trim());
|
const json = JSON.parse(value.trim());
|
||||||
this.onChange(json);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
value,
|
value,
|
||||||
|
json,
|
||||||
failed: false,
|
failed: false,
|
||||||
});
|
});
|
||||||
|
if (deepEqual(this.props.knob.value, this.state.json)) {
|
||||||
|
this.props.onChange(json);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({
|
this.setState({
|
||||||
value,
|
value,
|
||||||
@ -62,40 +42,30 @@ class ObjectType extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knob } = this.props;
|
|
||||||
const { value, failed } = this.state;
|
const { value, failed } = this.state;
|
||||||
const extraStyle = {};
|
|
||||||
|
|
||||||
if (failed) {
|
|
||||||
extraStyle.border = '1px solid #fadddd';
|
|
||||||
extraStyle.backgroundColor = '#fff5f5';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTextarea
|
<Textarea
|
||||||
id={knob.name}
|
valid={failed ? 'error' : null}
|
||||||
style={extraStyle}
|
|
||||||
value={value}
|
value={value}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
|
size="flex"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectType.defaultProps = {
|
|
||||||
knob: {},
|
|
||||||
onChange: value => value,
|
|
||||||
};
|
|
||||||
|
|
||||||
ObjectType.propTypes = {
|
ObjectType.propTypes = {
|
||||||
knob: PropTypes.shape({
|
knob: PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||||
}),
|
}).isRequired,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectType.serialize = object => JSON.stringify(object);
|
ObjectType.serialize = object => JSON.stringify(object);
|
||||||
ObjectType.deserialize = value => (value ? JSON.parse(value) : {});
|
ObjectType.deserialize = value => (value ? JSON.parse(value) : {});
|
||||||
|
|
||||||
|
polyfill(ObjectType);
|
||||||
|
|
||||||
export default ObjectType;
|
export default ObjectType;
|
||||||
|
@ -1,22 +1,9 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
|
||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
const Select = styled('select')({
|
import { Select } from '@storybook/components';
|
||||||
display: 'table-cell',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
height: '26px',
|
|
||||||
width: '100%',
|
|
||||||
outline: 'none',
|
|
||||||
border: '1px solid #f7f4f4',
|
|
||||||
borderRadius: 2,
|
|
||||||
fontSize: 11,
|
|
||||||
padding: '5px',
|
|
||||||
color: '#555',
|
|
||||||
});
|
|
||||||
|
|
||||||
class SelectType extends React.Component {
|
class SelectType extends Component {
|
||||||
renderOptionList({ options }) {
|
renderOptionList({ options }) {
|
||||||
if (Array.isArray(options)) {
|
if (Array.isArray(options)) {
|
||||||
return options.map(val => this.renderOption(val, val));
|
return options.map(val => this.renderOption(val, val));
|
||||||
@ -34,7 +21,7 @@ class SelectType extends React.Component {
|
|||||||
const { knob, onChange } = this.props;
|
const { knob, onChange } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select id={knob.name} value={knob.value} onChange={e => onChange(e.target.value)}>
|
<Select value={knob.value} onChange={e => onChange(e.target.value)} size="flex">
|
||||||
{this.renderOptionList(knob)}
|
{this.renderOptionList(knob)}
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
|
@ -1,53 +1,22 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
import Textarea from 'react-textarea-autosize';
|
import { Textarea } from '@storybook/components';
|
||||||
import debounce from 'lodash.debounce';
|
|
||||||
|
|
||||||
const StyledTextarea = styled(Textarea)({
|
|
||||||
display: 'table-cell',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
height: '26px',
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '100%',
|
|
||||||
outline: 'none',
|
|
||||||
border: '1px solid #f7f4f4',
|
|
||||||
borderRadius: 2,
|
|
||||||
fontSize: 11,
|
|
||||||
padding: '5px',
|
|
||||||
color: '#555',
|
|
||||||
});
|
|
||||||
|
|
||||||
class TextType extends React.Component {
|
class TextType extends React.Component {
|
||||||
constructor(props, context) {
|
shouldComponentUpdate(nextProps) {
|
||||||
super(props, context);
|
return nextProps.knob.value !== this.props.knob.value;
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: props.knob.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onChange = debounce(props.onChange, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.onChange.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = event => {
|
handleChange = event => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
|
this.props.onChange(value);
|
||||||
this.setState({ value });
|
|
||||||
|
|
||||||
this.onChange(value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { knob } = this.props;
|
const { knob } = this.props;
|
||||||
const { value } = this.state;
|
|
||||||
|
|
||||||
return <StyledTextarea id={knob.name} value={value} onChange={this.handleChange} />;
|
return <Textarea id={knob.name} value={knob.value} onChange={this.handleChange} size="flex" />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ export const withKnobs = makeDecorator({
|
|||||||
name: 'withKnobs',
|
name: 'withKnobs',
|
||||||
parameterName: 'knobs',
|
parameterName: 'knobs',
|
||||||
skipIfNoParametersOrOptions: false,
|
skipIfNoParametersOrOptions: false,
|
||||||
|
allowDeprecatedUsage: true,
|
||||||
wrapper: (getStory, context, { options, parameters }) => {
|
wrapper: (getStory, context, { options, parameters }) => {
|
||||||
const storyOptions = parameters || options;
|
const storyOptions = parameters || options;
|
||||||
const allOptions = { ...defaultOptions, ...storyOptions };
|
const allOptions = { ...defaultOptions, ...storyOptions };
|
||||||
|
@ -4,9 +4,9 @@ import Panel from './components/Panel';
|
|||||||
|
|
||||||
addons.register('storybooks/storybook-addon-knobs', api => {
|
addons.register('storybooks/storybook-addon-knobs', api => {
|
||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
|
|
||||||
addons.addPanel('storybooks/storybook-addon-knobs', {
|
addons.addPanel('storybooks/storybook-addon-knobs', {
|
||||||
title: 'Knobs',
|
title: 'Knobs',
|
||||||
render: () => <Panel channel={channel} api={api} key="knobs-panel" />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <Panel channel={channel} api={api} key="knobs-panel" active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-links",
|
"name": "@storybook/addon-links",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Story Links addon for storybook",
|
"description": "Story Links addon for storybook",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"storybook"
|
"storybook"
|
||||||
@ -20,9 +20,9 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"prop-types": "^15.6.1"
|
"prop-types": "^15.6.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-notes",
|
"name": "@storybook/addon-notes",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Write notes for your Storybook stories.",
|
"description": "Write notes for your Storybook stories.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"addon",
|
"addon",
|
||||||
@ -18,13 +18,11 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"marked": "^0.4.0",
|
"marked": "^0.4.0",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3"
|
||||||
"util-deprecate": "^1.0.2"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*"
|
"react": "*"
|
||||||
|
@ -10,6 +10,7 @@ export const withNotes = makeDecorator({
|
|||||||
name: 'withNotes',
|
name: 'withNotes',
|
||||||
parameterName: 'notes',
|
parameterName: 'notes',
|
||||||
skipIfNoParametersOrOptions: true,
|
skipIfNoParametersOrOptions: true,
|
||||||
|
allowDeprecatedUsage: true,
|
||||||
wrapper: (getStory, context, { options, parameters }) => {
|
wrapper: (getStory, context, { options, parameters }) => {
|
||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ export class Notes extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { active } = this.props;
|
||||||
const { text } = this.state;
|
const { text } = this.state;
|
||||||
const textAfterFormatted = text
|
const textAfterFormatted = text
|
||||||
? text
|
? text
|
||||||
@ -52,31 +53,34 @@ export class Notes extends React.Component {
|
|||||||
.replace(/\n/g, '<br />')
|
.replace(/\n/g, '<br />')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
return (
|
return active ? (
|
||||||
<Panel
|
<Panel
|
||||||
className="addon-notes-container"
|
className="addon-notes-container"
|
||||||
dangerouslySetInnerHTML={{ __html: textAfterFormatted }}
|
dangerouslySetInnerHTML={{ __html: textAfterFormatted }}
|
||||||
/>
|
/>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Notes.propTypes = {
|
Notes.propTypes = {
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
active: PropTypes.bool.isRequired,
|
||||||
channel: PropTypes.object,
|
channel: PropTypes.shape({
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
on: PropTypes.func,
|
||||||
api: PropTypes.object,
|
emit: PropTypes.func,
|
||||||
};
|
removeListener: PropTypes.func,
|
||||||
Notes.defaultProps = {
|
}).isRequired,
|
||||||
channel: {},
|
api: PropTypes.shape({
|
||||||
api: {},
|
onStory: PropTypes.func,
|
||||||
|
getQueryParam: PropTypes.func,
|
||||||
|
setQueryParams: PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register the addon with a unique name.
|
|
||||||
addons.register('storybook/notes', api => {
|
addons.register('storybook/notes', api => {
|
||||||
// Also need to set a unique name to the panel.
|
const channel = addons.getChannel();
|
||||||
addons.addPanel('storybook/notes/panel', {
|
addons.addPanel('storybook/notes/panel', {
|
||||||
title: 'Notes',
|
title: 'Notes',
|
||||||
render: () => <Notes channel={addons.getChannel()} api={api} />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <Notes channel={channel} api={api} active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-options",
|
"name": "@storybook/addon-options",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Options addon for storybook",
|
"description": "Options addon for storybook",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"storybook"
|
"storybook"
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0"
|
"babel-runtime": "^6.26.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-storyshots",
|
"name": "@storybook/addon-storyshots",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
|
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -16,7 +16,7 @@
|
|||||||
"storybook": "start-storybook -p 6006"
|
"storybook": "start-storybook -p 6006"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
@ -24,10 +24,10 @@
|
|||||||
"read-pkg-up": "^3.0.0"
|
"read-pkg-up": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-actions": "4.0.0-alpha.10",
|
"@storybook/addon-actions": "4.0.0-alpha.14",
|
||||||
"@storybook/addon-links": "4.0.0-alpha.10",
|
"@storybook/addon-links": "4.0.0-alpha.14",
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/react": "4.0.0-alpha.10",
|
"@storybook/react": "4.0.0-alpha.14",
|
||||||
"enzyme-to-json": "^3.3.4",
|
"enzyme-to-json": "^3.3.4",
|
||||||
"react": "^16.4.0"
|
"react": "^16.4.0"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-storyshots-puppeteer",
|
"name": "@storybook/addon-storyshots-puppeteer",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Image snappshots addition to StoryShots base on puppeteer",
|
"description": "Image snappshots addition to StoryShots base on puppeteer",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"prepare": "node ../../../scripts/prepare.js"
|
"prepare": "node ../../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/node-logger": "4.0.0-alpha.10",
|
"@storybook/node-logger": "4.0.0-alpha.14",
|
||||||
"jest-image-snapshot": "^2.4.2",
|
"jest-image-snapshot": "^2.4.2",
|
||||||
"puppeteer": "^1.4.0"
|
"puppeteer": "^1.4.0"
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-storysource",
|
"name": "@storybook/addon-storysource",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Stories addon for storybook",
|
"description": "Stories addon for storybook",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"storybook"
|
"storybook"
|
||||||
@ -20,8 +20,8 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"estraverse": "^4.2.0",
|
"estraverse": "^4.2.0",
|
||||||
"loader-utils": "^1.1.0",
|
"loader-utils": "^1.1.0",
|
||||||
|
@ -7,6 +7,7 @@ import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/pr
|
|||||||
import { createElement } from 'react-syntax-highlighter';
|
import { createElement } from 'react-syntax-highlighter';
|
||||||
import { EVENT_ID } from './';
|
import { EVENT_ID } from './';
|
||||||
|
|
||||||
|
// TODO: take from theme
|
||||||
const highlighterTheme = {
|
const highlighterTheme = {
|
||||||
...darcula,
|
...darcula,
|
||||||
'pre[class*="language-"]': {
|
'pre[class*="language-"]': {
|
||||||
@ -175,7 +176,8 @@ export default class StoryPanel extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
const { active } = this.props;
|
||||||
|
return active ? (
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language="jsx"
|
language="jsx"
|
||||||
showLineNumbers="true"
|
showLineNumbers="true"
|
||||||
@ -185,11 +187,12 @@ export default class StoryPanel extends Component {
|
|||||||
>
|
>
|
||||||
{this.state.source}
|
{this.state.source}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StoryPanel.propTypes = {
|
StoryPanel.propTypes = {
|
||||||
|
active: PropTypes.bool.isRequired,
|
||||||
api: PropTypes.shape({
|
api: PropTypes.shape({
|
||||||
selectStory: PropTypes.func.isRequired,
|
selectStory: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
@ -8,7 +8,8 @@ export function register() {
|
|||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
addons.addPanel(PANEL_ID, {
|
addons.addPanel(PANEL_ID, {
|
||||||
title: 'Story',
|
title: 'Story',
|
||||||
render: () => <StoryPanel channel={channel} api={api} />,
|
// eslint-disable-next-line react/prop-types
|
||||||
|
render: ({ active }) => <StoryPanel channel={channel} api={api} active={active} />,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/addon-viewport",
|
"name": "@storybook/addon-viewport",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook addon to change the viewport size to mobile",
|
"description": "Storybook addon to change the viewport size to mobile",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"storybook"
|
"storybook"
|
||||||
@ -11,13 +11,11 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/addons": "4.0.0-alpha.10",
|
"@storybook/addons": "4.0.0-alpha.14",
|
||||||
"@storybook/components": "4.0.0-alpha.10",
|
"@storybook/components": "4.0.0-alpha.14",
|
||||||
"@storybook/core-events": "4.0.0-alpha.10",
|
"@storybook/core-events": "4.0.0-alpha.14",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"emotion": "^9.1.3",
|
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"lodash.debounce": "^4.0.8",
|
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.1",
|
||||||
"react-emotion": "^9.1.3",
|
"react-emotion": "^9.1.3",
|
||||||
"util-deprecate": "^1.0.2"
|
"util-deprecate": "^1.0.2"
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { document } from 'global';
|
import { document } from 'global';
|
||||||
import debounce from 'lodash.debounce';
|
|
||||||
|
|
||||||
import styled from 'react-emotion';
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
|
import { ActionBar, ActionButton, Button, Select, Field } from '@storybook/components';
|
||||||
|
|
||||||
import { resetViewport, viewportsTransformer } from './viewportInfo';
|
import { resetViewport, viewportsTransformer } from './viewportInfo';
|
||||||
import { SelectViewport } from './SelectViewport';
|
|
||||||
import { RotateViewport } from './RotateViewport';
|
|
||||||
import {
|
import {
|
||||||
SET_STORY_DEFAULT_VIEWPORT_EVENT_ID,
|
SET_STORY_DEFAULT_VIEWPORT_EVENT_ID,
|
||||||
CONFIGURE_VIEWPORT_EVENT_ID,
|
CONFIGURE_VIEWPORT_EVENT_ID,
|
||||||
@ -17,14 +15,13 @@ import {
|
|||||||
DEFAULT_VIEWPORT,
|
DEFAULT_VIEWPORT,
|
||||||
} from '../../shared';
|
} from '../../shared';
|
||||||
|
|
||||||
import { Button } from './styles';
|
|
||||||
|
|
||||||
const storybookIframe = 'storybook-preview-iframe';
|
const storybookIframe = 'storybook-preview-iframe';
|
||||||
const Container = styled('div')({
|
const Container = styled('div')({
|
||||||
padding: 15,
|
padding: 15,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
});
|
});
|
||||||
|
Container.displayName = 'Container';
|
||||||
|
|
||||||
const getDefaultViewport = (viewports, candidateViewport) =>
|
const getDefaultViewport = (viewports, candidateViewport) =>
|
||||||
candidateViewport in viewports ? candidateViewport : Object.keys(viewports)[0];
|
candidateViewport in viewports ? candidateViewport : Object.keys(viewports)[0];
|
||||||
@ -32,36 +29,30 @@ const getDefaultViewport = (viewports, candidateViewport) =>
|
|||||||
const getViewports = viewports =>
|
const getViewports = viewports =>
|
||||||
Object.keys(viewports).length > 0 ? viewports : INITIAL_VIEWPORTS;
|
Object.keys(viewports).length > 0 ? viewports : INITIAL_VIEWPORTS;
|
||||||
|
|
||||||
const setStoryDefaultViewportWait = 100;
|
|
||||||
|
|
||||||
export class Panel extends Component {
|
export class Panel extends Component {
|
||||||
static defaultOptions = {
|
static defaultOptions = {
|
||||||
viewports: INITIAL_VIEWPORTS,
|
viewports: INITIAL_VIEWPORTS,
|
||||||
defaultViewport: DEFAULT_VIEWPORT,
|
defaultViewport: DEFAULT_VIEWPORT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
channel: PropTypes.shape({}).isRequired,
|
active: PropTypes.bool.isRequired,
|
||||||
api: PropTypes.shape({}).isRequired,
|
api: PropTypes.shape({
|
||||||
|
selectStory: PropTypes.func.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
channel: PropTypes.shape({
|
||||||
|
on: PropTypes.func,
|
||||||
|
emit: PropTypes.func,
|
||||||
|
removeListener: PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props, context) {
|
state = {
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
viewport: DEFAULT_VIEWPORT,
|
viewport: DEFAULT_VIEWPORT,
|
||||||
defaultViewport: DEFAULT_VIEWPORT,
|
defaultViewport: DEFAULT_VIEWPORT,
|
||||||
viewports: viewportsTransformer(INITIAL_VIEWPORTS),
|
viewports: viewportsTransformer(INITIAL_VIEWPORTS),
|
||||||
isLandscape: false,
|
isLandscape: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.previousViewport = DEFAULT_VIEWPORT;
|
|
||||||
|
|
||||||
this.setStoryDefaultViewport = debounce(
|
|
||||||
this.setStoryDefaultViewport,
|
|
||||||
setStoryDefaultViewportWait
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { channel, api } = this.props;
|
const { channel, api } = this.props;
|
||||||
|
|
||||||
@ -113,6 +104,7 @@ export class Panel extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
iframe = undefined;
|
iframe = undefined;
|
||||||
|
previousViewport = DEFAULT_VIEWPORT;
|
||||||
|
|
||||||
changeViewport = viewport => {
|
changeViewport = viewport => {
|
||||||
const { viewport: previousViewport } = this.state;
|
const { viewport: previousViewport } = this.state;
|
||||||
@ -180,28 +172,39 @@ export class Panel extends Component {
|
|||||||
viewport,
|
viewport,
|
||||||
viewports,
|
viewports,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
const { active } = this.props;
|
||||||
|
|
||||||
const disableDefault = viewport === storyDefaultViewport;
|
const isResponsive = viewport === storyDefaultViewport;
|
||||||
|
|
||||||
return (
|
return active ? (
|
||||||
<Container>
|
<Container>
|
||||||
<SelectViewport
|
<Field label="Device">
|
||||||
viewports={viewports}
|
<Select value={viewport} onChange={e => this.changeViewport(e.target.value)} size="flex">
|
||||||
defaultViewport={storyDefaultViewport}
|
{Object.entries(viewports).map(([key, { name }]) => (
|
||||||
activeViewport={viewport}
|
<option value={key} key={key}>
|
||||||
onChange={e => this.changeViewport(e.target.value)}
|
{key === defaultViewport ? `${name} (Default)` : name}
|
||||||
/>
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<RotateViewport
|
{!isResponsive ? (
|
||||||
onClick={this.toggleLandscape}
|
<Field label="Rotate">
|
||||||
disabled={disableDefault}
|
<Button onClick={this.toggleLandscape} active={isLandscape} size="flex">
|
||||||
active={isLandscape}
|
{isLandscape ? 'rotate to portrait' : 'rotate to landscape'}
|
||||||
/>
|
|
||||||
|
|
||||||
<Button onClick={() => this.changeViewport(storyDefaultViewport)} disabled={disableDefault}>
|
|
||||||
Reset Viewport
|
|
||||||
</Button>
|
</Button>
|
||||||
|
</Field>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<ActionBar>
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => this.changeViewport(storyDefaultViewport)}
|
||||||
|
disabled={isResponsive}
|
||||||
|
>
|
||||||
|
RESET
|
||||||
|
</ActionButton>
|
||||||
|
</ActionBar>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { Label, Row, Button } from './styles';
|
|
||||||
|
|
||||||
export const RotateViewport = ({ active, ...props }) => (
|
|
||||||
<Row>
|
|
||||||
<Label htmlFor="rotate">Rotate</Label>
|
|
||||||
<Button id="rotate" {...props}>
|
|
||||||
{active ? 'Vertical' : 'Landscape'}
|
|
||||||
</Button>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
|
|
||||||
RotateViewport.propTypes = {
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
active: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
RotateViewport.defaultProps = {
|
|
||||||
disabled: true,
|
|
||||||
active: false,
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { Label, Row, Select } from './styles';
|
|
||||||
|
|
||||||
export function SelectViewport({ viewports, defaultViewport, activeViewport, onChange }) {
|
|
||||||
return (
|
|
||||||
<Row>
|
|
||||||
<Label htmlFor="device">Device</Label>
|
|
||||||
<Select id="device" value={activeViewport} onChange={onChange}>
|
|
||||||
{Object.entries(viewports).map(([key, { name }]) => (
|
|
||||||
<option value={key} key={key}>
|
|
||||||
{key === defaultViewport ? `(Default) ${name}` : name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectViewport.propTypes = {
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
activeViewport: PropTypes.string.isRequired,
|
|
||||||
viewports: PropTypes.shape({}).isRequired,
|
|
||||||
defaultViewport: PropTypes.string.isRequired,
|
|
||||||
};
|
|
@ -1,36 +0,0 @@
|
|||||||
import styled from 'react-emotion';
|
|
||||||
|
|
||||||
export const Row = styled('div')({
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
marginBottom: 15,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Label = styled('label')({
|
|
||||||
width: 80,
|
|
||||||
marginRight: 15,
|
|
||||||
});
|
|
||||||
|
|
||||||
const actionColor = 'rgb(247, 247, 247)';
|
|
||||||
|
|
||||||
const basebutton = {
|
|
||||||
color: 'rgb(85, 85, 85)',
|
|
||||||
width: '100%',
|
|
||||||
border: `1px solid ${actionColor}`,
|
|
||||||
backgroundColor: actionColor,
|
|
||||||
borderRadius: 4,
|
|
||||||
padding: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Button = styled('button')(
|
|
||||||
basebutton,
|
|
||||||
({ disabled }) =>
|
|
||||||
disabled
|
|
||||||
? {
|
|
||||||
opacity: '0.5',
|
|
||||||
cursor: 'not-allowed',
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Select = styled('select')(basebutton);
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
|||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import { document } from 'global';
|
import { document } from 'global';
|
||||||
|
|
||||||
|
import { ActionButton, Select } from '@storybook/components';
|
||||||
|
|
||||||
import { Panel } from '../Panel';
|
import { Panel } from '../Panel';
|
||||||
import { resetViewport, viewportsTransformer } from '../viewportInfo';
|
import { resetViewport, viewportsTransformer } from '../viewportInfo';
|
||||||
import { DEFAULT_VIEWPORT, INITIAL_VIEWPORTS } from '../../../shared';
|
import { DEFAULT_VIEWPORT, INITIAL_VIEWPORTS } from '../../../shared';
|
||||||
@ -9,8 +11,6 @@ import { DEFAULT_VIEWPORT, INITIAL_VIEWPORTS } from '../../../shared';
|
|||||||
const initialViewportAt = index => Object.keys(INITIAL_VIEWPORTS)[index];
|
const initialViewportAt = index => Object.keys(INITIAL_VIEWPORTS)[index];
|
||||||
const transformedInitialViewports = viewportsTransformer(INITIAL_VIEWPORTS);
|
const transformedInitialViewports = viewportsTransformer(INITIAL_VIEWPORTS);
|
||||||
|
|
||||||
jest.mock('lodash.debounce', () => jest.fn(fn => fn));
|
|
||||||
|
|
||||||
describe('Viewport/Panel', () => {
|
describe('Viewport/Panel', () => {
|
||||||
const props = {
|
const props = {
|
||||||
channel: {
|
channel: {
|
||||||
@ -20,7 +20,9 @@ describe('Viewport/Panel', () => {
|
|||||||
},
|
},
|
||||||
api: {
|
api: {
|
||||||
onStory: jest.fn(),
|
onStory: jest.fn(),
|
||||||
|
selectStory: jest.fn(),
|
||||||
},
|
},
|
||||||
|
active: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let subject;
|
let subject;
|
||||||
@ -324,16 +326,18 @@ describe('Viewport/Panel', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
subject.instance().changeViewport = jest.fn();
|
subject.instance().changeViewport = jest.fn();
|
||||||
resetBtn = subject.find('Styled(button)');
|
resetBtn = subject.find(ActionButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enables the reset button if not default', () => {
|
it('enables the reset button if not default', () => {
|
||||||
subject.setState({ viewport: 'responsive' });
|
subject.setState({ viewport: 'responsive' });
|
||||||
resetBtn = subject.find('Styled(button)');
|
|
||||||
|
resetBtn = subject.find(ActionButton);
|
||||||
expect(resetBtn).toHaveProp('disabled', true);
|
expect(resetBtn).toHaveProp('disabled', true);
|
||||||
|
|
||||||
subject.setState({ viewport: 'iphone6' });
|
subject.setState({ viewport: 'iphone6' });
|
||||||
resetBtn = subject.find('Styled(button)');
|
|
||||||
|
resetBtn = subject.find(ActionButton);
|
||||||
expect(resetBtn).toHaveProp('disabled', false);
|
expect(resetBtn).toHaveProp('disabled', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -347,32 +351,16 @@ describe('Viewport/Panel', () => {
|
|||||||
let select;
|
let select;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
select = subject.find('SelectViewport');
|
select = subject.find(Select);
|
||||||
subject.instance().changeViewport = jest.fn();
|
subject.instance().changeViewport = jest.fn();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the activeViewport', () => {
|
it('passes the value', () => {
|
||||||
expect(select.props()).toEqual(
|
expect(select.props().value).toEqual('responsive');
|
||||||
expect.objectContaining({
|
|
||||||
activeViewport: DEFAULT_VIEWPORT,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the defaultViewport', () => {
|
it('passes the children', () => {
|
||||||
expect(select.props()).toEqual(
|
expect(select.props().children).toHaveLength(8);
|
||||||
expect.objectContaining({
|
|
||||||
defaultViewport: DEFAULT_VIEWPORT,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('passes the INITIAL_VIEWPORTS', () => {
|
|
||||||
expect(select.props()).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
viewports: transformedInitialViewports,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('onChange it updates the viewport', () => {
|
it('onChange it updates the viewport', () => {
|
||||||
@ -386,34 +374,21 @@ describe('Viewport/Panel', () => {
|
|||||||
let toggle;
|
let toggle;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
toggle = subject.find('RotateViewport');
|
toggle = subject.find('Field[label="Rotate"]');
|
||||||
jest.spyOn(subject.instance(), 'toggleLandscape');
|
jest.spyOn(subject.instance(), 'toggleLandscape');
|
||||||
subject.instance().forceUpdate();
|
subject.instance().forceUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the active prop based on the state of the panel', () => {
|
it('renders viewport is not default', () => {
|
||||||
expect(toggle.props().active).toEqual(subject.state('isLandscape'));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('is on the default viewport', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
subject.setState({ viewport: DEFAULT_VIEWPORT });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the disabled property', () => {
|
|
||||||
expect(toggle.props().disabled).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('is on a non-default viewport', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
subject.setState({ viewport: 'iphone6' });
|
subject.setState({ viewport: 'iphone6' });
|
||||||
toggle = subject.find('RotateViewport');
|
toggle = subject.find({ label: 'Rotate' });
|
||||||
|
expect(toggle).toExist();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('the disabled property is false', () => {
|
it('hidden the viewport is default', () => {
|
||||||
expect(toggle.props().disabled).toEqual(false);
|
subject.setState({ viewport: DEFAULT_VIEWPORT });
|
||||||
});
|
toggle = subject.find({ label: 'Rotate' });
|
||||||
|
expect(toggle).not.toExist();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import { RotateViewport } from '../RotateViewport';
|
|
||||||
|
|
||||||
const setup = addition => {
|
|
||||||
const props = {
|
|
||||||
onClick: jest.fn(),
|
|
||||||
disabled: false,
|
|
||||||
...addition,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = shallow(<RotateViewport {...props} />);
|
|
||||||
|
|
||||||
return { result, props };
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Viewport/RotateViewport', () => {
|
|
||||||
it('renders correctly', () => {
|
|
||||||
const { result } = setup();
|
|
||||||
expect(result).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a click handler set via props', () => {
|
|
||||||
const { result, props } = setup();
|
|
||||||
const button = result.find('Styled(button)');
|
|
||||||
expect(button).toHaveProp('onClick');
|
|
||||||
|
|
||||||
button.simulate('click');
|
|
||||||
expect(props.onClick).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the correctly if not-disabled', () => {
|
|
||||||
const { result } = setup({ disabled: false });
|
|
||||||
expect(result.find('Styled(button)')).toHaveProp('disabled', false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the correctly if disabled', () => {
|
|
||||||
const { result } = setup({ disabled: true });
|
|
||||||
expect(result.find('Styled(button)')).toHaveProp('disabled', true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the correctly if not-active', () => {
|
|
||||||
const { result } = setup({ active: false });
|
|
||||||
expect(result.html()).toContain('Landscape');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the correctly if active', () => {
|
|
||||||
const { result } = setup({ active: true });
|
|
||||||
expect(result.html()).toContain('Vertical');
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,44 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { shallow } from 'enzyme';
|
|
||||||
import { SelectViewport } from '../SelectViewport';
|
|
||||||
import { INITIAL_VIEWPORTS, DEFAULT_VIEWPORT } from '../../../shared';
|
|
||||||
|
|
||||||
const setup = () => {
|
|
||||||
const props = {
|
|
||||||
onChange: jest.fn(),
|
|
||||||
activeViewport: DEFAULT_VIEWPORT,
|
|
||||||
viewports: INITIAL_VIEWPORTS,
|
|
||||||
defaultViewport: DEFAULT_VIEWPORT,
|
|
||||||
};
|
|
||||||
|
|
||||||
return { props, result: shallow(<SelectViewport {...props} />) };
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Viewport/SelectViewport', () => {
|
|
||||||
it('is correctly rendered', () => {
|
|
||||||
const { result } = setup();
|
|
||||||
expect(result).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a default option first', () => {
|
|
||||||
const { result } = setup();
|
|
||||||
expect(result.find('Styled(select)').props().value).toEqual(DEFAULT_VIEWPORT);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has at least 1 option', () => {
|
|
||||||
const viewportKeys = Object.keys(INITIAL_VIEWPORTS);
|
|
||||||
expect(viewportKeys.length).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
const viewportKeys = Object.keys(INITIAL_VIEWPORTS);
|
|
||||||
const { result } = setup();
|
|
||||||
viewportKeys.forEach(key => {
|
|
||||||
const { name } = INITIAL_VIEWPORTS[key];
|
|
||||||
const expectedText = key === DEFAULT_VIEWPORT ? `(Default) ${name}` : name;
|
|
||||||
|
|
||||||
it(`renders an option for ${name}`, () => {
|
|
||||||
const option = result.find(`option[value="${key}"]`);
|
|
||||||
expect(option.text()).toEqual(expectedText);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,18 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Viewport/RotateViewport renders correctly 1`] = `
|
|
||||||
<Styled(div)>
|
|
||||||
<Styled(label)
|
|
||||||
htmlFor="rotate"
|
|
||||||
>
|
|
||||||
Rotate
|
|
||||||
</Styled(label)>
|
|
||||||
<Styled(button)
|
|
||||||
disabled={false}
|
|
||||||
id="rotate"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
>
|
|
||||||
Landscape
|
|
||||||
</Styled(button)>
|
|
||||||
</Styled(div)>
|
|
||||||
`;
|
|
@ -1,65 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Viewport/SelectViewport is correctly rendered 1`] = `
|
|
||||||
<Styled(div)>
|
|
||||||
<Styled(label)
|
|
||||||
htmlFor="device"
|
|
||||||
>
|
|
||||||
Device
|
|
||||||
</Styled(label)>
|
|
||||||
<Styled(select)
|
|
||||||
id="device"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
value="responsive"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
key="responsive"
|
|
||||||
value="responsive"
|
|
||||||
>
|
|
||||||
(Default) Responsive
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="iphone5"
|
|
||||||
value="iphone5"
|
|
||||||
>
|
|
||||||
iPhone 5
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="iphone6"
|
|
||||||
value="iphone6"
|
|
||||||
>
|
|
||||||
iPhone 6
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="iphone6p"
|
|
||||||
value="iphone6p"
|
|
||||||
>
|
|
||||||
iPhone 6 Plus
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="ipad"
|
|
||||||
value="ipad"
|
|
||||||
>
|
|
||||||
iPad
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="galaxys5"
|
|
||||||
value="galaxys5"
|
|
||||||
>
|
|
||||||
Galaxy S5
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="nexus5x"
|
|
||||||
value="nexus5x"
|
|
||||||
>
|
|
||||||
Nexus 5X
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
key="nexus6p"
|
|
||||||
value="nexus6p"
|
|
||||||
>
|
|
||||||
Nexus 6P
|
|
||||||
</option>
|
|
||||||
</Styled(select)>
|
|
||||||
</Styled(div)>
|
|
||||||
`;
|
|
@ -7,12 +7,10 @@ import { ADDON_ID, PANEL_ID } from '../shared';
|
|||||||
|
|
||||||
const addChannel = api => {
|
const addChannel = api => {
|
||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
|
|
||||||
addons.addPanel(PANEL_ID, {
|
addons.addPanel(PANEL_ID, {
|
||||||
title: 'Viewport',
|
title: 'Viewport',
|
||||||
render() {
|
// eslint-disable-next-line react/prop-types
|
||||||
return <Panel channel={channel} api={api} />;
|
render: ({ active }) => <Panel channel={channel} api={api} active={active} />,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ const applyViewportOptions = (options = {}) => {
|
|||||||
const withViewport = makeDecorator({
|
const withViewport = makeDecorator({
|
||||||
name: 'withViewport',
|
name: 'withViewport',
|
||||||
parameterName: 'viewport',
|
parameterName: 'viewport',
|
||||||
|
allowDeprecatedUsage: true,
|
||||||
wrapper: (getStory, context, { options, parameters }) => {
|
wrapper: (getStory, context, { options, parameters }) => {
|
||||||
const storyOptions = parameters || options;
|
const storyOptions = parameters || options;
|
||||||
const viewportOptions =
|
const viewportOptions =
|
||||||
|
1
app/angular/bin/build.js
vendored
1
app/angular/bin/build.js
vendored
@ -1,3 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
require('../dist/server/build');
|
require('../dist/server/build');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/angular",
|
"name": "@storybook/angular",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook for Angular: Develop Angular Components in isolation with Hot Reloading.",
|
"description": "Storybook for Angular: Develop Angular Components in isolation with Hot Reloading.",
|
||||||
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/angular",
|
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/angular",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -22,8 +22,8 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/core": "4.0.0-alpha.10",
|
"@storybook/core": "4.0.0-alpha.14",
|
||||||
"@storybook/node-logger": "4.0.0-alpha.10",
|
"@storybook/node-logger": "4.0.0-alpha.14",
|
||||||
"angular2-template-loader": "^0.6.2",
|
"angular2-template-loader": "^0.6.2",
|
||||||
"babel-runtime": "^6.23.0",
|
"babel-runtime": "^6.23.0",
|
||||||
"core-js": "^2.5.7",
|
"core-js": "^2.5.7",
|
||||||
|
@ -10,7 +10,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|||||||
button {
|
button {
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: #FFFFFF;
|
background-color: #ffffff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
|
@ -49,17 +49,17 @@ import { Component, Output, EventEmitter } from '@angular/core';
|
|||||||
main {
|
main {
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
max-width: 600;
|
max-width: 600;
|
||||||
lineHeight: 1.4;
|
line-height: 1.4;
|
||||||
fontFamily: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
|
fontfamily: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
opacity: 0.5,
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-code {
|
.inline-code {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
fontWeight: 600;
|
font-weight: 600;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
border: 1px solid #eae9e9;
|
border: 1px solid #eae9e9;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
1
app/angular/src/server/build.js
vendored
1
app/angular/src/server/build.js
vendored
@ -1,5 +1,4 @@
|
|||||||
import { buildStatic } from '@storybook/core/server';
|
import { buildStatic } from '@storybook/core/server';
|
||||||
|
|
||||||
import options from './options';
|
import options from './options';
|
||||||
|
|
||||||
buildStatic(options);
|
buildStatic(options);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
require('../dist/server/build');
|
require('../dist/server/build');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/html",
|
"name": "@storybook/html",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
|
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
|
||||||
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/html",
|
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/html",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -21,7 +21,7 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/core": "4.0.0-alpha.10",
|
"@storybook/core": "4.0.0-alpha.14",
|
||||||
"common-tags": "^1.8.0",
|
"common-tags": "^1.8.0",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"html-loader": "^0.5.5",
|
"html-loader": "^0.5.5",
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { buildStatic } from '@storybook/core/server';
|
import { buildStatic } from '@storybook/core/server';
|
||||||
|
|
||||||
import options from './options';
|
import options from './options';
|
||||||
|
|
||||||
buildStatic(options);
|
buildStatic(options);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
require('../dist/server/build');
|
require('../dist/server/build');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/marko",
|
"name": "@storybook/marko",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook for Marko: Develop Marko Component in isolation with Hot Reloading.",
|
"description": "Storybook for Marko: Develop Marko Component in isolation with Hot Reloading.",
|
||||||
"homepage": "https://github.com/storybooks/storybook/tree/master/app/marko",
|
"homepage": "https://github.com/storybooks/storybook/tree/master/app/marko",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/core": "4.0.0-alpha.10",
|
"@storybook/core": "4.0.0-alpha.14",
|
||||||
"common-tags": "^1.8.0",
|
"common-tags": "^1.8.0",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"marko-loader": "^1.3.3",
|
"marko-loader": "^1.3.3",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"react-dom": "^16.4.0"
|
"react-dom": "^16.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"marko": "^4",
|
"marko": "^4.10.0",
|
||||||
"marko-widgets": "^7.0.1"
|
"marko-widgets": "^7.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { buildStatic } from '@storybook/core/server';
|
import { buildStatic } from '@storybook/core/server';
|
||||||
|
|
||||||
import options from './options';
|
import options from './options';
|
||||||
|
|
||||||
buildStatic(options);
|
buildStatic(options);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
require('../dist/server/build');
|
require('../dist/server/build');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/mithril",
|
"name": "@storybook/mithril",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook for Mithril: Develop Mithril Component in isolation.",
|
"description": "Storybook for Mithril: Develop Mithril Component in isolation.",
|
||||||
"homepage": "https://github.com/storybooks/storybook/tree/master/app/mithril",
|
"homepage": "https://github.com/storybooks/storybook/tree/master/app/mithril",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/core": "4.0.0-alpha.10",
|
"@storybook/core": "4.0.0-alpha.14",
|
||||||
"common-tags": "^1.8.0",
|
"common-tags": "^1.8.0",
|
||||||
"global": "^4.3.2",
|
"global": "^4.3.2",
|
||||||
"react": "^16.4.0",
|
"react": "^16.4.0",
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { buildStatic } from '@storybook/core/server';
|
import { buildStatic } from '@storybook/core/server';
|
||||||
|
|
||||||
import options from './options';
|
import options from './options';
|
||||||
|
|
||||||
buildStatic(options);
|
buildStatic(options);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
require('../dist/server/build');
|
require('../dist/server/build');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@storybook/polymer",
|
"name": "@storybook/polymer",
|
||||||
"version": "4.0.0-alpha.10",
|
"version": "4.0.0-alpha.14",
|
||||||
"description": "Storybook for Polymer: Develop Polymer components in isolation with Hot Reloading.",
|
"description": "Storybook for Polymer: Develop Polymer components in isolation with Hot Reloading.",
|
||||||
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/polymer",
|
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/polymer",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -21,7 +21,7 @@
|
|||||||
"prepare": "node ../../scripts/prepare.js"
|
"prepare": "node ../../scripts/prepare.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/core": "4.0.0-alpha.10",
|
"@storybook/core": "4.0.0-alpha.14",
|
||||||
"@webcomponents/webcomponentsjs": "^1.2.0",
|
"@webcomponents/webcomponentsjs": "^1.2.0",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"common-tags": "^1.8.0",
|
"common-tags": "^1.8.0",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user