Skip to content

Commit

Permalink
feat(sidebar): develop new component (#2868)
Browse files Browse the repository at this point in the history
* chore: save my content

* chore: save my content

* chore: save my content

* fix: adopt some AI adivice

* chore: add unit test rate

* chore: add unit test rate

* feat: v15 style adaption

* feat: migrate doc update

* fix: review

* fix: support word wrap

* fix: support word wrap

* fix: disabled

* fix: make css easier

* fix: adjust some doc details

---------

Co-authored-by: xiaoyatong <[email protected]>
  • Loading branch information
Alex-huxiyang and xiaoyatong authored Dec 30, 2024
1 parent 5e3428d commit 16bfab6
Show file tree
Hide file tree
Showing 34 changed files with 1,540 additions and 3 deletions.
14 changes: 12 additions & 2 deletions migrate-from-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,16 @@ plugins: [

- `offset` 重命名为 `indent`

#### SideBar

- 新增SideBar组件
- 支持属性value,用于当前激活的`item`的key
- 支持属性defaultValue, 表示未设置value时,`item`的key的默认值
- 支持属性contentDuration, 用于内容滚动动画时长
- 支持属性sidebarDuration, 用于侧栏滚动动画时长
- 支持属性onClick, 点击标签时触发
- 支持属性onChange, 当前激活的标签改变时触发

#### Tabbar

- `unactiveColor` 重命名为 `inactiveColor`
Expand Down Expand Up @@ -597,7 +607,7 @@ plugins: [
- 移除 `isAsync`,通过 `checked`实现
- 移除 `activeColor` ,通过css变量`--nutui-switch-open-background-color`实现
- 移除 `inactiveColor`,通过css变量`--nutui-switch-close-background-color`实现
- `activeText 属性类型更改为 `ReactNode`
- `activeText` 属性类型更改为`ReactNode`
- `inactiveText` 属性类型更改为 `ReactNode`

#### Toast
Expand Down Expand Up @@ -780,7 +790,7 @@ plugins: [
- 移除 `pageContent`,通过 indicator 实现
- `autoplay` 重命名为 `autoplay`
- `initPage` 重命名为 `defaultValue`
- `paginationVisible` 重命名为 `indicator`,类型改为` ReactNode`
- `paginationVisible` 重命名为 `indicator`,类型改为`ReactNode`
- `isPreventDefault` 重命名为 `preventDefault`
- `isStopPropagation` 重命名为 `stopPropagation`
- `isCenter` 重命名为 `center`
Expand Down
24 changes: 24 additions & 0 deletions src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,30 @@
"author": "hx",
"dd": false
},
{
"version": "3.0.0",
"name": "SideBar",
"type": "component",
"cName": "侧边栏导航",
"desc": "用于侧边内容选择和切换",
"sort": 10,
"show": true,
"taro": true,
"author": "Alex.hxy",
"v15": true
},
{
"version": "3.0.0",
"name": "SideBarItem",
"type": "component",
"cName": "侧边栏导航子组件",
"desc": "用于侧边内容选择和切换",
"sort": 10,
"show": false,
"taro": true,
"author": "Alex.hxy",
"v15": true
},
{
"version": "2.0.0",
"name": "SideNavBarItem",
Expand Down
70 changes: 70 additions & 0 deletions src/packages/sidebar/_test_/sidebar.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react'
import { fireEvent, render } from '@testing-library/react'
import '@testing-library/jest-dom'
import { SideBar } from '../sidebar'

const list = Array.from(new Array(3).keys())

test('should render defaultValue correctly', async () => {
const { container } = render(
<SideBar style={{ height: 300 }} value="0">
{list.map((item) => (
<SideBar.Item key={item} title={`Opt ${item + 1}`}>
Content {item + 1}
</SideBar.Item>
))}
</SideBar>
)
const item = container.querySelectorAll('.nut-sidebar-titles-item')[0]
expect(item).toHaveClass('nut-sidebar-titles-item-active')
})

test('should choose and scroll to the right option', async () => {
const onChange = vi.fn()
const { container } = render(
<SideBar style={{ height: 300 }} value="0" onChange={onChange}>
{list.map((item) => (
<SideBar.Item key={item} title={`Opt ${item + 1}`}>
Content {item + 1}
</SideBar.Item>
))}
</SideBar>
)
const items = container.querySelectorAll('.nut-sidebar-titles-item')
fireEvent.click(items[1])
expect(onChange).toHaveBeenCalledWith(1)
})
test('disabled option', async () => {
const onChange = vi.fn()
const { container } = render(
<SideBar style={{ height: 300 }} value="0" onChange={onChange}>
{list.map((item) => (
<SideBar.Item key={item} title={`Opt ${item + 1}`} disabled>
Content {item + 1}
</SideBar.Item>
))}
</SideBar>
)
const items = container.querySelectorAll('.nut-sidebar-titles-item')
fireEvent.click(items[1])
expect(onChange).not.toHaveBeenCalled()
})
test('matchByValue', async () => {
const list1 = [
{ value: 'a', title: 'Opt a Opt a Opt a Opt a' },
{ value: 'b', title: 'Opt b' },
{ value: 'c', title: 'Opt c' },
]
const onChange = vi.fn()
const { container } = render(
<SideBar style={{ height: 300 }} value="b" onChange={onChange}>
{list1.map((item) => (
<SideBar.Item key={item.value} title={item.title} value={item.value}>
Content {item.value}
</SideBar.Item>
))}
</SideBar>
)
const items = container.querySelectorAll('.nut-sidebar-titles-item')
expect(items[1]).toHaveClass('nut-sidebar-titles-item-active')
})
56 changes: 56 additions & 0 deletions src/packages/sidebar/demo.taro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react'
import Taro from '@tarojs/taro'
import { ScrollView, View } from '@tarojs/components'
import { useTranslate } from '@/sites/assets/locale/taro'
import Header from '@/sites/components/header'
import Demo1 from './demos/taro/demo1'
import Demo2 from './demos/taro/demo2'
import Demo3 from './demos/taro/demo3'
import Demo4 from './demos/taro/demo4'
import Demo5 from './demos/taro/demo5'
import Demo6 from './demos/taro/demo6'

const TabsDemo = () => {
const [translated] = useTranslate({
'zh-CN': {
basic: '基础用法',
disabled: '禁用选项',
matchByValue: '根据value匹配',
multiTitle: '多个标题',
setDuration: '设置滚动动画时长',
padding: '内容区域留白边距',
},
'en-US': {
basic: 'Basic Usage',
disabled: 'Disabled',
matchByValue: 'Match By Value',
multiTitle: 'Multiple Titles',
setDuration: 'Set Scroll Animation Duration',
padding: 'Set Content Padding',
},
})

return (
<>
<Header />
<ScrollView
className={`demo ${Taro.getEnv() === 'WEB' ? 'web full' : ''}`}
>
<View className="h2">{translated.basic}</View>
<Demo1 />
<View className="h2">{translated.disabled}</View>
<Demo2 />
<View className="h2">{translated.matchByValue}</View>
<Demo3 />
<View className="h2">{translated.multiTitle}</View>
<Demo4 />
<View className="h2">{translated.setDuration}</View>
<Demo5 />
<View className="h2">{translated.padding}</View>
<Demo6 />
</ScrollView>
</>
)
}

export default TabsDemo
50 changes: 50 additions & 0 deletions src/packages/sidebar/demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react'
import { useTranslate } from '@/sites/assets/locale'
import Demo1 from './demos/h5/demo1'
import Demo2 from './demos/h5/demo2'
import Demo3 from './demos/h5/demo3'
import Demo4 from './demos/h5/demo4'
import Demo5 from './demos/h5/demo5'
import Demo6 from './demos/h5/demo6'

const SideNavBarDemo = () => {
const [translated] = useTranslate({
'zh-CN': {
basic: '基础用法',
disabled: '禁用选项',
matchByValue: '根据value匹配',
multiTitle: '多个标题',
setDuration: '设置滚动动画时长',
padding: '内容区域留白边距',
},
'en-US': {
basic: 'Basic Usage',
disabled: 'Disabled',
matchByValue: 'Match By Value',
multiTitle: 'Multiple Titles',
setDuration: 'Set Scroll Animation Duration',
padding: 'Set Content Padding',
},
})

return (
<>
<div className="demo">
<h2>{translated.basic}</h2>
<Demo1 />
<h2>{translated.disabled}</h2>
<Demo2 />
<h2>{translated.matchByValue}</h2>
<Demo3 />
<h2>{translated.multiTitle}</h2>
<Demo4 />
<h2>{translated.setDuration}</h2>
<Demo5 />
<h2>{translated.padding}</h2>
<Demo6 />
</div>
</>
)
}

export default SideNavBarDemo
25 changes: 25 additions & 0 deletions src/packages/sidebar/demos/h5/demo1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from 'react'
import { SideBar } from '@nutui/nutui-react'

const Demo1 = () => {
const [value, setValue] = useState<number | string>('0')
const list = Array.from(new Array(3).keys())
return (
<>
<SideBar
style={{ height: 300 }}
value={value}
onChange={(value) => {
setValue(value)
}}
>
{list.map((item) => (
<SideBar.Item key={item} title={`Opt ${item + 1}`}>
Content {item + 1}
</SideBar.Item>
))}
</SideBar>
</>
)
}
export default Demo1
22 changes: 22 additions & 0 deletions src/packages/sidebar/demos/h5/demo2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { useState } from 'react'
import { SideBar } from '@nutui/nutui-react'

const Demo2 = () => {
const [value, setValue] = useState<number | string>('0')
return (
<>
<SideBar
style={{ height: 300 }}
value={value}
onChange={(value) => {
setValue(value)
}}
>
<SideBar.Item title="Opt 1">Content 1</SideBar.Item>
<SideBar.Item title="Opt 2">Content 2</SideBar.Item>
<SideBar.Item title="Opt 3" disabled />
</SideBar>
</>
)
}
export default Demo2
28 changes: 28 additions & 0 deletions src/packages/sidebar/demos/h5/demo3.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useState } from 'react'
import { SideBar } from '@nutui/nutui-react'

const Demo3 = () => {
const [value, setValue] = useState<number | string>('b')
return (
<>
<SideBar
style={{ height: 300 }}
value={value}
onChange={(value) => {
setValue(value)
}}
>
<SideBar.Item title="Opt 1" value="a">
Content 1
</SideBar.Item>
<SideBar.Item title="Opt 2" value="b">
Content 2
</SideBar.Item>
<SideBar.Item title="Opt 3" value="c">
Content 3
</SideBar.Item>
</SideBar>
</>
)
}
export default Demo3
25 changes: 25 additions & 0 deletions src/packages/sidebar/demos/h5/demo4.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from 'react'
import { SideBar } from '@nutui/nutui-react'

const Demo4 = () => {
const [value, setValue] = useState<number | string>('0')
const list = Array.from(new Array(20).keys())
return (
<>
<SideBar
style={{ height: 300 }}
value={value}
onChange={(value) => {
setValue(value)
}}
>
{list.map((item) => (
<SideBar.Item key={item} title={`Opt ${item + 1}`}>
Content {item + 1}
</SideBar.Item>
))}
</SideBar>
</>
)
}
export default Demo4
27 changes: 27 additions & 0 deletions src/packages/sidebar/demos/h5/demo5.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useState } from 'react'
import { SideBar } from '@nutui/nutui-react'

const Demo5 = () => {
const [value, setValue] = useState<number | string>('0')
const list = Array.from(new Array(20).keys())
return (
<>
<SideBar
style={{ height: 300 }}
value={value}
contentDuration={500}
sidebarDuration={300}
onChange={(value) => {
setValue(value)
}}
>
{list.map((item) => (
<SideBar.Item key={item} title={`Opt ${item + 1}`}>
Content {item + 1}
</SideBar.Item>
))}
</SideBar>
</>
)
}
export default Demo5
Loading

0 comments on commit 16bfab6

Please sign in to comment.