Skip to content

Commit

Permalink
Add Pagination (#755)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Matt Seccafien <[email protected]>
Co-authored-by: Bret Little <[email protected]>
Co-authored-by: Anthony Frehner <[email protected]>
  • Loading branch information
4 people authored May 19, 2023
1 parent 2e97b1e commit ba54a3b
Show file tree
Hide file tree
Showing 25 changed files with 1,570 additions and 552 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-books-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen': patch
---

Add a `<Pagination__unstable>` component and `getPaginationVariables__unstable` helper to make rendering large lists from the Storefront API easy. This is an initial unstable release and we expect to finalize the API by the 2023-07 release. See the [`<Pagination>` component documentation](https://shopify.dev/docs/api/hydrogen/2023-04/components/pagination).
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,8 @@ jobs:
- name: 📥 Install dependencies
run: npm ci

- name: 📦 Build packages
run: npm run build

- name: 🔬 Test
run: npm run test
2 changes: 1 addition & 1 deletion packages/hydrogen-react/src/BuyNowButton.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {vi, afterEach, beforeEach, describe, it, expect} from 'vitest';
import {vi, describe, it, expect} from 'vitest';
import {CartProvider, useCart} from './CartProvider.js';
import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
Expand Down
602 changes: 490 additions & 112 deletions packages/hydrogen/docs/generated/generated_docs_data.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/hydrogen/hydrogen.config.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {Storefront} from './types';
import type {Storefront} from '@shopify/hydrogen';

declare global {
/**
Expand Down
2 changes: 1 addition & 1 deletion packages/hydrogen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"types": "dist/production/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "tsup --clean --config ../../tsup.config.ts && npm run copy-hydrogen-react",
"build": "npm run copy-hydrogen-react && tsup --clean --config ../../tsup.config.ts",
"copy-hydrogen-react": "node ../../scripts/copy-hydrogen-react.mjs",
"dev": "tsup --config ../../tsup.config.ts --watch ./src --watch ../../node_modules/@shopify/hydrogen-react/dist/browser-prod/index.mjs",
"typecheck": "tsc --noEmit",
Expand Down
8 changes: 4 additions & 4 deletions packages/hydrogen/src/cache/CacheCustom.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ const data: ReferenceEntityTemplateSchema = {
{
name: 'createStorefrontClient',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/createstorefrontclient',
url: '/docs/api/hydrogen/utilities/createstorefrontclient',
},
{
name: 'CacheNone',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachenone',
url: '/docs/api/hydrogen/utilities/cachenone',
},
{
name: 'CacheShort',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cacheshort',
url: '/docs/api/hydrogen/utilities/cacheshort',
},
{
name: 'CacheLong',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachelong',
url: '/docs/api/hydrogen/utilities/cachelong',
},
],
description: `This allows you to create your own caching strategy, using any of the options available in a \`CachingStrategy\` object.
Expand Down
8 changes: 4 additions & 4 deletions packages/hydrogen/src/cache/CacheLong.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ const data: ReferenceEntityTemplateSchema = {
{
name: 'createStorefrontClient',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/createstorefrontclient',
url: '/docs/api/hydrogen/utilities/createstorefrontclient',
},
{
name: 'CacheNone',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachenone',
url: '/docs/api/hydrogen/utilities/cachenone',
},
{
name: 'CacheShort',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cacheshort',
url: '/docs/api/hydrogen/utilities/cacheshort',
},
{
name: 'CacheCustom',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachecustom',
url: '/docs/api/hydrogen/utilities/cachecustom',
},
],
description: `The \`CacheLong\` strategy instructs caches to store data for 1 hour, and \`staleWhileRevalidate\` data for an additional 23 hours. Note: these time values are subject to change.
Expand Down
8 changes: 4 additions & 4 deletions packages/hydrogen/src/cache/CacheNone.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ const data: ReferenceEntityTemplateSchema = {
{
name: 'createStorefrontClient',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/createstorefrontclient',
url: '/docs/api/hydrogen/utilities/createstorefrontclient',
},
{
name: 'CacheShort',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cacheshort',
url: '/docs/api/hydrogen/utilities/cacheshort',
},
{
name: 'CacheLong',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachelong',
url: '/docs/api/hydrogen/utilities/cachelong',
},
{
name: 'CacheCustom',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachecustom',
url: '/docs/api/hydrogen/utilities/cachecustom',
},
],
description: `The CacheNone() strategy instructs caches not to store any data. The function accepts no arguments.
Expand Down
8 changes: 4 additions & 4 deletions packages/hydrogen/src/cache/CacheShort.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ const data: ReferenceEntityTemplateSchema = {
{
name: 'createStorefrontClient',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/createstorefrontclient',
url: '/docs/api/hydrogen/utilities/createstorefrontclient',
},
{
name: 'CacheNone',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachenone',
url: '/docs/api/hydrogen/utilities/cachenone',
},
{
name: 'CacheLong',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachelong',
url: '/docs/api/hydrogen/utilities/cachelong',
},
{
name: 'CacheCustom',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachecustom',
url: '/docs/api/hydrogen/utilities/cachecustom',
},
],
description: `The \`CacheShort\` strategy instructs caches to store data for 1 second, and \`staleWhileRevalidate\` data for an additional 9 seconds. Note: these time values are subject to change.
Expand Down
8 changes: 4 additions & 4 deletions packages/hydrogen/src/createStorefrontClient.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ const data: ReferenceEntityTemplateSchema = {
{
name: 'CacheNone',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachenone',
url: '/docs/api/hydrogen/utilities/cachenone',
},
{
name: 'CacheShort',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cacheshort',
url: '/docs/api/hydrogen/utilities/cacheshort',
},
{
name: 'CacheLong',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachelong',
url: '/docs/api/hydrogen/utilities/cachelong',
},
{
name: 'CacheCustom',
type: 'utilities',
url: '/docs/api/hydrogen/2023-04/utilities/cachecustom',
url: '/docs/api/hydrogen/utilities/cachecustom',
},
],
description: `This function extends \`createStorefrontClient\` from [Hydrogen React](/docs/api/hydrogen-react/latest/utilities/createstorefrontclient). The additional arguments enable internationalization (i18n), caching, and other features particular to Remix and Oxygen.
Expand Down
4 changes: 4 additions & 0 deletions packages/hydrogen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export {graphiqlLoader} from './routing/graphiql';
export {Seo} from './seo/seo';
export {type SeoConfig} from './seo/generate-seo-tags';
export type {SeoHandleFunction} from './seo/seo';
export {
Pagination as Pagination__unstable,
getPaginationVariables as getPaginationVariables__unstable,
} from './pagination/Pagination';

export {
AnalyticsEventName,
Expand Down
46 changes: 46 additions & 0 deletions packages/hydrogen/src/pagination/Pagination.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';

const data: ReferenceEntityTemplateSchema = {
name: 'Pagination',
category: 'components',
isVisualComponent: false,
related: [
{
name: 'getPaginationVariables',
type: 'utilities',
url: '/docs/api/hydrogen/utilities/getpaginationvariables',
},
],
description: `> Caution:
> This component is in an unstable pre-release state and may have breaking changes in a future release.
The [Storefront API uses cursors](https://shopify.dev/docs/api/usage/pagination-graphql) to paginate through lists of data and the \`<Pagination />\` component makes it easy to paginate data from the Storefront API. It is important for pagination state to be maintained in the URL, so that the user can navigate to a product and return back to the same scrolled position in a list. It is also important that the list state is shareable via URL. The \`<Pagination>\` component provides a render prop with properties to load more elements into your list.`,
type: 'component',
defaultExample: {
description: 'I am the default example',
codeblock: {
tabs: [
{
title: 'JavaScript',
code: './Pagination.example.jsx',
language: 'jsx',
},
{
title: 'TypeScript',
code: './Pagination.example.tsx',
language: 'tsx',
},
],
title: 'Example code',
},
},
definitions: [
{
title: 'Props',
type: 'PaginationProps',
description: '',
},
],
};

export default data;
62 changes: 62 additions & 0 deletions packages/hydrogen/src/pagination/Pagination.example.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {json} from '@shopify/remix-oxygen';
import {
Pagination__unstable as Pagination,
getPaginationVariables__unstable as getPaginationVariables,
} from '@shopify/hydrogen';
import {useLoaderData, Link} from '@remix-run/react';

export async function loader({request, context: {storefront}}) {
const variables = getPaginationVariables(request, {pageBy: 8});

const data = await storefront.query(ALL_PRODUCTS_QUERY, {
variables,
});

return json({products: data.products});
}

export default function List() {
const {products} = useLoaderData();

return (
<Pagination connection={products}>
{({nodes, PreviousLink, NextLink}) => (
<>
<PreviousLink>Previous</PreviousLink>
<div>
{nodes.map((product) => (
<Link key={product.id} to={`/products/${product.handle}`}>
{product.title}
</Link>
))}
</div>
<NextLink>Next</NextLink>
</>
)}
</Pagination>
);
}

const ALL_PRODUCTS_QUERY = `#graphql
query AllProducts(
$country: CountryCode
$language: LanguageCode
$first: Int
$last: Int
$startCursor: String
$endCursor: String
) @inContext(country: $country, language: $language) {
products(first: $first, last: $last, before: $startCursor, after: $endCursor) {
nodes { id
title
handle
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
`;
66 changes: 66 additions & 0 deletions packages/hydrogen/src/pagination/Pagination.example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
import {
Pagination__unstable as Pagination,
getPaginationVariables__unstable as getPaginationVariables,
} from '@shopify/hydrogen';
import {useLoaderData, Link} from '@remix-run/react';
import {ProductConnection} from '@shopify/hydrogen/storefront-api-types';

export async function loader({request, context: {storefront}}: LoaderArgs) {
const variables = getPaginationVariables(request, {pageBy: 8});

const data = await storefront.query<{products: ProductConnection}>(
ALL_PRODUCTS_QUERY,
{
variables,
},
);

return json({products: data.products});
}

export default function List() {
const {products} = useLoaderData<typeof loader>();

return (
<Pagination connection={products}>
{({nodes, NextLink, PreviousLink}) => (
<>
<PreviousLink>Previous</PreviousLink>
<div>
{nodes.map((product) => (
<Link key={product.id} to={`/products/${product.handle}`}>
{product.title}
</Link>
))}
</div>
<NextLink>Next</NextLink>
</>
)}
</Pagination>
);
}

const ALL_PRODUCTS_QUERY = `#graphql
query AllProducts(
$country: CountryCode
$language: LanguageCode
$first: Int
$last: Int
$startCursor: String
$endCursor: String
) @inContext(country: $country, language: $language) {
products(first: $first, last: $last, before: $startCursor, after: $endCursor) {
nodes { id
title
handle
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
`;
Loading

0 comments on commit ba54a3b

Please sign in to comment.