-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
<!-- How to write a good PR title: - Follow [the Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). - Give as much context as necessary and as little as possible - Prefix it with [WIP] while it’s a work in progress --> ## Self Checklist - [x] I wrote a PR title in **English** and added an appropriate **label** to the PR. - [x] I wrote the commit message in **English** and to follow [**the Conventional Commits specification**](https://www.conventionalcommits.org/en/v1.0.0/). - [x] I [added the **changeset**](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md) about the changes that needed to be released. (or didn't have to) - [x] I wrote or updated **documentation** related to the changes. (or didn't have to) - [x] I wrote or updated **tests** related to the changes. (or didn't have to) - [x] I tested the changes in various browsers. (or didn't have to) - Windows: Chrome, Edge, (Optional) Firefox - macOS: Chrome, Edge, Safari, (Optional) Firefox ## Related Issue <!-- Please link to issue if one exists --> <!-- Fixes #0000 --> - Fixes #2271 - Fixes #2277 ## Summary <!-- Please brief explanation of the changes made --> - ToggleButton와 ToggleButtonGroup컴포넌트를 구현합니다. radix-ui/primitive를 사용해서 구현했고, uncontrolled/controlled모두 지원하도록 했습니다. ## Details <!-- Please elaborate description of the changes --> - ~~uncontrolled로도 사용할 수 있게 하기 위해 defaultSelected를 옵셔널로 받을 수 있게 했습니다. 다만 텍스트의 볼드 유무가 selected에 따라 바뀌다 보니, defaultSelected를 라딕스 컴포넌트에 바로 넘기지 못하고 isSelected 상태를 내부에 한번 더 선언하고 defaultSelected 와 동기화 했습니다.~~ -> ToggleButtonGroup을 구현하다보니 selected, defaultSelected 속성이 아니라 value로 선택되었는지를 판단해야해서 이렇게 하면 안될 것 같네요. Text의 bold속성을 주지 않고 css 에서 직접 font-weight을 주는 것으로 변경했습니다. - toggle 할 때 bold가 on/off되면서 너비가 약간 달라져서 레이아웃이 살짝 shift되는 현상이 있었습니다. 안보이는 상태로 렌더링되는 bold상태의 텍스트를 기준으로 너비를 정해서 이런 현상을 막았습니다. - shape 속성은 본래 ToggleButton에만 있는 속성이지만 개발의 편의성을 위해 ToggleButtonGroup에 넣어줘서 한번만 써도 되도록 했습니다. Form에서도 비슷한 접근을 하고 있어서 일관성을 해치지 않을 것 같습니다. ### Breaking change? (Yes/No) <!-- If Yes, please describe the impact and migration path for users --> - No ## References <!-- Please list any other resources or points the reviewer should be aware of --> - https://www.radix-ui.com/primitives/docs/components/toggle-group#api-reference - https://www.radix-ui.com/primitives/docs/components/toggle - [디자인 (internal)](https://www.figma.com/design/fPXP9zfjZU9NyARnhTWL6o/Input?node-id=426-1255&t=XUHjD3u6gWmZOgqj-0) - [스펙 (internal)](https://www.notion.so/channelio/Toggle-Button-1efbe7a0b118456dac4b6d2bdd36b633)
- Loading branch information
1 parent
d8964fc
commit 8f6f948
Showing
25 changed files
with
633 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@channel.io/bezier-react': patch | ||
--- | ||
|
||
Add `AlphaToggleButton` component. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@channel.io/bezier-react': patch | ||
--- | ||
|
||
Add `AlphaToggleButtonGroup` component. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
packages/bezier-react/src/components/AlphaToggleButton/AlphaToggleButton.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { ArrowRightIcon, GiftIcon } from '@channel.io/bezier-icons' | ||
import { type Meta, type StoryObj } from '@storybook/react' | ||
|
||
import { AlphaToggleButton } from '~/src/components/AlphaToggleButton' | ||
|
||
const meta = { | ||
component: AlphaToggleButton, | ||
argTypes: { | ||
onClick: { action: 'onClick' }, | ||
}, | ||
} satisfies Meta<typeof AlphaToggleButton> | ||
|
||
export default meta | ||
|
||
export const Primary = { | ||
args: { | ||
text: 'Invite', | ||
selected: false, | ||
loading: false, | ||
prefixContent: GiftIcon, | ||
suffixContent: ArrowRightIcon, | ||
size: 'm', | ||
shape: 'capsule', | ||
value: 'invite', | ||
variant: 'primary', | ||
}, | ||
} satisfies StoryObj<typeof meta> |
127 changes: 127 additions & 0 deletions
127
packages/bezier-react/src/components/AlphaToggleButton/ToggleButton.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
@use '../../styles/mixins/dimension'; | ||
@use 'sass:map'; | ||
|
||
@import '../Icon/Icon.module'; | ||
|
||
.Button { | ||
position: relative; | ||
|
||
box-sizing: border-box; | ||
max-width: 200px; | ||
|
||
color: var(--txt-black-darkest); | ||
|
||
transition: background-color var(--transition-s); | ||
|
||
/* dimension, variant, and shape */ | ||
&:where(.size-m) { | ||
height: 36px; | ||
padding: 8px 12px; | ||
|
||
& :where(.ButtonText) { | ||
padding: 1px 4px; | ||
} | ||
} | ||
|
||
&:where(.variant-primary) { | ||
color: var(--alpha-color-fg-black-darkest); | ||
background-color: var(--alpha-color-bg-grey-lightest); | ||
box-shadow: var(--alpha-shadow-input-default); | ||
} | ||
|
||
&:where(.variant-secondary) { | ||
background-color: var(--alpha-color-bg-black-lightest); | ||
} | ||
|
||
&:where(.shape-rectangle) { | ||
border-radius: var(--alpha-dimension-10); | ||
} | ||
|
||
&:where(.shape-capsule) { | ||
border-radius: 9999px; | ||
} | ||
|
||
/* visual effect on interaction */ | ||
&:where(:hover) { | ||
&:where(.variant-primary) { | ||
background-color: var(--alpha-color-bg-grey-lighter); | ||
} | ||
|
||
&:where(.variant-secondary) { | ||
background-color: var(--alpha-color-bg-black-lighter); | ||
} | ||
|
||
&:where([data-state='off']) { | ||
& :is(.ButtonIcon) { | ||
color: var(--txt-black-darker); | ||
} | ||
} | ||
} | ||
|
||
&:where([data-state='on']) { | ||
color: var(--alpha-color-fg-blue-normal); | ||
|
||
&:where(.variant-primary) { | ||
background-color: var(--alpha-color-bg-blue-lightest); | ||
box-shadow: var(--alpha-shadow-input-default); | ||
box-shadow: 0 0 0 1px var(--alpha-color-primary-bg-normal) inset; | ||
} | ||
|
||
&:where(.variant-secondary) { | ||
background-color: var(--alpha-color-primary-bg-lighter); | ||
} | ||
|
||
& :where(.ButtonText) { | ||
font-weight: var(--font-weight-700); | ||
} | ||
} | ||
|
||
&:where(:focus-visible) { | ||
box-shadow: var(--alpha-shadow-input-active); | ||
} | ||
|
||
/* internal components */ | ||
&:where([data-state='off']) :where(.ButtonIcon) { | ||
color: var(--alpha-color-fg-black-dark); | ||
} | ||
|
||
& :where(.ButtonContent) { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
|
||
&:where(.loading) { | ||
visibility: hidden; | ||
} | ||
} | ||
|
||
& :where(.ButtonLoader) { | ||
position: absolute; | ||
inset: 0; | ||
|
||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
|
||
&:where(.size-s) { | ||
& :is(.Spinner) { | ||
@include dimension.square(#{map.get($size-map, 's')}px); | ||
} | ||
} | ||
} | ||
|
||
/* NOTE: this fixes container width when bold toggles */ | ||
& :where(.ButtonText)::after { | ||
content: attr(data-text); | ||
|
||
overflow: hidden; | ||
display: block; | ||
|
||
height: 0; | ||
|
||
font-weight: var(--font-weight-700); | ||
color: transparent; | ||
|
||
visibility: hidden; | ||
} | ||
} |
117 changes: 117 additions & 0 deletions
117
packages/bezier-react/src/components/AlphaToggleButton/ToggleButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import React, { forwardRef } from 'react' | ||
|
||
import { isBezierIcon } from '@channel.io/bezier-icons' | ||
import * as TogglePrimitive from '@radix-ui/react-toggle' | ||
import classNames from 'classnames' | ||
|
||
import { AlphaSpinner } from '~/src/components/AlphaSpinner' | ||
import { useToggleButtonContext } from '~/src/components/AlphaToggleButton/ToggleButtonContext' | ||
import { BaseButton } from '~/src/components/BaseButton' | ||
import { Icon, type IconSize } from '~/src/components/Icon' | ||
import { Text } from '~/src/components/Text' | ||
|
||
import { type ToggleButtonProps } from './ToggleButton.types' | ||
|
||
import styles from './ToggleButton.module.scss' | ||
|
||
function SideContent({ | ||
size, | ||
content, | ||
}: { | ||
size: IconSize | ||
content?: ToggleButtonProps['prefixContent'] | ||
}) { | ||
return isBezierIcon(content) ? ( | ||
<Icon | ||
source={content} | ||
size={size} | ||
className={styles.ButtonIcon} | ||
/> | ||
) : ( | ||
content | ||
) | ||
} | ||
|
||
export const ToggleButton = forwardRef<HTMLButtonElement, ToggleButtonProps>( | ||
function ToggleButton( | ||
{ | ||
as = BaseButton, | ||
text, | ||
prefixContent, | ||
suffixContent, | ||
variant = 'primary', | ||
shape: shapeProps, | ||
size = 'm', | ||
className, | ||
loading, | ||
onSelectedChange, | ||
...rest | ||
}, | ||
forwardedRef | ||
) { | ||
const { shape: shapeContext } = useToggleButtonContext() | ||
const shape = shapeProps ?? shapeContext ?? 'capsule' | ||
const Comp = as as typeof BaseButton | ||
const ICON_SIZE = 's' | ||
|
||
return ( | ||
<TogglePrimitive.Root | ||
asChild | ||
onPressedChange={onSelectedChange} | ||
{...rest} | ||
> | ||
<Comp | ||
ref={forwardedRef} | ||
className={classNames( | ||
styles.Button, | ||
styles[`size-${size}`], | ||
styles[`variant-${variant}`], | ||
shape && styles[`shape-${shape}`], | ||
className | ||
)} | ||
> | ||
<div | ||
className={classNames( | ||
styles.ButtonContent, | ||
loading && styles.loading | ||
)} | ||
> | ||
<SideContent | ||
size={ICON_SIZE} | ||
content={prefixContent} | ||
/> | ||
|
||
{/* TODO: use AlphaText later, add typo */} | ||
<Text | ||
className={styles.ButtonText} | ||
typo="14" | ||
data-text={text} | ||
truncated | ||
> | ||
{text} | ||
</Text> | ||
|
||
<SideContent | ||
size={ICON_SIZE} | ||
content={suffixContent} | ||
/> | ||
</div> | ||
|
||
{loading && ( | ||
<div | ||
className={classNames( | ||
styles.ButtonLoader, | ||
styles[`size-${ICON_SIZE}`] | ||
)} | ||
> | ||
<AlphaSpinner | ||
className={styles.Spinner} | ||
variant="secondary" | ||
/> | ||
</div> | ||
)} | ||
</Comp> | ||
</TogglePrimitive.Root> | ||
) | ||
} | ||
) |
Oops, something went wrong.