Compare commits

...
Author SHA1 Message Date
ohif-bot 5cda5159da chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.0.2
 - @ohif/viewer@3.0.3
2019-12-11 15:41:29 +00:00
Danny Brown 115a3ec0b0 refactor: remove-labelling-manager
Igor/refactor/1267 remove labelling manager
2019-12-11 10:39:36 -05:00
Danny Brown ac82a111e7 Merge branch 'master' into igor/refactor/1267-remove-labelling-manager 2019-12-11 09:16:18 -05:00
Igor a22fd67e13 Fix config check in extension init 2019-12-11 06:54:41 -03:00
Danny Brown 91f0af058f Merge pull request #1278 from OHIF/dannyrb/ci/visual-test
ci: make sure we're updating the base branch for comparisons (percy)
2019-12-10 23:58:53 -05:00
dannyrb 5a1186388d Merge branch 'dannyrb/ci/visual-test' of github.com:OHIF/Viewers into dannyrb/ci/visual-test 2019-12-10 23:52:54 -05:00
dannyrb 2f0f92c095 ci: make sure we're updating the base branch for comparisons (percy) 2019-12-10 23:45:29 -05:00
Danny Brown 5c686268ab Merge pull request #1274 from OHIF/refactor/igor/1267-remove-labelling-manager
refactor: 💡 Remove LabellingManager and Cleanup LabellingFlow #1267
2019-12-10 23:24:14 -05:00
Danny Brown 974aabc9b2 Merge pull request #1276 from OHIF/dannyrb/ci/visual-test
ci: optional visual test step
2019-12-10 23:12:30 -05:00
Danny Brown 17e3b49b06 Merge branch 'master' into dannyrb/ci/visual-test 2019-12-10 23:08:09 -05:00
dannyrb e3c119489a cI: Better job name for percy 2019-12-10 23:07:54 -05:00
dannyrb c91b6dddd8 ci: optional visual test step 2019-12-10 22:17:21 -05:00
Igor 92548191e4 refactor: 💡 Remove LabellingManager and Clean LabellingFlow
Migrate logic from LabellingManager to LabellingFlow and cleanup
LabellingFlow component

Closes: #1267
2019-12-10 18:31:55 -03:00
Igor 68fcaef19a Cleanup labelling flow 2019-12-10 18:18:37 -03:00
Igor ef611e4849 Refactoring labellingflow to function component 2019-12-10 18:08:51 -03:00
Igor d128fd6bf0 Remove LabellingManager component 2019-12-10 17:41:43 -03:00
Igor 3bdd434488 Cleanup LabellingFlow and LabellingManager components 2019-12-10 17:29:40 -03:00
11 changed files with 264 additions and 285 deletions

No files matched your search

+46
View File
@@ -367,6 +367,31 @@ workflows:
requires:
- UNIT_TESTS
PR_OPTIONAL_VISUAL_TESTS:
jobs:
- AWAIT_APPROVAL:
type: approval
# Update hub.docker.org
- cypress/run:
name: 'Generate Percy Snapshots'
executor: cypress/browsers-chrome76
pre-steps:
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
add wait-on' # Use yarn latest
yarn: true
store_artifacts: false
working_directory: platform/viewer
build: npx cross-env QUICK_BUILD=true yarn run build
# start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
no-workspace: true # Don't persist workspace
post-steps:
- store_artifacts:
path: platform/viewer/cypress/screenshots
requires:
- AWAIT_APPROVAL
PR_OPTIONAL_DOCKER_PUBLISH:
jobs:
# https://circleci.com/docs/2.0/workflows/#holding-a-workflow-for-a-manual-approval
@@ -419,6 +444,27 @@ workflows:
filters:
branches:
only: master
# Update base branch snapshots
- cypress/run:
name: 'Generate Percy Snapshots'
executor: cypress/browsers-chrome76
pre-steps:
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
add wait-on' # Use yarn latest
yarn: true
store_artifacts: false
working_directory: platform/viewer
build: npx cross-env QUICK_BUILD=true yarn run build
# start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
no-workspace: true # Don't persist workspace
post-steps:
- store_artifacts:
path: platform/viewer/cypress/screenshots
filters:
branches:
only: master
- DOCKER_MASTER_PUBLISH:
requires:
- NPM_PUBLISH
+8
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.0.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.0.1...@ohif/extension-cornerstone@2.0.2) (2019-12-11)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.0.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.0.0...@ohif/extension-cornerstone@2.0.1) (2019-12-09)
**Note:** Version bump only for package @ohif/extension-cornerstone
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.0.1",
"version": "2.0.2",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
+10 -14
View File
@@ -102,7 +102,7 @@ export default function init({ servicesManager, configuration }) {
];
/* Add extension tools configuration here. */
const extensionToolsConfiguration = {
const internalToolsConfig = {
ArrowAnnotate: {
configuration: {
getTextCallback: (callback, eventDetails) =>
@@ -113,19 +113,15 @@ export default function init({ servicesManager, configuration }) {
},
};
const isEmpty = obj => Object.keys(obj).length < 1;
if (!isEmpty(configuration.tools) || !isEmpty(extensionToolsConfiguration)) {
/* Add tools with its custom props through extension configuration. */
tools.forEach(tool => {
const toolName = tool.name.replace('Tool', '');
const configurationToolProps = configuration.tools[toolName] || {};
const extensionToolProps = extensionToolsConfiguration[toolName];
let props = merge(extensionToolProps, configurationToolProps);
csTools.addTool(tool, props);
});
} else {
tools.forEach(tool => csTools.addTool(tool));
}
/* Add tools with its custom props through extension configuration. */
tools.forEach(tool => {
const toolName = tool.name.replace('Tool', '');
const externalToolsConfig = configuration.tools || {};
const externalToolProps = externalToolsConfig[toolName] || {};
const internalToolProps = internalToolsConfig[toolName] || {};
const props = merge(internalToolProps, externalToolProps);
csTools.addTool(tool, props);
});
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
+8
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.
## [3.0.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.0.2...@ohif/viewer@3.0.3) (2019-12-11)
**Note:** Version bump only for package @ohif/viewer
## [3.0.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.0.1...@ohif/viewer@3.0.2) (2019-12-11)
**Note:** Version bump only for package @ohif/viewer
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "3.0.2",
"version": "3.0.3",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -46,7 +46,7 @@
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^2.0.2",
"@ohif/extension-cornerstone": "^2.0.1",
"@ohif/extension-cornerstone": "^2.0.2",
"@ohif/extension-dicom-html": "^1.0.2",
"@ohif/extension-dicom-microscopy": "^0.50.6",
"@ohif/extension-dicom-pdf": "^1.0.0",
+1 -1
View File
@@ -70,5 +70,5 @@ window.config = {
// ~ Cornerstone Tools
{ commandName: 'setZoomTool', label: 'Zoom', keys: ['z'] },
],
cornerstoneExtensionConfig: { tools: {} },
cornerstoneExtensionConfig: {},
};
+3 -3
View File
@@ -9,7 +9,7 @@ import { hot } from 'react-hot-loader/root';
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
import ToolContextMenu from './connectedComponents/ToolContextMenu';
import LabellingManager from './components/Labelling/LabellingManager';
import LabellingFlow from './components/Labelling/LabellingFlow';
import {
SnackbarProvider,
@@ -183,7 +183,7 @@ class App extends Component {
>
<LabellingFlowProvider
service={UILabellingFlowService}
labellingComponent={LabellingManager}
labellingComponent={LabellingFlow}
commandsManager={commandsManager}
>
<ContextMenuProvider
@@ -220,7 +220,7 @@ class App extends Component {
<ModalProvider modal={OHIFModal} service={UIModalService}>
<LabellingFlowProvider
service={UILabellingFlowService}
labellingComponent={LabellingManager}
labellingComponent={LabellingFlow}
commandsManager={commandsManager}
>
<ContextMenuProvider
@@ -6,8 +6,7 @@
max-height: 500px;
}
.labellingComponent .selectedLabel,
.labellingComponent .selectedDescription {
.labellingComponent .selectedLabel, .labellingComponent .selectedDescription {
padding: 5px;
background-color: white;
width: 150px;
@@ -86,14 +85,12 @@
color: #337ab7;
}
.labellingComponent .commonButtons,
.labellingComponent.editDescription .editDescriptionButtons {
.labellingComponent .commonButtons, .labellingComponent.editDescription .editDescriptionButtons {
display: block;
margin-left: 55px;
}
.labellingComponent.editDescription .commonButtons,
.labellingComponent .editDescriptionButtons {
.labellingComponent.editDescription .commonButtons, .labellingComponent .editDescriptionButtons {
display: none;
}
@@ -1,112 +1,165 @@
import { Icon, SelectTree } from '@ohif/ui';
import React, { Component } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep';
import LabellingTransition from './LabellingTransition.js';
import OHIFLabellingData from './OHIFLabellingData.js';
import EditDescriptionDialog from './../EditDescriptionDialog/EditDescriptionDialog.js';
import './LabellingFlow.css';
export default class LabellingFlow extends Component {
static propTypes = {
measurementData: PropTypes.object.isRequired,
labellingDoneCallback: PropTypes.func.isRequired,
updateLabelling: PropTypes.func.isRequired,
initialTopDistance: PropTypes.number,
skipAddLabelButton: PropTypes.bool,
editLocation: PropTypes.bool,
editDescription: PropTypes.bool,
const LabellingFlow = ({
measurementData,
editLocation,
editDescription,
skipAddLabelButton,
updateLabelling,
labellingDoneCallback,
editDescriptionOnDialog,
}) => {
const [fadeOutTimer, setFadeOutTimer] = useState();
const [showComponent, setShowComponent] = useState(true);
const descriptionInput = useRef();
const [state, setState] = useState({
measurementData,
editLocation,
editDescription,
skipAddLabelButton,
});
useEffect(() => {
const newMeasurementData = cloneDeep(measurementData);
if (editDescription) {
newMeasurementData.description = undefined;
}
if (editLocation) {
newMeasurementData.location = undefined;
}
let newEditLocation = editLocation;
if (!editDescription && !editLocation) {
newEditLocation = true;
}
setState(state => ({
...state,
editLocation: newEditLocation,
measurementData: newMeasurementData,
}));
}, [editDescription, editLocation, measurementData]);
useEffect(() => {
if (descriptionInput.current) {
descriptionInput.current.focus();
}
}, [state]);
const relabel = event =>
setState(state => ({ ...state, editLocation: true }));
const setDescriptionUpdateMode = () => {
descriptionInput.current.focus();
setState(state => ({ ...state, editDescription: true }));
};
constructor(props) {
super(props);
const descriptionCancel = () => {
const { description = '' } = cloneDeep(state);
descriptionInput.current.value = description;
setState(state => ({ ...state, editDescription: false }));
};
const { location, locationLabel, description } = props.measurementData;
this.state = {
location,
locationLabel,
description,
skipAddLabelButton: props.skipAddLabelButton,
editDescription: props.editDescription,
editLocation: props.editLocation,
confirmationState: false,
displayComponent: true,
};
this.mainElement = React.createRef();
this.descriptionInput = React.createRef();
this.initialItems = OHIFLabellingData;
this.currentItems = cloneDeep(this.initialItems);
}
componentDidUpdate = () => {
if (this.state.editDescription) {
this.descriptionInput.current.focus();
const handleKeyPress = event => {
if (event.key === 'Enter') {
descriptionSave();
}
};
render() {
let mainElementClassName = 'labellingComponent';
if (this.state.editDescription) {
mainElementClassName += ' editDescription';
}
const descriptionSave = () => {
const description = descriptionInput.current.value;
updateLabelling({ description });
return (
<LabellingTransition
displayComponent={this.state.displayComponent}
onTransitionExit={this.props.labellingDoneCallback}
>
<>
<div
className={mainElementClassName}
ref={this.mainElement}
onMouseLeave={this.fadeOutAndLeave}
onMouseEnter={this.clearFadeOutTimer}
>
{this.labellingStateFragment()}
</div>
</>
</LabellingTransition>
);
}
labellingStateFragment = () => {
const {
skipAddLabelButton,
editLocation,
setState(state => ({
...state,
description,
locationLabel,
} = this.state;
editDescription: false,
}));
};
const selectTreeSelectCallback = (event, itemSelected) => {
const location = itemSelected.value;
const locationLabel = itemSelected.label;
updateLabelling({ location });
setState(state => ({
...state,
editLocation: false,
measurementData: {
...state.measurementData,
location,
locationLabel,
},
}));
};
const showLabelling = () => {
setState(state => ({
...state,
skipAddLabelButton: true,
editLocation: false,
}));
};
/*
* Waits for 1 sec to dismiss the labelling component.
*
*/
const fadeOutAndLeave = () =>
setFadeOutTimer(setTimeout(fadeOutAndLeaveFast, 1000));
const fadeOutAndLeaveFast = () => setShowComponent(false);
const clearFadeOutTimer = () => {
if (fadeOutTimer) {
clearTimeout(fadeOutTimer);
setFadeOutTimer(null);
}
};
const descriptionDialogUpdate = description => {
updateLabelling({ description });
labellingDoneCallback();
};
const labellingStateFragment = () => {
const { skipAddLabelButton, editLocation, measurementData } = state;
const { description, locationLabel, location } = measurementData;
if (!skipAddLabelButton) {
return (
<>
<button
type="button"
className="addLabelButton"
onClick={this.showLabelling}
>
{this.state.location ? 'Edit' : 'Add'} Label
</button>
</>
<button
type="button"
className="addLabelButton"
onClick={showLabelling}
>
{location ? 'Edit' : 'Add'} Label
</button>
);
} else {
if (editLocation) {
return (
<SelectTree
items={this.currentItems}
items={OHIFLabellingData}
columns={1}
onSelected={this.selectTreeSelectCallback}
onSelected={selectTreeSelectCallback}
selectTreeFirstTitle="Assign Label"
/>
);
} else {
return (
<>
<div
className="checkIconWrapper"
onClick={this.fadeOutAndLeaveFast}
>
<div className="checkIconWrapper" onClick={fadeOutAndLeaveFast}>
<Icon name="check" className="checkIcon" />
</div>
<div className="locationDescriptionWrapper">
@@ -114,10 +167,10 @@ export default class LabellingFlow extends Component {
<div className="description">
<input
id="descriptionInput"
ref={this.descriptionInput}
ref={descriptionInput}
defaultValue={description || ''}
autoComplete="off"
onKeyPress={this.handleKeyPress}
onKeyPress={handleKeyPress}
/>
</div>
</div>
@@ -125,14 +178,14 @@ export default class LabellingFlow extends Component {
<button
type="button"
className="commonButton left"
onClick={this.relabel}
onClick={relabel}
>
Relabel
</button>
<button
type="button"
className="commonButton right"
onClick={this.setDescriptionUpdateMode}
onClick={setDescriptionUpdateMode}
>
{description ? 'Edit ' : 'Add '}
Description
@@ -142,14 +195,14 @@ export default class LabellingFlow extends Component {
<button
type="button"
className="commonButton left"
onClick={this.descriptionCancel}
onClick={descriptionCancel}
>
Cancel
</button>
<button
type="button"
className="commonButton right"
onClick={this.descriptionSave}
onClick={descriptionSave}
>
Save
</button>
@@ -160,77 +213,51 @@ export default class LabellingFlow extends Component {
}
};
relabel = event => this.setState({ editLocation: true });
setDescriptionUpdateMode = () => {
this.descriptionInput.current.focus();
this.setState({ editDescription: true });
};
descriptionCancel = () => {
const { description = '' } = cloneDeep(this.state);
this.descriptionInput.current.value = description;
this.setState({ editDescription: false });
};
handleKeyPress = e => {
if (e.key === 'Enter') {
this.descriptionSave();
}
};
descriptionSave = () => {
const description = this.descriptionInput.current.value;
this.props.updateLabelling({ description });
this.setState({
description,
editDescription: false,
});
};
selectTreeSelectCallback = (event, itemSelected) => {
const location = itemSelected.value;
this.props.updateLabelling({ location });
this.setState({
editLocation: false,
confirmationState: true,
location: itemSelected.value,
locationLabel: itemSelected.label,
});
if (this.isTouchScreen) {
this.setTimeout = setTimeout(() => {
this.setState({
displayComponent: false,
});
}, 2000);
}
};
showLabelling = () => {
this.setState({
skipAddLabelButton: true,
editLocation: false,
});
};
fadeOutAndLeave = () => {
// Wait for 1 sec to dismiss the labelling component
this.fadeOutTimer = setTimeout(
() => this.setState({ displayComponent: false }),
1000
if (editDescriptionOnDialog) {
return (
<EditDescriptionDialog
onCancel={labellingDoneCallback}
onUpdate={descriptionDialogUpdate}
measurementData={state.measurementData}
/>
);
};
}
fadeOutAndLeaveFast = () => this.setState({ displayComponent: false });
return (
<LabellingTransition
displayComponent={showComponent}
onTransitionExit={labellingDoneCallback}
>
<>
<div
className={`labellingComponent ${state.editDescription &&
'editDescription'}`}
onMouseLeave={fadeOutAndLeave}
onMouseEnter={clearFadeOutTimer}
>
{labellingStateFragment()}
</div>
</>
</LabellingTransition>
);
};
clearFadeOutTimer = () => {
if (!this.fadeOutTimer) {
return;
}
LabellingFlow.propTypes = {
measurementData: PropTypes.object.isRequired,
labellingDoneCallback: PropTypes.func.isRequired,
updateLabelling: PropTypes.func.isRequired,
initialTopDistance: PropTypes.number,
skipAddLabelButton: PropTypes.bool,
editLocation: PropTypes.bool,
editDescription: PropTypes.bool,
editDescriptionOnDialog: PropTypes.bool,
};
clearTimeout(this.fadeOutTimer);
};
}
LabellingFlow.defaultProps = {
skipAddLabelButton: false,
editLocation: false,
editDescription: false,
editDescriptionOnDialog: false,
};
export default LabellingFlow;
@@ -1,103 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep';
import EditDescriptionDialog from './../EditDescriptionDialog/EditDescriptionDialog.js';
import LabellingFlow from './LabellingFlow.js';
import './LabellingManager.css';
export default class LabellingManager extends Component {
static propTypes = {
measurementData: PropTypes.object.isRequired,
labellingDoneCallback: PropTypes.func.isRequired,
updateLabelling: PropTypes.func.isRequired,
skipAddLabelButton: PropTypes.bool,
editLocation: PropTypes.bool,
editDescription: PropTypes.bool,
editDescriptionOnDialog: PropTypes.bool,
};
static defaultProps = {
skipAddLabelButton: false,
editLocation: false,
editDescription: false,
editDescriptionOnDialog: false,
};
constructor(props) {
super(props);
const measurementData = cloneDeep(props.measurementData);
this.treatMeasurementData(measurementData);
let editLocation = props.editLocation;
if (!props.editDescription && !props.editLocation) {
editLocation = true;
}
this.state = {
skipAddLabelButton: props.skipAddLabelButton,
editLocation: editLocation,
editDescription: props.editDescription,
editDescriptionOnDialog: props.editDescriptionOnDialog,
measurementData: measurementData,
};
}
componentDidMount = () => {
document.addEventListener('touchstart', this.onTouchStart);
};
componentWillUnmount = () => {
document.removeEventListener('touchstart', this.onTouchStart);
};
render() {
return this.getRenderComponent();
}
getRenderComponent = () => {
const {
editLocation,
editDescription,
editDescriptionOnDialog,
measurementData,
} = this.state;
if (editDescriptionOnDialog) {
return (
<EditDescriptionDialog
onCancel={this.props.labellingDoneCallback}
onUpdate={this.descriptionDialogUpdate}
measurementData={measurementData}
/>
);
}
if (editLocation || editDescription) {
return <LabellingFlow {...this.props} />;
}
};
treatMeasurementData = measurementData => {
const { editDescription, editLocation } = this.props;
if (editDescription) {
measurementData.description = undefined;
}
if (editLocation) {
measurementData.location = undefined;
}
};
responseDialogUpdate = response => {
this.props.updateLabelling({ response });
this.props.labellingDoneCallback();
};
descriptionDialogUpdate = description => {
this.props.updateLabelling({ description });
this.props.labellingDoneCallback();
};
}