Skip to content

Commit

Permalink
Merge pull request #97 from boostcamp-2020/jy/components
Browse files Browse the repository at this point in the history
Jy/XButton, Input, Search Filter 구현
  • Loading branch information
cyjo9603 authored Nov 5, 2020
2 parents 7a5ebf8 + ce01e1d commit f8a2032
Show file tree
Hide file tree
Showing 18 changed files with 508 additions and 1 deletion.
16 changes: 16 additions & 0 deletions client/src/components/atoms/Input/FilterInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React, { useState } from 'react';
import { text } from '@storybook/addon-knobs';
import FilterInput from './FilterInput';

export default {
component: FilterInput,
title: 'FilterInput',
};
export const Default = () => {
const content = text('content', 'content');
const type = text('type', 'button');
const onClick = () => {
// 버튼 메서드
};
return <FilterInput type={type} content={content} onClick={onClick} />;
};
39 changes: 39 additions & 0 deletions client/src/components/atoms/Input/FilterInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';

interface Props {
content: string;
onClick?: () => void;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
type: string;
}

const StyledInput = styled.input`
font-size: 1rem;
text-decoration: none;
border: none;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
&:focus {
outline: none;
}
`;

const FilterInput: FunctionComponent<Props> = ({
type,
content,
onClick,
onChange,
}) => {
return (
<StyledInput
type={type}
onClick={onClick}
onChange={onChange}
value={content}
/>
);
};

export default FilterInput;
24 changes: 24 additions & 0 deletions client/src/components/atoms/Input/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useState } from 'react';
import { text } from '@storybook/addon-knobs';
import Input from './Input';

export default {
component: Input,
title: 'Input',
};
export const Default = () => {
const [value, setValue] = useState('');
const placeholder = text('placeholder', 'Title');
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
const type = text('type', 'text');
return (
<Input
value={value}
placeholder={placeholder}
onChange={onChange}
type={type}
/>
);
};
45 changes: 45 additions & 0 deletions client/src/components/atoms/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';

interface Props {
placeholder?: string;
value?: string;
type: string;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const StyledInput = styled.input`
width: 100%;
font-size: 1rem;
background-color: ${({ theme }) => theme.palette.BG_COLOR01};
padding: 5px 12px;
line-height: 1.5rem;
color: ${({ theme }) => theme.palette.PRIMARY};
border: 1px solid ${({ theme }) => theme.palette.BORDER_COLOR};
border-radius: 0.3rem;
outline: none;
&:focus {
border: 1px solid ${({ theme }) => theme.palette.LINK_BLUE};
outline: none;
box-shadow: 0 0 0 3px ${({ theme }) => theme.palette.BLUE_SHADOW};
}
`;

const Input: FunctionComponent<Props> = ({
placeholder = 'Input',
value = '',
type,
onChange,
}) => {
return (
<StyledInput
type={type}
placeholder={placeholder}
value={value}
onChange={onChange}
/>
);
};

export default Input;
2 changes: 2 additions & 0 deletions client/src/components/atoms/Input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Input } from './Input';
export { default as FilterInput } from './FilterInput';
13 changes: 13 additions & 0 deletions client/src/components/atoms/icons/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React, { FunctionComponent } from 'react';

const Dropdown: FunctionComponent = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1250">
<g>
<path d="M486 761l-400 -501c-6,-8 -5,-18 3,-24 3,-3 7,-4 10,-4l802 0c9,0 17,8 17,17 0,5 -2,9 -5,12l-400 501c-6,7 -16,8 -24,2 -1,-1 -2,-2 -3,-3z" />
</g>
</svg>
);
};

export default Dropdown;
11 changes: 11 additions & 0 deletions client/src/components/atoms/icons/Reset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React, { FunctionComponent } from 'react';

const Reset: FunctionComponent = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 30">
<path d="M19.707,4.293a1,1,0,0,0-1.414,0L12,10.586,5.707,4.293A1,1,0,0,0,4.293,5.707L10.586,12,4.293,18.293a1,1,0,1,0,1.414,1.414L12,13.414l6.293,6.293a1,1,0,0,0,1.414-1.414L13.414,12l6.293-6.293A1,1,0,0,0,19.707,4.293Z" />
</svg>
);
};

export default Reset;
2 changes: 2 additions & 0 deletions client/src/components/atoms/icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Reset } from './Reset';
export { default as Dropdown } from './Dropdown';
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import { text } from '@storybook/addon-knobs';
import FilterModal from './FilterModal';

export default {
component: FilterModal,
title: 'FilterModal',
};
export const Default = () => {
const optionHeader = text('optionHeader', 'Filter Issues');
const options = [
'Open issues',
'Your issues',
'Everything assigned to you',
'Everything mentioning to you',
'Closed issues',
];
const type = 'button';
const keys = ['search1', 'search2', 'search3', 'search4', 'search5'];
const onClick = () => {
// modal button 클릭 시
};
return (
<FilterModal
display="block"
optionHeader={optionHeader}
options={options}
onClick={onClick}
type={type}
keys={keys}
/>
);
};

export const Author = () => {
const optionHeader = text('optionHeader', 'Filter by author');
const options = ['user1', 'user2', 'user3', 'user4', 'user5'];
const onClick = () => {
// modal button 클릭 시
};
const type = 'checkbox';
const keys = ['radio1', 'radio2', 'radio3', 'radio4', 'radio5'];
const input = 'Filter users';
return (
<FilterModal
display="block"
optionHeader={optionHeader}
options={options}
onClick={onClick}
type={type}
input={input}
keys={keys}
/>
);
};
75 changes: 75 additions & 0 deletions client/src/components/molecules/FilterModal/FilterModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';
import { Input, FilterInput } from '@components/atoms/Input';

interface Props {
optionHeader: string;
options: string[];
onClick?: () => void;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
input?: string;
type: string;
keys: string[];
display: string;
}

const StyledUl = styled.ul`
border-radius: 0.3rem;
list-style: none;
border: 1px solid ${({ theme }) => theme.palette.BORDER_COLOR};
max-width: 300px;
margin: 10px 0;
`;

const StyledLi = styled.li`
& > h3 {
font-weight: 600;
}
& > input {
width: 100%;
text-align: left;
}
& > input,
& > h3 {
color: ${({ theme }) => theme.palette.PRIMARY};
font-size: 0.8rem;
padding: 7px 16px;
border-bottom: 1px solid ${({ theme }) => theme.palette.BORDER_COLOR};
}
& > input:hover {
background-color: ${({ theme }) => theme.palette.BG_COLOR02};
}
& > input:focus {
outline: none;
}
`;

const SearchFilter: FunctionComponent<Props> = ({
optionHeader,
options,
onClick,
type,
input,
display,
keys,
}) => {
return (
<StyledUl style={{ display }}>
<StyledLi>
<h3>{optionHeader}</h3>
</StyledLi>
{input ? <Input placeholder={input} type="text" /> : <></>}
{options.map((option: string, i: number) => (
<StyledLi key={keys[i]}>
<FilterInput type={type} content={option} onClick={onClick} />
</StyledLi>
))}
</StyledUl>
);
};

export default SearchFilter;
1 change: 1 addition & 0 deletions client/src/components/molecules/FilterModal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './FilterModal';
20 changes: 20 additions & 0 deletions client/src/components/molecules/XButton/XButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { text } from '@storybook/addon-knobs';
import { Reset } from '@components/atoms/icons';
import XButton from './XButton';

export default {
component: XButton,
title: 'XButton',
};
export const Default = () => {
const content = text(
'content',
'Clear current search query, filters, and sorts',
);
const Icon = Reset;
const onClick = () => {
// 필터 초기화
};
return <XButton Icon={Icon} content={content} onClick={onClick} />;
};
55 changes: 55 additions & 0 deletions client/src/components/molecules/XButton/XButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';

interface Props {
Icon: FunctionComponent;
content: string;
onClick: () => void;
}

const StyledButton = styled.button`
display: flex;
align-items: center;
font-size: 1rem;
font-weight: 600;
text-align: center;
color: ${({ theme }) => theme.palette.SECONDARY};
text-decoration: none;
border: none;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
& > svg {
width: 1rem;
height: 1rem;
padding: 1px;
margin-right: 3px;
fill: ${({ theme }) => theme.palette.LIGHT};
background-color: currentColor;
border-radius: 0.3rem;
border: none;
}
&:hover {
color: ${({ theme }) => theme.palette.LINK_BLUE};
}
&:hover svg {
background-color: ${({ theme }) => theme.palette.LINK_BLUE};
}
&:focus {
outline: none;
}
`;

const XButton: FunctionComponent<Props> = ({ Icon, content, onClick }) => {
return (
<StyledButton onClick={onClick}>
<Icon />
{content}
</StyledButton>
);
};

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

0 comments on commit f8a2032

Please sign in to comment.