feat: add support for interfaceName on radio and select fields to create reusable top level types (#11277)
Adds support for `interfaceName` on radio and select fields. Adding this property will extract your provided options into a top level type for re-use.  Added types test to make sure assignment is consistent.
This commit is contained in:
@@ -50,6 +50,7 @@ export const MyRadioField: Field = {
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ export const MySelectField: Field = {
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
|
||||
@@ -1045,6 +1045,13 @@ export type SelectField = {
|
||||
*/
|
||||
enumName?: DBIdentifierName
|
||||
hasMany?: boolean
|
||||
/** Customize generated GraphQL and Typescript schema names.
|
||||
* By default, it is bound to the collection.
|
||||
*
|
||||
* This is useful if you would like to generate a top level type to share amongst collections/fields.
|
||||
* **Note**: Top level types can collide, ensure they are unique amongst collections, arrays, groups, blocks, tabs.
|
||||
*/
|
||||
interfaceName?: string
|
||||
options: Option[]
|
||||
type: 'select'
|
||||
} & (
|
||||
@@ -1062,7 +1069,7 @@ export type SelectField = {
|
||||
export type SelectFieldClient = {
|
||||
admin?: AdminClient & Pick<SelectField['admin'], 'isClearable' | 'isSortable'>
|
||||
} & FieldBaseClient &
|
||||
Pick<SelectField, 'hasMany' | 'options' | 'type'>
|
||||
Pick<SelectField, 'hasMany' | 'interfaceName' | 'options' | 'type'>
|
||||
|
||||
type SharedRelationshipProperties = {
|
||||
filterOptions?: FilterOptions
|
||||
@@ -1269,6 +1276,13 @@ export type RadioField = {
|
||||
* Customize the DB enum name
|
||||
*/
|
||||
enumName?: DBIdentifierName
|
||||
/** Customize generated GraphQL and Typescript schema names.
|
||||
* By default, it is bound to the collection.
|
||||
*
|
||||
* This is useful if you would like to generate a top level type to share amongst collections/fields.
|
||||
* **Note**: Top level types can collide, ensure they are unique amongst collections, arrays, groups, blocks, tabs.
|
||||
*/
|
||||
interfaceName?: string
|
||||
options: Option[]
|
||||
type: 'radio'
|
||||
validate?: RadioFieldValidation
|
||||
@@ -1277,7 +1291,7 @@ export type RadioField = {
|
||||
export type RadioFieldClient = {
|
||||
admin?: AdminClient & Pick<RadioField['admin'], 'layout'>
|
||||
} & FieldBaseClient &
|
||||
Pick<RadioField, 'options' | 'type'>
|
||||
Pick<RadioField, 'interfaceName' | 'options' | 'type'>
|
||||
|
||||
type BlockFields = {
|
||||
[key: string]: any
|
||||
|
||||
@@ -496,6 +496,14 @@ export function fieldsToJSONSchema(
|
||||
enum: buildOptionEnums(field.options),
|
||||
}
|
||||
|
||||
if (field.interfaceName) {
|
||||
interfaceNameDefinitions.set(field.interfaceName, fieldSchema)
|
||||
|
||||
fieldSchema = {
|
||||
$ref: `#/definitions/${field.interfaceName}`,
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -657,6 +665,15 @@ export function fieldsToJSONSchema(
|
||||
fieldSchema.enum = optionEnums
|
||||
}
|
||||
}
|
||||
|
||||
if (field.interfaceName) {
|
||||
interfaceNameDefinitions.set(field.interfaceName, fieldSchema)
|
||||
|
||||
fieldSchema = {
|
||||
$ref: `#/definitions/${field.interfaceName}`,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
@@ -22,6 +22,38 @@ export default buildConfigWithDefaults({
|
||||
type: 'text',
|
||||
name: 'title',
|
||||
},
|
||||
{
|
||||
name: 'selectField',
|
||||
type: 'select',
|
||||
required: true,
|
||||
interfaceName: 'MySelectOptions',
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'option-1',
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: 'option-2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'radioField',
|
||||
type: 'radio',
|
||||
required: true,
|
||||
interfaceName: 'MyRadioOptions',
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'option-1',
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: 'option-2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,6 +6,16 @@
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "MySelectOptions".
|
||||
*/
|
||||
export type MySelectOptions = 'option-1' | 'option-2';
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "MyRadioOptions".
|
||||
*/
|
||||
export type MyRadioOptions = 'option-1' | 'option-2';
|
||||
/**
|
||||
* Supported timezones in IANA format.
|
||||
*
|
||||
@@ -132,6 +142,8 @@ export interface Post {
|
||||
id: string;
|
||||
text?: string | null;
|
||||
title?: string | null;
|
||||
selectField: MySelectOptions;
|
||||
radioField: MyRadioOptions;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -249,6 +261,8 @@ export interface PayloadMigration {
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
title?: T;
|
||||
selectField?: T;
|
||||
radioField?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,14 @@ import type {
|
||||
import payload from 'payload'
|
||||
import { describe, expect, test } from 'tstyche'
|
||||
|
||||
import type { Menu, Post, User } from './payload-types.js'
|
||||
import type {
|
||||
Menu,
|
||||
MyRadioOptions,
|
||||
MySelectOptions,
|
||||
Post,
|
||||
SupportedTimezones,
|
||||
User,
|
||||
} from './payload-types.js'
|
||||
|
||||
const asType = <T>() => {
|
||||
return '' as T
|
||||
@@ -124,4 +131,18 @@ describe('Types testing', () => {
|
||||
}>()
|
||||
})
|
||||
})
|
||||
|
||||
describe('generated types', () => {
|
||||
test('has SupportedTimezones', () => {
|
||||
expect<SupportedTimezones>().type.toBeAssignableTo<string>()
|
||||
})
|
||||
|
||||
test('has global generated options interface based on select field', () => {
|
||||
expect(asType<Post['selectField']>()).type.toBe<MySelectOptions>()
|
||||
})
|
||||
|
||||
test('has global generated options interface based on radio field', () => {
|
||||
expect(asType<Post['radioField']>()).type.toBe<MyRadioOptions>()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user