UI v0.19.2 Field접근성개선 - #36
Hidden character warning
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Code Review
This pull request improves the accessibility of the Field component by changing its wrapper from a label to a div and automatically injecting id, aria-describedby, and aria-invalid attributes into a single child element while linking the label via htmlFor. The review feedback suggests adding defense code to handle React.Fragment children, preserving existing aria attributes on the child element instead of overwriting them, and considering how custom components pass down these injected attributes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| */ | ||
|
|
||
| import { useId } from 'react' | ||
| import { useId, Children, isValidElement, cloneElement } from 'react' |
There was a problem hiding this comment.
| const child = Children.only(children) | ||
| if (isValidElement<InjectedInputProps>(child)) { | ||
| inputId = child.props.id ?? inputId | ||
| content = cloneElement(child, { | ||
| id: inputId, | ||
| 'aria-describedby': hasMessage ? messageId : undefined, | ||
| 'aria-invalid': error ? true : undefined, | ||
| }) | ||
| } |
There was a problem hiding this comment.
개선 제안 및 고려사항
- Fragment 방어 코드 추가:
children이 단일 요소이지만<React.Fragment>인 경우,cloneElement로 HTML 속성(id,aria-*)을 주입하면 런타임 경고가 발생합니다.child.type !== Fragment조건을 추가하여 이를 방지하는 것이 안전합니다. - 기존 aria 속성 보존: 자식 요소가 이미 자체적으로
aria-describedby나aria-invalid속성을 가지고 있을 수 있습니다. 현재 방식은 이를 완전히 덮어쓰므로, 기존 속성이 있다면 이를 유지하거나 병합(aria-describedby인 경우 공백으로 구분하여 결합)하도록 개선하는 것이 좋습니다. - 커스텀 컴포넌트 전달 한계 (구조적 고려사항):
DatePicker처럼 내부 대화형 요소(예:<button>)를<div>컨테이너로 감싸고...props를 컨테이너에 스프레드하는 컴포넌트의 경우,id와aria-*속성이 실제 버튼이 아닌 최상위<div>에 부여됩니다. 이로 인해 라벨 클릭 시 포커스가 가지 않거나 스크린 리더 접근성이 깨질 수 있습니다. 향후DatePicker등 커스텀 입력 컴포넌트들이id및aria-*속성을 내부 대화형 요소로 전달하도록 수정하는 작업을 함께 검토해 주세요.
const child = Children.only(children)
if (isValidElement<InjectedInputProps>(child) && child.type !== Fragment) {
inputId = child.props.id ?? inputId
const existingDescribedBy = child.props['aria-describedby']
content = cloneElement(child, {
id: inputId,
'aria-describedby': hasMessage
? (existingDescribedBy ? `${existingDescribedBy} ${messageId}` : messageId)
: existingDescribedBy,
'aria-invalid': error ? true : child.props['aria-invalid'],
})
}
개요
PR #35 리뷰(gemini-code-assist)에서 지적된 Field 컴포넌트 접근성/웹표준 이슈를 반영합니다. (v0.19.1 머지 후 도착한 리뷰라 별도 PR로 분리)
변경 내용
<label>전체 래핑 →<div>컨테이너 +<label htmlFor>연결 구조로 변경id/aria-describedby/aria-invalid자동 주입id를 가진 경우 기존 id 로 라벨 연결 (리뷰 제안 대비 보강)🤖 Generated with Claude Code