Skip to content

Commit 1d732ab

Browse files
committed
feat(components/feel): added onblur to handle post input
- onblur updates the value async - trims the whitespaces if exists - applied to feel-textfield and feel-textarea Closes #404 Related to: #309 post work Integration tests: bpmn-io/bpmn-js-properties-panel#1118
1 parent b2b4b42 commit 1d732ab

File tree

2 files changed

+169
-7
lines changed

2 files changed

+169
-7
lines changed

src/components/entries/FEEL/Feel.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function FeelTextfield(props) {
4343
label,
4444
hostLanguage,
4545
onInput,
46+
onBlur,
4647
onError,
4748
placeholder,
4849
feel,
@@ -80,6 +81,9 @@ function FeelTextfield(props) {
8081
_setFocus(position + offset);
8182
};
8283

84+
/**
85+
* @type { import('min-dash').DebouncedFunction }
86+
*/
8387
const handleInputCallback = useMemo(() => {
8488
return debounce((newValue) => {
8589
onInput(newValue);
@@ -89,12 +93,11 @@ function FeelTextfield(props) {
8993
const setLocalValue = newValue => {
9094
_setLocalValue(newValue);
9195

92-
if (typeof newValue === 'undefined' || newValue === '' || newValue === '=') {
93-
handleInputCallback(undefined);
94-
} else {
95-
handleInputCallback(newValue);
96-
}
96+
// we don't commit empty FEEL expressions,
97+
// but instead serialize them as <undefined>
98+
const newModelValue = (newValue === '' || newValue === '=') ? undefined : newValue;
9799

100+
handleInputCallback(newModelValue);
98101
};
99102

100103
const handleFeelToggle = useStaticCallback(() => {
@@ -127,6 +130,20 @@ function FeelTextfield(props) {
127130
}
128131
};
129132

133+
const handleOnBlur = (e) => {
134+
const value = e.target.value;
135+
136+
// we trim the value, if it is needed
137+
// and update input accordingly
138+
if (value.trim() !== value) {
139+
setLocalValue(value.trim());
140+
}
141+
142+
if (onBlur) {
143+
onBlur(e);
144+
}
145+
};
146+
130147
const handleLint = useStaticCallback((lint = []) => {
131148

132149
const syntaxError = lint.some(report => report.type === 'Syntax Error');
@@ -263,6 +280,7 @@ function FeelTextfield(props) {
263280
{ ...props }
264281
popupOpen={ isPopupOpen }
265282
onInput={ handleLocalInput }
283+
onBlur={ handleOnBlur }
266284
contentAttributes={ { 'id': prefixId(id), 'aria-label': label } }
267285
value={ localValue }
268286
ref={ editorRef }
@@ -558,7 +576,7 @@ export default function FeelEntry(props) {
558576
}
559577
}, [ value, validate ]);
560578

561-
const onInput = useStaticCallback((newValue) => {
579+
const onInput = useCallback((newValue) => {
562580
let newValidationError = null;
563581

564582
if (isFunction(validate)) {
@@ -571,7 +589,7 @@ export default function FeelEntry(props) {
571589
}
572590

573591
setValidationError(newValidationError);
574-
});
592+
},[ element ]);
575593

576594
const onError = useCallback(err => {
577595
setLocalError(err);

test/spec/components/Feel.spec.js

+144
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,78 @@ describe('<FeelEntry>', function() {
7979
expect(updateSpy).to.have.been.calledWith('foo');
8080
});
8181

82+
it('should trim whitespace on blur', async function() {
83+
84+
// given
85+
const setValueSpy = sinon.spy();
86+
87+
const result = createFeelField({
88+
container,
89+
setValue: setValueSpy
90+
});
91+
92+
const input = domQuery('.bio-properties-panel-input', result.container);
93+
94+
// when
95+
input.focus();
96+
changeInput(input, ' foo ');
97+
input.blur();
98+
99+
// then
100+
expect(setValueSpy).to.have.been.calledTwice;
101+
expect(setValueSpy).to.have.been.calledWith('foo');
102+
});
103+
104+
105+
it('should call onBlur if provided', async function() {
106+
107+
// given
108+
const setValueSpy = sinon.spy();
109+
const onBlurSpy = sinon.spy();
110+
111+
const result = createFeelField({
112+
container,
113+
setValue: setValueSpy,
114+
onBlur: onBlurSpy
115+
});
116+
117+
const input = domQuery('.bio-properties-panel-input', result.container);
118+
119+
// when
120+
input.focus();
121+
changeInput(input, ' foo ');
122+
input.blur();
123+
124+
// then
125+
expect(onBlurSpy).to.have.been.calledOnce;
126+
});
127+
128+
129+
it('should not call setValue if the value is same', async function() {
130+
131+
// given
132+
const setValueSpy = sinon.spy();
133+
134+
const result = createFeelField({
135+
container,
136+
setValue: setValueSpy
137+
});
138+
139+
const input = domQuery('.bio-properties-panel-input', result.container);
140+
141+
// when
142+
input.focus();
143+
changeInput(input, '');
144+
input.blur();
145+
146+
// then
147+
expect(setValueSpy).to.not.have.been.called;
148+
149+
});
150+
151+
82152
describe('#isEdited', function() {
153+
83154
it('should NOT be edited', function() {
84155

85156
// given
@@ -1065,7 +1136,78 @@ describe('<FeelEntry>', function() {
10651136
expect(updateSpy).to.have.been.calledWith('foo');
10661137
});
10671138

1139+
it('should trim whitespace on blur', async function() {
1140+
1141+
// given
1142+
const setValueSpy = sinon.spy();
1143+
1144+
const result = createFeelTextArea({
1145+
container,
1146+
setValue: setValueSpy,
1147+
});
1148+
1149+
const input = domQuery('.bio-properties-panel-input', result.container);
1150+
1151+
// when
1152+
input.focus();
1153+
changeInput(input, ' foo ');
1154+
input.blur();
1155+
1156+
// then
1157+
expect(setValueSpy).to.have.been.calledTwice;
1158+
expect(setValueSpy).to.have.been.calledWith('foo');
1159+
});
1160+
1161+
1162+
it('should call onBlur if provided', async function() {
1163+
1164+
// given
1165+
const setValueSpy = sinon.spy();
1166+
const onBlurSpy = sinon.spy();
1167+
1168+
const result = createFeelTextArea({
1169+
container,
1170+
setValue: setValueSpy,
1171+
onBlur: onBlurSpy
1172+
});
1173+
1174+
const input = domQuery('.bio-properties-panel-input', result.container);
1175+
1176+
// when
1177+
input.focus();
1178+
changeInput(input, ' foo ');
1179+
input.blur();
1180+
1181+
// then
1182+
expect(onBlurSpy).to.have.been.calledOnce;
1183+
});
1184+
1185+
1186+
it('should not call setValue if the value is same', async function() {
1187+
1188+
// given
1189+
const setValueSpy = sinon.spy();
1190+
1191+
const result = createFeelTextArea({
1192+
container,
1193+
setValue: setValueSpy
1194+
});
1195+
1196+
const input = domQuery('.bio-properties-panel-input', result.container);
1197+
1198+
// when
1199+
input.focus();
1200+
changeInput(input, '');
1201+
input.blur();
1202+
1203+
// then
1204+
expect(setValueSpy).to.not.have.been.called;
1205+
1206+
});
1207+
1208+
10681209
describe('#isEdited', function() {
1210+
10691211
it('should NOT be edited', function() {
10701212

10711213
// given
@@ -2692,6 +2834,7 @@ function createFeelField(options = {}, renderFn = render) {
26922834
container,
26932835
eventBus = new EventBus(),
26942836
onShow = noop,
2837+
onBlur = noop,
26952838
errors = {},
26962839
variables,
26972840
Component = FeelField,
@@ -2728,6 +2871,7 @@ function createFeelField(options = {}, renderFn = render) {
27282871
disabled={ disabled }
27292872
getValue={ getValue }
27302873
setValue={ setValue }
2874+
onBlur={ onBlur }
27312875
debounce={ debounce }
27322876
validate={ validate }
27332877
feel={ feel }

0 commit comments

Comments
 (0)