Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection Flow V2 Main Branch #2912

Closed
wants to merge 70 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
75d4a34
feat: added initial validator boilerplate & utilities & tests
chesterkmr Dec 2, 2024
b2c7a44
feat: implmeneted validate method & tests
chesterkmr Dec 3, 2024
e471f81
fix: test
chesterkmr Dec 3, 2024
7cb51fe
feat: implemented conditional validation rule apply & custom validators
chesterkmr Dec 4, 2024
4d2c708
feat: implemented useValidate & tests
chesterkmr Dec 4, 2024
c2987da
feat: finalized validator component
chesterkmr Dec 4, 2024
ce18a0c
feat: added story for validator
chesterkmr Dec 5, 2024
33544c6
feat: added renderer & tests & dynamic form boilerplate
chesterkmr Dec 10, 2024
4375b7d
feat: added rule engine
chesterkmr Dec 10, 2024
86b9afe
feat: implemented dynamic form context logic & tests & types
chesterkmr Dec 11, 2024
d8d4b25
feat: finalized core form logic & finalized field list & tests
chesterkmr Dec 13, 2024
5b175fe
fix: tests
chesterkmr Dec 13, 2024
0d94cb0
fix: build
chesterkmr Dec 13, 2024
b631eff
feat: added input boilerplates(unfinished) & field layout & tests
chesterkmr Dec 13, 2024
7be92c9
feat: added fields (wip)
chesterkmr Dec 16, 2024
e74241b
fix: fixed build & tests
chesterkmr Dec 17, 2024
32857eb
Merge branch 'dev' into bal-3139
chesterkmr Dec 17, 2024
a550b96
feat: implemented events & fixed tests
chesterkmr Dec 17, 2024
babea18
feat: implemented fields extend & removed elementsMap
chesterkmr Dec 18, 2024
7f07d19
feat: added select field & fixed types & tests
chesterkmr Dec 18, 2024
fbc135a
feat: added initial demo & bugfixes & updated tests
chesterkmr Dec 18, 2024
f4a885e
feat: added phone field & tests & updated storybook
chesterkmr Dec 18, 2024
8996991
feat: added file field & added tests & minor fixes
chesterkmr Dec 18, 2024
a8e111a
feat: added clear value on input hide & updates tests
chesterkmr Dec 19, 2024
e9e155f
feat: fixed onMount & onUnmount events & updated tests
chesterkmr Dec 19, 2024
7ebe91b
feat: added configurable file upload
chesterkmr Dec 19, 2024
93eec1e
feat: added task runner & tests
chesterkmr Dec 19, 2024
949373a
feat: implemented file upload on submit & tests
chesterkmr Dec 19, 2024
12534dc
feat: added more form stories & tests update
chesterkmr Dec 20, 2024
b633bcb
feat: added custom validators & custom inputs examples
chesterkmr Dec 20, 2024
695e0bd
feat: added radio field & tests
chesterkmr Dec 27, 2024
13bb76a
feat: added tags input & tests & config fixes
chesterkmr Dec 27, 2024
b34891c
Merge branch 'dev' into bal-3139
chesterkmr Dec 27, 2024
49869ff
feat: added v2 adapters for custom fields & updated exports from ui &…
chesterkmr Dec 27, 2024
82d8388
feat: added field descriptions & updated tests (#2914)
chesterkmr Dec 27, 2024
be6480d
Merge branch 'dev' into bal-3137
chesterkmr Jan 2, 2025
64a72fb
Merge branch 'dev' into bal-3137
alonp99 Jan 4, 2025
63020d1
feat: reworked ui elements for v2 & added tests (#2930)
chesterkmr Jan 6, 2025
1fdbda4
fix: better violation names on statistics page (BAL-3294) (#2932)
r4zendev Jan 5, 2025
d485660
fix: renamed property in applyWhen rule
chesterkmr Jan 7, 2025
a30f028
feat: added url format & refactor
chesterkmr Jan 7, 2025
8019fd8
Bal 3242 (#2939)
chesterkmr Jan 7, 2025
4ae6b95
feat: updated en translations
chesterkmr Jan 7, 2025
c62019a
fix: fixed kyb tests
chesterkmr Jan 7, 2025
6fc4c45
Bal 2977 (#2942)
chesterkmr Jan 7, 2025
76bd2d2
feat: added classnames to row & column
chesterkmr Jan 8, 2025
61ef477
fix: submit button styles
chesterkmr Jan 8, 2025
f5f1959
Bal 3330 (#2949)
chesterkmr Jan 8, 2025
be4e1b6
feat: implemented priority fields & bug fixes & tests (#2950)
chesterkmr Jan 9, 2025
85ff80d
fix: fixed build
chesterkmr Jan 9, 2025
7d49aa0
feat: implemented default data insertion on field list (#2951)
chesterkmr Jan 9, 2025
8b553a8
feat: added support of html tags in descriptions
chesterkmr Jan 9, 2025
11f078a
feat: added useControl hook
chesterkmr Jan 10, 2025
f79ee95
fix: reworked revision
chesterkmr Jan 10, 2025
2d20835
fix: fixed renderer stories
chesterkmr Jan 10, 2025
eeda079
Merge branch 'dev' into bal-3137
chesterkmr Jan 13, 2025
7e55313
Merge branch 'dev' into bal-3137
chesterkmr Jan 15, 2025
f5de88f
Bal 3356 (WIP) (#2967)
chesterkmr Jan 16, 2025
eb2d918
fix: fixed fieldlist
chesterkmr Jan 16, 2025
f96b888
feat: updated tests
chesterkmr Jan 16, 2025
93e33da
feat: added clear value for edge cases as documents
chesterkmr Jan 18, 2025
2712806
fix: fixed tests & plugins
chesterkmr Jan 20, 2025
b9f8c7c
fix: fixed fields extraction from definition
chesterkmr Jan 20, 2025
5ef9151
fix: fixed infinite call of useRules due to default array value
chesterkmr Jan 20, 2025
50829ad
feat: enabled sync rules execution for controls disable and elements …
chesterkmr Jan 20, 2025
4426708
feat: reworked cleanup
chesterkmr Jan 21, 2025
5db5214
feat: updated tests
chesterkmr Jan 21, 2025
4ceb67a
Merge branch 'dev' into bal-3137
chesterkmr Jan 24, 2025
5c19483
fix: added handling of dynamic indexes for useRquired & bugfixes
chesterkmr Jan 24, 2025
51a19ac
fix: fixed types
chesterkmr Jan 24, 2025
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
Next Next commit
feat: added initial validator boilerplate & utilities & tests
  • Loading branch information
chesterkmr committed Dec 2, 2024
commit 75d4a34ad39073c5bb39e4b42f97aef410888b3b
3 changes: 3 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
"clsx": "^1.2.1",
"cmdk": "^0.2.0",
"dayjs": "^1.11.6",
"email-validator": "^2.0.4",
"i18n-iso-countries": "^7.6.0",
"lodash": "^4.17.21",
"lucide-react": "^0.144.0",
@@ -76,6 +77,8 @@
"@storybook/react": "^7.0.26",
"@storybook/react-vite": "^7.0.26",
"@storybook/testing-library": "^0.0.14-next.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^13.3.0",
"@types/lodash": "^4.14.191",
"@types/node": "^20.4.1",
"@types/react": "^18.0.37",
1 change: 1 addition & 0 deletions packages/ui/src/components/organisms/Form/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./_Validator
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ValidatorContext } from './context';
import { IValidatorRef, useValidatorRef } from './hooks/internal/useValidatorRef';
import { IValidationSchema } from './types';

export interface IValidatorProviderProps<TValue> {
children: React.ReactNode | React.ReactNode[];
schema: IValidationSchema[];
value: TValue;

ref?: React.RefObject<IValidatorRef>;
validateOnChange?: boolean;
}

export const ValidatorProvider = <TValue,>({
children,
schema,
value,
ref,
}: IValidatorProviderProps<TValue>) => {
useValidatorRef(ref);

return <ValidatorContext.Provider value={{}}>{children}</ValidatorContext.Provider>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './types';
export * from './validator-context';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IValidationError } from '../types';

export interface IValidatorContext<TValues> {
errors: IValidationError[];
values: TValues;
isValid: boolean;
validate: () => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from 'react';
import { IValidatorContext } from './types';

export const ValidatorContext = createContext<IValidatorContext<unknown>>({
errors: [],
values: {},
isValid: true,
validate: () => {
throw new Error('Validator context is not provided.');
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useValidator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useContext } from 'react';
import { ValidatorContext } from '../../../context';

export const useValidator = () => {
const context = useContext(ValidatorContext);

if (!context) {
throw new Error('Validator context is not provided.');
}

return context;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './types';
export * from './useValidatorRef';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface IValidatorRef {
validate: () => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useImperativeHandle } from 'react';
import { useValidator } from '../../external/useValidator/useValidator';
import { IValidatorRef } from './types';

export const useValidatorRef = (refObject?: React.RefObject<IValidatorRef>): IValidatorRef => {
const context = useValidator();

useImperativeHandle(refObject, () => ({
validate: context.validate,
}));

return context;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useValidator } from '../../external/useValidator/useValidator';
import { useValidatorRef } from './useValidatorRef';

const mockValidate = vi.fn();

vi.mock('../../external/useValidator/useValidator', () => ({
useValidator: vi.fn(() => ({
validate: mockValidate,
})),
}));

describe('useValidatorRef', () => {
const mockRef = { current: null };

beforeEach(() => {
vi.clearAllMocks();
});

it('should return context from useValidator', () => {
const { result } = renderHook(() => useValidatorRef());

expect(result.current).toEqual({
validate: mockValidate,
});
expect(useValidator).toHaveBeenCalled();
});

it('should set ref.current.validate to context.validate when ref is provided', () => {
renderHook(() => useValidatorRef(mockRef));

expect(mockRef.current).toEqual({
validate: mockValidate,
});
});

it('should not set ref when no ref object is provided', () => {
const { result } = renderHook(() => useValidatorRef());

expect(result.current).toEqual({
validate: mockValidate,
});
});
});
4 changes: 4 additions & 0 deletions packages/ui/src/components/organisms/Form/Validator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './hooks/external/useValidator';
export * from './types';
export * from './utils/register-validator';
export * from './ValidatorProvider';
41 changes: 41 additions & 0 deletions packages/ui/src/components/organisms/Form/Validator/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export type TBaseValidationRules = 'json-logic';

export interface IValidationRule {
type: TBaseValidationRules;
value: object;
}

export type TBaseValidators =
| 'required'
| 'minLength'
| 'maxLength'
| 'pattern'
| 'minimum'
| 'maximum';

export interface ICommonValidator<T = object, TValidatorType = TBaseValidators> {
type: TValidatorType;
value: T;
message?: string;
applyWhen?: IValidationRule;
}

export interface IValidationSchema {
id: string;
validators: ICommonValidator[];
children?: IValidationSchema[];
}

export interface IValidationError {
id: string;
originId: string;
invalidValue: unknown;
message: string[];
}

export * from '../hooks/internal/useValidatorRef/types';

export type TValidator<T, TValidatorParams = unknown> = (
value: T,
validator: ICommonValidator<TValidatorParams>,
) => void;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const formatErrorMessage = (message: string, key: string, value: string) =>
message.replaceAll(`{${key}}`, value);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from 'vitest';
import { formatErrorMessage } from './format-error-message';

describe('formatErrorMessage', () => {
it('should replace single placeholder with value', () => {
const message = 'This is a {test} message';
const result = formatErrorMessage(message, 'test', 'sample');
expect(result).toBe('This is a sample message');
});

it('should replace multiple occurrences of the same placeholder', () => {
const message = 'The {value} is equal to {value}';
const result = formatErrorMessage(message, 'value', '42');
expect(result).toBe('The 42 is equal to 42');
});

it('should not modify message when placeholder is not found', () => {
const message = 'This message has no placeholders';
const result = formatErrorMessage(message, 'key', 'value');
expect(result).toBe('This message has no placeholders');
});

it('should handle empty strings', () => {
const message = '';
const result = formatErrorMessage(message, 'key', 'value');
expect(result).toBe('');
});

it('should handle special characters in placeholder values', () => {
const message = 'Special {char} test';
const result = formatErrorMessage(message, 'char', '$@#');
expect(result).toBe('Special $@# test');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './format-error-message';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ICommonValidator } from '../../types';
import { baseValidatorsMap, validatorsExtends } from '../../validators';

export const getValidator = (validator: ICommonValidator) => {
const validatorFn = baseValidatorsMap[validator.type] || validatorsExtends[validator.type];

if (!validatorFn) {
throw new Error(`Validator ${validator.type} not found.`);
}

return validatorFn;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ICommonValidator, TBaseValidators } from '../../types';
import { baseValidatorsMap, validatorsExtends } from '../../validators';
import { getValidator } from './get-validator';

vi.mock('../../validators', () => ({
baseValidatorsMap: {},
validatorsExtends: {},
}));

describe('getValidator', () => {
const mockValidator = vi.fn();
const validatorType = 'test';

beforeEach(() => {
vi.clearAllMocks();
});

it('should return validator from baseValidatorsMap if exists', () => {
baseValidatorsMap[validatorType as TBaseValidators] = mockValidator;

const result = getValidator({ type: validatorType } as unknown as ICommonValidator);

expect(result).toBe(mockValidator);
});

it('should return validator from validatorsExtends if exists and not in baseValidatorsMap', () => {
validatorsExtends[validatorType] = mockValidator;

const result = getValidator({ type: validatorType } as unknown as ICommonValidator);

expect(result).toBe(mockValidator);
});

it('should throw error if validator not found', () => {
expect(() =>
getValidator({ type: 'nonexistent' as TBaseValidators } as unknown as ICommonValidator),
).toThrow('Validator nonexistent not found.');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './get-validator';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './register-validator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { TValidator } from '../../types';
import { validatorsExtends } from '../../validators';

export const registerValidator = (type: string, validator: TValidator<any, any>) => {
validatorsExtends[type] = validator;

return validator;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { validatorsExtends } from '../../validators';
import { registerValidator } from './register-validator';

vi.mock('../../validators', () => ({
validatorsExtends: {},
}));

describe('registerValidator', () => {
const mockValidator = vi.fn();
const validatorType = 'test';

beforeEach(() => {
vi.clearAllMocks();
});

it('should register validator to validatorsExtends', () => {
registerValidator(validatorType, mockValidator);

expect(validatorsExtends[validatorType]).toBe(mockValidator);
});

it('should return the registered validator', () => {
const result = registerValidator(validatorType, mockValidator);

expect(result).toBe(mockValidator);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './validate';
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import EmailValidator from 'email-validator';
import { TValidator } from '../../types';
import { formatErrorMessage } from '../../utils/format-error-message';
import { IFormatValueValidatorParams } from './types';

export const formatValidator: TValidator<unknown, IFormatValueValidatorParams> = (
value,
params,
) => {
const { message = 'Invalid {format} format.' } = params;

if (params.value.format === 'email') {
const isValid = EmailValidator.validate(value as string);

if (!isValid) {
throw new Error(formatErrorMessage(message, 'format', 'email'));
}

return true;
}

throw new Error(`Format validator ${params.value.format} is not supported.`);
};
Loading