Skip to content

Commit

Permalink
Extracted DateFieldI18nContext
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexShukel committed Sep 12, 2023
1 parent dc396f8 commit fc47c39
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 125 deletions.
30 changes: 30 additions & 0 deletions packages/x/src/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 './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>
);
};
9 changes: 9 additions & 0 deletions packages/x/src/formatDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import dayjs from 'dayjs';

export const formatDate = (value: Date | null | undefined, pickTime: boolean) => {
if (!(value instanceof Date)) {
return '';
}

return dayjs(value).format(`YYYY-MM-DD${pickTime ? ' HH:mm' : ''}`);

Check warning on line 8 in packages/x/src/formatDate.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
};
71 changes: 22 additions & 49 deletions packages/x/src/useDateField.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useCallback } from 'react';
import { useCallback, useContext } from 'react';
import { FieldConfig, useFieldValidator } from '@reactive-forms/core';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';

import { DateFieldI18nContext } from './DateFieldI18n';
import { formatDate } from './formatDate';
import { ConversionError, ConverterFieldBag, useConverterField } from './useConverterField';

dayjs.extend(customParseFormat);
Expand All @@ -17,45 +19,14 @@ const defaultDateTimeFormats = [
'DD/MM/YYYY HH:mm',
];

export const defaultLocales: Intl.LocalesArgument = 'EN';

export const defaultFormatOptions: Intl.DateTimeFormatOptions = {};

const formatDate = (
value: Date | null | undefined,
locales?: Intl.LocalesArgument,
options?: Intl.DateTimeFormatOptions,
) => {
if (!(value instanceof Date)) {
return '';
}

return value.toLocaleString(locales, options);
};

export type DateFieldErrorMessages = {
invalidInput: string;
required: string;
earlierThanMinDate: (min: Date, locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions) => string;
laterThanMaxDate: (max: Date, locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions) => string;
};

export const defaultErrorMessages: DateFieldErrorMessages = {
invalidInput: 'Must be date',
required: 'Field is required',
earlierThanMinDate: (min, locales, options) => `Date must not be earlier than ${formatDate(min, locales, options)}`,
laterThanMaxDate: (max, locales, options) => `Date must not be later than ${formatDate(max, locales, options)}`,
};

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

formatDate?: (date: Date | null | undefined) => string;
parseDate?: (text: string) => Date;
errorMessages?: Partial<DateFieldErrorMessages>;
formatDate?: (date: Date | null | undefined, pickTime: boolean) => string;
parseDate?: (text: string, pickTime: boolean) => Date;

locales?: Intl.LocalesArgument;
formatOptions?: Intl.DateTimeFormatOptions;
Expand All @@ -70,13 +41,12 @@ export const useDateField = ({
required,
minDate,
maxDate,
pickTime,
pickTime = false,
formatDate: formatDateProps,
parseDate: parseDateProps,
errorMessages = defaultErrorMessages,
locales = defaultLocales,
formatOptions = defaultFormatOptions,
}: DateFieldConfig): DateFieldBag => {
const i18n = useContext(DateFieldI18nContext);

const parseDate = useCallback(
(text: string) => {
text = text.trim();
Expand All @@ -85,32 +55,35 @@ export const useDateField = ({
return null;
}

const errorMessage = errorMessages.invalidInput ?? defaultErrorMessages.invalidInput;

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

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

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

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

return formatDate(value, locales, formatOptions);
return formatDate(value, pickTime);
},
[formatDateProps, formatOptions, locales],
[formatDateProps, pickTime],
);

const parse = useCallback(
(text: string) => (parseDateProps ?? parseDate)(text, pickTime),
[parseDate, parseDateProps, pickTime],
);

const dateBag = useConverterField({
parse: parseDateProps ?? parseDate,
parse,
format,
name,
validator,
Expand All @@ -121,19 +94,19 @@ export const useDateField = ({
name,
validator: (value) => {
if (required && !(value instanceof Date)) {
return errorMessages.required ?? defaultErrorMessages.required;
return i18n.required;
}

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

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

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

return undefined;
Expand Down
Loading

0 comments on commit fc47c39

Please sign in to comment.