-
Notifications
You must be signed in to change notification settings - Fork 291
feat: 添加组件无障碍功能支持 #3385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat_v3.x_cpp
Are you sure you want to change the base?
feat: 添加组件无障碍功能支持 #3385
Conversation
- 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>
概览该PR为多个UI组件(按钮、倒计时、对话框、图片、输入数字、导航栏、通知栏、弹出层、价格、进度条、轮播、标签栏等)添加了ARIA无障碍属性支持,并提供了相应的演示组件和多语言本地化文本。 变更清单
代码审查工作量🎯 4 (复杂) | ⏱️ ~45-60 分钟 需要额外关注的区域:
相关PRs
建议审查者
诗歌
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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}%,这可能不适合国际化项目。建议:
- 如果项目有国际化方案,使用国际化函数包装默认文本
- 或者在文档中明确说明开发者应该为国际化场景提供自定义
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 最佳实践,对话框还应该包含:
aria-modal="true":当overlay为 true 时,表明这是一个模态对话框aria-labelledby或aria-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 属性:
ariaModal={overlay}:当overlay为 true 时标记为模态对话框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以支持键盘访问当前实现已正确添加了
ariaRole、ariaChecked和ariaDisabled属性,但缺少键盘可访问性支持。参考 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?: stringsrc/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
📒 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.tsxsrc/packages/countdown/countdown.tsxsrc/packages/navbar/demos/taro/demo4.tsxsrc/packages/countdown/countdown.taro.tsxsrc/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 实体(如¥)转换为实际字符- 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 都正确设置了
ariaLabel和ariaRoledescription- 代码结构清晰,易于理解
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-checked和aria-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 类型定义符合最佳实践为
BaseTabbar和BaseTabbarItem添加的可选无障碍属性设计合理:
- 属性均为可选,支持渐进式采用
- 类型定义清晰,便于 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 属性添加正确!将
role和aria-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: 代码实现正确!添加
ariaModal和ariaRole属性符合 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"、ariaSelected和ariaDisabled属性,能够准确反映标签的激活状态和禁用状态,有助于屏幕阅读器正确理解标签页的交互状态。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"正确标识了开关角色ariaChecked和ariaDisabled准确反映了组件状态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-label和aria-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 模态对话框的规范要求。
| {...rest} | ||
| dangerouslySetInnerHTML={{ | ||
| __html: `${renderTime}`, | ||
| __html: `${renderTime}<span style="display:none" role=${role}>${alertContent}</span>`, | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
存在 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.
| 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> | ||
| )) | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
添加 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.
| 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).
| 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> | ||
| )) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
验证自定义标题渲染器的完整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.
| 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.
- Dialog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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组件中添加:
tabIndex={0}- 使元素可获得焦点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-label和aria-hidden实现合理:
- 当蒙层可交互(
closeOnOverlayClick=true)时,暴露给辅助技术- 当蒙层不可交互时,对辅助技术隐藏
建议考虑以下增强:
- 为可交互的蒙层添加
role="button"属性,明确其交互性质- 添加键盘支持:当
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 属性接口添加
ariaRole和ariaDisabled属性很好地提升了组件的可访问性。不过,建议在
ButtonProps接口中暴露额外的 ARIA 相关属性(如ariaLabel、ariaLabelledBy、ariaDescribedBy等),以便使用者能够为特定场景提供自定义的无障碍标签。例如,仅包含图标的按钮需要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
📒 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 中正确解构
ariaLabel和ariaRoledescription,解决了之前评审中指出的缺失问题。
134-138: 无障碍属性实现正确所有之前评审中指出的问题均已修复:
- ✅ 第 136 行:
tabIndex使用了正确的驼峰式命名(之前是小写的tabindex)- ✅ 第 137-138 行:
ariaLabel和ariaRoledescription现在已正确传递给 View 组件- ✅
tabIndex={active ? 0 : -1}实现了正确的 roving tabindex 模式,符合 ARIA 无障碍规范中 tab 组件的键盘导航最佳实践- ✅
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 行使用的
ariaLabel和ariaHidden属性已符合 Taro 平台规范。Taro View 组件支持 ARIA 属性,包括 role、aria-label、aria-hidden 等标准 aria-* 属性。验证显示,项目中已在多个组件(navbar、tabs、tabpane、tabbar 等)成功使用ariaRole、ariaLabel等属性,确认实现方式与现有代码一致。无需调整,代码已遵循 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"代码实现已满足无障碍访问要求,无需修改。
| <View | ||
| className={closeClasses} | ||
| onClick={handleClose} | ||
| ariaRole="button" | ||
| ariaLabel="close" | ||
| tabindex={0} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
关闭按钮缺少键盘事件支持。
关闭按钮虽然设置了 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.
| <div | ||
| className={closeClasses} | ||
| onClick={handleClose} | ||
| role="button" | ||
| tabIndex={0} | ||
| aria-label="close" | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
关闭按钮缺少键盘事件支持。
关闭按钮虽然设置了 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} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
严重问题:所有标签页的 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.
| 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
加一个 button.tsx 下的多语言
| style={style} | ||
| onClick={handleClick} | ||
| role={ariaRole} | ||
| aria-label={ariaLabel} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个是否可以生效~看上去不符合react的语法~~
| ariaRole="button" | ||
| ariaLabel={locale.close} | ||
| tabindex={0} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
icon库升级到 @nutui/icons-react-taro 3.0.2-cpp.3.beta.3
| closeOnOverlayClick={closeOnOverlayClick} | ||
| lockScroll={lockScroll} | ||
| onClick={onHandleClickOverlay} | ||
| ariaLabel="背景蒙层" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里可以不要,写在overlay 组件里就行了
src/packages/dialog/dialog.taro.tsx
Outdated
| footerDirection={footerDirection} | ||
| visible={visible} | ||
| ariaRole={role} | ||
| ariaLabel={ariaLabel} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
确认是不是会干扰到里边的内容读取
src/packages/navbar/navbar.taro.tsx
Outdated
| [`${classPrefix}-left-back-children-rtl`]: left && rtl, | ||
| })} | ||
| onClick={(e) => onBackClick(e)} | ||
| ariaLabel="back" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
暴露一个属性、用户可以自定义
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
类似close
| onClick={handleClickIcon} | ||
| ariaLabel={`${closeable ? 'close' : rightIconAriaLabel}`} | ||
| > | ||
| {rightIcon || <Close size={12} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
可以写在496 行的close上~
| onClick: () => {}, | ||
| afterShow: () => {}, | ||
| afterClose: () => {}, | ||
| ariaLabel: '蒙层', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
类似close
| ) | ||
| const [popupHeight, setPopupHeight] = useState('') | ||
| const [topBottom, setTopBottom] = useState('') | ||
| const role = 'dialog' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
popup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
优先修改 popup
| onChange, | ||
| loading: propLoading, | ||
| onLoadingChange, | ||
| ariaLabel, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
默认可以加一下 切换按钮+关闭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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'?
src/packages/popup/popup.tsx
Outdated
| <div | ||
| className={`${classPrefix}-title-wrapper`} | ||
| aria-label={translated.title} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 10Length 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.
| <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.
- Image - Countdown - InputNumber - NavBar - NoticeBar - Price - Progress - Tabbar - Tabs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/types/spec/tabs/base.ts (1)
5-12: TabsTitle 中 ariaLabel 字段未使用且冗余
TabsTitle类型定义包含ariaLabel,但在实现中(tabs.tsx第 102 行和第 216 行、tabs.taro.tsx第 71 行和第 301 行)仅使用titleAriaLabel。ariaLabel从未被读取或使用。建议从TabsTitle和BaseTabPane中移除未使用的ariaLabel字段,以避免混淆。
♻️ Duplicate comments (5)
src/packages/tabs/tabs.tsx (1)
198-217: ARIA 属性应移至外层可交互元素当前
role="tab"、aria-selected、tabIndex和aria-disabled放在内层文本 div 上(line 206),但实际的点击处理器onClick在外层 div 上(line 172)。这会导致键盘用户聚焦到内层元素后,无法通过 Enter/Space 激活选项卡。根据 ARIA Authoring Practices Guide,
role="tab"应放在响应点击的元素上,并添加onKeyDown处理 Enter/Space 按键。<div key={item.value} ref={(ref: HTMLDivElement) => titleItemsRef.current.push(ref)} onClick={() => tabChange(item)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + tabChange(item) + } + }} className={classNames(`${classPrefix}-titles-item`, { ... })} + 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} + aria-label={item.titleAriaLabel} >src/packages/tabpane/tabpane.taro.tsx (1)
25-25: 类型安全问题:避免使用any类型断言
(props as any).active绕过了 TypeScript 类型检查。应在TabPanelInnerProps或TaroTabPaneProps中声明active属性。interface TabPanelInnerProps { autoHeightClassName: string + active?: boolean }然后改为:
- const active = !disabled && (props as any).active + const active = !disabled && props.activesrc/packages/tabs/demos/h5/demo25.tsx (1)
26-42: 严重问题:所有选项卡 tabIndex={-1} 导致键盘无法访问所有选项卡的
tabIndex都设置为-1,意味着没有任何选项卡可以通过键盘获得焦点,完全阻断了键盘导航。同时缺少
role="tablist"容器,且aria-label使用技术标识符而非用户友好文本。title={() => { - return list.map((item) => ( + return ( + <div role="tablist"> + {list.map((item) => ( <div onClick={() => setTabvalue(item.paneKey)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + setTabvalue(item.paneKey) + } + }} className={`nut-tabs-titles-item ${tabvalue === item.paneKey ? 'nut-tabs-titles-item-active' : ''}`} key={item.paneKey} role="tab" - tabIndex={-1} + tabIndex={tabvalue === item.paneKey ? 0 : -1} aria-selected={tabvalue === item.paneKey} - aria-label={item.paneKey} + aria-label={item.title} > {item.icon || null} <span className="nut-tabs-titles-item-text">{item.title}</span> <span className="nut-tabs-titles-item-line" /> </div> - )) + ))} + </div> + ) }}src/packages/tabs/demos/taro/demo25.tsx (1)
27-42: 自定义标题渲染器存在 ARIA 结构问题
tabIndex 设置不正确:所有选项卡都设置
tabIndex={0},应该只有当前激活的选项卡为 0,其他为 -1。缺少 tablist 容器:映射的选项卡元素应包裹在
role="tablist"容器中。aria-label 语义不佳:使用
item.paneKey(如 "c1")作为标签对用户没有实际意义,建议使用item.title。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} + tabIndex={tabvalue === item.paneKey ? 0 : -1} ariaSelected={tabvalue === item.paneKey} - ariaLabel={item.paneKey} + ariaLabel={item.title} > {item.icon} <Text className="nut-tabs-titles-item-text">{item.title}</Text> <Text className="nut-tabs-titles-item-line" /> </View> - )) + ))} + </View> + ) }}src/packages/inputnumber/inputnumber.taro.tsx (1)
228-234: 修复 TypeScript 类型错误与减号按钮相同的问题:
View组件不接受这些 ARIA 属性。请统一修复所有控制按钮的 ARIA 属性使用方式。
🧹 Nitpick comments (7)
src/packages/price/price.tsx (1)
57-65:replaceSpecialChar辅助函数实现正确HTML 实体转换逻辑完整,覆盖了常见的特殊字符。
此函数与
price.taro.tsx中的实现重复,可考虑提取到共享的工具模块中以减少代码重复。src/locales/id-ID.ts (1)
14-14:arithmetic.plus/minus印尼语文案语义略偏,建议更贴近「加/减」
back: 'Kembali'没问题,但arithmetic.plus当前为'Selain itu',更接近「此外/另外」,不是算术意义上的「加」。为与其它语言下的Plus/加含义保持一致,建议考虑改为更贴近算术操作的用词,例如:'Plus'/'Minus',或者本地化为Tambah/Kurangi一类动词,同时与其它键保持大小写风格一致。Also applies to: 146-149
src/packages/navbar/navbar.tsx (1)
7-8: NavBar 无障碍增强方向正确,但 back 区仍缺少键盘可访问性
- 使用
useConfig的locale.back作为返回按钮的aria-label,以及给外层容器增加role="navigation",这两点都对无障碍有帮助,整体方向正确。- 但当前返回区域仍是可点击的
<div>,没有role="button"、tabIndex={0},也没有处理键盘事件(如 Enter/Space)。从无障碍角度建议后续将其改为<button>元素,或补充相应的role、tabIndex和键盘事件支持。- 同时,
renderContent中对标题aria-label的逻辑已被注释掉,建议要么根据新的策略重新启用(例如直接用title或本地化文案),要么删除这段注释,避免长期保留死代码。Also applies to: 41-42, 64-73, 89-90, 112-112
src/locales/tr-TR.ts (1)
14-14:arithmetic.plus/minus土耳其语文案建议更贴近算术含义
back: 'Geri dön'合理;但arithmetic.plus当前为'Ayrıca',语义更接近「此外/而且」,而不是算术运算中的「加」。为了与其它语言下的Plus/加语义对齐,建议考虑改为更贴近算术的用词,例如:
plus: 'Artı'minus: 'Eksi'具体选择可与熟悉土耳其语的同事确认。
Also applies to: 154-157
src/packages/noticebar/noticebar.tsx (2)
14-15: 新增 rightIconAriaLabel 和多语言 label 整体合理,但建议两点调整整体设计不错:
- 通过
rightIconAriaLabel提供可配置的辅助说明;- 关闭态优先用
locale.close,对齐国际化需求。建议补充/微调:
垂直模式仍然硬编码
'close'
- Line 548 处
aria-label={${closeable ? 'close' : rightIconAriaLabel}}与水平方向使用的locale.close不一致。- 建议改为:
aria-label={closeable ? locale.close : rightIconAriaLabel}这样多端、多语言行为一致。
避免在非 closeable 且没传文案时写入空 aria-label
- 当前三元表达式会在
!closeable && !rightIconAriaLabel时生成aria-label=""。- 空
aria-label会让交互元素在无障碍树里成为“无名称控件”,不利于读屏体验。- 可以改为:
aria-label={ closeable ? locale.close : rightIconAriaLabel || undefined }垂直分支同理,这样只有有实际文案时才写入
aria-label。这两点都不影响现有功能,但能进一步提升 a11y 和 i18n 体验。
Also applies to: 33-34, 63-64, 69-70, 483-489, 544-551
483-489: 可选:为右侧关闭/图标容器补充键盘可达性既然本次是在做无障碍增强,这个点击关闭的容器目前还是纯
div,只支持鼠标点击:
- 没有
role="button"、tabIndex={0},键盘用户无法聚焦。- 也没有
onKeyDown等键盘事件来触发关闭。可以考虑在不破坏现有交互的前提下,顺手补充类似处理(示意):
<div className="nut-noticebar-box-right-icon" onClick={onClickIcon} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { onClickIcon(e as any) } }} aria-label={...} > ... </div>这样关闭操作对键盘用户会更友好。
src/packages/noticebar/noticebar.taro.tsx (1)
36-37: 可选:与 H5 版本一致,避免空 ariaLabel 并统一行为说明与 H5 版本类似,这里当
!closeable && !rightIconAriaLabel时会生成ariaLabel="":ariaLabel={`${closeable ? locale.close : rightIconAriaLabel}`}建议微调为只在有实际文案时才输出属性,例如:
<View className="nut-noticebar-box-right-icon" onClick={handleClickIcon} ariaLabel={ closeable ? locale.close : rightIconAriaLabel || undefined } > {rightIcon || <Close size={12} />} </View>这样:
- closeable 场景依旧有本地化的 “关闭” 文案;
- 自定义右侧图标时可以通过
rightIconAriaLabel明确传入说明;- 没有提供文案时不会写入空的无障碍名称。
同时建议在组件文档里补一句:
rightIconAriaLabel主要用于closeable={false}且自定义rightIcon时的辅助说明,方便使用方理解。请确认项目内 Taro 端屏读工具/无障碍检查工具对
ariaLabel的行为符合预期,确保这类属性在所有目标小程序/H5 端都被正确识别。Also applies to: 66-67, 79-80, 489-499
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
src/locales/base.ts(2 hunks)src/locales/en-US.ts(2 hunks)src/locales/id-ID.ts(2 hunks)src/locales/tr-TR.ts(2 hunks)src/locales/zh-CN.ts(2 hunks)src/locales/zh-TW.ts(2 hunks)src/locales/zh-UG.ts(2 hunks)src/packages/countdown/countdown.taro.tsx(6 hunks)src/packages/inputnumber/inputnumber.taro.tsx(6 hunks)src/packages/inputnumber/inputnumber.tsx(6 hunks)src/packages/navbar/navbar.taro.tsx(5 hunks)src/packages/navbar/navbar.tsx(5 hunks)src/packages/noticebar/noticebar.taro.tsx(5 hunks)src/packages/noticebar/noticebar.tsx(7 hunks)src/packages/price/price.taro.tsx(2 hunks)src/packages/price/price.tsx(3 hunks)src/packages/tabbar/demo.taro.tsx(5 hunks)src/packages/tabbar/demo.tsx(5 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(4 hunks)src/packages/tabs/tabs.tsx(5 hunks)src/types/spec/price/base.ts(1 hunks)src/types/spec/tabs/base.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
- src/packages/navbar/navbar.taro.tsx
- src/packages/tabs/demos/h5/demo24.tsx
- src/packages/tabs/demos/taro/demo24.tsx
- src/packages/tabs/demo.tsx
- src/packages/tabs/tabs.taro.tsx
- src/packages/tabpane/tabpane.tsx
- src/packages/tabs/demo.taro.tsx
- src/packages/tabbar/demo.taro.tsx
- src/packages/tabbar/demo.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/inputnumber/inputnumber.taro.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.tsxsrc/packages/tabs/demos/taro/demo25.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 (3)
src/packages/inputnumber/inputnumber.taro.tsx (1)
src/packages/configprovider/configprovider.taro.tsx (1)
useConfig(41-43)
src/packages/tabs/demos/h5/demo25.tsx (2)
src/packages/tabs/tabs.taro.tsx (1)
Tabs(27-327)src/packages/tabs/tabs.tsx (1)
Tabs(24-241)
src/types/spec/tabs/base.ts (1)
src/types/base/atoms.ts (1)
SimpleValue(1-1)
🪛 GitHub Check: build
src/packages/inputnumber/inputnumber.taro.tsx
[failure] 200-200:
Type '{ children: Element; className: string; onClick: (e: ITouchEvent) => Promise; ariaRole: string; ariaLabel: string; ariaDisabled: boolean; }' is not assignable to type 'IntrinsicAttributes & ViewProps'.
[failure] 193-193:
Type '{ children: Element[]; className: string; style: CSSProperties | undefined; ariaLabel: string | undefined; ariaDisabled: boolean; }' is not assignable to type 'IntrinsicAttributes & ViewProps'.
[failure] 233-233:
Type '{ children: Element; className: string; onClick: (e: ITouchEvent) => Promise; ariaRole: string; ariaLabel: string; ariaDisabled: boolean; }' is not assignable to type 'IntrinsicAttributes & ViewProps'.
src/packages/price/price.taro.tsx
[failure] 174-174:
Type '{ children: Element; className: string; style: CSSProperties | undefined; ariaLabel: string; }' is not assignable to type 'IntrinsicAttributes & TextProps'.
src/packages/noticebar/noticebar.taro.tsx
[failure] 498-498:
Type '{ size: number; ariaLabel: string; }' is not assignable to type 'IntrinsicAttributes & SVG_IconProps'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (22)
src/packages/countdown/countdown.taro.tsx (4)
69-72: ARIA 状态管理实现正确正确添加了
role、alertContent状态和alertTimerRef用于管理 ARIA 提示的生命周期。
112-117: 倒计时结束时的 ARIA 提示实现良好倒计时结束时设置
role="alert"并在 3 秒后清除,符合无障碍最佳实践。之前的window.setTimeout问题已修复。
273-275: 定时器清理逻辑正确组件卸载时正确清理
alertTimerRef,防止内存泄漏。基于 learnings,这是 React/Taro 组件中使用定时器的最佳实践。
350-365: 视觉隐藏样式实现正确之前
display: 'none'的问题已修复,现在使用标准的视觉隐藏 CSS 模式(绝对定位 + clip),确保元素对屏幕阅读器可见但视觉上不可见。src/types/spec/price/base.ts (1)
22-22: 类型定义扩展正确在
BasePrice接口中添加可选的ariaLabel属性,与 PR 中其他组件的无障碍增强保持一致。src/packages/price/price.tsx (1)
128-137: Web 版本的aria-label实现正确正确使用
aria-label(kebab-case)作为 HTML 属性名,并提供了合理的默认值构建逻辑,包含货币符号位置处理。src/packages/price/price.taro.tsx (1)
191-200: 同样的ariaLabel属性名问题View 组件也存在相同的问题,应使用
aria-label而不是ariaLabel。应用此差异修复:
<View className={`${classPrefix} ${classPrefix}-${color} ${className}`} style={style} - ariaLabel={ + aria-label={ ariaLabel || `${ symbol && position === 'before' ? replaceSpecialChar(symbol) : '' }${formatThousands(price)}${ checkPoint(price) || digits ? '.' : '' }${formatDecimal(price)}${ symbol && position === 'after' ? replaceSpecialChar(symbol) : '' }` } >⛔ Skipped due to learnings
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)中可能未被完全支持,导致兼容性问题。因此,使用硬编码的十六进制颜色值可以确保在鸿蒙系统上的正确显示。src/packages/tabs/tabs.tsx (1)
180-196: 装饰性元素正确隐藏 ✓为
line和smile装饰性指示器添加aria-hidden是正确的做法,确保屏幕阅读器不会朗读这些纯视觉元素。src/packages/tabpane/tabpane.taro.tsx (1)
35-43: TabPanel ARIA 实现正确 ✓
ariaRole="tabpanel"正确标识面板角色tabIndex基于激活状态动态设置(激活时 0,否则 -1)符合最佳实践ariaLabel正确传递src/types/spec/tabs/base.ts (1)
30-36: BaseTabPane 类型扩展合理 ✓
ariaLabel用于面板标签,titleAriaLabel用于选项卡标题标签,与TabsTitle保持一致,类型扩展合理。src/locales/zh-UG.ts (1)
14-14:back与arithmetic结构与 BaseLang 一致新增字段位置与类型都与
BaseLang定义保持一致,满足后续组件通过locale.back及locale.arithmetic取文案的需求,整体设计合理。Also applies to: 144-147
src/locales/en-US.ts (1)
14-14: 新增back与arithmetic键与现有 en-US 风格保持一致
back: 'Back'以及arithmetic.plus/minus的命名和大小写都与同级文案(如Save、Confirm)风格一致,也符合 BaseLang 的结构要求,可直接用于组件无障碍文案。Also applies to: 145-148
src/locales/base.ts (1)
12-12: 在 BaseLang 中增加back与arithmetic是合理的抽象为导航返回和加减操作提供统一的
back与arithmetic.plus/minus定义,有利于组件统一从locale读取无障碍文案。当前已看到多种语言实现了对应字段,后续如还有其它 locale 文件(如更多地区语言),建议也同步补齐这两个键,以确保 TypeScript 类型检查一致。Also applies to: 146-149
src/locales/zh-CN.ts (1)
14-14: 新增back与arithmetic键语义清晰并与简体中文风格一致
back: '返回'以及arithmetic.plus: '加'、arithmetic.minus: '减'均符合简体中文语境,与同文件其它文案风格统一,也满足组件通过locale.back/locale.arithmetic读取无障碍文案的需求。Also applies to: 146-149
src/packages/inputnumber/inputnumber.taro.tsx (3)
9-9: LGTM!正确引入了 useConfig 以获取国际化配置。
22-22: LGTM!为组件提供了合理的默认 aria-label。
64-64: 原始审查评论不成立经过验证,
locale.arithmetic属性确实存在且可正常访问:
- ✅ 所有locale配置文件都定义了
arithmetic: { plus, minus }属性(en-US.ts、zh-CN.ts、zh-TW.ts等)- ✅ BaseLang接口类型在
src/locales/base.ts第146-149行正确定义- ✅
useConfig()钩子正确返回包含locale对象的配置- ✅ 默认locale(zhCN)始终初始化可用
- ✅ 第64行
const { locale } = useConfig()的使用方式正确,与其他组件一致- ✅ 代码在第199行和232行正确使用
locale.arithmetic.minus和locale.arithmetic.plus无需修改。
Likely an incorrect or invalid review comment.
src/packages/inputnumber/inputnumber.tsx (4)
8-8: LGTM!正确引入了 useConfig 以获取国际化配置。
21-21: LGTM!为组件提供了合理的默认 aria-label。
187-193: LGTM!根容器的 ARIA 属性设置正确,提供了良好的无障碍访问支持。
62-62: 验证完成:locale.arithmetic 配置已正确定义搜索结果确认
locale.arithmetic.minus和locale.arithmetic.plus在所有语言配置文件中均已定义:
- 类型定义:
src/locales/base.ts第 146-149 行- 具体实现:所有语言文件(zh-CN.ts、en-US.ts 等)都包含 arithmetic 对象及其 plus 和 minus 属性
代码中对这些属性的使用完全正确,无需修改。
src/packages/noticebar/noticebar.tsx (1)
462-465: 左侧图标增加 aria-hidden 的处理是合理的两处左侧图标统一包裹在带
aria-hidden的容器中,能有效把纯装饰性 icon 从无障碍树中隐藏,避免读屏重复朗读,方向(水平方向、垂直方向)表现一致,这里实现是合理的。Also applies to: 501-503
| edit: '編輯', | ||
| reset: '重置', | ||
| close: '關閉', | ||
| back: '返回', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
繁体中文 arithmetic.minus 使用了简体字,建议改为「減」
整份 zh-TW 文案基本采用繁体字,但 arithmetic.minus 当前为 '减'(简体),与同文件如「暫無數據」「視頻」等用字风格不一致。建议改为繁体写法 '減',保证整体一致性:
- minus: '减',
+ minus: '減',Also applies to: 146-149
🤖 Prompt for AI Agents
In src/locales/zh-TW.ts around line 14 (and also lines 146-149), the Traditional
Chinese locale uses the simplified character '减' for arithmetic.minus; update
those entries to use the Traditional character '減' instead and scan the file for
any other occurrences of the simplified form to replace so the locale stays
consistent.
| <View | ||
| className={classes} | ||
| style={style} | ||
| ariaLabel={ariaLabel} | ||
| ariaDisabled={disabled} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
修复 TypeScript 类型错误
静态分析显示 Taro 的 View 组件不接受 ariaLabel 和 ariaDisabled 属性。在 Taro 中,需要使用小程序支持的 ARIA 属性格式(通常是 aria-label、aria-disabled),或者这些属性可能根本不受支持。
请检查 Taro View 组件的类型定义和目标平台(如鸿蒙、微信小程序)对 ARIA 属性的支持情况,并使用正确的属性名称。
运行以下脚本查找 Taro View 组件的类型定义:
🏁 Script executed:
#!/bin/bash
# 描述: 查找 Taro View 组件支持的 ARIA 属性
# 查找 ViewProps 接口定义
ast-grep --pattern 'interface ViewProps {
$$$
}'
# 查找 View 组件相关的 aria 属性定义
rg -n "aria.*:" --type=ts -g "**/components/**" -C3Length of output: 8240
我需要进一步验证信息。让我查看具体的文件内容和更准确的搜索。
</script>🏁 Script executed:
#!/bin/bash
# 查看 inputnumber.taro.tsx 文件内容(重点查看189-194行)
cat src/packages/inputnumber/inputnumber.taro.tsx | head -200 | tail -20
# 搜索项目中所有使用 ariaLabel 或 ariaDisabled 的地方
rg -n "ariaLabel|ariaDisabled" --type=ts --type=tsx -C2
# 检查 Taro 的类型定义或导入
rg -n "from '@tarojs|import.*View" --type=ts --type=tsx src/packages/inputnumber/ -C2Length of output: 647
</script>
🏁 Script executed:
#!/bin/bash
# 查找所有 taro 文件中 aria 属性的使用
find src -name "*.taro.tsx" -o -name "*.taro.ts" | head -20
# 查看所有 taro 文件中 aria 属性的使用情况
find src -name "*.taro.tsx" -exec grep -l "aria" {} \;
# 搜索 View 组件的导入和使用
grep -r "from '@tarojs" src/packages/ --include="*.taro.tsx" | head -10
# 查看项目中是否有类型定义或 d.ts 文件涉及 View 组件
find src -name "*.d.ts" | head -10Length of output: 2573
</script>
🏁 Script executed:
#!/bin/bash
# 在所有 taro 文件中搜索 ariaLabel 和 ariaDisabled 使用
grep -r "ariaLabel\|ariaDisabled" src --include="*.taro.tsx" --include="*.taro.ts" -c | grep -v ":0"
# 查看有多少文件使用这些属性
grep -r "ariaLabel\|ariaDisabled" src --include="*.taro.tsx" --include="*.taro.ts" -l | wc -l
# 查看 package.json 中 @tarojs 的版本
grep -A 2 "@tarojs" package.json
# 查看 tsconfig 或类型定义相关配置
cat tsconfig.json | grep -A 5 -B 5 "types\|typeRoots"Length of output: 1419
</web_search>
🌐 Web query:
Taro 4.0.12 View component ariaLabel ariaDisabled support
💡 Result:
Short answer: In Taro 4.0.12 the View component exposes ariaRole and ariaLabel props, but there is no built‑in ariaDisabled prop on View in that release.
Evidence:
- View API docs list ariaRole and ariaLabel in ViewProps. [1]
- @tarojs/components type/docs show many components expose ariaLabel/ariaRole; I couldn't find an ariaDisabled prop on View in the v4.x component types. [2]
If you need aria-disabled behavior on H5, add the attribute via nativeProps / spread to the rendered element (or set attribute on the underlying DOM node) and handle disabling behavior yourself; I can show an example if you want.
Sources:
- Taro View docs (ViewProps: ariaRole, ariaLabel). [1]
- @tarojs/components types / docs (v4.x references showing ariaLabel presence; no ariaDisabled on View). [2]
</script>
🏁 Script executed:
#!/bin/bash
# 查看 inputnumber.taro.tsx 中是否有 ts-ignore 或类型忽略
grep -n "ts-ignore\|@ts-\|any" src/packages/inputnumber/inputnumber.taro.tsx | head -20
# 检查项目的编译配置中是否禁用了类型检查
cat tsconfig.json | grep -i "strict\|skipLibCheck\|noImplicitAny"
# 查看 inputnumber.taro.tsx 完整的 View 属性使用
grep -A 10 "className={classes}" src/packages/inputnumber/inputnumber.taro.tsx | head -15Length of output: 535
移除不受支持的 ariaDisabled 属性
Taro 4.0.12 的 View 组件支持 ariaRole 和 ariaLabel,但不支持 ariaDisabled 属性。第189-194行及整个文件中的 ariaDisabled 需要移除,ariaLabel 可以保留。
此问题在项目其他约20个 Taro 文件中也存在,建议一并修复。如需在禁用状态下传达无障碍信息,可考虑在父容器或相关文本中添加语义标记。
🧰 Tools
🪛 GitHub Check: build
[failure] 193-193:
Type '{ children: Element[]; className: string; style: CSSProperties | undefined; ariaLabel: string | undefined; ariaDisabled: boolean; }' is not assignable to type 'IntrinsicAttributes & ViewProps'.
🤖 Prompt for AI Agents
In src/packages/inputnumber/inputnumber.taro.tsx around lines 189 to 194, remove
the unsupported ariaDisabled prop from the View element (and remove any other
occurrences of ariaDisabled in this file); keep ariaLabel as-is. Replace
accessibility conveyance for disabled state by using semantic markup or
parent/related accessible text instead of ariaDisabled. Also search the repo for
other Taro files (about ~20) that set ariaDisabled and remove or replace those
usages consistently.
| <View | ||
| className={`${classPrefix}-minus`} | ||
| onClick={handleReduce} | ||
| ariaRole="button" | ||
| ariaLabel={locale.arithmetic.minus} | ||
| ariaDisabled={Number(shadowValue) <= Number(min) || disabled} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
修复 TypeScript 类型错误
View 组件不接受 ariaRole、ariaLabel 和 ariaDisabled 属性。请参考上一条评论中的解决方案。
此外,对于可交互的按钮元素,还需要考虑:
- 是否支持键盘交互(在支持的平台上)
- 是否需要添加视觉焦点样式
🧰 Tools
🪛 GitHub Check: build
[failure] 200-200:
Type '{ children: Element; className: string; onClick: (e: ITouchEvent) => Promise; ariaRole: string; ariaLabel: string; ariaDisabled: boolean; }' is not assignable to type 'IntrinsicAttributes & ViewProps'.
| <div | ||
| className={`${classPrefix}-minus`} | ||
| onClick={handleReduce} | ||
| role="button" | ||
| tabIndex={0} | ||
| aria-label={locale.arithmetic.minus} | ||
| aria-disabled={Number(shadowValue) <= Number(min) || disabled} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
添加键盘事件处理器以支持完整的键盘交互
虽然设置了 role="button" 和 tabIndex={0} 使元素可通过键盘聚焦,但缺少键盘事件处理器(onKeyDown 或 onKeyPress)来响应 Enter 和 Space 键的按下。这会导致键盘用户无法激活该按钮。
应用以下修改添加键盘支持:
+const handleMinusKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ handleReduce(e as any)
+ }
+}
+
<div
className={`${classPrefix}-minus`}
onClick={handleReduce}
+ onKeyDown={handleMinusKeyDown}
role="button"
tabIndex={0}
aria-label={locale.arithmetic.minus}
aria-disabled={Number(shadowValue) <= Number(min) || disabled}
>📝 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.
| <div | |
| className={`${classPrefix}-minus`} | |
| onClick={handleReduce} | |
| role="button" | |
| tabIndex={0} | |
| aria-label={locale.arithmetic.minus} | |
| aria-disabled={Number(shadowValue) <= Number(min) || disabled} | |
| > | |
| const handleMinusKeyDown = (e: React.KeyboardEvent) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault() | |
| handleReduce(e as any) | |
| } | |
| } | |
| <div | |
| className={`${classPrefix}-minus`} | |
| onClick={handleReduce} | |
| onKeyDown={handleMinusKeyDown} | |
| role="button" | |
| tabIndex={0} | |
| aria-label={locale.arithmetic.minus} | |
| aria-disabled={Number(shadowValue) <= Number(min) || disabled} | |
| > |
🤖 Prompt for AI Agents
In src/packages/inputnumber/inputnumber.tsx around lines 194 to 201, the minus
control is focusable but has no keyboard handler so Enter/Space won't activate
it; add an onKeyDown handler that listens for Enter (KeyEnter) and Space (Space
or ' ') keys, prevents default, checks the same disabled/disabled-by-min
condition as onClick (Number(shadowValue) <= Number(min) || disabled), and calls
handleReduce when allowed; ensure the handler is bound to the element and that
it mirrors the onClick behavior (no-op when disabled/min reached) so keyboard
users can activate the control.
| <div | ||
| className={`${classPrefix}-add`} | ||
| role="button" | ||
| tabIndex={0} | ||
| aria-label={locale.arithmetic.plus} | ||
| aria-disabled={Number(shadowValue) >= Number(max) || disabled} | ||
| onClick={handlePlus} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
添加键盘事件处理器以支持完整的键盘交互
与减号按钮相同的问题:缺少键盘事件处理器来响应 Enter 和 Space 键。
应用以下修改添加键盘支持:
+const handlePlusKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ handlePlus(e as any)
+ }
+}
+
<div
className={`${classPrefix}-add`}
role="button"
tabIndex={0}
aria-label={locale.arithmetic.plus}
aria-disabled={Number(shadowValue) >= Number(max) || disabled}
onClick={handlePlus}
+ onKeyDown={handlePlusKeyDown}
>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/packages/inputnumber/inputnumber.tsx around lines 225 to 232, the plus
button lacks a keyboard event handler so Enter and Space don’t activate it; add
an onKeyDown handler (mirroring the minus button) that listens for Enter (key
=== "Enter" or keyCode 13) and Space (key === " " or key === "Spacebar" or
keyCode 32), calls handlePlus when those keys are pressed, and prevents the
default action; keep the existing role="button" and tabIndex so keyboard users
can focus and activate the control.
| rightIconAriaLabel: '', | ||
| } as TaroNoticeBarProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
修复 Close 图标上的 ariaLabel 类型错误,建议只在外层容器加无障碍文案
静态检查报错信息:Type '{ size: number; ariaLabel: string; }' is not assignable to type 'IntrinsicAttributes & SVG_IconProps'. 指向的是:
{rightIcon || <Close size={12} ariaLabel={locale.close} />}说明 @nutui/icons-react-taro 的 Close 组件 props 中并不支持 ariaLabel 字段。
鉴于你已经在外层 View 上通过 ariaLabel={${closeable ? locale.close : rightIconAriaLabel}} 提供了可访问名称,这里图标本身可以视为纯装饰,不再需要额外 aria 文案。
建议修改为:
- {rightIcon || <Close size={12} ariaLabel={locale.close} />}
+ {rightIcon || <Close size={12} />}这样既消除了 TS 报错,也符合常见实践(把可访问名称放在可点击容器上,图标纯视觉)。
请在本地跑一遍 tsc/构建,确认该改动能消除现有类型错误。
Also applies to: 66-67, 79-80, 489-499
---
🏁 Script executed:
```shell
#!/bin/bash
# 查看NoticeBar Taro组件的完整代码
cat src/packages/noticebar/noticebar.taro.tsx
Length of output: 15239
🏁 Script executed:
#!/bin/bash
# 搜索Close组件的导入和类型定义
rg -n "Close" src/packages/noticebar/noticebar.taro.tsx -B2 -A2Length of output: 714
🏁 Script executed:
#!/bin/bash
# 查找icons-react-taro中Close组件的类型定义
fd -e "tsx" -e "ts" -e "d.ts" src/packages | xargs rg -l "export.*Close" 2>/dev/null | head -5Length of output: 551
🏁 Script executed:
#!/bin/bash
# 检查useCallback的使用情况
rg -n "useCallback" src/packages/noticebar/noticebar.taro.tsx -A5Length of output: 1186
---
🏁 Script executed:
```shell
#!/bin/bash
# 查找@nutui/icons-react-taro的类型定义文件
find node_modules/@nutui/icons-react-taro -name "*.d.ts" 2>/dev/null | head -10
Length of output: 0
🏁 Script executed:
#!/bin/bash
# 搜索Close和Icon Props的类型定义
rg -n "Close|SVG_IconProps" --type=ts --type=tsx -g "*.d.ts" 2>/dev/null | head -20Length of output: 0
🏁 Script executed:
#!/bin/bash
# 查找项目中的tsconfig或type检查错误
rg -n "ariaLabel.*Close|Close.*ariaLabel" --type=ts --type=tsx -C3Length of output: 27
🏁 Script executed:
#!/bin/bash
# 检查是否有tsc错误日志或build日志
find . -name "*.log" -o -name "tsconfig*.json" 2>/dev/null | head -10Length of output: 423
---
🌐 Web query:
```web
@nutui/icons-react-taro Close component ariaLabel props support
💡 Result:
Do you mean the Close icon component from @nutui/icons-react-taro — asking whether it accepts an ariaLabel (or aria-label) prop so you can set accessible label for screen readers? If so, do you want: (A) how to pass an accessible label (example code), or (B) whether the package currently supports an ariaLabel prop (I can check the package source/docs and open an issue/PR if it doesn't)?
---
🌐 Web query:
```web
nutui icons react taro Close icon props type definition
💡 Result:
I couldn't find an official published TypeScript declaration for the Close icon in @nutui/icons-react-taro. Based on usages and common icon-component conventions, the Close icon component accepts standard SVG/React props. A practical TypeScript interface you can use is:
interface IconProps {
size?: number | string;
color?: string;
className?: string;
style?: React.CSSProperties;
onClick?: React.MouseEventHandler<SVGElement | HTMLElement>;
title?: string; // for accessibility
[key: string]: any;
}
Example usage in NutUI/Taro projects shows icons imported from @nutui/icons-react-taro and used like . [1][2]
Sources:
[1] Example project using Close and other icons from @nutui/icons-react-taro.
[2] Package listing / CDN info for @nutui/icons-react-taro (shows package exists but no public .d.ts found).
修复 Close 图标的 ariaLabel 类型错误和 useCallback 依赖缺失
在 renderRightIcon 中存在两个需要立即修复的问题:
1. Close 组件上的 ariaLabel 类型错误(第 498 行)
Close 组件不支持 ariaLabel 属性。由于外层 View 已通过 ariaLabel={closeable ? locale.close : rightIconAriaLabel} 提供无障碍标签,图标可视为装饰性,应移除:
- {rightIcon || <Close size={12} ariaLabel={locale.close} />}
+ {rightIcon || <Close size={12} />}2. useCallback 依赖列表缺失(第 489-502 行)
renderRightIcon 函数内使用了 locale 和 rightIconAriaLabel,但依赖列表中缺少这两项,导致闭包陈旧:
const renderRightIcon = useCallback(
() => (/* ... */),
- [rightIcon, closeable, handleClickIcon]
+ [rightIcon, closeable, handleClickIcon, locale, rightIconAriaLabel]
)缺失的依赖会导致:当语言或 rightIconAriaLabel 改变时,组件不会更新对应的无障碍标签。
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/packages/noticebar/noticebar.taro.tsx around lines 489-502 (specifically
line ~498), remove the unsupported ariaLabel prop from the Close icon component
so the decorative icon doesn't receive accessibility text (keep the outer View's
ariaLabel={closeable ? locale.close : rightIconAriaLabel} for the accessible
label), and update the useCallback dependency array for renderRightIcon to
include locale and rightIconAriaLabel so the callback refreshes when locale or
rightIconAriaLabel change.
| const { locale } = useConfig() | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
renderRightIcon 的 useCallback 依赖项缺少 locale / rightIconAriaLabel,可能导致多语言或文案更新不生效
renderRightIcon 使用了 locale.close 和 rightIconAriaLabel:
const { locale } = useConfig()
const renderRightIcon = useCallback(
() => (
<>
{rightIcon || closeable ? (
<View
className="nut-noticebar-box-right-icon"
onClick={handleClickIcon}
ariaLabel={`${closeable ? locale.close : rightIconAriaLabel}`}
>
{rightIcon || <Close size={12} />}
</View>
) : null}
</>
),
[rightIcon, closeable, handleClickIcon]
)但依赖数组里只有 [rightIcon, closeable, handleClickIcon],缺少 locale 和 rightIconAriaLabel,会导致:
- 运行时切换 ConfigProvider 多语言时,右侧图标的文案不会随着
locale.close更新; - 外层通过 props 传入新的
rightIconAriaLabel时,回调闭包仍然使用旧值。
建议补齐依赖:
- ),
- [rightIcon, closeable, handleClickIcon]
-)
+ ),
+ [rightIcon, closeable, handleClickIcon, locale, rightIconAriaLabel]
+)避免出现多语言/a11y 文案“卡死在初始值”的问题。
请在本地打开有动态切换语言或动态修改 rightIconAriaLabel 的场景,确认更新后 aria 文案会随之变化。
Also applies to: 489-499
我需要检查相关文件以验证这个评论中的问题。让我开始检查代码。
---
🏁 Script executed:
```shell
#!/bin/bash
# 检查 noticebar.taro.tsx 文件,特别是 renderRightIcon 函数和其依赖项
cat src/packages/noticebar/noticebar.taro.tsx | head -150
Length of output: 3819
🏁 Script executed:
#!/bin/bash
# 查看第 70-90 行范围的代码
sed -n '70,90p' src/packages/noticebar/noticebar.taro.tsxLength of output: 807
🏁 Script executed:
#!/bin/bash
# 查看第 480-510 行范围的代码,检查"Also applies to: 489-499"部分
sed -n '480,510p' src/packages/noticebar/noticebar.taro.tsxLength of output: 827
🏁 Script executed:
#!/bin/bash
# 在整个文件中查找 renderRightIcon 的所有出现
rg -n 'renderRightIcon' src/packages/noticebar/noticebar.taro.tsx -A 15Length of output: 1280
🏁 Script executed:
#!/bin/bash
# 在整个文件中查找 useCallback 出现
rg -n 'useCallback' src/packages/noticebar/noticebar.taro.tsx -B 2 -A 10Length of output: 1983
修复 renderRightIcon 中缺失的 useCallback 依赖项
经验证,renderRightIcon 函数(第489-504行)确实在回调中使用了 locale.close 和 rightIconAriaLabel,但依赖数组只包含 [rightIcon, closeable, handleClickIcon]。这会导致在 ConfigProvider 切换语言或外层修改 rightIconAriaLabel prop 时,闭包仍使用旧值,造成多语言和无障碍文案无法更新。
需要将依赖项补充为:
const renderRightIcon = useCallback(
() => (
<>
{rightIcon || closeable ? (
<View
className="nut-noticebar-box-right-icon"
onClick={handleClickIcon}
- ariaLabel={`${closeable ? locale.close : rightIconAriaLabel}`}
+ ariaLabel={`${closeable ? locale.close : rightIconAriaLabel}`}
>
- {rightIcon || <Close size={12} ariaLabel={locale.close} />}
+ {rightIcon || <Close size={12} ariaLabel={locale.close} />}
</View>
) : null}
</>
),
- [rightIcon, closeable, handleClickIcon]
+ [rightIcon, closeable, handleClickIcon, locale, rightIconAriaLabel]
)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/packages/noticebar/noticebar.taro.tsx around lines 79 and 489-504, the
renderRightIcon useCallback is missing dependencies: it currently has
[rightIcon, closeable, handleClickIcon] but the callback reads locale.close and
rightIconAriaLabel; update the dependency array to include locale.close and
rightIconAriaLabel (e.g., [rightIcon, closeable, handleClickIcon, locale?.close,
rightIconAriaLabel]) so the callback updates when language or aria label
changes.
| ariaLabel={ | ||
| ariaLabel || | ||
| `${ | ||
| symbol && position === 'before' ? replaceSpecialChar(symbol) : '' | ||
| }${formatThousands(price)}${ | ||
| checkPoint(price) || digits ? '.' : '' | ||
| }${formatDecimal(price)}${ | ||
| symbol && position === 'after' ? replaceSpecialChar(symbol) : '' | ||
| }` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeScript 类型错误:ariaLabel 不是 Taro Text 组件的有效属性
静态分析显示构建失败:ariaLabel 无法赋值给 TextProps。在 Taro 中,Text 和 View 组件使用 aria-label(kebab-case)作为属性名,而不是 ariaLabel(camelCase)。
应用此差异修复类型错误:
<Text
className={`${classPrefix} ${classPrefix}-${color} ${className}`}
style={style}
- ariaLabel={
+ aria-label={
ariaLabel ||
`${
symbol && position === 'before' ? replaceSpecialChar(symbol) : ''
}${formatThousands(price)}${
checkPoint(price) || digits ? '.' : ''
}${formatDecimal(price)}${
symbol && position === 'after' ? replaceSpecialChar(symbol) : ''
}`
}
>Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 GitHub Check: build
[failure] 174-174:
Type '{ children: Element; className: string; style: CSSProperties | undefined; ariaLabel: string; }' is not assignable to type 'IntrinsicAttributes & TextProps'.
🤖 Prompt for AI Agents
In src/packages/price/price.taro.tsx around lines 174 to 183, the prop ariaLabel
is not a valid TextProps for Taro (Text/View expect kebab-case aria-label), so
replace the camelCase prop with the kebab-case attribute; in JSX you can supply
it as an escaped prop name (e.g. {'aria-label': value}) or by using a spread
object with the key 'aria-label' so the generated prop is aria-label and
TypeScript accepts it, keeping the same computed string value currently assigned
to ariaLabel.
🤔 这个变动的性质是?
🔗 相关 Issue
💡 需求背景和解决方案
☑️ 请求合并前的自查清单
Summary by CodeRabbit
发布说明
✏️ Tip: You can customize this high-level summary in your review settings.