From 12e4d8f8fbf7f2e336e67a5c2d432ac7118b29e2 Mon Sep 17 00:00:00 2001 From: Toppanto Bence Date: Thu, 24 Sep 2026 14:30:44 +0200 Subject: [PATCH] fix(ui-date-input,ui-text-input): label the input with aria-labelledby MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DateInput passed `aria-label={callRenderProp(renderLabel)}` to TextInput. When `renderLabel` is a node rather than a string, `callRenderProp` returns a React element, so the input advertised an object as its accessible name instead of readable text. DateInput now generates a deterministic id, hands it to TextInput as `labelId`, and points `aria-labelledby` at it, so the input is labelled by the real rendered label element. TextInput v2 gains an optional `labelId` prop that overrides its generated label id. It falls back to the previous `deterministicId('TextInput-label')` value when omitted, so existing callers are unaffected. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) --- packages/ui-date-input/src/DateInput/v2/index.tsx | 10 ++++++++-- packages/ui-text-input/src/TextInput/v2/index.tsx | 2 +- packages/ui-text-input/src/TextInput/v2/props.ts | 6 ++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/ui-date-input/src/DateInput/v2/index.tsx b/packages/ui-date-input/src/DateInput/v2/index.tsx index 2ebc70c4bf..042696a5e5 100644 --- a/packages/ui-date-input/src/DateInput/v2/index.tsx +++ b/packages/ui-date-input/src/DateInput/v2/index.tsx @@ -33,7 +33,11 @@ import { } from '@instructure/ui-icons' import { Popover } from '@instructure/ui-popover/latest' import { TextInput } from '@instructure/ui-text-input/latest' -import { callRenderProp, passthroughProps } from '@instructure/ui-react-utils' +import { + callRenderProp, + passthroughProps, + useDeterministicId +} from '@instructure/ui-react-utils' import { getLocale, getTimezone } from '@instructure/ui-i18n' import type { DateInputProps } from './props' @@ -258,6 +262,7 @@ const DateInput = forwardRef( } const selectedDate = parseDate(value)[1] + const labelId = useDeterministicId('TextInput-label')() return ( { super(props) this._defaultId = props.deterministicId!() this._messagesId = props.deterministicId!('TextInput-messages') - this._labelId = props.deterministicId!('TextInput-label') + this._labelId = props.labelId ?? props.deterministicId!('TextInput-label') } ref: Element | null = null diff --git a/packages/ui-text-input/src/TextInput/v2/props.ts b/packages/ui-text-input/src/TextInput/v2/props.ts index e72c3eb293..8a9f5a405f 100644 --- a/packages/ui-text-input/src/TextInput/v2/props.ts +++ b/packages/ui-text-input/src/TextInput/v2/props.ts @@ -59,6 +59,11 @@ type TextInputOwnProps = { */ id?: string + /** + * id for the label. If empty, it's auto generated + */ + labelId?: string + /** * the selected value (must be accompanied by an `onChange` prop) */ @@ -228,6 +233,7 @@ const allowedProps: AllowedPropKeys = [ 'renderLabel', 'type', 'id', + 'labelId', 'value', 'defaultValue', 'interaction',