Skip to content

Commit 54dadbe

Browse files
committed
feat: preview now exposes most recent draft data
1 parent 2a8bc31 commit 54dadbe

File tree

6 files changed

+116
-89
lines changed

6 files changed

+116
-89
lines changed

docs/configuration/globals.mdx

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ desc: Set up your Global config for your needs by defining fields, adding slugs
66
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
77
---
88

9-
Global configs are in many ways similar to [Collections](/docs/configuration/collections). The big difference is that Collections will potentially contain *many* documents, while a Global is a "one-off". Globals are perfect for things like header nav, site-wide banner alerts, app-wide localized strings, and other "global" data that your site or app might rely on.
9+
Global configs are in many ways similar to [Collections](/docs/configuration/collections). The big difference is that Collections will potentially contain _many_ documents, while a Global is a "one-off". Globals are perfect for things like header nav, site-wide banner alerts, app-wide localized strings, and other "global" data that your site or app might rely on.
1010

1111
As with Collection configs, it's often best practice to write your Globals in separate files and then import them into the main Payload config.
1212

1313
## Options
1414

1515
| Option | Description |
16-
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
17-
| **`slug`** * | Unique, URL-friendly string that will act as an identifier for this Global. |
18-
| **`fields`** * | Array of field types that will determine the structure and functionality of the data stored within this Global. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
16+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17+
| **`slug`** \* | Unique, URL-friendly string that will act as an identifier for this Global. |
18+
| **`fields`** \* | Array of field types that will determine the structure and functionality of the data stored within this Global. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
1919
| **`label`** | Text for the name in the Admin panel or an object with keys for each language. Auto-generated from slug if not defined. |
2020
| **`description`** | Text or React component to display below the Global header to give editors more information. |
2121
| **`admin`** | Admin-specific configuration. See below for [more detail](/docs/configuration/globals#admin-options). |
@@ -24,33 +24,33 @@ As with Collection configs, it's often best practice to write your Globals in se
2424
| **`versions`** | Set to true to enable default options, or configure with object properties. [More](/docs/versions/overview#globals-config) |
2525
| **`endpoints`** | Add custom routes to the REST API. [More](/docs/rest-api/overview#custom-endpoints) |
2626
| **`graphQL.name`** | Text used in schema generation. Auto-generated from slug if not defined. |
27-
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
27+
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
2828

29-
*\* An asterisk denotes that a property is required.*
29+
_\* An asterisk denotes that a property is required._
3030

3131
#### Simple Global example
3232

3333
```ts
34-
import { GlobalConfig } from 'payload/types';
34+
import { GlobalConfig } from "payload/types";
3535

3636
const Nav: GlobalConfig = {
37-
slug: 'nav',
38-
fields: [
39-
{
40-
name: 'items',
41-
type: 'array',
42-
required: true,
43-
maxRows: 8,
44-
fields: [
45-
{
46-
name: 'page',
47-
type: 'relationship',
48-
relationTo: 'pages', // "pages" is the slug of an existing collection
49-
required: true,
50-
}
51-
]
52-
},
53-
]
37+
slug: "nav",
38+
fields: [
39+
{
40+
name: "items",
41+
type: "array",
42+
required: true,
43+
maxRows: 8,
44+
fields: [
45+
{
46+
name: "page",
47+
type: "relationship",
48+
relationTo: "pages", // "pages" is the slug of an existing collection
49+
required: true,
50+
},
51+
],
52+
},
53+
],
5454
};
5555

5656
export default Nav;
@@ -64,9 +64,47 @@ You can find an [example Global config](https://github.com/payloadcms/public-dem
6464

6565
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a Global's config.
6666

67-
| Option | Description |
68-
| ---------------------------- | -------------|
69-
| `components` | Swap in your own React components to be used within this Global. [More](/docs/admin/components#globals) |
67+
| Option | Description |
68+
| ------------ | ----------------------------------------------------------------------------------------------------------------------- |
69+
| `components` | Swap in your own React components to be used within this Global. [More](/docs/admin/components#globals) |
70+
| `preview` | Function to generate a preview URL within the Admin panel for this global that can point to your app. [More](#preview). |
71+
72+
### Preview
73+
74+
Global `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend of your app to preview data.
75+
76+
If the function is specified, a Preview button will automatically appear in the corresponding global's Edit view. Clicking the Preview button will link to the URL that is generated by the function.
77+
78+
**The preview function accepts two arguments:**
79+
80+
1. The document being edited
81+
1. An `options` object, containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT.
82+
83+
**Example global with preview function:**
84+
85+
```ts
86+
import { GlobalConfig } from "payload/types";
87+
88+
const MyGlobal: CollectionConfig = {
89+
slug: "my-global",
90+
fields: [
91+
{
92+
name: "slug",
93+
type: "text",
94+
required: true,
95+
},
96+
],
97+
admin: {
98+
preview: (doc, { locale }) => {
99+
if (doc?.slug) {
100+
return `https://bigbird.com/preview/${doc.slug}?locale=${locale}`;
101+
}
102+
103+
return null;
104+
},
105+
},
106+
};
107+
```
70108

71109
### Access control
72110

@@ -85,14 +123,14 @@ Globals support all field types that Payload has to offer—including simple fie
85123
You can import global types as follows:
86124

87125
```ts
88-
import { GlobalConfig } from 'payload/types';
126+
import { GlobalConfig } from "payload/types";
89127

90128
// This is the type used for incoming global configs.
91129
// Only the bare minimum properties are marked as required.
92130
```
93131

94132
```ts
95-
import { SanitizedGlobalConfig } from 'payload/types';
133+
import { SanitizedGlobalConfig } from "payload/types";
96134

97135
// This is the type used after an incoming global config is fully sanitized.
98136
// Generally, this is only used internally by Payload.
Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useCallback, useState } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { useAuth } from '../../utilities/Auth';
44
import Button from '../Button';
55
import { Props } from './types';
66
import { useLocale } from '../../utilities/Locale';
7+
import { useDocumentInfo } from '../../utilities/DocumentInfo';
8+
import { useConfig } from '../../utilities/Config';
79

810
import './index.scss';
911

@@ -12,46 +14,40 @@ const baseClass = 'preview-btn';
1214
const PreviewButton: React.FC<Props> = (props) => {
1315
const {
1416
generatePreviewURL,
15-
data,
1617
} = props;
1718

18-
const [url, setUrl] = useState<string | undefined>(undefined);
19+
const { id, collection, global } = useDocumentInfo();
1920

21+
const [isLoading, setIsLoading] = useState(false);
2022
const locale = useLocale();
2123
const { token } = useAuth();
24+
const { serverURL, routes: { api } } = useConfig();
2225
const { t } = useTranslation('version');
2326

24-
useEffect(() => {
25-
if (generatePreviewURL && typeof generatePreviewURL === 'function') {
26-
const makeRequest = async () => {
27-
const previewURL = await generatePreviewURL(data, { locale, token });
28-
setUrl(previewURL);
29-
};
30-
31-
makeRequest();
32-
}
33-
}, [
34-
generatePreviewURL,
35-
locale,
36-
token,
37-
data,
38-
]);
39-
40-
if (url) {
41-
return (
42-
<Button
43-
el="anchor"
44-
className={baseClass}
45-
buttonStyle="secondary"
46-
url={url}
47-
newTab
48-
>
49-
{t('preview')}
50-
</Button>
51-
);
52-
}
53-
54-
return null;
27+
const handleClick = useCallback(async () => {
28+
setIsLoading(true);
29+
30+
let url = `${serverURL}${api}`;
31+
if (collection) url = `${url}/${collection.slug}/${id}`;
32+
if (global) url = `${url}/globals/${global.slug}`;
33+
34+
const data = await fetch(`${url}?draft=true&locale=${locale}&fallback-locale=null`).then((res) => res.json());
35+
const previewURL = await generatePreviewURL(data, { locale, token });
36+
setIsLoading(false);
37+
38+
window.open(previewURL, '_blank');
39+
}, [serverURL, api, collection, global, id, generatePreviewURL, locale, token]);
40+
41+
return (
42+
<Button
43+
className={baseClass}
44+
buttonStyle="secondary"
45+
onClick={handleClick}
46+
disabled={isLoading}
47+
>
48+
{isLoading ? t('general:loading') : t('preview')}
49+
</Button>
50+
);
5551
};
5652

5753
export default PreviewButton;
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { Data } from '../../forms/Form/types';
21
import { GeneratePreviewURL } from '../../../../config/types';
32

43
export type Props = {
54
generatePreviewURL?: GeneratePreviewURL,
6-
data?: Data
75
}

src/admin/components/views/Account/Default.tsx

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const DefaultAccount: React.FC<Props> = (props) => {
8585
/>
8686
<Eyebrow />
8787
{!(collection.versions?.drafts && collection.versions?.drafts?.autosave) && (
88-
<LeaveWithoutSaving />
88+
<LeaveWithoutSaving />
8989
)}
9090
<div className={`${baseClass}__edit`}>
9191
<Gutter className={`${baseClass}__header`}>
@@ -129,18 +129,17 @@ const DefaultAccount: React.FC<Props> = (props) => {
129129
<div className={`${baseClass}__sidebar-sticky-wrap`}>
130130
<ul className={`${baseClass}__collection-actions`}>
131131
{(permissions?.create?.permission) && (
132-
<React.Fragment>
133-
<li><Link to={`${admin}/collections/${slug}/create`}>{t('general:createNew')}</Link></li>
134-
</React.Fragment>
132+
<React.Fragment>
133+
<li><Link to={`${admin}/collections/${slug}/create`}>{t('general:createNew')}</Link></li>
134+
</React.Fragment>
135135
)}
136136
</ul>
137137
<div className={`${baseClass}__document-actions${preview ? ` ${baseClass}__document-actions--with-preview` : ''}`}>
138138
<PreviewButton
139139
generatePreviewURL={preview}
140-
data={data}
141140
/>
142141
{hasSavePermission && (
143-
<FormSubmit buttonId="action-save">{t('general:save')}</FormSubmit>
142+
<FormSubmit buttonId="action-save">{t('general:save')}</FormSubmit>
144143
)}
145144
</div>
146145
<div className={`${baseClass}__sidebar-fields`}>
@@ -172,20 +171,20 @@ const DefaultAccount: React.FC<Props> = (props) => {
172171
<div>{data?.id}</div>
173172
</li>
174173
{timestamps && (
175-
<React.Fragment>
176-
{data.updatedAt && (
177-
<li>
178-
<div className={`${baseClass}__label`}>{t('general:lastModified')}</div>
179-
<div>{format(new Date(data.updatedAt), dateFormat)}</div>
180-
</li>
181-
)}
182-
{data.createdAt && (
183-
<li>
184-
<div className={`${baseClass}__label`}>{t('general:created')}</div>
185-
<div>{format(new Date(data.createdAt), dateFormat)}</div>
186-
</li>
187-
)}
188-
</React.Fragment>
174+
<React.Fragment>
175+
{data.updatedAt && (
176+
<li>
177+
<div className={`${baseClass}__label`}>{t('general:lastModified')}</div>
178+
<div>{format(new Date(data.updatedAt), dateFormat)}</div>
179+
</li>
180+
)}
181+
{data.createdAt && (
182+
<li>
183+
<div className={`${baseClass}__label`}>{t('general:created')}</div>
184+
<div>{format(new Date(data.createdAt), dateFormat)}</div>
185+
</li>
186+
)}
187+
</React.Fragment>
189188
)}
190189

191190
</ul>

src/admin/components/views/Global/Default.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
102102
{(preview && (!global.versions?.drafts || global.versions?.drafts?.autosave)) && (
103103
<PreviewButton
104104
generatePreviewURL={preview}
105-
data={data}
106105
/>
107106
)}
108107
{hasSavePermission && (
@@ -125,7 +124,6 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
125124
{(preview && (global.versions?.drafts && !global.versions?.drafts?.autosave)) && (
126125
<PreviewButton
127126
generatePreviewURL={preview}
128-
data={data}
129127
/>
130128
)}
131129
{global.versions?.drafts && (

src/admin/components/views/collections/Edit/Default.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ const DefaultEditView: React.FC<Props> = (props) => {
189189
{(preview && (!collection.versions?.drafts || collection.versions?.drafts?.autosave)) && (
190190
<PreviewButton
191191
generatePreviewURL={preview}
192-
data={data}
193192
/>
194193
)}
195194
{hasSavePermission && (
@@ -212,7 +211,6 @@ const DefaultEditView: React.FC<Props> = (props) => {
212211
{(isEditing && preview && (collection.versions?.drafts && !collection.versions?.drafts?.autosave)) && (
213212
<PreviewButton
214213
generatePreviewURL={preview}
215-
data={data}
216214
/>
217215
)}
218216
{collection.versions?.drafts && (

0 commit comments

Comments
 (0)