Skip to content
Open
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
41 changes: 41 additions & 0 deletions src/components/form/fields/MoneyField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { FormField } from '@/src/components/ui/form';
import { useFormFields } from '@/src/context';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { TextField, TextFieldProps } from './TextField';

export function MoneyField(props: TextFieldProps) {
const { components } = useFormFields();
const { control } = useFormContext();
console.log('MoneyField', props);
if (components?.money) {
return (
<FormField
control={control}
name={props.name}
render={({ field, fieldState }) => {
const CustomNumberField =
components.money as React.ComponentType<any>;
return (
<CustomNumberField
field={{
...field,
onChange: (value: any) => {
field.onChange(value);
props.onChange?.(value);
},
}}
fieldState={fieldState}
fieldData={props}
/>
);
}}
/>
);
}

return (
<TextField {...props} type="text" inputMode="decimal" pattern="^[0-9.]*$" />
);
}
2 changes: 1 addition & 1 deletion src/components/form/fields/NumberField.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { FormField } from '@/src/components/ui/form';
import { useFormFields } from '@/src/context';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { FormField } from '../../ui/form';
import { TextField, TextFieldProps } from './TextField';

export function NumberField(props: TextFieldProps) {
Expand Down
5 changes: 3 additions & 2 deletions src/components/form/fields/fieldsMapping.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { CheckBoxField } from '@/src/components/form/fields/CheckBoxField';
import { DatePickerField } from '@/src/components/form/fields/DatePickerField';
import { EmailField } from '@/src/components/form/fields/EmailField';
import { FieldSetField } from '@/src/components/form/fields/FieldSetField';
import { FileUploadField } from '@/src/components/form/fields/FileUploadField';
import { MoneyField } from '@/src/components/form/fields/MoneyField';
import { NumberField } from '@/src/components/form/fields/NumberField';
import { RadioGroupField } from '@/src/components/form/fields/RadioGroupField';
import { SelectField } from '@/src/components/form/fields/SelectField';
import { TextAreaField } from '@/src/components/form/fields/TextAreaField';
import { TextField } from '@/src/components/form/fields/TextField';
import { EmailField } from '@/src/components/form/fields/EmailField';
import { SupportedTypes } from '@/src/components/form/fields/types';
import React from 'react';

Expand All @@ -16,7 +17,7 @@ export const fieldsMap: Record<SupportedTypes, React.ComponentType<any>> = {
checkbox: CheckBoxField,
text: TextField,
email: EmailField,
money: NumberField,
money: MoneyField,
select: SelectField,
radio: RadioGroupField,
number: NumberField,
Expand Down
127 changes: 127 additions & 0 deletions src/components/form/fields/tests/MoneyField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useFormFields } from '@/src/context';
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { number } from 'yup';
import { MoneyField } from '../MoneyField';
import { TextFieldProps } from '../TextField';

// Mock dependencies
vi.mock('@/src/context', () => ({
useFormFields: vi.fn(),
}));

describe('MoneyField Component', () => {
const mockOnChange = vi.fn();
const defaultProps: TextFieldProps = {
name: 'testField',
label: 'Test Field',
description: 'This is a test field',
type: 'money',
computedAttributes: {},
errorMessage: {
required: 'This field is required',
},
inputType: 'number' as const,
isVisible: true,
jsonType: 'number',
required: true,
schema: number(),
scopedJsonSchema: {},
};

// Helper function to render the component with a form context
const renderWithFormContext = (props: TextFieldProps) => {
const TestComponent = () => {
const methods = useForm();
return (
<FormProvider {...methods}>
<MoneyField {...props} />
</FormProvider>
);
};

return render(<TestComponent />);
};

beforeEach(() => {
vi.clearAllMocks();
(useFormFields as any).mockReturnValue({ components: {} });
});

it('renders the default implementation correctly', () => {
renderWithFormContext(defaultProps);

expect(screen.getByText('Test Field')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Test Field')).toBeInTheDocument();
expect(screen.getByText('This is a test field')).toBeInTheDocument();
});

it('handles input change correctly', () => {
renderWithFormContext({ ...defaultProps, onChange: mockOnChange });

const input = screen.getByPlaceholderText('Test Field');
fireEvent.change(input, { target: { value: '123.45' } });

expect(mockOnChange).toHaveBeenCalledTimes(1);
});

it('renders custom money component when provided', () => {
const CustomMoneyField = vi
.fn()
.mockImplementation(() => (
<div data-testid="custom-money-field">Custom Money Field</div>
));

(useFormFields as any).mockReturnValue({
components: { money: CustomMoneyField },
});

renderWithFormContext({ ...defaultProps, onChange: mockOnChange });

expect(CustomMoneyField).toHaveBeenCalledTimes(1);
expect(screen.getByTestId('custom-money-field')).toBeInTheDocument();
});

it('passes field props to custom component correctly', () => {
const CustomMoneyField = vi
.fn()
.mockImplementation(() => (
<div data-testid="custom-money-field">Custom Money Field</div>
));

(useFormFields as any).mockReturnValue({
components: { money: CustomMoneyField },
});

renderWithFormContext({ ...defaultProps, onChange: mockOnChange });

const call = CustomMoneyField.mock.calls[0][0];
expect(call.fieldData).toMatchObject(defaultProps);
expect(call.field).toBeDefined();
expect(call.fieldState).toBeDefined();
});

it('handles onChange in custom money component', () => {
const CustomMoneyField = vi.fn().mockImplementation(({ field }) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
field.onChange(e);
};

return <input data-testid="custom-input" onChange={handleChange} />;
});

(useFormFields as any).mockReturnValue({
components: { money: CustomMoneyField },
});

renderWithFormContext({ ...defaultProps, onChange: mockOnChange });

const customInput = screen.getByTestId('custom-input');
fireEvent.change(customInput, { target: { value: '456.78' } });

expect(mockOnChange).toHaveBeenCalledTimes(1);
});
});
Loading