Allow setting initial theme from constructor in AppBridge (#162)

* Allow passing initial theme value, change initial notifyOnReady

* Update doc
This commit is contained in:
Lukasz Ostrowski 2023-01-24 19:21:05 +01:00 committed by GitHub
parent b32f1ff104
commit 6ffaba2dab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 196 additions and 20 deletions

View file

@ -20,6 +20,7 @@ type AppBridgeOptions = {
saleorApiUrl?: string; saleorApiUrl?: string;
initialLocale?: LocaleCode; initialLocale?: LocaleCode;
autoNotifyReady?: boolean; autoNotifyReady?: boolean;
initialTheme?: "dark" | "light"
}; };
``` ```

View file

@ -70,7 +70,7 @@
"typescript": "^4.8.2", "typescript": "^4.8.2",
"vi-fetch": "^0.8.0", "vi-fetch": "^0.8.0",
"vite": "^4.0.4", "vite": "^4.0.4",
"vitest": "^0.27.2" "vitest": "^0.28.1"
}, },
"lint-staged": { "lint-staged": {
"*.{js,ts,tsx}": "eslint --cache --fix", "*.{js,ts,tsx}": "eslint --cache --fix",

View file

@ -44,7 +44,7 @@ specifiers:
uuid: ^8.3.2 uuid: ^8.3.2
vi-fetch: ^0.8.0 vi-fetch: ^0.8.0
vite: ^4.0.4 vite: ^4.0.4
vitest: ^0.27.2 vitest: ^0.28.1
dependencies: dependencies:
debug: 4.3.4 debug: 4.3.4
@ -92,7 +92,7 @@ devDependencies:
typescript: 4.8.2 typescript: 4.8.2
vi-fetch: 0.8.0 vi-fetch: 0.8.0
vite: 4.0.4_@types+node@18.7.15 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: packages:
@ -1258,6 +1258,38 @@ packages:
- terser - terser
dev: true 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: /abab/2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
dev: true dev: true
@ -2067,6 +2099,11 @@ packages:
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
dev: true 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: /dir-glob/3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -4202,6 +4239,12 @@ packages:
get-func-name: 2.0.0 get-func-name: 2.0.0
dev: true 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: /lowercase-keys/2.0.0:
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -4655,6 +4698,13 @@ packages:
yocto-queue: 0.1.0 yocto-queue: 0.1.0
dev: true 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: /p-locate/5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -4782,6 +4832,10 @@ packages:
resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==}
dev: true dev: true
/pathe/1.1.0:
resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==}
dev: true
/pathval/1.1.1: /pathval/1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
dev: true dev: true
@ -4810,7 +4864,7 @@ packages:
dependencies: dependencies:
jsonc-parser: 3.2.0 jsonc-parser: 3.2.0
mlly: 1.1.0 mlly: 1.1.0
pathe: 1.0.0 pathe: 1.1.0
dev: true dev: true
/postcss-load-config/3.1.4: /postcss-load-config/3.1.4:
@ -5425,6 +5479,10 @@ packages:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
/std-env/3.3.1:
resolution: {integrity: sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==}
dev: true
/streamsearch/1.1.0: /streamsearch/1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@ -5989,6 +6047,29 @@ packages:
- terser - terser
dev: true 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: /vite/4.0.4_@types+node@18.7.15:
resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@ -6073,6 +6154,62 @@ packages:
- terser - terser
dev: true 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: /vm2/3.9.11:
resolution: {integrity: sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==} resolution: {integrity: sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -6320,6 +6457,11 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: true dev: true
/yocto-queue/1.0.0:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
dev: true
/zod/3.19.1: /zod/3.19.1:
resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==} resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==}
dev: false dev: false

View file

@ -25,7 +25,7 @@ describe("app-bridge-state.ts", () => {
saleorApiUrl: "https://my-saleor-instance.cloud/graphql/", saleorApiUrl: "https://my-saleor-instance.cloud/graphql/",
id: "foo-bar", id: "foo-bar",
path: "/", path: "/",
theme: "light", theme: "dark",
locale: "pl", locale: "pl",
}; };
@ -45,4 +45,12 @@ describe("app-bridge-state.ts", () => {
}).getState().locale }).getState().locale
).toBe("pl"); ).toBe("pl");
}); });
it("Can be constructed with initial theme", () => {
expect(
new AppBridgeStateContainer({
initialTheme: "dark",
}).getState().theme
).toBe("dark");
});
}); });

View file

@ -14,6 +14,7 @@ export type AppBridgeState = {
type Options = { type Options = {
initialLocale?: LocaleCode; initialLocale?: LocaleCode;
initialTheme?: ThemeType;
}; };
export class AppBridgeStateContainer { export class AppBridgeStateContainer {
@ -28,9 +29,8 @@ export class AppBridgeStateContainer {
}; };
constructor(options: Options = {}) { constructor(options: Options = {}) {
if (options.initialLocale) { this.state.locale = options.initialLocale ?? this.state.locale;
this.state.locale = options.initialLocale; this.state.theme = options.initialTheme ?? this.state.theme;
}
} }
getState() { getState() {

View file

@ -253,13 +253,26 @@ describe("AppBridge", () => {
window.location.href = currentLocationHref; window.location.href = currentLocationHref;
}); });
it("dispatches 'notifyReady' action when created", (done) => { it("Detects theme from URL param and set it to be initial", () => {
window.addEventListener("message", (event) => { const themeToOverwrite = "dark";
if (event.data.type === ActionType.notifyReady) {
done(); const currentLocationHref = window.location.href;
}
}); window.location.href = `${origin}?domain=${domain}&id=appid&theme=${themeToOverwrite}`;
appBridge = new AppBridge(); expect(new AppBridge().getState().theme).toBe(themeToOverwrite);
window.location.href = currentLocationHref;
}); });
it("dispatches 'notifyReady' action when enabled in constructor", () =>
new Promise<void>((res) => {
window.addEventListener("message", (event) => {
if (event.data.type === ActionType.notifyReady) {
res();
}
});
appBridge = new AppBridge({ autoNotifyReady: true });
}));
}); });

View file

@ -76,6 +76,7 @@ export type AppBridgeOptions = {
* If app loading time is longer, this can be disabled and sent manually. * If app loading time is longer, this can be disabled and sent manually.
*/ */
autoNotifyReady?: boolean; autoNotifyReady?: boolean;
initialTheme?: ThemeType;
}; };
/** /**
@ -94,11 +95,24 @@ const getDomainFromUrl = () =>
const getSaleorApiUrlFromUrl = () => const getSaleorApiUrlFromUrl = () =>
new URL(window.location.href).searchParams.get(AppIframeParams.SALEOR_API_URL) || ""; 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 => ({ const getDefaultOptions = (): AppBridgeOptions => ({
targetDomain: getDomainFromUrl(), targetDomain: getDomainFromUrl(),
saleorApiUrl: getSaleorApiUrlFromUrl(), saleorApiUrl: getSaleorApiUrlFromUrl(),
initialLocale: getLocaleFromUrl() ?? "en", initialLocale: getLocaleFromUrl() ?? "en",
autoNotifyReady: true, autoNotifyReady: false,
initialTheme: getThemeFromUrl() ?? undefined,
}); });
export class AppBridge { export class AppBridge {
@ -279,14 +293,12 @@ export class AppBridge {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const id = url.searchParams.get(AppIframeParams.APP_ID) || ""; const id = url.searchParams.get(AppIframeParams.APP_ID) || "";
const path = window.location.pathname || ""; const path = window.location.pathname || "";
const theme: ThemeType =
url.searchParams.get(AppIframeParams.THEME) === "light" ? "light" : "dark";
const state: Partial<AppBridgeState> = { const state: Partial<AppBridgeState> = {
domain: this.combinedOptions.targetDomain, domain: this.combinedOptions.targetDomain,
id, id,
path, path,
theme, theme: this.combinedOptions.initialTheme,
saleorApiUrl: this.combinedOptions.saleorApiUrl, saleorApiUrl: this.combinedOptions.saleorApiUrl,
locale: this.combinedOptions.initialLocale, locale: this.combinedOptions.initialLocale,
}; };