diff --git a/.gitignore b/.gitignore
index a5199c5671..a6c33bbdb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,8 @@ storybook-static/
node_modules/
jspm_packages/
+!packages/cli/src/__mocks__/**/node_modules
+
# Optional npm cache directory
.npm
diff --git a/apps/site/package.json b/apps/site/package.json
index c380f50253..0fd6e3f824 100644
--- a/apps/site/package.json
+++ b/apps/site/package.json
@@ -1,6 +1,6 @@
{
"name": "site",
- "version": "3.0.77",
+ "version": "3.0.88",
"author": "Emerson Laurentino @emersonlaurentino",
"license": "MIT",
"private": true,
@@ -10,9 +10,9 @@
"start": "next start"
},
"dependencies": {
- "@faststore/api": "^3.0.76",
- "@faststore/sdk": "^3.0.68",
- "@faststore/ui": "^3.0.77",
+ "@faststore/api": "^3.0.88",
+ "@faststore/sdk": "^3.0.88",
+ "@faststore/ui": "^3.0.88",
"next": "13.0.7",
"nextra": "^2.8.0",
"nextra-theme-docs": "^2.8.0",
@@ -21,7 +21,7 @@
"sass": "^1.57.1"
},
"devDependencies": {
- "@faststore/eslint-config": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
"@types/node": "^18.11.16",
"eslint": "7.32.0",
"react-docgen-typescript": "^2.2.2",
diff --git a/apps/site/pages/components/molecules/input-field.mdx b/apps/site/pages/components/molecules/input-field.mdx
index a9c8cf8e3e..12c4b4f187 100644
--- a/apps/site/pages/components/molecules/input-field.mdx
+++ b/apps/site/pages/components/molecules/input-field.mdx
@@ -205,13 +205,19 @@ export const propsInputField = [
#### Label
-
+
-
+
+
+
+#### Button
+
+
+
### Variants
diff --git a/apps/site/pages/components/molecules/order-summary.mdx b/apps/site/pages/components/molecules/order-summary.mdx
index 9bc0e655ef..bb0c831ba9 100644
--- a/apps/site/pages/components/molecules/order-summary.mdx
+++ b/apps/site/pages/components/molecules/order-summary.mdx
@@ -114,6 +114,10 @@ Follow the instructions in the [Importing FastStore UI component styles](/docs/c
token="--fs-order-summary-margin-bottom"
value="var(--fs-spacing-2)"
/>
+
+
diff --git a/apps/site/pages/components/molecules/toast.mdx b/apps/site/pages/components/molecules/toast.mdx
index 1480bd4efd..11624dcc69 100644
--- a/apps/site/pages/components/molecules/toast.mdx
+++ b/apps/site/pages/components/molecules/toast.mdx
@@ -315,7 +315,7 @@ export const propsToast = [
token="--fs-toast-title-weight"
value="var(--fs-text-weight-bold)"
/>
-
+
diff --git a/apps/site/pages/components/organisms/payment-methods.mdx b/apps/site/pages/components/organisms/payment-methods.mdx
index 14c552e8b1..c94eb2c530 100644
--- a/apps/site/pages/components/organisms/payment-methods.mdx
+++ b/apps/site/pages/components/organisms/payment-methods.mdx
@@ -84,14 +84,16 @@ Follow the instructions in the [Importing FastStore UI component styles](/docs/c
```tsx
export const flags = [
- { icon: { icon: 'Visa' }, alt: 'Visa' },
+ { icon: { icon: 'Amex' }, alt: 'Amex' },
+ { icon: { icon: 'ApplePay' }, alt: 'ApplePay' },
+ { icon: { icon: 'EloCard' }, alt: 'Elo Card' },
{ icon: { icon: 'Diners' }, alt: 'Diners Club' },
+ { icon: { icon: 'GooglePay' }, alt: 'GooglePay' },
{ icon: { icon: 'Mastercard' }, alt: 'Mastercard' },
- { icon: { icon: 'EloCard' }, alt: 'Elo Card' },
{ icon: { icon: 'PayPal' }, alt: 'PayPal' },
+ { icon: { icon: 'Pix' }, alt: 'Pix' },
{ icon: { icon: 'Stripe' }, alt: 'Stripe' },
- { icon: { icon: 'GooglePay' }, alt: 'GooglePay' },
- { icon: { icon: 'ApplePay' }, alt: 'ApplePay' },
+ { icon: { icon: 'Visa' }, alt: 'Visa' },
]
Payment Methods} flagList={flags} />
@@ -101,14 +103,16 @@ Follow the instructions in the [Importing FastStore UI component styles](/docs/c
export const flags = [
- { icon: { icon: 'Visa' }, alt: 'Visa' },
+ { icon: { icon: 'Amex' }, alt: 'Amex' },
+ { icon: { icon: 'ApplePay' }, alt: 'ApplePay' },
+ { icon: { icon: 'EloCard' }, alt: 'Elo Card' },
{ icon: { icon: 'Diners' }, alt: 'Diners Club' },
+ { icon: { icon: 'GooglePay' }, alt: 'GooglePay' },
{ icon: { icon: 'Mastercard' }, alt: 'Mastercard' },
- { icon: { icon: 'EloCard' }, alt: 'Elo Card' },
{ icon: { icon: 'PayPal' }, alt: 'PayPal' },
+ { icon: { icon: 'Pix' }, alt: 'Pix' },
{ icon: { icon: 'Stripe' }, alt: 'Stripe' },
- { icon: { icon: 'GooglePay' }, alt: 'GooglePay' },
- { icon: { icon: 'ApplePay' }, alt: 'ApplePay' },
+ { icon: { icon: 'Visa' }, alt: 'Visa' },
]
---
@@ -177,6 +181,11 @@ export const propsFlag = [
token="--fs-payment-methods-flag-height"
value="var(--fs-spacing-4)"
/>
+
+
+
@@ -45,41 +47,25 @@ export const desktopList = [
{ size: 14, token: 'var(--fs-text-size-1)' },
{ size: 16, token: 'var(--fs-text-size-2)' },
{ size: 20, token: 'var(--fs-text-size-3)' },
- { size: 25, token: 'var(--fs-text-size-4)' },
- { size: 31, token: 'var(--fs-text-size-5)' },
- { size: 39, token: 'var(--fs-text-size-6)' },
- { size: 48, token: 'var(--fs-text-size-7)' },
- { size: 61, token: 'var(--fs-text-size-8)' },
+ { size: 24, token: 'var(--fs-text-size-4)' },
+ { size: 28, token: 'var(--fs-text-size-5)' },
+ { size: 32, token: 'var(--fs-text-size-6)' },
+ { size: 40, token: 'var(--fs-text-size-7)' },
+ { size: 48, token: 'var(--fs-text-size-8)' },
+ { size: 56, token: 'var(--fs-text-size-9)' },
]
export const mobileList = [
{ size: 12, token: 'var(--fs-text-size-0)' },
{ size: 14, token: 'var(--fs-text-size-1)' },
{ size: 16, token: 'var(--fs-text-size-2)' },
- {
- size: 18,
- token: 'var(--fs-text-size-3)',
- },
- {
- size: 20,
- token: 'var(--fs-text-size-4)',
- },
- {
- size: 23,
- token: 'var(--fs-text-size-5)',
- },
- {
- size: 26,
- token: 'var(--fs-text-size-6)',
- },
- {
- size: 29,
- token: 'var(--fs-text-size-7)',
- },
- {
- size: 33,
- token: 'var(--fs-text-size-8)',
- },
+ { size: 18, token: 'var(--fs-text-size-3)' },
+ { size: 20, token: 'var(--fs-text-size-4)' },
+ { size: 22, token: 'var(--fs-text-size-5)' },
+ { size: 24, token: 'var(--fs-text-size-6)' },
+ { size: 28, token: 'var(--fs-text-size-7)' },
+ { size: 32, token: 'var(--fs-text-size-8)' },
+ { size: 36, token: 'var(--fs-text-size-9)' },
]
### Mobile Scale
@@ -92,35 +78,39 @@ export const mobileList = [
-
-
+
+
+
@@ -131,8 +121,8 @@ export const mobileList = [
### Sizes
-
-
+
+
tag for your font-family of choice */}
>
);
@@ -202,7 +192,7 @@ export default WebFonts;
// --------------------------------------------------------
// Typography (Branding Core)
// --------------------------------------------------------
- --fs-text-face-body: 'Lato', -apple-system, system-ui, BlinkMacSystemFont, sans-serif;
+ --fs-text-face-body: 'Inter', -apple-system, system-ui, BlinkMacSystemFont, sans-serif;
}
}
```
diff --git a/apps/site/pages/docs/icons.mdx b/apps/site/pages/docs/icons.mdx
index 119fe89457..854ab681d5 100644
--- a/apps/site/pages/docs/icons.mdx
+++ b/apps/site/pages/docs/icons.mdx
@@ -85,14 +85,16 @@ You can find more details about the `Icon` component [here](/components/atoms/ic
### Payment Flags
- } name="Visa" />
+ } name="Amex" />
+ } name="ApplePay" />
+ } name="EloCard" />
} name="Diners" />
+ } name="GooglePay" />
} name="Mastercard" />
- } name="EloCard" />
} name="PayPal" />
+ } name="Pix" />
} name="Stripe" />
- } name="GooglePay" />
- } name="ApplePay" />
+ } name="Visa" />
### Social
diff --git a/apps/site/public/icons.svg b/apps/site/public/icons.svg
index 14ae64a260..50a10851c5 100644
--- a/apps/site/public/icons.svg
+++ b/apps/site/public/icons.svg
@@ -1,65 +1,129 @@
diff --git a/lerna.json b/lerna.json
index 8e466899e9..bb7b77df1c 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "3.0.77",
+ "version": "3.0.88",
"npmClient": "yarn",
"command": {
"publish": {
diff --git a/packages/api/package.json b/packages/api/package.json
index 2e0f0a4d82..00f07030ee 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/api",
- "version": "3.0.76",
+ "version": "3.0.88",
"license": "MIT",
"main": "dist/cjs/src/index.js",
"typings": "dist/esm/src/index.d.ts",
@@ -51,8 +51,8 @@
},
"devDependencies": {
"@envelop/core": "^2.6.0",
- "@faststore/eslint-config": "^3.0.68",
- "@faststore/shared": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
+ "@faststore/shared": "^3.0.88",
"@graphql-codegen/cli": "2.2.0",
"@graphql-codegen/typescript": "2.2.2",
"@parcel/watcher": "^2.4.0",
diff --git a/packages/api/src/platforms/vtex/utils/skuVariants.ts b/packages/api/src/platforms/vtex/utils/skuVariants.ts
index b397198322..c2deb5cdf3 100644
--- a/packages/api/src/platforms/vtex/utils/skuVariants.ts
+++ b/packages/api/src/platforms/vtex/utils/skuVariants.ts
@@ -132,7 +132,13 @@ function sortVariants(variantsByName: SkuVariantsByName) {
const sortedVariants = variantsByName
for (const variantProperty in variantsByName) {
- variantsByName[variantProperty].sort((a, b) => compare(a.value, b.value))
+ const areAllNumbers = variantsByName[variantProperty].every(
+ (option: any) => !Number.isNaN(Number(option.value))
+ )
+
+ // Preserve Admin's variants order for cases variants are strings
+ areAllNumbers &&
+ variantsByName[variantProperty].sort((a, b) => compare(a.value, b.value))
}
return sortedVariants
@@ -182,7 +188,10 @@ export function getFormattedVariations(
previouslySeenPropertyValues.add(nameValueIdentifier)
- const variantImageToUse = findSkuVariantImage(variant.images, dominantVariantName)
+ const variantImageToUse = findSkuVariantImage(
+ variant.images,
+ dominantVariantName
+ )
const formattedVariant = {
src: variantImageToUse.imageUrl,
@@ -209,7 +218,10 @@ export function getFormattedVariations(
previouslySeenPropertyValues.add(nameValueIdentifier)
- const variantImageToUse = findSkuVariantImage(variant.images, variationProperty.name)
+ const variantImageToUse = findSkuVariantImage(
+ variant.images,
+ variationProperty.name
+ )
const formattedVariant = {
src: variantImageToUse.imageUrl,
diff --git a/packages/api/test/vtex.skuVariants.test.ts b/packages/api/test/vtex.skuVariants.test.ts
index 220cdb0666..13614658d4 100644
--- a/packages/api/test/vtex.skuVariants.test.ts
+++ b/packages/api/test/vtex.skuVariants.test.ts
@@ -110,18 +110,18 @@ describe('getFormattedVariations', () => {
it('should return all value options for dominantVariant and only possible combinations for others', () => {
const expected = {
Color: [
- {
- src: 'https://storecomponents.vtexassets.com/arquivos/ids/155559/pink-sku-variation.png?v=637087508159070000',
- alt: 'skuvariation',
- label: 'Color: Green',
- value: 'Green',
- },
{
src: 'https://storecomponents.vtexassets.com/arquivos/ids/155560/pink-cool-sku-variation.png?v=637063239809000000',
alt: 'skuvariation',
label: 'Color: Red',
value: 'Red',
},
+ {
+ src: 'https://storecomponents.vtexassets.com/arquivos/ids/155559/pink-sku-variation.png?v=637087508159070000',
+ alt: 'skuvariation',
+ label: 'Color: Green',
+ value: 'Green',
+ },
{
src: 'https://storecomponents.vtexassets.com/arquivos/ids/155561/white-sku-variation.png?v=637087507771770000',
alt: 'skuvariation',
diff --git a/packages/cli/README.md b/packages/cli/README.md
index 6fa0df56e0..e15ef58818 100644
--- a/packages/cli/README.md
+++ b/packages/cli/README.md
@@ -30,7 +30,7 @@ $ npm install -g @faststore/cli
$ faststore COMMAND
running command...
$ faststore (--version)
-@faststore/cli/3.0.71 linux-x64 node-v18.20.3
+@faststore/cli/3.0.88 linux-x64 node-v18.20.4
$ faststore --help [COMMAND]
USAGE
$ faststore COMMAND
@@ -41,55 +41,68 @@ USAGE
## Commands
-* [`faststore build`](#faststore-build)
-* [`faststore cms-sync`](#faststore-cms-sync)
-* [`faststore dev`](#faststore-dev)
-* [`faststore generate-graphql`](#faststore-generate-graphql)
+* [`faststore build [PATH]`](#faststore-build-path)
+* [`faststore cms-sync [PATH]`](#faststore-cms-sync-path)
+* [`faststore dev [PATH]`](#faststore-dev-path)
+* [`faststore generate-graphql [PATH]`](#faststore-generate-graphql-path)
* [`faststore help [COMMAND]`](#faststore-help-command)
-* [`faststore start`](#faststore-start)
-* [`faststore test`](#faststore-test)
+* [`faststore init [PATH]`](#faststore-init-path)
+* [`faststore start [PATH]`](#faststore-start-path)
+* [`faststore test [PATH]`](#faststore-test-path)
-## `faststore build`
+## `faststore build [PATH]`
```
USAGE
- $ faststore build
+ $ faststore build [PATH]
+
+ARGUMENTS
+ PATH The path where the FastStore being built is. Defaults to cwd.
```
-_See code: [dist/commands/build.ts](https://github.com/vtex/faststore/blob/v3.0.71/dist/commands/build.ts)_
+_See code: [dist/commands/build.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/build.ts)_
-## `faststore cms-sync`
+## `faststore cms-sync [PATH]`
```
USAGE
- $ faststore cms-sync [-d]
+ $ faststore cms-sync [PATH] [-d]
+
+ARGUMENTS
+ PATH The path where the FastStore being synched with the CMS is. Defaults to cwd.
FLAGS
-d, --dry-run
```
-_See code: [dist/commands/cms-sync.ts](https://github.com/vtex/faststore/blob/v3.0.71/dist/commands/cms-sync.ts)_
+_See code: [dist/commands/cms-sync.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/cms-sync.ts)_
-## `faststore dev`
+## `faststore dev [PATH]`
```
USAGE
- $ faststore dev
+ $ faststore dev [PATH]
+
+ARGUMENTS
+ PATH The path where the FastStore being run is. Defaults to cwd.
```
-_See code: [dist/commands/dev.ts](https://github.com/vtex/faststore/blob/v3.0.71/dist/commands/dev.ts)_
+_See code: [dist/commands/dev.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/dev.ts)_
-## `faststore generate-graphql`
+## `faststore generate-graphql [PATH]`
```
USAGE
- $ faststore generate-graphql [-d]
+ $ faststore generate-graphql [PATH] [-d]
+
+ARGUMENTS
+ PATH The path where the FastStore GraphQL customization is. Defaults to cwd.
FLAGS
-d, --debug
```
-_See code: [dist/commands/generate-graphql.ts](https://github.com/vtex/faststore/blob/v3.0.71/dist/commands/generate-graphql.ts)_
+_See code: [dist/commands/generate-graphql.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/generate-graphql.ts)_
## `faststore help [COMMAND]`
@@ -111,21 +124,47 @@ DESCRIPTION
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v5.1.22/src/commands/help.ts)_
-## `faststore start`
+## `faststore init [PATH]`
+
+Creates a discovery folder based on the starter.store template.
+
+```
+USAGE
+ $ faststore init [PATH]
+
+ARGUMENTS
+ PATH The path where the Discovery folder will be created. Defaults to ./discovery.
+
+DESCRIPTION
+ Creates a discovery folder based on the starter.store template.
+
+EXAMPLES
+ $ yarn faststore init discovery
+```
+
+_See code: [dist/commands/init.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/init.ts)_
+
+## `faststore start [PATH]`
```
USAGE
- $ faststore start
+ $ faststore start [PATH]
+
+ARGUMENTS
+ PATH The path where the FastStore being run is. Defaults to cwd.
```
-_See code: [dist/commands/start.ts](https://github.com/vtex/faststore/blob/v3.0.71/dist/commands/start.ts)_
+_See code: [dist/commands/start.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/start.ts)_
-## `faststore test`
+## `faststore test [PATH]`
```
USAGE
- $ faststore test
+ $ faststore test [PATH]
+
+ARGUMENTS
+ PATH The path where the FastStore being tested is. Defaults to cwd.
```
-_See code: [dist/commands/test.ts](https://github.com/vtex/faststore/blob/v3.0.71/dist/commands/test.ts)_
+_See code: [dist/commands/test.ts](https://github.com/vtex/faststore/blob/v3.0.88/dist/commands/test.ts)_
diff --git a/packages/cli/package.json b/packages/cli/package.json
index de8c310ebf..c33cc03c93 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/cli",
- "version": "3.0.71",
+ "version": "3.0.88",
"description": "FastStore CLI",
"author": "Emerson Laurentino @emersonlaurentino",
"bin": {
@@ -17,19 +17,24 @@
"/oclif.manifest.json"
],
"dependencies": {
+ "@antfu/ni": "^0.21.12",
+ "@faststore/core": "^3.0.88",
+ "@inquirer/prompts": "^5.1.2",
"@oclif/core": "^1.16.4",
"@oclif/plugin-help": "^5",
"@oclif/plugin-not-found": "^2.3.3",
"chalk": "~4.1.2",
"chokidar": "^3.5.3",
+ "degit": "^2.8.4",
"dotenv": "^16.4.5",
"fs-extra": "^10.1.0",
"path": "^0.12.7"
},
"devDependencies": {
- "@faststore/eslint-config": "^3.0.68",
- "@faststore/shared": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
+ "@faststore/shared": "^3.0.88",
"@types/chai": "^4",
+ "@types/degit": "^2.8.6",
"@types/fs-extra": "^9.0.13",
"@types/node": "^16.11.63",
"chai": "^4",
@@ -60,8 +65,8 @@
"lint": "eslint src/**/*.ts",
"test": "jest src/**/*.test.ts",
"postpack": "shx rm -f oclif.manifest.json",
- "posttest": "yarn lint",
- "prepack": "yarn build && oclif manifest && oclif readme",
+ "posttest": "na run lint",
+ "prepack": "na run && oclif manifest && oclif readme",
"version": "oclif readme && git add README.md"
},
"bugs": "https://github.com/vtex/faststore/issues",
diff --git a/packages/cli/src/__mocks__/monorepo/discovery/.gitkeep b/packages/cli/src/__mocks__/monorepo/discovery/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/cli/src/__mocks__/monorepo/node_modules/@faststore/core/.gitkeep b/packages/cli/src/__mocks__/monorepo/node_modules/@faststore/core/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/cli/src/__mocks__/store/node_modules/@faststore/core/.gitkeep b/packages/cli/src/__mocks__/store/node_modules/@faststore/core/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts
index d3500bf934..519a9124ba 100644
--- a/packages/cli/src/commands/build.ts
+++ b/packages/cli/src/commands/build.ts
@@ -3,25 +3,42 @@ import chalk from 'chalk'
import { spawnSync } from 'child_process'
import { existsSync } from 'fs'
import { copySync, moveSync, readdirSync, removeSync } from 'fs-extra'
-import { tmpDir, userDir } from '../utils/directory'
+import { withBasePath } from '../utils/directory'
import { generate } from '../utils/generate'
+import { getPreferredPackageManager } from '../utils/commands'
export default class Build extends Command {
+
+ static args = [
+ {
+ name: 'path',
+ description: 'The path where the FastStore being built is. Defaults to cwd.',
+ }
+ ]
+
async run() {
- await generate({ setup: true })
+ const { args } = await this.parse(Build)
+
+ const basePath = args.path ?? process.cwd()
+
+ const { tmpDir } = withBasePath(basePath)
- const yarnBuildResult = spawnSync(`yarn build`, {
+ await generate({ setup: true, basePath })
+
+ const packageManager = getPreferredPackageManager()
+
+ const buildResult = spawnSync(`${packageManager} run build`, {
shell: true,
cwd: tmpDir,
stdio: 'inherit',
})
- if (yarnBuildResult.status && yarnBuildResult.status !== 0) {
- process.exit(yarnBuildResult.status)
+ if (buildResult.status && buildResult.status !== 0) {
+ process.exit(buildResult.status)
}
- await normalizeStandaloneBuildDir()
- await copyResources()
+ await normalizeStandaloneBuildDir(basePath)
+ await copyResources(basePath)
}
}
@@ -42,7 +59,9 @@ async function copyResource(from: string, to: string) {
}
}
-async function normalizeStandaloneBuildDir() {
+async function normalizeStandaloneBuildDir(basePath: string) {
+ const { tmpDir } = withBasePath(basePath)
+
// Fix Next.js v13+ standalone build output directory
if (existsSync(`${tmpDir}/.next/standalone/.faststore`)) {
const standaloneBuildFiles = readdirSync(
@@ -62,7 +81,9 @@ async function normalizeStandaloneBuildDir() {
}
}
-async function copyResources() {
+async function copyResources(basePath: string) {
+ const { tmpDir, userDir } = withBasePath(basePath)
+
await copyResource(`${tmpDir}/.next`, `${userDir}/.next`)
await copyResource(`${tmpDir}/lighthouserc.js`, `${userDir}/lighthouserc.js`)
await copyResource(`${tmpDir}/public`, `${userDir}/public`)
diff --git a/packages/cli/src/commands/cms-sync.ts b/packages/cli/src/commands/cms-sync.ts
index 8e8c244aad..648a0ac166 100644
--- a/packages/cli/src/commands/cms-sync.ts
+++ b/packages/cli/src/commands/cms-sync.ts
@@ -1,6 +1,6 @@
import { Command, Flags } from '@oclif/core'
import { spawn } from 'child_process'
-import { tmpDir } from '../utils/directory'
+import { withBasePath } from '../utils/directory'
import { generate } from '../utils/generate'
import { mergeCMSFiles } from '../utils/hcms'
@@ -9,11 +9,22 @@ export default class CmsSync extends Command {
['dry-run']: Flags.boolean({ char: 'd' }),
}
+ static args = [
+ {
+ name: 'path',
+ description: 'The path where the FastStore being synched with the CMS is. Defaults to cwd.',
+ }
+ ]
+
+
async run() {
- const { flags } = await this.parse(CmsSync)
+ const { flags, args } = await this.parse(CmsSync)
+
+ const basePath = args.path ?? process.cwd()
+ const { tmpDir } = withBasePath(basePath)
- await generate({ setup: true })
- await mergeCMSFiles()
+ await generate({ setup: true, basePath })
+ await mergeCMSFiles(basePath)
if (flags['dry-run']) {
return
diff --git a/packages/cli/src/commands/dev.ts b/packages/cli/src/commands/dev.ts
index a68bf39b0b..0ad36dd264 100644
--- a/packages/cli/src/commands/dev.ts
+++ b/packages/cli/src/commands/dev.ts
@@ -1,12 +1,16 @@
import { Command } from '@oclif/core';
import { spawn } from 'child_process';
+import chalk from 'chalk';
import chokidar from 'chokidar';
import dotenv from 'dotenv';
-import { readFileSync } from 'fs';
+import { readFileSync, cpSync } from 'fs';
import path from 'path';
-import { getRoot, tmpDir } from '../utils/directory';
+import { withBasePath } from '../utils/directory';
import { generate } from '../utils/generate';
+import { getPreferredPackageManager } from '../utils/commands';
+import { runCommandSync } from '../utils/runCommandSync';
+
/**
* Taken from toolbelt
@@ -31,10 +35,28 @@ const defaultIgnored = [
const devAbortController = new AbortController()
-async function storeDev() {
- const envVars = dotenv.parse(readFileSync(path.join(getRoot(), 'vtex.env')))
+async function storeDev(rootDir: string, tmpDir: string, coreDir: string) {
+ const envVars = dotenv.parse(readFileSync(path.join(rootDir, 'vtex.env')))
+
+ const packageManager = getPreferredPackageManager()
+
+ runCommandSync({
+ cmd: `${packageManager} predev`,
+ errorMessage:
+ 'GraphQL was not optimized and TS files were not updated. Changes in the GraphQL layer did not take effect',
+ throws: 'error',
+ debug: true,
+ cwd: tmpDir,
+ })
- const devProcess = spawn('yarn dev', {
+ const { success } = copyGenerated(path.join(tmpDir, '@generated'), path.join(coreDir, '@generated'))
+
+ if (!success) {
+ console.log(`${chalk.yellow('warn')} - Failed to copy @generated schema back to node_modules, autocomplete and DX might be impacted.`)
+ console.log(`Attempted to copy from ${path.join(tmpDir, '@generated')} to ${path.join(coreDir, '@generated')}`)
+ }
+
+ const devProcess = spawn(`${packageManager} dev-only`, {
shell: true,
cwd: tmpDir,
signal: devAbortController.signal,
@@ -50,12 +72,32 @@ async function storeDev() {
})
}
+function copyGenerated(from: string, to: string) {
+ try {
+ cpSync(from, to, { recursive: true, force: true })
+
+ return { success: true }
+ } catch (err) {
+ return { success: false }
+ }
+}
+
export default class Dev extends Command {
+ static args = [
+ {
+ name: 'path',
+ description: 'The path where the FastStore being run is. Defaults to cwd.',
+ }
+ ]
+
async run() {
- const queueChange = (/* path: string, remove: boolean */) => {
- // getContentFromPath(path, remove)
+ const { args } = await this.parse(Dev)
+ const basePath = args.path ?? process.cwd()
- generate()
+ const { getRoot, tmpDir, coreDir } = withBasePath(basePath)
+
+ const queueChange = (/* path: string, remove: boolean */) => {
+ generate({ basePath })
}
const watcher = chokidar.watch([...defaultPatterns], {
@@ -74,9 +116,9 @@ export default class Dev extends Command {
watcher.close()
})
- await generate({ setup: true })
+ await generate({ setup: true, basePath })
- storeDev()
+ storeDev(getRoot(), tmpDir, coreDir)
return await new Promise((resolve, reject) => {
watcher
diff --git a/packages/cli/src/commands/generate-graphql.ts b/packages/cli/src/commands/generate-graphql.ts
index 4bc0dc1f66..8a5257bc80 100644
--- a/packages/cli/src/commands/generate-graphql.ts
+++ b/packages/cli/src/commands/generate-graphql.ts
@@ -1,9 +1,10 @@
+import chalk from 'chalk'
import { Command, Flags } from '@oclif/core'
import { existsSync } from 'fs-extra'
-import chalk from 'chalk'
-import { coreDir, tmpDir } from '../utils/directory'
+import { withBasePath } from '../utils/directory'
import { runCommandSync } from '../utils/runCommandSync'
+import { getPreferredPackageManager } from '../utils/commands'
export default class GenerateGraphql extends Command {
static flags = {
@@ -11,12 +12,24 @@ export default class GenerateGraphql extends Command {
core: Flags.boolean({ char: 'c', hidden: true }),
}
+ static args = [
+ {
+ name: 'path',
+ description: 'The path where the FastStore GraphQL customization is. Defaults to cwd.',
+ }
+ ]
+
async run() {
- const { flags } = await this.parse(GenerateGraphql)
+ const { flags, args } = await this.parse(GenerateGraphql)
+
+ const basePath = args.path ?? process.cwd()
+ const { tmpDir, coreDir } = withBasePath(basePath)
const debug = flags.debug ?? false
const isCore = flags.core ?? false
+ const packageManager = getPreferredPackageManager()
+
if (!isCore && !existsSync(tmpDir)) {
console.log(
`${chalk.red(
@@ -28,16 +41,15 @@ export default class GenerateGraphql extends Command {
}
runCommandSync({
- cmd: 'yarn generate:schema',
- errorMessage:
- "Failed to run 'yarn generate:schema'. Please check your setup.",
+ cmd: `${packageManager} run generate:schema`,
+ errorMessage: `Failed to run '${packageManager} generate:schema'. Please check your setup.`,
throws: 'error',
debug,
cwd: isCore ? undefined : tmpDir,
})
runCommandSync({
- cmd: 'yarn generate:codegen',
+ cmd: `${packageManager} run generate:codegen`,
errorMessage:
'GraphQL was not optimized and TS files were not updated. Changes in the GraphQL layer did not take effect',
throws: 'error',
@@ -46,19 +58,19 @@ export default class GenerateGraphql extends Command {
})
runCommandSync({
- cmd: 'yarn format:generated',
+ cmd: `${packageManager} run format:generated`,
errorMessage:
- "Failed to format generated files. 'yarn format:generated' thrown errors",
+ `Failed to format generated files. '${packageManager} format:generated' thrown errors`,
throws: 'warning',
debug,
cwd: isCore ? undefined : tmpDir,
})
- // yarn generate:copy-back expects the DESTINATION var to be present so it can copy the files to the correct directory
+ // The command generate:copy-back expects the DESTINATION var to be present so it can copy the files to the correct directory
runCommandSync({
- cmd: `DESTINATION=${coreDir} yarn generate:copy-back`,
+ cmd: `DESTINATION=${coreDir} ${packageManager} run generate:copy-back`,
errorMessage:
- "Failed to copy back typings files. 'yarn generate:copy-back' thrown errors",
+ `Failed to copy back typings files. '${packageManager} generate:copy-back' thrown errors`,
throws: 'warning',
debug,
cwd: isCore ? undefined : tmpDir,
diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts
new file mode 100644
index 0000000000..7082c00087
--- /dev/null
+++ b/packages/cli/src/commands/init.ts
@@ -0,0 +1,47 @@
+import fs from 'node:fs'
+import degit from 'degit'
+import { Command } from '@oclif/core'
+import { confirm } from '@inquirer/prompts'
+
+export default class Init extends Command {
+ static args = [
+ {
+ name: 'path',
+ description:
+ 'The path where the Discovery folder will be created. Defaults to ./discovery.',
+ },
+ ]
+
+ static description =
+ 'Creates a discovery folder based on the starter.store template.'
+
+ static examples = ['$ yarn faststore init discovery']
+
+ async run() {
+ const { args } = await this.parse(Init)
+
+ const discoveryPath = args.path ?? './discovery'
+ const discoveryFolderExists = fs.existsSync(discoveryPath)
+
+ if (discoveryFolderExists) {
+ const confirmOverride = await confirm({
+ message: `It looks like you already have a discovery folder named "${discoveryPath}" in your store. Do you want to override it?`,
+ })
+
+ if (!confirmOverride)
+ return this.log('🛑 Interrupted initializing discovery')
+ }
+
+ const discoveryEmitter = degit('vtex-sites/starter.store', {
+ force: true,
+ })
+
+ this.log('Pulling starter.store template...')
+
+ discoveryEmitter.clone(discoveryPath).then(() => {
+ this.log(
+ `Discovery created successfully! You can find it at ${discoveryPath}`
+ )
+ })
+ }
+}
diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts
index 8b8d941896..beb85f1d78 100644
--- a/packages/cli/src/commands/start.ts
+++ b/packages/cli/src/commands/start.ts
@@ -1,17 +1,30 @@
import { Command } from '@oclif/core'
import { spawn } from 'child_process'
import { existsSync } from 'fs-extra'
-import { tmpDir } from '../utils/directory'
+import { withBasePath } from '../utils/directory'
+import { getPreferredPackageManager } from '../utils/commands'
export default class Start extends Command {
+ static args = [
+ {
+ name: 'path',
+ description: 'The path where the FastStore being run is. Defaults to cwd.',
+ }
+ ]
+
async run() {
+ const { args } = await this.parse(Start)
+ const basePath = args.path ?? process.cwd()
+ const { tmpDir } = withBasePath(basePath)
+ const packageManager = getPreferredPackageManager()
+
if (!existsSync(tmpDir)) {
throw Error(
'The ".faststore" directory could not be found. If you are trying to serve your store, run "faststore build" first.'
)
}
- return spawn(`yarn serve`, {
+ return spawn(`${packageManager} run serve`, {
shell: true,
cwd: tmpDir,
stdio: 'inherit',
diff --git a/packages/cli/src/commands/test.ts b/packages/cli/src/commands/test.ts
index defe3607b5..b0e148c2a2 100644
--- a/packages/cli/src/commands/test.ts
+++ b/packages/cli/src/commands/test.ts
@@ -3,7 +3,8 @@ import { spawn } from 'child_process'
import chokidar from 'chokidar'
import { generate } from '../utils/generate'
-import { getRoot, tmpDir } from '../utils/directory'
+import { withBasePath } from '../utils/directory'
+import { getPreferredPackageManager } from '../utils/commands'
/**
* Taken from toolbelt
@@ -28,8 +29,10 @@ const defaultIgnored = [
const testAbortController = new AbortController()
-async function storeTest() {
- const testProcess = spawn('yarn test:e2e', {
+async function storeTest(tmpDir: string) {
+ const packageManager = getPreferredPackageManager()
+
+ const testProcess = spawn(`${packageManager} run test:e2e`, {
shell: true,
cwd: tmpDir,
signal: testAbortController.signal,
@@ -42,7 +45,18 @@ async function storeTest() {
}
export default class Test extends Command {
+ static args = [
+ {
+ name: 'path',
+ description: 'The path where the FastStore being tested is. Defaults to cwd.',
+ }
+ ]
+
async run() {
+ const { args } = await this.parse(Test)
+ const basePath = args.path ?? process.cwd()
+ const { getRoot, tmpDir } = withBasePath(basePath)
+
const watcher = chokidar.watch([...defaultPatterns], {
atomic: stabilityThreshold,
awaitWriteFinish: {
@@ -59,9 +73,9 @@ export default class Test extends Command {
watcher.close()
})
- await generate({ setup: true })
+ await generate({ setup: true, basePath })
- storeTest()
+ storeTest(tmpDir)
return await new Promise((resolve, reject) => {
watcher
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index 0237950aa4..c551ea6e21 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -1 +1,17 @@
export { run } from '@oclif/core'
+
+import { default as Init } from './commands/init'
+import { default as Dev } from './commands/dev'
+import { default as Build } from './commands/build'
+import { default as Serve } from './commands/start'
+import { default as CmsSync } from './commands/cms-sync'
+import { default as Test } from './commands/test'
+
+export const commands = {
+ init: Init,
+ dev: Dev,
+ build: Build,
+ serve: Serve,
+ 'cms-sync': CmsSync,
+ test: Test,
+}
diff --git a/packages/cli/src/utils/commands.ts b/packages/cli/src/utils/commands.ts
new file mode 100644
index 0000000000..478e644dee
--- /dev/null
+++ b/packages/cli/src/utils/commands.ts
@@ -0,0 +1,8 @@
+import { spawnSync } from "node:child_process"
+
+// Retrieves the package manager based on the developer lockfile, using `ni`.
+export function getPreferredPackageManager() {
+ const agent = spawnSync("na", ['\?'], { encoding: 'utf8' }).stdout.trim()
+
+ return agent
+}
diff --git a/packages/cli/src/utils/contentFromPath.ts b/packages/cli/src/utils/contentFromPath.ts
deleted file mode 100644
index f85e35446b..0000000000
--- a/packages/cli/src/utils/contentFromPath.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { resolve as resolvePath, sep } from 'path'
-import { Readable } from 'stream'
-import { readFileSync } from 'fs-extra'
-import { getRoot } from './directory'
-
-export interface ContentFromPath {
- path: string | null
- content: string | Readable | Buffer | NodeJS.ReadableStream
-}
-
-const getContentFromPath = (path: string, remove?: boolean): ContentFromPath => {
- const content = remove
- ? ''
- : readFileSync(resolvePath(getRoot(), path)).toString('base64')
-
- return {
- content,
- path: path.split(sep).join('/'),
- }
-}
-
-export { getContentFromPath }
\ No newline at end of file
diff --git a/packages/cli/src/utils/directory.test.ts b/packages/cli/src/utils/directory.test.ts
new file mode 100644
index 0000000000..718955efdb
--- /dev/null
+++ b/packages/cli/src/utils/directory.test.ts
@@ -0,0 +1,201 @@
+import path from "path"
+import { withBasePath } from "./directory"
+
+const pathsToMatch = (expected: string, desired: string) => {
+ const expectedResolved = path.resolve(expected)
+ const desiredResolved = path.resolve(desired)
+
+ return expectedResolved === desiredResolved
+}
+
+describe('withBasePath as the current dir `.`', () => {
+ const basePath = '.'
+
+ describe('tmpDir', () => {
+ it('is the basePath + .faststore', () => {
+ const { tmpDir: tmpDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpDirWithBase, './.faststore')).toBe(true)
+ })
+ })
+
+ describe('userDir', () => {
+ it("returns the directory of the starter's package.json", () => {
+ const { userSrcDir: userSrcDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userSrcDirWithBase, './src')).toBe(true)
+ })
+ })
+
+ describe('tmpCustomizationsSrcDir', () => {
+ it('returns the directory of customizations directory on the tmp dir', () => {
+ const { tmpCustomizationsSrcDir: tmpCustomizationsSrcDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpCustomizationsSrcDirWithBase, './.faststore/src/customizations/src')).toBe(true)
+ })
+ })
+
+ describe('userThemesFileDir', () => {
+ it("returns the directory of the starter's theme directory", () => {
+ const { userThemesFileDir: userThemesFileDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userThemesFileDirWithBase, './src/themes')).toBe(true)
+ })
+ })
+
+ describe('tmpThemesCustomizationsFile', () => {
+ it('returns the path of the theme file on the .faststore dir', () => {
+ const { tmpThemesCustomizationsFile: tmpThemesCustomizationsFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpThemesCustomizationsFileWithBase, './.faststore/src/customizations/src/themes/index.scss')).toBe(true)
+ })
+ })
+
+ describe('tmpCMSDir', () => {
+ it('returns the path of the CMS dir on the .faststore dir', () => {
+ const { tmpCMSDir: tmpCMSDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpCMSDirWithBase, './.faststore/cms/faststore')).toBe(true)
+ })
+ })
+
+ describe('userCMSDir', () => {
+ it('returns the path of the CMS dir on the user dir', () => {
+ const { userCMSDir: userCMSDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userCMSDirWithBase, './cms/faststore')).toBe(true)
+ })
+ })
+
+ describe('tmpCMSWebhookUrlsFile', () => {
+ it('returns the path of the CMS webhooks file on the .faststore dir', () => {
+ const { tmpCMSWebhookUrlsFile: tmpCMSWebhookUrlsFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpCMSWebhookUrlsFileWithBase, './.faststore/cms-webhook-urls.json')).toBe(true)
+ })
+ })
+
+ describe('userStoreConfigFile', () => {
+ it('returns the path of the user faststore.config file', () => {
+ const { userStoreConfigFile: userStoreConfigFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userStoreConfigFileWithBase, './faststore.config.js')).toBe(true)
+ })
+ })
+
+ describe('tmpStoreConfigFile', () => {
+ it('returns the path of the faststore.config file in the customizations dir', () => {
+ const { tmpStoreConfigFile: tmpStoreConfigFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpStoreConfigFileWithBase, './.faststore/src/customizations/faststore.config.js')).toBe(true)
+ })
+ })
+})
+
+describe('withBasePath as an arbitrary dir', () => {
+ const basePath = path.join(__dirname, '..', '__mocks__', 'store')
+
+ describe('coreDir', () => {
+ it('is the faststoreDir + core', () => {
+ const { coreDir: coreDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(coreDirWithBase, './src/__mocks__/store/node_modules/@faststore/core')).toBe(true)
+ })
+
+ describe('when is in a monorepo', () => {
+ it('can look at its parent until it reaches the monorepo directory', () => {
+ const { coreDir: coreDirWithBase } = withBasePath(path.join(__dirname, '..', '__mocks__', 'monorepo', 'discovery'))
+
+ expect(pathsToMatch(coreDirWithBase, './src/__mocks__/monorepo/node_modules/@faststore/core')).toBe(true)
+ })
+ })
+ })
+
+ describe('tmpDir', () => {
+ it('is the basePath + .faststore', () => {
+ const { tmpDir: tmpDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpDirWithBase, './src/__mocks__/store/.faststore')).toBe(true)
+ })
+ })
+
+ describe('userDir', () => {
+ it("returns the directory of the starter's package.json", () => {
+ const { userSrcDir: userSrcDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userSrcDirWithBase, './src/__mocks__/store/src')).toBe(true)
+ })
+ })
+
+ describe('tmpCustomizationsSrcDir', () => {
+ it('returns the directory of customizations directory on the tmp dir', () => {
+ const { tmpCustomizationsSrcDir: tmpCustomizationsSrcDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpCustomizationsSrcDirWithBase, './src/__mocks__/store/.faststore/src/customizations/src')).toBe(true)
+ })
+ })
+
+ describe('userThemesFileDir', () => {
+ it("returns the directory of the starter's theme directory", () => {
+ const { userThemesFileDir: userThemesFileDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userThemesFileDirWithBase, './src/__mocks__/store/src/themes')).toBe(true)
+ })
+ })
+
+ describe('tmpThemesCustomizationsFile', () => {
+ it('returns the path of the theme file on the .faststore dir', () => {
+ const { tmpThemesCustomizationsFile: tmpThemesCustomizationsFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpThemesCustomizationsFileWithBase, './src/__mocks__/store/.faststore/src/customizations/src/themes/index.scss')).toBe(true)
+ })
+ })
+
+ describe('tmpCMSDir', () => {
+ it('returns the path of the CMS dir on the .faststore dir', () => {
+ const { tmpCMSDir: tmpCMSDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpCMSDirWithBase, './src/__mocks__/store/.faststore/cms/faststore')).toBe(true)
+ })
+ })
+
+ describe('userCMSDir', () => {
+ it('returns the path of the CMS dir on the user dir', () => {
+ const { userCMSDir: userCMSDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userCMSDirWithBase, './src/__mocks__/store/cms/faststore')).toBe(true)
+ })
+ })
+
+ describe('coreCMSDir', () => {
+ it('returns the path of the CMS dir on @faststore/core package', () => {
+ const { coreCMSDir: coreCMSDirWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(coreCMSDirWithBase, './src/__mocks__/store/node_modules/@faststore/core/cms/faststore')).toBe(true)
+ })
+ })
+
+ describe('tmpCMSWebhookUrlsFile', () => {
+ it('returns the path of the CMS webhooks file on the .faststore dir', () => {
+ const { tmpCMSWebhookUrlsFile: tmpCMSWebhookUrlsFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpCMSWebhookUrlsFileWithBase, './src/__mocks__/store/.faststore/cms-webhook-urls.json')).toBe(true)
+ })
+ })
+
+ describe('userStoreConfigFile', () => {
+ it('returns the path of the user faststore.config file', () => {
+ const { userStoreConfigFile: userStoreConfigFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(userStoreConfigFileWithBase, './src/__mocks__/store/faststore.config.js')).toBe(true)
+ })
+ })
+
+ describe('tmpStoreConfigFile', () => {
+ it('returns the path of the faststore.config file in the customizations dir', () => {
+ const { tmpStoreConfigFile: tmpStoreConfigFileWithBase } = withBasePath(basePath)
+
+ expect(pathsToMatch(tmpStoreConfigFileWithBase, './src/__mocks__/store/.faststore/src/customizations/faststore.config.js')).toBe(true)
+ })
+ })
+})
diff --git a/packages/cli/src/utils/directory.ts b/packages/cli/src/utils/directory.ts
index ed251b484d..f331c8ccf1 100644
--- a/packages/cli/src/utils/directory.ts
+++ b/packages/cli/src/utils/directory.ts
@@ -1,74 +1,68 @@
import path from 'path'
+import fs from 'node:fs'
-// build folder
-export const tmpFolderName = '.faststore'
+export const withBasePath = (basepath: string) => {
+ const tmpFolderName = '.faststore'
-// always returns the root of the project, AKA the starter root or @faststore/core dir root when running in the monorepo
-export const getRoot = () => {
- if (process.env.OCLIF_COMPILATION) {
- return ''
- }
-
- if(process.cwd().endsWith(tmpFolderName)) {
- // if the current working directory is the build folder (tmp folder), return the starter root
- // this makes sure the semantics of the starter root are consistent with the directories declared below
- return path.join(process.cwd(), '..')
- }
+ const getRoot = () => {
+ if (process.env.OCLIF_COMPILATION) {
+ return ''
+ }
- return process.cwd()
-}
+ if (basepath.endsWith(tmpFolderName)) {
+ // if the current working directory is the build folder (tmp folder), return the starter root
+ // this makes sure the semantics of the starter root are consistent with the directories declared below
+ return path.join(basepath, '..')
+ }
-// starter root
-export const userDir = getRoot()
+ return basepath
+ }
-// node_modules folder for faststorer packages
-export const faststoreDir = path.join(userDir, 'node_modules', '@faststore')
+ /*
+ * This will loop from the basepath until the process.cwd() looking for node_modules/@faststore/core
+ *
+ * If it reaches process.cwd() (or /, as a safeguard), without finding it, it will throw an exception
+ */
+ const getCorePackagePath = () => {
+ const coreFromNodeModules = path.join('node_modules', '@faststore', 'core')
+ const resolvedCwd = path.resolve(process.cwd())
-// build folder dir
-export const tmpDir = path.join(userDir, tmpFolderName)
+ const parents: string[] = []
-// node_modules folder for @faststore/core
-export const coreFolderName = 'core'
-export const coreDir = path.join(faststoreDir, coreFolderName)
+ let attemptedPath
+ do {
+ attemptedPath = path.join(basepath, ...parents, coreFromNodeModules)
-// starter src/ folder
-export const srcFolderName = 'src'
-export const userSrcDir = path.join(userDir, srcFolderName)
+ if (fs.existsSync(attemptedPath)) {
+ return attemptedPath
+ }
-// build folder's folder to which starter files should always be copied
-export const customizationsFolderName = 'customizations'
-// build folder's root folder for starter files
-export const tmpCustomizationsDir = path.join(tmpDir, 'src', customizationsFolderName)
-// build folder's starter src files
-export const tmpCustomizationsSrcDir = path.join(tmpCustomizationsDir, srcFolderName)
+ parents.push('..')
+ } while (path.resolve(attemptedPath) !== resolvedCwd || path.resolve(attemptedPath) !== '/')
-// starter's folder for themes
-export const userThemesFileDir = path.join(userSrcDir, 'themes')
-// build folder's dir for theme
-export const tmpThemesCustomizationsFileDir = path.join(tmpCustomizationsSrcDir, 'themes', 'index.scss')
+ throw `Could not find @node_modules on ${basepath} or any of its parents until ${attemptedPath}`
+ }
-// path segment of cms files for faststore
-export const cmsFolderName = path.join('cms', 'faststore')
-// build folder's cms folder
-export const tmpCMSDir = path.join(tmpDir, cmsFolderName)
-// node_modules folder for @faststore/core's cms folder
-export const coreCMSDir = path.join(coreDir, cmsFolderName)
-// starter folder's cms folder
-export const userCMSDir = path.join(userDir, cmsFolderName)
+ const tmpDir = path.join(getRoot(), tmpFolderName)
+ const userSrcDir = path.join(getRoot(), 'src')
-// file name for faststore configs
-export const configFileName = 'faststore.config.js'
-// starter's config file dir
-export const userStoreConfigFileDir = path.join(userDir, configFileName)
-// build folder's config file dir
-export const tmpStoreConfigFileDir = path.join(tmpCustomizationsDir, configFileName)
+ return {
+ getRoot,
+ userDir: getRoot(),
+ userSrcDir,
+ userThemesFileDir: path.join(userSrcDir, 'themes'),
+ userCMSDir: path.join(getRoot(), 'cms', 'faststore'),
+ userStoreConfigFile: path.join(getRoot(), 'faststore.config.js'),
-// starter's node_modules
-export const userNodeModulesDir = path.join(userDir, 'node_modules')
-// build folder's node_modules
-export const tmpNodeModulesDir = path.join(tmpDir, 'node_modules')
+ tmpFolderName,
+ tmpDir,
+ tmpCustomizationsSrcDir: path.join(tmpDir, 'src', 'customizations', 'src'),
+ tmpThemesCustomizationsFile: path.join(tmpDir, 'src', 'customizations', 'src', 'themes', 'index.scss'),
+ tmpCMSDir: path.join(tmpDir, 'cms', 'faststore'),
+ tmpCMSWebhookUrlsFile: path.join(tmpDir, 'cms-webhook-urls.json'),
+ tmpStoreConfigFile: path.join(tmpDir, 'src', 'customizations', 'faststore.config.js'),
-// cms webhook config file name
-export const cmsWebhookUrlsFileName = 'cms-webhook-urls.json'
-// build folder's dir for webhook config
-export const tmpCmsWebhookUrlsFileDir = path.join(tmpDir, cmsWebhookUrlsFileName)
+ coreDir: getCorePackagePath(),
+ coreCMSDir: path.join(getCorePackagePath(), 'cms', 'faststore'),
+ }
+}
diff --git a/packages/cli/src/utils/generate.ts b/packages/cli/src/utils/generate.ts
index fe6bf9682a..a10075ac81 100644
--- a/packages/cli/src/utils/generate.ts
+++ b/packages/cli/src/utils/generate.ts
@@ -11,28 +11,19 @@ import {
} from 'fs-extra'
import path from 'path'
-import {
- coreDir,
- tmpCmsWebhookUrlsFileDir,
- tmpCustomizationsSrcDir,
- tmpDir,
- tmpFolderName,
- tmpStoreConfigFileDir,
- tmpThemesCustomizationsFileDir,
- userDir,
- userSrcDir,
- userStoreConfigFileDir,
- userThemesFileDir,
-} from './directory'
+import { withBasePath } from './directory'
interface GenerateOptions {
setup?: boolean
+ basePath: string
}
// package.json is copied manually after filtering its content
const ignorePaths = ['package.json', 'node_modules', 'cypress.config.ts']
-function createTmpFolder() {
+function createTmpFolder(basePath: string) {
+ const { tmpDir, tmpFolderName } = withBasePath(basePath)
+
try {
if (existsSync(tmpDir)) {
removeSync(tmpDir)
@@ -54,7 +45,9 @@ function createTmpFolder() {
* where sometimes the package.json from the .faststore folder
* took precedence over @faststore/core's package.json.
*/
-function filterAndCopyPackageJson() {
+function filterAndCopyPackageJson(basePath: string) {
+ const { coreDir, tmpDir } = withBasePath(basePath)
+
const corePackageJsonPath = path.join(coreDir, 'package.json')
const corePackageJsonFile = readFileSync(corePackageJsonPath, 'utf8')
@@ -67,7 +60,9 @@ function filterAndCopyPackageJson() {
})
}
-function copyCoreFiles() {
+function copyCoreFiles(basePath: string) {
+ const { coreDir, tmpDir } = withBasePath(basePath)
+
try {
copySync(coreDir, tmpDir, {
filter(src) {
@@ -80,7 +75,7 @@ function copyCoreFiles() {
},
})
- filterAndCopyPackageJson()
+ filterAndCopyPackageJson(basePath)
console.log(`${chalk.green('success')} - Core files copied`)
} catch (e) {
@@ -88,7 +83,9 @@ function copyCoreFiles() {
}
}
-function copyPublicFiles() {
+function copyPublicFiles(basePath: string) {
+ const { userDir, tmpDir } = withBasePath(basePath)
+
const allowList = ['json', 'txt', 'xml', 'ico', 'public']
try {
if (existsSync(`${userDir}/public`)) {
@@ -107,7 +104,9 @@ function copyPublicFiles() {
}
}
-async function copyCypressFiles() {
+async function copyCypressFiles(basePath: string) {
+ const { userDir, userStoreConfigFile, tmpDir } = withBasePath(basePath)
+
try {
// Cypress 9.x config file
if (existsSync(`${userDir}/cypress.json`)) {
@@ -119,7 +118,7 @@ async function copyCypressFiles() {
copySync(`${userDir}/cypress.config.ts`, `${tmpDir}/cypress.config.ts`)
}
- const userStoreConfig = await import(userStoreConfigFileDir)
+ const userStoreConfig = await import(path.resolve(userStoreConfigFile))
// Copy custom Cypress folder and files
if (
@@ -146,14 +145,16 @@ async function copyCypressFiles() {
}
}
-function copyUserStarterToCustomizations() {
+function copyUserStarterToCustomizations(basePath: string) {
+ const { userSrcDir, tmpCustomizationsSrcDir, userStoreConfigFile, tmpStoreConfigFile } = withBasePath(basePath)
+
try {
if (existsSync(userSrcDir) && readdirSync(userSrcDir).length > 0) {
copySync(userSrcDir, tmpCustomizationsSrcDir)
}
- if (existsSync(userStoreConfigFileDir)) {
- copySync(userStoreConfigFileDir, tmpStoreConfigFileDir)
+ if (existsSync(userStoreConfigFile)) {
+ copySync(userStoreConfigFile, tmpStoreConfigFile)
}
console.log(`${chalk.green('success')} - Starter files copied`)
@@ -162,8 +163,9 @@ function copyUserStarterToCustomizations() {
}
}
-async function createCmsWebhookUrlsJsonFile() {
- const userStoreConfig = await import(userStoreConfigFileDir)
+async function createCmsWebhookUrlsJsonFile(basePath: string) {
+ const { userStoreConfigFile, tmpCMSWebhookUrlsFile } = withBasePath(basePath)
+ const userStoreConfig = await import(path.resolve(userStoreConfigFile))
if (
userStoreConfig?.vtexHeadlessCms &&
@@ -173,7 +175,7 @@ async function createCmsWebhookUrlsJsonFile() {
try {
writeJsonSync(
- tmpCmsWebhookUrlsFileDir,
+ tmpCMSWebhookUrlsFile,
{ urls: webhookUrls },
{ spaces: 2 }
)
@@ -186,8 +188,9 @@ async function createCmsWebhookUrlsJsonFile() {
}
}
-async function copyTheme() {
- const storeConfig = await import(userStoreConfigFileDir)
+async function copyTheme(basePath: string) {
+ const { userStoreConfigFile, userThemesFileDir, tmpThemesCustomizationsFile } = withBasePath(basePath)
+ const storeConfig = await import(path.resolve(userStoreConfigFile))
if (storeConfig.theme) {
const customTheme = path.join(
userThemesFileDir,
@@ -195,10 +198,9 @@ async function copyTheme() {
)
if (existsSync(customTheme)) {
try {
- copyFileSync(customTheme, tmpThemesCustomizationsFileDir)
+ copyFileSync(customTheme, tmpThemesCustomizationsFile)
console.log(
- `${chalk.green('success')} - ${
- storeConfig.theme
+ `${chalk.green('success')} - ${storeConfig.theme
} theme has been applied`
)
} catch (err) {
@@ -206,10 +208,8 @@ async function copyTheme() {
}
} else {
console.info(
- `${chalk.blue('info')} - The ${
- storeConfig.theme
- } theme was added to the config file but the ${
- storeConfig.theme
+ `${chalk.blue('info')} - The ${storeConfig.theme
+ } theme was added to the config file but the ${storeConfig.theme
}.scss file does not exist in the themes folder. Read more: https://www.faststore.dev/docs/themes/overview`
)
}
@@ -225,24 +225,24 @@ async function copyTheme() {
}
}
-export async function generate(options?: GenerateOptions) {
- const { setup = false } = options ?? {}
+export async function generate(options: GenerateOptions) {
+ const { basePath, setup = false } = options
let setupPromise: Promise | null = null
if (setup) {
setupPromise = Promise.all([
- createTmpFolder(),
- copyCoreFiles(),
- copyCypressFiles(),
- copyPublicFiles(),
+ createTmpFolder(basePath),
+ copyCoreFiles(basePath),
+ copyCypressFiles(basePath),
+ copyPublicFiles(basePath),
])
}
await Promise.all([
setupPromise,
- copyUserStarterToCustomizations(),
- copyTheme(),
- createCmsWebhookUrlsJsonFile(),
+ copyUserStarterToCustomizations(basePath),
+ copyTheme(basePath),
+ createCmsWebhookUrlsJsonFile(basePath),
])
}
diff --git a/packages/cli/src/utils/hcms.test.ts b/packages/cli/src/utils/hcms.test.ts
index 64e18a03c5..b3e9aaf0ad 100644
--- a/packages/cli/src/utils/hcms.test.ts
+++ b/packages/cli/src/utils/hcms.test.ts
@@ -15,7 +15,7 @@ import {
splitCustomDefinitions,
mergeCMSFile,
} from './hcms'
-import { tmpCMSDir } from './directory'
+import { withBasePath } from './directory'
jest.mock('fs-extra', () => ({
readFileSync: jest.fn(),
@@ -26,11 +26,12 @@ jest.mock('fs-extra', () => ({
describe('mergeCMSFile', () => {
it("should create a resulting file that contains all core definitions if a custom definitions file doesn't exist", async () => {
const { readFileSync, existsSync, writeFileSync } = require('fs-extra')
+ const { tmpCMSDir } = withBasePath('.')
existsSync.mockReturnValueOnce(false)
readFileSync.mockReturnValueOnce(JSON.stringify(coreContentTypes))
- await mergeCMSFile('content-types.json')
+ await mergeCMSFile('content-types.json', '.')
expect(writeFileSync).toHaveBeenCalledWith(
path.join(tmpCMSDir, 'content-types.json'),
@@ -40,7 +41,7 @@ describe('mergeCMSFile', () => {
existsSync.mockReturnValueOnce(false)
readFileSync.mockReturnValueOnce(JSON.stringify(coreSections))
- await mergeCMSFile('sections.json')
+ await mergeCMSFile('sections.json', '.')
expect(writeFileSync).toHaveBeenCalledWith(
path.join(tmpCMSDir, 'sections.json'),
diff --git a/packages/cli/src/utils/hcms.ts b/packages/cli/src/utils/hcms.ts
index 8777c11ba8..469c8aef5d 100644
--- a/packages/cli/src/utils/hcms.ts
+++ b/packages/cli/src/utils/hcms.ts
@@ -3,7 +3,7 @@ import chalk from 'chalk'
import { CliUx } from '@oclif/core'
import { readFileSync, existsSync, writeFileSync } from 'fs-extra'
-import { userCMSDir, coreCMSDir, tmpCMSDir } from './directory'
+import { withBasePath } from './directory'
export interface ContentTypeOrSectionDefinition {
id?: string
@@ -96,8 +96,7 @@ async function confirmUserChoice(
fileName: string
) {
const goAhead = await CliUx.ux.confirm(
- `You are about to override default ${
- fileName.split('.')[0]
+ `You are about to override default ${fileName.split('.')[0]
}:\n\n${duplicates
.map((definition) => definition.id || definition.name)
.join('\n')}\n\nAre you sure? [yes/no]`
@@ -110,7 +109,9 @@ async function confirmUserChoice(
return
}
-export async function mergeCMSFile(fileName: string) {
+export async function mergeCMSFile(fileName: string, basePath: string) {
+ const { coreCMSDir, userCMSDir, tmpCMSDir } = withBasePath(basePath)
+
const coreFilePath = path.join(coreCMSDir, fileName)
const customFilePath = path.join(userCMSDir, fileName)
@@ -172,10 +173,10 @@ export async function mergeCMSFile(fileName: string) {
}
}
-export async function mergeCMSFiles() {
+export async function mergeCMSFiles(basePath: string) {
try {
- await mergeCMSFile('content-types.json')
- await mergeCMSFile('sections.json')
+ await mergeCMSFile('content-types.json', basePath)
+ await mergeCMSFile('sections.json', basePath)
} catch (err) {
console.error(`${chalk.red('error')} - ${err}`)
}
diff --git a/packages/components/package.json b/packages/components/package.json
index f17df36344..d3751b9316 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/components",
- "version": "3.0.77",
+ "version": "3.0.88",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"typings": "dist/esm/index.d.ts",
@@ -35,8 +35,8 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
- "@faststore/eslint-config": "^3.0.68",
- "@faststore/shared": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
+ "@faststore/shared": "^3.0.88",
"@testing-library/react": "^14.3.0",
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
diff --git a/packages/components/src/molecules/OrderSummary/OrderSummary.tsx b/packages/components/src/molecules/OrderSummary/OrderSummary.tsx
index 872471b9ad..cf72d86b83 100644
--- a/packages/components/src/molecules/OrderSummary/OrderSummary.tsx
+++ b/packages/components/src/molecules/OrderSummary/OrderSummary.tsx
@@ -5,24 +5,24 @@ import { Label, List } from '../../'
export interface OrderSummaryProps extends HTMLAttributes {
/**
- * ID to find this component in testing tools (e.g.: cypress,
+ * ID to find this component in testing tools (e.g., cypress,
* testing-library, and jest).
*/
testId?: string
/**
- * Label for the subtotal value of the order. Will only show if subtotalValue is provided.
+ * Label for the subtotal value of the order. It will only show if subtotalValue is provided.
*/
subtotalLabel?: string
/**
- * Subtotal value of the order. If provided, subtotal label and value will be shown.
+ * Subtotal value of the order. If provided, a subtotal label and value will be shown.
*/
subtotalValue?: string
/**
- * Label for the discount value for the order. Will only show if discountValue is provided.
+ * Label for the discount value for the order. It will only show if discountValue is provided.
*/
discountLabel?: string
/**
- * Discount value for the order. If provided, discount label and value will be shown.
+ * Discount value for the order. If provided, a discount label and value will be shown.
*/
discountValue?: string
/**
@@ -34,11 +34,11 @@ export interface OrderSummaryProps extends HTMLAttributes {
*/
totalValue?: string
/**
- * Enables to include taxes status.
+ * Specifies whether the displayed price should include taxes.
*/
includeTaxes?: boolean
/**
- * Label to determine if the price is with taxes included.
+ * Label to determine if the price includes taxes.
*/
includeTaxesLabel?: string
}
diff --git a/packages/components/src/molecules/ProductCard/ProductCardContent.tsx b/packages/components/src/molecules/ProductCard/ProductCardContent.tsx
index 9e9869f19a..5cffe72bf0 100644
--- a/packages/components/src/molecules/ProductCard/ProductCardContent.tsx
+++ b/packages/components/src/molecules/ProductCard/ProductCardContent.tsx
@@ -26,7 +26,7 @@ export interface ProductCardContentProps extends HTMLAttributes {
*/
title: string
/**
- * Props for the link from ProductCard component.
+ * Props for the link from the ProductCard component.
*/
linkProps?: Partial>
/**
@@ -34,7 +34,7 @@ export interface ProductCardContentProps extends HTMLAttributes {
*/
price?: PriceDefinition
/**
- * Enables a outOfStock status.
+ * Enables an outOfStock status.
*/
outOfStock?: boolean
/**
@@ -42,7 +42,7 @@ export interface ProductCardContentProps extends HTMLAttributes {
*/
outOfStockLabel?: string
/**
- * Specifies Rating Value of the product.
+ * Specifies the Rating Value of the product.
*/
ratingValue?: number
/**
@@ -54,15 +54,15 @@ export interface ProductCardContentProps extends HTMLAttributes {
*/
showDiscountBadge?: boolean
/**
- * Callback function when button is clicked.
+ * Callback function when the button is clicked.
*/
onButtonClick?: () => void
/**
- * Enables to include taxes status.
+ * Specifies whether the displayed price should include taxes.
*/
includeTaxes?: boolean
/**
- * Label to determine if the price is with taxes included.
+ * Label to determine if the price includes taxes.
*/
includeTaxesLabel?: string
}
diff --git a/packages/components/src/molecules/QuantitySelector/QuantitySelector.tsx b/packages/components/src/molecules/QuantitySelector/QuantitySelector.tsx
index 8ceccf2771..7253d68cfa 100644
--- a/packages/components/src/molecules/QuantitySelector/QuantitySelector.tsx
+++ b/packages/components/src/molecules/QuantitySelector/QuantitySelector.tsx
@@ -22,13 +22,13 @@ export interface QuantitySelectorProps {
*/
initial?: number
/**
- * Controls by how many units the value advances
- */
+ * Controls by how many units the value advances
+ */
unitMultiplier?: number
- /**
- * Controls wheter you use or not the unitMultiplier
- */
- useUnitMultiplier?: boolean
+ /**
+ * Controls wheter you use or not the unitMultiplier
+ */
+ useUnitMultiplier?: boolean
/**
* Specifies that the whole quantity selector component should be disabled.
*/
@@ -37,6 +37,10 @@ export interface QuantitySelectorProps {
* Event emitted when value is changed
*/
onChange?: (value: number) => void
+ /**
+ * Event emitted when value is out of the min and max bounds
+ */
+ onValidateBlur?: (min: number, maxValue: number, quantity: number) => void
}
const QuantitySelector = ({
@@ -47,21 +51,24 @@ const QuantitySelector = ({
initial,
disabled = false,
onChange,
+ onValidateBlur,
testId = 'fs-quantity-selector',
...otherProps
}: QuantitySelectorProps) => {
const [quantity, setQuantity] = useState(initial ?? min)
- const [multipliedUnit, setMultipliedUnit] = useState(quantity * unitMultiplier)
+ const [multipliedUnit, setMultipliedUnit] = useState(
+ quantity * unitMultiplier
+ )
const roundUpQuantityIfNeeded = (quantity: number) => {
- if(!useUnitMultiplier){
+ if (!useUnitMultiplier) {
return quantity
}
- return Math.ceil(quantity / unitMultiplier) * unitMultiplier;
- }
+ return Math.ceil(quantity / unitMultiplier) * unitMultiplier
+ }
const isLeftDisabled = quantity === min
- const isRightDisabled = quantity === max
+ const isRightDisabled = quantity === max
const changeQuantity = (increaseValue: number) => {
const quantityValue = validateQuantityBounds(quantity + increaseValue)
@@ -70,7 +77,7 @@ const QuantitySelector = ({
setQuantity(quantityValue)
setMultipliedUnit(quantityValue * unitMultiplier)
}
-
+
const increase = () => changeQuantity(1)
const decrease = () => changeQuantity(-1)
@@ -78,39 +85,37 @@ const QuantitySelector = ({
function validateQuantityBounds(n: number): number {
const maxValue = min ? Math.max(n, min) : n
- return max ? Math.min(maxValue, useUnitMultiplier ? max * unitMultiplier : max) : maxValue
+ return max
+ ? Math.min(maxValue, useUnitMultiplier ? max * unitMultiplier : max)
+ : maxValue
}
function validateBlur() {
- const roundedQuantity = roundUpQuantityIfNeeded(quantity)
+ const quantityValue = validateQuantityBounds(quantity)
+ const roundedQuantity = roundUpQuantityIfNeeded(quantityValue)
- setQuantity(() => {
- setMultipliedUnit(roundedQuantity)
- onChange?.(roundedQuantity / unitMultiplier)
-
- return roundedQuantity / unitMultiplier
- })
- }
-
- function validateInput(e: React.FormEvent) {
- const val = e.currentTarget.value
+ const maxValue = max ?? (min ? Math.max(quantity, min) : quantity)
+ const isOutOfBounds = quantity > maxValue || quantity < min
+ if (isOutOfBounds) {
+ onValidateBlur?.(min, maxValue, roundedQuantity)
+ }
- if (!Number.isNaN(Number(val))) {
- setQuantity(() => {
- const quantityValue = validateQuantityBounds(Number(val))
- setMultipliedUnit(quantityValue)
- onChange?.(quantityValue)
+ setQuantity(() => {
+ setMultipliedUnit(roundedQuantity)
+ onChange?.(roundedQuantity / unitMultiplier)
- return quantityValue
- })
- }
+ return roundedQuantity / unitMultiplier
+ })
}
-
useEffect(() => {
initial && setQuantity(initial)
}, [initial])
+ const changeInputValue = (e: React.ChangeEvent) => {
+ setQuantity(Number(e.currentTarget.value))
+ }
+
return (
diff --git a/packages/components/src/organisms/PaymentMethods/PaymentMethods.tsx b/packages/components/src/organisms/PaymentMethods/PaymentMethods.tsx
index f7d2ed8510..5f5977b631 100644
--- a/packages/components/src/organisms/PaymentMethods/PaymentMethods.tsx
+++ b/packages/components/src/organisms/PaymentMethods/PaymentMethods.tsx
@@ -60,7 +60,7 @@ const PaymentMethods = forwardRef(
data-fs-payment-methods-flag
key={`fs-payment-method-${index}-${text}`}
>
-
+
{text && }
))}
diff --git a/packages/core/README.md b/packages/core/README.md
index 18680027a0..491cadb3e2 100644
--- a/packages/core/README.md
+++ b/packages/core/README.md
@@ -14,6 +14,8 @@ This starter ships the main FastStore configuration files to get your store up a
1. **Install dependencies**
+> PS: you can install dependencies using the package manager of your choosing. In this guide, we'll be using `yarn` as an example.
+
Install dependencies with yarn
```shell
diff --git a/packages/core/package.json b/packages/core/package.json
index 97c86bda79..4ea517501b 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/core",
- "version": "3.0.77",
+ "version": "3.0.88",
"license": "MIT",
"repository": "vtex/faststore",
"browserslist": "supports es6-module and not dead",
@@ -13,17 +13,19 @@
"scripts": {
"generate:schema": "tsx src/server/generator/generateGraphQLSchemaFile.ts",
"generate:codegen": "graphql-codegen",
- "generate:copy-back": "copyfiles \"@generated/**/*\" $DESTINATION",
- "generate": "faststore generate-graphql -c -d",
- "build": "yarn partytown & yarn generate && next build",
- "dev": "yarn partytown & yarn generate && next dev",
+ "format:generated": "prettier --write \"@generated/**/*.{ts,js,tsx,jsx,json}\" --loglevel error",
+ "generate": "na run generate:schema && na run generate:codegen && na run format:generated",
+ "prebuild": "na run partytown && na run generate",
+ "build": "next build",
+ "predev": "na run partytown && na run generate",
+ "dev": "next dev",
+ "dev-only": "next dev",
"clean": "rm -r .next",
"serve": "next start",
"test:e2e": "cypress open",
"test": "jest",
"lhci": "lhci autorun",
"format": "prettier --write \"**/*.{ts,js,tsx,jsx,json}\"",
- "format:generated": "prettier --write \"@generated/**/*.{ts,js,tsx,jsx,json}\" --loglevel error",
"lint": "next lint",
"stylelint": "stylelint \"**/*.scss\"",
"stylelint:fix": "stylelint \"**/*.scss\" --fix",
@@ -35,16 +37,17 @@
},
"sideEffects": false,
"dependencies": {
+ "@antfu/ni": "^0.21.12",
"@builder.io/partytown": "^0.6.1",
"@envelop/core": "^1.2.0",
"@envelop/graphql-jit": "^1.1.1",
"@envelop/parser-cache": "^2.2.0",
"@envelop/validation-cache": "^2.2.0",
- "@faststore/api": "^3.0.76",
- "@faststore/components": "^3.0.77",
- "@faststore/graphql-utils": "^3.0.68",
- "@faststore/sdk": "^3.0.68",
- "@faststore/ui": "^3.0.77",
+ "@faststore/api": "^3.0.88",
+ "@faststore/components": "^3.0.88",
+ "@faststore/graphql-utils": "^3.0.88",
+ "@faststore/sdk": "^3.0.88",
+ "@faststore/ui": "^3.0.88",
"@graphql-codegen/cli": "^5.0.2",
"@graphql-codegen/client-preset": "^4.2.6",
"@graphql-codegen/typescript": "^4.0.7",
@@ -60,7 +63,6 @@
"@vtex/prettier-config": "1.0.0",
"autoprefixer": "^10.4.0",
"chalk": "^5.2.0",
- "copyfiles": "^2.4.1",
"css-loader": "^6.7.1",
"deepmerge": "^4.3.1",
"draftjs-to-html": "^0.9.1",
@@ -86,8 +88,7 @@
"devDependencies": {
"@cypress/code-coverage": "^3.12.1",
"@envelop/testing": "^6.0.0",
- "@faststore/cli": "^3.0.71",
- "@faststore/eslint-config": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
"@faststore/lighthouse": "^1.12.32",
"@lhci/cli": "^0.9.0",
"@testing-library/cypress": "^10.0.1",
diff --git a/packages/core/postinstall.js b/packages/core/postinstall.js
index 90bd383d32..875d8b3138 100644
--- a/packages/core/postinstall.js
+++ b/packages/core/postinstall.js
@@ -1,6 +1,3 @@
-if (
- process.cwd().includes('node_modules') ||
- process.cwd().includes('.faststore')
-) {
+if (__dirname.includes('node_modules') || __dirname.includes('.faststore')) {
process.exitCode = 1
}
diff --git a/packages/core/public/icons.svg b/packages/core/public/icons.svg
index a05940cd63..f0c616e74d 100644
--- a/packages/core/public/icons.svg
+++ b/packages/core/public/icons.svg
@@ -1,64 +1,128 @@
diff --git a/packages/core/src/components/ui/ProductDetails/ProductDetailsSettings.tsx b/packages/core/src/components/ui/ProductDetails/ProductDetailsSettings.tsx
index ebd7a87e64..97d248ce19 100644
--- a/packages/core/src/components/ui/ProductDetails/ProductDetailsSettings.tsx
+++ b/packages/core/src/components/ui/ProductDetails/ProductDetailsSettings.tsx
@@ -9,6 +9,7 @@ import { useFormattedPrice } from 'src/sdk/product/useFormattedPrice'
import Selectors from 'src/components/ui/SkuSelector'
import AddToCartLoadingSkeleton from './AddToCartLoadingSkeleton'
+import { Icon as UIIcon, useUI } from '@faststore/ui'
import { useOverrideComponents } from 'src/sdk/overrides/OverrideContext'
import { Label as UILabel } from '@faststore/ui'
@@ -49,6 +50,8 @@ function ProductDetailsSettings({
__experimentalNotAvailableButton: NotAvailableButton,
} = useOverrideComponents<'ProductDetails'>()
+ const { pushToast } = useUI()
+
const {
id,
sku,
@@ -157,6 +160,21 @@ function ProductDetailsSettings({
// Dynamic props shouldn't be overridable
// This decision can be reviewed later if needed
onChange={setQuantity}
+ // TODO: we should get the Toast values from the hCMS
+ onValidateBlur={(
+ min: number,
+ maxValue: number,
+ quantity: number
+ ) => {
+ pushToast({
+ title: 'Invalid quantity!',
+ message: `The quantity you entered is outside the range of ${min} to ${maxValue}. The quantity was set to ${quantity}.`,
+ status: 'INFO',
+ icon: (
+
+ ),
+ })
+ }}
/>
)}
diff --git a/packages/core/src/fonts/WebFonts.tsx b/packages/core/src/fonts/WebFonts.tsx
index 4fdff57f81..f4bc0d041c 100644
--- a/packages/core/src/fonts/WebFonts.tsx
+++ b/packages/core/src/fonts/WebFonts.tsx
@@ -6,7 +6,7 @@ function WebFonts() {
{/* Add a tag for your font-family of choice */}
{/* */}
>
)
diff --git a/packages/core/src/sdk/analytics/platform/vtex/search.ts b/packages/core/src/sdk/analytics/platform/vtex/search.ts
index 2c8f6444a1..57ec3e79f3 100644
--- a/packages/core/src/sdk/analytics/platform/vtex/search.ts
+++ b/packages/core/src/sdk/analytics/platform/vtex/search.ts
@@ -1,5 +1,5 @@
/**
- * More info at: https://www.notion.so/vtexhandbook/Event-API-Documentation-48eee26730cf4d7f80f8fd7262231f84
+ * More info at: https://developers.vtex.com/docs/api-reference/intelligent-search-events-api-headless
*/
import type { AnalyticsEvent } from '@faststore/sdk'
import type {
@@ -15,33 +15,35 @@ const ONE_YEAR_S = 365 * 24 * 3600
const randomUUID = () =>
typeof crypto.randomUUID === 'function'
- ? crypto.randomUUID()
+ ? crypto.randomUUID().replaceAll('-', '')
: (Math.random() * 1e6).toFixed(0)
-const createCookie = (key: string, expiresSecond: number) => {
+const createOrRefreshCookie = (key: string, expiresSecond: number) => {
// Setting the domain attribute specifies which host can receive it; we need it to make the cookies available on the `secure` subdomain.
// Although https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie mentioned leading dot (.) is not needed and ignored. I couldn't set the cookies without it.
- const urlDomain = `.${new URL(config.storeUrl).hostname}`
+ const urlDomain =
+ process.env.NODE_ENV === 'development'
+ ? '.localhost'
+ : `.${new URL(config.storeUrl).hostname}`
return () => {
- const isExpired = getCookie(key) === undefined
+ let currentValue = getCookie(key)
+ const isExpired = currentValue === undefined
if (isExpired) {
- const value = randomUUID()
-
- document.cookie = `${key}=${value}; max-age=${expiresSecond}; domain=${urlDomain}; path=/;`
- // Setting the `path=/` makes the cookie accessible on any path of the domain/subdomain
-
- return value
+ currentValue = randomUUID()
}
- return getCookie(key)
+ // Setting the `path=/` makes the cookie accessible on any path of the domain/subdomain
+ document.cookie = `${key}=${currentValue}; max-age=${expiresSecond}; domain=${urlDomain}; path=/;`
+
+ return currentValue
}
}
const user = {
- anonymous: createCookie('vtex-faststore-anonymous', ONE_YEAR_S),
- session: createCookie('vtex-faststore-session', THIRTY_MINUTES_S),
+ anonymous: createOrRefreshCookie('vtex-search-anonymous', ONE_YEAR_S),
+ session: createOrRefreshCookie('vtex-search-session', THIRTY_MINUTES_S),
}
type SearchEvent =
diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json
index cf2580ff97..080b3486dc 100644
--- a/packages/eslint-config/package.json
+++ b/packages/eslint-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/eslint-config",
- "version": "3.0.68",
+ "version": "3.0.88",
"main": "index.js",
"license": "MIT",
"repository": "vtex/faststore",
diff --git a/packages/graphql-utils/package.json b/packages/graphql-utils/package.json
index 5de5a579d2..8f14121e28 100644
--- a/packages/graphql-utils/package.json
+++ b/packages/graphql-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/graphql-utils",
- "version": "3.0.68",
+ "version": "3.0.88",
"description": "GraphQL utilities",
"repository": {
"type": "git",
@@ -21,8 +21,8 @@
"graphql": "^15.6.1"
},
"devDependencies": {
- "@faststore/eslint-config": "^3.0.68",
- "@faststore/shared": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
+ "@faststore/shared": "^3.0.88",
"cross-env": "^7.0.2",
"eslint": "7.32.0",
"typescript": "^4.2.4"
diff --git a/packages/lighthouse/package.json b/packages/lighthouse/package.json
index 06201785aa..06cc0b3278 100644
--- a/packages/lighthouse/package.json
+++ b/packages/lighthouse/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/lighthouse",
- "version": "3.0.68",
+ "version": "3.0.88",
"author": "Emerson Laurentino",
"license": "MIT",
"repository": {
@@ -24,8 +24,8 @@
"lint": "eslint src/**/*.ts"
},
"devDependencies": {
- "@faststore/eslint-config": "^3.0.68",
- "@faststore/shared": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
+ "@faststore/shared": "^3.0.88",
"eslint": "7.32.0",
"typescript": "^4.2.4"
}
diff --git a/packages/sdk/package.json b/packages/sdk/package.json
index 869d48a1fb..61ceccc3a5 100644
--- a/packages/sdk/package.json
+++ b/packages/sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/sdk",
- "version": "3.0.68",
+ "version": "3.0.88",
"description": "Hooks for creating your next component library",
"license": "MIT",
"repository": {
@@ -37,7 +37,7 @@
"react": "^18.2.0"
},
"devDependencies": {
- "@faststore/shared": "^3.0.68",
+ "@faststore/shared": "^3.0.88",
"@size-limit/preset-small-lib": "^7.0.8",
"@testing-library/react-hooks": "^8.0.1",
"fake-indexeddb": "^3.1.3",
diff --git a/packages/sdk/src/search/serializer.ts b/packages/sdk/src/search/serializer.ts
index 43861571eb..7d6a5f7c8e 100644
--- a/packages/sdk/src/search/serializer.ts
+++ b/packages/sdk/src/search/serializer.ts
@@ -3,6 +3,24 @@ import { isSearchSort, setFacet } from './facets'
import { initialize } from './useSearchState'
import type { SearchSort, State } from '../types'
+function getPassThroughSearchParams(
+ params: URLSearchParams,
+ denyList: string[]
+) {
+ const passthroughParams = new URLSearchParams()
+ const denySet = new Set(denyList)
+
+ const entriesArray = Array.from(params.entries())
+
+ for (const [key, value] of entriesArray) {
+ if (!denySet.has(key)) {
+ passthroughParams.append(key, value)
+ }
+ }
+
+ return passthroughParams
+}
+
export const parse = ({ pathname, searchParams }: URL): State => {
const state = initialize({
base: pathname,
@@ -28,5 +46,13 @@ export const parse = ({ pathname, searchParams }: URL): State => {
}
}
+ state.passThrough = getPassThroughSearchParams(searchParams, [
+ 'q',
+ 'sort',
+ 'page',
+ 'facets',
+ ...facets,
+ ])
+
return state
}
diff --git a/packages/sdk/src/search/useSearchState.ts b/packages/sdk/src/search/useSearchState.ts
index f2dd122337..c9db5cdba0 100644
--- a/packages/sdk/src/search/useSearchState.ts
+++ b/packages/sdk/src/search/useSearchState.ts
@@ -9,12 +9,14 @@ export const initialize = ({
term = null,
base = '/',
page = 0,
+ passThrough = new URLSearchParams()
}: Partial | undefined = {}) => ({
sort,
selectedFacets,
term,
base,
page,
+ passThrough,
})
const equals = (s1: State, s2: State) => format(s1).href === format(s2).href
diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts
index 4942d42bb6..190dbcb3ef 100644
--- a/packages/sdk/src/types.ts
+++ b/packages/sdk/src/types.ts
@@ -30,4 +30,8 @@ export interface State {
* @description current pagination cursor
*/
page: number
+ /**
+ * @description params from other sources to preserve when building URLs
+ */
+ passThrough: URLSearchParams
}
diff --git a/packages/sdk/src/utils/format.ts b/packages/sdk/src/utils/format.ts
index 86c7c065db..0bb0bed01f 100644
--- a/packages/sdk/src/utils/format.ts
+++ b/packages/sdk/src/utils/format.ts
@@ -2,7 +2,7 @@ import type { State } from '../types'
const format = (params: State): URL => {
const url = new URL(params.base, 'http://localhost')
- const { page, selectedFacets, sort, term } = params
+ const { page, selectedFacets, sort, term, passThrough } = params
if (term !== null) {
url.searchParams.set('q', term)
@@ -22,6 +22,12 @@ const format = (params: State): URL => {
url.searchParams.set('sort', sort)
url.searchParams.set('page', page.toString())
+if (passThrough) {
+ for (const [key, value] of passThrough.entries()) {
+ url.searchParams.append(key, value)
+ }
+ }
+
return url
}
diff --git a/packages/sdk/test/search/serializer.test.ts b/packages/sdk/test/search/serializer.test.ts
index d62b7d86c3..efeadfecef 100644
--- a/packages/sdk/test/search/serializer.test.ts
+++ b/packages/sdk/test/search/serializer.test.ts
@@ -90,6 +90,7 @@ test('Search State Serializer: Basic parsing', async () => {
sort: 'score_desc',
term: 'Hello World',
page: 0,
+ passThrough: new URLSearchParams()
})
expect(
@@ -104,6 +105,7 @@ test('Search State Serializer: Basic parsing', async () => {
sort: 'score_desc',
term: 'Hello World',
page: 1,
+ passThrough: new URLSearchParams()
})
expect(
@@ -116,5 +118,56 @@ test('Search State Serializer: Basic parsing', async () => {
sort: 'score_desc',
term: 'Hello World',
page: 10,
+ passThrough: new URLSearchParams()
})
})
+
+test('Search State Serializer: Passthrough param parsing', async () => {
+ expect(
+ parseSearchState(
+ new URL(
+ 'http://localhost/pt-br/sale?q=Hello+World&&sort=score_desc&price=10%3A100&page=0&facets=price&foo=bar'
+ )
+ )
+ ).toEqual({
+ base: '/pt-br/sale',
+ selectedFacets: [
+ {
+ key: 'price',
+ value: '10:100',
+ },
+ ],
+ sort: 'score_desc',
+ term: 'Hello World',
+ page: 0,
+ passThrough: new URLSearchParams({ foo: 'bar' })
+ })
+
+ expect(
+ parseSearchState(
+ new URL(
+ 'http://localhost/pt-br/sale?q=Hello+World&sort=score_desc&page=1&foo=bar'
+ )
+ )
+ ).toEqual({
+ base: '/pt-br/sale',
+ selectedFacets: [],
+ sort: 'score_desc',
+ term: 'Hello World',
+ page: 1,
+ passThrough: new URLSearchParams({ foo: 'bar' })
+ })
+
+ expect(
+ parseSearchState(
+ new URL('http://localhost?q=Hello+World&sort=score_desc&page=10&foo=bar')
+ )
+ ).toEqual({
+ base: '/',
+ selectedFacets: [],
+ sort: 'score_desc',
+ term: 'Hello World',
+ page: 10,
+ passThrough: new URLSearchParams({ foo: 'bar' })
+ })
+})
\ No newline at end of file
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 6118b96d52..4892a7c318 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/shared",
- "version": "3.0.68",
+ "version": "3.0.88",
"private": true,
"files": [
"tsconfig.json"
diff --git a/packages/ui/package.json b/packages/ui/package.json
index c8e8f7095e..c08a7dc5ce 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@faststore/ui",
- "version": "3.0.77",
+ "version": "3.0.88",
"description": "A lightweight, framework agnostic component library for React",
"author": "emersonlaurentino",
"license": "MIT",
@@ -48,7 +48,7 @@
}
],
"dependencies": {
- "@faststore/components": "^3.0.77",
+ "@faststore/components": "^3.0.88",
"include-media": "^1.4.10",
"modern-normalize": "^1.1.0",
"react-swipeable": "^7.0.0",
@@ -59,8 +59,8 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
- "@faststore/eslint-config": "^3.0.68",
- "@faststore/shared": "^3.0.68",
+ "@faststore/eslint-config": "^3.0.88",
+ "@faststore/shared": "^3.0.88",
"@size-limit/preset-small-lib": "^7.0.8",
"@types/tabbable": "^3.1.1",
"babel-loader": "^8.2.5",
diff --git a/packages/ui/src/components/molecules/InputField/styles.scss b/packages/ui/src/components/molecules/InputField/styles.scss
index a3940a08dd..a0f5b90efa 100644
--- a/packages/ui/src/components/molecules/InputField/styles.scss
+++ b/packages/ui/src/components/molecules/InputField/styles.scss
@@ -18,6 +18,9 @@
--fs-input-field-label-color : var(--fs-color-text-light);
--fs-input-field-label-size : var(--fs-text-size-tiny);
+ // Button
+ --fs-input-field-button-height : var(--fs-control-tap-size);
+
// Error
--fs-input-field-error-message-size : var(--fs-text-size-legend);
--fs-input-field-error-message-line-height : 1.1;
@@ -41,17 +44,18 @@
display: flex;
flex-flow: column;
- input,
- label {
+ [data-fs-input],
+ [data-fs-label] {
transition: var(--fs-input-field-transition-property) var(--fs-input-field-transition-timing) var(--fs-input-field-transition-function);
}
- input {
+ [data-fs-input] {
+ --fs-input-padding: var(--fs-input-field-padding);
padding: var(--fs-input-field-padding);
color: var(--fs-input-field-color);
&:placeholder-shown + label {
- top: calc(1.5 * (var(--fs-input-field-size) / 2));
+ top: calc(50% - (var(--fs-input-field-size) / 2));
overflow: hidden;
}
@@ -76,11 +80,11 @@
}
}
- label {
+ [data-fs-label] {
position: absolute;
padding: var(--fs-input-field-label-padding);
font-size: var(--fs-input-field-size);
- line-height: 1.5;
+ line-height: var(--fs-input-field-size);
color: var(--fs-input-field-label-color);
}
@@ -89,11 +93,12 @@
// --------------------------------------------------------
&[data-fs-input-field-error="true"] {
- input {
+ [data-fs-input] {
border-color: var(--fs-input-field-error-border-color);
-
- @include input-focus-ring($outline: #{var(--fs-input-field-error-focus-ring)},
- $border: #{var(--fs-input-field-error-border-color)});
+ @include input-focus-ring(
+ $outline: #{var(--fs-input-field-error-focus-ring)},
+ $border: #{var(--fs-input-field-error-border-color)}
+ );
&:hover:not(:disabled):not(:focus-visible):not(:focus) {
border-color: var(--fs-input-field-error-border-color);
@@ -112,14 +117,13 @@
&[data-fs-input-field-actionable="true"] {
min-width: rem(250px);
- input {
- padding-right: var(--fs-spacing-13);
- }
+ [data-fs-input] { padding-right: var(--fs-spacing-13); }
[data-fs-button] {
position: absolute;
top: 0;
right: 0;
+ height: var(--fs-input-field-button-height);
[data-fs-button-wrapper]::before {
position: absolute;
diff --git a/packages/ui/src/components/molecules/OrderSummary/styles.scss b/packages/ui/src/components/molecules/OrderSummary/styles.scss
index 618f4635cf..1e04684a9c 100644
--- a/packages/ui/src/components/molecules/OrderSummary/styles.scss
+++ b/packages/ui/src/components/molecules/OrderSummary/styles.scss
@@ -6,6 +6,7 @@
// Default properties
--fs-order-summary-padding : var(--fs-spacing-3);
--fs-order-summary-margin-bottom : var(--fs-spacing-2);
+ --fs-order-summary-row-gap : 0;
--fs-order-summary-discount-text-color : var(--fs-color-success-text);
@@ -20,7 +21,10 @@
// --------------------------------------------------------
// Structural Styles
// --------------------------------------------------------
+ display: flex;
+ flex-direction: column;
padding: var(--fs-order-summary-padding);
+ row-gap: var(--fs-order-summary-row-gap);
li {
display: flex;
diff --git a/packages/ui/src/components/molecules/ProductCard/styles.scss b/packages/ui/src/components/molecules/ProductCard/styles.scss
index c194b643d3..e968b01be2 100644
--- a/packages/ui/src/components/molecules/ProductCard/styles.scss
+++ b/packages/ui/src/components/molecules/ProductCard/styles.scss
@@ -39,7 +39,8 @@
--fs-product-card-price-size : var(--fs-text-size-base);
// Out Of Stock
- --fs-product-card-out-of-stock-bkg-color : var(--fs-color-disabled-bkg);
+ --fs-product-card-out-of-stock-bkg-color : transparent;
+ --fs-product-card-out-of-stock-border-color: var(--fs-color-neutral-1);
--fs-product-card-out-of-stock-img-opacity : .5;
// Taxes label
@@ -168,7 +169,8 @@
// --------------------------------------------------------
&[data-fs-product-card="out-of-stock"] {
- --fs-product-card-bkg-color : var(--fs-product-card-out-of-stock-bkg-color);
+ --fs-product-card-bkg-color : var(--fs-product-card-out-of-stock-bkg-color);
+ --fs-product-card-border-color : var(--fs-product-card-out-of-stock-border-color);
[data-fs-product-card-image] { opacity: var(--fs-product-card-out-of-stock-img-opacity); }
diff --git a/packages/ui/src/components/molecules/QuantitySelector/styles.scss b/packages/ui/src/components/molecules/QuantitySelector/styles.scss
index 49a8c87033..55dc72e09c 100644
--- a/packages/ui/src/components/molecules/QuantitySelector/styles.scss
+++ b/packages/ui/src/components/molecules/QuantitySelector/styles.scss
@@ -42,7 +42,6 @@
justify-content: center;
width: var(--fs-qty-selector-width);
height: var(--fs-qty-selector-height);
- border-radius: var(--fs-qty-selector-border-radius);
box-shadow: var(--fs-qty-selector-shadow);
[data-quantity-selector-input] {
@@ -53,6 +52,7 @@
color: var(--fs-qty-selector-text-color);
text-align: center;
border: var(--fs-qty-selector-border-width) solid var(--fs-qty-selector-border-color);
+ border-radius: var(--fs-qty-selector-border-radius);
}
[data-quantity-selector-button] {
diff --git a/packages/ui/src/components/molecules/Toast/styles.scss b/packages/ui/src/components/molecules/Toast/styles.scss
index 96703c233d..2dd22bfe19 100644
--- a/packages/ui/src/components/molecules/Toast/styles.scss
+++ b/packages/ui/src/components/molecules/Toast/styles.scss
@@ -32,7 +32,7 @@
// Title
--fs-toast-title-size : var(--fs-text-size-body);
--fs-toast-title-weight : var(--fs-text-weight-bold);
- --fs-toast-title-line-height : var(--fs-scale);
+ --fs-toast-title-line-height : 1.2;
--fs-toast-title-margin-left : var(--fs-spacing-3);
// Message
diff --git a/packages/ui/src/components/organisms/PaymentMethods/styles.scss b/packages/ui/src/components/organisms/PaymentMethods/styles.scss
index 2aa787e578..a5376d0ea1 100644
--- a/packages/ui/src/components/organisms/PaymentMethods/styles.scss
+++ b/packages/ui/src/components/organisms/PaymentMethods/styles.scss
@@ -13,6 +13,7 @@
// Flag
--fs-payment-methods-flag-width : var(--fs-spacing-5);
--fs-payment-methods-flag-height : var(--fs-spacing-4);
+ --fs-payment-methods-flag-bkg-color : var(--fs-color-neutral-0);
--fs-payment-methods-flag-border-width : var(--fs-border-width);
--fs-payment-methods-flag-border-color : var(--fs-color-neutral-3);
--fs-payment-methods-flag-border-radius : var(--fs-border-radius-small);
@@ -44,21 +45,20 @@
justify-content: space-between;
margin-top: var(--fs-payment-methods-flags-margin-top);
- li {
- display: flex;
- place-content: center;
- }
-
@include media("