Skip to content

Commit

Permalink
Merge pull request #21909 from martinnabhan/next-image-disable-lazy-l…
Browse files Browse the repository at this point in the history
…oading

Allow disabling next/image lazy loading
  • Loading branch information
valentinpalkovic authored and shilman committed Jul 4, 2023
1 parent 40f4981 commit 533bc69
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 7 deletions.
24 changes: 24 additions & 0 deletions code/e2e-tests/framework-nextjs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ test.describe('Next.js', () => {
await new SbPage(page).waitUntilLoaded();
});

test.describe('next/image', () => {
let sbPage: SbPage;

test.beforeEach(async ({ page }) => {
sbPage = new SbPage(page);
});

test('should lazy load images by default', async () => {
await sbPage.navigateToStory('frameworks/nextjs/Image', 'lazy');

const img = sbPage.previewRoot().locator('img');

expect(await img.evaluate<boolean, HTMLImageElement>((image) => image.complete)).toBeFalsy();
});

test('should eager load images when loading parameter is set to eager', async () => {
await sbPage.navigateToStory('frameworks/nextjs/Image', 'eager');

const img = sbPage.previewRoot().locator('img');

expect(await img.evaluate<boolean, HTMLImageElement>((image) => image.complete)).toBeTruthy();
});
});

test.describe('next/navigation', () => {
let root: Locator;
let sbPage: SbPage;
Expand Down
4 changes: 4 additions & 0 deletions code/frameworks/nextjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,16 @@ export default {
framework: {
name: '@storybook/nextjs',
options: {
image: {
loading: 'eager',
},
nextConfigPath: path.resolve(__dirname, '../next.config.js'),
},
},
};
```

- `image`: Props to pass to every instance of `next/image`
- `nextConfigPath`: The absolute path to the `next.config.js`

### Next.js's Image Component
Expand Down
16 changes: 16 additions & 0 deletions code/frameworks/nextjs/src/images/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createContext } from 'react';
import type { ImageProps, StaticImageData } from 'next/image';
import type { ImageProps as LegacyImageProps } from 'next/legacy/image';

// StaticRequire needs to be in scope for the TypeScript compiler to work.
// See: https://github.com/microsoft/TypeScript/issues/5711
// Since next/image doesn't export StaticRequire we need to re-define it here and set src's type to it.
interface StaticRequire {
default: StaticImageData;
}

declare type StaticImport = StaticRequire | StaticImageData;

export const ImageContext = createContext<
Partial<Omit<ImageProps, 'src'> & { src: string | StaticImport }> & Omit<LegacyImageProps, 'src'>
>({});
18 changes: 18 additions & 0 deletions code/frameworks/nextjs/src/images/decorator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import type { Addon_StoryContext } from '@storybook/types';
import { ImageContext } from './context';

export const ImageDecorator = (
Story: React.FC,
{ parameters }: Addon_StoryContext
): React.ReactNode => {
if (!parameters.nextjs?.image) {
return <Story />;
}

return (
<ImageContext.Provider value={parameters.nextjs.image}>
<Story />
</ImageContext.Provider>
);
};
35 changes: 28 additions & 7 deletions code/frameworks/nextjs/src/images/next-image-stub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import type * as _NextImage from 'next/image';
import type * as _NextLegacyImage from 'next/legacy/image';
import semver from 'semver';
import { ImageContext } from './context';

const defaultLoader = ({ src, width, quality }: _NextImage.ImageLoaderProps) => {
const missingValues = [];
Expand Down Expand Up @@ -38,7 +39,11 @@ const OriginalNextImage = NextImage.default;
Object.defineProperty(NextImage, 'default', {
configurable: true,
value: (props: _NextImage.ImageProps) => {
return <OriginalNextImage {...props} loader={props.loader ?? defaultLoader} />;
const imageParameters = React.useContext(ImageContext);

return (
<OriginalNextImage {...imageParameters} {...props} loader={props.loader ?? defaultLoader} />
);
},
});

Expand All @@ -48,9 +53,17 @@ if (semver.satisfies(process.env.__NEXT_VERSION!, '^13.0.0')) {

Object.defineProperty(OriginalNextLegacyImage, 'default', {
configurable: true,
value: (props: _NextLegacyImage.ImageProps) => (
<OriginalNextLegacyImage {...props} loader={props.loader ?? defaultLoader} />
),
value: (props: _NextLegacyImage.ImageProps) => {
const imageParameters = React.useContext(ImageContext);

return (
<OriginalNextLegacyImage
{...imageParameters}
{...props}
loader={props.loader ?? defaultLoader}
/>
);
},
});
}

Expand All @@ -60,8 +73,16 @@ if (semver.satisfies(process.env.__NEXT_VERSION!, '^12.2.0')) {

Object.defineProperty(OriginalNextFutureImage, 'default', {
configurable: true,
value: (props: _NextImage.ImageProps) => (
<OriginalNextFutureImage {...props} loader={props.loader ?? defaultLoader} />
),
value: (props: _NextImage.ImageProps) => {
const imageParameters = React.useContext(ImageContext);

return (
<OriginalNextFutureImage
{...imageParameters}
{...props}
loader={props.loader ?? defaultLoader}
/>
);
},
});
}
2 changes: 2 additions & 0 deletions code/frameworks/nextjs/src/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Addon_DecoratorFunction } from '@storybook/types';
import './config/preview';
import { ImageDecorator } from './images/decorator';
import { RouterDecorator } from './routing/decorator';
import { StyledJsxDecorator } from './styledJsx/decorator';
import './images/next-image-stub';
Expand All @@ -16,6 +17,7 @@ addNextHeadCount();

export const decorators: Addon_DecoratorFunction<any>[] = [
StyledJsxDecorator,
ImageDecorator,
RouterDecorator,
HeadManagerDecorator,
];
Expand Down
27 changes: 27 additions & 0 deletions code/frameworks/nextjs/template/stories/Image.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,30 @@ export const Sized = {
],
},
};

export const Lazy = {
args: {
src: 'https://storybook.js.org/images/placeholders/50x50.png',
width: 50,
height: 50,
},
decorators: [
(Story) => (
<>
<div style={{ height: '200vh' }} />
{Story()}
</>
),
],
};

export const Eager = {
...Lazy,
parameters: {
nextjs: {
image: {
loading: 'eager',
},
},
},
};

0 comments on commit 533bc69

Please sign in to comment.