-
Notifications
You must be signed in to change notification settings - Fork 266
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
feat(segmented): v14 #2762
Conversation
Walkthrough此次更改引入了一个新的组件 Changes
Suggested labels
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feat_v3.x #2762 +/- ##
=============================================
+ Coverage 84.00% 84.11% +0.10%
=============================================
Files 220 271 +51
Lines 17901 18075 +174
Branches 2678 2687 +9
=============================================
+ Hits 15038 15204 +166
- Misses 2858 2866 +8
Partials 5 5 ☔ View full report in Codecov by Sentry. |
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.
Actionable comments posted: 11
🧹 Outside diff range and nitpick comments (24)
src/packages/segmented/demos/h5/demo2.tsx (3)
1-4
: 建议将选项数组类型定义更严格建议为
defaultOptions
添加明确的类型定义,以增强类型安全性。建议修改如下:
import React, { useState } from 'react' import { Segmented } from '@nutui/nutui-react' -const defaultOptions = ['Daily', 'Weekly', 'Monthly'] +const defaultOptions: string[] = ['Daily', 'Weekly', 'Monthly']
9-15
: 建议优化回调函数的写法当前的 onChange 回调函数可以简化,直接传递 setValue 函数即可。
建议修改如下:
<Segmented value={value} options={defaultOptions} - onChange={(val) => { - setValue(val) - }} + onChange={setValue} />
6-17
: 建议添加组件说明文档作为演示组件,建议添加注释说明该组件的用途和使用场景。
建议在组件定义前添加如下注释:
+/** + * 演示 Segmented 组件的基础用法 + * 展示如何使用字符串选项数组和状态管理 + */ const Demo2 = () => {src/packages/segmented/demos/taro/demo2.tsx (2)
4-4
: 建议将选项定义为更有意义的常量对象建议将选项数组重构为具有更明确含义的常量对象,这样可以提高代码的可维护性和可读性。
-const defaultOptions = ['Daily', 'Weekly', 'Monthly'] +const TIME_RANGE_OPTIONS = { + DAILY: 'Daily', + WEEKLY: 'Weekly', + MONTHLY: 'Monthly' +} +const defaultOptions = Object.values(TIME_RANGE_OPTIONS)
9-16
: 建议添加必要的属性说明组件的基本功能实现正确,但建议:
- 添加
disabled
属性来展示禁用状态的用法- 考虑添加错误处理逻辑
- 为了提高可访问性,建议添加
aria-label
属性<Segmented value={value} options={defaultOptions} style={{ width: 150 }} + aria-label="时间范围选择" onChange={(val) => { + try { setValue(val) + } catch (error) { + console.error('选择器值更新失败:', error) + } }} />src/packages/segmented/types.ts (1)
4-10
: 建议添加属性描述和验证属性接口定义基本合理,但建议做如下改进:
- 添加JSDoc注释,说明每个属性的用途
- 考虑添加验证相关的属性,如
maxLength
用于限制label长度建议按照以下方式改进代码:
+/** + * 分段选择器选项配置 + */ export interface SegmentedItem { + /** 选项的显示文本 */ label: ReactNode + /** 选项的值 */ value: string | number + /** 选项的图标 */ icon?: ReactNode + /** 是否禁用该选项 */ disabled?: boolean + /** 自定义选项类名 */ className?: string + /** 文本最大长度 */ + maxLength?: number }src/packages/segmented/demo.tsx (2)
8-14
: 建议添加英文翻译当前仅实现了中文翻译,建议添加英文翻译以支持国际化。
const [translated] = useTranslate({ 'zh-CN': { uncontrolled: '非受控', controlled: '受控', optionItems: '设置图标', }, + 'en-US': { + uncontrolled: 'Uncontrolled', + controlled: 'Controlled', + optionItems: 'Set Icons', + }, })
16-24
: 建议优化样式结构当前的演示组件结构清晰,但建议为每个演示部分添加独立的样式类,以便更好地控制布局和间距。
<div className="demo"> - <h2>{translated.uncontrolled}</h2> - <Demo1 /> - <h2>{translated.controlled}</h2> - <Demo2 /> - <h2>{translated.optionItems}</h2> - <Demo3 /> + <section className="demo-section"> + <h2 className="demo-section__title">{translated.uncontrolled}</h2> + <Demo1 className="demo-section__content" /> + </section> + <section className="demo-section"> + <h2 className="demo-section__title">{translated.controlled}</h2> + <Demo2 className="demo-section__content" /> + </section> + <section className="demo-section"> + <h2 className="demo-section__title">{translated.optionItems}</h2> + <Demo3 className="demo-section__content" /> + </section> </div>src/packages/segmented/demos/h5/demo3.tsx (1)
18-29
: 建议进行以下改进:
- value 类型可以更具体,建议使用联合类型限制为实际可能的值
- 建议添加无障碍属性以提高可访问性
建议按照以下方式修改代码:
- const [value, setValue] = useState<string | number>('Apps') + const [value, setValue] = useState<'Apps' | 'AfterSaleService'>('Apps') return ( <Segmented + role="tablist" + aria-label="选项列表" value={value} options={defaultOptions} onChange={(val) => { setValue(val) }} /> )src/packages/segmented/demos/taro/demo3.tsx (3)
9-9
: 建议将图标颜色作为主题变量目前图标颜色被硬编码为白色(#ffffff),建议使用主题变量以支持不同的主题模式。
- icon: <Apps color="#ffffff" />, + icon: <Apps color="var(--nutui-color-primary-text)" />,- icon: <AfterSaleService color="#ffffff" />, + icon: <AfterSaleService color="var(--nutui-color-primary-text)" />,Also applies to: 14-14
24-24
: 建议使用响应式布局组件宽度目前使用固定像素值(173px),建议使用响应式单位或百分比以适应不同屏幕尺寸。
- style={{ width: 173 }} + style={{ width: '100%', maxWidth: '173px' }}
19-19
: 建议使用更严格的类型定义当前 value 的类型定义为
string | number
,但根据 options 的定义,实际上只可能是 'Apps' 或 'AfterSaleService'。建议使用字面量联合类型以提供更好的类型安全性。- const [value, setValue] = useState<string | number>('Apps') + const [value, setValue] = useState<'Apps' | 'AfterSaleService'>('Apps')src/packages/segmented/demo.taro.tsx (2)
11-17
: 建议优化翻译管理方式建议将翻译文本抽离到单独的语言文件中,以便于后期维护和扩展多语言支持。
建议修改如下:
+ // src/packages/segmented/locale/zh-CN.ts + export default { + uncontrolled: '非受控', + controlled: '受控', + optionItems: '设置图标', + } const [translated] = useTranslate({ - 'zh-CN': { - uncontrolled: '非受控', - controlled: '受控', - optionItems: '设置图标', - }, + 'zh-CN': require('./locale/zh-CN').default, })
21-28
: 渲染结构合理,建议优化类名判断组件结构清晰,但建议简化环境判断的写法。
建议修改如下:
- <ScrollView className={`demo ${Taro.getEnv() === 'WEB' ? 'web' : ''}`}> + <ScrollView className={`demo ${process.env.TARO_ENV === 'h5' ? 'web' : ''}`}>这样可以在编译时确定环境,提高性能。
src/packages/segmented/__tests__/segmented.spec.tsx (1)
7-24
: 建议增强测试用例覆盖范围当前测试用例可以进一步完善:
- 需要验证初始选中状态是否正确(defaultValue={0} 对应 'Daily')
- 点击后的视觉状态变化未验证
- 未验证 onChange 回调函数接收到的具体参数值
建议按照以下方式增强测试:
test('Simple Value Testing', () => { const handleChange = vi.fn(() => {}) const { queryByText } = render( <Segmented defaultValue={0} options={defaultOptions} onChange={handleChange} /> ) + // 验证初始选中状态 + const firstItem = queryByText('Daily') + expect(firstItem?.parentElement).toHaveClass('nut-segmented-item-active') + expect(queryByText('Weekly')).toBeInTheDocument() const secondItem = queryByText('Weekly') if (secondItem) { fireEvent.click(secondItem) } expect(handleChange).toBeCalled() + // 验证回调参数 + expect(handleChange).toHaveBeenCalledWith(1, 'Weekly') + // 验证视觉状态 + expect(secondItem?.parentElement).toHaveClass('nut-segmented-item-active') })src/packages/segmented/doc.taro.md (2)
1-4
: 建议扩充组件介绍内容建议在介绍部分添加更多使用场景和实际应用的示例,以帮助开发者更好地理解组件的适用范围。例如:
- 在商品详情页中切换商品信息和评价
- 在个人中心切换不同的订单状态
- 在新闻页面切换不同的新闻分类
37-57
: 建议在属性文档中添加示例值为了提高文档的可用性,建议在属性表格中添加示例值列,特别是对于以下属性:
- options: 添加数组示例
- value/defaultValue: 添加有效值示例
- icon: 添加图标使用示例
src/packages/segmented/segmented.tsx (2)
21-26
: 建议重新考虑默认值的处理方式当前实现将
finalValue
硬编码为 0,这可能不适用于所有场景。建议:
- 使用
options
数组的第一个选项值作为默认值- 或允许通过属性配置默认值
- finalValue: 0, + finalValue: options.length > 0 + ? (typeof options[0] === 'object' + ? (options[0] as SegmentedItem).value + : 0) + : 0,
75-79
: 建议增强组件的可定制性考虑添加以下功能:
- 支持自定义容器类名
- 添加
block
属性以支持块级布局- 支持自定义渲染项的回调函数
interface SegmentedProps { // 现有属性... className?: string; block?: boolean; renderItem?: (item: SegmentedItem, active: boolean) => React.ReactNode; }src/packages/segmented/segmented.taro.tsx (2)
8-13
: 建议改进默认的 onChange 处理函数默认的 onChange 处理函数中使用了 console.log,这在生产环境中并不合适。建议移除或使用更合适的默认行为。
建议修改为:
const defaultProps = { options: [], - onChange: <T = any,>(value: T) => { - console.log(value) - }, + onChange: <T = any,>(value: T) => {}, }
1-81
: 建议增强组件功能为了提升组件的完整性,建议考虑添加以下功能:
- 添加键盘导航支持(左右箭头键)
- 添加 ARIA 属性以提升可访问性
- 使用 React.memo 优化渲染性能
- 添加 block 属性支持自适应宽度
🧰 Tools
🪛 Biome
[error] 35-35: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 54-54: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
[error] 55-55: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
src/packages/segmented/doc.en-US.md (2)
1-4
: 建议扩充组件描述内容建议在组件描述中添加更多具体的使用场景和示例,比如:
- 在表单中用于选择互斥的选项
- 在数据展示页面中切换不同的数据视图
- 在设置页面中切换不同的配置选项
这将帮助用户更好地理解组件的适用场景。
41-56
: 建议为属性文档添加示例代码为了帮助用户更好地理解各个属性的使用方法,建议在属性表格后添加常见用法的代码示例,特别是:
options
属性的不同数据格式示例onChange
事件处理示例- 带有图标的
SegmentedItem
配置示例src/styles/variables.scss (1)
1945-1973
: 代码实现规范,建议补充注释说明变量定义遵循了设计系统规范,使用了合适的设计令牌作为默认值。建议为每个变量添加简短的中文注释,说明其用途。
例如:
// 分段器内边距 $segmented-padding: var(--nutui-segmented-padding, $spacing-xxxs) !default; // 分段器圆角 $segmented-border-radius: var(--nutui-segmented-radius, $radius-xs) !default;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (21)
src/config.json
(1 hunks)src/packages/segmented/__tests__/segmented.spec.tsx
(1 hunks)src/packages/segmented/demo.taro.tsx
(1 hunks)src/packages/segmented/demo.tsx
(1 hunks)src/packages/segmented/demos/h5/demo1.tsx
(1 hunks)src/packages/segmented/demos/h5/demo2.tsx
(1 hunks)src/packages/segmented/demos/h5/demo3.tsx
(1 hunks)src/packages/segmented/demos/taro/demo1.tsx
(1 hunks)src/packages/segmented/demos/taro/demo2.tsx
(1 hunks)src/packages/segmented/demos/taro/demo3.tsx
(1 hunks)src/packages/segmented/doc.en-US.md
(1 hunks)src/packages/segmented/doc.md
(1 hunks)src/packages/segmented/doc.taro.md
(1 hunks)src/packages/segmented/doc.zh-TW.md
(1 hunks)src/packages/segmented/index.taro.ts
(1 hunks)src/packages/segmented/index.ts
(1 hunks)src/packages/segmented/segmented.scss
(1 hunks)src/packages/segmented/segmented.taro.tsx
(1 hunks)src/packages/segmented/segmented.tsx
(1 hunks)src/packages/segmented/types.ts
(1 hunks)src/styles/variables.scss
(1 hunks)
✅ Files skipped from review due to trivial changes (5)
- src/packages/segmented/demos/h5/demo1.tsx
- src/packages/segmented/demos/taro/demo1.tsx
- src/packages/segmented/index.taro.ts
- src/packages/segmented/index.ts
- src/packages/segmented/segmented.scss
🧰 Additional context used
🪛 Markdownlint
src/packages/segmented/doc.en-US.md
62-62: null
Link fragments should be valid
(MD051, link-fragments)
src/packages/segmented/doc.md
62-62: null
Link fragments should be valid
(MD051, link-fragments)
src/packages/segmented/doc.taro.md
62-62: null
Link fragments should be valid
(MD051, link-fragments)
src/packages/segmented/doc.zh-TW.md
62-62: null
Link fragments should be valid
(MD051, link-fragments)
🪛 Biome
src/packages/segmented/segmented.taro.tsx
[error] 35-35: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 54-54: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
[error] 55-55: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
src/packages/segmented/segmented.tsx
[error] 34-34: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
[error] 53-53: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
[error] 54-54: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
🪛 GitHub Check: codecov/patch
src/packages/segmented/segmented.tsx
[warning] 10-11: src/packages/segmented/segmented.tsx#L10-L11
Added lines #L10 - L11 were not covered by tests
[warning] 43-45: src/packages/segmented/segmented.tsx#L43-L45
Added lines #L43 - L45 were not covered by tests
[warning] 48-48: src/packages/segmented/segmented.tsx#L48
Added line #L48 was not covered by tests
🔇 Additional comments (18)
src/packages/segmented/demos/taro/demo2.tsx (1)
1-19
: 验证组件在 Taro 环境下的兼容性
需要确保该演示组件在不同端(微信小程序、H5等)都能正常运行。
✅ Verification successful
Segmented 组件在 Taro 环境下兼容性验证已完成
经过代码分析,该组件已经完全适配了 Taro 环境:
- 使用了
@tarojs/components
中的View
组件作为基础渲染组件 - 样式实现采用了跨平台兼容的 CSS,没有平台特定的样式代码
- 组件逻辑中没有平台特定的判断,保证了跨端一致性
演示代码符合组件设计规范,可以在各端正常运行。
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 检查是否有其他 demo 文件包含平台特定的适配代码
rg -l "process\.env\.TARO_ENV" src/packages/segmented/demos/
Length of output: 62
Script:
#!/bin/bash
# 检查 Segmented 组件在 Taro 环境下的实现和适配代码
rg -g "*.tsx" -g "*.ts" "TARO_ENV|process\.env" src/packages/segmented/
# 检查组件是否有平台特定的样式
fd -e scss -e css . src/packages/segmented/
# 检查组件的主要实现文件
ast-grep --pattern 'export $_ function Segmented'
Length of output: 254
Script:
#!/bin/bash
# 检查 Segmented 组件的主要实现代码
cat src/packages/segmented/segmented.taro.tsx
# 检查样式文件中是否包含平台特定的样式
cat src/packages/segmented/segmented.scss
# 检查组件的测试用例
fd -e test.tsx -e spec.tsx . src/packages/segmented/
Length of output: 3391
src/packages/segmented/types.ts (1)
1-2
: 导入声明看起来没有问题!
导入的类型定义符合组件需求,使用了React的ReactNode类型和内部通用的BasicComponent类型。
src/packages/segmented/demo.tsx (2)
27-27
: 导出语句符合规范
组件导出方式正确,符合 React 组件开发规范。
1-6
: 建议验证 Taro 演示组件的实现
代码结构显示已经实现了 H5 版本的演示组件,建议确认是否需要同步实现 Taro 版本的演示组件。
✅ Verification successful
Taro 演示组件已经实现,无需额外操作
验证结果显示 Taro 版本的演示组件(demo1.tsx、demo2.tsx、demo3.tsx)已经在 src/packages/segmented/demos/taro
目录下完整实现。代码结构合理,同时支持了 H5 和 Taro 两个版本的演示。
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 检查是否存在对应的 Taro 演示组件
fd "demo[1-3].tsx" "src/packages/segmented/demos/taro"
Length of output: 186
src/packages/segmented/demos/h5/demo3.tsx (1)
1-16
: 导入和类型定义看起来不错!
代码结构清晰,类型定义准确,图标组件的使用恰当。
src/packages/segmented/demos/taro/demo3.tsx (2)
5-16
: 代码结构清晰,实现合理
选项定义结构清晰,类型注解正确,实现符合组件预期用途。
18-30
: 组件实现简洁有效
Demo3 组件实现简单明了,状态管理合理,展示了图标用法的基本示例。
src/packages/segmented/demo.taro.tsx (2)
1-8
: 导入语句结构清晰!
导入语句组织合理,按照框架、组件、工具类的顺序排列,便于代码维护。
4-6
: 验证示例组件的完整性
需要确保所有导入的示例组件都已正确实现并包含必要的功能演示。
✅ Verification successful
Let me check the actual implementation of these demo components to ensure they are properly implemented.
示例组件导入和实现验证完成
所有示例组件都已正确实现,每个组件都展示了不同的 Segmented 组件使用场景:
- Demo1:基础用法,展示默认选中项和onChange事件
- Demo2:受控组件用法,使用value和setValue管理状态
- Demo3:自定义选项,包含图标的高级用法
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 检查示例组件文件是否存在且包含必要的实现
for demo in demo1 demo2 demo3; do
echo "检查 $demo:"
rg -l "export default .*${demo^}" "src/packages/segmented/demos/taro/$demo.tsx"
done
Length of output: 545
Script:
#!/bin/bash
# 检查每个示例组件的具体实现
for demo in demo1 demo2 demo3; do
echo "=== $demo 的实现 ==="
rg -A 10 "export default .*${demo^}" "src/packages/segmented/demos/taro/$demo.tsx"
done
Length of output: 533
Script:
#!/bin/bash
# 检查每个示例组件的完整内容
for demo in demo1 demo2 demo3; do
echo "=== $demo 的完整实现 ==="
cat "src/packages/segmented/demos/taro/$demo.tsx"
echo -e "\n"
done
Length of output: 1938
src/packages/segmented/__tests__/segmented.spec.tsx (1)
1-6
: 导入路径验证
建议验证从 @nutui/nutui-react
导入是否正确,因为这可能是一个相对路径引用。
✅ Verification successful
Based on the search results, I can see that @nutui/nutui-react
is the correct import path used throughout the codebase, including in test files. The import path in the review comment is consistent with the codebase's usage pattern.
导入路径正确
导入路径 @nutui/nutui-react
已经在整个代码库中得到广泛使用,包括测试文件。当前的导入语句是正确的,无需修改。
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 描述:验证组件导入路径的正确性
# 检查项目中的导入路径模式
rg -g '*.{ts,tsx}' '@nutui/nutui-react'
Length of output: 112190
src/packages/segmented/doc.taro.md (2)
5-10
: 导入示例正确且清晰
11-36
: 验证示例代码文件是否存在
需要确认以下示例代码文件是否已经创建:
- h5/demo1.tsx
- h5/demo2.tsx
- h5/demo3.tsx
✅ Verification successful
示例代码文件已全部存在
所有引用的示例代码文件均已在正确的位置创建:
src/packages/segmented/demos/h5/demo1.tsx
src/packages/segmented/demos/h5/demo2.tsx
src/packages/segmented/demos/h5/demo3.tsx
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 检查示例代码文件是否存在
fd -e tsx "demo[1-3].tsx" "src/packages/segmented"
Length of output: 308
src/packages/segmented/segmented.tsx (2)
14-20
: 需要添加组件属性的单元测试
建议添加以下测试场景:
- 验证属性合并是否正确
- 验证默认属性是否生效
- 验证自定义属性是否正确覆盖默认值
需要我帮您生成相关的测试用例吗?
43-45
: 需要补充关键场景的测试用例
以下场景缺少测试覆盖:
- 禁用选项的点击处理
- 图标渲染功能
建议添加以下测试用例:
describe('Segmented', () => {
it('should not trigger onChange when option is disabled', () => {
// 测试禁用选项
});
it('should render icon correctly', () => {
// 测试图标渲染
});
});
Also applies to: 48-48
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 43-45: src/packages/segmented/segmented.tsx#L43-L45
Added lines #L43 - L45 were not covered by tests
src/packages/segmented/segmented.taro.tsx (2)
15-27
: 状态管理实现合理
组件的状态管理使用了 usePropsValue 钩子,实现了受控和非受控模式的统一处理,代码结构清晰。
76-81
: 渲染实现简洁合理
组件的渲染逻辑清晰,正确使用了 classNames 进行样式管理,并支持自定义样式。
src/packages/segmented/doc.en-US.md (2)
15-35
: 请确认示例代码文件的完整性
需要验证以下示例代码文件是否存在且内容完整:
- h5/demo1.tsx
- h5/demo2.tsx
- h5/demo3.tsx
#!/bin/bash
# 检查示例代码文件是否存在
for demo in demo1 demo2 demo3; do
fd -t f "${demo}.tsx" -p "h5/"
done
66-74
: 请确认基础样式变量的定义
文档中引用了多个基础样式变量,如 $spacing-xxxs
、$radius-xs
等。需要验证这些变量是否在项目中已定义。
✅ Verification successful
Let me try a different approach to find these style variable definitions. I'm searching for common naming patterns of design token/variable files and then looking for the specific variable definitions.
我看到有几个相关的变量和主题文件,让我们检查其内容来确认这些基础变量的定义。
基础样式变量已正确定义
通过检查 src/styles/variables.scss
和 src/styles/theme-default.scss
文件,我可以确认所有在 Segmented 组件中使用的基础样式变量都已正确定义:
$spacing-xxxs
: 定义在 variables.scss 中,值为 2px$radius-xs
: 定义在 variables.scss 中,值为 4px$color-mask-part
: 定义在 variables.scss 中,值为 rgba(0, 0, 0, 0.4)$spacing-xxs
: 定义在 variables.scss 中,值为 4px$spacing-xs
: 定义在 variables.scss 中,值为 6px$font-size-s
: 定义在 variables.scss 中,值为 12px$color-primary-text
: 定义在 variables.scss 中,值为 #ffffff
这些变量都有明确的定义,并且在主题系统中可以通过 CSS 变量进行自定义。
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 搜索基础样式变量的定义
rg -t css -t scss "\$(spacing|radius|color|font-size)"
Length of output: 449
Script:
#!/bin/bash
# 搜索样式变量定义文件
fd -t f "variables|theme|tokens|config" -e scss -e css
# 搜索具体变量定义
rg -t scss -t css "(\$spacing|\$radius|\$color|\$font-size)"
Length of output: 547
Script:
#!/bin/bash
# 检查主题变量文件内容
cat src/styles/variables.scss
cat src/styles/theme-default.scss
Length of output: 85666
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() | ||
}) |
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.
🛠️ 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
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
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() | |
}) | |
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() | |
}) |
|
||
### 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 comment
The reason will be displayed to describe this comment to others. Learn more.
修复 ConfigProvider 组件的链接
链接 [ConfigProvider component](#/en-US/component/configprovider)
格式不正确。建议修改为正确的相对路径。
-Please refer to [ConfigProvider component](#/en-US/component/configprovider).
+Please refer to [ConfigProvider component](../configprovider/doc.en-US.md).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/component/configprovider). | |
The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](../configprovider/doc.en-US.md). |
🧰 Tools
🪛 Markdownlint
62-62: null
Link fragments should be valid
(MD051, link-fragments)
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.
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.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (10)
src/packages/segmented/demos/h5/demo1.tsx
(1 hunks)src/packages/segmented/demos/h5/demo2.tsx
(1 hunks)src/packages/segmented/demos/h5/demo3.tsx
(1 hunks)src/packages/segmented/demos/taro/demo1.tsx
(1 hunks)src/packages/segmented/demos/taro/demo2.tsx
(1 hunks)src/packages/segmented/demos/taro/demo3.tsx
(1 hunks)src/packages/segmented/segmented.scss
(1 hunks)src/packages/segmented/segmented.taro.tsx
(1 hunks)src/packages/segmented/segmented.tsx
(1 hunks)src/styles/variables.scss
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- src/packages/segmented/demos/h5/demo1.tsx
- src/packages/segmented/demos/h5/demo3.tsx
- src/packages/segmented/demos/h5/demo2.tsx
- src/packages/segmented/demos/taro/demo2.tsx
- src/packages/segmented/demos/taro/demo1.tsx
- src/packages/segmented/demos/taro/demo3.tsx
- src/packages/segmented/segmented.scss
🧰 Additional context used
🪛 Biome (1.9.4)
src/packages/segmented/segmented.tsx
[error] 32-32: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
src/packages/segmented/segmented.taro.tsx
[error] 33-33: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🪛 GitHub Check: codecov/patch
src/packages/segmented/segmented.tsx
[warning] 41-43: src/packages/segmented/segmented.tsx#L41-L43
Added lines #L41 - L43 were not covered by tests
[warning] 46-46: src/packages/segmented/segmented.tsx#L46
Added line #L46 was not covered by tests
[warning] 53-54: src/packages/segmented/segmented.tsx#L53-L54
Added lines #L53 - L54 were not covered by tests
🔇 Additional comments (2)
src/packages/segmented/segmented.tsx (1)
29-70
: 🛠️ Refactor suggestion
优化 switch 语句的实现
switch 语句存在以下问题:
- case 'object' 中的变量声明可能导致作用域问题
- 类型检查可以更严格
建议按照以下方式重构:
switch (optionType) {
case 'object': {
- const opt = option as SegmentedItem
+ const opt = option as SegmentedItem
return (
<div
className={classNames(itemClassPrefix, {
[`${itemClassPrefix}-active`]: opt.value === value,
[`${opt.className}`]: !!opt.className,
})}
key={opt.value}
onClick={() => {
if (opt.disabled) return
setValue(opt.value)
}}
>
{opt.icon ? (
<span className="nut-segmented-icon">{opt.icon}</span>
) : null}
{opt.label}
</div>
)
+ }
default: {
+ if (typeof option !== 'string' && typeof option !== 'number') {
+ console.warn('Unsupported option type:', optionType)
+ return null
+ }
return (/* ... */)
}
}
Likely invalid or redundant comment.
🧰 Tools
🪛 Biome (1.9.4)
[error] 32-32: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🪛 GitHub Check: codecov/patch
[warning] 41-43: src/packages/segmented/segmented.tsx#L41-L43
Added lines #L41 - L43 were not covered by tests
[warning] 46-46: src/packages/segmented/segmented.tsx#L46
Added line #L46 was not covered by tests
[warning] 53-54: src/packages/segmented/segmented.tsx#L53-L54
Added lines #L53 - L54 were not covered by tests
src/styles/variables.scss (1)
1972-2002
: 样式变量定义合理
新增的 Segmented 组件样式变量:
- 遵循项目命名规范
- 提供了合适的默认值
- 支持自定义属性覆盖
if (opt.disabled) return | ||
setValue(opt.value) | ||
}} |
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.
增加测试覆盖率
以下代码路径缺少测试覆盖:
- 禁用状态的点击处理
- 图标渲染逻辑
- 不支持的选项类型处理
建议添加以下测试场景:
- 测试禁用状态的选项点击
- 测试带图标选项的渲染
- 测试传入不支持类型时的警告
Also applies to: 46-46, 53-54
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 41-43: src/packages/segmented/segmented.tsx#L41-L43
Added lines #L41 - L43 were not covered by tests
import React, { useCallback } from 'react' | ||
import classNames from 'classnames' | ||
import { View } from '@tarojs/components' | ||
import { SegmentedItem, SegmentedProps } from './types' | ||
import { usePropsValue } from '@/utils/use-props-value' | ||
import { mergeProps } from '@/utils/merge-props' | ||
|
||
const defaultProps = { | ||
options: [], | ||
onChange: <T = any,>(value: T) => {}, | ||
} | ||
|
||
export const Segmented = (props: Partial<SegmentedProps>) => { | ||
const classPrefix = 'nut-segmented' | ||
const itemClassPrefix = 'nut-segmented-item' | ||
const mergedProps = mergeProps(defaultProps, props) | ||
|
||
const { options, onChange } = mergedProps | ||
|
||
const [value, setValue] = usePropsValue({ | ||
value: mergedProps.value, | ||
defaultValue: mergedProps.defaultValue, | ||
finalValue: 0, | ||
onChange: mergedProps.onChange, | ||
}) | ||
const renderItems = useCallback( | ||
(options: SegmentedProps['options'], value: string | number) => { | ||
return options.map((option, index) => { | ||
const optionType = typeof option | ||
switch (optionType) { | ||
case 'object': | ||
// eslint-disable-next-line no-case-declarations | ||
const opt = option as SegmentedItem | ||
return ( | ||
<View | ||
className={classNames(itemClassPrefix, { | ||
[`${itemClassPrefix}-active`]: opt.value === value, | ||
[`${opt.className}`]: !!opt.className, | ||
})} | ||
key={opt.value} | ||
onClick={() => { | ||
if (opt.disabled) return | ||
setValue((option as SegmentedItem).value) | ||
}} | ||
> | ||
{opt.icon ? ( | ||
<View className="nut-segmented-icon">{opt.icon}</View> | ||
) : null} | ||
{opt.label} | ||
</View> | ||
) | ||
default: { | ||
if (typeof option !== 'string' && typeof option !== 'number') { | ||
console.warn('Unsupported option type:', optionType) | ||
} | ||
return ( | ||
<View | ||
className={classNames(itemClassPrefix, { | ||
[`${itemClassPrefix}-active`]: index === value, | ||
})} | ||
key={option.toString()} | ||
onClick={() => { | ||
setValue(index) | ||
}} | ||
> | ||
{option as string} | ||
</View> | ||
) | ||
} | ||
} | ||
}) | ||
}, | ||
[value] | ||
) | ||
|
||
return ( | ||
<View className={classNames(classPrefix)} style={mergedProps.style}> | ||
{renderItems(options, value)} | ||
</View> | ||
) | ||
} |
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.
🛠️ Refactor suggestion
建议提取共享逻辑
React 版本和 Taro 版本存在大量重复代码。建议:
- 提取共享的业务逻辑到独立的 hooks
- 只在视图层区分平台差异
建议创建以下文件:
// hooks/useSegmented.ts
export function useSegmented(props: SegmentedProps) {
const [value, setValue] = usePropsValue({/*...*/})
const renderItems = useCallback((/*...*/) => {
// 共享的渲染逻辑
}, [value])
return {
value,
setValue,
renderItems
}
}
然后在两个组件中复用:
// segmented.tsx / segmented.taro.tsx
export const Segmented = (props: SegmentedProps) => {
const { value, setValue, renderItems } = useSegmented(props)
// 只处理平台特定的视图逻辑
}
🧰 Tools
🪛 Biome (1.9.4)
[error] 33-33: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🤔 这个变动的性质是?
🔗 相关 Issue
💡 需求背景和解决方案
☑️ 请求合并前的自查清单
Summary by CodeRabbit
发布说明
新特性
Segmented
组件,支持内容卡片的切换。Demo1
,Demo2
,Demo3
),展示了不同的使用场景。文档
Segmented
组件新增了详细的英文、中文(简体和繁体)文档,包含组件的使用说明和属性描述。样式
Segmented
组件的视觉效果和可定制性。类型
SegmentedItem
和SegmentedProps
的类型定义,增强了组件的类型检查和可维护性。