diff --git a/packages/sdks-tests/playwright.config.ts b/packages/sdks-tests/playwright.config.ts index c27ef3de420..1f2e44fe6f7 100644 --- a/packages/sdks-tests/playwright.config.ts +++ b/packages/sdks-tests/playwright.config.ts @@ -66,7 +66,7 @@ export default defineConfig({ /** * Increase the default timeout for snippet tests because they're not deterministic. */ - timeout: testType === 'snippet' ? 30000 : 5000, + timeout: testType === 'snippet' ? 50000 : 5000, }, /* Configure projects for major browsers */ diff --git a/packages/sdks-tests/src/snippet-tests/blueprints-product-details.spec.ts b/packages/sdks-tests/src/snippet-tests/blueprints-product-details.spec.ts index 784b6a27192..e109850afe1 100644 --- a/packages/sdks-tests/src/snippet-tests/blueprints-product-details.spec.ts +++ b/packages/sdks-tests/src/snippet-tests/blueprints-product-details.spec.ts @@ -3,7 +3,21 @@ import { test } from '../helpers/index.js'; test.describe('Product Details Component', () => { test.beforeEach(async ({ page, packageName }) => { - test.skip(!['angular-16', 'angular-16-ssr', 'react'].includes(packageName)); + test.skip( + ![ + 'angular-16', + 'angular-16-ssr', + 'react', + 'vue', + 'nuxt', + 'svelte', + 'sveltekit', + 'qwik-city', + 'react-sdk-next-14-app', + 'react-sdk-next-pages', + 'hydrogen', + ].includes(packageName) + ); // Visit the page where ProductDetailsComponent is rendered await page.goto('/product/category/jacket'); }); diff --git a/packages/sdks/snippets/hydrogen/app/routes/category.$handle.tsx b/packages/sdks/snippets/hydrogen/app/routes/category.$handle.tsx new file mode 100644 index 00000000000..199e4125d8d --- /dev/null +++ b/packages/sdks/snippets/hydrogen/app/routes/category.$handle.tsx @@ -0,0 +1,30 @@ +import {fetchOneEntry} from '@builder.io/sdk-react'; +import type {LoaderFunctionArgs} from '@remix-run/node'; +import {useLoaderData} from '@remix-run/react'; + +export const loader = async ({params}: LoaderFunctionArgs) => { + const productDetails = await fetchOneEntry({ + model: 'product-details', + apiKey: 'ee9f13b4981e489a9a1209887695ef2b', + query: { + 'data.handle': params.handle, + }, + }); + + return {productDetails}; +}; + +export default function ProductDetailsPage() { + const {productDetails} = useLoaderData(); + + return ( + productDetails && ( +
+

{productDetails.data?.name}

+ {productDetails.data?.name} +

{productDetails.data?.collection.value.data.copy}

+

Price: {productDetails.data?.collection.value.data.price}

+
+ ) + ); +} diff --git a/packages/sdks/snippets/hydrogen/vite.config.ts b/packages/sdks/snippets/hydrogen/vite.config.ts index 4b188d0babc..5b57ef42318 100644 --- a/packages/sdks/snippets/hydrogen/vite.config.ts +++ b/packages/sdks/snippets/hydrogen/vite.config.ts @@ -15,6 +15,11 @@ export default defineConfig({ v3_relativeSplatPath: true, v3_throwAbortReason: true, }, + routes(defineRoutes) { + return defineRoutes((route) => { + route('/product/category/:handle', 'routes/category.$handle.tsx'); + }); + }, }), tsconfigPaths({ root: './', diff --git a/packages/sdks/snippets/nuxt/pages/product/category/[handle].vue b/packages/sdks/snippets/nuxt/pages/product/category/[handle].vue new file mode 100644 index 00000000000..6471abb1e24 --- /dev/null +++ b/packages/sdks/snippets/nuxt/pages/product/category/[handle].vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/sdks/snippets/qwik-city/src/routes/product/category/[handle]/index.tsx b/packages/sdks/snippets/qwik-city/src/routes/product/category/[handle]/index.tsx new file mode 100644 index 00000000000..94a61762b9b --- /dev/null +++ b/packages/sdks/snippets/qwik-city/src/routes/product/category/[handle]/index.tsx @@ -0,0 +1,31 @@ +import { component$ } from '@builder.io/qwik'; +import { routeLoader$ } from '@builder.io/qwik-city'; +import { fetchOneEntry } from '@builder.io/sdk-qwik'; + +export const useProductDetails = routeLoader$(async ({ params }) => { + return await fetchOneEntry({ + model: 'product-details', + apiKey: 'ee9f13b4981e489a9a1209887695ef2b', + query: { + 'data.handle': params.handle, + }, + }); +}); + +export default component$(() => { + const productResource = useProductDetails(); + + return ( + productResource.value && ( +
+

{productResource.value.data?.name}

+ {productResource.value.data?.name} +

{productResource.value.data?.collection.value.data.copy}

+

Price: {productResource.value.data?.collection.value.data.price}

+
+ ) + ); +}); diff --git a/packages/sdks/snippets/react-sdk-next-14-app/app/(blueprints-product-details)/product/category/[handle]/page.tsx b/packages/sdks/snippets/react-sdk-next-14-app/app/(blueprints-product-details)/product/category/[handle]/page.tsx new file mode 100644 index 00000000000..e09394db767 --- /dev/null +++ b/packages/sdks/snippets/react-sdk-next-14-app/app/(blueprints-product-details)/product/category/[handle]/page.tsx @@ -0,0 +1,31 @@ +import { fetchOneEntry } from '@builder.io/sdk-react'; +import Image from 'next/image'; + +export default async function ProductDetailsPage({ + params, +}: { + params: { handle: string }; +}) { + const productDetails = await fetchOneEntry({ + model: 'product-details', + apiKey: 'ee9f13b4981e489a9a1209887695ef2b', + query: { + 'data.handle': params.handle, + }, + }); + + return ( + productDetails && ( +
+

{productDetails.data?.name}

+ {productDetails.data?.name} +

{productDetails.data?.collection.value.data.copy}

+

Price: {productDetails.data?.collection.value.data.price}

+
+ ) + ); +} diff --git a/packages/sdks/snippets/react-sdk-next-14-app/next.config.mjs b/packages/sdks/snippets/react-sdk-next-14-app/next.config.mjs index 4678774e6d6..f9cd114a7d8 100644 --- a/packages/sdks/snippets/react-sdk-next-14-app/next.config.mjs +++ b/packages/sdks/snippets/react-sdk-next-14-app/next.config.mjs @@ -1,4 +1,12 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + remotePatterns: [ + { + hostname: 'cdn.builder.io', + }, + ], + }, +}; export default nextConfig; diff --git a/packages/sdks/snippets/react-sdk-next-pages/next.config.mjs b/packages/sdks/snippets/react-sdk-next-pages/next.config.mjs index d5456a15d4a..631f5fa3652 100644 --- a/packages/sdks/snippets/react-sdk-next-pages/next.config.mjs +++ b/packages/sdks/snippets/react-sdk-next-pages/next.config.mjs @@ -1,6 +1,21 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + async rewrites() { + return [ + { + source: '/product/category/:handle', + destination: '/product-details/product/category/:handle', + }, + ]; + }, + images: { + remotePatterns: [ + { + hostname: 'cdn.builder.io', + }, + ], + }, }; export default nextConfig; diff --git a/packages/sdks/snippets/react-sdk-next-pages/src/pages/product-details/product/category/[handle].tsx b/packages/sdks/snippets/react-sdk-next-pages/src/pages/product-details/product/category/[handle].tsx new file mode 100644 index 00000000000..ff26e513208 --- /dev/null +++ b/packages/sdks/snippets/react-sdk-next-pages/src/pages/product-details/product/category/[handle].tsx @@ -0,0 +1,36 @@ +import { fetchOneEntry, type BuilderContent } from '@builder.io/sdk-react'; +import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'; +import Image from 'next/image'; + +export const getServerSideProps = (async ({ params }) => { + const productDetails = await fetchOneEntry({ + model: 'product-details', + apiKey: 'ee9f13b4981e489a9a1209887695ef2b', + query: { + 'data.handle': params?.handle, + }, + }); + + return { props: { productDetails } }; +}) satisfies GetServerSideProps<{ + productDetails: BuilderContent | null; +}>; + +export default function ProductCategoryPage({ + productDetails, +}: InferGetServerSidePropsType) { + return ( + productDetails && ( +
+

{productDetails.data?.name}

+ {productDetails.data?.name} +

{productDetails.data?.collection.value.data.copy}

+

Price: {productDetails.data?.collection.value.data.price}

+
+ ) + ); +} diff --git a/packages/sdks/snippets/svelte/src/App.svelte b/packages/sdks/snippets/svelte/src/App.svelte index e70137c6769..c2782463f49 100644 --- a/packages/sdks/snippets/svelte/src/App.svelte +++ b/packages/sdks/snippets/svelte/src/App.svelte @@ -6,6 +6,7 @@ import CatchAll from './components/CatchAll.svelte'; import AnnouncementBar from './components/AnnouncementBar.svelte'; import LivePreview from './components/LivePreview.svelte'; + import ProductDetails from './components/blueprints/ProductDetails.svelte'; export let url = ''; @@ -16,5 +17,6 @@ + diff --git a/packages/sdks/snippets/svelte/src/components/blueprints/ProductDetails.svelte b/packages/sdks/snippets/svelte/src/components/blueprints/ProductDetails.svelte new file mode 100644 index 00000000000..69bfe922dc4 --- /dev/null +++ b/packages/sdks/snippets/svelte/src/components/blueprints/ProductDetails.svelte @@ -0,0 +1,26 @@ + + +{#if !productDetails} +

Loading product details...

+{:else} +
+

{productDetails.data?.name}

+ {productDetails.data?.name} +

{productDetails.data?.collection.value.data.copy}

+

Price: {productDetails.data?.collection.value.data.price}

+
+{/if} diff --git a/packages/sdks/snippets/sveltekit/src/routes/product/category/[handle]/+page.server.ts b/packages/sdks/snippets/sveltekit/src/routes/product/category/[handle]/+page.server.ts new file mode 100644 index 00000000000..21945c23d3c --- /dev/null +++ b/packages/sdks/snippets/sveltekit/src/routes/product/category/[handle]/+page.server.ts @@ -0,0 +1,14 @@ +import { fetchOneEntry } from '@builder.io/sdk-svelte'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ params }) => { + const productDetails = await fetchOneEntry({ + model: 'product-details', + apiKey: 'ee9f13b4981e489a9a1209887695ef2b', + query: { + 'data.handle': params.handle, + }, + }); + + return { productDetails }; +}; diff --git a/packages/sdks/snippets/sveltekit/src/routes/product/category/[handle]/+page.svelte b/packages/sdks/snippets/sveltekit/src/routes/product/category/[handle]/+page.svelte new file mode 100644 index 00000000000..59682f25c44 --- /dev/null +++ b/packages/sdks/snippets/sveltekit/src/routes/product/category/[handle]/+page.svelte @@ -0,0 +1,18 @@ + + +{#if !data.productDetails} +

Loading product details...

+{:else} +
+

{data.productDetails.data?.name}

+ {data.productDetails.data?.name} +

{data.productDetails.data?.collection.value.data.copy}

+

Price: {data.productDetails.data?.collection.value.data.price}

+
+{/if} diff --git a/packages/sdks/snippets/vue/src/router/index.ts b/packages/sdks/snippets/vue/src/router/index.ts index 2855a048f34..3f3d5890035 100644 --- a/packages/sdks/snippets/vue/src/router/index.ts +++ b/packages/sdks/snippets/vue/src/router/index.ts @@ -1,6 +1,7 @@ import AnnouncementBar from '@/views/AnnouncementBar.vue'; import LivePreview from '@/views/LivePreview.vue'; import QuickStart from '@/views/QuickStart.vue'; +import ProductDetails from '@/views/blueprints/ProductDetails.vue'; import { createRouter, createWebHistory } from 'vue-router'; const router = createRouter({ @@ -9,6 +10,7 @@ const router = createRouter({ // will match /announcements/:id or /announcements { path: '/announcements/:id?', component: AnnouncementBar }, { path: '/live-preview', component: LivePreview }, + { path: '/product/category/:handle', component: ProductDetails }, // will match everything and put it under `route.params.pathMatch` { path: '/:pathMatch(.*)*', component: QuickStart }, ], diff --git a/packages/sdks/snippets/vue/src/views/blueprints/ProductDetails.vue b/packages/sdks/snippets/vue/src/views/blueprints/ProductDetails.vue new file mode 100644 index 00000000000..2b9339a19b2 --- /dev/null +++ b/packages/sdks/snippets/vue/src/views/blueprints/ProductDetails.vue @@ -0,0 +1,34 @@ + + + + \ No newline at end of file