diff --git a/_data/sidebars/scos_dev_sidebar.yml b/_data/sidebars/scos_dev_sidebar.yml index 175a0aac860..f578561b25e 100644 --- a/_data/sidebars/scos_dev_sidebar.yml +++ b/_data/sidebars/scos_dev_sidebar.yml @@ -3391,6 +3391,22 @@ entries: - title: Routing url: /docs/scos/dev/front-end-development/oryx/building-pages/oryx-routing.html + - title: Building Components + url: /docs/scos/dev/front-end-development/oryx/building-components/oryx-building-components.html + include_versions: + - "202307.0" + nested: + - title: Component types + url: /docs/scos/dev/front-end-development/oryx/building-components/oryx-component-types.html + - title: Implementing components + url: /docs/scos/dev/front-end-development/oryx/building-components/oryx-implementing-components.html + - title: Providing component definitions + url: /docs/scos/dev/front-end-development/oryx/building-components/oryx-providing-component-definitions.html + - title: Managing components options + url: /docs/scos/dev/front-end-development/oryx/building-components/oryx-managing-component-options.html + - title: Integrating components + url: /docs/scos/dev/front-end-development/oryx/building-components/oryx-integrating-components.html + - title: Architecture nested: - title: Dependency injection diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-building-components.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-building-components.md new file mode 100644 index 00000000000..069c5c44c4b --- /dev/null +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-building-components.md @@ -0,0 +1,32 @@ +--- +title: "Oryx: Building components" +description: Components are the building blocks of Oryx applications +last_updated: Sep 19, 2023 +template: concept-topic-template +--- + +Oryx provides a fully component-based architecture where only components are used to render the application. Components are the building blocks used to create modular and reusable elements. The components are primarily concerned with UI/UX, leaving business logic and integrations to other application layers. + +Oryx contains a library of standard components organized and distributed in [packages](/docs/scos/dev/front-end-development/{{page.version}}/oryx/getting-started/oryx-packages.html). There are different [types of components](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-component-types.html), including a design system. The components are built with powerful UI/UX features: + +- Responsive design +- Themes support using [design tokens](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-design-tokens.html) +- [Typography](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-typography.html) +- [Icon system](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-icon-system.html) +- Internationalization (i18n) features: + - [Locales](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/dependency-injection/oryx-service-layer.html) + - Number and price formatting + - Directionality: left-to-right versus right-to-left +- Accessibility features: + - Dark and light mode + - [Color contrast](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-color-system.html) + - Keyboarding + - Screen reader support + +The components are rendered inside [compositions](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-compositions.html) and [pages](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-pages.html). The pages, organization, and layout of the components are provided in standard [feature sets](/docs/scos/dev/front-end-development/{{page.version}}/oryx/oryx-feature-sets.html). When you install an Oryx application, the feature sets are available in the [presets](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/oryx-presets.html) package. + +You can customize the components with a custom theme, style rules, [component options](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-managing-component-options.html), or component logic. You can also [implement custom components](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-implementing-components.html) and add them to the application. + +The components are built as web components, which makes them highly reusable in other web frameworks and systems. For more details, see [Integration of components](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-integrating-components.html). + +Oryx provides a reactive framework and is designed to work efficiently in a single page application architecture. To ensure _reactivity_ throughout the application, Oryx rerenders only fragments of the components that are affected by the changing application state. For more details, see [key concepts of reactivity](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/key-concepts-of-reactivity.html). diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-component-types.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-component-types.md new file mode 100644 index 00000000000..87402ccff71 --- /dev/null +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-component-types.md @@ -0,0 +1,54 @@ +--- +title: "Oryx: Component types" +description: Oryx components compared to Atomic Design +last_updated: Sept 23, 2023 +template: concept-topic-template +--- + +Oryx applications are built completely out of components. Whether it's a page or a section of the page or a button, they're all components. + +Oryx supports three types of components: + +- Design system components: highly reusable components that are used to build consistent user interfaces (UIs). +- Domain components: functional components that are concerned with a specific _domain_, like the product or cart domains. +- Composition components: containers that are used to render pages or sections by providing a list of components and their layout. + +Even though Oryx does not implement [Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/), the component types can be roughly mapped to the Atomic Design levels: + +| Atomic Design level | Oryx Component | Examples | +| ------------------- | ------------------------ | ------------------------------ | +| Atoms | Design system components | Button, form element | +| Molecules | Domain components | Product images, cart entry | +| Organisms | Compositions | Product carousel, product list | +| Templates | Composition references | Header, footer | +| Pages | Compositions | Product page, Cart page | + +## Design system components + +The Oryx design system offers a collection of highly reusable components that are essential for building a consistent and visually appealing UI. These components are agnostic of the application's context and serve as the building blocks for domain components. + +Design system components ensure a consistent and cohesive visual language across the application. The components do not interact with application logic directly. Because they don't know anything about their surroundings, they're considered fairly "dumb". They are used by domain components, which provide them with properties and dispatch events that can be used by the outer components. + +You can configure and customize design system components or replace them with your own. This enables you to build the required visual language throughout the application. Any reference to a design system component, like ``, is resolved by your custom version regardless of where it's used. + +Design system components are available in the `ui` package: `@spryker-oryx/ui`. They do not depend on any application logic, except for the integration of localized messages. + +## Domain components + +Oryx functionality is organized in domains. Domain packages contain functional components, also know as _domain components_. For example, all product-related components are organized in the product package. + +Domain components leverage the design system components to ensure a consistent UI/UX. The design system components are integrated with inputs (properties), and all of their events are handled by domain components. + +Domain components integrate with domain services to obtain and update the application state. The services handle the integration with backend APIs and application state management. In a single page application experience, domain components need to support [reactivity](/docs/scos/dev/front-end-development/{{page.version}}/oryx/reactivity/reactivity.html) to ensure the application state is reflected immediately after it is changed. The complexity of reactivity is avoided as much as possible in components by using [signals](/docs/scos/dev/front-end-development/{{page.version}}/oryx/reactivity/signals.html). To avoid repeating the boilerplate code that is required for each domain component, domains often provide a mixin. The mixin provides the required properties and signals that can be used by the components. + +Each domain package contains associated domain components. Product components, for example, are part of the `@spryker-oryx/ui` package. The components use a consistent naming convention for class and element names. For example, the Product Title component, is named `ProductTitleComponent` and can be used with the `` element. To avoid clashes with other frameworks, the elements are prefixed with `oryx-`. + +## Composition components + +Compositions are simple containers of components that are used to organize components on a page or a page section. The list of components and their options is configurable. The configurable approach allows for dynamic experiences that can be used to personalize or split-test experiences. + +Because of this generic approach, all pages and their compositions are rendered as a single component only: `CompositionComponent` (``). The composition component iterates over a list of components and applies layout and options to it. + +To better understand the concepts of pages and compositions, see [Pages](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-pages.html) and [Compositions](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-compositions.html). + +If you want to customize the application with your own pages, there's no need to follow the concept of compositions. You can create page-specific components, like `ProductDetailPageComponent`, and use them instead of using experience data. diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-implementing-components.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-implementing-components.md new file mode 100644 index 00000000000..824f4fd285e --- /dev/null +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-implementing-components.md @@ -0,0 +1,202 @@ +--- +title: "Oryx: Implementing components" +description: Learn how to create an Oryx component +last_updated: Sept 20, 2023 +template: concept-topic-template +--- + +Oryx components are web components built with [Lit](https://lit.dev). Lit is a lightweight open-source framework from Google that's used to build highly efficient web components. Web components can be created with any framework or even with vanilla HTML, CSS, and JavaScript. You can use any other framework instead of Lit. However, some Oryx utilities, like [signals](docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/signals.html) and component mixins, are available only with Lit. + +## Implementing a component + +This document describes how to implement a component using Lit. Lit's standard concepts are not covered. To learn more about them, see [Components overview](https://lit.dev/docs/components/overview/). + +We use the product _ID_ component as an example. It is a simple component that shows the basic concepts. The component already exists in the Oryx product package. + +In Oryx, components are organized in folders, like `src/product/id`, with some component logic located in separate files. However, you can create a component as a single file. To allow for lazy loading of the component, you still need to separate out its definition. + +### 1. Creating a component class + +Oryx components are based on the Web Components standard. One of the features of web components are custom elements. Custom elements are class-based elements that extend from `HTMLElement`. Lit provides `LitElement` as a base class to extend from when you create a custom element. + +```ts +import { LitElement, TemplateResult } from "lit"; + +export class ProductIdComponent extends LitElement { + protected override render(): TemplateResult { + return html`

The product id...

`; + } +} +``` + +Oryx components follow the naming convention for class names: `[Domain][Feature]Component`. In the example of the `ProductIdComponent` component, `Domain` is `Product`, and `Feature` is `Id`. + +{% info_block infoBox %} + +Oryx is built in TypeScript with strict typing configuration. This ensures high-quality standards and a good developer experience. If you use TypeScript in your code, which is optional, you define the typing configuration in `.tsconfig`. + +{% endinfo_block %} + +### 2. Integrating backend data + +In this step, you're going to resolve the product data and render the `id` field of the data. The product data comes from the backend API and is loaded asynchronously. Once the data is loaded, it's part of the _application state_. The state might change over time—for example, when a user navigates from one product page to another. To be able to render the state efficiently, the component must support [reactivity](https://docs.spryker.com/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/reactivity.html). + +Oryx provides standard [application layers](https://docs.spryker.com/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/key-concepts-of-reactivity.html#application-layers) to load and resolve the backend data. The service layer is intended to be used by components, and product components interact with `ProductService`. The integration with the product service and reactivity is simplified by using `ProductMixin`. Mixins provide component properties and methods, which you can use in components. + +{% info_block infoBox "Inheritance versus composition" %} + +While component classes extend from a base class, Oryx mostly avoids inheritance and uses the _composition_ design pattern. Not all the component logic can be composed, which is why mixins are used. + +{% endinfo_block %} + +The following example shows how to extend from `ProductMixin` and consume the product data. + +```ts +import { LitElement, TemplateResult } from "lit"; +import { ProductMixin } from "@spryker-oryx/product"; + +export class ProductIdComponent extends ProductMixin(LitElement) { + protected override render(): TemplateResult { + return html`id: ${this.$product()?.id}`; + } +} +``` + +This code shows the ease of use, but there's a lot going on in the background: + +1. The product _context_ (sku) is resolved from the URL or any of the component's ancestor DOM elements, depending on where the component is used. When the component is used inside a product card or cart entry, the `sku` is added as an attribute. When the component is used on the Product Details Page, the `sku` is resolved from the URL. The current locale and currency are used as additional context. When the context is changing, the product data is reloaded automatically. +2. `ProductService` is used to resolve the product data from the application state. When the product is not yet loaded from the backend, the service uses the `ProductAdapter` adapter to fetch the data. The HTTP response is converted to meet the client-side product model. _Command and Query_, Oryx's state management solution, prevents data reloading unless explicitly requested. +3. The `$product` signal subscribes to the application state using `ProductService`. Whenever the product state is changed, the [signal](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/signals.html) updates the associated DOM elements that are affected by the data. + +The preceding steps are a commonly used pattern across all Oryx domain components. It ensures efficient consumption of backend APIs and rendering of DOM elements. + +### 3. Configuring a component + +Oryx components can be made configurable with options. [Component options](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-managing-component-options.html) can be provided statically to the application or load from a backend API. Component options enable components to be reusable across different business models. For example, a component can render different results based on the provided option: `true` for a B2C application, but `false` for a B2B application. + +Component options are resolved by `ContentMixin`, similar to how `ProductService` resolves the product data. You can combine multiple mixins in a component implementation—for example: + +```ts +import { resolve } from "@spryker-oryx/di"; +import { ContentMixin } from "@spryker-oryx/experience"; +import { ProductMixin } from "@spryker-oryx/product"; + +interface ProductIdOptions { + myOption?: boolean; +} + +export class ProductIdComponent extends ProductMixin( + ContentMixin(LitElement) +) { + protected override render(): TemplateResult { + const { myOption } = this.$options(); + + if (!myOption) return; + + return html`id: ${this.$product()?.id}`; + } +} +``` + +You can provide default options in the component, in feature sets, or in the application. For more details, see [Component options](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-managing-component-options.html). + +### 4. Styling the component DOM + +Oryx components are styled with standard CSS. The components have a separate DOM attached using the open [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM). The shadow DOM encapsulates the styles so that they cannot _leak_ into other components and prevents global styles from cascading down to the component. + +Styling components in the shadow DOM is a big topic we recommend studying separately. However, there are a few things to know when it comes to Oryx and styling components: + +- Design system components are provided in the UI package. Components like `` or `` are used to ensure a common visual language. They can be customized. If your components use the design system as much as possible, you have a consistent design language throughout your application. +- While web components styles do not leak in other components, the font style rules like `font-face` or `font-size` do cascade into web components, no matter how deep they are nested. Standard font rules are provided therefor in the `` component. The [typography](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-typography.html) design tokens are used to ensure consistent styling. +- Custom properties, also known as CSS variables, cascade into web components, which is why the application theme is based on CSS variables. See [Design tokens](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-design-tokens.html) for more information. +- Oryx uses configurable breakpoints to set up the screen size for responsive designs. To avoid hardcoded breakpoints in the component styles, you can configure screen-specific styles in the component definition as described in the following sections. +- You can use Oryx themes and provide component styles for a specific theme. Similarly to breakpoint-specific styles, you can configure styles for a theme. +- Oryx provides an [icon system](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/styling/oryx-icon-system.html) that you can leverage in your components. + +### 5. Localizing messages + +Components often require some text labels or aria labels to guide the user. To support multiple locales, you can leverage [localization](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/oryx-localization.html). + +Localizations are resolved asynchronously, and require the UI to be rerendered whenever they're loaded or reloaded. `ContentMixin`, which you've used earlier to integrate the component options, provides access to the `i18n` directive. The `i18n` directive is available as a class method. The following example shows how to use it: + +```ts +protected render(): TemplateResult | void { + return html`${this.i18n('cart.add-to-cart')}`; +} +``` + +If you do not use `ContentMixin`, you can use `I18nMixin` instead. If you choose to not use mixins, you can integrate the `i18n` directive directly. + +### 6. Using services inside the component + +You've seen how `ProductMixin` resolves the product data and hides the integration with the `ProductService`. It is also common to use services directly in components. Oryx _injects_ services using [dependency injection (DI)](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/dependency-injection/dependency-injection.html). DI provides decoupling of components and shared business logic. This is a common design pattern that separates concerns and lets you customize services without touching the components or other depending services. + +The Oryx DI container is used to register and resolve services using a token. You can read more about resolving services in [Dependency Injection: Using services](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/dependency-injection/dependency-injection-using-services.html). The following example shows how the pricing service is resolved. + +```ts +import { resolve } from "@spryker-oryx/di"; +import { ProductMixin } from "@spryker-oryx/product"; +import { PricingService } from "@spryker-oryx/site"; +import { LitElement } from "lit"; + +export class ProductIdComponent extends ProductMixin(LitElement) { + protected pricingService = resolve(PricingService); +} +``` + +You can now use the pricing service API in the component. Service methods always return observables (using [RxJS](https://rxjs.dev/)), so that the service can be lazy loaded and the response can be used by [signals](docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/signals.html) to update the DOM efficiently. + +### 7. Configuring the component for server-side rendering and hydration + +If your application needs to be indexed by crawlers, such as Google Search or Pinterest, the application needs to be [server-side rendered](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/oryx-server-side-rendering.html). + +When a component is server-side rendered, some of the browser APIs are not available. Most commonly known are the `window` and `document` objects. Take this into account when implementing custom components. + +Oryx renders pages on the server and returns the minimum amount of JavaScript needed. A component doesn't need JavaScript initially, but when a user start interacting with it, or when the component needs to reflect a certain application state, additional JavaScript needs to be loaded. Loading the component logic at the client side is called _hydration_. Because the component logic is loaded over the network and initialized in the application, hydration is costly. Additionally, the component might need to fetch data from a backend API. Oryx therefore tries to avoid or delay hydration till it is needed. + +When developing a component, you need to configure the hydration trigger using the `@hydrate` decorator that can take an event or context. The following example shows how to set up the component to be hydrated when the context is changed: + +```ts +import { resolve } from "@spryker-oryx/di"; +import { ProductMixin } from "@spryker-oryx/product"; +import { hydrate } from "@spryker-oryx/utilities"; + +@hydrate({ context: ProductContext.SKU }) +export class ProductIdComponent extends ProductMixin(LitElement) { + // ... +} +``` + +Alternatively, you can configure hydration to be triggered by a specific event: + +```ts +@hydrate({ event: ["mouseover", "focus"] }) +export class ProductIdComponent extends ProductMixin(LitElement) { + // ... +} +``` + +## Registering the component definition + +The component implementation you've started building in the previous section is not imported anywhere in your application. You need to register the [component definition](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/oryx-providing-component-definitions.html) so that the application can get hold of it, whenever it needs to render the component. + +In the example below, you see how the component is registered inline in the appBuilder. However, we recommend creating a component definition in a separate file and maintain it in the component folder. + +```ts +import { appBuilder } from "@spryker-oryx/application"; + +export const app = appBuilder().withComponents([ + { + name: "oryx-product-id", + impl: () => import("./components/product/id.component"), + }, +]); +``` + +## Using the component in the page structure + +After you've implemented and registered the component, you can use it in the application. For example, in a [page](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-pages.html) or [composition](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-compositions.html), or inside CMS content. + +If you build your first component, you might want to skip creating custom pages altogether, and just see the component in action. You can quickly merge the component into an existing page structure. For example, add the component before or after an existing component. For more details, see [Oryx: Pages](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-pages.html). + +Also, you can merge the component into an existing page structure. For example, before or after an existing component or inside (prepend or append) the components of an existing composition. diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-integrating-components.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-integrating-components.md new file mode 100644 index 00000000000..1e29500f348 --- /dev/null +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-integrating-components.md @@ -0,0 +1,54 @@ +--- +title: "Oryx: Integrating components" +description: Oryx Components are build as web components +last_updated: Sept 20, 2023 +template: concept-topic-template +--- + +Oryx components are _framework agnostic_, so they can be used in other web frameworks. + +Oryx components are build as [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components). Web components are a suite of standard web technologies supported by most browser vendors. The purpose of web components is to provide components in isolation so that they can easily integrate with other web technologies. + +## Integrating Oryx components with other web frameworks + +Thanks to the web-component-based architecture, Oryx components integrate with any web framework. You can integrate them with component frameworks, such as [React](https://react.dev/), [Vue.js](https://vuejs.org/), or [Angular](https://angular.io/). + +You can also integrate Oryx components inside frontend meta frameworks, like [Next.js](https://nextjs.org/), [Nuxt.js](https://nuxt.com/), or [Astro](https://astro.build/). + +{% info_block infoBox %} +While the integration of Oryx components is relatively straightforward, Spryker does not provide production-ready integration boilerplate code. + +The integration of the [server-side rendering](/docs/scos/dev/front-end-development/oryx/oryx-server-side-rendering.html) part might be quite complex. +{% endinfo_block %} + +## Integrating Oryx components with content management systems + +Oryx can render content from other systems, like a headless content management system (CMS). More importantly, Oryx components can render inside the content provided by a CMS. + +When rich content, like markdown, contains Oryx components, the components are rendered as is together with the content. This allows for rich content integrations, like rendering a carousel of upsell products in the middle of storytelling content. + +You can use Oryx components inside rich content from an external CMS. The content is rendered inside Oryx, but any Oryx components inside the content are rendered transparently. This does not require any integration effort. + +The following example shows Oryx components next to standard markdown. + +```markdown +## Markdown example with an integrate Oryx Product images + +Lorem ipsum dolor sit amet, consectetur adipiscing elit... + + + +Duis aute irure dolor in reprehenderit in voluptate velit... +``` + +The next example shows the integration of compositions with layout. We use the product list component with a configuration to render it in a carousel. + +```markdown +## Markdown example with a carousel of products + +Lorem ipsum dolor sit amet, consectetur adipiscing elit... + + + +Duis aute irure dolor in reprehenderit in voluptate velit... +``` diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-managing-component-options.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-managing-component-options.md new file mode 100644 index 00000000000..24ab276856a --- /dev/null +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-managing-component-options.md @@ -0,0 +1,142 @@ +--- +title: "Oryx: Managing components options" +description: Components Options provide reusable components cross business models +last_updated: Sept 19, 2023 +template: concept-topic-template +--- + +Oryx components support configurable options to make the components reusable in different business contexts. Component options are JavaScript objects that can be configured as part of the application configuration or added by providing an attribute. To ensure a good developer experience, each component type can provide an interface for the options. + +To show the usage of component options, we use the tax message ("incl. vat") that is rendered on `ProductPriceComponent`. The tax message might not be useful for all businesses. For example, in a b2c context, the message might not be required. You can configure the message to be loaded conditionally. + +## Setting up component options + +Component options are provided by a TypeScript interface. This ensures a good developer experience when implementing a component and configuring the application. The component option interface is defined in a separate `*.model.ts` file in Oryx components, but there's no strict rule to follow. + +```ts +export interface ProductPriceOptions { + /** + * Indicates whether to show tax message for the price. + */ + enableTaxMessage?: boolean; +} +``` + +To resolve component options in new components, you can use `ContentMixin`. `ContentMixin` provides a type-safe `$option` [signal](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/signals.html) that guarantees efficient usage of the options. + +Oryx provides the `ContentMixin` mixin to work with component options. You can use the mixin to hand in the option interface. The following example shows how mixin is used to define the options. + +```ts +export class ProductPriceComponent extends ContentMixin( + LitElement +) { + // use the $options() signal in your code +} +``` + +## Configuring component options + +There are different ways to configure component options. Components can have default option values that are used as a fallback in case no values are provided. [Feature sets](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/oryx-feature-sets.html) can provide values for a specific business context. As an application developer, you can provide customized values, either for all component instances in the application configuration or by providing options per instance in the experience data. You can also override the options when using components in your code. + +The approaches to set up the values are detailed in the following sections. + +### Default component option values + +To avoid hardcoded default values in the component code, components can have default values for their options. Oryx provides a `@defaultOptions` decorator that components can use to set up the values. The decorated values are used by the `ContentMixin` and are transparent for the component developer. + +The default values are used as a fallback in case there are no specific values configured. Whenever more specific values are configured at a feature set or application, the default options are neglected. + +```ts +@defaultOptions({ + enableTaxMessage: true, +}) +export class ProductPriceComponent extends ContentMixin( + LitElement +) { + // ... +} +``` + +### Configuring feature set component options + +Default component options can be overridden in feature sets. [Feature sets](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-applications/oryx-feature-sets.html) simplify the setup of a specific business model, such as B2B or B2C. Besides providing page structures with components, feature sets can also add component configurations. The feature set configurations override default component values. + +### Configuring application-driven component options + +You can customize default component values and feature set values in the application configuration. The configuration is used every time the component is used in the application. + +The following example shows how to configure an application using `appBuilder`. + +```ts +export const app = appBuilder() + // ... + .withOptions({ + "oryx-product-price": { + enableTaxMessage: false, + }, + }) + .create(); +``` + +For more information, see [Application orchestration](/docs/scos/dev/front-end-development/{{page.version}}/oryx/oryx-application-orchestration/oryx-application-orchestration.html). + +### Configuring component option values in experience data + +The default options, feature set configurations, and application configurations are applied to the Component type. The options provided in the experience data are applied to a specific instance in the experience data structure. + +In the following configuration, you see a part of the experience data that sets the `enableSalesLabel` option to `false`. This configuration is only applied to the instance. This configuration does not affect the component when it's used elsewhere. + +```ts +{ + type: 'oryx-composition', + components: [ + { + type: 'oryx-product-price', + options: { enableSalesLabel: false }, + }, + ] +} +``` + +For more information about creating and customizing experience data, see [Oryx: Pages](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-pages.html). + +### Configuring hardcoded component options + +When using components in code, options can be provided using the `options` attribute. The options attribute is resolved automatically by `ContentMixin` that most domain components use. + +The following example shows how component options are written in the component `options` attribute. When options are added by an attribute, the web component specs require stringified JSON. [Lit](https://lit.dev) provides a convenient property mapping shown in the example below. + +```ts +protected override render(): TemplateResult { + + const options: ProductPriceOptions = { + enableSalesLabel: false; + } + return html``; +} +``` + +## Using component options + +To use component options asynchronously, it is important to observe the options and react to updates in the component UI. Oryx provides a [reactive](/docs/scos/dev/front-end-development/{{page.version}}/oryx/architecture/reactivity/reactivity.html) framework with observable data streams that can update the UI using [signals](/docs/scos/dev/front-end-development/{{page.version}}/oryx/reactivity/signals.html). To simplify the integration in the component logic, `ContentMixin` provides the `$options` signal that can be called in the render logic or other signals. + +The following code shows how to use the `$options` signal. Due to the component option interface, the usage of the signal is type safe. + +```ts +@defaultOptions({ + enableTaxMessage: true, +}) +export class ProductPriceComponent extends ContentMixin( + LitElement +) { + protected override render(): TemplateResult { + return html` ... ${this.renderTaxMessage()} ... `; + } + + protected renderTaxMessage(): TemplateResult | void { + if (!this.$options().enableTaxMessage) return; + + return html`...`; + } +} +``` diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-providing-component-definitions.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-providing-component-definitions.md new file mode 100644 index 00000000000..07be6be641e --- /dev/null +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-components/oryx-providing-component-definitions.md @@ -0,0 +1,37 @@ +--- +title: "Oryx: Providing component definitions" +description: Components are registered in an Oryx application by a definition file +last_updated: Sept 19, 2023 +template: concept-topic-template +--- + +Oryx components can be used in different ways. They can be configured in [pages](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-pages.html) and [compositions](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-pages/oryx-compositions.html), used in components, or integrated in CMS content. + +When a component is rendered for the first time, Oryx resolves the component definition from the registry and loads the associated implementation. With this, components are lazily loaded. + +To register a [component implementation](/docs/scos/dev/front-end-development/{{page.version}}/oryx/building-components/component-implementation.html), you need to provide a component definition. The component definition requires a name and an implementation. The name is used as the web component element name and consists of two or more words separated by a dash. We recommend prefixing component names with a project, brand, or company name. For example, Oryx components are prefixed with `oryx-`. + +{% info_block infoBox "Update definitions" %} +You can also update an existing component definition. To match an existing definition, you still need to provide a name. +{% endinfo_block %} + +The following example shows where a component is registered, providing both the name and implementation. + +```ts +import { componentDef } from "@spryker-oryx/utilities"; + +export const productIdComponent = componentDef({ + name: "oryx-product-id", + impl: () => import("./id.component").then((m) => m.ProductIdComponent), +}); +``` + +The [dynamic `import()` expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) is used to ensure the component is lazily loaded. Dynamic imports are used by build systems like [Vite](https://vitejs.dev/) to create a separate JavaScript chunk during the build. This allows to load the JavaScript chunk upon demand. + +Lazy loading components is the recommended technique as it avoids loading all the application components at startup of the application. Components are only loaded when they are used, which increases the application's performance. + +{% info_block warningBox "Static import of component files" %} + +To prevent breaking the lazy loading principals, do not import component files _statically_. + +{% endinfo_block %} diff --git a/docs/scos/dev/front-end-development/202307.0/oryx/building-pages/oryx-pages.md b/docs/scos/dev/front-end-development/202307.0/oryx/building-pages/oryx-pages.md index 9612476c73e..5bb11e14297 100644 --- a/docs/scos/dev/front-end-development/202307.0/oryx/building-pages/oryx-pages.md +++ b/docs/scos/dev/front-end-development/202307.0/oryx/building-pages/oryx-pages.md @@ -183,14 +183,16 @@ export const app = appBuilder() .create(); ``` +The merge types are given by the `ExperienceDataMergeType` enumeration, which is exported in the [@spryker-oryx/experience](https://www.npmjs.com/package/@spryker-oryx/experience) package. + The following table gives an overview of the various merge types. -| STRATEGY | DESCRIPTION | -| ---- | - | -| `replace` (default) | Replaces the selected element with the given content. | -| `patch` | Patches the selected component with the given component. This includes both the component options and content. All data, except for arrays, is deep-merged. | -| `remove` | Removes the selected component. | -| `before` | Adds the content before the selected component. | -| `after` | Adds the content after the selected component. | +| STRATEGY | DESCRIPTION | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `replace` (default) | Replaces the selected element with the given content. | +| `patch` | Patches the selected component with the given component. This includes both the component options and content. All data, except for arrays, is deep-merged. | +| `remove` | Removes the selected component. | +| `before` | Adds the content before the selected component. | +| `after` | Adds the content after the selected component. | | `append` | Adds the content after the last component of the composition components. If the selected component is not a composition, the custom component is not merged. | | `prepend` | Adds the content before the first component of the composition components. If the selected component is not a composition, the custom component is not merged. |