Skip to content

fix(form-control): import FormControlHelperText from the correct file#3422

Open
patrickwehbe wants to merge 1 commit into
gluestack:mainfrom
patrickwehbe:fix/form-control-helper-text-import
Open

fix(form-control): import FormControlHelperText from the correct file#3422
patrickwehbe wants to merge 1 commit into
gluestack:mainfrom
patrickwehbe:fix/form-control-helper-text-import

Conversation

@patrickwehbe

Copy link
Copy Markdown

What

packages/gluestack-core/src/form-control/creator/index.tsx imports the helper-text component from the wrong file:

import FormControlHelper from './FormControlHelper';
import FormControlHelperText from './FormControlHelper'; // <- should be './FormControlHelperText'

So FormControl.Helper.Text gets built from the helper wrapper (FormControlHelper) instead of the helper-text wrapper. The error side one line above already does it right (FormControlErrorText from ./FormControlErrorText); this just makes the helper side match.

Why it matters (a11y)

FormControlHelper is not a neutral pass-through. It does two things the helper text should not:

// FormControlHelper.tsx
React.useEffect(() => {
  combinedProps?.setHasHelpText(true);
  return () => combinedProps?.setHasHelpText(false);
});
// ...
<StyledFormControlHelper {...combinedProps} id={combinedProps?.labelId} />

Because of the wrong import:

  1. The help text element is forced to id={labelId}, so it renders with the label's id. But useFormControl builds aria-describedby from helpTextId (${id}-helptext). Nothing in the DOM has that id, so the input's aria-describedby points at a non-existent element and the help text is never announced. axe flags this as aria-valid-attr-value (same failure mode reported for Radio.Group in Radio.Group (web): hidden radio inputs get aria-describedby referencing a non-existent element — axe flags aria-valid-attr-value #3410).
  2. setHasHelpText fires twice, once from FormControl.Helper and again from FormControl.Helper.Text, so the context flag gets toggled by an element that shouldn't own it.

The real FormControlHelperText.tsx already exists and is the correct thin wrapper (<StyledFormControlHelperText ref={ref} {...props}>), it was just never imported.

Fix

One line: import FormControlHelperText from ./FormControlHelperText.

Verifying

I rendered the two wrappers in jsdom with a realistic FormControl context (id=field-1, labelId=field-1-label, helpTextId=field-1-helptext):

  • before (Helper HOC): help text element id is field-1-label, setHasHelpText fires from it
  • after (HelperText HOC): help text element keeps field-1-helptext, no extra setHasHelpText

Both wrappers export the same (Styled) => forwardRef(...) shape, so the IFormControlComponentType typing is unaffected and the change is just behavioral.

Changeset included (patch on @gluestack-ui/core).

FormControl.Helper.Text was being built from the helper wrapper, not the
helper-text wrapper, because the creator imported FormControlHelperText
from './FormControlHelper'. The helper wrapper forces id={labelId} and
calls setHasHelpText, so the help text ended up with the label's id while
aria-describedby still pointed at the help text id, which no element had.
That breaks the description association for screen readers (and trips axe's
aria-valid-attr-value, same failure mode as gluestack#3410 on Radio.Group).

The correct FormControlHelperText component already exists and is a plain
pass-through; just import it from './FormControlHelperText', matching the
FormControlErrorText import right above it.
@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the GeekyAnts Labs Team on Vercel.

A member of the Team first needs to authorize it.

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.

1 participant