Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist
coverage

yarn-error.log
.idea
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,55 @@
npm install @statflo/widget-sdk # or yarn add @statflo/widget-sdk
```

## Getting Started

### Example Widgets

Two example widgets are provided, one using React and one using vanilla javascript.

#### React Example Widget

1. Install the React example widget
```
yarn --cwd examples/react install
```
2. Start the React example widget
```
yarn --cwd examples/react start
```
3. View the React example widget at [http://localhost:3001](http://localhost:3001)

#### Vanilla JS Example Widget

1. Install the Vanilla JS example widget
```
yarn --cwd examples/vanillajs install
```
2. Start the Vanilla JS example widget
```
yarn --cwd examples/vanillajs start
```
3. View the Vanilla JS example widget at [http://localhost:3002](http://localhost:3002)

### Widget Playground

1. Install the Widget Playground
```
yarn --cwd docs install
```
2. Start the Widget Playground
```
yarn --cwd docs start
```
3. View the Widget Playground at [http://localhost:3000/widget-sdk/conversations](http://localhost:3000/widget-sdk/conversations)
Comment thread
Emgem marked this conversation as resolved.
Outdated
4. Check out "Getting Started" guide in the left-hand navigation menu.
5. Click on "Widget Manager" in the left-hand navigation menu and try adding the example widgets above by clicking "Add Widget" in the top left.
6. Click on "Conversations" in the left-hand navigation to view your newly added widgets.
7. Click on "Event Manager" to simulate sending events to your widgets.

We have provided a playground where you can test your widgets as you build them. We recommend building the example widgets
above and testing them in the playgound so you can get familiar with this project.

## Creating a widget

Please see our [Examples](https://github.com/Statflo/widget-sdk/tree/main/examples).
Expand Down Expand Up @@ -230,4 +279,4 @@ By default, the target origin will be the url of the widget you register with th

## Adding a widget to Statflo

Once your widget has been deployed and is ready to be integrated into the application, please reach out to the Statflo team.
Once your widget has been deployed and is ready to be integrated into the application, please reach out to the Statflo team.
5 changes: 2 additions & 3 deletions examples/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@statflo/widget-sdk": "./../../",
"@statflo/widget-sdk": "latest",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
Expand All @@ -21,8 +21,7 @@
"typescript": "^4.8.4",
"web-vitals": "^2.1.4",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"zustand": "^4.3.3"
"webpack-dev-server": "^4.11.1"
},
"scripts": {
"start": "webpack serve",
Expand Down
87 changes: 45 additions & 42 deletions examples/react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
import React, { useEffect, useState } from "react";
import "iframe-resizer-react";
import { create } from "zustand";
import { useStore } from "zustand";
import "./App.css";

import useWidgetStore, { WidgetEvent } from "@statflo/widget-sdk";
import useWidgetStore, {WidgetEvent, WidgetState} from "@statflo/widget-sdk";

const useBoundWidgetStore = create(useWidgetStore);

const App = () => {
const { events, publishEvent, getLatestEvent } = useBoundWidgetStore(
(state) => state
);
const events = useStore(useWidgetStore, (state: WidgetState) => state.events)
const publishEvent = useStore(useWidgetStore, (state: WidgetState) => state.publishEvent)
const getLatestEvent = useStore(useWidgetStore, (state: WidgetState) => state.getLatestEvent)

const [token, setToken] = useState("");
const [email, setEmail] = useState("");
const [account, setAccount] = useState("");
const [device, setDevice] = useState("");

const fetchUser = (token: string) => {
fetch("https://app.statflo.com/v2/api/auth/me", {
headers: { authorization: `Bearer ${token}` },
})
.then((response) => response.json())
.then((json) => {
setEmail(json.user.email);
})
.catch(() => {});
};
const getDevice = () => {
const devices = [
'Samsung Galaxy S25 Ultra',
'Apple iPhone 16 Pro Max',
'Google Pixel 9a',
];

useEffect(() => {
// Simulate a token being emitted from the Statflo UI to this widget.
// If you'd like to test with a live token it can be acquired
// at https://app.statflo.com/v2/api/auth/me after first logging in.
publishEvent(new WidgetEvent("TOKEN_UPDATED", "<YOUR TEST TOKEN>"));
}, []);
return devices[Math.floor(Math.random() * devices.length)];
}

useEffect(() => {
const latest = getLatestEvent();
Expand All @@ -41,36 +32,48 @@ const App = () => {
}

switch (latest.type) {
case "TOKEN_UPDATED":
setToken(latest.data);
break;
}
}, [events]);
case "CURRENT_ACCOUNT_ID":
setAccount(latest.data);
setDevice(getDevice());

useEffect(() => {
if (!token) {
return;
publishEvent(new WidgetEvent("REPLACE_MESSAGE", `Hi, how are you enjoying your ${device}?`));
break;
}

fetchUser(token);
}, [token]);
}, [device, events, getLatestEvent, publishEvent]);

return (
<div className="container g-0 border-top border-2">
<div className="row justify-content-center no-gutters g-0">
<h6 className="text-center pt-2">React Widget</h6>
<h6 className="text-center pt-2">
React Inventory Widget
</h6>

<form className="col pt-3 mb-4 border-bottom border-2">
<form
className="col pt-3 mb-4 border-bottom border-2"
>
<fieldset disabled>
<div className="mb-3">
<label className="form-label visually-hidden" htmlFor="Email">
Email
<label className="form-label" htmlFor="account">
Account
</label>
<input
name="account"
className="form-control form-control-sm"
id="account"
value={account}
readOnly={true}
placeholder="..."
/>
</div>
<div className="mb-3">
<label className="form-label" htmlFor="device">
Device
</label>
<input
name="email"
name="device"
className="form-control form-control-sm"
id="email"
value={email}
id="device"
value={device}
readOnly={true}
placeholder="..."
/>
Expand Down
4 changes: 2 additions & 2 deletions examples/react/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ module.exports = {
entry: "./src/index",
mode: "development",
output: {
publicPath: 'http://localhost:3000/'
publicPath: 'http://localhost:3001/'
},
devServer: {
port: 3000,
port: 3001,
allowedHosts: 'all'
},
module: {
Expand Down
24 changes: 13 additions & 11 deletions examples/react/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1638,15 +1638,19 @@
dependencies:
"@sinonjs/commons" "^1.7.0"

"@statflo/widget-sdk@./../../":
version "0.4.2"
"@statflo/widget-sdk@latest":
version "0.4.10"
resolved "https://registry.yarnpkg.com/@statflo/widget-sdk/-/widget-sdk-0.4.10.tgz#7d48e3a33eea8eabbe6763fe98432cd4590d7071"
integrity sha512-JEwQ/pLBSti5fUj831OfX1EYhj5wBZO2qZS9KzYJXxcdhipg4uDhkMdrAhdY0u+OdPluWUGEAzSBYIR1GK9BcQ==
dependencies:
iframe-resizer "^4.3.2"
iframe-resizer-react "^1.1.0"
react "^18.2.0"
react-error-boundary "^3.1.4"
typescript "^4.9.3"
uuid "^9.0.0"
zustand "^4.3.3"
zustand "^5.0.3"
optionalDependencies:
react ">=16.8"
use-sync-external-store "1.2.0"

"@surma/rollup-plugin-off-main-thread@^2.2.3":
Expand Down Expand Up @@ -7598,7 +7602,7 @@ react-scripts@5.0.1:
optionalDependencies:
fsevents "^2.3.2"

react@>=16.8, react@^18.2.0:
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
Expand Down Expand Up @@ -9374,9 +9378,7 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==

zustand@^4.3.3:
version "4.3.6"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.6.tgz#ce7804eb75361af0461a2d0536b65461ec5de86f"
integrity sha512-6J5zDxjxLE+yukC2XZWf/IyWVKnXT9b9HUv09VJ/bwGCpKNcaTqp7Ws28Xr8jnbvnZcdRaidztAPsXFBIqufiw==
dependencies:
use-sync-external-store "1.2.0"
zustand@^5.0.3:
version "5.0.5"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.5.tgz#3e236f6a953142d975336d179bc735d97db17e84"
integrity sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==
2 changes: 1 addition & 1 deletion examples/vanillajs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"webpack-dev-server": "^4.11.1"
},
"dependencies": {
"@statflo/widget-sdk": "./../../",
"@statflo/widget-sdk": "latest",
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.75.0"
},
Expand Down
60 changes: 34 additions & 26 deletions examples/vanillajs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,29 @@ const root = document.getElementById("root");

const { publishEvent } = useWidgetStore.getState();

// Simulate a token being emitted from the Statflo UI to this widget.
// If you'd like to test with a live token it can be acquired
// at https://app.statflo.com/v2/api/auth/me after first logging in.
publishEvent(new WidgetEvent("TOKEN_UPDATED", "<YOUR TEST TOKEN>"));
let account = "";
let device = "";

let token = "";
let email = "";
const getDevice = () => {
const devices = [
'Samsung Galaxy S25 Ultra',
'Apple iPhone 16 Pro Max',
'Google Pixel 9a',
];

const fetchUser = (token) => {
fetch("https://app.statflo.com/v2/api/auth/me", {
headers: { authorization: `Bearer ${token}` },
})
.then((response) => response.json())
.then((json) => {
email = json.user.email;
render(useWidgetStore.getState());
})
.catch(() => {});
};
return devices[Math.floor(Math.random() * devices.length)];
}

const render = (state) => {
const latest = state.getLatestEvent();

if (latest) {
switch (latest.type) {
case "TOKEN_UPDATED":
token = latest.data;
fetchUser(token);
case "CURRENT_ACCOUNT_ID":
account = latest.data;
device = getDevice();

publishEvent(new WidgetEvent("REPLACE_MESSAGE", `Hi, how are you enjoying your ${device}?`));
break;
}
}
Expand All @@ -40,22 +35,35 @@ const render = (state) => {
<div class="container g-0 border-top border-2">
<div class="row justify-content-center no-gutters g-0">
<h6 class="text-center pt-2">
Vanilla JS Widget
Vanilla JS Inventory Widget
</h6>

<form
class="col pt-3 mb-4 border-bottom border-2"
>
<fieldset disabled>
<div class="mb-3">
<label class="form-label" for="Email">
Current User
<label class="form-label" for="account">
Account
</label>
<input
name="account"
class="form-control form-control-sm"
id="account"
${account ? 'value="' + account + '"' : ""}
readonly
placeholder="..."
/>
</div>
<div class="mb-3">
<label class="form-label" for="device">
Device
</label>
<input
name="email"
name="device"
class="form-control form-control-sm"
id="email"
${email ? 'value="' + email + '"' : ""}
id="device"
${device ? 'value="' + device + '"' : ""}
readonly
placeholder="..."
/>
Expand Down
4 changes: 2 additions & 2 deletions examples/vanillajs/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ module.exports = {
output: {
path: path.resolve(__dirname, "./dist"),
filename: "index.js",
publicPath: "http://localhost:3000/",
publicPath: "http://localhost:3002/",
},
devServer: {
port: 3000,
port: 3002,
allowedHosts: "all",
},
plugins: [
Expand Down
Loading