mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-29 05:02:52 +08:00
storybook: add Button and Spinner examples
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { Button, CounterButton } from '@tih/ui';
|
||||
import { Button, Spinner } from '@tih/ui';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
@ -8,8 +8,8 @@ export default function HomePage() {
|
||||
<h1 className="text-center text-4xl font-bold text-red-600">
|
||||
Homepage
|
||||
</h1>
|
||||
<CounterButton />
|
||||
<Button label="Button text" size="md" variant="primary" />
|
||||
<Spinner size="md" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
@ -2,7 +2,18 @@ const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.tsx'],
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
|
||||
addons: [
|
||||
{
|
||||
name: '@storybook/addon-postcss',
|
||||
options: {
|
||||
postcssLoaderOptions: {
|
||||
implementation: require('postcss'),
|
||||
},
|
||||
},
|
||||
},
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
],
|
||||
framework: '@storybook/react',
|
||||
core: {
|
||||
builder: '@storybook/builder-vite',
|
||||
|
@ -1 +1,6 @@
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '@tih/ui/dist/styles.css';
|
||||
|
||||
export const parameters = {
|
||||
controls: { expanded: true },
|
||||
};
|
||||
|
@ -14,17 +14,21 @@
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@heroicons/react": "^2.0.11",
|
||||
"@storybook/addon-actions": "^6.4.18",
|
||||
"@storybook/addon-docs": "^6.4.22",
|
||||
"@storybook/addon-essentials": "^6.4.18",
|
||||
"@storybook/addon-links": "^6.4.18",
|
||||
"@storybook/addon-postcss": "^2.0.0",
|
||||
"@storybook/builder-vite": "^0.1.33",
|
||||
"@storybook/react": "^6.4.18",
|
||||
"@tih/tailwind-config": "*",
|
||||
"@tih/tsconfig": "*",
|
||||
"@vitejs/plugin-react": "^1.3.2",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"eslint-config-tih": "*",
|
||||
"serve": "^13.0.2",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"typescript": "^4.8.3",
|
||||
"vite": "^2.9.9"
|
||||
}
|
||||
|
@ -1,13 +1,6 @@
|
||||
// If you want to use other PostCSS plugins, see the following:
|
||||
// https://tailwindcss.com/docs/using-with-preprocessors
|
||||
|
||||
const config = require('@tih/tailwind-config/tailwind.config.js');
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
// Specifying the config is not necessary in most cases, but it is included
|
||||
// here to share the same config across the entire monorepo
|
||||
tailwindcss: { config },
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
@ -1,33 +1,243 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { ComponentMeta } from '@storybook/react';
|
||||
|
||||
import { Button } from '@tih/ui';
|
||||
import {
|
||||
Button,
|
||||
ButtonAddOnPosition,
|
||||
ButtonDisplay,
|
||||
ButtonSize,
|
||||
ButtonType,
|
||||
ButtonVariant,
|
||||
} from '@tih/ui';
|
||||
import React from 'react';
|
||||
import { EnvelopeIcon } from '@heroicons/react/24/solid';
|
||||
|
||||
const buttonTypes: ReadonlyArray<ButtonType> = ['button', 'reset', 'submit'];
|
||||
const buttonSizes: ReadonlyArray<ButtonSize> = ['sm', 'md', 'lg'];
|
||||
const buttonAddOnPositions: ReadonlyArray<ButtonAddOnPosition> = [
|
||||
'start',
|
||||
'end',
|
||||
];
|
||||
const buttonDisplays: ReadonlyArray<ButtonDisplay> = ['block', 'inline'];
|
||||
const buttonVariants: ReadonlyArray<ButtonVariant> = [
|
||||
'primary',
|
||||
'secondary',
|
||||
'tertiary',
|
||||
'special',
|
||||
'success',
|
||||
];
|
||||
|
||||
//👇 This default export determines where your story goes in the story list
|
||||
export default {
|
||||
/* 👇 The title prop is optional.
|
||||
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
|
||||
* to learn how to generate automatic titles
|
||||
*/
|
||||
title: 'Button',
|
||||
component: Button,
|
||||
argTypes: {
|
||||
addonPosition: {
|
||||
options: buttonAddOnPositions,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
display: {
|
||||
options: buttonDisplays,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
isDisabled: {
|
||||
control: 'boolean',
|
||||
},
|
||||
isLoading: {
|
||||
control: 'boolean',
|
||||
},
|
||||
label: {
|
||||
control: 'string',
|
||||
},
|
||||
size: {
|
||||
options: buttonSizes,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
type: {
|
||||
options: buttonTypes,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
variant: {
|
||||
options: buttonVariants,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof Button>;
|
||||
|
||||
//👇 We create a “template” of how args map to rendering
|
||||
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
|
||||
|
||||
export const PrimaryButton = Template.bind({});
|
||||
|
||||
PrimaryButton.args = {
|
||||
label: 'Button text',
|
||||
size: 'md',
|
||||
variant: 'primary',
|
||||
export const Basic = {
|
||||
args: {
|
||||
label: 'Click Me',
|
||||
size: 'md',
|
||||
variant: 'primary',
|
||||
},
|
||||
};
|
||||
|
||||
export const SecondaryButton = Template.bind({});
|
||||
export function Variant() {
|
||||
return (
|
||||
<div className="space-x-4">
|
||||
{buttonVariants.map((variant) => (
|
||||
<Button key={variant} label="Click Me" size="md" variant={variant} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SecondaryButton.args = {
|
||||
label: 'Button text',
|
||||
size: 'md',
|
||||
variant: 'secondary',
|
||||
};
|
||||
export function Size() {
|
||||
return (
|
||||
<div className="space-x-4">
|
||||
{buttonSizes.map((size) => (
|
||||
<Button key={size} label="Click Me" size={size} variant="primary" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Display() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{buttonSizes.map((size) => (
|
||||
<Button
|
||||
key={size}
|
||||
display="block"
|
||||
label="Click Me"
|
||||
size={size}
|
||||
variant="primary"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Disabled() {
|
||||
return (
|
||||
<div className="space-x-4">
|
||||
{buttonVariants.map((variant) => (
|
||||
<Button
|
||||
isDisabled={true}
|
||||
key={variant}
|
||||
label="Click Me"
|
||||
size="md"
|
||||
variant={variant}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Loading() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-x-4">
|
||||
{buttonVariants.map((variant) => (
|
||||
<Button
|
||||
isLoading={true}
|
||||
key={variant}
|
||||
label="Click Me"
|
||||
size="md"
|
||||
variant={variant}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="space-x-4">
|
||||
{buttonVariants.map((variant) => (
|
||||
<Button
|
||||
isDisabled={true}
|
||||
isLoading={true}
|
||||
key={variant}
|
||||
label="Click Me"
|
||||
size="md"
|
||||
variant={variant}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Icons() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-x-4">
|
||||
{buttonSizes.map((size) => (
|
||||
<Button
|
||||
key={size}
|
||||
icon={EnvelopeIcon}
|
||||
label="Click Me"
|
||||
size={size}
|
||||
variant="primary"
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
icon={EnvelopeIcon}
|
||||
isDisabled={true}
|
||||
label="Click Me"
|
||||
size="lg"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-x-4">
|
||||
{buttonSizes.map((size) => (
|
||||
<Button
|
||||
key={size}
|
||||
addonPosition="start"
|
||||
icon={EnvelopeIcon}
|
||||
label="Click Me"
|
||||
size={size}
|
||||
variant="primary"
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
addonPosition="start"
|
||||
icon={EnvelopeIcon}
|
||||
isDisabled={true}
|
||||
label="Click Me"
|
||||
size="lg"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{buttonSizes.map((size) => (
|
||||
<Button
|
||||
key={size}
|
||||
display="block"
|
||||
icon={EnvelopeIcon}
|
||||
label="Click Me"
|
||||
size={size}
|
||||
variant="primary"
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
display="block"
|
||||
icon={EnvelopeIcon}
|
||||
isDisabled={true}
|
||||
label="Click Me"
|
||||
size="lg"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function HiddenLabel() {
|
||||
return (
|
||||
<div className="space-x-4">
|
||||
{buttonSizes.map((size) => (
|
||||
<Button
|
||||
key={size}
|
||||
icon={EnvelopeIcon}
|
||||
isLabelHidden={true}
|
||||
label="Click Me"
|
||||
size={size}
|
||||
variant="primary"
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
icon={EnvelopeIcon}
|
||||
isDisabled={true}
|
||||
isLabelHidden={true}
|
||||
label="Click Me"
|
||||
size="lg"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { CounterButton } from '@tih/ui';
|
||||
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||
|
||||
<Meta title="Components/Button" component={CounterButton} />
|
||||
|
||||
# Button
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nisl eget consectetur tempor, nisl nunc egestas nisi, euismod aliquam nisl nunc euismod.
|
||||
|
||||
## Props
|
||||
|
||||
<ArgsTable of={CounterButton} />
|
||||
|
||||
## Examples
|
||||
|
||||
<Canvas>
|
||||
<Story name="Default">
|
||||
<CounterButton>Hello</CounterButton>
|
||||
</Story>
|
||||
</Canvas>
|
57
apps/storybook/stories/spinner.stories.tsx
Normal file
57
apps/storybook/stories/spinner.stories.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { ComponentMeta } from '@storybook/react';
|
||||
|
||||
import { Spinner, SpinnerColor, SpinnerSize, SpinnerDisplay } from '@tih/ui';
|
||||
import React from 'react';
|
||||
|
||||
const spinnerColors: ReadonlyArray<SpinnerColor> = ['default', 'inherit'];
|
||||
const spinnerDisplays: ReadonlyArray<SpinnerDisplay> = ['block', 'inline'];
|
||||
const spinnerSizes: ReadonlyArray<SpinnerSize> = ['xs', 'sm', 'md', 'lg'];
|
||||
|
||||
export default {
|
||||
title: 'Spinner',
|
||||
component: Spinner,
|
||||
argTypes: {
|
||||
color: {
|
||||
options: spinnerColors,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
display: {
|
||||
options: spinnerDisplays,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
label: {
|
||||
control: 'string',
|
||||
},
|
||||
size: {
|
||||
options: spinnerSizes,
|
||||
control: { type: 'select' },
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof Spinner>;
|
||||
|
||||
export const Basic = {
|
||||
args: {
|
||||
label: 'Loading data',
|
||||
size: 'md',
|
||||
},
|
||||
};
|
||||
|
||||
export function Size() {
|
||||
return (
|
||||
<div className="space-x-4">
|
||||
{spinnerSizes.map((size) => (
|
||||
<Spinner key={size} label="Loading..." size={size} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Display() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{spinnerSizes.map((size) => (
|
||||
<Spinner key={size} display="block" label="Loading..." size={size} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
7
apps/storybook/tailwind.config.js
Normal file
7
apps/storybook/tailwind.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
const config = require('@tih/tailwind-config/tailwind.config.js');
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
...config,
|
||||
content: [...config.content, './stories/**/*.{js,jsx,ts,tsx,md,mdx}'],
|
||||
};
|
Reference in New Issue
Block a user