Skip to content

Commit 56da847

Browse files
authored
Merge pull request #917 from NWACus/revert-nav-link
Undo nav link refactor
2 parents 6458a9e + 9390f1d commit 56da847

3 files changed

Lines changed: 153 additions & 144 deletions

File tree

src/fields/linkField.ts

Lines changed: 73 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -12,138 +12,90 @@ const validateLabel: TextFieldSingleValidation = (val, args) => {
1212
return Boolean(val) ? text(val, args) : 'You must define a label for an external link.'
1313
}
1414

15-
type LinkFieldsOptions = {
16-
includeLabel?: boolean
17-
/** When true, newTab checkbox only shows for external links and defaults to true */
18-
newTabForExternalOnly?: boolean
19-
/** Custom admin Description component path for the label field */
20-
labelDescriptionComponent?: string
21-
}
22-
23-
type LinkFieldOptions = LinkFieldsOptions & {
24-
fieldName?: string
25-
}
26-
27-
/**
28-
* Builds the raw link fields array.
29-
* Used internally by linkField and exported as linkFields for array contexts.
30-
*/
31-
const buildLinkFields = ({
32-
includeLabel = false,
33-
newTabForExternalOnly = false,
34-
labelDescriptionComponent,
35-
}: LinkFieldsOptions = {}): Field[] => {
36-
const newTabField: Field = {
37-
name: 'newTab',
38-
type: 'checkbox',
39-
admin: {
40-
...(newTabForExternalOnly
41-
? {
42-
condition: (_, siblingData) => siblingData?.type === 'external',
43-
}
44-
: {
45-
style: {
46-
alignSelf: 'flex-end',
47-
alignItems: 'flex-end',
48-
marginBottom: '4px',
49-
},
50-
width: '50%',
51-
}),
15+
const linkReferenceRow = (includeLabel = false): Field[] => {
16+
const fields: Field[] = [
17+
{
18+
name: 'reference',
19+
type: 'relationship',
20+
admin: {
21+
condition: (_, siblingData) => siblingData?.type === 'internal',
22+
width: '50%',
23+
},
24+
label: 'Select page or post',
25+
relationTo: ['pages', 'builtInPages', 'posts'],
26+
required: true,
27+
filterOptions: getTenantFilter,
5228
},
53-
...(newTabForExternalOnly ? { defaultValue: true } : {}),
54-
label: 'Open in new tab',
55-
}
56-
57-
const referenceField: Field = {
58-
name: 'reference',
59-
type: 'relationship',
60-
admin: {
61-
condition: (_, siblingData) => siblingData?.type === 'internal',
62-
width: '50%',
29+
{
30+
name: 'url',
31+
type: 'text',
32+
admin: {
33+
condition: (_, siblingData) => siblingData?.type === 'external',
34+
width: '100%',
35+
},
36+
label: 'External URL',
37+
validate: validateExternalUrl,
6338
},
64-
label: 'Select page or post',
65-
relationTo: ['pages', 'builtInPages', 'posts'],
66-
required: true,
67-
filterOptions: getTenantFilter,
68-
}
39+
]
6940

70-
const urlField: Field = {
71-
name: 'url',
72-
type: 'text',
73-
admin: {
74-
condition: (_, siblingData) => siblingData?.type === 'external',
75-
width: '100%',
76-
},
77-
label: 'External URL',
78-
validate: validateExternalUrl,
41+
if (includeLabel) {
42+
fields.push({
43+
name: 'label',
44+
type: 'text',
45+
admin: { width: '50%' },
46+
label: 'Label',
47+
validate: validateLabel,
48+
})
7949
}
8050

81-
const labelField: Field = {
82-
name: 'label',
83-
type: 'text',
84-
admin: {
85-
width: '50%',
86-
...(labelDescriptionComponent
87-
? {
88-
components: {
89-
Description: labelDescriptionComponent,
90-
},
91-
}
92-
: {}),
93-
},
94-
label: 'Label',
95-
validate: validateLabel,
96-
}
51+
return fields
52+
}
9753

98-
return [
99-
{
100-
type: 'row',
101-
fields: [
102-
{
103-
name: 'type',
104-
type: 'radio',
105-
admin: {
106-
layout: 'horizontal',
107-
width: '50%',
54+
export const linkToPageOrPost = (includeLabel = false): Field[] => [
55+
{
56+
type: 'row',
57+
fields: [
58+
{
59+
name: 'type',
60+
type: 'radio',
61+
admin: {
62+
layout: 'horizontal',
63+
width: '50%',
64+
},
65+
defaultValue: 'internal',
66+
options: [
67+
{ label: 'Internal link', value: 'internal' },
68+
{ label: 'External link', value: 'external' },
69+
],
70+
},
71+
{
72+
name: 'newTab',
73+
type: 'checkbox',
74+
admin: {
75+
style: {
76+
alignSelf: 'flex-end',
77+
alignItems: 'flex-end',
78+
marginBottom: '4px',
10879
},
109-
defaultValue: 'internal',
110-
options: [
111-
{ label: 'Internal link', value: 'internal' },
112-
{ label: 'External link', value: 'external' },
113-
],
80+
width: '50%',
11481
},
115-
...(newTabForExternalOnly ? [] : [newTabField]),
116-
],
117-
},
118-
{
119-
type: 'row',
120-
fields: [referenceField, urlField, ...(includeLabel ? [labelField] : [])],
121-
},
122-
...(newTabForExternalOnly ? [newTabField] : []),
123-
]
124-
}
82+
label: 'Open in new tab',
83+
},
84+
],
85+
},
86+
{
87+
type: 'row',
88+
fields: linkReferenceRow(includeLabel),
89+
},
90+
]
12591

126-
/**
127-
* Creates a link group field with configurable options.
128-
*
129-
* @example
130-
* // Basic usage
131-
* linkField()
132-
*
133-
* @example
134-
* // With custom field name and label
135-
* linkField({ fieldName: 'button', includeLabel: true })
136-
*
137-
* @example
138-
* // Navigation-style link (newTab only for external)
139-
* linkField({ includeLabel: true, newTabForExternalOnly: true })
140-
*/
14192
export const linkField = ({
14293
fieldName = 'link',
14394
includeLabel = false,
144-
newTabForExternalOnly = false,
145-
labelDescriptionComponent,
146-
}: LinkFieldOptions = {}): NamedGroupField => ({
95+
}: {
96+
fieldName?: string
97+
includeLabel?: boolean
98+
} = {}): NamedGroupField => ({
14799
name: fieldName,
148100
type: 'group',
149101
admin: {
@@ -152,19 +104,5 @@ export const linkField = ({
152104
hooks: {
153105
beforeChange: [clearIrrelevantLinkValues],
154106
},
155-
fields: buildLinkFields({ includeLabel, newTabForExternalOnly, labelDescriptionComponent }),
107+
fields: linkToPageOrPost(includeLabel),
156108
})
157-
158-
/**
159-
* Returns the raw link fields array for use in array field contexts.
160-
* Use this when you need the fields directly on array items without a group wrapper.
161-
*
162-
* @example
163-
* // In an array field
164-
* {
165-
* name: 'quickLinks',
166-
* type: 'array',
167-
* fields: linkFields(true), // includeLabel
168-
* }
169-
*/
170-
export const linkFields = (includeLabel = false): Field[] => buildLinkFields({ includeLabel })

src/fields/navLink/index.ts

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,79 @@
1-
import { linkField } from '@/fields/linkField'
1+
import { clearIrrelevantLinkValues } from '@/utilities/clearIrrelevantLinkValues'
2+
import { getTenantFilter } from '@/utilities/collectionFilters'
3+
import { validateExternalUrl } from '@/utilities/validateUrl'
4+
import { NamedGroupField, TextFieldSingleValidation } from 'payload'
25

3-
export const navLink = linkField({
4-
includeLabel: true,
5-
newTabForExternalOnly: true,
6-
labelDescriptionComponent:
7-
'@/fields/navLink/components/LinkLabelDescription#LinkLabelDescription',
8-
})
6+
const validateLabel: TextFieldSingleValidation = (val, { siblingData }) => {
7+
if (siblingData && typeof siblingData === 'object' && 'type' in siblingData) {
8+
if (siblingData.type === 'internal') return true
9+
}
10+
11+
return Boolean(val) || 'You must define a label for an external link.'
12+
}
13+
14+
export const navLink: NamedGroupField = {
15+
name: 'link',
16+
type: 'group',
17+
admin: {
18+
hideGutter: true,
19+
},
20+
hooks: {
21+
beforeChange: [clearIrrelevantLinkValues],
22+
},
23+
fields: [
24+
{
25+
name: 'type',
26+
type: 'radio',
27+
defaultValue: 'internal',
28+
options: [
29+
{
30+
label: 'Internal link',
31+
value: 'internal',
32+
},
33+
{
34+
label: 'External link',
35+
value: 'external',
36+
},
37+
],
38+
},
39+
{
40+
name: 'reference',
41+
type: 'relationship',
42+
admin: {
43+
condition: (_, siblingData) => siblingData?.type === 'internal',
44+
},
45+
label: 'Select page or post',
46+
relationTo: ['pages', 'builtInPages', 'posts'],
47+
filterOptions: getTenantFilter,
48+
},
49+
{
50+
name: 'url',
51+
type: 'text',
52+
admin: {
53+
condition: (_, siblingData) => siblingData?.type === 'external',
54+
},
55+
label: 'External URL',
56+
validate: validateExternalUrl,
57+
},
58+
{
59+
name: 'label',
60+
type: 'text',
61+
admin: {
62+
components: {
63+
Description: '@/fields/navLink/components/LinkLabelDescription#LinkLabelDescription',
64+
},
65+
},
66+
label: 'Label',
67+
validate: validateLabel,
68+
},
69+
{
70+
name: 'newTab',
71+
type: 'checkbox',
72+
defaultValue: true,
73+
admin: {
74+
condition: (_, siblingData) => siblingData?.type === 'external',
75+
},
76+
label: 'Open in new tab',
77+
},
78+
],
79+
}

src/fields/quickLinksFields.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { ArrayField } from 'payload'
2-
import { linkFields } from './linkField'
2+
import { linkToPageOrPost } from './linkField'
33

44
export const quickLinksField = ({ description }: { description?: string }): ArrayField => ({
55
name: 'quickLinks',
66
type: 'array',
77
admin: {
88
description,
99
},
10-
fields: linkFields(true),
10+
fields: linkToPageOrPost(true),
1111
})

0 commit comments

Comments
 (0)