11import React , {
2+ useId ,
23 ReactElement ,
34 InputHTMLAttributes ,
45 KeyboardEvent ,
@@ -37,12 +38,19 @@ function useInitComponentStyle() {
3738/**
3839 *
3940 */
40- const InputAddon = ( { content } : { content : string } ) => (
41+ const InputAddon = ( {
42+ content,
43+ id,
44+ } : {
45+ content : string ;
46+ id : string | undefined ;
47+ } ) => (
4148 < Text
4249 tag = 'span'
4350 className = 'slds-form-element__addon'
4451 category = 'body'
4552 type = 'regular'
53+ id = { id }
4654 >
4755 { content }
4856 </ Text >
@@ -155,32 +163,52 @@ export const Input = createFC<InputProps, { isFormElement: boolean }>(
155163 prevValueRef . current = e . target . value ;
156164 } ) ;
157165
166+ const labelId = useId ( ) ;
167+
168+ const fallbackLabelFor = useId ( ) ;
169+ const labelFor = id ?? fallbackLabelFor ;
170+
171+ const originalPreAddonId = useId ( ) ;
172+ const preAddonId = addonLeft ? originalPreAddonId : undefined ;
173+
174+ const originalPostAddonId = useId ( ) ;
175+ const postAddonId = addonRight ? originalPostAddonId : undefined ;
176+
177+ const labelledBy = [ labelId , preAddonId , postAddonId ]
178+ . filter ( ( id ) => id !== undefined )
179+ . join ( ' ' ) ;
180+
181+ const errorId = useId ( ) ;
182+
158183 const { isFieldSetColumn } = useContext ( FieldSetColumnContext ) ;
159184 const inputClassNames = classnames (
160185 className ,
161186 bare ? 'slds-input_bare' : 'slds-input'
162187 ) ;
163188 const inputElem = readOnly ? (
164189 < Text
165- id = { id }
190+ id = { labelFor }
166191 type = 'regular'
167192 category = 'body'
168- className = 'slds-form-element__static'
193+ aria-labelledby = { labelledBy }
169194 >
170195 { value }
171196 </ Text >
172197 ) : (
173198 < input
174199 ref = { inputRef }
175200 className = { inputClassNames }
176- id = { id }
201+ id = { labelFor }
177202 type = { type }
178203 value = { value }
179204 defaultValue = { defaultValue }
180205 readOnly = { htmlReadOnly }
181206 { ...rprops }
182207 onChange = { onChange }
183208 onKeyDown = { onKeyDown }
209+ aria-labelledby = { labelledBy }
210+ aria-describedby = { error ? errorId : undefined }
211+ aria-invalid = { error ? true : undefined }
184212 />
185213 ) ;
186214
@@ -196,20 +224,26 @@ export const Input = createFC<InputProps, { isFormElement: boolean }>(
196224 ) ;
197225 contentElem = (
198226 < div className = { wrapperClassName } >
199- { addonLeft ? < InputAddon content = { addonLeft } /> : undefined }
227+ { addonLeft ? (
228+ < InputAddon content = { addonLeft } id = { preAddonId } />
229+ ) : undefined }
200230 { iconLeft ? < InputIcon icon = { iconLeft } align = 'left' /> : undefined }
201231 { inputElem }
202232 { iconRight ? < InputIcon icon = { iconRight } align = 'right' /> : undefined }
203- { addonRight ? < InputAddon content = { addonRight } /> : undefined }
233+ { addonRight ? (
234+ < InputAddon content = { addonRight } id = { postAddonId } />
235+ ) : undefined }
204236 </ div >
205237 ) ;
206238 }
207239 if ( isFieldSetColumn || label || required || error || cols ) {
208240 const formElemProps = {
209- id,
241+ id : labelId ,
242+ controlId : labelFor ,
210243 label,
211244 required,
212245 error,
246+ errorId,
213247 readOnly,
214248 cols,
215249 tooltip,
0 commit comments