Skip to content
This repository was archived by the owner on Mar 3, 2023. It is now read-only.
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
53 changes: 53 additions & 0 deletions packages/react/src/ImageNew.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as React from 'react';
import type {Story} from '@ladle/react';
import {Image, shopifyLoader} from './ImageNew.js';

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

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

const Template: Story<{
as?: 'img' | 'source';
src: string;
// eslint-disable-next-line @typescript-eslint/ban-types
loader?: Function /* Should be a Function */;
width?: string | number;
height?: string | number;
crop?: Crop;
sizes?: string;
aspectRatio?: string;
config?: ImageConfig;
alt?: string;
loading?: 'lazy' | 'eager';
}> = (props) => {
return (
<>
<Image {...props} loading="eager" aspectRatio="1/1" sizes="100vw" />
<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" />
</>
);
};

export const Default = Template.bind({});
Default.args = {
as: 'img',
src: 'https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg',
width: '100%',
config: {
intervals: 10,
startingWidth: 300,
incrementSize: 300,
placeholderWidth: 100,
},
crop: 'center',
loading: 'lazy',
loader: shopifyLoader,
};
67 changes: 67 additions & 0 deletions packages/react/src/ImageNew.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {render, screen} from '@testing-library/react';
import {Image} from './ImageNew.js';

const defaultProps = {
src: 'https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg',
};

describe('<Image />', () => {
it('renders an `img` element', () => {
render(<Image {...defaultProps} />);

const image = screen.getByRole('img');

expect(image).toBeInTheDocument();
expect(image).toHaveAttribute('loading', 'lazy');
});

it('renders an `img` element with provided `id`', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});

it('renders an `img` element with provided `loading` value', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});

it('renders an `img` with `width` and `height` values', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});

it('renders an `img` element without `width` and `height` attributes when invalid dimensions are provided', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});

describe('Loaders', () => {
it('calls `shopifyImageLoader()` when no `loader` prop is provided', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});
});

it('allows passthrough props', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});

it('generates a default srcset', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});

it('generates a default srcset up to the image height and width', () => {
const image = screen.getByRole('img');
render(<Image {...defaultProps} />);
expect(image).toBeInTheDocument();
});
});
Loading