Skip to content

Commit

Permalink
feat: implement plugin wrappers (alpha) (#1332)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomzemp authored Mar 13, 2023
2 parents ce16f9e + 2480c1c commit 56a9a3f
Show file tree
Hide file tree
Showing 26 changed files with 561 additions and 48 deletions.
63 changes: 63 additions & 0 deletions docs/components/Plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Plugin Component

A wrapper that creates an iframe for a specified plugin and establishes a two-way communication channel with said plugin, allowing you to pass props (including callbacks between an app and a plugin). Note that the plugin must be built using the app-platform with entryPoints.plugin specified in the d2.config.js file.

## Basic Usage (Defining a plugin within an app)

Within an app you can specify a plugin (either by providing its short name `pluginShortName`, or by specifying a URL directly (`pluginSource`). If you have provided `pluginSource`, this will take precedence (Note: lookup logic is TBD? Should we allow a URL only in development mode, for example?).

```jsx
import { Plugin } from '@dhis2/app-runtime'

// within the app
const MyApp = () => (
<Plugin
pluginShortName={mutation}
onError={(err) => {
console.error(err)
}}
showAlertsInPlugin={true}
numberToPass={'42'}
callbackToPass={({ name }) => {
console.log(`Hi ${name}!`)
}}
/>
)
```

## Basic Usage (Using properties from the parent app)

You must build your plugin with the app-platform. If you have done this, your entry component will be passed the props from the parent app. From the example above, the properties `numberToPass` and `callbackToPass` will be available in the build plugin (when it is rendered with a <Plugin> component).

```jsx
// your plugin entry point (the plugin itself)

const MyPlugin = (propsFromParent) => {
const { numberToPass, callbackToPass: sayHi } = propsFromParent
return (
<>
<p>{`The meaning of life is: ${numberToPass}`}</p>
<button onClick={() => sayHi({ name: 'Arthur Dent' })}>
Say hi
</button>
</>
)
}
```

## Plugin Props (reserved props)

| Name | Type | Required | Description |
| :--------------------: | :------------: | :---------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **pluginShortName** | _string_ | _required_ if `pluginSource` is not provided | The shortName of the app/plugin you wish to load (matching the result from api/apps). Used to look up the plugin entry point. If this is not provided, `pluginSource` must be provided. `pluginSource` will take precedence if provided. |
| **pluginSource** | _string_ (url) | _required_ if `pluginShortName` is not provided | The URL of the plugin. If this is not provided, `pluginShortName` must be provided. |
| **onError** | _Function_ | _optional_ | Callback function to be called when an error in the plugin triggers an error boundary. You can use this to pass an error back up to the app and create a custom handling/UX if errors occur in the plugin. In general, it is recommended that you use the plugin's built-in error boundaries |
| **showAlertsInPlugin** | _boolean_ | _optional_ | If `true`, any alerts within the plugin (defined with the `useAlert` hook) will be rendered within the iframe. By default, this is `false`. It is recommended, in general, that you do not override this and allow alerts to be hoisted up to the app level |

## Plugin Props (custom props)

You can specify pass any other props on the <Plugin> component and these will be passed down to the plugin (provided it was built with app-platform). When props are updated, they will be passed back down to the plugin. This mimics the behaviour of a normal React component, and hence you should provide stable references as needed to prevent rerendering.

## Extended example

See these links for an extended example of how <Plugin> component can be used within an [app](https://github.com/tomzemp/workingplugin/blob/plugin-wrapper-in-platform/src/App.js) and consumed within the [plugin](https://github.com/tomzemp/workingplugin/blob/plugin-wrapper-in-platform/src/Plugin.js).
3 changes: 2 additions & 1 deletion examples/cra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"@dhis2/app-service-alerts": "file:../../services/alerts",
"@dhis2/app-service-config": "file:../../services/config",
"@dhis2/app-service-data": "file:../../services/data",
"@dhis2/app-service-offline": "file:../../services/offline"
"@dhis2/app-service-offline": "file:../../services/offline",
"@dhis2/app-service-plugin": "file:../../services/plugin"
},
"scripts": {
"start": "react-scripts start",
Expand Down
75 changes: 62 additions & 13 deletions examples/cra/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1054,29 +1054,34 @@
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==

"@dhis2/app-runtime@file:../../runtime":
version "3.6.1"
version "3.9.0"
dependencies:
"@dhis2/app-service-alerts" "3.6.1"
"@dhis2/app-service-config" "3.6.1"
"@dhis2/app-service-data" "3.6.1"
"@dhis2/app-service-offline" "3.6.1"
"@dhis2/app-service-alerts" "3.9.0"
"@dhis2/app-service-config" "3.9.0"
"@dhis2/app-service-data" "3.9.0"
"@dhis2/app-service-offline" "3.9.0"

"@dhis2/app-service-alerts@3.6.1", "@dhis2/app-service-alerts@file:../../services/alerts":
version "3.6.1"
"@dhis2/app-service-alerts@3.9.0", "@dhis2/app-service-alerts@file:../../services/alerts":
version "3.9.0"

"@dhis2/app-service-config@3.6.1", "@dhis2/app-service-config@file:../../services/config":
version "3.6.1"
"@dhis2/app-service-config@3.9.0", "@dhis2/app-service-config@file:../../services/config":
version "3.9.0"

"@dhis2/app-service-data@3.6.1", "@dhis2/app-service-data@file:../../services/data":
version "3.6.1"
"@dhis2/app-service-data@3.9.0", "@dhis2/app-service-data@file:../../services/data":
version "3.9.0"
dependencies:
react-query "^3.13.11"

"@dhis2/app-service-offline@3.6.1", "@dhis2/app-service-offline@file:../../services/offline":
version "3.6.1"
"@dhis2/app-service-offline@3.9.0", "@dhis2/app-service-offline@file:../../services/offline":
version "3.9.0"
dependencies:
lodash "^4.17.21"

"@dhis2/app-service-plugin@file:../../services/plugin":
version "3.7.0"
dependencies:
post-robot "^10.0.46"

"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
Expand Down Expand Up @@ -2259,6 +2264,15 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"

belter@^1.0.41:
version "1.0.190"
resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
dependencies:
cross-domain-safe-weakmap "^1"
cross-domain-utils "^2"
zalgo-promise "^1"

big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
Expand Down Expand Up @@ -3143,6 +3157,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"

cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
version "1.0.29"
resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
dependencies:
cross-domain-utils "^2.0.0"

cross-domain-utils@^2, cross-domain-utils@^2.0.0:
version "2.0.38"
resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
dependencies:
zalgo-promise "^1.0.11"

cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
Expand Down Expand Up @@ -7718,6 +7746,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=

post-robot@^10.0.46:
version "10.0.46"
resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
dependencies:
belter "^1.0.41"
cross-domain-safe-weakmap "^1.0.1"
cross-domain-utils "^2.0.0"
universal-serialize "^1.0.4"
zalgo-promise "^1.0.3"

postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
Expand Down Expand Up @@ -10326,6 +10365,11 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"

universal-serialize@^1.0.4:
version "1.0.10"
resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==

universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
Expand Down Expand Up @@ -11043,3 +11087,8 @@ yargs@^15.3.1:
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.1"

zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
version "1.0.48"
resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
3 changes: 2 additions & 1 deletion examples/query-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@dhis2/app-service-alerts": "file:../../services/alerts",
"@dhis2/app-service-config": "file:../../services/config",
"@dhis2/app-service-data": "file:../../services/data",
"@dhis2/app-service-offline": "file:../../services/offline"
"@dhis2/app-service-offline": "file:../../services/offline",
"@dhis2/app-service-plugin": "file:../../services/plugin"
}
}
89 changes: 69 additions & 20 deletions examples/query-playground/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1790,39 +1790,44 @@
moment "^2.24.0"

"@dhis2/app-runtime@*":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@dhis2/app-runtime/-/app-runtime-3.7.0.tgz#be738255ae95aaf597f9f59d1a56566b1a035dae"
integrity sha512-lKHgjaVVnCC5xxThckTVS0EH5mQlrSOdM8bC/7BSz5r9ztgLXJGUgxbnOvVbh95S/zFS9eyDvIls1HGum/MHTQ==
version "3.8.0"
resolved "https://registry.yarnpkg.com/@dhis2/app-runtime/-/app-runtime-3.8.0.tgz#4ec7fc4ec6647dc8428e3c0d2e14b2d188a993b9"
integrity sha512-f5M1RfUJb4yZaPDywTfogVXjzWcuYGJ7JQzny6iWXrJu1+qrRKYbfFutYNhB+4bXD4bB59DelHWqVaHtrGvbVA==
dependencies:
"@dhis2/app-service-alerts" "3.7.0"
"@dhis2/app-service-config" "3.7.0"
"@dhis2/app-service-data" "3.7.0"
"@dhis2/app-service-offline" "3.7.0"
"@dhis2/app-service-alerts" "3.8.0"
"@dhis2/app-service-config" "3.8.0"
"@dhis2/app-service-data" "3.8.0"
"@dhis2/app-service-offline" "3.8.0"

"@dhis2/app-runtime@^2.2.2", "@dhis2/app-runtime@file:../../runtime":
version "3.6.1"
version "3.9.0"
dependencies:
"@dhis2/app-service-alerts" "3.6.1"
"@dhis2/app-service-config" "3.6.1"
"@dhis2/app-service-data" "3.6.1"
"@dhis2/app-service-offline" "3.6.1"
"@dhis2/app-service-alerts" "3.9.0"
"@dhis2/app-service-config" "3.9.0"
"@dhis2/app-service-data" "3.9.0"
"@dhis2/app-service-offline" "3.9.0"

"@dhis2/app-service-alerts@3.6.1", "@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@file:../../services/alerts":
version "3.6.1"
"@dhis2/app-service-alerts@3.8.0", "@dhis2/app-service-alerts@3.9.0", "@dhis2/app-service-alerts@file:../../services/alerts":
version "3.9.0"

"@dhis2/app-service-config@3.6.1", "@dhis2/app-service-config@3.7.0", "@dhis2/app-service-config@file:../../services/config":
version "3.6.1"
"@dhis2/app-service-config@3.8.0", "@dhis2/app-service-config@3.9.0", "@dhis2/app-service-config@file:../../services/config":
version "3.9.0"

"@dhis2/app-service-data@3.6.1", "@dhis2/app-service-data@3.7.0", "@dhis2/app-service-data@file:../../services/data":
version "3.6.1"
"@dhis2/app-service-data@3.8.0", "@dhis2/app-service-data@3.9.0", "@dhis2/app-service-data@file:../../services/data":
version "3.9.0"
dependencies:
react-query "^3.13.11"

"@dhis2/app-service-offline@3.6.1", "@dhis2/app-service-offline@3.7.0", "@dhis2/app-service-offline@file:../../services/offline":
version "3.6.1"
"@dhis2/app-service-offline@3.8.0", "@dhis2/app-service-offline@3.9.0", "@dhis2/app-service-offline@file:../../services/offline":
version "3.9.0"
dependencies:
lodash "^4.17.21"

"@dhis2/app-service-plugin@file:../../services/plugin":
version "3.7.0"
dependencies:
post-robot "^10.0.46"

"@dhis2/app-shell@5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@dhis2/app-shell/-/app-shell-5.2.0.tgz#19fc3c6b18ea18048d3cdd1680ce535417edb6b3"
Expand Down Expand Up @@ -3369,6 +3374,15 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"

belter@^1.0.41:
version "1.0.190"
resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
dependencies:
cross-domain-safe-weakmap "^1"
cross-domain-utils "^2"
zalgo-promise "^1"

big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
Expand Down Expand Up @@ -4432,6 +4446,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"

cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
version "1.0.29"
resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
dependencies:
cross-domain-utils "^2.0.0"

cross-domain-utils@^2, cross-domain-utils@^2.0.0:
version "2.0.38"
resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
dependencies:
zalgo-promise "^1.0.11"

cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
Expand Down Expand Up @@ -9621,6 +9649,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=

post-robot@^10.0.46:
version "10.0.46"
resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
dependencies:
belter "^1.0.41"
cross-domain-safe-weakmap "^1.0.1"
cross-domain-utils "^2.0.0"
universal-serialize "^1.0.4"
zalgo-promise "^1.0.3"

postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
Expand Down Expand Up @@ -12675,6 +12714,11 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"

universal-serialize@^1.0.4:
version "1.0.10"
resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==

universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
Expand Down Expand Up @@ -13509,6 +13553,11 @@ yargs@^15.0.0, yargs@^15.3.1:
y18n "^4.0.0"
yargs-parser "^18.1.1"

zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
version "1.0.48"
resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==

zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
Expand Down
12 changes: 11 additions & 1 deletion runtime/src/Provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ type ProviderInput = {
config: Config
children: React.ReactNode
offlineInterface?: any // temporary until offline service has types
plugin: boolean
parentAlertsAdd: any
showAlertsInPlugin: boolean
}
export const Provider = ({
config,
children,
offlineInterface,
plugin,
parentAlertsAdd,
showAlertsInPlugin,
}: ProviderInput) => (
<ConfigProvider config={config}>
<AlertsProvider>
<AlertsProvider
plugin={plugin}
parentAlertsAdd={parentAlertsAdd}
showAlertsInPlugin={showAlertsInPlugin}
>
<DataProvider>
<OfflineProvider offlineInterface={offlineInterface}>
{children}
Expand Down
2 changes: 2 additions & 0 deletions runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ export {
clearSensitiveCaches,
} from '@dhis2/app-service-offline'

export { Plugin } from '@dhis2/app-service-plugin'

export { Provider } from './Provider'
3 changes: 3 additions & 0 deletions services/alerts/src/AlertsManagerContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const placeholder = () => {

const defaultAlertsManager: AlertsManager = {
add: placeholder,
plugin: false,
parentAlertsAdd: null,
showAlertsInPlugin: false,
}

export const AlertsManagerContext =
Expand Down
Loading

0 comments on commit 56a9a3f

Please sign in to comment.