feat(searchbar): V15适配,新增tag的实现方式#3214
Conversation
|
Warning Rate limit exceeded@xiaoyatong has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 10 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
## Walkthrough
本次更新对 SearchBar 组件进行了较大幅度的功能扩展与样式优化。主要包括:支持受控与非受控模式(value/defaultValue)、新增 tag 标签模式及 onItemClick 事件、完善了受控输入的处理逻辑,并对组件样式进行了重构和变量优化。相关文档、类型定义、测试用例和演示 demo 也同步进行了补充和调整,确保新特性和样式的正确展示与使用。此外,部分 SCSS 变量和类型声明进行了精简和替换,以适应新的样式结构和功能需求。
## Changes
| 文件/分组 | 变更摘要 |
|-------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| scripts/harmony/update-taro-entry.js | 优化 configRef 的构建与过滤逻辑,确保 pages 非空后再返回。 |
| src/packages/configprovider/types.ts | NutCSSVariables 类型新增 'nutuiSearchbarInnerGap'、'nutuiSearchbarIconSize',移除 'nutuiSearchbarContentPadding'。 |
| src/packages/searchbar/searchbar.tsx<br>src/packages/searchbar/searchbar.taro.tsx | SearchBar 组件重构,支持 tag、defaultValue、onItemClick 等新属性,受控/非受控模式切换,事件与渲染逻辑优化。 |
| src/packages/searchbar/searchbar.scss | SearchBar 样式重构,增加 tag 模式、焦点态、icon/间距/高度等变量优化。 |
| src/packages/searchbar/__tests__/searchbar.spec.tsx | 测试用例重写,覆盖新特性与交互,提升健壮性。 |
| src/packages/searchbar/demo.tsx<br>src/packages/searchbar/demo.taro.tsx | 新增 Demo10 演示 defaultValue、受控模式及 tag 标签用法,调整部分 demo 顺序。 |
| src/packages/searchbar/demos/h5/demo1.tsx<br>src/packages/searchbar/demos/taro/demo1.tsx | demo1 结构优化,丰富右侧内容与交互,icon 统一包内引入。 |
| src/packages/searchbar/demos/h5/demo10.tsx<br>src/packages/searchbar/demos/taro/demo10.tsx | 新增 demo10,演示 tag、defaultValue、onItemClick 等新特性。 |
| src/packages/searchbar/demos/h5/demo2.tsx<br>...<br>src/packages/searchbar/demos/h5/demo7.tsx | SearchBar 组件及相关依赖统一改为包内绝对路径引入,部分 icon 尺寸属性移除。 |
| src/packages/searchbar/demos/h5/demo3.tsx<br>src/packages/searchbar/demos/h5/demo4.tsx<br>src/packages/searchbar/demos/h5/demo5.tsx<br>src/packages/searchbar/demos/h5/demo6.tsx | demo 样式变量及组件引入方式优化,icon 尺寸属性移除,Popover 组件命名对齐。 |
| src/packages/searchbar/doc.md<br>src/packages/searchbar/doc.en-US.md<br>src/packages/searchbar/doc.taro.md<br>src/packages/searchbar/doc.zh-TW.md | 文档补充 defaultValue、tag、onItemClick 属性说明及演示,props 表格与 CSS 变量说明同步更新。 |
| src/sites/sites-react/doc/docs/react/migrate-from-v2.md<br>src/sites/sites-react/doc/docs/taro/migrate-from-v2.md | 迁移文档补充 value、defaultValue、onItemClick 新特性说明。 |
| src/styles/variables.scss<br>src/styles/variables-jmapp.scss<br>src/styles/variables-jrkf.scss | searchbar 相关 SCSS 变量调整,移除 content-padding,新增 inner-gap、icon-size 等。 |
| src/types/spec/searchbar/base.ts | BaseSearchBar 接口新增 defaultValue、tag、onItemClick 属性定义。 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant 用户
participant SearchBar
participant 父组件
用户->>SearchBar: 输入/聚焦/点击 tag
SearchBar->>SearchBar: 受控/非受控 value 管理(usePropsValue)
SearchBar->>SearchBar: tag 模式下渲染标签
用户->>SearchBar: 点击 tag 上 x
SearchBar->>父组件: 触发 onItemClick 回调
用户->>SearchBar: 点击清除按钮
SearchBar->>SearchBar: 清空 value 并聚焦输入框
SearchBar->>父组件: 触发 onClear/onChange 等事件Possibly related PRs
Suggested reviewers
Poem
|
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (11)
src/packages/searchbar/doc.en-US.md (1)
23-30: 校对文档标题与格式
英文文档中新加入的标题### DefaultValue And Controlled建议调整为更符合英语习惯的### Default Value & Controlled或### Default Value and Controlled,并考虑将defaultValue、tag、onItemClick等 Prop 名称用反引号包裹以提高可读性。此外,请验证h5/demo10.tsx文件是否存在并正确演示功能。src/packages/searchbar/demos/taro/demo10.tsx (2)
8-36: 完善的标签模式示例实现实现了搜索栏标签模式的功能展示,包含标签删除、焦点处理和值更新的完整逻辑流程。
不过,处理标签删除的逻辑较为复杂,建议提取为单独的辅助函数以提高可维护性:
-onItemClick={(val: string) => { - console.log('click', val) - const arr = value1.split(',') - const newArr = arr.filter((item: string) => item !== val) - const newVal = newArr.length > 1 ? newArr.join(',') : newArr.join('') - setValue1(newVal) -}} +onItemClick={(val: string) => { + console.log('click', val) + setValue1(removeTag(value1, val)) +}} // 在组件外部或内部定义辅助函数 +const removeTag = (valueStr: string, tagToRemove: string) => { + const arr = valueStr.split(',') + const newArr = arr.filter((item: string) => item !== tagToRemove) + return newArr.length > 1 ? newArr.join(',') : newArr.join('') +}
37-53: 移除注释掉的代码代码中包含注释掉的 SearchBar 组件。在生产环境的示例代码中,最好移除未使用的注释代码,保持示例的简洁性和清晰度。
-{/* <SearchBar - backable - leftIn={null} - value={value} - onChange={(val) => { - setValue(val) - }} - // autoFocus - rightIn={ - <div style={{ display: 'flex', alignItems: 'center' }}> - <Photograph color="#505259" style={{ marginRight: '12px' }} /> - <Button type="primary" size="mini"> - 搜索 - </Button> - </div> - } -/> */}src/packages/searchbar/demos/h5/demo10.tsx (2)
6-7: 考虑国际化支持的初始值设置当前使用了硬编码的中文字符串作为初始值。考虑到国际化需求,建议使用可翻译的字符串或从翻译模块获取初始值。
-const [value, setValue] = useState('醋溜土豆丝') -const [value1, setValue1] = useState('西红柿,铁皮') +// 假设有 useTranslate 或类似的翻译钩子 +const [translated] = useTranslate({ + 'zh-CN': { + defaultFood: '醋溜土豆丝', + taggedFood: '西红柿,铁皮' + }, + 'en-US': { + defaultFood: 'potato slices', + taggedFood: 'tomato,iron skin' + } +}) +const [value, setValue] = useState(translated.defaultFood) +const [value1, setValue1] = useState(translated.taggedFood)
10-36: 完善的标签模式实现SearchBar 组件的标签模式实现完整,包含了标签的展示、删除和状态管理逻辑。
同样,为提高可维护性,建议将标签删除逻辑提取为辅助函数:
-onItemClick={(val: string) => { - console.log('click', val) - const arr = value1.split(',') - const newArr = arr.filter((item: string) => item !== val) - const newVal = newArr.length > 1 ? newArr.join(',') : newArr.join('') - setValue1(newVal) -}} +onItemClick={(val: string) => { + console.log('click', val) + setValue1(removeTag(value1, val)) +}} // 在组件外部或内部定义辅助函数 +const removeTag = (valueStr: string, tagToRemove: string) => { + const arr = valueStr.split(',') + const newArr = arr.filter((item: string) => item !== tagToRemove) + return newArr.length > 1 ? newArr.join(',') : newArr.join('') +}src/packages/searchbar/searchbar.tsx (1)
109-117: blur 处理逻辑可能造成重复触发
handleBlur内再次调用searchInputRef.current?.blur(),在大多数浏览器中blur事件本身已处于失焦阶段,再次触发无必要且可能导致某些环境下触发两次blur监听。建议移除此行,或添加注释说明设计初衷。
🧰 Tools
🪛 Biome (1.9.4)
[error] 112-112: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
src/packages/searchbar/searchbar.scss (1)
73-84: 清除图标尺寸硬编码,建议使用变量以保持主题一致性
&-clear区块对容器和内部.nut-icon的宽高分别写死为20px/12px,一旦设计规范调整或需要适配不同密度屏幕时难以统一修改。
建议新增$searchbar-clear-size、$searchbar-clear-icon-size变量,或直接复用$searchbar-icon-size,同时把颜色var(--nutui-black-5)也替换成主题色变量,避免出现多处手动同步的问题。-&-clear { - width: 20px; - height: 20px; +&-clear { + width: $searchbar-clear-size; + height: $searchbar-clear-size; &.nut-searchbar-icon { .nut-icon { - width: 12px; - height: 12px; - color: var(--nutui-black-5); + width: $searchbar-clear-icon-size; + height: $searchbar-clear-icon-size; + color: $searchbar-clear-icon-color; margin-right: $searchbar-inner-gap; } } }src/packages/searchbar/searchbar.taro.tsx (3)
110-118:handleBlur中重复调用blur()意义不大
blur事件回调已经说明输入框失焦,内部再次searchInputRef.current?.blur()属于冗余操作,还可能触发意料外的副作用。
建议删除该调用。🧰 Tools
🪛 Biome (1.9.4)
[error] 113-113: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
133-143:focus样式命名与含义不符,建议更直观的状态标识
cls中的${classPrefix}-focus实际依据left || backable添加,看上去像“有返回/左侧图标” 状态而非真正的焦点状态。
为了避免误读,可改名为${classPrefix}-with-left或在逻辑上改为判断isFocused。
178-202: Tag 渲染未做空值与空白过滤,可能产生空标签
value.split(',')若存在连续逗号或首尾逗号会产生空字符串项,应在map前通过filter(Boolean)去除。-const list = value.split(',') +const list = value.split(',').filter(Boolean)src/styles/variables.scss (1)
1953-1958: 新增输入框 Padding 建议改用单行变量,便于使用者覆盖
$searchbar-input-padding的默认值写成0 0 0 8px,但很多场景仅想修改左右内边距。
推荐拆分为$searchbar-input-padding-x和$searchbar-input-padding-y或继续沿用四边统一变量,让配置更细粒度。
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (29)
scripts/harmony/update-taro-entry.js(2 hunks)src/packages/configprovider/types.ts(1 hunks)src/packages/searchbar/__tests__/searchbar.spec.tsx(1 hunks)src/packages/searchbar/demo.taro.tsx(4 hunks)src/packages/searchbar/demo.tsx(3 hunks)src/packages/searchbar/demos/h5/demo1.tsx(1 hunks)src/packages/searchbar/demos/h5/demo10.tsx(1 hunks)src/packages/searchbar/demos/h5/demo2.tsx(1 hunks)src/packages/searchbar/demos/h5/demo3.tsx(1 hunks)src/packages/searchbar/demos/h5/demo4.tsx(1 hunks)src/packages/searchbar/demos/h5/demo5.tsx(1 hunks)src/packages/searchbar/demos/h5/demo6.tsx(3 hunks)src/packages/searchbar/demos/h5/demo7.tsx(1 hunks)src/packages/searchbar/demos/taro/demo1.tsx(1 hunks)src/packages/searchbar/demos/taro/demo10.tsx(1 hunks)src/packages/searchbar/demos/taro/demo5.tsx(1 hunks)src/packages/searchbar/doc.en-US.md(1 hunks)src/packages/searchbar/doc.md(4 hunks)src/packages/searchbar/doc.taro.md(4 hunks)src/packages/searchbar/doc.zh-TW.md(3 hunks)src/packages/searchbar/searchbar.scss(2 hunks)src/packages/searchbar/searchbar.taro.tsx(4 hunks)src/packages/searchbar/searchbar.tsx(4 hunks)src/sites/sites-react/doc/docs/react/migrate-from-v2.md(1 hunks)src/sites/sites-react/doc/docs/taro/migrate-from-v2.md(1 hunks)src/styles/variables-jmapp.scss(2 hunks)src/styles/variables-jrkf.scss(2 hunks)src/styles/variables.scss(1 hunks)src/types/spec/searchbar/base.ts(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/packages/searchbar/demos/taro/demo5.tsx (1)
src/packages/calendarcard/icon.taro.tsx (1)
ArrowLeft(44-44)
src/packages/searchbar/demos/h5/demo5.tsx (2)
src/packages/searchbar/searchbar.tsx (1)
SearchBar(33-273)src/packages/calendarcard/icon.taro.tsx (1)
ArrowLeft(44-44)
scripts/harmony/update-taro-entry.js (3)
scripts/taro/generate-taro-route.js (2)
configRef(9-9)config(2-2)scripts/taro/generate-taro-pages.js (1)
config(4-4)scripts/create-component-mode.js (1)
config(5-5)
src/packages/searchbar/demos/h5/demo1.tsx (1)
src/packages/searchbar/searchbar.tsx (1)
SearchBar(33-273)
src/packages/searchbar/demos/h5/demo10.tsx (1)
src/packages/searchbar/searchbar.tsx (1)
SearchBar(33-273)
🪛 LanguageTool
src/packages/searchbar/doc.taro.md
[locale-violation] ~129-~129: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ing值 | 1px 8px | | --nutui-searchbar-background | 搜索框背景色 | $color-background-sunken |...
(GL_BARBARISM_REPLACE)
[locale-violation] ~129-~129: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...--nutui-searchbar-background | 搜索框背景色 | $color-background-sunken | | --nutui-searchbar-color | ...
(GL_BARBARISM_REPLACE)
[locale-violation] ~131-~131: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...| $color-title | | --nutui-searchbar-gap | 搜索框外部/大间距 | 12px | | --nutui-searc...
(GL_BARBARISM_REPLACE)
[locale-violation] ~132-~132: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...距 | 12px | | --nutui-searchbar-inner-gap | 搜索框内部元素间的间距 | 12px | | --nutui-sea...
(GL_BARBARISM_REPLACE)
[locale-violation] ~134-~134: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ze-base| | \--nutui-searchbar-content-background | 搜索框中间内容区的背景色 |$color-background-ove...
(GL_BARBARISM_REPLACE)
[locale-violation] ~134-~134: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...bar-content-background | 搜索框中间内容区的背景色 | $color-background-overlay | | --nutui-searchbar-content...
(GL_BARBARISM_REPLACE)
[locale-violation] ~137-~137: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...d模式下的圆角 | 19px | | --nutui-searchbar-input-height | 搜索框输入区高度 | 38px | | --nutui...
(GL_BARBARISM_REPLACE)
[locale-violation] ~138-~138: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...索框输入区高度 | 38px | | --nutui-searchbar-input-padding | 搜索框输入区padding | 0 0 0 8px |...
(GL_BARBARISM_REPLACE)
[duplication] ~138-~138: Posíbel erro tipográfico: hai unha palabra repetida.
Context: ...archbar-input-padding | 搜索框输入区padding | 0 0 0 8px | | --nutui-searchbar-input-tex...
(WORD_REPETITION)
src/packages/searchbar/doc.md
[locale-violation] ~127-~127: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ing值 | 1px 8px | | --nutui-searchbar-background | 搜索框背景色 | $color-background-sunken |...
(GL_BARBARISM_REPLACE)
[locale-violation] ~127-~127: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...--nutui-searchbar-background | 搜索框背景色 | $color-background-sunken | | --nutui-searchbar-color | ...
(GL_BARBARISM_REPLACE)
[locale-violation] ~129-~129: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...| $color-title | | --nutui-searchbar-gap | 搜索框外部/大间距 | 12px | | --nutui-searc...
(GL_BARBARISM_REPLACE)
[locale-violation] ~130-~130: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...距 | 12px | | --nutui-searchbar-inner-gap | 搜索框内部元素间的间距 | 12px | | --nutui-sea...
(GL_BARBARISM_REPLACE)
[locale-violation] ~132-~132: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ze-base| | \--nutui-searchbar-content-background | 搜索框中间内容区的背景色 |$color-background-ove...
(GL_BARBARISM_REPLACE)
[locale-violation] ~132-~132: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...bar-content-background | 搜索框中间内容区的背景色 | $color-background-overlay | | --nutui-searchbar-content...
(GL_BARBARISM_REPLACE)
[locale-violation] ~135-~135: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...d模式下的圆角 | 19px | | --nutui-searchbar-input-height | 搜索框输入区高度 | 38px | | --nutui...
(GL_BARBARISM_REPLACE)
[locale-violation] ~136-~136: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...索框输入区高度 | 38px | | --nutui-searchbar-input-padding | 搜索框输入区padding | 0 0 0 8px |...
(GL_BARBARISM_REPLACE)
[duplication] ~136-~136: Posíbel erro tipográfico: hai unha palabra repetida.
Context: ...archbar-input-padding | 搜索框输入区padding | 0 0 0 8px | | --nutui-searchbar-input-tex...
(WORD_REPETITION)
src/packages/searchbar/doc.zh-TW.md
[locale-violation] ~129-~129: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ing值 | 1px 8px | | --nutui-searchbar-background | 搜索框背景色 | $color-background-sunken |...
(GL_BARBARISM_REPLACE)
[locale-violation] ~129-~129: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...--nutui-searchbar-background | 搜索框背景色 | $color-background-sunken | | --nutui-searchbar-color | ...
(GL_BARBARISM_REPLACE)
[locale-violation] ~131-~131: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...| $color-title | | --nutui-searchbar-gap | 搜索框外部/大間距 | 12px | | --nutui-searc...
(GL_BARBARISM_REPLACE)
[locale-violation] ~132-~132: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...距 | 12px | | --nutui-searchbar-inner-gap | 搜索框內部元素間的間距 | 12px | | --nutui-sea...
(GL_BARBARISM_REPLACE)
[locale-violation] ~134-~134: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ze-base| | \--nutui-searchbar-content-background | 搜索框中間內容區的背景色 |$color-background-ove...
(GL_BARBARISM_REPLACE)
[locale-violation] ~134-~134: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...bar-content-background | 搜索框中間內容區的背景色 | $color-background-overlay | | --nutui-searchbar-content...
(GL_BARBARISM_REPLACE)
[locale-violation] ~137-~137: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...d模式下的圓角 | 19px | | --nutui-searchbar-input-height | 搜索框輸入區高度 | 38px | | --nutui...
(GL_BARBARISM_REPLACE)
[locale-violation] ~138-~138: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...索框輸入區高度 | 38px | | --nutui-searchbar-input-padding | 搜索框輸入區padding | 0 0 0 8px |...
(GL_BARBARISM_REPLACE)
[duplication] ~138-~138: Posíbel erro tipográfico: hai unha palabra repetida.
Context: ...archbar-input-padding | 搜索框輸入區padding | 0 0 0 8px | | --nutui-searchbar-input-tex...
(WORD_REPETITION)
🪛 Biome (1.9.4)
src/packages/searchbar/searchbar.tsx
[error] 88-88: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 103-103: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 112-112: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 124-124: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 125-125: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 137-137: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
src/packages/searchbar/searchbar.taro.tsx
[error] 95-95: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 104-104: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 113-113: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 125-125: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 126-126: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 258-259: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: build
- GitHub Check: test
🔇 Additional comments (43)
src/packages/searchbar/demos/h5/demo7.tsx (1)
2-2: 更新示例导入来源
将 SearchBar 的导入路径由相对路径切换为@nutui/nutui-react包路径,保持示例与发布包一致,结构清晰且无逻辑问题。src/packages/searchbar/demos/h5/demo2.tsx (1)
2-2: 统一示例导入路径
示例中将 SearchBar 从相对路径切换为@nutui/nutui-react包导入,与其他 demo 保持一致,推荐继续应用于所有示例。src/packages/searchbar/demos/taro/demo5.tsx (1)
17-18: 移除图标显式尺寸,依赖主题变量
示例中去除了ArrowLeft、Close、Star、More和Photograph的显式size或width/height属性,改为依赖全局 CSS 变量(如--nutui-searchbar-icon-size)进行统一控制,做法符合组件库最新样式规范。建议预览确认在不同主题或设备下图标尺寸和间距符合预期。Also applies to: 28-28
src/packages/searchbar/demos/h5/demo4.tsx (1)
2-2: 合并导入,简化路径
示例将SearchBar和Toast统一从@nutui/nutui-react导入,移除相对路径导入,建议继续检查其他 demo 是否同步更新,以保持一致性。src/packages/searchbar/demos/h5/demo3.tsx (2)
2-2: 导入方式优化将组件导入从相对路径导入优化为从统一包导入,简化了导入语句并提高了代码一致性。
10-10: 优化主题配置将搜索栏内容背景色从
#eee更新为#fff,使其更符合V15的视觉规范。src/packages/searchbar/demos/h5/demo6.tsx (2)
3-3: 规范化组件导入将 SearchBar 和 Popover 组件从相对路径导入优化为从统一包导入,保持代码风格一致性。
16-16: 组件名称修正将
PopOver修改为Popover,保持与导入的组件名称一致,避免潜在的引用错误。Also applies to: 27-27
src/packages/searchbar/demos/h5/demo5.tsx (2)
3-3: 统一导入路径将 SearchBar 组件从相对路径导入优化为从统一包导入,提高代码一致性。
11-12: 移除显式图标尺寸属性移除了图标组件上的显式 width 和 height 属性,使用组件的默认尺寸,简化代码并保持视觉一致性。这符合图标组件的最佳使用方式,让样式系统统一控制图标尺寸。
Also applies to: 22-22
src/sites/sites-react/doc/docs/taro/migrate-from-v2.md (1)
182-187: 添加SearchBar组件V15适配文档为V15版本的SearchBar组件添加了升级文档,包括:
value属性支持受控模式defaultValue属性用于设置默认搜索值,支持逗号分隔的多个值onItemClick属性处理标签删除事件这些新增特性完善了组件的可控性和交互能力,使搜索栏组件更加灵活。
scripts/harmony/update-taro-entry.js (3)
24-24: 变量声明优化将
configRef从常量(const)修改为可变变量(let),为后续代码中对该变量的修改做准备。
32-33: 条件判断逻辑优化优化了条件判断的嵌套结构,使逻辑更加清晰。
53-55: 过滤空页面数组添加了对空页面配置的过滤逻辑,确保最终生成的配置中不包含空页面的路由项,避免生成不必要的空包。这对构建优化和运行时性能都有积极影响。
src/packages/configprovider/types.ts (2)
658-658: 新增的CSS变量以支持SearchBar标签模式
nutuiSearchbarInnerGap变量已添加,用于控制SearchBar组件内部元素之间的间距,特别是在标签模式下的元素排列。这个变量增强了组件的灵活性,使开发者能够更精确地控制组件内部元素的间距。
663-663: 新增图标尺寸控制变量
nutuiSearchbarIconSize变量的添加使开发者能够直接控制SearchBar组件中图标的大小,这对于在不同设计系统中保持一致的视觉层次结构非常重要。src/styles/variables-jrkf.scss (2)
2149-2149: 为SearchBar组件添加内部间距变量添加了
$searchbar-inner-gap变量,默认值为8px,用于控制SearchBar组件内部元素之间的间距。这个变量与ConfigProvider中的CSS变量保持一致,确保了样式系统的统一性。
2166-2166: 定义SearchBar图标尺寸默认值添加了
$searchbar-icon-size变量,默认值为20px,用于控制SearchBar组件中图标的大小。这个变量与ConfigProvider中的CSS变量保持一致,确保了组件图标在整个应用中的视觉一致性。src/sites/sites-react/doc/docs/react/migrate-from-v2.md (1)
183-188: SearchBar组件新特性文档更新文档中添加了SearchBar组件的新功能说明,包括:
value属性,支持受控模式defaultValue属性,用于设置搜索默认值,支持以逗号分隔的多个值onItemClick属性,用于处理点击默认值上的关闭图标的回调这些新增特性增强了SearchBar组件的灵活性,使其能够更好地适应不同的使用场景。
src/types/spec/searchbar/base.ts (3)
7-7: 添加默认值属性在
BaseSearchBar接口中添加了defaultValue属性,用于支持非受控模式下的默认搜索值。这与文档中的描述一致,使开发者能够预设搜索框的初始值。
20-20: 添加标签模式属性增加了
tag布尔属性,用于启用SearchBar的标签模式。这是V15适配中的新特性,允许将默认值以标签形式展示,提升了用户体验。
27-27: 添加标签点击事件回调增加了
onItemClick回调函数,当用户点击标签上的关闭图标时触发。此函数接收被点击的值和事件对象作为参数,使开发者能够自定义处理标签的移除逻辑。src/styles/variables-jmapp.scss (2)
2043-2043: 新增内部间距变量,提升搜索栏布局灵活性这个新增的
$searchbar-inner-gap变量可以更精细地控制搜索栏内部元素的间距,对于适配不同尺寸屏幕和提升组件布局一致性很有帮助。默认值8px是合理的开始值。
2060-2060: 统一图标尺寸规范,优化视觉一致性添加
$searchbar-icon-size变量有助于保持搜索栏中所有图标的一致尺寸,这对于整体UI的协调性很重要。20px的默认值符合移动端图标的常见尺寸标准。src/packages/searchbar/demos/h5/demo1.tsx (2)
2-3: 优化导入结构,统一组件来源从
@nutui/icons-react和@nutui/nutui-react导入组件,而非相对路径导入,符合最佳实践,便于维护和更新。
8-19: 增强搜索栏展示形式,提供多样化交互方案新的示例代码展示了搜索栏的两种不同使用场景:
- 简单场景:右侧固定文本"搜索"
- 复杂场景:左侧扫描图标,右侧组合了拍照图标、分隔线和红色文本
这种实现既展示了组件的灵活性,也提供了实际应用中常见的UI模式。特别是第二个示例中的复合右侧内容,很好地展示了
rightIn属性的强大功能。src/packages/searchbar/demo.taro.tsx (3)
15-15: 添加新演示组件,扩展功能展示引入
Demo10组件用于展示搜索栏的默认值和受控模式功能,有助于用户理解组件的进阶用法。
21-21: 完善多语言支持,保持一致性为"默认值、受控"功能添加中文、繁体中文和英文三种语言的翻译支持,确保多语言环境下的用户体验一致性。
Also applies to: 31-31, 41-41
57-58: 优化Demo顺序,突出新功能将
Demo10组件放在第一个基础用法之后展示,突出了这个重要的新功能,同时保持了从基础到高级的逻辑顺序,有助于用户循序渐进地理解组件用法。src/packages/searchbar/demos/taro/demo1.tsx (3)
2-3: 统一Taro环境组件导入,提升一致性从Taro专用包
@nutui/nutui-react-taro和@nutui/icons-react-taro导入组件,保证了在Taro环境下的正确渲染和功能支持。
5-5: 统一组件命名约定将组件名从
Demo1更改为Demo,简化了命名并与其他示例保持一致,有助于保持代码库的命名规范性。Also applies to: 23-23
8-19: 丰富Taro环境下的搜索栏示例与H5版本保持一致的搜索栏示例,展示了在Taro环境下搜索栏的两种使用方式:
- 基础用法:右侧简单文本
- 高级用法:左侧图标和自定义右侧内容(带交互功能)
这种实现确保了跨平台的一致体验,同时展示了组件在Taro环境下的完整功能支持。
src/packages/searchbar/demo.tsx (3)
11-11: 导入新的 Demo10 组件导入新的 Demo10 组件用于展示搜索栏的默认值和受控功能,这是一个很好的功能补充。
17-17: 多语言支持完善已为中文、繁体中文和英文添加了相应的翻译文本,保证了多语言环境下的一致性。
Also applies to: 27-27, 37-37
52-53: 顺序合理的演示放置将新增的默认值和受控模式演示放在基础用法之后、形状设置之前,符合功能由基础到复杂的展示逻辑。
src/packages/searchbar/demos/h5/demo10.tsx (1)
37-53: 自动聚焦可能影响用户体验第二个 SearchBar 组件使用了
autoFocus属性。在移动设备上,自动聚焦可能会出现键盘弹出干扰用户体验的情况。考虑根据使用场景决定是否启用此属性。-autoFocus +// 根据实际场景决定是否需要自动聚焦src/packages/searchbar/doc.taro.md (5)
23-29: 文档完善:新增默认值和受控模式说明文档中新增了默认值和受控模式的说明部分,并添加了相应的示例代码引用,有助于用户理解和使用这些新功能。
97-98: 明确的属性文档说明属性表中明确标明了
value是受控属性,并新增了defaultValue属性的说明,包括支持通过逗号分隔成多个值的功能说明,文档清晰完整。
109-109: 更新了默认图标说明将
leftIn属性的默认值从<Search size="12" />更新为<Search />,与实际代码保持一致。
117-117: 新增标签点击事件说明文档中新增了
onItemClick事件的说明,清晰地解释了其触发时机和功能,有助于用户正确使用标签模式。
128-138: CSS 变量更新更新了多个 CSS 变量的默认值,包括 padding、背景色、间距、圆角和高度等,使组件样式更加精细和美观。这些变更与组件实现保持一致。
🧰 Tools
🪛 LanguageTool
[locale-violation] ~129-~129: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ing值 |1px 8px| | --nutui-searchbar-background | 搜索框背景色 |$color-background-sunken|...(GL_BARBARISM_REPLACE)
[locale-violation] ~129-~129: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...--nutui-searchbar-background | 搜索框背景色 |$color-background-sunken| | --nutui-searchbar-color | ...(GL_BARBARISM_REPLACE)
[locale-violation] ~131-~131: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...|$color-title| | --nutui-searchbar-gap | 搜索框外部/大间距 |12px| | --nutui-searc...(GL_BARBARISM_REPLACE)
[locale-violation] ~132-~132: 'gap' é un xenismo. É preferíbel dicir "hiato"
Context: ...距 |12px| | --nutui-searchbar-inner-gap | 搜索框内部元素间的间距 |12px| | --nutui-sea...(GL_BARBARISM_REPLACE)
[locale-violation] ~134-~134: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...ze-base| | \--nutui-searchbar-content-background | 搜索框中间内容区的背景色 |$color-background-ove...(GL_BARBARISM_REPLACE)
[locale-violation] ~134-~134: 'background' é un xenismo. É preferíbel dicir "formación de base"
Context: ...bar-content-background | 搜索框中间内容区的背景色 |$color-background-overlay| | --nutui-searchbar-content...(GL_BARBARISM_REPLACE)
[locale-violation] ~137-~137: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...d模式下的圆角 |19px| | --nutui-searchbar-input-height | 搜索框输入区高度 |38px| | --nutui...(GL_BARBARISM_REPLACE)
[locale-violation] ~138-~138: 'input' é un xenismo. É preferíbel dicir "entrada"
Context: ...索框输入区高度 |38px| | --nutui-searchbar-input-padding | 搜索框输入区padding |0 0 0 8px|...(GL_BARBARISM_REPLACE)
[duplication] ~138-~138: Posíbel erro tipográfico: hai unha palabra repetida.
Context: ...archbar-input-padding | 搜索框输入区padding |0 0 0 8px| | --nutui-searchbar-input-tex...(WORD_REPETITION)
src/packages/searchbar/searchbar.tsx (1)
143-154: 类名聚合依赖遗漏当
className或disabled、backable等 props 发生变化时,cls会随之更新,但目前依赖数组未包含tag。如果父组件动态切换 tag 模式,可能造成样式残留。建议补充依赖或通过classNames时机集中处理。src/packages/searchbar/searchbar.taro.tsx (1)
154-176: TaroInput 的focus属性未考虑 Tag 展示模式
当tag为true且处于非标签态 (!innerTag) 时应自动聚焦输入框,否则用户需额外点击。
可将focus={autoFocus}修改为focus={autoFocus || (!innerTag && tag)}。
| | onItemClick | 点击默认值的x号触发 | `(value: string) => void` | `-` | | ||
|
|
There was a problem hiding this comment.
文档参数描述与实现不一致
实现中 onItemClick 回调调用方式为 onItemClick?.(value, event),即第二个参数会透出 MouseEvent。
而文档目前仅声明为 (value: string) => void,容易导致使用方遗漏对事件对象的处理,产生类型或运行时错误。
-| onItemClick | 点击默认值的x号触发 | `(value: string) => void` | `-` |
+| onItemClick | 点击默认值的 x 号时触发 | `(value: string, event: MouseEvent<HTMLDivElement>) => void` | `-` |📝 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.
| | onItemClick | 点击默认值的x号触发 | `(value: string) => void` | `-` | | |
| | onItemClick | 点击默认值的 x 号时触发 | `(value: string, event: MouseEvent<HTMLDivElement>) => void` | `-` | |
| | onItemClick | 點擊默認值的x號觸發 | `(value: string) => void` | `-` | | ||
|
|
There was a problem hiding this comment.
文件描述与源码回调签名不一致
组件源码额外向 onItemClick 传入了 MouseEvent,请同步更新文档,避免使用者类型推断错误。
-| onItemClick | 點擊默認值的x號觸發 | `(value: string) => void` | `-` |
+| onItemClick | 點擊默認值 X 圖示時觸發 | `(value: string, event: MouseEvent<HTMLDivElement>) => void` | `-` |📝 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.
| | onItemClick | 點擊默認值的x號觸發 | `(value: string) => void` | `-` | | |
| | onItemClick | 點擊默認值 X 圖示時觸發 | `(value: string, event: MouseEvent<HTMLDivElement>) => void` | `-` | |
| test('should render with default value', () => { | ||
| const { container } = render(<SearchBar defaultValue="123" />) | ||
| const dvalues = container.querySelectorAll('.nut-searchbar-value') | ||
| expect(dvalues.length).toBe(1) | ||
| }) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
用例断言与默认行为不符
defaultValue 仅提供默认输入内容;若未显式设置 tag,组件不会渲染 .nut-searchbar-value,导致当前断言恒为 0 而测试失败。
-const { container } = render(<SearchBar defaultValue="123" />)
+const { container } = render(<SearchBar defaultValue="123" tag />)或改为断言输入框的默认值:
-const dvalues = container.querySelectorAll('.nut-searchbar-value')
-expect(dvalues.length).toBe(1)
+const input = container.querySelector('.nut-searchbar-input') as HTMLInputElement
+expect(input).toHaveValue('123')📝 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('should render with default value', () => { | |
| const { container } = render(<SearchBar defaultValue="123" />) | |
| const dvalues = container.querySelectorAll('.nut-searchbar-value') | |
| expect(dvalues.length).toBe(1) | |
| }) | |
| test('should render with default value', () => { | |
| const { container } = render(<SearchBar defaultValue="123" tag />) | |
| const dvalues = container.querySelectorAll('.nut-searchbar-value') | |
| expect(dvalues.length).toBe(1) | |
| }) |
| test('should render with default value', () => { | |
| const { container } = render(<SearchBar defaultValue="123" />) | |
| const dvalues = container.querySelectorAll('.nut-searchbar-value') | |
| expect(dvalues.length).toBe(1) | |
| }) | |
| test('should render with default value', () => { | |
| const { container } = render(<SearchBar defaultValue="123" />) | |
| const input = container.querySelector('.nut-searchbar-input') as HTMLInputElement | |
| expect(input).toHaveValue('123') | |
| }) |
| const renderValueByTags = useCallback(() => { | ||
| if (!value) { | ||
| setTimeout(() => { | ||
| forceFocus() | ||
| }, 0) | ||
| return null | ||
| } | ||
| const list = value.split(',') | ||
| if (!list) return null | ||
| return ( | ||
| <div className="nut-searchbar-values"> | ||
| {list.map((item, index) => ( | ||
| <div | ||
| key={`def-${index}`} | ||
| className="nut-searchbar-value" | ||
| onClick={(e) => onItemClick?.(item, e)} | ||
| > | ||
| {item} | ||
| <Close /> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ) | ||
| }, [value, onItemClick]) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Tag 渲染存在易用性与稳定性问题
value.split(',')未去除空白,标签文本可能出现前后空格。- 以索引作为 React
key,在删除/插入场景易导致复用错误。 - 关闭全部标签后立即
forceFocus,但外部innerTag仍为true,可能出现 UI 抖动。
-const list = value.split(',')
+const list = value.split(',').map((i) => i.trim()).filter(Boolean)
-key={`def-${index}`}
+key={item}若担心重复值,可使用 key={${item}-${index}} 兼顾稳定性。
📝 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.
| const renderValueByTags = useCallback(() => { | |
| if (!value) { | |
| setTimeout(() => { | |
| forceFocus() | |
| }, 0) | |
| return null | |
| } | |
| const list = value.split(',') | |
| if (!list) return null | |
| return ( | |
| <div className="nut-searchbar-values"> | |
| {list.map((item, index) => ( | |
| <div | |
| key={`def-${index}`} | |
| className="nut-searchbar-value" | |
| onClick={(e) => onItemClick?.(item, e)} | |
| > | |
| {item} | |
| <Close /> | |
| </div> | |
| ))} | |
| </div> | |
| ) | |
| }, [value, onItemClick]) | |
| const renderValueByTags = useCallback(() => { | |
| if (!value) { | |
| setTimeout(() => { | |
| forceFocus() | |
| }, 0) | |
| return null | |
| } | |
| - const list = value.split(',') | |
| + const list = value.split(',').map((i) => i.trim()).filter(Boolean) | |
| if (!list) return null | |
| return ( | |
| <div className="nut-searchbar-values"> | |
| {list.map((item, index) => ( | |
| <div | |
| - key={`def-${index}`} | |
| + key={item} | |
| className="nut-searchbar-value" | |
| onClick={(e) => onItemClick?.(item, e)} | |
| > | |
| {item} | |
| <Close /> | |
| </div> | |
| ))} | |
| </div> | |
| ) | |
| }, [value, onItemClick]) |
| (event: MouseEvent<HTMLDivElement>) => { | ||
| if (disabled || readOnly) return | ||
| setValue('') | ||
| forceFocus() | ||
| onChange && onChange('') | ||
| onClear && onClear(event) | ||
| }, |
There was a problem hiding this comment.
onChange 调用缺失事件参数
此处清空逻辑仅传入空字符串,未按约定额外传递 ChangeEvent,使用者若依赖该参数会产生 undefined 错误。
- onChange && onChange('')
+ // 构造与 handleChange 一致的参数签名
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
+ onChange && onChange('', event as unknown as ChangeEvent<HTMLInputElement>)也可考虑抽取公共 emitChange(value, evt?) 工具以避免重复代码。
📝 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.
| (event: MouseEvent<HTMLDivElement>) => { | |
| if (disabled || readOnly) return | |
| setValue('') | |
| forceFocus() | |
| onChange && onChange('') | |
| onClear && onClear(event) | |
| }, | |
| (event: MouseEvent<HTMLDivElement>) => { | |
| if (disabled || readOnly) return | |
| setValue('') | |
| forceFocus() | |
| // 构造与 handleChange 一致的参数签名 | |
| // eslint-disable-next-line @typescript-eslint/consistent-type-assertions | |
| onChange && onChange('', event as unknown as ChangeEvent<HTMLInputElement>) | |
| onClear && onClear(event) | |
| }, |
🧰 Tools
🪛 Biome (1.9.4)
[error] 124-124: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 125-125: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
| &-values { | ||
| position: absolute; | ||
| display: flex; | ||
| flex-direction: row; | ||
| z-index: 2; | ||
| background-color: #fff; | ||
| top: 9px; | ||
| left: 6px; | ||
| font-size: 12px; | ||
| line-height: 12px; | ||
|
|
||
| .nut-searchbar-value { | ||
| display: flex; | ||
| flex-direction: row; | ||
| align-items: center; | ||
| padding: 4px 8px; | ||
| background-color: #f7f8fc; | ||
| border-radius: 4px; | ||
| margin-right: 2px; | ||
| } | ||
|
|
||
| .nut-icon { | ||
| width: 6px; | ||
| height: 6px; | ||
| font-size: 6px; | ||
| color: #c2c4cc; | ||
| margin-left: 4px; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
硬编码颜色与尺寸,削弱了可主题化能力
.nut-searchbar-values 及其子元素直接使用 #fff / #c2c4cc / 6px 等字面量,不利于暗黑模式或品牌换色。
建议全部改为 $color-background-overlay、$color-text-disabled、$searchbar-icon-size 等变量形式,同时使用 rem 或 % 替代纯 px 以获得更好的可缩放性。
| &-focus { | ||
| padding: 5px $searchbar-gap; | ||
|
|
||
| .nut-searchbar-content { | ||
| border: 0.5px solid #ff5c67; | ||
| } | ||
| } |
There was a problem hiding this comment.
聚焦态改变外层 padding 会造成布局抖动
&-focus 里把根元素 padding 从 1px 8px 改成 5px $searchbar-gap,输入框获得/失去焦点时会导致组件整体宽高瞬间变化,体验不佳。
通常做法是在普通态就预留边框宽度,或使用 box-shadow/outline 表示焦点,而不是修改 padding。
| (event: ITouchEvent) => { | ||
| if (disabled || readOnly) return | ||
| setValue('') | ||
| onChange && onChange('') | ||
| onClear && onClear(event) | ||
| forceFocus() | ||
| }, | ||
| [disabled, readOnly, onChange, onClear, setValue] | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
清空逻辑调用顺序易导致受控模式数据不同步
在受控场景下,通常应先触发 onChange('') 通知父组件更新,再调用内部 setValue('')。
当前实现顺序反了,父组件若立即把外部 value 设为非空,将被随后内部状态覆盖。
- setValue('')
- onChange && onChange('')
+ onChange && onChange('')
+ setValue('')📝 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.
| (event: ITouchEvent) => { | |
| if (disabled || readOnly) return | |
| setValue('') | |
| onChange && onChange('') | |
| onClear && onClear(event) | |
| forceFocus() | |
| }, | |
| [disabled, readOnly, onChange, onClear, setValue] | |
| ) | |
| (event: ITouchEvent) => { | |
| if (disabled || readOnly) return | |
| onChange && onChange('') | |
| setValue('') | |
| onClear && onClear(event) | |
| forceFocus() | |
| }, | |
| [disabled, readOnly, onChange, onClear, setValue] | |
| ) |
🧰 Tools
🪛 Biome (1.9.4)
[error] 125-125: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 126-126: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
|
|
||
| const forceFocus = useCallback(() => { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
innerTag 状态未与外部 tag Prop 保持同步
组件初始化后若父组件动态修改 tag,innerTag 不会自动更新,造成显示状态不一致。
可在 useEffect 中监听 tag 变化并同步 setInnerTag(tag)。
+useEffect(() => {
+ setInnerTag(tag)
+}, [tag])📝 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.
| const [innerTag, setInnerTag] = useState(tag) | |
| const [innerTag, setInnerTag] = useState(tag) | |
| useEffect(() => { | |
| setInnerTag(tag) | |
| }, [tag]) |
| const { | ||
| value: outerValue, |
There was a problem hiding this comment.
Ref 类型不匹配,可能导致运行时类型错误
useRef<HTMLInputElement> 用于 Web,但在 Taro 环境下 TaroInput 返回的是 Input 组件实例而非原生 HTMLInputElement。
建议改为 useRef<any> 或精确到 Taro.Input 对应类型,避免类型断言失效。
-const searchInputRef = useRef<HTMLInputElement>(null)
+const searchInputRef = useRef<any>(null) // 或使用正确的 Taro 类型📝 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.
| const searchInputRef = useRef<HTMLInputElement>(null) | |
| const { | |
| const searchInputRef = useRef<any>(null) // 或使用正确的 Taro 类型 | |
| const { |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (11)
src/packages/searchbar/searchbar.taro.tsx (5)
103-109: 使用可选链操作符优化回调函数调用同样,这里也应使用可选链操作符优化
onFocus回调函数的调用。- onFocus && onFocus(event.detail?.value, event) + onFocus?.(event.detail?.value, event)🧰 Tools
🪛 Biome (1.9.4)
[error] 105-105: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
285-287: 使用可选链操作符优化回调函数调用这里对
onSearch回调函数的调用也应使用可选链操作符。- onSearch && onSearch(value as string) + onSearch?.(value as string)🧰 Tools
🪛 Biome (1.9.4)
[error] 286-287: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
126-135:⚠️ Potential issue清空逻辑调用顺序易导致受控模式数据不同步
在受控场景下,通常应先触发
onChange('')通知父组件更新,再调用内部setValue('')。当前实现顺序反了,父组件若立即把外部value设为非空,将被随后内部状态覆盖。- setValue('') - forceFocus() - onChange && onChange('') + onChange?.('') + setValue('') + forceFocus()🧰 Tools
🪛 Biome (1.9.4)
[error] 131-131: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 132-132: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
206-230: 🛠️ Refactor suggestionTag 渲染存在易用性与稳定性问题
value.split(',')未去除空白,标签文本可能出现前后空格。- 以索引作为 React
key,在删除/插入场景易导致复用错误。- 未检查标签是否为空字符串,可能导致渲染空标签。
- const list = value.split(',') - if (!list) return null + const list = value.split(',').map(item => item.trim()).filter(Boolean) + if (!list.length) return null - key={`def-${index}`} + key={`${item}-${index}`}
86-87:⚠️ Potential issue需要同步 innerTag 与 tag 属性
组件初始化后若父组件动态修改
tag属性,innerTag状态将不会自动更新,导致组件显示状态不一致。应添加useEffect确保二者同步。+useEffect(() => { + setInnerTag(tag) +}, [tag])src/packages/searchbar/searchbar.tsx (6)
85-92: 使用可选链操作符优化回调函数调用当前对
onChange的调用没有使用可选链操作符,在回调为undefined时会造成不必要的逻辑检查。- onChange && onChange(value, event) + onChange?.(value, event)🧰 Tools
🪛 Biome (1.9.4)
[error] 88-88: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
101-107: 使用可选链操作符优化回调函数调用同样,这里也应使用可选链操作符优化
onFocus回调函数的调用。- onFocus && onFocus(event.target?.value, event) + onFocus?.(event.target?.value, event)🧰 Tools
🪛 Biome (1.9.4)
[error] 103-103: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
130-141: 使用可选链操作符优化回调函数调用这里对
onSearch回调函数的调用也应使用可选链操作符。- onSearch && onSearch(value as string) + onSearch?.(value as string)🧰 Tools
🪛 Biome (1.9.4)
[error] 137-137: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
79-80:⚠️ Potential issue需要同步 innerTag 与 tag 属性
当前实现中,如果父组件动态改变
tag属性,组件内部的innerTag状态不会自动更新,导致显示状态不一致。应添加useEffect确保二者同步。+useEffect(() => { + setInnerTag(tag) +}, [tag])
119-128:⚠️ Potential issue清空逻辑调用顺序易导致受控模式数据不同步
在受控场景下,通常应先触发
onChange('')通知父组件更新,再调用内部setValue('')。当前实现顺序反了,父组件若立即把外部value设为非空,将被随后内部状态覆盖。此外,当清空输入时,onChange回调缺少事件参数,与handleChange的调用不一致。- setValue('') - forceFocus() - onChange && onChange('') - onClear && onClear(event) + onChange?.('') + setValue('') + forceFocus() + onClear?.(event)🧰 Tools
🪛 Biome (1.9.4)
[error] 124-124: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 125-125: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
181-204:⚠️ Potential issueTag 渲染存在易用性与稳定性问题
- 缺少
innerTag条件检查,与 Taro 版本不一致。value.split(',')未去除空白,标签文本可能出现前后空格。- 以索引作为 React
key,在删除/插入场景易导致复用错误。- 未检查标签是否为空字符串,可能导致渲染空标签。
+ if (!innerTag) return null if (!value) { setTimeout(() => { forceFocus() }, 0) return null } - const list = value.split(',') - if (!list) return null + const list = value.split(',').map(item => item.trim()).filter(Boolean) + if (!list.length) return null - key={`def-${index}`} + key={`${item}-${index}`}
🧹 Nitpick comments (2)
src/packages/searchbar/searchbar.taro.tsx (1)
92-101: 使用可选链操作符优化回调函数调用当前对
onChange的调用没有使用可选链操作符,在onChange为undefined时会造成不必要的逻辑检查。- onChange && onChange(eventValue, event) + onChange?.(eventValue, event)🧰 Tools
🪛 Biome (1.9.4)
[error] 96-96: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
src/packages/searchbar/searchbar.tsx (1)
267-268: 逻辑优化:标签与清除按钮的渲染判断当前实现中,标签显示和清除按钮的渲染条件是互斥的(
innerTag ? renderValueByTags() : renderClear()),但在renderClear中已有对value和clearable的检查。应拆分这两个组件的渲染,使逻辑更清晰。- {innerTag ? renderValueByTags() : renderClear()} + {renderValueByTags()} + {!innerTag && renderClear()}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/packages/searchbar/searchbar.taro.tsx(4 hunks)src/packages/searchbar/searchbar.tsx(4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/packages/searchbar/searchbar.tsx
[error] 88-88: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 103-103: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 112-112: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 124-124: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 125-125: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 137-137: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
src/packages/searchbar/searchbar.taro.tsx
[error] 96-96: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 105-105: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 114-114: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 131-131: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 132-132: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 286-287: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🪛 GitHub Check: build
src/packages/searchbar/searchbar.taro.tsx
[failure] 117-117:
Property 'value' does not exist on type 'Target'.
🪛 GitHub Actions: CI
src/packages/searchbar/searchbar.taro.tsx
[error] 117-117: TypeScript error TS2339: Property 'value' does not exist on type 'Target'.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: test
| const handleBlur = useCallback( | ||
| (event: BaseEventOrig<InputProps.inputValueEventDetail>) => { | ||
| searchInputRef.current?.blur() | ||
| onBlur && onBlur(event.detail?.value, event) | ||
| setTimeout(() => { | ||
| if (Taro.getEnv() === 'WEB') { | ||
| setInnerTag(event.target?.value ? tag : false) | ||
| } else { | ||
| setInnerTag(tag) | ||
| } | ||
| }, 200) | ||
| }, | ||
| [onBlur, tag, value] | ||
| ) |
There was a problem hiding this comment.
handleBlur 方法中的类型错误
event.target?.value 引用导致 TypeScript 错误,因为 Taro 环境中 Target 类型没有 value 属性。应使用 event.detail?.value。
- setInnerTag(event.target?.value ? tag : false)
+ setInnerTag(event.detail?.value ? tag : false)📝 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.
| const handleBlur = useCallback( | |
| (event: BaseEventOrig<InputProps.inputValueEventDetail>) => { | |
| searchInputRef.current?.blur() | |
| onBlur && onBlur(event.detail?.value, event) | |
| setTimeout(() => { | |
| if (Taro.getEnv() === 'WEB') { | |
| setInnerTag(event.target?.value ? tag : false) | |
| } else { | |
| setInnerTag(tag) | |
| } | |
| }, 200) | |
| }, | |
| [onBlur, tag, value] | |
| ) | |
| const handleBlur = useCallback( | |
| (event: BaseEventOrig<InputProps.inputValueEventDetail>) => { | |
| searchInputRef.current?.blur() | |
| onBlur && onBlur(event.detail?.value, event) | |
| setTimeout(() => { | |
| if (Taro.getEnv() === 'WEB') { | |
| - setInnerTag(event.target?.value ? tag : false) | |
| + setInnerTag(event.detail?.value ? tag : false) | |
| } else { | |
| setInnerTag(tag) | |
| } | |
| }, 200) | |
| }, | |
| [onBlur, tag, value] | |
| ) |
🧰 Tools
🪛 Biome (1.9.4)
[error] 114-114: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🪛 GitHub Check: build
[failure] 117-117:
Property 'value' does not exist on type 'Target'.
🪛 GitHub Actions: CI
[error] 117-117: TypeScript error TS2339: Property 'value' does not exist on type 'Target'.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
src/packages/searchbar/searchbar.taro.tsx (4)
49-49:⚠️ Potential issue修复 Ref 类型不匹配问题
当前
useRef<HTMLInputElement>类型在 Taro 环境下不适用,因为 Taro 的 Input 组件不是原生 HTML 元素。应使用更合适的类型定义。建议修改为:
-const searchInputRef = useRef<HTMLInputElement>(null) +const searchInputRef = useRef<any>(null) // 或使用 Taro.Input 对应类型
86-86:⚠️ Potential issue添加 innerTag 状态同步机制
当前实现中,innerTag 状态仅在初始化时从 tag prop 获取值,缺少动态同步机制。当父组件动态修改 tag prop 时,innerTag 不会自动更新。
需要添加 useEffect 来监听 tag 变化:
+useEffect(() => { + setInnerTag(tag) +}, [tag])
111-124:⚠️ Potential issue修复 handleBlur 中的类型错误
event.target?.value在 Taro 环境下会导致类型错误,因为 Taro 的事件对象结构与 Web 不同。建议修改:
- setInnerTag(event.target?.value ? tag : false) + setInnerTag(event.detail?.value ? tag : false)另外,依赖数组中缺少 forceFocus:
- }, [onBlur, tag, value] + }, [onBlur, tag, value, forceFocus]🧰 Tools
🪛 Biome (1.9.4)
[error] 114-114: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
126-135:⚠️ Potential issue调整清空函数逻辑顺序
在受控组件模式下,应该先通知父组件值变化,再更新内部状态,当前实现顺序可能导致状态不同步。
建议调整调用顺序:
- setValue('') - forceFocus() - onChange && onChange('') + onChange && onChange('') + setValue('') + forceFocus()🧰 Tools
🪛 Biome (1.9.4)
[error] 131-131: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 132-132: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🧹 Nitpick comments (3)
src/packages/searchbar/searchbar.taro.tsx (3)
206-230: 标签模式渲染实现👍新增的标签模式渲染逻辑设计合理,支持以标签形式展示逗号分隔的值,并添加了点击事件处理。
不过建议优化空检查逻辑:
- if (!list) return null + if (!list.length) return null
270-284: 清除按钮渲染逻辑优化建议清除按钮的显示逻辑考虑了标签模式,但建议使用 CSS classes 代替内联样式控制可见性。
- style={{ - visibility: `${!innerTag && value && clearable ? 'visible' : 'hidden'}`, - }} + className={classNames({ + [`${classPrefix}-clear-visible`]: !innerTag && value && clearable + })}然后在 CSS 中添加:
.nut-searchbar-clear-visible { visibility: visible; } .nut-searchbar-clear { visibility: hidden; }
93-101: 优化可选链使用按照静态分析工具建议,可以优化代码中的可选链用法。
- onChange && onChange(eventValue, event) + onChange?.(eventValue, event)🧰 Tools
🪛 Biome (1.9.4)
[error] 96-96: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/packages/searchbar/searchbar.taro.tsx(4 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/packages/searchbar/searchbar.taro.tsx
[error] 96-96: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 105-105: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 114-114: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 131-131: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 132-132: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 286-287: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: build
- GitHub Check: test
🔇 Additional comments (5)
src/packages/searchbar/searchbar.taro.tsx (5)
80-84: 使用 usePropsValue 改进受控组件实现👍将原有的 useState 替换为 usePropsValue 能更好地处理受控和非受控模式,让组件同时支持 value 和 defaultValue 模式。
88-90: 实现 forceFocus 函数优化焦点管理👍使用 useCallback 包装的 forceFocus 函数有效地避免了重复代码,统一了焦点处理逻辑。
92-101: 输入事件处理函数优化👍使用 useCallback 优化了 handleInput 函数,避免不必要的重渲染。处理逻辑也得到改进,包括防止重复设置相同值和清空后重新聚焦。
🧰 Tools
🪛 Biome (1.9.4)
[error] 96-96: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
232-240: 优化图标渲染函数👍使用 useCallback 优化了 renderLeftIn 函数,减少不必要的重渲染。
299-301: 渲染顺序优化👍在输入框后正确添加了标签渲染、清除按钮和右侧内容,布局结构清晰合理。
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feat_v3.x #3214 +/- ##
=============================================
+ Coverage 86.54% 86.64% +0.10%
=============================================
Files 289 289
Lines 18739 18798 +59
Branches 2822 2825 +3
=============================================
+ Hits 16217 16287 +70
+ Misses 2517 2506 -11
Partials 5 5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/packages/searchbar/__tests__/searchbar.spec.tsx (1)
1-83: 🛠️ Refactor suggestion缺少完整的功能测试覆盖
根据PR描述,此次更新增加了对SearchBar组件的V15适配和新的tag实现方式,但当前测试套件没有完全覆盖这些新功能。
建议增加以下测试:
- 受控与非受控模式的完整测试
- tag标签模式下的完整交互测试
- onItemClick事件处理测试
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/packages/searchbar/__tests__/searchbar.spec.tsx(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: build
- GitHub Check: test
🔇 Additional comments (4)
src/packages/searchbar/__tests__/searchbar.spec.tsx (4)
7-12: 优化了占位符测试逻辑使用
getByPlaceholderText代替直接查询DOM元素是一个很好的改进,这符合React Testing Library的最佳实践,更关注用户体验而非实现细节。
14-19: 测试断言更加明确和具体现在的测试更清晰地验证了maxlength属性和shape类,断言更加精确和有针对性。
21-27: 测试描述更加清晰重命名测试描述使其更加明确,这是一个很好的改进。
29-36: 新增rightIn元素渲染测试测试验证了
rightIn属性对字符串和React节点的支持,覆盖了组件的不同使用场景。
| test('should handle all events correctly', async () => { | ||
| const handleChange = vi.fn() | ||
| const handleFocus = vi.fn() | ||
| const handleBlur = vi.fn() | ||
| const handleClick = vi.fn() | ||
| const handleClear = vi.fn() | ||
| const Demo = () => { | ||
| const [value, setValue] = useState('奶茶') | ||
| const onChange = (newValue: string) => { | ||
| setValue(newValue) // 更新状态 | ||
| handleChange(newValue) // 调用传入的 onChange 处理函数 | ||
| } | ||
| return ( | ||
| <SearchBar | ||
| value={value} | ||
| autoFocus | ||
| onChange={onChange} | ||
| onFocus={handleFocus} | ||
| onBlur={handleBlur} | ||
| onInputClick={handleClick} | ||
| onClear={handleClear} | ||
| /> | ||
| ) | ||
| } | ||
|
|
||
| const { container } = render(<Demo />) | ||
| const inputEl = container.querySelector('.nut-searchbar-input') as Element | ||
| expect(inputEl).toHaveValue('奶茶') | ||
| fireEvent.click(inputEl) | ||
| expect(handleClick).toHaveBeenCalledTimes(1) | ||
| fireEvent.change(inputEl, { target: { value: '冰激凌' } }) | ||
|
|
||
| await waitFor(() => { | ||
| expect(handleFocus).toHaveBeenCalledTimes(1) | ||
| expect(handleChange).toHaveBeenCalledTimes(1) | ||
| expect(inputEl).toHaveValue('冰激凌') | ||
| fireEvent.blur(inputEl) | ||
| expect(handleBlur).toHaveBeenCalled() | ||
| }) | ||
|
|
||
| const clear = container.querySelector('.nut-searchbar-clear') as Element | ||
| fireEvent.click(clear) | ||
| await waitFor(() => { | ||
| expect(inputEl).toHaveValue('') | ||
| }) | ||
| }) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
事件处理测试需要细化和完善
这个测试综合验证了多个事件,但有几点可以改进:
- 使用了
autoFocus属性但没有断言它是否生效 - 在一个
waitFor块中验证多个不同时间发生的事件,可能导致测试不稳定 - 缺少对PR主要功能「tag模式」的测试
建议添加以下测试:
+test('should render with defaultValue', () => {
+ const { container } = render(<SearchBar defaultValue="123" />)
+ const input = container.querySelector('.nut-searchbar-input') as HTMLInputElement
+ expect(input).toHaveValue('123')
+})
+test('should render tags when tag prop is true', () => {
+ const { container } = render(<SearchBar defaultValue="标签1,标签2" tag />)
+ const tags = container.querySelectorAll('.nut-searchbar-value')
+ expect(tags.length).toBe(2)
+ expect(tags[0].textContent).toContain('标签1')
+ expect(tags[1].textContent).toContain('标签2')
+})
+test('should trigger onItemClick when tag close icon is clicked', async () => {
+ const handleItemClick = vi.fn()
+ const { container } = render(
+ <SearchBar defaultValue="标签1,标签2" tag onItemClick={handleItemClick} />
+ )
+ const closeIcon = container.querySelector('.nut-searchbar-value-item-close') as Element
+ fireEvent.click(closeIcon)
+
+ await waitFor(() => {
+ expect(handleItemClick).toHaveBeenCalledTimes(1)
+ })
+})另外,建议将事件处理测试细分为多个独立的waitFor块:
fireEvent.change(inputEl, { target: { value: '冰激凌' } })
await waitFor(() => {
expect(handleFocus).toHaveBeenCalledTimes(1)
expect(handleChange).toHaveBeenCalledTimes(1)
expect(inputEl).toHaveValue('冰激凌')
- fireEvent.blur(inputEl)
- expect(handleBlur).toHaveBeenCalled()
})
+
+fireEvent.blur(inputEl)
+await waitFor(() => {
+ expect(handleBlur).toHaveBeenCalled()
+})📝 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('should handle all events correctly', async () => { | |
| const handleChange = vi.fn() | |
| const handleFocus = vi.fn() | |
| const handleBlur = vi.fn() | |
| const handleClick = vi.fn() | |
| const handleClear = vi.fn() | |
| const Demo = () => { | |
| const [value, setValue] = useState('奶茶') | |
| const onChange = (newValue: string) => { | |
| setValue(newValue) // 更新状态 | |
| handleChange(newValue) // 调用传入的 onChange 处理函数 | |
| } | |
| return ( | |
| <SearchBar | |
| value={value} | |
| autoFocus | |
| onChange={onChange} | |
| onFocus={handleFocus} | |
| onBlur={handleBlur} | |
| onInputClick={handleClick} | |
| onClear={handleClear} | |
| /> | |
| ) | |
| } | |
| const { container } = render(<Demo />) | |
| const inputEl = container.querySelector('.nut-searchbar-input') as Element | |
| expect(inputEl).toHaveValue('奶茶') | |
| fireEvent.click(inputEl) | |
| expect(handleClick).toHaveBeenCalledTimes(1) | |
| fireEvent.change(inputEl, { target: { value: '冰激凌' } }) | |
| await waitFor(() => { | |
| expect(handleFocus).toHaveBeenCalledTimes(1) | |
| expect(handleChange).toHaveBeenCalledTimes(1) | |
| expect(inputEl).toHaveValue('冰激凌') | |
| fireEvent.blur(inputEl) | |
| expect(handleBlur).toHaveBeenCalled() | |
| }) | |
| const clear = container.querySelector('.nut-searchbar-clear') as Element | |
| fireEvent.click(clear) | |
| await waitFor(() => { | |
| expect(inputEl).toHaveValue('') | |
| }) | |
| }) | |
| // ——— New tests to cover defaultValue and tag mode ——— | |
| test('should render with defaultValue', () => { | |
| const { container } = render(<SearchBar defaultValue="123" />) | |
| const input = container.querySelector('.nut-searchbar-input') as HTMLInputElement | |
| expect(input).toHaveValue('123') | |
| }) | |
| test('should render tags when tag prop is true', () => { | |
| const { container } = render(<SearchBar defaultValue="标签1,标签2" tag />) | |
| const tags = container.querySelectorAll('.nut-searchbar-value') | |
| expect(tags.length).toBe(2) | |
| expect(tags[0].textContent).toContain('标签1') | |
| expect(tags[1].textContent).toContain('标签2') | |
| }) | |
| test('should trigger onItemClick when tag close icon is clicked', async () => { | |
| const handleItemClick = vi.fn() | |
| const { container } = render( | |
| <SearchBar defaultValue="标签1,标签2" tag onItemClick={handleItemClick} /> | |
| ) | |
| const closeIcon = container.querySelector( | |
| '.nut-searchbar-value-item-close' | |
| ) as Element | |
| fireEvent.click(closeIcon) | |
| await waitFor(() => { | |
| expect(handleItemClick).toHaveBeenCalledTimes(1) | |
| }) | |
| }) | |
| // ——— Updated existing composite event test ——— | |
| test('should handle all events correctly', async () => { | |
| const handleChange = vi.fn() | |
| const handleFocus = vi.fn() | |
| const handleBlur = vi.fn() | |
| const handleClick = vi.fn() | |
| const handleClear = vi.fn() | |
| const Demo = () => { | |
| const [value, setValue] = useState('奶茶') | |
| const onChange = (newValue: string) => { | |
| setValue(newValue) // 更新状态 | |
| handleChange(newValue) // 调用传入的 onChange | |
| } | |
| return ( | |
| <SearchBar | |
| value={value} | |
| autoFocus | |
| onChange={onChange} | |
| onFocus={handleFocus} | |
| onBlur={handleBlur} | |
| onInputClick={handleClick} | |
| onClear={handleClear} | |
| /> | |
| ) | |
| } | |
| const { container } = render(<Demo />) | |
| const inputEl = container.querySelector( | |
| '.nut-searchbar-input' | |
| ) as HTMLElement | |
| expect(inputEl).toHaveValue('奶茶') | |
| fireEvent.click(inputEl) | |
| expect(handleClick).toHaveBeenCalledTimes(1) | |
| fireEvent.change(inputEl, { target: { value: '冰激凌' } }) | |
| // 1. focus/change/value assertions | |
| await waitFor(() => { | |
| expect(handleFocus).toHaveBeenCalledTimes(1) | |
| expect(handleChange).toHaveBeenCalledTimes(1) | |
| expect(inputEl).toHaveValue('冰激凌') | |
| }) | |
| // 2. blur assertion in its own waitFor | |
| fireEvent.blur(inputEl) | |
| await waitFor(() => { | |
| expect(handleBlur).toHaveBeenCalled() | |
| }) | |
| const clear = container.querySelector( | |
| '.nut-searchbar-clear' | |
| ) as HTMLElement | |
| fireEvent.click(clear) | |
| await waitFor(() => { | |
| expect(inputEl).toHaveValue('') | |
| }) | |
| }) |
🤔 这个变动的性质是?
🔗 相关 Issue
💡 需求背景和解决方案
☑️ 请求合并前的自查清单
Summary by CodeRabbit
新功能
样式
文档
演示
测试