@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
131 lines (117 loc) • 3.28 kB
text/typescript
import cl from "clsx";
import React, { useContext } from "react";
import { ReadMore } from "../read-more/ReadMore";
import { useId } from "../util/hooks";
import { FieldsetContext } from "./fieldset/context";
export interface FormFieldProps {
/**
* Error message.
*/
error?: React.ReactNode;
/**
* Override internal errorId.
*/
errorId?: string;
/**
* Changes font-size, padding and gaps.
*/
size?: "medium" | "small";
/**
* **Avoid using if possible for accessibility purposes**.
*
* Disables element.
*/
disabled?: boolean;
/**
* Adds a description to extend the labeling.
*/
description?: React.ReactNode;
/**
* Override internal id.
*/
id?: string;
/**
* Read-only state.
*/
readOnly?: boolean;
}
export interface FormFieldType {
showErrorMsg: boolean;
hasError: boolean;
errorId: string;
inputDescriptionId: string;
size: "small" | "medium";
inputProps: {
id: string;
"aria-invalid"?: boolean;
"aria-describedby"?: string;
disabled?: boolean;
};
readOnly?: boolean;
}
/**
* Handles props and their state for various form-fields in context with Fieldset
*/
export const useFormField = (
props: FormFieldProps,
prefix: string,
): FormFieldType => {
const { size, error, errorId: propErrorId } = props;
const fieldset = useContext(FieldsetContext);
const genId = useId();
const id = props.id ?? `${prefix}-${genId}`;
const errorId = propErrorId ?? `${prefix}-error-${genId}`;
const inputDescriptionId = `${prefix}-description-${genId}`;
const disabled = fieldset?.disabled || props.disabled;
const readOnly =
((fieldset?.readOnly || props.readOnly) && !disabled) || undefined;
const hasError: boolean =
!disabled && !readOnly && !!(error || fieldset?.error);
const showErrorMsg =
!disabled && !readOnly && !!error && typeof error !== "boolean";
const ariaInvalid = { ...(hasError ? { "aria-invalid": true } : {}) };
if ((props as any)?.required && process.env.NODE_ENV !== "production") {
console.warn(
"Aksel: Use of 'required' in form-elements is heavily discuouraged. Docs about why here:",
);
console.warn(
"https://aksel.nav.no/god-praksis/artikler/obligatoriske-og-valgfrie-skjemafelter#dc7a536235fa",
);
}
return {
showErrorMsg,
hasError,
errorId,
inputDescriptionId,
size: size ?? fieldset?.size ?? "medium",
readOnly,
inputProps: {
id,
...ariaInvalid,
"aria-describedby":
cl(props["aria-describedby"], {
[inputDescriptionId]:
props.description && !containsReadMore(props.description),
[errorId]: showErrorMsg,
[fieldset?.errorId ?? ""]: hasError && fieldset?.error,
}) || undefined,
disabled,
},
};
};
export function containsReadMore(
children: React.ReactNode,
checkNested = true,
): boolean {
if (React.isValidElement(children)) {
if (children.type === ReadMore) {
return true;
}
if (children.props.children && checkNested) {
return containsReadMore(children.props.children, false);
}
} else if (Array.isArray(children)) {
return children.some((child) => containsReadMore(child, checkNested));
}
return false;
}