diff --git a/src/admin/components/forms/field-types/Array/index.tsx b/src/admin/components/forms/field-types/Array/index.tsx index 18fbd82ccb..0f428ad1da 100644 --- a/src/admin/components/forms/field-types/Array/index.tsx +++ b/src/admin/components/forms/field-types/Array/index.tsx @@ -63,7 +63,7 @@ const ArrayFieldType: React.FC = (props) => { const locale = useLocale(); const operation = useOperation(); const { t, i18n } = useTranslation('fields'); - const { localization } = useConfig(); + const { localization, collections } = useConfig(); const editingDefaultLocale = (() => { if (localization && localization.fallback) { @@ -95,7 +95,7 @@ const ArrayFieldType: React.FC = (props) => { showError, errorMessage, value, - rows, + rows = [], valid, } = useField({ path, @@ -142,8 +142,8 @@ const ArrayFieldType: React.FC = (props) => { dispatchFields({ type: 'SET_ROW_COLLAPSED', path, collapsed, rowID, setDocFieldPreferences }); }, [dispatchFields, path, setDocFieldPreferences]); - const hasMaxRows = maxRows && rows?.length >= maxRows; - const fieldErrorCount = (rows || []).reduce((total, row) => total + (row?.childErrorPaths?.size || 0), 0) + (valid ? 0 : 1); + const hasMaxRows = maxRows && rows.length >= maxRows; + const fieldErrorCount = rows.reduce((total, row) => total + (row?.childErrorPaths?.size || 0), 0) + (valid ? 0 : 1); const fieldHasErrors = submitted && fieldErrorCount > 0; const classes = [ @@ -152,9 +152,7 @@ const ArrayFieldType: React.FC = (props) => { className, fieldHasErrors ? `${baseClass}--has-error` : `${baseClass}--has-no-error`, ].filter(Boolean).join(' '); - - if (!rows) return null; - + console.log(path, collections[0].fields, fields); return (
= (props) => { row.id)} onDragEnd={({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex)} + className={`${baseClass}__draggable-rows`} > {rows.length > 0 && rows.map((row, i) => ( = (props) => { return true; })(); + const memoizedValidate = useCallback((value, options) => { // alternative locales can be null if (!editingDefaultLocale && value === null) { @@ -88,12 +89,11 @@ const BlocksField: React.FC = (props) => { return validate(value, { ...options, minRows, maxRows, required }); }, [maxRows, minRows, required, validate, editingDefaultLocale]); - const { showError, errorMessage, value, - rows, + rows = [], valid, } = useField({ path, @@ -141,7 +141,7 @@ const BlocksField: React.FC = (props) => { dispatchFields({ type: 'SET_ROW_COLLAPSED', path, collapsed, rowID, setDocFieldPreferences }); }, [dispatchFields, path, setDocFieldPreferences]); - const hasMaxRows = maxRows && rows?.length >= maxRows; + const hasMaxRows = maxRows && rows.length >= maxRows; const fieldErrorCount = rows.reduce((total, row) => total + (row?.childErrorPaths?.size || 0), 0); const fieldHasErrors = submitted && fieldErrorCount + (valid ? 0 : 1) > 0; @@ -153,8 +153,6 @@ const BlocksField: React.FC = (props) => { fieldHasErrors ? `${baseClass}--has-error` : `${baseClass}--has-no-error`, ].filter(Boolean).join(' '); - if (!rows) return null; - return (
(options: Options): FieldType => { +const useField = (options: Options): FieldType => { const { path, validate, @@ -30,7 +30,6 @@ const useField = (options: Options): FieldType => { const field = useFormFields(([fields]) => fields[path]); const { t } = useTranslation(); const dispatchField = useFormFields(([_, dispatch]) => dispatch); - const { fields } = useWatchForm(); const { getData, getSiblingData, setModified } = useForm(); @@ -147,10 +146,6 @@ const useField = (options: Options): FieldType => { field?.rows, ]); - if (path === 'items') { - console.log({ fieldRows: field.rows, fieldRowsFromState: fields.items.rows }); - } - return result; }; diff --git a/test/array-update/config.ts b/test/array-update/config.ts index 03861f6412..351cb5c58c 100644 --- a/test/array-update/config.ts +++ b/test/array-update/config.ts @@ -1,4 +1,5 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults'; +import { devUser } from '../credentials'; export default buildConfigWithDefaults({ collections: [ @@ -41,4 +42,13 @@ export default buildConfigWithDefaults({ ], }, ], + onInit: async (payload) => { + await payload.create({ + collection: 'users', + data: { + email: devUser.email, + password: devUser.password, + }, + }); + }, }); diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index 0183e92e22..f76d0c8c07 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -411,6 +411,88 @@ describe('fields', () => { const customRowLabel = await page.locator('#rowLabelAsComponent-row-0 >> .row-label :text("custom row label")'); await expect(customRowLabel).toHaveCSS('text-transform', 'uppercase'); }); + + describe('row manipulation', () => { + test('should add 2 new rows', async () => { + await page.goto(url.create); + + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1'); + await page.locator('#field-potentiallyEmptyArray__1__text').fill('array row 2'); + + await saveDocAndAssert(page); + }); + + test('should remove 2 new rows', async () => { + await page.goto(url.create); + + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1'); + await page.locator('#field-potentiallyEmptyArray__1__text').fill('array row 2'); + + await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click(); + await page.locator('#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__remove').click(); + await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click(); + await page.locator('#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__remove').click(); + + const rowsContainer = await page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows'); + const directChildDivCount = await rowsContainer.evaluate((element) => { + const childDivCount = element.querySelectorAll(':scope > div'); + return childDivCount.length; + }); + + expect(directChildDivCount).toBe(0); + }); + + test('should remove existing row', async () => { + await page.goto(url.create); + + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1'); + + await saveDocAndAssert(page); + + await page.locator('#potentiallyEmptyArray-row-0 .array-actions__button').click(); + await page.locator('#potentiallyEmptyArray-row-0 .popup__scroll .array-actions__action.array-actions__remove').click(); + + const rowsContainer = await page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows'); + const directChildDivCount = await rowsContainer.evaluate((element) => { + const childDivCount = element.querySelectorAll(':scope > div'); + return childDivCount.length; + }); + + expect(directChildDivCount).toBe(0); + }); + + test('should add row after removing existing row', async () => { + await page.goto(url.create); + + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + await page.locator('#field-potentiallyEmptyArray__0__text').fill('array row 1'); + await page.locator('#field-potentiallyEmptyArray__1__text').fill('array row 2'); + + await saveDocAndAssert(page); + + await page.locator('#potentiallyEmptyArray-row-1 .array-actions__button').click(); + await page.locator('#potentiallyEmptyArray-row-1 .popup__scroll .array-actions__action.array-actions__remove').click(); + await page.locator('#field-potentiallyEmptyArray > .array-field__add-button-wrap > button').click(); + + await page.locator('#field-potentiallyEmptyArray__1__text').fill('updated array row 2'); + + await saveDocAndAssert(page); + + const rowsContainer = await page.locator('#field-potentiallyEmptyArray > .array-field__draggable-rows'); + const directChildDivCount = await rowsContainer.evaluate((element) => { + const childDivCount = element.querySelectorAll(':scope > div'); + return childDivCount.length; + }); + + expect(directChildDivCount).toBe(2); + }); + }); }); describe('tabs', () => { @@ -494,7 +576,7 @@ describe('fields', () => { await wait(200); await editLinkModal.locator('button[type="submit"]').click(); const errorField = await page.locator('[id^=drawer_1_rich-text-link-] .render-fields > :nth-child(3)'); - const hasErrorClass = await errorField.evaluate(el => el.classList.contains('error')); + const hasErrorClass = await errorField.evaluate((el) => el.classList.contains('error')); expect(hasErrorClass).toBe(true); });