chore: fixes and cleanup
This commit is contained in:
@@ -201,7 +201,7 @@ export type TabsField = Omit<FieldBase, 'admin' | 'name' | 'localized'> & {
|
||||
admin?: TabsAdmin
|
||||
}
|
||||
|
||||
type TabAsField = Tab & {
|
||||
export type TabAsField = Tab & {
|
||||
type: 'tab'
|
||||
};
|
||||
|
||||
@@ -370,7 +370,6 @@ export type Field =
|
||||
| RowField
|
||||
| CollapsibleField
|
||||
| TabsField
|
||||
| TabAsField
|
||||
| UIField;
|
||||
|
||||
export type FieldAffectingData =
|
||||
@@ -465,11 +464,11 @@ export function fieldHasMaxDepth(field: Field): field is FieldWithMaxDepth {
|
||||
return (field.type === 'upload' || field.type === 'relationship') && typeof field.maxDepth === 'number';
|
||||
}
|
||||
|
||||
export function fieldIsPresentationalOnly(field: Field): field is UIField {
|
||||
export function fieldIsPresentationalOnly(field: Field | TabAsField): field is UIField {
|
||||
return field.type === 'ui';
|
||||
}
|
||||
|
||||
export function fieldAffectsData(field: Field): field is FieldAffectingData {
|
||||
export function fieldAffectsData(field: Field | TabAsField): field is FieldAffectingData {
|
||||
return 'name' in field && !fieldIsPresentationalOnly(field);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import { Field, fieldAffectsData, tabHasName } from '../../config/types';
|
||||
import { Field, fieldAffectsData, TabAsField, tabHasName } from '../../config/types';
|
||||
import { traverseFields } from './traverseFields';
|
||||
|
||||
type Args = {
|
||||
data: Record<string, unknown>
|
||||
doc: Record<string, unknown>
|
||||
field: Field
|
||||
field: Field | TabAsField
|
||||
operation: 'create' | 'update'
|
||||
req: PayloadRequest
|
||||
siblingData: Record<string, unknown>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Field } from '../../config/types';
|
||||
import { Field, TabAsField } from '../../config/types';
|
||||
import { promise } from './promise';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
|
||||
type Args = {
|
||||
data: Record<string, unknown>
|
||||
doc: Record<string, unknown>
|
||||
fields: Field[]
|
||||
fields: (Field | TabAsField)[]
|
||||
operation: 'create' | 'update'
|
||||
req: PayloadRequest
|
||||
siblingData: Record<string, unknown>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { Field, fieldAffectsData, tabHasName } from '../../config/types';
|
||||
import { Field, fieldAffectsData, TabAsField, tabHasName } from '../../config/types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import { traverseFields } from './traverseFields';
|
||||
import richTextRelationshipPromise from '../../richText/richTextRelationshipPromise';
|
||||
@@ -9,7 +9,7 @@ type Args = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
field: Field
|
||||
field: Field | TabAsField
|
||||
fieldPromises: Promise<void>[]
|
||||
findMany: boolean
|
||||
flattenLocales: boolean
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Field } from '../../config/types';
|
||||
import { Field, TabAsField } from '../../config/types';
|
||||
import { promise } from './promise';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
|
||||
@@ -7,7 +7,7 @@ type Args = {
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
fieldPromises: Promise<void>[]
|
||||
fields: Field[]
|
||||
fields: (Field | TabAsField)[]
|
||||
findMany: boolean
|
||||
flattenLocales: boolean
|
||||
populationPromises: Promise<void>[]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import merge from 'deepmerge';
|
||||
import { Field, fieldAffectsData, tabHasName } from '../../config/types';
|
||||
import { Field, fieldAffectsData, TabAsField, tabHasName } from '../../config/types';
|
||||
import { Operation } from '../../../types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import getValueWithDefault from '../../getDefaultValue';
|
||||
@@ -12,7 +12,7 @@ type Args = {
|
||||
doc: Record<string, unknown>
|
||||
docWithLocales: Record<string, unknown>
|
||||
errors: { message: string, field: string }[]
|
||||
field: Field
|
||||
field: Field | TabAsField
|
||||
id?: string | number
|
||||
mergeLocaleActions: (() => void)[]
|
||||
operation: Operation
|
||||
@@ -62,7 +62,7 @@ export const promise = async ({
|
||||
siblingData[field.name] = siblingDoc[field.name];
|
||||
}
|
||||
|
||||
// Otherwise compute default value
|
||||
// Otherwise compute default value
|
||||
} else if (typeof field.defaultValue !== 'undefined') {
|
||||
siblingData[field.name] = await getValueWithDefault({
|
||||
value: siblingData[field.name],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Field } from '../../config/types';
|
||||
import { Field, TabAsField } from '../../config/types';
|
||||
import { promise } from './promise';
|
||||
import { Operation } from '../../../types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
@@ -8,7 +8,7 @@ type Args = {
|
||||
doc: Record<string, unknown>
|
||||
docWithLocales: Record<string, unknown>
|
||||
errors: { message: string, field: string }[]
|
||||
fields: Field[]
|
||||
fields: (Field | TabAsField)[]
|
||||
id?: string | number
|
||||
mergeLocaleActions: (() => void)[]
|
||||
operation: Operation
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import { Field, fieldAffectsData, tabHasName, valueIsValueWithRelation } from '../../config/types';
|
||||
import { Field, fieldAffectsData, TabAsField, tabHasName, valueIsValueWithRelation } from '../../config/types';
|
||||
import { traverseFields } from './traverseFields';
|
||||
|
||||
type Args = {
|
||||
data: Record<string, unknown>
|
||||
doc: Record<string, unknown>
|
||||
field: Field
|
||||
field: Field | TabAsField
|
||||
id?: string | number
|
||||
operation: 'create' | 'update'
|
||||
overrideAccess: boolean
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import { Field } from '../../config/types';
|
||||
import { Field, TabAsField } from '../../config/types';
|
||||
import { promise } from './promise';
|
||||
|
||||
type Args = {
|
||||
data: Record<string, unknown>
|
||||
doc: Record<string, unknown>
|
||||
fields: Field[]
|
||||
fields: (Field | TabAsField)[]
|
||||
id?: string | number
|
||||
operation: 'create' | 'update'
|
||||
overrideAccess: boolean
|
||||
|
||||
@@ -16,10 +16,11 @@ import { GraphQLJSON } from 'graphql-type-json';
|
||||
import withNullableType from './withNullableType';
|
||||
import formatName from '../utilities/formatName';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
import { ArrayField, CodeField, DateField, EmailField, Field, fieldAffectsData, fieldIsPresentationalOnly, GroupField, NumberField, PointField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField, CollapsibleField, TabsField, CheckboxField, BlockField } from '../../fields/config/types';
|
||||
import { ArrayField, CodeField, DateField, EmailField, Field, fieldAffectsData, GroupField, NumberField, PointField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField, CollapsibleField, TabsField, CheckboxField, BlockField, tabHasName } from '../../fields/config/types';
|
||||
import { toWords } from '../../utilities/formatLabels';
|
||||
import { Payload } from '../../index';
|
||||
import { SanitizedCollectionConfig } from '../../collections/config/types';
|
||||
import { groupOrTabHasRequiredSubfield } from '../../utilities/groupOrTabHasRequiredSubfield';
|
||||
|
||||
export const getCollectionIDType = (config: SanitizedCollectionConfig): GraphQLScalarType => {
|
||||
const idField = config.fields.find((field) => fieldAffectsData(field) && field.name === 'id');
|
||||
@@ -163,7 +164,7 @@ function buildMutationInputType(payload: Payload, name: string, fields: Field[],
|
||||
};
|
||||
},
|
||||
group: (inputObjectTypeConfig: InputObjectTypeConfig, field: GroupField) => {
|
||||
const requiresAtLeastOneField = field.fields.some((subField) => (!fieldIsPresentationalOnly(subField) && subField.required && !subField.localized));
|
||||
const requiresAtLeastOneField = groupOrTabHasRequiredSubfield(field);
|
||||
const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
|
||||
let type: GraphQLType = buildMutationInputType(payload, fullName, field.fields, fullName);
|
||||
if (requiresAtLeastOneField) type = new GraphQLNonNull(type);
|
||||
@@ -186,16 +187,30 @@ function buildMutationInputType(payload: Payload, name: string, fields: Field[],
|
||||
if (addSubField) return addSubField(acc, subField);
|
||||
return acc;
|
||||
}, inputObjectTypeConfig),
|
||||
tabs: (inputObjectTypeConfig: InputObjectTypeConfig, field: TabsField) => field.tabs.reduce((acc, tab) => {
|
||||
return {
|
||||
...acc,
|
||||
...tab.fields.reduce((subFieldSchema, subField) => {
|
||||
const addSubField = fieldToSchemaMap[subField.type];
|
||||
if (addSubField) return addSubField(subFieldSchema, subField);
|
||||
return subFieldSchema;
|
||||
}, acc),
|
||||
};
|
||||
}, inputObjectTypeConfig),
|
||||
tabs: (inputObjectTypeConfig: InputObjectTypeConfig, field: TabsField) => {
|
||||
return field.tabs.reduce((acc, tab) => {
|
||||
if (tabHasName(tab)) {
|
||||
const fullName = combineParentName(parentName, toWords(tab.name, true));
|
||||
const requiresAtLeastOneField = groupOrTabHasRequiredSubfield(field);
|
||||
let type: GraphQLType = buildMutationInputType(payload, fullName, tab.fields, fullName);
|
||||
if (requiresAtLeastOneField) type = new GraphQLNonNull(type);
|
||||
|
||||
return {
|
||||
...inputObjectTypeConfig,
|
||||
[tab.name]: { type },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
...tab.fields.reduce((subFieldSchema, subField) => {
|
||||
const addSubField = fieldToSchemaMap[subField.type];
|
||||
if (addSubField) return addSubField(subFieldSchema, subField);
|
||||
return subFieldSchema;
|
||||
}, acc),
|
||||
};
|
||||
}, inputObjectTypeConfig);
|
||||
},
|
||||
};
|
||||
|
||||
const fieldName = formatName(name);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GraphQLNonNull, GraphQLType } from 'graphql';
|
||||
import { NonPresentationalField } from '../../fields/config/types';
|
||||
import { FieldAffectingData } from '../../fields/config/types';
|
||||
|
||||
const withNullableType = (field: NonPresentationalField, type: GraphQLType, forceNullable = false): GraphQLType => {
|
||||
const withNullableType = (field: FieldAffectingData, type: GraphQLType, forceNullable = false): GraphQLType => {
|
||||
const hasReadAccessControl = field.access && field.access.read;
|
||||
const condition = field.admin && field.admin.condition;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
DateField,
|
||||
EmailField,
|
||||
Field,
|
||||
FieldAffectingData,
|
||||
fieldAffectsData, fieldIsLocalized,
|
||||
fieldIsPresentationalOnly,
|
||||
GroupField,
|
||||
@@ -43,7 +44,7 @@ export type BuildSchemaOptions = {
|
||||
|
||||
type FieldSchemaGenerator = (field: Field, schema: Schema, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions) => void;
|
||||
|
||||
const formatBaseSchema = (field: NonPresentationalField, buildSchemaOptions: BuildSchemaOptions) => {
|
||||
const formatBaseSchema = (field: FieldAffectingData, buildSchemaOptions: BuildSchemaOptions) => {
|
||||
const schema: SchemaTypeOptions<unknown> = {
|
||||
unique: (!buildSchemaOptions.disableUnique && field.unique) || false,
|
||||
required: false,
|
||||
@@ -57,8 +58,8 @@ const formatBaseSchema = (field: NonPresentationalField, buildSchemaOptions: Bui
|
||||
return schema;
|
||||
};
|
||||
|
||||
const localizeSchema = (field: NonPresentationalField | Tab, schema, localization) => {
|
||||
if (fieldIsLocalized(field) && localization && Array.isArray(localization.locales)) {
|
||||
const localizeSchema = (entity: NonPresentationalField | Tab, schema, localization) => {
|
||||
if (fieldIsLocalized(entity) && localization && Array.isArray(localization.locales)) {
|
||||
return {
|
||||
type: localization.locales.reduce((localeSchema, locale) => ({
|
||||
...localeSchema,
|
||||
@@ -295,17 +296,18 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
tabs: (field: TabsField, schema: Schema, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): void => {
|
||||
field.tabs.forEach((tab) => {
|
||||
if (tabHasName(tab)) {
|
||||
const formattedBaseSchema = formatBaseSchema(field, buildSchemaOptions);
|
||||
|
||||
const baseSchema = {
|
||||
...formattedBaseSchema,
|
||||
type: buildSchema(config, tab.fields, {
|
||||
options: {
|
||||
_id: false,
|
||||
id: false,
|
||||
type: buildSchema(
|
||||
config,
|
||||
tab.fields,
|
||||
{
|
||||
options: {
|
||||
_id: false,
|
||||
id: false,
|
||||
},
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
},
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
schema.add({
|
||||
@@ -341,14 +343,10 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
});
|
||||
},
|
||||
group: (field: GroupField, schema: Schema, config: SanitizedConfig, buildSchemaOptions: BuildSchemaOptions): void => {
|
||||
let { required } = field;
|
||||
if (field?.admin?.condition || field?.localized || field?.access?.create) required = false;
|
||||
|
||||
const formattedBaseSchema = formatBaseSchema(field, buildSchemaOptions);
|
||||
|
||||
const baseSchema = {
|
||||
...formattedBaseSchema,
|
||||
required: required && field.fields.some((subField) => (!fieldIsPresentationalOnly(subField) && subField.required && !subField.localized && !subField?.admin?.condition && !subField?.access?.create)),
|
||||
type: buildSchema(
|
||||
config,
|
||||
field.fields,
|
||||
|
||||
15
src/utilities/groupOrTabHasRequiredSubfield.ts
Normal file
15
src/utilities/groupOrTabHasRequiredSubfield.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Field, fieldAffectsData, Tab } from '../fields/config/types';
|
||||
|
||||
export const groupOrTabHasRequiredSubfield = (entity: Field | Tab): boolean => {
|
||||
if ('type' in entity && entity.type === 'group') {
|
||||
return entity.fields.some((subField) => {
|
||||
return (fieldAffectsData(subField) && subField.required) || groupOrTabHasRequiredSubfield(subField);
|
||||
});
|
||||
}
|
||||
|
||||
if ('fields' in entity && 'name' in entity) {
|
||||
return (entity as Tab).fields.some((subField) => groupOrTabHasRequiredSubfield(subField));
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -176,25 +176,29 @@ const TabsFields: CollectionConfig = {
|
||||
label: 'Hooks Tab',
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ data }) => {
|
||||
({ data = {} }) => {
|
||||
if (!data.hooksTab) data.hooksTab = {};
|
||||
data.hooksTab.beforeValidate = true;
|
||||
return data.hooksTab;
|
||||
},
|
||||
],
|
||||
beforeChange: [
|
||||
({ data }) => {
|
||||
({ data = {} }) => {
|
||||
if (!data.hooksTab) data.hooksTab = {};
|
||||
data.hooksTab.beforeChange = true;
|
||||
return data.hooksTab;
|
||||
},
|
||||
],
|
||||
afterChange: [
|
||||
({ data }) => {
|
||||
({ data = {} }) => {
|
||||
if (!data.hooksTab) data.hooksTab = {};
|
||||
data.hooksTab.afterChange = true;
|
||||
return data.hooksTab;
|
||||
},
|
||||
],
|
||||
afterRead: [
|
||||
({ data }) => {
|
||||
({ data = {} }) => {
|
||||
if (!data.hooksTab) data.hooksTab = {};
|
||||
data.hooksTab.afterRead = true;
|
||||
return data.hooksTab;
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@ import config from '../uploads/config';
|
||||
import payload from '../../src';
|
||||
import { pointDoc } from './collections/Point';
|
||||
import type { ArrayField, GroupField, TabsField } from './payload-types';
|
||||
import type { ArrayField, GroupField } from './payload-types';
|
||||
import { arrayFieldsSlug, arrayDefaultValue, arrayDoc } from './collections/Array';
|
||||
import { groupFieldsSlug, groupDefaultChild, groupDefaultValue, groupDoc } from './collections/Group';
|
||||
import { defaultText } from './collections/Text';
|
||||
@@ -292,7 +291,7 @@ describe('Fields', () => {
|
||||
collection,
|
||||
id,
|
||||
locale: 'all',
|
||||
}) as unknown as {localized: {en: unknown, es: unknown}};
|
||||
}) as unknown as { localized: { en: unknown, es: unknown } };
|
||||
|
||||
expect(enDoc.localized[0].text).toStrictEqual(enText);
|
||||
expect(esDoc.localized[0].text).toStrictEqual(esText);
|
||||
|
||||
@@ -5814,11 +5814,6 @@ extract-files@^9.0.0:
|
||||
resolved "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a"
|
||||
integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==
|
||||
|
||||
"falsey@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/falsey/-/falsey-1.0.0.tgz#71bdd775c24edad9f2f5c015ce8be24400bb5d7d"
|
||||
integrity sha512-zMDNZ/Ipd8MY0+346CPvhzP1AsiVyNfTOayJza4reAIWf72xbkuFUDcJNxSAsQE1b9Bu0wijKb8Ngnh/a7fI5w==
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
|
||||
Reference in New Issue
Block a user