Skip to content

Commit

Permalink
Merge pull request #5381 from vuestorefront/docs-next
Browse files Browse the repository at this point in the history
Merge back docs to next
  • Loading branch information
filrak committed Jan 7, 2021
2 parents a287cee + 34aef79 commit a7fd035
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 33 deletions.
6 changes: 2 additions & 4 deletions packages/core/docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ module.exports = {
collapsable: false,
children: [
['/general/architecture', 'Architecture'],
['/general/i18n', 'i18n'],
['/general/error-handling', 'Error Handling'],
['/general/logging', 'Logging'],
['/general/performance', 'Performance'],
Expand All @@ -150,8 +149,7 @@ module.exports = {
collapsable: false,
children: [
['/guide/theme', 'Theme'],
['/guide/internationalization', 'Internationalization'],

['/guide/internationalization', 'Internationalization']
]
},
{
Expand Down Expand Up @@ -199,7 +197,7 @@ module.exports = {
['/contributing/api-design-philosophy', 'Rules and conventions'],
['/contributing/themes', 'Working with themes'],
['/contributing/server-side-rendering', 'Server-side rendering'],
['/contributing/changelog', 'Core Changelog'],
['/contributing/changelog', 'Core Changelog']
]
},
],
Expand Down
3 changes: 1 addition & 2 deletions packages/core/docs/composables/use-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Use `useReview` composition function can be used to:
## How to use it in your project?

```js
import { useReview, reviewGetters } from '{INTEGRATION}';
import { onSSR } from '@vue-storefront/core';
import { useReview, reviewGetters } from '@vsf-enterprise/ct-reviews';

export default {
setup() {
Expand All @@ -26,7 +26,6 @@ export default {
error
} = useReview('<UNIQUE_ID>');

// If you're using Nuxt or any other framework for Universal Vue apps
onSSR(async () => {
await search({ productId: '<PRODUCT_ID>' });
});
Expand Down
71 changes: 69 additions & 2 deletions packages/core/docs/contributing/api-design-philosophy.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,73 @@ While designing something so complex as Vue Storefront it's vital to set up rule
1. We build **simple**, **declarative** and **general-purpose** APIs that are not tied to implementation details or a specific solution to a problem. That way we can ensure that our APIs will remain general-purpose and won't break on updates even if we do heavy changes in the underlying business logic.
2. API Surface should be possibly minimal. If there is a feature that can be achieved with already existing APIs we shouldn't add new ones just to make it simplier.
3. Focus on good defaults and embracing [convention over configuration](https://en.wikipedia.org/wiki/Convention_over_configuration) paradigm. Every API should work as it is for most of the use cases and have ability to be configured for other ones.
4. APIs should not limit the users. If we can't fulfill all use cases with parameters we should provide extension points so users can do this by themselves.
5. Same code should work on every platform (excluding specific object properties and search params)
4. If you introduce a new, commonly used feature (like cache) try to provide a default configuration out of the box and let users customize it so they don't have to configure for the most common use cases, only for custom ones. This approach will dreastically reduce the number of boilerplate code users has to write.
```js
import { ueProduct } from '@vue-storefront/{eCommerce}'
import { cacheManager } from '@vue-storefront/core'

const { search } = useProduct()

// composable is handling most common scenario under the hood
search({ id: '123'}) // under the hood adds cache tag `P123`

// you can always modify the default tags
cacheManager.setTags((tags) => {
tags.push('C123')
return tags
})
```
5. APIs should not limit the users. If we can't fulfill all use cases with parameters we should provide extension points so users can do this by themselves.
6. Same code should work on every platform (excluding specific object properties and search params)
7. Follow Domain-Driven Design principles. Try to keep everything related to a specific domain within its composable.
```
"Separating concerns by files is as effective as separating school friendships by desks. Concerns are “separated” when there is no coupling: changing A wouldn’t break B. Increasing the distance without addressing the coupling only makes it easier to add bugs.
~ Dan Abramov
```
8. Composables should be independent and rely on each other only if they are from the same group (`useUser` `useUserOrders`). The only exception is `useUser` that has to be used in many other composables.
9. If you introduce a new feature shared across all/many composables (like Logging/cache) users should be able to configure this feature from core/core nuxt module.
```ts
// every function in composables is using logger
const removeFromCart = async (product: CART_ITEM, customQuery?: CustomQuery) => {
Logger.debug('userCart.removeFromCart', { product })
loading.value = true;
const updatedCart = await factoryParams.removeFromCart(
{
currentCart: cart.value,
product
},
customQuery
);
cart.value = updatedCart;
loading.value = false;
};
```
```js
// there is only one palce where we're configuring the logger/cache - core
['@vue-storefront/nuxt', {
coreDevelopment: true,
logger: customLogger
}]
```
10. If a feature is platform-specific and not shared across whole application provide integration through its config/nuxt module.
11. Provide a core interface for every feature, no matter if its paid or not (implementation can be paid, the way of implementing this feature by the user has to be always provided)

## Composables

We try to cover each subdomain of the eCommerce domain with a dedicated composable. For example we have a composable for Users Management domain, inventory domain, product catalog domain etc. If you have to add a new feature always think about business domain it correlates to and based on that decide if it should be a new composable or an existing one.

If composables share the same category/prefix it means that they most likely also share the same context eg. `useUserOrders` `useUserShipping` `useUserBilling` are all subcomposables of `useUser` and their content depends on this composable.

Each composable has usually 3 pieces:
- main data object (eg `products`)
- supportive data object/s (eg `loading`, `error`)
- search/load function (eg `search`)

```js
const { search, products, loading, erro } = useProduct()
```

### search or load
As a rule of thumb use
- `search` when you have to pass some search parameters (eg. in products search)
- `load` when you just have to load some content based on cookies/local storage etc (eg. cart load)
44 changes: 22 additions & 22 deletions packages/core/docs/general/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ A flexible way of error handling is essential for a framework like Vue Storefron

Each composable returns `error` - computed property. It is an object which has names of async functions from composable as keys and Error instance or null as a value.

Example usage:
```vue
<template>
<button @click="addToCart(product)">Add to cart</button>
Expand Down Expand Up @@ -37,29 +36,10 @@ export interface UseCartErrors {
}
```

## How to listen for errors?
Let's imagine you have some global components for error notifications. You want to send information about each new error to this component. But how to know when new error appears? You can observe error object with a simple watcher!

```ts
const { cart, error } = useCart()

watch(error, (error, prevError) => {
if (error.value.addItem && error.value.addItem !== prevError.value.addItem) sendInAppNotification('error', error.value.addItem.message)
if (error.value.removeItem && error.value.removeItem !== prevError.value.removeItem) sendInAppNotification('error', error.value.removeItem.message)
})
```

## Where can I find interface of the error property from a certain composable?
When you are writing a code inside a script part of the Vue's component, your IDE should give you hints dedicated for each type of composable. That's why you probably do not need to check these interfaces in the core's code.

However, if somewhy you still want to do that, you could find them inside [`packages/core/core/src/types.ts`](https://github.com/vuestorefront/vue-storefront/blob/next/packages/core/core/src/types.ts). Just search for `UseCartErrors` with your IDE inside.

Feel free to replace `UseCartErrors` with other composable name - `UseFacetErrors`, `UseWishlistErrors`, `UseProductErrors` etc.
:::details Where does the error come from?

## Where does error come from?
To better understand this part you should know what are factories of composables in our core.

Inside each factory's async method we are clearing the current error before integration's method call and setting it in catch block.
Inside each async method we are clearing the current error before integration's method call and setting it in catch block.
```ts
const addItem = async ({ product, quantity, customQuery }) => {
Logger.debug('useCart.addItem', { product, quantity });
Expand All @@ -85,3 +65,23 @@ const addItem = async ({ product, quantity, customQuery }) => {
}
};
```
:::

:::details Where can I find interface of the error property from a certain composable?

When you are writing a code inside a script part of the Vue's component, your IDE should give you hints dedicated for each type of composable. That's why you probably do not need to check these interfaces in the core's code.

However, if somewhy you still want to do that, you could find them inside [`packages/core/core/src/types.ts`](https://github.com/vuestorefront/vue-storefront/blob/next/packages/core/core/src/types.ts).

:::
## How to listen for errors?
Let's imagine you have some global components for error notifications. You want to send information about each new error to this component. But how to know when new error appears? You can observe error object with a simple watcher!

```ts
const { cart, error } = useCart()

watch(error, (error, prevError) => {
if (error.value.addItem && error.value.addItem !== prevError.value.addItem) sendInAppNotification('error', error.value.addItem.message)
if (error.value.removeItem && error.value.removeItem !== prevError.value.removeItem) sendInAppNotification('error', error.value.removeItem.message)
})
```
19 changes: 16 additions & 3 deletions packages/core/docs/guide/internationalization.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
# Internationalization

By default we are using [`nuxt-i18n`](https://i18n.nuxtjs.org/) module for handling internationalization, but it's not mandatory to use it even if you are using Nuxt.
If you're building a shop for an international brand you want it being translated to different languages and using different currencies. In this doc you will learn how we're approaching i18n (internationalization) in Vue Storefront.

In order to provide a unified way of configuring i18n across the application for different modules and integrations, we have introduced the field `i18n` in each module's configuration that has the same format as `nuxt-i18n` options. Clearly, it's possible to add there any configuration if there is a necessity and it will be propagated to all other Vue Storefront modules.
::: tip i18n is not multi-tenancy!
This document explains only how to make a single shop instance available for multiple countries. If you need to build a system for multiple tenants we suggest creating an instance of Vue Storefront for each tenant and sharing common resources through an NPM package.
:::

By default, all Vue Storefront modules have `useNuxtI18nModule` property set to `true`. It means that they will use the same configuration as you provided for `nuxt-i18n` in `i18n` field of your `nuxt.config.js`
## Default setup
By default we are using [`nuxt-i18n`](https://i18n.nuxtjs.org/) module for handling both translations and currencies.

In the theme `nuxt-i18n` is using `$t('key')` to translate strings and `$n(number)` to add the currency sign. You can find the translation keys in `lang` directory of your project.

::: tip
Even though the module is included into the default theme it's not mandatory for your app to work and [you can always get rid of it.](#custom-configuration).
:::

In order to provide a unified way of configuring i18n across the application for different modules and integrations, we have introduced the field `i18n` in each module's configuration that has the same format as `nuxt-i18n` options. Add there any configuration if there is a necessity and it will be propagated to all other Vue Storefront modules.

All Vue Storefront integrations have `useNuxtI18nModule` property set to `true`. It means that they will use the same configuration as you provided for `nuxt-i18n` in `i18n` field of your `nuxt.config.js`

```js
// nuxt.config.js
Expand Down

0 comments on commit a7fd035

Please sign in to comment.