Skip to content

Commit

Permalink
Merge branch 'master' of github.com:storyofams/storyblok-toolkit into…
Browse files Browse the repository at this point in the history
… beta
  • Loading branch information
BJvdA committed May 6, 2021
2 parents 8a7bac6 + 9d109c3 commit de45de6
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 10 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"dependencies": {
"fast-deep-equal": "^3.1.3",
"intersection-observer": "^0.12.0",
"qs": "^6.10.1",
"storyblok-rich-text-react-renderer": "^2.1.1"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion src/bridge/withStory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const withStory = <T extends WithStoryProps = WithStoryProps>(
if (
(props as any)?.__storyblok_toolkit_preview &&
typeof window !== 'undefined' &&
!window.storyblok?.isInEditor()
(!window.storyblok || !window.storyblok.isInEditor())
) {
setPreview(true);
}
Expand All @@ -44,6 +44,7 @@ export const withStory = <T extends WithStoryProps = WithStoryProps>(
backgroundColor: '#111',
color: '#fff',
boxShadow: '0px 12px 24px rgb(102, 102, 102, 0.25)',
zIndex: 9999999,
}}
>
<div
Expand Down
32 changes: 31 additions & 1 deletion src/next/__tests__/previewHandlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,42 @@ describe('[next] nextPreviewHandlers', () => {
expect(setPreviewDataMock).not.toBeCalled();
});

it('should enable preview mode and redirect if disable story check', async () => {
server.use(
rest.get(
`https://api.storyblok.com/v1/cdn/stories/${slug}`,
async (_, res, ctx) => {
return res(ctx.status(404), ctx.json({}));
},
),
);

const setPreviewDataMock = jest.fn();
EventEmitter.prototype.setPreviewData = setPreviewDataMock;

const { req, res } = createMocks(
{ method: 'GET', query: { slug, token: previewToken, anything: true } },
{
eventEmitter: EventEmitter,
},
);

await nextPreviewHandlers({
disableStoryCheck: true,
previewToken,
storyblokToken,
})(req as any, res as any);

expect(res._getRedirectUrl()).toBe(`/${slug}?anything=true`);
expect(setPreviewDataMock).toBeCalledWith({});
});

it('should exit preview mode on clear route', async () => {
const clearPreviewData = jest.fn();
EventEmitter.prototype.clearPreviewData = clearPreviewData;

const { req, res } = createMocks(
{ method: 'GET', query: { slug: ['clear'] } },
{ method: 'GET', query: { handle: ['clear'] } },
{
eventEmitter: EventEmitter,
},
Expand Down
28 changes: 23 additions & 5 deletions src/next/previewHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import qs from 'qs';

interface NextPreviewHandlersProps {
/**
* Disable checking if a story with slug exists
*
* @default false
*/
disableStoryCheck?: boolean;
/**
* A secret token (random string of characters) to activate preview mode.
*/
Expand All @@ -12,26 +19,37 @@ interface NextPreviewHandlersProps {
}

export const nextPreviewHandlers = ({
disableStoryCheck,
previewToken,
storyblokToken,
}: NextPreviewHandlersProps) => async (
req: NextApiRequest,
res: NextApiResponse,
) => {
if (req.query.slug?.[0] === 'clear') {
const { token, slug, handle, ...rest } = req.query;

if (handle?.[0] === 'clear') {
res.clearPreviewData();
return res.redirect(req.headers.referer || '/');
}

// Check the secret and next parameters
// This secret should only be known to this API route and the CMS
if (req.query.token !== previewToken || !req.query.slug) {
if (token !== previewToken) {
return res.status(401).json({ message: 'Invalid token' });
}

const restParams =
rest && Object.keys(rest).length ? `?${qs.stringify(rest)}` : '';

if (disableStoryCheck) {
res.setPreviewData({});
return res.redirect(`/${slug}${restParams}`);
}

// Fetch Storyblok to check if the provided `slug` exists
const { story } = await fetch(
`https://api.storyblok.com/v1/cdn/stories/${req.query.slug}?token=${storyblokToken}&version=draft`,
let { story } = await fetch(
`https://api.storyblok.com/v1/cdn/stories/${slug}?token=${storyblokToken}&version=draft`,
{
method: 'GET',
},
Expand All @@ -47,5 +65,5 @@ export const nextPreviewHandlers = ({

// Redirect to the path from the fetched post
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
res.redirect(`/${story.full_slug}`);
res.redirect(`/${story.full_slug}${restParams}`);
};
12 changes: 9 additions & 3 deletions website/docs/api/nextPreviewHandlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ A function that provides API handlers to implement Next.js's preview mode.

```ts no-transpile
interface NextPreviewHandlersProps {
/**
* Disable checking if a story with slug exists
*
* @default false
*/
disableStoryCheck?: boolean;
/**
* A secret token (random string of characters) to activate preview mode.
*/
Expand All @@ -32,7 +38,7 @@ const nextPreviewHandlers: (options: NextPreviewHandlersProps) => (req: NextApiR
### Basic example
Create the file `./pages/api/preview/[[...slug]].ts` with the following contents:
Create the file `./pages/api/preview/[[...handle]].ts` with the following contents:
```ts
import { nextPreviewHandlers } from '@storyofams/storyblok-toolkit';
Expand All @@ -44,10 +50,10 @@ export default nextPreviewHandlers({
```

To open preview mode of a story at `/article/article-1`, go to:
`/api/preview?token=YOUR_PREVIEW_TOKEN&slug=/article/article-1`
`/api/preview?token=YOUR_PREVIEW_TOKEN&slug=article/article-1`

You can configure preview mode as a preview URL in Storyblok:
`YOUR_WEBSITE/api/preview?token=YOUR_PREVIEW_TOKEN&slug=/`
`YOUR_WEBSITE/api/preview?token=YOUR_PREVIEW_TOKEN&slug=`

If you are using the preview handlers and are on a page configured with `withStory`, you will automatically be shown a small indicator to remind you that you are viewing the page in preview mode. It also allows you to exit preview mode. Alternatively you can go to `/api/preview/clear` to exit preview mode.

Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10699,6 +10699,13 @@ qrcode-terminal@^0.12.0:
resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819"
integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==

qs@^6.10.1:
version "6.10.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a"
integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==
dependencies:
side-channel "^1.0.4"

qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
Expand Down

0 comments on commit de45de6

Please sign in to comment.