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:
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:
|
||||
|
||||
@@ -40,6 +40,7 @@ describe('Top level exports', () => {
|
||||
'DicomMetadataStore',
|
||||
'pubSubServiceInterface',
|
||||
'PubSubService',
|
||||
'PanelService',
|
||||
].sort();
|
||||
|
||||
const exports = Object.keys(OHIF).sort();
|
||||
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -27,4 +27,5 @@ export default interface Services {
|
||||
syncGroupService?: Record<string, unknown>;
|
||||
cornerstoneCacheService?: Record<string, unknown>;
|
||||
segmentationService?: Record<string, unknown>;
|
||||
panelService?: Record<string, unknown>;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user