@storybook/preact 7.0.0-alpha.46 → 7.0.0-alpha.47
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +7 -6
- package/template/cli/.eslintrc.json +7 -0
- package/template/cli/Button.jsx +49 -0
- package/template/cli/Button.stories.jsx +40 -0
- package/template/cli/Header.jsx +58 -0
- package/template/cli/Header.stories.jsx +25 -0
- package/template/cli/Page.jsx +68 -0
- package/template/cli/Page.stories.jsx +25 -0
- package/template/components/Button.jsx +14 -0
- package/template/components/Form.jsx +38 -0
- package/template/components/Html.jsx +10 -0
- package/template/components/Pre.jsx +21 -0
- package/template/components/index.js +9 -0
- package/template/stories/React.js +50 -0
- package/template/stories/react-compat.stories.js +15 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@storybook/preact",
|
3
|
-
"version": "7.0.0-alpha.
|
3
|
+
"version": "7.0.0-alpha.47",
|
4
4
|
"description": "Storybook Preact renderer",
|
5
5
|
"keywords": [
|
6
6
|
"storybook"
|
@@ -41,6 +41,7 @@
|
|
41
41
|
"types": "dist/index.d.ts",
|
42
42
|
"files": [
|
43
43
|
"dist/**/*",
|
44
|
+
"template/**/*",
|
44
45
|
"README.md",
|
45
46
|
"*.js",
|
46
47
|
"*.d.ts"
|
@@ -50,10 +51,10 @@
|
|
50
51
|
"prep": "../../../scripts/prepare/bundle.ts"
|
51
52
|
},
|
52
53
|
"dependencies": {
|
53
|
-
"@storybook/addons": "7.0.0-alpha.
|
54
|
-
"@storybook/core-client": "7.0.0-alpha.
|
55
|
-
"@storybook/store": "7.0.0-alpha.
|
56
|
-
"@storybook/types": "7.0.0-alpha.
|
54
|
+
"@storybook/addons": "7.0.0-alpha.47",
|
55
|
+
"@storybook/core-client": "7.0.0-alpha.47",
|
56
|
+
"@storybook/store": "7.0.0-alpha.47",
|
57
|
+
"@storybook/types": "7.0.0-alpha.47",
|
57
58
|
"global": "^4.4.0",
|
58
59
|
"react": "16.14.0",
|
59
60
|
"react-dom": "16.14.0",
|
@@ -79,5 +80,5 @@
|
|
79
80
|
],
|
80
81
|
"platform": "browser"
|
81
82
|
},
|
82
|
-
"gitHead": "
|
83
|
+
"gitHead": "1c706a4a778831e012343c905f86225fa71491a7"
|
83
84
|
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import PropTypes from 'prop-types';
|
2
|
+
import './button.css';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Primary UI component for user interaction
|
6
|
+
*/
|
7
|
+
export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
|
8
|
+
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
|
9
|
+
return (
|
10
|
+
<button
|
11
|
+
type="button"
|
12
|
+
className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
|
13
|
+
style={backgroundColor && { backgroundColor }}
|
14
|
+
{...props}
|
15
|
+
>
|
16
|
+
{label}
|
17
|
+
</button>
|
18
|
+
);
|
19
|
+
};
|
20
|
+
|
21
|
+
Button.propTypes = {
|
22
|
+
/**
|
23
|
+
* Is this the principal call to action on the page?
|
24
|
+
*/
|
25
|
+
primary: PropTypes.bool,
|
26
|
+
/**
|
27
|
+
* What background color to use
|
28
|
+
*/
|
29
|
+
backgroundColor: PropTypes.string,
|
30
|
+
/**
|
31
|
+
* How large should the button be?
|
32
|
+
*/
|
33
|
+
size: PropTypes.oneOf(['small', 'medium', 'large']),
|
34
|
+
/**
|
35
|
+
* Button contents
|
36
|
+
*/
|
37
|
+
label: PropTypes.string.isRequired,
|
38
|
+
/**
|
39
|
+
* Optional click handler
|
40
|
+
*/
|
41
|
+
onClick: PropTypes.func,
|
42
|
+
};
|
43
|
+
|
44
|
+
Button.defaultProps = {
|
45
|
+
backgroundColor: null,
|
46
|
+
primary: false,
|
47
|
+
size: 'medium',
|
48
|
+
onClick: undefined,
|
49
|
+
};
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { Button } from './Button';
|
2
|
+
|
3
|
+
// More on default export: https://storybook.js.org/docs/preact/writing-stories/introduction#default-export
|
4
|
+
export default {
|
5
|
+
title: 'Example/Button',
|
6
|
+
component: Button,
|
7
|
+
// More on argTypes: https://storybook.js.org/docs/preact/api/argtypes
|
8
|
+
argTypes: {
|
9
|
+
backgroundColor: { control: 'color' },
|
10
|
+
onClick: { action: 'onClick' },
|
11
|
+
},
|
12
|
+
};
|
13
|
+
|
14
|
+
// More on component templates: https://storybook.js.org/docs/preact/writing-stories/introduction#using-args
|
15
|
+
export const Primary = {
|
16
|
+
args: {
|
17
|
+
primary: true,
|
18
|
+
label: 'Button',
|
19
|
+
},
|
20
|
+
};
|
21
|
+
|
22
|
+
export const Secondary = {
|
23
|
+
args: {
|
24
|
+
label: 'Button',
|
25
|
+
},
|
26
|
+
};
|
27
|
+
|
28
|
+
export const Large = {
|
29
|
+
args: {
|
30
|
+
size: 'large',
|
31
|
+
label: 'Button',
|
32
|
+
},
|
33
|
+
};
|
34
|
+
|
35
|
+
export const Small = {
|
36
|
+
args: {
|
37
|
+
size: 'small',
|
38
|
+
label: 'Button',
|
39
|
+
},
|
40
|
+
};
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import PropTypes from 'prop-types';
|
2
|
+
|
3
|
+
import { Button } from './Button';
|
4
|
+
import './header.css';
|
5
|
+
|
6
|
+
export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
|
7
|
+
<header>
|
8
|
+
<div className="wrapper">
|
9
|
+
<div>
|
10
|
+
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
11
|
+
<g fill="none" fillRule="evenodd">
|
12
|
+
<path
|
13
|
+
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
|
14
|
+
fill="#FFF"
|
15
|
+
/>
|
16
|
+
<path
|
17
|
+
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
|
18
|
+
fill="#555AB9"
|
19
|
+
/>
|
20
|
+
<path
|
21
|
+
d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z"
|
22
|
+
fill="#91BAF8"
|
23
|
+
/>
|
24
|
+
</g>
|
25
|
+
</svg>
|
26
|
+
<h1>Acme</h1>
|
27
|
+
</div>
|
28
|
+
<div>
|
29
|
+
{user ? (
|
30
|
+
<>
|
31
|
+
<span className="welcome">
|
32
|
+
Welcome, <b>{user.name}</b>!
|
33
|
+
</span>
|
34
|
+
<Button size="small" onClick={onLogout} label="Log out" />
|
35
|
+
</>
|
36
|
+
) : (
|
37
|
+
<>
|
38
|
+
<Button size="small" onClick={onLogin} label="Log in" />
|
39
|
+
<Button primary size="small" onClick={onCreateAccount} label="Sign up" />
|
40
|
+
</>
|
41
|
+
)}
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</header>
|
45
|
+
);
|
46
|
+
|
47
|
+
Header.propTypes = {
|
48
|
+
user: PropTypes.shape({
|
49
|
+
name: PropTypes.string.isRequired,
|
50
|
+
}),
|
51
|
+
onLogin: PropTypes.func.isRequired,
|
52
|
+
onLogout: PropTypes.func.isRequired,
|
53
|
+
onCreateAccount: PropTypes.func.isRequired,
|
54
|
+
};
|
55
|
+
|
56
|
+
Header.defaultProps = {
|
57
|
+
user: null,
|
58
|
+
};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Header } from './Header';
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Example/Header',
|
5
|
+
component: Header,
|
6
|
+
parameters: {
|
7
|
+
// More on Story layout: https://storybook.js.org/docs/preact/configure/story-layout
|
8
|
+
layout: 'fullscreen',
|
9
|
+
},
|
10
|
+
argTypes: {
|
11
|
+
onLogin: { action: 'onLogin' },
|
12
|
+
onLogout: { action: 'onLogout' },
|
13
|
+
onCreateAccount: { action: 'onCreateAccount' },
|
14
|
+
},
|
15
|
+
};
|
16
|
+
|
17
|
+
export const LoggedIn = {
|
18
|
+
args: {
|
19
|
+
user: {
|
20
|
+
name: 'Jane Doe',
|
21
|
+
},
|
22
|
+
},
|
23
|
+
};
|
24
|
+
|
25
|
+
export const LoggedOut = {};
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import { useState } from 'preact/hooks';
|
2
|
+
import { Header } from './Header';
|
3
|
+
import './page.css';
|
4
|
+
|
5
|
+
export const Page = () => {
|
6
|
+
const [user, setUser] = useState();
|
7
|
+
|
8
|
+
return (
|
9
|
+
<article>
|
10
|
+
<Header
|
11
|
+
user={user}
|
12
|
+
onLogin={() => setUser({ name: 'Jane Doe' })}
|
13
|
+
onLogout={() => setUser(undefined)}
|
14
|
+
onCreateAccount={() => setUser({ name: 'Jane Doe' })}
|
15
|
+
/>
|
16
|
+
|
17
|
+
<section>
|
18
|
+
<h2>Pages in Storybook</h2>
|
19
|
+
<p>
|
20
|
+
We recommend building UIs with a{' '}
|
21
|
+
<a href="https://componentdriven.org" target="_blank" rel="noopener noreferrer">
|
22
|
+
<strong>component-driven</strong>
|
23
|
+
</a>{' '}
|
24
|
+
process starting with atomic components and ending with pages.
|
25
|
+
</p>
|
26
|
+
<p>
|
27
|
+
Render pages with mock data. This makes it easy to build and review page states without
|
28
|
+
needing to navigate to them in your app. Here are some handy patterns for managing page
|
29
|
+
data in Storybook:
|
30
|
+
</p>
|
31
|
+
<ul>
|
32
|
+
<li>
|
33
|
+
Use a higher-level connected component. Storybook helps you compose such data from the
|
34
|
+
"args" of child component stories
|
35
|
+
</li>
|
36
|
+
<li>
|
37
|
+
Assemble data in the page component from your services. You can mock these services out
|
38
|
+
using Storybook.
|
39
|
+
</li>
|
40
|
+
</ul>
|
41
|
+
<p>
|
42
|
+
Get a guided tutorial on component-driven development at{' '}
|
43
|
+
<a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
|
44
|
+
Storybook tutorials
|
45
|
+
</a>
|
46
|
+
. Read more in the{' '}
|
47
|
+
<a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">
|
48
|
+
docs
|
49
|
+
</a>
|
50
|
+
.
|
51
|
+
</p>
|
52
|
+
<div className="tip-wrapper">
|
53
|
+
<span className="tip">Tip</span> Adjust the width of the canvas with the{' '}
|
54
|
+
<svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
55
|
+
<g fill="none" fillRule="evenodd">
|
56
|
+
<path
|
57
|
+
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
|
58
|
+
id="a"
|
59
|
+
fill="#999"
|
60
|
+
/>
|
61
|
+
</g>
|
62
|
+
</svg>
|
63
|
+
Viewports addon in the toolbar
|
64
|
+
</div>
|
65
|
+
</section>
|
66
|
+
</article>
|
67
|
+
);
|
68
|
+
};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { within, userEvent } from '@storybook/testing-library';
|
2
|
+
|
3
|
+
import { Page } from './Page';
|
4
|
+
|
5
|
+
export default {
|
6
|
+
title: 'Example/Page',
|
7
|
+
component: Page,
|
8
|
+
parameters: {
|
9
|
+
// More on Story layout: https://storybook.js.org/docs/preact/configure/story-layout
|
10
|
+
layout: 'fullscreen',
|
11
|
+
},
|
12
|
+
};
|
13
|
+
|
14
|
+
export const LoggedOut = {};
|
15
|
+
|
16
|
+
// More on interaction testing: https://storybook.js.org/docs/preact/writing-tests/interaction-testing
|
17
|
+
export const LoggedIn = {
|
18
|
+
play: async ({ canvasElement }) => {
|
19
|
+
const canvas = within(canvasElement);
|
20
|
+
const loginButton = await canvas.getByRole('button', {
|
21
|
+
name: /Log in/i,
|
22
|
+
});
|
23
|
+
await userEvent.click(loginButton);
|
24
|
+
},
|
25
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/* eslint-disable react/react-in-jsx-scope */
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
|
5
|
+
export const Button = ({ onClick, label }) => (
|
6
|
+
<button type="button" onClick={onClick}>
|
7
|
+
{label}
|
8
|
+
</button>
|
9
|
+
);
|
10
|
+
|
11
|
+
Button.propTypes = {
|
12
|
+
onClick: PropTypes.func.isRequired,
|
13
|
+
label: PropTypes.node.isRequired,
|
14
|
+
};
|
@@ -0,0 +1,38 @@
|
|
1
|
+
/* eslint-disable react/react-in-jsx-scope */
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
import { useState } from 'preact/hooks';
|
5
|
+
|
6
|
+
export const Form = ({ onSuccess }) => {
|
7
|
+
const [value, setValue] = useState('');
|
8
|
+
const [complete, setComplete] = useState(false);
|
9
|
+
|
10
|
+
function onSubmit(event) {
|
11
|
+
event.preventDefault();
|
12
|
+
onSuccess(value);
|
13
|
+
|
14
|
+
setTimeout(() => setComplete(true), 500);
|
15
|
+
setTimeout(() => setComplete(false), 1500);
|
16
|
+
}
|
17
|
+
|
18
|
+
return (
|
19
|
+
<form id="interaction-test-form" onSubmit={onSubmit}>
|
20
|
+
<label>
|
21
|
+
Enter Value
|
22
|
+
<input
|
23
|
+
type="text"
|
24
|
+
data-testid="value"
|
25
|
+
value={value}
|
26
|
+
required
|
27
|
+
onChange={(event) => setValue(event.target.value)}
|
28
|
+
/>
|
29
|
+
</label>
|
30
|
+
<button type="submit">Submit</button>
|
31
|
+
{complete && <p>Completed!!</p>}
|
32
|
+
</form>
|
33
|
+
);
|
34
|
+
};
|
35
|
+
|
36
|
+
Form.propTypes = {
|
37
|
+
onSuccess: PropTypes.func.isRequired,
|
38
|
+
};
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/* eslint-disable react/react-in-jsx-scope */
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
|
5
|
+
// eslint-disable-next-line react/no-danger
|
6
|
+
export const Html = ({ content }) => <div dangerouslySetInnerHTML={{ __html: content }} />;
|
7
|
+
|
8
|
+
Html.propTypes = {
|
9
|
+
content: PropTypes.string.isRequired,
|
10
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/* eslint-disable react/react-in-jsx-scope */
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
|
5
|
+
export const Pre = ({ style, object, text }) => (
|
6
|
+
<pre style={style} data-testid="pre">
|
7
|
+
{object ? JSON.stringify(object, null, 2) : text}
|
8
|
+
</pre>
|
9
|
+
);
|
10
|
+
|
11
|
+
Pre.propTypes = {
|
12
|
+
style: PropTypes.shape({}),
|
13
|
+
object: PropTypes.shape({}),
|
14
|
+
text: PropTypes.string,
|
15
|
+
};
|
16
|
+
|
17
|
+
Pre.defaultProps = {
|
18
|
+
style: {},
|
19
|
+
object: null,
|
20
|
+
text: '',
|
21
|
+
};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import globalThis from 'global';
|
2
|
+
|
3
|
+
import { Button } from './Button.jsx';
|
4
|
+
import { Pre } from './Pre.jsx';
|
5
|
+
import { Form } from './Form.jsx';
|
6
|
+
import { Html } from './Html.jsx';
|
7
|
+
|
8
|
+
globalThis.Components = { Button, Pre, Form, Html };
|
9
|
+
globalThis.storybookRenderer = 'preact';
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
|
5
|
+
export const ReactFunctionalComponent = ({ label }) => {
|
6
|
+
const [clicks, setValue] = React.useState(0);
|
7
|
+
return (
|
8
|
+
<div
|
9
|
+
tabIndex={0}
|
10
|
+
onClick={() => setValue(clicks + 1)}
|
11
|
+
style={{ cursor: 'pointer' }}
|
12
|
+
onKeyDown={() => undefined}
|
13
|
+
role="button"
|
14
|
+
>
|
15
|
+
<div style={{ color: 'red' }}>{label}</div>
|
16
|
+
<div>Clicked {clicks} times.</div>
|
17
|
+
</div>
|
18
|
+
);
|
19
|
+
};
|
20
|
+
|
21
|
+
ReactFunctionalComponent.propTypes = {
|
22
|
+
label: PropTypes.string.isRequired,
|
23
|
+
};
|
24
|
+
|
25
|
+
export class ReactClassComponent extends React.Component {
|
26
|
+
state = {
|
27
|
+
clicks: 0,
|
28
|
+
};
|
29
|
+
|
30
|
+
render() {
|
31
|
+
const { label } = this.props;
|
32
|
+
const { clicks } = this.state;
|
33
|
+
return (
|
34
|
+
<div
|
35
|
+
tabIndex={0}
|
36
|
+
onClick={() => this.setState({ clicks: clicks + 1 })}
|
37
|
+
onKeyDown={() => undefined}
|
38
|
+
style={{ cursor: 'pointer' }}
|
39
|
+
role="button"
|
40
|
+
>
|
41
|
+
<div style={{ color: 'green' }}>{label}</div>
|
42
|
+
<div>Clicked {clicks} times.</div>
|
43
|
+
</div>
|
44
|
+
);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
ReactClassComponent.propTypes = {
|
49
|
+
label: PropTypes.string.isRequired,
|
50
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/* eslint-disable react/react-in-jsx-scope */
|
2
|
+
import { ReactFunctionalComponent, ReactClassComponent } from './React';
|
3
|
+
|
4
|
+
export default {
|
5
|
+
component: ReactFunctionalComponent,
|
6
|
+
};
|
7
|
+
|
8
|
+
export const ReactComponentDemo = () => (
|
9
|
+
<div>
|
10
|
+
<h1>React component demo</h1>
|
11
|
+
<ReactFunctionalComponent label="This is a React functional component rendered by Preact" />
|
12
|
+
<hr />
|
13
|
+
<ReactClassComponent label="This is a React class component rendered by Preact" />
|
14
|
+
</div>
|
15
|
+
);
|