From 7895bb9b7ffea3673c755b489011f4baf4216483 Mon Sep 17 00:00:00 2001 From: Thomas Zemp Date: Thu, 4 Jan 2024 12:41:28 +0100 Subject: [PATCH] feat: release experimental plugin wrappers [LIBS-397] [skip release] (#1366) * feat: plugin wrappers [LIBS-397] --------- Co-authored-by: Mozafar Haider --- .github/workflows/dhis2-verify-commits.yml | 1 + .github/workflows/dhis2-verify-lib.yml | 4 +- CHANGELOG.md | 120 +++++++++++- docs/components/Plugin.md | 67 +++++++ examples/cra/package.json | 3 +- examples/cra/yarn.lock | 76 ++++++-- examples/query-playground/package.json | 3 +- examples/query-playground/yarn.lock | 90 +++++++-- package.json | 2 +- runtime/package.json | 21 ++- runtime/src/Provider.tsx | 12 +- runtime/src/experimental.ts | 4 + services/alerts/package.json | 2 +- services/alerts/src/AlertsManagerContext.ts | 3 + services/alerts/src/AlertsProvider.tsx | 12 +- services/alerts/src/index.ts | 1 + services/alerts/src/makeAlertsManager.ts | 4 +- services/alerts/src/types.ts | 3 + services/alerts/src/useAlert.ts | 29 ++- services/config/package.json | 2 +- services/data/package.json | 4 +- services/offline/package.json | 4 +- services/plugin/.gitignore | 5 + services/plugin/README.md | 11 ++ services/plugin/d2.config.js | 9 + services/plugin/jest.config.js | 10 + services/plugin/package.json | 48 +++++ services/plugin/src/Plugin.tsx | 176 ++++++++++++++++++ services/plugin/src/PluginError.tsx | 23 +++ .../plugin/src/__tests__/integration.test.tsx | 16 ++ services/plugin/src/index.ts | 1 + services/plugin/src/setupRTL.ts | 5 + services/plugin/src/types.ts | 3 + services/plugin/tsconfig.json | 10 + yarn.lock | 83 +++++++++ 35 files changed, 798 insertions(+), 69 deletions(-) create mode 100644 docs/components/Plugin.md create mode 100644 runtime/src/experimental.ts create mode 100644 services/plugin/.gitignore create mode 100644 services/plugin/README.md create mode 100644 services/plugin/d2.config.js create mode 100644 services/plugin/jest.config.js create mode 100644 services/plugin/package.json create mode 100644 services/plugin/src/Plugin.tsx create mode 100644 services/plugin/src/PluginError.tsx create mode 100644 services/plugin/src/__tests__/integration.test.tsx create mode 100644 services/plugin/src/index.ts create mode 100644 services/plugin/src/setupRTL.ts create mode 100644 services/plugin/src/types.ts create mode 100644 services/plugin/tsconfig.json diff --git a/.github/workflows/dhis2-verify-commits.yml b/.github/workflows/dhis2-verify-commits.yml index ae7831b56..e55c91447 100644 --- a/.github/workflows/dhis2-verify-commits.yml +++ b/.github/workflows/dhis2-verify-commits.yml @@ -19,6 +19,7 @@ jobs: lint-commits: runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.title, '[skip release]') }} steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/dhis2-verify-lib.yml b/.github/workflows/dhis2-verify-lib.yml index 9221727ec..a93ae0cdb 100644 --- a/.github/workflows/dhis2-verify-lib.yml +++ b/.github/workflows/dhis2-verify-lib.yml @@ -1,8 +1,6 @@ name: 'dhis2: verify (lib)' -on: - push: - branches: +on: [push, pull_request] env: GIT_AUTHOR_NAME: '@dhis2-bot' diff --git a/CHANGELOG.md b/CHANGELOG.md index 32012c471..576951ee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,116 @@ -## [3.10.1](https://github.com/dhis2/app-runtime/compare/v3.10.0...v3.10.1) (2023-12-14) +# [3.11.0-alpha.1](https://github.com/dhis2/app-runtime/compare/v3.10.1...v3.11.0-alpha.1) (2023-12-21) + + +### Bug Fixes + +* add back plugin service dependency [LIBS-583] ([ca10691](https://github.com/dhis2/app-runtime/commit/ca10691ba01be083379db54ea2ec29f1666955a8)) +* add back plugin service dependency [LIBS-583] ([6d43ae3](https://github.com/dhis2/app-runtime/commit/6d43ae3688814ec3ae1f969123d63bd8e80146ce)) +* add documentation, clean up ([c537590](https://github.com/dhis2/app-runtime/commit/c537590176b2e6aebf278653a87705b3417bcc38)) +* add in plugin service in runtime package ([#1343](https://github.com/dhis2/app-runtime/issues/1343)) ([ed06a9f](https://github.com/dhis2/app-runtime/commit/ed06a9f4af7d3db40600ffd7e1b03cd095db36fc)) +* add width to plugin documentation [LIBS-487] ([b2c6273](https://github.com/dhis2/app-runtime/commit/b2c62732bf37e6d52b30f155b68c8b126068e8e5)) +* check memomized props for postMessage communication [LIBS-514] ([b1a3a0a](https://github.com/dhis2/app-runtime/commit/b1a3a0a4aeadc234aa181a9af2f176d0ca8d505c)) +* clean up ([e53ecbd](https://github.com/dhis2/app-runtime/commit/e53ecbd0632e35681e135de9072169e6bfefe13c)) +* clean up, add useless test ([b14952b](https://github.com/dhis2/app-runtime/commit/b14952b137971f625283c8de60afac061176b80e)) +* custom error handling ([c72fc6e](https://github.com/dhis2/app-runtime/commit/c72fc6eac576ce043ab706e13030497a0dab3d3a)) +* dependency array ([03ce64f](https://github.com/dhis2/app-runtime/commit/03ce64fb65af73190ec920d8d720250ec2ba3715)) +* dependency resolution ([2480c1c](https://github.com/dhis2/app-runtime/commit/2480c1c6b82daaeee0ab82ef45962fbcabd0e778)) +* merge issues ([496472a](https://github.com/dhis2/app-runtime/commit/496472a458f745d01890dec505d6ddb9259378d1)) +* move eslint disable line ([48912d7](https://github.com/dhis2/app-runtime/commit/48912d76630d5490b983e5e730a7d82d06762148)) +* plugin experimental docs ([be215b2](https://github.com/dhis2/app-runtime/commit/be215b2c292e451662b72043f20921d42bf36d33)) +* prevent sending updated props to plugin when props do not change [LIBS-514] ([86c6f75](https://github.com/dhis2/app-runtime/commit/86c6f75cf2e14a0d8f6f4b86f2d6de7403de62c6)) +* reset communication on either pluginSource or pluginShortName change ([3fdae5b](https://github.com/dhis2/app-runtime/commit/3fdae5becf3af038fef8ca5f0be7ecec44680600)) +* temporarily disable failing test ([6664199](https://github.com/dhis2/app-runtime/commit/666419955d0f1f70c7b101a49d7440dcd33857a0)) +* trigger props resend when iframe src changes [LIBS-488] ([f4a6680](https://github.com/dhis2/app-runtime/commit/f4a668004fe2f97d77e3c74a6047246cf358ade4)) +* trigger props resend when iframe src changes [LIBS-488] [#1344](https://github.com/dhis2/app-runtime/issues/1344) ([cea7600](https://github.com/dhis2/app-runtime/commit/cea760040d919508b1e13158dbf47ca9ed3686f6)) +* type error ([9c17206](https://github.com/dhis2/app-runtime/commit/9c17206a6942776f4c90c662153677ae9c00c350)) +* update alpha branch [skip release] ([ccb793c](https://github.com/dhis2/app-runtime/commit/ccb793c1e125484f39415cbedf1789a5de193e3d)) +* working autorsize width ([2991045](https://github.com/dhis2/app-runtime/commit/29910456aca9739848cac403c48a2ac4e64be1e3)) + + +### Features + +* add autoresizing for height ([dbb6e26](https://github.com/dhis2/app-runtime/commit/dbb6e265e0b3b01dfe975538703fcdde7c5f2b53)) +* experimental plugin release ([f5cca86](https://github.com/dhis2/app-runtime/commit/f5cca86924afa995d916aab2a1b58348b6dcaee7)) +* ideas for plugin wrappers [LIBS-397] ([be38607](https://github.com/dhis2/app-runtime/commit/be38607698b12309af5b79259afbbf037e7027bc)) +* implement plugin wrappers (alpha) ([#1332](https://github.com/dhis2/app-runtime/issues/1332)) ([56a9a3f](https://github.com/dhis2/app-runtime/commit/56a9a3fb734e07c6c2d8140c6413222b42be82a1)) +* plugin experimental export ([25f02a6](https://github.com/dhis2/app-runtime/commit/25f02a6c97733b79ff1aed5e8c4c1726c16144fe)) +* plugin wrappers, errors + alerts ([bda6a43](https://github.com/dhis2/app-runtime/commit/bda6a4352fe0ad8a076f55040e3fe702f9d0c4eb)) +* update plugin wrappers ([30c963c](https://github.com/dhis2/app-runtime/commit/30c963c112b2865ae824c7e3ce06279ed374983c)) + +# [3.10.0-alpha.8](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.7...v3.10.0-alpha.8) (2023-12-20) + + +### Bug Fixes + +* plugin experimental docs ([be215b2](https://github.com/dhis2/app-runtime/commit/be215b2c292e451662b72043f20921d42bf36d33)) + + +### Features + +* experimental plugin release ([f5cca86](https://github.com/dhis2/app-runtime/commit/f5cca86924afa995d916aab2a1b58348b6dcaee7)) +* plugin experimental export ([25f02a6](https://github.com/dhis2/app-runtime/commit/25f02a6c97733b79ff1aed5e8c4c1726c16144fe)) + +# [3.10.0-alpha.7](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.6...v3.10.0-alpha.7) (2023-12-20) + + +### Bug Fixes + +* check memomized props for postMessage communication [LIBS-514] ([b1a3a0a](https://github.com/dhis2/app-runtime/commit/b1a3a0a4aeadc234aa181a9af2f176d0ca8d505c)) +* dependency array ([03ce64f](https://github.com/dhis2/app-runtime/commit/03ce64fb65af73190ec920d8d720250ec2ba3715)) +* move eslint disable line ([48912d7](https://github.com/dhis2/app-runtime/commit/48912d76630d5490b983e5e730a7d82d06762148)) +* prevent sending updated props to plugin when props do not change [LIBS-514] ([86c6f75](https://github.com/dhis2/app-runtime/commit/86c6f75cf2e14a0d8f6f4b86f2d6de7403de62c6)) +* type error ([9c17206](https://github.com/dhis2/app-runtime/commit/9c17206a6942776f4c90c662153677ae9c00c350)) + +# [3.10.0-alpha.6](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.5...v3.10.0-alpha.6) (2023-10-09) ### Bug Fixes -* handle low cli-app-scripts version [LIBS-501] ([#1349](https://github.com/dhis2/app-runtime/issues/1349)) ([d15bce1](https://github.com/dhis2/app-runtime/commit/d15bce1869dc9fde2bdc7728f90e65d29332c861)) +* add back plugin service dependency [LIBS-583] ([ca10691](https://github.com/dhis2/app-runtime/commit/ca10691ba01be083379db54ea2ec29f1666955a8)) +* add back plugin service dependency [LIBS-583] ([6d43ae3](https://github.com/dhis2/app-runtime/commit/6d43ae3688814ec3ae1f969123d63bd8e80146ce)) + +# [3.10.0-alpha.5](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.4...v3.10.0-alpha.5) (2023-09-28) + -# [3.10.0](https://github.com/dhis2/app-runtime/compare/v3.9.4...v3.10.0) (2023-12-12) +### Bug Fixes + +* merge issues ([496472a](https://github.com/dhis2/app-runtime/commit/496472a458f745d01890dec505d6ddb9259378d1)) +* reset communication on either pluginSource or pluginShortName change ([3fdae5b](https://github.com/dhis2/app-runtime/commit/3fdae5becf3af038fef8ca5f0be7ecec44680600)) +* trigger props resend when iframe src changes [LIBS-488] ([f4a6680](https://github.com/dhis2/app-runtime/commit/f4a668004fe2f97d77e3c74a6047246cf358ade4)) +* trigger props resend when iframe src changes [LIBS-488] [#1344](https://github.com/dhis2/app-runtime/issues/1344) ([cea7600](https://github.com/dhis2/app-runtime/commit/cea760040d919508b1e13158dbf47ca9ed3686f6)) + +# [3.10.0-alpha.2](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.1...v3.10.0-alpha.2) (2023-03-15) + +### Bug Fixes + +* add in plugin service in runtime package ([#1343](https://github.com/dhis2/app-runtime/issues/1343)) ([ed06a9f](https://github.com/dhis2/app-runtime/commit/ed06a9f4af7d3db40600ffd7e1b03cd095db36fc)) + +# [3.10.0-alpha.4](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.3...v3.10.0-alpha.4) (2023-09-28) + + +### Bug Fixes + +* add width to plugin documentation [LIBS-487] ([b2c6273](https://github.com/dhis2/app-runtime/commit/b2c62732bf37e6d52b30f155b68c8b126068e8e5)) +* clean up ([e53ecbd](https://github.com/dhis2/app-runtime/commit/e53ecbd0632e35681e135de9072169e6bfefe13c)) +* temporarily disable failing test ([6664199](https://github.com/dhis2/app-runtime/commit/666419955d0f1f70c7b101a49d7440dcd33857a0)) +* working autorsize width ([2991045](https://github.com/dhis2/app-runtime/commit/29910456aca9739848cac403c48a2ac4e64be1e3)) ### Features -* add value-independent hook for setting online status message [LIBS-369] ([#1363](https://github.com/dhis2/app-runtime/issues/1363)) ([a2831e6](https://github.com/dhis2/app-runtime/commit/a2831e6eefef94dd2c81721d40a98ca89c53fee6)) +* add autoresizing for height ([dbb6e26](https://github.com/dhis2/app-runtime/commit/dbb6e265e0b3b01dfe975538703fcdde7c5f2b53)) + +# [3.10.0-alpha.3](https://github.com/dhis2/app-runtime/compare/v3.10.0-alpha.2...v3.10.0-alpha.3) (2023-08-22) + + +### Bug Fixes + +* **connection-status:** responsiveness to online events [LIBS-497] ([#1348](https://github.com/dhis2/app-runtime/issues/1348)) ([91a3d4d](https://github.com/dhis2/app-runtime/commit/91a3d4d820e6a87b819334bd72709c1de1a777f5)) +* **types:** add generic result type to oncomplete param ([#1350](https://github.com/dhis2/app-runtime/issues/1350)) ([a069603](https://github.com/dhis2/app-runtime/commit/a069603b4f3b2c9caa2158d7b4087e432cf90668)) +* [DHIS2] Type generic T = QueryResult to useDataQuery ([#1297](https://github.com/dhis2/app-runtime/issues/1297)) ([7c5c083](https://github.com/dhis2/app-runtime/commit/7c5c08308b919f70acac5cba6c2b851e4a6e50fa)) +* account for daylight savings time [LIBS-490] ([06eaa5d](https://github.com/dhis2/app-runtime/commit/06eaa5d5a0d3d4f69f701a3031610310d2d37ccf)) +* account for daylight savings time [LIBS-490] [#1345](https://github.com/dhis2/app-runtime/issues/1345) ([fb00533](https://github.com/dhis2/app-runtime/commit/fb00533008e828f7c9fa17f959dcc375fca8b6bd)) +* add test for when time zones are the same [LIBS-490] ([7911f8b](https://github.com/dhis2/app-runtime/commit/7911f8b992aabc34a1451144660a34b6b5035286)) ## [3.9.4](https://github.com/dhis2/app-runtime/compare/v3.9.3...v3.9.4) (2023-06-19) @@ -24,6 +124,18 @@ ### Bug Fixes +* add documentation, clean up ([c537590](https://github.com/dhis2/app-runtime/commit/c537590176b2e6aebf278653a87705b3417bcc38)) +* clean up, add useless test ([b14952b](https://github.com/dhis2/app-runtime/commit/b14952b137971f625283c8de60afac061176b80e)) +* custom error handling ([c72fc6e](https://github.com/dhis2/app-runtime/commit/c72fc6eac576ce043ab706e13030497a0dab3d3a)) +* dependency resolution ([2480c1c](https://github.com/dhis2/app-runtime/commit/2480c1c6b82daaeee0ab82ef45962fbcabd0e778)) + + +### Features + +* ideas for plugin wrappers [LIBS-397] ([be38607](https://github.com/dhis2/app-runtime/commit/be38607698b12309af5b79259afbbf037e7027bc)) +* implement plugin wrappers (alpha) ([#1332](https://github.com/dhis2/app-runtime/issues/1332)) ([56a9a3f](https://github.com/dhis2/app-runtime/commit/56a9a3fb734e07c6c2d8140c6413222b42be82a1)) +* plugin wrappers, errors + alerts ([bda6a43](https://github.com/dhis2/app-runtime/commit/bda6a4352fe0ad8a076f55040e3fe702f9d0c4eb)) +* update plugin wrappers ([30c963c](https://github.com/dhis2/app-runtime/commit/30c963c112b2865ae824c7e3ce06279ed374983c)) * **connection-status:** responsiveness to online events [LIBS-497] ([#1348](https://github.com/dhis2/app-runtime/issues/1348)) ([91a3d4d](https://github.com/dhis2/app-runtime/commit/91a3d4d820e6a87b819334bd72709c1de1a777f5)) ## [3.9.2](https://github.com/dhis2/app-runtime/compare/v3.9.1...v3.9.2) (2023-05-02) diff --git a/docs/components/Plugin.md b/docs/components/Plugin.md new file mode 100644 index 000000000..e8ea7f528 --- /dev/null +++ b/docs/components/Plugin.md @@ -0,0 +1,67 @@ +# Plugin Component + +_NOTE:_ This component is experimental and is available for import from `@dhis2/app-runtime/experimental`. The api for this component is not guaranteed to be stable. + +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. + +```jsx +import { Plugin } from '@dhis2/app-runtime/experimental' + +// within the app +const MyApp = () => ( + { + 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 component). + +```jsx +// your plugin entry point (the plugin itself) + +const MyPlugin = (propsFromParent) => { + const { numberToPass, callbackToPass: sayHi } = propsFromParent + return ( + <> +

{`The meaning of life is: ${numberToPass}`}

+ + + ) +} +``` + +## 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 | +| **height** | _number_ | _optional_ | If a height is provided, the iframe will be fixed to the specified height. If no height is provided, the iframe will automatically resize based on its contents. | +| **width** | _number_ | _optional_ | If a width is provided, the iframe will be fixed to the specified width. If no width is provided, the iframe will automatically resize based on its contents. | + +## Plugin Props (custom props) + +You can specify pass any other props on the 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 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). diff --git a/examples/cra/package.json b/examples/cra/package.json index 4553c9729..e7eec2caf 100644 --- a/examples/cra/package.json +++ b/examples/cra/package.json @@ -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", diff --git a/examples/cra/yarn.lock b/examples/cra/yarn.lock index 12bc2d9f0..2bcc31f09 100644 --- a/examples/cra/yarn.lock +++ b/examples/cra/yarn.lock @@ -1054,29 +1054,35 @@ integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== "@dhis2/app-runtime@file:../../runtime": - version "3.6.1" + version "3.10.0-alpha.2" 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.10.0-alpha.2" + "@dhis2/app-service-config" "3.10.0-alpha.2" + "@dhis2/app-service-data" "3.10.0-alpha.2" + "@dhis2/app-service-offline" "3.10.0-alpha.2" + "@dhis2/app-service-plugin" "3.10.0-alpha.2" -"@dhis2/app-service-alerts@3.6.1", "@dhis2/app-service-alerts@file:../../services/alerts": - version "3.6.1" +"@dhis2/app-service-alerts@3.10.0-alpha.2", "@dhis2/app-service-alerts@file:../../services/alerts": + version "3.10.0-alpha.2" -"@dhis2/app-service-config@3.6.1", "@dhis2/app-service-config@file:../../services/config": - version "3.6.1" +"@dhis2/app-service-config@3.10.0-alpha.2", "@dhis2/app-service-config@file:../../services/config": + version "3.10.0-alpha.2" -"@dhis2/app-service-data@3.6.1", "@dhis2/app-service-data@file:../../services/data": - version "3.6.1" +"@dhis2/app-service-data@3.10.0-alpha.2", "@dhis2/app-service-data@file:../../services/data": + version "3.10.0-alpha.2" 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.10.0-alpha.2", "@dhis2/app-service-offline@file:../../services/offline": + version "3.10.0-alpha.2" dependencies: lodash "^4.17.21" +"@dhis2/app-service-plugin@3.10.0-alpha.2", "@dhis2/app-service-plugin@file:../../services/plugin": + version "3.10.0-alpha.2" + 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" @@ -2259,6 +2265,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" @@ -3143,6 +3158,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" @@ -7718,6 +7747,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" @@ -10326,6 +10366,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" @@ -11043,3 +11088,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== diff --git a/examples/query-playground/package.json b/examples/query-playground/package.json index ccfa94237..60a58f94b 100644 --- a/examples/query-playground/package.json +++ b/examples/query-playground/package.json @@ -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" } } diff --git a/examples/query-playground/yarn.lock b/examples/query-playground/yarn.lock index 34974816f..2e25804ca 100644 --- a/examples/query-playground/yarn.lock +++ b/examples/query-playground/yarn.lock @@ -1790,39 +1790,45 @@ 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.10.0-alpha.2" 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.10.0-alpha.2" + "@dhis2/app-service-config" "3.10.0-alpha.2" + "@dhis2/app-service-data" "3.10.0-alpha.2" + "@dhis2/app-service-offline" "3.10.0-alpha.2" + "@dhis2/app-service-plugin" "3.10.0-alpha.2" -"@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.10.0-alpha.2", "@dhis2/app-service-alerts@3.8.0", "@dhis2/app-service-alerts@file:../../services/alerts": + version "3.10.0-alpha.2" -"@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.10.0-alpha.2", "@dhis2/app-service-config@3.8.0", "@dhis2/app-service-config@file:../../services/config": + version "3.10.0-alpha.2" -"@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.10.0-alpha.2", "@dhis2/app-service-data@3.8.0", "@dhis2/app-service-data@file:../../services/data": + version "3.10.0-alpha.2" 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.10.0-alpha.2", "@dhis2/app-service-offline@3.8.0", "@dhis2/app-service-offline@file:../../services/offline": + version "3.10.0-alpha.2" dependencies: lodash "^4.17.21" +"@dhis2/app-service-plugin@3.10.0-alpha.2", "@dhis2/app-service-plugin@file:../../services/plugin": + version "3.10.0-alpha.2" + 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" @@ -3369,6 +3375,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" @@ -4432,6 +4447,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" @@ -9621,6 +9650,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" @@ -12675,6 +12715,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" @@ -13509,6 +13554,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" diff --git a/package.json b/package.json index 977f5a6d2..e648daa62 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.10.1", + "version": "3.11.0-alpha.1", "description": "A singular runtime dependency for applications on the DHIS2 platform", "repository": "https://github.com/dhis2/app-runtime.git", "author": "Austin McGee ", diff --git a/runtime/package.json b/runtime/package.json index dbbe6be9a..afeb52bcf 100644 --- a/runtime/package.json +++ b/runtime/package.json @@ -1,13 +1,19 @@ { "name": "@dhis2/app-runtime", "description": "A singular runtime dependency for applications on the DHIS2 platform", - "version": "3.10.1", + "version": "3.11.0-alpha.1", "main": "./build/cjs/index.js", "module": "./build/es/index.js", "types": "./build/types/index.d.ts", "exports": { - "import": "./build/es/index.js", - "require": "./build/cjs/index.js" + ".": { + "import": "./build/es/index.js", + "require": "./build/cjs/index.js" + }, + "./experimental": { + "import": "./build/es/experimental.js", + "require": "./build/cjs/experimental.js" + } }, "repository": { "type": "git", @@ -23,10 +29,11 @@ "build/**" ], "dependencies": { - "@dhis2/app-service-config": "3.10.1", - "@dhis2/app-service-data": "3.10.1", - "@dhis2/app-service-alerts": "3.10.1", - "@dhis2/app-service-offline": "3.10.1" + "@dhis2/app-service-config": "3.11.0-alpha.1", + "@dhis2/app-service-data": "3.11.0-alpha.1", + "@dhis2/app-service-alerts": "3.11.0-alpha.1", + "@dhis2/app-service-offline": "3.11.0-alpha.1", + "@dhis2/app-service-plugin": "3.11.0-alpha.1" }, "peerDependencies": { "prop-types": "^15.7.2", diff --git a/runtime/src/Provider.tsx b/runtime/src/Provider.tsx index 19ad016d8..312af148c 100644 --- a/runtime/src/Provider.tsx +++ b/runtime/src/Provider.tsx @@ -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) => ( - + {children} diff --git a/runtime/src/experimental.ts b/runtime/src/experimental.ts new file mode 100644 index 000000000..d6c425c49 --- /dev/null +++ b/runtime/src/experimental.ts @@ -0,0 +1,4 @@ +/** + * this is experimental and the API is unstable + */ +export { Plugin } from '@dhis2/app-service-plugin' diff --git a/services/alerts/package.json b/services/alerts/package.json index c185ad579..601a2f7bc 100644 --- a/services/alerts/package.json +++ b/services/alerts/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/app-service-alerts", - "version": "3.10.1", + "version": "3.11.0-alpha.1", "main": "./build/cjs/index.js", "module": "./build/es/index.js", "types": "./build/types/index.d.ts", diff --git a/services/alerts/src/AlertsManagerContext.ts b/services/alerts/src/AlertsManagerContext.ts index 0c3ed4d8b..9c3527fa9 100644 --- a/services/alerts/src/AlertsManagerContext.ts +++ b/services/alerts/src/AlertsManagerContext.ts @@ -9,6 +9,9 @@ const placeholder = () => { const defaultAlertsManager: AlertsManager = { add: placeholder, + plugin: false, + parentAlertsAdd: null, + showAlertsInPlugin: false, } export const AlertsManagerContext = diff --git a/services/alerts/src/AlertsProvider.tsx b/services/alerts/src/AlertsProvider.tsx index e59cbf766..fc8876ce4 100644 --- a/services/alerts/src/AlertsProvider.tsx +++ b/services/alerts/src/AlertsProvider.tsx @@ -5,17 +5,25 @@ import { makeAlertsManager } from './makeAlertsManager' import type { Alert, AlertsManager } from './types' export const AlertsProvider = ({ + plugin, + parentAlertsAdd, + showAlertsInPlugin, children, }: { + plugin: boolean + parentAlertsAdd: any + showAlertsInPlugin: boolean children: React.ReactNode }): ReactElement => { const [alerts, setAlerts] = useState([]) const [alertsManager] = useState(() => - makeAlertsManager(setAlerts) + makeAlertsManager(setAlerts, plugin) ) return ( - + {children} diff --git a/services/alerts/src/index.ts b/services/alerts/src/index.ts index f2e0367bd..f02489ab6 100644 --- a/services/alerts/src/index.ts +++ b/services/alerts/src/index.ts @@ -1,3 +1,4 @@ export { AlertsProvider } from './AlertsProvider' export { useAlerts } from './useAlerts' export { useAlert } from './useAlert' +export { AlertsManagerContext } from './AlertsManagerContext' diff --git a/services/alerts/src/makeAlertsManager.ts b/services/alerts/src/makeAlertsManager.ts index b97bf264e..6963d58db 100644 --- a/services/alerts/src/makeAlertsManager.ts +++ b/services/alerts/src/makeAlertsManager.ts @@ -4,7 +4,8 @@ const toVisibleAlertsArray = (alertsMap: AlertsMap) => Array.from(alertsMap.values()) export const makeAlertsManager = ( - setAlerts: React.Dispatch> + setAlerts: React.Dispatch>, + plugin: boolean ): AlertsManager => { const alertsMap: AlertsMap = new Map() let id = 0 @@ -29,5 +30,6 @@ export const makeAlertsManager = ( return { add, + plugin, } } diff --git a/services/alerts/src/types.ts b/services/alerts/src/types.ts index 73c563852..654a0560b 100644 --- a/services/alerts/src/types.ts +++ b/services/alerts/src/types.ts @@ -15,4 +15,7 @@ export type AlertsMap = Map export type AlertsManager = { add: (alert: Alert, alertRef: AlertRef) => Alert + plugin: boolean + parentAlertsAdd?: any + showAlertsInPlugin?: boolean } diff --git a/services/alerts/src/useAlert.ts b/services/alerts/src/useAlert.ts index 87454cace..adc84e211 100644 --- a/services/alerts/src/useAlert.ts +++ b/services/alerts/src/useAlert.ts @@ -6,7 +6,8 @@ export const useAlert = ( message: string | ((props: any) => string), options: AlertOptions | ((props: any) => AlertOptions) = {} ): { show: (props?: any) => void; hide: () => void } => { - const { add }: AlertsManager = useContext(AlertsManagerContext) + const { add, plugin, parentAlertsAdd, showAlertsInPlugin }: AlertsManager = + useContext(AlertsManagerContext) const alertRef = useRef(null) const show = useCallback( @@ -17,15 +18,25 @@ export const useAlert = ( const resolvedOptions = typeof options === 'function' ? options(props) : options - alertRef.current = add( - { - message: resolvedMessage, - options: resolvedOptions, - }, - alertRef - ) + if (plugin && parentAlertsAdd && !showAlertsInPlugin) { + alertRef.current = parentAlertsAdd( + { + message: resolvedMessage, + options: resolvedOptions, + }, + alertRef + ) + } else { + alertRef.current = add( + { + message: resolvedMessage, + options: resolvedOptions, + }, + alertRef + ) + } }, - [add, message, options] + [add, parentAlertsAdd, message, options, plugin, showAlertsInPlugin] ) const hide = useCallback(() => { diff --git a/services/config/package.json b/services/config/package.json index ad06c34a7..1ac8ec3c4 100644 --- a/services/config/package.json +++ b/services/config/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/app-service-config", - "version": "3.10.1", + "version": "3.11.0-alpha.1", "main": "./build/cjs/index.js", "module": "./build/es/index.js", "types": "build/types/index.d.ts", diff --git a/services/data/package.json b/services/data/package.json index 7e474d118..33c7c260c 100644 --- a/services/data/package.json +++ b/services/data/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/app-service-data", - "version": "3.10.1", + "version": "3.11.0-alpha.1", "main": "./build/cjs/index.js", "module": "./build/es/index.js", "types": "build/types/index.d.ts", @@ -22,7 +22,7 @@ "build/**" ], "peerDependencies": { - "@dhis2/app-service-config": "3.10.1", + "@dhis2/app-service-config": "3.11.0-alpha.1", "@dhis2/cli-app-scripts": "^7.1.1", "prop-types": "^15.7.2", "react": "^16.8", diff --git a/services/offline/package.json b/services/offline/package.json index 69aef9003..2b904748c 100644 --- a/services/offline/package.json +++ b/services/offline/package.json @@ -1,7 +1,7 @@ { "name": "@dhis2/app-service-offline", "description": "A runtime service for online/offline detection and offline caching", - "version": "3.10.1", + "version": "3.11.0-alpha.1", "main": "./build/cjs/index.js", "module": "./build/es/index.js", "types": "build/types/index.d.ts", @@ -33,7 +33,7 @@ "coverage": "yarn test --coverage" }, "peerDependencies": { - "@dhis2/app-service-config": "3.10.1", + "@dhis2/app-service-config": "3.11.0-alpha.1", "prop-types": "^15.7.2", "react": "^16.8.6", "react-dom": "^16.8.6" diff --git a/services/plugin/.gitignore b/services/plugin/.gitignore new file mode 100644 index 000000000..6570aa5cb --- /dev/null +++ b/services/plugin/.gitignore @@ -0,0 +1,5 @@ +# DHIS2 Platform +node_modules +.d2 +src/locales +build \ No newline at end of file diff --git a/services/plugin/README.md b/services/plugin/README.md new file mode 100644 index 000000000..0aa8a0ae7 --- /dev/null +++ b/services/plugin/README.md @@ -0,0 +1,11 @@ +# DHIS2 App Plugin Service + +Application configuration for [DHIS2](https://dhis2.org) applications + +This library is intended for use with the [DHIS2 Application Platform](https://github.com/dhis2/app-platform). + +## Installation + +This package is internal to `@dhis2/app-runtime` and generally should not be installed independently. + +See [the docs](https://runtime.dhis2.nu) for more. diff --git a/services/plugin/d2.config.js b/services/plugin/d2.config.js new file mode 100644 index 000000000..84bec20f1 --- /dev/null +++ b/services/plugin/d2.config.js @@ -0,0 +1,9 @@ +const config = { + type: 'lib', + + entryPoints: { + lib: './src/index.ts', + }, +} + +module.exports = config diff --git a/services/plugin/jest.config.js b/services/plugin/jest.config.js new file mode 100644 index 000000000..66cd80913 --- /dev/null +++ b/services/plugin/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + collectCoverageFrom: [ + 'src/**/*.(js|jsx|ts|tsx)', + '!src/index.ts', + '!src/types.ts', + ], + + // Setup react-testing-library + setupFilesAfterEnv: ['/src/setupRTL.ts'], +} diff --git a/services/plugin/package.json b/services/plugin/package.json new file mode 100644 index 000000000..8ed065716 --- /dev/null +++ b/services/plugin/package.json @@ -0,0 +1,48 @@ +{ + "name": "@dhis2/app-service-plugin", + "version": "3.11.0-alpha.1", + "main": "./build/cjs/index.js", + "module": "./build/es/index.js", + "types": "build/types/index.d.ts", + "exports": { + "import": "./build/es/index.js", + "require": "./build/cjs/index.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/dhis2/app-runtime.git", + "directory": "services/plugin" + }, + "author": "Thomas Zemp ", + "license": "BSD-3-Clause", + "publishConfig": { + "access": "public" + }, + "files": [ + "build/**" + ], + "dependencies": { + "post-robot": "^10.0.46" + }, + "peerDependencies": { + "prop-types": "^15.7.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "@dhis2/app-service-alerts": "3.11.0-alpha.1", + "@dhis2/app-service-data": "3.11.0-alpha.1" + }, + "devDependencies": { + "@types/post-robot": "^10.0.3" + }, + "scripts": { + "clean": "rimraf ./build/*", + "build:types": "tsc --emitDeclarationOnly --outDir ./build/types", + "build:package": "d2-app-scripts build", + "build": "concurrently -n build,types \"yarn build:package\" \"yarn build:types\"", + "watch": "NODE_ENV=development concurrently -n build,types \"yarn build:package --watch\" \"yarn build:types --watch\"", + "type-check": "tsc --noEmit --allowJs --checkJs", + "type-check:watch": "yarn type-check --watch", + "test": "d2-app-scripts test", + "coverage": "yarn test --coverage" + } +} diff --git a/services/plugin/src/Plugin.tsx b/services/plugin/src/Plugin.tsx new file mode 100644 index 000000000..f8c7d6cb0 --- /dev/null +++ b/services/plugin/src/Plugin.tsx @@ -0,0 +1,176 @@ +import { AlertsManagerContext } from '@dhis2/app-service-alerts' +import { useDataQuery } from '@dhis2/app-service-data' +import postRobot from 'post-robot' +import React, { useContext, useEffect, useMemo, useRef, useState } from 'react' +import PluginError from './PluginError' + +const appsInfoQuery = { + apps: { + resource: 'apps', + }, +} + +// sample logic subject to change depending on actual endpoint details +const getPluginEntryPoint = ({ + apps, + appShortName, +}: { + apps: any + appShortName?: string +}): string => { + return apps.find( + ({ short_name }: { short_name: string }) => + short_name && short_name === appShortName + )?.pluginLaunchUrl +} + +export const Plugin = ({ + pluginSource, + pluginShortName, + ...propsToPassNonMemoized +}: { + pluginSource?: string + pluginShortName?: string + propsToPass: any +}): JSX.Element => { + const iframeRef = useRef(null) + + // we do not know what is being sent in passed props, so for stable reference, memoize using JSON representation + const propsToPassNonMemoizedJSON = JSON.stringify(propsToPassNonMemoized) + const propsToPass: any = useMemo( + () => ({ ...propsToPassNonMemoized }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [propsToPassNonMemoizedJSON] + ) + const { height, width } = propsToPass + + const { add: alertsAdd } = useContext(AlertsManagerContext) + + const { data } = useDataQuery(appsInfoQuery) + const pluginEntryPoint = + pluginSource ?? + getPluginEntryPoint({ + apps: data?.apps || [], + appShortName: pluginShortName, + }) + + const [communicationReceived, setCommunicationReceived] = + useState(false) + const [prevCommunicationReceived, setPrevCommunicationReceived] = + useState(false) + + const [inErrorState, setInErrorState] = useState(false) + const [pluginHeight, setPluginHeight] = useState(150) + const [pluginWidth, setPluginWidth] = useState(500) + + useEffect(() => { + if (height) { + setPluginHeight(height) + } + if (width) { + setPluginWidth(width) + } + }, [height, width]) + + // since we do not know what props are passed, the dependency array has to be keys of whatever is standard prop + const memoizedPropsToPass = useMemo( + () => propsToPass, + /* eslint-disable react-hooks/exhaustive-deps */ + [ + ...Object.keys(propsToPass) + .sort() + .map((k) => (propsToPass as any)[k]), + ] + /* eslint-enable react-hooks/exhaustive-deps */ + ) + + useEffect(() => { + setCommunicationReceived(false) + }, [pluginEntryPoint]) + + useEffect(() => { + // if communicationReceived switches from false to true, the props have been sent + const prevCommunication = prevCommunicationReceived + setPrevCommunicationReceived(communicationReceived) + if (prevCommunication === false && communicationReceived === true) { + return + } + + if (iframeRef?.current) { + const iframeProps = { + ...memoizedPropsToPass, + alertsAdd, + setPluginHeight, + setPluginWidth, + setInErrorState, + setCommunicationReceived, + } + + // if iframe has not sent initial request, set up a listener + if (!communicationReceived && !inErrorState) { + const listener = postRobot.on( + 'getPropsFromParent', + // listen for messages coming only from the iframe rendered by this component + { window: iframeRef.current.contentWindow }, + (): any => { + setCommunicationReceived(true) + return iframeProps + } + ) + return () => listener.cancel() + } + + // if iframe has sent initial request, send new props + if ( + communicationReceived && + iframeRef.current.contentWindow && + !inErrorState + ) { + postRobot + .send( + iframeRef.current.contentWindow, + 'updated', + iframeProps + ) + .catch((err) => { + // log postRobot errors, but do not bubble them up + console.error(err) + }) + } + } + // prevCommunicationReceived update should not retrigger this hook + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [memoizedPropsToPass, communicationReceived, inErrorState, alertsAdd]) + + if (data && !pluginEntryPoint) { + return ( + + ) + } + + if (pluginEntryPoint) { + return ( +
+ +
+ ) + } + + return <> +} diff --git a/services/plugin/src/PluginError.tsx b/services/plugin/src/PluginError.tsx new file mode 100644 index 000000000..2883f88a5 --- /dev/null +++ b/services/plugin/src/PluginError.tsx @@ -0,0 +1,23 @@ +import React from 'react' + +// PLACEHOLDER plugin error component (e.g. for dealing with missing/inaccessible plugin) +// note that d2-i18n does not work with typescript projects, so we cannot currently translate + +const PluginError = ({ + missingEntryPoint, + appShortName, +}: { + missingEntryPoint: boolean + appShortName?: string +}) => ( + <> +

Plugin unavailable

+ {missingEntryPoint ? ( + <> +

{`You do not have access to the requested plugin ${appShortName}, or it is not installed`}

+ + ) : null} + +) + +export default PluginError diff --git a/services/plugin/src/__tests__/integration.test.tsx b/services/plugin/src/__tests__/integration.test.tsx new file mode 100644 index 000000000..b5270713f --- /dev/null +++ b/services/plugin/src/__tests__/integration.test.tsx @@ -0,0 +1,16 @@ +import * as React from 'react' +import PluginError from '../PluginError' + +// empty tests (to no trigger test failure) +describe('', () => { + it('should render without failing', async () => { + const missingEntryPoint = false + const appShortName = 'some_app' + const wrapper = () => ( + + ) + }) +}) diff --git a/services/plugin/src/index.ts b/services/plugin/src/index.ts new file mode 100644 index 000000000..2321646ed --- /dev/null +++ b/services/plugin/src/index.ts @@ -0,0 +1 @@ +export { Plugin } from './Plugin' diff --git a/services/plugin/src/setupRTL.ts b/services/plugin/src/setupRTL.ts new file mode 100644 index 000000000..0deaa77ec --- /dev/null +++ b/services/plugin/src/setupRTL.ts @@ -0,0 +1,5 @@ +import '@testing-library/jest-dom/extend-expect' + +process.on('unhandledRejection', (err) => { + throw err +}) diff --git a/services/plugin/src/types.ts b/services/plugin/src/types.ts new file mode 100644 index 000000000..f00b281de --- /dev/null +++ b/services/plugin/src/types.ts @@ -0,0 +1,3 @@ +import { ReactNode } from 'react' + +// file is a placeholder to allow build diff --git a/services/plugin/tsconfig.json b/services/plugin/tsconfig.json new file mode 100644 index 000000000..3e229a39d --- /dev/null +++ b/services/plugin/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], + "exclude": [ + "src/setupRTL.ts", + "src/__tests__", + "**/*.test.ts", + "**/*.test.tsx" + ] +} diff --git a/yarn.lock b/yarn.lock index 47ed2bb2d..a40432512 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2734,6 +2734,40 @@ "@dhis2/pwa" "10.2.0" moment "^2.24.0" +"@dhis2/app-runtime@^3.6.1": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@dhis2/app-runtime/-/app-runtime-3.9.0.tgz#c7e295fd0a68fac976a930bc77105206ded0b61a" + integrity sha512-n0S4pbyvK7FnBQFMONGrhR9YYavBQI+mQLHfCX/vtvOyeoioBUNIinuQlGysuLMEkSVaK5OjV40rvTMzdxF2kQ== + dependencies: + "@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.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@dhis2/app-service-alerts/-/app-service-alerts-3.9.0.tgz#48d3805676e75ee58104fea4f76cfa779335444e" + integrity sha512-z2eZxm/pxrmFbisbK7/qJKtif2CNWJjaaAH5rfrs5OIajlHy3rO37vSaTQHWv+xWvZFQrs2Op2InxzG0qh5ncA== + +"@dhis2/app-service-config@3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@dhis2/app-service-config/-/app-service-config-3.9.0.tgz#8dc59d8de246f54057c0c685d5f94b4cbade6f73" + integrity sha512-OuRn2mJGrQQ8QIC+oIVYYpclB4LErRK2wtsuy/cXLfRbeUti1qWIh110rgd1hnTx+BgRCs5s3NWdIQxS4hYGIQ== + +"@dhis2/app-service-data@3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@dhis2/app-service-data/-/app-service-data-3.9.0.tgz#37f528b5f7f589cbab8dcc7f997c1668bc6566a9" + integrity sha512-/FJgJhL6YGtIVNX5oaNmavkGmimrVHQsS8ueeUO4FvTjYXGlnnN3IuxypQcy/x4yiUyigbPgFJRnbC1J2af2fg== + dependencies: + react-query "^3.13.11" + +"@dhis2/app-service-offline@3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@dhis2/app-service-offline/-/app-service-offline-3.9.0.tgz#fe4f4a91a1da77554965f6a5fe6f6951d4c467f4" + integrity sha512-0q5zl0vw+a47Ab2qgu6hsZY5ybnH/ea43Vkk4aXYdgcf57xB8ck9DkIcNbc2e1+k9FhvimipxsgTZSbEA/8hJA== + dependencies: + lodash "^4.17.21" + "@dhis2/app-shell@10.2.0": version "10.2.0" resolved "https://registry.yarnpkg.com/@dhis2/app-shell/-/app-shell-10.2.0.tgz#2f69aa047dedb6545c75052d8969a80502f0d4a6" @@ -3961,6 +3995,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/post-robot@^10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/post-robot/-/post-robot-10.0.3.tgz#d1429085f2faf4c87f841dab4e51472457edbf31" + integrity sha512-y8ysuxddaG8V/oA1Ay6Err7nSADRa9Bv1rl0ZQpJ0qgdIQ7ks3CHcOsYL4qE8w75+/XYDS94dBeXDs0xexm3tA== + "@types/prettier@^2.1.5": version "2.7.2" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" @@ -5267,6 +5306,15 @@ bcryptjs@^2.3.0: resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= +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" + bfj@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2" @@ -6263,6 +6311,20 @@ crc@^3.4.4: dependencies: buffer "^5.1.0" +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@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -11926,6 +11988,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@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741" @@ -15251,6 +15324,11 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.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" @@ -16119,6 +16197,11 @@ 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== +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"