feat(ui): make select and relationship field placeholder configurable (#12253)
### What? Allows to overwrite the default placeholder text of select and relationship fields. ### Why? The default placeholder text is generic. In some scenarios a custom placeholder can guide the user better. ### How? Adds a new property `admin.placeholder` to relationship and select field which allows to define an alternative text or translation function for the placeholder. The placeholder is used in the form fields as well as in the filter options.   --------- Co-authored-by: Dan Ribbens <DanRibbens@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
@@ -94,6 +94,7 @@ The Relationship Field inherits all of the default options from the base [Field
|
|||||||
| **`allowCreate`** | Set to `false` if you'd like to disable the ability to create new documents from within the relationship field. |
|
| **`allowCreate`** | Set to `false` if you'd like to disable the ability to create new documents from within the relationship field. |
|
||||||
| **`allowEdit`** | Set to `false` if you'd like to disable the ability to edit documents from within the relationship field. |
|
| **`allowEdit`** | Set to `false` if you'd like to disable the ability to edit documents from within the relationship field. |
|
||||||
| **`sortOptions`** | Define a default sorting order for the options within a Relationship field's dropdown. [More](#sort-options) |
|
| **`sortOptions`** | Define a default sorting order for the options within a Relationship field's dropdown. [More](#sort-options) |
|
||||||
|
| **`placeholder`** | Define a custom text or function to replace the generic default placeholder |
|
||||||
| **`appearance`** | Set to `drawer` or `select` to change the behavior of the field. Defaults to `select`. |
|
| **`appearance`** | Set to `drawer` or `select` to change the behavior of the field. Defaults to `select`. |
|
||||||
|
|
||||||
### Sort Options
|
### Sort Options
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ The Select Field inherits all of the default options from the base [Field Admin
|
|||||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **`isClearable`** | Set to `true` if you'd like this field to be clearable within the Admin UI. |
|
| **`isClearable`** | Set to `true` if you'd like this field to be clearable within the Admin UI. |
|
||||||
| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`) |
|
| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`) |
|
||||||
|
| **`placeholder`** | Define a custom text or function to replace the generic default placeholder |
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
|||||||
@@ -1061,6 +1061,7 @@ export type SelectField = {
|
|||||||
} & Admin['components']
|
} & Admin['components']
|
||||||
isClearable?: boolean
|
isClearable?: boolean
|
||||||
isSortable?: boolean
|
isSortable?: boolean
|
||||||
|
placeholder?: LabelFunction | string
|
||||||
} & Admin
|
} & Admin
|
||||||
/**
|
/**
|
||||||
* Customize the SQL table name
|
* Customize the SQL table name
|
||||||
@@ -1093,7 +1094,7 @@ export type SelectField = {
|
|||||||
Omit<FieldBase, 'validate'>
|
Omit<FieldBase, 'validate'>
|
||||||
|
|
||||||
export type SelectFieldClient = {
|
export type SelectFieldClient = {
|
||||||
admin?: AdminClient & Pick<SelectField['admin'], 'isClearable' | 'isSortable'>
|
admin?: AdminClient & Pick<SelectField['admin'], 'isClearable' | 'isSortable' | 'placeholder'>
|
||||||
} & FieldBaseClient &
|
} & FieldBaseClient &
|
||||||
Pick<SelectField, 'hasMany' | 'interfaceName' | 'options' | 'type'>
|
Pick<SelectField, 'hasMany' | 'interfaceName' | 'options' | 'type'>
|
||||||
|
|
||||||
@@ -1160,10 +1161,11 @@ type RelationshipAdmin = {
|
|||||||
>
|
>
|
||||||
} & Admin['components']
|
} & Admin['components']
|
||||||
isSortable?: boolean
|
isSortable?: boolean
|
||||||
|
placeholder?: LabelFunction | string
|
||||||
} & Admin
|
} & Admin
|
||||||
|
|
||||||
type RelationshipAdminClient = AdminClient &
|
type RelationshipAdminClient = AdminClient &
|
||||||
Pick<RelationshipAdmin, 'allowCreate' | 'allowEdit' | 'appearance' | 'isSortable'>
|
Pick<RelationshipAdmin, 'allowCreate' | 'allowEdit' | 'appearance' | 'isSortable' | 'placeholder'>
|
||||||
|
|
||||||
export type PolymorphicRelationshipField = {
|
export type PolymorphicRelationshipField = {
|
||||||
admin?: {
|
admin?: {
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
|||||||
captureMenuScroll
|
captureMenuScroll
|
||||||
customProps={customProps}
|
customProps={customProps}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
|
||||||
{...props}
|
{...props}
|
||||||
className={classes}
|
className={classes}
|
||||||
classNamePrefix="rs"
|
classNamePrefix="rs"
|
||||||
@@ -113,6 +112,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
|||||||
onMenuClose={onMenuClose}
|
onMenuClose={onMenuClose}
|
||||||
onMenuOpen={onMenuOpen}
|
onMenuOpen={onMenuOpen}
|
||||||
options={options}
|
options={options}
|
||||||
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
styles={styles}
|
styles={styles}
|
||||||
unstyled={true}
|
unstyled={true}
|
||||||
value={value}
|
value={value}
|
||||||
@@ -160,7 +160,6 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
|||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
captureMenuScroll
|
captureMenuScroll
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
placeholder={getTranslation(placeholder, i18n)}
|
|
||||||
{...props}
|
{...props}
|
||||||
className={classes}
|
className={classes}
|
||||||
classNamePrefix="rs"
|
classNamePrefix="rs"
|
||||||
@@ -191,6 +190,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
|||||||
onMenuClose={onMenuClose}
|
onMenuClose={onMenuClose}
|
||||||
onMenuOpen={onMenuOpen}
|
onMenuOpen={onMenuOpen}
|
||||||
options={options}
|
options={options}
|
||||||
|
placeholder={getTranslation(placeholder, i18n)}
|
||||||
styles={styles}
|
styles={styles}
|
||||||
unstyled={true}
|
unstyled={true}
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { LabelFunction } from 'payload'
|
||||||
import type { CommonProps, GroupBase, Props as ReactSelectStateManagerProps } from 'react-select'
|
import type { CommonProps, GroupBase, Props as ReactSelectStateManagerProps } from 'react-select'
|
||||||
|
|
||||||
import type { DocumentDrawerProps, UseDocumentDrawer } from '../DocumentDrawer/types.js'
|
import type { DocumentDrawerProps, UseDocumentDrawer } from '../DocumentDrawer/types.js'
|
||||||
@@ -101,7 +102,7 @@ export type ReactSelectAdapterProps = {
|
|||||||
onMenuOpen?: () => void
|
onMenuOpen?: () => void
|
||||||
onMenuScrollToBottom?: () => void
|
onMenuScrollToBottom?: () => void
|
||||||
options: Option[] | OptionGroup[]
|
options: Option[] | OptionGroup[]
|
||||||
placeholder?: string
|
placeholder?: LabelFunction | string
|
||||||
showError?: boolean
|
showError?: boolean
|
||||||
value?: Option | Option[]
|
value?: Option | Option[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const maxResultsPerRequest = 10
|
|||||||
export const RelationshipFilter: React.FC<Props> = (props) => {
|
export const RelationshipFilter: React.FC<Props> = (props) => {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
field: { admin: { isSortable } = {}, hasMany, relationTo },
|
field: { admin: { isSortable, placeholder } = {}, hasMany, relationTo },
|
||||||
filterOptions,
|
filterOptions,
|
||||||
onChange,
|
onChange,
|
||||||
value,
|
value,
|
||||||
@@ -412,7 +412,7 @@ export const RelationshipFilter: React.FC<Props> = (props) => {
|
|||||||
onInputChange={handleInputChange}
|
onInputChange={handleInputChange}
|
||||||
onMenuScrollToBottom={handleScrollToBottom}
|
onMenuScrollToBottom={handleScrollToBottom}
|
||||||
options={options}
|
options={options}
|
||||||
placeholder={t('general:selectValue')}
|
placeholder={placeholder}
|
||||||
value={valueToRender}
|
value={valueToRender}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import { formatOptions } from './formatOptions.js'
|
|||||||
|
|
||||||
export const Select: React.FC<Props> = ({
|
export const Select: React.FC<Props> = ({
|
||||||
disabled,
|
disabled,
|
||||||
|
field: {
|
||||||
|
admin: { placeholder },
|
||||||
|
},
|
||||||
isClearable,
|
isClearable,
|
||||||
onChange,
|
onChange,
|
||||||
operator,
|
operator,
|
||||||
@@ -77,6 +80,7 @@ export const Select: React.FC<Props> = ({
|
|||||||
isMulti={isMulti}
|
isMulti={isMulti}
|
||||||
onChange={onSelect}
|
onChange={onSelect}
|
||||||
options={options.map((option) => ({ ...option, label: getTranslation(option.label, i18n) }))}
|
options={options.map((option) => ({ ...option, label: getTranslation(option.label, i18n) }))}
|
||||||
|
placeholder={placeholder}
|
||||||
value={valueToRender}
|
value={valueToRender}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Option, SelectFieldClient } from 'payload'
|
import type { LabelFunction, Option, SelectFieldClient } from 'payload'
|
||||||
|
|
||||||
import type { DefaultFilterProps } from '../types.js'
|
import type { DefaultFilterProps } from '../types.js'
|
||||||
|
|
||||||
@@ -7,5 +7,6 @@ export type SelectFilterProps = {
|
|||||||
readonly isClearable?: boolean
|
readonly isClearable?: boolean
|
||||||
readonly onChange: (val: string) => void
|
readonly onChange: (val: string) => void
|
||||||
readonly options: Option[]
|
readonly options: Option[]
|
||||||
|
readonly placeholder?: LabelFunction | string
|
||||||
readonly value: string
|
readonly value: string
|
||||||
} & DefaultFilterProps
|
} & DefaultFilterProps
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {
|
|||||||
import { dequal } from 'dequal/lite'
|
import { dequal } from 'dequal/lite'
|
||||||
import { wordBoundariesRegex } from 'payload/shared'
|
import { wordBoundariesRegex } from 'payload/shared'
|
||||||
import * as qs from 'qs-esm'
|
import * as qs from 'qs-esm'
|
||||||
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
|
||||||
|
|
||||||
import type { DocumentDrawerProps } from '../../elements/DocumentDrawer/types.js'
|
import type { DocumentDrawerProps } from '../../elements/DocumentDrawer/types.js'
|
||||||
import type { ListDrawerProps } from '../../elements/ListDrawer/types.js'
|
import type { ListDrawerProps } from '../../elements/ListDrawer/types.js'
|
||||||
@@ -56,6 +56,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
className,
|
className,
|
||||||
description,
|
description,
|
||||||
isSortable = true,
|
isSortable = true,
|
||||||
|
placeholder,
|
||||||
sortOptions,
|
sortOptions,
|
||||||
} = {},
|
} = {},
|
||||||
hasMany,
|
hasMany,
|
||||||
@@ -779,6 +780,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
|
|||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
options={options}
|
options={options}
|
||||||
|
placeholder={placeholder}
|
||||||
showError={showError}
|
showError={showError}
|
||||||
value={valueToRender ?? null}
|
value={valueToRender ?? null}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { OptionObject, StaticDescription, StaticLabel } from 'payload'
|
import type { LabelFunction, OptionObject, StaticDescription, StaticLabel } from 'payload'
|
||||||
|
|
||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@@ -33,6 +33,7 @@ export type SelectInputProps = {
|
|||||||
readonly onInputChange?: ReactSelectAdapterProps['onInputChange']
|
readonly onInputChange?: ReactSelectAdapterProps['onInputChange']
|
||||||
readonly options?: OptionObject[]
|
readonly options?: OptionObject[]
|
||||||
readonly path: string
|
readonly path: string
|
||||||
|
readonly placeholder?: LabelFunction | string
|
||||||
readonly readOnly?: boolean
|
readonly readOnly?: boolean
|
||||||
readonly required?: boolean
|
readonly required?: boolean
|
||||||
readonly showError?: boolean
|
readonly showError?: boolean
|
||||||
@@ -58,6 +59,7 @@ export const SelectInput: React.FC<SelectInputProps> = (props) => {
|
|||||||
onInputChange,
|
onInputChange,
|
||||||
options,
|
options,
|
||||||
path,
|
path,
|
||||||
|
placeholder,
|
||||||
readOnly,
|
readOnly,
|
||||||
required,
|
required,
|
||||||
showError,
|
showError,
|
||||||
@@ -125,6 +127,7 @@ export const SelectInput: React.FC<SelectInputProps> = (props) => {
|
|||||||
...option,
|
...option,
|
||||||
label: getTranslation(option.label, i18n),
|
label: getTranslation(option.label, i18n),
|
||||||
}))}
|
}))}
|
||||||
|
placeholder={placeholder}
|
||||||
showError={showError}
|
showError={showError}
|
||||||
value={valueToRender as OptionObject}
|
value={valueToRender as OptionObject}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
|||||||
description,
|
description,
|
||||||
isClearable = true,
|
isClearable = true,
|
||||||
isSortable = true,
|
isSortable = true,
|
||||||
|
placeholder,
|
||||||
} = {} as SelectFieldClientProps['field']['admin'],
|
} = {} as SelectFieldClientProps['field']['admin'],
|
||||||
hasMany = false,
|
hasMany = false,
|
||||||
label,
|
label,
|
||||||
@@ -118,6 +119,7 @@ const SelectFieldComponent: SelectFieldClientComponent = (props) => {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
options={options}
|
options={options}
|
||||||
path={path}
|
path={path}
|
||||||
|
placeholder={placeholder}
|
||||||
readOnly={readOnly || disabled}
|
readOnly={readOnly || disabled}
|
||||||
required={required}
|
required={required}
|
||||||
showError={showError}
|
showError={showError}
|
||||||
|
|||||||
41
test/admin/collections/Placeholder.ts
Normal file
41
test/admin/collections/Placeholder.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
|
import { placeholderCollectionSlug } from '../slugs.js'
|
||||||
|
|
||||||
|
export const Placeholder: CollectionConfig = {
|
||||||
|
slug: placeholderCollectionSlug,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'defaultSelect',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'option1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'placeholderSelect',
|
||||||
|
type: 'select',
|
||||||
|
options: [{ label: 'Option 1', value: 'option1' }],
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Custom placeholder',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'defaultRelationship',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'posts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'placeholderRelationship',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'posts',
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Custom placeholder',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
versions: true,
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
const filename = fileURLToPath(import.meta.url)
|
|
||||||
const dirname = path.dirname(filename)
|
|
||||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||||
import { Array } from './collections/Array.js'
|
import { Array } from './collections/Array.js'
|
||||||
import { BaseListFilter } from './collections/BaseListFilter.js'
|
import { BaseListFilter } from './collections/BaseListFilter.js'
|
||||||
@@ -19,6 +18,7 @@ import { CollectionHidden } from './collections/Hidden.js'
|
|||||||
import { ListDrawer } from './collections/ListDrawer.js'
|
import { ListDrawer } from './collections/ListDrawer.js'
|
||||||
import { CollectionNoApiView } from './collections/NoApiView.js'
|
import { CollectionNoApiView } from './collections/NoApiView.js'
|
||||||
import { CollectionNotInView } from './collections/NotInView.js'
|
import { CollectionNotInView } from './collections/NotInView.js'
|
||||||
|
import { Placeholder } from './collections/Placeholder.js'
|
||||||
import { Posts } from './collections/Posts.js'
|
import { Posts } from './collections/Posts.js'
|
||||||
import { UploadCollection } from './collections/Upload.js'
|
import { UploadCollection } from './collections/Upload.js'
|
||||||
import { UploadTwoCollection } from './collections/UploadTwo.js'
|
import { UploadTwoCollection } from './collections/UploadTwo.js'
|
||||||
@@ -43,7 +43,8 @@ import {
|
|||||||
protectedCustomNestedViewPath,
|
protectedCustomNestedViewPath,
|
||||||
publicCustomViewPath,
|
publicCustomViewPath,
|
||||||
} from './shared.js'
|
} from './shared.js'
|
||||||
|
const filename = fileURLToPath(import.meta.url)
|
||||||
|
const dirname = path.dirname(filename)
|
||||||
export default buildConfigWithDefaults({
|
export default buildConfigWithDefaults({
|
||||||
admin: {
|
admin: {
|
||||||
importMap: {
|
importMap: {
|
||||||
@@ -165,6 +166,7 @@ export default buildConfigWithDefaults({
|
|||||||
BaseListFilter,
|
BaseListFilter,
|
||||||
with300Documents,
|
with300Documents,
|
||||||
ListDrawer,
|
ListDrawer,
|
||||||
|
Placeholder,
|
||||||
],
|
],
|
||||||
globals: [
|
globals: [
|
||||||
GlobalHidden,
|
GlobalHidden,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import type { Page } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
import type { User as PayloadUser } from 'payload'
|
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test'
|
import { expect, test } from '@playwright/test'
|
||||||
import { mapAsync } from 'payload'
|
import { mapAsync } from 'payload'
|
||||||
import * as qs from 'qs-esm'
|
import * as qs from 'qs-esm'
|
||||||
|
|
||||||
import type { Config, Geo, Post, User } from '../../payload-types.js'
|
import type { Config, Geo, Post } from '../../payload-types.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ensureCompilationIsDone,
|
ensureCompilationIsDone,
|
||||||
@@ -21,6 +20,7 @@ import {
|
|||||||
customViews1CollectionSlug,
|
customViews1CollectionSlug,
|
||||||
geoCollectionSlug,
|
geoCollectionSlug,
|
||||||
listDrawerSlug,
|
listDrawerSlug,
|
||||||
|
placeholderCollectionSlug,
|
||||||
postsCollectionSlug,
|
postsCollectionSlug,
|
||||||
with300DocumentsSlug,
|
with300DocumentsSlug,
|
||||||
} from '../../slugs.js'
|
} from '../../slugs.js'
|
||||||
@@ -64,6 +64,7 @@ describe('List View', () => {
|
|||||||
let customViewsUrl: AdminUrlUtil
|
let customViewsUrl: AdminUrlUtil
|
||||||
let with300DocumentsUrl: AdminUrlUtil
|
let with300DocumentsUrl: AdminUrlUtil
|
||||||
let withListViewUrl: AdminUrlUtil
|
let withListViewUrl: AdminUrlUtil
|
||||||
|
let placeholderUrl: AdminUrlUtil
|
||||||
let user: any
|
let user: any
|
||||||
|
|
||||||
let serverURL: string
|
let serverURL: string
|
||||||
@@ -87,7 +88,7 @@ describe('List View', () => {
|
|||||||
baseListFiltersUrl = new AdminUrlUtil(serverURL, 'base-list-filters')
|
baseListFiltersUrl = new AdminUrlUtil(serverURL, 'base-list-filters')
|
||||||
customViewsUrl = new AdminUrlUtil(serverURL, customViews1CollectionSlug)
|
customViewsUrl = new AdminUrlUtil(serverURL, customViews1CollectionSlug)
|
||||||
withListViewUrl = new AdminUrlUtil(serverURL, listDrawerSlug)
|
withListViewUrl = new AdminUrlUtil(serverURL, listDrawerSlug)
|
||||||
|
placeholderUrl = new AdminUrlUtil(serverURL, placeholderCollectionSlug)
|
||||||
const context = await browser.newContext()
|
const context = await browser.newContext()
|
||||||
page = await context.newPage()
|
page = await context.newPage()
|
||||||
initPageConsoleErrorCatch(page)
|
initPageConsoleErrorCatch(page)
|
||||||
@@ -1408,6 +1409,66 @@ describe('List View', () => {
|
|||||||
).toHaveText('Title')
|
).toHaveText('Title')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('placeholder', () => {
|
||||||
|
test('should display placeholder in filter options', async () => {
|
||||||
|
await page.goto(
|
||||||
|
`${placeholderUrl.list}${qs.stringify(
|
||||||
|
{
|
||||||
|
where: {
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
defaultSelect: {
|
||||||
|
equals: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
placeholderSelect: {
|
||||||
|
equals: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
defaultRelationship: {
|
||||||
|
equals: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
placeholderRelationship: {
|
||||||
|
equals: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ addQueryPrefix: true },
|
||||||
|
)}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const conditionValueSelects = page.locator('#list-controls-where .condition__value')
|
||||||
|
await expect(conditionValueSelects.nth(0)).toHaveText('Select a value')
|
||||||
|
await expect(conditionValueSelects.nth(1)).toHaveText('Custom placeholder')
|
||||||
|
await expect(conditionValueSelects.nth(2)).toHaveText('Select a value')
|
||||||
|
await expect(conditionValueSelects.nth(3)).toHaveText('Custom placeholder')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('should display placeholder in edit view', async () => {
|
||||||
|
await page.goto(placeholderUrl.create)
|
||||||
|
|
||||||
|
await expect(page.locator('#field-defaultSelect .rs__placeholder')).toHaveText('Select a value')
|
||||||
|
await expect(page.locator('#field-placeholderSelect .rs__placeholder')).toHaveText(
|
||||||
|
'Custom placeholder',
|
||||||
|
)
|
||||||
|
await expect(page.locator('#field-defaultRelationship .rs__placeholder')).toHaveText(
|
||||||
|
'Select a value',
|
||||||
|
)
|
||||||
|
await expect(page.locator('#field-placeholderRelationship .rs__placeholder')).toHaveText(
|
||||||
|
'Custom placeholder',
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function createPost(overrides?: Partial<Post>): Promise<Post> {
|
async function createPost(overrides?: Partial<Post>): Promise<Post> {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const noApiViewCollectionSlug = 'collection-no-api-view'
|
|||||||
export const disableDuplicateSlug = 'disable-duplicate'
|
export const disableDuplicateSlug = 'disable-duplicate'
|
||||||
export const disableCopyToLocale = 'disable-copy-to-locale'
|
export const disableCopyToLocale = 'disable-copy-to-locale'
|
||||||
export const uploadCollectionSlug = 'uploads'
|
export const uploadCollectionSlug = 'uploads'
|
||||||
|
export const placeholderCollectionSlug = 'placeholder'
|
||||||
|
|
||||||
export const uploadTwoCollectionSlug = 'uploads-two'
|
export const uploadTwoCollectionSlug = 'uploads-two'
|
||||||
export const customFieldsSlug = 'custom-fields'
|
export const customFieldsSlug = 'custom-fields'
|
||||||
|
|||||||
Reference in New Issue
Block a user