-
Notifications
You must be signed in to change notification settings - Fork 47
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
Migrate ListItem
component with scss
#1925
Changes from all commits
4de16e3
e832fbe
69614bf
a1e5d82
73d13db
87a49e1
0982ef5
67b2749
b29ebf7
598ba76
487db6f
04193d2
95523df
5f7b065
6475413
18517ae
ddefe40
5fc3949
522636f
829473b
763e57c
30c4e60
fac5cf7
e31a6df
069e127
ab666cc
88f120b
1bb458b
2f46dd6
5c7764d
9b59ce4
f4033bd
0cbc20b
2fc2643
c842b41
07d70c2
7db374f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
--- | ||
"@channel.io/bezier-react": major | ||
--- | ||
|
||
**Breaking Changes: Property updates in `ListItem` component** | ||
|
||
- No longer support `interpolation` property. Replace any usage of `interpolation` property with appropriate `style` or `className` implementations. | ||
- No longer support `iconStyle`, `iconClassName`, `iconInterpolation`, `contentStyle`, `contentClassName` and `contentInterpolation`. This decision was made to reduce excessive flexibility in the interface. | ||
- No longer support `leftIcon` property. Removed for consistency with other component interfaces. Replace it to `leftContent`. | ||
- No longer support `name` property. The second argument (name) of `onClick` is also removed. If you need an identifier, combine functions outside of the component. | ||
- No longer support `hide`, `nested`, `optionKey` and `disableIconActive` property. Removed because it is a legacy property. Replace `hide` property with conditional rendering. | ||
- The size changes according to the `ListItemSize`. This is a change to unify the design. Please change it like below. | ||
- `ListItemSize.S` -> `ListItemSize.XS` | ||
- `ListItemSize.M` -> `ListItemSize.S` | ||
- `ListItemSize.L` -> `ListItemSize.M` | ||
- `ListItemSize.XL` -> `ListItemSize.L` | ||
|
||
**Minor Changes:** | ||
|
||
- Fix incorrect text size for `XL`(now `L`) size. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
.ListItemLeftIcon { | ||
transition: color var(--transition-m); | ||
} | ||
|
||
.ListItem { | ||
all: unset; | ||
|
||
cursor: pointer; | ||
|
||
display: flex; | ||
align-items: center; | ||
|
||
text-decoration: none; | ||
|
||
transition: background-color var(--transition-s); | ||
|
||
&:where(.variant-monochrome) { | ||
color: var(--txt-black-darkest); | ||
|
||
& :where(.ListItemLeftIcon) { | ||
color: var(--txt-black-dark); | ||
} | ||
} | ||
|
||
&:where(.size-xs) { | ||
padding: 4px 6px; | ||
border-radius: var(--radius-6); | ||
} | ||
|
||
&:where(.size-s) { | ||
padding: 6px; | ||
border-radius: var(--radius-6); | ||
} | ||
|
||
&:where(.size-m) { | ||
padding: 8px 6px; | ||
border-radius: var(--radius-8); | ||
} | ||
|
||
&:where(.size-l) { | ||
padding: 10px 6px; | ||
border-radius: var(--radius-12); | ||
} | ||
|
||
&:where(.disabled) { | ||
cursor: default; | ||
opacity: var(--opacity-disabled); | ||
} | ||
|
||
&:where(:not(.disabled, .active)) { | ||
&:where(.focused, :focus-visible) { | ||
background-color: var(--bg-black-lighter); | ||
} | ||
|
||
&:where(:hover) { | ||
background-color: var(--bg-black-lighter); | ||
} | ||
} | ||
|
||
&:where(.active) { | ||
color: var(--bgtxt-blue-normal); | ||
background-color: var(--bgtxt-blue-lightest); | ||
|
||
&:where(.focused, :focus-visible) { | ||
background-color: var(--bgtxt-blue-lighter); | ||
} | ||
|
||
& :where(.ListItemLeftIcon) { | ||
color: var(--bgtxt-blue-normal); | ||
} | ||
|
||
/* NOTE: When multiple adjacent elements are active, it naturally stitches the border together. */ | ||
/* stylelint-disable selector-max-specificity */ | ||
&:has(+ .ListItem.active) { | ||
border-bottom-right-radius: 0; | ||
border-bottom-left-radius: 0; | ||
} | ||
|
||
& + .ListItem.active { | ||
border-top-left-radius: 0; | ||
border-top-right-radius: 0; | ||
} | ||
/* stylelint-enable selector-max-specificity */ | ||
} | ||
Comment on lines
+72
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 기존 훅이 하던 부분 |
||
|
||
&.variant-blue { | ||
color: var(--bgtxt-blue-normal); | ||
|
||
& :where(.ListItemLeftIcon) { | ||
color: var(--bgtxt-blue-normal); | ||
} | ||
} | ||
|
||
&.variant-red { | ||
color: var(--bgtxt-red-normal); | ||
|
||
& :where(.ListItemLeftIcon) { | ||
color: var(--bgtxt-red-normal); | ||
} | ||
} | ||
|
||
&.variant-green { | ||
color: var(--bgtxt-green-normal); | ||
|
||
& :where(.ListItemLeftIcon) { | ||
color: var(--bgtxt-green-normal); | ||
} | ||
} | ||
|
||
&.variant-cobalt { | ||
color: var(--bgtxt-cobalt-normal); | ||
|
||
& :where(.ListItemLeftIcon) { | ||
color: var(--bgtxt-cobalt-normal); | ||
} | ||
} | ||
} | ||
|
||
.ListItemContent { | ||
display: grid; | ||
grid-template-columns: fit-content(100%) minmax(0, 1fr); | ||
width: 100%; | ||
} | ||
|
||
.ListItemRightContent { | ||
display: flex; | ||
margin-left: 8px; | ||
white-space: nowrap; | ||
} | ||
|
||
.ListItemTitle { | ||
display: flex; | ||
grid-column: 2; | ||
grid-row: 1; | ||
align-items: center; | ||
|
||
& * { | ||
transition: color var(--transition-s); | ||
} | ||
} | ||
|
||
.ListItemDescription { | ||
display: flex; | ||
grid-column: 2; | ||
grid-row: 2; | ||
align-items: center; | ||
|
||
width: 100%; | ||
margin-top: 2px; | ||
} | ||
|
||
.ListItemLeftContent { | ||
display: flex; | ||
grid-column: 1; | ||
grid-row: 1; | ||
align-items: center; | ||
|
||
margin-right: 8px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,4 @@ | ||
import React, { | ||
useMemo, | ||
useState, | ||
} from 'react' | ||
import React, { useState } from 'react' | ||
|
||
import { InboxIcon } from '@channel.io/bezier-icons' | ||
import { | ||
|
@@ -10,126 +7,68 @@ import { | |
type StoryObj, | ||
} from '@storybook/react' | ||
|
||
import { compact } from '~/src/utils/array' | ||
import { range } from '~/src/utils/number' | ||
|
||
import ListItem from './ListItem' | ||
import type ListItemProps from './ListItem.types' | ||
import { ListItem } from './ListItem' | ||
import { | ||
type ListItemProps, | ||
ListItemSize, | ||
ListItemVariant, | ||
} from './ListItem.types' | ||
|
||
const meta: Meta<typeof ListItem> = { | ||
component: ListItem, | ||
} | ||
export default meta | ||
|
||
interface ArgTypes extends ListItemProps { | ||
width: number | ||
} | ||
|
||
const Template: StoryFn<ArgTypes> = ({ width, ...listItemProps }) => ( | ||
<div style={{ width }}> | ||
<ListItem optionKey="menu-item-0" {...listItemProps} /> | ||
const Template: StoryFn<ListItemProps> = (props) => ( | ||
<div style={{ width: 400 }}> | ||
<ListItem {...props} /> | ||
</div> | ||
) | ||
|
||
export const Primary = { | ||
export const Primary: StoryObj<ListItemProps> = { | ||
render: Template, | ||
|
||
args: { | ||
width: 388, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 프로덕션 모드에선 args를 추가하지 않아도 전부 나오므로 제거함 |
||
size: ListItemSize.M, | ||
size: ListItemSize.S, | ||
content: '상담이 열릴 때', | ||
description: | ||
'고객이 첫 메시지를 보내거나, 매니저가 상담을 다시 열거나, 자동으로 리오픈되면 트리거됩니다.', | ||
rightContent: '', | ||
leftIcon: InboxIcon, | ||
leftContent: InboxIcon, | ||
active: false, | ||
focused: false, | ||
disableIconActive: false, | ||
descriptionMaxLines: 0, | ||
href: '', | ||
}, | ||
|
||
argTypes: { | ||
width: { | ||
control: { | ||
type: 'range', | ||
min: 50, | ||
max: 500, | ||
step: 1, | ||
}, | ||
}, | ||
disabled: { control: { type: 'boolean' } }, | ||
variant: { | ||
control: { | ||
type: 'radio', | ||
}, | ||
options: [...Object.values(ListItemVariant)], | ||
}, | ||
active: { control: { type: 'boolean' } }, | ||
size: { | ||
control: { | ||
type: 'select', | ||
}, | ||
options: ListItemSize, | ||
}, | ||
description: '고객이 첫 메시지를 보내거나, 매니저가 상담을 다시 열거나, 자동으로 리오픈되면 트리거됩니다.', | ||
descriptionMaxLines: 2, | ||
}, | ||
} | ||
|
||
interface CompositionProps { | ||
listRange: number | ||
} | ||
const list = range(0, 10) | ||
|
||
const CompositionTemplate = ({ listRange }: CompositionProps) => { | ||
const [activeIndex, setActiveIndex] = useState<Set<number>>(() => { | ||
const randomActiveIndex = Array.from(Array(listRange).keys()).map((index) => (Math.random() < 0.5 ? index : null)) | ||
return new Set(compact<number>([...randomActiveIndex])) | ||
}) | ||
const CompositionTemplate = () => { | ||
const [activeIndex, setActiveIndex] = useState<Set<number>>(new Set([0, 1, 2, 4])) | ||
|
||
const isActive = (index: number) => activeIndex.has(index) | ||
|
||
const toggleActive = (index: number) => setActiveIndex((prevSet) => { | ||
if (prevSet.has(index)) { | ||
prevSet.delete(index) | ||
return new Set(Array.from(prevSet)) | ||
return new Set(prevSet) | ||
} | ||
return new Set(Array.from(prevSet.add(index))) | ||
return new Set(prevSet.add(index)) | ||
}) | ||
|
||
const list = useMemo(() => Array.from(Array(listRange).keys()), [listRange]) | ||
|
||
return ( | ||
<div> | ||
<div style={{ width: 200 }}> | ||
{ list.map((index) => ( | ||
<ListItem | ||
key={index} | ||
className={isActive(index) ? 'active' : undefined} | ||
optionKey={`menu-item-${index}`} | ||
active={isActive(index)} | ||
onClick={() => toggleActive(index)} | ||
content={`이것은 ${index}번 아이템입니다.`} | ||
content="Click me!" | ||
/> | ||
)) } | ||
</div> | ||
) | ||
} | ||
|
||
export const Composition: StoryObj<CompositionProps> = { | ||
export const Composition: StoryObj = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. composition 은 있어도 되지 않을까요? 여러개 붙어있을 때 border-radius 가 0인지 보여주는 게 좋아보입니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 기존 스토리에 추가해보도록 할게요 |
||
render: CompositionTemplate, | ||
|
||
args: { | ||
listRange: 10, | ||
}, | ||
|
||
argTypes: { | ||
listRange: { | ||
control: { | ||
type: 'range', | ||
min: 2, | ||
max: 20, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
export default meta |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기는 기존과 다른 스타일링 같습니다. active && focused 일 때 --bgtxt-blue-lightest 이었습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ListItem을 interactive content(button)으로 변경해보는 시도를 하다가 넣었던 스타일인데, 다시 div로 되돌리기로 결정해서 이전과 동일하게 제거하면 되겠네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기만 다시 봐주시면 될것같습니다! 말씀하신대로 div 라서 focused 인터페이스가 조금 이상한거같은데 사용하는 곳이 은근 있네요..