diff --git a/.changeset/brown-avocados-dream.md b/.changeset/brown-avocados-dream.md new file mode 100644 index 00000000000..90aacc3970c --- /dev/null +++ b/.changeset/brown-avocados-dream.md @@ -0,0 +1,12 @@ +--- +"@builder.io/sdk-angular": patch +"@builder.io/sdk-react-nextjs": patch +"@builder.io/sdk-qwik": patch +"@builder.io/sdk-react": patch +"@builder.io/sdk-react-native": patch +"@builder.io/sdk-solid": patch +"@builder.io/sdk-svelte": patch +"@builder.io/sdk-vue": patch +--- + +docs: document `getBuilderSearchParams` and `isPreviewing` diff --git a/packages/sdks-tests/src/snippet-tests/advanced-child.spec.ts b/packages/sdks-tests/src/snippet-tests/advanced-child.spec.ts index 6c4c35af0eb..3ac93a44751 100644 --- a/packages/sdks-tests/src/snippet-tests/advanced-child.spec.ts +++ b/packages/sdks-tests/src/snippet-tests/advanced-child.spec.ts @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { test, testClickAndVerifyVisibility } from '../helpers/index.js'; +import { findTextInPage, test, testClickAndVerifyVisibility } from '../helpers/index.js'; test.describe('Advanced child sub components', () => { test('Display two buttons with label Tab 1 and Tab 2', async ({ page, packageName }) => { @@ -62,4 +62,19 @@ test.describe('Advanced child sub components', () => { expect(Tab2ContentVisible).toBe(true); expect(await page.locator('div').filter({ hasText: 'Tab 1 Content' }).isVisible()).toBeFalsy(); }); + + test('Advanced child components work in preview mode with isPreviewing', async ({ + page, + packageName, + }) => { + test.skip(!['react-none-gen2'].includes(packageName)); + + await page.goto( + '/advanced-child?builder.space=ee9f13b4981e489a9a1209887695ef2b&builder.cachebust=true&builder.preview=page&builder.noCache=true&builder.allowTextEdit=true&__builder_editing__=true&builder.overrides.page=91744fcdc9f04bb8884a5f4c9feb0dc1&builder.overrides.91744fcdc9f04bb8884a5f4c9feb0dc1=91744fcdc9f04bb8884a5f4c9feb0dc1&builder.overrides.page:/advanced-child=91744fcdc9f04bb8884a5f4c9feb0dc1' + ); + + await page.waitForLoadState('networkidle'); + + await findTextInPage({ page, text: 'advanced child draft' }); + }); }); diff --git a/packages/sdks-tests/src/snippet-tests/editable-regions.spec.ts b/packages/sdks-tests/src/snippet-tests/editable-regions.spec.ts index 070c35b00f6..75756113ecc 100644 --- a/packages/sdks-tests/src/snippet-tests/editable-regions.spec.ts +++ b/packages/sdks-tests/src/snippet-tests/editable-regions.spec.ts @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { test } from '../helpers/index.js'; +import { findTextInPage, test } from '../helpers/index.js'; test.describe('Editable regions in custom components', () => { test('should render a div with two columns with builder-path attr', async ({ @@ -57,4 +57,19 @@ test.describe('Editable regions in custom components', () => { expect(columnTexts).toContain('column 1 text'); expect(columnTexts).toContain('column 2 text'); }); + + test('Editable regions show draft content in preview mode with isPreviewing', async ({ + page, + packageName, + }) => { + test.skip(!['react-none-gen2'].includes(packageName)); + + await page.goto( + '/editable-region?builder.space=ee9f13b4981e489a9a1209887695ef2b&builder.cachebust=true&builder.preview=page&builder.noCache=true&builder.allowTextEdit=true&__builder_editing__=true&builder.overrides.page=bb909e043ad34915b0075091911647e8&builder.overrides.bb909e043ad34915b0075091911647e8=bb909e043ad34915b0075091911647e8&builder.overrides.page:/editable-region=bb909e043ad34915b0075091911647e8' + ); + await findTextInPage({ page, text: 'draft: column 1 text' }); + await findTextInPage({ page, text: 'draft: column 2 text' }); + + await page.waitForLoadState('networkidle'); + }); }); diff --git a/packages/sdks/e2e/angular-16-ssr/src/app/app.resolver.ts b/packages/sdks/e2e/angular-16-ssr/src/app/app.resolver.ts new file mode 100644 index 00000000000..4c187b8767c --- /dev/null +++ b/packages/sdks/e2e/angular-16-ssr/src/app/app.resolver.ts @@ -0,0 +1,13 @@ +import type { ActivatedRouteSnapshot, ResolveFn } from '@angular/router'; +import { _processContentResult, fetchOneEntry } from '@builder.io/sdk-angular'; +import { getProps } from '@sdk/tests'; + +export const appResolver: ResolveFn = (route: ActivatedRouteSnapshot) => { + const urlPath = `/${route.url.join('/')}`; + + return getProps({ + pathname: urlPath, + _processContentResult, + fetchOneEntry, + }); +}; diff --git a/packages/sdks/e2e/angular-16/src/app/app.component.ts b/packages/sdks/e2e/angular-16/src/app/app.component.ts new file mode 100644 index 00000000000..1a7646e6dab --- /dev/null +++ b/packages/sdks/e2e/angular-16/src/app/app.component.ts @@ -0,0 +1,81 @@ +import { Component } from '@angular/core'; +import { + _processContentResult, + fetchOneEntry, + type RegisteredComponent, +} from '@builder.io/sdk-angular'; +import { getProps } from '@sdk/tests'; +import { customComponents } from './custom-components'; + +interface BuilderProps { + apiVersion: string; + canTrack?: boolean; + trustedHosts?: undefined; + apiKey: string; + model: string; + content: any; + data?: any; + apiHost?: string; + locale?: string; +} + +@Component({ + selector: 'app-root', + template: ` + + + + + +
404 - Content not found
+
+ `, +}) +export class AppComponent { + title = 'angular'; + apiVersion: BuilderProps['apiVersion'] = 'v3'; + canTrack: BuilderProps['canTrack']; + trustedHosts: BuilderProps['trustedHosts']; + apiKey: BuilderProps['apiKey'] = 'abcd'; + model: BuilderProps['model'] = 'page'; + content: BuilderProps['content']; + data: BuilderProps['data']; + apiHost: BuilderProps['apiHost']; + locale: BuilderProps['locale']; + + customComponents: RegisteredComponent[] = customComponents; + + async ngOnInit() { + const urlPath = window.location.pathname || ''; + + const builderProps = await getProps({ + pathname: urlPath, + _processContentResult, + fetchOneEntry, + }); + + if (!builderProps) { + return; + } + + this.content = builderProps.content; + this.canTrack = builderProps.canTrack; + this.trustedHosts = builderProps.trustedHosts; + this.apiKey = builderProps.apiKey; + this.model = builderProps.model; + this.apiVersion = builderProps.apiVersion; + this.data = builderProps.data; + this.apiHost = builderProps.apiHost; + this.locale = builderProps.locale; + } +} diff --git a/packages/sdks/e2e/angular-19-ssr/src/app/catch-all.component.ts b/packages/sdks/e2e/angular-19-ssr/src/app/catch-all.component.ts index a57030de90e..5742081b31f 100644 --- a/packages/sdks/e2e/angular-19-ssr/src/app/catch-all.component.ts +++ b/packages/sdks/e2e/angular-19-ssr/src/app/catch-all.component.ts @@ -7,7 +7,6 @@ import { Content, _processContentResult, fetchOneEntry, - getBuilderSearchParams, registerAction, type RegisteredComponent, } from '@builder.io/sdk-angular'; @@ -95,7 +94,6 @@ export class CatchAllComponent { const builderProps = await getProps({ pathname: urlPath, _processContentResult, - options: getBuilderSearchParams(searchParams), fetchOneEntry: (args) => { return fetchOneEntry({ ...args, diff --git a/packages/sdks/e2e/nextjs-sdk-next-app/src/app/[[...slug]]/page.tsx b/packages/sdks/e2e/nextjs-sdk-next-app/src/app/[[...slug]]/page.tsx index 5e04b42a490..a8af36fcefd 100644 --- a/packages/sdks/e2e/nextjs-sdk-next-app/src/app/[[...slug]]/page.tsx +++ b/packages/sdks/e2e/nextjs-sdk-next-app/src/app/[[...slug]]/page.tsx @@ -5,7 +5,6 @@ import { Content, _processContentResult, fetchOneEntry, - getBuilderSearchParams, } from '@builder.io/sdk-react-nextjs'; import { getProps } from '@sdk/tests'; import MyTextBox from '../../components/MyTextBox/MyTextBox'; @@ -73,7 +72,6 @@ export default async function Page(props: MyPageProps) { const builderProps = await getProps({ pathname: urlPath, _processContentResult, - options: getBuilderSearchParams(props.searchParams), fetchOneEntry, }); diff --git a/packages/sdks/e2e/react-sdk-next-14-app/src/app/[[...slug]]/page.tsx b/packages/sdks/e2e/react-sdk-next-14-app/src/app/[[...slug]]/page.tsx index e7424dc0a9e..182b3dce06f 100644 --- a/packages/sdks/e2e/react-sdk-next-14-app/src/app/[[...slug]]/page.tsx +++ b/packages/sdks/e2e/react-sdk-next-14-app/src/app/[[...slug]]/page.tsx @@ -3,7 +3,6 @@ import { Content, _processContentResult, fetchOneEntry, - getBuilderSearchParams, } from '@builder.io/sdk-react'; import { getProps } from '@sdk/tests'; @@ -63,7 +62,6 @@ export default async function Page(props: PageProps) { const builderProps = await getProps({ pathname: urlPath, _processContentResult, - options: getBuilderSearchParams(props.searchParams), fetchOneEntry, }); diff --git a/packages/sdks/e2e/react-sdk-next-15-app/src/app/[[...slug]]/page.tsx b/packages/sdks/e2e/react-sdk-next-15-app/src/app/[[...slug]]/page.tsx index cf982b07aac..8384512b08e 100644 --- a/packages/sdks/e2e/react-sdk-next-15-app/src/app/[[...slug]]/page.tsx +++ b/packages/sdks/e2e/react-sdk-next-15-app/src/app/[[...slug]]/page.tsx @@ -4,7 +4,6 @@ import { Content, _processContentResult, fetchOneEntry, - getBuilderSearchParams, } from '@builder.io/sdk-react'; import { getProps } from '@sdk/tests'; @@ -22,7 +21,6 @@ export default async function Page(props: PageProps) { const builderProps = await getProps({ pathname: urlPath, _processContentResult, - options: getBuilderSearchParams(await props.searchParams), fetchOneEntry, }); diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/advanced-child/advanced-child.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/advanced-child/advanced-child.component.ts index 4d994e85911..4092407748d 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/advanced-child/advanced-child.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/advanced-child/advanced-child.component.ts @@ -1,7 +1,11 @@ import { CommonModule } from '@angular/common'; import { Component, type OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Content, type BuilderContent } from '@builder.io/sdk-angular'; +import { + Content, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; import { CustomTabsComponent, customTabsInfo, @@ -12,7 +16,7 @@ import { standalone: true, imports: [CommonModule, Content, CustomTabsComponent], template: ` -
+
-
404
+ 404 `, }) export class AdvancedChildComponent implements OnInit { @@ -28,7 +32,7 @@ export class AdvancedChildComponent implements OnInit { modelName = 'page'; content: BuilderContent | null = null; - notFound = false; + canShowContent = false; customComponents = [customTabsInfo]; constructor(private route: ActivatedRoute) {} @@ -36,6 +40,8 @@ export class AdvancedChildComponent implements OnInit { ngOnInit() { this.route.data.subscribe((data: any) => { this.content = data.content; + const searchParams = this.route.snapshot.queryParams; + this.canShowContent = !!this.content || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.component.ts index 447b0fdacb9..85626a8015f 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.component.ts @@ -5,37 +5,42 @@ */ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Content, type BuilderContent } from '@builder.io/sdk-angular'; +import { + Content, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; @Component({ selector: 'app-announcement-bar', standalone: true, imports: [Content, CommonModule], template: ` - + - - -
The rest of your page goes here
+ 404 `, }) -export class AnnouncementBarComponent { +export class AnnouncementBarComponent implements OnInit { apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; model = 'announcement-bar'; content: BuilderContent | null = null; + canShowContent = false; constructor(private activatedRoute: ActivatedRoute) {} ngOnInit() { this.activatedRoute.data.subscribe((data: any) => { this.content = data.content; + const searchParams = this.activatedRoute.snapshot.queryParams; + this.canShowContent = !!this.content || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.resolver.ts b/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.resolver.ts index 325670b3330..37528c1bc52 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.resolver.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/announcement-bar/announcement-bar.resolver.ts @@ -1,22 +1,16 @@ import type { ActivatedRouteSnapshot, ResolveFn } from '@angular/router'; -import { - BuilderContent, - fetchOneEntry, - getBuilderSearchParams, -} from '@builder.io/sdk-angular'; +import { BuilderContent, fetchOneEntry } from '@builder.io/sdk-angular'; -export const announcementBarResolver: ResolveFn = ( +export const announcementBarResolver: ResolveFn = async ( route: ActivatedRouteSnapshot ) => { const urlPath = `/${route.url.join('/')}`; - const searchParams = getBuilderSearchParams(route.queryParams); - return fetchOneEntry({ + return await fetchOneEntry({ apiKey: 'ee9f13b4981e489a9a1209887695ef2b', model: 'announcement-bar', userAttributes: { urlPath, }, - options: searchParams, }); }; diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/blog-article/blog-article.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/blog-article/blog-article.component.ts index 00c22903e4e..f110860bcea 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/blog-article/blog-article.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/blog-article/blog-article.component.ts @@ -1,32 +1,40 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Content, type BuilderContent } from '@builder.io/sdk-angular'; +import { + Content, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; @Component({ selector: 'app-blog-article', standalone: true, imports: [Content, CommonModule], - template: `
-

{{ article.data?.title }}

-

{{ article.data?.['blurb'] }}

- - -
`, + template: `
+

{{ article?.data?.title }}

+

{{ article?.data?.['blurb'] }}

+ + +
+ 404 `, }) export class BlogArticleComponent { article: BuilderContent | null = null; model = 'blog-article'; apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; + canShowContent = false; constructor(private route: ActivatedRoute) {} async ngOnInit() { this.route.data.subscribe((data) => { this.article = data['article']; + const searchParams = this.route.snapshot.queryParams; + this.canShowContent = !!this.article || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.component.ts index 7d3793d5c91..0c5565b3f46 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.component.ts @@ -6,14 +6,18 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Content, type BuilderContent } from '@builder.io/sdk-angular'; +import { + Content, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; @Component({ selector: 'app-catchall', standalone: true, imports: [Content, CommonModule], template: ` - + - -
404 - Content not found
-
+ 404 `, }) export class CatchAllComponent { apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; model = 'page'; content: BuilderContent | null = null; + canShowContent = false; constructor(private activatedRoute: ActivatedRoute) {} ngOnInit() { this.activatedRoute.data.subscribe((data: any) => { this.content = data.content; + const searchParams = this.activatedRoute.snapshot.queryParams; + this.canShowContent = !!this.content || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.resolver.ts b/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.resolver.ts index 7acdfff0e5d..d59aa92d542 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.resolver.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/catch-all/catch-all.resolver.ts @@ -1,17 +1,13 @@ import type { ActivatedRouteSnapshot, ResolveFn } from '@angular/router'; -import { - BuilderContent, - fetchOneEntry, - getBuilderSearchParams, -} from '@builder.io/sdk-angular'; +import { fetchOneEntry, type BuilderContent } from '@builder.io/sdk-angular'; -export const catchAllResolver: ResolveFn = ( +export const catchAllResolver: ResolveFn = async ( route: ActivatedRouteSnapshot ) => { const urlPath = `/${route.url.join('/')}`; - const searchParams = getBuilderSearchParams(route.queryParams); + const searchParams = route.queryParams; - return fetchOneEntry({ + return await fetchOneEntry({ apiKey: 'ee9f13b4981e489a9a1209887695ef2b', model: 'page', userAttributes: { diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/custom-child/custom-child.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/custom-child/custom-child.component.ts index 7749c5f19e5..411d85b27f0 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/custom-child/custom-child.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/custom-child/custom-child.component.ts @@ -1,7 +1,11 @@ import { CommonModule } from '@angular/common'; import { Component, type OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Content, type BuilderContent } from '@builder.io/sdk-angular'; +import { + Content, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; import { customHeroInfo } from './custom-hero/custom-hero.component'; @Component({ @@ -9,25 +13,27 @@ import { customHeroInfo } from './custom-hero/custom-hero.component'; standalone: true, imports: [Content, CommonModule], template: ` -
+
- - 404 Not Found + 404 `, }) export class CustomChildComponent implements OnInit { content: BuilderContent | null = null; customHeroInfo = customHeroInfo; + canShowContent = false; constructor(private route: ActivatedRoute) {} async ngOnInit() { this.route.data.subscribe((data: any) => { this.content = data.content; + const searchParams = this.route.snapshot.queryParams; + this.canShowContent = !!this.content || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/editable-regions/editable-regions.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/editable-regions/editable-regions.component.ts index 1be890b1e34..a9f86d8a39b 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/editable-regions/editable-regions.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/editable-regions/editable-regions.component.ts @@ -1,7 +1,11 @@ import { CommonModule } from '@angular/common'; import { Component, type OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Content, type BuilderContent } from '@builder.io/sdk-angular'; +import { + Content, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; import { customColumnsInfo } from './custom-columns/custom-columns.component'; @Component({ @@ -9,7 +13,7 @@ import { customColumnsInfo } from './custom-columns/custom-columns.component'; standalone: true, imports: [CommonModule, Content], template: ` -
+
{ this.content = data.content; + const searchParams = this.route.snapshot.queryParams; + this.canShowContent = !!this.content || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16-ssr/src/app/home/homepage.component.ts b/packages/sdks/snippets/angular-16-ssr/src/app/home/homepage.component.ts index 6b1c1facb3b..b09bb08d297 100644 --- a/packages/sdks/snippets/angular-16-ssr/src/app/home/homepage.component.ts +++ b/packages/sdks/snippets/angular-16-ssr/src/app/home/homepage.component.ts @@ -13,29 +13,27 @@ import { imports: [Content, CommonModule], template: ` - -
404
-
+ 404 `, }) export class HomepageComponent implements OnInit { content: BuilderContent | null = null; - model = 'homepage'; apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; - - isPreviewing = isPreviewing; + canShowContent = false; constructor(private route: ActivatedRoute) {} async ngOnInit() { this.route.data.subscribe((data: any) => { this.content = data.content; + const searchParams = this.route.snapshot.queryParams; + this.canShowContent = !!this.content || isPreviewing(searchParams); }); } } diff --git a/packages/sdks/snippets/angular-16/src/app/advanced-child/advanced-child.component.ts b/packages/sdks/snippets/angular-16/src/app/advanced-child/advanced-child.component.ts index 1045366f240..6b36fbf4414 100644 --- a/packages/sdks/snippets/angular-16/src/app/advanced-child/advanced-child.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/advanced-child/advanced-child.component.ts @@ -3,6 +3,7 @@ import { Component, type OnInit } from '@angular/core'; import { Content, fetchOneEntry, + isPreviewing, type BuilderContent, } from '@builder.io/sdk-angular'; import { @@ -43,6 +44,6 @@ export class AdvancedChildComponent implements OnInit { }, }); - this.notFound = !this.content; + this.notFound = !this.content && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/announcement-bar/announcement-bar.component.ts b/packages/sdks/snippets/angular-16/src/app/announcement-bar/announcement-bar.component.ts index 5682c099527..be6e11f4bb1 100644 --- a/packages/sdks/snippets/angular-16/src/app/announcement-bar/announcement-bar.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/announcement-bar/announcement-bar.component.ts @@ -9,6 +9,7 @@ import { Component } from '@angular/core'; import { Content, fetchOneEntry, + isPreviewing, type BuilderContent, } from '@builder.io/sdk-angular'; @@ -17,7 +18,7 @@ import { standalone: true, imports: [CommonModule, Content], template: ` - + + +
404 - Content not found
+
The rest of your page goes here
`, @@ -33,6 +37,7 @@ export class AnnouncementBarComponent { apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; model = 'announcement-bar'; content: BuilderContent | null = null; + notFound = false; async ngOnInit() { const urlPath = window.location.pathname || ''; @@ -45,10 +50,7 @@ export class AnnouncementBarComponent { }, }); - if (!content) { - return; - } - this.content = content; + this.notFound = !content && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/blog-article/blog-article.component.ts b/packages/sdks/snippets/angular-16/src/app/blog-article/blog-article.component.ts index 7bffe8dbf58..d65cc6568b3 100644 --- a/packages/sdks/snippets/angular-16/src/app/blog-article/blog-article.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/blog-article/blog-article.component.ts @@ -3,6 +3,7 @@ import { Component } from '@angular/core'; import { Content, fetchOneEntry, + isPreviewing, type BuilderContent, } from '@builder.io/sdk-angular'; @@ -10,16 +11,18 @@ import { selector: 'app-blog-article', standalone: true, imports: [Content, CommonModule], - template: `
-

{{ article.data?.title }}

-

{{ article.data?.['blurb'] }}

- - -
`, + template: `
+

{{ article?.data?.title }}

+

{{ article?.data?.['blurb'] }}

+ + +
+
404 - Content not found
`, }) export class BlogArticleComponent { article: BuilderContent | null = null; model = 'blog-article'; + notFound = false; async ngOnInit() { this.article = await fetchOneEntry({ @@ -29,5 +32,6 @@ export class BlogArticleComponent { 'data.handle': 'new-product-line', }, }); + this.notFound = !this.article && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/catch-all/catch-all.component.ts b/packages/sdks/snippets/angular-16/src/app/catch-all/catch-all.component.ts index 369ce1c3fc3..6e6dc1568bb 100644 --- a/packages/sdks/snippets/angular-16/src/app/catch-all/catch-all.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/catch-all/catch-all.component.ts @@ -8,6 +8,7 @@ import { Component } from '@angular/core'; import { Content, fetchOneEntry, + isPreviewing, type BuilderContent, } from '@builder.io/sdk-angular'; @@ -16,7 +17,7 @@ import { standalone: true, imports: [Content, CommonModule], template: ` - + +
-
404 Not Found
`, }) @@ -39,9 +39,6 @@ export class CustomChildComponent implements OnInit { urlPath: window.location.pathname, }, }); - - if (!this.content) { - this.notFound = true; - } + this.notFound = !this.content && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/editable-regions/editable-regions.component.ts b/packages/sdks/snippets/angular-16/src/app/editable-regions/editable-regions.component.ts index 8388112372d..674823d207f 100644 --- a/packages/sdks/snippets/angular-16/src/app/editable-regions/editable-regions.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/editable-regions/editable-regions.component.ts @@ -3,6 +3,7 @@ import { Component, type OnInit } from '@angular/core'; import { Content, fetchOneEntry, + isPreviewing, type BuilderContent, } from '@builder.io/sdk-angular'; import { customColumnsInfo } from './custom-columns/custom-columns.component'; @@ -38,6 +39,6 @@ export class EditableRegionComponent implements OnInit { urlPath: window.location.pathname, }, }); - this.notFound = !this.content; + this.notFound = !this.content && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/live-preview/live-preview.component.ts b/packages/sdks/snippets/angular-16/src/app/live-preview/live-preview.component.ts index 244da8b4683..ab56fd406a3 100644 --- a/packages/sdks/snippets/angular-16/src/app/live-preview/live-preview.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/live-preview/live-preview.component.ts @@ -2,31 +2,54 @@ import { CommonModule } from '@angular/common'; import type { OnDestroy, OnInit } from '@angular/core'; import { Component } from '@angular/core'; import type { BuilderContent } from '@builder.io/sdk-angular'; -import { subscribeToEditor } from '@builder.io/sdk-angular'; +import { + fetchOneEntry, + isPreviewing, + subscribeToEditor, +} from '@builder.io/sdk-angular'; @Component({ standalone: true, selector: 'app-live-preview', imports: [CommonModule], template: ` -
-
Blog Title: {{ content.data?.title }}
-
Authored by: {{ content.data?.['author'] }}
-
Handle: {{ content.data?.['handle'] }}
+
Loading Data...
+ +
+
Blog Title: {{ content!.data?.title }}
+
Authored by: {{ content!.data?.['author'] }}
+
Handle: {{ content!.data?.['handle'] }}
+
404 - Content not found
`, }) export class LivePreviewComponent implements OnInit, OnDestroy { content: BuilderContent | null = null; + loading = true; + notFound = false; private unsubscribeFn: () => void = () => {}; ngOnInit(): void { + fetchOneEntry({ + model: 'blog-data', + apiKey: 'ee9f13b4981e489a9a1209887695ef2b', + userAttributes: { urlPath: window.location.pathname }, + }) + .then((data) => { + this.content = data; + this.notFound = !this.content && !isPreviewing(); + }) + .finally(() => { + this.loading = false; + }); + this.unsubscribeFn = subscribeToEditor({ model: 'blog-data', apiKey: 'ee9f13b4981e489a9a1209887695ef2b', callback: (updatedContent) => { this.content = updatedContent; + this.notFound = !this.content && !isPreviewing(); }, }); } diff --git a/packages/sdks/snippets/angular-16/src/app/nav-bar/nav-bar.component.ts b/packages/sdks/snippets/angular-16/src/app/nav-bar/nav-bar.component.ts index f49c004486e..cfaf9da2971 100644 --- a/packages/sdks/snippets/angular-16/src/app/nav-bar/nav-bar.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/nav-bar/nav-bar.component.ts @@ -1,6 +1,10 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { fetchOneEntry, type BuilderContent } from '@builder.io/sdk-angular'; +import { + fetchOneEntry, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; import { NavLinksComponent } from './nav-links/nav-links.component'; @Component({ @@ -10,6 +14,7 @@ import { NavLinksComponent } from './nav-links/nav-links.component'; template: ` +
404 - Navigation not found
`, }) export class NavBarComponent { links: BuilderContent = { data: { links: [] } }; + notFound = false; async ngOnInit() { this.links = @@ -33,5 +40,6 @@ export class NavBarComponent { model: 'navigation-links', apiKey: 'ee9f13b4981e489a9a1209887695ef2b', })) || this.links; + this.notFound = !this.links?.data?.['links']?.length && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/product-details/product-details.component.ts b/packages/sdks/snippets/angular-16/src/app/product-details/product-details.component.ts index eb5dfab769b..c65e355609c 100644 --- a/packages/sdks/snippets/angular-16/src/app/product-details/product-details.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/product-details/product-details.component.ts @@ -1,33 +1,38 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { fetchOneEntry, type BuilderContent } from '@builder.io/sdk-angular'; +import { + fetchOneEntry, + isPreviewing, + type BuilderContent, +} from '@builder.io/sdk-angular'; @Component({ selector: 'app-product-details', standalone: true, imports: [CommonModule], template: ` -
-

{{ productDetails.data?.['name'] }}

+
+

{{ productDetails?.data?.['name'] }}

-

{{ productDetails.data?.['collection'].value.data.copy }}

+

{{ productDetails?.data?.['collection'].value.data.copy }}

Price: - {{ productDetails.data?.['collection'].value.data.price }} + {{ productDetails?.data?.['collection'].value.data.price }}

-
-

Loading product details...

+
+

404 - Product not found

`, }) export class ProductDetailsComponent { productDetails: BuilderContent | null = null; apiKey: string = 'ee9f13b4981e489a9a1209887695ef2b'; + notFound = false; async ngOnInit() { this.productDetails = await fetchOneEntry({ @@ -37,5 +42,6 @@ export class ProductDetailsComponent { 'data.handle': 'jacket', }, }); + this.notFound = !this.productDetails && !isPreviewing(); } } diff --git a/packages/sdks/snippets/angular-16/src/app/product-editorial/product-editorial.component.ts b/packages/sdks/snippets/angular-16/src/app/product-editorial/product-editorial.component.ts index c5670e982a4..bcce8b4cf3b 100644 --- a/packages/sdks/snippets/angular-16/src/app/product-editorial/product-editorial.component.ts +++ b/packages/sdks/snippets/angular-16/src/app/product-editorial/product-editorial.component.ts @@ -3,6 +3,7 @@ import { Component } from '@angular/core'; import { Content, fetchOneEntry, + isPreviewing, type BuilderContent, } from '@builder.io/sdk-angular'; import { FooterComponent } from './footer/footer.component'; @@ -24,7 +25,10 @@ import { ProductInfoComponent } from './product-info/product-info.component'; - + + + +
404
`, @@ -33,11 +37,13 @@ export class ProductEditorialComponent { product: any; editorial: BuilderContent | null = null; productId?: string; + notFound = false; async ngOnInit() { this.productId = window.location.pathname.split('/').pop() || ''; if (this.productId) { await this.fetchProductAndEditorial(); + this.notFound = !this.editorial && !isPreviewing(); } } diff --git a/packages/sdks/snippets/hydrogen/app/components/AnnouncementBarPage.tsx b/packages/sdks/snippets/hydrogen/app/components/AnnouncementBarPage.tsx index 911884f94f6..225c2a19632 100644 --- a/packages/sdks/snippets/hydrogen/app/components/AnnouncementBarPage.tsx +++ b/packages/sdks/snippets/hydrogen/app/components/AnnouncementBarPage.tsx @@ -3,11 +3,7 @@ * snippets/hydrogen/app/components/AnnouncementBarPage.tsx */ -import { - Content, - fetchOneEntry, - getBuilderSearchParams, -} from '@builder.io/sdk-react'; +import {Content, fetchOneEntry} from '@builder.io/sdk-react'; import type {LoaderFunction} from '@remix-run/node'; import {useLoaderData} from '@remix-run/react'; import {useNonce} from '@shopify/hydrogen'; @@ -29,7 +25,6 @@ export const announcementsLoader: LoaderFunction = async ({ userAttributes: { urlPath: pathname, }, - options: getBuilderSearchParams(url.searchParams), }); return {content, model}; diff --git a/packages/sdks/snippets/hydrogen/app/components/app.tsx b/packages/sdks/snippets/hydrogen/app/components/app.tsx index 67dc4edc666..c76e55b6b23 100644 --- a/packages/sdks/snippets/hydrogen/app/components/app.tsx +++ b/packages/sdks/snippets/hydrogen/app/components/app.tsx @@ -3,11 +3,7 @@ * snippets/hydrogen/app/components/app.tsx */ -import { - Content, - fetchOneEntry, - getBuilderSearchParams, -} from '@builder.io/sdk-react'; +import {Content, fetchOneEntry, isPreviewing} from '@builder.io/sdk-react'; import type {LoaderFunction} from '@remix-run/node'; import {useLoaderData} from '@remix-run/react'; import {useNonce} from '@shopify/hydrogen'; @@ -26,9 +22,8 @@ export const builderLoader: LoaderFunction = async ({params, request}) => { userAttributes: { urlPath: pathname, }, - options: getBuilderSearchParams(url.searchParams), }); - return {content, model}; + return {content, model, searchParams: Object.fromEntries(url.searchParams)}; } catch (e) { console.error(e); return {content: null}; @@ -36,20 +31,27 @@ export const builderLoader: LoaderFunction = async ({params, request}) => { }; export default function BuilderPage() { - const {content, model} = useLoaderData<{content: any; model: string}>(); + const {content, model, searchParams} = useLoaderData<{ + content: any; + model: string; + searchParams: Record; + }>(); const nonce = useNonce(); + const canShowContent = content || isPreviewing(searchParams); + return (
- {content && ( + {canShowContent ? ( + ) : ( +
404
)} -
The rest of your page goes here
); } diff --git a/packages/sdks/snippets/hydrogen/app/routes/advanced-child.tsx b/packages/sdks/snippets/hydrogen/app/routes/advanced-child.tsx index 423e6a852d7..4f202e84611 100644 --- a/packages/sdks/snippets/hydrogen/app/routes/advanced-child.tsx +++ b/packages/sdks/snippets/hydrogen/app/routes/advanced-child.tsx @@ -6,29 +6,33 @@ import {customTabsInfo} from '~/components/CustomTabs'; const MODEL_NAME = 'advanced-child'; const API_KEY = 'ee9f13b4981e489a9a1209887695ef2b'; -export const loader: LoaderFunction = async ({params}) => { +export const loader: LoaderFunction = async ({params, request}) => { + const url = new URL(request.url); const content = await fetchOneEntry({ model: MODEL_NAME, apiKey: API_KEY, userAttributes: {urlPath: params.pathname}, }); - return {content}; + return {content, searchParams: Object.fromEntries(url.searchParams)}; }; export default function AdvancedChildRoute() { - const {content} = useLoaderData(); + const {content, searchParams} = useLoaderData<{ + content: any; + searchParams: Record; + }>(); - if (!content && !isPreviewing()) { - return
404
; - } + const canShowContent = content || isPreviewing(searchParams); - return ( + return canShowContent ? ( + ) : ( +
404
); } diff --git a/packages/sdks/snippets/hydrogen/app/routes/blogs.$handle.tsx b/packages/sdks/snippets/hydrogen/app/routes/blogs.$handle.tsx index 4d7b544a37c..8f9fb2b7f24 100644 --- a/packages/sdks/snippets/hydrogen/app/routes/blogs.$handle.tsx +++ b/packages/sdks/snippets/hydrogen/app/routes/blogs.$handle.tsx @@ -5,25 +5,32 @@ import {fetchOneEntry, Content, isPreviewing} from '@builder.io/sdk-react'; const MODEL = 'blog-article'; const API_KEY = 'ee9f13b4981e489a9a1209887695ef2b'; -export const loader = async ({params}: LoaderFunctionArgs) => { +export const loader = async ({params, request}: LoaderFunctionArgs) => { + const url = new URL(request.url); const content = await fetchOneEntry({ model: MODEL, apiKey: API_KEY, query: {'data.handle': params.handle}, }); - return {content}; + return {content, searchParams: Object.fromEntries(url.searchParams)}; }; + export default function BlogArticlePage() { - const {content} = useLoaderData<{content: any}>(); + const {content, searchParams} = useLoaderData<{ + content: any; + searchParams: Record; + }>(); - if (!content && !isPreviewing()) return
404
; + const canShowContent = content || isPreviewing(searchParams); - return ( + return canShowContent ? (

{content.data?.title}

{content.data?.blurb}

{content.data?.title}
+ ) : ( +
404
); } diff --git a/packages/sdks/snippets/hydrogen/app/routes/custom-child.tsx b/packages/sdks/snippets/hydrogen/app/routes/custom-child.tsx index f828a1bd840..d7b068300ce 100644 --- a/packages/sdks/snippets/hydrogen/app/routes/custom-child.tsx +++ b/packages/sdks/snippets/hydrogen/app/routes/custom-child.tsx @@ -1,4 +1,4 @@ -import type {LoaderFunction, LoaderFunctionArgs} from '@remix-run/node'; +import type {LoaderFunction} from '@remix-run/node'; import {useLoaderData} from '@remix-run/react'; import {Content, fetchOneEntry, isPreviewing} from '@builder.io/sdk-react'; import {customHeroInfo} from '~/components/CustomHero'; @@ -6,7 +6,8 @@ import {customHeroInfo} from '~/components/CustomHero'; const MODEL_NAME = 'custom-child'; const API_KEY = 'ee9f13b4981e489a9a1209887695ef2b'; -export const loader: LoaderFunction = async ({params}) => { +export const loader: LoaderFunction = async ({params, request}) => { + const url = new URL(request.url); const content = await fetchOneEntry({ model: MODEL_NAME, apiKey: API_KEY, @@ -14,21 +15,24 @@ export const loader: LoaderFunction = async ({params}) => { urlPath: params.pathname, }, }); - return {content}; + return {content, searchParams: Object.fromEntries(url.searchParams)}; }; export default function CustomChildRoute() { - const {content} = useLoaderData(); + const {content, searchParams} = useLoaderData<{ + content: any; + searchParams: Record; + }>(); - if (!content && !isPreviewing()) { - return
404
; - } + const canShowContent = content || isPreviewing(searchParams); - return ( + return canShowContent ? ( + ) : ( +
404
); } diff --git a/packages/sdks/snippets/hydrogen/app/routes/editable-region.tsx b/packages/sdks/snippets/hydrogen/app/routes/editable-region.tsx index 86703daf74a..13693029778 100644 --- a/packages/sdks/snippets/hydrogen/app/routes/editable-region.tsx +++ b/packages/sdks/snippets/hydrogen/app/routes/editable-region.tsx @@ -6,29 +6,33 @@ import {customColumnsInfo} from '~/components/CustomColumns'; const MODEL_NAME = 'editable-regions'; const API_KEY = 'ee9f13b4981e489a9a1209887695ef2b'; -export const loader: LoaderFunction = async ({params}) => { +export const loader: LoaderFunction = async ({params, request}) => { + const url = new URL(request.url); const content = await fetchOneEntry({ model: MODEL_NAME, apiKey: API_KEY, userAttributes: {urlPath: params.pathname}, }); - return {content}; + return {content, searchParams: Object.fromEntries(url.searchParams)}; }; export default function EditableRegionsRoute() { - const {content} = useLoaderData(); + const {content, searchParams} = useLoaderData<{ + content: any; + searchParams: Record; + }>(); - if (!content && !isPreviewing()) { - return
404
; - } + const canShowContent = content || isPreviewing(searchParams); - return ( + return canShowContent ? ( + ) : ( +
404
); } diff --git a/packages/sdks/snippets/hydrogen/app/routes/home.tsx b/packages/sdks/snippets/hydrogen/app/routes/home.tsx index 6a18d58a242..ec065ee3186 100644 --- a/packages/sdks/snippets/hydrogen/app/routes/home.tsx +++ b/packages/sdks/snippets/hydrogen/app/routes/home.tsx @@ -5,18 +5,24 @@ import {useLoaderData} from '@remix-run/react'; const model = 'homepage'; const apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; -export const loader: LoaderFunction = async () => { +export const loader: LoaderFunction = async ({request}) => { + const url = new URL(request.url); const content = await fetchOneEntry({ model: model, apiKey: apiKey, }); - return {content}; + return {content, searchParams: Object.fromEntries(url.searchParams)}; }; export default function Home() { - const {content} = useLoaderData(); + const {content, searchParams} = useLoaderData<{ + content: any; + searchParams: Record; + }>(); - return content || isPreviewing() ? ( + const canShowContent = content || isPreviewing(searchParams); + + return canShowContent ? ( ) : (
404
diff --git a/packages/sdks/snippets/nextjs-sdk-next-app/app/[[...slug]]/page.tsx b/packages/sdks/snippets/nextjs-sdk-next-app/app/[[...slug]]/page.tsx index 655f96e0665..2dcb914c6b0 100644 --- a/packages/sdks/snippets/nextjs-sdk-next-app/app/[[...slug]]/page.tsx +++ b/packages/sdks/snippets/nextjs-sdk-next-app/app/[[...slug]]/page.tsx @@ -23,10 +23,10 @@ export default async function Page(props: PageProps) { const urlPath = '/' + (props.params?.slug?.join('/') || ''); const content = await fetchOneEntry({ - options: getBuilderSearchParams(props.searchParams), apiKey: PUBLIC_API_KEY, model: 'page', userAttributes: { urlPath }, + options: getBuilderSearchParams(props.searchParams), }); const canShowContent = content || isPreviewing(props.searchParams); diff --git a/packages/sdks/snippets/nextjs-sdk-next-app/app/announcements/[...slug]/page.tsx b/packages/sdks/snippets/nextjs-sdk-next-app/app/announcements/[...slug]/page.tsx index 14f90f35db5..aac37f831ad 100644 --- a/packages/sdks/snippets/nextjs-sdk-next-app/app/announcements/[...slug]/page.tsx +++ b/packages/sdks/snippets/nextjs-sdk-next-app/app/announcements/[...slug]/page.tsx @@ -6,7 +6,6 @@ import { Content, fetchOneEntry, - getBuilderSearchParams, isEditing, isPreviewing, } from '@builder.io/sdk-react-nextjs'; @@ -27,8 +26,9 @@ export default async function Page(props: PageProps) { const content = await fetchOneEntry({ apiKey, model, - options: getBuilderSearchParams(props.searchParams), + userAttributes: { urlPath }, + }); const canShowContent = diff --git a/packages/sdks/snippets/nuxt/pages/[...app].vue b/packages/sdks/snippets/nuxt/pages/[...app].vue index ff00915bd60..661d6dfee07 100644 --- a/packages/sdks/snippets/nuxt/pages/[...app].vue +++ b/packages/sdks/snippets/nuxt/pages/[...app].vue @@ -5,7 +5,6 @@ import { Content, fetchOneEntry, - getBuilderSearchParams, isPreviewing, } from '@builder.io/sdk-vue'; import { ref } from 'vue'; @@ -22,7 +21,7 @@ const { data: content } = await useAsyncData( fetchOneEntry({ model, apiKey, - options: getBuilderSearchParams(route.query), + userAttributes: { urlPath: route.path }, }) ); diff --git a/packages/sdks/snippets/nuxt/pages/advanced-child.vue b/packages/sdks/snippets/nuxt/pages/advanced-child.vue index be5ff612ce1..359988369c2 100644 --- a/packages/sdks/snippets/nuxt/pages/advanced-child.vue +++ b/packages/sdks/snippets/nuxt/pages/advanced-child.vue @@ -7,6 +7,10 @@ const route = useRoute(); const model = 'advanced-child'; const apiKey = 'ee9f13b4981e489a9a1209887695ef2b'; +const searchParams = process.server + ? new URLSearchParams(useRequestURL().search) + : new URLSearchParams(window.location.search); + const { data: content, pending } = await useAsyncData( `builder-${model}-${route.path}`, () => @@ -23,7 +27,7 @@ const { data: content, pending } = await useAsyncData(