diff --git a/src/context/ContextGetHelp.jsx b/src/context/ContextGetHelp.jsx
index 2e3d473011..6b8abb31d7 100644
--- a/src/context/ContextGetHelp.jsx
+++ b/src/context/ContextGetHelp.jsx
@@ -108,85 +108,109 @@ class ContextGetHelp extends React.Component {
}
}
- renderOptions() {
- // if getHelp is not selected, don't show the additional options
- if (this.state.selectedIndex === -1) return null;
+ renderOptionsWithGetHelp = () => {
+ // Get name of the shortcut for the getHelp text
+ const initiatingTrigger = this.props.shortcut.getDisplayText();
- return (
-
- {this.state.getHelpOptions.map((option, index) => {
- // the parent 'get help' option is not included in the getHelpOptions array
- // but it is included as a selectedIndex, so there is an off by one that needs
- // to be calculated, hence the updatedIndex + 1 from the index of the getHelpOptions
- const updatedIndex = index + 1;
- return (
- { this.setSelectedIndex(updatedIndex); }}
- >
- {option.text}
-
- );
- })}
-
- );
- }
+ // Determine if we should display anything other than the getHelp option
+ const isGetHelpClosed = this.state.selectedIndex === -1;
+ // For any informational flags, define them here and chain them together into a single variable
+ // This variable will determine if we display a horizontal bar, separating information from actions
+ const isMissingParent = this.props.shortcut.isMissingParent;
+ const isInformationAvailable = isMissingParent || false;
+
+ // Create our icon class to signal the expanding/collapsing getHelp option, based on open/closedness
+ let iconClass = 'fa fa-angle-';
+ isGetHelpClosed ? iconClass += 'down' : iconClass += 'up';
- renderIsCompleteMessage() {
- const initiatingTrigger = this.props.shortcut.getDisplayText();
return (
-
+
- { this.setSelectedIndex(0); }}
+ onClick={() => { this.setSelectedIndex(-1); }}
>
- {initiatingTrigger} is already complete
+ get help with {initiatingTrigger}
+
-
+
+
+ {!isGetHelpClosed && this.state.getHelpOptions.map((option, index) => {
+ // the parent 'get help' option is not included in the getHelpOptions array
+ // but it is included as a selectedIndex, so there is an off by one that needs
+ // to be calculated, hence the updatedIndex + 1 from the index of the getHelpOptions
+ const updatedIndex = index + 1;
+ return (
+ { this.setSelectedIndex(updatedIndex); }}
+ >
+ {option.text}
+
+ );
+ })}
+
+ {(!isGetHelpClosed && isInformationAvailable) && this.renderHorizontalLine()}
+ {(!isGetHelpClosed && isMissingParent) && this.renderIsMissingParent()}
+
);
}
- renderIsMissingParent() {
+ renderIsCompleteMessage() {
const initiatingTrigger = this.props.shortcut.getDisplayText();
+ const infoIconiconClass = "fa fa-info-circle";
return (
-
- -
-
- {initiatingTrigger} is missing a parent
-
-
-
+
+
+
+ {initiatingTrigger} is already complete
+
+
);
}
+ renderIsMissingParent() {
+ const shortcut = this.props.shortcut;
+ const initiatingTrigger = shortcut.getDisplayText();
+ const potentialParentText = shortcut.potentialParents.map(parentID => this.props.shortcutManager.getShortcutLabel(parentID)).join(", or");
+ const infoIconiconClass = "fa fa-info-circle";
+ return (
+
+
+
+ {initiatingTrigger} needs more context, try mentioning {potentialParentText} beforehand
+
+
+ );
+ }
+ renderHorizontalLine() {
+ return (
+
+ );
+ }
render() {
- // If the shortcut we're responsible for is missing a parent, display a message to the user to avoid confusion
- if (!this.props.shortcut.hasParentContext() && this.props.shortcut.hasChildren()) return this.renderIsMissingParent();
- // If the shortcut we're responsible for is complete, display a message to the user to avoid confusion
- if (this.props.shortcut.isComplete) return this.renderIsCompleteMessage();
- // Else we should display all our getHelp message
- const initiatingTrigger = this.props.shortcut.getDisplayText();
- let iconClass = 'fa fa-angle-';
- this.state.selectedIndex === -1 ? iconClass += 'down' : iconClass += 'up';
+ // Decide the list content and render whatever it is in the UL element
+ let listContent = null;
+ if (this.props.shortcut.isMissingParent && this.props.shortcut.hasChildren()) {
+ // If the shortcut we're responsible for is missing a parent but is already expanded, display a message to the user to avoid confusion
+ listContent = this.renderIsMissingParent();
+ } else if (this.props.shortcut.isComplete) {
+ // Else, if the shortcut we're responsible for is complete, display a message to the user to avoid confusion
+ listContent = this.renderIsCompleteMessage();
+ } else {
+ // Else we should display all our getHelp message
+ listContent = this.renderOptionsWithGetHelp();
+ }
+
return (
- - { this.setSelectedIndex(0); }}
- >
-
- get help with {initiatingTrigger}
-
-
-
- {this.renderOptions()}
+ {listContent}
);
}
diff --git a/src/context/ContextGetHelp.scss b/src/context/ContextGetHelp.scss
index 76f51120e9..da074eee13 100644
--- a/src/context/ContextGetHelp.scss
+++ b/src/context/ContextGetHelp.scss
@@ -28,17 +28,19 @@ ul.context-get-help {
margin-bottom: 5px;
.context-get-help-text {
- span {
+ span.fa-angle-up, span.fa-angle-down {
+ padding: 0;
padding-left: 15px;
}
}
+ .context-information-text {
+ span.fa-info-circle {
+ padding: 0 5px 0 0;
+ }
+ }
}
.context-get-help-options {
- li:first-child {
- border-top: 1px solid $line-gray;
- }
-
li:last-child {
margin-bottom: 5px;
}
diff --git a/src/notes/FluxNotesEditor.jsx b/src/notes/FluxNotesEditor.jsx
index 7eb07be909..29dcb8589d 100644
--- a/src/notes/FluxNotesEditor.jsx
+++ b/src/notes/FluxNotesEditor.jsx
@@ -1787,6 +1787,7 @@ class FluxNotesEditor extends React.Component {
onSelected={this.onCompletionComponentValueSelection}
closePortal={this.closeCompletionPortal}
shortcut={this.state.completionComponentShortcut}
+ shortcutManager={this.props.shortcutManager}
state={this.state.state}
insertShortcut={this.insertShortcut}
/>
diff --git a/src/notes/SuggestionPortalPlaceholderSearchIndex.jsx b/src/notes/SuggestionPortalPlaceholderSearchIndex.jsx
index f7e27b432f..24d84e2173 100644
--- a/src/notes/SuggestionPortalPlaceholderSearchIndex.jsx
+++ b/src/notes/SuggestionPortalPlaceholderSearchIndex.jsx
@@ -15,7 +15,7 @@ class SuggestionPortalPlaceholderSearchIndex extends SuggestionPortalSearchIndex
const relevantShortcuts = [];
placeholders.forEach((placeholder) => {
- const triggers = this.shortcutManager.getTriggersForShortcut(placeholder.id);
+ const triggers = this.shortcutManager.getTriggersWithoutLabelForShortcut(placeholder.id);
triggers.forEach((trigger) => {
const triggerNoPrefix = trigger.name.substring(1);
relevantShortcuts.push({
diff --git a/src/notes/SuggestionPortalShortcutSearchIndex.jsx b/src/notes/SuggestionPortalShortcutSearchIndex.jsx
index 19e185b63f..e8943e5b35 100644
--- a/src/notes/SuggestionPortalShortcutSearchIndex.jsx
+++ b/src/notes/SuggestionPortalShortcutSearchIndex.jsx
@@ -20,7 +20,7 @@ class SuggestionPortalShortcutSearchIndex extends SuggestionPortalSearchIndex {
allShortcutObjs.forEach((shortcutObj) => {
const shortcutId = shortcutObj.id;
const shortcutMetadata = this.shortcutManager.getShortcutMetadata(shortcutId);
- const triggers = this.shortcutManager.getTriggersForShortcut(shortcutId);
+ const triggers = this.shortcutManager.getTriggersWithoutLabelForShortcut(shortcutId);
// Scores get sorted from smallest to greatest
// ActiveContexts is sorted from most recent to least recent
// We want shortcuts for the most recent shortcuts to have the smallest bonus score, so as to appear earlier
diff --git a/src/shortcuts/CreatorBase.jsx b/src/shortcuts/CreatorBase.jsx
index a366e745e5..9a7fe39f7e 100644
--- a/src/shortcuts/CreatorBase.jsx
+++ b/src/shortcuts/CreatorBase.jsx
@@ -1,4 +1,5 @@
import EntryShortcut from './EntryShortcut';
+import _ from 'lodash';
export default class CreatorBase extends EntryShortcut {
constructor(onUpdate, metadata, patient, shortcutData) {
@@ -22,4 +23,21 @@ export default class CreatorBase extends EntryShortcut {
get isComplete() {
return this.hasParentContext() && this.hasChildren();
}
+
+ get isMissingParent() {
+ return !this.hasParentContext();
+ }
+
+ get potentialParents() {
+ const knownParent = this.metadata["knownParentContexts"];
+ if (knownParent === 'Patient' || knownParent === undefined) return [];
+ if (_.isArray(knownParent)) {
+ return knownParent;
+ } else if (_.isString(knownParent)) {
+ return [knownParent];
+ } else {
+ console.warn("unknown type for knownParent: element looks like ", knownParent);
+ return [];
+ }
+ }
}
diff --git a/src/shortcuts/CreatorIntermediary.jsx b/src/shortcuts/CreatorIntermediary.jsx
index 188601ab54..e1ef4ee005 100644
--- a/src/shortcuts/CreatorIntermediary.jsx
+++ b/src/shortcuts/CreatorIntermediary.jsx
@@ -1,5 +1,5 @@
import Shortcut from './Shortcut';
-import Lang from 'lodash';
+import _ from 'lodash';
export default class CreatorIntermediary extends Shortcut {
constructor(onUpdate, metadata) {
@@ -15,11 +15,11 @@ export default class CreatorIntermediary extends Shortcut {
initialize(contextManager, trigger = undefined, updatePatient = true) {
super.initialize(contextManager, trigger, updatePatient);
- if (Lang.isUndefined(this.parentContext)) {
+ if (_.isUndefined(this.parentContext)) {
super.determineParentContext(contextManager, this.metadata["knownParentContexts"], this.metadata["parentAttribute"]);
}
- if (!Lang.isUndefined(this.parentContext) && this.parentContext.children.indexOf(this) === -1) {
+ if (!_.isUndefined(this.parentContext) && this.parentContext.children.indexOf(this) === -1) {
this.parentContext.setAttributeValue(this.metadata["parentAttribute"], true, false, updatePatient);
this.parentContext.addChild(this);
}
@@ -115,10 +115,26 @@ export default class CreatorIntermediary extends Shortcut {
}
hasValueObjectAttributes() {
- return !Lang.isEmpty(this.metadata["valueObjectAttributes"]);
+ return !_.isEmpty(this.metadata["valueObjectAttributes"]);
}
get isComplete() {
return this.hasParentContext() && this.hasChildren();
}
+
+ get isMissingParent() {
+ return !this.hasParentContext();
+ }
+ get potentialParents() {
+ const knownParent = this.metadata["knownParentContexts"];
+ if (knownParent === 'Patient' || knownParent === undefined) return [];
+ if (_.isArray(knownParent)) {
+ return knownParent;
+ } else if (_.isString(knownParent)) {
+ return [knownParent];
+ } else {
+ console.warn("unknown type for knownParent: element looks like ", knownParent);
+ return [];
+ }
+ }
}
diff --git a/src/shortcuts/ShortcutManager.js b/src/shortcuts/ShortcutManager.js
index 0a910e209c..7e199ae735 100644
--- a/src/shortcuts/ShortcutManager.js
+++ b/src/shortcuts/ShortcutManager.js
@@ -268,6 +268,7 @@ class ShortcutManager {
} else {
addTriggerForCurrentShortcut.bind(this)(triggers, item);
}
+ // If this shortcut has a label, then add the label as a shortcut.
if (item.label) {
// Add a string trigger for incomplete placeholder
addTriggerForCurrentShortcut.bind(this)({
@@ -341,7 +342,7 @@ class ShortcutManager {
}
let numberOfValidTriggers;
if (this.triggersPerShortcut[shortcutId]) {
- // If the shortcut has a label defined, don't include in in the list of valid triggers per shortcut
+ // If the shortcut has a label defined, don't include in in the list of valid triggers per shortcut
numberOfValidTriggers = this.triggersPerShortcut[shortcutId].length - (shortcut.label ? 1 : 0);
} else {
numberOfValidTriggers = shortcut.label ? 1 : 0;
@@ -402,10 +403,20 @@ class ShortcutManager {
// stringTriggers is directly from shortcut metadata
const stringTriggers = this.shortcuts[shortcutId].stringTriggers;
// triggers is the computed options based on the valueset defined in metadata
- const triggers = this.getTriggersForShortcut(shortcutId, context);
-
- // Filter out the label from triggers if there are defined stringTriggers that can be added
- return triggers.filter(t => stringTriggers.length === 0 || t.name !== label);
+ // Clone this information so we aren't changing anything by reference
+ const triggers = [...this.getTriggersForShortcut(shortcutId, context)];
+
+ // Get the index of the label in the triggers list
+ const indexOfLabel = _.findIndex(triggers, t => t.name === label);
+ // If there is an instance of the label, and the stringTriggers isn't empty then we want to remove this instance of the label
+ // When stringTriggers doesn't have a length, we might be using the label as a shorthand for writing down the string trigger as well
+ if (indexOfLabel !== -1 && stringTriggers.length !== 0) {
+ // We only splice out this one instance of it in case the string trigger itself happens to match the label
+ // In this case there will be two instances of the label in our list and we want to remove one of them
+ // In the case where there is just one instance, then we can remove it safely
+ triggers.splice(indexOfLabel, 1);
+ }
+ return triggers;
}
getKeywordsForShortcut(shortcutId, context) {
@@ -439,6 +450,10 @@ class ShortcutManager {
return this.shortcuts[shortcutId]["shortcutGroupName"];
}
+ getShortcutLabel(shortcutId) {
+ return this.shortcuts[shortcutId].label;
+ }
+
getShortcutMetadata(shortcutId) {
return this.shortcuts[shortcutId];
}
diff --git a/src/shortcuts/Shortcuts.json b/src/shortcuts/Shortcuts.json
index ce752774e1..6e8215297b 100644
--- a/src/shortcuts/Shortcuts.json
+++ b/src/shortcuts/Shortcuts.json
@@ -48,6 +48,7 @@
},
{ "type": "CreatorBase",
"id": "StagingCreator",
+ "label": "#staging",
"name": "staging",
"subtype": "menu",
"getData": null,
@@ -113,6 +114,7 @@
},
{ "type": "CreatorBase",
"id": "ProgressionCreator",
+ "label": "#disease status",
"name": "disease status",
"subtype": "menu",
"getData": null,
@@ -193,6 +195,7 @@
},
{ "type": "CreatorBase",
"id": "ToxicityCreator",
+ "label": "#toxicity",
"name": "toxicity",
"subtype": "menu",
"getData": null,
@@ -354,6 +357,7 @@
{ "type": "InsertValue",
"subtype": "choice",
"id": "ConditionInserter",
+ "label": "@condition",
"getData": {"object": "patient", "method": "getConditions", "itemKey": "entryInfo.entryId.id", "itemContext":"type", "dateLabel": "diagnosisDate"},
"isContext": true,
"isGlobalContext": true,
@@ -562,6 +566,7 @@
},
{ "type": "CreatorBase",
"id": "MedicationChangeReduceCreator",
+ "label": "#reduce medication",
"name": "reduce medication",
"subtype": "menu",
"getData": null,