Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/.changeset/khaki-schools-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'pydantic-forms': patch
---

Fixes form labels
5 changes: 5 additions & 0 deletions frontend/.changeset/loose-boxes-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'pydantic-forms': patch
---

Makes rowRenderer configurable
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,35 @@
*
*/
import React from 'react';
import type { ControllerFieldState } from 'react-hook-form';

import ResetNullableFieldTrigger from '@/components/form/ResetNullableFieldTrigger';
import { usePydanticFormContext } from '@/core';
import { PydanticFormField } from '@/types';

import { FormRow } from './FormRow';

interface FieldWrapProps {
pydanticFormField: PydanticFormField;
fieldState: ControllerFieldState;
children: React.ReactNode;
}

export const FieldWrap = ({
pydanticFormField,
fieldState,
children,
}: FieldWrapProps) => {
const { errorDetails } = usePydanticFormContext();

export const FieldWrap = ({ pydanticFormField, children }: FieldWrapProps) => {
const { errorDetails, rhf, config } = usePydanticFormContext();
const RowRenderer = config?.rowRenderer ? config.rowRenderer : FormRow;
const fieldState = rhf.getFieldState(pydanticFormField.id);
const errorMsg =
errorDetails?.mapped?.[pydanticFormField.id]?.msg ??
fieldState.error?.message;
const isInvalid = errorMsg ?? fieldState.invalid;

return (
<FormRow
label={
<>
{pydanticFormField.title}
<ResetNullableFieldTrigger field={pydanticFormField} />
</>
}
<RowRenderer
title={pydanticFormField.title}
description={pydanticFormField.description}
required={pydanticFormField.required}
isInvalid={!!isInvalid}
error={errorMsg as string}
>
<div>{children}</div>
</FormRow>
</RowRenderer>
);
};
20 changes: 9 additions & 11 deletions frontend/packages/pydantic-forms/src/components/fields/FormRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { HTMLAttributes, ReactElement, ReactNode } from 'react';

type FormRowProps = {
children: ReactElement;
label?: ReactNode;
title: string;
isInvalid?: boolean;
error?: ReactNode | ReactNode[];
description?: string;
Expand All @@ -12,19 +12,17 @@ type FormRowProps = {

export const FormRow = ({
children,
label,
isInvalid,
title,
error,
...rest
description,
required,
}: FormRowProps) => {
// TODO: readd required, description, classname
return (
<div {...(rest as HTMLAttributes<HTMLElement>)}>
{label && (
<label>
{label} {isInvalid && '!!!'}
</label>
)}
<div>
<label>
{title} {required && <span style={{ color: 'red' }}>*</span>}
</label>
{description && <div>{description}</div>}
{children}
{error}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ const RenderForm = (contextProps: PydanticFormContextProps) => {

return (
<form action={''} onSubmit={submitForm}>
{title !== false && <h2>{title ?? pydanticFormSchema.title}</h2>}
{title !== false && title !== 'undefined' && (
<h2>{title ?? pydanticFormSchema.title}</h2>
)}

{headerComponent}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ function PydanticFormContextProvider({
const { pydanticFormSchema, isLoading: isParsingSchema } =
usePydanticFormParser(
rawSchema,
formLabels,
formLabels?.labels,
fieldDetailProvider,
formStructureMutator,
);
Expand Down Expand Up @@ -223,7 +223,7 @@ function PydanticFormContextProvider({
}

const initialData = getFormValuesFromFieldOrLabels(pydanticFormSchema, {
...formLabels,
...formLabels?.data,
...customData,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const WrapFieldElement = ({
<Controller
name={pydanticFormField.id}
control={rhf.control}
render={({ field, fieldState }) => {
render={({ field }) => {
const { onChange, onBlur, value, name, ref } = field;
const onChangeHandle = (val: string) => {
onChange(val);
Expand All @@ -30,10 +30,7 @@ export const WrapFieldElement = ({
};

return (
<FieldWrap
pydanticFormField={pydanticFormField}
fieldState={fieldState}
>
<FieldWrap pydanticFormField={pydanticFormField}>
<PydanticFormControlledElement
onChange={onChangeHandle}
onBlur={onBlur}
Expand Down
3 changes: 1 addition & 2 deletions frontend/packages/pydantic-forms/src/core/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,7 @@ export const getFieldBySection = (components: PydanticFormComponents) => {
*/
export const getFormValuesFromFieldOrLabels = (
pydanticFormSchema: PydanticFormSchema,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
labelData?: Record<string, any>,
labelData?: Record<string, string>,
) => {
const fieldValues: Record<string, string> = {};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

/**
* Pydantic Forms
*
Expand All @@ -22,12 +20,7 @@ import type {
PydanticFormMetaData,
} from '@/types';

const ignoreApiErrors = async (
req: Promise<unknown>,
// ignoreCodes: number[],
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any> => {
// TODO: What to use these for: ignoreCodes
const ignoreApiErrors = async (req: Promise<unknown>): Promise<unknown> => {
try {
return await req;
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import useSWR from 'swr';

import { PydanticFormCustomDataProvider, PydanticFormLabels } from '@/types';
import {
PydanticFormCustomDataProvider,
PydanticFormLabelProviderResponse,
} from '@/types';

export const useCustomDataProvider = (
customDataProviderCacheKey: number,
customDataProvider?: PydanticFormCustomDataProvider,
) => {
return useSWR<PydanticFormLabels>(
return useSWR<PydanticFormLabelProviderResponse['data']>(
// cache key
[`pydanticFormsDataProvider-${customDataProviderCacheKey}`],

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

/**
* Pydantic Forms
*
Expand All @@ -16,7 +14,10 @@
*/
import useSWR, { SWRConfiguration } from 'swr';

import { PydanticFormLabelProvider } from '@/types';
import {
PydanticFormLabelProvider,
PydanticFormLabelProviderResponse,
} from '@/types';

export function useLabelProvider(
labelProvider?: PydanticFormLabelProvider,
Expand All @@ -25,7 +26,7 @@ export function useLabelProvider(
cacheKey?: number,
swrConfig?: SWRConfiguration,
) {
return useSWR<Record<string, string> | undefined>(
return useSWR<PydanticFormLabelProviderResponse | undefined>(
// cache key
[labelProvider, formKey, id, swrConfig, cacheKey],

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ const parseProperties = (

const parsedProperty: PydanticFormField = {
id,
title: translateLabel(
propertyId,
propertySchema.title,
formLabels,
),
title:
translateLabel(
propertyId,
propertySchema.title,
formLabels,
) || propertyId,
description: translateLabel(
`${propertyId}_info`,
propertySchema.description,
Expand Down
28 changes: 20 additions & 8 deletions frontend/packages/pydantic-forms/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export enum PydanticFormState {

export interface PydanticFormField {
id: string;
title?: string;
title: string;
description?: string;
type: PydanticFormFieldType;
format: PydanticFormFieldFormat;
Expand Down Expand Up @@ -249,6 +249,15 @@ export type PydanticFormZodValidationFn = (
rhf?: ReturnType<typeof useForm>,
) => z.ZodTypeAny;

export type RowRenderer = React.JSXElementConstructor<{
title: string;
description?: string;
required?: boolean;
isInvalid?: boolean;
error?: string;
children: React.ReactNode;
}>;

export interface PydanticFormZodValidationPresets {
[type: string]: PydanticFormZodValidationFn;
}
Expand Down Expand Up @@ -286,6 +295,7 @@ export interface PydanticFormsContextConfig {

formRenderer?: FormRenderer;
footerRenderer?: React.JSXElementConstructor<object>;
rowRenderer?: RowRenderer;

// Extend field definitions
fieldDetailProvider?: PydanticFormFieldDetailProvider;
Expand All @@ -308,20 +318,17 @@ interface PydanticFormComponent {

export type PydanticFormComponents = PydanticFormComponent[];

export type PydanticFormCustomDataProvider = () => Promise<PydanticFormLabels>;

export interface PydanticFormLabels {
[key: string]: string[] | number[] | string | number | null;
}
export type PydanticFormCustomDataProvider = () => Promise<
PydanticFormLabelProviderResponse['data']
>;

export type PydanticFormLabelProvider = ({
formKey,
id,
}: {
formKey: string;
id?: string | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}) => Promise<Record<string, any>>;
}) => Promise<PydanticFormLabelProviderResponse>;

// will return column
export type PydanticFormLayoutColumnProvider = (fieldId: string) => number;
Expand Down Expand Up @@ -360,6 +367,11 @@ export type PydanticFormCustomValidationRuleFn = (
rhf?: ReturnType<typeof useForm>,
) => Zod.ZodTypeAny | undefined;

export interface PydanticFormLabelProviderResponse {
labels: Record<string, string>;
data: Record<string, string>;
}

export interface PydanticFormApiResponse {
detail?: string;
status: number;
Expand Down