-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
792 additions
and
39 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,40 @@ | ||
/* External dependencies */ | ||
import React from 'react' | ||
import base from 'paths.macro' | ||
|
||
/* Internal dependencies */ | ||
import { Navigation } from '../../../layout/Navigation' | ||
import { getTitle } from '../../../utils/utils' | ||
import ListItem from './ListItem' | ||
|
||
export default { | ||
title: getTitle(base), | ||
component: ListItem, | ||
argTypes: { | ||
onClick: { control: { action: 'onClick' } }, | ||
active: { control: { type: 'boolean' } }, | ||
}, | ||
} | ||
|
||
const SIDEBAR_WIDTH = 240 | ||
|
||
const Template = ({ ...otherListItemProps }) => ( | ||
<Navigation | ||
withScroll | ||
disableResize | ||
title="사이드바" | ||
minWidth={SIDEBAR_WIDTH} | ||
> | ||
<ListItem | ||
optionKey="menu-item-0" | ||
{...otherListItemProps} | ||
/> | ||
</Navigation> | ||
) | ||
|
||
export const Primary = Template.bind({}) | ||
|
||
Primary.args = { | ||
content: '전체 상태', | ||
active: false, | ||
} |
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,32 @@ | ||
/* Internal dependencies */ | ||
import { css, styled } from '../../../styling/Theme' | ||
import Palette from '../../../styling/Palette' | ||
import { StyledWrapperProps } from './ListItem.types' | ||
|
||
const ActiveItemStyle = css<StyledWrapperProps>` | ||
color: ${Palette.blue500}; | ||
background-color: ${Palette.blue100}; | ||
` | ||
|
||
export const Wrapper = styled.div<StyledWrapperProps>` | ||
display: flex; | ||
align-items: center; | ||
height: 32px; | ||
padding: 0 8px; | ||
margin-right: 6px; | ||
margin-left: 6px; | ||
font-size: 14px; | ||
font-weight: normal; | ||
color: ${props => props.theme?.colors?.text7}; | ||
text-decoration: none; | ||
cursor: pointer; | ||
border-radius: 6px; | ||
&:hover { | ||
${props => (props.active ? '' : ` | ||
background-color: ${props.theme?.colors?.background3}; | ||
`)} | ||
} | ||
${props => (props.active && ActiveItemStyle)} | ||
` |
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,45 @@ | ||
/* External dependencies */ | ||
import React from 'react' | ||
import { render } from '@testing-library/react' | ||
|
||
/* Internal dependencies */ | ||
import ListItem, { SIDEBAR_MENU_ITEM_TEST_ID } from './ListItem' | ||
import ListItemProps from './ListItem.types' | ||
|
||
describe('ListItem', () => { | ||
let props: ListItemProps | ||
|
||
beforeEach(() => { | ||
props = { | ||
content: 'this is content', | ||
optionKey: 'menu-item', | ||
active: false, | ||
} | ||
}) | ||
|
||
const renderComponent = (optionProps?: Partial<ListItemProps>) => render( | ||
<ListItem {...props} {...optionProps} />, | ||
) | ||
|
||
it('should have "optionKey" value on "data-option-key" ', () => { | ||
const { getByTestId } = renderComponent({ optionKey: 'my-menu-item' }) | ||
const rendered = getByTestId(SIDEBAR_MENU_ITEM_TEST_ID) | ||
|
||
expect(rendered).toHaveAttribute('data-option-key', 'my-menu-item') | ||
}) | ||
|
||
it('should have "data-active" attribute when "active" prop is "true', () => { | ||
const { getByTestId } = renderComponent({ active: true }) | ||
const rendered = getByTestId(SIDEBAR_MENU_ITEM_TEST_ID) | ||
|
||
expect(rendered).toHaveAttribute('data-active', 'true') | ||
}) | ||
|
||
it('should have "a tag" related attributes when "href" prop is string', () => { | ||
const { getByTestId } = renderComponent({ href: 'https://naver.com' }) | ||
const rendered = getByTestId(SIDEBAR_MENU_ITEM_TEST_ID) | ||
expect(rendered).toHaveAttribute('href', 'https://naver.com') | ||
expect(rendered).toHaveAttribute('rel', 'noopener noreferer') | ||
expect(rendered).toHaveAttribute('target', '_blank') | ||
}) | ||
}) |
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,91 @@ | ||
/* External dependencies */ | ||
import React, { Ref, forwardRef, useCallback, useMemo } from 'react' | ||
import { get, noop, isNil } from 'lodash-es' | ||
|
||
/* Internal dependencies */ | ||
import { mergeClassNames } from '../../../utils/stringUtils' | ||
import ListItemProps from './ListItem.types' | ||
import { Wrapper } from './ListItem.styled' | ||
|
||
export const SIDEBAR_MENU_ITEM_COMPONENT_NAME = 'ListItem' | ||
export const SIDEBAR_MENU_ITEM_TEST_ID = 'ch-design-system-sidebar-menu-item' | ||
|
||
export function isListItem(element: any): element is React.ReactElement<ListItemProps> { | ||
return React.isValidElement(element) && | ||
get(element, 'type.displayName') === SIDEBAR_MENU_ITEM_COMPONENT_NAME | ||
} | ||
|
||
function ListItemComponent({ | ||
as, | ||
testId = SIDEBAR_MENU_ITEM_TEST_ID, | ||
content, | ||
href, | ||
hide, | ||
/* OptionItem Props */ | ||
optionKey, | ||
/* Activable Element Props */ | ||
active = false, | ||
activeClassName, | ||
/* HTMLAttribute Props */ | ||
onClick = noop, | ||
className, | ||
...othreProps | ||
}: ListItemProps, forwardedRef: Ref<any>) { | ||
const clazzName = useMemo(() => ( | ||
mergeClassNames(className, ((active && activeClassName) || undefined)) | ||
), [ | ||
className, | ||
activeClassName, | ||
active, | ||
]) | ||
|
||
const handleClick = useCallback((e) => { | ||
if (!active) { | ||
onClick(e) | ||
} | ||
}, [active, onClick]) | ||
|
||
if (hide) return null | ||
|
||
if (!isNil(href)) { | ||
return ( | ||
<Wrapper | ||
ref={forwardedRef} | ||
as="a" | ||
className={clazzName} | ||
draggable={false} | ||
href={href} | ||
target="_blank" | ||
rel="noopener noreferer" | ||
onClick={handleClick} | ||
active={active} | ||
data-active={active} | ||
data-option-key={optionKey} | ||
data-testid={testId} | ||
{...othreProps} | ||
> | ||
{ content } | ||
</Wrapper> | ||
) | ||
} | ||
|
||
return ( | ||
<Wrapper | ||
as={as} | ||
className={clazzName} | ||
onClick={handleClick} | ||
active={active} | ||
data-active={active} | ||
data-option-key={optionKey} | ||
data-testid={testId} | ||
{...othreProps} | ||
> | ||
{ content } | ||
</Wrapper> | ||
) | ||
} | ||
|
||
const ListItem = forwardRef(ListItemComponent) | ||
ListItem.displayName = SIDEBAR_MENU_ITEM_COMPONENT_NAME | ||
|
||
export default ListItem |
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,11 @@ | ||
/* Internal dependencies */ | ||
import ActivableElement from '../../../types/ActivatableElement' | ||
import { ContentComponentProps, UIComponentProps } from '../../../types/ComponentProps' | ||
import OptionItem from '../../../types/OptionItem' | ||
|
||
export default interface ListItemProps extends ContentComponentProps, OptionItem, ActivableElement { | ||
href?: string | ||
hide?: boolean | ||
} | ||
|
||
export interface StyledWrapperProps extends UIComponentProps, OptionItem, ActivableElement {} |
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,10 @@ | ||
import ListItem from './ListItem' | ||
import type ListItemProps from './ListItem.types' | ||
|
||
export type { | ||
ListItemProps, | ||
} | ||
|
||
export { | ||
ListItem, | ||
} |
54 changes: 54 additions & 0 deletions
54
src/components/List/ListMenuGroup/ListMenuGroup.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,54 @@ | ||
/* External dependencies */ | ||
import React from 'react' | ||
import base from 'paths.macro' | ||
import { v4 as uuid } from 'uuid' | ||
import { range } from 'lodash-es' | ||
|
||
/* Internal dependencies */ | ||
import { Navigation } from '../../../layout/Navigation' | ||
import { getTitle } from '../../../utils/utils' | ||
import { ListItem } from '../ListItem' | ||
import ListMenuGroup from './ListMenuGroup' | ||
|
||
export default { | ||
title: getTitle(base), | ||
component: ListMenuGroup, | ||
argTypes: { | ||
open: { | ||
control: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
const SIDEBAR_WIDTH = 240 | ||
|
||
const Template = ({ ...otherListMenuGroupProps }) => ( | ||
<Navigation | ||
withScroll | ||
disableResize | ||
title="사이드바" | ||
minWidth={SIDEBAR_WIDTH} | ||
> | ||
<ListMenuGroup | ||
{...otherListMenuGroupProps} | ||
> | ||
{ range(0, 4).map(n => ( | ||
<ListItem | ||
key={uuid()} | ||
optionKey={`menu-item-${n}`} | ||
content={`아이템 ${n}`} | ||
/> | ||
)) } | ||
</ListMenuGroup> | ||
</Navigation> | ||
) | ||
|
||
export const Primary = Template.bind({}) | ||
|
||
Primary.args = { | ||
content: '전체 상태', | ||
leftIcon: 'sent', | ||
selectedOptionIndex: null, | ||
} |
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,42 @@ | ||
/* External dependencies */ | ||
import { isNil } from 'lodash-es' | ||
|
||
/* Internal dependencies */ | ||
import { styled } from '../../../styling/Theme' | ||
import { StyledWrapperProps } from './ListMenuGroup.types' | ||
|
||
export const GroupItemWrapper = styled.div<StyledWrapperProps>` | ||
display: flex; | ||
align-items: center; | ||
height: 32px; | ||
padding: 0 8px; | ||
margin-right: 6px; | ||
margin-left: 6px; | ||
font-size: 14px; | ||
font-weight: normal; | ||
color: ${props => props.theme?.colors?.text7}; | ||
text-decoration: none; | ||
cursor: pointer; | ||
border-radius: 6px; | ||
&:hover { | ||
background-color: ${props => props.theme?.colors?.background3}; | ||
} | ||
${props => !isNil(props.currentMenuItemIndex) && ` | ||
color: ${props.theme?.colors?.focus5}; | ||
background-color: ${props.theme?.colors?.background2}; | ||
`} | ||
` | ||
|
||
export const GroupItemContentWrapper = styled.div` | ||
display: flex; | ||
flex: 1; | ||
align-items: center; | ||
` | ||
|
||
export const ChildrenWrapper = styled.div` | ||
& > * { | ||
padding-left: 42px; | ||
} | ||
` |
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,61 @@ | ||
/* External dependencies */ | ||
import React from 'react' | ||
import { fireEvent, render, screen } from '@testing-library/react' | ||
import { v4 as uuid } from 'uuid' | ||
import { range } from 'lodash-es' | ||
|
||
/* Internal dependencies */ | ||
import { ListItem } from '../ListItem' | ||
import ListMenuGroup, { SIDEBAR_MENU_GROUP_TEST_ID } from './ListMenuGroup' | ||
import ListMenuGroupProps from './ListMenuGroup.types' | ||
|
||
describe('ListMenuGroup', () => { | ||
let props: ListMenuGroupProps | ||
|
||
beforeEach(() => { | ||
props = { | ||
open: true, | ||
selectedOptionIndex: 0, | ||
content: 'campaigns', | ||
} | ||
}) | ||
|
||
const renderComponent = (optionProps?: Partial<ListMenuGroupProps>) => render( | ||
<ListMenuGroup {...props} {...optionProps}> | ||
{ range(0, 4).map(n => ( | ||
<ListItem | ||
key={uuid()} | ||
optionKey={`menu-item-${n}`} | ||
content={`item ${n}`} | ||
/> | ||
)) } | ||
</ListMenuGroup>, | ||
) | ||
|
||
it('should have default styles', () => { | ||
const { getByTestId } = renderComponent() | ||
const rendered = getByTestId(SIDEBAR_MENU_GROUP_TEST_ID) | ||
|
||
expect(rendered).toHaveStyle('display: flex;') | ||
expect(rendered).toHaveStyle('align-items: center;') | ||
expect(rendered).toHaveStyle('height: 32px;') | ||
}) | ||
|
||
it( | ||
'should have index on "data-active-index" attr when "selectedOptionIndex" given', | ||
() => { | ||
const { getByTestId } = renderComponent({ selectedMenuItemIndex: 2 }) | ||
const rendered = getByTestId(SIDEBAR_MENU_GROUP_TEST_ID) | ||
|
||
expect(rendered).toHaveAttribute('data-active-index', '2') | ||
}) | ||
|
||
it('should change "data-active-index"', () => { | ||
const { getByTestId } = renderComponent({ selectedMenuItemIndex: 1 }) | ||
const rendered = getByTestId(SIDEBAR_MENU_GROUP_TEST_ID) | ||
|
||
expect(rendered).toHaveAttribute('data-active-index', '1') | ||
fireEvent.click(screen.getByText('item 3')) | ||
expect(rendered).toHaveAttribute('data-active-index', '3') | ||
}) | ||
}) |
Oops, something went wrong.