跳到主要内容

Input 新增 allowClear + 补充 visual case(DES-126)

版本: 0.2.00.2.2 · 类型: ✨ 新功能

2026-06-17 Input 清空按钮补充 focus-visible 焦点环(a11y)

PR #2489 review 反馈

改动文件

  • src/components/Input/styles.ts
  • src/components/Input/__tests__/Input.test.tsx

改动内容

  • INPUT_CLEAR_CLASS 补充 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-(--Labels-Primary),对齐 ui/inputui/buttonCalendarTree 既有键盘焦点环约定(ring-1 + Labels/Primary),为键盘聚焦清空按钮提供一致的可见指示(WCAG 2.4.7)。
  • 补充单测断言清空按钮带 focus 环 class。

根因

清空按钮为可聚焦 <button>,原仅依赖浏览器默认焦点环,缺少与设计系统一致的 token 焦点环。


2026-06-17 抽取 useClearableInput 复用 Input / InputSearch 清空逻辑

DES-126(sub issue of DES-125 Input);本文件当日 Input 家族改动(allowClear / visual case / 语义 class / useClearableInput 重构)统一归属此 sub issue

改动文件

  • src/components/Input/use-clearable-input.ts(新增)
  • src/components/Input/Input.tsx
  • src/components/Input/InputSearch.tsx

改动内容

  • Input(allowClear)与 InputSearch 中逐行重复的清空逻辑(受控/非受控 value 托管、聚焦态、清空按钮可见性、合成 change 事件回填空值、ref 转发)抽取为共享 hook useClearableInput,消除约 50 行重复。
  • hook 新增 enabled 开关:Inputprefix/suffix(无 allowClear)时不再托管内部 value,避免每次输入触发无效 setInternalValue 与 re-render。
  • 纯结构重构,对外 API、渲染输出与 visual 快照不变;Input 家族 86 条单测全部通过。

2026-06-17 Input 新增 allowClear 清空能力 + 补充 visual case

改动文件

  • src/components/Input/Input.tsx
  • src/components/Input/styles.ts
  • src/components/Input/InputDesignSpec.md
  • src/components/Input/__tests__/Input.test.tsx
  • src/components/Input/__tests__/Input.ct.spec.tsx
  • src/components/Input/__tests__/InputFixture.visual.tsx(新增)
  • scripts/visual/visual-diff-config.ts
  • packages/design-site/docs/components/atomic/input.mdx

改动内容

  • Input 新增 allowClear?: booleanonClear?: () => void:聚焦且有内容且未禁用时,在 suffix 前渲染 × 清空按钮;失焦或无内容时隐藏。支持受控 / 非受控,点击回填空值 onChange + onClear 并重新聚焦。
  • 清空按钮以 onMouseDown preventDefault 防止点击时 input 先失焦导致按钮消失。
  • 新增 INPUT_CLEAR_CLASS:28×28 可点击区域(20px XmarkIcon + 4px 内边距),圆角 5px,图标 Labels/Primary,hover 浅灰底。
  • allowClear 时统一走 wrapper 渲染路径(与 prefix/suffix 一致);无 prefix/suffix/allowClear 时保持原始透传路径不变。
  • 补充 visual case:覆盖 Figma Input 组件集(21028:552)全部 12 组变体(State × Filled × Status),逐变体与 Figma 节点 1:1 对比。每个 case 还原 Figma 示例内容——InputFixture.visual.tsx 内联 Figma icon_user(节点 21039:5202)作前缀、HelpIcon 作 28×28 后缀按钮,Focused+Filled 额外展示 allowClear 清空按钮,并在 visual-diff-config.ts 登记 12 个节点映射。
  • design-site 新增 Clearable 示例与 allowClear / onClear Props 行。
  • 修复 getInputWrapperClass(prefix/suffix 路径)错误态聚焦边框被黑色覆盖的问题:error 时聚焦边框保持 Status/Destructive(红),对齐 Figma focused+error 变体与无插槽路径 getInputTokenClass 的行为。

根因

Figma Input 组件集(节点 21028:552)更新,Filled=True + Focused 变体(21028:557 Default / 21069:7015 Error)新增 Clear 实例(icon_xmark_for_panel,28×28)。design 层补齐对应能力并对齐视觉。


2026-06-17 InputSearch / InputPassword 补充 visual case + Search 清空改聚焦门控

改动文件

  • src/components/Input/InputSearch.tsx
  • src/components/Input/__tests__/InputSearch.test.tsx
  • src/components/Input/__tests__/InputSearch.ct.spec.tsx(新增)
  • src/components/Input/__tests__/InputPassword.ct.spec.tsx(新增)
  • scripts/visual/visual-diff-config.ts
  • scripts/visual/generate-visual-gallery.ts
  • packages/design-site/docs/components/atomic/input.mdx

改动内容

  • InputSearch 清空按钮改为聚焦门控:原为「有内容即显示」,改为「聚焦且有内容且未禁用时显示」,对齐更新后的 Figma Search Field(Normal+Filled 无 ×,Focused+Filled 才有 ×)并与 Input 行为一致。新增 onMouseDown preventDefault 防止点击失焦;清空图标由 CloseIconPlaceholder 换为契约图标 XmarkIcon,与 Input 清空按钮统一。
  • 补充 InputSearch visual case:覆盖 Search Field 组件集(277:24750)全部 16 组变体(Size × Style × Filled × State),逐变体与 Figma 1:1 对比。
  • 补充 InputPassword visual case:覆盖 Password 组件集(21054:763)全部 16 组变体(State × Filled × Visible × Status)。Masked 渲染圆点 + eye-close,Visible 经点击 toggle 渲染明文 + eye-open;Normal+Visible 通过 toggle 后失焦还原。
  • visual-diff-config.ts 登记 InputSearch / InputPassword 各 16 个节点映射;新增可选 screenshotDir 字段,使子组件截图归属 Input 目录,generate-visual-gallery.ts 据此解析截图路径。
  • design-site Search 的「Controlled + Clear」示例说明更新为「聚焦且有内容时显示」。

根因

补齐 Input 家族(Search / Password)的 visual 覆盖;对齐更新后的 Figma Search Field 清空按钮聚焦门控行为(用户确认采用对齐 Figma 方案)。


2026-06-17 Input 家族补充语义化 class 标识(BEM)

改动文件

  • src/components/Input/Input.tsxstyles.ts
  • src/components/Input/InputSearch.tsx(经 input-search-styles.ts)、input-search-styles.ts
  • src/components/Input/InputPassword.tsxinput-password-styles.ts
  • src/components/Input/__tests__/Input.test.tsxInputSearch.test.tsxInputPassword.test.tsx

改动内容

  • 为 Input 家族所有 DOM 节点补充语义化 class(plaud-<component> 块 + plaud-<component>__<element> 子元素,置于 class 串首,遵循 Toast/Skeleton 既有约定),供测试选择器与样式定位,避免出现仅有 Tailwind utility class 的节点:
    • Input:plaud-input(裸 input / affix 容器)、plaud-input__prefix / __content / __field / __clear / __suffix
    • InputSearch:plaud-input-search(容器)、plaud-input-search__icon / __field / __clear
    • InputPassword:plaud-input-password(根)、plaud-input-password__toggle
  • 三个组件各补充语义 class 断言锁定。
  • 语义 class 不带 CSS 规则,渲染与 visual 快照不变。

2026-06-17 清空合成事件改用原生 input 派发(修复 PR review finding)

关联:PR #2489 review finding(use-clearable-input.ts:99

问题

handleClear 通过 Object.create 构造的合成 ChangeEvent 仅含 target / currentTarget,缺少 nativeEvent / type / bubbles / preventDefault 等标准 SyntheticEvent 字段。消费方(React Hook Form / Formik 等)读取 e.nativeEvent 会得到 undefined 并静默失败;e.type 取到的是 input 的 type 属性值(如 'text')而非事件类型 'change'

改动内容

  • 改用原生 value setter 写空值并派发真实 input 事件,让 React 合成事件管道正常运转:
    Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set?.call(input, '');
    input.dispatchEvent(new Event('input', { bubbles: true }));
    消费方拿到的是字段完整的标准 SyntheticEvent;受控与非受控模式都经由 onChangehandleChange)收到空值,无需再手动维护内部 state(移除 setInternalValue('') 与手动 onChange 构造)。
  • 受控模式两条单测改为在 onChange同步读取 e.target.value:真实事件的 event.target 是实时 DOM 节点,受控且父组件未回填 value 时 React 会在事件后将其还原回原值,原断言异步读取会拿到还原值;同步读取与真实消费方时序一致,并真实反映受控行为。

根因

原合成事件为「够用即可」的最小实现,与 React SyntheticEvent 契约不符,对依赖 nativeEvent / type 的表单库存在静默失败风险。


2026-06-17 Input 家族 review finding 二次修复(focus-visible / 聚焦态 re-render / aria-label 本地化)

关联:PR #2489 review findings

改动文件

  • src/components/Input/input-search-styles.ts
  • src/components/Input/Input.tsxInputSearch.tsx
  • src/components/Input/__tests__/Input.test.tsxInputSearch.test.tsx
  • packages/design-site/docs/components/atomic/input.mdx(含 zh-CN)

改动内容

  • [critical] InputSearch 清空按钮补 focus-visible 焦点环INPUT_SEARCH_CLEAR_CLASS 此前未同步 commit 52389cdc6 为 Input 补的焦点环,键盘 Tab 到清空按钮无可见指示(违反 WCAG 2.4.7)。补充 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-(--Labels-Primary),与 Input 一致;新增焦点环单测锁定。
  • [perf] Input 无 allowClear 时不再托管聚焦态:affix 模式(仅 prefix/suffix、allowClear=false)下 onFocus/onBlur/onChange 原先恒绑 hook 版处理器,每次聚焦/失焦都会 setFocused 触发无效 re-render(showClear 恒为 false)。改为仅 allowClear === true 时使用 hook 版处理器,否则直接透传原始回调,消除相对旧代码的性能回退。
  • [convention] 清空按钮 aria-label 支持本地化:Input / InputSearch 新增 clearButtonAriaLabel?: string,默认分别为 '清空输入' / '清空搜索',多语言消费方可覆盖;补充默认值与自定义值单测,design-site Props 表(中英文)登记该 prop。

根因

首轮 allowClear 实现遗漏 InputSearch 焦点环同步、affix 场景聚焦态托管引入无效 re-render,以及组件库层硬编码中文 aria-label 不利于多语言消费方。