Merge pull request #475 from danditomaso/issue-473-label-inputs-not-connected
fix: connected labels to inputs, improving accessibility and testability
This commit is contained in:
@@ -98,24 +98,27 @@ export function DynamicForm<T extends FieldValues>({
|
|||||||
<Subtle>{fieldGroup.description}</Subtle>
|
<Subtle>{fieldGroup.description}</Subtle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{fieldGroup.fields.map((field) => (
|
{fieldGroup.fields.map((field) => {
|
||||||
<FieldWrapper
|
return (
|
||||||
key={field.label}
|
<FieldWrapper
|
||||||
label={field.label}
|
key={field.label}
|
||||||
description={field.description}
|
label={field.label}
|
||||||
valid={
|
fieldName={field.name}
|
||||||
field.validationText === undefined ||
|
description={field.description}
|
||||||
field.validationText === ""
|
valid={
|
||||||
}
|
field.validationText === undefined ||
|
||||||
validationText={field.validationText}
|
field.validationText === ""
|
||||||
>
|
}
|
||||||
<DynamicFormField
|
validationText={field.validationText}
|
||||||
field={field}
|
>
|
||||||
control={control}
|
<DynamicFormField
|
||||||
disabled={isDisabled(field.disabledBy, field.disabled)}
|
field={field}
|
||||||
/>
|
control={control}
|
||||||
</FieldWrapper>
|
disabled={isDisabled(field.disabledBy, field.disabled)}
|
||||||
))}
|
/>
|
||||||
|
</FieldWrapper>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{hasSubmitButton && <Button type="submit">Submit</Button>}
|
{hasSubmitButton && <Button type="submit">Submit</Button>}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export function GenericInput<T extends FieldValues>({
|
|||||||
}
|
}
|
||||||
step={field.properties?.step}
|
step={field.properties?.step}
|
||||||
value={field.type === "number" ? Number.parseFloat(value) : value}
|
value={field.type === "number" ? Number.parseFloat(value) : value}
|
||||||
|
id={field.name}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (field.inputChange) field.inputChange(e);
|
if (field.inputChange) field.inputChange(e);
|
||||||
onChange(
|
onChange(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Controller, type FieldValues } from "react-hook-form";
|
|||||||
|
|
||||||
export interface PasswordGeneratorProps<T> extends BaseFormBuilderProps<T> {
|
export interface PasswordGeneratorProps<T> extends BaseFormBuilderProps<T> {
|
||||||
type: "passwordGenerator";
|
type: "passwordGenerator";
|
||||||
|
id: string;
|
||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
bits?: { text: string; value: string; key: string }[];
|
bits?: { text: string; value: string; key: string }[];
|
||||||
devicePSKBitCount: number;
|
devicePSKBitCount: number;
|
||||||
@@ -41,6 +42,7 @@ export function PasswordGenerator<T extends FieldValues>({
|
|||||||
render={({ field: { value, ...rest } }) => (
|
render={({ field: { value, ...rest } }) => (
|
||||||
<Generator
|
<Generator
|
||||||
type={field.hide && !passwordShown ? "password" : "text"}
|
type={field.hide && !passwordShown ? "password" : "text"}
|
||||||
|
id={field.id}
|
||||||
action={
|
action={
|
||||||
field.hide
|
field.hide
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function SelectInput<T extends FieldValues>({
|
|||||||
{...remainingProperties}
|
{...remainingProperties}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger id={field.name}>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export function ToggleInput<T extends FieldValues>({
|
|||||||
<Switch
|
<Switch
|
||||||
checked={value}
|
checked={value}
|
||||||
onCheckedChange={onChangeHandler(onChange)}
|
onCheckedChange={onChangeHandler(onChange)}
|
||||||
|
id={field.name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
{...field.properties}
|
{...field.properties}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Label } from "@components/UI/Label.tsx";
|
|||||||
|
|
||||||
export interface FieldWrapperProps {
|
export interface FieldWrapperProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
fieldName: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -11,6 +12,7 @@ export interface FieldWrapperProps {
|
|||||||
|
|
||||||
export const FieldWrapper = ({
|
export const FieldWrapper = ({
|
||||||
label,
|
label,
|
||||||
|
fieldName,
|
||||||
description,
|
description,
|
||||||
children,
|
children,
|
||||||
valid,
|
valid,
|
||||||
@@ -19,7 +21,7 @@ export const FieldWrapper = ({
|
|||||||
<div className="pt-6 sm:pt-5">
|
<div className="pt-6 sm:pt-5">
|
||||||
<fieldset aria-labelledby="label-notifications">
|
<fieldset aria-labelledby="label-notifications">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:items-baseline sm:gap-4">
|
<div className="sm:grid sm:grid-cols-3 sm:items-baseline sm:gap-4">
|
||||||
<Label>{label}</Label>
|
<Label htmlFor={fieldName}>{label}</Label>
|
||||||
<div className="sm:col-span-2">
|
<div className="sm:col-span-2">
|
||||||
<div className="max-w-lg">
|
<div className="max-w-lg">
|
||||||
<p className="text-sm text-slate-500">{description}</p>
|
<p className="text-sm text-slate-500">{description}</p>
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ export const Security = () => {
|
|||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: "passwordGenerator",
|
type: "passwordGenerator",
|
||||||
|
id: "pskInput",
|
||||||
name: "privateKey",
|
name: "privateKey",
|
||||||
label: "Private Key",
|
label: "Private Key",
|
||||||
description: "Used to create a shared key with a remote device",
|
description: "Used to create a shared key with a remote device",
|
||||||
@@ -234,6 +235,7 @@ export const Security = () => {
|
|||||||
{
|
{
|
||||||
type: "passwordGenerator",
|
type: "passwordGenerator",
|
||||||
name: "adminKey",
|
name: "adminKey",
|
||||||
|
id: "adminKeyInput",
|
||||||
label: "Admin Key",
|
label: "Admin Key",
|
||||||
description:
|
description:
|
||||||
"The public key authorized to send admin messages to this node",
|
"The public key authorized to send admin messages to this node",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
|
|||||||
type: "text" | "password";
|
type: "text" | "password";
|
||||||
devicePSKBitCount?: number;
|
devicePSKBitCount?: number;
|
||||||
value: string;
|
value: string;
|
||||||
|
id: string;
|
||||||
variant: "default" | "invalid";
|
variant: "default" | "invalid";
|
||||||
actionButtons: {
|
actionButtons: {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -37,6 +38,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
|||||||
{
|
{
|
||||||
type,
|
type,
|
||||||
devicePSKBitCount,
|
devicePSKBitCount,
|
||||||
|
id = "pskInput",
|
||||||
variant,
|
variant,
|
||||||
value,
|
value,
|
||||||
actionButtons,
|
actionButtons,
|
||||||
@@ -73,7 +75,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
|||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
type={type}
|
type={type}
|
||||||
id="pskInput"
|
id={id}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={inputChange}
|
onChange={inputChange}
|
||||||
|
|||||||
Reference in New Issue
Block a user