fix: select field component value prop type does not support array values (#13510)

### What?

Update `SelectFieldBaseClientProps` type so `value` accepts `string[]`
for `hasMany` selects

### Why?

Multi-selects currently error with “Type 'string[]' is not assignable to
type 'string'”.

### How?

- Change `value?: string` to `value?: string | string[]`
- Also adds additional multi select custom component to `admin` test
suite for testing

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
This commit is contained in:
Patrik
2025-08-19 14:54:52 -04:00
committed by GitHub
parent 9e7bb24ffb
commit 4f6d0d8ed2
6 changed files with 81 additions and 17 deletions

View File

@@ -0,0 +1,41 @@
'use client'
import type { Option, SelectFieldClientComponent } from 'payload'
import { SelectField, useField } from '@payloadcms/ui'
import React from 'react'
export const CustomMultiSelect: SelectFieldClientComponent = (props) => {
const { path } = props
const { setValue, value } = useField<string[]>({ path })
const [options, setOptions] = React.useState<Option[]>([])
React.useEffect(() => {
const fetchOptions = () => {
const fetched: Option[] = [
{ label: 'Label 1', value: 'value1' },
{ label: 'Label 2', value: 'value2' },
]
setOptions(fetched)
}
void fetchOptions()
}, [])
const onChange = (val: string | string[]) => {
setValue(Array.isArray(val) ? val : val ? [val] : [])
}
return (
<SelectField
{...props}
field={{
...props.field,
name: path,
hasMany: true,
options,
}}
onChange={onChange}
value={value ?? []}
/>
)
}

View File

@@ -8,28 +8,21 @@ import React from 'react'
export const CustomSelect: SelectFieldClientComponent = (props) => {
const { path } = props
const { setValue, value } = useField<string>({ path })
const [options, setOptions] = React.useState<{ label: string; value: string }[]>([])
const [options, setOptions] = React.useState<Option[]>([])
React.useEffect(() => {
const fetchOptions = () => {
const fetchedOptions = [
{
label: 'Label 1',
value: 'value1',
},
{
label: 'Label 2',
value: 'value2',
},
const fetchedOptions: Option[] = [
{ label: 'Label 1', value: 'value1' },
{ label: 'Label 2', value: 'value2' },
]
setOptions(fetchedOptions)
}
void fetchOptions()
}, [])
const onChange = (selected: Option | Option[]) => {
const options = Array.isArray(selected) ? selected : [selected]
setValue(options.map((option) => (typeof option === 'string' ? option : option.value)))
const onChange = (val: string | string[]) => {
setValue(Array.isArray(val) ? (val[0] ?? '') : val)
}
return (
@@ -38,12 +31,10 @@ export const CustomSelect: SelectFieldClientComponent = (props) => {
{...props}
field={{
...props.field,
name: path,
hasMany: true,
options,
}}
onChange={onChange}
value={value}
value={value ?? ''}
/>
</div>
)

View File

@@ -82,6 +82,16 @@ export const CustomFields: CollectionConfig = {
},
},
},
{
name: 'customMultiSelectField',
type: 'text',
hasMany: true,
admin: {
components: {
Field: '/collections/CustomFields/fields/Select/CustomMultiSelect.js#CustomMultiSelect',
},
},
},
{
name: 'relationshipFieldWithBeforeAfterInputs',
type: 'relationship',

View File

@@ -649,6 +649,26 @@ describe('Document View', () => {
await page.locator('#field-customSelectField .rs__control').click()
await expect(page.locator('#field-customSelectField .rs__option')).toHaveCount(2)
})
test('should render custom multi select options', async () => {
await page.goto(customFieldsURL.create)
await page.locator('#field-customMultiSelectField .rs__control').click()
await expect(page.locator('#field-customMultiSelectField .rs__option')).toHaveCount(2)
})
test('should allow selecting multiple values in custom multi select', async () => {
await page.goto(customFieldsURL.create)
const control = page.locator('#field-customMultiSelectField .rs__control')
await control.click()
await page.locator('.rs__option', { hasText: 'Label 1' }).click()
await expect(page.locator('#field-customMultiSelectField .rs__multi-value')).toHaveCount(1)
await control.click()
await page.locator('.rs__option', { hasText: 'Label 2' }).click()
await expect(page.locator('#field-customMultiSelectField .rs__multi-value')).toHaveCount(2)
})
})
})

View File

@@ -384,6 +384,7 @@ export interface CustomField {
descriptionAsComponent?: string | null;
customSelectField?: string | null;
customSelectInput?: string | null;
customMultiSelectField?: string[] | null;
relationshipFieldWithBeforeAfterInputs?: (string | null) | Post;
arrayFieldWithBeforeAfterInputs?:
| {
@@ -927,6 +928,7 @@ export interface CustomFieldsSelect<T extends boolean = true> {
descriptionAsComponent?: T;
customSelectField?: T;
customSelectInput?: T;
customMultiSelectField?: T;
relationshipFieldWithBeforeAfterInputs?: T;
arrayFieldWithBeforeAfterInputs?:
| T