From bfb0737d1447e37d65e7cae3f13e33841a01a2bb Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Tue, 8 Sep 2026 09:47:32 +0200 Subject: [PATCH] Fix to ensure labels, descriptions and validation errors are correctly linked to their inputs (#50080) ## Problem `FormItemLayout` does not correctly binds inputs descriptions and validation messages to their inputs. This is because the input ids are generated and not correctly propagated to the `FormMessage` and `FormDescription` components. Besides, we still pass `name` or `id` directly to the inputs or `FormItemLayout` in some places. ## Solution - Fix `FormItemLayout` to correctly binds inputs descriptions and validation messages to their inputs - Fix incorrect usages - Fix Design System documentation ## How to test The issue is visible in production: - Open https://supabase.com/design-system/docs/ui-patterns/forms - Open the devtool and check the labels `for`, the description `id` and the input `id` or `aria-describedby` attributes. You'll see they often don't match Do the same on staging: - Open https://design-system-git-fix-a11y-form-input-descriptions-supabase.vercel.app/design-system/docs/ui-patterns/forms - Open the devtool and check the labels `for`, the description `id` and the input `id` or `aria-describedby` attributes. They now match Dashboard fixes: - https://studio-staging-git-fix-a11y-form-input-descriptions-supabase.vercel.app/dashboard/account/tokens: _Expires in_ select button is now correctly linked to its label - https://studio-staging-git-fix-a11y-form-input-descriptions-supabase.vercel.app/dashboard/account/me: the switches are now correctly linked to their label - In Database/Indexes: the select buttons when creating an index are now correctly linked to their label - All other changes are the same things --- .../default/example/date-picker-form.tsx | 13 +- .../example/form-patterns-pagelayout.tsx | 42 +-- .../example/form-patterns-sidepanel.tsx | 42 +-- .../AccessTokens/Scoped/Form/TokenDetails.tsx | 32 +-- .../Account/Preferences/DashboardToggle.tsx | 8 +- .../Auth/AuthProvidersForm/FormField.tsx | 109 ++++---- .../Database/Indexes/CreateIndexSidePanel.tsx | 243 +++++++++--------- .../Integrations/Wrappers/ColumnType.tsx | 50 ++-- .../ProjectCreation/OrganizationSelector.tsx | 53 ++-- .../ProjectCreation/ProjectNameInput.tsx | 4 +- .../src/form/Layout/FormLayout.tsx | 1 - 11 files changed, 291 insertions(+), 306 deletions(-) diff --git a/apps/design-system/registry/default/example/date-picker-form.tsx b/apps/design-system/registry/default/example/date-picker-form.tsx index 52aa14fc345..129fc91412f 100644 --- a/apps/design-system/registry/default/example/date-picker-form.tsx +++ b/apps/design-system/registry/default/example/date-picker-form.tsx @@ -8,6 +8,7 @@ import { Button, Calendar, Form, + FormControl, FormDescription, FormField, FormItem, @@ -53,11 +54,13 @@ export default function DatePickerForm() { Date of birth - - - {field.value ? format(field.value, 'PPP') : 'Pick a date'} - - + + + + {field.value ? format(field.value, 'PPP') : 'Pick a date'} + + + - + + + public @@ -675,23 +677,23 @@ export default function FormPatternsPageLayout() { label="Date Picker" description="Date selection with calendar popover" > - - + + {field.value ? format(field.value, 'PPP') : 'Pick a date'} - - - - - + + + + + )} /> diff --git a/apps/design-system/registry/default/example/form-patterns-sidepanel.tsx b/apps/design-system/registry/default/example/form-patterns-sidepanel.tsx index c3e734a6d7e..090dce8da98 100644 --- a/apps/design-system/registry/default/example/form-patterns-sidepanel.tsx +++ b/apps/design-system/registry/default/example/form-patterns-sidepanel.tsx @@ -610,14 +610,16 @@ export default function FormPatternsSidePanel() { size="small" className="w-full" > - + + + public @@ -677,23 +679,23 @@ export default function FormPatternsSidePanel() { label="Date Picker" description="Date selection with calendar popover" > - - + + {field.value ? format(field.value, 'PPP') : 'Pick a date'} - - - - - + + + + + )} /> diff --git a/apps/studio/components/interfaces/Account/AccessTokens/Scoped/Form/TokenDetails.tsx b/apps/studio/components/interfaces/Account/AccessTokens/Scoped/Form/TokenDetails.tsx index 7ef6accc758..48d2897c301 100644 --- a/apps/studio/components/interfaces/Account/AccessTokens/Scoped/Form/TokenDetails.tsx +++ b/apps/studio/components/interfaces/Account/AccessTokens/Scoped/Form/TokenDetails.tsx @@ -72,25 +72,25 @@ export const TokenDetails = ({ control, setValue }: TokenDetailsProps) => { name="expiresAt" control={control} render={({ field }) => ( - +
- - + + - - {EXPIRY_OPTIONS.map((option) => ( - - - {option.label} - {option.recommended && Recommended} - - - ))} - - - + + + {EXPIRY_OPTIONS.map((option) => ( + + + {option.label} + {option.recommended && Recommended} + + + ))} + + {field.value === 'custom' && ( ({ control={form.control} name={name} render={({ field }) => ( - + { field.onChange(value) diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersForm/FormField.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersForm/FormField.tsx index f6520fc841d..8f480f2d05d 100644 --- a/apps/studio/components/interfaces/Auth/AuthProvidersForm/FormField.tsx +++ b/apps/studio/components/interfaces/Auth/AuthProvidersForm/FormField.tsx @@ -118,8 +118,8 @@ const FormField = ({ ) : null } > - - + +