Merge branch 'master' of github.com:payloadcms/payload into fix/relationship-access-missing-id
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import joi from 'joi';
|
||||
|
||||
const component = joi.alternatives().try(
|
||||
joi.object().unknown(),
|
||||
joi.func(),
|
||||
);
|
||||
import { componentSchema } from '../../utilities/componentSchema';
|
||||
|
||||
export const baseAdminFields = joi.object().keys({
|
||||
description: joi.alternatives().try(
|
||||
joi.string(),
|
||||
componentSchema,
|
||||
),
|
||||
position: joi.string().valid('sidebar'),
|
||||
width: joi.string(),
|
||||
style: joi.object().unknown(),
|
||||
@@ -14,9 +14,9 @@ export const baseAdminFields = joi.object().keys({
|
||||
disabled: joi.boolean().default(false),
|
||||
condition: joi.func(),
|
||||
components: joi.object().keys({
|
||||
Cell: component,
|
||||
Field: component,
|
||||
Filter: component,
|
||||
Cell: componentSchema,
|
||||
Field: componentSchema,
|
||||
Filter: componentSchema,
|
||||
}).default({}),
|
||||
});
|
||||
|
||||
@@ -47,6 +47,13 @@ export const baseField = joi.object().keys({
|
||||
admin: baseAdminFields.default(),
|
||||
}).default();
|
||||
|
||||
export const idField = baseField.keys({
|
||||
name: joi.string().valid('id'),
|
||||
type: joi.string().valid('text', 'number'),
|
||||
required: joi.not(false, 0).default(true),
|
||||
localized: joi.invalid(true),
|
||||
});
|
||||
|
||||
export const text = baseField.keys({
|
||||
type: joi.string().valid('text').required(),
|
||||
name: joi.string().required(),
|
||||
@@ -111,7 +118,7 @@ export const select = baseField.keys({
|
||||
options: joi.array().items(joi.alternatives().try(
|
||||
joi.string(),
|
||||
joi.object({
|
||||
value: joi.string().allow('').required(),
|
||||
value: joi.string().required(),
|
||||
label: joi.string().required(),
|
||||
}),
|
||||
)).required(),
|
||||
@@ -128,7 +135,7 @@ export const radio = baseField.keys({
|
||||
options: joi.array().items(joi.alternatives().try(
|
||||
joi.string(),
|
||||
joi.object({
|
||||
value: joi.string().allow('').required(),
|
||||
value: joi.string().required(),
|
||||
label: joi.string().required(),
|
||||
}),
|
||||
)).required(),
|
||||
@@ -141,16 +148,21 @@ export const radio = baseField.keys({
|
||||
export const row = baseField.keys({
|
||||
type: joi.string().valid('row').required(),
|
||||
fields: joi.array().items(joi.link('#field')),
|
||||
admin: baseAdminFields.keys({
|
||||
description: joi.forbidden(),
|
||||
readOnly: joi.forbidden(),
|
||||
hidden: joi.forbidden(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const group = baseField.keys({
|
||||
type: joi.string().valid('group').required(),
|
||||
name: joi.string().required(),
|
||||
label: joi.string(),
|
||||
fields: joi.array().items(joi.link('#field')),
|
||||
defaultValue: joi.object(),
|
||||
admin: baseAdminFields.keys({
|
||||
hideGutter: joi.boolean().default(false),
|
||||
description: joi.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -180,6 +192,12 @@ export const checkbox = baseField.keys({
|
||||
defaultValue: joi.boolean(),
|
||||
});
|
||||
|
||||
export const point = baseField.keys({
|
||||
type: joi.string().valid('point').required(),
|
||||
name: joi.string().required(),
|
||||
defaultValue: joi.array().items(joi.number()).max(2).min(2),
|
||||
});
|
||||
|
||||
export const relationship = baseField.keys({
|
||||
type: joi.string().valid('relationship').required(),
|
||||
hasMany: joi.boolean().default(false),
|
||||
@@ -226,9 +244,9 @@ export const richText = baseField.keys({
|
||||
joi.string(),
|
||||
joi.object({
|
||||
name: joi.string().required(),
|
||||
Button: component,
|
||||
Element: component,
|
||||
plugins: joi.array().items(component),
|
||||
Button: componentSchema,
|
||||
Element: componentSchema,
|
||||
plugins: joi.array().items(componentSchema),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -237,9 +255,9 @@ export const richText = baseField.keys({
|
||||
joi.string(),
|
||||
joi.object({
|
||||
name: joi.string().required(),
|
||||
Button: component,
|
||||
Leaf: component,
|
||||
plugins: joi.array().items(component),
|
||||
Button: componentSchema,
|
||||
Leaf: componentSchema,
|
||||
plugins: joi.array().items(componentSchema),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -285,6 +303,7 @@ const fieldSchema = joi.alternatives()
|
||||
richText,
|
||||
blocks,
|
||||
date,
|
||||
point,
|
||||
)
|
||||
.id('field');
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export type FieldAccess = (args: {
|
||||
siblingData: Record<string, unknown>
|
||||
}) => Promise<boolean> | boolean;
|
||||
|
||||
export type Condition = (data: Record<string, unknown>, siblingData: Record<string, unknown>) => boolean
|
||||
export type Condition = (data: Record<string, unknown>, siblingData: Record<string, unknown>) => boolean;
|
||||
|
||||
type Admin = {
|
||||
position?: string;
|
||||
@@ -31,10 +31,17 @@ type Admin = {
|
||||
readOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
condition?: Condition;
|
||||
components?: { [key: string]: React.ComponentType };
|
||||
description?: Description;
|
||||
components?: {
|
||||
Filter?: React.ComponentType;
|
||||
Cell?: React.ComponentType;
|
||||
Field?: React.ComponentType;
|
||||
}
|
||||
hidden?: boolean
|
||||
}
|
||||
|
||||
export type Description = string | ((value: Record<string, unknown>) => string);
|
||||
|
||||
export type Labels = {
|
||||
singular: string;
|
||||
plural: string;
|
||||
@@ -133,7 +140,13 @@ export type GroupField = FieldBase & {
|
||||
}
|
||||
}
|
||||
|
||||
export type RowField = FieldBase & {
|
||||
export type RowAdmin = Omit<Admin, 'description'> & {
|
||||
readOnly?: false;
|
||||
hidden?: false;
|
||||
};
|
||||
|
||||
export type RowField = Omit<FieldBase, 'admin'> & {
|
||||
admin?: RowAdmin;
|
||||
type: 'row';
|
||||
fields: Field[];
|
||||
}
|
||||
@@ -184,7 +197,7 @@ export type RichTextCustomLeaf = {
|
||||
plugins?: RichTextPlugin[]
|
||||
}
|
||||
|
||||
export type RichTextElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'blockquote' | 'ul' | 'ol' | 'link' | 'relationship' | RichTextCustomElement;
|
||||
export type RichTextElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'blockquote' | 'ul' | 'ol' | 'link' | 'relationship' | 'upload' | RichTextCustomElement;
|
||||
export type RichTextLeaf = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'code' | RichTextCustomLeaf;
|
||||
|
||||
export type RichTextField = FieldBase & {
|
||||
@@ -214,11 +227,11 @@ export type RadioField = FieldBase & {
|
||||
}
|
||||
|
||||
export type Block = {
|
||||
slug: string,
|
||||
labels?: Labels
|
||||
fields: Field[],
|
||||
imageURL?: string
|
||||
imageAltText?: string
|
||||
slug: string;
|
||||
labels?: Labels;
|
||||
fields: Field[];
|
||||
imageURL?: string;
|
||||
imageAltText?: string;
|
||||
}
|
||||
|
||||
export type BlockField = FieldBase & {
|
||||
@@ -230,6 +243,10 @@ export type BlockField = FieldBase & {
|
||||
labels?: Labels
|
||||
}
|
||||
|
||||
export type PointField = FieldBase & {
|
||||
type: 'point',
|
||||
}
|
||||
|
||||
export type Field =
|
||||
TextField
|
||||
| NumberField
|
||||
@@ -246,6 +263,7 @@ export type Field =
|
||||
| SelectField
|
||||
| UploadField
|
||||
| CodeField
|
||||
| PointField
|
||||
| RowField;
|
||||
|
||||
export type FieldWithPath = Field & {
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Payload } from '..';
|
||||
import { ValidationError } from '../errors';
|
||||
import sanitizeFallbackLocale from '../localization/sanitizeFallbackLocale';
|
||||
import traverseFields from './traverseFields';
|
||||
import { CollectionConfig } from '../collections/config/types';
|
||||
import { GlobalConfig } from '../globals/config/types';
|
||||
import { SanitizedCollectionConfig } from '../collections/config/types';
|
||||
import { SanitizedGlobalConfig } from '../globals/config/types';
|
||||
import { Operation } from '../types';
|
||||
import { PayloadRequest } from '../express/types';
|
||||
import { HookName } from './config/types';
|
||||
@@ -25,7 +25,7 @@ type Arguments = {
|
||||
currentDepth?: number
|
||||
}
|
||||
|
||||
export default async function performFieldOperations(this: Payload, entityConfig: CollectionConfig | GlobalConfig, args: Arguments): Promise<{ [key: string]: unknown }> {
|
||||
export default async function performFieldOperations(this: Payload, entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig, args: Arguments): Promise<{ [key: string]: unknown }> {
|
||||
const {
|
||||
data,
|
||||
originalDoc: fullOriginalDoc,
|
||||
@@ -66,6 +66,7 @@ export default async function performFieldOperations(this: Payload, entityConfig
|
||||
const relationshipPopulations = [];
|
||||
const hookPromises = [];
|
||||
const unflattenLocaleActions = [];
|
||||
const transformActions = [];
|
||||
const errors: { message: string, field: string }[] = [];
|
||||
|
||||
// //////////////////////////////////////////
|
||||
@@ -98,26 +99,35 @@ export default async function performFieldOperations(this: Payload, entityConfig
|
||||
showHiddenFields,
|
||||
unflattenLocales,
|
||||
unflattenLocaleActions,
|
||||
transformActions,
|
||||
docWithLocales,
|
||||
});
|
||||
|
||||
await Promise.all(hookPromises);
|
||||
if (hook === 'afterRead') {
|
||||
transformActions.forEach((action) => action());
|
||||
}
|
||||
|
||||
const hookResults = hookPromises.map((promise) => promise());
|
||||
await Promise.all(hookResults);
|
||||
|
||||
validationPromises.forEach((promise) => promise());
|
||||
|
||||
await Promise.all(validationPromises);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new ValidationError(errors);
|
||||
}
|
||||
|
||||
if (hook === 'beforeChange') {
|
||||
transformActions.forEach((action) => action());
|
||||
}
|
||||
|
||||
unflattenLocaleActions.forEach((action) => action());
|
||||
|
||||
await Promise.all(accessPromises);
|
||||
const accessResults = accessPromises.map((promise) => promise());
|
||||
await Promise.all(accessResults);
|
||||
|
||||
const relationshipPopulationPromises = relationshipPopulations.map((population) => population());
|
||||
|
||||
await Promise.all(relationshipPopulationPromises);
|
||||
const relationshipPopulationResults = relationshipPopulations.map((population) => population());
|
||||
await Promise.all(relationshipPopulationResults);
|
||||
|
||||
return fullData;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ const recurseRichText = ({
|
||||
(children as any[]).forEach((element) => {
|
||||
const collection = payload.collections[element?.relationTo];
|
||||
|
||||
if (element.type === 'relationship'
|
||||
if ((element.type === 'relationship' || element.type === 'upload')
|
||||
&& element?.value?.id
|
||||
&& collection
|
||||
&& (depth && currentDepth <= depth)) {
|
||||
|
||||
@@ -15,7 +15,7 @@ type Arguments = {
|
||||
flattenLocales: boolean
|
||||
locale: string
|
||||
fallbackLocale: string
|
||||
accessPromises: Promise<void>[]
|
||||
accessPromises: (() => Promise<void>)[]
|
||||
operation: Operation
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
@@ -24,7 +24,7 @@ type Arguments = {
|
||||
depth: number
|
||||
currentDepth: number
|
||||
hook: HookName
|
||||
hookPromises: Promise<void>[]
|
||||
hookPromises: (() => Promise<void>)[]
|
||||
fullOriginalDoc: Record<string, any>
|
||||
fullData: Record<string, any>
|
||||
validationPromises: (() => Promise<string | boolean>)[]
|
||||
@@ -33,7 +33,9 @@ type Arguments = {
|
||||
showHiddenFields: boolean
|
||||
unflattenLocales: boolean
|
||||
unflattenLocaleActions: (() => void)[]
|
||||
transformActions: (() => void)[]
|
||||
docWithLocales?: Record<string, any>
|
||||
skipValidation?: boolean
|
||||
}
|
||||
|
||||
const traverseFields = (args: Arguments): void => {
|
||||
@@ -63,22 +65,46 @@ const traverseFields = (args: Arguments): void => {
|
||||
showHiddenFields,
|
||||
unflattenLocaleActions,
|
||||
unflattenLocales,
|
||||
transformActions,
|
||||
docWithLocales = {},
|
||||
skipValidation,
|
||||
} = args;
|
||||
|
||||
fields.forEach((field) => {
|
||||
const dataCopy = data;
|
||||
|
||||
if (hook === 'afterRead' && field.hidden && typeof data[field.name] !== 'undefined' && !showHiddenFields) {
|
||||
delete data[field.name];
|
||||
if (hook === 'afterRead') {
|
||||
if (field.type === 'group') {
|
||||
// Fill groups with empty objects so fields with hooks within groups can populate
|
||||
// themselves virtually as necessary
|
||||
if (typeof data[field.name] === 'undefined' && typeof originalDoc[field.name] === 'undefined') {
|
||||
data[field.name] = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (field.hidden && typeof data[field.name] !== 'undefined' && !showHiddenFields) {
|
||||
delete data[field.name];
|
||||
}
|
||||
|
||||
if (field.type === 'point') {
|
||||
transformActions.push(() => {
|
||||
if (data[field.name]?.coordinates && Array.isArray(data[field.name].coordinates) && data[field.name].coordinates.length === 2) {
|
||||
data[field.name] = data[field.name].coordinates;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((field.type === 'upload' || field.type === 'relationship')
|
||||
&& (data[field.name] === '' || data[field.name] === 'none' || data[field.name] === 'null')) {
|
||||
dataCopy[field.name] = null;
|
||||
if (field.type === 'relationship' && field.hasMany === true) {
|
||||
dataCopy[field.name] = [];
|
||||
} else {
|
||||
dataCopy[field.name] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'relationship' && field.hasMany && (data[field.name]?.[0] === '' || data[field.name]?.[0] === 'none' || data[field.name]?.[0] === 'null')) {
|
||||
if (field.type === 'relationship' && field.hasMany && (data[field.name] === '' || data[field.name] === 'none' || data[field.name] === 'null')) {
|
||||
dataCopy[field.name] = [];
|
||||
}
|
||||
|
||||
@@ -86,6 +112,15 @@ const traverseFields = (args: Arguments): void => {
|
||||
dataCopy[field.name] = parseFloat(data[field.name]);
|
||||
}
|
||||
|
||||
if (field.name === 'id') {
|
||||
if (field.type === 'number' && typeof data[field.name] === 'string') {
|
||||
dataCopy[field.name] = parseFloat(data[field.name]);
|
||||
}
|
||||
if (field.type === 'text' && typeof data[field.name]?.toString === 'function' && typeof data[field.name] !== 'string') {
|
||||
dataCopy[field.name] = dataCopy[field.name].toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'checkbox') {
|
||||
if (data[field.name] === 'true') dataCopy[field.name] = true;
|
||||
if (data[field.name] === 'false') dataCopy[field.name] = false;
|
||||
@@ -125,6 +160,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
if (hasLocalizedValue) {
|
||||
let localizedValue = data[field.name][locale];
|
||||
if (typeof localizedValue === 'undefined' && fallbackLocale) localizedValue = data[field.name][fallbackLocale];
|
||||
if (typeof localizedValue === 'undefined' && field.type === 'group') localizedValue = {};
|
||||
if (typeof localizedValue === 'undefined') localizedValue = null;
|
||||
dataCopy[field.name] = localizedValue;
|
||||
}
|
||||
@@ -161,7 +197,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
});
|
||||
}
|
||||
|
||||
accessPromises.push(accessPromise({
|
||||
accessPromises.push(() => accessPromise({
|
||||
data,
|
||||
fullData,
|
||||
originalDoc,
|
||||
@@ -177,7 +213,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
payload,
|
||||
}));
|
||||
|
||||
hookPromises.push(hookPromise({
|
||||
hookPromises.push(() => hookPromise({
|
||||
data,
|
||||
field,
|
||||
hook,
|
||||
@@ -187,11 +223,15 @@ const traverseFields = (args: Arguments): void => {
|
||||
fullData,
|
||||
}));
|
||||
|
||||
const passesCondition = (field.admin?.condition && hook === 'beforeChange') ? field.admin.condition(fullData, data) : true;
|
||||
const skipValidationFromHere = skipValidation || !passesCondition;
|
||||
|
||||
if (fieldHasSubFields(field)) {
|
||||
if (field.name === undefined) {
|
||||
traverseFields({
|
||||
...args,
|
||||
fields: field.fields,
|
||||
skipValidation: skipValidationFromHere,
|
||||
});
|
||||
} else if (fieldIsArrayType(field)) {
|
||||
if (Array.isArray(data[field.name])) {
|
||||
@@ -207,6 +247,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
originalDoc: originalDoc?.[field.name]?.[i],
|
||||
docWithLocales: docWithLocales?.[field.name]?.[i],
|
||||
path: `${path}${field.name}.${i}.`,
|
||||
skipValidation: skipValidationFromHere,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -218,6 +259,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
originalDoc: originalDoc[field.name],
|
||||
docWithLocales: docWithLocales?.[field.name],
|
||||
path: `${path}${field.name}.`,
|
||||
skipValidation: skipValidationFromHere,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -235,6 +277,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
originalDoc: originalDoc?.[field.name]?.[i],
|
||||
docWithLocales: docWithLocales?.[field.name]?.[i],
|
||||
path: `${path}${field.name}.${i}.`,
|
||||
skipValidation: skipValidationFromHere,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -248,6 +291,58 @@ const traverseFields = (args: Arguments): void => {
|
||||
updatedData[field.name] = field.defaultValue;
|
||||
}
|
||||
|
||||
if (field.type === 'relationship' || field.type === 'upload') {
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
if (Array.isArray(dataCopy[field.name])) {
|
||||
dataCopy[field.name].forEach((relatedDoc: {value: unknown, relationTo: string}, i) => {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === relatedDoc.relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name][i] = { ...relatedDoc, value: parseFloat(relatedDoc.value as string) };
|
||||
}
|
||||
});
|
||||
}
|
||||
if (field.type === 'relationship' && field.hasMany !== true && dataCopy[field.name]?.relationTo) {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === dataCopy[field.name].relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name] = { ...dataCopy[field.name], value: parseFloat(dataCopy[field.name].value as string) };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(dataCopy[field.name])) {
|
||||
dataCopy[field.name].forEach((relatedDoc: unknown, i) => {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === field.relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name][i] = parseFloat(relatedDoc as string);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (field.type === 'relationship' && field.hasMany !== true && dataCopy[field.name]) {
|
||||
const relatedCollection = payload.config.collections.find((collection) => collection.slug === field.relationTo);
|
||||
const relationshipIDField = relatedCollection.fields.find((collectionField) => collectionField.name === 'id');
|
||||
if (relationshipIDField?.type === 'number') {
|
||||
dataCopy[field.name] = parseFloat(dataCopy[field.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'point' && data[field.name]) {
|
||||
transformActions.push(() => {
|
||||
if (Array.isArray(data[field.name]) && data[field.name][0] !== null && data[field.name][1] !== null) {
|
||||
data[field.name] = {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
parseFloat(data[field.name][0]),
|
||||
parseFloat(data[field.name][1]),
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (field.type === 'array' || field.type === 'blocks') {
|
||||
const hasRowsOfNewData = Array.isArray(data[field.name]);
|
||||
const newRowCount = hasRowsOfNewData ? (data[field.name] as Record<string, unknown>[]).length : 0;
|
||||
@@ -267,6 +362,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
existingData: { [field.name]: existingRowCount },
|
||||
field,
|
||||
path,
|
||||
skipValidation: skipValidationFromHere,
|
||||
}));
|
||||
} else {
|
||||
validationPromises.push(() => validationPromise({
|
||||
@@ -276,6 +372,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
existingData: originalDoc,
|
||||
field,
|
||||
path,
|
||||
skipValidation: skipValidationFromHere,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ type Arguments = {
|
||||
errors: {message: string, field: string}[]
|
||||
newData: Record<string, unknown>
|
||||
existingData: Record<string, unknown>
|
||||
skipValidation?: boolean
|
||||
}
|
||||
|
||||
const validationPromise = async ({
|
||||
@@ -16,8 +17,9 @@ const validationPromise = async ({
|
||||
existingData,
|
||||
field,
|
||||
path,
|
||||
skipValidation,
|
||||
}: Arguments): Promise<string | boolean> => {
|
||||
if (hook !== 'beforeChange') return true;
|
||||
if (hook !== 'beforeChange' || skipValidation) return true;
|
||||
|
||||
const hasCondition = field.admin && field.admin.condition;
|
||||
const shouldValidate = field.validate && !hasCondition;
|
||||
|
||||
@@ -199,6 +199,24 @@ export const blocks: Validate = (value, options = {}) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const point: Validate = (value: [number | string, number | string] = ['', ''], options = {}) => {
|
||||
const x = parseFloat(String(value[0]));
|
||||
const y = parseFloat(String(value[1]));
|
||||
if (
|
||||
(value[0] && value[1] && typeof x !== 'number' && typeof y !== 'number')
|
||||
|| (options.required && (Number.isNaN(x) || Number.isNaN(y)))
|
||||
|| (Array.isArray(value) && value.length !== 2)
|
||||
) {
|
||||
return 'This field requires two numbers';
|
||||
}
|
||||
|
||||
if (!options.required && typeof value[0] !== typeof value[1]) {
|
||||
return 'This field requires two numbers or both can be empty';
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default {
|
||||
number,
|
||||
text,
|
||||
@@ -216,4 +234,5 @@ export default {
|
||||
select,
|
||||
radio,
|
||||
blocks,
|
||||
point,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user