Skip to content

Commit

Permalink
feat: add menu components
Browse files Browse the repository at this point in the history
  • Loading branch information
eizyc committed Jan 18, 2024
1 parent effee5a commit e8681d4
Show file tree
Hide file tree
Showing 16 changed files with 649 additions and 17 deletions.
3 changes: 1 addition & 2 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ const preview: Preview = {
docs:{
source: {
transform: (code, storyContext) => {
const regex = /render: \(\) => ([\s\S]*?)(,|\})/
const regex = /render: \(\) => ([\s\S]*)(,|\})/
const found = code.match(regex)
console.log(found, found?.length)
if (found && found.length == 3) {
const text = found[1]?.replaceAll(/<>|<\/>/gi, '')
return text
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,11 @@ Support Components:
+ [Common mistakes with React Testing Library](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
+ [Component Story Format 3 is here](https://storybook.js.org/blog/storybook-csf3-is-here/)
+ [Structuring your Storybook](https://storybook.js.org/blog/structuring-your-storybook/)
+ [Introduction to Design Systems](https://fem-design-systems.netlify.app/)
+ [Introduction to Design Systems](https://fem-design-systems.netlify.app/)


## Some Solutions of bugs
+ [Removal of implicit children](https://solverfox.dev/writing/no-implicit-children/)
+ [Testing Sass with Jest](https://obyford.com/posts/testing-sass-with-jest/)
+ [Unable to resolve files inside Jest](https://github.com/sass/dart-sass/issues/710)
+ [Helper to attach a stylesheet to the document](https://github.com/testing-library/jest-dom/issues/70)
3 changes: 2 additions & 1 deletion src/components/Button/_style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '../../styles/variables';
@import '../../styles/mixins/button';
.#{$prefix}-btn {
position: relative;
display: inline-block;
Expand All @@ -13,7 +15,6 @@
box-shadow: $btn-box-shadow;
cursor: pointer;
transition: $btn-transition;
&-disabled,
&[disabled] {
cursor: not-allowed;
opacity: $btn-disabled-opacity;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const buttonWithType = {
* ~~~
*/
const meta:Meta<typeof Button> = {
title: 'Button',
title: 'Basic/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
Expand Down
17 changes: 9 additions & 8 deletions src/components/Button/button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { STYLE_PREFIX } from "../../utils/const";

const defaultProps = {
onClick:jest.fn()
}
}

const primaryBtnProps:ButtonProps = {
buttonType: 'primary',
Expand All @@ -22,9 +22,8 @@ const linkBtnProps:ButtonProps = {
describe('test Button component', () => {
it('should render the corrent default button', ()=> {
render(<Button {...defaultProps}>Hello</Button>)
const el = screen.getByText('Hello')
const el = screen.getByRole('button', { name: 'Hello' })
expect(el).toBeInTheDocument()
expect(el.tagName).toEqual('BUTTON')
expect(el).toHaveClass(`${STYLE_PREFIX}-btn ${STYLE_PREFIX}-btn-default`)
expect(el).not.toBeDisabled()
fireEvent.click(el)
Expand All @@ -33,17 +32,19 @@ describe('test Button component', () => {

it( 'should render the correct component based on different props', ()=> {
render(<Button {...primaryBtnProps}>Hello</Button>)
const el = screen.getByText('Hello')
const el = screen.getByRole('button', { name: 'Hello' })
expect(el).toBeInTheDocument()
expect(el).toHaveClass(`${STYLE_PREFIX}-btn-primary ${STYLE_PREFIX}-btn-sm ${primaryBtnProps.className}`)
expect(el).not.toBeDisabled()
})

it( 'should render a link when btnType equals link and href id provided', ()=> {
render(<Button {...linkBtnProps}>Link</Button>)
const el = screen.getByText('Link')
expect(el).toBeInTheDocument()
expect(el.tagName).toEqual('A')
render(<Button {...linkBtnProps}>link button</Button>)
const el = screen.getByText((content, element) => element?.tagName.toLowerCase() === 'span')
const a = screen.getByRole('link')
expect(a.tagName).toEqual('A')
expect(a).toHaveTextContent('link button')
expect(a).toBeInTheDocument()
expect(el).toHaveClass(`${STYLE_PREFIX}-btn-link`)
expect(el).not.toBeDisabled()
})
Expand Down
5 changes: 2 additions & 3 deletions src/components/Button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, ButtonHTMLAttributes, AnchorHTMLAttributes } from 'react';
import React, { FC, ButtonHTMLAttributes, AnchorHTMLAttributes, PropsWithChildren } from 'react';
import classnames from 'classnames'
import { STYLE_PREFIX } from "../../utils/const";

Expand All @@ -14,7 +14,6 @@ export interface BaseButtonProps {
size?: ButtonSize,
buttonType?: ButtonType;
ghost?: boolean;
children: React.ReactNode;
href?: string;
}

Expand All @@ -24,7 +23,7 @@ type NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLElement>
type AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLElement>
export type ButtonProps =Partial<NativeButtonProps & AnchorButtonProps>

export const Button: FC<ButtonProps> = (props) => {
export const Button: FC<PropsWithChildren<ButtonProps>> = (props) => {
const {buttonType, className, disabled, size, ghost, children, href, ...restProps} = props
// button btn-lg btn-primary
const classes = classnames(prefixCls, className, {
Expand Down
98 changes: 98 additions & 0 deletions src/components/Menu/_style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@import '../../styles/variables';
.#{$prefix}-menu {
display: flex;
list-style: none;
&-item{
padding: $menu-item-padding-y $menu-item-padding-x;
cursor: pointer;
transition: $menu-transition;
&.disabled {
color: $menu-item-disabled-color;
cursor: $menu-item-disabled-cursor;
}
}
}

.#{$prefix}-menu-horizontal {
border-bottom: $menu-border-width solid $menu-border-color;
flex-direction: row;
box-shadow: $menu-box-shadow;
.#{$prefix}-menu-item{
border-bottom: $menu-item-active-border-width solid transparent;
&.active{
color: $menu-item-active-color;
border-bottom: $menu-item-active-border-width solid $menu-item-active-color;
}
&:not(.disabled):hover{
color: $menu-item-active-color;
border-bottom: $menu-item-active-border-width solid $menu-item-active-color;
}
}
}

.#{$prefix}-menu-vertical {
flex-direction: column;
display: inline-flex;
border-right: $menu-border-width solid $menu-border-color;
.#{$prefix}-menu-item{
border-left: $menu-item-active-border-width solid transparent;
&.active{
color: $menu-item-active-color;
border-left: $menu-item-active-border-width solid $menu-item-active-color;
}
&:not(.disabled):hover{
color: $menu-item-active-color;
border-left: $menu-item-active-border-width solid $menu-item-active-color;
}
}
}

.#{$prefix}-submenu{
position: relative;
padding: $menu-item-padding-y $menu-item-padding-x;
cursor: pointer;
&-menu{
padding-left: 0;
white-space: nowrap;
list-style: none;
transition: $menu-transition;
color: $body-color;
&-item{
padding: $menu-item-padding-y $menu-item-padding-x;
}
}
}

.#{$prefix}-submenu-menu{
display: none;
&.open {
display: block;
}
}
.#{$prefix}-submenu-horizontal{
.#{$prefix}-submenu-menu{
position: absolute;
background: $white;
z-index: 100;
top: calc(100% + $submenu-menu-offset);
border: $menu-border-width solid $menu-border-color;
left: 50%;
transform: translateX(-50%);
box-shadow: $submenu-box-shadow;
.#{$prefix}-menu-item:not(.disabled):hover{
border-bottom: $menu-item-active-border-width solid transparent;
}
.#{$prefix}-menu-item.active{
border-bottom: $menu-item-active-border-width solid transparent;
}
}
}

.#{$prefix}-submenu-vertical{
.#{$prefix}-menu-item:not(.disabled):hover{
border-left: $menu-item-active-border-width solid transparent;
}
.#{$prefix}-menu-item.active{
border-left: $menu-item-active-border-width solid transparent;
}
}
116 changes: 116 additions & 0 deletions src/components/Menu/menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { action } from '@storybook/addon-actions'
import type { Meta, StoryObj } from '@storybook/react'
import Menu from './menu'
import MenuItem from './menuItem'
import SubMenu from './subMenu'

type Story = StoryObj<typeof Menu>


// export const Playground: Story = {
// args: {
// }
// }

export const defaultMenu: Story = {
render: ()=>(
<Menu
defaultActive={'0'}
onSelect={action('selected!')}
mode="horizontal"
defaultOpenSubMenus={[]}
>
<MenuItem>
cool link
</MenuItem>
<MenuItem>
cool link 2
</MenuItem>
<MenuItem disabled>
disabled
</MenuItem>
<SubMenu title="下拉选项">
<MenuItem>
下拉选项一
</MenuItem>
<MenuItem>
下拉选项二
</MenuItem>
</SubMenu>
</Menu>
)
}

export const MenuWithVertical: Story = {
render: ()=>(
<Menu
defaultActive={'0'}
onSelect={action('selected!')}
mode="vertical"
defaultOpenSubMenus={[]}
>
<MenuItem>
cool link
</MenuItem>
<MenuItem>
cool link 2
</MenuItem>
<SubMenu title="点击下拉选项">
<MenuItem>
下拉选项一
</MenuItem>
<MenuItem>
下拉选项二
</MenuItem>
</SubMenu>
</Menu>
)
}

export const MenuWithDefaultOpenSubMenus: Story = {
render: ()=>(
<Menu
defaultActive={'0'}
onSelect={action('selected!')}
defaultOpenSubMenus={['2']}
mode="vertical"
>
<MenuItem>
cool link
</MenuItem>
<MenuItem>
cool link 2
</MenuItem>
<SubMenu title="默认展开下拉选项">
<MenuItem>
下拉选项一
</MenuItem>
<MenuItem>
下拉选项二
</MenuItem>
</SubMenu>
</Menu>
)
}

/**
* ### How to import
* ~~~js
* import { Menu, MenuItem, SubMenu } from 'eizy-ui-react'
* ~~~
*/
const meta:Meta<typeof Menu> = {
title: 'Basic/Menu',
component: Menu,
tags: ['autodocs'],
argTypes: {
onSelect: {
control: {
type: 'function'
},
description: 'description'
}
}
};

export default meta
Loading

0 comments on commit e8681d4

Please sign in to comment.