diff --git a/gradle/changelog/form_input_a11y.yaml b/gradle/changelog/form_input_a11y.yaml new file mode 100644 index 0000000000..db63e6a426 --- /dev/null +++ b/gradle/changelog/form_input_a11y.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Connect labels with their respective inputs for improved accessibility diff --git a/scm-ui/ui-components/src/index.ts b/scm-ui/ui-components/src/index.ts index 2fc9e1705f..3fc13b758c 100644 --- a/scm-ui/ui-components/src/index.ts +++ b/scm-ui/ui-components/src/index.ts @@ -138,3 +138,5 @@ export { export { urls }; export const getPageFromMatch = urls.getPageFromMatch; + +export { default as useGeneratedId } from "./useGeneratedId"; diff --git a/scm-ui/ui-components/src/useGeneratedId.tsx b/scm-ui/ui-components/src/useGeneratedId.tsx new file mode 100644 index 0000000000..1ae7473e1b --- /dev/null +++ b/scm-ui/ui-components/src/useGeneratedId.tsx @@ -0,0 +1,31 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { useMemo } from "react"; + +let counter = 0; + +export default function useGeneratedId(fallback?: string) { + return useMemo(() => fallback ?? `scm-id-${++counter}`, [fallback]); +} diff --git a/scm-ui/ui-forms/src/input/InputField.tsx b/scm-ui/ui-forms/src/input/InputField.tsx index c10f61b344..85387d24ad 100644 --- a/scm-ui/ui-forms/src/input/InputField.tsx +++ b/scm-ui/ui-forms/src/input/InputField.tsx @@ -29,6 +29,7 @@ import Label from "../base/label/Label"; import FieldMessage from "../base/field-message/FieldMessage"; import Input from "./Input"; import Help from "../base/help/Help"; +import { useGeneratedId } from "@scm-manager/ui-components"; type InputFieldProps = { label: string; @@ -41,16 +42,17 @@ type InputFieldProps = { * @see https://bulma.io/documentation/form/input/ */ const InputField = React.forwardRef( - ({ label, helpText, error, className, ...props }, ref) => { + ({ label, helpText, error, className, id, ...props }, ref) => { + const inputId = useGeneratedId(id ?? props.testId); const variant = error ? "danger" : undefined; return ( - diff --git a/scm-ui/ui-forms/src/select/SelectField.tsx b/scm-ui/ui-forms/src/select/SelectField.tsx index 1da9dcbc4e..0b8300428e 100644 --- a/scm-ui/ui-forms/src/select/SelectField.tsx +++ b/scm-ui/ui-forms/src/select/SelectField.tsx @@ -29,6 +29,7 @@ import Label from "../base/label/Label"; import FieldMessage from "../base/field-message/FieldMessage"; import Help from "../base/help/Help"; import Select from "./Select"; +import { useGeneratedId } from "@scm-manager/ui-components"; type Props = { label: string; @@ -40,16 +41,17 @@ type Props = { * @see https://bulma.io/documentation/form/select/ */ const SelectField = React.forwardRef( - ({ label, helpText, error, className, ...props }, ref) => { + ({ label, helpText, error, className, id, ...props }, ref) => { + const selectId = useGeneratedId(id ?? props.testId); const variant = error ? "danger" : undefined; return ( -