-
Notifications
You must be signed in to change notification settings - Fork 296
feat(segmented): v14 #2762
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
feat(segmented): v14 #2762
Changes from all commits
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,47 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as React from 'react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { fireEvent, render } from '@testing-library/react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '@testing-library/jest-dom' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { SegmentedItem, Segmented } from '@nutui/nutui-react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultOptions = ['Daily', 'Weekly', 'Monthly'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('Simple Value Testing', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleChange = vi.fn(() => {}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { queryByText } = render( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Segmented | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultValue={0} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options={defaultOptions} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={handleChange} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(queryByText('Weekly')).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const secondItem = queryByText('Weekly') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (secondItem) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.click(secondItem) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(handleChange).toBeCalled() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test('Complex Value Testing', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultOptions: SegmentedItem[] = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: 'Apps', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: 'Apps', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: 'AfterSaleService', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: 'AfterSaleService', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleChange = vi.fn(() => {}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { container, queryByText } = render( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Segmented | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultValue={0} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options={defaultOptions} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={handleChange} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(queryByText('AfterSaleService')).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
+47
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. 🛠️ Refactor suggestion 复杂值测试用例需要补充 当前的复杂值测试过于简单,建议增加以下测试场景:
建议按照以下方式重构测试: test('Complex Value Testing', () => {
const defaultOptions: SegmentedItem[] = [
{
label: 'Apps',
value: 'Apps',
},
{
label: 'AfterSaleService',
value: 'AfterSaleService',
},
]
const handleChange = vi.fn(() => {})
const { container, queryByText } = render(
<Segmented
defaultValue={0}
options={defaultOptions}
onChange={handleChange}
/>
)
expect(queryByText('AfterSaleService')).toBeInTheDocument()
+
+ // 验证初始选中状态
+ const firstItem = queryByText('Apps')
+ expect(firstItem?.parentElement).toHaveClass('nut-segmented-item-active')
+
+ // 测试交互
+ const secondItem = queryByText('AfterSaleService')
+ if (secondItem) {
+ fireEvent.click(secondItem)
+ }
+
+ // 验证回调和状态
+ expect(handleChange).toHaveBeenCalledWith(1, 'AfterSaleService')
+ expect(secondItem?.parentElement).toHaveClass('nut-segmented-item-active')
})
+
+// 添加错误情况测试
+test('Invalid Props Testing', () => {
+ const invalidOptions = [{ invalid: 'structure' }]
+ expect(() => {
+ render(<Segmented options={invalidOptions} />)
+ }).toThrow()
+})📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import React from 'react' | ||
| import { ScrollView, View } from '@tarojs/components' | ||
| import Taro from '@tarojs/taro' | ||
| import Demo1 from './demos/taro/demo1' | ||
| import Demo2 from './demos/taro/demo2' | ||
| import Demo3 from './demos/taro/demo3' | ||
| import { useTranslate } from '@/sites/assets/locale/taro' | ||
| import Header from '@/sites/components/header' | ||
|
|
||
| const SegmentedDemo = () => { | ||
| const [translated] = useTranslate({ | ||
| 'zh-CN': { | ||
| uncontrolled: '非受控', | ||
| controlled: '受控', | ||
| optionItems: '设置图标', | ||
| }, | ||
| }) | ||
| return ( | ||
| <> | ||
| <Header /> | ||
| <ScrollView className={`demo ${Taro.getEnv() === 'WEB' ? 'web' : ''}`}> | ||
| <View className="h2">{translated.uncontrolled}</View> | ||
| <Demo1 /> | ||
| <View className="h2">{translated.controlled}</View> | ||
| <Demo2 /> | ||
| <View className="h2">{translated.optionItems}</View> | ||
| <Demo3 /> | ||
| </ScrollView> | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| export default SegmentedDemo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import React from 'react' | ||
| import Demo1 from './demos/h5/demo1' | ||
| import Demo2 from './demos/h5/demo2' | ||
| import Demo3 from './demos/h5/demo3' | ||
| import { useTranslate } from '@/sites/assets/locale' | ||
|
|
||
| const SegmentedDemo = () => { | ||
| const [translated] = useTranslate({ | ||
| 'zh-CN': { | ||
| uncontrolled: '非受控', | ||
| controlled: '受控', | ||
| optionItems: '设置图标', | ||
| }, | ||
| }) | ||
| return ( | ||
| <div className="demo"> | ||
| <h2>{translated.uncontrolled}</h2> | ||
| <Demo1 /> | ||
| <h2>{translated.controlled}</h2> | ||
| <Demo2 /> | ||
| <h2>{translated.optionItems}</h2> | ||
| <Demo3 /> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default SegmentedDemo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import React from 'react' | ||
| import { Segmented, Cell } from '@nutui/nutui-react' | ||
|
|
||
| const defaultOptions = ['Daily', 'Weekly', 'Monthly'] | ||
|
|
||
| const Demo1 = () => { | ||
| return ( | ||
| <Cell> | ||
| <Segmented | ||
| defaultValue={0} | ||
| options={defaultOptions} | ||
| onChange={(e) => { | ||
| console.log('onChange', e) | ||
| }} | ||
| /> | ||
| </Cell> | ||
| ) | ||
| } | ||
| export default Demo1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import React, { useState } from 'react' | ||
| import { Segmented, Cell } from '@nutui/nutui-react' | ||
|
|
||
| const defaultOptions = ['Daily', 'Weekly', 'Monthly'] | ||
|
|
||
| const Demo2 = () => { | ||
| const [value, setValue] = useState<string | number>(1) | ||
oasis-cloud marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return ( | ||
| <Cell> | ||
| <Segmented | ||
| value={value} | ||
| options={defaultOptions} | ||
| onChange={(val) => { | ||
| setValue(val) | ||
| }} | ||
| /> | ||
| </Cell> | ||
| ) | ||
| } | ||
| export default Demo2 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import React, { useState } from 'react' | ||
| import { Segmented, SegmentedItem, Cell } from '@nutui/nutui-react' | ||
| import { AfterSaleService, Apps } from '@nutui/icons-react' | ||
|
|
||
| const defaultOptions: SegmentedItem[] = [ | ||
| { | ||
| label: 'Apps', | ||
| value: 'Apps', | ||
| icon: <Apps />, | ||
| }, | ||
| { | ||
| label: 'AfterSaleService', | ||
| value: 'AfterSaleService', | ||
| icon: <AfterSaleService />, | ||
| }, | ||
| ] | ||
|
|
||
| const Demo3 = () => { | ||
| const [value, setValue] = useState<string | number>('Apps') | ||
| return ( | ||
| <Cell> | ||
| <Segmented | ||
| value={value} | ||
| options={defaultOptions} | ||
| onChange={(val) => { | ||
| setValue(val) | ||
| }} | ||
| /> | ||
| </Cell> | ||
| ) | ||
| } | ||
| export default Demo3 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import React from 'react' | ||
| import { Segmented, Cell } from '@nutui/nutui-react-taro' | ||
|
|
||
| const defaultOptions = ['Daily', 'Weekly', 'Monthly'] | ||
|
|
||
| const Demo1 = () => { | ||
| return ( | ||
| <Cell> | ||
| <Segmented | ||
| defaultValue={0} | ||
| options={defaultOptions} | ||
| onChange={(e) => { | ||
| console.log('onChange', e) | ||
| }} | ||
| /> | ||
| </Cell> | ||
| ) | ||
| } | ||
| export default Demo1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import React, { useState } from 'react' | ||
| import { Segmented, Cell } from '@nutui/nutui-react-taro' | ||
|
|
||
| const defaultOptions = ['Daily', 'Weekly', 'Monthly'] | ||
|
|
||
| const Demo2 = () => { | ||
| const [value, setValue] = useState<string | number>(1) | ||
oasis-cloud marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return ( | ||
| <Cell> | ||
| <Segmented | ||
| value={value} | ||
| options={defaultOptions} | ||
| onChange={(val) => { | ||
| setValue(val) | ||
| }} | ||
| /> | ||
| </Cell> | ||
| ) | ||
| } | ||
| export default Demo2 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import React, { useState } from 'react' | ||
| import { Segmented, SegmentedItem, Cell } from '@nutui/nutui-react-taro' | ||
| import { AfterSaleService, Apps } from '@nutui/icons-react-taro' | ||
|
|
||
| const defaultOptions: SegmentedItem[] = [ | ||
| { | ||
| label: 'Apps', | ||
| value: 'Apps', | ||
| icon: <Apps color="#ffffff" />, | ||
| }, | ||
| { | ||
| label: 'AfterSaleService', | ||
| value: 'AfterSaleService', | ||
| icon: <AfterSaleService color="#ffffff" />, | ||
| }, | ||
| ] | ||
|
|
||
| const Demo3 = () => { | ||
| const [value, setValue] = useState<string | number>('Apps') | ||
| return ( | ||
| <Cell> | ||
| <Segmented | ||
| value={value} | ||
| options={defaultOptions} | ||
| onChange={(val) => { | ||
| setValue(val) | ||
| }} | ||
| /> | ||
| </Cell> | ||
| ) | ||
| } | ||
| export default Demo3 |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||
| # Segmented | ||||||
|
|
||||||
| Used for switching content cards with page type distinctions, such as switching between main image videos and text with images. | ||||||
|
|
||||||
| ## Import | ||||||
|
|
||||||
| ```tsx | ||||||
| import { Segmented } from '@nutui/nutui-react' | ||||||
| ``` | ||||||
|
|
||||||
| ## Demo | ||||||
|
|
||||||
| ### Uncontrolled | ||||||
|
|
||||||
| :::demo | ||||||
|
|
||||||
| <CodeBlock src='h5/demo1.tsx'></CodeBlock> | ||||||
|
|
||||||
| ::: | ||||||
|
|
||||||
| ### Controlled | ||||||
|
|
||||||
| :::demo | ||||||
|
|
||||||
| <CodeBlock src='h5/demo2.tsx'></CodeBlock> | ||||||
|
|
||||||
| ::: | ||||||
|
|
||||||
| ### Icon | ||||||
|
|
||||||
| :::demo | ||||||
|
|
||||||
| <CodeBlock src='h5/demo3.tsx'></CodeBlock> | ||||||
|
|
||||||
| ::: | ||||||
|
|
||||||
| ## Segmented | ||||||
|
|
||||||
| ### Props | ||||||
|
|
||||||
| | Property | Description | Type | Default | | ||||||
| | --- | --- | --- | --- | | ||||||
| | value | Currently selected value | `string \| number` | `-` | | ||||||
| | defaultValue | Default selected value | `string \| number` | `-` | | ||||||
| | options | Set children optional | `string[] \| number[]\| SegmentedItem[]` | `-` | | ||||||
| | onChange | The callback function that is triggered when the state changes | `(value: string \| number) => void` | `-` | | ||||||
|
|
||||||
| ### SegmentedItem | ||||||
|
|
||||||
| | Property | Description | Type | Default | | ||||||
| | --- | --- | --- | --- | | ||||||
| | label | Display text for Segmented item | `ReactNode` | `-` | | ||||||
| | value | Value for Segmented item | `string \| number` | `-` | | ||||||
| | disabled | Disabled state of segmented item | `boolean` | `false` | | ||||||
| | icon | Display icon for Segmented item | `ReactNode` | `-` | | ||||||
| | className | The additional css class | `string` | `-` | | ||||||
|
|
||||||
| ## Theming | ||||||
|
|
||||||
| ### CSS Variables | ||||||
|
|
||||||
| The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/component/configprovider). | ||||||
|
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. 修复 ConfigProvider 组件的链接 链接 -Please refer to [ConfigProvider component](#/en-US/component/configprovider).
+Please refer to [ConfigProvider component](../configprovider/doc.en-US.md).📝 Committable suggestion
Suggested change
🧰 Tools🪛 Markdownlint62-62: null (MD051, link-fragments) |
||||||
|
|
||||||
| | Name | Description | Default Value | | ||||||
| | --- | --- | --- | | ||||||
| | \--nutui-segmented-padding | Padding inside the segmented selector | `$spacing-xxxs` | | ||||||
| | \--nutui-segmented-radius | Border radius of the segmented selector | `$radius-xs` | | ||||||
| | \--nutui-segmented-background | Background color of the segmented selector | `$color-mask-part` | | ||||||
| | \--nutui-segmented-item-padding | Padding for options in the segmented selector | `$spacing-xxs $spacing-xs` | | ||||||
| | \--nutui-segmented-item-radius | Border radius for options in the segmented selector | `$spacing-xxs $spacing-xs` | | ||||||
| | \--nutui-segmented-item-fontsize | Font size for options in the segmented selector | `$font-size-s` | | ||||||
| | \--nutui-segmented-item-color | Text color for options in the segmented selector | `$color-primary-text` | | ||||||
| | \--nutui-segmented-active-background | Background color for selected options in the segmented selector | `$color-mask-part` | | ||||||
| | \--nutui-segmented-icon-margin-right | Spacing between options in the segmented selector | `$color-mask-part` | | ||||||
Uh oh!
There was an error while loading. Please reload this page.