Skip to content

Commit

Permalink
feat: added phone field & tests & updated storybook
Browse files Browse the repository at this point in the history
  • Loading branch information
chesterkmr committed Dec 18, 2024
1 parent fbc135a commit f4a885e
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ const schema: Array<IFormElement<any, any>> = [
label: 'Checkbox Field',
},
},
{
id: 'PhoneField',
element: 'phonefield',
valueDestination: 'phone',
params: {
label: 'Phone Field',
defaultCountry: 'il',
},
},
{
id: 'FieldList',
element: 'fieldlist',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { PhoneNumberInput } from '@/components/atoms';
import { createTestId } from '@/components/organisms/Renderer';
import { useField } from '../../hooks/external';
import { FieldErrors } from '../../layouts/FieldErrors';
import { FieldLayout } from '../../layouts/FieldLayout';
import { TDynamicFormElement } from '../../types';
import { useStack } from '../FieldList/providers/StackProvider';

export interface IPhoneFieldParams {
defaultCountry?: string;
}

export const PhoneField: TDynamicFormElement<string, IPhoneFieldParams> = ({ element }) => {
const { defaultCountry = 'us' } = element.params || {};
const { stack } = useStack();
const { value, onChange, onBlur, onFocus } = useField<string | undefined>(element, stack);

return (
<FieldLayout element={element}>
<PhoneNumberInput
country={defaultCountry}
testId={createTestId(element, stack)}
value={value}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
/>
<FieldErrors element={element} />
</FieldLayout>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { PhoneNumberInput } from '@/components/atoms';
import { createTestId } from '@/components/organisms/Renderer';
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useField } from '../../hooks/external';
import { FieldErrors } from '../../layouts/FieldErrors';
import { FieldLayout } from '../../layouts/FieldLayout';
import { IFormElement } from '../../types';
import { useStack } from '../FieldList/providers/StackProvider';
import { IPhoneFieldParams, PhoneField } from './PhoneField';

vi.mock('@/components/atoms', () => ({
PhoneNumberInput: vi.fn(),
}));

vi.mock('../../hooks/external', () => ({
useField: vi.fn(),
}));

vi.mock('../../layouts/FieldErrors', () => ({
FieldErrors: vi.fn(),
}));

vi.mock('../../layouts/FieldLayout', () => ({
FieldLayout: vi.fn(({ children }) => <div>{children}</div>),
}));

vi.mock('../FieldList/providers/StackProvider', () => ({
useStack: vi.fn(),
}));

vi.mock('@/components/organisms/Renderer', () => ({
createTestId: vi.fn(),
}));

describe('PhoneField', () => {
const mockElement = {
id: 'test-phone',
params: {},
valueDestination: 'test.path',
element: 'phonefield',
} as IFormElement<string, IPhoneFieldParams>;

const mockFieldValues = {
value: '+1234567890',
onChange: vi.fn(),
onBlur: vi.fn(),
onFocus: vi.fn(),
};

beforeEach(() => {
vi.clearAllMocks();
vi.mocked(useStack).mockReturnValue({ stack: [] });
vi.mocked(useField).mockReturnValue(mockFieldValues as any);
vi.mocked(createTestId).mockReturnValue('test-id');
});

it('should render PhoneNumberInput with default country "us"', () => {
render(<PhoneField element={mockElement} />);

expect(PhoneNumberInput).toHaveBeenCalledWith(
expect.objectContaining({
country: 'us',
testId: 'test-id',
value: '+1234567890',
onChange: mockFieldValues.onChange,
onBlur: mockFieldValues.onBlur,
onFocus: mockFieldValues.onFocus,
}),
expect.anything(),
);
});

it('should render PhoneNumberInput with custom country from params', () => {
const elementWithCustomCountry = {
...mockElement,
params: { defaultCountry: 'il' },
};

render(<PhoneField element={elementWithCustomCountry} />);

expect(PhoneNumberInput).toHaveBeenCalledWith(
expect.objectContaining({
country: 'il',
}),
expect.anything(),
);
});

it('should render FieldLayout with element prop', () => {
render(<PhoneField element={mockElement} />);

expect(FieldLayout).toHaveBeenCalledWith(
expect.objectContaining({
element: mockElement,
}),
expect.anything(),
);
});

it('should render FieldErrors with element prop', () => {
render(<PhoneField element={mockElement} />);

expect(FieldErrors).toHaveBeenCalledWith(
expect.objectContaining({
element: mockElement,
}),
expect.anything(),
);
});

it('should pass stack to createTestId', () => {
const mockStack = [0, 1];
vi.mocked(useStack).mockReturnValue({ stack: mockStack });

render(<PhoneField element={mockElement} />);

expect(createTestId).toHaveBeenCalledWith(mockElement, mockStack);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './PhoneField';
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ export const FieldErrors: FunctionComponent<IFieldErrorsProps> = ({ element }) =
.flat();
}, [_validationErrors, id, touched]);

return <ErrorsList errors={fieldErrors || []} />;
return <ErrorsList errors={fieldErrors || []} className="mt-2" />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CheckboxListField } from '../fields/CheckboxList';
import { DateField } from '../fields/DateField';
import { FieldList } from '../fields/FieldList';
import { MultiselectField } from '../fields/MultiselectField';
import { PhoneField } from '../fields/PhoneField';
import { SelectField } from '../fields/SelectField';
import { TextField } from '../fields/TextField';
import { TDynamicFormField } from '../types';
Expand All @@ -19,6 +20,7 @@ export const baseFields = {
fieldlist: FieldList,
selectfield: SelectField,
submitbutton: SubmitButton,
phonefield: PhoneField,
} as const;

export type TBaseFields = keyof typeof baseFields & string;
Expand Down

0 comments on commit f4a885e

Please sign in to comment.