Skip to content

feat(searchbar): V15适配,新增tag的实现方式#3214

Closed
xiaoyatong wants to merge 14 commits intojdf2e:feat_v3.xfrom
xiaoyatong:feat3/searchbar-tags
Closed

feat(searchbar): V15适配,新增tag的实现方式#3214
xiaoyatong wants to merge 14 commits intojdf2e:feat_v3.xfrom
xiaoyatong:feat3/searchbar-tags

Conversation

@xiaoyatong
Copy link
Copy Markdown
Collaborator

@xiaoyatong xiaoyatong commented May 1, 2025

🤔 这个变动的性质是?

  • 新特性提交
  • 日常 bug 修复
  • 站点、文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • TypeScript 定义更新
  • 包体积优化
  • 性能优化
  • 功能增强
  • 国际化改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他改动(是关于什么的改动?)

🔗 相关 Issue

💡 需求背景和解决方案

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • fork仓库代码是否为最新避免文件冲突
  • Files changed 没有 package.json lock 等无关文件

Summary by CodeRabbit

  • 新功能

    • SearchBar 组件新增受控模式,支持 value 和 defaultValue 属性,可通过逗号分隔设置多个默认值,支持标签展示及标签移除(onItemClick 事件)。
    • 新增 tag 属性,支持输入内容以标签形式展示。
    • SearchBar Taro 端同步支持上述新特性。
  • 样式

    • SearchBar 组件样式优化,调整内外间距、背景色、圆角、输入区高度、图标尺寸等,提升视觉效果和易用性。
    • 新增和调整相关 SCSS 变量,支持更灵活的自定义。
  • 文档

    • SearchBar 组件中英文及多语言文档补充 defaultValue、受控用法、onItemClick 事件说明,并新增相关演示和 API 说明。
    • 移除部分冗余或已废弃的文档内容。
  • 演示

    • 新增 defaultValue 和受控模式相关 Demo,完善多场景演示。
    • 现有 Demo 统一改为从包内引入组件,部分 Demo 增加图标及交互演示。
  • 测试

    • SearchBar 组件测试用例大幅扩充,覆盖更多交互场景和受控逻辑。

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2025

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 08dd01e and 1937622.

📒 Files selected for processing (1)
  • src/packages/searchbar/__tests__/searchbar.spec.tsx (1 hunks)
## 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

  • oasis-cloud

Poem

小兔新添 SearchBar,标签模式真不差,
受控非控随你选,清除回调乐哈哈。
样式变量齐优化,文档 demo 全刷刷,
测试覆盖更周全,代码跃进顶呱呱!
🐰✨🔍

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=jdf2e/nutui-react&utm_content=3214):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

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)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@github-actions github-actions Bot added action:review This PR needs more reviews (less than 2 approvals) 3.x Target branch 3.x labels May 1, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,并考虑将 defaultValuetagonItemClick 等 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

📥 Commits

Reviewing files that changed from the base of the PR and between bd9f30b and e78d326.

📒 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: 移除图标显式尺寸,依赖主题变量
示例中去除了 ArrowLeftCloseStarMorePhotograph 的显式 sizewidth/height 属性,改为依赖全局 CSS 变量(如 --nutui-searchbar-icon-size)进行统一控制,做法符合组件库最新样式规范。建议预览确认在不同主题或设备下图标尺寸和间距符合预期。

Also applies to: 28-28

src/packages/searchbar/demos/h5/demo4.tsx (1)

2-2: 合并导入,简化路径
示例将 SearchBarToast 统一从 @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组件添加了升级文档,包括:

  1. value属性支持受控模式
  2. defaultValue属性用于设置默认搜索值,支持逗号分隔的多个值
  3. 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组件的新功能说明,包括:

  1. value属性,支持受控模式
  2. defaultValue属性,用于设置搜索默认值,支持以逗号分隔的多个值
  3. 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: 增强搜索栏展示形式,提供多样化交互方案

新的示例代码展示了搜索栏的两种不同使用场景:

  1. 简单场景:右侧固定文本"搜索"
  2. 复杂场景:左侧扫描图标,右侧组合了拍照图标、分隔线和红色文本

这种实现既展示了组件的灵活性,也提供了实际应用中常见的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环境下搜索栏的两种使用方式:

  1. 基础用法:右侧简单文本
  2. 高级用法:左侧图标和自定义右侧内容(带交互功能)

这种实现确保了跨平台的一致体验,同时展示了组件在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: 类名聚合依赖遗漏

classNamedisabledbackable 等 props 发生变化时,cls 会随之更新,但目前依赖数组未包含 tag。如果父组件动态切换 tag 模式,可能造成样式残留。建议补充依赖或通过 classNames 时机集中处理。

src/packages/searchbar/searchbar.taro.tsx (1)

154-176: TaroInput 的 focus 属性未考虑 Tag 展示模式
tagtrue 且处于非标签态 (!innerTag) 时应自动聚焦输入框,否则用户需额外点击。
可将 focus={autoFocus} 修改为 focus={autoFocus || (!innerTag && tag)}

Comment on lines +115 to 116
| onItemClick | 点击默认值的x号触发 | `(value: string) => void` | `-` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

文档参数描述与实现不一致

实现中 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.

Suggested change
| onItemClick | 点击默认值的x号触发 | `(value: string) => void` | `-` |
| onItemClick | 点击默认值的 x 号时触发 | `(value: string, event: MouseEvent<HTMLDivElement>) => void` | `-` |

Comment on lines +117 to 118
| onItemClick | 點擊默認值的x號觸發 | `(value: string) => void` | `-` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

文件描述与源码回调签名不一致

组件源码额外向 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.

Suggested change
| onItemClick | 點擊默認值的x號觸發 | `(value: string) => void` | `-` |
| onItemClick | 點擊默認值 X 圖示時觸發 | `(value: string, event: MouseEvent<HTMLDivElement>) => void` | `-` |

Comment on lines +29 to +33
test('should render with default value', () => {
const { container } = render(<SearchBar defaultValue="123" />)
const dvalues = container.querySelectorAll('.nut-searchbar-value')
expect(dvalues.length).toBe(1)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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.

Suggested change
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)
})
Suggested change
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')
})

Comment on lines +181 to +204
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])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Tag 渲染存在易用性与稳定性问题

  1. value.split(',') 未去除空白,标签文本可能出现前后空格。
  2. 以索引作为 React key,在删除/插入场景易导致复用错误。
  3. 关闭全部标签后立即 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.

Suggested change
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])

Comment on lines +120 to +126
(event: MouseEvent<HTMLDivElement>) => {
if (disabled || readOnly) return
setValue('')
forceFocus()
onChange && onChange('')
onClear && onClear(event)
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
(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)

Comment on lines +86 to +113
&-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;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

硬编码颜色与尺寸,削弱了可主题化能力
.nut-searchbar-values 及其子元素直接使用 #fff / #c2c4cc / 6px 等字面量,不利于暗黑模式或品牌换色。
建议全部改为 $color-background-overlay$color-text-disabled$searchbar-icon-size 等变量形式,同时使用 rem% 替代纯 px 以获得更好的可缩放性。

Comment on lines +184 to +190
&-focus {
padding: 5px $searchbar-gap;

.nut-searchbar-content {
border: 0.5px solid #ff5c67;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

聚焦态改变外层 padding 会造成布局抖动
&-focus 里把根元素 padding 从 1px 8px 改成 5px $searchbar-gap,输入框获得/失去焦点时会导致组件整体宽高瞬间变化,体验不佳。
通常做法是在普通态就预留边框宽度,或使用 box-shadow/outline 表示焦点,而不是修改 padding。

Comment on lines +122 to +130
(event: ITouchEvent) => {
if (disabled || readOnly) return
setValue('')
onChange && onChange('')
onClear && onClear(event)
forceFocus()
},
[disabled, readOnly, onChange, onClear, setValue]
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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.

Suggested change
(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)

Comment on lines +85 to +86

const forceFocus = useCallback(() => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

innerTag 状态未与外部 tag Prop 保持同步
组件初始化后若父组件动态修改 taginnerTag 不会自动更新,造成显示状态不一致。
可在 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.

Suggested change
const [innerTag, setInnerTag] = useState(tag)
const [innerTag, setInnerTag] = useState(tag)
useEffect(() => {
setInnerTag(tag)
}, [tag])

Comment on lines 48 to 49
const {
value: outerValue,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
const searchInputRef = useRef<HTMLInputElement>(null)
const {
const searchInputRef = useRef<any>(null) // 或使用正确的 Taro 类型
const {

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 suggestion

Tag 渲染存在易用性与稳定性问题

  1. value.split(',') 未去除空白,标签文本可能出现前后空格。
  2. 以索引作为 React key,在删除/插入场景易导致复用错误。
  3. 未检查标签是否为空字符串,可能导致渲染空标签。
-    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 issue

Tag 渲染存在易用性与稳定性问题

  1. 缺少 innerTag 条件检查,与 Taro 版本不一致。
  2. value.split(',') 未去除空白,标签文本可能出现前后空格。
  3. 以索引作为 React key,在删除/插入场景易导致复用错误。
  4. 未检查标签是否为空字符串,可能导致渲染空标签。
+    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 的调用没有使用可选链操作符,在 onChangeundefined 时会造成不必要的逻辑检查。

-      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 中已有对 valueclearable 的检查。应拆分这两个组件的渲染,使逻辑更清晰。

-        {innerTag ? renderValueByTags() : renderClear()}
+        {renderValueByTags()}
+        {!innerTag && renderClear()}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e78d326 and e86b883.

📒 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

Comment on lines +111 to +124
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]
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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'.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between e86b883 and cd5dbab.

📒 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
Copy link
Copy Markdown

codecov Bot commented May 1, 2025

Codecov Report

Attention: Patch coverage is 90.00000% with 14 lines in your changes missing coverage. Please review.

Project coverage is 86.64%. Comparing base (bc1f644) to head (1937622).
Report is 2 commits behind head on feat_v3.x.

Files with missing lines Patch % Lines
src/packages/searchbar/searchbar.tsx 90.00% 14 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/packages/searchbar/__tests__/searchbar.spec.tsx (1)

1-83: 🛠️ Refactor suggestion

缺少完整的功能测试覆盖

根据PR描述,此次更新增加了对SearchBar组件的V15适配和新的tag实现方式,但当前测试套件没有完全覆盖这些新功能。

建议增加以下测试:

  1. 受控与非受控模式的完整测试
  2. tag标签模式下的完整交互测试
  3. onItemClick事件处理测试
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd5dbab and 08dd01e.

📒 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节点的支持,覆盖了组件的不同使用场景。

Comment on lines +38 to 83
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('')
})
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

事件处理测试需要细化和完善

这个测试综合验证了多个事件,但有几点可以改进:

  1. 使用了autoFocus属性但没有断言它是否生效
  2. 在一个waitFor块中验证多个不同时间发生的事件,可能导致测试不稳定
  3. 缺少对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.

Suggested change
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('')
})
})

@xiaoyatong xiaoyatong closed this May 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant