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:
parent
e4e62e9e14
commit
5670a4d8d1
@ -1,6 +1,8 @@
|
||||
import { id } from './id';
|
||||
import React from 'react';
|
||||
|
||||
import { Types } from '@ohif/core';
|
||||
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import PanelSegmentation from './panels/PanelSegmentation';
|
||||
|
||||
@ -39,13 +41,13 @@ const extension = {
|
||||
commandsManager,
|
||||
configuration = {},
|
||||
}) => {},
|
||||
/**
|
||||
/**
|
||||
* PanelModule should provide a list of panels that will be available in OHIF
|
||||
* for Modes to consume and render. Each panel is defined by a {name,
|
||||
* iconName, iconLabel, label, component} object. Example of a panel module
|
||||
* is the StudyBrowserPanel that is provided by the default extension in OHIF.
|
||||
*/
|
||||
getPanelModule: ({ servicesManager, commandsManager, extensionManager }) => {
|
||||
getPanelModule: ({ servicesManager, commandsManager, extensionManager }): Types.Panel[] => {
|
||||
const wrappedPanelSegmentation = () => {
|
||||
return (
|
||||
<PanelSegmentation
|
||||
|
||||
@ -4,7 +4,6 @@ import { SegmentationGroupTable } from '@ohif/ui';
|
||||
import callInputDialog from './callInputDialog';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import callColorPickerDialog from './callColorPickerDialog';
|
||||
|
||||
export default function PanelSegmentation({
|
||||
servicesManager,
|
||||
|
||||
@ -158,6 +158,7 @@ function ViewerLayout({
|
||||
const { content, entry } = getComponent(id);
|
||||
|
||||
return {
|
||||
id: entry.id,
|
||||
iconName: entry.iconName,
|
||||
iconLabel: entry.iconLabel,
|
||||
label: entry.label,
|
||||
@ -227,6 +228,7 @@ function ViewerLayout({
|
||||
side="left"
|
||||
activeTabIndex={leftPanelDefaultClosed ? null : 0}
|
||||
tabs={leftPanelComponents}
|
||||
servicesManager={servicesManager}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
) : null}
|
||||
@ -248,6 +250,7 @@ function ViewerLayout({
|
||||
side="right"
|
||||
activeTabIndex={rightPanelDefaultClosed ? null : 0}
|
||||
tabs={rightPanelComponents}
|
||||
servicesManager={servicesManager}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
) : null}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Types } from '@ohif/core';
|
||||
import {
|
||||
PanelMeasurementTableTracking,
|
||||
PanelStudyBrowserTracking,
|
||||
@ -11,7 +12,7 @@ function getPanelModule({
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
}) {
|
||||
}): Types.Panel[] {
|
||||
return [
|
||||
{
|
||||
name: 'seriesList',
|
||||
@ -24,6 +25,7 @@ function getPanelModule({
|
||||
servicesManager,
|
||||
}),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'trackedMeasurements',
|
||||
iconName: 'tab-linear',
|
||||
|
||||
@ -57,6 +57,7 @@ const extensionDependencies = {
|
||||
};
|
||||
|
||||
function modeFactory() {
|
||||
let _activatePanelTriggersSubscriptions = [];
|
||||
return {
|
||||
// TODO: We're using this as a route segment
|
||||
// We should not be.
|
||||
@ -71,6 +72,8 @@ function modeFactory() {
|
||||
measurementService,
|
||||
toolbarService,
|
||||
toolGroupService,
|
||||
panelService,
|
||||
segmentationService,
|
||||
} = servicesManager.services;
|
||||
|
||||
measurementService.clearMeasurements();
|
||||
@ -121,6 +124,28 @@ function modeFactory() {
|
||||
'Crosshairs',
|
||||
'MoreTools',
|
||||
]);
|
||||
|
||||
// // ActivatePanel event trigger for when a segmentation or measurement is added.
|
||||
// // Do not force activation so as to respect the state the user may have left the UI in.
|
||||
// _activatePanelTriggersSubscriptions = [
|
||||
// ...panelService.addActivatePanelTriggers(dicomSeg.panel, [
|
||||
// {
|
||||
// sourcePubSubService: segmentationService,
|
||||
// sourceEvents: [
|
||||
// segmentationService.EVENTS.SEGMENTATION_PIXEL_DATA_CREATED,
|
||||
// ],
|
||||
// },
|
||||
// ]),
|
||||
// ...panelService.addActivatePanelTriggers(tracked.measurements, [
|
||||
// {
|
||||
// sourcePubSubService: measurementService,
|
||||
// sourceEvents: [
|
||||
// measurementService.EVENTS.MEASUREMENT_ADDED,
|
||||
// measurementService.EVENTS.RAW_MEASUREMENT_ADDED,
|
||||
// ],
|
||||
// },
|
||||
// ]),
|
||||
// ];
|
||||
},
|
||||
onModeExit: ({ servicesManager }) => {
|
||||
const {
|
||||
@ -131,6 +156,9 @@ function modeFactory() {
|
||||
cornerstoneViewportService,
|
||||
} = servicesManager.services;
|
||||
|
||||
_activatePanelTriggersSubscriptions.forEach(sub => sub.unsubscribe());
|
||||
_activatePanelTriggersSubscriptions = [];
|
||||
|
||||
toolbarService.reset();
|
||||
toolGroupService.destroy();
|
||||
syncGroupService.destroy();
|
||||
@ -162,7 +190,7 @@ function modeFactory() {
|
||||
props: {
|
||||
leftPanels: [tracked.thumbnailList],
|
||||
rightPanels: [dicomSeg.panel, tracked.measurements],
|
||||
// rightPanelDefaultClosed: true, // optional prop to start with collapse panels
|
||||
rightPanelDefaultClosed: true,
|
||||
viewports: [
|
||||
{
|
||||
namespace: tracked.viewport,
|
||||
|
||||
@ -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 };
|
||||
|
||||
61
platform/core/src/services/PanelService/PanelService.ts
Normal file
61
platform/core/src/services/PanelService/PanelService.ts
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
3
platform/core/src/services/PanelService/index.ts
Normal file
3
platform/core/src/services/PanelService/index.ts
Normal file
@ -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;
|
||||
};
|
||||
|
||||
30
platform/core/src/types/PanelModule.ts
Normal file
30
platform/core/src/types/PanelModule.ts
Normal 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,
|
||||
};
|
||||
@ -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
|
||||
|
||||
@ -25,6 +25,10 @@ optionally provide its own `context` value.
|
||||
The `getPanelModule` receives an object containing the `ExtensionManager`'s
|
||||
associated `ServicesManager` and `CommandsManager`.
|
||||
|
||||
An extension can also trigger to activate/open a panel via the `PanelService` -
|
||||
either by explicitly calling `PanelService.activatePanel` or triggering panel
|
||||
activation when some other event fires.
|
||||
|
||||
```jsx
|
||||
import PanelMeasurementTable from './PanelMeasurementTable.js';
|
||||
|
||||
@ -64,7 +68,15 @@ providing the component with its.
|
||||
|
||||
New: You can easily add multiple panels to the left/right side of the viewer
|
||||
using the mode configuration. As seen below, the `leftPanels` and `rightPanels`
|
||||
accept an `Array` of the `IDs`.
|
||||
accept an `Array` of the `IDs`. The mode configuration also allows for either (or
|
||||
both) side panels to be closed by default. In the code below, the right panel
|
||||
is closed by default. The mode can optionally add event triggers to
|
||||
the `PanelService` that when fired will cause a side panel that was defaulted
|
||||
closed to open. In the code below, the right side panel, that contains the
|
||||
`trackedMeasurements` panel, is triggered to open when a measurement is added.
|
||||
Note that once a default closed side panel has been opened once,
|
||||
only a `PanelService.EVENTS.ACTIVATE_PANEL` event with `forceActive === true`
|
||||
will cause it open (again).
|
||||
|
||||
```js
|
||||
|
||||
@ -79,6 +91,7 @@ const id = 'viewer'
|
||||
const version = '3.0.0
|
||||
|
||||
function modeFactory({ modeConfiguration }) {
|
||||
let _activatePanelTriggersSubscriptions = [];
|
||||
return {
|
||||
id,
|
||||
routes: [
|
||||
@ -94,12 +107,35 @@ function modeFactory({ modeConfiguration }) {
|
||||
rightPanels: [
|
||||
'@ohif/extension-measurement-tracking.panelModule.trackedMeasurements',
|
||||
],
|
||||
rightPanelDefaultClosed: true,
|
||||
viewports,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
onModeEnter: ({ servicesManager }) => {
|
||||
const {
|
||||
measurementService,
|
||||
panelService,
|
||||
} = servicesManager.services;
|
||||
|
||||
_activatePanelTriggersSubscriptions = [
|
||||
...panelService.addActivatePanelTriggers('@ohif/extension-measurement-tracking.panelModule.trackedMeasurements', [
|
||||
{
|
||||
sourcePubSubService: measurementService,
|
||||
sourceEvents: [
|
||||
measurementService.EVENTS.MEASUREMENT_ADDED,
|
||||
measurementService.EVENTS.RAW_MEASUREMENT_ADDED,
|
||||
],
|
||||
},
|
||||
]),
|
||||
];
|
||||
},
|
||||
onModeExit: () => {
|
||||
_activatePanelTriggersSubscriptions.forEach(sub => sub.unsubscribe());
|
||||
_activatePanelTriggersSubscriptions = [];
|
||||
},
|
||||
extensions: extensionDependencies
|
||||
};
|
||||
}
|
||||
|
||||
49
platform/docs/docs/platform/services/data/PanelService.md
Normal file
49
platform/docs/docs/platform/services/data/PanelService.md
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
sidebar_label: Panel Service
|
||||
---
|
||||
|
||||
# Panel Service
|
||||
|
||||
## Overview
|
||||
|
||||
The Panel Service provides for activating/showing a panel that was registered
|
||||
via the `getPanelModule` extension method. Such panels can be either explicitly
|
||||
activated or implicitly triggered to activate when some other event occurs.
|
||||
|
||||
## Events
|
||||
|
||||
The following events are published in `PanelService`.
|
||||
|
||||
| Event | Description |
|
||||
| --------------------- | ------------------------------------------------------ |
|
||||
| ACTIVATE__PANEL | Fires a `ActivatePanelEvent` when a particular panel should be activated (i.e. shown). |
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Panel Activation
|
||||
|
||||
- `activatePanel`: Fires the `ACTIVATE_PANEL` event for a particular panel (id).
|
||||
An optional `forceActive` flag can be passed that when `true` "forces" a
|
||||
panel to show. Ultimately, it is up to a panel's container whether it
|
||||
is appropriate to activate/show the panel. For instance, if the user opened and then
|
||||
closed a side panel that contains the panel to activate, that side panel
|
||||
may decide that the user knows best and will not open the panel (again).
|
||||
|
||||
- `addActivatePanelTriggers`: Creates and returns event subscriptions that when
|
||||
fired will activate the specified panel with an optional `forceActive` flag
|
||||
(see `activatePanel`). This allows for panel activation to be directly triggered
|
||||
by some other event(s). When the triggers are no longer needed, simply
|
||||
unsubscribe to the returned subscriptions. For example, a panel
|
||||
for tracking measurements might get activated every time the
|
||||
`MeasurementService` fires a `MEASUREMENT_ADDED` event like this:
|
||||
```js
|
||||
panelService.addActivatePanelTriggers('measurement-tracking-panel-id', [
|
||||
sourcePubSubService: measurementService,
|
||||
sourceEvents: [
|
||||
measurementService.EVENTS.MEASUREMENT_ADDED,
|
||||
measurementService.EVENTS.RAW_MEASUREMENT_ADDED,
|
||||
],
|
||||
]);
|
||||
```
|
||||
@ -20,6 +20,7 @@ We maintain the following non-ui Services:
|
||||
- [Toolbar Service](../data/ToolBarService.md)
|
||||
- [Measurement Service](../data/MeasurementService.md)
|
||||
- [Customization Service](customization-service.md)
|
||||
- [Panel Service](../data/PanelService.md)
|
||||
|
||||
## Service Architecture
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SwiperCore, {
|
||||
A11y,
|
||||
@ -11,6 +11,8 @@ import SwiperCore, {
|
||||
} from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
|
||||
import { PanelService, ServicesManager, Types } from '@ohif/core';
|
||||
|
||||
import { Button, Icon, IconButton, Tooltip } from '../';
|
||||
|
||||
import 'swiper/css';
|
||||
@ -67,15 +69,23 @@ const position = {
|
||||
};
|
||||
|
||||
const SidePanel = ({
|
||||
servicesManager,
|
||||
side,
|
||||
className,
|
||||
activeTabIndex: activeTabIndexProp,
|
||||
tabs,
|
||||
}) => {
|
||||
const panelService: PanelService = servicesManager.services.panelService;
|
||||
|
||||
const { t } = useTranslation('SidePanel');
|
||||
|
||||
// Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
|
||||
// Thus going to the Study List page and back to the viewer resets this flag for a SidePanel.
|
||||
const [hasBeenOpened, setHasBeenOpened] = useState(
|
||||
activeTabIndexProp !== null
|
||||
);
|
||||
const [panelOpen, setPanelOpen] = useState(activeTabIndexProp !== null);
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(activeTabIndexProp || 0);
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(activeTabIndexProp ?? 0);
|
||||
const swiperRef = useRef() as any;
|
||||
const [swiper, setSwiper] = useState<any>();
|
||||
|
||||
@ -102,6 +112,41 @@ const SidePanel = ({
|
||||
}
|
||||
}, [swiper]);
|
||||
|
||||
const updatePanelOpen = useCallback((panelOpen: boolean) => {
|
||||
setPanelOpen(panelOpen);
|
||||
if (panelOpen) {
|
||||
setHasBeenOpened(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateActiveTabIndex = useCallback(
|
||||
(activeTabIndex: number) => {
|
||||
setActiveTabIndex(activeTabIndex);
|
||||
updatePanelOpen(true);
|
||||
},
|
||||
[updatePanelOpen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const activatePanelSubscription = panelService.subscribe(
|
||||
panelService.EVENTS.ACTIVATE_PANEL,
|
||||
(activatePanelEvent: Types.ActivatePanelEvent) => {
|
||||
if (!hasBeenOpened || activatePanelEvent.forceActive) {
|
||||
const tabIndex = tabs.findIndex(
|
||||
tab => tab.id === activatePanelEvent.panelId
|
||||
);
|
||||
if (tabIndex !== -1) {
|
||||
updateActiveTabIndex(tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
activatePanelSubscription.unsubscribe();
|
||||
};
|
||||
}, [tabs, hasBeenOpened, panelService, updateActiveTabIndex]);
|
||||
|
||||
const getCloseStateComponent = () => {
|
||||
const _childComponents = Array.isArray(tabs) ? tabs : [tabs];
|
||||
return (
|
||||
@ -112,7 +157,7 @@ const SidePanel = ({
|
||||
side === 'left' ? 'pr-2 justify-end' : 'pl-2 justify-start'
|
||||
)}
|
||||
onClick={() => {
|
||||
setPanelOpen(prev => !prev);
|
||||
updatePanelOpen(prev => !prev);
|
||||
}}
|
||||
data-cy={`side-panel-header-${side}`}
|
||||
>
|
||||
@ -142,8 +187,7 @@ const SidePanel = ({
|
||||
size="initial"
|
||||
className="text-primary-active"
|
||||
onClick={() => {
|
||||
setActiveTabIndex(index);
|
||||
setPanelOpen(true);
|
||||
updateActiveTabIndex(index);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
@ -180,7 +224,7 @@ const SidePanel = ({
|
||||
tabs.length === 1 && 'mb-1'
|
||||
)}
|
||||
onClick={() => {
|
||||
setPanelOpen(prev => !prev);
|
||||
updatePanelOpen(prev => !prev);
|
||||
// slideToActivePanel();
|
||||
}}
|
||||
data-cy={`side-panel-header-${side}`}
|
||||
@ -215,8 +259,7 @@ const SidePanel = ({
|
||||
nextRef,
|
||||
tabs,
|
||||
activeTabIndex,
|
||||
setActiveTabIndex,
|
||||
setPanelOpen
|
||||
updateActiveTabIndex
|
||||
)}
|
||||
{/** carousel navigation with the arrows */}
|
||||
{/** only show carousel nav if tabs are more than 3 tabs */}
|
||||
@ -250,6 +293,7 @@ SidePanel.defaultProps = {
|
||||
};
|
||||
|
||||
SidePanel.propTypes = {
|
||||
servicesManager: PropTypes.instanceOf(ServicesManager),
|
||||
side: PropTypes.oneOf(['left', 'right']).isRequired,
|
||||
className: PropTypes.string,
|
||||
activeTabIndex: PropTypes.number,
|
||||
@ -273,8 +317,7 @@ function _getMoreThanOneTabLayout(
|
||||
nextRef: React.MutableRefObject<undefined>,
|
||||
tabs: any,
|
||||
activeTabIndex: any,
|
||||
setActiveTabIndex: React.Dispatch<any>,
|
||||
setPanelOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
updateActiveTabIndex
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
@ -309,8 +352,7 @@ function _getMoreThanOneTabLayout(
|
||||
)}
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setActiveTabIndex(index);
|
||||
setPanelOpen(true);
|
||||
updateActiveTabIndex(index);
|
||||
}}
|
||||
data-cy={`${obj.name}-btn`}
|
||||
>
|
||||
|
||||
@ -27,9 +27,10 @@ describe('OHIF MPR', () => {
|
||||
});
|
||||
|
||||
it('should render correctly the MPR', () => {
|
||||
cy.wait(1000);
|
||||
cy.wait(250);
|
||||
|
||||
cy.get(':nth-child(3) > [data-cy="study-browser-thumbnail"]').dblclick();
|
||||
cy.wait(250);
|
||||
cy.get('[data-cy="MPR"]').click();
|
||||
|
||||
cy.get('[data-cy="thumbnail-viewport-labels"]').should('have.length', 3);
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
UserAuthenticationService,
|
||||
errorHandler,
|
||||
CustomizationService,
|
||||
PanelService,
|
||||
// utils,
|
||||
} from '@ohif/core';
|
||||
|
||||
@ -58,6 +59,7 @@ async function appInit(appConfigOrFunc, defaultExtensions, defaultModes) {
|
||||
HangingProtocolService.REGISTRATION,
|
||||
CineService.REGISTRATION,
|
||||
UserAuthenticationService.REGISTRATION,
|
||||
PanelService.REGISTRATION,
|
||||
]);
|
||||
|
||||
errorHandler.getHTTPErrorHandler = () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user