Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jy/XButton, Input, Search Filter 구현 #97

Merged
merged 10 commits into from
Nov 5, 2020
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

display type을 string보다는 좀더 명확하게 (ex 'flex' | 'block') 하는게 좋습니당

}

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';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useState } from 'react';
import { text } from '@storybook/addon-knobs';
import { Dropdown } from '@components/atoms/Icons';
import SearchFilter from './SearchFilter';

export default {
component: SearchFilter,
title: 'SearchFilter',
};
export const Default = () => {
const label = text('label', 'filter');
const optionHeader = text('optionHeader', 'Filter Issues');
const options = [
'Open issues',
'Your issues',
'Everything assigned to you',
'Everything mentioning to you',
'Closed issues',
];
const Icon = Dropdown;
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.target.value);
};
const keys = [1,2,3,4,5];
return (
<SearchFilter
label={label}
optionHeader={optionHeader}
options={options}
Icon={Icon}
onClick={onClick}
keys={keys}
/>
);
};
66 changes: 66 additions & 0 deletions client/src/components/molecules/SearchFilter/SearchFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { FunctionComponent } from 'react';
import styled from '@themes/styled';

interface Props {
label: string;
optionHeader: string;
options: string[];
Icon: FunctionComponent;
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
keys: number[];
}

const StyledSummary = styled.summary`
padding: 5px 16px;
font-size: 1rem;
font-weight: 500;
line-height: 20px;
color: ${({ theme }) => theme.palette.PRIMARY};
background-color: ${({ theme }) => theme.palette.LIGHT};
border: 1px solid ${({ theme }) => theme.palette.BORDER_COLOR};
border-radius: 6px 0 0 6px;
display: inline-block;

&::-webkit-details-marker {
display: none;
}

&:hover {
background-color: ${({ theme }) => theme.palette.BG_COLOR01};
}

&:focus {
outline: none;
}
`;
const StyledUl = styled.ul``;
const StyledLi = styled.li``;
const StyledButton = styled.button``;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

비어있는 컴포넌트라면 굳이 styled를 사용할 필요 없이 <ul></ul>처럼 직접 태그를 사용해도 됩니다


const SearchFilter: FunctionComponent<Props> = ({
label,
optionHeader,
options,
Icon,
onClick,
keys,
}) => {
return (
<details>
<StyledSummary>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

summary tag 새롭게 배워갑니다~!

{label}
<Icon />
</StyledSummary>
<StyledUl>
<StyledLi>{optionHeader}</StyledLi>
{options.map((option: string, i: number) => (
<StyledLi key={keys[i]}>
<StyledButton onClick={onClick}>{option}</StyledButton>
</StyledLi>
))}
</StyledUl>
</details>
);
};

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