Skip to content

Conversation

@yangyanAurora
Copy link

@yangyanAurora yangyanAurora commented Nov 13, 2025

  • button
  • Checkbox
  • Countdown
  • Empty
  • Image
  • InputNumber
  • Price
  • Progress
  • NavBar
  • NoticeBar
  • Radio
  • Switch
  • Tabbar
  • Tabs
  • Toast
  • Popup
  • Dialog

🤔 这个变动的性质是?

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

🔗 相关 Issue

💡 需求背景和解决方案

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

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

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

Summary by CodeRabbit

新功能

  • 无障碍支持增强 - 为按钮、倒计时、对话框、图片、数字输入、导航栏、通知栏、弹出框、价格、进度条、滑块、标签栏和标签页等多个组件添加了ARIA标签和角色属性,改善屏幕阅读器兼容性,使应用更易被残疾用户访问。

bigbigDreamer and others added 16 commits October 16, 2025 11:56
- button
- Checkbox
- Countdown
- Empty
- Image
- InputNumber
- Price
- Progress
- NavBar
- NoticeBar
- Radio
- Switch
- Tabbar
- Tabs
- Toast
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@github-actions github-actions bot added the action:review This PR needs more reviews (less than 2 approvals) label Nov 13, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 13, 2025

无障碍访问功能增强评审

Walkthrough

本次 PR 在多个 NutUI React 组件中系统地添加了 ARIA 无障碍访问支持,包括语义化角色(role)、标签(label)、禁用状态(disabled)等属性。这些改进涵盖按钮、对话框、图片、输入框、选项卡等核心组件的 Web 版本和 Taro 版本,并新增相应的演示文件。

Changes

无障碍属性添加 - 核心组件 文件路径
按钮和基础 UI 元素 src/packages/button/button.taro.tsx
对话框组件 src/packages/dialog/content.taro.tsx, src/packages/dialog/dialog.taro.tsx, src/packages/dialog/dialog.tsx
浮层和弹出窗 src/packages/overlay/overlay.taro.tsx, src/packages/overlay/overlay.tsx, src/packages/popup/popup.taro.tsx, src/packages/popup/popup.tsx
导航栏 src/packages/navbar/navbar.taro.tsx, src/packages/navbar/navbar.tsx
进度条 src/packages/progress/progress.taro.tsx, src/packages/progress/progress.tsx
倒计时组件完整增强 文件路径
倒计时主实现 src/packages/countdown/countdown.taro.tsx, src/packages/countdown/countdown.tsx
倒计时演示 src/packages/countdown/demo.taro.tsx, src/packages/countdown/demo.tsx, src/packages/countdown/demos/h5/demo10.tsx, src/packages/countdown/demos/taro/demo10.tsx
图片组件增强 文件路径
图片实现和演示 src/packages/image/image.taro.tsx, src/packages/image/image.tsx, src/packages/image/demo.taro.tsx, src/packages/image/demo.tsx, src/packages/image/demos/h5/demo9.tsx, src/packages/image/demos/taro/demo9.tsx
数字输入框增强 文件路径
数字输入框实现和演示 src/packages/inputnumber/inputnumber.taro.tsx, src/packages/inputnumber/inputnumber.tsx, src/packages/inputnumber/demo.taro.tsx, src/packages/inputnumber/demo.tsx, src/packages/inputnumber/demos/h5/demo11.tsx, src/packages/inputnumber/demos/taro/demo11.tsx
通知栏和价格组件 文件路径
通知栏 src/packages/noticebar/noticebar.taro.tsx, src/packages/noticebar/noticebar.tsx, src/packages/noticebar/demo.taro.tsx, src/packages/noticebar/demo.tsx, src/packages/noticebar/demos/h5/demo12.tsx, src/packages/noticebar/demos/taro/demo12.tsx
价格显示 src/packages/price/price.taro.tsx, src/packages/price/price.tsx
轮播和选项卡组件 文件路径
轮播 src/packages/swiper/swiper.taro.tsx, src/packages/swiper/swiper.tsx, src/packages/swiper/effects/default.tsx, src/packages/swiper/effects/focus.tsx, src/packages/swiper/demo.taro.tsx, src/packages/swiper/demos/taro/demo10.tsx
选项卡栏 src/packages/tabbar/tabbar.taro.tsx, src/packages/tabbar/tabbar.tsx, src/packages/tabbar/tabbaritem.taro.tsx, src/packages/tabbar/tabbaritem.tsx, src/packages/tabbar/demo.taro.tsx, src/packages/tabbar/demo.tsx, src/packages/tabbar/demos/h5/demo10.tsx, src/packages/tabbar/demos/taro/demo10.tsx
标签页组件 文件路径
标签页实现和演示 src/packages/tabs/tabs.taro.tsx, src/packages/tabs/tabs.tsx, src/packages/tabpane/tabpane.taro.tsx, src/packages/tabpane/tabpane.tsx, src/packages/tabs/demo.taro.tsx, src/packages/tabs/demo.tsx, src/packages/tabs/demos/h5/demo24.tsx, src/packages/tabs/demos/h5/demo25.tsx, src/packages/tabs/demos/taro/demo24.tsx, src/packages/tabs/demos/taro/demo25.tsx
类型定义更新 文件路径
基础类型接口 src/types/spec/countdown/base.ts, src/types/spec/image/base.ts, src/types/spec/inputnumber/base.ts, src/types/spec/noticebar/base.ts, src/types/spec/swiper/taro.ts, src/types/spec/tabbar/base.ts
其他更改 文件路径
站点和公共组件 src/sites/components/header.tsx

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

需要特别关注的领域

  • 跨平台一致性:需验证 Web 版本(.tsx)和 Taro 版本(.taro.tsx)中 ARIA 属性的实现是否一致,特别是属性名称的使用(aria-* vs ariaXxx
  • InputNumber 和 Tabs 组件:这些组件的增强较为复杂,涉及多个控制元素的 ARIA 属性链接(如按钮的禁用状态、标签页的选中状态)
  • 新增 props 的默认值:确保所有新增的 ariaLabelariaRole 等属性都有合理的默认值和中文本地化
  • 演示文件的完整性:验证新增的十多个演示文件是否完整展示了无障碍功能,特别是倒计时结束提醒、轮播导航等场景

Possibly related PRs

Suggested reviewers

  • xiaoyatong
  • irisSong

Poem

🐰 无障碍之路,兔子来开辟
按钮添标签,标签页聚焦
对话框有声,轮播能诉说
屏幕读不懂,现已全听得
每个按键,都闪烁着关怀✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR描述缺少关键信息。虽然列举了受影响的组件,但未填写问题背景、解决方案、API用法等必需部分,且自查清单全部未勾选。 请完善PR描述:填写需求背景和解决方案部分、添加相关Issue链接、补充API实现说明和演示代码、勾选自查清单确认文档和TypeScript定义已更新。
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确,直接反映了PR的主要变动——为多个组件添加无障碍功能支持。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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: 30

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/packages/noticebar/noticebar.taro.tsx (1)

466-474: 建议为 leftIcon 添加 aria-hidden 属性

注意到 Web 版本(src/packages/noticebar/noticebar.tsx Lines 460-462)为 leftIcon 添加了 aria-hidden 属性,但 Taro 版本缺少此属性。为了保持一致性并提供更好的无障碍体验,建议也为 Taro 版本的装饰性 leftIcon 添加 aria-hidden

建议修改:

 const renderLeftIcon = useCallback(() => {
   return (
     <>
       {leftIcon ? (
-        <View className="nut-noticebar-box-left-icon">{leftIcon}</View>
+        <View className="nut-noticebar-box-left-icon" aria-hidden>
+          {leftIcon}
+        </View>
       ) : null}
     </>
   )
 }, [leftIcon])
src/packages/swiper/swiper.taro.tsx (1)

51-155: ariaLabel 未被透传导致改动无效

在解构 props 后并没有把 ariaLabel 传给任一容器或 TaroSwiper 本体,最终渲染里不会出现该属性,使用方设置 ariaLabel 也不会生效。请补上透传,例如直接给 TaroSwiper 增加 ariaLabel={ariaLabel}(或其它合适的节点),以兑现新增的对外 API。

           <TaroSwiper
             current={innerValue}
             circular={loop}
             autoplay={autoplay || autoPlay}
             vertical={direction === 'vertical' || vertical}
             indicatorDots={false}
             onChange={(e) => {
               handleOnChange(e)
               props.onChange?.(e)
             }}
             style={{
               width: !width ? '100%' : width,
               height: !height ? pxTransform(150) : height,
             }}
+            ariaLabel={ariaLabel}
             {...rest}
           >
src/packages/inputnumber/inputnumber.tsx (1)

203-240: 需补足键盘可操作性

为减号/加号容器加了 role="button"tabIndex={0} 后,它们会进入键盘焦点,但未绑定 onKeyDown(或改用 <button> 元素),键盘用户按回车/空格不会触发增减逻辑。请补上键盘事件(或改用原生 button),保障无障碍交互。

       <div
         className={`${classPrefix}-minus`}
         onClick={handleReduce}
         role="button"
         tabIndex={0}
         aria-label={translated.mins}
         aria-disabled={Number(shadowValue) <= Number(min) || disabled}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault()
+            e.currentTarget.click()
+          }
+        }}
       >
...
       <div
         className={`${classPrefix}-add`}
         role="button"
         tabIndex={0}
         aria-label={translated.plus}
         aria-disabled={Number(shadowValue) >= Number(max) || disabled}
         onClick={handlePlus}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter' || e.key === ' ') {
+            e.preventDefault()
+            e.currentTarget.click()
+          }
+        }}
       >
🧹 Nitpick comments (21)
src/packages/tabpane/tabpane.tsx (1)

35-37: ARIA 实现良好,建议补充 aria-labelledby 属性。

添加 role="tabpanel" 和动态 tabIndex 是正确的无障碍实现。tabIndex 逻辑确保激活的面板可聚焦(0),非激活的面板不可聚焦(-1)。

建议补充 aria-labelledby 属性,指向对应的 tab 元素的 ID,以便屏幕阅读器能够建立 tab 和 tabpanel 之间的关联关系。

参考 ARIA 最佳实践:

-    <div className={classes} role="tabpanel" tabIndex={active ? 0 : -1}>
+    <div 
+      className={classes} 
+      role="tabpanel" 
+      tabIndex={active ? 0 : -1}
+      aria-labelledby={/* 对应 tab 的 ID */}
+    >
       {!disabled && children}
     </div>
src/packages/tabpane/tabpane.taro.tsx (1)

36-38: ARIA 实现良好,建议补充 ariaLabelledby 属性。

使用 ariaRole="tabpanel" 和动态 tabIndex 是正确的 Taro 无障碍实现。tabIndex 逻辑确保激活的面板可聚焦(0),非激活的面板不可聚焦(-1)。

建议补充 ariaLabelledby 属性(注意 Taro 使用驼峰命名),指向对应的 tab 元素的 ID,以便屏幕阅读器能够建立 tab 和 tabpanel 之间的关联关系。

参考 ARIA 最佳实践:

-    <View className={classes} ariaRole="tabpanel" tabIndex={active ? 0 : -1}>
+    <View 
+      className={classes} 
+      ariaRole="tabpanel" 
+      tabIndex={active ? 0 : -1}
+      ariaLabelledby={/* 对应 tab 的 ID */}
+    >
       {!disabled && children}
     </View>
src/packages/progress/progress.taro.tsx (1)

225-225: 考虑国际化支持。

默认的 ariaLabel 值使用硬编码的中文文本 进度${percent}%,这可能不适合国际化项目。建议:

  1. 如果项目有国际化方案,使用国际化函数包装默认文本
  2. 或者在文档中明确说明开发者应该为国际化场景提供自定义 ariaLabel

示例(假设项目使用 i18n):

-      ariaLabel={ariaLabel || `进度${percent}%`}
+      ariaLabel={ariaLabel || i18n.t('progress.ariaLabel', { percent })}

或者如果没有国际化方案,至少在组件文档中说明此问题。

src/packages/popup/popup.tsx (1)

309-309: 建议补充 aria-modal 和可访问名称属性

当前仅添加了 role="dialog",但根据 ARIA 最佳实践,对话框还应该包含:

  1. aria-modal="true":当 overlay 为 true 时,表明这是一个模态对话框
  2. aria-labelledbyaria-label:为对话框提供可访问的名称,可以关联到 title 属性

建议应用以下改进:

         <div
           ref={nodeRef}
           style={popStyles}
           className={popClassName}
           onClick={onClick}
           onTouchStart={handleTouchStart}
           onTouchMove={handleTouchMove}
           onTouchEnd={handleTouchEnd}
           onTouchCancel={handleTouchEnd}
           role="dialog"
+          aria-modal={overlay ? 'true' : undefined}
+          aria-label={typeof title === 'string' ? title : undefined}
         >
src/packages/popup/popup.taro.tsx (1)

349-349: 建议补充 ariaModal 和可访问名称属性

popup.tsx 类似,Taro 版本也应该补充完整的对话框 ARIA 属性:

  1. ariaModal={overlay}:当 overlay 为 true 时标记为模态对话框
  2. ariaLabel:为对话框提供可访问的名称

建议应用以下改进:

         onTouchEnd={handleTouchEnd}
         onTouchCancel={handleTouchEnd}
         ariaRole="dialog"
+        ariaModal={overlay}
+        ariaLabel={typeof title === 'string' ? title : undefined}
       >
src/packages/radio/radio.taro.tsx (1)

140-147: 建议添加 tabIndex 以支持键盘访问

当前实现已正确添加了 ariaRoleariaCheckedariaDisabled 属性,但缺少键盘可访问性支持。参考 Web 版本的 Checkbox 实现(checkbox.tsx 第 213 行),应该添加 tabIndex 属性。

建议应用以下改进:

     <View
       className={cls}
       style={style}
       onClick={handleClick}
       ariaRole="radio"
       ariaChecked={checkedStatement}
       ariaDisabled={disabled}
+      tabIndex={disabled ? -1 : 0}
     >
src/types/spec/noticebar/base.ts (1)

26-26: 建议将 rightIconAriaLabel 设为可选属性

将无障碍属性设为必需可能会破坏现有代码。根据 PR 中其他文件的模式(如 src/types/spec/tabbar/base.ts 中的 ariaLabel 等属性),无障碍属性通常应该是可选的,以支持渐进式采用。

应用此 diff 使属性可选:

-  rightIconAriaLabel: string
+  rightIconAriaLabel?: string
src/packages/navbar/demos/h5/demo4.tsx (1)

52-62: 移除冗余和不必要的 aria-label

  • 第55行:aria-label="页面标题" 与可见文本内容重复,可以移除
  • 第59行:aria-label="副标题" 用于非交互的展示性元素,通常不需要额外的 aria-label

建议修改:

 <div style={{ ...styles.flexCenter, flexDirection: 'column' }}>
   <span
     style={styles.title}
     onClick={(e) => Toast.show('标题')}
-    aria-label="页面标题"
   >
     页面标题
   </span>
-  <span style={styles.description} aria-label="副标题">
+  <span style={styles.description}>
     副标题
   </span>
 </div>
src/packages/tabs/demos/h5/demo25.tsx (1)

36-36: 简化 JSX 表达式

{item.icon || null} 中的 || null 是多余的,JSX 会自动处理 falsy 值。

建议改为:

-{item.icon || null}
+{item.icon}
src/sites/components/header.tsx (1)

32-32: 考虑将 aria-label 国际化

添加 role="button"aria-label="back" 是很好的无障碍实践。不过,aria-label 的值 "back" 是硬编码的英文字符串。为了更好的用户体验,建议使用国际化的标签值,就像组件名称的处理方式一样。

src/packages/navbar/navbar.tsx (1)

69-69: 返回按钮的 aria-label 应该国际化

为返回按钮添加 aria-label="back" 是好的做法,但标签值是硬编码的英文字符串。建议使用国际化的文本,以保持与组件其他部分的一致性。

src/packages/switch/demos/h5/demo9.tsx (1)

7-7: 改善 ariaLabel 的描述性以提高可访问性

当前的 ariaLabel 值 "弹幕" 对于屏幕阅读器用户来说不够描述性。ARIA 标签应该清楚地传达控件的目的和功能。建议使用更具描述性的标签,例如 "开启弹幕" 或 "弹幕开关",以便用户理解这是一个用于控制弹幕功能的开关。

应用以下差异来改进 ariaLabel:

-      <Switch defaultChecked ariaLabel="弹幕" />
+      <Switch defaultChecked ariaLabel="弹幕开关" />
src/packages/tabbar/demos/taro/demo10.tsx (1)

10-10: 建议优化 ariaRoledescription 的使用。

ariaRoledescription 属性应该描述元素的角色类型,而不是操作行为。当前设置为 "切换tab操作" 描述的是用户操作,而非角色本身。

根据 ARIA 规范,aria-roledescription 应该提供对角色的人类可读描述。建议:

  • 改为 "导航标签""标签页",更准确地描述角色
  • 或者移除此属性,使用元素默认的角色描述

参考修改示例:

     <Tabbar.Item
       title="首页"
       ariaLabel="首页"
-      ariaRoledescription="切换tab操作"
+      ariaRoledescription="导航标签"
       icon={<Home />}
     />

Also applies to: 16-16, 22-22, 28-28, 34-34

src/packages/countdown/demos/h5/demo10.tsx (1)

13-35: 建议为不同展示类型的倒计时提供更具描述性的 ARIA 标签。

目前三个 CountDown 组件使用相同的 ariaLabel。虽然它们显示相同的倒计时,但使用了不同的展示类型(primary、默认、text)。为了更好的无障碍体验,建议在标签中区分展示类型。

可以考虑以下改进:

      <Cell>
        <CountDown
          endTime={stateRef.current.endTime}
          type="primary"
          onEnd={onEnd}
-         ariaLabel="双十一活动倒计时"
+         ariaLabel="双十一活动倒计时 主要样式"
        />
      </Cell>
      <Cell>
        <CountDown
          endTime={stateRef.current.endTime}
          onEnd={onEnd}
-         ariaLabel="双十一活动倒计时"
+         ariaLabel="双十一活动倒计时 默认样式"
        />
      </Cell>
      <Cell>
        <CountDown
          endTime={stateRef.current.endTime}
          type="text"
          onEnd={onEnd}
-         ariaLabel="双十一活动倒计时"
+         ariaLabel="双十一活动倒计时 文本样式"
        />
      </Cell>
src/packages/countdown/demos/taro/demo10.tsx (1)

13-35: 建议为不同展示类型的倒计时提供更具描述性的 ARIA 标签。

目前三个 CountDown 组件使用相同的 ariaLabel。虽然它们显示相同的倒计时,但使用了不同的展示类型(primary、默认、text)。为了更好的无障碍体验,建议在标签中区分展示类型。

可以考虑以下改进:

      <Cell>
        <CountDown
          endTime={stateRef.current.endTime}
          type="primary"
          onEnd={onEnd}
-         ariaLabel="双十一活动倒计时"
+         ariaLabel="双十一活动倒计时 主要样式"
        />
      </Cell>
      <Cell>
        <CountDown
          endTime={stateRef.current.endTime}
          onEnd={onEnd}
-         ariaLabel="双十一活动倒计时"
+         ariaLabel="双十一活动倒计时 默认样式"
        />
      </Cell>
      <Cell>
        <CountDown
          endTime={stateRef.current.endTime}
          type="text"
          onEnd={onEnd}
-         ariaLabel="双十一活动倒计时"
+         ariaLabel="双十一活动倒计时 文本样式"
        />
      </Cell>
src/packages/noticebar/noticebar.tsx (1)

484-484: 建议国际化 aria-label 文本

当前使用硬编码的 'close' 字符串作为 aria-label。为了更好的国际化支持,建议通过 i18n 系统提供可翻译的文本标签,或允许开发者通过 props 自定义关闭按钮的 aria-label。

示例改进:

-aria-label={`${closeable ? 'close' : rightIconAriaLabel}`}
+aria-label={closeable ? (closeAriaLabel || 'close') : rightIconAriaLabel}

同时在 props 中添加 closeAriaLabel 属性。

Also applies to: 546-546

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

494-494: 建议国际化 aria-label 文本

与 Web 版本相同,这里也使用了硬编码的 'close' 字符串。为了更好的国际化支持,建议通过 i18n 系统提供可翻译的文本标签。

参考 Web 版本的改进建议,在 props 中添加 closeAriaLabel 属性:

-ariaLabel={`${closeable ? 'close' : rightIconAriaLabel}`}
+ariaLabel={closeable ? (closeAriaLabel || 'close') : rightIconAriaLabel}
src/packages/dialog/content.taro.tsx (1)

31-66: ariaModal 支持显式传值

目前解构出 ariaModal 却固定写死为 visible,导致外部无法按需覆写。建议改成优先使用传入值(例如 ariaModal ?? visible),保持灵活性并避免新增 API 成为摆设。

-      ariaModal={visible}
+      ariaModal={ariaModal ?? visible}
src/packages/countdown/countdown.taro.tsx (1)

27-27: 建议添加国际化支持

ariaLabel 的默认值使用了硬编码的中文字符串,但与 inputnumber 组件不同,此处没有使用 useTranslate 提供国际化支持。建议保持组件库的一致性,为 ariaLabel 添加多语言支持。

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

22-22: 建议为 ariaLabel 添加国际化

虽然组件中已经使用了 useTranslate 来翻译按钮标签,但 ariaLabel 的默认值仍然是硬编码的中文。建议将其也纳入国际化配置中,以提供一致的多语言支持。

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

312-312: 建议为 ariaRoledescription 添加国际化

ariaRoledescription 使用了硬编码的中文 "背景蒙层",缺少国际化支持。为了提供完整的无障碍体验,建议使用 locale 或 useTranslate 提供多语言支持。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 075b551 and e4f1ed9.

📒 Files selected for processing (90)
  • src/packages/button/button.taro.tsx (1 hunks)
  • src/packages/checkbox/checkbox.taro.tsx (1 hunks)
  • src/packages/checkbox/checkbox.tsx (1 hunks)
  • src/packages/countdown/countdown.taro.tsx (6 hunks)
  • src/packages/countdown/countdown.tsx (6 hunks)
  • src/packages/countdown/demo.taro.tsx (5 hunks)
  • src/packages/countdown/demo.tsx (5 hunks)
  • src/packages/countdown/demos/h5/demo10.tsx (1 hunks)
  • src/packages/countdown/demos/taro/demo10.tsx (1 hunks)
  • src/packages/dialog/content.taro.tsx (2 hunks)
  • src/packages/dialog/content.tsx (2 hunks)
  • src/packages/dialog/dialog.taro.tsx (5 hunks)
  • src/packages/dialog/dialog.tsx (2 hunks)
  • src/packages/image/demo.taro.tsx (4 hunks)
  • src/packages/image/demo.tsx (4 hunks)
  • src/packages/image/demos/h5/demo9.tsx (1 hunks)
  • src/packages/image/demos/taro/demo9.tsx (1 hunks)
  • src/packages/image/image.taro.tsx (2 hunks)
  • src/packages/image/image.tsx (2 hunks)
  • src/packages/inputnumber/demo.taro.tsx (5 hunks)
  • src/packages/inputnumber/demo.tsx (5 hunks)
  • src/packages/inputnumber/demos/h5/demo11.tsx (1 hunks)
  • src/packages/inputnumber/demos/taro/demo11.tsx (1 hunks)
  • src/packages/inputnumber/inputnumber.taro.tsx (6 hunks)
  • src/packages/inputnumber/inputnumber.tsx (6 hunks)
  • src/packages/navbar/demo.taro.tsx (2 hunks)
  • src/packages/navbar/demo.tsx (2 hunks)
  • src/packages/navbar/demos/h5/demo4.tsx (1 hunks)
  • src/packages/navbar/demos/taro/demo4.tsx (1 hunks)
  • src/packages/navbar/navbar.taro.tsx (3 hunks)
  • src/packages/navbar/navbar.tsx (3 hunks)
  • src/packages/noticebar/demo.taro.tsx (4 hunks)
  • src/packages/noticebar/demo.tsx (4 hunks)
  • src/packages/noticebar/demos/h5/demo12.tsx (1 hunks)
  • src/packages/noticebar/demos/taro/demo12.tsx (1 hunks)
  • src/packages/noticebar/noticebar.taro.tsx (3 hunks)
  • src/packages/noticebar/noticebar.tsx (6 hunks)
  • src/packages/popup/popup.taro.tsx (1 hunks)
  • src/packages/popup/popup.tsx (1 hunks)
  • src/packages/price/price.taro.tsx (1 hunks)
  • src/packages/price/price.tsx (2 hunks)
  • src/packages/progress/progress.taro.tsx (1 hunks)
  • src/packages/progress/progress.tsx (1 hunks)
  • src/packages/radio/radio.taro.tsx (1 hunks)
  • src/packages/radio/radio.tsx (1 hunks)
  • src/packages/steps/steps.taro.tsx (1 hunks)
  • src/packages/steps/steps.tsx (1 hunks)
  • src/packages/swiper/demo.taro.tsx (4 hunks)
  • src/packages/swiper/demos/taro/demo10.tsx (1 hunks)
  • src/packages/swiper/effects/default.tsx (3 hunks)
  • src/packages/swiper/effects/focus.tsx (1 hunks)
  • src/packages/swiper/swiper.taro.tsx (3 hunks)
  • src/packages/swiper/swiper.tsx (2 hunks)
  • src/packages/switch/demo.taro.tsx (5 hunks)
  • src/packages/switch/demo.tsx (5 hunks)
  • src/packages/switch/demos/h5/demo9.tsx (1 hunks)
  • src/packages/switch/demos/taro/demo9.tsx (1 hunks)
  • src/packages/switch/switch.taro.tsx (2 hunks)
  • src/packages/switch/switch.tsx (2 hunks)
  • src/packages/tabbar/demo.taro.tsx (5 hunks)
  • src/packages/tabbar/demo.tsx (5 hunks)
  • src/packages/tabbar/demos/h5/demo10.tsx (1 hunks)
  • src/packages/tabbar/demos/taro/demo10.tsx (1 hunks)
  • src/packages/tabbar/tabbar.taro.tsx (3 hunks)
  • src/packages/tabbar/tabbar.tsx (3 hunks)
  • src/packages/tabbaritem/tabbaritem.taro.tsx (1 hunks)
  • src/packages/tabbaritem/tabbaritem.tsx (2 hunks)
  • src/packages/tabpane/tabpane.taro.tsx (1 hunks)
  • src/packages/tabpane/tabpane.tsx (1 hunks)
  • src/packages/tabs/demo.taro.tsx (4 hunks)
  • src/packages/tabs/demo.tsx (4 hunks)
  • src/packages/tabs/demos/h5/demo24.tsx (1 hunks)
  • src/packages/tabs/demos/h5/demo25.tsx (1 hunks)
  • src/packages/tabs/demos/taro/demo24.tsx (1 hunks)
  • src/packages/tabs/demos/taro/demo25.tsx (1 hunks)
  • src/packages/tabs/tabs.taro.tsx (2 hunks)
  • src/packages/tabs/tabs.tsx (3 hunks)
  • src/packages/textarea/textarea.taro.tsx (1 hunks)
  • src/packages/toast/Notification.tsx (1 hunks)
  • src/packages/toast/toast.taro.tsx (1 hunks)
  • src/sites/components/header.tsx (1 hunks)
  • src/types/spec/countdown/base.ts (1 hunks)
  • src/types/spec/dialog/base.ts (1 hunks)
  • src/types/spec/dialog/taro.ts (1 hunks)
  • src/types/spec/image/base.ts (1 hunks)
  • src/types/spec/inputnumber/base.ts (1 hunks)
  • src/types/spec/noticebar/base.ts (1 hunks)
  • src/types/spec/swiper/taro.ts (1 hunks)
  • src/types/spec/switch/base.ts (1 hunks)
  • src/types/spec/tabbar/base.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: oasis-cloud
Repo: jdf2e/nutui-react PR: 2700
File: src/packages/animatingnumbers/animatingnumbers.harmony.css:25-32
Timestamp: 2024-11-06T05:56:06.800Z
Learning: 在优化 NutUI React 动画性能时,添加 `will-change` 属性可能会对布局产生影响,需要谨慎使用。
📚 Learning: 2025-05-02T01:45:09.576Z
Learnt from: irisSong
Repo: jdf2e/nutui-react PR: 3209
File: src/packages/searchbar/searchbar.taro.tsx:111-124
Timestamp: 2025-05-02T01:45:09.576Z
Learning: 在 React/Taro 组件中使用 setTimeout 或 setInterval 时,应当在组件卸载时通过 useEffect 的清理函数清除定时器,以防止内存泄漏。可以使用 useState 存储定时器 ID,并在 useEffect 的返回函数中清除。

Applied to files:

  • src/packages/countdown/demos/h5/demo10.tsx
  • src/packages/countdown/countdown.tsx
  • src/packages/navbar/demos/taro/demo4.tsx
  • src/packages/countdown/countdown.taro.tsx
  • src/packages/countdown/demos/taro/demo10.tsx
📚 Learning: 2024-11-06T05:56:06.800Z
Learnt from: oasis-cloud
Repo: jdf2e/nutui-react PR: 2700
File: src/packages/animatingnumbers/animatingnumbers.harmony.css:25-32
Timestamp: 2024-11-06T05:56:06.800Z
Learning: 在优化 NutUI React 动画性能时,添加 `will-change` 属性可能会对布局产生影响,需要谨慎使用。

Applied to files:

  • src/packages/noticebar/demos/h5/demo12.tsx
🧬 Code graph analysis (25)
src/packages/countdown/demos/h5/demo10.tsx (2)
src/packages/countdown/countdown.taro.tsx (1)
  • CountDown (359-361)
src/packages/countdown/countdown.tsx (1)
  • CountDown (312-314)
src/packages/tabs/demos/taro/demo24.tsx (2)
src/packages/tabs/tabs.taro.tsx (1)
  • Tabs (27-319)
src/packages/tabs/tabs.tsx (1)
  • Tabs (24-234)
src/packages/inputnumber/demos/h5/demo11.tsx (2)
src/packages/inputnumber/inputnumber.taro.tsx (1)
  • InputNumber (26-256)
src/packages/inputnumber/inputnumber.tsx (1)
  • InputNumber (25-253)
src/types/spec/swiper/taro.ts (1)
src/types/spec/swiper/base.ts (1)
  • BaseSwiperItem (33-35)
src/packages/image/demos/h5/demo9.tsx (2)
src/packages/image/image.taro.tsx (1)
  • Image (25-141)
src/packages/image/image.tsx (1)
  • Image (27-204)
src/packages/switch/demos/h5/demo9.tsx (2)
src/packages/switch/switch.taro.tsx (1)
  • Switch (19-135)
src/packages/switch/switch.tsx (1)
  • Switch (18-136)
src/packages/tabbar/demos/h5/demo10.tsx (2)
src/packages/tabbar/tabbar.taro.tsx (1)
  • Tabbar (23-101)
src/packages/tabbar/tabbar.tsx (1)
  • Tabbar (22-99)
src/packages/noticebar/demos/h5/demo12.tsx (2)
src/packages/noticebar/noticebar.tsx (1)
  • NoticeBar (35-554)
src/packages/button/button.taro.tsx (1)
  • Button (64-185)
src/packages/tabbar/demos/taro/demo10.tsx (2)
src/packages/tabbar/tabbar.taro.tsx (1)
  • Tabbar (23-101)
src/packages/tabbar/tabbar.tsx (1)
  • Tabbar (22-99)
src/packages/navbar/demo.tsx (1)
scripts/create-properties.js (1)
  • h2 (58-58)
src/packages/image/demo.tsx (1)
scripts/create-properties.js (1)
  • h2 (58-58)
src/packages/swiper/demos/taro/demo10.tsx (3)
src/types/spec/swiper/taro.ts (1)
  • TaroSwiperProps (8-24)
src/packages/swiper/swiper.taro.tsx (1)
  • Swiper (32-181)
src/packages/image/image.taro.tsx (1)
  • Image (25-141)
src/packages/switch/demos/taro/demo9.tsx (1)
src/packages/switch/switch.taro.tsx (1)
  • Switch (19-135)
src/packages/image/demos/taro/demo9.tsx (1)
src/packages/image/image.taro.tsx (1)
  • Image (25-141)
src/packages/tabs/demos/h5/demo24.tsx (2)
src/packages/tabs/tabs.taro.tsx (1)
  • Tabs (27-319)
src/packages/tabs/tabs.tsx (1)
  • Tabs (24-234)
src/packages/inputnumber/demos/taro/demo11.tsx (1)
src/packages/inputnumber/inputnumber.taro.tsx (1)
  • InputNumber (26-256)
src/packages/navbar/demos/taro/demo4.tsx (4)
src/utils/taro/px-transform.ts (1)
  • pxTransform (5-9)
src/packages/navbar/navbar.taro.tsx (1)
  • NavBar (19-138)
src/utils/taro/platform.ts (1)
  • harmony (3-7)
src/packages/navbar/demos/taro/demo1.tsx (1)
  • NavBar (6-72)
src/packages/tabs/demos/h5/demo25.tsx (2)
src/packages/tabs/tabs.taro.tsx (1)
  • Tabs (27-319)
src/packages/tabs/tabs.tsx (1)
  • Tabs (24-234)
src/packages/tabbar/demo.tsx (1)
scripts/create-properties.js (1)
  • h2 (58-58)
src/packages/navbar/demos/h5/demo4.tsx (4)
src/packages/navbar/navbar.taro.tsx (1)
  • NavBar (19-138)
src/packages/navbar/navbar.tsx (1)
  • NavBar (18-137)
src/packages/toast/toast.taro.tsx (1)
  • Toast (39-197)
src/packages/navbar/demos/h5/demo2.tsx (1)
  • NavBar (5-71)
src/packages/swiper/effects/focus.tsx (1)
src/hooks/use-ref-state.ts (1)
  • getRefValue (3-5)
src/packages/countdown/demos/taro/demo10.tsx (2)
src/packages/countdown/countdown.taro.tsx (1)
  • CountDown (359-361)
src/packages/countdown/countdown.tsx (1)
  • CountDown (312-314)
src/packages/countdown/demo.tsx (1)
scripts/create-properties.js (1)
  • h2 (58-58)
src/packages/noticebar/demos/taro/demo12.tsx (2)
src/packages/noticebar/noticebar.taro.tsx (1)
  • NoticeBar (37-578)
src/packages/button/button.taro.tsx (1)
  • Button (64-185)
src/packages/tabs/demos/taro/demo25.tsx (2)
src/packages/tabs/tabs.taro.tsx (1)
  • Tabs (27-319)
src/packages/tabs/tabs.tsx (1)
  • Tabs (24-234)
🪛 ast-grep (0.39.9)
src/packages/countdown/countdown.tsx

[warning] 302-302: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 ESLint
src/packages/steps/steps.taro.tsx

[error] 42-43: Delete ⏎·······

(prettier/prettier)

src/packages/steps/steps.tsx

[error] 42-43: Delete ⏎·······

(prettier/prettier)

🔇 Additional comments (66)
src/packages/toast/Notification.tsx (1)

137-137: LGTM!正确使用 role="alert" 提升通知组件的无障碍性

为 Notification 内容容器添加 role="alert" 是符合 ARIA 最佳实践的,可以确保屏幕阅读器即时播报重要的通知信息。该角色隐式设置了 aria-live="assertive"aria-atomic="true",无需额外声明。

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

181-181: Taro View 组件的 ariaRole 属性验证已完成

代码实现正确。在代码库中,ariaRole 是 Taro View 组件的标准 ARIA 属性命名约定(对应 HTML 的 role 属性)。为 Toast 容器添加 ariaRole="alert" 符合无障碍规范,使屏幕阅读器能够即时播报提示信息。该做法与代码库中其他组件(tabs、switch、radio、button 等)的实践一致。

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

144-144: 修正传递给 Taro Textarea 组件的不受支持的属性

根据代码验证,showCount 属性在第 144 行被传递给 Taro Textarea 组件,但在 src/types/spec/textarea/taro.ts 中的类型定义明确将其从 TextareaProps 中排除:

export type TaroTextAreaProps = Omit<BaseTextArea, 'style'> &
  Omit<TextareaProps, 'showCount' | 'onFocus' | 'onBlur'>

这意味着 Taro 的原生 Textarea 组件不支持 showCount 属性。该属性不应传递给 Taro 组件。第 146-154 行中已实现的自定义计数显示是实际的字符计数器实现。

建议:删除第 144 行的 showCount={showCount} 属性,因为它会被 Taro Textarea 组件忽略或导致类型错误。

Likely an incorrect or invalid review comment.

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

173-179: 无障碍功能增强实现得很好!

为组件添加 ariaLabel 属性能够让屏幕阅读器准确读取价格信息。实现细节值得肯定:

  • 正确使用 replaceSpecialChar 将 HTML 实体(如 &yen;)转换为实际字符
  • aria-label 的构建逻辑准确反映了视觉渲染内容(符号位置、千分位、小数部分)
  • 在 harmony 和非 harmony 分支中保持了一致的实现

Also applies to: 187-193

src/packages/price/price.tsx (2)

56-64: 新增的工具函数实现简洁清晰!

replaceSpecialChar 函数使用链式调用的方式转换 HTML 实体,比 Taro 版本中的多次赋值方式更加简洁易读。该函数确保了 aria-label 中的符号(如 ¥)能被屏幕阅读器正确识别。


127-133: 无障碍支持实现准确且完整!

为根元素添加 aria-label 属性,让屏幕阅读器能够获取格式化后的价格信息。实现要点:

  • 正确处理符号位置(前置/后置)
  • 包含千分位格式化、小数点和小数部分
  • 与视觉渲染逻辑保持一致
  • 与 Taro 版本实现保持统一

这是一个优秀的无障碍功能增强!

src/packages/tabbar/demos/h5/demo10.tsx (1)

1-40: LGTM!

这个演示很好地展示了 Tabbar 组件的无障碍功能:

  • 为 Tabbar 容器添加了 ariaLabel="底部导航"
  • 每个 TabbarItem 都正确设置了 ariaLabelariaRoledescription
  • 代码结构清晰,易于理解
src/types/spec/switch/base.ts (1)

14-14: LGTM!

BaseSwitch 接口添加 ariaLabel 属性是正确的,与其他组件的无障碍类型定义保持一致。

src/packages/checkbox/checkbox.tsx (1)

212-214: LGTM!

复选框的无障碍实现非常完善:

  • 正确使用了 role="checkbox"
  • tabIndex 根据禁用状态正确设置(禁用时为 -1,启用时为 0)
  • aria-checked 正确处理了三种状态:选中、未选中和不确定('mixed'

这是一个标准的无障碍复选框实现。

src/packages/swiper/demos/taro/demo10.tsx (1)

1-40: LGTM!

这个演示很好地展示了 Swiper 组件的无障碍功能:

  • 为每个 Swiper.Item 正确设置了 ariaLabel,使用数组中的对应标签
  • onChange 处理函数使用了正确的类型注解
  • 代码结构清晰,易于理解和维护
src/packages/navbar/navbar.taro.tsx (1)

110-110: navigation 角色添加正确

为导航栏容器添加 ariaRole="navigation" 符合无障碍最佳实践,有助于屏幕阅读器用户理解页面结构。

src/packages/radio/radio.tsx (1)

133-142: 单选按钮无障碍属性添加正确

为单选按钮添加的无障碍属性符合 ARIA 最佳实践:

  • role="radio" 明确语义
  • tabIndex={disabled ? -1 : 0} 正确处理禁用状态下的键盘导航
  • aria-checkedaria-disabled 准确反映组件状态
src/packages/tabs/tabs.tsx (2)

161-161: tablist 角色添加正确

为 Tabs 容器添加 role="tablist" 符合 ARIA 规范,有助于屏幕阅读器正确识别选项卡组件结构。


185-195: 装饰性元素正确标记为隐藏

为 line 和 smile 指示器添加 aria-hidden 属性是正确的做法,这些纯装饰性元素不应该暴露给辅助技术。

src/types/spec/tabbar/base.ts (1)

13-13: Tabbar 类型定义符合最佳实践

BaseTabbarBaseTabbarItem 添加的可选无障碍属性设计合理:

  • 属性均为可选,支持渐进式采用
  • 类型定义清晰,便于 TypeScript 类型检查

Also applies to: 26-28

src/packages/tabs/demos/taro/demo24.tsx (1)

1-27: 示例代码实现正确

该 Taro 示例正确演示了受控 Tabs 组件的用法,包括禁用状态的处理。代码清晰易懂,适合作为文档示例。

src/packages/tabs/demos/h5/demo24.tsx (1)

1-27: H5 示例代码实现正确

该 H5 示例正确演示了受控 Tabs 组件的用法。代码清晰易懂,适合作为文档示例。

注意:此 H5 示例与 Taro 版本(src/packages/tabs/demos/taro/demo24.tsx)略有不同,Taro 版本包含一个禁用的选项卡,而 H5 版本没有。如果这是有意为之以展示不同场景,则无需调整。

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

34-36: LGTM! 交互元素的 ARIA 标签使用得当

为图标按钮添加 aria-label="Share" 有助于屏幕阅读器用户理解控件功能。

Also applies to: 43-45

src/packages/countdown/demo.tsx (1)

12-12: LGTM! Demo10 集成正确

新的 ARIA 演示组件已正确导入和渲染,与现有演示结构保持一致。

Also applies to: 75-76

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

157-165: LGTM! ARIA role 属性添加正确

由于使用 View 组件而非原生 Button 元素,添加 ariaRole="button" 对于辅助技术正确识别该组件至关重要。

src/types/spec/inputnumber/base.ts (1)

23-23: LGTM! 类型定义符合无障碍功能需求

ariaLabel 属性的添加与 PR 目标一致,为 InputNumber 组件提供无障碍支持。

src/types/spec/dialog/base.ts (1)

47-47: LGTM! Dialog role 属性添加合理

role 属性的添加为 Dialog 组件提供了 ARIA 角色支持。根据相关代码上下文,该属性会被传递为 ariaRole 到 Content 组件,符合无障碍功能架构设计。

src/packages/navbar/demo.taro.tsx (1)

10-46: 代码变更看起来不错!

Demo4 组件的导入和渲染符合现有的代码模式,翻译键也已正确添加到所有语言环境中。ARIA 基础用法示例的集成很清晰。

src/packages/dialog/content.tsx (1)

30-63: ARIA 属性添加正确!

rolearia-modal 属性添加到外层容器是正确的做法。aria-modal={visible} 准确地反映了对话框的模态状态,这符合 ARIA 最佳实践。

src/packages/dialog/dialog.tsx (1)

250-259: 对话框的 ARIA 实现符合标准!

在 Content 组件上设置 role="dialog" 以及在外层包装器上添加 aria-modal={visible} 都是正确的做法,完全符合 W3C ARIA 对话框模式的规范。

src/packages/swiper/effects/focus.tsx (1)

82-83: 轮播组件的无障碍实现很出色!

添加 tabIndex={0} 使幻灯片可以通过键盘导航,而 aria-hidden={!(index === getRefValue(current))} 确保只有当前显示的幻灯片对屏幕阅读器可见。这完全符合 ARIA 轮播组件的最佳实践。

src/packages/navbar/navbar.tsx (1)

109-109: 导航栏的语义化角色正确!

为导航栏包装器添加 role="navigation" 是完全正确的做法,这有助于屏幕阅读器用户理解这是一个导航区域。

src/packages/inputnumber/demo.taro.tsx (1)

17-96: Demo 集成清晰规范!

Demo11 的导入和渲染遵循了现有的模式,翻译键也已添加到所有语言环境中。ARIA 支持示例的集成很规范。

src/packages/countdown/demo.taro.tsx (1)

15-84: ARIA 演示集成完整!

Demo10 的导入和使用与其他演示文件保持一致,翻译键已正确添加。倒计时组件的 ARIA 基础用法演示集成得很好。

src/types/spec/countdown/base.ts (1)

30-30: 代码无误!

为倒计时组件添加可选的 ariaLabel 属性是合理的,这将提高组件的可访问性支持。

src/packages/navbar/demo.tsx (1)

7-7: 代码实现正确!

新增的 Demo4 和 ARIA 相关翻译集成良好,遵循了现有的代码模式。翻译在三个语言环境中保持一致。

Also applies to: 15-15, 21-21, 27-27, 39-40

src/types/spec/dialog/taro.ts (1)

6-7: 代码实现正确!

添加 ariaModalariaRole 属性符合 Taro 框架的 ARIA 属性命名约定(使用驼峰式而非短横线分隔)。根据 AI 摘要,这些属性的默认值在组件实现中提供,因此非可选的类型定义是合理的。

src/packages/inputnumber/demo.tsx (1)

13-13: 代码实现正确!

Demo11 的集成和翻译添加遵循了现有模式。虽然英文翻译 "support ARIA" 使用了小写,但这与文件中其他一些标题的风格保持一致(如 "support formatter"),因此是可接受的。

Also applies to: 31-31, 47-47, 63-63, 90-91

src/packages/tabs/demo.tsx (1)

26-27: 代码实现正确!

新增的 Demo24 和 Demo25 集成良好,遵循现有代码结构。翻译在两个语言环境中均已添加。注意英文翻译中 "support ARIA" 使用小写是为了与现有的 title8 "Custom Tab Bar" 样式保持一致。

Also applies to: 54-55, 80-81, 134-137

src/types/spec/image/base.ts (1)

26-26: 代码实现正确!

为 Image 组件添加可选的 ariaLabel 属性是合理的,这与 PR 中其他组件的无障碍功能增强保持一致。

src/packages/image/image.tsx (1)

45-45: LGTM! ARIA 标签实现正确。

为 Image 组件添加 ariaLabel 属性并正确应用到 img 元素的 aria-label 上,可以为辅助技术提供更详细的图片描述信息。实现符合 ARIA 最佳实践。

Also applies to: 198-198

src/packages/tabs/tabs.taro.tsx (2)

248-248: ARIA 角色设置正确。

为标签页列表容器添加了 role="tablist"ariaRole="tablist",正确标识了标签列表的语义角色。在 Taro 环境中同时设置这两个属性可以确保跨平台兼容性。


289-294: 标签项的 ARIA 属性实现良好。

每个标签项正确设置了 ariaRole="tab"ariaSelectedariaDisabled 属性,能够准确反映标签的激活状态和禁用状态,有助于屏幕阅读器正确理解标签页的交互状态。

src/packages/noticebar/demo.taro.tsx (1)

19-19: LGTM! Demo12 集成正确。

成功为 NoticeBar 组件添加了 ARIA 支持的示例,翻译准确,集成方式与其他演示保持一致。

Also applies to: 88-89

src/packages/inputnumber/demos/h5/demo11.tsx (1)

12-12: LGTM! ARIA 标签使用得当。

为 InputNumber 组件添加了清晰的 ariaLabel 属性,准确描述了输入框的用途("商品数量输入框" 和 "金额输入框"),有助于屏幕阅读器用户理解输入控件的功能。

Also applies to: 24-24

src/packages/countdown/demos/h5/demo10.tsx (1)

5-7: 演示代码使用 useRef 存储静态倒计时结束时间,符合预期。

对于演示目的,使用 useRef 存储固定的结束时间是合理的,避免了不必要的重渲染。

src/packages/swiper/demo.taro.tsx (1)

17-17: ARIA 演示集成正确。

新增的 Demo10 组件和对应的翻译键已正确集成到演示页面中,代码结构清晰。

Also applies to: 31-31, 43-43, 84-85

src/packages/tabbar/demo.taro.tsx (1)

15-15: ARIA 演示集成正确。

新增的 Demo10 组件和多语言翻译键已正确添加,支持简体中文、繁体中文和英文。

Also applies to: 29-29, 41-41, 53-53, 78-79

src/packages/countdown/demos/taro/demo10.tsx (1)

5-7: 演示代码使用 useRef 存储静态倒计时结束时间,符合预期。

对于演示目的,使用 useRef 存储固定的结束时间是合理的,避免了不必要的重渲染。

src/packages/tabbar/tabbar.tsx (1)

19-19: 无障碍属性实现正确。

为 Tabbar 组件添加的 ARIA 属性符合 Web 无障碍标准:

  • aria-label 允许自定义标签
  • role="tablist" 正确标识标签栏的语义角色

Also applies to: 37-37, 74-76

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

38-38: ariaLabel 属性正确传递给底层图片组件。

在 Taro 环境中正确地将无障碍标签传递给原生图片组件。

Also applies to: 133-133

src/packages/tabbar/demo.tsx (1)

12-12: ARIA 演示集成正确。

新增的 Demo10 组件和多语言翻译键已正确添加到 H5 演示页面。

Also applies to: 26-26, 38-38, 50-50, 73-74

src/packages/tabs/demo.taro.tsx (1)

31-32: LGTM!

新增的 ARIA 演示代码结构清晰,国际化翻译完整。

Also applies to: 60-61, 87-88, 149-152

src/packages/image/demos/taro/demo9.tsx (1)

1-13: LGTM!

演示代码简洁明了,正确展示了 Image 组件的无障碍属性使用。

src/packages/inputnumber/demos/taro/demo11.tsx (1)

1-30: LGTM!

演示代码展示了 InputNumber 组件的无障碍属性使用,ariaLabel 描述准确且语义清晰。同时正确演示了 ConfigProvider 的主题定制功能。

src/types/spec/swiper/taro.ts (1)

22-23: LGTM!

ARIA 相关的类型定义正确,属性命名符合规范。

Also applies to: 29-29

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

85-95: ARIA 属性实现正确!

Switch 组件的无障碍属性实现符合 WAI-ARIA 规范:

  • ariaRole="switch" 正确标识了开关角色
  • ariaCheckedariaDisabled 准确反映了组件状态
  • tabIndex 根据禁用状态正确设置,确保键盘导航体验
src/packages/noticebar/noticebar.tsx (1)

460-462: 装饰性图标的无障碍处理正确!

为 leftIcon 添加 aria-hidden 属性是正确的做法,这确保了装饰性图标不会被屏幕阅读器朗读,避免信息冗余。

Also applies to: 499-501

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

76-78: Tabbar ARIA 实现符合规范!

实现符合 WAI-ARIA 的标签栏模式:

  • 根容器的 ariaLabel 用于标识整个标签栏
  • 包装器的 ariaRole="tablist" 正确标识了标签列表的语义角色

与 TabbarItem 中的 role="tab"aria-selected 配合使用,构成完整的无障碍标签栏。

src/packages/noticebar/demos/h5/demo12.tsx (1)

1-44: Demo 示例清晰展示了无障碍功能!

该演示文件很好地展示了 NoticeBar 组件的新增无障碍特性:

  • 基础用法
  • 带自定义图标和 rightIconAriaLabel 的用法
  • 与其他组件(Button)的集成

示例代码结构清晰,有助于开发者理解如何使用这些新的 ARIA 属性。

src/packages/tabbaritem/tabbaritem.tsx (1)

122-126: TabbarItem 无障碍实现优秀!

标签项的 ARIA 属性实现遵循了 WAI-ARIA 最佳实践:

  • role="tab" 明确标识标签角色
  • tabIndex 实现了漫游式焦点管理(roving tabindex),仅活动标签可聚焦
  • aria-selected 准确反映选中状态
  • aria-labelaria-roledescription 提供了灵活的自定义标签能力

与父组件 Tabbar 的 role="tablist" 配合,构成完整的标签页无障碍结构。

src/packages/noticebar/demos/taro/demo12.tsx (1)

1-63: Taro Demo 全面展示了组件能力!

该 Taro 演示文件全面展示了 NoticeBar 在小程序环境中的无障碍功能:

  • 水平和垂直两种滚动模式
  • rightIconAriaLabel 在不同场景下的使用
  • 与事件处理器(onItemClick)的配合
  • 自定义图标的 ARIA 标签应用

演示覆盖面广,有助于 Taro 开发者理解组件的完整功能。

src/packages/countdown/countdown.tsx (1)

66-69: ARIA alert 模式实现合理!

倒计时结束时使用 role="alert" 向屏幕阅读器发送通知是正确的无障碍实践。通过 state 管理 alert 内容并在 3 秒后清除,避免了持续性的干扰。

定时器清理也已在 componentWillUnmount 中妥善处理(Lines 286-288),防止内存泄漏。

Also applies to: 107-112

src/packages/countdown/countdown.taro.tsx (3)

69-72: ARIA 状态管理实现正确

引入的 role、alertContent 状态和 alertTimerRef 用于管理 ARIA 提示,实现思路清晰。


273-275: 定时器清理实现正确

在组件卸载时正确清理了 alert 定时器,避免了内存泄漏问题。根据学习记录,这是 React/Taro 组件中使用定时器的最佳实践。

Based on learnings


346-352: ARIA 无障碍实现符合标准

通过 ariaLabel 和隐藏的 alert 区域实现屏幕阅读器提示,这是标准的无障碍访问模式。隐藏元素使用 display: 'none' 是合适的做法。

src/packages/inputnumber/inputnumber.taro.tsx (2)

197-209: ARIA 属性正确绑定到减少按钮

ariaRole、ariaLabel 和 ariaDisabled 属性正确反映了按钮的状态,增强了无障碍访问性。禁用状态的判断逻辑与图标样式保持一致。


236-242: ARIA 属性正确绑定到增加按钮

与减少按钮类似,增加按钮的无障碍属性实现正确,禁用状态判断符合预期。

src/packages/dialog/dialog.taro.tsx (3)

48-48: 角色属性默认值合理

role 属性的默认值 'dialog' 符合 ARIA 规范,正确标识了对话框的语义角色。


288-288: 角色属性正确传递到 Content 组件

role 属性通过 ariaRole 正确传递给 Content 组件,保持了 ARIA 语义的连贯性。


301-301: ariaModal 绑定正确

ariaModal 属性绑定到 visible 状态是正确的实现,符合 ARIA 模态对话框的规范要求。

Comment on lines 302 to 305
{...rest}
dangerouslySetInnerHTML={{
__html: `${renderTime}`,
__html: `${renderTime}<span style="display:none" role=${role}>${alertContent}</span>`,
}}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

存在 XSS 安全风险,建议重构

使用 dangerouslySetInnerHTML 注入包含用户提供的 ariaLabel 的内容存在跨站脚本攻击(XSS)风险。虽然 ariaLabel 有默认值,但如果开发者传入了包含恶意脚本的字符串,这些脚本会被执行。

建议采用更安全的实现方式:

方案一:使用独立的 React 元素(推荐)

-<div
-  className={`${classPrefix} ${className}`}
-  style={{ ...style }}
-  aria-label={ariaLabel}
-  {...rest}
-  dangerouslySetInnerHTML={{
-    __html: `${renderTime}<span style="display:none" role=${role}>${alertContent}</span>`,
-  }}
-/>
+<div
+  className={`${classPrefix} ${className}`}
+  style={{ ...style }}
+  aria-label={ariaLabel}
+  {...rest}
+>
+  <span dangerouslySetInnerHTML={{ __html: renderTime }} />
+  <span style={{ display: 'none' }} role={role}>
+    {alertContent}
+  </span>
+</div>

方案二:如果必须使用 HTML,进行内容转义
使用 DOMPurify 或类似库对 ariaLabel 进行清理:

import DOMPurify from 'dompurify'
const sanitizedLabel = DOMPurify.sanitize(ariaLabel)

基于静态分析工具的提示。

🧰 Tools
🪛 ast-grep (0.39.9)

[warning] 302-302: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🤖 Prompt for AI Agents
In src/packages/countdown/countdown.tsx around lines 302 to 305, the code uses
dangerouslySetInnerHTML to inject renderTime plus an ariaLabel-derived string,
which creates an XSS risk; replace this with safe React elements: render the
rendered time as normal children and add a separate span element (visually
hidden if needed) with the appropriate role and the ariaLabel/alertContent as
plain text child so React escapes it automatically; if you absolutely must
inject HTML instead, sanitize ariaLabel first with a library like DOMPurify and
use the sanitized result in dangerouslySetInnerHTML.

Comment on lines 27 to 41
return list.map((item) => (
<div
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
role="tab"
tabIndex={0}
aria-selected={tabvalue === item.paneKey}
>
{item.icon || null}
<span className="nut-tabs-titles-item-text">{item.title}</span>
<span className="nut-tabs-titles-item-line" />
</div>
))
}}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

添加 role="tablist" 容器以完善 ARIA 结构

自定义标题渲染器应该包含一个带有 role="tablist" 的外层容器,以符合 ARIA 规范。参考主组件实现(tabs.tsx 第142行)。

建议修改:

 title={() => {
-  return list.map((item) => (
+  return (
+    <div role="tablist">
+      {list.map((item) => (
+        <div
+          onClick={() => setTabvalue(item.paneKey)}
+          className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
+          key={item.paneKey}
+          role="tab"
+          tabIndex={0}
+          aria-selected={tabvalue === item.paneKey}
+        >
+          {item.icon}
+          <span className="nut-tabs-titles-item-text">{item.title}</span>
+          <span className="nut-tabs-titles-item-line" />
+        </div>
+      ))}
+    </div>
+  )
-    <div
-      onClick={() => setTabvalue(item.paneKey)}
-      className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
-      key={item.paneKey}
-      role="tab"
-      tabIndex={0}
-      aria-selected={tabvalue === item.paneKey}
-    >
-      {item.icon || null}
-      <span className="nut-tabs-titles-item-text">{item.title}</span>
-      <span className="nut-tabs-titles-item-line" />
-    </div>
-  ))
 }}
📝 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
return list.map((item) => (
<div
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
role="tab"
tabIndex={0}
aria-selected={tabvalue === item.paneKey}
>
{item.icon || null}
<span className="nut-tabs-titles-item-text">{item.title}</span>
<span className="nut-tabs-titles-item-line" />
</div>
))
}}
return (
<div role="tablist">
{list.map((item) => (
<div
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
role="tab"
tabIndex={0}
aria-selected={tabvalue === item.paneKey}
>
{item.icon}
<span className="nut-tabs-titles-item-text">{item.title}</span>
<span className="nut-tabs-titles-item-line" />
</div>
))}
</div>
)
🤖 Prompt for AI Agents
In src/packages/tabs/demos/h5/demo25.tsx around lines 27 to 41, the custom title
renderer returns tab elements without the required ARIA container; wrap the
mapped items in an outer element that has role="tablist" (and optionally
aria-orientation if relevant) so the tabs form a proper ARIA tablist; implement
by returning a single container element with role="tablist" that contains the
existing map output (keep existing per-tab role="tab" attributes unchanged).

Comment on lines +28 to +41
return list.map((item) => (
<View
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
ariaRole="tab"
tabIndex={0}
ariaSelected={tabvalue === item.paneKey}
>
{item.icon}
<Text className="nut-tabs-titles-item-text">{item.title}</Text>
<Text className="nut-tabs-titles-item-line" />
</View>
))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

验证自定义标题渲染器的完整ARIA结构

当使用自定义 title 渲染器时,确保在外层包装一个带有 role="tablist" 的容器。参考主组件实现(tabs.taro.tsx 第265行),标准的标签列表应该包裹在 <View className="nut-tabs-list" role="tablist" ariaRole="tablist"> 中。

建议修改:

 title={() => {
-  return list.map((item) => (
+  return (
+    <View role="tablist" ariaRole="tablist">
+      {list.map((item) => (
+        <View
+          onClick={() => setTabvalue(item.paneKey)}
+          className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
+          key={item.paneKey}
+          ariaRole="tab"
+          tabIndex={0}
+          ariaSelected={tabvalue === item.paneKey}
+        >
+          {item.icon}
+          <Text className="nut-tabs-titles-item-text">{item.title}</Text>
+          <Text className="nut-tabs-titles-item-line" />
+        </View>
+      ))}
+    </View>
+  )
-    <View
-      onClick={() => setTabvalue(item.paneKey)}
-      className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
-      key={item.paneKey}
-      ariaRole="tab"
-      tabIndex={0}
-      ariaSelected={tabvalue === item.paneKey}
-    >
-      {item.icon}
-      <Text className="nut-tabs-titles-item-text">{item.title}</Text>
-      <Text className="nut-tabs-titles-item-line" />
-    </View>
-  ))
 }}
📝 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
return list.map((item) => (
<View
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
ariaRole="tab"
tabIndex={0}
ariaSelected={tabvalue === item.paneKey}
>
{item.icon}
<Text className="nut-tabs-titles-item-text">{item.title}</Text>
<Text className="nut-tabs-titles-item-line" />
</View>
))
return (
<View role="tablist" ariaRole="tablist">
{list.map((item) => (
<View
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
ariaRole="tab"
tabIndex={0}
ariaSelected={tabvalue === item.paneKey}
>
{item.icon}
<Text className="nut-tabs-titles-item-text">{item.title}</Text>
<Text className="nut-tabs-titles-item-line" />
</View>
))}
</View>
)
🤖 Prompt for AI Agents
In src/packages/tabs/demos/taro/demo25.tsx around lines 28 to 41, the custom
title renderer returns individual tab items but does not ensure they are wrapped
by a container with role="tablist"; update the demo so the rendered tab items
are enclosed in a parent View with className="nut-tabs-list" and
role/ariaRole="tablist" (matching the main component at tabs.taro.tsx line
~265), keeping each child View as role="tab" and preserving existing
ariaSelected/tabIndex attributes.

Copy link

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/packages/button/button.taro.tsx (1)

158-167: 添加键盘事件处理和焦点管理以满足可访问性要求

组件使用 View 替代语义化 button 元素后,虽然添加了 ariaRole="button",但这仅改变屏幕阅读器的公告,不会自动添加键盘行为。当前实现缺少键盘交互支持:

  • 缺少 tabIndex 属性,使元素无法通过键盘获得焦点
  • 缺少 onKeyDown 事件处理,无法通过 Enter 和 Space 键触发点击

修复方案 - 在 src/packages/button/button.taro.tsx 第 158-167 行的 View 组件中添加:

  1. tabIndex={0} - 使元素可获得焦点
  2. onKeyDown 处理器 - 监听 Enter 和 Space 键,调用 handleClick,并对 Space 键调用 preventDefault() 防止页面滚动

参考实现:对于 Enter 或 Space 键,验证 e.key === 'Enter'e.key === ' ',调用 handleClick() 前对 Space 键调用 preventDefault()

🧹 Nitpick comments (7)
src/packages/switch/demos/taro/demo9.tsx (1)

4-10: 建议使用更具描述性的无障碍标签

Demo 正确地展示了 ariaLabel 属性的使用。不过,当前的标签 "开关按钮" 仅描述了组件类型,而无障碍标签的最佳实践是描述开关所控制的具体功能。建议使用更具体的功能描述,例如 "启用通知" 或 "深色模式切换",这样能更好地向使用辅助技术的用户传达开关的用途。

示例修改:

-      <Switch defaultChecked ariaLabel="开关按钮" />
+      <Switch defaultChecked ariaLabel="启用通知" />

或使用其他具体的功能描述。

src/types/spec/dialog/h5.ts (1)

5-6: 建议将 ARIA 属性设为可选。

这两个属性目前被定义为必需属性,但在实现中通过 defaultProps 提供了默认值。建议将它们改为可选属性以提供更好的开发体验,并与实际使用方式保持一致。

应用此修改:

 export interface WebContentProps extends BaseContentProps {
   onClick: (event: MouseEvent) => void
-  ariaRole: string
-  ariaLabel: string
+  ariaRole?: string
+  ariaLabel?: string
 }
src/packages/overlay/overlay.tsx (1)

83-84: ARIA 属性逻辑正确,建议增强键盘无障碍支持。

条件化的 aria-labelaria-hidden 实现合理:

  • 当蒙层可交互(closeOnOverlayClick=true)时,暴露给辅助技术
  • 当蒙层不可交互时,对辅助技术隐藏

建议考虑以下增强:

  1. 为可交互的蒙层添加 role="button" 属性,明确其交互性质
  2. 添加键盘支持:当 closeOnOverlayClick=true 时,设置 tabIndex={0} 并处理 onKeyDown 事件(Enter/Space 键)以支持键盘关闭

示例实现:

+      const handleKeyDown = (e: React.KeyboardEvent) => {
+        if (closeOnOverlayClick && (e.key === 'Enter' || e.key === ' ')) {
+          e.preventDefault()
+          onClick && onClick(e as any)
+        }
+      }
+
       <div
         ref={nodeRef}
         className={classes}
         style={styles}
         {...rest}
         onClick={handleClick}
+        onKeyDown={handleKeyDown}
+        role={closeOnOverlayClick ? 'button' : undefined}
+        tabIndex={closeOnOverlayClick ? 0 : undefined}
         aria-label={closeOnOverlayClick ? ariaLabel : ''}
         aria-hidden={!closeOnOverlayClick}
       >

请验证此模式是否与 PR 中其他组件的无障碍实现保持一致。

src/packages/tabs/demos/h5/demo25.tsx (1)

36-36: 可选优化:简化图标渲染逻辑

item.icon || null 中的 || null 是多余的,因为如果 item.icon 为假值,React 会自动忽略它。

可以简化为:

-              {item.icon || null}
+              {item.icon}
src/packages/button/button.taro.tsx (2)

86-86: 可选:内联 role 常量

role 常量仅使用一次,可以直接在第 165 行内联为 ariaRole="button" 以简化代码。

-    const role = 'button'
     const getStyle = useMemo(() => {

并在第 165 行:

-        ariaRole={role}
+        ariaRole="button"

165-166: 良好的无障碍支持,建议补充 ARIA 属性接口

添加 ariaRoleariaDisabled 属性很好地提升了组件的可访问性。

不过,建议在 ButtonProps 接口中暴露额外的 ARIA 相关属性(如 ariaLabelariaLabelledByariaDescribedBy 等),以便使用者能够为特定场景提供自定义的无障碍标签。例如,仅包含图标的按钮需要 ariaLabel 来为屏幕阅读器提供文本描述。

可在 ButtonProps 接口中添加:

export interface ButtonProps extends BasicComponent {
  color: string
  shape: ButtonShape
  type: ButtonType
  size: ButtonSize
  fill: ButtonFill
  block: boolean
  loading: boolean
  disabled: boolean
  icon: React.ReactNode
  rightIcon: React.ReactNode
  nativeType: 'submit' | 'reset' // | 'button'
  onClick: (e: MouseEvent<HTMLButtonElement>) => void
  ariaLabel?: string
  ariaLabelledBy?: string
  ariaDescribedBy?: string
}

然后在组件中透传这些属性到 View:

  (props, ref) => {
    const {
      color,
      shape,
      fill,
      loading,
      disabled,
      type,
      size,
      block,
      icon,
      rightIcon,
      children,
      className,
      style,
      // formType,
      nativeType,
      onClick,
+     ariaLabel,
+     ariaLabelledBy,
+     ariaDescribedBy,
      ...rest
    } = { ...defaultProps, ...props }
      <View
        {...rest}
        ref={ref}
        // formType={formType || nativeType}
        className={buttonClassNames}
        style={{ ...getStyle, ...style }}
        onClick={(e) => handleClick(e as any)}
        ariaRole={role}
        ariaDisabled={disabled}
+       ariaLabel={ariaLabel}
+       ariaLabelledBy={ariaLabelledBy}
+       ariaDescribedBy={ariaDescribedBy}
      >
src/packages/countdown/countdown.taro.tsx (1)

113-117: 重复触发结束前需先清掉旧定时器

当前在 setAlertContent 之后直接覆盖 alertTimerRef.current,没有先 clearTimeout。如果倒计时在 3 秒内被重新拉起并再次结束,第一次留下的 handle 仍会按原定时清空,导致第二次的提示被过早清除,甚至和新的提示互相抢占。建议在设置新定时器前先判空并调用 clearTimeout,再写入最新 handle,确保提示生存周期稳定。

-          setAlertContent(`${ariaLabel}倒计时结束`)
-          alertTimerRef.current = setTimeout(() => {
+          setAlertContent(`${ariaLabel}倒计时结束`)
+          if (alertTimerRef.current) {
+            clearTimeout(alertTimerRef.current)
+          }
+          alertTimerRef.current = setTimeout(() => {
             setRole('')
             setAlertContent('')
           }, 3000)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4f1ed9 and 66092f4.

📒 Files selected for processing (28)
  • src/packages/button/button.taro.tsx (2 hunks)
  • src/packages/checkbox/checkbox.taro.tsx (1 hunks)
  • src/packages/countdown/countdown.taro.tsx (6 hunks)
  • src/packages/countdown/demo.taro.tsx (5 hunks)
  • src/packages/countdown/demo.tsx (5 hunks)
  • src/packages/dialog/content.taro.tsx (2 hunks)
  • src/packages/dialog/content.tsx (2 hunks)
  • src/packages/dialog/dialog.taro.tsx (5 hunks)
  • src/packages/dialog/dialog.tsx (4 hunks)
  • src/packages/image/demo.taro.tsx (4 hunks)
  • src/packages/image/demo.tsx (4 hunks)
  • src/packages/image/demos/h5/demo9.tsx (1 hunks)
  • src/packages/inputnumber/inputnumber.taro.tsx (6 hunks)
  • src/packages/overlay/overlay.taro.tsx (3 hunks)
  • src/packages/overlay/overlay.tsx (3 hunks)
  • src/packages/progress/progress.taro.tsx (1 hunks)
  • src/packages/progress/progress.tsx (1 hunks)
  • src/packages/switch/demo.taro.tsx (5 hunks)
  • src/packages/switch/demo.tsx (5 hunks)
  • src/packages/switch/demos/h5/demo9.tsx (1 hunks)
  • src/packages/switch/demos/taro/demo9.tsx (1 hunks)
  • src/packages/tabbaritem/tabbaritem.taro.tsx (2 hunks)
  • src/packages/tabs/demos/h5/demo25.tsx (1 hunks)
  • src/packages/tabs/tabs.tsx (3 hunks)
  • src/types/spec/dialog/base.ts (1 hunks)
  • src/types/spec/dialog/h5.ts (1 hunks)
  • src/types/spec/dialog/taro.ts (1 hunks)
  • src/types/spec/overlay/base.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (15)
  • src/packages/image/demo.tsx
  • src/packages/image/demos/h5/demo9.tsx
  • src/packages/switch/demo.tsx
  • src/packages/image/demo.taro.tsx
  • src/packages/inputnumber/inputnumber.taro.tsx
  • src/packages/switch/demos/h5/demo9.tsx
  • src/packages/progress/progress.tsx
  • src/packages/progress/progress.taro.tsx
  • src/packages/switch/demo.taro.tsx
  • src/types/spec/dialog/taro.ts
  • src/types/spec/dialog/base.ts
  • src/packages/dialog/content.taro.tsx
  • src/packages/countdown/demo.tsx
  • src/packages/checkbox/checkbox.taro.tsx
  • src/packages/dialog/content.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-11-06T02:16:07.069Z
Learnt from: oasis-cloud
Repo: jdf2e/nutui-react PR: 2540
File: src/packages/trendarrow/trendarrow.taro.tsx:29-30
Timestamp: 2024-11-06T02:16:07.069Z
Learning: 在`TrendArrow`组件(`src/packages/trendarrow/trendarrow.taro.tsx`)中,CSS变量在鸿蒙系统(Harmony OS)中可能未被完全支持,导致兼容性问题。因此,使用硬编码的十六进制颜色值可以确保在鸿蒙系统上的正确显示。

Applied to files:

  • src/packages/switch/demos/taro/demo9.tsx
📚 Learning: 2025-05-02T01:45:09.576Z
Learnt from: irisSong
Repo: jdf2e/nutui-react PR: 3209
File: src/packages/searchbar/searchbar.taro.tsx:111-124
Timestamp: 2025-05-02T01:45:09.576Z
Learning: 在 React/Taro 组件中使用 setTimeout 或 setInterval 时,应当在组件卸载时通过 useEffect 的清理函数清除定时器,以防止内存泄漏。可以使用 useState 存储定时器 ID,并在 useEffect 的返回函数中清除。

Applied to files:

  • src/packages/countdown/countdown.taro.tsx
📚 Learning: 2025-02-25T02:21:32.906Z
Learnt from: xiaoyatong
Repo: jdf2e/nutui-react PR: 2990
File: src/packages/pickerview/__test__/pickerview.spec.tsx:0-0
Timestamp: 2025-02-25T02:21:32.906Z
Learning: In React component tests, avoid using setTimeout with fixed delays. Instead, use act() to wrap async state updates and waitFor() for assertions, which makes tests more stable and reliable.

Applied to files:

  • src/packages/countdown/countdown.taro.tsx
🧬 Code graph analysis (4)
src/packages/tabs/demos/h5/demo25.tsx (2)
src/packages/tabs/tabs.tsx (1)
  • Tabs (24-238)
src/packages/tabs/tabs.taro.tsx (1)
  • Tabs (27-319)
src/packages/switch/demos/taro/demo9.tsx (1)
src/packages/switch/switch.taro.tsx (1)
  • Switch (19-135)
src/packages/dialog/dialog.tsx (1)
src/utils/merge-props.ts (1)
  • mergeProps (4-16)
src/packages/dialog/dialog.taro.tsx (2)
src/hooks/taro/use-custom-event.ts (1)
  • useParams (32-52)
src/utils/merge-props.ts (1)
  • mergeProps (4-16)
🔇 Additional comments (14)
src/packages/switch/demos/taro/demo9.tsx (1)

1-2: 导入语句正确

React 和组件的导入都是正确且必要的。

src/packages/tabbaritem/tabbaritem.taro.tsx (2)

39-40: 正确提取了无障碍属性

现在已从 props 中正确解构 ariaLabelariaRoledescription,解决了之前评审中指出的缺失问题。


134-138: 无障碍属性实现正确

所有之前评审中指出的问题均已修复:

  1. ✅ 第 136 行:tabIndex 使用了正确的驼峰式命名(之前是小写的 tabindex
  2. ✅ 第 137-138 行:ariaLabelariaRoledescription 现在已正确传递给 View 组件
  3. tabIndex={active ? 0 : -1} 实现了正确的 roving tabindex 模式,符合 ARIA 无障碍规范中 tab 组件的键盘导航最佳实践
  4. ariaRole="tab"ariaSelected={active} 的组合使用正确
src/packages/dialog/dialog.taro.tsx (2)

48-48: LGTM!

为 Dialog 添加默认的 ariaLabel 有助于辅助技术识别组件。


319-319: 无需修改 - Overlay 组件已支持 ariaLabel 属性

验证结果确认,Overlay 组件的类型定义 BaseOverlay 中已明确包含 ariaLabel?: string 属性(位置:src/types/spec/overlay/base.ts 第 13 行)。TaroOverlayProps 继承了该属性,因此 dialog.taro.tsx 第 319 行传递的 ariaLabel="背景蒙层" 完全符合 Overlay 组件的 Props 定义,无需修改。

src/packages/dialog/dialog.tsx (2)

46-46: LGTM!

为 Dialog 添加默认的 ariaLabel 有助于辅助技术识别组件。


259-260: LGTM!

正确地将 ARIA 属性传递给 Content 组件,使对话框容器具有适当的无障碍语义。

src/packages/tabs/tabs.tsx (2)

161-161: LGTM!

正确添加了 role="tablist" 到外层容器,符合 ARIA 选项卡组件的标准模式。


185-194: LGTM!

正确地为装饰性指示器元素添加了 aria-hidden 属性,使屏幕阅读器可以忽略这些纯视觉元素。

src/types/spec/overlay/base.ts (1)

13-13: LGTM!类型定义清晰。

为 BaseOverlay 接口添加可选的 ariaLabel 属性,支持无障碍功能,类型定义合理。

src/packages/overlay/overlay.tsx (1)

24-24: 默认值和属性传递正确。

ariaLabel 的默认值和属性解构处理得当,为蒙层组件提供了合理的无障碍标签。

Also applies to: 41-41

src/packages/overlay/overlay.taro.tsx (2)

18-18: 实现与 Web 版本保持一致。

Taro 版本的 ariaLabel 默认值和属性处理与 Web 版本一致,保证了跨平台的无障碍体验一致性。

Also applies to: 36-36


68-69: 无障碍属性实现正确,已验证 Taro 平台支持。

代码在第 68-69 行使用的 ariaLabelariaHidden 属性已符合 Taro 平台规范。Taro View 组件支持 ARIA 属性,包括 role、aria-label、aria-hidden 等标准 aria-* 属性。验证显示,项目中已在多个组件(navbar、tabs、tabpane、tabbar 等)成功使用 ariaRoleariaLabel 等属性,确认实现方式与现有代码一致。

无需调整,代码已遵循 Taro 无障碍最佳实践。

src/packages/tabs/demos/h5/demo25.tsx (1)

27-40: ARIA 结构验证确认无误,当前代码实现正确

验证结果表明:

  • 父组件 tabs.tsx 第 161 行确实设置了 role="tablist"(非第 142 行,但结构相同)
  • 自定义标题渲染器返回的元素被渲染在该容器内部
  • 每个标签项都具有正确的 role="tab"tabIndex={-1}aria-selected 属性
  • 当前结构符合 ARIA 规范,不应添加嵌套的 role="tablist"

代码实现已满足无障碍访问要求,无需修改。

Comment on lines +350 to +352
<View role={role} style={{ display: 'none' }}>
{alertContent}
</View>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

移除 display:none 确保 alert 可被读屏器触达

这里给 role="alert" 的节点加了 display: 'none',读屏器会直接把它从可访问树里剔除,导致倒计时结束的提示永远不会被播报,整个无障碍增强形同虚设。建议改成“视觉隐藏但仍留在可访问树”的写法。可以先套一个不可见样式(例如绝对定位到屏幕外或使用 clip),避免影响布局同时保留对辅助技术的可见性。

-          <View role={role} style={{ display: 'none' }}>
-            {alertContent}
-          </View>
+          <View
+            role={role}
+            style={{
+              position: 'absolute',
+              width: 1,
+              height: 1,
+              padding: 0,
+              margin: -1,
+              overflow: 'hidden',
+              clip: 'rect(0 0 0 0)',
+              whiteSpace: 'nowrap',
+              border: 0,
+            }}
+          >
+            {alertContent}
+          </View>
📝 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
<View role={role} style={{ display: 'none' }}>
{alertContent}
</View>
<View
role={role}
style={{
position: 'absolute',
width: 1,
height: 1,
padding: 0,
margin: -1,
overflow: 'hidden',
clip: 'rect(0 0 0 0)',
whiteSpace: 'nowrap',
border: 0,
}}
>
{alertContent}
</View>
🤖 Prompt for AI Agents
In src/packages/countdown/countdown.taro.tsx around lines 350–352, the element
with role="alert" is using display:'none', which removes it from the
accessibility tree; remove that display:none and instead apply a "visually
hidden" approach (e.g., absolute positioning off-screen or clip-based style that
preserves accessibility) by giving the View a visually-hidden className or style
object so it is visually hidden but remains reachable by screen readers, keeping
role="alert" and the alert content intact.

Comment on lines 253 to 259
<View
className={closeClasses}
onClick={handleClose}
ariaRole="button"
ariaLabel="close"
tabindex={0}
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

关闭按钮缺少键盘事件支持。

关闭按钮虽然设置了 tabindex={0} 使其可获得键盘焦点,但仅有 onClick 处理器。为了完整的键盘无障碍支持,需要添加键盘事件处理器来响应 Enter 和 Space 键。

建议添加键盘事件处理:

+    const handleKeyDown = (e: any) => {
+      if (e.key === 'Enter' || e.key === ' ') {
+        e.preventDefault()
+        handleClose()
+      }
+    }
+
     return (
       <View
         className={closeClasses}
         onClick={handleClose}
+        onKeyDown={handleKeyDown}
         ariaRole="button"
         ariaLabel="close"
         tabindex={0}
       >
         {React.isValidElement(closeIcon) ? closeIcon : systomIcon}
       </View>
     )

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/packages/dialog/dialog.taro.tsx around lines 253 to 259, the close button
has tabindex and onClick but lacks keyboard event handling; add an onKeyDown
handler that listens for Enter (key === 'Enter') and Space (key === ' ' or key
=== 'Spacebar' for older browsers) and calls handleClose when either is pressed,
calling event.preventDefault() for Space to avoid scrolling; attach this handler
to the same element (keeping ariaRole="button" and tabindex) so keyboard users
can activate the close action.

Comment on lines 222 to 228
<div
className={closeClasses}
onClick={handleClose}
role="button"
tabIndex={0}
aria-label="close"
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

关闭按钮缺少键盘事件支持。

关闭按钮虽然设置了 tabIndex={0} 使其可获得键盘焦点,但仅有 onClick 处理器。为了完整的键盘无障碍支持,需要添加键盘事件处理器来响应 Enter 和 Space 键。

建议添加键盘事件处理:

+    const handleKeyDown = (e: React.KeyboardEvent) => {
+      if (e.key === 'Enter' || e.key === ' ') {
+        e.preventDefault()
+        handleClose()
+      }
+    }
+
     return (
       <div
         className={closeClasses}
         onClick={handleClose}
+        onKeyDown={handleKeyDown}
         role="button"
         tabIndex={0}
         aria-label="close"
       >
         {React.isValidElement(closeIcon) ? closeIcon : systomIcon}
       </div>
     )

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/packages/dialog/dialog.tsx around lines 222 to 228, the close control has
tabIndex and role but only an onClick handler; add a keyboard handler (e.g.,
onKeyDown) that listens for Enter and Space keys and invokes handleClose so
keyboard users can activate the button, call event.preventDefault() for Space to
avoid page scroll, and keep role="button" and tabIndex={0} intact.

className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
role="tab"
tabIndex={-1}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

严重问题:所有标签页的 tabIndex 均为 -1,导致无法通过键盘访问

当前所有标签页的 tabIndex 都设置为 -1,这意味着没有任何标签页可以通过键盘获得焦点,严重影响键盘导航的可访问性。参考主组件实现(tabs.tsx 第179-184行),激活的标签页应该设置 tabIndex={0},其他标签页设置为 -tabIndex={-1}

应用以下修改:

              role="tab"
-              tabIndex={-1}
+              tabIndex={tabvalue === item.paneKey ? 0 : -1}
              aria-selected={tabvalue === item.paneKey}
📝 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
tabIndex={-1}
title={() => {
return list.map((item) => (
<div
onClick={() => setTabvalue(item.paneKey)}
className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`}
key={item.paneKey}
role="tab"
tabIndex={tabvalue === item.paneKey ? 0 : -1}
aria-selected={tabvalue === item.paneKey}
>
{item.icon}
<span className="nut-tabs-titles-item-text">{item.title}</span>
<span className="nut-tabs-titles-item-line" />
</div>
))
}}
🤖 Prompt for AI Agents
In src/packages/tabs/demos/h5/demo25.tsx around line 33, all tab elements are
currently rendered with tabIndex={-1} which prevents keyboard focus; update the
tabIndex logic to match the main tabs implementation (tabs.tsx lines ~179-184)
so the active/selected tab gets tabIndex={0} and non-active tabs get
tabIndex={-1}; use the component's active/selected state or prop to
conditionally set tabIndex (e.g., tabIndex={isActive ? 0 : -1}) so keyboard
users can focus and navigate tabs.

Comment on lines +204 to +213
role="tab"
aria-selected={
!item.disabled && String(item.value) === String(value)
}
tabIndex={
!item.disabled && String(item.value) === String(value)
? 0
: -1
}
aria-disabled={item.disabled}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ARIA 属性应放置在可交互元素上

当前将 role="tab"aria-selectedtabIndexaria-disabled 放在了内层的文本 div 上(line 196),但实际的点击处理器 onClick 在外层 div 上(line 167)。这会导致以下问题:

  1. 屏幕阅读器将焦点定位到内层文本元素,但该元素本身不可交互
  2. 键盘用户通过 Tab 键聚焦到文本元素后,无法直接通过 Enter/Space 激活选项卡(因为 onClick 在父元素上)
  3. 违反了 ARIA 最佳实践,即交互角色应放在可交互元素上

根据 ARIA Authoring Practices Guiderole="tab" 和相关属性应该放在响应点击事件的元素上。

应用此 diff 将 ARIA 属性移至外层可交互元素:

                <div
                  key={item.value}
                  ref={(ref: HTMLDivElement) => titleItemsRef.current.push(ref)}
                  onClick={() => tabChange(item)}
                  className={classNames(`${classPrefix}-titles-item`, {
                    [`nut-tabs-titles-item-active`]:
                      !item.disabled && String(item.value) === String(value),
                    [`nut-tabs-titles-item-disabled`]: item.disabled,
                    [`nut-tabs-titles-item-${align}`]: align,
                  })}
+                  role="tab"
+                  aria-selected={
+                    !item.disabled && String(item.value) === String(value)
+                  }
+                  tabIndex={
+                    !item.disabled && String(item.value) === String(value)
+                      ? 0
+                      : -1
+                  }
+                  aria-disabled={item.disabled}
                >
                  {activeType === 'line' && (
                    <div
                      className={classNames(
                        `${classPrefix}-titles-item-line`,
                        `${classPrefix}-titles-item-line-${direction}`
                      )}
                      style={{ background: activeColor }}
                      aria-hidden
                    />
                  )}
                  {activeType === 'smile' && (
                    <div
                      className={`${classPrefix}-titles-item-smile`}
                      aria-hidden
                    >
                      <JoySmile color={activeColor} width={40} height={20} />
                    </div>
                  )}
                  <div
                    className={classNames(
                      {
                        [`${classPrefix}-ellipsis`]: direction === 'vertical',
                      },
                      `${classPrefix}-titles-item-text`
                    )}
                    style={{ color: activeColor }}
-                    role="tab"
-                    aria-selected={
-                      !item.disabled && String(item.value) === String(value)
-                    }
-                    tabIndex={
-                      !item.disabled && String(item.value) === String(value)
-                        ? 0
-                        : -1
-                    }
-                    aria-disabled={item.disabled}
                  >
                    {item.title}
                  </div>
                </div>
📝 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
role="tab"
aria-selected={
!item.disabled && String(item.value) === String(value)
}
tabIndex={
!item.disabled && String(item.value) === String(value)
? 0
: -1
}
aria-disabled={item.disabled}
<div
key={item.value}
ref={(ref: HTMLDivElement) => titleItemsRef.current.push(ref)}
onClick={() => tabChange(item)}
className={classNames(`${classPrefix}-titles-item`, {
[`nut-tabs-titles-item-active`]:
!item.disabled && String(item.value) === String(value),
[`nut-tabs-titles-item-disabled`]: item.disabled,
[`nut-tabs-titles-item-${align}`]: align,
})}
role="tab"
aria-selected={
!item.disabled && String(item.value) === String(value)
}
tabIndex={
!item.disabled && String(item.value) === String(value)
? 0
: -1
}
aria-disabled={item.disabled}
>
{activeType === 'line' && (
<div
className={classNames(
`${classPrefix}-titles-item-line`,
`${classPrefix}-titles-item-line-${direction}`
)}
style={{ background: activeColor }}
aria-hidden
/>
)}
{activeType === 'smile' && (
<div
className={`${classPrefix}-titles-item-smile`}
aria-hidden
>
<JoySmile color={activeColor} width={40} height={20} />
</div>
)}
<div
className={classNames(
{
[`${classPrefix}-ellipsis`]: direction === 'vertical',
},
`${classPrefix}-titles-item-text`
)}
style={{ color: activeColor }}
>
{item.title}
</div>
</div>
🤖 Prompt for AI Agents
In src/packages/tabs/tabs.tsx around lines 167 to 213, the ARIA attributes
(role="tab", aria-selected, tabIndex, aria-disabled) are currently on the inner
text div (around line 196) while the click handler lives on the outer div
(around line 167); move those ARIA attributes to the outer interactive div so
the element that receives clicks and keyboard events is the same element exposed
to assistive tech. Concretely: remove role, aria-selected, tabIndex and
aria-disabled from the inner div; add role="tab", aria-selected={...},
aria-disabled={item.disabled} and tabIndex={item.disabled ? -1 :
(String(item.value) === String(value) ? 0 : -1)} to the outer div; ensure the
outer div also handles keyboard activation (onKeyDown handling Enter/Space to
call the same handler) and preserves disabled behavior and existing click
handler.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66092f4 and 57ad0d6.

📒 Files selected for processing (12)
  • src/locales/base.ts (1 hunks)
  • src/locales/en-US.ts (1 hunks)
  • src/locales/id-ID.ts (1 hunks)
  • src/locales/tr-TR.ts (1 hunks)
  • src/locales/zh-CN.ts (1 hunks)
  • src/locales/zh-TW.ts (1 hunks)
  • src/packages/dialog/dialog.taro.tsx (5 hunks)
  • src/packages/dialog/dialog.tsx (5 hunks)
  • src/packages/navbar/navbar.taro.tsx (3 hunks)
  • src/packages/navbar/navbar.tsx (3 hunks)
  • src/packages/popup/popup.taro.tsx (5 hunks)
  • src/packages/popup/popup.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/packages/navbar/navbar.taro.tsx
  • src/packages/navbar/navbar.tsx
  • src/packages/popup/popup.tsx
  • src/packages/dialog/dialog.tsx
  • src/packages/dialog/dialog.taro.tsx
  • src/packages/popup/popup.taro.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: oasis-cloud
Repo: jdf2e/nutui-react PR: 2700
File: src/packages/animatingnumbers/animatingnumbers.harmony.css:25-32
Timestamp: 2024-11-06T05:56:06.800Z
Learning: 在优化 NutUI React 动画性能时,添加 `will-change` 属性可能会对布局产生影响,需要谨慎使用。
🔇 Additional comments (5)
src/locales/id-ID.ts (1)

13-13: LGTM!翻译准确。

印尼语中 "Tutup" 是 "关闭" 的正确翻译,与无障碍功能需求一致。

src/locales/zh-TW.ts (1)

13-13: LGTM!繁体中文翻译准确。

"關閉" 是关闭的正确繁体中文翻译。

src/locales/tr-TR.ts (1)

13-13: LGTM!土耳其语翻译准确。

"Kapat" 是土耳其语中 "关闭" 的正确翻译。

src/locales/zh-CN.ts (1)

13-13: LGTM!简体中文翻译准确。

"关闭" 是关闭的正确简体中文翻译,符合无障碍标签需求。

src/locales/base.ts (1)

11-11: LGTM!类型定义合理。

close 定义为可选属性是合理的,这为向后兼容性和渐进式采用提供了灵活性。所有语言包都已提供此键的翻译,确保了无障碍功能的完整支持。

style={{ ...getStyle, ...style }}
onClick={(e) => handleClick(e as any)}
ariaRole={role}
ariaDisabled={disabled}
Copy link
Collaborator

Choose a reason for hiding this comment

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

加一个 button.tsx 下的多语言

style={style}
onClick={handleClick}
role={ariaRole}
aria-label={ariaLabel}
Copy link
Collaborator

Choose a reason for hiding this comment

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

这个是否可以生效~看上去不符合react的语法~~

ariaRole="button"
ariaLabel={locale.close}
tabindex={0}
>
Copy link
Collaborator

Choose a reason for hiding this comment

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

icon库升级到 @nutui/icons-react-taro 3.0.2-cpp.3.beta.3

closeOnOverlayClick={closeOnOverlayClick}
lockScroll={lockScroll}
onClick={onHandleClickOverlay}
ariaLabel="背景蒙层"
Copy link
Collaborator

Choose a reason for hiding this comment

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

这里可以不要,写在overlay 组件里就行了

footerDirection={footerDirection}
visible={visible}
ariaRole={role}
ariaLabel={ariaLabel}
Copy link
Collaborator

Choose a reason for hiding this comment

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

确认是不是会干扰到里边的内容读取

[`${classPrefix}-left-back-children-rtl`]: left && rtl,
})}
onClick={(e) => onBackClick(e)}
ariaLabel="back"
Copy link
Collaborator

Choose a reason for hiding this comment

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

暴露一个属性、用户可以自定义

Copy link
Collaborator

Choose a reason for hiding this comment

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

类似close

onClick={handleClickIcon}
ariaLabel={`${closeable ? 'close' : rightIconAriaLabel}`}
>
{rightIcon || <Close size={12} />}
Copy link
Collaborator

Choose a reason for hiding this comment

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

可以写在496 行的close上~

onClick: () => {},
afterShow: () => {},
afterClose: () => {},
ariaLabel: '蒙层',
Copy link
Collaborator

Choose a reason for hiding this comment

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

类似close

)
const [popupHeight, setPopupHeight] = useState('')
const [topBottom, setTopBottom] = useState('')
const role = 'dialog'
Copy link
Collaborator

Choose a reason for hiding this comment

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

popup

Copy link
Collaborator

Choose a reason for hiding this comment

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

优先修改 popup

onChange,
loading: propLoading,
onLoadingChange,
ariaLabel,
Copy link
Collaborator

Choose a reason for hiding this comment

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

默认可以加一下 切换按钮+关闭

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57ad0d6 and 01456f9.

📒 Files selected for processing (4)
  • src/packages/dialog/dialog.taro.tsx (3 hunks)
  • src/packages/overlay/overlay.tsx (1 hunks)
  • src/packages/popup/popup.taro.tsx (2 hunks)
  • src/packages/popup/popup.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/packages/popup/popup.taro.tsx
  • src/packages/overlay/overlay.tsx
  • src/packages/dialog/dialog.taro.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: oasis-cloud
Repo: jdf2e/nutui-react PR: 2700
File: src/packages/animatingnumbers/animatingnumbers.harmony.css:25-32
Timestamp: 2024-11-06T05:56:06.800Z
Learning: 在优化 NutUI React 动画性能时,添加 `will-change` 属性可能会对布局产生影响,需要谨慎使用。
🪛 GitHub Actions: CI
src/packages/popup/popup.tsx

[error] 223-223: ReferenceError: translated is not defined in renderTitle. Ensure translation data is available or handle undefined gracefully.

🪛 GitHub Check: build
src/packages/popup/popup.tsx

[failure] 223-223:
Cannot find name 'translated'. Did you mean 'CSSTranslate'?

Comment on lines 221 to 224
<div
className={`${classPrefix}-title-wrapper`}
aria-label={translated.title}
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

修复未定义的变量引用导致的运行时错误。

代码中引用了未定义的 translated 变量,这会导致运行时错误。根据文件中现有的模式(如第 192 行使用 locale.close),应该使用 locale 对象而不是 translated

应用以下修复:

-                <div
-                  className={`${classPrefix}-title-wrapper`}
-                  aria-label={translated.title}
-                >
+                <div
+                  className={`${classPrefix}-title-wrapper`}
+                  aria-label={locale.popup?.title || title}
+                >

或者,如果需要传入自定义的 ariaLabel 属性,建议添加一个新的 prop:

+  ariaLabel,
 } = { ...defaultProps, ...props }

然后使用:

                 <div
                   className={`${classPrefix}-title-wrapper`}
-                  aria-label={translated.title}
+                  aria-label={ariaLabel || locale.popup?.title || title}
                 >

请运行以下脚本验证 locale 对象的结构,以确定正确的属性名称:


🏁 Script executed:

#!/bin/bash
# 查找 locale/configprovider 相关的类型定义和默认值
rg -n -A 10 -B 2 "popup.*title|title.*popup" --type=ts --type=tsx -g '!node_modules' -g '!dist'

# 查找 useConfig 的类型定义
ast-grep --pattern 'export const useConfig = () => {
  $$$
}'

# 查找 locale 默认配置
fd -e ts -e tsx "locale|lang" --exec rg -l "popup" {}

Length of output: 526


Now I'll search the actual codebase for the relevant configuration files and variable definitions:


🏁 Script executed:

#!/bin/bash
# Find getDefaultConfig implementation
echo "=== Searching for getDefaultConfig implementation ==="
rg -n "getDefaultConfig" --type=ts -g '!node_modules' -g '!dist' -A 20

# Find references to "translated" variable
echo -e "\n=== Searching for 'translated' variable ==="
rg -n "translated" --type=ts -g '!node_modules' -g '!dist' -C 3

# Find popup component props and their types
echo -e "\n=== Searching for popup component interface/type ==="
rg -n "interface.*Popup|type.*Popup" --type=ts -g '!node_modules' -g '!dist' -A 10

Length of output: 306997


修复:popup.tsx 中引用未定义的 translated(运行时错误)

src/packages/popup/popup.tsx 第 221–224 行使用了未定义的 translated(aria-label={translated.title}),会在渲染时抛出 ReferenceError。将该引用替换为使用 locale 或新增 ariaLabel prop,并以组件的 title 回退。建议最小修复:

-                  aria-label={translated.title}
+                  aria-label={ariaLabel || locale.popup?.title || title}

若新增 prop,请在 props 解构处添加 ariaLabel

📝 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
<div
className={`${classPrefix}-title-wrapper`}
aria-label={translated.title}
>
<div
className={`${classPrefix}-title-wrapper`}
aria-label={ariaLabel || locale.popup?.title || title}
>
🧰 Tools
🪛 GitHub Actions: CI

[error] 223-223: ReferenceError: translated is not defined in renderTitle. Ensure translation data is available or handle undefined gracefully.

🪛 GitHub Check: build

[failure] 223-223:
Cannot find name 'translated'. Did you mean 'CSSTranslate'?

🤖 Prompt for AI Agents
In src/packages/popup/popup.tsx around lines 221-224, the code references an
undefined variable translated (aria-label={translated.title}) causing a runtime
ReferenceError; replace that reference with a safe value such as a new ariaLabel
prop or the locale value with fallback to the component title. Update the
component props destructuring to include ariaLabel (if adding prop), then set
aria-label to ariaLabel ?? locale?.title ?? title (or similar order: locale
first then title) so it never references an undefined symbol; ensure TypeScript
types/prop defaults are updated accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

3 participants