Skip to content

Commit 58b7415

Browse files
authored
feat(ui): adds beforeInput & afterInput props for arrays, blocks, collapsible, group, radio, & relationship fields. (#9674)
The following fields did not have the ability to add beforeInput & afterInput: - Arrays - Blocks - Collapsibles - Groups - Radios - Relationships ### How? Adds the ability to define and add before and after inputs to these fields in your config. Here are examples of the above fields with beforeInputs & afterInputs defined (I.e. `#before-input` & `#after-input`): `Arrays`: ![Screenshot 2024-12-02 at 1 22 15 PM](https://github.com/user-attachments/assets/8a2d5351-acab-4b28-b9bd-b19e1942da7e) `Blocks`: ![Screenshot 2024-12-02 at 1 35 53 PM](https://github.com/user-attachments/assets/e9b5f22b-2671-4efb-8ee4-b4de3a1addc5) `Collapsible`: ![Screenshot 2024-12-02 at 1 46 34 PM](https://github.com/user-attachments/assets/38f7a016-8a79-4ece-90d2-0b1761db19cd) `Groups`: ![Screenshot 2024-12-02 at 1 52 34 PM](https://github.com/user-attachments/assets/dc6a123b-ee01-4021-bbbf-ac1b8c086072) `Radios`: ![Screenshot 2024-12-02 at 1 59 35 PM](https://github.com/user-attachments/assets/4ddd16a1-2ad2-44f5-a7e9-86aa6e4e947b) `Relationships`: ![Screenshot 2024-12-02 at 1 21 55 PM](https://github.com/user-attachments/assets/b4679baf-6157-41c9-8485-ca570be979d2)
1 parent 631edd4 commit 58b7415

File tree

9 files changed

+149
-8
lines changed

9 files changed

+149
-8
lines changed

packages/payload/src/fields/config/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,8 @@ export type DateFieldClient = {
619619
export type GroupField = {
620620
admin?: {
621621
components?: {
622+
afterInput?: CustomComponent[]
623+
beforeInput?: CustomComponent[]
622624
Label?: CustomComponent<GroupFieldLabelClientComponent | GroupFieldLabelServerComponent>
623625
} & Admin['components']
624626
hideGutter?: boolean
@@ -660,6 +662,8 @@ export type CollapsibleField = {
660662
| {
661663
admin: {
662664
components: {
665+
afterInput?: CustomComponent[]
666+
beforeInput?: CustomComponent[]
663667
Label: CustomComponent<
664668
CollapsibleFieldLabelClientComponent | CollapsibleFieldLabelServerComponent
665669
>
@@ -671,6 +675,8 @@ export type CollapsibleField = {
671675
| {
672676
admin?: {
673677
components?: {
678+
afterInput?: CustomComponent[]
679+
beforeInput?: CustomComponent[]
674680
Label?: CustomComponent<
675681
CollapsibleFieldLabelClientComponent | CollapsibleFieldLabelServerComponent
676682
>
@@ -1029,6 +1035,8 @@ type RelationshipAdmin = {
10291035
allowCreate?: boolean
10301036
allowEdit?: boolean
10311037
components?: {
1038+
afterInput?: CustomComponent[]
1039+
beforeInput?: CustomComponent[]
10321040
Error?: CustomComponent<
10331041
RelationshipFieldErrorClientComponent | RelationshipFieldErrorServerComponent
10341042
>
@@ -1124,6 +1132,8 @@ export type RichTextFieldClient<
11241132
export type ArrayField = {
11251133
admin?: {
11261134
components?: {
1135+
afterInput?: CustomComponent[]
1136+
beforeInput?: CustomComponent[]
11271137
Error?: CustomComponent<ArrayFieldErrorClientComponent | ArrayFieldErrorServerComponent>
11281138
Label?: CustomComponent<ArrayFieldLabelClientComponent | ArrayFieldLabelServerComponent>
11291139
RowLabel?: RowLabelComponent
@@ -1163,6 +1173,8 @@ export type ArrayFieldClient = {
11631173
export type RadioField = {
11641174
admin?: {
11651175
components?: {
1176+
afterInput?: CustomComponent[]
1177+
beforeInput?: CustomComponent[]
11661178
Error?: CustomComponent<RadioFieldErrorClientComponent | RadioFieldErrorServerComponent>
11671179
Label?: CustomComponent<RadioFieldLabelClientComponent | RadioFieldLabelServerComponent>
11681180
} & Admin['components']
@@ -1302,6 +1314,8 @@ export type ClientBlock = {
13021314
export type BlocksField = {
13031315
admin?: {
13041316
components?: {
1317+
afterInput?: CustomComponent[]
1318+
beforeInput?: CustomComponent[]
13051319
Error?: CustomComponent<BlocksFieldErrorClientComponent | BlocksFieldErrorServerComponent>
13061320
} & Admin['components']
13071321
initCollapsed?: boolean

packages/ui/src/fields/Array/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
110110
)
111111

112112
const {
113-
customComponents: { Description, Error, Label, RowLabels } = {},
113+
customComponents: { AfterInput, BeforeInput, Description, Error, Label, RowLabels } = {},
114114
errorPaths,
115115
rows: rowsData = [],
116116
showError,
@@ -268,6 +268,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
268268
/>
269269
</header>
270270
<NullifyLocaleField fieldValue={value} localized={localized} path={path} />
271+
{BeforeInput}
271272
{(rowsData?.length > 0 || (!valid && (showRequired || showMinRows))) && (
272273
<DraggableSortable
273274
className={`${baseClass}__draggable-rows`}
@@ -349,6 +350,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
349350
{t('fields:addLabel', { label: getTranslation(labels.singular, i18n) })}
350351
</Button>
351352
)}
353+
{AfterInput}
352354
</div>
353355
)
354356
}

packages/ui/src/fields/Blocks/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
9595
)
9696

9797
const {
98-
customComponents: { Description, Error, Label } = {},
98+
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
9999
errorPaths,
100100
rows = [],
101101
showError,
@@ -250,6 +250,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
250250
Fallback={<FieldDescription description={description} path={path} />}
251251
/>
252252
</header>
253+
{BeforeInput}
253254
<NullifyLocaleField fieldValue={value} localized={localized} path={path} />
254255
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (
255256
<DraggableSortable
@@ -350,6 +351,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => {
350351
/>
351352
</Fragment>
352353
)}
354+
{AfterInput}
353355
</div>
354356
)
355357
}

packages/ui/src/fields/Collapsible/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const CollapsibleFieldComponent: CollapsibleFieldClientComponent = (props) => {
4242
const [errorCount, setErrorCount] = useState(0)
4343
const fieldHasErrors = errorCount > 0
4444

45-
const { customComponents: { Description, Label } = {} } = useField({
45+
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {} } = useField({
4646
path,
4747
})
4848

@@ -120,6 +120,7 @@ const CollapsibleFieldComponent: CollapsibleFieldClientComponent = (props) => {
120120
id={`field-${fieldPreferencesKey}`}
121121
style={styles}
122122
>
123+
{BeforeInput}
123124
<CollapsibleElement
124125
className={`${baseClass}__collapsible`}
125126
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
@@ -142,6 +143,7 @@ const CollapsibleFieldComponent: CollapsibleFieldClientComponent = (props) => {
142143
readOnly={readOnly}
143144
/>
144145
</CollapsibleElement>
146+
{AfterInput}
145147
<RenderCustomComponent
146148
CustomComponent={Description}
147149
Fallback={<FieldDescription description={description} path={path} />}

packages/ui/src/fields/Group/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => {
4040
const isWithinGroup = useGroup()
4141
const isWithinRow = useRow()
4242
const isWithinTab = useTabs()
43-
const { customComponents: { Description, Label } = {}, errorPaths } = useField({ path })
43+
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, errorPaths } =
44+
useField({ path })
4445
const submitted = useFormSubmitted()
4546
const errorCount = errorPaths.length
4647
const fieldHasErrors = submitted && errorCount > 0
@@ -94,6 +95,7 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => {
9495
)}
9596
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
9697
</div>
98+
{BeforeInput}
9799
<RenderFields
98100
fields={fields}
99101
margins="small"
@@ -105,6 +107,7 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => {
105107
/>
106108
</div>
107109
</GroupProvider>
110+
{AfterInput}
108111
</div>
109112
)
110113
}

packages/ui/src/fields/Number/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
149149
CustomComponent={Error}
150150
Fallback={<FieldError path={path} showError={showError} />}
151151
/>
152+
{BeforeInput}
152153
{hasMany ? (
153154
<ReactSelect
154155
className={`field-${path.replace(/\./g, '__')}`}
@@ -177,7 +178,6 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
177178
/>
178179
) : (
179180
<div>
180-
{BeforeInput}
181181
<input
182182
disabled={readOnly}
183183
id={`field-${path.replace(/\./g, '__')}`}
@@ -194,9 +194,9 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
194194
type="number"
195195
value={typeof value === 'number' ? value : ''}
196196
/>
197-
{AfterInput}
198197
</div>
199198
)}
199+
{AfterInput}
200200
<RenderCustomComponent
201201
CustomComponent={Description}
202202
Fallback={<FieldDescription description={description} path={path} />}

packages/ui/src/fields/RadioGroup/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => {
5252
)
5353

5454
const {
55-
customComponents: { Description, Error, Label } = {},
55+
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
5656
setValue,
5757
showError,
5858
value: valueFromContext,
@@ -90,6 +90,7 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => {
9090
}
9191
/>
9292
<div className={`${fieldBaseClass}__wrap`}>
93+
{BeforeInput}
9394
<ul className={`${baseClass}--group`} id={`field-${path.replace(/\./g, '__')}`}>
9495
{options.map((option) => {
9596
let optionValue = ''
@@ -127,6 +128,7 @@ const RadioGroupFieldComponent: RadioFieldClientComponent = (props) => {
127128
)
128129
})}
129130
</ul>
131+
{AfterInput}
130132
<RenderCustomComponent
131133
CustomComponent={Description}
132134
Fallback={<FieldDescription description={description} path={path} />}

packages/ui/src/fields/Relationship/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
100100
)
101101

102102
const {
103-
customComponents: { Description, Error, Label } = {},
103+
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
104104
filterOptions,
105105
initialValue,
106106
setValue,
@@ -603,6 +603,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
603603
CustomComponent={Error}
604604
Fallback={<FieldError path={path} showError={showError} />}
605605
/>
606+
{BeforeInput}
606607
{!errorLoading && (
607608
<div className={`${baseClass}__wrap`}>
608609
<ReactSelect
@@ -707,6 +708,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) =>
707708
</div>
708709
)}
709710
{errorLoading && <div className={`${baseClass}__error-loading`}>{errorLoading}</div>}
711+
{AfterInput}
710712
<RenderCustomComponent
711713
CustomComponent={Description}
712714
Fallback={<FieldDescription description={description} path={path} />}

test/admin/collections/CustomFields/index.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,119 @@ export const CustomFields: CollectionConfig = {
7373
},
7474
},
7575
},
76+
{
77+
name: 'relationshipFieldWithBeforeAfterInputs',
78+
type: 'relationship',
79+
admin: {
80+
components: {
81+
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
82+
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
83+
},
84+
},
85+
relationTo: 'posts',
86+
},
87+
{
88+
name: 'arrayFieldWithBeforeAfterInputs',
89+
type: 'array',
90+
admin: {
91+
components: {
92+
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
93+
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
94+
},
95+
},
96+
fields: [
97+
{
98+
name: 'someTextField',
99+
type: 'text',
100+
},
101+
],
102+
},
103+
{
104+
name: 'blocksFieldWithBeforeAfterInputs',
105+
type: 'blocks',
106+
admin: {
107+
components: {
108+
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
109+
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
110+
},
111+
},
112+
blocks: [
113+
{
114+
slug: 'blockFields',
115+
fields: [
116+
{
117+
name: 'textField',
118+
type: 'text',
119+
},
120+
],
121+
},
122+
],
123+
},
124+
{
125+
label: 'Collapsible Field With Before & After Inputs',
126+
type: 'collapsible',
127+
admin: {
128+
components: {
129+
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
130+
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
131+
},
132+
description: 'This is a collapsible field.',
133+
initCollapsed: false,
134+
},
135+
fields: [
136+
{
137+
name: 'text',
138+
type: 'text',
139+
},
140+
],
141+
},
142+
{
143+
name: 'groupFieldWithBeforeAfterInputs',
144+
type: 'group',
145+
admin: {
146+
components: {
147+
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
148+
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
149+
},
150+
},
151+
fields: [
152+
{
153+
name: 'textOne',
154+
type: 'text',
155+
},
156+
{
157+
name: 'textTwo',
158+
type: 'text',
159+
},
160+
],
161+
},
162+
{
163+
name: 'radioFieldWithBeforeAfterInputs',
164+
label: {
165+
en: 'Radio en',
166+
es: 'Radio es',
167+
},
168+
type: 'radio',
169+
admin: {
170+
components: {
171+
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
172+
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
173+
},
174+
},
175+
options: [
176+
{
177+
label: { en: 'Value One', es: 'Value Uno' },
178+
value: 'one',
179+
},
180+
{
181+
label: 'Value Two',
182+
value: 'two',
183+
},
184+
{
185+
label: 'Value Three',
186+
value: 'three',
187+
},
188+
],
189+
},
76190
],
77191
}

0 commit comments

Comments
 (0)