feat(PanelService): Left or right side panel to auto start as closed but auto open when needed (#3212)

* feat(SidePanel)
OHIF issue #3135
- Added a method to set a callback for a Panel to invoke when it is ready
to be shown (e.g. its data is loaded).
- Implemented such methods for both the segmentation and measurement panels.
- The SidePanel component now adds a callback to Panel components so that
it will automatically open a Panel that was initially closed and yet to
be opened.
- Updated the OHIF documentation accordingly.

* PR feedback
- added a PanelService that centralized much of the logic that existed in panel PanelModule
- the SidePanel subscribes to PanelService.EVENTS.ACTIVATE_PANEL for each of its child panels

* Removed the PanelMeasurementTableTracking setMeasurementPanelContentReadyCallback method.

* Made the forceActive flag in the PanelService optional and defaulted it to false.

* Fixed failing top level exports unit test.

* - PanelService subscriptions are now per panel (id) so subscribers do not
necessarily need to check the panel id in the event when it is fired
- PanelService activate panel trigger subscriptions are now returned
so that they can be (better) managed outside of the service
- updated/created the various documentation for panels and PanelService

* Clarified various documentation.
Moved the code to add the activate panel triggers out of the extensions
and into the longitudinal mode.

* Removed the openWhenPanelActivated flag.
PanelService now conforms to extending PubSubService like the other services.
Updated various documentation.

* Fixed failing e2e, mpr test.

* Renamed the ActivatePanelTriggers type properties.
The ExtensionManager now sets the id of various modules as a property on each of those modules.
This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2023-03-09 22:30:09 -05:00
1 parent e4e62e9e14
commit 5670a4d8d1
21 files changed
+294 -23

No files matched your search

@@ -274,9 +274,9 @@ export default class ExtensionManager {
// Default for most extension points,
// Just adds each entry ready for consumption by mode.
extensionModule.forEach(element => {
this.modulesMap[
`${extensionId}.${moduleType}.${element.name}`
] = element;
const id = `${extensionId}.${moduleType}.${element.name}`;
element.id = id;
this.modulesMap[id] = element;
});
break;
default:
+1
View File
@@ -40,6 +40,7 @@ describe('Top level exports', () => {
'DicomMetadataStore',
'pubSubServiceInterface',
'PubSubService',
'PanelService',
].sort();
const exports = Object.keys(OHIF).sort();
+3
View File
@@ -29,6 +29,7 @@ import {
PubSubService,
UserAuthenticationService,
CustomizationService,
PanelService,
} from './services';
import IWebApiDataSource from './DataSources/IWebApiDataSource';
@@ -74,6 +75,7 @@ const OHIF = {
DicomMetadataStore,
pubSubServiceInterface,
PubSubService,
PanelService,
};
export {
@@ -112,6 +114,7 @@ export {
pubSubServiceInterface,
PubSubService,
Types,
PanelService,
};
export { OHIF };
@@ -0,0 +1,61 @@
import { ActivatePanelTriggers } from '../../types';
import { Subscription } from '../../types/IPubSub';
import { PubSubService } from '../_shared/pubSubServiceInterface';
export const EVENTS = {
ACTIVATE_PANEL: 'event::panelService:activatePanel',
};
export default class PanelService extends PubSubService {
public static REGISTRATION = {
name: 'panelService',
create: (): PanelService => {
return new PanelService();
},
};
constructor() {
super(EVENTS);
}
/**
* Activates the panel with the given id. If the forceActive flag is false
* then it is up to the component containing the panel whether to activate
* it immediately or not. For instance, the panel might not be activated when
* the forceActive flag is false in the case where the user might have
* activated/displayed and then closed the panel already.
* Note that this method simply fires a broadcast event: ActivatePanelEvent.
* @param panelId the panel's id
* @param forceActive optional flag indicating if the panel should be forced to be activated or not
*/
activatePanel(panelId: string, forceActive = false): void {
this._broadcastEvent(EVENTS.ACTIVATE_PANEL, { panelId, forceActive });
}
/**
* Adds a mapping of events (activatePanelTriggers.sourceEvents) broadcast by
* activatePanelTrigger.sourcePubSubService that
* when fired/broadcasted must in turn activate the panel with the given id.
* The subscriptions created are returned such that they can be managed and unsubscribed
* as appropriate.
* @param panelId the id of the panel to activate
* @param activatePanelTriggers an array of triggers
* @param forceActive optional flag indicating if the panel should be forced to be activated or not
* @returns an array of the subscriptions subscribed to
*/
addActivatePanelTriggers(
panelId: string,
activatePanelTriggers: ActivatePanelTriggers[],
forceActive = false
): Subscription[] {
return activatePanelTriggers
.map(trigger =>
trigger.sourceEvents.map(eventName =>
trigger.sourcePubSubService.subscribe(eventName, () =>
this.activatePanel(panelId, forceActive)
)
)
)
.flat();
}
}
@@ -0,0 +1,3 @@
import PanelService from './PanelService';
export default PanelService;
+2
View File
@@ -17,6 +17,7 @@ import UserAuthenticationService from './UserAuthenticationService';
import CustomizationService from './CustomizationService';
import Services from '../types/Services';
import PanelService from './PanelService';
export {
Services,
@@ -36,4 +37,5 @@ export {
pubSubServiceInterface,
PubSubService,
UserAuthenticationService,
PanelService,
};
+4 -1
View File
@@ -1,6 +1,5 @@
import Consumer from './Consumer';
export default interface IPubSub {
subscribe: (eventName: string, callback: Consumer) => void;
_broadcastEvent: (
@@ -10,3 +9,7 @@ export default interface IPubSub {
_unsubscribe: (eventName: string, listenerId: string) => void;
_isValidEvent: (eventName: string) => boolean;
}
export type Subscription = {
unsubscribe: () => void;
};
+30
View File
@@ -0,0 +1,30 @@
import { PubSubService } from "../services";
type Panel = {
id?: string;
name: string;
iconName: string;
iconLabel: string;
label: string;
component: React.FC;
};
type ActivatePanelTriggers = {
sourcePubSubService: PubSubService;
sourceEvents: string[]
}
interface PanelEvent {
panelId: string;
}
interface ActivatePanelEvent extends PanelEvent {
forceActive: boolean;
}
export type {
ActivatePanelEvent,
ActivatePanelTriggers,
Panel,
PanelEvent,
};
+1
View File
@@ -27,4 +27,5 @@ export default interface Services {
syncGroupService?: Record<string, unknown>;
cornerstoneCacheService?: Record<string, unknown>;
segmentationService?: Record<string, unknown>;
panelService?: Record<string, unknown>;
}
+2
View File
@@ -9,6 +9,8 @@ export * from './AppConfig';
export * from './Consumer';
export * from './Command';
export * from './StudyMetadata';
export * from './PanelModule';
export * from './IPubSub';
/**
* Export the types used within the various services and managers, but