feat: add medusa-react (#913)
This commit is contained in:
+18
-15
@@ -1,22 +1,25 @@
|
||||
const path = require(`path`);
|
||||
const glob = require(`glob`);
|
||||
const fs = require(`fs`);
|
||||
const path = require(`path`)
|
||||
const glob = require(`glob`)
|
||||
const fs = require(`fs`)
|
||||
|
||||
const pkgs = glob
|
||||
.sync(`./packages/*`)
|
||||
.map((p) => p.replace(/^\./, `<rootDir>`));
|
||||
const pkgs = glob.sync(`./packages/*`).map((p) => p.replace(/^\./, `<rootDir>`))
|
||||
|
||||
const reMedusa = /medusa$/;
|
||||
const medusaDir = pkgs.find((p) => reMedusa.exec(p));
|
||||
const medusaBuildDirs = [`dist`].map((dir) => path.join(medusaDir, dir));
|
||||
const reMedusa = /medusa$/
|
||||
const medusaDir = pkgs.find((p) => reMedusa.exec(p))
|
||||
const medusaBuildDirs = [`dist`].map((dir) => path.join(medusaDir, dir))
|
||||
const builtTestsDirs = pkgs
|
||||
.filter((p) => fs.existsSync(path.join(p, `src`)))
|
||||
.map((p) => path.join(p, `__tests__`));
|
||||
const distDirs = pkgs.map((p) => path.join(p, `dist`));
|
||||
const ignoreDirs = [].concat(medusaBuildDirs, builtTestsDirs, distDirs);
|
||||
.map((p) => path.join(p, `__tests__`))
|
||||
const distDirs = pkgs.map((p) => path.join(p, `dist`))
|
||||
const ignoreDirs = [].concat(
|
||||
medusaBuildDirs,
|
||||
builtTestsDirs,
|
||||
distDirs,
|
||||
"<rootDir>/packages/medusa-react/*"
|
||||
)
|
||||
|
||||
const coverageDirs = pkgs.map((p) => path.join(p, `src/**/*.js`));
|
||||
const useCoverage = !!process.env.GENERATE_JEST_REPORT;
|
||||
const coverageDirs = pkgs.map((p) => path.join(p, `src/**/*.js`))
|
||||
const useCoverage = !!process.env.GENERATE_JEST_REPORT
|
||||
|
||||
module.exports = {
|
||||
notify: true,
|
||||
@@ -46,4 +49,4 @@ module.exports = {
|
||||
testEnvironment: `node`,
|
||||
moduleFileExtensions: [`js`, `jsx`, `ts`, `tsx`, `json`],
|
||||
// setupFiles: [`<rootDir>/.jestSetup.js`],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"pg-god": "^1.0.11",
|
||||
"prettier": "^2.1.1",
|
||||
"resolve-cwd": "^3.0.0",
|
||||
"ts-jest": "^27.1.1",
|
||||
"typeorm": "^0.2.31"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
||||
@@ -1300,13 +1300,6 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5:
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-align@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
|
||||
integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==
|
||||
dependencies:
|
||||
string-width "^3.0.0"
|
||||
|
||||
ansi-escapes@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
||||
@@ -1473,13 +1466,6 @@ aws4@^1.8.0:
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
||||
|
||||
axios-retry@^3.1.9:
|
||||
version "3.1.9"
|
||||
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8"
|
||||
integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA==
|
||||
dependencies:
|
||||
is-retry-allowed "^1.1.0"
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.2.tgz#21297d5084b2aeeb422f5d38e7be4fbb82239017"
|
||||
@@ -1603,20 +1589,6 @@ bl@^4.1.0:
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
boxen@^5.0.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.1.tgz#4faca6a437885add0bf8d99082e272d480814cd4"
|
||||
integrity sha512-JtIQYts08AFAYGF4eSh3pUt3NQkYV/e75pRtQmAVTLNWR/1L7Bsswxlgzgk8nmLEM+gFszsIlA9BgD3XnSqp3g==
|
||||
dependencies:
|
||||
ansi-align "^3.0.0"
|
||||
camelcase "^6.2.0"
|
||||
chalk "^4.1.0"
|
||||
cli-boxes "^2.2.1"
|
||||
string-width "^4.2.2"
|
||||
type-fest "^0.20.2"
|
||||
widest-line "^3.1.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@@ -1721,11 +1693,6 @@ camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
camelcase@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
caniuse-lite@^1.0.30001219:
|
||||
version "1.0.30001236"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001236.tgz#0a80de4cdf62e1770bb46a30d884fc8d633e3958"
|
||||
@@ -1813,11 +1780,6 @@ ci-info@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
|
||||
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
|
||||
|
||||
ci-info@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6"
|
||||
integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==
|
||||
|
||||
class-utils@^0.3.5:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
|
||||
@@ -1835,11 +1797,6 @@ clean-stack@^3.0.0:
|
||||
dependencies:
|
||||
escape-string-regexp "4.0.0"
|
||||
|
||||
cli-boxes@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
|
||||
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
|
||||
|
||||
cli-cursor@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
|
||||
@@ -2246,11 +2203,6 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dom-walk@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
|
||||
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
|
||||
|
||||
domexception@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
|
||||
@@ -2875,14 +2827,6 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
global@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
|
||||
integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
|
||||
dependencies:
|
||||
min-document "^2.19.0"
|
||||
process "^0.11.10"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
@@ -3220,7 +3164,7 @@ is-docker@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b"
|
||||
integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==
|
||||
|
||||
is-docker@^2.1.1, is-docker@^2.2.1:
|
||||
is-docker@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
||||
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
|
||||
@@ -3319,11 +3263,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||
dependencies:
|
||||
isobject "^3.0.1"
|
||||
|
||||
is-retry-allowed@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
|
||||
integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
@@ -4104,21 +4043,6 @@ medusa-core-utils@^0.1.27:
|
||||
"@hapi/joi" "^16.1.8"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-telemetry@^0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/medusa-telemetry/-/medusa-telemetry-0.0.5.tgz#d7d08fca5cbecc0e853b4e0406194a92c5206caa"
|
||||
integrity sha512-h7hP5Lc33OkFhMcvfrPcwINzMOuPoG8Vn8O6niKGFxF9RmmQnJgaAG1J43/Eq9ZWBrWi0n42XBttibKwCMViHw==
|
||||
dependencies:
|
||||
axios "^0.21.1"
|
||||
axios-retry "^3.1.9"
|
||||
boxen "^5.0.1"
|
||||
ci-info "^3.2.0"
|
||||
configstore "5.0.1"
|
||||
global "^4.4.0"
|
||||
is-docker "^2.2.1"
|
||||
remove-trailing-slash "^0.1.1"
|
||||
uuid "^8.3.2"
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
@@ -4181,13 +4105,6 @@ mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
min-document@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||
integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
|
||||
dependencies:
|
||||
dom-walk "^0.1.0"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
@@ -4727,11 +4644,6 @@ process-nextick-args@~2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||
|
||||
process@^0.11.10:
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
|
||||
|
||||
progress@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
@@ -4925,11 +4837,6 @@ remove-trailing-separator@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
|
||||
|
||||
remove-trailing-slash@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d"
|
||||
integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==
|
||||
|
||||
repeat-element@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
|
||||
@@ -5417,7 +5324,7 @@ string-width@^3.0.0:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^5.1.0"
|
||||
|
||||
string-width@^4.0.0, string-width@^4.2.2:
|
||||
string-width@^4.0.0:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
|
||||
integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
|
||||
@@ -5701,11 +5608,6 @@ type-fest@^0.11.0:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
|
||||
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
|
||||
|
||||
type-fest@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
type-fest@^0.21.3:
|
||||
version "0.21.3"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||
@@ -5831,11 +5733,6 @@ uuid@^3.3.2:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
v8-compile-cache@^2.0.3:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@medusajs/medusa": "^1.1.59",
|
||||
"axios": "^0.21.0",
|
||||
"axios": "^0.24.0",
|
||||
"retry-axios": "^2.6.0"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import axios, { AxiosError, AxiosInstance } from "axios"
|
||||
import axios, { AxiosError, AxiosInstance, AxiosRequestHeaders } from "axios"
|
||||
import * as rax from "retry-axios"
|
||||
import { v4 as uuidv4 } from "uuid"
|
||||
|
||||
@@ -108,7 +108,7 @@ class Client {
|
||||
userHeaders: RequestOptions,
|
||||
method: RequestMethod,
|
||||
path: string
|
||||
): object {
|
||||
): AxiosRequestHeaders {
|
||||
let defaultHeaders: object = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { AxiosResponse } from "axios"
|
||||
export interface HTTPResponse {
|
||||
status: number
|
||||
statusText: string
|
||||
headers: Record<string, string> & {
|
||||
"set-cookie"?: string[]
|
||||
}
|
||||
config: any
|
||||
request?: any
|
||||
}
|
||||
|
||||
export type Response<T> = T & {
|
||||
response: Omit<AxiosResponse<T>, "data">
|
||||
response: HTTPResponse
|
||||
}
|
||||
|
||||
export type ResponsePromise<T = any> = Promise<Response<T>>
|
||||
|
||||
@@ -1186,13 +1186,20 @@ axios-retry@^3.1.9:
|
||||
"@babel/runtime" "^7.15.4"
|
||||
is-retry-allowed "^2.2.0"
|
||||
|
||||
axios@^0.21.0, axios@^0.21.1:
|
||||
axios@^0.21.1:
|
||||
version "0.21.4"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
|
||||
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.4"
|
||||
|
||||
babel-jest@^26.6.3:
|
||||
version "26.6.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056"
|
||||
@@ -2741,6 +2748,11 @@ follow-redirects@^1.14.0:
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381"
|
||||
integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==
|
||||
|
||||
follow-redirects@^1.14.4:
|
||||
version "1.14.6"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
|
||||
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
|
||||
|
||||
for-each@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -879,18 +879,6 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
|
||||
integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==
|
||||
|
||||
"@hapi/topo@^5.0.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
|
||||
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
@@ -1083,23 +1071,6 @@
|
||||
component-type "^1.2.1"
|
||||
join-component "^1.1.0"
|
||||
|
||||
"@sideway/address@^4.1.0":
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1"
|
||||
integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@sideway/formula@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
|
||||
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
|
||||
|
||||
"@sideway/pinpoint@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
|
||||
@@ -3026,11 +2997,6 @@ is-number@^3.0.0:
|
||||
dependencies:
|
||||
kind-of "^3.0.2"
|
||||
|
||||
is-number@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
||||
integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
@@ -3521,22 +3487,6 @@ jest@^25.5.2:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^25.5.4"
|
||||
|
||||
joi-objectid@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
||||
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
||||
|
||||
joi@^17.3.0:
|
||||
version "17.4.0"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20"
|
||||
integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
"@hapi/topo" "^5.0.0"
|
||||
"@sideway/address" "^4.1.0"
|
||||
"@sideway/formula" "^3.0.0"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
join-component@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5"
|
||||
@@ -3776,11 +3726,6 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
math-random@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
|
||||
integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
|
||||
|
||||
md5@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
|
||||
@@ -3795,23 +3740,6 @@ media-typer@0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||
|
||||
medusa-core-utils@^1.1.16:
|
||||
version "1.1.16"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.16.tgz#e7002d861aebf81dec7bd0654615eefbd55cb530"
|
||||
integrity sha512-O176mtAILLbwahxJu2dAOZRnz9AzrX6Oa1NDhrtbBvaPuaFZlhxiajwZkP3KM58bGZ9feKfJ4mVuY2Mtsgj3YA==
|
||||
dependencies:
|
||||
joi "^17.3.0"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-test-utils@^1.1.19:
|
||||
version "1.1.19"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.1.19.tgz#f765b6ba39e0bfe6301423a9b39d4e7e3b1ed32b"
|
||||
integrity sha512-GkGWOUQsrNTm7tv2P7fG9f651+C05cLpy+nuPPSouIAkxpS0mDqeD1VSrHjFnpz1BPUsjwJGWFFAH5Jd8X5yvQ==
|
||||
dependencies:
|
||||
"@babel/plugin-transform-classes" "^7.9.5"
|
||||
medusa-core-utils "^1.1.16"
|
||||
randomatic "^3.1.1"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
@@ -4362,15 +4290,6 @@ qs@~6.5.2:
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
|
||||
randomatic@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
|
||||
integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
|
||||
dependencies:
|
||||
is-number "^4.0.0"
|
||||
kind-of "^6.0.0"
|
||||
math-random "^1.0.1"
|
||||
|
||||
range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
name: CI
|
||||
on: [push]
|
||||
jobs:
|
||||
build:
|
||||
name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["16.x"]
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node ${{ matrix.node }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install deps and build (with cache)
|
||||
uses: bahmutov/npm-install@v1
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
- name: Test
|
||||
run: yarn test --ci --coverage --maxWorkers=2
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
@@ -0,0 +1,12 @@
|
||||
name: size
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
size:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_JOB_NUMBER: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: andresz1/size-limit-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,5 @@
|
||||
*.log
|
||||
.DS_Store
|
||||
node_modules
|
||||
.cache
|
||||
dist
|
||||
@@ -0,0 +1,12 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
test/
|
||||
public/
|
||||
.storybook/
|
||||
mocks/
|
||||
stories/
|
||||
yarn.lock
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx)'],
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
|
||||
// https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration
|
||||
typescript: {
|
||||
check: true, // type-check stories during Storybook build
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from "react"
|
||||
import { QueryClient } from "react-query"
|
||||
import { MedusaProvider } from "../src"
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: Infinity,
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const DefaultMedusaProvider = (props) => (
|
||||
<MedusaProvider
|
||||
queryClientProviderProps={{ client: queryClient }}
|
||||
baseUrl={""}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
export default DefaultMedusaProvider
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from "react"
|
||||
import DefaultMedusaProvider from "./medusa-context"
|
||||
import { initialize, mswDecorator } from "msw-storybook-addon"
|
||||
import { handlers } from "../mocks/handlers"
|
||||
|
||||
initialize()
|
||||
|
||||
// https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters
|
||||
export const parameters = {
|
||||
// https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args
|
||||
actions: { argTypesRegex: "^on.*" },
|
||||
msw: {
|
||||
handlers,
|
||||
},
|
||||
}
|
||||
|
||||
export const decorators = [
|
||||
mswDecorator,
|
||||
(Story) => (
|
||||
<DefaultMedusaProvider>
|
||||
<Story />
|
||||
</DefaultMedusaProvider>
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Zakaria S. El Asri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,306 @@
|
||||
# Medusa React
|
||||
|
||||
A React library providing a set of components, utilities, and hooks for interacting seamlessly with a Medusa backend and building custom React storefronts.
|
||||
|
||||
## Installation
|
||||
|
||||
The library uses [react-query](https://react-query.tanstack.com/overview) as a solution for server-side state management and lists the library as a peer dependency.
|
||||
|
||||
In order to install the package, run the following
|
||||
|
||||
```bash
|
||||
npm install @medusajs/medusa-react react-query
|
||||
# or
|
||||
yarn add @medusajs/medusa-react react-query
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
In order to use the hooks exposed by medusa-react, you will need to include the `MedusaProvider` somewhere up in your component tree. The `MedusaProvider` takes a `baseUrl` prop which should point to your Medusa server. Under the hood, `medusa-react` uses the `medusa-js` client library (built on top of axios) to interact with your server.
|
||||
|
||||
In addition, because medusa-react is built on top of react-query, you can pass an object representing react-query's [QueryClientProvider](https://react-query.tanstack.com/reference/QueryClientProvider#_top) props, which will be passed along by `MedusaProvider`.
|
||||
|
||||
```jsx
|
||||
// App.tsx
|
||||
|
||||
import * as React from "react"
|
||||
import { QueryClient } from "react-query"
|
||||
import { MedusaProvider } from "../src"
|
||||
import MyStorefront from "./my-storefront"
|
||||
|
||||
// Your react-query's query client config
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 30000,
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<MedusaProvider
|
||||
queryClientProviderProps={{ client: queryClient }}
|
||||
baseUrl="http://localhost:9000"
|
||||
>
|
||||
<MyStorefront />
|
||||
</MedusaProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
The hooks exposed by `medusa-react` fall into two main categories: queries and mutations.
|
||||
|
||||
### Queries
|
||||
|
||||
[Queries](https://react-query.tanstack.com/guides/queries#_top) simply wrap around react-query's `useQuery` hook to fetch some data from your medusa server
|
||||
|
||||
```jsx
|
||||
// ./my-storefront.tsx
|
||||
import * as React from "react"
|
||||
import { useProducts } from "@medusajs/medusa-react"
|
||||
|
||||
const MyStorefront = () => {
|
||||
const { products, isLoading } = useProducts()
|
||||
|
||||
return isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
products.map((product) => <Product product={product} />)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
In general, the queries will return everything react-query returns from [`useQuery`](https://react-query.tanstack.com/reference/useQuery#_top) except the `data` field, which will be flattened out. In addition, you can also access the HTTP response object returned from the `medusa-js` client including things like `status`, `headers`, etc.
|
||||
|
||||
So, in other words, we can express what the above query returns as the following:
|
||||
|
||||
```typescript
|
||||
import { UseQueryResult } from "react-query"
|
||||
|
||||
// This is what a Medusa server returns when you hit the GET /store/products endpoint
|
||||
type ProductsResponse = {
|
||||
products: Product[]
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
// UseProductsQuery refers to what's returned by the useProducts hook
|
||||
type UseProductsQuery = ProductsResponse &
|
||||
Omit<UseQueryResult, "data"> & {
|
||||
response: {
|
||||
status: number
|
||||
statusText: string
|
||||
headers: Record<string, string> & {
|
||||
"set-cookie"?: string[]
|
||||
}
|
||||
config: any
|
||||
request?: any
|
||||
}
|
||||
}
|
||||
|
||||
// More generally ...
|
||||
|
||||
type QueryReturnType = APIResponse &
|
||||
Omit<UseQueryResult, "data"> & {
|
||||
response: {
|
||||
status: number
|
||||
statusText: string
|
||||
headers: Record<string, string> & {
|
||||
"set-cookie"?: string[]
|
||||
}
|
||||
config: any
|
||||
request?: any
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mutations
|
||||
|
||||
[Mutations](https://react-query.tanstack.com/guides/mutations#_top) wrap around react-query's `useMutation` to mutate data and perform server-side effects on your medusa server. If you are not entirely familiar with this idea of "mutations", creating a cart would be a mutation because it creates a cart in your server (and database). Mutations also have to be invoked imperatively, meaning that calling for the mutation to take action, you will have to call a `mutate()` function returned from mutation hooks.
|
||||
|
||||
```jsx
|
||||
import * as React from "react"
|
||||
import { useCreateCart } from "@medusajs/medusa-react"
|
||||
|
||||
const CreateCartButton = () => {
|
||||
const createCart = useCreateCart()
|
||||
const handleClick = () => {
|
||||
createCart.mutate({}) // create an empty cart
|
||||
}
|
||||
|
||||
return (
|
||||
<Button isLoading={createCart.isLoading} onClick={handleClick}>
|
||||
Create cart
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The mutation hooks will return exactly what react-query's [`useMutation`](https://react-query.tanstack.com/reference/useMutation#_top) returns. In addition, the options you pass in to the hooks will be passed along to `useMutation`.
|
||||
|
||||
### Utilities
|
||||
|
||||
A set of utility functions are also exposed from the library to make your life easier when dealing with displaying money amounts
|
||||
|
||||
#### `formatVariantPrice()`
|
||||
|
||||
- `formatVariantPrice(params: FormatVariantPriceParams): string`
|
||||
|
||||
```typescript
|
||||
type FormatVariantPriceParams = {
|
||||
variant: ProductVariantInfo
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
|
||||
type ProductVariantInfo = Pick<ProductVariant, "prices">
|
||||
|
||||
type RegionInfo = {
|
||||
currency_code: string
|
||||
tax_code: string
|
||||
tax_rate: number
|
||||
}
|
||||
```
|
||||
|
||||
Given a variant and region, will return a string representing the localized amount (i.e: `$19.50`)
|
||||
|
||||
The behavior of minimumFractionDigits and maximumFractionDigits is the same as the one explained by MDN [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). In fact, in order to convert the decimal amount, we use the browser's `Intl.NumberFormat` method.
|
||||
|
||||
#### `computeVariantPrice()`
|
||||
|
||||
- `computeVariantPrice(params: ComputeVariantPriceParams): number`
|
||||
|
||||
```typescript
|
||||
type ComputeVariantPriceParams = {
|
||||
variant: ProductVariantInfo
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
Determines a variant's price based on the region provided. Returns a decimal number representing the amount.
|
||||
|
||||
#### `formatAmount()`
|
||||
|
||||
- `formatAmount(params: FormatAmountParams): string`
|
||||
|
||||
```typescript
|
||||
type FormatAmountParams = {
|
||||
amount: number
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
```
|
||||
|
||||
Returns a localized string based on the input params representing the amount (i.e: "$10.99").
|
||||
|
||||
#### `computeAmount()`
|
||||
|
||||
- `computeAmount(params: ComputeAmountParams): number`
|
||||
|
||||
```typescript
|
||||
type ComputeAmountParams = {
|
||||
amount: number
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
Takes an integer amount, a region, and includeTaxes boolean. Returns a decimal amount including (or excluding) taxes.
|
||||
|
||||
### Context Providers (Experimental)
|
||||
|
||||
In order to make building custom storefronts easier, we also expose a `SessionCartProvider` and a `CartProvider` . At first, the two sound very similar to each other, however, the main distinction between the two is that the `SessionCartProvider` never interacts with your medusa server.
|
||||
|
||||
The main goal behind the provider is to manage the state related to your users' cart experience. In other words, the provider keeps track of the items users add to their cart and help you interact with those items through a set of helpful methods like `addItem`, `updateQuantity`, `removeItem` , etc.
|
||||
|
||||
On the other hand the `CartProvider` makes use of some of the hooks already exposed by `medusa-react` to help you create a cart (on the medusa backend), start the checkout flow, authorize payment sessions, etc. It also manages one single global piece of state which represents a cart, exactly like the one created on your medusa backend.
|
||||
|
||||
You can think of a `sessionCart` as a purely client-side lightweight cart, in other words, just a javascript object living in your browser, whereas `cart` is the entity which you have stored in your database.
|
||||
|
||||
### SessionCart
|
||||
|
||||
The first step to using the `SessionCartProvider` is by inserting it somewhere up in your component tree.
|
||||
|
||||
```jsx
|
||||
// App.tsx
|
||||
|
||||
import * as React from "react"
|
||||
import { QueryClient } from "react-query"
|
||||
import { MedusaProvider, SessionCartProvider } from "medusa-react"
|
||||
import MyStorefront from "./my-storefront"
|
||||
|
||||
// Your react-query's query client config
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 30000,
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<MedusaProvider
|
||||
queryClientProviderProps={{ client: queryClient }}
|
||||
baseUrl="http://localhost:9000"
|
||||
>
|
||||
<SessionCartProvider>
|
||||
<MyStorefront />
|
||||
</SessionCartProvider>
|
||||
</MedusaProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
### Cart
|
||||
|
||||
```jsx
|
||||
// App.tsx
|
||||
|
||||
import * as React from "react"
|
||||
import { QueryClient } from "react-query"
|
||||
import { MedusaProvider, CartProvider } from "medusa-react"
|
||||
import MyStorefront from "./my-storefront"
|
||||
|
||||
// Your react-query's query client config
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 30000,
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<MedusaProvider
|
||||
queryClientProviderProps={{ client: queryClient }}
|
||||
baseUrl="http://localhost:9000"
|
||||
>
|
||||
<CartProvider>
|
||||
<MyStorefront />
|
||||
</CartProvider>
|
||||
</MedusaProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
// [...]
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
diagnostics: false,
|
||||
isolatedModules: true,
|
||||
},
|
||||
},
|
||||
setupFilesAfterEnv: ["./jest.setup.js"],
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
const { server } = require("./mocks/server")
|
||||
|
||||
beforeAll(() => {
|
||||
server.listen({
|
||||
onUnhandledRequest: (req) => {
|
||||
console.log("found an error")
|
||||
console.log(req)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
window.localStorage.clear()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
server.resetHandlers()
|
||||
window.localStorage.clear()
|
||||
})
|
||||
|
||||
afterAll(() => server.close())
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
import data from "./fixtures.json"
|
||||
|
||||
const resources = data["resources"]
|
||||
|
||||
type Resources = typeof resources
|
||||
|
||||
type ResourcesWithKey<Entity extends string, T> = {
|
||||
[K in keyof T]: { [_ in Entity]: K } & T[K]
|
||||
}
|
||||
|
||||
type KeyedResources = ResourcesWithKey<"entity", Resources>
|
||||
|
||||
export const fixtures = {
|
||||
get<Entity extends keyof Resources>(
|
||||
entity: Entity
|
||||
): Omit<KeyedResources[Entity], "entity"> {
|
||||
return resources[entity as string]
|
||||
},
|
||||
list<Entity extends keyof Resources>(
|
||||
entity: Entity,
|
||||
number = 2
|
||||
): Omit<KeyedResources[Entity], "entity">[] {
|
||||
return Array(number)
|
||||
.fill(null)
|
||||
.map((_) => fixtures.get(entity))
|
||||
},
|
||||
} as const
|
||||
@@ -0,0 +1,424 @@
|
||||
import { fixtures } from "./data/"
|
||||
import { rest } from "msw"
|
||||
|
||||
export const handlers = [
|
||||
rest.get("/store/products", (req, res, ctx) => {
|
||||
const limit = parseInt(req.url.searchParams.get("limit") || "2")
|
||||
const offset = parseInt(req.url.searchParams.get("offset") || "0")
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
products: fixtures.list("product", limit),
|
||||
offset,
|
||||
limit,
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/products/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
product: fixtures.get("product"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/collections/", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
collections: fixtures.list("product_collection"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/collections/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
collection: fixtures.get("product_collection"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/regions/", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
regions: fixtures.list("region"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/regions/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
region: fixtures.get("region"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/gift-cards/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
gift_card: fixtures.get("gift_card"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/orders/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
order: fixtures.get("order"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/orders/cart/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
order: fixtures.get("order"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/orders/", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
orders: fixtures.get("order"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/return-reasons/", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
return_reasons: fixtures.list("return_reason"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/return-reasons/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
return_reason: fixtures.get("return_reason"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/shipping-options/:cart_id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
shipping_options: fixtures.list("shipping_option"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/shipping-options/", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
shipping_options: fixtures.list("shipping_option", 5),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/swaps/:cart_id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
swap: fixtures.get("swap"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/customers/me", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
customer: fixtures.get("customer"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/customers/me/orders", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
orders: fixtures.list("order", 5),
|
||||
limit: 5,
|
||||
offset: 0,
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/customers/", (req, res, ctx) => {
|
||||
const body = req.body as Record<string, string>
|
||||
const dummyCustomer = fixtures.get("customer")
|
||||
const customer = {
|
||||
...dummyCustomer,
|
||||
...body,
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
customer,
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/customers/me", (req, res, ctx) => {
|
||||
const body = req.body as Record<string, string>
|
||||
const dummyCustomer = fixtures.get("customer")
|
||||
const customer = {
|
||||
...dummyCustomer,
|
||||
...body,
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
customer,
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.get("/store/carts/:id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: fixtures.get("cart"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/returns/", (req, res, ctx) => {
|
||||
const { items, ...body } = req.body as Record<string, any>
|
||||
const ret = fixtures.get("return")
|
||||
const item = ret.items[0]
|
||||
ret.items = items.map((i) => ({ ...i, ...item }))
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
return: {
|
||||
...ret,
|
||||
...body,
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/swaps/", (req, res, ctx) => {
|
||||
const { additional_items, return_items, ...body } = req.body as Record<
|
||||
string,
|
||||
any
|
||||
>
|
||||
const swap = fixtures.get("swap")
|
||||
const additional_item = swap.additional_items[0]
|
||||
swap.additional_items = additional_items.map((i) => ({
|
||||
...i,
|
||||
...additional_item,
|
||||
}))
|
||||
const return_item = swap.return_order.items[0]
|
||||
swap.return_order.items = return_items.map((i) => ({
|
||||
...i,
|
||||
...return_item,
|
||||
}))
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
swap: {
|
||||
...swap,
|
||||
...body,
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/carts/:id/line-items", (req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
const { quantity, variant_id } = req.body as Record<string, any>
|
||||
const item = fixtures.get("line_item")
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
items: [
|
||||
{
|
||||
...item,
|
||||
quantity,
|
||||
variant_id,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/carts/:id/line-items/:line_id", (req, res, ctx) => {
|
||||
const { id, line_id } = req.params
|
||||
const { quantity } = req.body as Record<string, any>
|
||||
const item = fixtures.get("line_item")
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
items: [
|
||||
{
|
||||
...item,
|
||||
id: line_id,
|
||||
quantity,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.delete("/store/carts/:id/line-items/:line_id", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: fixtures.get("cart"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/carts/", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: fixtures.get("cart"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/carts/:id", (req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
const body = req.body as Record<string, any>
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
...body,
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/carts/:id/complete", (req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
type: "order",
|
||||
data: fixtures.get("order"),
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/store/carts/:id/payment-sessions", (req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post(
|
||||
"/store/carts/:id/payment-sessions/:provider_id",
|
||||
(req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
rest.post(
|
||||
"/store/carts/:id/payment-sessions/:provider_id/refresh",
|
||||
(req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
rest.post("/store/carts/:id/payment-session", (req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.delete(
|
||||
"/store/carts/:id/payment-sessions/:provider_id",
|
||||
(req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
rest.post("/store/carts/:id/shipping-methods", (req, res, ctx) => {
|
||||
const { id } = req.params
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
cart: {
|
||||
...fixtures.get("cart"),
|
||||
id,
|
||||
},
|
||||
})
|
||||
)
|
||||
}),
|
||||
]
|
||||
@@ -0,0 +1,4 @@
|
||||
import { setupServer } from "msw/node"
|
||||
import { handlers } from "./handlers"
|
||||
|
||||
export const server = setupServer(...handlers)
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "tsdx watch",
|
||||
"build": "tsdx build",
|
||||
"test": "tsdx test --passWithNoTests",
|
||||
"lint": "tsdx lint",
|
||||
"prepare": "tsdx build",
|
||||
"size": "size-limit",
|
||||
"analyze": "size-limit --why",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-query": ">= 3.29.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "tsdx lint"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
},
|
||||
"name": "medusa-react",
|
||||
"author": "Zakaria S. El Asri",
|
||||
"module": "dist/medusa-react.esm.js",
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "dist/medusa-react.cjs.production.min.js",
|
||||
"limit": "10 KB"
|
||||
},
|
||||
{
|
||||
"path": "dist/medusa-react.esm.js",
|
||||
"limit": "10 KB"
|
||||
}
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@size-limit/preset-small-lib": "^6.0.4",
|
||||
"@storybook/addon-contexts": "^5.3.21",
|
||||
"@storybook/addon-essentials": "^6.3.12",
|
||||
"@storybook/addon-info": "^5.3.21",
|
||||
"@storybook/addon-links": "^6.3.12",
|
||||
"@storybook/addons": "^6.3.12",
|
||||
"@storybook/react": "^6.3.12",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
"@types/lodash": "^4.14.177",
|
||||
"@types/react": "^17.0.33",
|
||||
"@types/react-dom": "^17.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"axios": "^0.24.0",
|
||||
"babel-loader": "^8.2.3",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"eslint-plugin-prettier": "3.4.1",
|
||||
"husky": "^7.0.4",
|
||||
"msw": "^0.35.0",
|
||||
"msw-storybook-addon": "^1.5.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-is": "^17.0.2",
|
||||
"react-json-view": "^1.21.3",
|
||||
"size-limit": "^6.0.4",
|
||||
"tsdx": "^0.14.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/medusa": "^1.1.59",
|
||||
"@medusajs/medusa-js": "^1.0.7",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react-query": "^3.31.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/@typescript-eslint/eslint-plugin": "^4.11.1",
|
||||
"**/@typescript-eslint/parser": "^4.11.1",
|
||||
"**/jest": "^26.6.3",
|
||||
"**/ts-jest": "^26.4.4",
|
||||
"**/typescript": "^4.1.3"
|
||||
},
|
||||
"msw": {
|
||||
"workerDirectory": "public"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
/**
|
||||
* Mock Service Worker (0.35.0).
|
||||
* @see https://github.com/mswjs/msw
|
||||
* - Please do NOT modify this file.
|
||||
* - Please do NOT serve this file on production.
|
||||
*/
|
||||
|
||||
const INTEGRITY_CHECKSUM = 'f0a916b13c8acc2b526a03a6d26df85f'
|
||||
const bypassHeaderName = 'x-msw-bypass'
|
||||
const activeClientIds = new Set()
|
||||
|
||||
self.addEventListener('install', function () {
|
||||
return self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', async function (event) {
|
||||
return self.clients.claim()
|
||||
})
|
||||
|
||||
self.addEventListener('message', async function (event) {
|
||||
const clientId = event.source.id
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return
|
||||
}
|
||||
|
||||
const client = await self.clients.get(clientId)
|
||||
|
||||
if (!client) {
|
||||
return
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll()
|
||||
|
||||
switch (event.data) {
|
||||
case 'KEEPALIVE_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'KEEPALIVE_RESPONSE',
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'INTEGRITY_CHECK_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'INTEGRITY_CHECK_RESPONSE',
|
||||
payload: INTEGRITY_CHECKSUM,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_ACTIVATE': {
|
||||
activeClientIds.add(clientId)
|
||||
|
||||
sendToClient(client, {
|
||||
type: 'MOCKING_ENABLED',
|
||||
payload: true,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_DEACTIVATE': {
|
||||
activeClientIds.delete(clientId)
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
const remainingClients = allClients.filter((client) => {
|
||||
return client.id !== clientId
|
||||
})
|
||||
|
||||
// Unregister itself when there are no more clients
|
||||
if (remainingClients.length === 0) {
|
||||
self.registration.unregister()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Resolve the "master" client for the given event.
|
||||
// Client that issues a request doesn't necessarily equal the client
|
||||
// that registered the worker. It's with the latter the worker should
|
||||
// communicate with during the response resolving phase.
|
||||
async function resolveMasterClient(event) {
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
if (client.frameType === 'top-level') {
|
||||
return client
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll()
|
||||
|
||||
return allClients
|
||||
.filter((client) => {
|
||||
// Get only those clients that are currently visible.
|
||||
return client.visibilityState === 'visible'
|
||||
})
|
||||
.find((client) => {
|
||||
// Find the client ID that's recorded in the
|
||||
// set of clients that have registered the worker.
|
||||
return activeClientIds.has(client.id)
|
||||
})
|
||||
}
|
||||
|
||||
async function handleRequest(event, requestId) {
|
||||
const client = await resolveMasterClient(event)
|
||||
const response = await getResponse(event, client, requestId)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
;(async function () {
|
||||
const clonedResponse = response.clone()
|
||||
sendToClient(client, {
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
requestId,
|
||||
type: clonedResponse.type,
|
||||
ok: clonedResponse.ok,
|
||||
status: clonedResponse.status,
|
||||
statusText: clonedResponse.statusText,
|
||||
body:
|
||||
clonedResponse.body === null ? null : await clonedResponse.text(),
|
||||
headers: serializeHeaders(clonedResponse.headers),
|
||||
redirected: clonedResponse.redirected,
|
||||
},
|
||||
})
|
||||
})()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
async function getResponse(event, client, requestId) {
|
||||
const { request } = event
|
||||
const requestClone = request.clone()
|
||||
const getOriginalResponse = () => fetch(requestClone)
|
||||
|
||||
// Bypass mocking when the request client is not active.
|
||||
if (!client) {
|
||||
return getOriginalResponse()
|
||||
}
|
||||
|
||||
// Bypass initial page load requests (i.e. static assets).
|
||||
// The absence of the immediate/parent client in the map of the active clients
|
||||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
||||
// and is not ready to handle requests.
|
||||
if (!activeClientIds.has(client.id)) {
|
||||
return await getOriginalResponse()
|
||||
}
|
||||
|
||||
// Bypass requests with the explicit bypass header
|
||||
if (requestClone.headers.get(bypassHeaderName) === 'true') {
|
||||
const cleanRequestHeaders = serializeHeaders(requestClone.headers)
|
||||
|
||||
// Remove the bypass header to comply with the CORS preflight check.
|
||||
delete cleanRequestHeaders[bypassHeaderName]
|
||||
|
||||
const originalRequest = new Request(requestClone, {
|
||||
headers: new Headers(cleanRequestHeaders),
|
||||
})
|
||||
|
||||
return fetch(originalRequest)
|
||||
}
|
||||
|
||||
// Send the request to the client-side MSW.
|
||||
const reqHeaders = serializeHeaders(request.headers)
|
||||
const body = await request.text()
|
||||
|
||||
const clientMessage = await sendToClient(client, {
|
||||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers: reqHeaders,
|
||||
cache: request.cache,
|
||||
mode: request.mode,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body,
|
||||
bodyUsed: request.bodyUsed,
|
||||
keepalive: request.keepalive,
|
||||
},
|
||||
})
|
||||
|
||||
switch (clientMessage.type) {
|
||||
case 'MOCK_SUCCESS': {
|
||||
return delayPromise(
|
||||
() => respondWithMock(clientMessage),
|
||||
clientMessage.payload.delay,
|
||||
)
|
||||
}
|
||||
|
||||
case 'MOCK_NOT_FOUND': {
|
||||
return getOriginalResponse()
|
||||
}
|
||||
|
||||
case 'NETWORK_ERROR': {
|
||||
const { name, message } = clientMessage.payload
|
||||
const networkError = new Error(message)
|
||||
networkError.name = name
|
||||
|
||||
// Rejecting a request Promise emulates a network error.
|
||||
throw networkError
|
||||
}
|
||||
|
||||
case 'INTERNAL_ERROR': {
|
||||
const parsedBody = JSON.parse(clientMessage.payload.body)
|
||||
|
||||
console.error(
|
||||
`\
|
||||
[MSW] Uncaught exception in the request handler for "%s %s":
|
||||
|
||||
${parsedBody.location}
|
||||
|
||||
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\
|
||||
`,
|
||||
request.method,
|
||||
request.url,
|
||||
)
|
||||
|
||||
return respondWithMock(clientMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return getOriginalResponse()
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
const { request } = event
|
||||
const accept = request.headers.get('accept') || ''
|
||||
|
||||
// Bypass server-sent events.
|
||||
if (accept.includes('text/event-stream')) {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (request.mode === 'navigate') {
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been deleted (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = uuidv4()
|
||||
|
||||
return event.respondWith(
|
||||
handleRequest(event, requestId).catch((error) => {
|
||||
if (error.name === 'NetworkError') {
|
||||
console.warn(
|
||||
'[MSW] Successfully emulated a network error for the "%s %s" request.',
|
||||
request.method,
|
||||
request.url,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, any exception indicates an issue with the original request/response.
|
||||
console.error(
|
||||
`\
|
||||
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
|
||||
request.method,
|
||||
request.url,
|
||||
`${error.name}: ${error.message}`,
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
function serializeHeaders(headers) {
|
||||
const reqHeaders = {}
|
||||
headers.forEach((value, name) => {
|
||||
reqHeaders[name] = reqHeaders[name]
|
||||
? [].concat(reqHeaders[name]).concat(value)
|
||||
: value
|
||||
})
|
||||
return reqHeaders
|
||||
}
|
||||
|
||||
function sendToClient(client, message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel()
|
||||
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data && event.data.error) {
|
||||
return reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(JSON.stringify(message), [channel.port2])
|
||||
})
|
||||
}
|
||||
|
||||
function delayPromise(cb, duration) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => resolve(cb()), duration)
|
||||
})
|
||||
}
|
||||
|
||||
function respondWithMock(clientMessage) {
|
||||
return new Response(clientMessage.payload.body, {
|
||||
...clientMessage.payload,
|
||||
headers: clientMessage.payload.headers,
|
||||
})
|
||||
}
|
||||
|
||||
function uuidv4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = (Math.random() * 16) | 0
|
||||
const v = c == 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import React, { useState } from "react"
|
||||
import {
|
||||
useAddShippingMethodToCart,
|
||||
useCompleteCart,
|
||||
useCreateCart,
|
||||
useSetPaymentSession,
|
||||
useUpdateCart,
|
||||
useCreatePaymentSession,
|
||||
} from "../hooks/carts"
|
||||
import { Cart } from "../types"
|
||||
|
||||
interface CartState {
|
||||
cart?: Cart
|
||||
}
|
||||
|
||||
interface CartContext extends CartState {
|
||||
setCart: (cart: Cart) => void
|
||||
pay: ReturnType<typeof useSetPaymentSession>
|
||||
createCart: ReturnType<typeof useCreateCart>
|
||||
startCheckout: ReturnType<typeof useCreatePaymentSession>
|
||||
completeCheckout: ReturnType<typeof useCompleteCart>
|
||||
updateCart: ReturnType<typeof useUpdateCart>
|
||||
addShippingMethod: ReturnType<typeof useAddShippingMethodToCart>
|
||||
totalItems: number
|
||||
}
|
||||
|
||||
const CartContext = React.createContext<CartContext | null>(null)
|
||||
|
||||
export const useCart = () => {
|
||||
const context = React.useContext(CartContext)
|
||||
if (!context) {
|
||||
throw new Error("useCart must be used within a CartProvider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
interface CartProps {
|
||||
children: React.ReactNode
|
||||
initialState?: Cart
|
||||
}
|
||||
|
||||
const defaultInitialState = {
|
||||
id: "",
|
||||
items: [] as any,
|
||||
} as Cart
|
||||
|
||||
export const CartProvider = ({
|
||||
children,
|
||||
initialState = defaultInitialState,
|
||||
}: CartProps) => {
|
||||
const [cart, setCart] = useState<Cart>(initialState)
|
||||
|
||||
const createCart = useCreateCart({
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const updateCart = useUpdateCart(cart?.id, {
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const addShippingMethod = useAddShippingMethodToCart(cart?.id, {
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const startCheckout = useCreatePaymentSession(cart?.id, {
|
||||
onSuccess: ({ cart }) => setCart(cart),
|
||||
})
|
||||
|
||||
const pay = useSetPaymentSession(cart?.id, {
|
||||
onSuccess: ({ cart }) => {
|
||||
setCart(cart)
|
||||
},
|
||||
})
|
||||
|
||||
const completeCheckout = useCompleteCart(cart?.id)
|
||||
|
||||
const totalItems = cart?.items
|
||||
.map((i) => i.quantity)
|
||||
.reduce((acc, curr) => acc + curr, 0)
|
||||
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
cart,
|
||||
setCart,
|
||||
createCart,
|
||||
pay,
|
||||
startCheckout,
|
||||
completeCheckout,
|
||||
updateCart,
|
||||
addShippingMethod,
|
||||
totalItems: totalItems || 0,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./medusa"
|
||||
export * from "./session-cart"
|
||||
export * from "./cart"
|
||||
@@ -0,0 +1,38 @@
|
||||
import React from "react"
|
||||
import { QueryClientProvider, QueryClientProviderProps } from "react-query"
|
||||
import Medusa from "@medusajs/medusa-js"
|
||||
|
||||
interface MedusaContextState {
|
||||
client: Medusa
|
||||
}
|
||||
|
||||
const MedusaContext = React.createContext<MedusaContextState | null>(null)
|
||||
|
||||
export const useMedusa = () => {
|
||||
const context = React.useContext(MedusaContext)
|
||||
if (!context) {
|
||||
throw new Error("useMedusa must be used within a MedusaProvider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
interface MedusaProviderProps {
|
||||
baseUrl: string
|
||||
queryClientProviderProps: QueryClientProviderProps
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const MedusaProvider = ({
|
||||
queryClientProviderProps,
|
||||
baseUrl,
|
||||
children,
|
||||
}: MedusaProviderProps) => {
|
||||
const medusaClient = new Medusa({ baseUrl, maxRetries: 0 })
|
||||
return (
|
||||
<QueryClientProvider {...queryClientProviderProps}>
|
||||
<MedusaContext.Provider value={{ client: medusaClient }}>
|
||||
{children}
|
||||
</MedusaContext.Provider>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
import React, { useContext, useEffect } from "react"
|
||||
import { useLocalStorage } from "../hooks/utils"
|
||||
import { RegionInfo, ProductVariant } from "../types"
|
||||
import { getVariantPrice } from "../utils"
|
||||
import { isArray, isEmpty, isObject } from "lodash"
|
||||
|
||||
interface Item {
|
||||
variant: ProductVariant
|
||||
quantity: number
|
||||
readonly total?: number
|
||||
}
|
||||
|
||||
export interface SessionCartState {
|
||||
region: RegionInfo
|
||||
items: Item[]
|
||||
totalItems: number
|
||||
total: number
|
||||
}
|
||||
|
||||
interface SessionCartContextState extends SessionCartState {
|
||||
setRegion: (region: RegionInfo) => void
|
||||
addItem: (item: Item) => void
|
||||
removeItem: (id: string) => void
|
||||
updateItem: (id: string, item: Partial<Item>) => void
|
||||
setItems: (items: Item[]) => void
|
||||
updateItemQuantity: (id: string, quantity: number) => void
|
||||
incrementItemQuantity: (id: string) => void
|
||||
decrementItemQuantity: (id: string) => void
|
||||
getItem: (id: string) => Item | undefined
|
||||
clearItems: () => void
|
||||
}
|
||||
|
||||
const SessionCartContext = React.createContext<SessionCartContextState | null>(
|
||||
null
|
||||
)
|
||||
|
||||
enum ACTION_TYPES {
|
||||
INIT,
|
||||
ADD_ITEM,
|
||||
SET_ITEMS,
|
||||
REMOVE_ITEM,
|
||||
UPDATE_ITEM,
|
||||
CLEAR_ITEMS,
|
||||
SET_REGION,
|
||||
}
|
||||
|
||||
type Action =
|
||||
| { type: ACTION_TYPES.SET_REGION; payload: RegionInfo }
|
||||
| { type: ACTION_TYPES.INIT; payload: object }
|
||||
| { type: ACTION_TYPES.ADD_ITEM; payload: Item }
|
||||
| {
|
||||
type: ACTION_TYPES.UPDATE_ITEM
|
||||
payload: { id: string; item: Partial<Item> }
|
||||
}
|
||||
| { type: ACTION_TYPES.REMOVE_ITEM; payload: { id: string } }
|
||||
| { type: ACTION_TYPES.SET_ITEMS; payload: Item[] }
|
||||
| { type: ACTION_TYPES.CLEAR_ITEMS }
|
||||
|
||||
const reducer = (state: SessionCartState, action: Action) => {
|
||||
switch (action.type) {
|
||||
case ACTION_TYPES.INIT: {
|
||||
return state
|
||||
}
|
||||
case ACTION_TYPES.SET_REGION: {
|
||||
return generateCartState(
|
||||
{
|
||||
...state,
|
||||
region: action.payload,
|
||||
},
|
||||
state.items
|
||||
)
|
||||
}
|
||||
case ACTION_TYPES.ADD_ITEM: {
|
||||
const duplicateVariantIndex = state.items.findIndex(
|
||||
(item) => item.variant.id === action.payload?.variant?.id
|
||||
)
|
||||
if (duplicateVariantIndex !== -1) {
|
||||
state.items.splice(duplicateVariantIndex, 1)
|
||||
}
|
||||
const items = [...state.items, action.payload]
|
||||
return generateCartState(state, items)
|
||||
}
|
||||
case ACTION_TYPES.UPDATE_ITEM: {
|
||||
const items = state.items.map((item) =>
|
||||
item.variant.id === action.payload.id
|
||||
? { ...item, ...action.payload.item }
|
||||
: item
|
||||
)
|
||||
|
||||
return generateCartState(state, items)
|
||||
}
|
||||
case ACTION_TYPES.REMOVE_ITEM: {
|
||||
const items = state.items.filter(
|
||||
(item) => item.variant.id !== action.payload.id
|
||||
)
|
||||
return generateCartState(state, items)
|
||||
}
|
||||
case ACTION_TYPES.SET_ITEMS: {
|
||||
return generateCartState(state, action.payload)
|
||||
}
|
||||
case ACTION_TYPES.CLEAR_ITEMS: {
|
||||
return {
|
||||
...state,
|
||||
items: [],
|
||||
total: 0,
|
||||
totalItems: 0,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export const generateCartState = (state: SessionCartState, items: Item[]) => {
|
||||
const newItems = generateItems(state.region, items)
|
||||
return {
|
||||
...state,
|
||||
items: newItems,
|
||||
totalItems: items.reduce((sum, item) => sum + item.quantity, 0),
|
||||
total: calculateSessionCartTotal(newItems),
|
||||
}
|
||||
}
|
||||
|
||||
const generateItems = (region: RegionInfo, items: Item[]) => {
|
||||
return items.map((item) => ({
|
||||
...item,
|
||||
total: getVariantPrice(item.variant, region),
|
||||
}))
|
||||
}
|
||||
|
||||
const calculateSessionCartTotal = (items: Item[]) => {
|
||||
return items.reduce(
|
||||
(total, item) => total + item.quantity * (item.total || 0),
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
interface SessionCartProviderProps {
|
||||
children: React.ReactNode
|
||||
initialState?: SessionCartState
|
||||
}
|
||||
|
||||
const defaultInitialState: SessionCartState = {
|
||||
region: {} as RegionInfo,
|
||||
items: [],
|
||||
total: 0,
|
||||
totalItems: 0,
|
||||
}
|
||||
|
||||
export const SessionCartProvider = ({
|
||||
initialState = defaultInitialState,
|
||||
children,
|
||||
}: SessionCartProviderProps) => {
|
||||
const [saved, save] = useLocalStorage(
|
||||
"medusa-session-cart",
|
||||
JSON.stringify(initialState)
|
||||
)
|
||||
|
||||
const [state, dispatch] = React.useReducer(reducer, JSON.parse(saved))
|
||||
|
||||
useEffect(() => {
|
||||
save(JSON.stringify(state))
|
||||
}, [state, save])
|
||||
|
||||
const setRegion = (region: RegionInfo) => {
|
||||
if (!isObject(region) || isEmpty(region)) {
|
||||
throw new Error("region must be a non-empty object")
|
||||
}
|
||||
|
||||
dispatch({ type: ACTION_TYPES.SET_REGION, payload: region })
|
||||
}
|
||||
|
||||
const getItem = (id: string) => {
|
||||
return state.items.find((item) => item.variant.id === id)
|
||||
}
|
||||
|
||||
const setItems = (items: Item[]) => {
|
||||
if (!isArray(items)) {
|
||||
throw new Error("items must be an array of items")
|
||||
}
|
||||
|
||||
dispatch({ type: ACTION_TYPES.SET_ITEMS, payload: items })
|
||||
}
|
||||
|
||||
const addItem = (item: Item) => {
|
||||
if (!isObject(item) || isEmpty(item)) {
|
||||
throw new Error("item must be a non-empty object")
|
||||
}
|
||||
|
||||
dispatch({ type: ACTION_TYPES.ADD_ITEM, payload: item })
|
||||
}
|
||||
|
||||
const updateItem = (id: string, item: Partial<Item>) => {
|
||||
dispatch({ type: ACTION_TYPES.UPDATE_ITEM, payload: { id, item } })
|
||||
}
|
||||
|
||||
const updateItemQuantity = (id: string, quantity: number) => {
|
||||
const item = getItem(id)
|
||||
if (!item) return
|
||||
|
||||
quantity = quantity <= 0 ? 1 : quantity
|
||||
|
||||
dispatch({
|
||||
type: ACTION_TYPES.UPDATE_ITEM,
|
||||
payload: {
|
||||
id,
|
||||
item: {
|
||||
...item,
|
||||
quantity: Math.min(item.variant.inventory_quantity, quantity),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const incrementItemQuantity = (id: string) => {
|
||||
const item = getItem(id)
|
||||
if (!item) return
|
||||
|
||||
dispatch({
|
||||
type: ACTION_TYPES.UPDATE_ITEM,
|
||||
payload: {
|
||||
id,
|
||||
item: {
|
||||
...item,
|
||||
quantity: Math.min(
|
||||
item.variant.inventory_quantity,
|
||||
item.quantity + 1
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const decrementItemQuantity = (id: string) => {
|
||||
const item = getItem(id)
|
||||
if (!item) return
|
||||
|
||||
dispatch({
|
||||
type: ACTION_TYPES.UPDATE_ITEM,
|
||||
payload: {
|
||||
id,
|
||||
item: { ...item, quantity: Math.max(0, item.quantity - 1) },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const removeItem = (id: string) => {
|
||||
dispatch({
|
||||
type: ACTION_TYPES.REMOVE_ITEM,
|
||||
payload: { id },
|
||||
})
|
||||
}
|
||||
|
||||
const clearItems = () => {
|
||||
dispatch({
|
||||
type: ACTION_TYPES.CLEAR_ITEMS,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<SessionCartContext.Provider
|
||||
value={{
|
||||
...state,
|
||||
setRegion,
|
||||
addItem,
|
||||
updateItem,
|
||||
updateItemQuantity,
|
||||
incrementItemQuantity,
|
||||
decrementItemQuantity,
|
||||
removeItem,
|
||||
getItem,
|
||||
setItems,
|
||||
clearItems,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SessionCartContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSessionCart = () => {
|
||||
const context = useContext(SessionCartContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSessionCart should be used as a child of SessionCartProvider"
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./queries"
|
||||
export * from "./mutations"
|
||||
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
StoreCartsRes,
|
||||
StoreCompleteCartRes,
|
||||
StorePostCartReq,
|
||||
StorePostCartsCartPaymentSessionReq,
|
||||
StorePostCartsCartPaymentSessionUpdateReq,
|
||||
StorePostCartsCartReq,
|
||||
StorePostCartsCartShippingMethodReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
|
||||
export const useCreateCart = (
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartReq | undefined
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data?: StorePostCartReq | undefined) => client.carts.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useUpdateCart = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCartsRes, Error, StorePostCartsCartReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartReq) => client.carts.update(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useCompleteCart = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCompleteCartRes, Error>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(() => client.carts.complete(cartId), options)
|
||||
}
|
||||
|
||||
export const useCreatePaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCartsRes, Error>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(() => client.carts.createPaymentSessions(cartId), options)
|
||||
}
|
||||
|
||||
export const useUpdatePaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
{ provider_id: string } & StorePostCartsCartPaymentSessionUpdateReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(
|
||||
data: { provider_id: string } & StorePostCartsCartPaymentSessionUpdateReq
|
||||
) => client.carts.updatePaymentSession(cartId, data.provider_id, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
type RefreshPaymentSessionMutationData = {
|
||||
provider_id: string
|
||||
}
|
||||
|
||||
export const useRefreshPaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
RefreshPaymentSessionMutationData
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ provider_id }: RefreshPaymentSessionMutationData) =>
|
||||
client.carts.refreshPaymentSession(cartId, provider_id),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
type SetPaymentSessionMutationData = { provider_id: string }
|
||||
|
||||
export const useSetPaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
SetPaymentSessionMutationData
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartPaymentSessionReq) =>
|
||||
client.carts.setPaymentSession(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useAddShippingMethodToCart = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartsCartShippingMethodReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartShippingMethodReq) =>
|
||||
client.carts.addShippingMethod(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
type DeletePaymentSessionMutationData = {
|
||||
provider_id: string
|
||||
}
|
||||
|
||||
export const useDeletePaymentSession = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
DeletePaymentSessionMutationData
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ provider_id }: DeletePaymentSessionMutationData) =>
|
||||
client.carts.deletePaymentSession(cartId, provider_id),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useStartCheckout = (
|
||||
options?: UseMutationOptions<StoreCartsRes["cart"], Error, StorePostCartReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const mutation = useMutation(async (data?: StorePostCartReq) => {
|
||||
const { cart } = await client.carts.create(data)
|
||||
const res = await client.carts.createPaymentSessions(cart.id)
|
||||
return res.cart
|
||||
}, options)
|
||||
|
||||
return mutation
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreCartsRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const CARTS_QUERY_KEY = `carts` as const
|
||||
|
||||
export const cartKeys = makeKeysFactory(CARTS_QUERY_KEY)
|
||||
type CartQueryKey = typeof cartKeys
|
||||
|
||||
export const useGetCart = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCartsRes>,
|
||||
Error,
|
||||
ReturnType<CartQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
cartKeys.detail(id),
|
||||
() => client.carts.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
StoreCollectionsListRes,
|
||||
StoreCollectionsRes,
|
||||
StoreGetCollectionsParams,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
import { UseQueryOptionsWrapper } from "./../../types"
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
|
||||
const COLLECTIONS_QUERY_KEY = `collections` as const
|
||||
|
||||
export const collectionKeys = makeKeysFactory(COLLECTIONS_QUERY_KEY)
|
||||
|
||||
type CollectionQueryKey = typeof collectionKeys
|
||||
|
||||
export const useCollection = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCollectionsRes>,
|
||||
Error,
|
||||
ReturnType<CollectionQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
collectionKeys.detail(id),
|
||||
() => client.collections.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCollections = (
|
||||
query?: StoreGetCollectionsParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCollectionsListRes>,
|
||||
Error,
|
||||
ReturnType<CollectionQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
collectionKeys.list(query),
|
||||
() => client.collections.list(query),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./queries"
|
||||
export * from "./mutations"
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
StoreCustomersRes,
|
||||
StorePostCustomersCustomerReq,
|
||||
StorePostCustomersReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts/medusa"
|
||||
|
||||
export const useCreateCustomer = (
|
||||
options?: UseMutationOptions<StoreCustomersRes, Error, StorePostCustomersReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCustomersReq) => client.customers.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useUpdateMe = (
|
||||
options?: UseMutationOptions<
|
||||
StoreCustomersRes,
|
||||
Error,
|
||||
{ id: string } & StorePostCustomersCustomerReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ id, ...data }: { id: string } & StorePostCustomersCustomerReq) =>
|
||||
client.customers.update(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
StoreCustomersListOrdersRes,
|
||||
StoreCustomersRes,
|
||||
StoreGetCustomersCustomerOrdersParams,
|
||||
} from "@medusajs/medusa"
|
||||
import { useQuery } from "react-query"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
|
||||
const CUSTOMERS_QUERY_KEY = `customers` as const
|
||||
|
||||
export const customerKeys = {
|
||||
...makeKeysFactory(CUSTOMERS_QUERY_KEY),
|
||||
orders: (id: string) => [...customerKeys.detail(id), "orders"] as const,
|
||||
}
|
||||
|
||||
type CustomerQueryKey = typeof customerKeys
|
||||
|
||||
export const useMeCustomer = (
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCustomersRes>,
|
||||
Error,
|
||||
ReturnType<CustomerQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
customerKeys.detail("me"),
|
||||
() => client.customers.retrieve(),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCustomerOrders = (
|
||||
query: StoreGetCustomersCustomerOrdersParams = { limit: 10, offset: 0 },
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreCustomersListOrdersRes>,
|
||||
Error,
|
||||
ReturnType<CustomerQueryKey["orders"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
customerKeys.orders("me"),
|
||||
() => client.customers.listOrders(query),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,29 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreGiftCardsRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const GIFT_CARDS_QUERY_KEY = `gift_cards` as const
|
||||
|
||||
export const giftCardKeys = makeKeysFactory(GIFT_CARDS_QUERY_KEY)
|
||||
|
||||
type GiftCardQueryKey = typeof giftCardKeys
|
||||
|
||||
export const useGiftCard = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreGiftCardsRes>,
|
||||
Error,
|
||||
ReturnType<GiftCardQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
giftCardKeys.detail(id),
|
||||
() => client.giftCards.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export * from "./products/"
|
||||
export * from "./carts/"
|
||||
export * from "./shipping-options/"
|
||||
export * from "./regions/"
|
||||
export * from "./return-reasons/"
|
||||
export * from "./swaps/"
|
||||
export * from "./carts/"
|
||||
export * from "./orders/"
|
||||
export * from "./customers/"
|
||||
export * from "./returns/"
|
||||
export * from "./gift-cards/"
|
||||
export * from "./line-items/"
|
||||
export * from "./collections"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./mutations"
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
StoreCartsRes,
|
||||
StorePostCartsCartLineItemsReq,
|
||||
StorePostCartsCartLineItemsItemReq,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
|
||||
export const useCreateLineItem = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartsCartLineItemsReq
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostCartsCartLineItemsReq) =>
|
||||
client.carts.lineItems.create(cartId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useUpdateLineItem = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<
|
||||
StoreCartsRes,
|
||||
Error,
|
||||
StorePostCartsCartLineItemsItemReq & { lineId: string }
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({
|
||||
lineId,
|
||||
...data
|
||||
}: StorePostCartsCartLineItemsItemReq & { lineId: string }) =>
|
||||
client.carts.lineItems.update(cartId, lineId, data),
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export const useDeleteLineItem = (
|
||||
cartId: string,
|
||||
options?: UseMutationOptions<StoreCartsRes, Error, { lineId: string }>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
({ lineId }: { lineId: string }) =>
|
||||
client.carts.lineItems.delete(cartId, lineId),
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,71 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreOrdersRes, StoreGetOrdersParams } from "@medusajs/medusa"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
|
||||
const ORDERS_QUERY_KEY = `orders` as const
|
||||
|
||||
export const orderKeys = {
|
||||
...makeKeysFactory<typeof ORDERS_QUERY_KEY, StoreGetOrdersParams>(
|
||||
ORDERS_QUERY_KEY
|
||||
),
|
||||
cart: (cartId: string) => [...orderKeys.details(), "cart", cartId] as const,
|
||||
}
|
||||
|
||||
type OrderQueryKey = typeof orderKeys
|
||||
|
||||
export const useOrder = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreOrdersRes>,
|
||||
Error,
|
||||
ReturnType<OrderQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
orderKeys.detail(id),
|
||||
() => client.orders.retrieve(id),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCartOrder = (
|
||||
cartId: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreOrdersRes>,
|
||||
Error,
|
||||
ReturnType<OrderQueryKey["cart"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
orderKeys.cart(cartId),
|
||||
() => client.orders.retrieveByCartId(cartId),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useOrderLookup = (
|
||||
query: StoreGetOrdersParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreOrdersRes>,
|
||||
Error,
|
||||
ReturnType<OrderQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
orderKeys.list(query),
|
||||
() => client.orders.lookupOrder(query),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import {
|
||||
StoreGetProductsParams,
|
||||
StoreProductsListRes,
|
||||
StoreProductsRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
|
||||
const PRODUCTS_QUERY_KEY = `products` as const
|
||||
|
||||
export const productKeys = makeKeysFactory<
|
||||
typeof PRODUCTS_QUERY_KEY,
|
||||
StoreGetProductsParams
|
||||
>(PRODUCTS_QUERY_KEY)
|
||||
type ProductQueryKey = typeof productKeys
|
||||
|
||||
export const useProducts = (
|
||||
query?: StoreGetProductsParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreProductsListRes>,
|
||||
Error,
|
||||
ReturnType<ProductQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
productKeys.list(query),
|
||||
() => client.products.list(query),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useProduct = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreProductsRes>,
|
||||
Error,
|
||||
ReturnType<ProductQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
productKeys.detail(id),
|
||||
() => client.products.retrieve(id),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,45 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { StoreRegionsRes, StoreRegionsListRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
|
||||
const REGIONS_QUERY_KEY = `regions` as const
|
||||
|
||||
const regionsKey = makeKeysFactory(REGIONS_QUERY_KEY)
|
||||
|
||||
type RegionQueryType = typeof regionsKey
|
||||
|
||||
export const useRegions = (
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreRegionsListRes>,
|
||||
Error,
|
||||
ReturnType<RegionQueryType["lists"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
regionsKey.lists(),
|
||||
() => client.regions.list(),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useRegion = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreRegionsRes>,
|
||||
Error,
|
||||
ReturnType<RegionQueryType["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
regionsKey.detail(id),
|
||||
() => client.regions.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,48 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import {
|
||||
StoreReturnReasonsListRes,
|
||||
StoreReturnReasonsRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const RETURNS_REASONS_QUERY_KEY = `return_reasons` as const
|
||||
|
||||
const returnReasonsKey = makeKeysFactory(RETURNS_REASONS_QUERY_KEY)
|
||||
|
||||
type ReturnReasonsQueryKey = typeof returnReasonsKey
|
||||
|
||||
export const useReturnReasons = (
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreReturnReasonsListRes>,
|
||||
Error,
|
||||
ReturnType<ReturnReasonsQueryKey["lists"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
returnReasonsKey.lists(),
|
||||
() => client.returnReasons.list(),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useReturnReason = (
|
||||
id: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreReturnReasonsRes>,
|
||||
Error,
|
||||
ReturnType<ReturnReasonsQueryKey["detail"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
returnReasonsKey.detail(id),
|
||||
() => client.returnReasons.retrieve(id),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./mutations"
|
||||
@@ -0,0 +1,13 @@
|
||||
import { StoreReturnsRes, StorePostReturnsReq } from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
|
||||
export const useCreateReturn = (
|
||||
options?: UseMutationOptions<StoreReturnsRes, Error, StorePostReturnsReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostReturnsReq) => client.returns.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./queries"
|
||||
@@ -0,0 +1,52 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import {
|
||||
StoreShippingOptionsListRes,
|
||||
StoreGetShippingOptionsParams,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
|
||||
const SHIPPING_OPTION_QUERY_KEY = `shipping_options` as const
|
||||
|
||||
const shippingOptionKey = {
|
||||
...makeKeysFactory(SHIPPING_OPTION_QUERY_KEY),
|
||||
cart: (cartId: string) => [...shippingOptionKey.all, "cart", cartId] as const,
|
||||
}
|
||||
|
||||
type ShippingOptionQueryKey = typeof shippingOptionKey
|
||||
|
||||
export const useShippingOptions = (
|
||||
query?: StoreGetShippingOptionsParams,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreShippingOptionsListRes>,
|
||||
Error,
|
||||
ReturnType<ShippingOptionQueryKey["list"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
shippingOptionKey.list(query),
|
||||
async () => client.shippingOptions.list(query),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
|
||||
export const useCartShippingOptions = (
|
||||
cartId: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreShippingOptionsListRes>,
|
||||
Error,
|
||||
ReturnType<ShippingOptionQueryKey["cart"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
shippingOptionKey.cart(cartId),
|
||||
async () => client.shippingOptions.listCartOptions(cartId),
|
||||
options
|
||||
)
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./queries"
|
||||
export * from "./mutations"
|
||||
@@ -0,0 +1,13 @@
|
||||
import { StoreSwapsRes, StorePostSwapsReq } from "@medusajs/medusa"
|
||||
import { useMutation, UseMutationOptions } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
|
||||
export const useCreateSwap = (
|
||||
options?: UseMutationOptions<StoreSwapsRes, Error, StorePostSwapsReq>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
return useMutation(
|
||||
(data: StorePostSwapsReq) => client.swaps.create(data),
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { makeKeysFactory } from "./../utils/index"
|
||||
import { StoreSwapsRes } from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useQuery } from "react-query"
|
||||
import { useMedusa } from "../../contexts"
|
||||
import { UseQueryOptionsWrapper } from "../../types"
|
||||
|
||||
const SWAPS_QUERY_KEY = `swaps` as const
|
||||
|
||||
const swapKey = {
|
||||
...makeKeysFactory(SWAPS_QUERY_KEY),
|
||||
cart: (cartId: string) => [...swapKey.all, "cart", cartId] as const,
|
||||
}
|
||||
|
||||
type SwapQueryKey = typeof swapKey
|
||||
|
||||
export const useCartSwap = (
|
||||
cartId: string,
|
||||
options?: UseQueryOptionsWrapper<
|
||||
Response<StoreSwapsRes>,
|
||||
Error,
|
||||
ReturnType<SwapQueryKey["cart"]>
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const { data, ...rest } = useQuery(
|
||||
swapKey.cart(cartId),
|
||||
() => client.swaps.retrieveByCartId(cartId),
|
||||
options
|
||||
)
|
||||
|
||||
return { ...data, ...rest } as const
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import * as React from "react"
|
||||
|
||||
type TQueryKey<TKey, TListQuery = any, TDetailQuery = string> = {
|
||||
all: [TKey]
|
||||
lists: () => [...TQueryKey<TKey>["all"], "list"]
|
||||
list: (
|
||||
query?: TListQuery
|
||||
) => [
|
||||
...ReturnType<TQueryKey<TKey>["lists"]>,
|
||||
{ query: TListQuery | undefined }
|
||||
]
|
||||
details: () => [...TQueryKey<TKey>["all"], "detail"]
|
||||
detail: (
|
||||
id: TDetailQuery
|
||||
) => [...ReturnType<TQueryKey<TKey>["details"]>, TDetailQuery]
|
||||
}
|
||||
|
||||
export const makeKeysFactory = <
|
||||
T,
|
||||
TListQueryType = any,
|
||||
TDetailQueryType = string
|
||||
>(
|
||||
globalKey: T
|
||||
) => {
|
||||
const queryKeyFactory: TQueryKey<T, TListQueryType, TDetailQueryType> = {
|
||||
all: [globalKey],
|
||||
lists: () => [...queryKeyFactory.all, "list"],
|
||||
list: (query?: TListQueryType) => [...queryKeyFactory.lists(), { query }],
|
||||
details: () => [...queryKeyFactory.all, "detail"],
|
||||
detail: (id: TDetailQueryType) => [...queryKeyFactory.details(), id],
|
||||
}
|
||||
return queryKeyFactory
|
||||
}
|
||||
|
||||
export const useLocalStorage = (key: string, initialState: string) => {
|
||||
const [item, setItem] = React.useState(() => {
|
||||
try {
|
||||
const item =
|
||||
typeof window !== "undefined" && window.localStorage.getItem(key)
|
||||
|
||||
return item || initialState
|
||||
} catch (err) {
|
||||
return initialState
|
||||
}
|
||||
})
|
||||
|
||||
const save = (data: string) => {
|
||||
setItem(data)
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(key, data)
|
||||
}
|
||||
}
|
||||
|
||||
const remove = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
return [item, save, remove] as const
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./contexts"
|
||||
export * from "./hooks/"
|
||||
export * from "./utils/"
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
Region,
|
||||
ProductVariant as ProductVariantEntity,
|
||||
StoreCartsRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { QueryKey, UseQueryOptions } from "react-query"
|
||||
|
||||
export type UseQueryOptionsWrapper<
|
||||
// Return type of queryFn
|
||||
TQueryFn = unknown,
|
||||
// Type thrown in case the queryFn rejects
|
||||
E = Error,
|
||||
// Query key type
|
||||
TQueryKey extends QueryKey = QueryKey
|
||||
> = Omit<
|
||||
UseQueryOptions<TQueryFn, E, TQueryFn, TQueryKey>,
|
||||
"queryKey" | "queryFn" | "select" | "refetchInterval"
|
||||
>
|
||||
|
||||
// Choose only a subset of the type Region to allow for some flexibility
|
||||
export type RegionInfo = Pick<Region, "currency_code" | "tax_code" | "tax_rate">
|
||||
export type ProductVariant = ConvertDateToString<
|
||||
Omit<ProductVariantEntity, "beforeInsert">
|
||||
>
|
||||
export type ProductVariantInfo = Pick<ProductVariant, "prices">
|
||||
|
||||
type ConvertDateToString<T extends {}> = {
|
||||
[P in keyof T]: T[P] extends Date ? Date | string : T[P]
|
||||
}
|
||||
|
||||
export type Cart = StoreCartsRes["cart"]
|
||||
@@ -0,0 +1,168 @@
|
||||
import { isEmpty } from "lodash"
|
||||
import { RegionInfo, ProductVariantInfo } from "../types"
|
||||
|
||||
type FormatVariantPriceParams = {
|
||||
variant: ProductVariantInfo
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a product variant and a region, and converts the variant's price to a localized decimal format
|
||||
*/
|
||||
export const formatVariantPrice = ({
|
||||
variant,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
...rest
|
||||
}: FormatVariantPriceParams) => {
|
||||
const amount = computeVariantPrice({ variant, region, includeTaxes })
|
||||
|
||||
return convertToLocale({
|
||||
amount,
|
||||
currency_code: region?.currency_code,
|
||||
...rest,
|
||||
})
|
||||
}
|
||||
|
||||
type ComputeVariantPriceParams = {
|
||||
variant: ProductVariantInfo
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a product variant and region, and returns the variant price as a decimal number
|
||||
* @param params.variant - product variant
|
||||
* @param params.region - region
|
||||
* @param params.includeTaxes - whether to include taxes or not
|
||||
*/
|
||||
export const computeVariantPrice = ({
|
||||
variant,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
}: ComputeVariantPriceParams) => {
|
||||
const amount = getVariantPrice(variant, region)
|
||||
|
||||
return computeAmount({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the price amount correspoding to the region selected
|
||||
* @param variant - the product variant
|
||||
* @param region - the region
|
||||
* @returns - the price's amount
|
||||
*/
|
||||
export const getVariantPrice = (
|
||||
variant: ProductVariantInfo,
|
||||
region: RegionInfo
|
||||
) => {
|
||||
let price = variant?.prices?.find(
|
||||
(p) =>
|
||||
p.currency_code.toLowerCase() === region?.currency_code?.toLowerCase()
|
||||
)
|
||||
|
||||
return price?.amount || 0
|
||||
}
|
||||
|
||||
type ComputeAmountParams = {
|
||||
amount: number
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an amount, a region, and returns the amount as a decimal including or excluding taxes
|
||||
*/
|
||||
export const computeAmount = ({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
}: ComputeAmountParams) => {
|
||||
const toDecimal = convertToDecimal(amount, region)
|
||||
|
||||
const taxRate = includeTaxes ? getTaxRate(region) : 0
|
||||
|
||||
const amountWithTaxes = toDecimal * (1 + taxRate)
|
||||
|
||||
return amountWithTaxes
|
||||
}
|
||||
|
||||
type FormatAmountParams = {
|
||||
amount: number
|
||||
region: RegionInfo
|
||||
includeTaxes?: boolean
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an amount and a region, and converts the amount to a localized decimal format
|
||||
*/
|
||||
export const formatAmount = ({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes = true,
|
||||
...rest
|
||||
}: FormatAmountParams) => {
|
||||
const taxAwareAmount = computeAmount({
|
||||
amount,
|
||||
region,
|
||||
includeTaxes,
|
||||
})
|
||||
return convertToLocale({
|
||||
amount: taxAwareAmount,
|
||||
currency_code: region.currency_code,
|
||||
...rest,
|
||||
})
|
||||
}
|
||||
|
||||
// we should probably add a more extensive list
|
||||
const noDivisionCurrencies = ["krw", "jpy", "vnd"]
|
||||
|
||||
const convertToDecimal = (amount: number, region: RegionInfo) => {
|
||||
const divisor = noDivisionCurrencies.includes(
|
||||
region?.currency_code?.toLowerCase()
|
||||
)
|
||||
? 1
|
||||
: 100
|
||||
|
||||
return Math.floor(amount) / divisor
|
||||
}
|
||||
|
||||
const getTaxRate = (region?: RegionInfo) => {
|
||||
return region && !isEmpty(region) ? region?.tax_rate / 100 : 0
|
||||
}
|
||||
|
||||
const convertToLocale = ({
|
||||
amount,
|
||||
currency_code,
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
locale = "en-US",
|
||||
}: ConvertToLocaleParams) => {
|
||||
return currency_code && !isEmpty(currency_code)
|
||||
? new Intl.NumberFormat(locale, {
|
||||
style: "currency",
|
||||
currency: currency_code,
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
}).format(amount)
|
||||
: amount.toString()
|
||||
}
|
||||
|
||||
type ConvertToLocaleParams = {
|
||||
amount: number
|
||||
currency_code: string
|
||||
minimumFractionDigits?: number
|
||||
maximumFractionDigits?: number
|
||||
locale?: string
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useGetCart } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const Cart = ({ showHookData, id }) => {
|
||||
const data = useGetCart(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Cart: {id}</h3>
|
||||
<div>
|
||||
{data?.cart?.email} - {data?.cart?.total}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Carts",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; id: string }) => (
|
||||
<Cart {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "cart id",
|
||||
defaultValue: "cart_...",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useCollection, useCollections } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const Collections = ({ showHookData, limit, offset }) => {
|
||||
const data = useCollections({ limit, offset })
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Collections</h3>
|
||||
<ul>
|
||||
{data?.collections?.map((Collection) => (
|
||||
<li key={Collection.id}>{Collection.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const Collection = ({ showHookData, id }) => {
|
||||
const data = useCollection(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Collection: {id}</h3>
|
||||
<div>
|
||||
{data?.collection?.title} - {data?.collection?.handle}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Collections",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const List = (args: {
|
||||
limit: number
|
||||
offset: number
|
||||
showHookData: boolean
|
||||
}) => <Collections {...args} />
|
||||
|
||||
List.argTypes = {
|
||||
limit: {
|
||||
control: {
|
||||
type: "number",
|
||||
},
|
||||
name: "limit",
|
||||
defaultValue: 5,
|
||||
},
|
||||
offset: {
|
||||
control: {
|
||||
type: "number",
|
||||
},
|
||||
defaultValue: 0,
|
||||
},
|
||||
}
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; id: string }) => (
|
||||
<Collection {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "Collection id",
|
||||
defaultValue: "pcol_",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMeCustomer, useCustomerOrders } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const CustomerOrders = ({ showHookData, id }) => {
|
||||
const data = useCustomerOrders(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Customer Orders</h3>
|
||||
<ul>
|
||||
{data?.orders?.map((order) => (
|
||||
<li key={order.id}>price: {order.total}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const Customer = ({ showHookData }) => {
|
||||
const data = useMeCustomer()
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Customer: </h3>
|
||||
<div>
|
||||
{data?.customer?.first_name} {data?.customer?.last_name} -{" "}
|
||||
{data?.customer?.email}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Customers",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const ListOrders = (args: { showHookData: boolean; id: string }) => (
|
||||
<CustomerOrders {...args} />
|
||||
)
|
||||
|
||||
ListOrders.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "customer id",
|
||||
defaultValue: "reg_",
|
||||
},
|
||||
}
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean }) => (
|
||||
<Customer {...args} />
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useCartOrder, useOrder, useProduct, useProducts } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const CartOrderComponent = ({ showHookData, cartId }) => {
|
||||
const data = useCartOrder(cartId)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Order from cart with id: {cartId}</h3>
|
||||
<div>
|
||||
{data?.order?.email} - {data?.order?.currency_code}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const Order = ({ showHookData, id }) => {
|
||||
const data = useOrder(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Order: {id}</h3>
|
||||
<div>
|
||||
{data?.order?.email} - {data?.order?.currency_code}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Orders",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const CartOrder = (args: { cartId: string; showHookData: boolean }) => (
|
||||
<CartOrderComponent {...args} />
|
||||
)
|
||||
|
||||
CartOrder.argTypes = {
|
||||
cartId: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "cart id",
|
||||
defaultValue: "cart_...",
|
||||
},
|
||||
}
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; id: string }) => (
|
||||
<Order {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "order id",
|
||||
defaultValue: "order_...",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useProduct, useProducts } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const Products = ({ showHookData, limit, offset }) => {
|
||||
const data = useProducts({ limit, offset })
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Products</h3>
|
||||
<ul>
|
||||
{data?.products?.map((product) => (
|
||||
<li key={product.id}>{product.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const Product = ({ showHookData, id }) => {
|
||||
const data = useProduct(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Product: {id}</h3>
|
||||
<div>
|
||||
{data?.product?.title} - {data?.product?.handle}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Products",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const List = (args: {
|
||||
limit: number
|
||||
offset: number
|
||||
showHookData: boolean
|
||||
}) => <Products {...args} />
|
||||
|
||||
List.argTypes = {
|
||||
limit: {
|
||||
control: {
|
||||
type: "number",
|
||||
},
|
||||
name: "limit",
|
||||
defaultValue: 5,
|
||||
},
|
||||
offset: {
|
||||
control: {
|
||||
type: "number",
|
||||
},
|
||||
defaultValue: 0,
|
||||
},
|
||||
}
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; id: string }) => (
|
||||
<Product {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "product id",
|
||||
defaultValue: "prod_",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useRegion, useRegions } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const Regions = ({ showHookData }) => {
|
||||
const data = useRegions()
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Regions</h3>
|
||||
<ul>
|
||||
{data?.regions?.map((region) => (
|
||||
<li key={region.id}>{region.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const Region = ({ showHookData, id }) => {
|
||||
const data = useRegion(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Region: {id}</h3>
|
||||
<div>
|
||||
{data?.region?.name} - {data?.region?.currency_code}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Regions",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const List = (args: { showHookData: boolean }) => <Regions {...args} />
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; id: string }) => (
|
||||
<Region {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "region id",
|
||||
defaultValue: "reg_",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useReturnReason, useReturnReasons } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const ReturnReasons = ({ showHookData }) => {
|
||||
const data = useReturnReasons()
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Return Reasons</h3>
|
||||
<ul>
|
||||
{data?.return_reasons?.map((rr) => (
|
||||
<li key={rr.id}>
|
||||
{rr.label} - {rr.value}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const ReturnReason = ({ showHookData, id }) => {
|
||||
const data = useReturnReason(id)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Return Reason: {id}</h3>
|
||||
<div>
|
||||
{data?.return_reason?.label} - {data?.return_reason?.value}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Return Reasons",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const List = (args: { showHookData: boolean }) => (
|
||||
<ReturnReasons {...args} />
|
||||
)
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; id: string }) => (
|
||||
<ReturnReason {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
id: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "return reason id",
|
||||
defaultValue: "rr_...",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useCartShippingOptions, useShippingOptions } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const ShippingOptions = ({ showHookData, cartId }) => {
|
||||
const data = useShippingOptions(cartId)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Shipping Options</h3>
|
||||
<ul>
|
||||
{data?.shipping_options?.map((so) => (
|
||||
<li key={so.id}>
|
||||
{so.name} - {so.amount}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const CartShippingOptions = ({ showHookData, cartId }) => {
|
||||
const data = useCartShippingOptions(cartId)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Cart shipping options: {cartId}</h3>
|
||||
<ul>
|
||||
{data?.shipping_options?.map((so) => (
|
||||
<li key={so.id}>
|
||||
{so.name} - {so.amount}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Shipping Options",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const List = (args: { showHookData: boolean; cartId: string }) => (
|
||||
<ShippingOptions {...args} />
|
||||
)
|
||||
|
||||
List.argTypes = {
|
||||
cartId: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "cart id",
|
||||
defaultValue: "cart_...",
|
||||
},
|
||||
}
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; cartId: string }) => (
|
||||
<CartShippingOptions {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
cartId: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "cart id",
|
||||
defaultValue: "cart_...",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Meta } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useCartSwap } from "../src"
|
||||
import Layout from "./components/Layout"
|
||||
|
||||
const Swap = ({ showHookData, cartId }) => {
|
||||
const data = useCartSwap(cartId)
|
||||
return (
|
||||
<Layout showHookData={showHookData} data={data}>
|
||||
<h3>Cart swap: {cartId}</h3>
|
||||
<div>
|
||||
{data?.swap?.order_id} - {data?.swap?.cart_id}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: "Swaps",
|
||||
argTypes: {
|
||||
showHookData: {
|
||||
name: "Show hook data",
|
||||
description:
|
||||
"Whether or not story should display JSON of data returned from hook",
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: { expanded: true },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
|
||||
export const GetOne = (args: { showHookData: boolean; cartId: string }) => (
|
||||
<Swap {...args} />
|
||||
)
|
||||
|
||||
GetOne.argTypes = {
|
||||
cartId: {
|
||||
control: {
|
||||
type: "text",
|
||||
},
|
||||
name: "cart id",
|
||||
defaultValue: "cart_...",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import ReactJson from "react-json-view"
|
||||
|
||||
const Layout = ({ children, showHookData, data }) => {
|
||||
return (
|
||||
<div style={{ display: "grid", gridTemplateColumns: "50% 50%" }}>
|
||||
<div>{children}</div>
|
||||
<div style={{ overflowX: "auto" }}>
|
||||
<h3>Raw</h3>
|
||||
{showHookData && <ReactJson src={data} collapsed={true} />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
@@ -0,0 +1,196 @@
|
||||
import { useCart } from "../../src"
|
||||
import { act, renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../mocks/data"
|
||||
import { createCartWrapper } from "../utils"
|
||||
import { Cart } from "../../src/types"
|
||||
|
||||
describe("useBag hook", () => {
|
||||
describe("sets a cart", () => {
|
||||
test("success", async () => {
|
||||
const { result } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
})
|
||||
const { setCart } = result.current
|
||||
|
||||
act(() => {
|
||||
setCart(fixtures.get("cart") as unknown as Cart)
|
||||
})
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual(fixtures.get("cart"))
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createCart", () => {
|
||||
test("creates a cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
})
|
||||
const { createCart } = result.current
|
||||
|
||||
act(() => {
|
||||
createCart.mutate({})
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.createCart.isSuccess)
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual(fixtures.get("cart"))
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("startCheckout", () => {
|
||||
test("creates a payment session and updates the cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
initialProps: {
|
||||
initialCartState: {
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
} as unknown as Cart,
|
||||
},
|
||||
})
|
||||
const { startCheckout } = result.current
|
||||
|
||||
act(() => {
|
||||
startCheckout.mutate()
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.startCheckout.isSuccess)
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateCart", () => {
|
||||
test("updates the cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
initialProps: {
|
||||
initialCartState: {
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
} as unknown as Cart,
|
||||
},
|
||||
})
|
||||
const { updateCart } = result.current
|
||||
|
||||
act(() => {
|
||||
updateCart.mutate({
|
||||
email: "zak@test.com",
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.updateCart.isSuccess)
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
email: "zak@test.com",
|
||||
})
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("addShippingMethod", () => {
|
||||
test("adds a shipping method and updates the cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
initialProps: {
|
||||
initialCartState: {
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
} as unknown as Cart,
|
||||
},
|
||||
})
|
||||
const { addShippingMethod } = result.current
|
||||
|
||||
act(() => {
|
||||
addShippingMethod.mutate({
|
||||
option_id: "test-option",
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.addShippingMethod.isSuccess)
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("pay", () => {
|
||||
test("sets a payment session and updates the cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
initialProps: {
|
||||
initialCartState: {
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
} as unknown as Cart,
|
||||
},
|
||||
})
|
||||
const { pay } = result.current
|
||||
|
||||
act(() => {
|
||||
pay.mutate({
|
||||
provider_id: "test-provider",
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.pay.isSuccess)
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("completeCheckout", () => {
|
||||
test("calls complete cart, does not update the cart, and returns an order", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCart(), {
|
||||
wrapper: createCartWrapper(),
|
||||
initialProps: {
|
||||
initialCartState: fixtures.get("cart") as unknown as Cart,
|
||||
},
|
||||
})
|
||||
const { completeCheckout } = result.current
|
||||
|
||||
act(() => {
|
||||
completeCheckout.mutate()
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.completeCheckout.isSuccess)
|
||||
|
||||
const { cart, totalItems } = result.current
|
||||
|
||||
expect(cart).toEqual(fixtures.get("cart"))
|
||||
expect(totalItems).toEqual(0)
|
||||
|
||||
expect(result.current.completeCheckout.data.type).toEqual("order")
|
||||
expect(result.current.completeCheckout.data.data).toEqual(
|
||||
fixtures.get("order")
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,203 @@
|
||||
import {
|
||||
useCreateCart,
|
||||
useUpdateCart,
|
||||
useCompleteCart,
|
||||
useCreatePaymentSession,
|
||||
useUpdatePaymentSession,
|
||||
useRefreshPaymentSession,
|
||||
useSetPaymentSession,
|
||||
useDeletePaymentSession,
|
||||
useAddShippingMethodToCart,
|
||||
} from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCreateCart hook", () => {
|
||||
test("creates a cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCreateCart(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate({})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual(fixtures.get("cart"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("useUpdateCart hook", () => {
|
||||
test("updates a cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useUpdateCart("some-cart"), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate({
|
||||
email: "new@email.com",
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "some-cart",
|
||||
email: "new@email.com",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useCompleteCart hook", () => {
|
||||
test("completes a cart", async () => {
|
||||
const { result, waitFor } = renderHook(() => useCompleteCart("test-cart"), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate()
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.type).toEqual("order")
|
||||
expect(result.current.data.data).toEqual(fixtures.get("order"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("useCreatePaymentSession hook", () => {
|
||||
test("creates a payment session", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useCreatePaymentSession("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate()
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useUpdatePaymentSession hook", () => {
|
||||
test("updates a payment session", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useUpdatePaymentSession("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate({
|
||||
data: {},
|
||||
provider_id: "stripe",
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useRefreshPaymentSession hook", () => {
|
||||
test("refreshes a payment session", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useRefreshPaymentSession("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate({
|
||||
provider_id: "stripe",
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useSetPaymentSession hook", () => {
|
||||
test("sets a payment session", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useSetPaymentSession("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate({
|
||||
provider_id: "stripe",
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useDeletePaymentSession hook", () => {
|
||||
test("deletes a payment session", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useDeletePaymentSession("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate({
|
||||
provider_id: "stripe",
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useAddShippingMethodToCart hook", () => {
|
||||
test("adds a shipping method to a cart", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useAddShippingMethodToCart("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate({
|
||||
option_id: "test-option",
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual({
|
||||
...fixtures.get("cart"),
|
||||
id: "test-cart",
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useGetCart } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useGetCart hook", () => {
|
||||
test("returns a cart", async () => {
|
||||
const cart = fixtures.get("cart")
|
||||
const { result, waitFor } = renderHook(() => useGetCart(cart.id), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.cart).toEqual(cart)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useCollections, useCollection } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCollections hook", () => {
|
||||
test("returns a list of collections", async () => {
|
||||
const collections = fixtures.list("product_collection")
|
||||
const { result, waitFor } = renderHook(() => useCollections(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.collections).toEqual(collections)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useCollection hook", () => {
|
||||
test("returns a collection", async () => {
|
||||
const collection = fixtures.get("product_collection")
|
||||
const { result, waitFor } = renderHook(() => useCollection(collection.id), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.collection).toEqual(collection)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useCreateCustomer, useUpdateMe } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCreateCustomer hook", () => {
|
||||
test("creates a new customer", async () => {
|
||||
const customer = {
|
||||
first_name: "john",
|
||||
last_name: "wick",
|
||||
email: "johnwick@medusajs.com",
|
||||
password: "supersecret",
|
||||
phone: "111111",
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(() => useCreateCustomer(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate(customer)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.customer).toEqual({
|
||||
...fixtures.get("customer"),
|
||||
...customer,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useUpdateMe hook", () => {
|
||||
test("updates current customer", async () => {
|
||||
const customer = {
|
||||
first_name: "lebron",
|
||||
last_name: "james",
|
||||
email: "lebronjames@medusajs.com",
|
||||
password: "supersecret",
|
||||
phone: "111111",
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(() => useUpdateMe(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate({
|
||||
id: "cus_test",
|
||||
...customer,
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.customer).toEqual({
|
||||
...fixtures.get("customer"),
|
||||
...customer,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,89 @@
|
||||
import { rest } from "msw"
|
||||
import { server } from "./../../../mocks/server"
|
||||
import { useMeCustomer, useCustomerOrders } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useMeCustomer hook", () => {
|
||||
test("returns customer", async () => {
|
||||
const customer = fixtures.get("customer")
|
||||
const { result, waitFor } = renderHook(() => useMeCustomer(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.customer).toEqual(customer)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useCustomerOrders hook", () => {
|
||||
test("returns customer's orders", async () => {
|
||||
const orders = fixtures.list("order", 5)
|
||||
|
||||
const { result, waitFor } = renderHook(() => useCustomerOrders(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.orders).toEqual(orders)
|
||||
expect(result.current.limit).toEqual(5)
|
||||
expect(result.current.offset).toEqual(0)
|
||||
})
|
||||
|
||||
test("propagates query params and returns customer's orders", async () => {
|
||||
const orders = fixtures.list("order")
|
||||
|
||||
server.use(
|
||||
rest.get("/store/customers/me/orders", (req, res, ctx) => {
|
||||
const limit = req.url.searchParams.get("limit")
|
||||
const offset = req.url.searchParams.get("offset")
|
||||
const expand = req.url.searchParams.get("expand")
|
||||
const fields = req.url.searchParams.get("fields")
|
||||
expect({
|
||||
limit,
|
||||
offset,
|
||||
expand,
|
||||
fields,
|
||||
}).toEqual({
|
||||
limit: "2",
|
||||
offset: "5",
|
||||
expand: "relation_1,relation_2",
|
||||
fields: "field_1,field_2",
|
||||
})
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
orders,
|
||||
limit: 2,
|
||||
offset: 5,
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() =>
|
||||
useCustomerOrders({
|
||||
limit: 2,
|
||||
offset: 5,
|
||||
expand: "relation_1,relation_2",
|
||||
fields: "field_1,field_2",
|
||||
}),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.orders).toEqual(orders)
|
||||
expect(result.current.limit).toEqual(2)
|
||||
expect(result.current.offset).toEqual(5)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useGiftCard } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useGiftCard hook", () => {
|
||||
test("returns a gift card", async () => {
|
||||
const giftCard = fixtures.get("gift_card")
|
||||
const { result, waitFor } = renderHook(() => useGiftCard(giftCard.id), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.gift_card).toEqual(giftCard)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
useCreateLineItem,
|
||||
useUpdateLineItem,
|
||||
useDeleteLineItem,
|
||||
} from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCreateLineItem hook", () => {
|
||||
test("creates a line item", async () => {
|
||||
const lineItem = {
|
||||
variant_id: "test-variant",
|
||||
quantity: 1,
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useCreateLineItem("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate(lineItem)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
...lineItem,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useUpdateLineItem hook", () => {
|
||||
test("updates a line item", async () => {
|
||||
const lineItem = {
|
||||
lineId: "some-item-id",
|
||||
quantity: 3,
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useUpdateLineItem("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate(lineItem)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: lineItem.lineId,
|
||||
quantity: lineItem.quantity,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useDeleteLineItem hook", () => {
|
||||
test("deletes a line item", async () => {
|
||||
const lineItem = {
|
||||
lineId: "some-item-id",
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useDeleteLineItem("test-cart"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate(lineItem)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.cart).toEqual(fixtures.get("cart"))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useOrderLookup } from "./../../../src/hooks/orders/queries"
|
||||
import { rest } from "msw"
|
||||
import { server } from "./../../../mocks/server"
|
||||
import { useCartOrder, useOrder } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useOrder hook", () => {
|
||||
test("returns an order", async () => {
|
||||
const order = fixtures.get("order")
|
||||
const { result, waitFor } = renderHook(() => useOrder(order.id), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.order).toEqual(order)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useCartOrder hook", () => {
|
||||
test("returns a cart order", async () => {
|
||||
const order = fixtures.get("order")
|
||||
const { result, waitFor } = renderHook(() => useCartOrder("test_cart"), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.order).toEqual(order)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useOrderLookup hook", () => {
|
||||
test("propagates the query params and returns an order", async () => {
|
||||
const order = fixtures.get("order")
|
||||
const displayId = 400,
|
||||
emailParam = "customer@test.com"
|
||||
|
||||
server.use(
|
||||
rest.get("/store/orders", (req, res, ctx) => {
|
||||
const display_id = req.url.searchParams.get("display_id")
|
||||
const email = req.url.searchParams.get("email")
|
||||
expect({
|
||||
display_id,
|
||||
email,
|
||||
}).toEqual({
|
||||
email: emailParam,
|
||||
display_id: displayId.toString(),
|
||||
})
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
order,
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() =>
|
||||
useOrderLookup({
|
||||
display_id: displayId,
|
||||
email: emailParam,
|
||||
}),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.order).toEqual(order)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,53 @@
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { useProducts, useProduct } from "../../../src"
|
||||
import { createWrapper } from "../../utils"
|
||||
import { fixtures } from "../../../mocks/data/index"
|
||||
|
||||
describe("useProducts hook", () => {
|
||||
test("gets a list of products", async () => {
|
||||
const { result, waitFor } = renderHook(() => useProducts(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.products).toEqual(fixtures.list("product"))
|
||||
})
|
||||
|
||||
test("gets a list of products based on limit and offset", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() =>
|
||||
useProducts({
|
||||
limit: 2,
|
||||
offset: 5,
|
||||
}),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.products).toEqual(fixtures.list("product"))
|
||||
expect(result.current.limit).toEqual(2)
|
||||
expect(result.current.offset).toEqual(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useProducts hook", () => {
|
||||
test("success", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useProduct("prod_01F0YESHQ27Y31CAMD0NV6W9YP"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.product).toEqual(fixtures.get("product"))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
import { useRegion, useRegions } from "../../../src/"
|
||||
|
||||
describe("useRegions hook", () => {
|
||||
test("success", async () => {
|
||||
const regions = fixtures.list("region")
|
||||
const { result, waitFor } = renderHook(() => useRegions(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.regions).toEqual(regions)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useRegion hook", () => {
|
||||
test("success", async () => {
|
||||
const region = fixtures.get("region")
|
||||
const { result, waitFor } = renderHook(() => useRegion(region.id), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.region).toEqual(region)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
import { useReturnReason, useReturnReasons } from "../../../src/"
|
||||
|
||||
describe("useReturnReasons hook", () => {
|
||||
test("returns a list of return reasons", async () => {
|
||||
const return_reasons = fixtures.list("return_reason")
|
||||
const { result, waitFor } = renderHook(() => useReturnReasons(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.return_reasons).toEqual(return_reasons)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useReturnReason hook", () => {
|
||||
test("returns a return reason", async () => {
|
||||
const return_reason = fixtures.get("return_reason")
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useReturnReason(return_reason.id),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.return_reason).toEqual(return_reason)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { useCreateReturn } from "../../../src"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCreateReturn hook", () => {
|
||||
test("creates a return", async () => {
|
||||
const ret = {
|
||||
order_id: "order_38",
|
||||
items: [
|
||||
{
|
||||
item_id: "test-item",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(() => useCreateReturn(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate(ret)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.return).toEqual(
|
||||
expect.objectContaining({
|
||||
...fixtures.get("return"),
|
||||
order_id: ret.order_id,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,83 @@
|
||||
import { rest } from "msw"
|
||||
import { server } from "./../../../mocks/server"
|
||||
import { useShippingOptions, useCartShippingOptions } from "../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useShippingOptions hook", () => {
|
||||
test("returns a list of shipping options", async () => {
|
||||
const shippingOptions = fixtures.list("shipping_option", 5)
|
||||
const { result, waitFor } = renderHook(() => useShippingOptions(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.shipping_options).toEqual(shippingOptions)
|
||||
})
|
||||
|
||||
test("when shipping options params are provided, then they should be sent as query params", async () => {
|
||||
const shippingOptions = fixtures.list("shipping_option")
|
||||
|
||||
server.use(
|
||||
rest.get("/store/shipping-options/", (req, res, ctx) => {
|
||||
const product_ids = req.url.searchParams.get("product_ids")
|
||||
const is_return = req.url.searchParams.get("is_return")
|
||||
const region_id = req.url.searchParams.get("region_id")
|
||||
|
||||
expect({
|
||||
product_ids,
|
||||
is_return,
|
||||
region_id,
|
||||
}).toEqual({
|
||||
product_ids: "1,2,3",
|
||||
is_return: "false",
|
||||
region_id: "test-region",
|
||||
})
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
shipping_options: fixtures.list("shipping_option"),
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
const { result, waitFor } = renderHook(
|
||||
() =>
|
||||
useShippingOptions({
|
||||
product_ids: "1,2,3",
|
||||
is_return: "false",
|
||||
region_id: "test-region",
|
||||
}),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.shipping_options).toEqual(shippingOptions)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useCartShippingOptions hook", () => {
|
||||
test("success", async () => {
|
||||
const cartShippingOptions = fixtures.list("shipping_option")
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useCartShippingOptions("cart_test"),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.shipping_options).toEqual(cartShippingOptions)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import { useCreateSwap } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCreateSwap hook", () => {
|
||||
test("creates a return", async () => {
|
||||
const swap = {
|
||||
order_id: "order_test",
|
||||
additional_items: [
|
||||
{
|
||||
variant_id: "new-item",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
return_items: [
|
||||
{
|
||||
item_id: "return-item",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { result, waitFor } = renderHook(() => useCreateSwap(), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
result.current.mutate(swap)
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data.swap).toEqual(
|
||||
expect.objectContaining({
|
||||
...fixtures.get("swap"),
|
||||
order_id: swap.order_id,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useCartSwap } from "./../../../src/"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../../mocks/data"
|
||||
import { createWrapper } from "../../utils"
|
||||
|
||||
describe("useCartSwap hook", () => {
|
||||
test("returns a swap", async () => {
|
||||
const swap = fixtures.get("swap")
|
||||
const { result, waitFor } = renderHook(() => useCartSwap("cart_test"), {
|
||||
wrapper: createWrapper(),
|
||||
})
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.response.status).toEqual(200)
|
||||
expect(result.current.swap).toEqual(swap)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,322 @@
|
||||
import { generateCartState } from "../../src/contexts/session-cart"
|
||||
import { ProductVariant } from "@medusajs/medusa"
|
||||
import { useSessionCart } from "../../src"
|
||||
import { act, renderHook } from "@testing-library/react-hooks"
|
||||
import { fixtures } from "../../mocks/data"
|
||||
import { createSessionCartWrapper } from "../utils"
|
||||
|
||||
const initialSessionCartState = {
|
||||
region: fixtures.get("region"),
|
||||
totalItems: 0,
|
||||
total: 0,
|
||||
items: [],
|
||||
}
|
||||
|
||||
describe("useSessionCart hook", () => {
|
||||
describe("sets a region", () => {
|
||||
test("success", async () => {
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
})
|
||||
const { setRegion } = result.current
|
||||
|
||||
act(() => {
|
||||
setRegion(fixtures.get("region"))
|
||||
})
|
||||
|
||||
const { region, total, totalItems } = result.current
|
||||
|
||||
expect(region).toEqual(fixtures.get("region"))
|
||||
expect(total).toEqual(0)
|
||||
expect(totalItems).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("item operations", () => {
|
||||
test("addItem", () => {
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: initialSessionCartState,
|
||||
},
|
||||
})
|
||||
const { addItem } = result.current
|
||||
const variant = fixtures.get("product_variant")
|
||||
|
||||
act(() => {
|
||||
addItem({
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 1,
|
||||
})
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(1)
|
||||
expect(total).toBe(1000)
|
||||
expect(items).toEqual([
|
||||
{
|
||||
variant: expect.objectContaining(variant),
|
||||
quantity: 1,
|
||||
total: 1000,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("updateItem", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
variant: {
|
||||
...variant,
|
||||
id: "test-variant",
|
||||
} as unknown as ProductVariant,
|
||||
quantity: 1,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { updateItem } = result.current
|
||||
|
||||
act(() => {
|
||||
updateItem(variant.id, {
|
||||
quantity: 4,
|
||||
})
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(5)
|
||||
expect(total).toBe(5 * 1000)
|
||||
expect(items).toEqual([
|
||||
{
|
||||
variant: expect.objectContaining(variant),
|
||||
quantity: 4,
|
||||
total: 1000,
|
||||
},
|
||||
{
|
||||
variant: {
|
||||
...variant,
|
||||
id: "test-variant",
|
||||
},
|
||||
quantity: 1,
|
||||
total: 1000,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("removeItem", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 3,
|
||||
},
|
||||
{
|
||||
variant: {
|
||||
...variant,
|
||||
id: "test-variant",
|
||||
} as unknown as ProductVariant,
|
||||
quantity: 1,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { removeItem } = result.current
|
||||
|
||||
act(() => {
|
||||
removeItem(variant.id)
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(1)
|
||||
expect(total).toBe(1000)
|
||||
expect(items).toEqual([
|
||||
{
|
||||
variant: {
|
||||
...variant,
|
||||
id: "test-variant",
|
||||
},
|
||||
quantity: 1,
|
||||
total: 1000,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("incrementItemQuantity", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 2,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { incrementItemQuantity } = result.current
|
||||
|
||||
act(() => {
|
||||
incrementItemQuantity(variant.id)
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(3)
|
||||
expect(total).toBe(3 * 1000)
|
||||
expect(items).toEqual([
|
||||
{
|
||||
variant,
|
||||
quantity: 3,
|
||||
total: 1000,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("decrementItemQuantity", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 4,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { decrementItemQuantity } = result.current
|
||||
|
||||
act(() => {
|
||||
decrementItemQuantity(variant.id)
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(3)
|
||||
expect(total).toBe(3 * 1000)
|
||||
expect(items).toEqual([
|
||||
{
|
||||
variant,
|
||||
quantity: 3,
|
||||
total: 1000,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("setItems", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 4,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { setItems } = result.current
|
||||
|
||||
act(() => {
|
||||
setItems([
|
||||
{
|
||||
variant: {
|
||||
...variant,
|
||||
id: "test-variant",
|
||||
} as unknown as ProductVariant,
|
||||
quantity: 1,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(1)
|
||||
expect(total).toBe(1000)
|
||||
expect(items).toEqual([
|
||||
{
|
||||
variant: expect.objectContaining({
|
||||
id: "test-variant",
|
||||
}),
|
||||
quantity: 1,
|
||||
total: 1000,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("getItem", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 1,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { getItem } = result.current
|
||||
let item
|
||||
act(() => {
|
||||
item = getItem(variant.id)
|
||||
})
|
||||
|
||||
expect(item).toEqual({
|
||||
variant,
|
||||
quantity: 1,
|
||||
total: 1000,
|
||||
})
|
||||
})
|
||||
|
||||
test("clearItems", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const { result } = renderHook(() => useSessionCart(), {
|
||||
wrapper: createSessionCartWrapper(),
|
||||
initialProps: {
|
||||
initialState: generateCartState(initialSessionCartState, [
|
||||
{
|
||||
variant: variant as unknown as ProductVariant,
|
||||
quantity: 4,
|
||||
},
|
||||
]),
|
||||
},
|
||||
})
|
||||
|
||||
const { clearItems } = result.current
|
||||
|
||||
act(() => {
|
||||
clearItems()
|
||||
})
|
||||
|
||||
const { items, totalItems, total } = result.current
|
||||
|
||||
expect(totalItems).toBe(0)
|
||||
expect(total).toBe(0)
|
||||
expect(items).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,72 @@
|
||||
import * as React from "react"
|
||||
import { QueryClient } from "react-query"
|
||||
import {
|
||||
SessionCartProvider,
|
||||
SessionCartState,
|
||||
CartProvider,
|
||||
MedusaProvider,
|
||||
} from "../src"
|
||||
import { Cart } from "../src/types"
|
||||
|
||||
const createTestQueryClient = () =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export function createWrapper() {
|
||||
const qc = createTestQueryClient()
|
||||
|
||||
return ({ children }) => (
|
||||
<MedusaProvider queryClientProviderProps={{ client: qc }} baseUrl="">
|
||||
{children}
|
||||
</MedusaProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export function createSessionCartWrapper() {
|
||||
const qc = createTestQueryClient()
|
||||
|
||||
return ({
|
||||
children,
|
||||
initialState,
|
||||
}: {
|
||||
initialState: SessionCartState
|
||||
children?: React.ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<MedusaProvider queryClientProviderProps={{ client: qc }} baseUrl="">
|
||||
<SessionCartProvider initialState={initialState}>
|
||||
{children}
|
||||
</SessionCartProvider>
|
||||
</MedusaProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function createCartWrapper() {
|
||||
const qc = createTestQueryClient()
|
||||
|
||||
return ({
|
||||
children,
|
||||
initialSessionCartState,
|
||||
initialCartState,
|
||||
}: {
|
||||
initialSessionCartState?: SessionCartState
|
||||
initialCartState?: Cart
|
||||
children?: React.ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<MedusaProvider queryClientProviderProps={{ client: qc }} baseUrl="">
|
||||
<SessionCartProvider initialState={initialSessionCartState}>
|
||||
<CartProvider initialState={initialCartState}>
|
||||
{children}
|
||||
</CartProvider>
|
||||
</SessionCartProvider>
|
||||
</MedusaProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import { RegionInfo, ProductVariantInfo } from "./../../src/types"
|
||||
import { fixtures } from "./../../mocks/data/"
|
||||
import {
|
||||
computeVariantPrice,
|
||||
getVariantPrice,
|
||||
computeAmount,
|
||||
formatAmount,
|
||||
formatVariantPrice,
|
||||
} from "./../../src/"
|
||||
|
||||
describe("getVariantPrice", () => {
|
||||
test("finds the variant price and returns its amount", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const region = fixtures.get("region")
|
||||
const amount = getVariantPrice(
|
||||
variant as unknown as ProductVariantInfo,
|
||||
region
|
||||
)
|
||||
|
||||
expect(amount).toEqual(1000)
|
||||
})
|
||||
|
||||
test("when no region is provided, then it should return 0", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const amount = getVariantPrice(
|
||||
variant as unknown as ProductVariantInfo,
|
||||
{} as RegionInfo
|
||||
)
|
||||
|
||||
expect(amount).toEqual(0)
|
||||
})
|
||||
|
||||
test("when no product variant is provided, then it should return 0", () => {
|
||||
const region = fixtures.get("region")
|
||||
const amount = getVariantPrice({} as ProductVariantInfo, region)
|
||||
|
||||
expect(amount).toEqual(0)
|
||||
})
|
||||
|
||||
test("when no product variant and region are provided, then it should return 0", () => {
|
||||
const amount = getVariantPrice({} as ProductVariantInfo, {} as RegionInfo)
|
||||
|
||||
expect(amount).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("computeAmount", () => {
|
||||
test("given an amount and a region, it should return a decimal amount not including taxes", () => {
|
||||
const region = fixtures.get("region")
|
||||
const amount = computeAmount({ amount: 3000, region, includeTaxes: false })
|
||||
|
||||
expect(amount).toEqual(30)
|
||||
})
|
||||
|
||||
test("given an amount and a region, it should return a decimal amount including taxes", () => {
|
||||
const region = fixtures.get("region")
|
||||
const amount = computeAmount({
|
||||
amount: 3000,
|
||||
region: {
|
||||
...region,
|
||||
tax_rate: 10,
|
||||
},
|
||||
})
|
||||
|
||||
expect(amount).toEqual(33)
|
||||
})
|
||||
|
||||
test("when no region is provided, then it should return the decimal amount", () => {
|
||||
const region = fixtures.get("region")
|
||||
const amount = computeAmount({ amount: 2000, region })
|
||||
|
||||
expect(amount).toEqual(20)
|
||||
})
|
||||
})
|
||||
|
||||
describe("computeVariantPrice", () => {
|
||||
test("finds the variant price and returns a decimal amount not including taxes", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const region = fixtures.get("region")
|
||||
const price = computeVariantPrice({
|
||||
variant: variant as unknown as ProductVariantInfo,
|
||||
region,
|
||||
})
|
||||
|
||||
expect(price).toEqual(10)
|
||||
})
|
||||
|
||||
test("finds the variant price and returns a decimal amount including taxes", () => {
|
||||
const variant = fixtures.get("product_variant")
|
||||
const region = fixtures.get("region")
|
||||
const price = computeVariantPrice({
|
||||
variant: variant as unknown as ProductVariantInfo,
|
||||
region: {
|
||||
...region,
|
||||
tax_rate: 15,
|
||||
},
|
||||
includeTaxes: true,
|
||||
})
|
||||
|
||||
expect(price).toEqual(11.5)
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatVariantPrice", () => {
|
||||
test("given a variant and region, should return a decimal localized amount including taxes and the region's currency code", () => {
|
||||
const region = fixtures.get("region")
|
||||
const variant = fixtures.get("product_variant")
|
||||
const price = formatVariantPrice({
|
||||
variant: variant as unknown as ProductVariantInfo,
|
||||
region: {
|
||||
...region,
|
||||
tax_rate: 15,
|
||||
},
|
||||
})
|
||||
|
||||
expect(price).toEqual("$11.50")
|
||||
})
|
||||
|
||||
test("given a variant, region, and 1 digit, should return a decimal (1 fraction digit) localized amount including taxes and the region's currency code", () => {
|
||||
const region = fixtures.get("region")
|
||||
const variant = fixtures.get("product_variant")
|
||||
const price = formatVariantPrice({
|
||||
variant: variant as unknown as ProductVariantInfo,
|
||||
region: {
|
||||
...region,
|
||||
tax_rate: 15,
|
||||
},
|
||||
maximumFractionDigits: 1,
|
||||
})
|
||||
|
||||
expect(price).toEqual("$11.5")
|
||||
})
|
||||
|
||||
test("given a variant, region, and a custom locale, should return a decimal localized amount including taxes and the region's currency code", () => {
|
||||
const region = fixtures.get("region")
|
||||
const variant = fixtures.get("product_variant")
|
||||
const price = formatVariantPrice({
|
||||
variant: variant as unknown as ProductVariantInfo,
|
||||
region: {
|
||||
...region,
|
||||
tax_rate: 15,
|
||||
},
|
||||
locale: "fr-FR",
|
||||
})
|
||||
|
||||
expect(price.replace(/\s/, " ")).toEqual("11,50 $US")
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatAmount", () => {
|
||||
test("given an amount and region, should return a decimal localized amount including taxes and the region's currency code", () => {
|
||||
const region = fixtures.get("region")
|
||||
const price = formatAmount({
|
||||
amount: 3000,
|
||||
region: {
|
||||
...region,
|
||||
tax_rate: 15,
|
||||
},
|
||||
})
|
||||
|
||||
expect(price).toEqual("$34.50")
|
||||
})
|
||||
|
||||
test("given an amount and no region, should return a decimal localized amount", () => {
|
||||
const price = formatAmount({ amount: 3000, region: {} as RegionInfo })
|
||||
|
||||
expect(price).toEqual("30")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
// see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
|
||||
"include": ["src", "types"],
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"lib": ["dom", "esnext"],
|
||||
"importHelpers": true,
|
||||
// output .d.ts declaration files for consumers
|
||||
"declaration": true,
|
||||
// output .js.map sourcemap files for consumers
|
||||
"sourceMap": true,
|
||||
// match output dir to input dir. e.g. dist/index instead of dist/src/index
|
||||
"rootDir": "./src",
|
||||
// stricter type-checking for stronger correctness. Recommended by TS
|
||||
"strict": true,
|
||||
// linter checks for common issues
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
// noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
// use Node's module resolution algorithm, instead of the legacy TS one
|
||||
"moduleResolution": "node",
|
||||
// transpile JSX to React.createElement
|
||||
"jsx": "react",
|
||||
// interop between ESM and CJS modules. Recommended by TS
|
||||
"esModuleInterop": true,
|
||||
// significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
|
||||
"skipLibCheck": true,
|
||||
// error out if import and file system have a casing mismatch. Recommended by TS
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
|
||||
"noEmit": true,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -857,18 +857,6 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
|
||||
integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==
|
||||
|
||||
"@hapi/topo@^5.0.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
|
||||
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
@@ -1070,23 +1058,6 @@
|
||||
readdirp "^2.2.1"
|
||||
upath "^1.1.1"
|
||||
|
||||
"@sideway/address@^4.1.0":
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1"
|
||||
integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@sideway/formula@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
|
||||
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
|
||||
|
||||
"@sideway/pinpoint@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b"
|
||||
@@ -3196,22 +3167,6 @@ jest@^25.5.2:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^25.5.4"
|
||||
|
||||
joi-objectid@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/joi-objectid/-/joi-objectid-3.0.1.tgz#63ace7860f8e1a993a28d40c40ffd8eff01a3668"
|
||||
integrity sha512-V/3hbTlGpvJ03Me6DJbdBI08hBTasFOmipsauOsxOSnsF1blxV537WTl1zPwbfcKle4AK0Ma4OPnzMH4LlvTpQ==
|
||||
|
||||
joi@^17.3.0:
|
||||
version "17.4.2"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.2.tgz#02f4eb5cf88e515e614830239379dcbbe28ce7f7"
|
||||
integrity sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
"@hapi/topo" "^5.0.0"
|
||||
"@sideway/address" "^4.1.0"
|
||||
"@sideway/formula" "^3.0.0"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -3424,14 +3379,6 @@ math-random@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
|
||||
integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
|
||||
|
||||
medusa-core-utils@^1.1.22:
|
||||
version "1.1.22"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.22.tgz#84ce0af0a7c672191d758ea462056e30a39d08b1"
|
||||
integrity sha512-kMuRkWOuNG4Bw6epg/AYu95UJuE+rjHTeTWRLbEPrYGjWREV82tLWVDI21/QcccmaHmMU98Rkw2z9JwyFZIiyw==
|
||||
dependencies:
|
||||
joi "^17.3.0"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
|
||||
+44
-1642
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user