Skip to content

Commit

Permalink
🏷️ - chore: improve (typing) of (Form)
Browse files Browse the repository at this point in the history
  • Loading branch information
svenvandescheur committed Dec 9, 2024
1 parent 5295d83 commit 451a237
Show file tree
Hide file tree
Showing 16 changed files with 174 additions and 99 deletions.
5 changes: 3 additions & 2 deletions src/components/data/attributetable/attributetable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useId, useState } from "react";

import {
Field,
SerializedFormData,
TypedField,
string2Title,
typedFieldByFields,
Expand All @@ -19,7 +20,7 @@ export type AttributeTableProps<T extends object = object> = {
labeledObject?: Record<string, { label: React.ReactNode; value: unknown }>;
editable?: boolean;
fields?: Field<T>[] | TypedField<T>[];
formProps?: FormProps<T>;
formProps?: FormProps<Record<keyof T, SerializedFormData[string]>>;
labelCancel?: string;
labelEdit?: string;
valign?: "middle" | "start";
Expand Down Expand Up @@ -60,7 +61,7 @@ export const AttributeTable = <T extends object = object>({

const renderTable = () => {
return editable ? (
<Form
<Form<Record<keyof T, SerializedFormData[string]>>
fieldsetClassName="mykn-attributetable__body"
showActions={isFormOpenState}
secondaryActions={[
Expand Down
1 change: 1 addition & 0 deletions src/components/data/datagrid/datagrid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export const Filterable: Story = {
argTypes: {
...DataGridComponent.argTypes,
onFilter: { action: "onFilter" },
filterTransform: { action: "filterTransform" },
},
};

Expand Down
7 changes: 5 additions & 2 deletions src/components/data/datagrid/datagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Field,
SerializedFormData,
TypedField,
TypedSerializedFormData,
filterDataArray,
typedFieldByFields,
} from "../../../lib";
Expand Down Expand Up @@ -67,7 +68,9 @@ export type DataGridProps<T extends object = object, F extends object = T> = {
* A function transforming the filter values.
* This can be used to adjust filter input to an API spec.
*/
filterTransform?: (value: T) => F;
filterTransform?: (
value: Partial<TypedSerializedFormData<keyof T & string>>,
) => F;

/**
* Can be any valid CSS `height` property or `"fill-available-space"` to
Expand Down Expand Up @@ -272,7 +275,7 @@ type PaginatorPropsAliases = {
* filtering, row selection, and pagination.
*
* @typeParam T - The shape of a single data row.
* @typeParam F - If the shape of the filtered returned by `filterTransform`
* @typeParam F - If the shape of the data returned by `filterTransform`
*/
export const DataGrid = <T extends object = object, F extends object = T>(
props: DataGridProps<T, F>,
Expand Down
9 changes: 7 additions & 2 deletions src/components/data/datagrid/datagridfilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ export const DataGridFilter = <
e.preventDefault();
}

const data = serializeForm(input.form as HTMLFormElement) as T;
const data = serializeForm<keyof T & string>(
input.form as HTMLFormElement,
true,
);
const _data = filterTransform ? filterTransform(data) : data;

// Reset page on filter (length of dataset may change).
Expand Down Expand Up @@ -122,7 +125,9 @@ export const DataGridFilter = <
}
onClick={
field.type === "boolean"
? (e: React.MouseEvent<HTMLInputElement>) => handleFilter(e)
? (e: React.MouseEvent<HTMLInputElement>) => {
handleFilter(e);
}
: undefined
}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/data/datagrid/datagridtoolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export const DataGridToolbar = <
labelSubmit={ucFirst(_labelSaveFieldSelection)}
onSubmit={(e) => {
const form = e.target as HTMLFormElement;
const data = serializeForm(form);
const data = serializeForm(form, false);
const selectedFields = (data.fields || []) as string[];
const newTypedFieldsState = fields.map((f) => ({
...f,
Expand Down
11 changes: 9 additions & 2 deletions src/components/form/checkbox/checkboxgroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,37 @@ export type CheckboxGroupProps = ChoiceFieldProps<
>;

export const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
form,
id = "",
label,
name,
options,
variant,
required,
onChange,
onClick,
}) => {
const reactId = useId();
const _id = id || reactId;

return (
<div className={clsx("mykn-checkboxgroup")}>
<div className={clsx("mykn-checkboxgroup")} aria-label={label}>
{options.map((option, index) => {
const checkboxId = `${_id}-choice-${index}`;

return (
<Checkbox
key={option.value || option.label || index}
checked={option.selected}
defaultChecked={option.defaultChecked}
form={form}
id={checkboxId}
key={option.value || option.label || index}
name={name}
required={required}
value={option.value || option.label}
variant={variant}
onChange={onChange}
onClick={onClick}
>
{option.label}
</Checkbox>
Expand Down
15 changes: 12 additions & 3 deletions src/components/form/choicefield/choicefield.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,30 @@ export type ChoiceFieldProps<
/** Can be used to generate `SelectOption` components from an array of objects. */
options: Option[];

/** The associated form's id. */
form?: string;

/** The (accessible) label .*/
label?: string;

/** Form element name. */
name?: string;

/** Whether a value is required. */
required?: boolean;

/** Form element type. */
type?: string;

/** Gets called when the input is blurred. */
onBlur?: React.FormEventHandler<FormElement>;

/** Value of the form element */
value?: Option["value"] | null;

/** The variant (style) of the form element. */
variant?: "normal" | "transparent";

/** Gets called when the input is blurred. */
onBlur?: React.FormEventHandler<FormElement>;

/**
*
* A custom "change" event created with `detail` set to the selected option.
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/datepicker/datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export type DatePickerProps = Omit<
placeholder?: string;

/** Can be a `Date` `[Date, Date]` or a (date) `string` or a (time) `number`. */
value?: DatePickerValue | string | number;
value?: DatePickerValue | string | number | null;

labelNextYear?: string;
labelPreviousYear?: string;
Expand Down
26 changes: 15 additions & 11 deletions src/components/form/form/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@ import clsx from "clsx";
import React, { FormEvent, useContext, useEffect, useState } from "react";

import { ConfigContext } from "../../../contexts";
import { FormField } from "../../../lib/form/typeguards";
import { getValueFromFormData, serializeForm } from "../../../lib/form/utils";
import { ucFirst } from "../../../lib";
import { useIntl } from "../../../lib";
import { FormField } from "../../../lib";
import {
SerializedFormData,
getValueFromFormData,
serializeForm,
} from "../../../lib";
import {
DEFAULT_VALIDATION_ERROR_REQUIRED,
Validator,
getErrorFromErrors,
validateForm,
} from "../../../lib/form/validation";
import { forceArray } from "../../../lib/format/array";
import { ucFirst } from "../../../lib/format/string";
import { useIntl } from "../../../lib/i18n/useIntl";
} from "../../../lib";
import { forceArray } from "../../../lib";
import { ButtonProps } from "../../button";
import { Toolbar, ToolbarItem, ToolbarProps } from "../../toolbar";
import { ErrorMessage } from "../errormessage";
import { FormControl } from "../formcontrol";
import "./form.scss";

export type FormProps<T extends object = object> = Omit<
export type FormProps<T extends SerializedFormData = SerializedFormData> = Omit<
React.ComponentProps<"form">,
"onChange" | "onSubmit"
> & {
Expand Down Expand Up @@ -105,7 +109,7 @@ export type FormProps<T extends object = object> = Omit<
*
* @typeParam T - The shape of the serialized form data.
*/
export const Form = <T extends object = object>({
export const Form = <T extends SerializedFormData = SerializedFormData>({
buttonProps,
children,
debug = false,
Expand Down Expand Up @@ -134,7 +138,7 @@ export const Form = <T extends object = object>({
const _debug = debug || contextDebug;
const _nonFieldErrors = forceArray(nonFieldErrors);

const [valuesState, setValuesState] = useState(initialValues);
const [valuesState, setValuesState] = useState<T>(initialValues);
const [errorsState, setErrorsState] = useState(errors || {});

useEffect(() => {
Expand Down Expand Up @@ -178,7 +182,7 @@ export const Form = <T extends object = object>({
const form = (event.target as HTMLInputElement).form;

if (form && !onChange) {
const data = serializeForm(form, useTypedResults) as T;
const data = serializeForm<keyof T & string>(form, useTypedResults) as T;
setValuesState(data);
}
};
Expand All @@ -200,7 +204,7 @@ export const Form = <T extends object = object>({
}

const form = event.target as HTMLFormElement;
const data = serializeForm(form, useTypedResults) as T;
const data = serializeForm<keyof T & string>(form, useTypedResults) as T;

if (onSubmit) {
onSubmit(event, data);
Expand Down
9 changes: 7 additions & 2 deletions src/components/form/radio/radiogroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ export type RadioGroupProps = ChoiceFieldProps<
>;
export const RadioGroup: React.FC<RadioGroupProps> = ({
id = "",
label,
name,
options,
required,
variant,
onChange,
onClick,
}) => {
const reactId = useId();
const _id = id || reactId;
Expand All @@ -34,7 +37,7 @@ export const RadioGroup: React.FC<RadioGroupProps> = ({
};

return (
<div className={clsx("mykn-radiogroup")}>
<div className={clsx("mykn-radiogroup")} aria-label={label}>
{options.map((option, index) => {
const radioId = `${_id}-choice-${index}`;
const optionValue = option.value || option.label;
Expand All @@ -43,11 +46,13 @@ export const RadioGroup: React.FC<RadioGroupProps> = ({
<Radio
id={radioId}
key={optionValue || index}
checked={selectedValueState === optionValue}
name={name}
required={required}
value={optionValue}
variant={variant}
checked={selectedValueState === optionValue}
onChange={handleChange}
onClick={onClick}
>
{option.label}
</Radio>
Expand Down
9 changes: 0 additions & 9 deletions src/components/form/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ export type SelectProps = ChoiceFieldProps & {
/** Component to use as icon. */
icon?: React.ReactNode;

/** Select label. */
label?: string;

/** Whether a value is required, a required select can't be cleared. */
required?: boolean;

/** Whether to apply padding. */
pad?: boolean | "h" | "v";

Expand All @@ -49,9 +43,6 @@ export type SelectProps = ChoiceFieldProps & {

/** The clear value (accessible) label. */
labelClear?: string;

/** The associated form's id. */
form?: React.ComponentProps<"select">["form"];
};

/**
Expand Down
8 changes: 5 additions & 3 deletions src/hooks/dialog/useformdialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useContext, useEffect } from "react";

import { Form, FormProps, ModalProps, P } from "../../components";
import { ModalServiceContext } from "../../contexts";
import { FormField } from "../../lib";
import { FormField, SerializedFormData } from "../../lib";
import { useDialog } from "./usedialog";

/**
Expand Down Expand Up @@ -31,7 +31,9 @@ export const useFormDialog = <T extends object = object>() => {
* @param formProps
* @param autofocus
*/
const fn = <FT extends object = T>(
const fn = <
FT extends SerializedFormData = Record<keyof T, SerializedFormData[string]>,
>(
title: string,
message: React.ReactNode,
fields: FormField[],
Expand Down Expand Up @@ -66,7 +68,7 @@ export const useFormDialog = <T extends object = object>() => {
return fn;
};

const PromptForm = <T extends object = object>({
const PromptForm = <T extends SerializedFormData = SerializedFormData>({
fields,
labelConfirm,
labelCancel,
Expand Down
3 changes: 3 additions & 0 deletions src/lib/form/typeguards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { DateInputProps } from "../../components/form/dateinput";
import { DateRangeInputProps } from "../../components/form/daterangeinput";

export type FormField =
| CheckboxProps
| RadioProps
| ChoiceFieldProps
| DateInputProps
| DateRangeInputProps
| DatePickerProps
Expand Down
Loading

0 comments on commit 451a237

Please sign in to comment.