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

feat: Chip 컴포넌트 구현 #137

Merged
merged 12 commits into from
Aug 10, 2024
6,716 changes: 1,719 additions & 4,997 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

256 changes: 256 additions & 0 deletions src/components/Chip/Chip.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import { Canvas, Meta, Controls } from '@storybook/blocks';
import * as ChipStories from './Chip.stories';

<Meta of={ChipStories} />

# Chip

Chip은 항목을 설명하는 키워드를 사용하여 항목에 레이블을 지정하거나 분류하거나 구성합니다.
사용자는 칩을 통해 정보를 입력하거나 선택하여 콘텐츠를 필터링하거나 행동을 유발할 수 있습니다.

<Canvas of={ChipStories.Control} />
<Controls />

<br />
<br />

## ChipGroup

둘 이상의 Chip들을 조작하는 UI를 구성하고 싶다면, <code><a style={{ textDecoration: "underline" }} href="/?path=/docs/components-chipgroup--docs#chipgroup" alt="ChipGroup">ChipGroup</a></code>을 사용하세요.

<br />
<br />

## 사용법

Chip은 필수 프로퍼티로 `size` 와 `role` 을 받습니다.

- `size`: 칩의 크기를 결정합니다. 값은 `small`, `medium`, `large` 중 하나입니다.
- `role`: 칩의 용법을 결정합니다. 값은 `input`, `filter`, `suggestion` 중 하나입니다.

```tsx
import { Chip } from '@yourssu/design-system-react';
```

```tsx
<Chip size="medium" role="suggestion">
</Chip>
```

<Canvas of={ChipStories.DefaultUsage} withSource="none" />

### role

Chip 컴포넌트는 `role` 프로퍼티를 통해 칩의 용법을 결정합니다.

- `input`: 사용자가 입력한 개별적인 정보를 나타내기 위해 사용합니다.
- `filter`: 콘텐츠를 필터링할 때 사용합니다. `ChipGroup` 과 함께 사용했을 때 다중선택이 가능합니다.
- `suggestion`: 사용자가 선택할 수 있는 옵션을 제공할 때 사용합니다. `ChipGroup` 과 함께 사용했을 때 다중선택이 불가능합니다.

또한, `filter` 와 `suggestion` 의 경우 기본적으로 사용자 인터랙션에 따른 스타일이 적용됩니다.

```tsx
<Chip size="medium" role="input">input</Chip>

<Chip size="medium" role="filter">filter</Chip>

<Chip size="medium" role="suggestion">suggestion</Chip>
```

<Canvas of={ChipStories.Role} withSource="none" />

<br />
<br />

### role : input

role:input 으로 설정된 Chip 컴포넌트에서
사용자에게 인터랙션이 존재한다고 알려주어야 한다면, `onClick` 프로퍼티를 사용하세요.

```tsx
<Chip size="medium" role="input">No Interaction</Chip>

<Chip size="medium" role="input" onClick={() => {}}>Can Interaction</Chip>

<Chip size="medium" role="input" onClick={() => alert('클릭했습니다')}>Alert</Chip>
```

<Canvas of={ChipStories.RoleInput} withSource="none" />

<br />
<br />

### role : group

단일 Chip 컴포넌트에 부여할 수 없습니다.

단, `ChipGroup` 컴포넌트와 함께 사용할 때 반드시 부여해야합니다.

`ChipGroup` 내부의 Chip 컴포넌트는 마운트시 `ChipGroup` 컴포넌트의 `role` 프로퍼티를 상속받습니다.

```tsx
<Chip size="medium" role="group">
</Chip>


<ChipGroup role="suggestion">
<Chip size="medium" role="group">
</Chip>
</ChipGroup>
```

<br />
<br />

### 삭제 아이콘

`Chip.Remove` 컴포넌트를 선언하여
Chip 컴포넌트에 삭제 아이콘을 추가할 수 있습니다.

삭제 아이콘의 위치는 JSX 상의 선언 위치에 관계없이 항상 Chip 컴포넌트의 마지막에 위치합니다.
이를 보장하기 위해 칩의 콘텐츠 선언은 `Chip.Content` 컴포넌트로 감싸주는 것을 권장합니다.

```tsx
<Chip size="medium" role="group">
<Chip.Content>삭제 가능한 칩1</Chip.Content>
<Chip.Remove />
</Chip>
```

```tsx
// ✅ Chip.Remove의 선언 위치에 상관없이 삭제 아이콘은 항상 마지막에 위치하게 됩니다.
<Chip size="medium" role="group">
<Chip.Remove />
<Chip.Content>삭제 가능한 칩2</Chip.Content>
</Chip>

// ❌ 추천하지 않음
<Chip size="medium" role="group">
<Chip.Remove />
삭제 가능한 칩3
</Chip>
```

<Canvas of={ChipStories.RemovableChip} withSource="none" />

<br />
<br />

### 삭제 아이콘 : onClick

삭제 아이콘에 클릭 이벤트를 부여하고 싶다면, `Chip.Remove` 컴포넌트에 `onClick` 프로퍼티를 사용하세요.

```tsx
const Component = () => {
const onRemoveClick = (e: React.MouseEvent<HTMLDivElement>) => {
const target = e.currentTarget as HTMLDivElement;
const chip = target.closest('.chip') as HTMLDivElement;
alert(`삭제되었습니다: ${chip?.dataset.chip}`);
};

return (
<Chip size="medium" role="suggestion" data-chip="2024">
이벤트가 달린 삭제 가능한 칩
<Chip.Remove onClick={onRemoveClick} />
</Chip>
);
};
```

<Canvas of={ChipStories.EventRemovableChip} withSource="none" />

<br />
<br />

### 아이콘 추가

삭제 아이콘 외에도, Chip 컴포넌트에 원하는 아이콘을 추가할 수 있습니다.

`Chip.Icon` 컴포넌트를 사용해주세요.

```tsx
<Chip size="medium" role="suggestion">
<Chip.Icon>
<IcInfoCircleLine />
</Chip.Icon>
<Chip.Content>아이콘 추가</Chip.Content>
</Chip>
```

<Canvas of={ChipStories.IconChip} withSource="none" />

<br />
<br />

### 아이콘 추가 : position

`Chip.Icon` 컴포넌트는 `position` 프로퍼티를 통해 아이콘의 위치를 결정할 수 있습니다.

- `left`: 아이콘을 왼쪽에 배치합니다.
- `right`: 아이콘을 오른쪽에 배치합니다.

만약 `position` 프로퍼티를 선언하지 않는다면, 선언된 순서에 따라 아이콘의 위치가 결정됩니다.

이 순서를 보장하기 위해 칩의 콘텐츠 선언은 `Chip.Content` 컴포넌트로 감싸주는 것을 권장합니다.

```tsx
<Chip size="medium" role="suggestion">
<Chip.Icon position="left">
<IcInfoCircleLine />
</Chip.Icon>
<Chip.Icon position="right">
<IcArrowDownLine />
</Chip.Icon>
<Chip.Icon position="right">
<IcArrowDownLine />
</Chip.Icon>
<Chip.Content>아이콘 순서 변경</Chip.Content>
</Chip>
```

<Canvas of={ChipStories.IconOrderChip} withSource="none" />

<br />
<br />

## 예시

### disabled

인터랙션에 의한 Chip의 상태를 변경할 수 없도록 막습니다.

```tsx
<Chip size="medium" role="suggestion" disabled>
변경 불가
</Chip>
```

<Canvas of={ChipStories.ChipDisabled} withSource="none" />

<br />
<br />

### 상태 전달

체크박스의 선택 여부를 외부에서 상태로 관리할 수 있게 합니다.

```tsx
const Component = () => {
const [selected, setSelected] = useState(false);

const onClick = () => {
setSelected((prev) => !prev);
};

return (
<Chip size="medium" role="suggestion" selected={selected} onClick={onClick}>
상태 관리 칩
</Chip>
);
};
```

<Canvas of={ChipStories.StateManagement} withSource="none" />
Loading