Skip to content

Commit

Permalink
fix: RichText link custom fields (#2756)
Browse files Browse the repository at this point in the history
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
  • Loading branch information
AlessioGr and DanRibbens committed Jun 2, 2023
1 parent f978299 commit 23be263
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ import LinkIcon from '../../../../../../icons/Link';
import reduceFieldsToValues from '../../../../../Form/reduceFieldsToValues';
import { useConfig } from '../../../../../../utilities/Config';
import isElementActive from '../../isActive';
import { unwrapLink } from '../utilities';
import { getBaseFields } from '../LinkDrawer/baseFields';
import { transformExtraFields, unwrapLink } from '../utilities';
import { LinkDrawer } from '../LinkDrawer';
import { Field } from '../../../../../../../../fields/config/types';
import { Props as RichTextFieldProps } from '../../../types';
import buildStateFromSchema from '../../../../../Form/buildStateFromSchema';
import { useAuth } from '../../../../../../utilities/Auth';
import { Fields } from '../../../../../Form/types';
import { useLocale } from '../../../../../../utilities/Locale';
import { useDrawerSlug } from '../../../../../../elements/Drawer/useDrawerSlug';

/**
* This function is called when an new link is created - not when an existing link is edited.
*/
const insertLink = (editor, fields) => {
const isCollapsed = editor.selection && Range.isCollapsed(editor.selection);
const data = reduceFieldsToValues(fields, true);
Expand All @@ -29,7 +30,7 @@ const insertLink = (editor, fields) => {
url: data.url,
doc: data.doc,
newTab: data.newTab,
fields: data.fields,
fields: data.fields, // Any custom user-added fields are part of data.fields
children: [],
};

Expand Down Expand Up @@ -68,25 +69,7 @@ export const LinkButton: React.FC<{
const config = useConfig();

const [fieldSchema] = useState(() => {
const baseFields: Field[] = getBaseFields(config);

const fields = typeof customFieldSchema === 'function' ? customFieldSchema({ defaultFields: baseFields, config, i18n }) : baseFields;

if (Array.isArray(customFieldSchema)) {
fields.push({
name: 'fields',
type: 'group',
admin: {
style: {
margin: 0,
padding: 0,
borderTop: 0,
borderBottom: 0,
},
},
fields: customFieldSchema,
});
}
const fields = transformExtraFields(customFieldSchema, config, i18n);

return fields;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import { ReactEditor, useSlate } from 'slate-react';
import { Transforms, Node, Editor } from 'slate';
import { useModal } from '@faceless-ui/modal';
import { Trans, useTranslation } from 'react-i18next';
import { unwrapLink } from '../utilities';
import { transformExtraFields, unwrapLink } from '../utilities';
import Popup from '../../../../../../elements/Popup';
import { LinkDrawer } from '../LinkDrawer';
import { Fields } from '../../../../../Form/types';
import buildStateFromSchema from '../../../../../Form/buildStateFromSchema';
import { useAuth } from '../../../../../../utilities/Auth';
import { useLocale } from '../../../../../../utilities/Locale';
import { useConfig } from '../../../../../../utilities/Config';
import { getBaseFields } from '../LinkDrawer/baseFields';
import { Field } from '../../../../../../../../fields/config/types';
import reduceFieldsToValues from '../../../../../Form/reduceFieldsToValues';
import deepCopyObject from '../../../../../../../../utilities/deepCopyObject';
import Button from '../../../../../../elements/Button';
Expand All @@ -23,6 +21,10 @@ import { useDrawerSlug } from '../../../../../../elements/Drawer/useDrawerSlug';

const baseClass = 'rich-text-link';

/**
* This function is called when an existing link is edited.
* When a link is first created, another function is called: {@link ../Button/index.tsx#insertLink}
*/
const insertChange = (editor, fields, customFieldSchema) => {
const data = reduceFieldsToValues(fields, true);

Expand Down Expand Up @@ -79,25 +81,8 @@ export const LinkElement: React.FC<{
const [renderPopup, setRenderPopup] = useState(false);
const [initialState, setInitialState] = useState<Fields>({});
const [fieldSchema] = useState(() => {
const baseFields: Field[] = getBaseFields(config);

const fields = typeof customFieldSchema === 'function' ? customFieldSchema({ defaultFields: baseFields, config, i18n }) : baseFields;

if (Array.isArray(customFieldSchema)) {
fields.push({
name: 'fields',
type: 'group',
admin: {
style: {
margin: 0,
padding: 0,
borderTop: 0,
borderBottom: 0,
},
},
fields: customFieldSchema,
});
}
const fields = transformExtraFields(customFieldSchema, config, i18n);


return fields;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Editor, Transforms, Range, Element } from 'slate';
import type { i18n } from 'i18next';
import type { SanitizedConfig } from 'payload/config';
import { getBaseFields } from './LinkDrawer/baseFields';
import { Field } from '../../../../../../../fields/config/types';

export const unwrapLink = (editor: Editor): void => {
Transforms.unwrapNodes(editor, { match: (n) => Element.isElement(n) && n.type === 'link' });
Expand Down Expand Up @@ -37,3 +41,48 @@ export const withLinks = (incomingEditor: Editor): Editor => {

return editor;
};

/**
* This function is run to enrich the basefields which every link has with potential, custom user-added fields.
*/
export function transformExtraFields(customFieldSchema: Field[] | ((args: {
defaultFields: Field[];
config: SanitizedConfig;
i18n: i18n;
}) => Field[]), config: SanitizedConfig, i18n: i18n): Field[] {
const baseFields: Field[] = getBaseFields(config);

const fields = typeof customFieldSchema === 'function' ? customFieldSchema({ defaultFields: baseFields, config, i18n }) : baseFields;

// Wrap fields which are not part of the base schema in a group named 'fields' - otherwise they will be rendered but not saved
const extraFields = [];
fields.forEach((field) => {
if ('name' in field) {
if (!baseFields.find((baseField) => !('name' in baseField) || baseField.name === field.name)) {
if (field.name !== 'fields' && field.type !== 'group') {
extraFields.push(field);
// Remove from fields from now, as they need to be part of the fields group below
fields.splice(fields.indexOf(field), 1);
}
}
}
});


if (Array.isArray(customFieldSchema) || fields.length > 0) {
fields.push({
name: 'fields',
type: 'group',
admin: {
style: {
margin: 0,
padding: 0,
borderTop: 0,
borderBottom: 0,
},
},
fields: Array.isArray(customFieldSchema) ? customFieldSchema.concat(extraFields) : extraFields,
});
}
return fields;
}
23 changes: 9 additions & 14 deletions test/fields/collections/RichText/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,16 @@ const RichTextFields: CollectionConfig = {
'upload',
],
link: {
fields: () => [
{
required: false,
name: 'rel',
label: 'Rel Attribute',
type: 'select',
hasMany: true,
options: [
'noopener', 'noreferrer', 'nofollow',
],
admin: {
description: 'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
fields: ({ defaultFields }) => {
return [
...defaultFields,
{
label: 'Custom',
name: 'customLinkField',
type: 'text',
},
},
],
];
},
},
upload: {
collections: {
Expand Down
33 changes: 29 additions & 4 deletions test/fields/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,16 +563,41 @@ describe('fields', () => {
});

test('should respect customizing the default fields', async () => {
const linkText = 'link';
const value = 'test value';
await navigateToRichTextFields();
const field = page.locator('.rich-text', { has: page.locator('#field-richTextCustomFields') });
// open link drawer
const button = await field.locator('button.rich-text__button.link');

await button.click();

// fill link fields
const linkDrawer = await page.locator('[id^=drawer_1_rich-text-link-]');
await expect(linkDrawer).toBeVisible();
const fieldCount = await linkDrawer.locator('.render-fields > .field-type').count();
await expect(fieldCount).toEqual(1);
const fields = await linkDrawer.locator('.render-fields > .field-type');
await fields.locator('#field-text').fill(linkText);
await fields.locator('#field-url').fill('https://payloadcms.com');
const input = await fields.locator('#field-fields__customLinkField');
await input.fill(value);

// submit link closing drawer
await linkDrawer.locator('button[type="submit"]').click();
const linkInEditor = field.locator(`.rich-text-link >> text="${linkText}"`);
await saveDocAndAssert(page);

// open modal again
await linkInEditor.click();

const popup = page.locator('.popup--active .rich-text-link__popup');
await expect(popup).toBeVisible();

await popup.locator('.rich-text-link__link-edit').click();

const linkDrawer2 = await page.locator('[id^=drawer_1_rich-text-link-]');
const fields2 = await linkDrawer2.locator('.render-fields > .field-type');
const input2 = await fields2.locator('#field-fields__customLinkField');


await expect(input2).toHaveValue(value);
});
});

Expand Down

0 comments on commit 23be263

Please sign in to comment.