From aaa19f03dd334c30ccbc0958563875308ee2b9b7 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 07:56:39 +0900 Subject: [PATCH 1/8] feat(icon-button): copy button style --- .../AlphaIconButton/IconButton.module.scss | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss new file mode 100644 index 0000000000..eb70869726 --- /dev/null +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss @@ -0,0 +1,267 @@ +$chromatic-colors: 'blue', 'red', 'green', 'cobalt', 'orange', 'pink', 'purple'; + +.Button { + position: relative; + box-sizing: border-box; + transition: background-color var(--transition-s); + + /* dimension */ + &:where(.size-xs) { + height: 20px; + padding: 0 4px; + + & :where(.ButtonText) { + padding: 0 3px; + } + } + + &:where(.size-s) { + height: 24px; + padding: 0 6px; + + & :where(.ButtonText) { + padding: 0 3px; + } + + & :where(.ButtonContent) { + gap: 1px; + } + } + + &:where(.size-m) { + height: 36px; + padding: 0 10px; + + & :where(.ButtonText) { + padding: 0 4px; + } + } + + &:where(.size-l) { + height: 44px; + padding: 0 14px; + + & :where(.ButtonText) { + padding: 0 6px; + } + } + + &:where(.size-xl) { + height: 54px; + padding: 0 18px; + + & :where(.ButtonText) { + padding: 0 6px; + } + } + + /* background-color */ + &:where(.variant-primary) { + $background-color-by-color: ( + blue: var(--alpha-color-primary-bg-normal), + cobalt: var(--alpha-color-accent-bg-normal), + red: var(--alpha-color-critical-bg-normal), + orange: var(--alpha-color-warning-bg-normal), + green: var(--alpha-color-success-bg-normal), + pink: var(--alpha-color-bg-pink-normal), + purple: var(--alpha-color-bg-purple-normal), + dark-grey: var(--alpha-color-bg-grey-darkest), + light-grey: var(--alpha-color-bg-black-dark), + ); + + @each $color, $background-color in $background-color-by-color { + &:where(.color-#{$color}) { + background-color: $background-color; + } + } + } + + &:where(.variant-secondary) { + $background-color-by-color: ( + blue: var(--alpha-color-primary-bg-lightest), + cobalt: var(--alpha-color-accent-bg-lightest), + red: var(--alpha-color-critical-bg-lightest), + orange: var(--alpha-color-warning-bg-lightest), + green: var(--alpha-color-success-bg-lightest), + pink: var(--alpha-color-bg-pink-lightest), + purple: var(--alpha-color-bg-purple-lightest), + dark-grey: var(--alpha-color-bg-black-lighter), + light-grey: var(--alpha-color-bg-black-lighter), + ); + + @each $color, $background-color in $background-color-by-color { + &:where(.color-#{$color}) { + background-color: $background-color; + } + } + } + + &:where(.variant-tertiary) { + background-color: initial; + } + + /* color */ + /* stylelint-disable-next-line no-duplicate-selectors */ + &:where(.variant-primary) { + color: var(--alpha-color-fg-absolute-white-dark); + + &:where(.color-dark-grey) { + color: var(--alpha-color-fg-white-normal); + } + + &:where(.color-light-grey) { + color: var(--alpha-color-fg-absolute-white-normal); + } + } + + &:where(.variant-secondary, .variant-tertiary) { + $color-map: ( + blue: var(--alpha-color-primary-fg-normal), + cobalt: var(--alpha-color-accent-fg-normal), + red: var(--alpha-color-critical-fg-normal), + orange: var(--alpha-color-warning-fg-normal), + green: var(--alpha-color-success-fg-normal), + pink: var(--alpha-color-fg-pink-normal), + purple: var(--alpha-color-fg-purple-normal), + dark-grey: var(--alpha-color-fg-black-darkest), + light-grey: var(--alpha-color-fg-black-darker), + ); + + @each $button-color, $color in $color-map { + &:where(.color-#{$button-color}) { + color: $color; + } + } + + &:where(.color-dark-grey) { + & :where(.ButtonIcon) { + color: var(--alpha-color-fg-black-darker); + } + } + + &:where(.color-light-grey) { + & :where(.ButtonIcon) { + color: var(--alpha-color-fg-black-dark); + } + } + } + + &:where(.variant-tertiary.color-white) { + & :where(.ButtonIcon) { + color: var(--alpha-color-fg-absolute-white-light); + } + + & :where(.ButtonText) { + color: var(--alpha-color-fg-absolute-white-normal); + } + } + + /* border-radius */ + &:where(.variant-primary, .variant-secondary, .variant-tertiary) { + $border-radius-by-size: ( + xs: var(--alpha-dimension-6), + s: var(--alpha-dimension-7), + m: var(--alpha-dimension-10), + l: var(--alpha-dimension-12), + xl: var(--alpha-dimension-14), + ); + + @each $size, $border-radius in $border-radius-by-size { + &:where(.size-#{$size}) { + border-radius: $border-radius; + } + } + } + + /* TODO: use v2 token when design is specified */ + + /* visual effect on interaction */ + &:where(.active, :hover):where(:not(:disabled)) { + &:where(.variant-primary) { + @each $color in $chromatic-colors { + &:where(.color-#{$color}) { + background-color: var(--bgtxt-#{$color}-dark); + } + } + + &:where(.color-dark-grey) { + background-color: var(--bg-grey-darkest); + } + + &:where(.color-light-grey) { + background-color: var(--bg-black-darker); + } + } + + &:where(.variant-secondary) { + @each $color in $chromatic-colors { + &:where(.color-#{$color}) { + background-color: var(--bgtxt-#{$color}-lighter); + } + } + + &:where(.color-dark-grey, .color-light-grey) { + background-color: var(--bg-black-light); + } + } + + &:where(.variant-tertiary) { + @each $color in $chromatic-colors { + &:where(.color-#{$color}) { + background-color: var(--bgtxt-#{$color}-lightest); + } + } + + &:where(.color-dark-grey, .color-light-grey, .color-white) { + background-color: var(--bg-black-lighter); + } + } + + &:where(.color-dark-grey):where(.variant-secondary, .variant-tertiary) { + & :is(.ButtonIcon, .ButtonLoader) { + color: var(--txt-black-darkest); + } + } + + &:where(.color-light-grey):where(.variant-secondary, .variant-tertiary) { + & :is(.ButtonIcon, .ButtonLoader) { + color: var(--txt-black-darker); + } + } + + &:where(.color-white.variant-tertiary) { + & :where(.ButtonIcon) { + color: var(--alpha-color-fg-absolute-white-normal); + } + } + } + + &:where(.variant-primary.color-blue:focus-visible) { + outline: 3px solid var(--bgtxt-blue-light); + } + + &:disabled { + cursor: not-allowed; + opacity: var(--alpha-opacity-disabled); + } + + /* internal components */ + .ButtonContent { + display: flex; + align-items: center; + justify-content: center; + + &:where(.loading) { + visibility: hidden; + } + } + + .ButtonLoader { + position: absolute; + inset: 0; + + display: flex; + align-items: center; + justify-content: center; + } +} From 6e2dd6f02d2d59cfff82b02bf05e2d1051ef7d28 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 07:57:31 +0900 Subject: [PATCH 2/8] feat(icon-button): modify icon button style (dimension, radius) --- .../AlphaIconButton/IconButton.module.scss | 78 +++++++------------ 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss index eb70869726..029a8fa27b 100644 --- a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss @@ -1,58 +1,36 @@ +@use '../../styles/mixins/dimension'; + $chromatic-colors: 'blue', 'red', 'green', 'cobalt', 'orange', 'pink', 'purple'; -.Button { +.IconButton { position: relative; box-sizing: border-box; transition: background-color var(--transition-s); /* dimension */ &:where(.size-xs) { - height: 20px; - padding: 0 4px; - - & :where(.ButtonText) { - padding: 0 3px; - } + @include dimension.square(20px); + padding: 4px; } &:where(.size-s) { - height: 24px; - padding: 0 6px; - - & :where(.ButtonText) { - padding: 0 3px; - } - - & :where(.ButtonContent) { - gap: 1px; - } + @include dimension.square(24px); + padding: 4px; } &:where(.size-m) { - height: 36px; - padding: 0 10px; - - & :where(.ButtonText) { - padding: 0 4px; - } + @include dimension.square(36px); + padding: 8px; } &:where(.size-l) { - height: 44px; - padding: 0 14px; - - & :where(.ButtonText) { - padding: 0 6px; - } + @include dimension.square(44px); + padding: 12px; } &:where(.size-xl) { - height: 54px; - padding: 0 18px; - - & :where(.ButtonText) { - padding: 0 6px; - } + @include dimension.square(54px); + padding: 15px; } /* background-color */ @@ -148,31 +126,29 @@ $chromatic-colors: 'blue', 'red', 'green', 'cobalt', 'orange', 'pink', 'purple'; &:where(.variant-tertiary.color-white) { & :where(.ButtonIcon) { - color: var(--alpha-color-fg-absolute-white-light); - } - - & :where(.ButtonText) { color: var(--alpha-color-fg-absolute-white-normal); } } /* border-radius */ - &:where(.variant-primary, .variant-secondary, .variant-tertiary) { - $border-radius-by-size: ( - xs: var(--alpha-dimension-6), - s: var(--alpha-dimension-7), - m: var(--alpha-dimension-10), - l: var(--alpha-dimension-12), - xl: var(--alpha-dimension-14), - ); + $border-radius-by-size: ( + xs: var(--alpha-dimension-6), + s: var(--alpha-dimension-7), + m: var(--alpha-dimension-10), + l: var(--alpha-dimension-12), + xl: var(--alpha-dimension-14), + ); - @each $size, $border-radius in $border-radius-by-size { - &:where(.size-#{$size}) { - border-radius: $border-radius; - } + @each $size, $border-radius in $border-radius-by-size { + &:where(.size-#{$size}) { + border-radius: $border-radius; } } + &:where(.shape-circle) { + border-radius: 9999px; + } + /* TODO: use v2 token when design is specified */ /* visual effect on interaction */ From 89742af8c8e6eecc3c3c944e7d978ce76263e175 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 08:36:11 +0900 Subject: [PATCH 3/8] feat(icon-button): add type, component, storybook --- .../AlphaIconButton.stories.tsx | 29 ++++++ .../components/AlphaIconButton/IconButton.tsx | 95 +++++++++++++++++++ .../AlphaIconButton/IconButton.types.ts | 74 +++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx create mode 100644 packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx create mode 100644 packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts diff --git a/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx b/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx new file mode 100644 index 0000000000..4ba740482d --- /dev/null +++ b/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx @@ -0,0 +1,29 @@ +import { PlusIcon } from '@channel.io/bezier-icons' +import { type Meta, type StoryObj } from '@storybook/react' + +import { + AlphaIconButton, + type AlphaIconButtonProps, +} from '~/src/components/AlphaIconButton' + +const meta: Meta = { + component: AlphaIconButton, + argTypes: { + onClick: { action: 'onClick' }, + }, +} +export default meta + +export const Playground: StoryObj = { + args: { + text: 'Invite', + disabled: false, + active: false, + loading: false, + icon: PlusIcon, + shape: 'rectangle', + size: 'm', + variant: 'primary', + color: 'blue', + }, +} diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx b/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx new file mode 100644 index 0000000000..c9658f2abf --- /dev/null +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx @@ -0,0 +1,95 @@ +import React, { forwardRef } from 'react' + +import classNames from 'classnames' + +import { type AlphaIconButtonProps } from '~/src/components/AlphaIconButton' +import { BaseButton } from '~/src/components/BaseButton' +import { type ButtonSize } from '~/src/components/Button' +import { Icon } from '~/src/components/Icon' +import { Spinner } from '~/src/components/Spinner' + +import styles from './IconButton.module.scss' + +function getIconSize(size: ButtonSize) { + return ( + { + xs: 'xxs', + s: 'xs', + m: 's', + l: 's', + xl: 'm', + } as const + )[size] +} + +function getSpinnerSize(size: ButtonSize) { + return ( + { + xs: 'xs', + s: 'xs', + m: 's', + l: 's', + xl: 's', + } as const + )[size] +} + +export const IconButton = forwardRef( + function IconButton( + { + as = BaseButton, + text, + color = 'blue', + variant = 'primary', + size = 'm', + disabled, + active, + shape = 'rectangle', + icon, + loading, + className, + ...rest + }, + forwardedRef + ) { + const Comp = as as typeof BaseButton + + return ( + +
+ {icon && ( + + )} +
+ + {/* TODO: use AlphaSpinner */} + {loading && ( +
+ +
+ )} +
+ ) + } +) diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts b/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts new file mode 100644 index 0000000000..ac3613577c --- /dev/null +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts @@ -0,0 +1,74 @@ +import { type BezierIcon } from '@channel.io/bezier-icons' + +import { + type BezierComponentProps, + type DisableProps, + type PolymorphicProps, + type SizeProps, +} from '~/src/types/props' + +type IconButtonVariant = 'primary' | 'secondary' | 'tertiary' + +type IconButtonColor = + | 'blue' + | 'cobalt' + | 'red' + | 'orange' + | 'green' + | 'pink' + | 'purple' + | 'dark-grey' + | 'light-grey' + | 'white' + +type IconButtonSize = 'xs' | 's' | 'm' | 'l' | 'xl' + +interface IconButtonOwnProps { + /** + * The text content in the button. + */ + text: string + + /** + * If `loading` is true, spinner will be shown, replacing the content. + * @default false + */ + loading?: boolean + + /** + * If `active` is true, the button will be styled as if it is hovered. + * You may want to use this prop for a button which opens dropdown, etc. + * @default false + */ + active?: boolean + + /** + * Types of visual styles for button. + * @default 'primary' + */ + variant?: IconButtonVariant + + /** + * Color of the button. + * @default 'blue' + */ + color?: IconButtonColor + + /** + * Icon in the button. + */ + icon?: BezierIcon + + /** + * Shape of the button. + * @default 'rectangle' + */ + shape?: 'rectangle' | 'circle' +} + +export interface ButtonProps + extends Omit, 'color'>, + PolymorphicProps, + SizeProps, + DisableProps, + IconButtonOwnProps {} From a03c7158a8657b5b936a338b1d9251358b5cd658 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 08:37:41 +0900 Subject: [PATCH 4/8] feat(icon-button): export component --- packages/bezier-react/src/components/AlphaIconButton/index.ts | 2 ++ packages/bezier-react/src/index.ts | 1 + 2 files changed, 3 insertions(+) create mode 100644 packages/bezier-react/src/components/AlphaIconButton/index.ts diff --git a/packages/bezier-react/src/components/AlphaIconButton/index.ts b/packages/bezier-react/src/components/AlphaIconButton/index.ts new file mode 100644 index 0000000000..c2eeecdc11 --- /dev/null +++ b/packages/bezier-react/src/components/AlphaIconButton/index.ts @@ -0,0 +1,2 @@ +export { IconButton as AlphaIconButton } from './IconButton' +export type { ButtonProps as AlphaIconButtonProps } from './IconButton.types' diff --git a/packages/bezier-react/src/index.ts b/packages/bezier-react/src/index.ts index 0ec0daff85..eaa0c97ccc 100644 --- a/packages/bezier-react/src/index.ts +++ b/packages/bezier-react/src/index.ts @@ -10,6 +10,7 @@ export * from '~/src/components/AlphaAvatarGroup' export * from '~/src/components/AlphaButton' export * from '~/src/components/AlphaDialogPrimitive' export * from '~/src/components/AlphaFloatingButton' +export * from '~/src/components/AlphaIconButton' export * from '~/src/components/AlphaTooltipPrimitive' export * from '~/src/components/AppProvider' export * from '~/src/components/AutoFocus' From 0890b75ba1c2b5d34700c6309b9da82388f09351 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 09:29:38 +0900 Subject: [PATCH 5/8] chore(changeset): add --- .changeset/pink-paws-eat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/pink-paws-eat.md diff --git a/.changeset/pink-paws-eat.md b/.changeset/pink-paws-eat.md new file mode 100644 index 0000000000..b3045ed97f --- /dev/null +++ b/.changeset/pink-paws-eat.md @@ -0,0 +1,5 @@ +--- +"@channel.io/bezier-react": patch +--- + +Add `AlphaIconButton` component. From eb894b4dc5c8bc083f95efeb1b98c1c298e6a3b8 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 09:34:21 +0900 Subject: [PATCH 6/8] style(icon-button): resolve stylelint error --- .../src/components/AlphaIconButton/IconButton.module.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss index 029a8fa27b..17ad3b9bd7 100644 --- a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss @@ -10,26 +10,31 @@ $chromatic-colors: 'blue', 'red', 'green', 'cobalt', 'orange', 'pink', 'purple'; /* dimension */ &:where(.size-xs) { @include dimension.square(20px); + padding: 4px; } &:where(.size-s) { @include dimension.square(24px); + padding: 4px; } &:where(.size-m) { @include dimension.square(36px); + padding: 8px; } &:where(.size-l) { @include dimension.square(44px); + padding: 12px; } &:where(.size-xl) { @include dimension.square(54px); + padding: 15px; } @@ -131,6 +136,7 @@ $chromatic-colors: 'blue', 'red', 'green', 'cobalt', 'orange', 'pink', 'purple'; } /* border-radius */ + /* stylelint-disable-next-line order/order */ $border-radius-by-size: ( xs: var(--alpha-dimension-6), s: var(--alpha-dimension-7), From f96f944f2eff2839b0349870666e926595a7f0fb Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 09:51:03 +0900 Subject: [PATCH 7/8] feat(icon-button): rm text from props, add aria-label to storybook --- .../components/AlphaIconButton/AlphaIconButton.stories.tsx | 2 +- .../src/components/AlphaIconButton/IconButton.types.ts | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx b/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx index 4ba740482d..b69df31c9e 100644 --- a/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx +++ b/packages/bezier-react/src/components/AlphaIconButton/AlphaIconButton.stories.tsx @@ -16,12 +16,12 @@ export default meta export const Playground: StoryObj = { args: { - text: 'Invite', disabled: false, active: false, loading: false, icon: PlusIcon, shape: 'rectangle', + 'aria-label': 'invite', size: 'm', variant: 'primary', color: 'blue', diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts b/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts index ac3613577c..8ed92ebcb2 100644 --- a/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.types.ts @@ -24,11 +24,6 @@ type IconButtonColor = type IconButtonSize = 'xs' | 's' | 'm' | 'l' | 'xl' interface IconButtonOwnProps { - /** - * The text content in the button. - */ - text: string - /** * If `loading` is true, spinner will be shown, replacing the content. * @default false From a790bc0087dcbd97736c3496dfdf91aadcf739e3 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 8 May 2024 10:26:42 +0900 Subject: [PATCH 8/8] chore(icon-button): resolve ci fail --- .../AlphaIconButton/IconButton.module.scss | 25 ++++++++++--------- .../components/AlphaIconButton/IconButton.tsx | 1 - 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss index 17ad3b9bd7..517d5ddabf 100644 --- a/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.module.scss @@ -136,18 +136,19 @@ $chromatic-colors: 'blue', 'red', 'green', 'cobalt', 'orange', 'pink', 'purple'; } /* border-radius */ - /* stylelint-disable-next-line order/order */ - $border-radius-by-size: ( - xs: var(--alpha-dimension-6), - s: var(--alpha-dimension-7), - m: var(--alpha-dimension-10), - l: var(--alpha-dimension-12), - xl: var(--alpha-dimension-14), - ); - - @each $size, $border-radius in $border-radius-by-size { - &:where(.size-#{$size}) { - border-radius: $border-radius; + &:where(.shape-rectangle) { + $border-radius-by-size: ( + xs: var(--alpha-dimension-6), + s: var(--alpha-dimension-7), + m: var(--alpha-dimension-10), + l: var(--alpha-dimension-12), + xl: var(--alpha-dimension-14), + ); + + @each $size, $border-radius in $border-radius-by-size { + &:where(.size-#{$size}) { + border-radius: $border-radius; + } } } diff --git a/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx b/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx index c9658f2abf..c19f6d8549 100644 --- a/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx +++ b/packages/bezier-react/src/components/AlphaIconButton/IconButton.tsx @@ -38,7 +38,6 @@ export const IconButton = forwardRef( function IconButton( { as = BaseButton, - text, color = 'blue', variant = 'primary', size = 'm',