Detect locale param from URL in AppBridge (#58)
* Detect locale param from URL in AppBridge * Add docs
This commit is contained in:
parent
910b50a18a
commit
126e949366
6 changed files with 94 additions and 15 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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",
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -104,4 +104,12 @@ export const DashboardEventFactory = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
createLocaleChangedEvent(newLocale: LocaleCode): LocaleChangedEvent {
|
||||||
|
return {
|
||||||
|
type: "localeChanged",
|
||||||
|
payload: {
|
||||||
|
locale: newLocale,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue