fix(ui): allow selectinputs to reset to their initial values if theres no provided value (#11252)

When reusing the SelectInput component from the UI package, if you set
value to `''` it will continue to display the previously selected value
instead of clearing out the field as expected.

The ReactSelect component doesn't behave in this way and instead will
clear out the field.

This fix addresses this difference by resetting `valueToRender` inside
the SelectInput to null.
This commit is contained in:
Paul
2025-02-18 16:40:29 +00:00
committed by GitHub
parent 313ff047df
commit 1c4eba41b7
5 changed files with 99 additions and 10 deletions

View File

@@ -0,0 +1,61 @@
'use client'
import type { OptionObject, UIField } from 'payload'
import { SelectInput, useField } from '@payloadcms/ui'
import { useEffect, useMemo } from 'react'
interface Props {
field: UIField
path: string
required?: boolean
}
const selectOptions = [
{
label: 'Option 1',
value: 'option-1',
},
{
label: 'Option 2',
value: 'option-2',
},
]
export function CustomInput({ field, path, required = false }: Props) {
const { setValue, value } = useField<string>({ path })
const options = useMemo(() => {
const internal: OptionObject[] = []
internal.push(...selectOptions)
return internal
}, [])
return (
<div className="custom-select-input">
<SelectInput
label={field.label}
name={field.name}
onChange={(option) => {
const selectedValue = (Array.isArray(option) ? option[0]?.value : option?.value) || ''
setValue(selectedValue)
}}
options={options}
path={path}
required={required}
value={value}
/>
<button
className="clear-value"
onClick={(e) => {
e.preventDefault()
setValue('')
}}
type="button"
>
Click me to reset value
</button>
</div>
)
}

View File

@@ -73,6 +73,15 @@ export const CustomFields: CollectionConfig = {
},
},
},
{
name: 'customSelectInput',
type: 'text',
admin: {
components: {
Field: '/collections/CustomFields/fields/Select/CustomInput.js#CustomInput',
},
},
},
{
name: 'relationshipFieldWithBeforeAfterInputs',
type: 'relationship',

View File

@@ -21,10 +21,10 @@ import {
customEditLabel,
customNestedTabViewPath,
customNestedTabViewTitle,
customTabAdminDescription,
customTabLabel,
customTabViewPath,
customTabViewTitle,
customTabAdminDescription,
} from '../../shared.js'
import {
customFieldsSlug,
@@ -274,9 +274,7 @@ describe('Document View', () => {
test('List drawer should not effect underlying breadcrumbs', async () => {
await navigateToDoc(page, postsUrl)
expect(await page.locator('.step-nav.app-header__step-nav a').nth(1).innerText()).toBe(
'Posts',
)
await expect(page.locator('.step-nav.app-header__step-nav a').nth(1)).toHaveText('Posts')
await page.locator('#field-upload button.upload__listToggler').click()
await expect(page.locator('[id^=list-drawer_1_]')).toBeVisible()
@@ -286,9 +284,7 @@ describe('Document View', () => {
page.locator('.step-nav.app-header__step-nav .step-nav__last'),
).not.toContainText('Uploads')
expect(await page.locator('.step-nav.app-header__step-nav a').nth(1).innerText()).toBe(
'Posts',
)
await expect(page.locator('.step-nav.app-header__step-nav a').nth(1)).toHaveText('Posts')
})
})
@@ -400,9 +396,9 @@ describe('Document View', () => {
await page.waitForURL(postsUrl.create)
const secondTab = page.locator('.tabs-field__tab-button').nth(1)
secondTab.click()
await secondTab.click()
wait(500)
await wait(500)
const tabsContent = page.locator('.tabs-field__content-wrap')
await expect(
@@ -463,6 +459,24 @@ describe('Document View', () => {
).toBeVisible()
})
test('custom select input can have its value cleared', async () => {
await page.goto(customFieldsURL.create)
await page.waitForURL(customFieldsURL.create)
await expect(page.locator('#field-customSelectInput')).toBeVisible()
await page.locator('#field-customSelectInput .rs__control').click()
await page.locator('#field-customSelectInput .rs__option').first().click()
await expect(page.locator('#field-customSelectInput .rs__single-value')).toHaveText(
'Option 1',
)
await page.locator('.clear-value').click()
await expect(page.locator('#field-customSelectInput .rs__placeholder')).toHaveText(
'Select a value',
)
})
describe('field descriptions', () => {
test('should render static field description', async () => {
await page.goto(customFieldsURL.create)
@@ -535,7 +549,7 @@ describe('Document View', () => {
describe('publish button', () => {
test('should show publish active locale button with defaultLocalePublishOption', async () => {
await navigateToDoc(page, postsUrl)
const publishButton = await page.locator('#action-save')
const publishButton = page.locator('#action-save')
await expect(publishButton).toBeVisible()
await expect(publishButton).toContainText('Publish in English')
})

View File

@@ -334,6 +334,7 @@ export interface CustomField {
descriptionAsFunction?: string | null;
descriptionAsComponent?: string | null;
customSelectField?: string | null;
customSelectInput?: string | null;
relationshipFieldWithBeforeAfterInputs?: (string | null) | Post;
arrayFieldWithBeforeAfterInputs?:
| {
@@ -717,6 +718,7 @@ export interface CustomFieldsSelect<T extends boolean = true> {
descriptionAsFunction?: T;
descriptionAsComponent?: T;
customSelectField?: T;
customSelectInput?: T;
relationshipFieldWithBeforeAfterInputs?: T;
arrayFieldWithBeforeAfterInputs?:
| T