Skip to content

Commit

Permalink
Merge pull request #2 from zoopi-palette/feat/button
Browse files Browse the repository at this point in the history
Feat/button
  • Loading branch information
ghtea authored Apr 17, 2022
2 parents 9ba028d + f63dbf3 commit 713d83a
Show file tree
Hide file tree
Showing 11 changed files with 8,921 additions and 110 deletions.
8 changes: 7 additions & 1 deletion .babelrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
module.exports = {
presets: [
"next/babel",
[
"@babel/preset-react", {
"runtime": "automatic",
"importSource": "@emotion/react"
}
],
],
plugins: ["@emotion"]
plugins: ["@emotion/babel-plugin"]
}
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
"extends": ["next", "next/core-web-vitals"],
"extends": ["next", "next/core-web-vitals", "plugin:storybook/recommended"],
"rules": {
"indent": ["error", 2],
"quotes": ["error", "double"],
Expand Down
32 changes: 32 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = {
"stories": [
"../**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": "@storybook/react",
// ref: https://github.com/storybookjs/storybook/issues/7540#issuecomment-797495306
webpackFinal: async (config) => {
config.module.rules[0].use[0].options.presets = [
require.resolve('@babel/preset-env'),
require.resolve('@babel/preset-typescript'),
[
require.resolve('@babel/preset-react'),
{
runtime: 'automatic',
importSource: '@emotion/react',
},
],
]

config.module.rules[0].use[0].options.plugins = [
...config.module.rules[0].use[0].options.plugins,
'@emotion/babel-plugin',
]

return config
},
}
21 changes: 21 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {GlobalStyle} from "../styles/globalStyle"
import {ThemeProvider} from "../styles/theme"

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}

export const decorators = [
(Story) => (
<ThemeProvider>
<GlobalStyle/>
<Story />
</ThemeProvider>
),
];
70 changes: 70 additions & 0 deletions components/button/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {ComponentStory, ComponentMeta} from "@storybook/react";
import React from "react";

import {Button, ButtonAppearance, ButtonColor} from "./button";

const buttonColors: ButtonColor[] = ["main", "gray"]
const buttonApperances: ButtonAppearance[] = ["filled", "outline"]

export default {
title: "atoms/Button",
component: Button,
argTypes: {
size: {
options: buttonColors,
control: {type: "radio"},
},
appearance: {
options: buttonApperances,
control: {type: "radio"},
},
disabled: {
control: {type: "boolean"},
},
},
args: {
disabled: false,
size: "md",
},
} as ComponentMeta<typeof Button>;

const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;

export const Main = Template.bind({});
Main.args = {
color: "main",
children: "main",
};

export const Gray = Template.bind({});
Gray.args = {
color: "gray",
children: "gray",
};

export const AllButtons = () => {
return (
<div css={{
display: "flex",
flexDirection: "row"
}}>
{buttonColors.map(color=>(
<div key={color} css={{display: "flex", flexDirection: "column"}}>{buttonApperances.map(appearance=>(
<div key={`${color}-${appearance}`} css={{display: "flex", flexDirection: "column", padding: 8}}>
<div css={{padding: 8}} >
<Button color={color} appearance={appearance}>
{`${color}-${appearance}`}
</Button>
</div>
<div css={{padding: 8}} >
<Button color={color} appearance={appearance} disabled>
{"disabled"}
</Button>
</div>
</div>
))}</div>
))}
</div>
)
}

114 changes: 114 additions & 0 deletions components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {MouseEventHandler, ReactNode, useCallback} from "react"
import {Css, CssObject, Theme} from "styles/theme"

export type ButtonProps = {
children?: ReactNode
className?: string
disabled?: boolean
color?: ButtonColor
appearance?: ButtonAppearance
onClick?: MouseEventHandler<HTMLButtonElement>
}

export type ButtonColor = "main" | "gray"
export type ButtonAppearance = "filled" | "outline"

export const Button = ({
children,
disabled = false,
color = "gray",
appearance = "filled",
onClick,
...rest
}: ButtonProps) => {
const css: Css = useCallback((theme: Theme)=>{
const colorAndAppearanceCss: CssObject = {
borderWidth: 1,
borderStyle: "solid",
borderRadius: "12px",
...colorAppearanceCssRecord[`${color}-${appearance}`](theme)
}

const disabledCss: CssObject = {
...(disabled ? {cursor: "default", opacity: 0.4, pointerEvents: "none"} : {cursor: "pointer"}),
}

return ({
margin: 0,
padding: 0,
width: "100%",
minWidth: 200,
height: 56,
...colorAndAppearanceCss,
...disabledCss,
} as CssObject)
},[appearance, color, disabled])

const handleClick: MouseEventHandler<HTMLButtonElement> = useCallback((event)=>{
if (disabled) return;
onClick?.(event)
},[disabled, onClick])

return (
<button
onClick={handleClick}
{...rest}
css={css}
>
{children}
</button>
)
}

const colorAppearanceCssRecord: Record<`${ButtonColor}-${ButtonAppearance}`, (theme: Theme) => CssObject> = {
"gray-filled": theme => ({
borderColor: theme.colors["grey-30"],
backgroundColor: theme.colors["grey-30"],
color: theme.colors["grey-50"],
":hover": {
borderColor: "#BABBBE",
backgroundColor: "#BABBBE",
},
":focus": {
borderColor: "rgba(0, 0, 0, 0.2)",
borderWidth: 2,
}
}),
"gray-outline": theme => ({
borderColor: theme.colors["grey-40"],
backgroundColor: "transparent",
color: theme.colors["grey-90"],
":hover": {
backgroundColor: "rgba(0, 0, 0, 0.1)",
},
":focus": {
borderColor: theme.colors["grey-30"],
borderWidth: 2,
}
}),
"main-filled": theme => ({
borderColor: theme.colors.main,
backgroundColor: theme.colors.main,
color: theme.colors.white,
":hover": {
borderColor: "#CC3231",
backgroundColor: "#CC3231",
},
":focus": {
borderColor: "rgba(0, 0, 0, 0.2)",
borderWidth: 2,
}
}),
"main-outline": theme => ({
borderColor: theme.colors.main,
backgroundColor: "transparent",
color: theme.colors["grey-90"],
":hover": {
backgroundColor: "rgba(255, 62, 61, 0.1)",
},
":focus": {
borderColor: theme.colors.main,
borderWidth: 2,
}
}),
}
1 change: 1 addition & 0 deletions components/button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./button"
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"start": "next start",
"lint": "next lint",
"lint-fix": "next lint --fix",
"ts-check": "tsc --project ./tsconfig.json"
"ts-check": "tsc --project ./tsconfig.json",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"dependencies": {
"@emotion/react": "^11.9.0",
Expand All @@ -18,13 +20,22 @@
"react-dom": "18.0.0"
},
"devDependencies": {
"@babel/core": "^7.17.9",
"@emotion/babel-plugin": "^11.9.2",
"@storybook/addon-actions": "^6.4.22",
"@storybook/addon-essentials": "^6.4.22",
"@storybook/addon-interactions": "^6.4.22",
"@storybook/addon-links": "^6.4.22",
"@storybook/react": "^6.4.22",
"@storybook/testing-library": "^0.0.9",
"@types/node": "17.0.23",
"@types/react": "18.0.1",
"@types/react-dom": "18.0.0",
"babel-loader": "^8.2.4",
"eslint": "8.13.0",
"eslint-config-next": "12.1.4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-storybook": "^0.5.10",
"typescript": "4.6.3"
}
}
2 changes: 1 addition & 1 deletion pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {NextPage} from "next"
const Home: NextPage = () => {
return (
<div>
Home
test
</div>
)
}
Expand Down
14 changes: 11 additions & 3 deletions styles/theme.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {ThemeProvider as EmotionThemeProvider, ThemeProviderProps} from "@emotion/react"
import {InterpolationWithTheme, ObjectInterpolation} from "@emotion/core"
import {ThemeProvider as EmotionThemeProvider} from "@emotion/react"

const palette = {
"grey-10": "#FDFDFD",
Expand All @@ -19,11 +20,18 @@ const theme = {
sub: "#3B91F5",
error: "#FF6A3A",
success: "#4EC28A",
white: "#ffffff",
black: "#000000",
},
zIndex: {
zIndices: {
modal: 100
}
}
} as const

export type Theme = typeof theme

export type Css = InterpolationWithTheme<Theme>
export type CssObject = ObjectInterpolation<undefined>

export const ThemeProvider = ({children}: {children: React.ReactNode}) => {
return (
Expand Down
Loading

0 comments on commit 713d83a

Please sign in to comment.