From fdbdf93250a1c0a2226bb030454470d1e015c6dc Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Sun, 22 Nov 2020 00:46:10 -0500 Subject: [PATCH 1/2] add types for all field-types, and other type tweaks --- demo/collections/AllFields.js | 4 -- src/collections/config/types.ts | 3 +- src/fields/config/types.ts | 123 ++++++++++++++++++++++++++------ 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/demo/collections/AllFields.js b/demo/collections/AllFields.js index d4d58d6a27..074768300d 100644 --- a/demo/collections/AllFields.js +++ b/demo/collections/AllFields.js @@ -191,10 +191,6 @@ const AllFields = { { type: 'blocks', label: 'Blocks Content', - labels: { - singular: 'Block', - plural: 'Blocks', - }, name: 'blocks', minRows: 2, blocks: [Email, NumberBlock, Quote, CallToAction], diff --git a/src/collections/config/types.ts b/src/collections/config/types.ts index 4c9170758e..f430897d3d 100644 --- a/src/collections/config/types.ts +++ b/src/collections/config/types.ts @@ -15,6 +15,7 @@ export type Collection = { singular: string; plural: string; }; + fields: Field[]; admin?: { useAsTitle?: string; defaultColumns?: string[]; @@ -62,10 +63,10 @@ export type Collection = { generateEmailSubject?: (args?: {req?: PayloadRequest}) => string, } }; - fields: Field[]; upload: { imageSizes: ImageSize[]; staticURL: string; staticDir: string; + adminThumbnail?: string; }; }; diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index 8735f91844..27d4041110 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -1,4 +1,6 @@ +/* eslint-disable no-use-before-define */ import { CSSProperties } from 'react'; +import { Access } from '../../config/types'; import { PayloadRequest } from '../../express/types/payloadRequest'; // TODO: add generic type and use mongoose types for originalDoc & data @@ -9,29 +11,17 @@ export type FieldHook = (args: { operation?: 'create' | 'update', req?: PayloadRequest}) => Promise | any; -export type Field = { +type FieldBase = { name: string; - label?: string; - type: - | 'number' - | 'text' - | 'email' - | 'textarea' - | 'richText' - | 'code' - | 'radio' - | 'checkbox' - | 'date' - | 'upload' - | 'relationship' - | 'row' - | 'array' - | 'group' - | 'select' - | 'blocks'; - localized?: boolean; + label: string; + required?: boolean; + unique?: boolean; + defaultValue?: any; hidden?: boolean; - fields?: Field[]; + localized?: boolean; + maxLength?: number; + height?: number; + // eslint-disable-next-line no-use-before-define hooks?: { beforeValidate?: FieldHook[]; beforeChange?: FieldHook[]; @@ -39,10 +29,99 @@ export type Field = { afterRead?: FieldHook[]; } admin?: { - position?: string; + position?: 'sidebar'; width?: string; style?: CSSProperties; readOnly?: boolean; disabled?: boolean; + components?: { [key: string]: JSX.Element | (() => JSX.Element) }; }; + access?: { + create?: Access; + read?: Access; + update?: Access; + delete?: Access; + admin?: Access; + unlock?: Access; + }; +} + +export type StandardField = FieldBase & { + fields?: Field[]; +} + +export type NumberField = StandardField & { type: 'number'; }; +export type TextField = StandardField & { type: 'text'; }; +export type EmailField = StandardField & { type: 'email'; }; +export type TextareaField = StandardField & { type: 'textarea'; }; +export type CodeField = StandardField & { type: 'code'; }; +export type CheckboxField = StandardField & { type: 'checkbox'; }; +export type DateboxField = StandardField & { type: 'date'; }; +export type GroupField = StandardField & { type: 'group'; }; +export type RowField = StandardField & { type: 'row'; }; + +export type UploadField = FieldBase & { + type: 'upload'; + relationTo: string; +} + +export type SelectField = FieldBase & { + type: 'select'; + options: { + value: string; + label: string; + }[]; + hasMany?: boolean; +} + +export type SelectManyField = SelectField & { + hasMany: true; +} + +export type RelationshipField = FieldBase & { + type: 'relationship'; + relationTo: string | string[]; +} + +type RichTextElements = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'blockquote' | 'ul' | 'ol' | 'link'; +type RichTextLeaves = 'bold' | 'italic' | 'underline' | 'strikethrough'; +export type RichTextField = FieldBase & { + type: 'richText'; + admin?: { + elements?: RichTextElements[]; + leaves?: RichTextLeaves[]; + } +} + +export type ArrayField = FieldBase & { + type: 'array'; + minRows?: number; + maxRows?: number; + fields?: Field[]; +} + +export type RadioField = FieldBase & { + type: 'radio'; + options: { + value: string; + label: string; + }[]; +} + +export type Block = { + slug: string, + labels: { + singular: string; + plural: string; + }; + fields: Field[], +} + +export type BlockField = FieldBase & { + type: 'blocks'; + minRows?: number; + maxRows?: number; + blocks?: Block[]; }; + +export type Field = NumberField | TextField | EmailField | TextareaField | CodeField | CheckboxField | DateboxField | BlockField | RadioField | RelationshipField | ArrayField | RichTextField | GroupField | RowField | SelectField | SelectManyField | UploadField; From 314ea43e6d70744ea063cb79b8e978cda4deb3bf Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Sun, 22 Nov 2020 01:22:28 -0500 Subject: [PATCH 2/2] revise field types and type buildSchema --- src/fields/config/types.ts | 18 ++++++++++++---- src/mongoose/buildSchema.ts | 43 +++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index 27d4041110..13219c70bc 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -34,6 +34,7 @@ type FieldBase = { style?: CSSProperties; readOnly?: boolean; disabled?: boolean; + condition?: (...args: any[]) => any | void; components?: { [key: string]: JSX.Element | (() => JSX.Element) }; }; access?: { @@ -56,7 +57,7 @@ export type EmailField = StandardField & { type: 'email'; }; export type TextareaField = StandardField & { type: 'textarea'; }; export type CodeField = StandardField & { type: 'code'; }; export type CheckboxField = StandardField & { type: 'checkbox'; }; -export type DateboxField = StandardField & { type: 'date'; }; +export type DateField = StandardField & { type: 'date'; }; export type GroupField = StandardField & { type: 'group'; }; export type RowField = StandardField & { type: 'row'; }; @@ -78,11 +79,20 @@ export type SelectManyField = SelectField & { hasMany: true; } -export type RelationshipField = FieldBase & { +type RelationShipSingleField = FieldBase & { type: 'relationship'; - relationTo: string | string[]; + relationTo: string; + hasMany?: false; } +type RelationShipManyField = FieldBase & { + type: 'relationship'; + relationTo: string[] | string; + hasMany: true; +} + +export type RelationshipField = RelationShipSingleField | RelationShipManyField; + type RichTextElements = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'blockquote' | 'ul' | 'ol' | 'link'; type RichTextLeaves = 'bold' | 'italic' | 'underline' | 'strikethrough'; export type RichTextField = FieldBase & { @@ -124,4 +134,4 @@ export type BlockField = FieldBase & { blocks?: Block[]; }; -export type Field = NumberField | TextField | EmailField | TextareaField | CodeField | CheckboxField | DateboxField | BlockField | RadioField | RelationshipField | ArrayField | RichTextField | GroupField | RowField | SelectField | SelectManyField | UploadField; +export type Field = NumberField | TextField | EmailField | TextareaField | CodeField | CheckboxField | DateField | BlockField | RadioField | RelationshipField | ArrayField | RichTextField | GroupField | RowField | SelectField | SelectManyField | UploadField; diff --git a/src/mongoose/buildSchema.ts b/src/mongoose/buildSchema.ts index 0420435547..5a8a13697f 100644 --- a/src/mongoose/buildSchema.ts +++ b/src/mongoose/buildSchema.ts @@ -1,8 +1,9 @@ /* eslint-disable no-use-before-define */ import { Schema } from 'mongoose'; import { MissingFieldInputOptions } from '../errors'; +import { ArrayField, BlockField, CheckboxField, CodeField, DateField, EmailField, Field, GroupField, NumberField, RadioField, RelationshipField, RichTextField, RowField, SelectField, TextareaField, TextField, UploadField } from '../fields/config/types'; -const setBlockDiscriminators = (fields, schema) => { +const setBlockDiscriminators = (fields: Field[], schema) => { fields.forEach((field) => { if (field.type === 'blocks' && field.blocks && field.blocks.length > 0) { field.blocks.forEach((block) => { @@ -24,7 +25,7 @@ const setBlockDiscriminators = (fields, schema) => { }); }; -const formatBaseSchema = (field) => { +const formatBaseSchema = (field: Field) => { const createAccess = field.access && field.access.create; const condition = field.admin && field.admin.condition; @@ -38,7 +39,7 @@ const formatBaseSchema = (field) => { }; }; -const buildSchema = (configFields, options = {}) => { +const buildSchema = (configFields: Field[], options = {}): Schema => { let fields = {}; configFields.forEach((field) => { @@ -57,31 +58,31 @@ const buildSchema = (configFields, options = {}) => { }; const fieldToSchemaMap = { - number: (field, fields) => ({ + number: (field: NumberField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: Number }, }), - text: (field, fields) => ({ + text: (field: TextField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: String }, }), - email: (field, fields) => ({ + email: (field: EmailField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: String }, }), - textarea: (field, fields) => ({ + textarea: (field: TextareaField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: String }, }), - richText: (field, fields) => ({ + richText: (field: RichTextField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: Schema.Types.Mixed }, }), - code: (field, fields) => ({ + code: (field: CodeField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: String }, }), - radio: (field, fields) => { + radio: (field: RadioField, fields: Field[]) => { if (!field.options || field.options.length === 0) { throw new MissingFieldInputOptions(field); } @@ -97,18 +98,18 @@ const fieldToSchemaMap = { return { ...fields, - [field.name]: field.hasMany ? [schema] : schema, + [field.name]: field.hasMany ? [schema] : schema, // TODO: radio group with hasMany?? }; }, - checkbox: (field, fields) => ({ + checkbox: (field: CheckboxField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: Boolean }, }), - date: (field, fields) => ({ + date: (field: DateField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), type: Date }, }), - upload: (field, fields) => ({ + upload: (field: UploadField, fields: Field[]) => ({ ...fields, [field.name]: { ...formatBaseSchema(field), @@ -116,8 +117,8 @@ const fieldToSchemaMap = { ref: field.relationTo, }, }), - relationship: (field, fields) => { - let schema = {}; + relationship: (field: RelationshipField, fields: Field[]) => { + let schema: { [key: string]: any } = {}; if (Array.isArray(field.relationTo)) { schema.value = { @@ -146,7 +147,7 @@ const fieldToSchemaMap = { [field.name]: schema, }; }, - row: (field, fields) => { + row: (field: RowField, fields: Field[]) => { const newFields = { ...fields }; field.fields.forEach((rowField) => { @@ -160,7 +161,7 @@ const fieldToSchemaMap = { return newFields; }, - array: (field, fields) => { + array: (field: ArrayField, fields: Field[]) => { const schema = buildSchema(field.fields, { _id: false, id: false }); return { @@ -171,7 +172,7 @@ const fieldToSchemaMap = { }, }; }, - group: (field, fields) => { + group: (field: GroupField, fields: Field[]) => { const schema = buildSchema(field.fields, { _id: false, id: false }); return { @@ -183,7 +184,7 @@ const fieldToSchemaMap = { }, }; }, - select: (field, fields) => { + select: (field: SelectField, fields: Field[]) => { if (!field.options || field.options.length === 0) { throw new MissingFieldInputOptions(field); } @@ -202,7 +203,7 @@ const fieldToSchemaMap = { [field.name]: field.hasMany ? [schema] : schema, }; }, - blocks: (field, fields) => { + blocks: (field: BlockField, fields: Field[]) => { const flexibleSchema = new Schema({ blockName: String }, { discriminatorKey: 'blockType', _id: false, id: false }); return {