18 changed files with 549 additions and 11 deletions
@ -0,0 +1,11 @@ |
|||||||
|
module.exports = { |
||||||
|
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'], |
||||||
|
addons: [ |
||||||
|
'@storybook/addon-links', |
||||||
|
'@storybook/addon-essentials', |
||||||
|
'@storybook/addon-interactions', |
||||||
|
'@storybook/preset-scss', |
||||||
|
'@storybook/addon-postcss', |
||||||
|
], |
||||||
|
framework: '@storybook/react', |
||||||
|
}; |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
import 'antd/dist/antd.css'; |
||||||
|
import '../styles/globals.scss'; |
||||||
|
|
||||||
|
export const parameters = { |
||||||
|
actions: { argTypesRegex: '^on[A-Z].*' }, |
||||||
|
controls: { |
||||||
|
matchers: { |
||||||
|
color: /(background|color)$/i, |
||||||
|
date: /Date$/, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
module.exports = { |
||||||
|
plugins: ['postcss-flexbugs-fixes', 'autoprefixer'], |
||||||
|
}; |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { Menu, Dropdown } from 'antd'; |
||||||
|
import { DownOutlined } from '@ant-design/icons'; |
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'; |
||||||
|
|
||||||
|
const menu = ( |
||||||
|
<Menu> |
||||||
|
<Menu.Item key="0"> |
||||||
|
<a href="https://owncast.online">1st menu item</a> |
||||||
|
</Menu.Item> |
||||||
|
<Menu.Item key="1"> |
||||||
|
<a href="https://directory.owncast.online">2nd menu item</a> |
||||||
|
</Menu.Item> |
||||||
|
<Menu.Divider /> |
||||||
|
<Menu.Item key="3">3rd menu item</Menu.Item> |
||||||
|
</Menu> |
||||||
|
); |
||||||
|
|
||||||
|
const DropdownExample = () => ( |
||||||
|
<Dropdown overlay={menu} trigger={['click']}> |
||||||
|
<button type="button" className="ant-dropdown-link" onClick={e => e.preventDefault()}> |
||||||
|
Click me <DownOutlined /> |
||||||
|
</button> |
||||||
|
</Dropdown> |
||||||
|
); |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'owncast/Dropdown', |
||||||
|
component: Dropdown, |
||||||
|
parameters: {}, |
||||||
|
} as ComponentMeta<typeof Dropdown>; |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const Template: ComponentStory<typeof Dropdown> = args => <DropdownExample />; |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const Basic = Template.bind({}); |
||||||
@ -0,0 +1,110 @@ |
|||||||
|
import React, { useState } from 'react'; |
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'; |
||||||
|
import { |
||||||
|
Button, |
||||||
|
Form, |
||||||
|
Input, |
||||||
|
Radio, |
||||||
|
Select, |
||||||
|
Cascader, |
||||||
|
DatePicker, |
||||||
|
InputNumber, |
||||||
|
TreeSelect, |
||||||
|
Switch, |
||||||
|
} from 'antd'; |
||||||
|
|
||||||
|
const FormExample = () => { |
||||||
|
const [componentSize, setComponentSize] = useState('default'); |
||||||
|
|
||||||
|
const onFormLayoutChange = ({ size }) => { |
||||||
|
setComponentSize(size); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Form |
||||||
|
labelCol={{ |
||||||
|
span: 4, |
||||||
|
}} |
||||||
|
wrapperCol={{ |
||||||
|
span: 14, |
||||||
|
}} |
||||||
|
layout="horizontal" |
||||||
|
initialValues={{ |
||||||
|
size: componentSize, |
||||||
|
}} |
||||||
|
onValuesChange={onFormLayoutChange} |
||||||
|
size={componentSize} |
||||||
|
> |
||||||
|
<Form.Item label="Form Size" name="size"> |
||||||
|
<Radio.Group> |
||||||
|
<Radio.Button value="small">Small</Radio.Button> |
||||||
|
<Radio.Button value="default">Default</Radio.Button> |
||||||
|
<Radio.Button value="large">Large</Radio.Button> |
||||||
|
</Radio.Group> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="Required text input" required> |
||||||
|
<Input /> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="Select"> |
||||||
|
<Select> |
||||||
|
<Select.Option value="demo">Demo</Select.Option> |
||||||
|
</Select> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="TreeSelect"> |
||||||
|
<TreeSelect |
||||||
|
treeData={[ |
||||||
|
{ |
||||||
|
title: 'Light', |
||||||
|
value: 'light', |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
title: 'Bamboo', |
||||||
|
value: 'bamboo', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
]} |
||||||
|
/> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="Cascader"> |
||||||
|
<Cascader |
||||||
|
options={[ |
||||||
|
{ |
||||||
|
value: 'zhejiang', |
||||||
|
label: 'Zhejiang', |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
value: 'hangzhou', |
||||||
|
label: 'Hangzhou', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
]} |
||||||
|
/> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="DatePicker"> |
||||||
|
<DatePicker /> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="InputNumber"> |
||||||
|
<InputNumber /> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="Switch" valuePropName="checked"> |
||||||
|
<Switch /> |
||||||
|
</Form.Item> |
||||||
|
<Form.Item label="Button"> |
||||||
|
<Button>Button</Button> |
||||||
|
</Form.Item> |
||||||
|
</Form> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'owncast/Form', |
||||||
|
component: Form, |
||||||
|
// parameters: {},
|
||||||
|
} as ComponentMeta<typeof Form>; |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const Template: ComponentStory<typeof Form> = args => <FormExample />; |
||||||
|
|
||||||
|
export const Demo = Template.bind({}); |
||||||
@ -0,0 +1,211 @@ |
|||||||
|
import { Meta } from '@storybook/addon-docs'; |
||||||
|
import Code from './assets/code-brackets.svg'; |
||||||
|
import Colors from './assets/colors.svg'; |
||||||
|
import Comments from './assets/comments.svg'; |
||||||
|
import Direction from './assets/direction.svg'; |
||||||
|
import Flow from './assets/flow.svg'; |
||||||
|
import Plugin from './assets/plugin.svg'; |
||||||
|
import Repo from './assets/repo.svg'; |
||||||
|
import StackAlt from './assets/stackalt.svg'; |
||||||
|
|
||||||
|
<Meta title="Example/Introduction" /> |
||||||
|
|
||||||
|
<style>{` |
||||||
|
.subheading { |
||||||
|
--mediumdark: '#999999'; |
||||||
|
font-weight: 900; |
||||||
|
font-size: 13px; |
||||||
|
color: #999; |
||||||
|
letter-spacing: 6px; |
||||||
|
line-height: 24px; |
||||||
|
text-transform: uppercase; |
||||||
|
margin-bottom: 12px; |
||||||
|
margin-top: 40px; |
||||||
|
} |
||||||
|
|
||||||
|
.link-list { |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 1fr; |
||||||
|
grid-template-rows: 1fr 1fr; |
||||||
|
row-gap: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
@media (min-width: 620px) { |
||||||
|
.link-list { |
||||||
|
row-gap: 20px; |
||||||
|
column-gap: 20px; |
||||||
|
grid-template-columns: 1fr 1fr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@media all and (-ms-high-contrast:none) { |
||||||
|
.link-list { |
||||||
|
display: -ms-grid; |
||||||
|
-ms-grid-columns: 1fr 1fr; |
||||||
|
-ms-grid-rows: 1fr 1fr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.link-item { |
||||||
|
display: block; |
||||||
|
padding: 20px 30px 20px 15px; |
||||||
|
border: 1px solid #00000010; |
||||||
|
border-radius: 5px; |
||||||
|
transition: background 150ms ease-out, border 150ms ease-out, transform 150ms ease-out; |
||||||
|
color: #333333; |
||||||
|
display: flex; |
||||||
|
align-items: flex-start; |
||||||
|
} |
||||||
|
|
||||||
|
.link-item:hover { |
||||||
|
border-color: #1EA7FD50; |
||||||
|
transform: translate3d(0, -3px, 0); |
||||||
|
box-shadow: rgba(0, 0, 0, 0.08) 0 3px 10px 0; |
||||||
|
} |
||||||
|
|
||||||
|
.link-item:active { |
||||||
|
border-color: #1EA7FD; |
||||||
|
transform: translate3d(0, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
.link-item strong { |
||||||
|
font-weight: 700; |
||||||
|
display: block; |
||||||
|
margin-bottom: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
.link-item img { |
||||||
|
height: 40px; |
||||||
|
width: 40px; |
||||||
|
margin-right: 15px; |
||||||
|
flex: none; |
||||||
|
} |
||||||
|
|
||||||
|
.link-item span { |
||||||
|
font-size: 14px; |
||||||
|
line-height: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.tip { |
||||||
|
display: inline-block; |
||||||
|
border-radius: 1em; |
||||||
|
font-size: 11px; |
||||||
|
line-height: 12px; |
||||||
|
font-weight: 700; |
||||||
|
background: #E7FDD8; |
||||||
|
color: #66BF3C; |
||||||
|
padding: 4px 12px; |
||||||
|
margin-right: 10px; |
||||||
|
vertical-align: top; |
||||||
|
} |
||||||
|
|
||||||
|
.tip-wrapper { |
||||||
|
font-size: 13px; |
||||||
|
line-height: 20px; |
||||||
|
margin-top: 40px; |
||||||
|
margin-bottom: 40px; |
||||||
|
} |
||||||
|
|
||||||
|
.tip-wrapper code { |
||||||
|
font-size: 12px; |
||||||
|
display: inline-block; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
`}</style> |
||||||
|
|
||||||
|
# Welcome to Storybook |
||||||
|
|
||||||
|
Storybook helps you build UI components in isolation from your app's business logic, data, and context. |
||||||
|
That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA. |
||||||
|
|
||||||
|
Browse example stories now by navigating to them in the sidebar. |
||||||
|
View their code in the `src/stories` directory to learn how they work. |
||||||
|
We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages. |
||||||
|
|
||||||
|
<div className="subheading">Configure</div> |
||||||
|
|
||||||
|
<div className="link-list"> |
||||||
|
<a |
||||||
|
className="link-item" |
||||||
|
href="https://storybook.js.org/docs/react/addons/addon-types" |
||||||
|
target="_blank" |
||||||
|
> |
||||||
|
<img src={Plugin} alt="plugin" /> |
||||||
|
<span> |
||||||
|
<strong>Presets for popular tools</strong> |
||||||
|
Easy setup for TypeScript, SCSS and more. |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
<a |
||||||
|
className="link-item" |
||||||
|
href="https://storybook.js.org/docs/react/configure/webpack" |
||||||
|
target="_blank" |
||||||
|
> |
||||||
|
<img src={StackAlt} alt="Build" /> |
||||||
|
<span> |
||||||
|
<strong>Build configuration</strong> |
||||||
|
How to customize webpack and Babel |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
<a |
||||||
|
className="link-item" |
||||||
|
href="https://storybook.js.org/docs/react/configure/styling-and-css" |
||||||
|
target="_blank" |
||||||
|
> |
||||||
|
<img src={Colors} alt="colors" /> |
||||||
|
<span> |
||||||
|
<strong>Styling</strong> |
||||||
|
How to load and configure CSS libraries |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
<a |
||||||
|
className="link-item" |
||||||
|
href="https://storybook.js.org/docs/react/get-started/setup#configure-storybook-for-your-stack" |
||||||
|
target="_blank" |
||||||
|
> |
||||||
|
<img src={Flow} alt="flow" /> |
||||||
|
<span> |
||||||
|
<strong>Data</strong> |
||||||
|
Providers and mocking for data libraries |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="subheading">Learn</div> |
||||||
|
|
||||||
|
<div className="link-list"> |
||||||
|
<a className="link-item" href="https://storybook.js.org/docs" target="_blank"> |
||||||
|
<img src={Repo} alt="repo" /> |
||||||
|
<span> |
||||||
|
<strong>Storybook documentation</strong> |
||||||
|
Configure, customize, and extend |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
<a className="link-item" href="https://storybook.js.org/tutorials/" target="_blank"> |
||||||
|
<img src={Direction} alt="direction" /> |
||||||
|
<span> |
||||||
|
<strong>In-depth guides</strong> |
||||||
|
Best practices from leading teams |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
<a className="link-item" href="https://github.com/storybookjs/storybook" target="_blank"> |
||||||
|
<img src={Code} alt="code" /> |
||||||
|
<span> |
||||||
|
<strong>GitHub project</strong> |
||||||
|
View the source and add issues |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
<a className="link-item" href="https://discord.gg/storybook" target="_blank"> |
||||||
|
<img src={Comments} alt="comments" /> |
||||||
|
<span> |
||||||
|
<strong>Discord chat</strong> |
||||||
|
Chat with maintainers and the community |
||||||
|
</span> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="tip-wrapper"> |
||||||
|
<span className="tip">Tip</span>Edit the Markdown in{' '} |
||||||
|
<code>src/stories/Introduction.stories.mdx</code> |
||||||
|
</div> |
||||||
@ -0,0 +1,47 @@ |
|||||||
|
import React, { useState } from 'react'; |
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'; |
||||||
|
import { Modal, Button } from 'antd'; |
||||||
|
|
||||||
|
const Usage = () => { |
||||||
|
const [isModalVisible, setIsModalVisible] = useState(false); |
||||||
|
|
||||||
|
const showModal = () => { |
||||||
|
setIsModalVisible(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleOk = () => { |
||||||
|
setIsModalVisible(false); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleCancel = () => { |
||||||
|
setIsModalVisible(false); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Button type="primary" onClick={showModal}> |
||||||
|
Test Modal |
||||||
|
</Button> |
||||||
|
<Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={handleCancel}> |
||||||
|
<p>Some contents...</p> |
||||||
|
<p>Some contents...</p> |
||||||
|
<p>Some contents...</p> |
||||||
|
</Modal> |
||||||
|
</> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'owncast/Modal', |
||||||
|
component: Modal, |
||||||
|
parameters: {}, |
||||||
|
} as ComponentMeta<typeof Modal>; |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const Template: ComponentStory<typeof Modal> = args => <Usage />; |
||||||
|
|
||||||
|
export const Basic = Template.bind({}); |
||||||
|
|
||||||
|
Usage.propTypes = {}; |
||||||
|
|
||||||
|
Usage.defaultProps = {}; |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import PropTypes from 'prop-types'; |
||||||
|
import { Tabs, Radio } from 'antd'; |
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react'; |
||||||
|
|
||||||
|
const { TabPane } = Tabs; |
||||||
|
|
||||||
|
class TabsExample extends React.Component { |
||||||
|
constructor(props) { |
||||||
|
super(props); |
||||||
|
|
||||||
|
this.state = { size: 'small' }; |
||||||
|
} |
||||||
|
|
||||||
|
onChange = e => { |
||||||
|
this.setState({ size: e.target.value }); |
||||||
|
}; |
||||||
|
|
||||||
|
render() { |
||||||
|
const { size } = this.state; |
||||||
|
const { type } = this.props; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<Radio.Group value={size} onChange={this.onChange} style={{ marginBottom: 16 }}> |
||||||
|
<Radio.Button value="small">Small</Radio.Button> |
||||||
|
<Radio.Button value="default">Default</Radio.Button> |
||||||
|
<Radio.Button value="large">Large</Radio.Button> |
||||||
|
</Radio.Group> |
||||||
|
|
||||||
|
<Tabs defaultActiveKey="1" type={type} size={size}> |
||||||
|
<TabPane tab="Card Tab 1" key="1"> |
||||||
|
Content of card tab 1 |
||||||
|
</TabPane> |
||||||
|
<TabPane tab="Card Tab 2" key="2"> |
||||||
|
Content of card tab 2 |
||||||
|
</TabPane> |
||||||
|
<TabPane tab="Card Tab 3" key="3"> |
||||||
|
Content of card tab 3 |
||||||
|
</TabPane> |
||||||
|
</Tabs> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'owncast/Tabs', |
||||||
|
component: Tabs, |
||||||
|
} as ComponentMeta<typeof Tabs>; |
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Tabs> = args => <TabsExample {...args} />; |
||||||
|
|
||||||
|
export const Card = Template.bind({}); |
||||||
|
Card.args = { type: 'card' }; |
||||||
|
|
||||||
|
export const Basic = Template.bind({}); |
||||||
|
Basic.args = { type: '' }; |
||||||
|
|
||||||
|
TabsExample.propTypes = { |
||||||
|
type: PropTypes.string, |
||||||
|
}; |
||||||
|
|
||||||
|
TabsExample.defaultProps = { |
||||||
|
type: '', |
||||||
|
}; |
||||||
|
After Width: | Height: | Size: 8.3 KiB |
Loading…
Reference in new issue