From 6ffaba2dabe9d17fdd973bc0a0b746b658e7aa27 Mon Sep 17 00:00:00 2001 From: Lukasz Ostrowski Date: Tue, 24 Jan 2023 19:21:05 +0100 Subject: [PATCH] Allow setting initial theme from constructor in AppBridge (#162) * Allow passing initial theme value, change initial notifyOnReady * Update doc --- docs/app-bridge.md | 1 + package.json | 2 +- pnpm-lock.yaml | 148 +++++++++++++++++++++++- src/app-bridge/app-bridge-state.test.ts | 10 +- src/app-bridge/app-bridge-state.ts | 6 +- src/app-bridge/app-bridge.test.ts | 29 +++-- src/app-bridge/app-bridge.ts | 20 +++- 7 files changed, 196 insertions(+), 20 deletions(-) diff --git a/docs/app-bridge.md b/docs/app-bridge.md index de44469..3908f24 100644 --- a/docs/app-bridge.md +++ b/docs/app-bridge.md @@ -20,6 +20,7 @@ type AppBridgeOptions = { saleorApiUrl?: string; initialLocale?: LocaleCode; autoNotifyReady?: boolean; + initialTheme?: "dark" | "light" }; ``` diff --git a/package.json b/package.json index 771b052..f5a79b5 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "typescript": "^4.8.2", "vi-fetch": "^0.8.0", "vite": "^4.0.4", - "vitest": "^0.27.2" + "vitest": "^0.28.1" }, "lint-staged": { "*.{js,ts,tsx}": "eslint --cache --fix", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1492685..ee18c34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,7 +44,7 @@ specifiers: uuid: ^8.3.2 vi-fetch: ^0.8.0 vite: ^4.0.4 - vitest: ^0.27.2 + vitest: ^0.28.1 dependencies: debug: 4.3.4 @@ -92,7 +92,7 @@ devDependencies: typescript: 4.8.2 vi-fetch: 0.8.0 vite: 4.0.4_@types+node@18.7.15 - vitest: 0.27.2_jsdom@20.0.3 + vitest: 0.28.1_jsdom@20.0.3 packages: @@ -1258,6 +1258,38 @@ packages: - terser dev: true + /@vitest/expect/0.28.1: + resolution: {integrity: sha512-BOvWjBoocKrrTTTC0opIvzOEa7WR/Ovx4++QYlbjYKjnQJfWRSEQkTpAIEfOURtZ/ICcaLk5jvsRshXvjarZew==} + dependencies: + '@vitest/spy': 0.28.1 + '@vitest/utils': 0.28.1 + chai: 4.3.7 + dev: true + + /@vitest/runner/0.28.1: + resolution: {integrity: sha512-kOdmgiNe+mAxZhvj2eUTqKnjfvzzknmrcS+SZXV7j6VgJuWPFAMCv3TWOe03nF9dkqDfVLCDRw/hwFuCzmzlQg==} + dependencies: + '@vitest/utils': 0.28.1 + p-limit: 4.0.0 + pathe: 1.1.0 + dev: true + + /@vitest/spy/0.28.1: + resolution: {integrity: sha512-XGlD78cG3IxXNnGwEF121l0MfTNlHSdI25gS2ik0z6f/D9wWUOru849QkJbuNl4CMlZCtNkx3b5IS6MRwKGKuA==} + dependencies: + tinyspy: 1.0.2 + dev: true + + /@vitest/utils/0.28.1: + resolution: {integrity: sha512-a7cV1fs5MeU+W+8sn8gM9gV+q7V/wYz3/4y016w/icyJEKm9AMdSHnrzxTWaElJ07X40pwU6m5353Jlw6Rbd8w==} + dependencies: + cli-truncate: 3.1.0 + diff: 5.1.0 + loupe: 2.3.6 + picocolors: 1.0.0 + pretty-format: 27.5.1 + dev: true + /abab/2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true @@ -2067,6 +2099,11 @@ packages: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dev: true + /diff/5.1.0: + resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + engines: {node: '>=0.3.1'} + dev: true + /dir-glob/3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -4202,6 +4239,12 @@ packages: get-func-name: 2.0.0 dev: true + /loupe/2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + /lowercase-keys/2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -4655,6 +4698,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-limit/4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate/5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -4782,6 +4832,10 @@ packages: resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} dev: true + /pathe/1.1.0: + resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + dev: true + /pathval/1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true @@ -4810,7 +4864,7 @@ packages: dependencies: jsonc-parser: 3.2.0 mlly: 1.1.0 - pathe: 1.0.0 + pathe: 1.1.0 dev: true /postcss-load-config/3.1.4: @@ -5425,6 +5479,10 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + /std-env/3.3.1: + resolution: {integrity: sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==} + dev: true + /streamsearch/1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -5989,6 +6047,29 @@ packages: - terser dev: true + /vite-node/0.28.1_@types+node@18.7.15: + resolution: {integrity: sha512-Mmab+cIeElkVn4noScCRjy8nnQdh5LDIR4QCH/pVWtY15zv5Z1J7u6/471B9JZ2r8CEIs42vTbngaamOVkhPLA==} + engines: {node: '>=v14.16.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.1.0 + pathe: 1.1.0 + picocolors: 1.0.0 + source-map: 0.6.1 + source-map-support: 0.5.21 + vite: 4.0.4_@types+node@18.7.15 + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite/4.0.4_@types+node@18.7.15: resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -6073,6 +6154,62 @@ packages: - terser dev: true + /vitest/0.28.1_jsdom@20.0.3: + resolution: {integrity: sha512-F6wAO3K5+UqJCCGt0YAl3Ila2f+fpBrJhl9n7qWEhREwfzQeXlMkkCqGqGtzBxCSa8kv5QHrkshX8AaPTXYACQ==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.4 + '@types/chai-subset': 1.3.3 + '@types/node': 18.7.15 + '@vitest/expect': 0.28.1 + '@vitest/runner': 0.28.1 + '@vitest/spy': 0.28.1 + '@vitest/utils': 0.28.1 + acorn: 8.8.1 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + debug: 4.3.4 + jsdom: 20.0.3 + local-pkg: 0.4.2 + pathe: 1.1.0 + picocolors: 1.0.0 + source-map: 0.6.1 + std-env: 3.3.1 + strip-literal: 1.0.0 + tinybench: 2.3.1 + tinypool: 0.3.0 + tinyspy: 1.0.2 + vite: 4.0.4_@types+node@18.7.15 + vite-node: 0.28.1_@types+node@18.7.15 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vm2/3.9.11: resolution: {integrity: sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==} engines: {node: '>=6.0'} @@ -6320,6 +6457,11 @@ packages: engines: {node: '>=10'} dev: true + /yocto-queue/1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + /zod/3.19.1: resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==} dev: false diff --git a/src/app-bridge/app-bridge-state.test.ts b/src/app-bridge/app-bridge-state.test.ts index 06e6c5a..69211ce 100644 --- a/src/app-bridge/app-bridge-state.test.ts +++ b/src/app-bridge/app-bridge-state.test.ts @@ -25,7 +25,7 @@ describe("app-bridge-state.ts", () => { saleorApiUrl: "https://my-saleor-instance.cloud/graphql/", id: "foo-bar", path: "/", - theme: "light", + theme: "dark", locale: "pl", }; @@ -45,4 +45,12 @@ describe("app-bridge-state.ts", () => { }).getState().locale ).toBe("pl"); }); + + it("Can be constructed with initial theme", () => { + expect( + new AppBridgeStateContainer({ + initialTheme: "dark", + }).getState().theme + ).toBe("dark"); + }); }); diff --git a/src/app-bridge/app-bridge-state.ts b/src/app-bridge/app-bridge-state.ts index 01e4822..9a7f6dd 100644 --- a/src/app-bridge/app-bridge-state.ts +++ b/src/app-bridge/app-bridge-state.ts @@ -14,6 +14,7 @@ export type AppBridgeState = { type Options = { initialLocale?: LocaleCode; + initialTheme?: ThemeType; }; export class AppBridgeStateContainer { @@ -28,9 +29,8 @@ export class AppBridgeStateContainer { }; constructor(options: Options = {}) { - if (options.initialLocale) { - this.state.locale = options.initialLocale; - } + this.state.locale = options.initialLocale ?? this.state.locale; + this.state.theme = options.initialTheme ?? this.state.theme; } getState() { diff --git a/src/app-bridge/app-bridge.test.ts b/src/app-bridge/app-bridge.test.ts index 8e0e2df..eed43e3 100644 --- a/src/app-bridge/app-bridge.test.ts +++ b/src/app-bridge/app-bridge.test.ts @@ -253,13 +253,26 @@ describe("AppBridge", () => { window.location.href = currentLocationHref; }); - it("dispatches 'notifyReady' action when created", (done) => { - window.addEventListener("message", (event) => { - if (event.data.type === ActionType.notifyReady) { - done(); - } - }); - - appBridge = new AppBridge(); + it("Detects theme from URL param and set it to be initial", () => { + const themeToOverwrite = "dark"; + + const currentLocationHref = window.location.href; + + window.location.href = `${origin}?domain=${domain}&id=appid&theme=${themeToOverwrite}`; + + expect(new AppBridge().getState().theme).toBe(themeToOverwrite); + + window.location.href = currentLocationHref; }); + + it("dispatches 'notifyReady' action when enabled in constructor", () => + new Promise((res) => { + window.addEventListener("message", (event) => { + if (event.data.type === ActionType.notifyReady) { + res(); + } + }); + + appBridge = new AppBridge({ autoNotifyReady: true }); + })); }); diff --git a/src/app-bridge/app-bridge.ts b/src/app-bridge/app-bridge.ts index 44b0c85..f72da2b 100644 --- a/src/app-bridge/app-bridge.ts +++ b/src/app-bridge/app-bridge.ts @@ -76,6 +76,7 @@ export type AppBridgeOptions = { * If app loading time is longer, this can be disabled and sent manually. */ autoNotifyReady?: boolean; + initialTheme?: ThemeType; }; /** @@ -94,11 +95,24 @@ const getDomainFromUrl = () => const getSaleorApiUrlFromUrl = () => new URL(window.location.href).searchParams.get(AppIframeParams.SALEOR_API_URL) || ""; +const getThemeFromUrl = () => { + const value = new URL(window.location.href).searchParams.get(AppIframeParams.THEME); + + switch (value) { + case "dark": + case "light": + return value; + default: + return undefined; + } +}; + const getDefaultOptions = (): AppBridgeOptions => ({ targetDomain: getDomainFromUrl(), saleorApiUrl: getSaleorApiUrlFromUrl(), initialLocale: getLocaleFromUrl() ?? "en", - autoNotifyReady: true, + autoNotifyReady: false, + initialTheme: getThemeFromUrl() ?? undefined, }); export class AppBridge { @@ -279,14 +293,12 @@ export class AppBridge { const url = new URL(window.location.href); const id = url.searchParams.get(AppIframeParams.APP_ID) || ""; const path = window.location.pathname || ""; - const theme: ThemeType = - url.searchParams.get(AppIframeParams.THEME) === "light" ? "light" : "dark"; const state: Partial = { domain: this.combinedOptions.targetDomain, id, path, - theme, + theme: this.combinedOptions.initialTheme, saleorApiUrl: this.combinedOptions.saleorApiUrl, locale: this.combinedOptions.initialLocale, };