diff --git a/.changeset/spotty-hounds-teach.md b/.changeset/spotty-hounds-teach.md new file mode 100644 index 0000000000..b078646184 --- /dev/null +++ b/.changeset/spotty-hounds-teach.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Fixed form field descriptions not being connected to inputs via `aria-describedby`, making them accessible to screen readers. Added a `descriptionSlot` prop to `FieldLabel` that uses React Aria's slot mechanism to automatically wire up the connection. + +**Affected components:** FieldLabel, TextField, PasswordField, SearchField, Select, RadioGroup, CheckboxGroup diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 3b79537f54..181fa48731 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -1243,6 +1243,7 @@ export const FieldLabelDefinition: { readonly htmlFor: {}; readonly id: {}; readonly descriptionId: {}; + readonly descriptionSlot: {}; readonly className: {}; }; }; @@ -1255,6 +1256,7 @@ export type FieldLabelOwnProps = { htmlFor?: string; id?: string; descriptionId?: string; + descriptionSlot?: string; className?: string; }; diff --git a/packages/ui/src/components/CheckboxGroup/CheckboxGroup.tsx b/packages/ui/src/components/CheckboxGroup/CheckboxGroup.tsx index 708b6e4b86..d835e0da52 100644 --- a/packages/ui/src/components/CheckboxGroup/CheckboxGroup.tsx +++ b/packages/ui/src/components/CheckboxGroup/CheckboxGroup.tsx @@ -68,6 +68,7 @@ export const CheckboxGroup = forwardRef( label={label} secondaryLabel={secondaryLabelText} description={description} + descriptionSlot="description" />
{children}
diff --git a/packages/ui/src/components/FieldLabel/FieldLabel.tsx b/packages/ui/src/components/FieldLabel/FieldLabel.tsx index 1b451be909..516d48b3a8 100644 --- a/packages/ui/src/components/FieldLabel/FieldLabel.tsx +++ b/packages/ui/src/components/FieldLabel/FieldLabel.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Label } from 'react-aria-components'; +import { Label, Text } from 'react-aria-components'; import { forwardRef } from 'react'; import type { FieldLabelProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; @@ -35,6 +35,7 @@ export const FieldLabel = forwardRef( htmlFor, id, descriptionId, + descriptionSlot, } = ownProps; if (!label) return null; @@ -52,9 +53,14 @@ export const FieldLabel = forwardRef( )} {description && ( -
+ {description} -
+ )} ); diff --git a/packages/ui/src/components/FieldLabel/definition.ts b/packages/ui/src/components/FieldLabel/definition.ts index 399ea9e54d..5d9364177f 100644 --- a/packages/ui/src/components/FieldLabel/definition.ts +++ b/packages/ui/src/components/FieldLabel/definition.ts @@ -37,6 +37,7 @@ export const FieldLabelDefinition = defineComponent()({ htmlFor: {}, id: {}, descriptionId: {}, + descriptionSlot: {}, className: {}, }, }); diff --git a/packages/ui/src/components/FieldLabel/types.ts b/packages/ui/src/components/FieldLabel/types.ts index ca18c6c8c1..b20a1cf59f 100644 --- a/packages/ui/src/components/FieldLabel/types.ts +++ b/packages/ui/src/components/FieldLabel/types.ts @@ -46,6 +46,11 @@ export type FieldLabelOwnProps = { */ descriptionId?: string; + /** + * The slot name to set on the description's React Aria `` element. + */ + descriptionSlot?: string; + className?: string; }; diff --git a/packages/ui/src/components/PasswordField/PasswordField.tsx b/packages/ui/src/components/PasswordField/PasswordField.tsx index cbff6063a4..4a7718555e 100644 --- a/packages/ui/src/components/PasswordField/PasswordField.tsx +++ b/packages/ui/src/components/PasswordField/PasswordField.tsx @@ -69,6 +69,7 @@ export const PasswordField = forwardRef( label={label} secondaryLabel={secondaryLabelText} description={description} + descriptionSlot="description" />
( + + Bulbasaur + Charmander + Squirtle + + ), +}); + export const Horizontal = meta.story({ args: { ...Default.input.args, diff --git a/packages/ui/src/components/RadioGroup/RadioGroup.tsx b/packages/ui/src/components/RadioGroup/RadioGroup.tsx index 8e869da988..b1175a701a 100644 --- a/packages/ui/src/components/RadioGroup/RadioGroup.tsx +++ b/packages/ui/src/components/RadioGroup/RadioGroup.tsx @@ -64,6 +64,7 @@ export const RadioGroup = forwardRef( label={label} secondaryLabel={secondaryLabelText} description={description} + descriptionSlot="description" />
{children}
diff --git a/packages/ui/src/components/SearchField/SearchField.tsx b/packages/ui/src/components/SearchField/SearchField.tsx index 1df389d5a3..d8f892edab 100644 --- a/packages/ui/src/components/SearchField/SearchField.tsx +++ b/packages/ui/src/components/SearchField/SearchField.tsx @@ -96,6 +96,7 @@ export const SearchField = forwardRef( label={label} secondaryLabel={secondaryLabelText} description={description} + descriptionSlot="description" />
diff --git a/packages/ui/src/components/TextField/TextField.tsx b/packages/ui/src/components/TextField/TextField.tsx index a2318d3d08..16f8940fdb 100644 --- a/packages/ui/src/components/TextField/TextField.tsx +++ b/packages/ui/src/components/TextField/TextField.tsx @@ -59,6 +59,7 @@ export const TextField = forwardRef( label={label} secondaryLabel={secondaryLabelText} description={description} + descriptionSlot="description" />