feat: select field filter options (#12487)
It is a common pattern to dynamically show and validate a select field's
options based on various criteria such as the current user or underlying
document.
Some examples of this might include:
- Restricting options based on a user's role, e.g. admin-only options
- Displaying different options based on the value of another field, e.g.
a city/state selector
While this is already possible to do with a custom `validate` function,
the user can still view and select the forbidden option...unless you
_also_ wired up a custom component.
Now, you can define `filterOptions` on select fields.
This behaves similarly to the existing `filterOptions` property on
relationship and upload fields, except the return value of this function
is simply an array of options, not a query constraint. The result of
this function will determine what is shown to the user and what is
validated on the server.
Here's an example:
```ts
{
name: 'select',
type: 'select',
options: [
{
label: 'One',
value: 'one',
},
{
label: 'Two',
value: 'two',
},
{
label: 'Three',
value: 'three',
},
],
filterOptions: ({ options, data }) =>
data.disallowOption1
? options.filter(
(option) => (typeof option === 'string' ? options : option.value) !== 'one',
)
: options,
}
```
This commit is contained in:
@@ -80,7 +80,7 @@ export const MyArrayField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Array Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Array Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -78,7 +78,7 @@ export const MyBlocksField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Blocks Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Blocks Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | -------------------------------------------------------------------------- |
|
||||
|
||||
@@ -68,7 +68,7 @@ export const MyCodeField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Code Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Code Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -58,7 +58,7 @@ export const MyCollapsibleField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Collapsible Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Collapsible Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------- |
|
||||
|
||||
@@ -65,7 +65,7 @@ export const MyDateField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Date Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Date Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -65,7 +65,7 @@ export const MyEmailField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Email Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Email Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------- |
|
||||
|
||||
@@ -69,7 +69,7 @@ export const MyGroupField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Group Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Group Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
|
||||
@@ -67,7 +67,7 @@ export const MyJSONField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The JSON Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The JSON Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -70,7 +70,7 @@ export const MyNumberField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Number Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Number Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -82,7 +82,7 @@ export const MyRadioField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Radio Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Radio Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -86,7 +86,7 @@ export const MyRelationshipField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Relationship Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Relationship Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -54,6 +54,7 @@ export const MySelectField: Field = {
|
||||
| **`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). |
|
||||
| **`filterOptions`** | Dynamically filter which options are available based on the user, data, etc. [More details](#filterOptions) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
@@ -67,6 +68,61 @@ _\* An asterisk denotes that a property is required._
|
||||
used as a GraphQL enum.
|
||||
</Banner>
|
||||
|
||||
### filterOptions
|
||||
|
||||
Used to dynamically filter which options are available based on the user, data, etc.
|
||||
|
||||
Some examples of this might include:
|
||||
|
||||
- Restricting options based on a user's role, e.g. admin-only options
|
||||
- Displaying different options based on the value of another field, e.g. a city/state selector
|
||||
|
||||
The result of `filterOptions` will determine:
|
||||
|
||||
- Which options are displayed in the Admin Panel
|
||||
- Which options can be saved to the database
|
||||
|
||||
To do this, use the `filterOptions` property in your [Field Config](./overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
|
||||
export const MySelectField: Field = {
|
||||
// ...
|
||||
// highlight-start
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: 'One',
|
||||
value: 'one',
|
||||
},
|
||||
{
|
||||
label: 'Two',
|
||||
value: 'two',
|
||||
},
|
||||
{
|
||||
label: 'Three',
|
||||
value: 'three',
|
||||
},
|
||||
],
|
||||
filterOptions: ({ options, data }) =>
|
||||
data.disallowOption1
|
||||
? options.filter(
|
||||
(option) =>
|
||||
(typeof option === 'string' ? options : option.value) !== 'one',
|
||||
)
|
||||
: options,
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
**Note:** This property is similar to `filterOptions` in
|
||||
[Relationship](./relationship) or [Upload](./upload) fields, except that the
|
||||
return value of this function is simply an array of options, not a query
|
||||
constraint.
|
||||
</Banner>
|
||||
|
||||
## Admin Options
|
||||
|
||||
To customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
|
||||
@@ -83,7 +139,7 @@ export const MySelectField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Select Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Select Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Property | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -70,7 +70,7 @@ export const MyTextField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Text Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Text Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -67,7 +67,7 @@ export const MyTextareaField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
The Textarea Field inherits all of the default options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
The Textarea Field inherits all of the default admin options from the base [Field Admin Config](./overview#admin-options), plus the following additional options:
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -20,7 +20,11 @@ export const resolveAllFilterOptions = async ({
|
||||
return
|
||||
}
|
||||
|
||||
if ('name' in field && 'filterOptions' in field && field.filterOptions) {
|
||||
if (
|
||||
(field.type === 'relationship' || field.type === 'upload') &&
|
||||
'filterOptions' in field &&
|
||||
field.filterOptions
|
||||
) {
|
||||
const options = await resolveFilterOptions(field.filterOptions, {
|
||||
id: undefined,
|
||||
blockData: undefined,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { MarkOptional } from 'ts-essentials'
|
||||
|
||||
import type { SelectField, SelectFieldClient } from '../../fields/config/types.js'
|
||||
import type { Option, SelectField, SelectFieldClient } from '../../fields/config/types.js'
|
||||
import type { SelectFieldValidation } from '../../fields/validations.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
|
||||
import type {
|
||||
ClientFieldBase,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type SupportedLanguages } from '@payloadcms/translations'
|
||||
|
||||
import type { SanitizedDocumentPermissions } from '../../auth/types.js'
|
||||
import type { Field, Validate } from '../../fields/config/types.js'
|
||||
import type { Field, Option, Validate } from '../../fields/config/types.js'
|
||||
import type { TypedLocale } from '../../index.js'
|
||||
import type { DocumentPreferences } from '../../preferences/types.js'
|
||||
import type { PayloadRequest, SelectType, Where } from '../../types/index.js'
|
||||
@@ -67,6 +67,10 @@ export type FieldState = {
|
||||
lastRenderedPath?: string
|
||||
passesCondition?: boolean
|
||||
rows?: Row[]
|
||||
/**
|
||||
* The result of running `field.filterOptions` on select fields.
|
||||
*/
|
||||
selectFilterOptions?: Option[]
|
||||
valid?: boolean
|
||||
validate?: Validate
|
||||
value?: unknown
|
||||
|
||||
@@ -36,7 +36,7 @@ export type ServerOnlyFieldProperties =
|
||||
| 'dbName' // can be a function
|
||||
| 'editor' // This is a `richText` only property
|
||||
| 'enumName' // can be a function
|
||||
| 'filterOptions' // This is a `relationship` and `upload` only property
|
||||
| 'filterOptions' // This is a `relationship`, `upload`, and `select` only property
|
||||
| 'graphQL'
|
||||
| 'label'
|
||||
| 'typescriptSchema'
|
||||
@@ -53,7 +53,7 @@ const serverOnlyFieldProperties: Partial<ServerOnlyFieldProperties>[] = [
|
||||
'access',
|
||||
'validate',
|
||||
'defaultValue',
|
||||
'filterOptions', // This is a `relationship` and `upload` only property
|
||||
'filterOptions', // This is a `relationship`, `upload`, and `select` only property
|
||||
'editor', // This is a `richText` only property
|
||||
'custom',
|
||||
'typescriptSchema',
|
||||
@@ -67,10 +67,12 @@ const serverOnlyFieldProperties: Partial<ServerOnlyFieldProperties>[] = [
|
||||
// `tabs`
|
||||
// `admin`
|
||||
]
|
||||
|
||||
const serverOnlyFieldAdminProperties: Partial<ServerOnlyFieldAdminProperties>[] = [
|
||||
'condition',
|
||||
'components',
|
||||
]
|
||||
|
||||
type FieldWithDescription = {
|
||||
admin: AdminClient
|
||||
} & ClientField
|
||||
|
||||
@@ -42,6 +42,7 @@ import type {
|
||||
CollapsibleFieldLabelClientComponent,
|
||||
CollapsibleFieldLabelServerComponent,
|
||||
ConditionalDateProps,
|
||||
Data,
|
||||
DateFieldClientProps,
|
||||
DateFieldErrorClientComponent,
|
||||
DateFieldErrorServerComponent,
|
||||
@@ -1103,8 +1104,19 @@ export type SelectField = {
|
||||
* Customize the DB enum name
|
||||
*/
|
||||
enumName?: DBIdentifierName
|
||||
/**
|
||||
* Reduce the available options based on the current user, value of another field, etc.
|
||||
* Similar to the `filterOptions` property on `relationship` and `upload` fields, except with a different return type.
|
||||
*/
|
||||
filterOptions?: (args: {
|
||||
data: Data
|
||||
options: Option[]
|
||||
req: PayloadRequest
|
||||
siblingData: Data
|
||||
}) => Option[]
|
||||
hasMany?: boolean
|
||||
/** Customize generated GraphQL and Typescript schema names.
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -327,7 +327,7 @@ describe('Field Validations', () => {
|
||||
},
|
||||
],
|
||||
}
|
||||
it('should allow valid input', () => {
|
||||
it('should allow valid input', async () => {
|
||||
const val = 'one'
|
||||
const result = select(val, selectOptions)
|
||||
expect(result).toStrictEqual(true)
|
||||
|
||||
@@ -865,13 +865,23 @@ export type SelectFieldSingleValidation = Validate<string, unknown, unknown, Sel
|
||||
|
||||
export const select: SelectFieldValidation = (
|
||||
value,
|
||||
{ hasMany, options, req: { t }, required },
|
||||
{ data, filterOptions, hasMany, options, req, req: { t }, required, siblingData },
|
||||
) => {
|
||||
const filteredOptions =
|
||||
typeof filterOptions === 'function'
|
||||
? filterOptions({
|
||||
data,
|
||||
options,
|
||||
req,
|
||||
siblingData,
|
||||
})
|
||||
: options
|
||||
|
||||
if (
|
||||
Array.isArray(value) &&
|
||||
value.some(
|
||||
(input) =>
|
||||
!options.some(
|
||||
!filteredOptions.some(
|
||||
(option) => option === input || (typeof option !== 'string' && option?.value === input),
|
||||
),
|
||||
)
|
||||
@@ -881,7 +891,7 @@ export const select: SelectFieldValidation = (
|
||||
|
||||
if (
|
||||
typeof value === 'string' &&
|
||||
!options.some(
|
||||
!filteredOptions.some(
|
||||
(option) => option === value || (typeof option !== 'string' && option.value === value),
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -22,6 +22,7 @@ export type SelectInputProps = {
|
||||
readonly Description?: React.ReactNode
|
||||
readonly description?: StaticDescription
|
||||
readonly Error?: React.ReactNode
|
||||
readonly filterOption?: ReactSelectAdapterProps['filterOption']
|
||||
readonly hasMany?: boolean
|
||||
readonly isClearable?: boolean
|
||||
readonly isSortable?: boolean
|
||||
@@ -49,6 +50,7 @@ export const SelectInput: React.FC<SelectInputProps> = (props) => {
|
||||
Description,
|
||||
description,
|
||||
Error,
|
||||
filterOption,
|
||||
hasMany = false,
|
||||
isClearable = true,
|
||||
isSortable = true,
|
||||
@@ -118,6 +120,7 @@ export const SelectInput: React.FC<SelectInputProps> = (props) => {
|
||||
{BeforeInput}
|
||||
<ReactSelect
|
||||
disabled={readOnly}
|
||||
filterOption={filterOption}
|
||||
isClearable={isClearable}
|
||||
isMulti={hasMany}
|
||||
isSortable={isSortable}
|
||||
|
||||
@@ -67,6 +67,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||
disabled,
|
||||
path,
|
||||
selectFilterOptions,
|
||||
setValue,
|
||||
showError,
|
||||
value,
|
||||
@@ -109,6 +110,14 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
||||
Description={Description}
|
||||
description={description}
|
||||
Error={Error}
|
||||
filterOption={
|
||||
selectFilterOptions
|
||||
? ({ value }) =>
|
||||
selectFilterOptions?.some(
|
||||
(option) => (typeof option === 'string' ? option : option.value) === value,
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
hasMany={hasMany}
|
||||
isClearable={isClearable}
|
||||
isSortable={isSortable}
|
||||
|
||||
@@ -631,6 +631,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'relationship':
|
||||
case 'upload': {
|
||||
if (field.filterOptions) {
|
||||
@@ -719,6 +720,28 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
|
||||
break
|
||||
}
|
||||
|
||||
case 'select': {
|
||||
if (typeof field.filterOptions === 'function') {
|
||||
fieldState.selectFilterOptions = field.filterOptions({
|
||||
data: fullData,
|
||||
options: field.options,
|
||||
req,
|
||||
siblingData: data,
|
||||
})
|
||||
}
|
||||
|
||||
if (data[field.name] !== undefined) {
|
||||
fieldState.value = data[field.name]
|
||||
fieldState.initialValue = data[field.name]
|
||||
}
|
||||
|
||||
if (!filter || filter(args)) {
|
||||
state[path] = fieldState
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (data[field.name] !== undefined) {
|
||||
fieldState.value = data[field.name]
|
||||
|
||||
@@ -128,6 +128,7 @@ export const useField = <TValue,>(options?: Options): FieldType<TValue> => {
|
||||
initialValue,
|
||||
path,
|
||||
rows: field?.rows,
|
||||
selectFilterOptions: field?.selectFilterOptions,
|
||||
setValue,
|
||||
showError,
|
||||
valid: field?.valid,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FieldState, FilterOptionsResult, Row, Validate } from 'payload'
|
||||
import type { FieldState, FilterOptionsResult, Option, Row, Validate } from 'payload'
|
||||
|
||||
export type Options = {
|
||||
disableFormData?: boolean
|
||||
@@ -40,6 +40,7 @@ export type FieldType<T> = {
|
||||
path: string
|
||||
readOnly?: boolean
|
||||
rows?: Row[]
|
||||
selectFilterOptions?: Option[]
|
||||
setValue: (val: unknown, disableModifyingForm?: boolean) => void
|
||||
showError: boolean
|
||||
valid?: boolean
|
||||
|
||||
@@ -95,4 +95,19 @@ describe('Select', () => {
|
||||
|
||||
await expect(page.locator('.cell-selectWithJsxLabelOption svg#payload-logo')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should reduce options', async () => {
|
||||
await page.goto(url.create)
|
||||
const field = page.locator('#field-selectWithFilteredOptions')
|
||||
await field.click({ delay: 100 })
|
||||
const options = page.locator('.rs__option')
|
||||
await expect(options.locator('text=One')).toBeVisible()
|
||||
|
||||
// click the field again to close the options
|
||||
await field.click({ delay: 100 })
|
||||
|
||||
await page.locator('#field-disallowOption1').click()
|
||||
await field.click({ delay: 100 })
|
||||
await expect(options.locator('text=One')).toBeHidden()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -243,6 +243,36 @@ const SelectFields: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'disallowOption1',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'selectWithFilteredOptions',
|
||||
label: 'Select with filtered options',
|
||||
type: 'select',
|
||||
defaultValue: 'one',
|
||||
options: [
|
||||
{
|
||||
label: 'Value One',
|
||||
value: 'one',
|
||||
},
|
||||
{
|
||||
label: 'Value Two',
|
||||
value: 'two',
|
||||
},
|
||||
{
|
||||
label: 'Value Three',
|
||||
value: 'three',
|
||||
},
|
||||
],
|
||||
filterOptions: ({ options, data }) =>
|
||||
data.disallowOption1
|
||||
? options.filter(
|
||||
(option) => (typeof option === 'string' ? options : option.value) !== 'one',
|
||||
)
|
||||
: options,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -848,6 +848,34 @@ describe('Fields', () => {
|
||||
})
|
||||
expect(data.hasMany).toStrictEqual(['a'])
|
||||
})
|
||||
|
||||
it('should prevent against saving a value excluded by `filterOptions`', async () => {
|
||||
try {
|
||||
const result = await payload.create({
|
||||
collection: 'select-fields',
|
||||
data: {
|
||||
disallowOption1: true,
|
||||
selectWithFilteredOptions: 'one',
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toBeFalsy()
|
||||
} catch (error) {
|
||||
expect((error as Error).message).toBe(
|
||||
'The following field is invalid: Select with filtered options',
|
||||
)
|
||||
}
|
||||
|
||||
const result = await payload.create({
|
||||
collection: 'select-fields',
|
||||
data: {
|
||||
disallowOption1: true,
|
||||
selectWithFilteredOptions: 'two',
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('number', () => {
|
||||
|
||||
@@ -1380,6 +1380,8 @@ export interface SelectField {
|
||||
category?: ('a' | 'b')[] | null;
|
||||
};
|
||||
selectWithJsxLabelOption?: ('one' | 'two' | 'three') | null;
|
||||
disallowOption1?: boolean | null;
|
||||
selectWithFilteredOptions?: ('one' | 'two' | 'three') | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -2877,6 +2879,8 @@ export interface SelectFieldsSelect<T extends boolean = true> {
|
||||
category?: T;
|
||||
};
|
||||
selectWithJsxLabelOption?: T;
|
||||
disallowOption1?: T;
|
||||
selectWithFilteredOptions?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user