Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

New Image component #114

Open
wants to merge 11 commits into
base: 2023-01
Choose a base branch
from
10 changes: 9 additions & 1 deletion apps/nextjs/gql/fragment-masking.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
import { TypedDocumentNode as DocumentNode, ResultOf } from '@graphql-typed-document-node/core';


export type FragmentType<TDocumentType extends DocumentNode<any, any>> = TDocumentType extends DocumentNode<
Expand Down Expand Up @@ -38,3 +38,11 @@ export function useFragment<TType>(
): TType | ReadonlyArray<TType> | null | undefined {
return fragmentType as any
}


export function makeFragmentData<
F extends DocumentNode,
FT extends ResultOf<F>
>(data: FT, _fragment: F): FragmentType<F> {
return data as FragmentType<F>;
}
26 changes: 26 additions & 0 deletions apps/nextjs/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,39 @@
import * as types from './graphql';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';

/**
* Map of all GraphQL operations in the project.
*
* This map has several performance disadvantages:
* 1. It is not tree-shakeable, so it will include all operations in the project.
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
* 3. It does not support dead code elimination, so it will add unused operations.
*
* Therefore it is highly recommended to use the babel-plugin for production.
*/
const documents = {
"\n query IndexQuery {\n shop {\n name\n }\n products(first: 1) {\n nodes {\n # if you uncomment 'blah', it should have a GraphQL validation error in your IDE if you have a GraphQL plugin. It should also give an error during 'npm run dev'\n # blah\n id\n title\n publishedAt\n handle\n variants(first: 1) {\n nodes {\n id\n image {\n url\n altText\n width\n height\n }\n }\n }\n }\n }\n }\n": types.IndexQueryDocument,
};

/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query IndexQuery {\n shop {\n name\n }\n products(first: 1) {\n nodes {\n # if you uncomment 'blah', it should have a GraphQL validation error in your IDE if you have a GraphQL plugin. It should also give an error during 'npm run dev'\n # blah\n id\n title\n publishedAt\n handle\n variants(first: 1) {\n nodes {\n id\n image {\n url\n altText\n width\n height\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query IndexQuery {\n shop {\n name\n }\n products(first: 1) {\n nodes {\n # if you uncomment 'blah', it should have a GraphQL validation error in your IDE if you have a GraphQL plugin. It should also give an error during 'npm run dev'\n # blah\n id\n title\n publishedAt\n handle\n variants(first: 1) {\n nodes {\n id\n image {\n url\n altText\n width\n height\n }\n }\n }\n }\n }\n }\n"];

/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*
*
* @example
* ```ts
* const query = gql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
* ```
*
* The query argument is unknown!
* Please regenerate the types.
**/
export function graphql(source: string): unknown;

export function graphql(source: string) {
return (documents as any)[source] ?? {};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/ExternalVideo.test.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {PartialDeep} from 'type-fest';
import type {ExternalVideo as ExternalVideoType} from './storefront-api-types.js';
import {faker} from '@faker-js/faker';
import {getPreviewImage} from './Image.test.helpers.js';
import {getPreviewImage} from './ImageLegacy.test.helpers.js';

export function getExternalVideoData(
externalVideo: Partial<ExternalVideoType> = {}
Expand Down
100 changes: 66 additions & 34 deletions packages/react/src/Image.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,77 @@
import * as React from 'react';
import type {Story} from '@ladle/react';
import {Image, type ShopifyImageProps} from './Image.js';
import {IMG_SRC_SET_SIZES} from './image-size.js';
import {Image, ShopifyLoaderOptions, ShopifyLoaderParams} from './Image.js';
import type {PartialDeep} from 'type-fest';
import type {Image as ImageType} from './storefront-api-types.js';

type Crop = 'center' | 'top' | 'bottom' | 'left' | 'right';

type ImageConfig = {
intervals: number;
startingWidth: number;
incrementSize: number;
placeholderWidth: number;
};

type HtmlImageProps = React.ImgHTMLAttributes<HTMLImageElement>;

const Template: Story<{
'data.url': ShopifyImageProps['data']['url'];
'data.width': ShopifyImageProps['data']['width'];
'data.height': ShopifyImageProps['data']['height'];
width: ShopifyImageProps['width'];
height: ShopifyImageProps['height'];
widths: ShopifyImageProps['widths'];
loaderOptions: ShopifyImageProps['loaderOptions'];
as?: 'img' | 'source';
data?: PartialDeep<ImageType, {recurseIntoArrays: true}>;
loader?: (params: ShopifyLoaderParams) => string;
src: string;
width?: string | number;
height?: string | number;
crop?: Crop;
sizes?: string;
aspectRatio?: string;
config?: ImageConfig;
alt?: string;
loading?: 'lazy' | 'eager';
loaderOptions?: ShopifyLoaderOptions;
widths?: (HtmlImageProps['width'] | ImageType['width'])[];
}> = (props) => {
const finalProps: ShopifyImageProps = {
data: {
url: props['data.url'],
width: props['data.width'],
height: props['data.height'],
id: 'testing',
},
width: props.width,
height: props.height,
widths: props.widths,
loaderOptions: props.loaderOptions,
};
return <Image {...finalProps} />;
return (
<>
{/* Standard Usage */}
<Image
{...props}
width="100%"
loading="eager"
aspectRatio="1/1"
sizes="100vw"
/>
{/* */}
<Image
{...props}
src="https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg"
width="100%"
aspectRatio="1/1"
sizes="100vw"
/>
<Image {...props} sizes="50vw" width="50vw" />
<Image {...props} aspectRatio="4/3" width="50vw" sizes="50vw" />
<Image {...props} width="30vw" sizes="30vw" />
<Image {...props} width={100} height={200} />
<Image {...props} width="5rem" />
<Image {...props} widths={[100, 200, 300]} />
<Image
{...props}
loaderOptions={{
crop: 'center',
}}
widths={[200, 300]}
/>
</>
);
};

export const Default = Template.bind({});
Default.args = {
'data.url':
'https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg',
'data.width': 100,
'data.height': 100,
width: 500,
height: 500,
widths: IMG_SRC_SET_SIZES,
loaderOptions: {
crop: 'center',
scale: 2,
width: 500,
height: 500,
data: {
url: 'https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg',
altText: 'alt text',
width: 3908,
height: 3908,
},
};
Loading