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

V1.0.0 #307

Draft
wants to merge 79 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
29116ff
Created `useConverterField` hook
AlexShukel Aug 25, 2023
7b3ccb8
Merge branch 'main' into create-converter-field-abstraction
AlexShukel Aug 25, 2023
3b753e6
Synchronize form value with text state
AlexShukel Aug 25, 2023
d008ef8
Created changeset
AlexShukel Aug 25, 2023
95da53a
Added more tests
AlexShukel Aug 25, 2023
da1df9b
Added validator in ConverterField
AlexShukel Aug 25, 2023
cb6f762
Handled one more test case
AlexShukel Aug 25, 2023
fe213ed
Set field touched=true on blur
AlexShukel Aug 26, 2023
6e6cab1
Created new option "ignoreFormStateUpdatesWhileFocus"
AlexShukel Aug 26, 2023
424da59
Removed unused dependency
AlexShukel Aug 26, 2023
2c62e2d
Created forceSetValue function in useConverterField
AlexShukel Aug 26, 2023
abfe761
Wrap all functions with useCallback in useConverterField
AlexShukel Aug 26, 2023
787e656
Added test case for changing format function
AlexShukel Aug 26, 2023
c8fa353
Created test for changing parse function
AlexShukel Aug 26, 2023
4f4936f
Fixed entrypoint config in package.json
AlexShukel Aug 26, 2023
6301354
Created useIntegerField hook
AlexShukel Aug 27, 2023
1b20781
Created useStringField
AlexShukel Aug 27, 2023
fbc42f6
Merge branch 'main' into create-useIntegerField
AlexShukel Aug 27, 2023
f713677
Merge branch 'main' into create-useStringField
AlexShukel Aug 27, 2023
0416226
Added tests for useStringField
AlexShukel Aug 28, 2023
92c70e9
Added changeset
AlexShukel Aug 28, 2023
829eb49
Added tests for custom errors
AlexShukel Aug 28, 2023
f038b5a
Added test for `formatter` prop
AlexShukel Aug 28, 2023
a2732f5
Added tests for custom errors in useIntegerField
AlexShukel Aug 28, 2023
d970e2e
Added test
AlexShukel Aug 28, 2023
c2d69d4
Added changeset
AlexShukel Aug 28, 2023
9240ac8
Created useBooleanField hook
AlexShukel Aug 28, 2023
db2d685
Added changeset
AlexShukel Aug 28, 2023
6fa257a
Created useDecimalField
AlexShukel Aug 28, 2023
2758e02
Added changeset
AlexShukel Aug 28, 2023
5e8ae6a
Refactored tests
AlexShukel Aug 28, 2023
9bbd99c
Refactoring
AlexShukel Aug 28, 2023
fe963a0
Refactoring
AlexShukel Aug 28, 2023
e099312
Refactoring
AlexShukel Aug 28, 2023
90279ab
Added possibility to customize parseDecimal function
AlexShukel Aug 28, 2023
dc396f8
Created useDateField
AlexShukel Aug 29, 2023
191422f
Refactored useDecimalField
AlexShukel Aug 29, 2023
428c51f
Refactored API for custom errors
AlexShukel Aug 30, 2023
76546bd
Fixed lodash import
AlexShukel Aug 30, 2023
3c11ed7
Refactored IntegerField custom errors API
AlexShukel Aug 30, 2023
95e2d53
Fixed StringField validator
AlexShukel Aug 30, 2023
330ad02
Refactored BooleanField custom errors API
AlexShukel Aug 30, 2023
581c7cc
Refactored DecimalField custom errors API
AlexShukel Aug 30, 2023
b1d42cd
Covered more test cases in useIntegerField
AlexShukel Aug 30, 2023
b6ac825
Covered more test cases in useStringField
AlexShukel Aug 30, 2023
cf8ef56
Refactoring
AlexShukel Aug 30, 2023
c98acd8
Merge branch 'main' into create-useStringField
AlexShukel Sep 10, 2023
f6c637a
Refactoring
AlexShukel Sep 10, 2023
a6e9d71
Removed `formatter` prop
AlexShukel Sep 10, 2023
44c3fcc
Extracted StringFieldI18nContext
AlexShukel Sep 12, 2023
412823a
Extracted IntegerFieldI18nContext
AlexShukel Sep 12, 2023
581cafe
Fixed lodash import
AlexShukel Sep 12, 2023
e0e5bf9
Refactoring
AlexShukel Sep 12, 2023
cb622d9
Extracted BooleanFieldI18nContext
AlexShukel Sep 12, 2023
a0fa8d2
Extracted DecimalFieldI18nContext
AlexShukel Sep 12, 2023
88aac09
Extracted formatDecimal function
AlexShukel Sep 12, 2023
fc47c39
Extracted DateFieldI18nContext
AlexShukel Sep 12, 2023
355d958
Added tests for formatDate
AlexShukel Sep 12, 2023
16a0733
Added tests for pickTime option
AlexShukel Sep 12, 2023
b458edc
Modified default formatDate function
AlexShukel Sep 12, 2023
3d1a94f
Removed unused props
AlexShukel Sep 12, 2023
1406c5f
Refactoring
AlexShukel Sep 12, 2023
90e955c
Added changeset
AlexShukel Sep 12, 2023
04922e1
Removed unused type
AlexShukel Sep 12, 2023
8bc9f27
Extracted `ValueConverter` type
AlexShukel Sep 12, 2023
dec9992
Refactoring
AlexShukel Sep 12, 2023
41b1454
Small fix
AlexShukel Sep 12, 2023
fc9c8a7
Refactoring
AlexShukel Sep 12, 2023
3725f75
Merge branch 'create-useDateField' into v1.0.0
AlexShukel Sep 13, 2023
e613de6
Merge branch 'create-useDecimalField' into v1.0.0
AlexShukel Sep 13, 2023
384c97f
Merge branch 'create-useBooleanField' into v1.0.0
AlexShukel Sep 13, 2023
8a573a9
Merge branch 'create-useStringField' into v1.0.0
AlexShukel Sep 13, 2023
8f04373
Merge branch 'create-useIntegerField' into v1.0.0
AlexShukel Sep 13, 2023
006c31f
Configured tsc build in core package
AlexShukel Sep 13, 2023
0d9ec10
Fixed ts errors in tests
AlexShukel Sep 13, 2023
a7e729e
Moved x package in core
AlexShukel Sep 13, 2023
80b15f8
Updated jest version
AlexShukel Sep 13, 2023
51a7ea5
Rearranged core package structure
AlexShukel Sep 13, 2023
641e2ee
Fixed tests
AlexShukel Sep 13, 2023
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 .changeset/chilly-clocks-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reactive-forms/core': patch
---

Created useStringField hook
5 changes: 5 additions & 0 deletions .changeset/healthy-comics-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reactive-forms/core': minor
---

Created useDecimalField hook
5 changes: 5 additions & 0 deletions .changeset/red-badgers-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reactive-forms/core': minor
---

Created useIntegerField hook
5 changes: 5 additions & 0 deletions .changeset/red-flies-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reactive-forms/core': patch
---

Created useDateField hook
5 changes: 5 additions & 0 deletions .changeset/stale-cars-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reactive-forms/core': minor
---

Created useBooleanField hook
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ yarn-error.log*

scripts/*.mjs
coverage
**/.turbo
**/.turbo

# vscode
launch.json
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"name": "Debug Core Tests",
"type": "node",
"request": "launch",
"runtimeArgs": ["--inspect-brk", "${workspaceFolder}/node_modules/aqu/dist/aqu.js", "test", "--runInBand"],
"cwd": "${workspaceFolder}/packages/core",
"runtimeArgs": ["--inspect-brk", ".\\node_modules\\jest\\bin\\jest.js", "--watch", "useDecimalField"],
"cwd": "${workspaceFolder}/packages/x",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"port": 9229
Expand Down
15 changes: 15 additions & 0 deletions .vscode/launch.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Core Tests",
"type": "node",
"request": "launch",
"runtimeArgs": ["--inspect-brk", ".\\node_modules\\jest\\bin\\jest.js", "--watch"],
"cwd": "${workspaceFolder}/packages/core",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"port": 9229
}
]
}
12 changes: 0 additions & 12 deletions packages/core/aqu.config.json

This file was deleted.

2 changes: 1 addition & 1 deletion packages/core/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const config = {
preset: 'ts-jest',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'cjs', 'mjs', 'json', 'node'],
collectCoverageFrom: ['src/**/*.{ts,tsx,js,jsx,cjs,mjs}'],
testMatch: ['<rootDir>/**/*.(spec|test).{ts,tsx,js,jsx,cjs,mjs}'],
testMatch: ['<rootDir>/tests/**/*.(spec|test).{ts,tsx,js,jsx,cjs,mjs}'],
testEnvironmentOptions: {
url: 'http://localhost',
},
Expand Down
14 changes: 7 additions & 7 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
"directory": "prepared-package"
},
"scripts": {
"build": "aqu build && rimraf ./prepared-package && clean-publish",
"dev": "aqu watch --no-cleanup",
"build": "tsc && rimraf ./prepared-package && clean-publish",
"dev": "tsc --watch",
"lint": "eslint .",
"lint:fix": "npm run lint --fix",
"test": "jest --passWithNoTests",
"test:log-coverage": "jest --passWithNoTests --coverage --silent --ci --coverageReporters=text",
"test:watch": "jest --passWithNoTests --watch"
},
"dependencies": {
"dayjs": "^1.11.9",
"lodash": "4.17.21",
"lodash-es": "4.17.15",
"pxth": "0.7.0",
Expand All @@ -33,17 +34,16 @@
"devDependencies": {
"@babel/core": "7.19.6",
"@reactive-tools/eslint-config": "workspace:*",
"@testing-library/react": "13.4.0",
"@types/jest": "26.0.24",
"@testing-library/react": "14.0.0",
"@types/jest": "29.5.4",
"@types/lodash": "4.14.161",
"@types/node": "^18.11.18",
"@types/react": "18.0.23",
"aqu": "0.4.3",
"jest": "29.2.2",
"jest": "29.7.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"rimraf": "3.0.2",
"ts-jest": "29.0.3",
"ts-jest": "29.1.1",
"tslib": "2.3.1",
"typescript": "4.8.4",
"yup": "0.32.9"
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/components/BooleanField/BooleanFieldI18n.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { createContext, PropsWithChildren } from 'react';
import merge from 'lodash/merge';

export type BooleanFieldI18n = {
required: string;
};

export const defaultBooleanFieldI18n: BooleanFieldI18n = {
required: 'Field is required',
};

export const BooleanFieldI18nContext = createContext<BooleanFieldI18n>(defaultBooleanFieldI18n);

export type BooleanFieldI18nContextProviderProps = PropsWithChildren<{ i18n?: Partial<BooleanFieldI18n> }>;

export const BooleanFieldI18nContextProvider = ({ i18n, children }: BooleanFieldI18nContextProviderProps) => {
return (
<BooleanFieldI18nContext.Provider value={merge(defaultBooleanFieldI18n, i18n)}>
{children}
</BooleanFieldI18nContext.Provider>
);
};
7 changes: 7 additions & 0 deletions packages/core/src/components/BooleanField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { type BooleanFieldBag, type BooleanFieldConfig, useBooleanField } from './useBooleanField';
export {
type BooleanFieldI18n,
BooleanFieldI18nContextProvider,
type BooleanFieldI18nContextProviderProps,
defaultBooleanFieldI18n,
} from './BooleanFieldI18n';
44 changes: 44 additions & 0 deletions packages/core/src/components/BooleanField/useBooleanField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useContext } from 'react';

import { BooleanFieldI18nContext } from './BooleanFieldI18n';
import { useFieldValidator } from '../../helpers/useFieldValidator';
import { FieldContext } from '../../typings/FieldContext';
import { FieldConfig, useField } from '../Field/useField';

export type BooleanFieldConfig = FieldConfig<boolean | null | undefined> & {
required?: boolean;
};

export type BooleanFieldBag = FieldContext<boolean | null | undefined> & {
onBlur: () => void;
};

export const useBooleanField = ({ required, ...config }: BooleanFieldConfig) => {
const fieldBag = useField(config);

const {
control: { setTouched },
} = fieldBag;

const i18n = useContext(BooleanFieldI18nContext);

const onBlur = () => {
setTouched({ $touched: true });
};

useFieldValidator({
name: config.name,
validator: (value) => {
if (required && !value) {
return i18n.required;
}

return undefined;
},
});

return {
...fieldBag,
onBlur,
};
};
6 changes: 6 additions & 0 deletions packages/core/src/components/ConverterField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export {
useConverterField,
ConversionError,
type ConverterFieldBag,
type ConverterFieldConfig,
} from './useConverterField';
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { FieldConfig, FieldContext, FieldError, FieldTouched, useField, useFieldValidator } from '@reactive-forms/core';
import isObject from 'lodash/isObject';

import { useFieldValidator } from '../../helpers';
import { FieldContext } from '../../typings/FieldContext';
import { FieldError } from '../../typings/FieldError';
import { FieldTouched } from '../../typings/FieldTouched';
import { FieldConfig, useField } from '../Field/useField';

export class ConversionError extends Error {
public constructor(errorMessage: string) {
super(errorMessage);
}
}

export type ConverterFieldConfig<T> = {
export type ValueConverter<T> = {
parse: (value: string) => T;
format: (value: T) => string;
} & FieldConfig<T>;
};

export type ConverterFieldConfig<T> = ValueConverter<T> & FieldConfig<T>;

export type ConverterFieldBag<T> = {
text: string;
Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/components/DateField/DateFieldI18n.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { createContext, PropsWithChildren } from 'react';
import merge from 'lodash/merge';

import { formatDate } from '../../utils/formatDate';

export type DateFieldI18n = {
required: string;
invalidInput: string;
minDate: (min: Date, pickTime: boolean) => string;
maxDate: (max: Date, pickTime: boolean) => string;
};

export const defaultDateFieldI18n: DateFieldI18n = {
required: 'Field is required',
invalidInput: 'Must be date',
minDate: (min, pickTime) => `Date must not be earlier than ${formatDate(min, pickTime)}`,
maxDate: (max, pickTime) => `Date must not be later than ${formatDate(max, pickTime)}`,
};

export const DateFieldI18nContext = createContext<DateFieldI18n>(defaultDateFieldI18n);

export type DateFieldI18nContextProviderProps = PropsWithChildren<{ i18n?: Partial<DateFieldI18n> }>;

export const DateFieldI18nContextProvider = ({ i18n, children }: DateFieldI18nContextProviderProps) => {
return (
<DateFieldI18nContext.Provider value={merge(defaultDateFieldI18n, i18n)}>
{children}
</DateFieldI18nContext.Provider>
);
};
7 changes: 7 additions & 0 deletions packages/core/src/components/DateField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { type DateFieldBag, type DateFieldConfig, useDateField } from './useDateField';
export {
type DateFieldI18n,
DateFieldI18nContextProvider,
type DateFieldI18nContextProviderProps,
defaultDateFieldI18n,
} from './DateFieldI18n';
112 changes: 112 additions & 0 deletions packages/core/src/components/DateField/useDateField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useCallback, useContext } from 'react';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';

import { DateFieldI18nContext } from './DateFieldI18n';
import { useFieldValidator } from '../../helpers';
import { formatDate } from '../../utils';
import { ConversionError, ConverterFieldBag, useConverterField } from '../ConverterField';
import { ValueConverter } from '../ConverterField/useConverterField';
import { FieldConfig } from '../Field';

dayjs.extend(customParseFormat);

const defaultDateFormats = ['DD.MM.YYYY', 'YYYY-MM-DD', 'YYYY/MM/DD', 'YYYY.MM.DD', 'DD-MM-YYYY', 'DD/MM/YYYY'];
const defaultDateTimeFormats = [
'DD.MM.YYYY HH:mm',
'YYYY-MM-DD HH:mm',
'YYYY/MM/DD HH:mm',
'YYYY.MM.DD HH:mm',
'DD-MM-YYYY HH:mm',
'DD/MM/YYYY HH:mm',
];

export type DateFieldConfig = FieldConfig<Date | null | undefined> & {
required?: boolean;
minDate?: Date;
maxDate?: Date;
pickTime?: boolean;
} & Partial<ValueConverter<Date | null | undefined>>;

export type DateFieldBag = ConverterFieldBag<Date | null | undefined>;

export const useDateField = ({
name,
validator,
schema,
required,
minDate,
maxDate,
pickTime = false,
format: customFormatDate,
parse: customParseDate,
}: DateFieldConfig): DateFieldBag => {
const i18n = useContext(DateFieldI18nContext);

const parse = useCallback(
(text: string) => {
text = text.trim();

if (customParseDate) {
return customParseDate(text);
}

if (text.length === 0) {
return null;
}

const date = dayjs(text, [...defaultDateFormats, ...(pickTime ? defaultDateTimeFormats : [])], true);

if (!date.isValid()) {
throw new ConversionError(i18n.invalidInput);
}

return date.toDate();
},
[customParseDate, i18n.invalidInput, pickTime],
);

const format = useCallback(
(value: Date | null | undefined) => {
if (customFormatDate) {
return customFormatDate(value);
}

return formatDate(value, pickTime);
},
[customFormatDate, pickTime],
);

const dateBag = useConverterField({
parse,
format,
name,
validator,
schema,
});

useFieldValidator({
name,
validator: (value) => {
if (required && !(value instanceof Date)) {
return i18n.required;
}

if (!(value instanceof Date)) {
return undefined;
}

if (minDate instanceof Date && dayjs(minDate).diff(dayjs(value)) > 0) {
return i18n.minDate(minDate, pickTime);
}

if (maxDate instanceof Date && dayjs(value).diff(dayjs(maxDate)) > 0) {
return i18n.maxDate(maxDate, pickTime);
}

return undefined;
},
});

return dateBag;
};
Loading
Loading