diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2cc3578e2..5faaa8cb21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,12 +13,12 @@ Our versioning strategy is as follows:
### 🎉 New Features & Improvements
-* `[templates/nextjs-sxa]` `[sitecore-jss-react]` "Bring Your Own Code" (BYOC) feature is introduced. This allows developers and editors more flexibility when developing and working with new components, i.e.:
+* `[templates/nextjs-sxa]` `[sitecore-jss-react]` `[sitecore-jss-nextjs]` "Bring Your Own Code" (BYOC) feature is introduced. This allows developers and editors more flexibility when developing and working with new components, i.e.:
* Avoid the jss deploy process for components, and use FEAAS registration instead
* Put components anywhere in the project,
* Use any prop type, without dependence on Layout Service data
- Check the BYOC documentation for more info. ([#1568](https://github.com/Sitecore/jss/pull/1568)) ([#1603](https://github.com/Sitecore/jss/pull/1603))
+ Check the BYOC documentation for more info. ([#1568](https://github.com/Sitecore/jss/pull/1568)) ([#1603](https://github.com/Sitecore/jss/pull/1603))([#1605](https://github.com/Sitecore/jss/pull/1605))
* `[templates/nextjs-sxa]` Scaffolding components for BYOC is added. Use '--byoc' flag at the end of `jss scaffold` command to create a boilerplate component for BYOC ([#1572](https://github.com/Sitecore/jss/pull/1572))
* `[sitecore-jss-nextjs]` Stylesheet loading via page head links for FEAAS and BYOC is implemented. This allows stylesheets to be loaded during SSR and avoid extra calls on client. ([#1587](https://github.com/Sitecore/jss/pull/1587))
* `[templates/nextjs]` Scaffold new components outside of 'src/components' folder by specifying a path with src in it, i.e. `jss scaffold src/new-folder/NewComponent` ([#1572](https://github.com/Sitecore/jss/pull/1572))
diff --git a/packages/create-sitecore-jss/src/templates/nextjs-sxa/package.json b/packages/create-sitecore-jss/src/templates/nextjs-sxa/package.json
index 547d68f94a..b27c6cdcf7 100644
--- a/packages/create-sitecore-jss/src/templates/nextjs-sxa/package.json
+++ b/packages/create-sitecore-jss/src/templates/nextjs-sxa/package.json
@@ -1,6 +1,5 @@
{
"dependencies": {
- "@sitecore-feaas/clientside": "^0.3.12",
"bootstrap": "^5.1.3",
"font-awesome": "^4.7.0",
"sass": "^1.52.3",
diff --git a/packages/create-sitecore-jss/src/templates/nextjs/package.json b/packages/create-sitecore-jss/src/templates/nextjs/package.json
index a8f7ddc277..4a7b3aa0e6 100644
--- a/packages/create-sitecore-jss/src/templates/nextjs/package.json
+++ b/packages/create-sitecore-jss/src/templates/nextjs/package.json
@@ -29,6 +29,7 @@
},
"license": "Apache-2.0",
"dependencies": {
+ "@sitecore-feaas/clientside": "^0.3.14",
"@sitecore-jss/sitecore-jss-nextjs": "~21.3.0-canary",
"graphql": "~15.8.0",
"graphql-tag": "^2.12.6",
diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/index.ts
index 99707b8a9c..2fd63ecb01 100644
--- a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/index.ts
+++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/index.ts
@@ -4,8 +4,8 @@ import { PackageDefinition, ComponentFile } from '@sitecore-jss/sitecore-jss-dev
export interface ComponentBuilderPluginConfig {
watch?: boolean;
- packages?: PackageDefinition[];
- components?: ComponentFile[];
+ packages: PackageDefinition[];
+ components: ComponentFile[];
}
export interface ComponentBuilderPlugin {
@@ -38,6 +38,8 @@ export interface ComponentBuilderPlugin {
const defaultConfig: ComponentBuilderPluginConfig = {
watch: process.argv.some(arg => arg === '--watch'),
+ packages: [],
+ components: [],
};
(Object.values(plugins) as ComponentBuilderPlugin[])
diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/plugins/feaas.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/plugins/feaas.ts
new file mode 100644
index 0000000000..bdbce27569
--- /dev/null
+++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/plugins/feaas.ts
@@ -0,0 +1,28 @@
+import { ComponentBuilderPlugin, ComponentBuilderPluginConfig } from '..';
+
+/**
+ * Provides Sitecore Components (FEaaS) packages configuration
+ */
+class FEaaSPlugin implements ComponentBuilderPlugin {
+ order = 1;
+
+ exec(config: ComponentBuilderPluginConfig) {
+ config.packages.push({
+ name: '@sitecore-jss/sitecore-jss-nextjs',
+ components: [
+ {
+ componentName: 'BYOCWrapper',
+ moduleName: 'BYOCWrapper',
+ },
+ {
+ componentName: 'FEaaSWrapper',
+ moduleName: 'FEaaSWrapper',
+ },
+ ],
+ });
+
+ return config;
+ }
+}
+
+export const feaasPlugin = new FEaaSPlugin();
diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/feaas.js b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/feaas.js
new file mode 100644
index 0000000000..1cb6b91c3f
--- /dev/null
+++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/feaas.js
@@ -0,0 +1,24 @@
+/**
+ * @param {import('next').NextConfig} nextConfig
+ */
+const feaasPlugin = (nextConfig = {}) => {
+ return Object.assign({}, nextConfig, {
+ webpack: (config, options) => {
+ if (options.isServer) {
+ // Force use of CommonJS on the server for FEAAS SDK since JSS also uses CommonJS entrypoint to FEAAS SDK.
+ // This prevents issues arising due to FEAAS SDK's dual CommonJS/ES module support on the server (via conditional exports).
+ // See https://nodejs.org/api/packages.html#dual-package-hazard.
+ config.externals = [{ '@sitecore-feaas/clientside/react': 'commonjs @sitecore-feaas/clientside/react' }, ...config.externals];
+ }
+
+ // Overload the Webpack config if it was already overloaded
+ if (typeof nextConfig.webpack === 'function') {
+ return nextConfig.webpack(config, options);
+ }
+
+ return config;
+ }
+ });
+};
+
+module.exports = feaasPlugin;
diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js
index f6f54147ea..f1ce7c9a26 100644
--- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js
+++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js
@@ -1,3 +1,6 @@
+const path = require('path');
+const CWD = process.cwd();
+
/**
* @param {import('next').NextConfig} nextConfig
*/
@@ -7,6 +10,12 @@ const monorepoPlugin = (nextConfig = {}) => {
if (options.isServer) {
config.externals = ['react', 'vertx', ...config.externals];
}
+ // Monorepo support for @sitecore-feaas/clientside/react
+ config.resolve.alias['@sitecore-feaas/clientside/react'] = path.resolve(
+ CWD, options.isServer ?
+ './node_modules/@sitecore-feaas/clientside/dist/node/react.cjs' :
+ './node_modules/@sitecore-feaas/clientside/dist/browser/react.esm.js'
+ );
// Overload the Webpack config if it was already overloaded
if (typeof nextConfig.webpack === 'function') {
diff --git a/packages/sitecore-jss-nextjs/src/ComponentBuilder.ts b/packages/sitecore-jss-nextjs/src/ComponentBuilder.ts
index aeb8b725d4..3d3b976030 100644
--- a/packages/sitecore-jss-nextjs/src/ComponentBuilder.ts
+++ b/packages/sitecore-jss-nextjs/src/ComponentBuilder.ts
@@ -13,7 +13,7 @@ export type LazyModule = {
/**
* Component is a module or a lazy module
*/
-type Component = Module | LazyModule;
+type Component = Module | LazyModule | ComponentType;
/**
* Configuration for ComponentBuilder
@@ -97,7 +97,11 @@ export class ComponentBuilder {
return (component as Module)[exportName];
}
- return (component as Module).Default || (component as Module).default || null;
+ return (
+ (component as Module).Default ||
+ (component as Module).default ||
+ (component as ComponentType)
+ );
};
}
}
diff --git a/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/components/FEaaSWrapper.tsx b/packages/sitecore-jss-nextjs/src/components/FEaaSWrapper.tsx
similarity index 53%
rename from packages/create-sitecore-jss/src/templates/nextjs-sxa/src/components/FEaaSWrapper.tsx
rename to packages/sitecore-jss-nextjs/src/components/FEaaSWrapper.tsx
index 27e4536fe5..e4f0330511 100644
--- a/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/components/FEaaSWrapper.tsx
+++ b/packages/sitecore-jss-nextjs/src/components/FEaaSWrapper.tsx
@@ -1,38 +1,34 @@
import {
- GetServerSideComponentProps,
- GetStaticComponentProps,
- FEaaSComponent,
- FEaaSComponentProps,
+ FEaaSWrapper,
FEaaSComponentParams,
fetchFEaaSComponentServerProps,
- constants,
-} from '@sitecore-jss/sitecore-jss-nextjs';
-import React from 'react';
+} from '@sitecore-jss/sitecore-jss-react';
+import {
+ GetStaticComponentProps,
+ GetServerSideComponentProps,
+} from '../sharedTypes/component-props';
+import { constants } from '@sitecore-jss/sitecore-jss';
-export const Default = (props: FEaaSComponentProps): JSX.Element => {
- const styles = `component feaas ${props.params?.styles}`.trimEnd();
- const id = props.params?.RenderingIdentifier;
- return (
-
- );
-};
+/**
+ * This is a repackaged version of the React FEaaSWrapper component with support for
+ * server rendering in Next.js (using component-level data-fetching feature of JSS).
+ */
/**
* Will be called during SSG
* @param {ComponentRendering} rendering
* @param {LayoutServiceData} layoutData
- * @param {GetStaticPropsContext} context
+ * @returns {GetStaticPropsContext} context
*/
export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData) => {
if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) {
return null;
}
const params: FEaaSComponentParams = rendering.params || {};
- const result = await fetchFEaaSComponentServerProps(params, layoutData.sitecore.context.pageState);
+ const result = await fetchFEaaSComponentServerProps(
+ params,
+ layoutData.sitecore.context.pageState
+ );
return result;
};
@@ -40,13 +36,18 @@ export const getStaticProps: GetStaticComponentProps = async (rendering, layoutD
* Will be called during SSR
* @param {ComponentRendering} rendering
* @param {LayoutServiceData} layoutData
- * @param {GetStaticPropsContext} context
+ * @returns {GetStaticPropsContext} context
*/
export const getServerSideProps: GetServerSideComponentProps = async (rendering, layoutData) => {
if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) {
return null;
}
const params: FEaaSComponentParams = rendering.params || {};
- const result = await fetchFEaaSComponentServerProps(params, layoutData.sitecore.context.pageState);
+ const result = await fetchFEaaSComponentServerProps(
+ params,
+ layoutData.sitecore.context.pageState
+ );
return result;
};
+
+export default FEaaSWrapper;
diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts
index 90ab5a8f42..b34d35bf37 100644
--- a/packages/sitecore-jss-nextjs/src/index.ts
+++ b/packages/sitecore-jss-nextjs/src/index.ts
@@ -152,6 +152,8 @@ export { RichText, RichTextProps } from './components/RichText';
export { Placeholder } from './components/Placeholder';
export { EditingComponentPlaceholder } from './components/EditingComponentPlaceholder';
export { NextImage } from './components/NextImage';
+import * as FEaaSWrapper from './components/FEaaSWrapper';
+export { FEaaSWrapper };
export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder';
@@ -175,6 +177,7 @@ export {
BYOCComponent,
BYOCComponentProps,
getFEAASLibraryStylesheetLinks,
+ BYOCWrapper,
File,
FileField,
RichTextField,
diff --git a/packages/sitecore-jss-react/package.json b/packages/sitecore-jss-react/package.json
index c78c743ad6..f121cae91c 100644
--- a/packages/sitecore-jss-react/package.json
+++ b/packages/sitecore-jss-react/package.json
@@ -27,6 +27,7 @@
"url": "https://github.com/sitecore/jss/issues"
},
"devDependencies": {
+ "@sitecore-feaas/clientside": "^0.3.14",
"@types/chai": "^4.3.4",
"@types/chai-string": "^1.4.2",
"@types/deep-equal": "^1.0.1",
@@ -57,11 +58,11 @@
"typescript": "~4.9.3"
},
"peerDependencies": {
+ "@sitecore-feaas/clientside": "^0.3.14",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"dependencies": {
- "@sitecore-feaas/clientside": "^0.3.12",
"@sitecore-jss/sitecore-jss": "21.3.0-canary.51",
"deep-equal": "^2.1.0",
"prop-types": "^15.8.1",
diff --git a/packages/sitecore-jss-react/src/components/BYOCComponent.test.tsx b/packages/sitecore-jss-react/src/components/BYOCComponent.test.tsx
index 5006543a82..791a6e9570 100644
--- a/packages/sitecore-jss-react/src/components/BYOCComponent.test.tsx
+++ b/packages/sitecore-jss-react/src/components/BYOCComponent.test.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
+import * as FEAAS from '@sitecore-feaas/clientside/react';
import { BYOCComponent } from './BYOCComponent';
import { MissingComponent, MissingComponentProps } from './MissingComponent';
@@ -8,15 +9,25 @@ describe('BYOCComponent', () => {
it('should render with props when ComponentProps is provided', () => {
const mockProps = {
params: {
- ComponentName: 'ExternalComponent',
+ ComponentName: 'Foo',
ComponentProps: JSON.stringify({ prop1: 'value1' }),
},
};
+ const Foo = () => Test
;
+ FEAAS.External.registerComponent(Foo, {
+ name: 'Foo',
+ properties: {
+ prop1: {
+ type: 'string',
+ },
+ },
+ });
const wrapper = mount();
- const externalComponent = wrapper.find('Se');
- expect(externalComponent).to.have.lengthOf(1);
- expect(externalComponent.prop('componentName')).to.equal('ExternalComponent');
- expect(externalComponent.prop('prop1')).to.equal('value1');
+ const fooComponent = wrapper.find('feaas-external');
+ expect(fooComponent).to.have.lengthOf(1);
+ expect(fooComponent.prop('prop1')).to.equal('value1');
+ expect(fooComponent.prop('data-external-id')).to.equal('Foo');
+ expect(fooComponent.find('#foo-content')).to.have.length(1);
});
});
diff --git a/packages/sitecore-jss-react/src/components/BYOCComponent.tsx b/packages/sitecore-jss-react/src/components/BYOCComponent.tsx
index a1d3d33ac1..afcdf7268b 100644
--- a/packages/sitecore-jss-react/src/components/BYOCComponent.tsx
+++ b/packages/sitecore-jss-react/src/components/BYOCComponent.tsx
@@ -4,6 +4,8 @@ import { getDataFromFields } from '../utils';
import { MissingComponent, MissingComponentProps } from './MissingComponent';
import * as FEAAS from '@sitecore-feaas/clientside/react';
+export const BYOC_COMPONENT_RENDERING_NAME = 'BYOCComponent';
+
/**
* Data from rendering params on Sitecore's BYOC rendering
*/
diff --git a/packages/sitecore-jss-react/src/components/BYOCWrapper.test.tsx b/packages/sitecore-jss-react/src/components/BYOCWrapper.test.tsx
new file mode 100644
index 0000000000..cd2a2579ac
--- /dev/null
+++ b/packages/sitecore-jss-react/src/components/BYOCWrapper.test.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { stub } from 'sinon';
+import { expect } from 'chai';
+import { mount } from 'enzyme';
+import { BYOCWrapper } from './BYOCWrapper';
+import * as BYOCComponent from './BYOCComponent';
+
+describe('', () => {
+ it('should render', () => {
+ const byocComponentStub = stub(BYOCComponent, 'BYOCComponent').callsFake(() => Foo
);
+ const mockProps = {
+ params: {
+ ComponentName: 'xxx',
+ ComponentProps: JSON.stringify({ prop1: 'value1' }),
+ RenderingIdentifier: 'foo-id',
+ styles: 'bar car ',
+ },
+ };
+ const wrapper = mount();
+
+ const byocComponent = wrapper.find('BYOCComponent');
+ expect(byocComponent).to.have.lengthOf(1);
+ const props = byocComponent.props() as BYOCComponent.BYOCComponentProps;
+ expect(props.params).to.deep.equal({
+ ComponentName: 'xxx',
+ ComponentProps: JSON.stringify({ prop1: 'value1' }),
+ RenderingIdentifier: 'foo-id',
+ styles: 'bar car ',
+ });
+
+ const root = wrapper.find('.bar');
+ expect(root).to.have.lengthOf(1);
+ expect(root.props().className).to.equal('bar car');
+ expect(root.props().id).to.equal('foo-id');
+
+ byocComponentStub.restore();
+ });
+});
diff --git a/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/components/BYOCWrapper.tsx b/packages/sitecore-jss-react/src/components/BYOCWrapper.tsx
similarity index 61%
rename from packages/create-sitecore-jss/src/templates/nextjs-sxa/src/components/BYOCWrapper.tsx
rename to packages/sitecore-jss-react/src/components/BYOCWrapper.tsx
index 89f3cf9885..7bca3572aa 100644
--- a/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/components/BYOCWrapper.tsx
+++ b/packages/sitecore-jss-react/src/components/BYOCWrapper.tsx
@@ -1,7 +1,9 @@
-import { BYOCComponentProps, BYOCComponent } from '@sitecore-jss/sitecore-jss-nextjs';
+import { BYOCComponentProps, BYOCComponent } from './BYOCComponent';
import React from 'react';
-export const Default = (props: BYOCComponentProps): JSX.Element => {
+export const BYOC_WRAPPER_RENDERING_NAME = 'BYOCWrapper';
+
+export const BYOCWrapper = (props: BYOCComponentProps): JSX.Element => {
const styles = props.params?.styles?.trimEnd();
const id = props.params?.RenderingIdentifier;
diff --git a/packages/sitecore-jss-react/src/components/FEaaSWrapper.test.tsx b/packages/sitecore-jss-react/src/components/FEaaSWrapper.test.tsx
new file mode 100644
index 0000000000..e96057ffac
--- /dev/null
+++ b/packages/sitecore-jss-react/src/components/FEaaSWrapper.test.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import { stub } from 'sinon';
+import { expect } from 'chai';
+import { mount } from 'enzyme';
+import { ComponentFields } from '@sitecore-jss/sitecore-jss/layout';
+import { FEaaSWrapper } from './FEaaSWrapper';
+import * as FEaaSComponent from './FEaaSComponent';
+
+describe('', () => {
+ const params: FEaaSComponent.FEaaSComponentParams = {
+ LibraryId: 'library123',
+ ComponentId: 'component123',
+ ComponentVersion: 'version123',
+ ComponentRevision: 'staged',
+ ComponentHostName: 'host123',
+ RenderingIdentifier: 'foo-id',
+ styles: 'foo bar ',
+ };
+
+ const fields: ComponentFields = {
+ sampleText: {
+ value: 'Welcome-to-Sitecore-JSS',
+ },
+ };
+
+ const fetchedData = {
+ foo: 'bar',
+ baz: 42,
+ };
+
+ it('should render', () => {
+ const feaasComponentStub = stub(FEaaSComponent, 'FEaaSComponent').callsFake(() => Foo
);
+
+ const mockProps: FEaaSComponent.FEaaSComponentProps = {
+ params,
+ fields,
+ fetchedData,
+ };
+ const wrapper = mount();
+
+ const feaasComponent = wrapper.find('FEaaSComponent');
+ expect(feaasComponent).to.have.lengthOf(1);
+ const props = feaasComponent.props() as FEaaSComponent.FEaaSComponentProps;
+ expect(props.params).to.deep.equal({
+ LibraryId: 'library123',
+ ComponentId: 'component123',
+ ComponentVersion: 'version123',
+ ComponentRevision: 'staged',
+ ComponentHostName: 'host123',
+ RenderingIdentifier: 'foo-id',
+ styles: 'foo bar ',
+ });
+ expect(props.fields).to.deep.equal({
+ sampleText: {
+ value: 'Welcome-to-Sitecore-JSS',
+ },
+ });
+ expect(props.fetchedData).to.deep.equal({
+ foo: 'bar',
+ baz: 42,
+ });
+
+ const root = wrapper.find('.bar');
+ expect(root).to.have.lengthOf(1);
+ expect(root.props().className).to.equal('component feaas foo bar');
+ expect(root.props().id).to.equal('foo-id');
+
+ feaasComponentStub.restore();
+ });
+});
diff --git a/packages/sitecore-jss-react/src/components/FEaaSWrapper.tsx b/packages/sitecore-jss-react/src/components/FEaaSWrapper.tsx
new file mode 100644
index 0000000000..267b22c95d
--- /dev/null
+++ b/packages/sitecore-jss-react/src/components/FEaaSWrapper.tsx
@@ -0,0 +1,16 @@
+import { FEaaSComponent, FEaaSComponentProps } from './FEaaSComponent';
+import React from 'react';
+
+export const FEAAS_WRAPPER_RENDERING_NAME = 'FEaaSWrapper';
+
+export const FEaaSWrapper = (props: FEaaSComponentProps): JSX.Element => {
+ const styles = `component feaas ${props.params?.styles}`.trimEnd();
+ const id = props.params?.RenderingIdentifier;
+ return (
+
+ );
+};
diff --git a/packages/sitecore-jss-react/src/components/Placeholder.test.tsx b/packages/sitecore-jss-react/src/components/Placeholder.test.tsx
index ffc3ddaf0f..00351e7b36 100644
--- a/packages/sitecore-jss-react/src/components/Placeholder.test.tsx
+++ b/packages/sitecore-jss-react/src/components/Placeholder.test.tsx
@@ -4,6 +4,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import PropTypes from 'prop-types';
+import { stub } from 'sinon';
import { expect } from 'chai';
import { shallow, mount } from 'enzyme';
import { ComponentRendering, RouteData } from '@sitecore-jss/sitecore-jss/layout';
@@ -18,11 +19,17 @@ import {
sxaRenderingVariantData,
sxaRenderingVariantDataWithCommonContainerName as sxaRenderingCommonContainerName,
sxaRenderingVariantDataWithoutCommonContainerName as sxaRenderingWithoutContainerName,
+ byocWrapperData,
+ feaasWrapperData,
} from '../test-data/non-ee-data';
import { convertedData as eeData, emptyPlaceholderData } from '../test-data/ee-data';
import * as SxaRichText from '../test-data/sxa-rich-text';
import { MissingComponent, MissingComponentProps } from './MissingComponent';
import { HiddenRendering } from './HiddenRendering';
+import * as BYOCComponent from './BYOCComponent';
+import * as BYOCWrapper from './BYOCWrapper';
+import * as FEAASComponent from './FEaaSComponent';
+import * as FEAASWrapper from './FEaaSWrapper';
const componentFactory: ComponentFactory = (componentName: string) => {
const components = new Map();
@@ -364,6 +371,72 @@ describe('', () => {
});
});
+ describe('BYOC fallback', () => {
+ let byocComponentStub;
+ let byocWrapperStub;
+
+ const componentFactory: ComponentFactory = (_componentName: string, _exportName?: string) =>
+ null;
+
+ it('should render', () => {
+ const component = byocWrapperData.sitecore.route as RouteData;
+ const phKey = 'main';
+
+ byocComponentStub = stub(BYOCComponent, 'BYOCComponent').callsFake(() => (
+ Foo
+ ));
+
+ byocWrapperStub = stub(BYOCWrapper, 'BYOCWrapper').callsFake(() => (
+
+
+
+ ));
+
+ const renderedComponent = mount(
+
+ );
+
+ expect(renderedComponent.find('.byoc-component').length).to.equal(2);
+ expect(renderedComponent.find('.byoc-wrapper').length).to.equal(1);
+
+ byocComponentStub.restore();
+ byocWrapperStub.restore();
+ });
+ });
+
+ describe('FEaaS fallback', () => {
+ let feaasComponentStub;
+ let feaasWrapperStub;
+
+ const componentFactory: ComponentFactory = (_componentName: string, _exportName?: string) =>
+ null;
+
+ it('should render', () => {
+ const component = feaasWrapperData.sitecore.route as RouteData;
+ const phKey = 'main';
+
+ feaasComponentStub = stub(FEAASComponent, 'FEaaSComponent').callsFake(() => (
+ Foo
+ ));
+
+ feaasWrapperStub = stub(FEAASWrapper, 'FEaaSWrapper').callsFake(() => (
+
+
+
+ ));
+
+ const renderedComponent = mount(
+
+ );
+
+ expect(renderedComponent.find('.feaas-component').length).to.equal(2);
+ expect(renderedComponent.find('.feaas-wrapper').length).to.equal(1);
+
+ feaasComponentStub.restore();
+ feaasWrapperStub.restore();
+ });
+ });
+
it('should populate the "key" attribute of placeholder chrome', () => {
const component: any = eeData.sitecore.route;
const phKey = 'main';
diff --git a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx
index 3626e080a4..a7f2bd0390 100644
--- a/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx
+++ b/packages/sitecore-jss-react/src/components/PlaceholderCommon.tsx
@@ -12,6 +12,9 @@ import {
import { convertAttributesToReactProps } from '../utils';
import { HiddenRendering, HIDDEN_RENDERING_NAME } from './HiddenRendering';
import { FEaaSComponent, FEAAS_COMPONENT_RENDERING_NAME } from './FEaaSComponent';
+import { FEaaSWrapper, FEAAS_WRAPPER_RENDERING_NAME } from './FEaaSWrapper';
+import { BYOCComponent, BYOC_COMPONENT_RENDERING_NAME } from './BYOCComponent';
+import { BYOCWrapper, BYOC_WRAPPER_RENDERING_NAME } from './BYOCWrapper';
/**
* These patterns need for right rendering Dynamic placeholders.
@@ -217,8 +220,17 @@ export class PlaceholderCommon extends React.Compone
component = this.getComponentForRendering(componentRendering);
}
- if (componentRendering.componentName === FEAAS_COMPONENT_RENDERING_NAME) {
- component = FEaaSComponent;
+ // Fallback/defaults for Sitecore Component renderings (in case not defined in component factory)
+ if (!component) {
+ if (componentRendering.componentName === FEAAS_COMPONENT_RENDERING_NAME) {
+ component = FEaaSComponent;
+ } else if (componentRendering.componentName === FEAAS_WRAPPER_RENDERING_NAME) {
+ component = FEaaSWrapper;
+ } else if (componentRendering.componentName === BYOC_COMPONENT_RENDERING_NAME) {
+ component = BYOCComponent;
+ } else if (componentRendering.componentName === BYOC_WRAPPER_RENDERING_NAME) {
+ component = BYOCWrapper;
+ }
}
if (!component) {
diff --git a/packages/sitecore-jss-react/src/index.ts b/packages/sitecore-jss-react/src/index.ts
index 4e166e2bdc..e3d3dbaffe 100644
--- a/packages/sitecore-jss-react/src/index.ts
+++ b/packages/sitecore-jss-react/src/index.ts
@@ -63,7 +63,9 @@ export {
FEaaSComponentParams,
fetchFEaaSComponentServerProps,
} from './components/FEaaSComponent';
+export { FEaaSWrapper } from './components/FEaaSWrapper';
export { BYOCComponent, BYOCComponentParams, BYOCComponentProps } from './components/BYOCComponent';
+export { BYOCWrapper } from './components/BYOCWrapper';
export { Link, LinkField, LinkFieldValue, LinkProps, LinkPropTypes } from './components/Link';
export { File, FileField } from './components/File';
export { VisitorIdentification } from './components/VisitorIdentification';
diff --git a/packages/sitecore-jss-react/src/test-data/non-ee-data.ts b/packages/sitecore-jss-react/src/test-data/non-ee-data.ts
index 7e49ac785b..b82de9796f 100644
--- a/packages/sitecore-jss-react/src/test-data/non-ee-data.ts
+++ b/packages/sitecore-jss-react/src/test-data/non-ee-data.ts
@@ -369,3 +369,105 @@ export const sxaRenderingColumnSplitterVariant = {
},
},
};
+
+export const byocWrapperData = {
+ sitecore: {
+ context: {
+ pageEditing: false,
+ },
+ route: {
+ name: 'Home',
+ displayName: 'Home',
+ fields: {
+ key: {
+ value: 'This is a some sample <p>field data</p> o'boy! "wow"',
+ },
+ },
+ placeholders: {
+ main: [
+ {
+ uid: '278b99a7-8d73-4362-ac05-53e7c35154d5',
+ componentName: 'BYOCWrapper',
+ dataSource: '',
+ params: {
+ ComponentName: 'Foo',
+ ComponentProps: '{ "columns": 7 }',
+ GridParameters: 'col-12',
+ DynamicPlaceholderId: '1',
+ FieldNames: 'Default',
+ },
+ },
+ {
+ uid: '278b99a7-8d73-4362-ac05-53e7c35154d5',
+ componentName: 'BYOCComponent',
+ dataSource: '',
+ params: {
+ ComponentName: 'Bar',
+ ComponentProps: '{ "columns": 5 }',
+ GridParameters: 'col-12',
+ DynamicPlaceholderId: '1',
+ FieldNames: 'Default',
+ },
+ },
+ ],
+ },
+ },
+ },
+};
+
+export const feaasWrapperData = {
+ sitecore: {
+ context: {
+ pageEditing: false,
+ },
+ route: {
+ name: 'Home',
+ displayName: 'Home',
+ fields: {
+ key: {
+ value: 'This is a some sample <p>field data</p> o'boy! "wow"',
+ },
+ },
+ placeholders: {
+ main: [
+ {
+ uid: 'd07d1832-f2a1-4f56-a949-5d1ab263a1d5',
+ componentName: 'FEaaSWrapper',
+ dataSource: '',
+ params: {
+ ComponentName: 'Two product teaser',
+ LibraryId: '4lcTPh6h5L4soeuM0WkXtf',
+ ComponentId: 'bkpRNHFB2v',
+ ComponentVersion: 'responsive',
+ ComponentRevision: 'staged',
+ ComponentHostName: 'https://feaas.windows.net',
+ ComponentInstanceId: 'bSE5grxVRMCVB0K',
+ GridParameters: 'col-12',
+ CacheClearingBehavior: 'Clear on publish',
+ DynamicPlaceholderId: '2',
+ FieldNames: 'Default',
+ },
+ },
+ {
+ uid: 'd07d1832-f2a1-4f56-a949-5d1ab263a1d5',
+ componentName: 'FEaaSComponent',
+ dataSource: '',
+ params: {
+ ComponentName: 'One product teaser',
+ LibraryId: '4lcTPh6h5L4soeuM0WkXtf',
+ ComponentId: 'bkpRNHFB2v',
+ ComponentVersion: 'responsive',
+ ComponentRevision: 'staged',
+ ComponentHostName: 'https://feaas.windows.net',
+ ComponentInstanceId: 'bSE5grxVRMCVB0K',
+ GridParameters: 'col-9',
+ CacheClearingBehavior: 'Clear on publish',
+ DynamicPlaceholderId: '2',
+ FieldNames: 'Default',
+ },
+ },
+ ],
+ },
+ },
+ },
+};
diff --git a/yarn.lock b/yarn.lock
index 894fe47c52..5167d25b19 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6446,12 +6446,12 @@ __metadata:
languageName: node
linkType: hard
-"@sitecore-feaas/clientside@npm:^0.3.12":
- version: 0.3.12
- resolution: "@sitecore-feaas/clientside@npm:0.3.12"
+"@sitecore-feaas/clientside@npm:^0.3.14":
+ version: 0.3.14
+ resolution: "@sitecore-feaas/clientside@npm:0.3.14"
peerDependencies:
react-dom: ">=16.8.0"
- checksum: de095e6dd0d38456789a913c0443c626f206b05e376d8803b8ac3595fc5f2e67b7a4184fcadeeb0987e194e05eb0626bc13c80e5d2b40882db12cc658cdac334
+ checksum: e793a475862dfb4d9aaeff1e74745578c8eb2fe168ea85ac133a1f7b20f63c225ad2e82b536377e1b63dfc8f03e6a5d83f24217e89b03d035d329a0356f492aa
languageName: node
linkType: hard
@@ -6779,7 +6779,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@sitecore-jss/sitecore-jss-react@workspace:packages/sitecore-jss-react"
dependencies:
- "@sitecore-feaas/clientside": ^0.3.12
+ "@sitecore-feaas/clientside": ^0.3.14
"@sitecore-jss/sitecore-jss": 21.3.0-canary.51
"@types/chai": ^4.3.4
"@types/chai-string": ^1.4.2
@@ -6813,6 +6813,7 @@ __metadata:
ts-node: ^10.9.1
typescript: ~4.9.3
peerDependencies:
+ "@sitecore-feaas/clientside": ^0.3.14
react: ^18.2.0
react-dom: ^18.2.0
languageName: unknown