Compare commits

...

8 Commits

Author SHA1 Message Date
ohif-bot
4293c073f0 chore(release): publish [skip ci]
- @ohif/extension-dicom-p10-downloader@0.1.1
 - @ohif/extension-vtk@1.6.1
 - @ohif/core@2.9.7
 - @ohif/viewer@4.2.2
2020-06-04 11:02:53 +00:00
Emanuel Fiuza de Oliveira
62a2cd5ff3
Fix: #1130 Fixing support for download of an entire study (#1753)
Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-04 12:01:05 +01:00
ohif-bot
2d827c2a49 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.1
 - @ohif/viewer@4.2.1
2020-06-04 10:27:47 +00:00
Igor Octaviano
a8305772d0
fix: 🐛 Infinite frame index change when quickly jumping between (#1734)
* Remove debounce and use react-cornerstone-viewport native debounce time

* fix: 🐛 Infinite frame index change when quickly jumping between

This change removes the debounced callback function from the app and
instead uses the native debounce time from react-cornerstone-viewport.
This change allows the application to set its state in a more controled
way.

 Closes: #1733

* Bump rcv version

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-04 11:25:19 +01:00
ohif-bot
eab115e70c chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.0
 - @ohif/extension-dicom-microscopy@0.51.0
 - @ohif/extension-dicom-rt@0.2.0
 - @ohif/extension-dicom-segmentation@0.3.0
 - @ohif/extension-vtk@1.6.0
 - @ohif/ui@1.5.0
 - @ohif/viewer@4.2.0
2020-06-04 09:54:24 +00:00
Rodrigo Antinarelli
c02b232b0c
feat: 🎸 1729 - error boundary wrapper (#1764)
* Add error boundaries

* Fix PWA e2e.

* feat: ErrorBoundaryDialog

* replace component to use ErrorBoundaryDialog

* add proptypes

* fix context

* remove ErrorBoundary from extensions

Co-authored-by: igoroctaviano <igoroctaviano@gmail.com>
Co-authored-by: James A. Petts <jamesapetts@gmail.com>
2020-06-04 10:52:30 +01:00
ohif-bot
f01640d009 chore(release): publish [skip ci]
- @ohif/viewer@4.1.0
2020-05-15 18:59:23 +00:00
Steve Pieper
63fd65690c
feat: expose some app internals as window.app (#1735)
* feat: expose some app internals as window.app

This can help developers explore and access some
internal functionaltiy for debugging in the console.

For example, this command can download the currently
viewed study:

ohif.app.commandsManager.runCommand("downloadAndZip", {listOfUIDs: [window.location.href.split("/").pop()]})

TODO: collect this example and other handy functions on a wiki page

Co-authored-by: dannyrb <danny.ri.brown@gmail.com>

* fix: typo in window.ohif.app assignment

Co-authored-by: dannyrb <danny.ri.brown@gmail.com>
2020-05-15 14:55:54 -04:00
46 changed files with 526 additions and 178 deletions

View File

@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.8.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.0...@ohif/extension-cornerstone@2.8.1) (2020-06-04)
### Bug Fixes
* 🐛 Infinite frame index change when quickly jumping between ([#1734](https://github.com/OHIF/Viewers/issues/1734)) ([a830577](https://github.com/OHIF/Viewers/commit/a8305772d0fbec506549ad7ea08504a8bc3a4f39)), closes [#1733](https://github.com/OHIF/Viewers/issues/1733)
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.3...@ohif/extension-cornerstone@2.8.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [2.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.2...@ohif/extension-cornerstone@2.7.3) (2020-05-12)

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.7.3",
"version": "2.8.1",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@ -51,6 +51,6 @@
"classnames": "^2.2.6",
"lodash.merge": "^4.6.2",
"lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "2.3.8"
"react-cornerstone-viewport": "2.3.9"
}
}

View File

@ -4,7 +4,6 @@ import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
import OHIF from '@ohif/core';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import debounce from 'lodash.debounce';
const { StackManager } = OHIF.utils;
@ -216,9 +215,10 @@ class OHIFCornerstoneViewport extends Component {
});
}
const debouncedNewImageHandler = debounce(({ currentImageIdIndex, sopInstanceUid }) => {
const newImageHandler = ({ currentImageIdIndex, sopInstanceUid }) => {
const { displaySet } = this.props.viewportData;
const { StudyInstanceUID } = displaySet;
if (currentImageIdIndex > 0) {
this.props.onNewImage({
StudyInstanceUID,
@ -227,7 +227,7 @@ class OHIFCornerstoneViewport extends Component {
activeViewportIndex: viewportIndex,
});
}
}, 700);
};
return (
<>
@ -235,7 +235,8 @@ class OHIFCornerstoneViewport extends Component {
viewportIndex={viewportIndex}
imageIds={imageIds}
imageIdIndex={currentImageIdIndex}
onNewImage={debouncedNewImageHandler}
onNewImage={newImageHandler}
onNewImageDebounceTime={700}
// ~~ Connected (From REDUX)
// frameRate={frameRate}
// isPlaying={false}

View File

@ -39,7 +39,9 @@ export default {
const onNewImageHandler = jumpData => {
commandsManager.runCommand('jumpToImage', jumpData);
};
return <OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />;
return (
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
);
};
return ExtendedOHIFCornerstoneViewport;

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.51.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.9...@ohif/extension-dicom-microscopy@0.51.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [0.50.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.8...@ohif/extension-dicom-microscopy@0.50.9) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-microscopy",
"version": "0.50.9",
"version": "0.51.0",
"description": "OHIF extension for Dicom Microscopy",
"author": "OHIF",
"license": "MIT",

View File

@ -92,8 +92,8 @@ class DicomMicroscopyViewport extends Component {
{this.state.error ? (
<h2>{JSON.stringify(this.state.error)}</h2>
) : (
<div style={style} ref={this.container} />
)}
<div style={style} ref={this.container} />
)}
</div>
);
}

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.0...@ohif/extension-dicom-p10-downloader@0.1.1) (2020-06-04)
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23)

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-p10-downloader",
"version": "0.1.0",
"version": "0.1.1",
"description": "OHIF extension for downloading DICOM P10 files",
"author": "OHIF",
"license": "MIT",

View File

@ -2,6 +2,7 @@ import OHIF from '@ohif/core';
import {
save,
getDicomWebClientFromContext,
getStudyInstanceUIDFromStudies,
getSOPInstanceReferenceFromActiveViewport,
getSOPInstanceReferencesFromViewports,
} from './utils';
@ -41,6 +42,14 @@ export function getCommands(context) {
listOfUIDs
);
},
downloadAndZipStudy({ servers, studies, progress }) {
const dicomWebClient = getDicomWebClientFromContext(context, servers);
const listOfUIDs = getStudyInstanceUIDFromStudies(studies);
return save(
_downloadAndZip(dicomWebClient, listOfUIDs, { progress }),
listOfUIDs
);
},
downloadAndZipSeriesOnViewports({ servers, viewports, progress }) {
const dicomWebClient = getDicomWebClientFromContext(context, servers);
const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports);
@ -64,6 +73,11 @@ export function getCommands(context) {
commandFn: queue.bindSafe(actions.downloadAndZip, error),
storeContexts: ['servers'],
},
downloadAndZipStudy: {
commandFn: queue.bindSafe(actions.downloadAndZipStudy, error),
storeContexts: ['servers', 'studies'],
options: { progress },
},
downloadAndZipSeriesOnViewports: {
commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error),
storeContexts: ['servers', 'viewports'],

View File

@ -100,11 +100,16 @@ function save(promise, listOfUIDs) {
});
}
function getStudyInstanceUIDFromStudies(studies) {
return Object.keys(Object(Object(studies).studyData)).slice(0, 1);
}
export {
save,
validDicomUid,
getDicomWebClientFromConfig,
getDicomWebClientFromContext,
getStudyInstanceUIDFromStudies,
getSOPInstanceReferenceFromActiveViewport,
getSOPInstanceReferencesFromViewports,
};

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.4...@ohif/extension-dicom-rt@0.2.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [0.1.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.3...@ohif/extension-dicom-rt@0.1.4) (2020-05-14)

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.1.4",
"version": "0.2.0",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",

View File

@ -31,7 +31,18 @@ const refreshViewport = () => {
* @param {number} props.isOpen - isOpen
* @returns component
*/
const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }) => {
const RTPanel = ({
studies,
viewports,
activeIndex,
isOpen,
onContourItemClick,
activeContexts = [],
contexts = {}
}) => {
const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
const [selectedContour, setSelectedContour] = useState();
const DEFAULT_SET_INDEX = 0;
const DEFAULT_STATE = {
@ -119,33 +130,35 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
onClick={() => {
setSelectedContour(isSameContour ? null : ROINumber);
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (!toolState) {
return;
if (!toolState) {
return;
}
const imageIds = toolState.data[0].imageIds;
const module = cornerstoneTools.getModule('rtstruct');
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
state.selectedSet.SeriesInstanceUID,
ROINumber,
imageIds
);
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get('SOPInstanceUID', imageId);
const StudyInstanceUID = cornerstone.metaData.get('StudyInstanceUID', imageId);
onContourItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex
});
}
const imageIds = toolState.data[0].imageIds;
const module = cornerstoneTools.getModule('rtstruct');
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
state.selectedSet.SeriesInstanceUID,
ROINumber,
imageIds
);
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get('SOPInstanceUID', imageId);
const StudyInstanceUID = cornerstone.metaData.get('StudyInstanceUID', imageId);
onContourItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex
});
}}
label={`${ROIName} ${interpretedType}`}
index={ROINumber}

View File

@ -20,14 +20,21 @@ export default {
preRegistration({ servicesManager, configuration = {} }) {
init({ servicesManager, configuration });
},
getPanelModule({ commandsManager }) {
getPanelModule({ commandsManager, api }) {
const ExtendedRTPanel = props => {
const { activeContexts } = api.hooks.useAppContext();
const contourItemClickHandler = contourData => {
commandsManager.runCommand('jumpToImage', contourData);
};
return (
<RTPanel {...props} onContourItemClick={contourItemClickHandler} />
<RTPanel
{...props}
onContourItemClick={contourItemClickHandler}
activeContexts={activeContexts}
contexts={api.contexts}
/>
);
};

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.6...@ohif/extension-dicom-segmentation@0.3.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.5...@ohif/extension-dicom-segmentation@0.2.6) (2020-05-12)

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.2.6",
"version": "0.3.0",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",

View File

@ -49,8 +49,8 @@ const SegmentationPanel = ({
onConfigurationChange,
onDisplaySetLoadFailure,
onSelectedSegmentationChange,
activeContexts,
contexts,
activeContexts = [],
contexts = {},
}) => {
const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);

View File

@ -46,7 +46,7 @@ export default {
const onSegmentVisibilityChangeHandler = (segmentNumber, visible) => {
commandsManager.runCommand('setSegmentConfiguration', {
segmentNumber,
visible
visible,
});
};
@ -55,7 +55,7 @@ export default {
globalOpacity: configuration.fillAlpha,
outlineThickness: configuration.outlineWidth,
renderOutline: configuration.renderOutline,
visible: configuration.renderFill
visible: configuration.renderFill,
});
};

View File

@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.0...@ohif/extension-vtk@1.6.1) (2020-06-04)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.6...@ohif/extension-vtk@1.6.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [1.5.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.5...@ohif/extension-vtk@1.5.6) (2020-05-14)
**Note:** Version bump only for package @ohif/extension-vtk

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.5.6",
"version": "1.6.1",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@ -53,8 +53,8 @@
"react-vtkjs-viewport": "^0.9.0"
},
"devDependencies": {
"@ohif/core": "^2.9.6",
"@ohif/ui": "^1.4.4",
"@ohif/core": "^2.9.7",
"@ohif/ui": "^1.5.0",
"cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3",

View File

@ -1,3 +1,4 @@
import React from 'react';
import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js';
@ -16,7 +17,8 @@ const vtkExtension = {
id: 'vtk',
getViewportModule({ commandsManager }) {
return withCommandsManager(OHIFVTKViewport, commandsManager);
const ExtendedVTKViewport = props => <OHIFVTKViewport {...props} />;
return withCommandsManager(ExtendedVTKViewport, commandsManager);
},
getToolbarModule() {
return toolbarModule;

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.9.7](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.6...@ohif/core@2.9.7) (2020-06-04)
**Note:** Version bump only for package @ohif/core
## [2.9.6](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.5...@ohif/core@2.9.6) (2020-05-14)

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.9.6",
"version": "2.9.7",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",

View File

@ -13,7 +13,7 @@ const findMostRecentStructuredReport = studies => {
// Skip series that may not have instances yet
// This can happen if we have retrieved just the initial
// details about the series via QIDO-RS, but not the full metadata
if (!series.instances.length) {
if (!series.instances || !series.instances.length) {
return;
}

View File

@ -6,11 +6,12 @@ const defaultState = {
const servers = (state = defaultState, action) => {
switch (action.type) {
case 'SET_STUDY_DATA':
const updatedStudyData = cloneDeep(state).studyData;
updatedStudyData[action.StudyInstanceUID] = action.data;
case 'SET_STUDY_DATA': {
const updatedStudyData = cloneDeep(state.studyData);
updatedStudyData[action.StudyInstanceUID] = cloneDeep(action.data);
return Object.assign({}, state, { studyData: updatedStudyData });
}
default:
return state;
}

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.4.4...@ohif/ui@1.5.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [1.4.4](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.4.3...@ohif/ui@1.4.4) (2020-05-04)

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/ui",
"version": "1.4.4",
"version": "1.5.0",
"description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors",
"license": "MIT",
@ -53,6 +53,7 @@
"react-dnd-html5-backend": "^9.4.0",
"react-dnd-touch-backend": "^9.4.0",
"react-draggable": "^4.1.0",
"react-error-boundary": "^2.2.1",
"react-i18next": "^10.11.0",
"react-modal": "^3.11.1",
"react-with-direction": "1.3.0"

View File

@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ErrorBoundary } from 'react-error-boundary';
import './ErrorFallback.css';
const ErrorFallback = ({ error, componentStack, resetErrorBoundary }) => {
return (
<div className="ErrorFallback" role="alert">
<p>Something went wrong.</p>
<pre>{error.message}</pre>
<pre>{componentStack}</pre>
</div>
);
};
const OHIFErrorBoundary = ({
context = 'OHIF',
onReset = () => {},
onError = () => {},
fallbackComponent,
children,
}) => {
const onErrorHandler = (error, componentStack) => {
console.error(`${context} Error Boundary`, error, componentStack);
onError(error, componentStack);
};
const onResetHandler = () => {
onReset();
};
return (
<ErrorBoundary
FallbackComponent={fallbackComponent || ErrorFallback}
onReset={onResetHandler}
onError={onErrorHandler}
>
{children}
</ErrorBoundary>
);
};
OHIFErrorBoundary.propTypes = {
context: PropTypes.string,
onReset: PropTypes.func,
onError: PropTypes.func,
children: PropTypes.node.isRequired,
fallbackComponent: PropTypes.element,
};
export default OHIFErrorBoundary;

View File

@ -0,0 +1,4 @@
.ErrorFallback {
padding: 10px;
color: var(--active-color);
}

View File

@ -0,0 +1 @@
export { default as ErrorBoundary } from './ErrorBoundary';

View File

@ -7,7 +7,6 @@ import { AboutContent } from './content/aboutContent/AboutContent';
import { TabComponents, TabFooter } from './tabComponents';
import { HotkeyField } from './customForm';
import { LanguageSwitcher } from './languageSwitcher';
import { Checkbox } from './checkbox';
import { CineDialog } from './cineDialog';
import { ViewportDownloadForm } from './content/viewportDownloadForm';
@ -25,8 +24,10 @@ import {
} from './studyList';
import { ToolbarSection } from './toolbarSection';
import { Tooltip } from './tooltip';
import { ErrorBoundary } from './errorBoundary';
export {
ErrorBoundary,
ContextMenu,
Checkbox,
CineDialog,

View File

@ -29,6 +29,7 @@ import {
Tooltip,
AboutContent,
OHIFModal,
ErrorBoundary
} from './components';
import { useDebounce, useMedia } from './hooks';
@ -123,6 +124,7 @@ export {
DialogProvider,
withDialog,
useDialog,
ErrorBoundary,
// Hooks
useDebounce,
useMedia,

View File

@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.2.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.1...@ohif/viewer@4.2.2) (2020-06-04)
**Note:** Version bump only for package @ohif/viewer
## [4.2.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.0...@ohif/viewer@4.2.1) (2020-06-04)
**Note:** Version bump only for package @ohif/viewer
# [4.2.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.1.0...@ohif/viewer@4.2.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
# [4.1.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.0.0...@ohif/viewer@4.1.0) (2020-05-15)
### Features
* expose some app internals as window.app ([#1735](https://github.com/OHIF/Viewers/issues/1735)) ([63fd656](https://github.com/OHIF/Viewers/commit/63fd65690cba450721870a6222e0fb3ad71bb291))
# [4.0.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.11...@ohif/viewer@4.0.0) (2020-05-14)

View File

@ -1,7 +1,7 @@
describe('OHIF Microscopy Extension', () => {
before(() => {
cy.openStudyModality('SM');
cy.expectMinimumThumbnails(6);
cy.expectMinimumThumbnails(2);
});
it('checks if series thumbnails are being displayed', () => {

View File

@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "4.0.0",
"version": "4.2.2",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@ -48,18 +48,18 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^2.9.6",
"@ohif/extension-cornerstone": "^2.7.3",
"@ohif/core": "^2.9.7",
"@ohif/extension-cornerstone": "^2.8.1",
"@ohif/extension-dicom-html": "^1.2.2",
"@ohif/extension-dicom-microscopy": "^0.50.9",
"@ohif/extension-dicom-p10-downloader": "^0.1.0",
"@ohif/extension-dicom-microscopy": "^0.51.0",
"@ohif/extension-dicom-p10-downloader": "^0.1.1",
"@ohif/extension-dicom-pdf": "^1.0.3",
"@ohif/extension-dicom-rt": "^0.1.4",
"@ohif/extension-dicom-segmentation": "^0.2.6",
"@ohif/extension-dicom-rt": "^0.2.0",
"@ohif/extension-dicom-segmentation": "^0.3.0",
"@ohif/extension-lesion-tracker": "^0.2.0",
"@ohif/extension-vtk": "^1.5.6",
"@ohif/extension-vtk": "^1.6.1",
"@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.4.4",
"@ohif/ui": "^1.5.0",
"@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6",
"core-js": "^3.2.1",

View File

@ -13,6 +13,7 @@ import {
ModalProvider,
DialogProvider,
OHIFModal,
ErrorBoundary
} from '@ohif/ui';
import {
@ -71,6 +72,14 @@ let extensionManager;
// TODO[react] Use a provider when the whole tree is React
window.store = store;
window.ohif = window.ohif || {};
window.ohif.app = {
commandsManager,
hotkeysManager,
servicesManager,
extensionManager,
};
class App extends Component {
static propTypes = {
config: PropTypes.oneOfType([
@ -159,53 +168,57 @@ class App extends Component {
if (this._userManager) {
return (
<Provider store={store}>
<AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}>
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
modal={OHIFModal}
service={UIModalService}
>
<OHIFStandaloneViewer
userManager={this._userManager}
/>
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
</I18nextProvider>
</AppProvider>
</Provider>
<ErrorBoundary context='App'>
<Provider store={store}>
<AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}>
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
modal={OHIFModal}
service={UIModalService}
>
<OHIFStandaloneViewer
userManager={this._userManager}
/>
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
</I18nextProvider>
</AppProvider>
</Provider>
</ErrorBoundary>
);
}
return (
<Provider store={store}>
<AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
<OHIFStandaloneViewer />
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</I18nextProvider>
</AppProvider>
</Provider>
<ErrorBoundary context='App'>
<Provider store={store}>
<AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
<OHIFStandaloneViewer />
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</I18nextProvider>
</AppProvider>
</Provider>
</ErrorBoundary>
);
}

View File

@ -5,7 +5,7 @@ import { Route, Switch } from 'react-router-dom';
import { NProgress } from '@tanem/react-nprogress';
import { CSSTransition } from 'react-transition-group';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from '@ohif/ui';
import { ViewerbaseDragDropContext, ErrorBoundary } from '@ohif/ui';
import { SignoutCallbackComponent } from 'redux-oidc';
import asyncComponent from './components/AsyncComponent.js';
import * as RoutesUtil from './routes/routesUtil';
@ -191,7 +191,9 @@ class OHIFStandaloneViewer extends Component {
{match === null ? (
<></>
) : (
<Component match={match} location={this.props.location} />
<ErrorBoundary context={match.url}>
<Component match={match} location={this.props.location} />
</ErrorBoundary>
)}
</CSSTransition>
)}

View File

@ -0,0 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ErrorBoundary } from '@ohif/ui';
import { servicesManager } from './../../App';
const { UIModalService } = servicesManager.services;
const ErrorBoundaryDialog = ({ context, children }) => {
const handleOnError = (error, componentStack) => {
const ErrorDialog = () => (
<div className="ErrorFallback" role="alert">
<div>
<h3>
{context}: <span>{error.message}</span>
</h3>
</div>
<pre>{componentStack}</pre>
</div>
);
UIModalService.show({
content: ErrorDialog,
title: `${context}: ${error.message}`,
});
};
const fallbackComponent = () => (
<div className="ErrorFallback" role="alert">
<p>
Error rendering {context}. <br /> Check the browser console for more
details.
</p>
</div>
);
return (
<ErrorBoundary
fallbackComponent={fallbackComponent}
context={context}
onError={handleOnError}
>
{children}
</ErrorBoundary>
);
};
ErrorBoundaryDialog.propTypes = {
context: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
};
export default ErrorBoundaryDialog;

View File

@ -0,0 +1,3 @@
import ErrorBoundaryDialog from './ErrorBoundaryDialog';
export default ErrorBoundaryDialog;

View File

@ -12,7 +12,7 @@ import EmptyViewport from './EmptyViewport.js';
const { loadAndCacheDerivedDisplaySets } = utils;
const ViewportGrid = function (props) {
const ViewportGrid = function(props) {
const {
activeViewportIndex,
availablePlugins,
@ -24,7 +24,7 @@ const ViewportGrid = function (props) {
studies,
viewportData,
children,
isStudyLoaded
isStudyLoaded,
} = props;
const rowSize = 100 / numRows;
@ -54,7 +54,7 @@ const ViewportGrid = function (props) {
});
});
}
}, [studies, viewportData, isStudyLoaded]);
}, [studies, viewportData, isStudyLoaded, snackbar]);
const getViewportPanes = () =>
layout.viewports.map((layout, viewportIndex) => {

View File

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import './ViewportPane.css';
const ViewportPane = function(props) {
const ViewportPane = function (props) {
const { children, onDrop, viewportIndex, className: propClassName } = props;
const [{ hovered, highlighted }, drop] = useDrop({
accept: 'thumbnail',

View File

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
import OHIF from '@ohif/core';
const { clearViewportSpecificData } = OHIF.redux.actions;
const { clearViewportSpecificData, setStudyData } = OHIF.redux.actions;
const isActive = a => a.active === true;
const mapStateToProps = (state, ownProps) => {
@ -14,6 +14,9 @@ const mapStateToProps = (state, ownProps) => {
};
const mapDispatchToProps = dispatch => {
return {
setStudyData: (StudyInstanceUID, data) => {
dispatch(setStudyData(StudyInstanceUID, data));
},
clearViewportSpecificData: () => {
dispatch(clearViewportSpecificData());
},

View File

@ -2,8 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { MODULE_TYPES } from '@ohif/core';
import OHIF, { DICOMSR } from '@ohif/core';
import OHIF, { MODULE_TYPES, DICOMSR } from '@ohif/core';
import { withDialog } from '@ohif/ui';
import moment from 'moment';
import ConnectedHeader from './ConnectedHeader.js';
@ -11,6 +10,7 @@ import ToolbarRow from './ToolbarRow.js';
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ConnectedViewerMain from './ConnectedViewerMain.js';
import SidePanel from './../components/SidePanel.js';
import ErrorBoundaryDialog from './../components/ErrorBoundaryDialog';
import { extensionManager } from './../App.js';
// Contexts
@ -26,6 +26,7 @@ class Viewer extends Component {
PropTypes.shape({
StudyInstanceUID: PropTypes.string.isRequired,
StudyDate: PropTypes.string,
PatientID: PropTypes.string,
displaySets: PropTypes.arrayOf(
PropTypes.shape({
displaySetInstanceUID: PropTypes.string.isRequired,
@ -256,42 +257,44 @@ class Viewer extends Component {
</WhiteLabelingContext.Consumer>
{/* TOOLBAR */}
<ToolbarRow
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
isRightSidePanelOpen={this.state.isRightSidePanelOpen}
selectedLeftSidePanel={
this.state.isLeftSidePanelOpen
? this.state.selectedLeftSidePanel
: ''
}
selectedRightSidePanel={
this.state.isRightSidePanelOpen
? this.state.selectedRightSidePanel
: ''
}
handleSidePanelChange={(side, selectedPanel) => {
const sideClicked = side && side[0].toUpperCase() + side.slice(1);
const openKey = `is${sideClicked}SidePanelOpen`;
const selectedKey = `selected${sideClicked}SidePanel`;
const updatedState = Object.assign({}, this.state);
const isOpen = updatedState[openKey];
const prevSelectedPanel = updatedState[selectedKey];
// RoundedButtonGroup returns `null` if selected button is clicked
const isSameSelectedPanel =
prevSelectedPanel === selectedPanel || selectedPanel === null;
updatedState[selectedKey] = selectedPanel || prevSelectedPanel;
const isClosedOrShouldClose = !isOpen || isSameSelectedPanel;
if (isClosedOrShouldClose) {
updatedState[openKey] = !updatedState[openKey];
<ErrorBoundaryDialog context="ToolbarRow">
<ToolbarRow
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
isRightSidePanelOpen={this.state.isRightSidePanelOpen}
selectedLeftSidePanel={
this.state.isLeftSidePanelOpen
? this.state.selectedLeftSidePanel
: ''
}
selectedRightSidePanel={
this.state.isRightSidePanelOpen
? this.state.selectedRightSidePanel
: ''
}
handleSidePanelChange={(side, selectedPanel) => {
const sideClicked = side && side[0].toUpperCase() + side.slice(1);
const openKey = `is${sideClicked}SidePanelOpen`;
const selectedKey = `selected${sideClicked}SidePanel`;
const updatedState = Object.assign({}, this.state);
this.setState(updatedState);
}}
studies={this.props.studies}
/>
const isOpen = updatedState[openKey];
const prevSelectedPanel = updatedState[selectedKey];
// RoundedButtonGroup returns `null` if selected button is clicked
const isSameSelectedPanel =
prevSelectedPanel === selectedPanel || selectedPanel === null;
updatedState[selectedKey] = selectedPanel || prevSelectedPanel;
const isClosedOrShouldClose = !isOpen || isSameSelectedPanel;
if (isClosedOrShouldClose) {
updatedState[openKey] = !updatedState[openKey];
}
this.setState(updatedState);
}}
studies={this.props.studies}
/>
</ErrorBoundaryDialog>
{/*<ConnectedStudyLoadingMonitor studies={this.props.studies} />*/}
{/*<StudyPrefetcher studies={this.props.studies} />*/}
@ -299,37 +302,46 @@ class Viewer extends Component {
{/* VIEWPORTS + SIDEPANELS */}
<div className="FlexboxLayout">
{/* LEFT */}
<SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}>
{VisiblePanelLeft ? (
<VisiblePanelLeft
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
) : (
<ErrorBoundaryDialog context="LeftSidePanel">
<SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}>
{VisiblePanelLeft ? (
<VisiblePanelLeft
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
) : (
<ConnectedStudyBrowser
studies={this.state.thumbnails}
studyMetadata={this.props.studies}
/>
)}
</SidePanel>
</SidePanel>
</ErrorBoundaryDialog>
{/* MAIN */}
<div className={classNames('main-content')}>
<ConnectedViewerMain studies={this.props.studies} isStudyLoaded={this.props.isStudyLoaded} />
<ErrorBoundaryDialog context="ViewerMain">
<ConnectedViewerMain
studies={this.props.studies}
isStudyLoaded={this.props.isStudyLoaded}
/>
</ErrorBoundaryDialog>
</div>
{/* RIGHT */}
<SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}>
{VisiblePanelRight && (
<VisiblePanelRight
isOpen={this.state.isRightSidePanelOpen}
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
)}
</SidePanel>
<ErrorBoundaryDialog context="RightSidePanel">
<SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}>
{VisiblePanelRight && (
<VisiblePanelRight
isOpen={this.state.isRightSidePanelOpen}
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
)}
</SidePanel>
</ErrorBoundaryDialog>
</div>
</>
);
@ -349,7 +361,7 @@ export default withDialog(Viewer);
* @param {Study[]} studies
* @param {DisplaySet[]} studies[].displaySets
*/
const _mapStudiesToThumbnails = function (studies) {
const _mapStudiesToThumbnails = function(studies) {
return studies.map(study => {
const { StudyInstanceUID } = study;

View File

@ -154,11 +154,21 @@ const _sortStudyDisplaySet = (study, studyMetadata) => {
studyMetadata.sortDisplaySets(study.displaySets);
};
const _thinStudyData = study => {
return {
StudyInstanceUID: study.StudyInstanceUID,
series: study.series.map(item => ({
SeriesInstanceUID: item.SeriesInstanceUID
})),
}
};
function ViewerRetrieveStudyData({
server,
studyInstanceUIDs,
seriesInstanceUIDs,
clearViewportSpecificData,
setStudyData,
}) {
// hooks
const [error, setError] = useState(false);
@ -220,6 +230,7 @@ function ViewerRetrieveStudyData({
if (Array.isArray(studiesData) && studiesData.length > 0) {
// Map studies to new format, update metadata manager?
const studies = studiesData.map(study => {
setStudyData(study.StudyInstanceUID, _thinStudyData(study));
const studyMetadata = new OHIFStudyMetadata(
study,
study.StudyInstanceUID
@ -369,6 +380,7 @@ ViewerRetrieveStudyData.propTypes = {
seriesInstanceUIDs: PropTypes.array,
server: PropTypes.object,
clearViewportSpecificData: PropTypes.func.isRequired,
setStudyData: PropTypes.func.isRequired,
};
export default ViewerRetrieveStudyData;

View File

@ -1247,7 +1247,7 @@
pirates "^4.0.0"
source-map-support "^0.5.9"
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.6":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
@ -15603,10 +15603,10 @@ react-codemirror2@^6.0.0:
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-6.0.0.tgz#180065df57a64026026cde569a9708fdf7656525"
integrity sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag==
react-cornerstone-viewport@2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.8.tgz#7af8360f29bca986ae4e36b4e503269b88ddc52f"
integrity sha512-aiG2uVNrDY6SQx4t/HBxIA3zsMsCwT+6TpcXK9qSSoXhs+X6OTmYEKncWUqL0jtxU1yfh6JTUz8ARTg03gtF+A==
react-cornerstone-viewport@2.3.9:
version "2.3.9"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.9.tgz#f9761da8e536f0a217137c6ca1a983f5882249f9"
integrity sha512-qrhq8CbX/jq6b93cQjV2qC/mhHOvFBpxzxcHlvHzEQZt/rmRMcaYxCqjpNaNbUmmBu61wNkxesUVsggkPTTcqg==
dependencies:
classnames "^2.2.6"
date-fns "^2.2.1"
@ -15796,6 +15796,13 @@ react-dropzone@^10.1.7:
file-selector "^0.1.11"
prop-types "^15.7.2"
react-error-boundary@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-2.2.1.tgz#32ed74386a90482060cc2fea948bb7135465c4cb"
integrity sha512-8SZMkJRFUb0JuluHKwuUtkh5vvVWBg3O/bJIWgNMhMJv529aG//TmcphR3cSMhCWjz1vFZDb4taJd6pmwP5mEQ==
dependencies:
"@babel/runtime" "^7.9.6"
react-error-overlay@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.1.tgz#417addb0814a90f3a7082eacba7cee588d00da89"