Skip to content

Commit

Permalink
feat: 로드맵을 위한 하위 컴포넌트 구현 (#1047)
Browse files Browse the repository at this point in the history
* chore: storybook 사용을 위해 reactDocgen 에러 설정

* feat: 상위에서 크기를 제어하는 ResponsiveButton 컴포넌트 구현

* feat: LabelledImage 컴포넌트 구현

* feat: KeywordSection 컴포넌트 구현

* refactor: Omit -> Pick으로 타입 변경

* feat: fontSize를 Style Props 인자로 내려주기
  • Loading branch information
euijinkk authored and hanull committed Oct 17, 2022
1 parent 66e4d08 commit b1a0d76
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 9 deletions.
18 changes: 9 additions & 9 deletions frontend/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/preset-create-react-app',
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app"
]
}
typescript: {
reactDocgen: 'none',
},
};
54 changes: 54 additions & 0 deletions frontend/src/components/Button/ResponsiveButton.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/** @jsxImportSource @emotion/react */

import { css } from '@emotion/react';
import { COLOR } from '../../enumerations/color';
import ResponsiveButton from './ResponsiveButton';

export default {
title: 'Component/ResponsiveButton',
component: ResponsiveButton,
argTypes: {},
};

const Template = (size) => (args) => (
<div
css={css`
width: ${size}px;
`}
>
<ResponsiveButton {...args} />
</div>
);

const SessionTemplate = Template(135);

export const Session = SessionTemplate.bind({});

Session.args = {
text: '프론트엔드 세션1',
color: 'white',
backgroundColor: COLOR.LIGHT_BLUE_900,
height: '36px',
};

const Depth1Template = Template(820);

export const Depth1 = Depth1Template.bind({});

Depth1.args = {
text: 'JavaScript',
color: 'white',
backgroundColor: COLOR.LIGHT_BLUE_800,
height: '50px',
};

const Depth2Template = Template(386);

export const Depth2 = Depth2Template.bind({});

Depth2.args = {
text: 'JavaScript',
color: 'white',
backgroundColor: COLOR.LIGHT_BLUE_500,
height: '50px',
};
47 changes: 47 additions & 0 deletions frontend/src/components/Button/ResponsiveButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import React, { ButtonHTMLAttributes } from 'react';

interface ResponsiveButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
text: string;
fontSize?: string;
color?: string;
backgroundColor?: string;
height?: string;
}

const ResponsiveButton = ({
text,
fontSize,
color,
backgroundColor,
height,
}: ResponsiveButtonProps) => {
return (
<StyledResponsiveButton
fontSize={fontSize}
color={color}
backgroundColor={backgroundColor}
height={height}
>
{text}
</StyledResponsiveButton>
);
};

export default ResponsiveButton;

const StyledResponsiveButton = styled.button<
Pick<ResponsiveButtonProps, 'fontSize' | 'color' | 'backgroundColor' | 'height'>
>`
width: 100%;
border-radius: 12px;
text-align: center;
${({ fontSize, color, backgroundColor, height }) => css`
font-size: ${fontSize || '16px'};
color: ${color};
background-color: ${backgroundColor};
height: ${height || '50px'};
`};
`;
19 changes: 19 additions & 0 deletions frontend/src/components/KeywordSection/KeywordSection.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import KeywordSection from './KeywordSection';
import SomeImage from '../../assets/images/background-image.png';

export default {
title: 'Component/KeywordSection',
component: KeywordSection,
argTypes: {},
};

const Template = (args) => <KeywordSection {...args} />;

export const Selected = Template.bind({});

Selected.args = {
src: SomeImage,
alt: '이미지',
text: 'JavaScript',
isSelected: true,
};
61 changes: 61 additions & 0 deletions frontend/src/components/KeywordSection/KeywordSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import styled from '@emotion/styled';
import React, { useState } from 'react';
import SomeImage from '../../assets/images/background-image.png';
import { COLOR } from '../../enumerations/color';
import LabelledImage from '../LabelledImage/LabelledImage';

interface Keyword {
src: string;
alt: string;
text: string;
}

const keywords: Keyword[] = [
{ src: SomeImage, alt: 'JavaScript', text: 'JavaScript' },
{ src: SomeImage, alt: 'JavaScript', text: 'JavaScript' },
{ src: SomeImage, alt: 'JavaScript', text: 'JavaScript' },
{ src: SomeImage, alt: 'JavaScript', text: 'JavaScript' },
];

const KeywordSection = () => {
const [keywordOrder, setKeywordOrder] = useState(0);

const handleClickKeyword = (order: number) => {
setKeywordOrder(order);
};
return (
<StyledRoot>
{keywords.map((keyword, index) => (
<StyledWrapper>
<LabelledImage
{...keyword}
isSelected={keywordOrder === index}
onClick={() => setKeywordOrder(index)}
/>
{index + 1 !== keywords.length && <StyledArrow />}
</StyledWrapper>
))}
</StyledRoot>
);
};

export default KeywordSection;

const StyledRoot = styled.div`
display: flex;
`;

const StyledArrow = styled.div`
width: 0;
height: 0;
border: 9px solid transparent;
border-top: 0;
border-bottom: 15px solid ${COLOR.LIGHT_BLUE_700};
transform: rotate(90deg);
margin: 0 12px;
`;

const StyledWrapper = styled.div`
display: flex;
align-items: center;
`;
28 changes: 28 additions & 0 deletions frontend/src/components/LabelledImage/LabelledImage.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import LabelledImage from './LabelledImage';
import SomeImage from '../../assets/images/background-image.png';

export default {
title: 'Component/LabelledImage',
component: LabelledImage,
argTypes: {},
};

const Template = (args) => <LabelledImage {...args} />;

export const Selected = Template.bind({});

Selected.args = {
src: SomeImage,
alt: '이미지',
text: 'JavaScript',
isSelected: true,
};

export const UnSelected = Template.bind({});

UnSelected.args = {
src: SomeImage,
alt: '이미지',
text: 'JavaScript',
isSelected: false,
};
30 changes: 30 additions & 0 deletions frontend/src/components/LabelledImage/LabelledImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import React, { ImgHTMLAttributes } from 'react';
import { COLOR } from '../../enumerations/color';

interface LabelledImageProps extends ImgHTMLAttributes<HTMLImageElement> {
text: string;
isSelected?: boolean;
}

const LabelledImage = ({ src, alt, text, isSelected }: LabelledImageProps) => {
return (
<StyledRoot isSelected={isSelected}>
<img src={src} alt={alt} width="100px" height="100px" />
<h3>{text}</h3>
</StyledRoot>
);
};

export default LabelledImage;

const StyledRoot = styled.div<Pick<LabelledImageProps, 'isSelected'>>`
text-align: center;
opacity: ${({ isSelected }) => (isSelected ? 1 : 0.6)};
& > img {
border-radius: 50%;
border: ${({ isSelected }) => isSelected && `4px solid ${COLOR.LIGHT_BLUE_700}`};
}
`;

0 comments on commit b1a0d76

Please sign in to comment.