diff --git a/package-lock.json b/package-lock.json
index 0e1fc40c..e32cfe2e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1601,6 +1601,11 @@
"preact": "^10.11.2"
}
},
+ "node_modules/@bpmn-io/dmn-variable-resolver": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@bpmn-io/dmn-variable-resolver/-/dmn-variable-resolver-0.3.1.tgz",
+ "integrity": "sha512-wKuL15nVAb0EO7mwto1+sB/CFZLbwxJAg2MKJa7a0gFL59sgO1vS9wwXwBiCCcvFKiEkCSNxhyFPjQItVEgS8A=="
+ },
"node_modules/@bpmn-io/feel-editor": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@bpmn-io/feel-editor/-/feel-editor-0.9.1.tgz",
@@ -19779,6 +19784,7 @@
"version": "14.4.1",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
+ "@bpmn-io/dmn-variable-resolver": "^0.3.1",
"css.escape": "^1.5.1",
"diagram-js": "^12.0.0",
"dmn-js-shared": "^14.4.1",
@@ -19815,6 +19821,7 @@
"version": "14.4.3",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
+ "@bpmn-io/dmn-variable-resolver": "^0.3.1",
"diagram-js": "^12.0.0",
"dmn-js-shared": "^14.4.1",
"escape-html": "^1.0.3",
@@ -20911,6 +20918,11 @@
"preact": "^10.11.2"
}
},
+ "@bpmn-io/dmn-variable-resolver": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@bpmn-io/dmn-variable-resolver/-/dmn-variable-resolver-0.3.1.tgz",
+ "integrity": "sha512-wKuL15nVAb0EO7mwto1+sB/CFZLbwxJAg2MKJa7a0gFL59sgO1vS9wwXwBiCCcvFKiEkCSNxhyFPjQItVEgS8A=="
+ },
"@bpmn-io/feel-editor": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@bpmn-io/feel-editor/-/feel-editor-0.9.1.tgz",
@@ -24822,6 +24834,7 @@
"dmn-js-decision-table": {
"version": "file:packages/dmn-js-decision-table",
"requires": {
+ "@bpmn-io/dmn-variable-resolver": "^0.3.1",
"css.escape": "^1.5.1",
"diagram-js": "^12.0.0",
"dmn-font": "^0.6.2",
@@ -24852,6 +24865,7 @@
"dmn-js-literal-expression": {
"version": "file:packages/dmn-js-literal-expression",
"requires": {
+ "@bpmn-io/dmn-variable-resolver": "^0.3.1",
"diagram-js": "^12.0.0",
"dmn-font": "^0.6.2",
"dmn-js-shared": "^14.4.1",
diff --git a/packages/dmn-js-decision-table/package.json b/packages/dmn-js-decision-table/package.json
index bbb18c3d..4d46158c 100644
--- a/packages/dmn-js-decision-table/package.json
+++ b/packages/dmn-js-decision-table/package.json
@@ -30,6 +30,7 @@
"inferno-test-utils": "~5.6.2"
},
"dependencies": {
+ "@bpmn-io/dmn-variable-resolver": "^0.3.1",
"css.escape": "^1.5.1",
"diagram-js": "^12.0.0",
"dmn-js-shared": "^14.4.1",
diff --git a/packages/dmn-js-decision-table/src/Editor.js b/packages/dmn-js-decision-table/src/Editor.js
index 6245b839..cc3d3a5b 100644
--- a/packages/dmn-js-decision-table/src/Editor.js
+++ b/packages/dmn-js-decision-table/src/Editor.js
@@ -1,3 +1,4 @@
+import { DmnVariableResolverModule } from '@bpmn-io/dmn-variable-resolver';
import Viewer from './Viewer';
import addRuleModule from './features/add-rule';
@@ -76,7 +77,8 @@ export default class Editor extends Viewer {
simpleDurationEditModule,
simpleNumberEditModule,
simpleStringEditModule,
- simpleTimeEditModule
+ simpleTimeEditModule,
+ DmnVariableResolverModule
];
}
diff --git a/packages/dmn-js-decision-table/src/features/decision-rules/components/DecisionRulesCellEditorComponent.js b/packages/dmn-js-decision-table/src/features/decision-rules/components/DecisionRulesCellEditorComponent.js
index 8ec21ee5..274b3341 100644
--- a/packages/dmn-js-decision-table/src/features/decision-rules/components/DecisionRulesCellEditorComponent.js
+++ b/packages/dmn-js-decision-table/src/features/decision-rules/components/DecisionRulesCellEditorComponent.js
@@ -124,6 +124,7 @@ class TableCellEditor extends Component {
this._expressionLanguages = context.injector.get('expressionLanguages');
this._translate = context.injector.get('translate');
+ this._variableResolver = context.injector.get('variableResolver', false);
}
isDefaultExpressionLanguage(businessObject) {
@@ -187,6 +188,13 @@ class TableCellEditor extends Component {
this.getDefaultExpressionLanguage(businessObject).value;
}
+ _getVariables() {
+ const { businessObject } = this.props;
+
+ return this._variableResolver &&
+ this._variableResolver.getVariables(businessObject);
+ }
+
render() {
const {
businessObject,
@@ -204,6 +212,7 @@ class TableCellEditor extends Component {
const isScript = this.isScript(businessObject);
const Editor = this.getEditor();
+ const variables = this._getVariables();
return (
@@ -217,6 +226,7 @@ class TableCellEditor extends Component {
onInput={ onChange }
value={ value }
placeholder={ placeholder }
+ variables={ variables }
/>
{
!isDefaultExpressionLanguage && (
diff --git a/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputCellContextMenu.js b/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputCellContextMenu.js
index 4ab2aa33..105d7426 100644
--- a/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputCellContextMenu.js
+++ b/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputCellContextMenu.js
@@ -79,6 +79,7 @@ export default class InputCellContextMenu extends Component {
);
}
diff --git a/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputEditor.js b/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputEditor.js
index 67f32a85..cce280ea 100644
--- a/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputEditor.js
+++ b/packages/dmn-js-decision-table/src/features/decision-table-head/editor/components/InputEditor.js
@@ -10,6 +10,7 @@ export default class InputEditor extends Component {
this.translate = context.injector ? context.injector.get('translate') : noopTranslate;
this.expressionLanguages = context.injector.get('expressionLanguages', false);
+ this.variableResolver = context.injector.get('variableResolver', false);
this.handleValue = (text) => {
@@ -54,6 +55,11 @@ export default class InputEditor extends Component {
}
};
+ _getVariables() {
+ return this.variableResolver &&
+ this.variableResolver.getVariables(this.props.element);
+ }
+
render() {
const {
@@ -63,6 +69,8 @@ export default class InputEditor extends Component {
const ExpressionEditor = this.getExpressionEditorComponent();
+ const variables = this._getVariables();
+
return (
@@ -92,7 +100,8 @@ export default class InputEditor extends Component {
].join(' ')
}
onInput={ this.handleValue }
- value={ text || '' } />
+ value={ text || '' }
+ variables={ variables } />
);
diff --git a/packages/dmn-js-decision-table/test/spec/features/decision-rules/DecisionRulesEditorSpec.js b/packages/dmn-js-decision-table/test/spec/features/decision-rules/DecisionRulesEditorSpec.js
index c0e73a56..ed4a487f 100644
--- a/packages/dmn-js-decision-table/test/spec/features/decision-rules/DecisionRulesEditorSpec.js
+++ b/packages/dmn-js-decision-table/test/spec/features/decision-rules/DecisionRulesEditorSpec.js
@@ -2,6 +2,8 @@ import { bootstrapModeler, inject, act } from 'test/helper';
import { query as domQuery } from 'min-dom';
+import { DmnVariableResolverModule } from '@bpmn-io/dmn-variable-resolver';
+
import { triggerInputEvent } from 'dmn-js-shared/test/util/EventUtil';
import { queryEditor } from 'dmn-js-shared/test/util/EditorUtil';
@@ -367,6 +369,44 @@ describe('features/decision-rules', function() {
});
+
+ describe('integration', function() {
+
+ beforeEach(bootstrapModeler(emptyRuleXML, {
+ modules: [
+ CoreModule,
+ ModelingModule,
+ DecisionRulesModule,
+ DecisionRulesEditorModule,
+ DmnVariableResolverModule
+ ],
+ debounceInput: false
+ }));
+
+
+ it('should pass variables to editor', async function() {
+
+ // given
+ const editor = queryEditor('[data-element-id="unaryTest_1"]', testContainer);
+
+ await act(() => editor.focus());
+
+ // when
+ await changeInput(document.activeElement, 'Var');
+
+ // then
+ await expectEventually(() => {
+ const options = testContainer.querySelectorAll('[role="option"]');
+
+ expect(options).to.exist;
+ expect(options).to.satisfy(options => {
+ const result = Array.from(options).some(
+ option => option.textContent === 'Variable');
+ return result;
+ });
+ });
+ });
+ });
});
// helpers //////////////////
@@ -390,4 +430,19 @@ function isFirefox() {
function skipFF() {
return isFirefox() ? it.only : it;
-}
\ No newline at end of file
+}
+
+async function expectEventually(fn) {
+ for (let i = 0; i < 10; i++) {
+ try {
+ await act(() => {});
+ await fn();
+ return;
+ } catch (e) {
+
+ // wait
+ }
+ }
+
+ return fn();
+}
diff --git a/packages/dmn-js-decision-table/test/spec/features/decision-rules/empty-rule.dmn b/packages/dmn-js-decision-table/test/spec/features/decision-rules/empty-rule.dmn
index 60543eb2..d320ed54 100644
--- a/packages/dmn-js-decision-table/test/spec/features/decision-rules/empty-rule.dmn
+++ b/packages/dmn-js-decision-table/test/spec/features/decision-rules/empty-rule.dmn
@@ -1,6 +1,9 @@
-
+
+
+
+
@@ -18,4 +21,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditor.dmn b/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditor.dmn
index 79188c80..1e0f6db3 100644
--- a/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditor.dmn
+++ b/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditor.dmn
@@ -1,6 +1,9 @@
-
+
+
+
+
@@ -86,4 +89,20 @@ else
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditorCellSpec.js b/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditorCellSpec.js
index 9a5c23f5..36fc8f40 100644
--- a/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditorCellSpec.js
+++ b/packages/dmn-js-decision-table/test/spec/features/decision-table-head/editor/input/InputEditorCellSpec.js
@@ -1,10 +1,12 @@
-import { bootstrapModeler, inject } from 'test/helper';
+import { bootstrapModeler, inject, act } from 'test/helper';
import {
triggerInputEvent,
triggerMouseEvent
} from 'dmn-js-shared/test/util/EventUtil';
+import { DmnVariableResolverModule } from '@bpmn-io/dmn-variable-resolver';
+
import { query as domQuery } from 'min-dom';
import TestContainer from 'mocha-test-container-support';
@@ -26,7 +28,8 @@ describe('decision-table-head/editor - input', function() {
DecisionTableHeadModule,
DecisionTableHeadEditorModule,
ModelingModule,
- KeyboardModule
+ KeyboardModule,
+ DmnVariableResolverModule
],
debounceInput: false
}));
@@ -182,6 +185,32 @@ describe('decision-table-head/editor - input', function() {
expect(inputBo.inputExpression.text).to.equal('foo');
}));
});
+
+
+ describe('integration', function() {
+
+ it('should pass variables to editor', async function() {
+
+ // given
+ const editorEl = openEditor('input2');
+ const input = getControl('.ref-text [role="textbox"]', editorEl);
+
+ // when
+ await changeInput(input, 'Var');
+
+ // then
+ await expectEventually(() => {
+ const options = testContainer.querySelectorAll('[role="option"]');
+
+ expect(options).to.exist;
+ expect(options).to.satisfy(options => {
+ const result = Array.from(options).some(
+ option => option.textContent === 'Variable');
+ return result;
+ });
+ });
+ });
+ });
});
@@ -240,11 +269,22 @@ function getControl(selector, parent) {
* @param {string} value
*/
function changeInput(input, value) {
- input.textContent = value;
-
- return new Promise(resolve => {
- requestAnimationFrame(() => {
- resolve();
- });
+ return act(() => {
+ input.textContent = value;
});
}
+
+async function expectEventually(fn) {
+ for (let i = 0; i < 10; i++) {
+ try {
+ await act(() => {});
+ await fn();
+ return;
+ } catch (e) {
+
+ // wait
+ }
+ }
+
+ return fn();
+}
\ No newline at end of file
diff --git a/packages/dmn-js-literal-expression/package.json b/packages/dmn-js-literal-expression/package.json
index 026e358c..a5f5e6f5 100644
--- a/packages/dmn-js-literal-expression/package.json
+++ b/packages/dmn-js-literal-expression/package.json
@@ -30,6 +30,7 @@
"inferno-test-utils": "~5.6.2"
},
"dependencies": {
+ "@bpmn-io/dmn-variable-resolver": "^0.3.1",
"diagram-js": "^12.0.0",
"dmn-js-shared": "^14.4.1",
"escape-html": "^1.0.3",
diff --git a/packages/dmn-js-literal-expression/src/Editor.js b/packages/dmn-js-literal-expression/src/Editor.js
index 09ce9ccf..91da0303 100644
--- a/packages/dmn-js-literal-expression/src/Editor.js
+++ b/packages/dmn-js-literal-expression/src/Editor.js
@@ -1,3 +1,5 @@
+import { DmnVariableResolverModule } from '@bpmn-io/dmn-variable-resolver';
+
import ExpressionLanguagesModule from 'dmn-js-shared/lib/features/expression-languages';
import DataTypesModule from 'dmn-js-shared/lib/features/data-types';
@@ -26,7 +28,8 @@ export default class Editor extends Viewer {
ModelingModule,
ExpressionLanguagesModule,
DataTypesModule,
- TextareaEditorComponent
+ TextareaEditorComponent,
+ DmnVariableResolverModule
];
}
}
\ No newline at end of file
diff --git a/packages/dmn-js-literal-expression/src/features/textarea/components/TextareaEditorComponent.js b/packages/dmn-js-literal-expression/src/features/textarea/components/TextareaEditorComponent.js
index 60bfb07b..ccd299b2 100644
--- a/packages/dmn-js-literal-expression/src/features/textarea/components/TextareaEditorComponent.js
+++ b/packages/dmn-js-literal-expression/src/features/textarea/components/TextareaEditorComponent.js
@@ -12,6 +12,7 @@ export default class TextareaEditorComponent extends Component {
this._viewer = context.injector.get('viewer');
this._expressionLanguages = context.injector.get('expressionLanguages');
+ this._variableResolver = context.injector.get('variableResolver', false);
this.editLiteralExpressionText = this.editLiteralExpressionText.bind(this);
this.onElementsChanged = this.onElementsChanged.bind(this);
@@ -49,17 +50,26 @@ export default class TextareaEditorComponent extends Component {
this._expressionLanguages.getDefault().value;
}
+ _getVariables() {
+ const businessObject = this.getLiteralExpression();
+
+ return this._variableResolver &&
+ this._variableResolver.getVariables(businessObject);
+ }
+
render() {
// there is only one single element
const { text } = this.getLiteralExpression();
const Editor = this.getEditor();
+ const variables = this._getVariables();
return (
+ onChange={ this.editLiteralExpressionText }
+ variables={ variables } />
);
}
}
@@ -70,6 +80,7 @@ class FeelEditor extends Component {
className={ this.props.className }
value={ this.props.value }
onInput={ this.props.onChange }
+ variables={ this.props.variables }
/>;
}
}
diff --git a/packages/dmn-js-literal-expression/test/spec/features/textarea/TextareaEditorSpec.js b/packages/dmn-js-literal-expression/test/spec/features/textarea/TextareaEditorSpec.js
index 7cb58cf1..3a314939 100644
--- a/packages/dmn-js-literal-expression/test/spec/features/textarea/TextareaEditorSpec.js
+++ b/packages/dmn-js-literal-expression/test/spec/features/textarea/TextareaEditorSpec.js
@@ -2,6 +2,8 @@ import { bootstrapModeler, inject } from 'test/helper';
import { query as domQuery } from 'min-dom';
+import { DmnVariableResolverModule } from '@bpmn-io/dmn-variable-resolver';
+
import { queryEditor } from 'dmn-js-shared/test/util/EditorUtil';
import ExpressionLanguagesModule from 'dmn-js-shared/lib/features/expression-languages';
@@ -26,7 +28,8 @@ describe('textarea editor', function() {
CoreModule,
TextareaEditorModule,
ModelingModule,
- ExpressionLanguagesModule
+ ExpressionLanguagesModule,
+ DmnVariableResolverModule
],
debounceInput: false
}));
@@ -75,6 +78,33 @@ describe('textarea editor', function() {
expect(viewer.getDecision().decisionLogic.text).to.equal('foo');
}));
+
+ describe('integration', function() {
+
+ it('should pass variables to editor', inject(async function(viewer) {
+
+ // given
+ const editor = queryEditor('.textarea', testContainer);
+
+ await act(() => editor.focus());
+
+ // when
+ await changeInput(document.activeElement, 'Var');
+
+ // then
+ await expectEventually(() => {
+ const options = testContainer.querySelectorAll('[role="option"]');
+
+ expect(options).to.exist;
+ expect(options).to.satisfy(options => {
+ const result = Array.from(options).some(
+ option => option.textContent === 'Variable');
+ return result;
+ });
+ });
+ }));
+ });
+
});
// helpers //////////
@@ -87,11 +117,26 @@ function changeInput(input, value) {
return act(() => input.textContent = value);
}
-function act(fn) {
- fn();
+async function act(fn) {
+ await fn();
return new Promise(resolve => {
requestAnimationFrame(() => {
resolve();
});
});
}
+
+async function expectEventually(fn) {
+ for (let i = 0; i < 10; i++) {
+ try {
+ await act(() => {});
+ await fn();
+ return;
+ } catch (e) {
+
+ // wait
+ }
+ }
+
+ return fn();
+}
diff --git a/packages/dmn-js-literal-expression/test/spec/literal-expression.dmn b/packages/dmn-js-literal-expression/test/spec/literal-expression.dmn
index 9d5b7410..bdf8de40 100644
--- a/packages/dmn-js-literal-expression/test/spec/literal-expression.dmn
+++ b/packages/dmn-js-literal-expression/test/spec/literal-expression.dmn
@@ -1,16 +1,28 @@
-
+
+
+
+
calendar.getSeason(date)
+
-
+
+
+
+
+
+
+
+
+
diff --git a/packages/dmn-js-shared/src/components/LiteralExpression.js b/packages/dmn-js-shared/src/components/LiteralExpression.js
index 1fbaecd3..f5dbe1d5 100644
--- a/packages/dmn-js-shared/src/components/LiteralExpression.js
+++ b/packages/dmn-js-shared/src/components/LiteralExpression.js
@@ -46,7 +46,8 @@ export default class LiteralExpression extends Component {
this.editor = new FeelEditor({
container: this.node,
onChange: this.handleChange,
- value: this.state.value
+ value: this.state.value,
+ variables: this.props.variables || [],
});
this.node.addEventListener('mousedown', this.handleMouseEvent);
@@ -70,6 +71,10 @@ export default class LiteralExpression extends Component {
this.editor.setValue(value);
});
}
+
+ if (!deepEqual(prevProps.variables, this.props.variables)) {
+ this.editor.setVariables(this.props.variables);
+ }
}
componentWillUnmount() {
@@ -149,3 +154,7 @@ function isCmd(event) {
function isAutocompleteOpen(node) {
return node.querySelector('.cm-tooltip-autocomplete');
}
+
+function deepEqual(a, b) {
+ return JSON.stringify(a) === JSON.stringify(b);
+}