diff --git a/docs/latest/SUMMARY.md b/docs/latest/SUMMARY.md index 61c9926f0..967252afd 100644 --- a/docs/latest/SUMMARY.md +++ b/docs/latest/SUMMARY.md @@ -26,6 +26,8 @@ - [Modules](advanced/extensions.md#modules) - [Registering](advanced/extensions.md#registering-extensions) - [OHIF Maintained](advanced/extensions.md#ohif-maintained-extensions) +- [Services](services/index.md) + - [UI](services/ui/index.md) - [Custom Tools](advanced/custom-tools.md) --- diff --git a/docs/latest/assets/img/canny-full.png b/docs/latest/assets/img/canny-full.png deleted file mode 100644 index cb8d0ee27..000000000 Binary files a/docs/latest/assets/img/canny-full.png and /dev/null differ diff --git a/docs/latest/assets/img/canny-logo-name.svg b/docs/latest/assets/img/canny-logo-name.svg deleted file mode 100644 index 99ca594b7..000000000 --- a/docs/latest/assets/img/canny-logo-name.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/latest/assets/img/canny-logo.svg b/docs/latest/assets/img/canny-logo.svg deleted file mode 100644 index e6bb0370c..000000000 --- a/docs/latest/assets/img/canny-logo.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - diff --git a/docs/latest/assets/img/dialog-example.gif b/docs/latest/assets/img/dialog-example.gif new file mode 100644 index 000000000..b6f9754c4 Binary files /dev/null and b/docs/latest/assets/img/dialog-example.gif differ diff --git a/docs/latest/assets/img/modal-example.gif b/docs/latest/assets/img/modal-example.gif new file mode 100644 index 000000000..834705de1 Binary files /dev/null and b/docs/latest/assets/img/modal-example.gif differ diff --git a/docs/latest/assets/img/notification-example.gif b/docs/latest/assets/img/notification-example.gif new file mode 100644 index 000000000..34d564cb4 Binary files /dev/null and b/docs/latest/assets/img/notification-example.gif differ diff --git a/docs/latest/assets/img/services.png b/docs/latest/assets/img/services.png new file mode 100644 index 000000000..569c046c0 Binary files /dev/null and b/docs/latest/assets/img/services.png differ diff --git a/docs/latest/assets/img/ui-services.png b/docs/latest/assets/img/ui-services.png new file mode 100644 index 000000000..dd5306377 Binary files /dev/null and b/docs/latest/assets/img/ui-services.png differ diff --git a/docs/latest/services/index.md b/docs/latest/services/index.md new file mode 100644 index 000000000..15064f3a7 --- /dev/null +++ b/docs/latest/services/index.md @@ -0,0 +1,117 @@ +# Services + +- [Overview](#/) +- [Anatomy](#/) +- [UI Services](#/) +- [Parting Words](#/) + +## Overview + +Services are a beefier version of [commands][commands]. They provide a set of +operations, often tied to some shared state, and are made available to +extensions via the `ServicesManager`. Services are particularly well suited to +address [cross-cutting concerns][cross-cutting-concerns]. + +
+ + UI Services Diagram + +
Diagram showing relationship between React Context and UI Service
+
+ +Each service should be: + +- self-contained +- able to fail and/or be removed without breaking the application +- completely interchangeable with another module implementing the same interface + +### An Example + +The simplest service return a new object that has a `name` property, and +methods/properties that give the service its functionality. The "Factory +Function" that creates the service is provided with the implementation (this is +slightly different for UI Services). + +```js +const _speak = () => { + console.warn('Speak is not implemented'); +}; + +/** + * Factory function to create `HelloWorldService` + * + * @param {object} implementation + * @param {function} implementation.speak - Speak's implementation + * @returns HelloWorldService + */ +export default function createHelloWorldService({ speak }) { + return { + name: 'HelloWorldService', + speak: speak || _speak, + }; +} +``` + +A service, once created, can be registered with the `ServicesManager` to make it +accessible to extensions. Similarly, the application code can access named +services from the `ServicesManager`. + +```js +// In the application +const speak = () => { + window.alert('HELLO WORLD'); +}; +const HelloWorldService = createHelloWorldService({ speak }); +const servicesManager = new ServicesManager(); + +servicesManager.registerService(HelloWorldService); + +// In an extension +const { HelloWorldService } = servicesManager.services; + +if (HelloWorldService) { + HelloWorldService.speak(); +} +``` + +### A work in progress + +Today, we only have maintained UI Services. You can read more about them below. + +In practice, services are live, but the patterns and guidance for them may shift +slightly as we begin to develop real-world features that utilize them. + +## UI Services + +A typical web application will have components and state for common UI like +modals, notifications, dialogs, etc. A UI service makes it possible to leverage +these components from an extension. + +We maintain the following UI Services: + +- [UIDialogService](./ui/ui-dialog-service.md) +- [UIModalService](./ui/ui-modal-service.md) +- [UINotificationService](./ui/ui-notification-service.md) + +You can read more about a specific service by selecting it in the above list, +and more about [UI services in general: here](./ui/index.md) + +## Parting Words + +Services are "concern-specific" code modules that can be consumed across layers. +We try to minimize the coupling they introduce by authoring services that are +able to fail or be removed. Related patterns that may reduce coupling include: + +- Pub/Sub +- Commands + + + + +[commands]: #/ +[core-services]: https://github.com/OHIF/Viewers/tree/master/platform/core/src/services +[services-manager]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/ServicesManager.js +[cross-cutting-concerns]: https://en.wikipedia.org/wiki/Cross-cutting_concern + diff --git a/docs/latest/services/ui/index.md b/docs/latest/services/ui/index.md new file mode 100644 index 000000000..d5b02989e --- /dev/null +++ b/docs/latest/services/ui/index.md @@ -0,0 +1,106 @@ +# UI Services + +- [Overview](#/) +- [Example](#/) +- [Tips & Tricks](#/) +- [Maintained Services](#/) + +## Overview + +A typical web application will have components and state for common UI like +modals, notifications, dialogs, etc. A UI service makes it possible to leverage +these components from an extension. + +
+ + UI Services Diagram + +
Diagram showing relationship between React Context and UI Service
+
+ +In `@ohif/core`, we have a collection of service factories. We select one we +would like our application to support, create an instance of it, and pass that +instance to our `ServicesManager` AND to a React component (in this example, +`ModalContext`'s provider). + +The `ModalContext`'s provider: + +- Exposes context values +- Exposes methods that leverage `useCallback` hooks +- Sets the service's implementation in a `useEffect` hook + +The `ServicesManager` is: + +- Passed to the `ExtensionManager` +- The `ExtensionManager` makes the `ServicesManager` available to: + - All of it's lifecycle hooks (`preInit`) + - Each "getModuleFunction" (`getToolbarModule`, `getPanelModule`, etc.) + +## An Example + +That's all fine and good, but it's still a little too abstract. What does this +translate to in practice? + +```js +// In the application +const UINotificationService = createUINotificationService(); +const servicesManager = new ServicesManager(); + +servicesManager.registerService(UINotificationService); + +// UI Service Provider +useEffect(() => { + if (service) { + service.setServiceImplementation({ hide, show }); + } +}, [service, hide, show]); + +// In an extension +const { UINotificationService } = servicesManager.services; + +if (UINotificationService) { + UINotificationService.show('Hello from the other side 👋'); +} +``` + +
+ + UI Notification Service Example + +
GIF showing successful call of UINotificationService from an extension.
+
+ +## Tips & Tricks + +It's important to remember that all we're doing is making it possible to control +bits of the application's UI from an extension. Here are a few non-obvious +takeaways worth mentioning: + +- Your application code should continue to use React context + (consumers/providers) as it normally would +- You can substitute our "out of the box" UI implementations with your own +- You can create and register your own UI services +- You can choose not to register a service or provide a service implementation +- In extensions, you can provide fallback/alternative behavior if an expected + service is not registered + - No `UIModalService`? Use the `UINotificationService` to notify users. +- While we don't have an examples of this, you can technically register a + service in an extension and expose it to the core application + +> Note: These are recommended patterns, not hard and fast rules. Following them +> will help reduce confusion and interoperability with the larger OHIF +> community, but they're not silver bullets. Please speak up, create an issue, +> if you would like to discuss new services or improvements to this pattern. + +## Maintained Services + +Our `@ohif/viewer` project is an example of how to glue together the different +parts and pieces of the OHIF Platform to create a polished and powerful product. +To accomplish that, we maintain several UI Services that you can use in your own +project, or provide alternative implementations for: + +| Name | Description | Docs | +| --------------------- | ----------- | ------------------------------------ | +| UIDialogService | | [Here](./ui-dialog-service.md) | +| UIModalService | | [Here](./ui-modal-service.md) | +| UINotificationService | | [Here](./ui-notification-service.md) | diff --git a/docs/latest/services/ui/ui-dialog-service.md b/docs/latest/services/ui/ui-dialog-service.md new file mode 100644 index 000000000..441845ce5 --- /dev/null +++ b/docs/latest/services/ui/ui-dialog-service.md @@ -0,0 +1,49 @@ +# UI Dialog Service + +Dialogs have similar characteristics to that of Modals, but often with a +streamlined focus. They can be helpful when: + +- We need to grab the user's attention +- We need user input +- We need to show additional information + +If you're curious about the DOs and DON'Ts of dialogs and modals, check out this +article: ["Best Practices for Modals / Overlays / Dialog Windows"][ux-article] + +
+ + UI Dialog Service Example + +
GIF showing successful call of UIDialogService from an extension.
+
+ +## Interface + +For a more detailed look on the options and return values each of these methods +is expected to support, [check out it's interface in `@ohif/core`][interface] + +| API Member | Description | +| -------------- | ------------------------------------------------------ | +| `create()` | Creates a new Dialog that is displayed until dismissed | +| `dismiss()` | Dismisses the specified dialog | +| `dismissAll()` | Dismisses all dialogs | + +## Implementations + +| Implementation | Consumer | +| ------------------------------------ | -------------------------- | +| [Dialog Provider][dialog-provider]\* | Baked into Dialog Provider | + +`*` - Denotes maintained by OHIF + +> 3rd Party implementers may be added to this table via pull requests. + + + + +[interface]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/UIDialogService/index.js +[dialog-provider]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/contextProviders/DialogProvider.js +[ux-article]: https://uxplanet.org/best-practices-for-modals-overlays-dialog-windows-c00c66cddd8c + diff --git a/docs/latest/services/ui/ui-modal-service.md b/docs/latest/services/ui/ui-modal-service.md new file mode 100644 index 000000000..89819da81 --- /dev/null +++ b/docs/latest/services/ui/ui-modal-service.md @@ -0,0 +1,50 @@ +# UI Modal Service + +Modals have similar characteristics to that of Dialogs, but are often larger, +and only allow for a single instance to be viewable at once. They also tend to +be centered, and not draggable. They're commonly used when: + +- We need to grab the user's attention +- We need user input +- We need to show additional information + +If you're curious about the DOs and DON'Ts of dialogs and modals, check out this +article: ["Best Practices for Modals / Overlays / Dialog Windows"][ux-article] + +
+ + UI Modal Service Example + +
GIF showing successful call of UIModalService from an extension.
+
+ +## Interface + +For a more detailed look on the options and return values each of these methods +is expected to support, [check out it's interface in `@ohif/core`][interface] + +| API Member | Description | +| ---------- | ------------------------------------- | +| `hide()` | Hides the open modal | +| `show()` | Shows the provided content in a modal | + +## Implementations + +| Implementation | Consumer | +| ---------------------------------- | ----------------------------- | +| [Modal Provider][modal-provider]\* | [OHIFModal][modal-consumer]\* | + +`*` - Denotes maintained by OHIF + +> 3rd Party implementers may be added to this table via pull requests. + + + + +[interface]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/UIModalService/index.js +[modal-provider]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/contextProviders/ModalProvider.js +[modal-consumer]: https://github.com/OHIF/Viewers/tree/master/platform/ui/src/components/ohifModal +[ux-article]: https://uxplanet.org/best-practices-for-modals-overlays-dialog-windows-c00c66cddd8c + diff --git a/docs/latest/services/ui/ui-notification-service.md b/docs/latest/services/ui/ui-notification-service.md new file mode 100644 index 000000000..bc49f5bef --- /dev/null +++ b/docs/latest/services/ui/ui-notification-service.md @@ -0,0 +1,51 @@ +# UI Notification Service + +Notifications can be annoying and disruptive. They can also deliver timely +helpful information, or expedite the user's workflow. Here is some high level +guidance on when and how to use them: + +- Notifications should be non-interfering (timely, relevant, important) +- We should only show small/brief notifications +- Notifications should be contextual to current behavior/actions +- Notifications can serve warnings (acting as a confirmation) + +If you're curious about the DOs and DON'Ts of notifications, check out this +article: ["How To Design Notifications For Better UX"][ux-article] + +
+ + UI Notification Service Example + +
GIF showing successful call of UINotificationService from an extension.
+
+ +## Interface + +For a more detailed look on the options and return values each of these methods +is expected to support, [check out it's interface in `@ohif/core`][interface] + +| API Member | Description | +| ---------- | --------------------------------------- | +| `hide()` | Hides the specified notification | +| `show()` | Creates and displays a new notification | + +## Implementations + +| Implementation | Consumer | +| ---------------------------------------- | ----------------------------------------- | +| [Snackbar Provider][snackbar-provider]\* | [SnackbarContainer][snackbar-container]\* | + +`*` - Denotes maintained by OHIF + +> 3rd Party implementers may be added to this table via pull requests. + + + + +[interface]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/UINotificationService/index.js +[snackbar-provider]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/contextProviders/SnackbarProvider.js +[snackbar-container]: https://github.com/OHIF/Viewers/blob/master/platform/ui/src/components/snackbar/SnackbarContainer.js +[ux-article]: https://uxplanet.org/how-to-design-notifications-for-better-ux-6fb0711be54d +