Skip to content

Commit

Permalink
Merge pull request #96 from boostcamp-2020/cy/components
Browse files Browse the repository at this point in the history
Cy/IconText, Button 컴포넌트 구현
  • Loading branch information
cyjo9603 authored Nov 5, 2020
2 parents dfa2af4 + be18077 commit 7a5ebf8
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 0 deletions.
1 change: 1 addition & 0 deletions client/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/**/*.stories.tsx
55 changes: 55 additions & 0 deletions client/src/components/atoms/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { text } from '@storybook/addon-knobs';

import IconTag from '@components/atoms/icons/IconTag';
import IconText from '@components/molecules/TextWithIcon';
import Button from './index';

export default {
title: 'Atoms/Button',
component: Button,
};

export const Default = () => {
const content = text('text', 'default');
const disabled = text('disable', '');

return (
<Button disabled={!!disabled}>
<IconText icon={IconTag} text={content} />
</Button>
);
};

export const Error = () => {
const content = text('text', 'default');
const disabled = text('disable', '');

return (
<Button type="error" disabled={!!disabled}>
<IconText icon={IconTag} text={content} />
</Button>
);
};

export const Primary = () => {
const content = text('text', 'default');
const disabled = text('disable', '');

return (
<Button type="primary" disabled={!!disabled}>
<IconText icon={IconTag} text={content} />
</Button>
);
};

export const transparent = () => {
const content = text('text', 'default');
const disabled = text('disable', '');

return (
<Button type="transparent" disabled={!!disabled}>
<IconText icon={IconTag} text={content} />
</Button>
);
};
51 changes: 51 additions & 0 deletions client/src/components/atoms/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';

import { ButtonType } from '.';
import styleMapper from './styleMapper';

interface Props {
type?: ButtonType;
disabled?: boolean;
children: React.ReactChild;
onClick?: () => void;
}

interface StyledProps {
buttonType: ButtonType;
}

const StyledButton = styled.button<StyledProps>`
padding: 5px 16px;
border: 1px solid ${({ theme }) => theme.palette.BORDER_COLOR};
border-radius: 6px;
cursor: pointer;
${({ buttonType }) => styleMapper(buttonType)}
&:disabled {
cursor: not-allowed;
opacity: 0.8;
}
&:focus {
outline: none;
}
`;

const Button: FunctionComponent<Props> = ({
type = 'default',
disabled = false,
children,
onClick,
}) => (
<StyledButton
buttonType={type}
type={type === 'primary' ? 'submit' : 'button'}
disabled={disabled}
onClick={onClick}
>
{children}
</StyledButton>
);

export default Button;
3 changes: 3 additions & 0 deletions client/src/components/atoms/Button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default } from './Button';

export type ButtonType = 'default' | 'error' | 'primary' | 'transparent';
51 changes: 51 additions & 0 deletions client/src/components/atoms/Button/styleMapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import theme from '@themes/index';

import { ButtonType } from '.';

export default (type: ButtonType) => {
switch (type) {
case 'default':
return `
transition: filter 0.4s;
&:hover:not(:disabled) {
filter: brightness(0.95);
}
`;
case 'error':
return `
transition: background 0.4s;
color: ${theme.palette.BTN_RED};
& > span {
color: currentColor;
}
&:hover:not(:disabled) {
color: ${theme.palette.LIGHT};
background-color: ${theme.palette.BTN_RED};
}
`;
case 'primary':
return `
transition: filter 0.4s;
color: ${theme.palette.LIGHT};
background-color: ${theme.palette.BTN_GREEN};
& > span {
color: currentColor;
}
&:hover:not(:disabled) {
filter: brightness(0.9);
}
`;
case 'transparent':
return `
background-color: rgba(0,0,0,0);
border: none;
`;
default:
return '';
}
};
12 changes: 12 additions & 0 deletions client/src/components/atoms/icons/IconTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React, { FunctionComponent } from 'react';

const IconTag: FunctionComponent = () => (
<svg viewBox="0 0 16 16">
<path
fillRule="evenodd"
d="M2.5 7.775V2.75a.25.25 0 01.25-.25h5.025a.25.25 0 01.177.073l6.25 6.25a.25.25 0 010 .354l-5.025 5.025a.25.25 0 01-.354 0l-6.25-6.25a.25.25 0 01-.073-.177zm-1.5 0V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 010 2.474l-5.026 5.026a1.75 1.75 0 01-2.474 0l-6.25-6.25A1.75 1.75 0 011 7.775zM6 5a1 1 0 100 2 1 1 0 000-2z"
/>
</svg>
);

export default IconTag;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { text } from '@storybook/addon-knobs';

import IconTag from '@components/atoms/icons/IconTag';
import TextWithIcon from './index';

export default {
title: 'Molecules/TextWithIcon',
component: TextWithIcon,
};

export const Left = () => {
const content = text('content', 'default');

return <TextWithIcon text={content} icon={IconTag} />;
};

export const Right = () => {
const content = text('content', 'default');

return <TextWithIcon text={content} icon={IconTag} align="right" />;
};
54 changes: 54 additions & 0 deletions client/src/components/molecules/TextWithIcon/TextWithIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';

import { AlignType } from '.';

interface Props {
icon: FunctionComponent;
text: string;
align?: AlignType;
}

interface StyledProps {
alignIcon: AlignType;
}

const StyledTextWithIcon = styled.span<StyledProps>`
display: flex;
align-items: center;
color: ${({ theme }) => theme.palette.PRIMARY};
& > svg {
width: 1rem;
fill: currentColor;
margin: ${({ alignIcon }) =>
alignIcon === 'left' ? '0 6px 0 0' : '0 0 0 6px'};
}
& > span {
font-weight: 700;
font-size: 0.95rem;
}
`;

const TextWithIcon: FunctionComponent<Props> = ({
icon: Icon,
text,
align = 'left',
}) => (
<StyledTextWithIcon alignIcon={align}>
{align === 'left' ? (
<>
<Icon />
<span>{text}</span>
</>
) : (
<>
<span>{text}</span>
<Icon />
</>
)}
</StyledTextWithIcon>
);

export default TextWithIcon;
3 changes: 3 additions & 0 deletions client/src/components/molecules/TextWithIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default } from './TextWithIcon';

export type AlignType = 'left' | 'right';
1 change: 1 addition & 0 deletions client/src/themes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const theme = {
BG_COLOR01: '#fafbfc',
BG_COLOR02: '#f6f8fa',
BG_COLOR03: 'rgba(209,213,218,0.5)',
BORDER_COLOR: 'rgba(27,31,35,0.15)',
LIGHT_BLUE01: '#f1f8ff',
LIGHT_BLUE02: 'rgba(3,102,214,0.2)',
},
Expand Down

0 comments on commit 7a5ebf8

Please sign in to comment.