Skip to content

Commit

Permalink
✨ Select Component (#113)
Browse files Browse the repository at this point in the history
* ✨ Select Component

* 💄 style review 반영

* 💄 menulist style 수정

* 💄 style review 반영

* 💄 tag style 변경

* 🐛 multi select일때 label 텍스트클릭시 옵션 안꺼지도록 처리

* Temp

* ✨ menulist에 loading 그리기
  • Loading branch information
dohye1 committed Mar 14, 2023
1 parent b505cac commit 01095de
Show file tree
Hide file tree
Showing 15 changed files with 2,903 additions and 1,377 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@

Design system for Rubric Labs

## Todo

- [ ] Typography
- [ ] Card
- [ ] Pagination
- [ ] FilePicker
- [ ] Select

## Documentation & Storybook

Documentation is in ready.
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
"eslint-plugin-unused-imports": "^2.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-select": "^5.7.0",
"react-select-async-paginate": "^0.7.2",
"react-transition-group": "4.4.5",
"rxjs": "^7.8.0",
"shallowequal": "^1.1.0",
Expand Down
1 change: 0 additions & 1 deletion src/Layout/Box.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const commonBoxStyle = css<BoxProps>`
paddingBottom,
paddingLeft,
paddingRight,
margin,
marginTop,
marginBottom,
Expand Down
221 changes: 221 additions & 0 deletions src/Select/AsyncSelect.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { Story, Meta } from '@storybook/react';
import {
GroupBase,
MultiValue,
OptionsOrGroups,
SingleValue,
} from 'react-select';
import Select from './Select';
import { AsyncSelectProps, SelectAdditional } from './Select.types';
import { useState } from 'react';
import ActionAddIcon from '../parte-icons/Icons/ActionAddIcon';

export default {
title: 'Components/Select/AsyncSelect',
component: Select,
parameters: {
layout: 'centered',
viewport: 'responsive',
},
} as Meta;

const getOptionsAsync = (page: number): Promise<Option<string>[]> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(
Array.from({ length: 30 }).map((_, index) => ({
label: `page${page}-${index}`,
value: `page${page}-${index}`,
icon: index % 3 === 0 ? <ActionAddIcon size={12} /> : undefined,
}))
);
}, 500);
});
};

const Template: Story<
AsyncSelectProps<string> & {
isMulti?: boolean;
isError?: boolean;
}
> = ({ isMulti, ...args }) => {
const [selectedValue, setSelectedValue] = useState<
Option<string>[] | SingleValue<Option<string>> | undefined
>([...((args?.value ?? []) as Option<string>[])]);
const loadOptions = async (
search: string,
loadedOptions: OptionsOrGroups<Option<string>, GroupBase<Option<string>>>,
additional: SelectAdditional = { page: 0 }
) => {
const OPTION = await getOptionsAsync(additional.page);
return {
options: OPTION,
hasMore: true,
additional: {
...additional,
page: (additional?.page ?? 0) + 1,
},
};
};

const onChange = (
option: MultiValue<Option<string>> | SingleValue<Option<string>>
) => {
if (typeof option === 'object' && !!option) {
if (isMulti) {
if ('length' in option) {
const selected = option as Option<string>[];

setSelectedValue(selected);
} else {
const selected = option as Option<string>;

setSelectedValue([
...((selectedValue as Option<string>[]) ?? []),
selected,
]);
}

return;
}
setSelectedValue(option as SingleValue<Option<string>>);
}
};

return (
<Select
{...args}
type="async"
value={selectedValue}
onChange={onChange}
isMulti={isMulti}
loadOptions={loadOptions}
/>
);
};

export const Default = Template.bind({});
Default.args = {
isSearchable: false,
isClearable: true,
};

export const OpenMenuDefault = Template.bind({});
OpenMenuDefault.args = {
isSearchable: false,
isClearable: true,
menuIsOpen: true,
};

export const DefaultWithLabel = Template.bind({});
DefaultWithLabel.args = {
label: 'TEST',
description: '이것은 설명입니다.',
required: true,
};

export const Loading = Template.bind({});
Loading.args = {
isLoading: true,
};

export const Multi = Template.bind({});
Multi.args = {
isSearchable: false,
isClearable: true,
isMulti: true,
};

export const Error = Template.bind({});
Error.args = {
isError: true,
errorText: 'this is error',
isMulti: false,
};

export const Disabled = Template.bind({});
Disabled.args = {
isDisabled: true,
isMulti: false,
};

const GroupTemplate: Story<
AsyncSelectProps<string> & {
isMulti?: boolean;
isError?: boolean;
}
> = ({ isMulti, ...args }) => {
const loadOptions = async (
search: string,
loadedOptions: OptionsOrGroups<Option<string>, GroupBase<Option<string>>>,
additional: SelectAdditional = { page: 0 }
) => {
const OPTION = await getOptionsAsync(additional.page);

return {
options: [{ options: OPTION, label: `${additional.page + 1}번째 그룹` }],
hasMore: true,
additional: {
...additional,
page: (additional?.page ?? 0) + 1,
},
};
};

const [selectedValue, setSelectedValue] =
useState<Option<string>[] | SingleValue<Option<string>> | undefined>();

const onChange = (
option: MultiValue<Option<string>> | SingleValue<Option<string>>
) => {
if (typeof option === 'object' && !!option) {
if (isMulti) {
if ('length' in option) {
const selected = option as Option<string>[];

setSelectedValue(selected);
} else {
const selected = option as Option<string>;

setSelectedValue([
...((selectedValue as Option<string>[]) ?? []),
selected,
]);
}

return;
}
setSelectedValue(option as SingleValue<Option<string>>);
}
};

return (
<Select
{...args}
type="async"
value={selectedValue}
defaultOptions
isMulti={isMulti}
onChange={onChange}
loadOptions={loadOptions}
/>
);
};

export const GroupDefault = GroupTemplate.bind({});

GroupDefault.args = {
isSearchable: false,
};
export const OpenMenuGroupDefault = GroupTemplate.bind({});

OpenMenuGroupDefault.args = {
isSearchable: false,
menuIsOpen: true,
};

export const GroupMultiDefault = GroupTemplate.bind({});

GroupMultiDefault.args = {
isMulti: true,
};

0 comments on commit 01095de

Please sign in to comment.