From c405e5958f3b9fe9f8c9ed5b2a604ff8b21ff959 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 23 Jul 2024 14:57:53 -0400 Subject: [PATCH] fix(ui): email field now correctly renders autocomplete attribute (#7322) Adds test as well for the email field --- .../ComponentMap/buildComponentMap/fields.tsx | 1 + test/fields/collections/Email/AfterInput.tsx | 7 + test/fields/collections/Email/BeforeInput.tsx | 7 + test/fields/collections/Email/CustomError.tsx | 22 ++ test/fields/collections/Email/CustomLabel.tsx | 18 + test/fields/collections/Email/e2e.spec.ts | 325 ++++++++++++++++++ test/fields/collections/Email/index.ts | 124 +++++++ test/fields/collections/Email/shared.ts | 16 + test/fields/config.ts | 2 + test/fields/payload-types.ts | 120 ++++--- test/fields/seed.ts | 17 + test/fields/slugs.ts | 1 + 12 files changed, 611 insertions(+), 49 deletions(-) create mode 100644 test/fields/collections/Email/AfterInput.tsx create mode 100644 test/fields/collections/Email/BeforeInput.tsx create mode 100644 test/fields/collections/Email/CustomError.tsx create mode 100644 test/fields/collections/Email/CustomLabel.tsx create mode 100644 test/fields/collections/Email/e2e.spec.ts create mode 100644 test/fields/collections/Email/index.ts create mode 100644 test/fields/collections/Email/shared.ts diff --git a/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx b/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx index 4151e7840..7027c58c5 100644 --- a/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx +++ b/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx @@ -446,6 +446,7 @@ export const mapFields = (args: { const emailField: EmailFieldProps = { ...baseFieldProps, name: field.name, + autoComplete: field.admin?.autoComplete, className: field.admin?.className, disabled: field.admin?.disabled, placeholder: field.admin?.placeholder, diff --git a/test/fields/collections/Email/AfterInput.tsx b/test/fields/collections/Email/AfterInput.tsx new file mode 100644 index 000000000..50796416f --- /dev/null +++ b/test/fields/collections/Email/AfterInput.tsx @@ -0,0 +1,7 @@ +'use client' + +import React from 'react' + +export const AfterInput: React.FC = () => { + return +} diff --git a/test/fields/collections/Email/BeforeInput.tsx b/test/fields/collections/Email/BeforeInput.tsx new file mode 100644 index 000000000..4ff7fcd4f --- /dev/null +++ b/test/fields/collections/Email/BeforeInput.tsx @@ -0,0 +1,7 @@ +'use client' + +import React from 'react' + +export const BeforeInput: React.FC = () => { + return +} diff --git a/test/fields/collections/Email/CustomError.tsx b/test/fields/collections/Email/CustomError.tsx new file mode 100644 index 000000000..7ff8665fd --- /dev/null +++ b/test/fields/collections/Email/CustomError.tsx @@ -0,0 +1,22 @@ +'use client' + +import { useField, useFormFields, useFormSubmitted } from '@payloadcms/ui' +import React from 'react' + +const CustomError: React.FC = (props) => { + const { path: pathFromProps } = props + const submitted = useFormSubmitted() + const { path } = useField(pathFromProps) + const field = useFormFields(([fields]) => (fields && fields?.[path]) || null) + const { valid } = field || {} + + const showError = submitted && !valid + + if (showError) { + return
#custom-error
+ } + + return null +} + +export default CustomError diff --git a/test/fields/collections/Email/CustomLabel.tsx b/test/fields/collections/Email/CustomLabel.tsx new file mode 100644 index 000000000..96ad9b403 --- /dev/null +++ b/test/fields/collections/Email/CustomLabel.tsx @@ -0,0 +1,18 @@ +'use client' + +import { useFieldProps } from '@payloadcms/ui' +import React from 'react' + +const CustomLabel = ({ schemaPath }) => { + const { path: pathFromContext } = useFieldProps() + + const path = pathFromContext ?? schemaPath // pathFromContext will be undefined in list view + + return ( + + ) +} + +export default CustomLabel diff --git a/test/fields/collections/Email/e2e.spec.ts b/test/fields/collections/Email/e2e.spec.ts new file mode 100644 index 000000000..9b132b380 --- /dev/null +++ b/test/fields/collections/Email/e2e.spec.ts @@ -0,0 +1,325 @@ +import type { Page } from '@playwright/test' + +import { expect, test } from '@playwright/test' +import path from 'path' +import { wait } from 'payload/shared' +import { fileURLToPath } from 'url' + +import type { PayloadTestSDK } from '../../../helpers/sdk/index.js' +import type { Config } from '../../payload-types.js' + +import { + ensureCompilationIsDone, + exactText, + initPageConsoleErrorCatch, + saveDocAndAssert, +} from '../../../helpers.js' +import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js' +import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js' +import { reInitializeDB } from '../../../helpers/reInitializeDB.js' +import { RESTClient } from '../../../helpers/rest.js' +import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js' +import { emailFieldsSlug } from '../../slugs.js' +import { anotherEmailDoc, emailDoc } from './shared.js' + +const filename = fileURLToPath(import.meta.url) +const currentFolder = path.dirname(filename) +const dirname = path.resolve(currentFolder, '../../') + +const { beforeAll, beforeEach, describe } = test + +let payload: PayloadTestSDK +let client: RESTClient +let page: Page +let serverURL: string +// If we want to make this run in parallel: test.describe.configure({ mode: 'parallel' }) +let url: AdminUrlUtil + +describe('Email', () => { + beforeAll(async ({ browser }, testInfo) => { + testInfo.setTimeout(TEST_TIMEOUT_LONG) + process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit + ;({ payload, serverURL } = await initPayloadE2ENoConfig({ + dirname, + // prebuild, + })) + url = new AdminUrlUtil(serverURL, emailFieldsSlug) + + const context = await browser.newContext() + page = await context.newPage() + initPageConsoleErrorCatch(page) + await reInitializeDB({ + serverURL, + snapshotKey: 'fieldsEmailTest', + uploadsDir: path.resolve(dirname, './collections/Upload/uploads'), + }) + await ensureCompilationIsDone({ page, serverURL }) + }) + beforeEach(async () => { + await reInitializeDB({ + serverURL, + snapshotKey: 'fieldsEmailTest', + uploadsDir: path.resolve(dirname, './collections/Upload/uploads'), + }) + + if (client) { + await client.logout() + } + client = new RESTClient(null, { defaultSlug: 'users', serverURL }) + await client.login() + + await ensureCompilationIsDone({ page, serverURL }) + }) + + test('should display field in list view', async () => { + await page.goto(url.list) + const emailCell = page.locator('.row-1 .cell-email') + await expect(emailCell).toHaveText(emailDoc.email) + }) + + test('should hide field in column selector when admin.disableListColumn', async () => { + await page.goto(url.list) + await page.locator('.list-controls__toggle-columns').click() + + await expect(page.locator('.column-selector')).toBeVisible() + + // Check if "Disable List Column Text" is not present in the column options + await expect( + page.locator(`.column-selector .column-selector__column`, { + hasText: exactText('Disable List Column Text'), + }), + ).toBeHidden() + }) + + test('should show field in filter when admin.disableListColumn is true', async () => { + await page.goto(url.list) + await page.locator('.list-controls__toggle-where').click() + await page.locator('.where-builder__add-first-filter').click() + + const initialField = page.locator('.condition__field') + await initialField.click() + + await expect( + initialField.locator(`.rs__menu-list:has-text("Disable List Column Text")`), + ).toBeVisible() + }) + + test('should display field in list view column selector if admin.disableListColumn is false and admin.disableListFilter is true', async () => { + await page.goto(url.list) + await page.locator('.list-controls__toggle-columns').click() + + await expect(page.locator('.column-selector')).toBeVisible() + + // Check if "Disable List Filter Text" is present in the column options + await expect( + page.locator(`.column-selector .column-selector__column`, { + hasText: exactText('Disable List Filter Text'), + }), + ).toBeVisible() + }) + + test('should hide field in filter when admin.disableListFilter is true', async () => { + await page.goto(url.list) + await page.locator('.list-controls__toggle-where').click() + await page.locator('.where-builder__add-first-filter').click() + + const initialField = page.locator('.condition__field') + await initialField.click() + + await expect( + initialField.locator(`.rs__option :has-text("Disable List Filter Text")`), + ).toBeHidden() + }) + + test('should have autocomplete', async () => { + await page.goto(url.create) + const autoCompleteEmail = page.locator('#field-emailWithAutocomplete') + await expect(autoCompleteEmail).toHaveAttribute('autocomplete') + }) + + test('should show i18n label', async () => { + await page.goto(url.create) + + await expect(page.locator('label[for="field-i18nEmail"]')).toHaveText('Text en') + }) + + test('should show i18n placeholder', async () => { + await page.goto(url.create) + await expect(page.locator('#field-i18nEmail')).toHaveAttribute('placeholder', 'en placeholder') + }) + + test('should show i18n descriptions', async () => { + await page.goto(url.create) + const description = page.locator('.field-description-i18nEmail') + await expect(description).toHaveText('en description') + }) + + test('should render custom label', async () => { + await page.goto(url.create) + const label = page.locator('label.custom-label[for="field-customLabel"]') + await expect(label).toHaveText('#label') + }) + + test('should render custom error', async () => { + await page.goto(url.create) + const input = page.locator('input[id="field-customError"]') + await input.fill('ab') + await expect(input).toHaveValue('ab') + const error = page.locator('.custom-error:near(input[id="field-customError"])') + const submit = page.locator('button[type="button"][id="action-save"]') + await submit.click() + await expect(error).toHaveText('#custom-error') + }) + + test('should render beforeInput and afterInput', async () => { + await page.goto(url.create) + const input = page.locator('input[id="field-beforeAndAfterInput"]') + + const prevSibling = await input.evaluateHandle((el) => { + return el.previousElementSibling + }) + const prevSiblingText = await page.evaluate((el) => el.textContent, prevSibling) + expect(prevSiblingText).toEqual('#before-input') + + const nextSibling = await input.evaluateHandle((el) => { + return el.nextElementSibling + }) + const nextSiblingText = await page.evaluate((el) => el.textContent, nextSibling) + expect(nextSiblingText).toEqual('#after-input') + }) + + test('should reset filter conditions when adding additional filters', async () => { + await page.goto(url.list) + + // open the first filter options + await page.locator('.list-controls__toggle-where').click() + await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible() + await page.locator('.where-builder__add-first-filter').click() + + const firstInitialField = page.locator('.condition__field') + const firstOperatorField = page.locator('.condition__operator') + const firstValueField = page.locator('.condition__value >> input') + + await firstInitialField.click() + const firstInitialFieldOptions = firstInitialField.locator('.rs__option') + await firstInitialFieldOptions.locator('text=text').first().click() + await expect(firstInitialField.locator('.rs__single-value')).toContainText('Text') + + await firstOperatorField.click() + await firstOperatorField.locator('.rs__option').locator('text=equals').click() + + await firstValueField.fill('hello') + + await wait(500) + + await expect(firstValueField).toHaveValue('hello') + + // open the second filter options + await page.locator('.condition__actions-add').click() + + const secondLi = page.locator('.where-builder__and-filters li:nth-child(2)') + + await expect(secondLi).toBeVisible() + + const secondInitialField = secondLi.locator('.condition__field') + const secondOperatorField = secondLi.locator('.condition__operator >> input') + const secondValueField = secondLi.locator('.condition__value >> input') + + await expect(secondInitialField.locator('.rs__single-value')).toContainText('Email') + await expect(secondOperatorField).toHaveValue('') + await expect(secondValueField).toHaveValue('') + }) + + test('should not re-render page upon typing in a value in the filter value field', async () => { + await page.goto(url.list) + + // open the first filter options + await page.locator('.list-controls__toggle-where').click() + await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible() + await page.locator('.where-builder__add-first-filter').click() + + const firstInitialField = page.locator('.condition__field') + const firstOperatorField = page.locator('.condition__operator') + const firstValueField = page.locator('.condition__value >> input') + + await firstInitialField.click() + const firstInitialFieldOptions = firstInitialField.locator('.rs__option') + await firstInitialFieldOptions.locator('text=text').first().click() + await expect(firstInitialField.locator('.rs__single-value')).toContainText('Text') + + await firstOperatorField.click() + await firstOperatorField.locator('.rs__option').locator('text=equals').click() + + // Type into the input field instead of filling it + await firstValueField.click() + await firstValueField.type('hello', { delay: 100 }) // Add a delay to simulate typing speed + + // Wait for a short period to see if the input loses focus + await page.waitForTimeout(500) + + // Check if the input still has the correct value + await expect(firstValueField).toHaveValue('hello') + }) + + test('should still show second filter if two filters exist and first filter is removed', async () => { + await page.goto(url.list) + + // open the first filter options + await page.locator('.list-controls__toggle-where').click() + await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible() + await page.locator('.where-builder__add-first-filter').click() + + const firstInitialField = page.locator('.condition__field') + const firstOperatorField = page.locator('.condition__operator') + const firstValueField = page.locator('.condition__value >> input') + + await firstInitialField.click() + const firstInitialFieldOptions = firstInitialField.locator('.rs__option') + await firstInitialFieldOptions.locator('text=text').first().click() + await expect(firstInitialField.locator('.rs__single-value')).toContainText('Text') + + await firstOperatorField.click() + await firstOperatorField.locator('.rs__option').locator('text=equals').click() + + await firstValueField.fill('hello') + + await wait(500) + + await expect(firstValueField).toHaveValue('hello') + + // open the second filter options + await page.locator('.condition__actions-add').click() + + const secondLi = page.locator('.where-builder__and-filters li:nth-child(2)') + + await expect(secondLi).toBeVisible() + + const secondInitialField = secondLi.locator('.condition__field') + const secondOperatorField = secondLi.locator('.condition__operator') + const secondValueField = secondLi.locator('.condition__value >> input') + + await secondInitialField.click() + const secondInitialFieldOptions = secondInitialField.locator('.rs__option') + await secondInitialFieldOptions.locator('text=text').first().click() + await expect(secondInitialField.locator('.rs__single-value')).toContainText('Text') + + await secondOperatorField.click() + await secondOperatorField.locator('.rs__option').locator('text=equals').click() + + await secondValueField.fill('world') + await expect(secondValueField).toHaveValue('world') + + await wait(500) + + const firstLi = page.locator('.where-builder__and-filters li:nth-child(1)') + const removeButton = firstLi.locator('.condition__actions-remove') + + // remove first filter + await removeButton.click() + + const filterListItems = page.locator('.where-builder__and-filters li') + await expect(filterListItems).toHaveCount(1) + + await expect(firstValueField).toHaveValue('world') + }) +}) diff --git a/test/fields/collections/Email/index.ts b/test/fields/collections/Email/index.ts new file mode 100644 index 000000000..800cc775c --- /dev/null +++ b/test/fields/collections/Email/index.ts @@ -0,0 +1,124 @@ +import type { CollectionConfig } from 'payload' + +import { AfterInput } from './AfterInput.js' +import { BeforeInput } from './BeforeInput.js' +import CustomError from './CustomError.js' +import CustomLabel from './CustomLabel.js' +import { defaultEmail, emailFieldsSlug } from './shared.js' + +const EmailFields: CollectionConfig = { + slug: emailFieldsSlug, + admin: { + useAsTitle: 'text', + }, + defaultSort: 'id', + fields: [ + { + name: 'email', + type: 'email', + required: true, + }, + { + name: 'localizedEmail', + type: 'email', + localized: true, + }, + { + name: 'emailWithAutocomplete', + type: 'email', + admin: { + autoComplete: 'username', + }, + }, + { + name: 'i18nEmail', + type: 'email', + admin: { + description: { + en: 'en description', + es: 'es description', + }, + placeholder: { + en: 'en placeholder', + es: 'es placeholder', + }, + }, + label: { + en: 'Text en', + es: 'Text es', + }, + }, + { + name: 'defaultEmail', + type: 'email', + defaultValue: defaultEmail, + }, + { + name: 'defaultEmptyString', + type: 'email', + defaultValue: '', + }, + { + name: 'defaultFunction', + type: 'email', + defaultValue: () => defaultEmail, + }, + { + name: 'defaultAsync', + type: 'email', + defaultValue: async (): Promise => { + return new Promise((resolve) => + setTimeout(() => { + resolve(defaultEmail) + }, 1), + ) + }, + }, + { + name: 'customLabel', + type: 'email', + admin: { + components: { + Label: CustomLabel, + }, + }, + }, + { + name: 'customError', + type: 'email', + admin: { + components: { + Error: CustomError, + }, + }, + }, + { + name: 'beforeAndAfterInput', + type: 'email', + admin: { + components: { + afterInput: [AfterInput], + beforeInput: [BeforeInput], + }, + }, + }, + { + name: 'disableListColumnText', + type: 'email', + admin: { + disableListColumn: true, + disableListFilter: false, + }, + }, + { + name: 'disableListFilterText', + type: 'email', + admin: { + disableListColumn: false, + disableListFilter: true, + }, + }, + ], +} + +export default EmailFields diff --git a/test/fields/collections/Email/shared.ts b/test/fields/collections/Email/shared.ts new file mode 100644 index 000000000..87ae3c9c7 --- /dev/null +++ b/test/fields/collections/Email/shared.ts @@ -0,0 +1,16 @@ +import type { RequiredDataFromCollection } from 'payload/types' + +import type { EmailField } from '../../payload-types.js' + +export const defaultEmail = 'dev@example.com' + +export const emailFieldsSlug = 'email-fields' + +export const emailDoc: RequiredDataFromCollection = { + email: 'dev@example.com', + localizedEmail: 'another@example.com', +} + +export const anotherEmailDoc: RequiredDataFromCollection = { + email: 'user@example.com', +} diff --git a/test/fields/config.ts b/test/fields/config.ts index e50cd88fe..e1c695667 100644 --- a/test/fields/config.ts +++ b/test/fields/config.ts @@ -13,6 +13,7 @@ import CodeFields from './collections/Code/index.js' import CollapsibleFields from './collections/Collapsible/index.js' import ConditionalLogic from './collections/ConditionalLogic/index.js' import DateFields from './collections/Date/index.js' +import EmailFields from './collections/Email/index.js' import GroupFields from './collections/Group/index.js' import IndexedFields from './collections/Indexed/index.js' import JSONFields from './collections/JSON/index.js' @@ -61,6 +62,7 @@ export const collectionSlugs: CollectionConfig[] = [ CollapsibleFields, ConditionalLogic, DateFields, + EmailFields, RadioFields, GroupFields, RowFields, diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 48b322536..3e8c6a7ee 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -39,6 +39,7 @@ export interface Config { 'collapsible-fields': CollapsibleField; 'conditional-logic': ConditionalLogic; 'date-fields': DateField; + 'email-fields': EmailField; 'radio-fields': RadioField; 'group-fields': GroupField; 'row-fields': RowField; @@ -60,7 +61,7 @@ export interface Config { 'payload-migrations': PayloadMigration; }; db: { - defaultIDType: string; + defaultIDType: number; }; globals: { tabsWithRichText: TabsWithRichText; @@ -91,7 +92,7 @@ export interface UserAuthOperations { * via the `definition` "lexical-fields". */ export interface LexicalField { - id: string; + id: number; title: string; lexicalSimple?: { root: { @@ -132,7 +133,7 @@ export interface LexicalField { * via the `definition` "lexical-migrate-fields". */ export interface LexicalMigrateField { - id: string; + id: number; title: string; lexicalWithLexicalPluginData?: { root: { @@ -227,7 +228,7 @@ export interface LexicalMigrateField { * via the `definition` "lexical-localized-fields". */ export interface LexicalLocalizedField { - id: string; + id: number; title: string; lexicalBlocksSubLocalized?: { root: { @@ -267,7 +268,7 @@ export interface LexicalLocalizedField { * via the `definition` "users". */ export interface User { - id: string; + id: number; canViewConditionalField?: boolean | null; updatedAt: string; createdAt: string; @@ -285,7 +286,7 @@ export interface User { * via the `definition` "array-fields". */ export interface ArrayField { - id: string; + id: number; title?: string | null; items: { text: string; @@ -349,7 +350,7 @@ export interface ArrayField { * via the `definition` "block-fields". */ export interface BlockField { - id: string; + id: number; blocks: ( | { text: string; @@ -676,7 +677,7 @@ export interface BlockField { | null; relationshipBlocks?: | { - relationship?: (string | null) | TextField; + relationship?: (number | null) | TextField; id?: string | null; blockName?: string | null; blockType: 'relationships'; @@ -690,7 +691,7 @@ export interface BlockField { * via the `definition` "text-fields". */ export interface TextField { - id: string; + id: number; text: string; localizedText?: string | null; i18nText?: string | null; @@ -719,7 +720,7 @@ export interface TextField { * via the `definition` "checkbox-fields". */ export interface CheckboxField { - id: string; + id: number; checkbox: boolean; updatedAt: string; createdAt: string; @@ -729,7 +730,7 @@ export interface CheckboxField { * via the `definition` "code-fields". */ export interface CodeField { - id: string; + id: number; javascript?: string | null; typescript?: string | null; json?: string | null; @@ -743,7 +744,7 @@ export interface CodeField { * via the `definition` "collapsible-fields". */ export interface CollapsibleField { - id: string; + id: number; text: string; group?: { textWithinGroup?: string | null; @@ -775,7 +776,7 @@ export interface CollapsibleField { * via the `definition` "conditional-logic". */ export interface ConditionalLogic { - id: string; + id: number; text: string; toggleField?: boolean | null; fieldToToggle?: string | null; @@ -800,7 +801,7 @@ export interface ConditionalLogic { * via the `definition` "date-fields". */ export interface DateField { - id: string; + id: number; default: string; timeOnly?: string | null; timeOnlyWithCustomFormat?: string | null; @@ -810,12 +811,33 @@ export interface DateField { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "email-fields". + */ +export interface EmailField { + id: number; + email: string; + localizedEmail?: string | null; + i18nEmail?: string | null; + defaultEmail?: string | null; + defaultEmptyString?: string | null; + defaultFunction?: string | null; + defaultAsync?: string | null; + customLabel?: string | null; + customError?: string | null; + beforeAndAfterInput?: string | null; + disableListColumnText?: string | null; + disableListFilterText?: string | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "radio-fields". */ export interface RadioField { - id: string; + id: number; radio?: ('one' | 'two' | 'three') | null; updatedAt: string; createdAt: string; @@ -825,7 +847,7 @@ export interface RadioField { * via the `definition` "group-fields". */ export interface GroupField { - id: string; + id: number; group: { text: string; defaultParent?: string | null; @@ -900,7 +922,7 @@ export interface RowField { * via the `definition` "indexed-fields". */ export interface IndexedField { - id: string; + id: number; text: string; uniqueText?: string | null; uniqueRequiredText: string; @@ -929,7 +951,7 @@ export interface IndexedField { * via the `definition` "json-fields". */ export interface JsonField { - id: string; + id: number; json?: { foo?: 'bar' | 'foobar'; number?: 10 | 5; @@ -954,7 +976,7 @@ export interface JsonField { * via the `definition` "number-fields". */ export interface NumberField { - id: string; + id: number; number?: number | null; min?: number | null; max?: number | null; @@ -975,7 +997,7 @@ export interface NumberField { * via the `definition` "point-fields". */ export interface PointField { - id: string; + id: number; /** * @minItems 2 * @maxItems 2 @@ -1001,49 +1023,49 @@ export interface PointField { * via the `definition` "relationship-fields". */ export interface RelationshipField { - id: string; + id: number; text?: string | null; relationship: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; }; relationHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } )[] | null; - relationToSelf?: (string | null) | RelationshipField; - relationToSelfSelectOnly?: (string | null) | RelationshipField; - relationWithDynamicDefault?: (string | null) | User; + relationToSelf?: (number | null) | RelationshipField; + relationToSelfSelectOnly?: (number | null) | RelationshipField; + relationWithDynamicDefault?: (number | null) | User; relationHasManyWithDynamicDefault?: { relationTo: 'users'; - value: string | User; + value: number | User; } | null; - relationshipWithMin?: (string | TextField)[] | null; - relationshipWithMax?: (string | TextField)[] | null; - relationshipHasMany?: (string | TextField)[] | null; + relationshipWithMin?: (number | TextField)[] | null; + relationshipWithMax?: (number | TextField)[] | null; + relationshipHasMany?: (number | TextField)[] | null; array?: | { - relationship?: (string | null) | TextField; + relationship?: (number | null) | TextField; id?: string | null; }[] | null; relationshipWithMinRows?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; updatedAt: string; @@ -1054,7 +1076,7 @@ export interface RelationshipField { * via the `definition` "rich-text-fields". */ export interface RichTextField { - id: string; + id: number; title: string; lexicalCustomFields: { root: { @@ -1129,7 +1151,7 @@ export interface RichTextField { * via the `definition` "select-fields". */ export interface SelectField { - id: string; + id: number; select?: ('one' | 'two' | 'three') | null; selectReadOnly?: ('one' | 'two' | 'three') | null; selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null; @@ -1147,7 +1169,7 @@ export interface SelectField { * via the `definition` "tabs-fields-2". */ export interface TabsFields2 { - id: string; + id: number; tabsInArray?: | { text?: string | null; @@ -1165,7 +1187,7 @@ export interface TabsFields2 { * via the `definition` "tabs-fields". */ export interface TabsField { - id: string; + id: number; sidebarField?: string | null; array: { text: string; @@ -1274,9 +1296,9 @@ export interface TabsField { * via the `definition` "uploads". */ export interface Upload { - id: string; + id: number; text?: string | null; - media?: string | Upload | null; + media?: number | Upload | null; richText?: { root: { type: string; @@ -1309,9 +1331,9 @@ export interface Upload { * via the `definition` "uploads2". */ export interface Uploads2 { - id: string; + id: number; text?: string | null; - media?: string | Uploads2 | null; + media?: number | Uploads2 | null; updatedAt: string; createdAt: string; url?: string | null; @@ -1329,8 +1351,8 @@ export interface Uploads2 { * via the `definition` "uploads3". */ export interface Uploads3 { - id: string; - media?: string | Uploads3 | null; + id: number; + media?: number | Uploads3 | null; richText?: { root: { type: string; @@ -1363,7 +1385,7 @@ export interface Uploads3 { * via the `definition` "ui-fields". */ export interface UiField { - id: string; + id: number; text: string; updatedAt: string; createdAt: string; @@ -1373,10 +1395,10 @@ export interface UiField { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: string; + id: number; user: { relationTo: 'users'; - value: string | User; + value: number | User; }; key?: string | null; value?: @@ -1396,7 +1418,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: string; + id: number; name?: string | null; batch?: number | null; updatedAt: string; @@ -1407,7 +1429,7 @@ export interface PayloadMigration { * via the `definition` "tabsWithRichText". */ export interface TabsWithRichText { - id: string; + id: number; tab1?: { rt1?: { root: { diff --git a/test/fields/seed.ts b/test/fields/seed.ts index 2bc4c0bb7..972414e51 100644 --- a/test/fields/seed.ts +++ b/test/fields/seed.ts @@ -12,6 +12,7 @@ import { codeDoc } from './collections/Code/shared.js' import { collapsibleDoc } from './collections/Collapsible/shared.js' import { conditionalLogicDoc } from './collections/ConditionalLogic/shared.js' import { dateDoc } from './collections/Date/shared.js' +import { anotherEmailDoc, emailDoc } from './collections/Email/shared.js' import { groupDoc } from './collections/Group/shared.js' import { jsonDoc } from './collections/JSON/shared.js' import { lexicalDocData } from './collections/Lexical/data.js' @@ -34,6 +35,7 @@ import { collectionSlugs, conditionalLogicSlug, dateFieldsSlug, + emailFieldsSlug, groupFieldsSlug, jsonFieldsSlug, lexicalFieldsSlug, @@ -50,6 +52,7 @@ import { uploadsSlug, usersSlug, } from './slugs.js' + const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -158,6 +161,20 @@ export const seed = async (_payload: Payload) => { overrideAccess: true, }) + await _payload.create({ + collection: emailFieldsSlug, + data: emailDoc, + depth: 0, + overrideAccess: true, + }) + + await _payload.create({ + collection: emailFieldsSlug, + data: anotherEmailDoc, + depth: 0, + overrideAccess: true, + }) + const createdRichTextDoc = await _payload.create({ collection: richTextFieldsSlug, data: richTextDocWithRelationship, diff --git a/test/fields/slugs.ts b/test/fields/slugs.ts index 0eb38ac05..1fa9de9b7 100644 --- a/test/fields/slugs.ts +++ b/test/fields/slugs.ts @@ -6,6 +6,7 @@ export const codeFieldsSlug = 'code-fields' export const collapsibleFieldsSlug = 'collapsible-fields' export const conditionalLogicSlug = 'conditional-logic' export const dateFieldsSlug = 'date-fields' +export const emailFieldsSlug = 'email-fields' export const groupFieldsSlug = 'group-fields' export const indexedFieldsSlug = 'indexed-fields' export const jsonFieldsSlug = 'json-fields'