Skip to content

Commit

Permalink
Merge pull request #29593 from storybookjs/yann/fix-next-13-sandboxes…
Browse files Browse the repository at this point in the history
…-again

Build: Move more stories out of next 13 sandbox
  • Loading branch information
yannbf authored Nov 12, 2024
2 parents 0676c9c + fa533f1 commit 22df58c
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';

import type { Meta, StoryObj } from '@storybook/react';

import { getImageProps } from 'next/image';

import Accessibility from '../../assets/accessibility.svg';
import Testing from '../../assets/testing.png';

// referenced from https://nextjs.org/docs/pages/api-reference/components/image#theme-detection-picture
const Component = (props: any) => {
const {
props: { srcSet: dark },
} = getImageProps({ src: Accessibility, ...props });
const {
// capture rest on one to spread to img as default; it doesn't matter which barring art direction
props: { srcSet: light, ...rest },
} = getImageProps({ src: Testing, ...props });

return (
<picture>
<source media="(prefers-color-scheme: dark)" srcSet={dark} />
<source media="(prefers-color-scheme: light)" srcSet={light} />
<img {...rest} />
</picture>
);
};

export default {
component: Component,
args: {
alt: 'getImageProps Example',
},
} as Meta<typeof Component>;

export const Default: StoryObj<typeof Component> = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { cookies, headers } from '@storybook/nextjs/headers.mock';
import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';

import NextHeader from './NextHeader';

export default {
component: NextHeader,
parameters: {
react: {
rsc: true,
},
},
} as Meta<typeof NextHeader>;

type Story = StoryObj<typeof NextHeader>;

export const Default: Story = {
loaders: async () => {
cookies().set('firstName', 'Jane');
cookies().set({
name: 'lastName',
value: 'Doe',
});
headers().set('timezone', 'Central European Summer Time');
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
const headersMock = headers();
const cookiesMock = cookies();
await step('Cookie and header store apis are called upon rendering', async () => {
await expect(cookiesMock.getAll).toHaveBeenCalled();
await expect(headersMock.entries).toHaveBeenCalled();
});

await step('Upon clicking on submit, the user-id cookie is set', async () => {
const submitButton = await canvas.findByRole('button');
await userEvent.click(submitButton);

await expect(cookiesMock.set).toHaveBeenCalledWith('user-id', 'encrypted-id');
});

await step('The user-id cookie is available in cookie and header stores', async () => {
await expect(headersMock.get('cookie')).toContain('user-id=encrypted-id');
await expect(cookiesMock.get('user-id')).toEqual({
name: 'user-id',
value: 'encrypted-id',
});
});
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';

import { cookies, headers } from 'next/headers';

export default async function Component() {
async function handleClick() {
'use server';
(await cookies()).set('user-id', 'encrypted-id');
}

return (
<>
<h3>Cookies:</h3>
{(await cookies()).getAll().map(({ name, value }) => {
return (
<p key={name} style={{ display: 'flex', flexDirection: 'row', gap: 8 }}>
<strong>Name:</strong> <span>{name}</span>
<strong>Value:</strong> <span>{value}</span>
</p>
);
})}

<h3>Headers:</h3>
{Array.from((await headers()).entries()).map(([name, value]: [string, string]) => {
return (
<p key={name} style={{ display: 'flex', flexDirection: 'row', gap: 8 }}>
<strong>Name:</strong> <span>{name}</span>
<strong>Value:</strong> <span>{value}</span>
</p>
);
})}

<form action={handleClick}>
<button>add user-id cookie</button>
</form>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';

import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/test';

import { redirect } from 'next/navigation';

let state = 'Bug! Not invalidated';

export default {
render() {
return (
<div>
<div>{state}</div>
<form
action={() => {
state = 'State is invalidated successfully.';
redirect('/');
}}
>
<button>Submit</button>
</form>
</div>
);
},
parameters: {
test: {
// This is needed until Next will update to the React 19 beta: https://github.com/vercel/next.js/pull/65058
// In the React 19 beta ErrorBoundary errors (such as redirect) are only logged, and not thrown.
// We will also suspress console.error logs for re the console.error logs for redirect in the next framework.
// Using the onCaughtError react root option:
// react: {
// rootOptions: {
// onCaughtError(error: unknown) {
// if (isNextRouterError(error)) return;
// console.error(error);
// },
// },
// See: code/frameworks/nextjs/src/preview.tsx
dangerouslyIgnoreUnhandledErrors: true,
},
nextjs: {
appDirectory: true,
navigation: {
pathname: '/',
},
},
},
tags: ['!test'],
} as Meta;

export const SingletonStateGetsInvalidatedAfterRedirecting: StoryObj = {
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole('button'));
},
};

0 comments on commit 22df58c

Please sign in to comment.