Skip to content

Commit

Permalink
feat(ui-kit): Selection 컴포넌트 추가 (#32)
Browse files Browse the repository at this point in the history
* feat(ui-kit): Selection 컴포넌트 추가

* feat(ui-kit): Selection 컴포넌트에 아이콘 추가

* feat(ui-kit): Selection 컴포넌트 스토리 추가

* feat(ui-kit): 타입 픽스

* 타입 픽스

* feat(ui-kit): Selection 컴포넌트를 Borderless로 변경

* feat(ui-kit): 리뷰 반영
  • Loading branch information
evan-moon authored Jan 10, 2021
1 parent e783e9b commit bcf8652
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 18 deletions.
8 changes: 5 additions & 3 deletions ui-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"author": "Lubycon",
"dependencies": {
"@types/classnames": "^2.2.11",
"classnames": "^2.2.6"
"classnames": "^2.2.6",
"ionicons": "^5.2.3"
},
"scripts": {
"start": "start-storybook -p 6006 --no-dll",
Expand All @@ -34,7 +35,8 @@
"clean": "rm -rf dist && mkdir dist",
"copy": "ncp package.json dist/package.json",
"copy-sass": "ncp src/sass/modules dist/sass",
"copy-version": "ncp dist/package.json package.json"
"copy-version": "ncp dist/package.json package.json",
"typecheck": "tsc --noEmit"
},
"eslintConfig": {
"extends": [
Expand Down Expand Up @@ -74,8 +76,8 @@
"@types/react-dom": "^16.9.8",
"autoprefixer": "^9.0.0",
"babel-loader": "^8.2.1",
"gh-pages": "^3.1.0",
"classnames": "^2.2.6",
"gh-pages": "^3.1.0",
"postcss": "^7.0.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
Expand Down
3 changes: 1 addition & 2 deletions ui-kit/src/components/Checkbox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { forwardRef } from 'react';
import { Ref } from 'react';
import React, { forwardRef, Ref } from 'react';
import { CombineElementProps } from 'src/types/utils';
import classnames from 'classnames';
import { generateID } from 'utils/index';
Expand Down
2 changes: 1 addition & 1 deletion ui-kit/src/components/Container/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ interface ContainerProps extends HTMLAttributes<HTMLDivElement> {
size: 'fluid' | 'sm' | 'md' | 'lg' | 'xl';
}

export default function Container({ size, ...props }: ContainerProps): JSX.Element {
export default function Container({ ...props }: ContainerProps): JSX.Element {
return <div>{props.children}</div>;
}
2 changes: 0 additions & 2 deletions ui-kit/src/components/Container/style.scss

This file was deleted.

10 changes: 8 additions & 2 deletions ui-kit/src/components/Grid/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ interface RowBaseProps {
}
type RowProps<T extends ElementType = typeof DEFAULT_ELEMENT> = OverridableProps<T, RowBaseProps>;

const Row = (
{ as, direction = 'row', justify = 'flex-start', alignItems = 'flex-start', ...props }: RowProps,
const Row = <T extends ElementType = typeof DEFAULT_ELEMENT>(
{
as,
direction = 'row',
justify = 'flex-start',
alignItems = 'flex-start',
...props
}: RowProps<T>,
ref: Ref<any>
) => {
const Component = as ?? DEFAULT_ELEMENT;
Expand Down
38 changes: 38 additions & 0 deletions ui-kit/src/components/Icon/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useMemo } from 'react';
import classnames from 'classnames';
import { Colors, colors } from 'src/constants/colors';

/**
* UI Kit 내부에서만 사용
*/

interface Props {
icon: string;
size?: number;
type: 'outline' | 'filled';
color?: Colors;
}

const Icon = ({ icon, size = 16, type, color = colors.gray100 }: Props) => {
const svgTag = useMemo(() => {
return icon.replace(/data:image\/svg\+xml;utf8,/, '');
}, [icon]);

const coloredSvg = useMemo(() => {
const targetAttr = type === 'outline' ? 'stroke' : 'fill';
return svgTag.replace(/(<path\s)\b/gm, `$1${targetAttr}="${color}" `);
}, [svgTag, color]);

return (
<span
className={classnames('lubycon-icon', {
'lubycon-icon--outline': type === 'outline',
'lubycon-icon--filled': type === 'filled',
})}
style={{ width: size, height: size }}
dangerouslySetInnerHTML={{ __html: coloredSvg }}
/>
);
};

export default Icon;
3 changes: 1 addition & 2 deletions ui-kit/src/components/Radio/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { forwardRef } from 'react';
import { Ref } from 'react';
import React, { forwardRef, Ref } from 'react';
import { CombineElementProps } from 'src/types/utils';
import clxs from 'classnames';
import { generateID } from 'src/utils/generateID';
Expand Down
63 changes: 63 additions & 0 deletions ui-kit/src/components/Selection/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { forwardRef, Ref } from 'react';
import { CombineElementProps } from 'src/types/utils';
import classnames from 'classnames';
import { chevronDownOutline } from 'ionicons/icons';
import Icon from 'components/Icon';
import { colors } from 'src/constants/colors';
import { useState } from 'react';
import { useEffect } from 'react';
import { Typographys } from '../Text/types';

interface SelectionBaseProps {
placeholder?: string;
size?: 'small' | 'medium' | 'large';
}
type SelectionProps = Omit<CombineElementProps<'select', SelectionBaseProps>, 'multiple'>;

const Selection = (
{
placeholder = '옵션을 선택하세요',
disabled,
children,
value,
onChange,
size = 'medium',
...props
}: SelectionProps,
ref: Ref<HTMLSelectElement>
) => {
const [innerValue, setInnerValue] = useState(value ?? '');
const iconColor = disabled ? colors.gray60 : colors.gray40;
const typography: Typographys = size === 'large' ? 'content' : 'content2';

useEffect(() => setInnerValue(value ?? ''), [value]);

return (
<div
className={classnames('lubycon-selection', `lubycon-selection--size-${size}`, {
'lubycon-selection--disabled': disabled,
'lubycon-selection--empty': innerValue === '',
})}
>
<select
ref={ref}
multiple={false}
{...props}
value={innerValue}
className={classnames('lubycon-selection__select', `lubycon-typography--${typography}`)}
onChange={(e) => {
onChange?.(e);
setInnerValue(e.target.value ?? '');
}}
>
<option value="" hidden={true} className="lubycon-selection__placeholder">
{placeholder}
</option>
{children}
</select>
<Icon icon={chevronDownOutline} type="outline" color={iconColor} />
</div>
);
};

export default forwardRef(Selection) as typeof Selection;
3 changes: 1 addition & 2 deletions ui-kit/src/components/Switch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { forwardRef } from 'react';
import { Ref } from 'react';
import React, { forwardRef, Ref } from 'react';
import { CombineElementProps } from 'src/types/utils';
import clxs from 'classnames';
import { generateID } from 'src/utils/generateID';
Expand Down
3 changes: 1 addition & 2 deletions ui-kit/src/components/Text/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { ElementType, Ref } from 'react';
import React, { ElementType, Ref, forwardRef } from 'react';
import { DEFAULT_ELEMENT, FontWeights, Typographys } from './types';
import { forwardRef } from 'react';
import { OverridableProps } from 'types/OverridableProps';
import clxs from 'classnames';

Expand Down
6 changes: 4 additions & 2 deletions ui-kit/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { default as Button } from './Button';
export { default as Text } from './Text';
export { Row, Column } from './Grid';
export { default as Checkbox } from './Checkbox';
export { Row, Column } from './Grid';
export { default as Radio } from './Radio';
export { default as Selection } from './Selection';
export { default as Switch } from './Switch';
export { default as Text } from './Text';
17 changes: 17 additions & 0 deletions ui-kit/src/sass/components/_Icon.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.lubycon-icon {
display: inline-block;
&--outline {
path {
fill: transparent;
}
}
&--filled {
path {
stroke: transparent;
}
}
svg {
width: 100%;
vertical-align: top;
}
}
57 changes: 57 additions & 0 deletions ui-kit/src/sass/components/_Selection.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.lubycon-selection {
position: relative;
display: inline-block;
background-color: white;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s ease-in-out;

&:hover {
background-color: get-color('gray20');
}

&--disabled {
background-color: get-color('gray40');
cursor: not-allowed;
&:hover {
background-color: get-color('gray40');
}
}

&--empty select.lubycon-selection__select {
color: get-color('gray50');
}

&--size-small {
.lubycon-selection__select {
padding: 4px 32px 4px 16px;
}
}
&--size-medium {
.lubycon-selection__select {
padding: 8px 36px 8px 16px;
}
}
&--size-large {
.lubycon-selection__select {
padding: 12px 60px 12px 20px;
}
}

.lubycon-selection__select {
width: 100%;
background-color: transparent;
appearance: none;
outline: none;
border: none;
cursor: inherit;
}

.lubycon-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
stroke: get-color('grey40');
}
}
2 changes: 2 additions & 0 deletions ui-kit/src/sass/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
@import './Column';
@import './Checkbox';
@import './Switch';
@import './Selection';
@import './Icon';
63 changes: 63 additions & 0 deletions ui-kit/src/stories/Selection.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useMemo, useState } from 'react';
import { Meta } from '@storybook/react/types-6-0';
import Selection from 'components/Selection';
import Text from 'components/Text';

export default {
title: 'Lubycon UI Kit/Selection',
} as Meta;

export const Default = () => {
const [state, setState] = useState('');

return (
<div>
<Selection onChange={(e) => setState(e.target.value)}>
<option>옵션1</option>
<option>옵션2</option>
<option>옵션3</option>
</Selection>
<Text style={{ display: 'block' }}>선택된 값은 {state}입니다.</Text>
</div>
);
};

export const Sizes = () => {
const selections = useMemo(() => {
return ['small', 'medium', 'large'].map((size) => (
<li key={size} style={{ marginBottom: 16, listStyle: ' none' }}>
<Selection placeholder={size} size={size as any}>
<option>옵션1</option>
<option>옵션2</option>
<option>옵션3</option>
</Selection>
</li>
));
}, []);
return <ul>{selections}</ul>;
};

export const Disabled = () => {
return (
<div>
<Selection disabled>
<option>옵션1</option>
<option>옵션2</option>
<option>옵션3</option>
</Selection>
</div>
);
};

export const Placeholder = () => {
return (
<div>
<Selection placeholder="어느 곳에 사시나요?">
<option>서울</option>
<option>경기</option>
<option>인천</option>
<option>충남</option>
</Selection>
</div>
);
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9408,6 +9408,11 @@ invert-kv@^1.0.0:
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=

ionicons@^5.2.3:
version "5.2.3"
resolved "https://registry.yarnpkg.com/ionicons/-/ionicons-5.2.3.tgz#af4018ea7585b1e9ae11757731c77f2f36e0c7ac"
integrity sha512-87qtgBkieKVFagwYA9Cf91B3PCahQbEOMwMt8bSvlQSgflZ4eE5qI4MGj2ZlIyadeX0dgo+0CzZsy3ow0CsBAg==

ip-regex@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
Expand Down

0 comments on commit bcf8652

Please sign in to comment.