Detect locale param from URL in AppBridge (#58)

* Detect locale param from URL in AppBridge

* Add docs
This commit is contained in:
Lukasz Ostrowski 2022-09-14 14:21:53 +02:00 committed by GitHub
parent 910b50a18a
commit 126e949366
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 15 deletions

View file

@ -39,6 +39,42 @@ type AppBridgeState = {
}; };
``` ```
## AppBridgeProvider
`AppBridgeProvider` and `useAppBridge` hook are exposed from app-sdk
```tsx
// app.tsx
import { AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
<AppBridgeProvider>
<YourApp />
</AppBridgeProvider>;
```
`AppBridgeProvider` can optionally receive AppBridge instance in props, otherwise will create one automatically
### useAppBridge hook
In components wrapped with `AppBridgeProvider`, `useAppBridge` hook is available
```tsx
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
import { useEffect } from "react";
const MyComponent = () => {
const { appBridge, appBridgeState } = useAppBridge();
useEffect(() => {
appBridge?.dispatch(/* Something */);
}, [appBridge]);
return <div>Current locale is: {appBridgeState?.locale}</div>;
};
```
`appBridgeState?` and `appBridge` can be nullish, because in server side context it's not available
## Events ## Events
Events are messages that originate in Saleor Dashboard. AppBridge can subscribe on events and app can react on them Events are messages that originate in Saleor Dashboard. AppBridge can subscribe on events and app can react on them
@ -76,12 +112,15 @@ appBridge.unsubscribeAll();
### Available event types ### Available event types
| Event type | Description | | Event type | Description |
| :---------- | :--------------------------------------------------------------------------- | | :-------------- | :--------------------------------------------------------------------------- |
| `handshake` | Fired when iFrame containing the App is initialized or new token is assigned | | `handshake` | Fired when iFrame containing the App is initialized or new token is assigned |
| `response` | Fired when Dashboard responds to an Action | | `response` | Fired when Dashboard responds to an Action |
| `redirect` | Fired when Dashboard changes a subpath within the app path | | `redirect` | Fired when Dashboard changes a subpath within the app path |
| `theme` | Fired when Dashboard changes the theme | | `theme` | Fired when Dashboard changes the theme |
| `localeChanged` | Fired when Dashboard changes locale (and passes locale code in payload) |
See [source code for detailed payload](./src/app-bridge/events.ts)
## Actions ## Actions

View file

@ -24,10 +24,6 @@ export const AppContext = React.createContext<AppBridgeContext>({
mounted: false, mounted: false,
}); });
/**
* Experimental - try to use provider in app-sdk itself
* Consider monorepo with dedicated react package
*/
export function AppBridgeProvider({ appBridgeInstance, ...props }: React.PropsWithChildren<Props>) { export function AppBridgeProvider({ appBridgeInstance, ...props }: React.PropsWithChildren<Props>) {
debug("Provider mounted"); debug("Provider mounted");
const [appBridge, setAppBridge] = useState<AppBridge | undefined>(appBridgeInstance); const [appBridge, setAppBridge] = useState<AppBridge | undefined>(appBridgeInstance);

View file

@ -220,4 +220,16 @@ describe("AppBridge", () => {
expect(instance.getState().locale).toBe(locale); expect(instance.getState().locale).toBe(locale);
}); });
it("Detects locale from URL param and set it to be initial", () => {
const localeToOverwrite = "pl";
const currentLocationHref = window.location.href;
window.location.href = `${origin}?domain=${domain}&id=appid&locale=${localeToOverwrite}`;
expect(new AppBridge().getState().locale).toBe(localeToOverwrite);
window.location.href = currentLocationHref;
});
}); });

View file

@ -71,8 +71,20 @@ export type AppBridgeOptions = {
initialLocale?: LocaleCode; initialLocale?: LocaleCode;
}; };
/**
* TODO: Consider validating locale if wrong code provided
*/
const getLocaleFromUrl = () =>
(new URL(window.location.href).searchParams.get("locale") as LocaleCode) || undefined;
/**
* TODO: Probably remove empty string fallback
*/
const getDomainFromUrl = () => new URL(window.location.href).searchParams.get("domain") || "";
const getDefaultOptions = (): AppBridgeOptions => ({ const getDefaultOptions = (): AppBridgeOptions => ({
targetDomain: new URL(window.location.href).searchParams.get("domain") || "", targetDomain: getDomainFromUrl(),
initialLocale: getLocaleFromUrl(),
}); });
export class AppBridge { export class AppBridge {
@ -93,15 +105,15 @@ export class AppBridge {
); );
} }
this.state = new AppBridgeStateContainer({
initialLocale: options.initialLocale,
});
this.combinedOptions = { this.combinedOptions = {
...this.combinedOptions, ...this.combinedOptions,
...options, ...options,
}; };
this.state = new AppBridgeStateContainer({
initialLocale: this.combinedOptions.initialLocale,
});
debug("Resolved combined AppBridge options: %j", this.combinedOptions); debug("Resolved combined AppBridge options: %j", this.combinedOptions);
if (!this.refererOrigin) { if (!this.refererOrigin) {

View file

@ -12,6 +12,7 @@ describe("DashboardEventFactory", () => {
type: "handshake", type: "handshake",
}); });
}); });
it("Creates redirect event", () => { it("Creates redirect event", () => {
expect(DashboardEventFactory.createRedirectEvent("/new-path")).toEqual({ expect(DashboardEventFactory.createRedirectEvent("/new-path")).toEqual({
payload: { payload: {
@ -20,6 +21,7 @@ describe("DashboardEventFactory", () => {
type: "redirect", type: "redirect",
}); });
}); });
it("Creates dispatch response event", () => { it("Creates dispatch response event", () => {
expect(DashboardEventFactory.createDispatchResponseEvent("123", true)).toEqual({ expect(DashboardEventFactory.createDispatchResponseEvent("123", true)).toEqual({
payload: { payload: {
@ -29,6 +31,7 @@ describe("DashboardEventFactory", () => {
type: "response", type: "response",
}); });
}); });
it("Creates theme change event", () => { it("Creates theme change event", () => {
expect(DashboardEventFactory.createThemeChangeEvent("light")).toEqual({ expect(DashboardEventFactory.createThemeChangeEvent("light")).toEqual({
payload: { payload: {
@ -37,4 +40,13 @@ describe("DashboardEventFactory", () => {
type: "theme", type: "theme",
}); });
}); });
it("Creates locale change event", () => {
expect(DashboardEventFactory.createLocaleChangedEvent("it")).toEqual({
payload: {
locale: "it",
},
type: "localeChanged",
});
});
}); });

View file

@ -104,4 +104,12 @@ export const DashboardEventFactory = {
}, },
}; };
}, },
createLocaleChangedEvent(newLocale: LocaleCode): LocaleChangedEvent {
return {
type: "localeChanged",
payload: {
locale: newLocale,
},
};
},
}; };