Skip to content

Commit b850452

Browse files
authored
Preview: Side panel (stable, CMS) (#2411)
* Update Preview for Side Editor * Add mention of beta * Remove beta mention of side panel for Preview * Add updated badge * Update for latest iteration * Fix conflicts * Fix broken anchors * Update anchor
1 parent 87e9896 commit b850452

File tree

2 files changed

+139
-5
lines changed

2 files changed

+139
-5
lines changed

docusaurus/docs/cms/features/preview.md

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,142 @@ export async function GET(request: Request) {
291291

292292
On the Strapi side, [the `allowedOrigins` configuration parameter](#allowed-origins) allows the admin panel to load the front-end window in an iframe. But allowing the embedding works both ways, so on the front-end side, you also need to allow the window to be embedded in Strapi's admin panel.
293293

294-
This requires the front-end application to have its own header directive, the CSP `frame-ancestors` directive. Setting this directive up depends on how your website is built. For instance, setting this up in Next.js requires a middleware configuration (see <ExternalLink to="https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy" text="Next.js docs"/>).
294+
This requires the front-end application to have its own header directive, the CSP `frame-ancestors` directive. Setting this directive up depends on how your website is built. For instance, setting this up in Next.js requires a middleware configuration (see [Next.js docs](https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy)).
295+
296+
#### 6. [Front end] Detect changes in Strapi and refresh the front-end {#6-refresh-frontend}
297+
298+
Strapi emits a `strapiUpdate` message to inform the front end that data has changed.
299+
300+
To track this, within your front-end application, add an event listener to listen to events posted through [the `postMessage()` API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) on the `window` object. The listener needs to filter through messages and react only to Strapi-initiated messages, then refresh the iframe content.
301+
302+
With Next.js, the recommended way to refresh the iframe content is with <ExternalLink to="https://nextjs.org/docs/app/building-your-application/caching#routerrefresh" text="the `router.refresh()` method" />.
303+
304+
<Tabs groupId="js-ts">
305+
<TabItem value="js" label="JavaScript" >
306+
307+
```tsx title="next/app/path/to/your/front/end/logic.jsx" {6-17}
308+
export default function MyClientComponent({...props}) {
309+
//
310+
const router = useRouter();
311+
312+
useEffect(() => {
313+
const handleMessage = async (message) => {
314+
if (
315+
// Filters events emitted through the postMessage() API
316+
message.origin === process.env.NEXT_PUBLIC_API_URL &&
317+
message.data.type === "strapiUpdate"
318+
) { // Recommended way to refresh with Next.js
319+
router.refresh();
320+
}
321+
};
322+
323+
// Add the event listener
324+
window.addEventListener("message", handleMessage);
325+
326+
// Cleanup the event listener on unmount
327+
return () => {
328+
window.removeEventListener("message", handleMessage);
329+
};
330+
}, [router]);
331+
332+
// ...
333+
}
334+
```
335+
336+
</TabItem>
337+
<TabItem value="ts" label="TypeScript" >
338+
339+
```tsx title="next/app/path/to/your/front/end/logic.tsx" {6-17}
340+
export default function MyClientComponent({
341+
//
342+
const router = useRouter();
343+
344+
useEffect(() => {
345+
const handleMessage = async (message: MessageEvent<any>) => {
346+
if (
347+
// Filters events emitted through the postMessage() API
348+
message.origin === process.env.NEXT_PUBLIC_API_URL &&
349+
message.data.type === "strapiUpdate"
350+
) { // Recommended way to refresh with Next.js
351+
router.refresh();
352+
}
353+
};
354+
355+
// Add the event listener
356+
window.addEventListener("message", handleMessage);
357+
358+
// Cleanup the event listener on unmount
359+
return () => {
360+
window.removeEventListener("message", handleMessage);
361+
};
362+
}, [router]);
363+
364+
//
365+
})
366+
```
367+
368+
</TabItem>
369+
370+
</Tabs>
371+
372+
<details>
373+
<summary>Caching in Next.js:</summary>
374+
375+
In Next.js, [cache persistence](https://nextjs.org/docs/app/building-your-application/caching) may require additional steps. You might need to invalidate the cache by making an API call from the client side to the server, where the revalidation logic will be handled. Please refer to Next.js documentation for details, for instance with the [revalidatePath() method](https://nextjs.org/docs/app/building-your-application/caching#revalidatepath).
376+
<br/>
377+
378+
</details>
379+
380+
#### [Front end] Next steps
381+
382+
Once the preview system is set up, you need to adapt your data fetching logic to handle draft content appropriately. This involves the following steps:
383+
384+
1. Create or adapt your data fetching utility to check if draft mode is enabled
385+
2. Update your API calls to include the draft status parameter when appropriate
386+
387+
The following, taken from the <ExternalLink to="https://github.com/strapi/LaunchPad/tree/feat/preview" text="Launchpad" /> Strapi demo application, is an example of how to implement draft-aware data fetching in your Next.js front-end application:
388+
389+
```typescript {8-18}
390+
import { draftMode } from "next/headers";
391+
import qs from "qs";
392+
393+
export default async function fetchContentType(
394+
contentType: string,
395+
params: Record = {}
396+
): Promise {
397+
// Check if Next.js draft mode is enabled
398+
const { isEnabled: isDraftMode } = draftMode();
399+
400+
try {
401+
const queryParams = { ...params };
402+
// Add status=draft parameter when draft mode is enabled
403+
if (isDraftMode) {
404+
queryParams.status = "draft";
405+
}
406+
407+
const url = `${baseURL}/${contentType}?${qs.stringify(queryParams)}`;
408+
const response = await fetch(url);
409+
if (!response.ok) {
410+
throw new Error(
411+
`Failed to fetch data from Strapi (url=${url}, status=${response.status})`
412+
);
413+
}
414+
return await response.json();
415+
} catch (error) {
416+
console.error("Error fetching content:", error);
417+
throw error;
418+
}
419+
}
420+
```
421+
422+
This utility method can then be used in your page components to fetch either draft or published content based on the preview state:
423+
424+
```typescript
425+
// In your page component:
426+
const pageData = await fetchContentType('api::page.page', {
427+
// Your other query parameters
428+
});
429+
```
295430

296431
#### 6. [Front end] Detect changes in Strapi and refresh the front-end {#6-refresh-frontend}
297432

@@ -459,10 +594,6 @@ Additionally, you can:
459594
- with <GrowthBadge /> and <EnterpriseBadge /> plans, expand the side panel by clicking on the <Icon name="arrow-line-left" classes="ph-bold" /> button to make the Preview full screen,
460595
- and, with the <EnterpriseBadge /> plan, use buttons at the top right of the editor to define the assignee and stage for the [Review Workflows feature](/cms/features/review-workflows) if enabled.
461596

462-
:::info
463-
Please note that the side panel for Preview is currently in beta and only accessible if you installed strapi with the beta flag, with the following command: `npx create-strapi@beta`.
464-
:::
465-
466597
:::note
467598
In the Edit view of the Content Manager, the Open preview button will be disabled if you have unsaved changes. Save your latest changes and you should be able to preview content again.
468599
:::

docusaurus/sidebars.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ const sidebars = {
8888
type: 'doc',
8989
label: 'Preview',
9090
id: 'cms/features/preview',
91+
customProps: {
92+
updated: true
93+
}
9194
},
9295
{
9396
type: 'doc',

0 commit comments

Comments
 (0)