Skip to content

fix(text-input): do not clobber aria-invalid computed by Field validation#627

Open
MaxLee-dev wants to merge 2 commits into
mainfrom
worktree-text-input-aria-invalid-fix
Open

fix(text-input): do not clobber aria-invalid computed by Field validation#627
MaxLee-dev wants to merge 2 commits into
mainfrom
worktree-text-input-aria-invalid-fix

Conversation

@MaxLee-dev

@MaxLee-dev MaxLee-dev commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Related Issues

Description of Changes

Summary by CodeRabbit

  • Bug Fixes

    • Fixed text input validation state so accessibility attributes correctly reflect invalid fields.
    • Prevented the input from overriding the validation status when it is determined automatically by the form field.
  • Tests

    • Added coverage for invalid email entry and blur behavior to confirm the invalid state and error message appear as expected.

TextInputaria-invalid={invalid}를 항상 명시적으로 전달하고 있어, invalid prop이 지정되지 않은 경우 undefined 값이 Base UI Field 검증이 계산한 aria-invalid="true"를 덮어쓰는 문제를 수정했습니다.

증상: Field 검증이 실패하면 data-invalid 속성은 붙어서 시각적으로는 invalid 상태로 보이지만, aria-invalid가 제거되어 스크린 리더 등 보조기술에는 invalid로 전달되지 않는 접근성 결함이 있었습니다.

수정: invalid prop이 실제로 지정된 경우에만 aria-invalid를 전달하도록 변경했습니다. prop이 없으면 Base UI가 계산한 값이 그대로 유지됩니다.

상황 Before After
Field 검증 실패 + invalid prop 미지정 aria-invalid 없음 (덮어씀) aria-invalid="true" (Base UI 계산값 유지)
invalid={true} 명시 aria-invalid="true" aria-invalid="true" (동일)
invalid={false} 명시 aria-invalid 없음 aria-invalid 없음 (동일)

Screenshots

Checklist

  • The PR title follows the Conventional Commits convention. (e.g., feat, fix, docs, style, refactor, test, chore)
  • I have added tests for my changes.
  • I have updated the Storybook or relevant documentation.
  • I have added a changeset for this change. (e.g., for any changes that affect users, such as component prop changes or new features).
  • I have performed a self-code review.
  • I have followed the project's coding conventions and component patterns.

🤖 Generated with Claude Code

…tion

TextInput이 aria-invalid={invalid}를 항상 명시 전달해, invalid prop이
없을 때 undefined가 Base UI Field 검증이 계산한 aria-invalid="true"를
덮어쓰고 있었다 — data-invalid는 붙지만 보조기술에는 invalid로 전달되지
않는 접근성 결함. invalid prop이 실제로 지정된 경우에만 전달하도록 수정.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MaxLee-dev MaxLee-dev requested a review from noahchoii as a code owner July 7, 2026 06:52
@changeset-bot

changeset-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a18800a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@vapor-ui/core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vapor-ui Ready Ready Preview, Comment Jul 7, 2026 7:21am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@MaxLee-dev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d6daafca-fee2-4efa-be2c-a27624f0014a

📥 Commits

Reviewing files that changed from the base of the PR and between 5630706 and a18800a.

📒 Files selected for processing (2)
  • packages/core/src/components/text-input/text-input.test.tsx
  • packages/core/src/components/text-input/text-input.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-text-input-aria-invalid-fix

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

❤️ Share

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

@vapor-ui

vapor-ui commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

All tests passed!

Tests Passed Failed Duration Report
156 156 0 2m 24s Open report ↗︎

Click here if you need to update snapshots.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/core/src/components/text-input/text-input.test.tsx (1)

85-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

테스트 로직은 정확하나 axe 접근성 검증 보강 고려

이 테스트는 Field 검증 시나리오에서 aria-invalid가 유지되는지 정확히 검증하고 있어 버그 수정 목적에 부합합니다. 다만 이번 수정 자체가 접근성(ARIA) 관련이라는 점을 고려하면, vitest-axe로 렌더링 결과물의 접근성 위반 여부까지 함께 검증하면 회귀 방지에 도움이 될 것입니다.

♻️ 제안: axe 검증 추가
+        expect(await axe(rendered.container)).toHaveNoViolations();
         expect(rendered.getByText('Please enter a valid email')).toBeInTheDocument();
         expect(input).toHaveAttribute('aria-invalid', 'true');

As per coding guidelines, "Add vitest-axe accessibility checks for public components when feasible."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/components/text-input/text-input.test.tsx` around lines 85
- 101, The current test in TextInput covers the Field validation behavior
correctly, but it should also assert accessibility with vitest-axe since this is
an ARIA-related public component. In the existing test case around TextInput and
Field.Root, add an axe check against the rendered output after interaction,
alongside the current aria-invalid assertion, so regressions in accessibility
are caught in the same scenario.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/components/text-input/text-input.tsx`:
- Around line 61-63: The TextInput aria-invalid passthrough is still forwarding
false as an explicit value, which can override the Field validation state.
Update the conditional spread in text-input.tsx inside the TextInput component
to match the select.tsx and multi-select.tsx pattern by using invalid ||
undefined so only a true invalid value is passed through and false is omitted.

---

Nitpick comments:
In `@packages/core/src/components/text-input/text-input.test.tsx`:
- Around line 85-101: The current test in TextInput covers the Field validation
behavior correctly, but it should also assert accessibility with vitest-axe
since this is an ARIA-related public component. In the existing test case around
TextInput and Field.Root, add an axe check against the rendered output after
interaction, alongside the current aria-invalid assertion, so regressions in
accessibility are caught in the same scenario.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ac18b526-1f37-42c9-b9f2-b9992dbb18ba

📥 Commits

Reviewing files that changed from the base of the PR and between d90b538 and 5630706.

📒 Files selected for processing (3)
  • .changeset/thick-planes-refuse.md
  • packages/core/src/components/text-input/text-input.test.tsx
  • packages/core/src/components/text-input/text-input.tsx

Comment thread packages/core/src/components/text-input/text-input.tsx Outdated
invalid={false}도 aria-invalid="false"로 전달돼 Field 검증이 계산한
값을 덮어쓰던 문제 수정. 단, Base UI Input은 aria-invalid 키가
존재하기만 해도 계산값을 덮어쓰므로 invalid || undefined 대신
truthy 조건부 스프레드를 유지한다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MaxLee-dev

Copy link
Copy Markdown
Contributor Author

Fixes Applied

Addressed 1 CodeRabbit feedback item (invalid=false should not be forwarded as aria-invalid="false").

Note: the literal suggestion (aria-invalid={invalid || undefined}) breaks the Field validation test — Base UI's Input overrides the computed aria-invalid whenever the key is present in props, even with an undefined value. Kept the conditional spread instead, now gated on truthy: {...(invalid && { 'aria-invalid': true })}. Added a regression test for invalid={false} inside Field.Root.

Files modified:

  • packages/core/src/components/text-input/text-input.tsx
  • packages/core/src/components/text-input/text-input.test.tsx

Commit: a18800a8

@noahchoii noahchoii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 부분은 사용자가 입력한 invalid와 시스템에 의해 설정된 invalid가 달라서 발생하는 문제 같아서, 좀 더 근본적으로 해결할 필요가 있을 것 같네요..!

계속 우선순위가 밀리고 있던 내용이지만, invalid 상태의 관리 책임을 시스템에 전부 위임할지, 아니면 지금처럼 디자이너와 공동으로 관리할 것인지 이 참에 논의를 해보면 좋을 것 같아요!

Comment on lines +61 to +63
// true일 때만 키를 추가 — aria-invalid 키가 존재하기만 해도(값이 false/undefined여도)
// Base UI가 Field 검증에서 계산한 aria-invalid를 덮어써 버린다
{...(invalid && { 'aria-invalid': true })}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NOTE: 프리픽스를 추가해서 주석 앵커들을 한 번에 트래킹 할 수 있도록 하면 더 좋을 것 같아요!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InputGroup에도 남기긴 했지만 changeset을 어떻게 작성하면 좋을지 같이 정리해두고 기준으로 삼으면 좋을 것 같아요!

우선 저는 아래 예시들처럼 체인지로그를 작성하면 좋을 것 같아요!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants