diff --git a/demo/collections/AllFields.ts b/demo/collections/AllFields.ts
index 92a1f7348d..81338fbf31 100644
--- a/demo/collections/AllFields.ts
+++ b/demo/collections/AllFields.ts
@@ -40,6 +40,25 @@ const AllFields: PayloadCollectionConfig = {
read: ({ req: { user } }) => Boolean(user),
},
},
+ {
+ name: 'descriptionText',
+ type: 'text',
+ label: 'Text with text description',
+ defaultValue: 'Default Value',
+ admin: {
+ description: 'This text describes the field',
+ },
+ },
+ {
+ name: 'descriptionFunction',
+ type: 'text',
+ label: 'Text with function description',
+ defaultValue: 'Default Value',
+ maxLength: 20,
+ admin: {
+ description: ({ value }) => (typeof value === 'string' ? `${20 - value.length} characters left` : ''),
+ },
+ },
{
name: 'image',
type: 'upload',
diff --git a/demo/collections/CustomComponents/index.ts b/demo/collections/CustomComponents/index.ts
index d4fb667354..c784275b29 100644
--- a/demo/collections/CustomComponents/index.ts
+++ b/demo/collections/CustomComponents/index.ts
@@ -7,6 +7,7 @@ import GroupField from './components/fields/Group/Field';
import NestedGroupField from './components/fields/NestedGroupCustomField/Field';
import NestedText1Field from './components/fields/NestedText1/Field';
import ListView from './components/views/List';
+import CustomDescriptionComponent from '../../customComponents/Description';
const CustomComponents: PayloadCollectionConfig = {
slug: 'custom-components',
@@ -38,6 +39,17 @@ const CustomComponents: PayloadCollectionConfig = {
},
},
},
+ {
+ name: 'descriptionComponent',
+ type: 'text',
+ label: 'Text with description component',
+ defaultValue: 'Default Value',
+ admin: {
+ components: {
+ Description: CustomDescriptionComponent,
+ },
+ },
+ },
{
name: 'array',
label: 'Array',
diff --git a/demo/customComponents/Description/index.tsx b/demo/customComponents/Description/index.tsx
new file mode 100644
index 0000000000..581c56391a
--- /dev/null
+++ b/demo/customComponents/Description/index.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+
+const CustomDescriptionComponent: React.FC = ({ value }) => (
+
+ character count:
+ {' '}
+ { value.length }
+
+);
+
+export default CustomDescriptionComponent;
diff --git a/src/admin/components/forms/Label/index.scss b/src/admin/components/forms/Label/index.scss
index 98c4ceeb69..c6b9c3b1fc 100644
--- a/src/admin/components/forms/Label/index.scss
+++ b/src/admin/components/forms/Label/index.scss
@@ -3,7 +3,7 @@
label.field-label {
display: flex;
padding-bottom: base(.25);
- color: $color-gray;
+ color: $color-dark-gray;
.required {
color: $color-red;
diff --git a/src/admin/components/forms/RenderFieldDescription/index.scss b/src/admin/components/forms/RenderFieldDescription/index.scss
new file mode 100644
index 0000000000..2a6f97e10a
--- /dev/null
+++ b/src/admin/components/forms/RenderFieldDescription/index.scss
@@ -0,0 +1,8 @@
+@import '../../../scss/styles.scss';
+
+.field-description {
+ display: flex;
+ padding-top: base(.25);
+ padding-bottom: base(.25);
+ color: $color-gray;
+}
diff --git a/src/admin/components/forms/RenderFieldDescription/index.tsx b/src/admin/components/forms/RenderFieldDescription/index.tsx
new file mode 100644
index 0000000000..255f125601
--- /dev/null
+++ b/src/admin/components/forms/RenderFieldDescription/index.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { Props } from './types';
+import './index.scss';
+
+const RenderFieldDescription: React.FC = (props) => {
+ const {
+ description,
+ value,
+ CustomComponent,
+ } = props;
+
+ if (CustomComponent) {
+ return (
+
+
+
+ );
+ }
+
+ if (description) {
+ return (
+
+ {typeof description === 'function' ? description({ value }) : description}
+
+ );
+ }
+
+ return null;
+};
+
+export default RenderFieldDescription;
diff --git a/src/admin/components/forms/RenderFieldDescription/types.ts b/src/admin/components/forms/RenderFieldDescription/types.ts
new file mode 100644
index 0000000000..c784b8419b
--- /dev/null
+++ b/src/admin/components/forms/RenderFieldDescription/types.ts
@@ -0,0 +1,5 @@
+export type Props = {
+ description?: string | ((value) => string);
+ CustomComponent?: React.ComponentType<{ value: unknown }>;
+ value: unknown;
+}
diff --git a/src/admin/components/forms/field-types/Text/index.tsx b/src/admin/components/forms/field-types/Text/index.tsx
index 792e95803a..edbcfbb8ec 100644
--- a/src/admin/components/forms/field-types/Text/index.tsx
+++ b/src/admin/components/forms/field-types/Text/index.tsx
@@ -5,6 +5,7 @@ import Label from '../../Label';
import Error from '../../Error';
import { text } from '../../../../../fields/validations';
import { Props } from './types';
+import RenderFieldDescription from '../../RenderFieldDescription';
import './index.scss';
@@ -21,6 +22,10 @@ const Text: React.FC = (props) => {
style,
width,
condition,
+ description,
+ components: {
+ Description,
+ } = {},
} = {},
} = props;
@@ -73,6 +78,11 @@ const Text: React.FC = (props) => {
id={path}
name={path}
/>
+
);
};
diff --git a/src/collections/config/types.ts b/src/collections/config/types.ts
index 14e718d91f..1002251956 100644
--- a/src/collections/config/types.ts
+++ b/src/collections/config/types.ts
@@ -99,7 +99,7 @@ export type PayloadCollectionConfig = {
admin?: {
useAsTitle?: string;
defaultColumns?: string[];
- disableDuplicate?: boolean
+ disableDuplicate?: boolean;
components?: {
views?: {
Edit?: React.ComponentType
diff --git a/src/fields/config/schema.ts b/src/fields/config/schema.ts
index e3a958edde..b957206064 100644
--- a/src/fields/config/schema.ts
+++ b/src/fields/config/schema.ts
@@ -6,6 +6,10 @@ const component = joi.alternatives().try(
);
export const baseAdminFields = joi.object().keys({
+ description: joi.alternatives().try(
+ joi.string(),
+ joi.func(),
+ ),
position: joi.string().valid('sidebar'),
width: joi.string(),
style: joi.object().unknown(),
@@ -17,6 +21,7 @@ export const baseAdminFields = joi.object().keys({
Cell: component,
Field: component,
Filter: component,
+ Description: component,
}).default({}),
});
diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts
index 7d615d1ac9..2131e03d26 100644
--- a/src/fields/config/types.ts
+++ b/src/fields/config/types.ts
@@ -22,7 +22,7 @@ export type FieldAccess = (args: {
siblingData: Record
}) => Promise | boolean;
-export type Condition = (data: Record, siblingData: Record) => boolean
+export type Condition = (data: Record, siblingData: Record) => boolean;
type Admin = {
position?: string;
@@ -31,7 +31,13 @@ type Admin = {
readOnly?: boolean;
disabled?: boolean;
condition?: Condition;
- components?: { [key: string]: React.ComponentType };
+ description?: string | ((data: Record) => string);
+ components?: {
+ Filter?: React.ComponentType;
+ Cell?: React.ComponentType;
+ Field?: React.ComponentType;
+ Description?: React.ComponentType<{value: unknown}>
+ }
hidden?: boolean
}
@@ -214,11 +220,11 @@ export type RadioField = FieldBase & {
}
export type Block = {
- slug: string,
- labels?: Labels
- fields: Field[],
- imageURL?: string
- imageAltText?: string
+ slug: string;
+ labels?: Labels;
+ fields: Field[];
+ imageURL?: string;
+ imageAltText?: string;
}
export type BlockField = FieldBase & {