diff --git a/.eslintignore b/.eslintignore index 6a3935a8e..38b4aa797 100755 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,8 @@ **/node_modules/**/* **/lib/* -packages/composables/nuxt/plugin.js -packages/api-client/types/GraphQL.ts +packages/api-client/src/types/GraphQL.ts +packages/api-client/server +packages/composables/lib +packages/api-client/lib +packages/theme/static/sw.js +.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js index d0858feb9..5327c1907 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -13,8 +13,8 @@ module.exports = { parser: '@typescript-eslint/parser', project: [ resolve(__dirname, './tsconfig.json'), - resolve(__dirname, './packages/api-client/tsconfig.json'), - resolve(__dirname, './packages/composables/tsconfig.json'), + resolve(__dirname, './packages/api-client/tsconfig.eslint.json'), + resolve(__dirname, './packages/composables/tsconfig.eslint.json'), resolve(__dirname, './packages/theme/tsconfig.json'), resolve(__dirname, './packages/theme/tests/e2e/tsconfig.json'), ], @@ -39,6 +39,22 @@ module.exports = { } ], "no-plusplus": "off", - } + }, + overrides: [ + { + "files": ["packages/theme/tests/e2e/**/*"], + "rules": { + "jest/expect-expect": "off", + "promise/catch-or-return": "off", // conflicts with Cypress.Chainable + "promise/always-return": "off", + } + }, + { + "files": ["internals/**/*"], + "rules": { + "unicorn/prefer-module": "off", + } + } + ] } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c81da536b..d1ed60d80 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,5 +36,10 @@ jobs: - name: Build packages run: yarn build + # typescript-eslint needs all packages to be built + # to check type-related linting rules + - name: Check for linting errors + run: yarn lint + - name: Test theme run: yarn test:theme diff --git a/commitlint.config.js b/commitlint.config.js index 28fe5c5bf..047108857 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1,2 @@ -module.exports = {extends: ['@commitlint/config-conventional']} +// eslint-disable-next-line unicorn/prefer-module +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index d91eaf8db..900793535 100755 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -51,6 +51,7 @@ module.exports = { ], themeConfig: { GTM_TAG, + sidebarDepth: 0, repo: 'https://github.com/vuestorefront/magento2/', editLinks: true, docsDir: 'docs', @@ -76,11 +77,18 @@ module.exports = { ], }, { - title: 'Creating a Storefront', + title: 'Getting started', collapsable: false, children: [ - ['/guide/creating-a-store', 'Creating a Store'], - ['/guide/configuration', 'Configuration'], + ['/getting-started/configure-magento', 'Configuring Magento'], + ['/getting-started/configure-integration', 'Configuring Vue Storefront'], + ], + }, + { + title: 'Guides', + collapsable: false, + children: [ + ['/guide/image-optimization', 'Image optimization'], ['/guide/override-queries', 'Override queries'], ['/guide/testing', 'Testing'], ['/guide/recaptcha', 'ReCaptcha'], diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl index fad3ec455..9cf26251a 100644 --- a/docs/.vuepress/styles/index.styl +++ b/docs/.vuepress/styles/index.styl @@ -25,3 +25,8 @@ a code { color: $accentColor !important; font-weight: bold; } + +div.theme-default-content:not(.custom) { + max-width: 1024px; +} + diff --git a/docs/api-reference/magento-api.carttotalqty.md b/docs/api-reference/magento-api.carttotalqty.md deleted file mode 100644 index 5eca60faf..000000000 --- a/docs/api-reference/magento-api.carttotalqty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@vue-storefront/magento-api](./magento-api.md) > [cartTotalQty](./magento-api.carttotalqty.md) - -## cartTotalQty variable - -Signature: - -```typescript -_default: (context: Context, cartId: string, customQuery?: CustomQuery) => Promise> -``` diff --git a/docs/api-reference/magento-api.wishlistitemscount.md b/docs/api-reference/magento-api.wishlistitemscount.md deleted file mode 100644 index a67972b8f..000000000 --- a/docs/api-reference/magento-api.wishlistitemscount.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@vue-storefront/magento-api](./magento-api.md) > [wishlistItemsCount](./magento-api.wishlistitemscount.md) - -## wishlistItemsCount variable - -Signature: - -```typescript -_default: (context: Context, customQuery?: CustomQuery) => Promise> -``` diff --git a/docs/assets/images/magento-marketplace-access-keys.jpg b/docs/assets/images/magento-marketplace-access-keys.jpg new file mode 100644 index 000000000..1cb6f6e8b Binary files /dev/null and b/docs/assets/images/magento-marketplace-access-keys.jpg differ diff --git a/docs/getting-started/configure-integration.md b/docs/getting-started/configure-integration.md new file mode 100644 index 000000000..a6799cd81 --- /dev/null +++ b/docs/getting-started/configure-integration.md @@ -0,0 +1,85 @@ +# Configuring Vue Storefront for Magento 2 + +This guide explains the steps needed to install and set up Vue Storefront for Magento 2. + +## Prerequisites + +Before you can get started, you need the following: + +- **Node.js 16** installed, +- Magento 2 server configured following the [Configuring Magento](/getting-started/configure-magento.html) guide. + +To check the Node version you are using, run the following command: + +```bash +node -v +``` + +## Creating the Vue Storefront for Magento 2 + +To install Vue Storefront, run the command below. It will ask you for the project name and the integration of your choice. Keep in mind that the project name will also be used as the folder name, and be sure to select the Magento 2 integration. + +```sh +npx @vue-storefront/cli init +``` + +### 1. Configure environment variables + +After installation, the first step is configuring the integration using the environment variables. + +1. Go to the root folder of your project. +2. Make a copy of the `.env.example` file and rename it to `.env`. You can do it manually or use the following command: + + ```sh + cp .env.example .env + ``` + +3. Update values in the `.env` file. + +### 2. Setup store configuration + +The `plugins/storeConfigPlugin.ts` plugin loads store configuration data from Magento and saves it into the Pinia store under the `$state.storeConfig` property. By default, the amount of data loaded from Magento is minimal to avoid over-fetching, but as your application grows, you might need to pull more data. + +This plugin loads the store configuration data based on a query in the `plugins/query/StoreConfig.gql.ts` file. You can modify this file to change what data is loaded. + +### 3. Configure multistore and localization + +Each Magento store view needs a corresponding locale configuration object in the `i18n` object in the `nuxt.config.js` file. + +#### 3.1 `i18n.locales` + +The `i18n.locales` array contains all supported locales. Each item in this array is an object containing the following properties: + +- `code` - unique identifier of the locale corresponding to Magento store view code. +- `file` - the name of the file containing translations for this locale in the `lang` folder. +- `iso` - corresponding ISO country code. + +For other properties please follow official [nuxt-i18n documentation](https://i18n.nuxtjs.org/options-reference#locales). + +In the example configuration below, you need to have two Magento store views with corresponding store codes: `default` and `german`. + +```javascript +// nuxt.config.js + +export default { + locales: [ + { + code: 'default', + file: 'en.js', + iso: 'en_US', + }, + { + code: 'german', + file: 'de.js', + iso: 'de_DE', + } + ] +}; +``` + +#### 3.2 Translations + +When working with translation in your application, you need to: + +1. Add translations in Magento for products and categories. +2. Update the `i18n.locales` array in the `nuxt.config.js` file and add translations to the corresponding files in the `lang` directory. diff --git a/docs/getting-started/configure-magento.md b/docs/getting-started/configure-magento.md new file mode 100644 index 000000000..122c92853 --- /dev/null +++ b/docs/getting-started/configure-magento.md @@ -0,0 +1,137 @@ +# Configuring Magento store + +This guide explains the step needed to install and set up Magento 2 store for Vue Storefront. + +## Prerequisites + +Before you can get started, you need the following: + +- **Docker Desktop** to setup Magento 2 locally, +- **Magento Marketplace account** to download Magento 2. To create it, visit [this page](https://account.magento.com/customer/account/create/). + +## Creating the Magento 2 store + +We're going to install Magento 2 inside the `server` folder. Run the following commands to create a `server` folder first: + +```sh +mkdir server +cd server +``` + +### 1. Get Magento Marketplace access keys + +Registry that stores Magento and other third-party packages require authentication. You'll need to generate access keys in the Magento Marketplace to install them. + +Follow the [Get your authentication keys](https://devdocs.magento.com/guides/v2.4/install-gde/prereq/connect-auth.html) guide to generate new access keys. + +![Access keys generated in Magento Marketplace](../assets/images/magento-marketplace-access-keys.jpg) + +### 2. Install Magento 2 store + +To simplify the setup, let's use the [`markshust/docker-magento`](https://github.com/markshust/docker-magento) script. + +Run the command below to create the store. It will ask you for the Username and Password. Use your public access key as a username and your private access key as a password from the previous step. + +```sh +curl -s https://raw.githubusercontent.com/markshust/docker-magento/master/lib/onelinesetup | bash -s -- magento.test 2.4.4 +``` + +### 3. Setup authentication + +In the Magento 2 folder, copy the `src/auth.json.sample` file and rename it to `src/auth.json`. You can do it manually or use the following command: + +```sh +cp src/auth.json.sample src/auth.json +``` + +Update the username and password values with your Magento public and private keys. + +```json +{ + "http-basic": { + "repo.magento.com": { + "username": "a1c69cb…", + "password": "af041560…" + } + } +} +``` + +Finally, copy the file to the container by running the following command: + +```sh +bin/copytocontainer auth.json +``` + +### 4. Increase default GraphQL query complexity + +For security reasons, Magento 2, by default, allows maximum GraphQL query complexity of 300 and depth of 20. You need to change these values using the [GraphQL CustomConfig module](https://github.com/caravelx/module-graphql-config), which allows configuring these values in the admin panel. + +To install the Magento 2 GraphQL Config module, run the following commands on your Magento installation: + +```bash +composer require caravelx/module-graphql-config +php bin/magento module:enable Caravel_GraphQlConfig +php bin/magento setup:upgrade +php bin/magento setup:di:compile +php bin/magento setup:static-content:deploy +``` + +Then go to the admin panel, find the configuration panel of the `GraphQL CustomConfig` module, and set: + +- **complexity** to 1500, +- **depth** to 20. + +For more information, see the [GraphQL security configuration](https://devdocs.magento.com/guides/v2.4/graphql/security-configuration.html) page. + +### 5. Enable CORS + +You may need to enable CORS origins in your Magento store to accept requests from other domains, e.g., `magento.dev`. In the Magento 2 folder, add the package `graycore/magento2-cors` by running the command below: + +```sh +bin/composer require graycore/magento2-cors +``` + +Then, add the following configuration at the end of `src/app/etc/env.php`: + +```php + 'system' => [ + 'default' => [ + 'web' => [ + 'graphql' => [ + 'cors_max_age' => 86400, + 'cors_allow_credentials' => 0, + 'cors_allowed_methods' => 'POST, OPTIONS, GET', + 'cors_allowed_headers' => 'Content-Currency, Store, X-Magento-Cache-Id, X-Captcha, Content-Type, Authorization, DNT, TE', + 'cors_allowed_origins' => '*' + ] + ] + ] + ] +``` + +Enable the graycore cors. + +```sh +bin/magento module:enable Graycore_Cors +``` + +Then update the configuration: + +```sh +bin/magento setup:upgrade +``` + +### 6. Populate store with sample data (optional) + +In the Magento 2 folder, execute the commands below to add sample data to your store. + +```sh +bin/magento sampledata:deploy +``` + +Then update the configuration: + +```sh +bin/magento setup:upgrade +``` diff --git a/docs/guide/about.md b/docs/guide/about.md index d658166d3..684a9f953 100644 --- a/docs/guide/about.md +++ b/docs/guide/about.md @@ -2,8 +2,9 @@ ## Resources -- [Vue Storefront Documentation](https://docs.vuestorefront.io/v2/) -- [Magento 2 integration Documentation (WIP)](https://docs.vuestorefront.io/magento) +- [GitHub repository](https://github.com/vuestorefront/magento2/) +- [Vue Storefront website](https://www.vuestorefront.io/) +- [Core Documentation](https://docs.vuestorefront.io/v2/) - [Community Chat](https://discord.vuestorefront.io) ## Support diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md deleted file mode 100644 index 88023878e..000000000 --- a/docs/guide/configuration.md +++ /dev/null @@ -1,166 +0,0 @@ -# Configuration the Magento Integration - -After the creation of the store, you must configure the Magento integration. You can configure the integration using the `environment variables` or using a `configuration file`. - -## Environment Variables - -For configure the integration using `environment variables`, you can have a `.env` file in the root folder, or define those in the server `environment`. - -```dotenv -# Example environment configuration -STORE_ENV=dev # Store environment (Usage for file configuration) -NUXT_APP_ENV=development # Define nuxt application environment -NUXT_APP_PORT=3000 # Define nuxt port -MAGENTO_GRAPHQL_URL=https://{YOUR_SITE_FRONT_URL}/graphql # Define Magento GraphQL endpoint -MAGENTO_EXTERNAL_CHECKOUT_ENABLED=false # Flag if VSF will use External Checkout -MAGENTO_EXTERNAL_CHECKOUT_URL=https://{YOUR_SITE_FRONT_URL} # External checkout URL -MAGENTO_EXTERNAL_CHECKOUT_SYNC_PATH=/vue/cart/sync # External Checkout synchronization path -MAGENTO_BASE_URL={YOUR_SITE_FRONT_URL} # base url of your Magento instance -IMAGE_PROVIDER={YOUR_IMAGE_PROVIDER} # your image provider, for example cloudinary, default is ipx -IMAGE_PROVIDER_BASE_URL={YOUR_IMAGE_PROVIDER_BASE_URL} # base url provided from your image provider. It's used by nuxt-img to fetch images -``` - -## Configuration file - -To use the configuration file in your application, first you need to define an `environment variables` called `STORE_ENV` which will be used to attribute what file the application will use to load the configuration from. You can create an `.env` file and add the following configuration. - -```dotenv -# .env file example -STORE_ENV=dev # Store environment (Usage for file configuration) -``` - -Then on the `config` folder create a file `dev.json` with your configurations. - -```json5 -{ - "magentoGraphQl": "https://{YOUR_SITE_FRONT_URL}/graphql", // Define Magento GraphQL endpoint - "enableMagentoExternalCheckout": false, // Flag if VSF will use External Checkout - "externalCheckoutUrl": "https://{YOUR_SITE_FRONT_URL}", // External checkout URL - "externalCheckoutSyncPath": "/vue/cart/sync", // External Checkout synchronization path - "nuxtAppEnvironment": "development", // Define nuxt application environment - "nuxtAppPort": 3000, // Define nuxt port - "imageProvider": "ipx", // define image provider - "magentoBaseUrl": "https://magento2-instance.vuestorefront.io/", // define your Magento base URL - "imageProviderBaseUrl": "https://res-4.cloudinary.com/{YOUR_CLOUD_ID}/image/upload/" // define image provider base url - this is example from cloudinary -} -``` - - -## Store Config -This type contains information about the Magento's Store Configuration which is stored in Pinia `$state.storeConfig`. To avoid over fetch, by default, the amount of data pulled from Magento is minimal but as your application grows you might want to pull more config data for different purposes. - -Plugin `plugins/storeConfigPlugin.ts` is responsible for initial population of the Store Config data based on query in `plugins/query/StoreConfig.gql.ts`. To modify the initial Store Configuration state simply adjust the query to your needs. - -## Multistore and localization - -Each Magento Store View need to have corresponding locale configuration object in `i18n.locales` array in `nuxt.config.js`. - -### Locale configuration object reference - -`code` (required) - unique identifier of the locale - corresponding to Magento store view code -For other properties please follow official [nuxt-i18n docs](https://i18n.nuxtjs.org/options-reference#locales) - -### Sample configuration - -```json -locales: [ - { - code: 'default', - file: 'en.js', - iso: 'en_US', - }, - { - code: 'german', - file: 'de.js', - iso: 'de_DE', - }, -], - ``` -So for this configuration you need to have two Magento store views with corresponding store codes: `default` and `german` - -## Translations - -There are two steps to translate whole storefront: -1. Add translations in Magento for products and categories if necessary -2. Add translations to files in the `lang` directory - - -## Image Providers -Thanks to the [nuxt-img](https://image.nuxtjs.org/) you can use external image providers. - -By default, we use the `ipx` provider. that means the images are fetched from Magento, and from `static` directory. - -### How to configure external image provider - -1. Configure ENV variables - 1. `MAGENTO_BASE_URL` - base URL of Magento shop. It's used by the `useImage` composable to extract image's path from full Magento URL. - 2. `IMAGE_PROVIDER` - for example: `cloudinary`. List of available providers is [here](https://image.nuxtjs.org/getting-started/providers) - 3. `IMAGE_PROVIDER_BASE_URL` - the base url of your project in for example cloudinary or other image providers -2. Configure your provider in `nuxt.config.js`. Here is the example: -```javascript -image: { - provider: process.env.VSF_IMAGE_PROVIDER - magekit: { - baseURL: process.env.VSF_IMAGE_PROVIDER_BASE_URL - } -}, -``` -3. Sync your Magento images with external provider - 1. For example if you have anb image in Magento with path `{YOUR_MAGENTO_BASE_URL}o/media/catalog/product/w/g/wg02-bk-0.jpg` - you should have corresponding image in your external provider: `media/catalog/product/w/g/wg02-bk-0.jpg` -4. Sync your local images with external provider - 1. Upload content of `static` directory to your external media library - -### The useImage composable - -Magento GraphQL API returns an URL to cached images, for example: `https://magento2-instance.vuestorefront.io/media/catalog/product/cache/d3f55929541f2d022ca06067148d0eae/w/g/wg02-bk-0.jpg`. -When you basically download all images from your server (from media) directory, paths are different, they does not include `cache/***/` part. - -Because of that, we created the `useImage` hook, which provides `getMagentoImage(fullImageUrl: string)` method. -That methods returns an URL to image, without magento base url, and cache part. Then you can use it to get images from external providers. - - -When you want to use this composable you need to: - -1. import it in component - `import { useImage } from '~/composables';` -2. Export `getMagentoImage to a template -```javascript -// component body (typically a setup() function) -const { getMagentoImage } = useImage(); - -return { - ... // other things like computed properties, methods and so on - getMagentoImage -} -``` -3. Use the `getMagentoImage` method in template like this: -```vue - -``` - -### ImageSizes enum -There is helper object in `enums/imageEnums.js` file that export configuration for various image types: -```javascript -module.exports = { - productCard: { - width: 216, - height: 268, - }, - productCardHorizontal: { - width: 140, - height: 200, - }, - cartItem: { - width: 100, - height: 100, - }, -}; - -``` diff --git a/docs/guide/creating-a-store.md b/docs/guide/creating-a-store.md deleted file mode 100644 index c035aa121..000000000 --- a/docs/guide/creating-a-store.md +++ /dev/null @@ -1,60 +0,0 @@ -# Creating a new store - -To create a new Vue Storefront Magento 2 store, there are two available options: - -1. [Using the Vue Storefront CLI](#using-the-vue-storefront-cli) -2. [Cloning the template store](#cloning-the-template-store) - -## Requirements - -- Node.Js 16+ -- Magento 2.4.3+ instance for GraphQL endpoint -- Change Magento GraphQL Query Complexity and Depth values - -::: warning Don't forget to change the Magento GraphQL Query Complexity and Depth values -Magento 2 by default has a lower value for the complexity of 300, and a higher value for the depth of 20. [Magento 2 - Issue #32427](https://github.com/magento/magento2/issues/32427#issuecomment-860478483) - -The changes are required, due to the size of the queries and mutations in the `api-client` implementation. - -To do this changes, you can use the [Magento 2 module](https://github.com/caravelx/module-graphql-config), which adds a configuration panel to your admin, or do this changes manually. -::: - -To install the Magento 2 GraphQL Config module, on your Magento installation execute: - -```bash -composer require caravelx/module-graphql-config - -php bin/magento module:enable Caravel_GraphQlConfig - -php bin/magento setup:upgrade - -php bin/magento setup:di:compile - -php bin/magento setup:static-content:deploy -``` - -Find more information about the module [GraphQl Custom Config](https://github.com/caravelx/module-graphql-config) - -## Using the Vue Storefront CLI - -To create a new store using the Vue Storefront CLI, first you need to install the CLI - -```bash -npm i -g @vue-storefront/cli -``` - -Then you must create the new store using the newly installed CLI - -```bash -vsf init -# And choose Magento 2 -``` - -## Cloning the template store - -To create a new store cloning the template store, you need to clone the Magento base template store. - -```bash -git clone https://github.com/vuestorefront/template-magento -``` - diff --git a/docs/guide/image-optimization.md b/docs/guide/image-optimization.md new file mode 100644 index 000000000..914f191c6 --- /dev/null +++ b/docs/guide/image-optimization.md @@ -0,0 +1,78 @@ +# Image optimization + +You can use external image providers to optimize your images thanks to the [nuxt-img](https://image.nuxtjs.org/) module. + +By default, Vue Storefront uses the `ipx` provider, an open-source, self-hosted image optimizer. + +## Configure external image provider + +If you decide to use an external image provider, you have to update the following environment variables inside the `.env` file: + +1. `VSF_MAGENTO_BASE_URL` - base URL of Magento shop. The `useImage` composable uses it to extract the image path from the full Magento URL. +2. `VSF_IMAGE_PROVIDER` - name of the external provider, e.g. `cloudinary`. See the [Providers](https://image.nuxtjs.org/getting-started/providers) page for more information. +3. `VSF_IMAGE_PROVIDER_DOMAIN` - domain of the image provider. +4. `VSF_IMAGE_PROVIDER_BASE_URL` - base URL of the image provider to upload images. + +See the `image` property in the `nuxt.config.js` to learn how these environment variables are used and to configure any [other options](https://image.nuxtjs.org/api/options) supported by the [nuxt-img](https://image.nuxtjs.org/) plugin. + +## Synchronize Magento images with an external provider + +You need to synchronize all your images from the two folders below manually or create a script to do so: + +- `media` folder in Magento. We recommend using the same path as in Magento. For example, if your image path is `{MAGENTO_BASE_URL}/media/catalog/product/{IMAGE_PATH}`, you should have the corresponding image in your external provider with the same path, e.g. `media/catalog/product/{IMAGE_PATH}`. +- `static` folder in Vue Storefront. + +## The `useImage()` composable + +Magento GraphQL API returns an URL to cached images, e.g. `{MAGENTO_BASE_URL}/media/catalog/product/cache/{IMAGE_PATH}`. When you download all images from your server's `media` directory, paths don't include the `cache/***/` part. + +Because of that, we created the `useImage()` hook, which provides the `getMagentoImage(fullImageUrl: string)` method. This method returns an URL to the image without the Magento base URL and cache part. You can use it to get images from external providers. + +Here's an example of how to use the `useImage()` composable: + +```vue + + + +``` + +## Configure `ImageSizes` enum + +There is a helper object in the `enums/imageEnums.js` file that export configuration for various image types: + +```javascript +module.exports = { + productCard: { + width: 216, + height: 268, + }, + productCardHorizontal: { + width: 140, + height: 200, + }, + cartItem: { + width: 100, + height: 100, + }, +}; +``` diff --git a/docs/guide/supported-features.md b/docs/guide/supported-features.md index b02db5176..d5336e64a 100644 --- a/docs/guide/supported-features.md +++ b/docs/guide/supported-features.md @@ -1,6 +1,6 @@ # Supported features -This page shows the Magento 2 features supported by this integration. If the feature you need is not supported, check out our [Roadmap](./roadmap.html) or reach out to us on [our Discord server](https://discord.vuestorefront.io/). +This page shows the Magento 2 features supported by this integration. If the feature you need is not supported, reach out to us on [our Discord server](https://discord.vuestorefront.io/). ## Product types @@ -33,8 +33,8 @@ This page shows the Magento 2 features supported by this integration. If the fea | Configurable product swatches | ❌ | | Filtering | ✅ | | Sorting | ✅ | -| Breadcrumb | ❌ | -| Category Landing Pages | ❌ | +| Breadcrumb | ✅ | +| Category Landing Pages | ✅ | ## Product page diff --git a/docs/index.md b/docs/index.md index f336ed2ff..13dd1e2ce 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,21 @@ - + -# Vue Storefront 2 integration with Magento 2 +# Vue Storefront 2 for Magento [BETA] -This project is a Magento 2 integration for Vue Storefront 2. +Welcome to the documentation of Vue Storefront 2 integration for Magento 2. -This integration is currently a **Beta** and not ready for production usage. You can help us to make the integration production-ready faster by reporting bugs and contributing to the code at the [repository issues page](https://github.com/vuestorefront/magento2/issues). +It's part of our ecosystem of integrations and extensions, which you can learn more about in our [core documentation](https://docs.vuestorefront.io/v2/). + +## Demo + +If you want to see the integration in action, check out our [demo environments](/guide/environments.html). + +## Where to start? + +To get started, see the following guides: + +- [Configuring Magento](/getting-started/configure-magento.html) to install the Magento server and configure it for Vue Storefront. +- [Configuring Vue Storefront](/getting-started/configure-integration.html) to install Vue Storefront and configure it. diff --git a/docs/migration-guides/1.0.0-rc.8/index.md b/docs/migration-guides/1.0.0-rc.8/index.md new file mode 100644 index 000000000..d2b59374e --- /dev/null +++ b/docs/migration-guides/1.0.0-rc.8/index.md @@ -0,0 +1,79 @@ +# Vue Storefront for Magento 1.0.0-rc.8 release notes + +Vue Storefront for Magento 1.0.0.rc.8 contains backward-incompatible changes. To review these backward-incompatible changes, see + +[1.0.0-rc.7 **Backward incompatible changes reference**](./rc.8-bic.md) + +## Vue Storefront for Magento 1.0.0-rc.8 highlights + +### Improved Customer Account Pages + +Customer pages (my account) have been rewritten, and from now, each account page is a separate route. + +### Improved code quality + +All errors reported by the eslint have been fixed, and `lang=”ts”` attribute has been added to all vue templates. Missing types have been also added. + +### New features + +- feat: show configurable option values in order history AND feat: show more order details [#996](https://github.com/vuestorefront/magento2/pull/996) +- feat: add skeleton loaders for address edit and addresses details pages [#999](https://github.com/vuestorefront/magento2/pull/999) +- feat: add skeleton loaders in category navbar [#936](https://github.com/vuestorefront/magento2/pull/936) + +### Performance improvements + +- perf: make related and upsell products lazy loaded when visible [#981](https://github.com/vuestorefront/magento2/pull/981) +- perf: make read reviews loaded on request [#982](https://github.com/vuestorefront/magento2/pull/982) +- perf: make PDP Instagram feed section loaded when visible [#980](https://github.com/vuestorefront/magento2/pull/980) +- perf: make mobile store banner lazy-loaded on PDP [#979](https://github.com/vuestorefront/magento2/pull/979) +- perf: remove TopBar layout shift [#1034](https://github.com/vuestorefront/magento2/pull/1034) +- perf: prevent loading all main images on the mobile PDP gallery [#985](https://github.com/vuestorefront/magento2/pull/985) + +### **Bugfix** + +- fix: issue with displaying product price od PDP and PLP [#1053](https://github.com/vuestorefront/magento2/pull/1053) +- fix: empty wishlist implementation [#1006](https://github.com/vuestorefront/magento2/pull/1006) +- fix: fixed errors during wishlist loading [#995](https://github.com/vuestorefront/magento2/pull/995) +- fix: fetch new orders on each orders history visit AND move order information to separate section [#1046](https://github.com/vuestorefront/magento2/pull/1046) +- fix: remove unwanted authorization errors in the console [#976](https://github.com/vuestorefront/magento2/pull/976) +- fix: coupon code invalid error message [#1009](https://github.com/vuestorefront/magento2/pull/1009) +- fix: coupon code doesn't show error [#958](https://github.com/vuestorefront/magento2/pull/958) +- fix: prevent SfSidebar disableBodyScroll triggering on desktop [#1027](https://github.com/vuestorefront/magento2/pull/1027) +- fix: category page - equal amount of product for a row [#1007](https://github.com/vuestorefront/magento2/pull/1007) +- fix: wrong warning announcement on modal window to login [#1004](https://github.com/vuestorefront/magento2/pull/1004) +- fix: entities on filters are not displayed properly [#989](https://github.com/vuestorefront/magento2/pull/989) +- fix: add html content purify for the selected filters [#1039](https://github.com/vuestorefront/magento2/pull/1039) +- fix: useProductGallery reactivity [#1033](https://github.com/vuestorefront/magento2/pull/1033) +- fix: sfcontentpages style missing on my account develop [#992](https://github.com/vuestorefront/magento2/pull/992) +- fix: filter by category issue [#977](https://github.com/vuestorefront/magento2/pull/977) + +### Refactors + +- refactor!: moved customer pages to subroutes [#991](https://github.com/vuestorefront/magento2/pull/991) +- refactor!: create renderers for each product type [#1014](https://github.com/vuestorefront/magento2/pull/1014) +- refactor: updated order details totals section styling [#1042](https://github.com/vuestorefront/magento2/pull/988) +- refactor!: use order.number instead of deprecated order.order_number [#1000](https://github.com/vuestorefront/magento2/pull/1000) +- refactor: add product to cart from wishlist [#1026](https://github.com/vuestorefront/magento2/pull/1026) +- refactor: remove useless order getters [#1016](https://github.com/vuestorefront/magento2/pull/1016) +- refactor!: create wishlist module [#945](https://github.com/vuestorefront/magento2/pull/945) + +### Tests + +- test: added tests for cmscontent component [#1056](https://github.com/vuestorefront/magento2/pull/1056) +- test: categorynavbar component [#952](https://github.com/vuestorefront/magento2/pull/952) +- test: category-breadcrumbs component [#987](https://github.com/vuestorefront/magento2/pull/987) + +### Chore + +- chore: fix all remaining .vue lang="ts" errors [#1043](https://github.com/vuestorefront/magento2/pull/1043) +- refactor: fix eslint warnings in .vue files [#1036](https://github.com/vuestorefront/magento2/pull/1036) +- chore: implement overlooked additional tasks [#1023](https://github.com/vuestorefront/magento2/pull/1023) +- chore: fix all remaining eslint errors [#1010](https://github.com/vuestorefront/magento2/pull/1010) +- chore: fix ~50 eslint errors [#1008](https://github.com/vuestorefront/magento2/pull/1008) +- chore: fix ~240 eslint errors/warnings [#1005](https://github.com/vuestorefront/magento2/pull/1005) + +### Documentation + +- docs: update composables docs [#994](https://github.com/vuestorefront/magento2/pull/994) +- docs: refactor the setup guide docs [#975](https://github.com/vuestorefront/magento2/pull/975) +- docs: update useAddresses API reference [#935](https://github.com/vuestorefront/magento2/pull/935) diff --git a/docs/migration-guides/1.0.0-rc.8/rc.8-bic.md b/docs/migration-guides/1.0.0-rc.8/rc.8-bic.md new file mode 100644 index 000000000..9063e28e8 --- /dev/null +++ b/docs/migration-guides/1.0.0-rc.8/rc.8-bic.md @@ -0,0 +1,95 @@ +# 1.0.0-rc.8 **Backward incompatible changes reference** + +In this document, you can see crucial breaking changes in the `1.0.0-rc.8` comparing to `1.0.0-rc.7` release. To see all changes, please take a look at the [release pull request](https://github.com/vuestorefront/magento2/pull/1063). + +## Theme + +In this document, we described the most crucial changes. We recommend checking each component/template to see all changes. + +| File | what and how it changed | +|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| packages/theme/components/AppHeader.vue | URL to the customer page has been changed in the handleAccountClick function | +| packages/theme/components/BottomNavigation.vue | The useCategoryStore import has been changed and the URL to the customer page has been changed in the handleAccountClick function | +| packages/theme/components/CartSidebar.vue | Import paths have been changed | +| packages/theme/components/Checkout/CartPreview.vue | import paths have been changed | +| packages/theme/components/Checkout/VsfShippingProvider.vue | import paths have been changed | +| packages/theme/components/CouponCode.vue | import paths have been changed | +| packages/theme/components/Header/SearchBar/SearchBar.vue | import paths have been changed | +| packages/theme/components/Hero.vue | File has been renamed to HeroSection.vue | +| packages/theme/components/LoginModal.vue | import paths have been changed | +| packages/theme/components/tests/ProductAddReviewForm.spec.js | import paths have been changed | +| packages/theme/composables/useBilling/BillingDetails.ts | File has been removed | +| packages/theme/composables/useBilling/useBilling.ts | File has been moved to packages/theme/modules/checkout/composables/useBilling/useBilling.ts | +| packages/theme/composables/usePaymentProvider/PaymentMethodParams.ts | File has been removed | +| packages/theme/composables/useShippingProvider/ | Has been moved to packages/theme/modules/checkout/composables/useShippingProvider/ | +| packages/theme/getters/checkoutGetters.ts | Checkout getters have been removed | +| packages/theme/getters/index.ts | Import paths have been changed | +| packages/theme/getters/orderGetters.ts | Order getters have been moved to packages/theme/modules/checkout/getters/orderGetters.ts | +| packages/theme/helpers/cacheControl.js | The file has been removed | +| packages/theme/layouts/default.vue | Import paths have been changed | +| packages/theme/modules/catalog/category/components/cms/categoryContent.gql.ts | query name and parameters have been changed | +| packages/theme/composables/useCategorySearch | Has been moved to packages/theme/modules/catalog/category/composables/useCategorySearch | +| packages/theme/stores/category.ts | Has been moved topackages/theme/modules/catalog/category/stores/category.ts | +| packages/theme/stores/graphql/categoryList.gql.ts | Has been moved to packages/theme/modules/catalog/category/stores/graphql/categoryList.gql.ts | +| packages/theme/modules/catalog/pages/category.vue | Return values from setup function have been changed | +| packages/theme/modules/catalog/pages/product.vue | Content of page have been rewritten | +| packages/theme/components/ProductAddReviewForm.vue | Has been moved to packages/theme/modules/catalog/product/components/ProductAddReviewForm.vue | +| packages/theme/modules/catalog/product/components/BundleProductSelector.vu | Has been moved to packages/theme/modules/catalog/product/components/product-types/bundle/BundleProductSelector.vue | +| packages/theme/modules/catalog/product/components/GroupedProductSelector.vue | Has been moved to packages/theme/modules/catalog/product/components/product-types/grouped/GroupedProductSelector.vue | +| packages/theme/composables/useBilling/commands/saveBillingAddressCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useBilling/commands/saveBillingAddressCommand.ts | +| packages/theme/composables/useBilling/index.ts | Has been moved to packages/theme/modules/checkout/composables/useBilling/index.ts | +| packages/theme/composables/useBilling/index.ts | Has been moved to packages/theme/modules/checkout/composables/useBilling/index.ts | +| packages/them/composables/useCart/commands/addItemCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/addItemCommand.ts | +| packages/theme/composables/useCart/commands/applyCouponCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/applyCouponCommand.ts | +| packages/theme/composables/useCart/commands/clearCartCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/clearCartCommand.ts | +| packages/theme/composables/useCart/commands/loadCartCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/loadCartCommand.ts | +| packages/theme/composables/useCart/commands/loadTotalQtyCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/loadTotalQtyCommand.ts | +| packages/theme/composables/useCart/commands/removeCouponCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/removeCouponCommand.ts | +| packages/theme/composables/useCart/commands/removeItemCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/removeItemCommand.ts | +| packages/theme/composables/useCart/commands/updateItemQtyCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/commands/updateItemQtyCommand.ts | +| packages/theme/composables/useCart/index.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/index.ts | +| packages/theme/composables/useCart/useCart.ts | Has been moved to packages/theme/modules/checkout/composables/useCart/useCart.ts | +| packages/theme/composables/useGetShippingMethods/commands/getCustomerShippingMethodsCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useGetShippingMethods/commands/getCustomerShippingMethodsCommand.ts | +| packages/theme/composables/useGetShippingMethods/commands/getGuestShippingMethodsCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useGetShippingMethods/commands/getGuestShippingMethodsCommand.ts | +| packages/theme/composables/useGetShippingMethods/index.ts | Has been moved to packages/theme/modules/checkout/composables/useGetShippingMethods/index.ts | +| packages/theme/composables/useGetShippingMethods/index.ts | Has been moved to packages/theme/modules/checkout/composables/useGetShippingMethods/index.ts | +| packages/theme/composables/useMakeOrder/commands/placeOrderCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useMakeOrder/commands/placeOrderCommand.ts | +| packages/theme/composables/useMakeOrder/index.ts | Has been moved to packages/theme/modules/checkout/composables/useMakeOrder/index.ts | +| packages/theme/composables/useMakeOrder/useMakeOrder.ts | Has been moved to packages/theme/modules/checkout/composables/useMakeOrder/useMakeOrder.ts | +| packages/theme/composables/usePaymentProvider/commands/getAvailablePaymentMethodsCommand.ts | Has been moved to packages/theme/modules/checkout/composables/usePaymentProvider/commands/getAvailablePaymentMethodsCommand.ts | +| packages/theme/composables/usePaymentProvider/commands/setPaymentMethodOnCartCommand.ts | Has been moved to packages/theme/modules/checkout/composables/usePaymentProvider/commands/setPaymentMethodOnCartCommand.ts | +| packages/theme/composables/usePaymentProvider/index.ts | Has been moved to packages/theme/modules/checkout/composables/usePaymentProvider/index.ts | +| packages/theme/composables/usePaymentProvider/usePaymentProvider.ts | Has been moved to packages/theme/modules/checkout/composables/usePaymentProvider/usePaymentProvider.ts | +| packages/theme/composables/useShipping/index.ts | Has been moved to packages/theme/modules/checkout/composables/useShipping/index.ts | +| packages/theme/composables/useShipping/useShipping.ts | Has been moved to packages/theme/modules/checkout/composables/useShipping/useShipping.ts | +| packages/theme/composables/useShippingProvider/commands/setShippingMethodsOnCartCommand.ts | Has been moved to packages/theme/modules/checkout/composables/useShippingProvider/commands/setShippingMethodsOnCartCommand.ts | +| packages/theme/composables/useShippingProvider/index.ts | Has been moved to packages/theme/modules/checkout/composables/useShippingProvider/index.ts | +| packages/theme/getters/cartGetters.ts | Has been moved to packages/theme/modules/checkout/getters/cartGetters.ts | +| packages/theme/pages/Checkout.vue | Has been moved to packages/theme/modules/checkout/pages/Checkout.vue, import paths have been changed | +| packages/theme/pages/Checkout/Billing.vue | Has been moved to packages/theme/modules/checkout/pages/Checkout/Billing.vue | +| packages/theme/pages/Checkout/Payment.vue | Has been moved to packages/theme/modules/checkout/pages/Checkout/Payment.vue | +| packages/theme/pages/Checkout/Shipping.vue | Has been moved to packages/theme/modules/checkout/pages/Checkout/Shipping.vue | +| packages/theme/pages/Checkout/ThankYou.vue | Has been moved to packages/theme/modules/checkout/pages/Checkout/ThankYou.vue | +| packages/theme/pages/Checkout/UserAccount.vue | Has been moved to packages/theme/modules/checkout/pages/Checkout/UserAccount.vue | +| packages/theme/modules/customer/pages/MyAccount.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyAccount.vue | +| packages/theme/modules/customer/components/AddressForm.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/AddressesDetails/AddressForm.vue | +| packages/theme/modules/customer/pages/AddressesDetails.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/AddressesDetails/AddressesDetails.vue | +| packages/theme/modules/customer/pages/MyNewsletter.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyNewsletter.vue | +| packages/theme/modules/customer/pages/MyProfile.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyProfile/MyProfile.vue | +| packages/theme/modules/customer/components/PasswordResetForm.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyProfile/PasswordResetForm.vue | +| packages/theme/modules/customer/components/ProfileUpdateForm.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyProfile/ProfileUpdateForm.vue | +| packages/theme/modules/customer/pages/MyReviews.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyReviews.vue | +| packages/theme/modules/customer/pages/MyWishlist.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/MyWishlist.vue | +| packages/theme/modules/customer/pages/OrderHistory.vue | Has been moved to packages/theme/modules/customer/pages/MyAccount/OrderHistory/OrderHistory.vue | +| packages/theme/stores/customer.ts | Has been moved to packages/theme/modules/customer/stores/customer.ts | +| packages/theme/modules/theme/components/WishlistSidebar.vue | Has been moved to packages/theme/modules/wishlist/components/WishlistSidebar.vue | +| packages/theme/composables/useWishlist/ | Has been moved to packages/theme/modules/wishlist/composables/useWishlist/ | +| packages/theme/getters/wishlistGetters.ts | Has been moved to packages/theme/modules/wishlist/getters/wishlistGetters.ts | +| packages/theme/nuxt.config.js | module theme has been removed, module checkout has been added to modules config | +| packages/theme/routes.js | Routes declarations have been moved to the checkout and customer modules | + +## API Client + +| File | what and how it changed | +|--------------------------------------------------------------|-------------------------------------------------------------------------------------------| +| packages/api-client/src/api/customerOrders/customerOrders.ts | deprecated fields order_number and created_at have been replaced by number and order_date | diff --git a/docs/migration-guides/index.md b/docs/migration-guides/index.md index eda652111..ffd32ebb3 100644 --- a/docs/migration-guides/index.md +++ b/docs/migration-guides/index.md @@ -1,3 +1,4 @@ # Migration guides - [1.0.0-rc.7](./1.0.0-rc.7/) +- [1.0.0-rc.8](./1.0.0-rc.8/) diff --git a/internals/eslint-import/index.js b/internals/eslint-import/index.js index de54dd087..941083af6 100644 --- a/internals/eslint-import/index.js +++ b/internals/eslint-import/index.js @@ -5,5 +5,5 @@ module.exports = { './rules/extends', './rules/rules', './rules/settings', - ].map(require.resolve), + ].map((element) => require.resolve(element)), }; diff --git a/internals/eslint-import/package.json b/internals/eslint-import/package.json index aa57a97e8..d0434c369 100644 --- a/internals/eslint-import/package.json +++ b/internals/eslint-import/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/eslint-config-import", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "author": "Heitor Ramon Ribeiro ", "license": "MIT", "scripts": { diff --git a/internals/eslint-import/rules/plugins.js b/internals/eslint-import/rules/plugins.js index c7aac4061..9b7b4df5d 100644 --- a/internals/eslint-import/rules/plugins.js +++ b/internals/eslint-import/rules/plugins.js @@ -1,5 +1,5 @@ module.exports = { plugins: [ - 'import' + 'import', ], }; diff --git a/internals/eslint-jest/index.js b/internals/eslint-jest/index.js index c02f0dc6c..344edc3eb 100644 --- a/internals/eslint-jest/index.js +++ b/internals/eslint-jest/index.js @@ -3,5 +3,5 @@ module.exports = { './rules/plugins', './rules/extends', './rules/rules', - ].map(require.resolve), + ].map((element) => require.resolve(element)), }; diff --git a/internals/eslint-jest/package.json b/internals/eslint-jest/package.json index 05e93deb1..29cb2b5bb 100644 --- a/internals/eslint-jest/package.json +++ b/internals/eslint-jest/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/eslint-config-jest", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "author": "Heitor Ramon Ribeiro ", "license": "MIT", "scripts": { diff --git a/internals/eslint-jest/rules/extends.js b/internals/eslint-jest/rules/extends.js index d4a022229..38dc63056 100644 --- a/internals/eslint-jest/rules/extends.js +++ b/internals/eslint-jest/rules/extends.js @@ -1,5 +1,5 @@ module.exports = { extends: [ - 'plugin:jest/recommended' + 'plugin:jest/recommended', ], }; diff --git a/internals/eslint-jest/rules/plugins.js b/internals/eslint-jest/rules/plugins.js index f0c3ecc84..63a35a033 100644 --- a/internals/eslint-jest/rules/plugins.js +++ b/internals/eslint-jest/rules/plugins.js @@ -1,5 +1,5 @@ module.exports = { plugins: [ - 'jest' + 'jest', ], }; diff --git a/internals/eslint-typescript/index.js b/internals/eslint-typescript/index.js index c02f0dc6c..344edc3eb 100644 --- a/internals/eslint-typescript/index.js +++ b/internals/eslint-typescript/index.js @@ -3,5 +3,5 @@ module.exports = { './rules/plugins', './rules/extends', './rules/rules', - ].map(require.resolve), + ].map((element) => require.resolve(element)), }; diff --git a/internals/eslint-typescript/package.json b/internals/eslint-typescript/package.json index f07801f2f..8891b7565 100644 --- a/internals/eslint-typescript/package.json +++ b/internals/eslint-typescript/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/eslint-config-typescript", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "author": "Heitor Ramon Ribeiro ", "license": "MIT", "scripts": { diff --git a/internals/eslint-typescript/rules/extends.js b/internals/eslint-typescript/rules/extends.js index c413a3296..6d6f7a124 100644 --- a/internals/eslint-typescript/rules/extends.js +++ b/internals/eslint-typescript/rules/extends.js @@ -1,5 +1,5 @@ module.exports = { extends: [ - 'plugin:@typescript-eslint/recommended-requiring-type-checking' + 'plugin:@typescript-eslint/recommended-requiring-type-checking', ], }; diff --git a/internals/eslint-typescript/rules/plugins.js b/internals/eslint-typescript/rules/plugins.js index d3e2b6231..40d8a41c1 100644 --- a/internals/eslint-typescript/rules/plugins.js +++ b/internals/eslint-typescript/rules/plugins.js @@ -1,5 +1,5 @@ module.exports = { plugins: [ - '@typescript-eslint' + '@typescript-eslint', ], }; diff --git a/internals/eslint-vue/package.json b/internals/eslint-vue/package.json index 0f465e077..990636ff1 100644 --- a/internals/eslint-vue/package.json +++ b/internals/eslint-vue/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/eslint-config-vue", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "author": "Heitor Ramon Ribeiro ", "license": "MIT", "scripts": { diff --git a/internals/eslint-vue/rules/plugins.js b/internals/eslint-vue/rules/plugins.js index 72325c7c5..b3dca7b71 100644 --- a/internals/eslint-vue/rules/plugins.js +++ b/internals/eslint-vue/rules/plugins.js @@ -1,5 +1,5 @@ module.exports = { plugins: [ - 'vue' + 'vue', ], }; diff --git a/internals/eslint/index.js b/internals/eslint/index.js index a478b2696..09abf5da3 100644 --- a/internals/eslint/index.js +++ b/internals/eslint/index.js @@ -6,5 +6,5 @@ module.exports = { './rules/plugins', './rules/extends', './rules/rules', - ].map(require.resolve), + ].map((element) => require.resolve(element)), }; diff --git a/internals/eslint/package.json b/internals/eslint/package.json index 429bc2761..348c9e22d 100644 --- a/internals/eslint/package.json +++ b/internals/eslint/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/eslint-config-base", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "author": "Heitor Ramon Ribeiro ", "license": "MIT", "scripts": { diff --git a/internals/eslint/rules/globals.js b/internals/eslint/rules/globals.js index 85837d335..8e94a4609 100644 --- a/internals/eslint/rules/globals.js +++ b/internals/eslint/rules/globals.js @@ -5,6 +5,6 @@ module.exports = { __statics: true, process: true, Capacitor: true, - chrome: true + chrome: true, }, }; diff --git a/internals/eslint/rules/plugins.js b/internals/eslint/rules/plugins.js index 6ca75d9e8..6b39a9d0e 100644 --- a/internals/eslint/rules/plugins.js +++ b/internals/eslint/rules/plugins.js @@ -1,5 +1,5 @@ module.exports = { plugins: [ - 'unicorn' + 'unicorn', ], }; diff --git a/package.json b/package.json index 94517fe67..16d96e997 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "docs:dev": "cd docs && yarn dev", "docs:build": "cd docs && yarn build", "graphql:codegen": "graphql-codegen --config codegen.yml", - "lint": "eslint . --ext .ts,.vue --fix", + "lint": "eslint . --ext .ts,.vue", "prepare": "[ -d '.husky' ] && (husky install && shx rm -rf .git/hooks && shx ln -s ../.husky .git/hooks) || true", "publish:api-client": "node ./scripts/publishApi.js", "publish:composables": "node ./scripts/publishComposable.js", diff --git a/packages/api-client/__tests__/api/storeConfig/storeConfig.spec.ts b/packages/api-client/__tests__/api/storeConfig/storeConfig.spec.ts index 1207868f7..0b17a6bc2 100644 --- a/packages/api-client/__tests__/api/storeConfig/storeConfig.spec.ts +++ b/packages/api-client/__tests__/api/storeConfig/storeConfig.spec.ts @@ -1,5 +1,5 @@ import request from '../../setup/request'; -import {STORE_CONFIG_MOCK_RESP} from './../../mockData/api/storeConfig' +import { STORE_CONFIG_MOCK_RESP } from '../../mockData/api/storeConfig'; describe('[Magento-API-Client] storeConfig', () => { it('Fetching the storeConfig', async () => { @@ -60,8 +60,8 @@ describe('[Magento-API-Client] storeConfig', () => { welcome } } - ` - }) + `, + }), }); const { data } = await res.json(); diff --git a/packages/api-client/__tests__/setup/index.ts b/packages/api-client/__tests__/setup/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/api-client/babel.config.js b/packages/api-client/babel.config.js index d3540f51b..6c66aa368 100644 --- a/packages/api-client/babel.config.js +++ b/packages/api-client/babel.config.js @@ -1,9 +1,10 @@ +// eslint-disable-next-line unicorn/prefer-module module.exports = { presets: [ ['@babel/preset-env', { targets: { - node: 'current' - } - }] - ] + node: 'current', + }, + }], + ], }; diff --git a/packages/api-client/jest.config.js b/packages/api-client/jest.config.js index 180a0a0ec..87c8e4e24 100644 --- a/packages/api-client/jest.config.js +++ b/packages/api-client/jest.config.js @@ -23,7 +23,7 @@ module.exports = { testEnvironment: 'jsdom', transform: { '^.+\\.(m)js$': 'babel-jest', - '^.+\\.ts$': 'ts-jest' + '^.+\\.ts$': 'ts-jest', }, transformIgnorePatterns: [ 'node_modules', diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 3956c6091..e9be8c4a8 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/magento-api", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "sideEffects": false, "homepage": "https://github.com/vuestorefront/magento2", "bugs": { diff --git a/packages/api-client/possible-types.js b/packages/api-client/possible-types.js index 53590aae3..05abd9a2d 100644 --- a/packages/api-client/possible-types.js +++ b/packages/api-client/possible-types.js @@ -1,7 +1,9 @@ +/* eslint-disable unicorn/prefer-module */ require('dotenv').config(); const fetch = require('cross-fetch'); const fs = require('fs'); +// eslint-disable-next-line promise/catch-or-return fetch(process.env.VSF_MAGENTO_GRAPHQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -22,9 +24,11 @@ fetch(process.env.VSF_MAGENTO_GRAPHQL_URL, { `, }), }).then((result) => result.json()) + // eslint-disable-next-line promise/always-return .then((result) => { const possibleTypes = {}; + // eslint-disable-next-line no-underscore-dangle result.data.__schema.types.forEach((supertype) => { if (supertype.possibleTypes) { possibleTypes[supertype.name] = supertype.possibleTypes.map((subtype) => subtype.name); diff --git a/packages/api-client/src/api/cmsPage/index.ts b/packages/api-client/src/api/cmsPage/index.ts index 67dcb7ed8..893b01cd0 100644 --- a/packages/api-client/src/api/cmsPage/index.ts +++ b/packages/api-client/src/api/cmsPage/index.ts @@ -45,4 +45,4 @@ export default async function getCmsPage( Logger.error(error); throw error.networkError?.result || error; } -}; +} diff --git a/packages/api-client/src/api/customerOrders/customerOrders.ts b/packages/api-client/src/api/customerOrders/customerOrders.ts index c93411867..7b24b5908 100644 --- a/packages/api-client/src/api/customerOrders/customerOrders.ts +++ b/packages/api-client/src/api/customerOrders/customerOrders.ts @@ -5,9 +5,9 @@ export default gql` customer { orders(currentPage: $currentPage, filter: $filter, pageSize: $pageSize) { items { - order_number + number id - created_at + order_date grand_total total { discounts { @@ -237,6 +237,18 @@ export default gql` suffix telephone } + billing_address { + city + country_code + firstname + lastname + postcode + prefix + region + street + suffix + telephone + } shipping_method } page_info { diff --git a/packages/api-client/src/api/wishlist/index.ts b/packages/api-client/src/api/wishlist/index.ts index 86c52a708..31e24c14a 100644 --- a/packages/api-client/src/api/wishlist/index.ts +++ b/packages/api-client/src/api/wishlist/index.ts @@ -1,6 +1,5 @@ import { ApolloQueryResult } from '@apollo/client/core'; import { CustomQuery } from '@vue-storefront/core'; -import gql from 'graphql-tag'; import { WishlistQuery, WishlistQueryVariables, diff --git a/packages/api-client/src/helpers/magentoLink/graphQl.ts b/packages/api-client/src/helpers/magentoLink/graphQl.ts index 1a21e7e5d..4508bdd57 100644 --- a/packages/api-client/src/helpers/magentoLink/graphQl.ts +++ b/packages/api-client/src/helpers/magentoLink/graphQl.ts @@ -29,7 +29,13 @@ const createErrorHandler = () => onError(({ message, locations, path, + extensions, }) => { + // Mute all GraphQL authorization errors + if (extensions?.category === 'graphql-authorization') { + return; + } + if (!message.includes('Resource Owner Password Credentials Grant')) { if (!locations) { Logger.error(`[GraphQL error]: Message: ${message}, Path: ${path}`); diff --git a/packages/api-client/src/types/API.ts b/packages/api-client/src/types/API.ts index b30404d5a..aeb85396f 100644 --- a/packages/api-client/src/types/API.ts +++ b/packages/api-client/src/types/API.ts @@ -203,7 +203,7 @@ export interface MagentoApiMethods { applyCouponToCart( input: ApplyCouponToCartInput, customQuery?: CustomQuery - ): Promise>; + ): Promise>; availableStores(customQuery?: CustomQuery): Promise>; @@ -355,7 +355,7 @@ export interface MagentoApiMethods { removeCouponFromCart( input: RemoveCouponFromCartInput, customQuery?: CustomQuery - ): Promise>; + ): Promise>; removeItemFromCart( input: RemoveItemFromCartInput, diff --git a/packages/api-client/tsconfig.eslint.json b/packages/api-client/tsconfig.eslint.json new file mode 100644 index 000000000..a47730729 --- /dev/null +++ b/packages/api-client/tsconfig.eslint.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "exclude": [ + "lib", + "node_modules" + ] +} diff --git a/packages/composables/__tests__/_mountComposable.ts b/packages/composables/__tests__/_mountComposable.ts index b2e35eeca..cfebcb992 100644 --- a/packages/composables/__tests__/_mountComposable.ts +++ b/packages/composables/__tests__/_mountComposable.ts @@ -1,12 +1,12 @@ import { shallowMount } from '@vue/test-utils'; -import { createComponent } from '@vue/composition-api'; +import { defineComponent } from '@vue/composition-api'; const mountComposable = (composableFn) => { - const component = createComponent({ - template: '
my component
', + const component = defineComponent({ setup() { return composableFn(); }, + template: '
my component
', }); return shallowMount(component); diff --git a/packages/composables/__tests__/getters/productHelpers.spec.ts b/packages/composables/__tests__/getters/productHelpers.spec.ts index b4233c7cd..d09b187f1 100644 --- a/packages/composables/__tests__/getters/productHelpers.spec.ts +++ b/packages/composables/__tests__/getters/productHelpers.spec.ts @@ -1,144 +1,131 @@ +import { Product } from '@vue-storefront/magento-api'; import { getAttributes, - getAverageRating, - getBreadcrumbs, - getCategory, getCategoryIds, getCoverImage, - getDescription, getFiltered, - getFormattedPrice, getGallery, getId, getName, getPrice, - getProductRelatedProduct, - getProductSku, - getProductThumbnailImage, - getProductUpsellProduct, - getShortDescription, getSlug, - getTotalReviews, - getTypeId, - getSwatchData, - getGroupedProducts, - getBundleProducts, } from '../../src/getters/productGetters'; const product = { - "options_container": null, - "meta_description": null, - "meta_keyword": null, - "meta_title": null, - "description": { - "html": "

The sporty Joust Duffle Bag can't be beat - not in the gym, not on the luggage carousel, not anywhere. Big enough to haul a basketball or soccer ball and some sneakers with plenty of room to spare, it's ideal for athletes with places to go.

\n

    \n
  • Dual top handles.
  • \n
  • Adjustable shoulder strap.
  • \n
  • Full-length zipper.
  • \n
  • L 29\" x W 13\" x H 11\".
  • \n
", - "__typename": "ComplexTextValue" + options_container: null, + meta_description: null, + meta_keyword: null, + meta_title: null, + description: { + html: '

The sporty Joust Duffle Bag can\'t be beat - not in the gym, not on the luggage carousel, not anywhere. Big enough to haul a basketball or soccer ball and some sneakers with plenty of room to spare, it\'s ideal for athletes with places to go.

\n

    \n
  • Dual top handles.
  • \n
  • Adjustable shoulder strap.
  • \n
  • Full-length zipper.
  • \n
  • L 29" x W 13" x H 11".
  • \n
', + __typename: 'ComplexTextValue', }, - "short_description": { "html": "", "__typename": "ComplexTextValue" }, - "uid": "MQ==", - "__typename": "SimpleProduct", - "sku": "24-MB01", - "name": "Joust Duffle Bag", - "stock_status": "IN_STOCK", - "only_x_left_in_stock": null, - "rating_summary": 50, - "thumbnail": { - "url": "https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg", - "position": null, - "disabled": null, - "label": "Joust Duffle Bag", - "__typename": "ProductImage" + short_description: { html: '', __typename: 'ComplexTextValue' }, + uid: 'MQ==', + __typename: 'SimpleProduct', + sku: '24-MB01', + name: 'Joust Duffle Bag', + stock_status: 'IN_STOCK', + only_x_left_in_stock: null, + rating_summary: 50, + thumbnail: { + url: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', + position: null, + disabled: null, + label: 'Joust Duffle Bag', + __typename: 'ProductImage', }, - "price_range": { - "maximum_price": { - "final_price": { - "currency": "USD", - "value": 34, - "__typename": "Money" + price_range: { + maximum_price: { + final_price: { + currency: 'USD', + value: 34, + __typename: 'Money', }, - "regular_price": { "currency": "USD", "value": 34, "__typename": "Money" }, - "__typename": "ProductPrice" + regular_price: { currency: 'USD', value: 34, __typename: 'Money' }, + __typename: 'ProductPrice', }, - "minimum_price": { - "final_price": { - "currency": "USD", - "value": 34, - "__typename": "Money" + minimum_price: { + final_price: { + currency: 'USD', + value: 34, + __typename: 'Money', }, - "regular_price": { "currency": "USD", "value": 34, "__typename": "Money" }, - "__typename": "ProductPrice" + regular_price: { currency: 'USD', value: 34, __typename: 'Money' }, + __typename: 'ProductPrice', }, - "__typename": "PriceRange" + __typename: 'PriceRange', }, - "url_key": "joust-duffle-bag", - "url_rewrites": [{ - "url": "joust-duffle-bag.html", - "__typename": "UrlRewrite" + url_key: 'joust-duffle-bag', + url_rewrites: [{ + url: 'joust-duffle-bag.html', + __typename: 'UrlRewrite', }, { - "url": "gear/joust-duffle-bag.html", - "__typename": "UrlRewrite" - }, { "url": "gear/bags/joust-duffle-bag.html", "__typename": "UrlRewrite" }], - "categories": [{ - "uid": "Mw==", - "name": "Gear", - "url_suffix": ".html", - "url_path": "gear", - "breadcrumbs": null, - "__typename": "CategoryTree" + url: 'gear/joust-duffle-bag.html', + __typename: 'UrlRewrite', + }, { url: 'gear/bags/joust-duffle-bag.html', __typename: 'UrlRewrite' }], + categories: [{ + uid: 'Mw==', + name: 'Gear', + url_suffix: '.html', + url_path: 'gear', + breadcrumbs: null, + __typename: 'CategoryTree', }, { - "uid": "NA==", - "name": "Bags", - "url_suffix": ".html", - "url_path": "gear/bags", - "breadcrumbs": [{ - "category_name": "Gear", - "category_url_path": "gear", - "__typename": "Breadcrumb" + uid: 'NA==', + name: 'Bags', + url_suffix: '.html', + url_path: 'gear/bags', + breadcrumbs: [{ + category_name: 'Gear', + category_url_path: 'gear', + __typename: 'Breadcrumb', }], - "__typename": "CategoryTree" + __typename: 'CategoryTree', }], - "review_count": 2, - "reviews": { - "items": [{ - "average_rating": 60, - "ratings_breakdown": [{ - "name": "Rating", - "value": "3", - "__typename": "ProductReviewRating" + review_count: 2, + reviews: { + items: [{ + average_rating: 60, + ratings_breakdown: [{ + name: 'Rating', + value: '3', + __typename: 'ProductReviewRating', }], - "__typename": "ProductReview" + __typename: 'ProductReview', }, { - "average_rating": 40, - "ratings_breakdown": [{ - "name": "Rating", - "value": "2", - "__typename": "ProductReviewRating" + average_rating: 40, + ratings_breakdown: [{ + name: 'Rating', + value: '2', + __typename: 'ProductReviewRating', }], - "__typename": "ProductReview" - }], "__typename": "ProductReviews" + __typename: 'ProductReview', + }], + __typename: 'ProductReviews', }, - "small_image": { - "url": "https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg", - "position": null, - "disabled": null, - "label": "Joust Duffle Bag", - "__typename": "ProductImage" + small_image: { + url: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', + position: null, + disabled: null, + label: 'Joust Duffle Bag', + __typename: 'ProductImage', }, - "image": { - "url": "https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg", - "position": null, - "disabled": null, - "label": "Joust Duffle Bag", - "__typename": "ProductImage" + image: { + url: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', + position: null, + disabled: null, + label: 'Joust Duffle Bag', + __typename: 'ProductImage', }, - "media_gallery": [{ - "url": "https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg", - "position": 1, - "disabled": false, - "label": "Image", - "__typename": "ProductImage" - }] -} as any; + media_gallery: [{ + url: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', + position: 1, + disabled: false, + label: 'Image', + __typename: 'ProductImage', + }], +} as unknown as Product; describe('[magento-getters] product getters', () => { it('returns default values', () => { @@ -165,20 +152,21 @@ describe('[magento-getters] product getters', () => { { small: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', normal: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', - big: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg' - } + big: 'https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg', + }, ]); }); it('returns cover image', () => { - expect(getCoverImage({ images: [] } as any)).toEqual(null); + // @ts-expect-error intentional lack of image + expect(getCoverImage({})).toEqual(null); expect(getCoverImage(product)).toEqual('https://m2.caravelx.com/media/catalog/product/cache/746ba992681b73af7e339699b3e0caf7/m/b/mb01-blue-0.jpg'); }); it('returns product categories', () => { expect(getCategoryIds(product)).toEqual([ 'Mw==', - 'NA==' + 'NA==', ]); }); diff --git a/packages/composables/babel.config.js b/packages/composables/babel.config.js index d3540f51b..6c66aa368 100644 --- a/packages/composables/babel.config.js +++ b/packages/composables/babel.config.js @@ -1,9 +1,10 @@ +// eslint-disable-next-line unicorn/prefer-module module.exports = { presets: [ ['@babel/preset-env', { targets: { - node: 'current' - } - }] - ] + node: 'current', + }, + }], + ], }; diff --git a/packages/composables/jest.config.js b/packages/composables/jest.config.js index 65abac5c8..3e0bbf46f 100644 --- a/packages/composables/jest.config.js +++ b/packages/composables/jest.config.js @@ -22,7 +22,7 @@ module.exports = { testEnvironment: 'jsdom', transform: { '^.+\\.(m)js$': 'babel-jest', - '^.+\\.ts$': 'ts-jest' + '^.+\\.ts$': 'ts-jest', }, transformIgnorePatterns: [ 'node_modules', diff --git a/packages/composables/nuxt/cookie.js b/packages/composables/nuxt/cookie.js index 203e3ec44..21bcd8ed3 100644 --- a/packages/composables/nuxt/cookie.js +++ b/packages/composables/nuxt/cookie.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ import CookieUniversal from 'cookie-universal'; const baseSetCookieOptions = { diff --git a/packages/composables/nuxt/helpers/index.js b/packages/composables/nuxt/helpers/index.js index 397921d94..5abbf3f89 100644 --- a/packages/composables/nuxt/helpers/index.js +++ b/packages/composables/nuxt/helpers/index.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-extraneous-dependencies import defaultConfig from '@vue-storefront/magento/nuxt/defaultConfig'; export const getLocaleSettings = (app, moduleOptions) => { diff --git a/packages/composables/nuxt/index.js b/packages/composables/nuxt/index.js index 57c828263..aaed24be7 100644 --- a/packages/composables/nuxt/index.js +++ b/packages/composables/nuxt/index.js @@ -11,7 +11,7 @@ const getMissingFields = (options) => [ 'locales', ].filter((o) => options[o] === undefined); -export default function (moduleOptions) { +export default function composablesModule(moduleOptions) { const options = isNuxtI18nUsed(moduleOptions) ? { ...moduleOptions, diff --git a/packages/composables/nuxt/plugin.js b/packages/composables/nuxt/plugin.js index d3b40aa7f..669d6994e 100644 --- a/packages/composables/nuxt/plugin.js +++ b/packages/composables/nuxt/plugin.js @@ -1,4 +1,6 @@ -import { integrationPlugin } from '@vue-storefront/magento-theme/helpers/integrationPlugin' +/* eslint-disable import/no-extraneous-dependencies */ +// for removal of reliance on Vue Storefront Core +import { integrationPlugin } from '@vue-storefront/magento-theme/helpers/integrationPlugin'; import { mapConfigToSetupObject } from '@vue-storefront/magento/nuxt/helpers'; import defaultConfig from '@vue-storefront/magento/nuxt/defaultConfig'; import cookie from '@vue-storefront/magento/nuxt/cookie'; @@ -6,7 +8,9 @@ import cookie from '@vue-storefront/magento/nuxt/cookie'; const moduleOptions = JSON.parse('<%= JSON.stringify(options) %>'); // TODO should be moved to THEME and expose consistent cookie management API -export default integrationPlugin(({ app, res, req, integration }) => { +export default integrationPlugin(({ + app, res, req, integration, +}) => { const cartCookieName = moduleOptions.cookies?.cartCookieName || defaultConfig.cookies.cartCookieName; const customerCookieName = moduleOptions.cookies?.customerCookieName || defaultConfig.cookies.customerCookieName; const storeCookieName = moduleOptions.cookies?.storeCookieName || defaultConfig.cookies.storeCookieName; @@ -22,34 +26,34 @@ export default integrationPlugin(({ app, res, req, integration }) => { const getCartId = () => getCookies(cartCookieName); - const setCartId = (id) => !id ? removeCookie(cartCookieName) : setCookie(cartCookieName, id); + const setCartId = (id) => (!id ? removeCookie(cartCookieName) : setCookie(cartCookieName, id)); const getCustomerToken = () => getCookies(customerCookieName); - const setCustomerToken = (token) => !token ? removeCookie(customerCookieName) : setCookie(customerCookieName, token); + const setCustomerToken = (token) => (!token ? removeCookie(customerCookieName) : setCookie(customerCookieName, token)); const getStore = () => getCookies(storeCookieName); - const setStore = (id) => !id ? removeCookie(storeCookieName) : setCookie(storeCookieName, id); + const setStore = (id) => (!id ? removeCookie(storeCookieName) : setCookie(storeCookieName, id)); const getCurrency = () => getCookies(currencyCookieName); - const setCurrency = (id) => !id ? removeCookie(currencyCookieName) : setCookie(currencyCookieName, id); + const setCurrency = (id) => (!id ? removeCookie(currencyCookieName) : setCookie(currencyCookieName, id)); const getLocale = () => getCookies(localeCookieName); - const setLocale = (id) => !id ? removeCookie(localeCookieName) : setCookie(localeCookieName, id); + const setLocale = (id) => (!id ? removeCookie(localeCookieName) : setCookie(localeCookieName, id)); const getCountry = () => getCookies(countryCookieName); - const setCountry = id => !id ? removeCookie(countryCookieName) : setCookie(countryCookieName, id); + const setCountry = (id) => (!id ? removeCookie(countryCookieName) : setCookie(countryCookieName, id)); const settings = mapConfigToSetupObject({ moduleOptions, app, additionalProperties: { state: { - //Cart + // Cart getCartId, setCartId, // Customer @@ -65,7 +69,7 @@ export default integrationPlugin(({ app, res, req, integration }) => { getCountry, setCountry, }, - } + }, }); integration.configure('magento', settings); diff --git a/packages/composables/package.json b/packages/composables/package.json index 525729113..f16b21171 100644 --- a/packages/composables/package.json +++ b/packages/composables/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/magento", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "license": "MIT", "homepage": "https://github.com/vuestorefront/magento2", "bugs": { diff --git a/packages/composables/src/composables/useCart/index.ts b/packages/composables/src/composables/useCart/index.ts index 34466777f..3409fa882 100644 --- a/packages/composables/src/composables/useCart/index.ts +++ b/packages/composables/src/composables/useCart/index.ts @@ -67,6 +67,7 @@ const factoryParams: UseCartFactoryParams = { return createVirtualCart(); } + // eslint-disable-next-line no-param-reassign cartId = await createRealCart(); apiState.setCartId(cartId); } diff --git a/packages/composables/src/composables/useCategorySearch/index.ts b/packages/composables/src/composables/useCategorySearch/index.ts index 29c6eae27..8c24ad440 100644 --- a/packages/composables/src/composables/useCategorySearch/index.ts +++ b/packages/composables/src/composables/useCategorySearch/index.ts @@ -20,6 +20,7 @@ const factoryParams: UseCategorySearchFactory UseCategorySearch = useCategorySearchFactory(factoryParams); export default useCategorySearch; diff --git a/packages/composables/src/composables/useCurrency/index.ts b/packages/composables/src/composables/useCurrency/index.ts index 008da2dbd..e736b0e7f 100644 --- a/packages/composables/src/composables/useCurrency/index.ts +++ b/packages/composables/src/composables/useCurrency/index.ts @@ -7,7 +7,7 @@ import { useCurrencyFactory, UseCurrencyFactoryParams } from '../../factories/us import { UseCurrency } from '../../types/composables'; const factoryParams: UseCurrencyFactoryParams = { - load: async (context: Context, params): Promise => { + load: async (context: Context, _params): Promise => { const { data } = await context.$magento.api.currency(); return data.currency || {}; diff --git a/packages/composables/src/composables/useExternalCheckout/index.ts b/packages/composables/src/composables/useExternalCheckout/index.ts index b42f4ca86..e655aa072 100644 --- a/packages/composables/src/composables/useExternalCheckout/index.ts +++ b/packages/composables/src/composables/useExternalCheckout/index.ts @@ -12,6 +12,7 @@ const factoryParams: UseExternalCheckoutFactoryParams = { cart: useCart(), }; }, + // eslint-disable-next-line @typescript-eslint/require-await initializeCheckout: async (context: Context, params) => { Logger.debug('[Magento]: Initialize external checkout', { params }); diff --git a/packages/composables/src/composables/useReview/index.ts b/packages/composables/src/composables/useReview/index.ts index ee2b930c3..a768922ca 100644 --- a/packages/composables/src/composables/useReview/index.ts +++ b/packages/composables/src/composables/useReview/index.ts @@ -46,7 +46,7 @@ ProductReviewRatingMetadata> = { return data?.createProductReview?.review ?? {}; }, - loadReviewMetadata: async (context: Context, params) => { + loadReviewMetadata: async (context: Context, _params) => { Logger.debug('[Magento] load review metadata'); const { data } = await context.$magento.api.productReviewRatingsMetadata(); diff --git a/packages/composables/src/composables/useUserShipping/index.ts b/packages/composables/src/composables/useUserShipping/index.ts index de231d6c1..07859bd94 100644 --- a/packages/composables/src/composables/useUserShipping/index.ts +++ b/packages/composables/src/composables/useUserShipping/index.ts @@ -31,7 +31,7 @@ const factoryParams: UseUserShippingFactoryParams = { deleteAddress: async (context: Context, params) => { Logger.debug('[Magento] delete shipping address', { params }); - const { data } = await context.$magento.api.deleteCustomerAddress(params.address.id); + const { data } = await context.$magento.api.deleteCustomerAddress(params.address.id as number); return data?.deleteCustomerAddress ?? {}; }, diff --git a/packages/composables/src/composables/useWishlist/index.ts b/packages/composables/src/composables/useWishlist/index.ts index a238636aa..182be3a56 100644 --- a/packages/composables/src/composables/useWishlist/index.ts +++ b/packages/composables/src/composables/useWishlist/index.ts @@ -121,10 +121,7 @@ const factoryParams: UseWishlistFactoryParams = { } }, removeItem: async (context, params) => { - const { - product, - currentWishlist, - } = params; + const { currentWishlist } = params; Logger.debug('[Magento Storefront]: useWishlist.removeItem params->', params); const itemOnWishlist = findItemOnWishlist(currentWishlist, params.product); @@ -137,8 +134,8 @@ const factoryParams: UseWishlistFactoryParams = { return data?.removeProductsFromWishlist?.wishlist ?? {}; }, - clear: async ({ currentWishlist }) => ({}), - isInWishlist: (context, params) => { + clear: () => (Promise.resolve()), + isInWishlist: (_context, params) => { const { currentWishlist, product, diff --git a/packages/composables/src/factories/useCurrencyFactory.ts b/packages/composables/src/factories/useCurrencyFactory.ts index 294b4ea5b..51f652f6f 100644 --- a/packages/composables/src/factories/useCurrencyFactory.ts +++ b/packages/composables/src/factories/useCurrencyFactory.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ /** * @deprecated since version 1.0.0 */ diff --git a/packages/composables/src/factories/useForgotPasswordFactory.ts b/packages/composables/src/factories/useForgotPasswordFactory.ts index d8414b60f..a0fc0173f 100644 --- a/packages/composables/src/factories/useForgotPasswordFactory.ts +++ b/packages/composables/src/factories/useForgotPasswordFactory.ts @@ -6,7 +6,6 @@ import { ComposableFunctionArgs, configureFactoryParams, Context, - CustomQuery, FactoryParams, Logger, sharedRef, diff --git a/packages/composables/src/factories/useStoreFactory.ts b/packages/composables/src/factories/useStoreFactory.ts index 76f193a22..c5fa07ece 100644 --- a/packages/composables/src/factories/useStoreFactory.ts +++ b/packages/composables/src/factories/useStoreFactory.ts @@ -39,7 +39,7 @@ export function useStoreFactory): Promise => { + const change = (store: ComposableFunctionArgs) => { loading.value = true; try { _factoryParams.change(store); diff --git a/packages/composables/src/factories/useWishlistFactory.ts b/packages/composables/src/factories/useWishlistFactory.ts index 2bf155c17..81f053679 100644 --- a/packages/composables/src/factories/useWishlistFactory.ts +++ b/packages/composables/src/factories/useWishlistFactory.ts @@ -12,6 +12,7 @@ import { ComposableFunctionArgs, PlatformApi, } from '@vue-storefront/core'; +import { Wishlist } from '@vue-storefront/magento-api'; import { UseWishlist, UseWishlistErrors } from '../types/composables'; export interface UseWishlistFactoryParams( factoryParams: UseWishlistFactoryParams, ) => { - const calculateWishlistTotal = (wishlists) => wishlists.reduce((prev, next) => (prev?.items_count ?? 0) + (next?.items_count ?? 0), 0); + const calculateWishlistTotal = (wishlists: Wishlist[]) => wishlists.reduce((prev, next) => prev + (next?.items_count ?? 0), 0); const useWishlist = (ssrKey = 'useWishlistFactory'): UseWishlist => { const loading: Ref = sharedRef(false, `useWishlist-loading-${ssrKey}`); @@ -135,9 +136,9 @@ export const useWishlistFactory = { Logger.debug(`useWishlist/${ssrKey}/loadItemsCount`); try { - const loadedWishlist = await _factoryParams.loadItemsCount(params); - itemsCount.value = calculateWishlistTotal(loadedWishlist); + const loadedWishlist : WISHLIST = await _factoryParams.loadItemsCount(params); + itemsCount.value = calculateWishlistTotal([loadedWishlist]); error.value.loadItemsCount = null; } catch (err) { error.value.loadItemsCount = err; diff --git a/packages/composables/src/getters/categoryGetters.ts b/packages/composables/src/getters/categoryGetters.ts index 4298c4c6e..8d95164bf 100644 --- a/packages/composables/src/getters/categoryGetters.ts +++ b/packages/composables/src/getters/categoryGetters.ts @@ -2,11 +2,11 @@ * @deprecated since version 1.0.0 */ import { CategoryGetters, AgnosticCategoryTree, AgnosticBreadcrumb } from '@vue-storefront/core'; -import { Category } from '@vue-storefront/magento-api'; +import { CategoryTree } from '@vue-storefront/magento-api'; import { buildCategoryTree } from '../helpers/buildCategoryTree'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getTree = (category: Category): AgnosticCategoryTree | null => { +export const getTree = (category: CategoryTree): AgnosticCategoryTree | null => { if (!category) { return null; } @@ -14,7 +14,7 @@ export const getTree = (category: Category): AgnosticCategoryTree | null => { }; export const getCategoryTree = ( - category: Category, + category: CategoryTree, currentCategory: string = '', withProducts = false, ): AgnosticCategoryTree | null => ( @@ -45,7 +45,7 @@ export const getCategoryBreadcrumbs = (category: any): AgnosticBreadcrumb[] => { return breadcrumbs; }; -const categoryGetters: CategoryGetters = { +const categoryGetters: CategoryGetters = { getTree, getBreadcrumbs: getCategoryBreadcrumbs, getCategoryTree, diff --git a/packages/composables/src/getters/facetGetters.ts b/packages/composables/src/getters/facetGetters.ts index 59192e0df..7353e45ef 100644 --- a/packages/composables/src/getters/facetGetters.ts +++ b/packages/composables/src/getters/facetGetters.ts @@ -24,7 +24,7 @@ import { // eslint-disable-next-line @typescript-eslint/no-unused-vars const getAll = (searchData: SearchData, criteria?: string[]): AgnosticFacet[] => buildFacets(searchData, reduceForFacets, criteria); -const getGrouped = (searchData, criteria?: string[]): AgnosticGroupedFacet[] => buildFacets(searchData, reduceForGroupedFacets, criteria) +const getGrouped = (searchData: SearchData, criteria?: string[]): AgnosticGroupedFacet[] => buildFacets(searchData, reduceForGroupedFacets, criteria) ?.filter((facet) => facet.options && facet.options.length > 0); // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/composables/src/getters/orderGetters.ts b/packages/composables/src/getters/orderGetters.ts index 6512be9ec..a07c36c0a 100644 --- a/packages/composables/src/getters/orderGetters.ts +++ b/packages/composables/src/getters/orderGetters.ts @@ -2,24 +2,25 @@ * @deprecated since version 1.0.0 */ import { AgnosticPagination } from '@vue-storefront/core'; +import { CustomerOrder, OrderItemInterface } from '@vue-storefront/magento-api'; -export const getDate = (order: any): string => new Date(order?.created_at).toLocaleDateString() || ''; +export const getDate = (order: CustomerOrder): string => new Date(order?.created_at).toLocaleDateString() || ''; -export const getId = (order: any): string => String(Number.parseInt(order?.order_number, 10) || Math.floor(Math.random() * 100)); +export const getId = (order: CustomerOrder): string => String(Number.parseInt(order?.order_number, 10) || Math.floor(Math.random() * 100)); -export const getStatus = (order: any): string => order?.status || 'Failed'; +export const getStatus = (order: CustomerOrder): string => order?.status || 'Failed'; -export const getPrice = (order: any): number | null => order?.grand_total || 0; +export const getPrice = (order: CustomerOrder): number | null => order?.grand_total || 0; -export const getItems = (order: any): any[] => order?.items || []; +export const getItems = (order: CustomerOrder): OrderItemInterface[] => order?.items || []; -export const getItemSku = (item: any): string => item?.product_sku || 0; +export const getItemSku = (item: OrderItemInterface): string => item?.product_sku || ''; -export const getItemName = (item: any): string => item?.product_name || 0; +export const getItemName = (item: OrderItemInterface): string => item?.product_name || ''; -export const getItemQty = (item: any): number => item?.quantity_ordered || 0; +export const getItemQty = (item: OrderItemInterface): number => item?.quantity_ordered || 0; -export const getItemPrice = (item: any): number => item?.product_sale_price?.value || 0; +export const getItemPrice = (item: OrderItemInterface): number => item?.product_sale_price?.value || 0; export const getFormattedPrice = (price: number) => String(price); diff --git a/packages/composables/src/getters/productGetters.ts b/packages/composables/src/getters/productGetters.ts index 630016e1f..b158918c1 100644 --- a/packages/composables/src/getters/productGetters.ts +++ b/packages/composables/src/getters/productGetters.ts @@ -10,7 +10,7 @@ import { } from '@vue-storefront/core'; import { BundleProduct, - Category, GroupedProduct, + Category, CategoryTree, GroupedProduct, Product, } from '@vue-storefront/magento-api'; @@ -105,7 +105,7 @@ export const getProductThumbnailImage = (product: Product): string => { return product.thumbnail.url; }; -export const getFiltered = (products: Product[], filters: ProductVariantFilters | any = {}): Product[] => { +export const getFiltered = (products: Product[], _filters: ProductVariantFilters | any = {}): Product[] => { if (!products) { return []; } @@ -213,7 +213,7 @@ export const getFormattedPrice = (price: number) => { }).format(price); }; -export const getBreadcrumbs = (product: any, category?: Category): AgnosticBreadcrumb[] => { +export const getBreadcrumbs = (product: Product, category?: CategoryTree): AgnosticBreadcrumb[] => { let breadcrumbs = []; if (!product) { @@ -221,7 +221,7 @@ export const getBreadcrumbs = (product: any, category?: Category): AgnosticBread } if (category) { - breadcrumbs = categoryGetters.getBreadcrumbs(category) as AgnosticBreadcrumb[]; + breadcrumbs = categoryGetters.getBreadcrumbs(category); } breadcrumbs.push({ diff --git a/packages/composables/src/getters/reviewGetters.ts b/packages/composables/src/getters/reviewGetters.ts index 2bf2ca85c..348e3d616 100644 --- a/packages/composables/src/getters/reviewGetters.ts +++ b/packages/composables/src/getters/reviewGetters.ts @@ -27,7 +27,7 @@ export const getReviewDate = (item: ProductReview): string => item.created_at; export const getTotalReviews = (review: ProductReviews): number => review?.review_count || 0; -export const getAverageRating = (review): number => (review?.reviews?.items?.reduce((acc, curr) => Number.parseInt(`${acc}`, 10) + getReviewRating(curr), 0)) / (review?.review_count || 1) || 0; +export const getAverageRating = (review: ProductReviews): number => (review?.reviews?.items?.reduce((acc, curr) => Number.parseInt(`${acc}`, 10) + getReviewRating(curr as ProductReview), 0) ?? 0) / (review?.review_count || 1) || 0; export const getRatesCount = (_review: ProductReviews): AgnosticRateCount[] => []; diff --git a/packages/composables/src/getters/wishlistGetters.ts b/packages/composables/src/getters/wishlistGetters.ts index 97763ad9a..f08b91371 100644 --- a/packages/composables/src/getters/wishlistGetters.ts +++ b/packages/composables/src/getters/wishlistGetters.ts @@ -7,19 +7,19 @@ import { AgnosticTotals, AgnosticPagination, } from '@vue-storefront/core'; import { - Wishlist, WishlistQuery, + Wishlist, WishlistItemInterface, } from '@vue-storefront/magento-api'; -export type WishlistProduct = WishlistQuery['customer']['wishlists'][0]['items_v2']['items'][0] & { variant: any }; +export type WishlistProduct = WishlistItemInterface; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getItems = (wishlist): WishlistProduct[] => wishlist.items_v2.items; +export const getItems = (wishlist: Wishlist): WishlistItemInterface[] => wishlist.items_v2.items; -export const getItemName = (product: WishlistProduct): string => product?.product?.name || ''; +export const getItemName = (product: WishlistItemInterface): string => product?.product?.name || ''; -export const getItemImage = (product: WishlistProduct): string => product?.product?.thumbnail.url || ''; +export const getItemImage = (product: WishlistItemInterface): string => product?.product?.thumbnail.url || ''; -export const getItemPrice = (product: WishlistProduct): AgnosticPrice => { +export const getItemPrice = (product: WishlistItemInterface): AgnosticPrice => { let regular = 0; let special = null; @@ -38,22 +38,16 @@ export const getItemPrice = (product: WishlistProduct): AgnosticPrice => { }; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getItemQty = (product: WishlistProduct): number => product.quantity; +export const getItemQty = (product: WishlistItemInterface): number => product.quantity; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getItemAttributes = (product: WishlistProduct, filterByAttributeName?: string[]) => ({ '': '' }); +export const getItemAttributes = (_product: WishlistItemInterface, _filterByAttributeName?: string[]) => ({ '': '' }); -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getItemSku = (product: WishlistProduct): string => product?.product?.sku || ''; +export const getItemSku = (product: WishlistItemInterface): string => product?.product?.sku || ''; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getTotals = (wishlist): AgnosticTotals => { +export const getTotals = (wishlist: Wishlist | Wishlist[]): AgnosticTotals => { if (Array.isArray(wishlist)) { return wishlist[0]?.items_v2?.items.reduce((acc, curr) => ({ - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands total: acc.total + getItemPrice(curr).special, - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands subtotal: acc.subtotal + getItemPrice(curr).regular, }), ({ total: 0, subtotal: 0 })); } @@ -63,14 +57,11 @@ export const getTotals = (wishlist): AgnosticTotals => { }), ({ total: 0, subtotal: 0 })); }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getShippingPrice = (wishlist: Wishlist): number => 0; +export const getShippingPrice = (_wishlist: Wishlist): number => 0; -// eslint-disable-next-line @typescript-eslint/no-unused-vars export const getTotalItems = (wishlist: Wishlist): number => (Array.isArray(wishlist) ? wishlist[0]?.items_count : (wishlist?.items_count || 0)); -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const getFormattedPrice = (price: number): string => ''; +export const getFormattedPrice = (_price: number): string => ''; const getPagination = (wishlistData: Wishlist): AgnosticPagination => ({ currentPage: wishlistData?.items_v2?.page_info?.current_page || 1, @@ -81,22 +72,24 @@ const getPagination = (wishlistData: Wishlist): AgnosticPagination => ({ }); const getProducts = (wishlistData: Wishlist[] | Wishlist): { - product: WishlistProduct; + product: WishlistItemInterface; quantity: number; added_at: string; }[] => { if (!wishlistData || (Array.isArray(wishlistData) && wishlistData.length === 0)) { return []; } - - const reducer = (acc, curr) => [...acc, ...curr?.items_v2?.items.map((item) => ({ + const reducer = ( + acc, + curr: Wishlist, + ) => [...acc, ...curr?.items_v2?.items.map((item) => ({ product: item.product, quantity: item.quantity, added_at: item.added_at, id: item.id, - }))]; + })) ?? []]; - const mapper = (item) => ({ + const mapper = (item: WishlistItemInterface) => ({ product: item.product, quantity: item.quantity, added_at: item.added_at, @@ -104,19 +97,19 @@ const getProducts = (wishlistData: Wishlist[] | Wishlist): { }); return Array.isArray(wishlistData) - ? wishlistData.reduce((acc, curr) => reducer(acc, curr), []) - : wishlistData?.items_v2?.items.map((e) => mapper(e)); + ? wishlistData.reduce((accumulator, element) => reducer(accumulator, element), []) + : wishlistData?.items_v2?.items.map((element) => mapper(element)); }; -export interface WishlistGetters extends BaseWishlistGetters { +export interface WishlistGetters extends BaseWishlistGetters { getShippingPrice(wishlist: Wishlist): number; - getItemQty(product: WishlistProduct): number; + getItemQty(product: WishlistItemInterface): number; getPagination(wishlistData): AgnosticPagination; getProducts(wishlistData): { - product: WishlistProduct; + product: WishlistItemInterface; quantity: number; added_at: string; }[]; diff --git a/packages/composables/src/helpers/buildCategoryTree.ts b/packages/composables/src/helpers/buildCategoryTree.ts index 272b5556a..1c67c8b79 100644 --- a/packages/composables/src/helpers/buildCategoryTree.ts +++ b/packages/composables/src/helpers/buildCategoryTree.ts @@ -2,9 +2,10 @@ * @deprecated since version 1.0.0 */ import { AgnosticCategoryTree } from '@vue-storefront/core'; +import { CategoryTree } from '@vue-storefront/magento-api'; import { htmlDecode } from './htmlDecoder'; -export const buildCategoryTree = (rootCategory: any, currentCategory: string, withProducts = false): AgnosticCategoryTree => { +export const buildCategoryTree = (rootCategory: CategoryTree, currentCategory: string, withProducts = false): AgnosticCategoryTree => { const hasChildren = Array.isArray(rootCategory.children) && rootCategory.children.length > 0; const isCurrent = rootCategory.uid === currentCategory; const label = htmlDecode(rootCategory.name); diff --git a/packages/composables/src/helpers/htmlDecoder.ts b/packages/composables/src/helpers/htmlDecoder.ts index 2e23e5247..7f6ee5622 100644 --- a/packages/composables/src/helpers/htmlDecoder.ts +++ b/packages/composables/src/helpers/htmlDecoder.ts @@ -1,7 +1,7 @@ /** * @deprecated since version 1.0.0 */ -export function htmlDecode(input) { +export function htmlDecode(input: string) { const formatName = () => { try { const domParser = new DOMParser(); diff --git a/packages/composables/tsconfig.eslint.json b/packages/composables/tsconfig.eslint.json new file mode 100644 index 000000000..a47730729 --- /dev/null +++ b/packages/composables/tsconfig.eslint.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "exclude": [ + "lib", + "node_modules" + ] +} diff --git a/packages/theme/app/router.scrollBehavior.js b/packages/theme/app/router.scrollBehavior.js index 4e0816d4c..64be137d8 100644 --- a/packages/theme/app/router.scrollBehavior.js +++ b/packages/theme/app/router.scrollBehavior.js @@ -1,6 +1,6 @@ -export default function (_to, _from, savedPosition) { +export default function scrollBehavior(_to, _from, savedPosition) { return savedPosition || { x: 0, y: 0, }; -}; +} diff --git a/packages/theme/components/AddToWishlist.vue b/packages/theme/components/AddToWishlist.vue index f7137a114..f9e88c2b3 100644 --- a/packages/theme/components/AddToWishlist.vue +++ b/packages/theme/components/AddToWishlist.vue @@ -17,7 +17,7 @@ - diff --git a/packages/theme/components/CartSidebar.vue b/packages/theme/components/CartSidebar.vue index 292189ef9..06f3f7481 100644 --- a/packages/theme/components/CartSidebar.vue +++ b/packages/theme/components/CartSidebar.vue @@ -238,7 +238,7 @@ - diff --git a/packages/theme/components/CurrencySelector.vue b/packages/theme/components/CurrencySelector.vue index f7c9c6e2a..ebbc34a72 100644 --- a/packages/theme/components/CurrencySelector.vue +++ b/packages/theme/components/CurrencySelector.vue @@ -14,7 +14,7 @@ /> - @@ -82,15 +109,27 @@ export default defineComponent({ width: 100%; min-height: 14rem; color: var(--c-text); - background-image: var(--hero-background-image, var(--hero-background-image-mobile)); - background-repeat: no-repeat; - background-size: cover; + + &__image { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + + ::v-deep img { + width: 100%; + height: 100%; + object-fit: cover; + } + } &__wrapper { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; + position: relative; width: 100%; padding: var(--spacer-sm); text-decoration: none; @@ -130,7 +169,6 @@ export default defineComponent({ } @include for-desktop { - background-image: var(--hero-background-image); min-height: 36.625rem; &__wrapper { diff --git a/packages/theme/components/InstagramFeed.vue b/packages/theme/components/InstagramFeed.vue index a1f0bbf5e..1dc2c30ab 100644 --- a/packages/theme/components/InstagramFeed.vue +++ b/packages/theme/components/InstagramFeed.vue @@ -92,7 +92,7 @@ - diff --git a/packages/theme/components/TopBar/useTopBar.ts b/packages/theme/components/TopBar/useTopBar.ts index 6c5f1137e..37ee4ae6d 100644 --- a/packages/theme/components/TopBar/useTopBar.ts +++ b/packages/theme/components/TopBar/useTopBar.ts @@ -1,20 +1,25 @@ import { ref, onMounted } from '@nuxtjs/composition-api'; +import type { Currency, StoreConfig } from '~/modules/GraphQL/types'; -import useApi from '~/composables/useApi'; +import { useApi } from '~/composables/useApi'; import checkStoresAndCurrencyQuery from './checkStoresAndCurrency.gql'; +type StoresAndCurrencyQueryResponse = { + availableStores: Pick[], + currency: Pick, +}; + export const useTopBar = () => { const { query } = useApi(); - const hasStoresToSelect = ref(null); - const hasCurrencyToSelect = ref(null); + const hasStoresToSelect = ref(null); + const hasCurrencyToSelect = ref(null); onMounted(() => { - query(checkStoresAndCurrencyQuery) + query(checkStoresAndCurrencyQuery) + // eslint-disable-next-line promise/always-return .then((data) => { - hasStoresToSelect.value = data?.availableStores?.length; - hasCurrencyToSelect.value = data?.currency?.available_currency_codes?.length > 1; - - return data; + hasStoresToSelect.value = data.availableStores.length > 1; + hasCurrencyToSelect.value = data.currency.available_currency_codes.length > 1; }) .catch(() => { hasStoresToSelect.value = false; diff --git a/packages/theme/components/UserAddressDetails.vue b/packages/theme/components/UserAddressDetails.vue index beea8b071..1be1d1039 100644 --- a/packages/theme/components/UserAddressDetails.vue +++ b/packages/theme/components/UserAddressDetails.vue @@ -37,15 +37,17 @@ - + diff --git a/packages/theme/modules/catalog/category/components/breadcrumbs/__tests__/CategoryBreadcrumbs.spec.ts b/packages/theme/modules/catalog/category/components/breadcrumbs/__tests__/CategoryBreadcrumbs.spec.ts new file mode 100644 index 000000000..98408f455 --- /dev/null +++ b/packages/theme/modules/catalog/category/components/breadcrumbs/__tests__/CategoryBreadcrumbs.spec.ts @@ -0,0 +1,67 @@ +import { mount } from '@vue/test-utils'; +import * as composables from '@nuxtjs/composition-api'; +import { + categoryAncestorsFirstLevelMock, + categoryAncestorsSecondLevelMock, + categoryAncestorsThirdLevelMock, + useTraverseCategoryMock, +} from '~/test-utils/mocks/useTraverseCategoryMock'; +import { useUiHelpers } from '~/composables'; +import { useTraverseCategory } from '~/modules/catalog/category/helpers/useTraverseCategory'; +import CategoryBreadcrumbs from '~/modules/catalog/category/components/breadcrumbs/CategoryBreadcrumbs.vue'; +import type { CategoryTree } from '~/modules/GraphQL/types'; + +jest.mock('@nuxtjs/composition-api', () => { + const originalComposables = jest.requireActual('@nuxtjs/composition-api'); + return { + ...originalComposables, + useContext: jest.fn(), + }; +}); +jest.mock('~/composables'); +jest.mock('~/modules/catalog/category/helpers/useTraverseCategory'); + +(composables.useContext as jest.Mock).mockReturnValue({ + localePath: jest.fn((path: string) => path), +}); +(useUiHelpers as jest.Mock).mockReturnValue({ + getCatLink: jest.fn( + (category: CategoryTree): string => `/c/${category.url_path}${category.url_suffix || ''}`, + ), +}); + +describe('CategoryBreadcrumbs.vue', () => { + it('Breadcrumbs should not render if there is only a first level category', () => { + (useTraverseCategory as jest.Mock).mockReturnValue(useTraverseCategoryMock(categoryAncestorsFirstLevelMock)); + const wrapper = mount(CategoryBreadcrumbs); + expect(wrapper).toBeDefined(); + expect(wrapper.find('li').exists()).toBeFalsy(); + }); + + it('Breadcrumbs must have one item for the second level category', () => { + (useTraverseCategory as jest.Mock).mockReturnValue(useTraverseCategoryMock(categoryAncestorsSecondLevelMock)); + const wrapper = mount(CategoryBreadcrumbs); + expect(wrapper.findAll('li')).toHaveLength(1); + }); + + it('Breadcrumbs must have two item for the third level category', () => { + (useTraverseCategory as jest.Mock).mockReturnValue(useTraverseCategoryMock(categoryAncestorsThirdLevelMock)); + const wrapper = mount(CategoryBreadcrumbs); + expect(wrapper.findAll('li')).toHaveLength(2); + }); + + it('Breadcrumb must have link that should have href attribute', () => { + (useTraverseCategory as jest.Mock).mockReturnValue(useTraverseCategoryMock(categoryAncestorsSecondLevelMock)); + const wrapper = mount(CategoryBreadcrumbs); + const link = wrapper.find('a'); + expect(link).toBeDefined(); + expect(link.attributes('href')).toBeDefined(); + }); + + it('The last breadcrumb must have \'current\' class', () => { + (useTraverseCategory as jest.Mock).mockReturnValue(useTraverseCategoryMock(categoryAncestorsThirdLevelMock)); + const wrapper = mount(CategoryBreadcrumbs); + const links = wrapper.findAll('a'); + expect(links.at(-1).classes()).toContain('current'); + }); +}); diff --git a/packages/theme/modules/catalog/category/components/cms/__tests__/CmsContent.spec.ts b/packages/theme/modules/catalog/category/components/cms/__tests__/CmsContent.spec.ts new file mode 100644 index 000000000..9203bc26d --- /dev/null +++ b/packages/theme/modules/catalog/category/components/cms/__tests__/CmsContent.spec.ts @@ -0,0 +1,6 @@ +import { cmsContentTest } from '~/test-utils/tests/cmsContent'; +import CmsContent from '~/modules/catalog/category/components/cms/CmsContent.vue'; + +describe('CmsContent.vue', () => { + cmsContentTest(CmsContent); +}); diff --git a/packages/theme/modules/catalog/category/components/cms/categoryContent.gql.ts b/packages/theme/modules/catalog/category/components/cms/categoryContent.gql.ts index 0cfc6c001..d0413f313 100644 --- a/packages/theme/modules/catalog/category/components/cms/categoryContent.gql.ts +++ b/packages/theme/modules/catalog/category/components/cms/categoryContent.gql.ts @@ -1,9 +1,9 @@ import { gql } from 'graphql-request'; export default gql` - query category($id: Int!) { - category(id: $id) { - id + query getCategoryContentData($filters: CategoryFilterInput) { + categoryList(filters: $filters) { + uid display_mode landing_page cms_block { diff --git a/packages/theme/modules/catalog/category/components/cms/useCategoryContent.ts b/packages/theme/modules/catalog/category/components/cms/useCategoryContent.ts index bc062e01a..dbe633aa1 100644 --- a/packages/theme/modules/catalog/category/components/cms/useCategoryContent.ts +++ b/packages/theme/modules/catalog/category/components/cms/useCategoryContent.ts @@ -21,10 +21,11 @@ interface CmsContentInterface { export const useCategoryContent = () => { const { query } = useApi(); - const getContentData = async (id: number): Promise => { - const data = await query(categoryContentQuery, { id }); - const cmsBlock = data?.category?.cms_block ?? { cmsBlock: { content: '' } }; - const displayMode = data?.category?.display_mode ?? displayModesEnum.PRODUCTS; + const getContentData = async (uid: string): Promise => { + const data = await query(categoryContentQuery, { filters: { category_uid: { eq: uid } } }); + const category = data.categoryList[0] ?? {}; + const cmsBlock = category?.cms_block ?? { cmsBlock: { content: '' } }; + const displayMode = category?.display_mode ?? displayModesEnum.PRODUCTS; const isShowCms = displayMode === displayModesEnum.PAGE || displayMode === displayModesEnum.PRODUCTS_AND_PAGE; const isShowProducts = displayMode === displayModesEnum.PRODUCTS || displayMode === displayModesEnum.PRODUCTS_AND_PAGE; diff --git a/packages/theme/modules/catalog/category/components/filters/CategoryFilters.vue b/packages/theme/modules/catalog/category/components/filters/CategoryFilters.vue index 1e54535ce..a33d2603c 100644 --- a/packages/theme/modules/catalog/category/components/filters/CategoryFilters.vue +++ b/packages/theme/modules/catalog/category/components/filters/CategoryFilters.vue @@ -63,8 +63,10 @@ + - - {{ filter.label }} - +