diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..5541ef677 Binary files /dev/null and b/.DS_Store differ diff --git a/_data/navigation.yml b/_data/navigation.yml index 8e3aac083..e185e7478 100755 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -131,6 +131,17 @@ docs: - title: Storefront Development Guide url: /dev-guide/ children: + - title: "Best practices ➢" + url: /best-practises/ + subchildren: + - title: "Layout best practises➢" + url: /layout-best-practices/ + - title: "I18n best practises➢" + url: /i18n-best-practices/ + - title: "Structure best practises➢" + url: /structure-best-practices/ + - title: "Extending best practises➢" + url: /extending-best-practices/ - title: "Accessibility ➢" url: /a11y/ subchildren: diff --git a/_pages/dev/best-practises/best-practises.md b/_pages/dev/best-practises/best-practises.md new file mode 100644 index 000000000..7fab793e3 --- /dev/null +++ b/_pages/dev/best-practises/best-practises.md @@ -0,0 +1,14 @@ +--- +title: Best practises +--- + +This is a landing page for developers best practises topics. It includes the following: + +- [{% assign linkedpage = site.pages | where: "name", "layout-best-practices.md" %}{{ linkedpage[0].title }}]({{ site.baseurl }}{% link _pages/dev/best-practises/layout-best-practices.md %}) +- [{% assign linkedpage = site.pages | where: "name", "naming-best-practices.md" %}{{ linkedpage[0].title }}]({{ site.baseurl }}{% link _pages/dev/best-practises/naming-best-practices.md %}) +- [{% assign linkedpage = site.pages | where: "name", "i18n-best-practices.md" %}{{ linkedpage[0].title }}]({{ site.baseurl }}{% link _pages/dev/best-practises/i18n-best-practices.md %}) +- [{% assign linkedpage = site.pages | where: "name", "structure-best-practices.md" %}{{ linkedpage[0].title }}]({{ site.baseurl }}{% link _pages/dev/best-practises/structure-best-practices.md %}) +- [{% assign linkedpage = site.pages | where: "name", "extending-best-practices.md" %}{{ linkedpage[0].title }}]({{ site.baseurl }}{% link _pages/dev/best-practises/extending-best-practices.md %}) +- [{% assign linkedpage = site.pages | where: "name", "styles-best-practices.md" %}{{ linkedpage[0].title }}]({{ site.baseurl }}{% link _pages/dev/best-practises/styles-best-practices.md %}) + +All of the above instructions are only suggestions on how to use Spartacus from the core team members. diff --git a/_pages/dev/best-practises/extending-best-practices/extending-best-practices.md b/_pages/dev/best-practises/extending-best-practices/extending-best-practices.md new file mode 100644 index 000000000..4666c2e15 --- /dev/null +++ b/_pages/dev/best-practises/extending-best-practices/extending-best-practices.md @@ -0,0 +1,99 @@ +--- +title: Extending Best Practices +--- + +The Spartacus library gives us great opportunities. Many Spartacus elements can be reused and some of them just need to be carefully extended in our own way. + +Extending the elements should be the basic action we will perform in Spartacus implementation projects. + +Each time you have to implement a new feature or a single functionality, find it or something similar in the Spartacus Code and analyse how much you can reuse. + +### Extending components + +In many cases, we will only need to implement the component appearance because of the designs. What we should do is to create our own component, extends it with Spartacus component and use our own template and styles. If we have to add any new method or override existing we can to it in our own component. + +``` +@Component({ + selector: 'custom-cart-totals', + templateUrl: './cart-totals.component.html', + styleUrls: ['./cart-totals.component.scss'] +}) +export class CustomCartTotalsComponent extends CartTotalsComponent implements OnInit { + + constructor( + activeCartService: ActiveCartService + ) { + super(activeCartService) + } + + ngOnInit(): void { + super.ngOnInit(); + // additional action + } + + customMethod(): void { + // custom method + } + + customMethodWithActiveCartService(): Observable { + // Custom method + // return this.activeCartService.getActive(); + } +} +``` + +### Extending adapters, services, serializers, guards + +If we need to extend any adapter/service/serializer/guard we should do it in a similar way to the component. Creating own custom adapter/service/serializer/guard and extending the Spartacus adapter/service/serializer/guard with our own. Additionally, we have to provide it in our module. + +``` +@NgModule({ + imports: [ + CommonModule + ], + providers: [ + { + provide: CartAdapter, + useClass: CustomCartAdapter, + }, + { + provide: ActiveCartService, + useClass: CustomActiveCartService + }, + { + provide: SiteContextUrlSerializer, + useClass: CustomSiteContextUrlSerializer, + }, + { + provide: AuthGuard, + useClass: CustomAuthGuard, + }, + ] +}) +export class CustomCartTotalModule { } +``` + +### Extending pageMetaResolvers and normalizers + +Extending Page Meta Resolvers or Normalizer looks the same as with adapters, the only difference is providing them. + +``` +@NgModule({ + imports: [ + CommonModule + ], + providers: [ + { + provide: PageMetaResolver, + useExisting: CustomCartPageMetaResolver, + multi: true, + }, + { + provide: PRODUCT_NORMALIZER, + useClass: CustomProductNormalizer, + multi: true, + }, + ] +}) +export class CustomCartTotalModule { } +``` \ No newline at end of file diff --git a/_pages/dev/best-practises/i18n-best-practises/i18n-best-practises.md b/_pages/dev/best-practises/i18n-best-practises/i18n-best-practises.md new file mode 100644 index 000000000..9d9b99413 --- /dev/null +++ b/_pages/dev/best-practises/i18n-best-practises/i18n-best-practises.md @@ -0,0 +1,35 @@ +--- +title: I18n Best Practices +--- + +Spartacus project has default translations. It is obvious that in each project we will need to add our own translations. + +The best place to do it is to create file `i18n.ts` in `src/app/spartacus/configurations` folder and export it as `I18nConfig`. We sincerely suggest to add only global translations here and to add all the rest related to individual modules translations in those modules (describe in modules-best-practices). + +For global translations use create `i18n-assets` inside `assets` and than create seperate folder for each language, example: `en`, `de`, etc. + +`i18n.ts` example: + +``` +export const customI18nConfig: I18nConfig = { + i18n: { + backend: { + loadPath: 'assets/i18n-assets/{{lng}}/{{ns}}.json', + }, + chunks: { + common: ['custom_common_chunk',], + errors: ['custom_error_chunk'], + }, + fallbackLang: 'en', + }, +}), +``` + +Once we will create the file and provide our own customization we have to provide it in `src/app/spartacus/spartacus-configuration.module.ts`: + +``` +@NgModule({ + ... + providers: [ + provideConfig(customI18nConfig), + ... diff --git a/_pages/dev/best-practises/layout-best-practices/layout-best-practices.md b/_pages/dev/best-practises/layout-best-practices/layout-best-practices.md new file mode 100644 index 000000000..4421ae75b --- /dev/null +++ b/_pages/dev/best-practises/layout-best-practices/layout-best-practices.md @@ -0,0 +1,90 @@ +--- +title: Layout Best Practices +--- + +## Layout config + +By design, each Spartacus project is based on CMS components from Back Office. Spartacus has default `layoutConfig` but it is based on `SpartacusSampleData` and it is unlikely to fit in any other design. + +That is why we can override the default Spartacus layout config with our own one. + +The best place to do it is to create file `layout.ts` in `src/app/spartacus/configurations` folder and export it as `LayoutConfig`. We can put it here only differences with Sparactus config or provide our custom whole layout config. + +Another very common case is adding additional layouts to the configuration and assigning them slots, example: + +```export const customLayoutConfig: LayoutConfig = { + layoutSlots: { + prologue: { + slots: [ + 'PreHeader', + 'SearchBox', + 'SiteLogo', + ], + }, + } +}; + +There is a possibility to add breakpoints in the layout config, but it affects performance and causes flickering in SSR. If it is possible, responsiveness should be achieved via CSS whenever it is possible. + +Once we will create the file and provide our own customization we have to provide it in `src/app/spartacus/spartacus-configuration.module.ts`: + +``` +@NgModule({ + ... + providers: [ + provideConfig(customLayoutConfig), + ... + +``` + +## Storefront layout + +In many projects the cx-storefront directive usage will be sufficient, but it is highly probable that to facilitate the work on spartacus it will be easier for us to slightly modify the default storefront layout. + +In this case we suggest to: +1. Create module with component `-storefront` in `src/app/-storefront` folder. +2. Extend our own `StorefrontComponent` with Spartacus `StorefrontComponent`. +3. Copy Spartacus Storefron Component template and paste it in our own Storefront template with our own customization. We can here: + - modify elements order + - add custom elements +4. Import in your Storefront Module all required modules +5. Import your Storefront module in `AppModule` + +Example custom storefront template: + +``` + + + + +
+ + +
+ + +
+ +
+ +
+ + +
+ +
+
+ + +
+ +``` diff --git a/_pages/dev/best-practises/naming-best-practices/naming-best-practices.md b/_pages/dev/best-practises/naming-best-practices/naming-best-practices.md new file mode 100644 index 000000000..bc102af64 --- /dev/null +++ b/_pages/dev/best-practises/naming-best-practices/naming-best-practices.md @@ -0,0 +1,8 @@ +--- +title: Naming Best Practices +--- + +We highly suggest naming all custom elements with a project prefix. This will avoid problems with importing elements. It is easy to mistake a custom module with a Spartacus module, for example: + +Instead of own module `CartTotalModule` use `CartTotalModule` where `your_prefix` is globally used prefix in the project. + diff --git a/_pages/dev/best-practises/structure-best-practices/structure-best-practices.md b/_pages/dev/best-practises/structure-best-practices/structure-best-practices.md new file mode 100644 index 000000000..40ffa0d24 --- /dev/null +++ b/_pages/dev/best-practises/structure-best-practices/structure-best-practices.md @@ -0,0 +1,108 @@ +--- +title: Structure Best Practices +--- + +Spartacus schematics installation gives us the basic structure of folder structure. It is adding `spartacus` folder with required modules and `feature` folder with feature libs and their modules. We are suggesting to not modify `spartacus` folder with any own folder/files because of schematics installation and possible conflicts in the future. The only once modification we can suggest is about `configuration` folder describe in `layout-best-practices.md` and `i18n-best-practices.md`. + +## Our structure recommandations: + +### Shared folder + +Under `app`, create `shared` folder. This folder will contain all elements used globally in the project like cms-components, components, adapters, connectors, guards, configs, directives, pipes, etc. + +We suggest creating a separate folder for each of the above examples. Then divide the folder for components into: `components` and `cms-components`, the first one is to keep shared components, but the second one is to keep all shared components with cms mapping. + +### Features folder + +Next folder we recommand to create is `features` folder next to the `shared`. This folder will containt all page main features/modules. For each page funcionality/module we should create seperate folder, including following folders: +- components +- services +- adapters +- configs +- assets +- ... + +Below there is an example of the structure: + +Shopping Cart + +### Features module + +For each feature module, we are suggesting to provide all configs in main feature module: + +``` +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CustomCartTotalModule } from './components/cart-total/cart-total.module'; +import { + cartCmsConfig, + cartOccConfig, + cartRoutingConfig, + cartTranslationsConfig, +} from './configs/cart.config'; +import { provideConfig } from '@spartacus/core'; + +@NgModule({ + imports: [CommonModule, CustomCartTotalModule], + providers: [ + provideConfig(cartCmsConfig), + provideConfig(cartOccConfig), + provideConfig(cartRoutingConfig), + provideConfig(cartTranslationsConfig), + ], +}) +export class CustomCartModule {} +``` + +with config file `cart.config.ts` in `src/app/features/cart/configs/cart.config.ts`: + +``` +import { + CmsConfig, + I18nConfig, + OccConfig, + RoutingConfig, +} from '@spartacus/core'; + +export const cartRoutingConfig: RoutingConfig = { + routing: { + routes: { + customRoute: { + paths: ['cart/custom'], + }, + }, + }, +}; + +export const cartOccConfig: OccConfig = { + backend: { + occ: { + endpoints: { + cart: 'users/${userId}/carts/${cartId}?fields=Full', + }, + }, + }, +}; + +export const cartCmsConfig: CmsConfig = { + cmsComponents: { + CartTotalsComponent: { + component: CustomCartTotalsComponent, + }, + }, +}; + +const cartTranslationsOverwrites = { + en: { + cart: { + custom: 'Custom', + }, + }, +}; + +export const cartTranslationsConfig: I18nConfig = { + i18n: { + resources: cartTranslationsOverwrites, + }, +}; +``` diff --git a/_pages/dev/best-practises/styles-best-practices/styles-best-practices.md b/_pages/dev/best-practises/styles-best-practices/styles-best-practices.md new file mode 100644 index 000000000..faa7753a4 --- /dev/null +++ b/_pages/dev/best-practises/styles-best-practices/styles-best-practices.md @@ -0,0 +1,5 @@ +--- +title: Styles Best Practices +--- + +Spartacus styles are optional and if the project has a very custom design and has time to do everything from scratch, it's best to turn them off immediately. Avoid using global styles to overwrite Spartacus styles. diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 000000000..c0a47494b Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/images/.DS_Store b/assets/images/.DS_Store new file mode 100644 index 000000000..12797510f Binary files /dev/null and b/assets/images/.DS_Store differ diff --git a/assets/images/structure-best-practises-1.png b/assets/images/structure-best-practises-1.png new file mode 100644 index 000000000..07afa8bae Binary files /dev/null and b/assets/images/structure-best-practises-1.png differ