Skip to content

Commit 0cf766f

Browse files
committed
feat: implement script component properties panel
Related to #1102
1 parent 1e32faa commit 0cf766f

File tree

10 files changed

+406
-18
lines changed

10 files changed

+406
-18
lines changed

packages/form-js-editor/src/features/properties-panel/entries/ConditionEntry.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function Condition(props) {
5050
let description = 'Condition under which the field is hidden';
5151

5252
// special case for expression fields which do not render
53-
if (field.type === 'expression') {
53+
if ([ 'expression', 'script' ].includes(field.type)) {
5454
label = 'Deactivate if';
5555
description = 'Condition under which the field is deactivated';
5656
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { simpleBoolEntryFactory } from './factories';
2+
3+
export function DoNotSubmitEntry(props) {
4+
const {
5+
field,
6+
getService
7+
} = props;
8+
9+
const formFields = getService('formFields');
10+
11+
const fieldDescriptors = {
12+
script: "function's",
13+
expression: "expression's",
14+
};
15+
16+
const entries = [
17+
simpleBoolEntryFactory({
18+
id: 'doNotSubmit',
19+
label: `Do not submit the ${fieldDescriptors[field.type] || "field's"} result with the form submission`,
20+
tooltip: 'Prevents the data associated with this form element from being submitted by the form. Use for intermediate calculations.',
21+
path: [ 'doNotSubmit' ],
22+
props,
23+
isDefaultVisible: (field) => {
24+
const { config } = formFields.get(field.type);
25+
return config.keyed && config.allowDoNotSubmit;
26+
}
27+
})
28+
];
29+
30+
return entries;
31+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { FeelEntry, isFeelEntryEdited, TextAreaEntry, isTextAreaEntryEdited, SelectEntry, isSelectEntryEdited } from '@bpmn-io/properties-panel';
2+
import { get } from 'min-dash';
3+
import { simpleRangeIntegerEntryFactory } from './factories';
4+
5+
import { useService, useVariables } from '../hooks';
6+
7+
export function JSFunctionEntry(props) {
8+
const {
9+
editField,
10+
field
11+
} = props;
12+
13+
const entries = [
14+
{
15+
id: 'variable-mappings',
16+
component: FunctionParameters,
17+
editField: editField,
18+
field: field,
19+
isEdited: isFeelEntryEdited,
20+
isDefaultVisible: (field) => field.type === 'script'
21+
},
22+
{
23+
id: 'function',
24+
component: FunctionDefinition,
25+
editField: editField,
26+
field: field,
27+
isEdited: isTextAreaEntryEdited,
28+
isDefaultVisible: (field) => field.type === 'script'
29+
},
30+
{
31+
id: 'computeOn',
32+
component: JSFunctionComputeOn,
33+
isEdited: isSelectEntryEdited,
34+
editField,
35+
field,
36+
isDefaultVisible: (field) => field.type === 'script'
37+
},
38+
simpleRangeIntegerEntryFactory({
39+
id: 'interval',
40+
label: 'Time interval (ms)',
41+
path: [ 'interval' ],
42+
min: 100,
43+
max: 60000,
44+
props,
45+
isDefaultVisible: (field) => field.type === 'script' && field.computeOn === 'interval'
46+
})
47+
];
48+
49+
return entries;
50+
}
51+
52+
function FunctionParameters(props) {
53+
const {
54+
editField,
55+
field,
56+
id
57+
} = props;
58+
59+
const debounce = useService('debounce');
60+
61+
const variables = useVariables().map(name => ({ name }));
62+
63+
const path = [ 'functionParameters' ];
64+
65+
const getValue = () => {
66+
return get(field, path, '');
67+
};
68+
69+
const setValue = (value) => {
70+
return editField(field, path, value || '');
71+
};
72+
73+
const tooltip = <div>
74+
Functions parameters should be described as an object, e.g.:
75+
<pre><code>{`{
76+
name: user.name,
77+
age: user.age
78+
}`}</code></pre>
79+
</div>;
80+
81+
return FeelEntry({
82+
debounce,
83+
feel: 'required',
84+
element: field,
85+
getValue,
86+
id,
87+
label: 'Function parameters',
88+
tooltip,
89+
description: 'Define the parameters to pass to the javascript function.',
90+
setValue,
91+
variables
92+
});
93+
}
94+
95+
function FunctionDefinition(props) {
96+
const {
97+
editField,
98+
field,
99+
id
100+
} = props;
101+
102+
const debounce = useService('debounce');
103+
104+
const path = [ 'jsFunction' ];
105+
106+
const getValue = () => {
107+
return get(field, path, '');
108+
};
109+
110+
const setValue = (value) => {
111+
return editField(field, path, value || '');
112+
};
113+
114+
return TextAreaEntry({
115+
debounce,
116+
element: field,
117+
getValue,
118+
description: 'Define the javascript function to execute.\nAccess the `data` object and use `setValue` to update the form state.',
119+
id,
120+
label: 'Javascript code',
121+
setValue
122+
});
123+
}
124+
125+
function JSFunctionComputeOn(props) {
126+
const { editField, field, id } = props;
127+
128+
const getValue = () => field.computeOn || '';
129+
130+
const setValue = (value) => {
131+
editField(field, [ 'computeOn' ], value);
132+
};
133+
134+
const getOptions = () => ([
135+
{ value: 'load', label: 'Form load' },
136+
{ value: 'change', label: 'Value change' },
137+
{ value: 'interval', label: 'Time interval' }
138+
]);
139+
140+
return SelectEntry({
141+
id,
142+
label: 'Compute on',
143+
description: 'Define when to execute the function',
144+
getValue,
145+
setValue,
146+
getOptions
147+
});
148+
}

packages/form-js-editor/src/features/properties-panel/entries/KeyEntry.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,28 @@ import { useService } from '../hooks';
77
import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-panel';
88
import { useCallback } from 'preact/hooks';
99

10-
1110
export function KeyEntry(props) {
1211
const {
1312
editField,
1413
field,
1514
getService
1615
} = props;
1716

18-
const entries = [];
19-
20-
entries.push({
21-
id: 'key',
22-
component: Key,
23-
editField: editField,
24-
field: field,
25-
isEdited: isTextFieldEntryEdited,
26-
isDefaultVisible: (field) => {
27-
const formFields = getService('formFields');
28-
const { config } = formFields.get(field.type);
29-
return config.keyed;
17+
const formFields = getService('formFields');
18+
19+
const entries = [
20+
{
21+
id: 'key',
22+
component: Key,
23+
editField: editField,
24+
field: field,
25+
isEdited: isTextFieldEntryEdited,
26+
isDefaultVisible: (field) => {
27+
const { config } = formFields.get(field.type);
28+
return config.keyed;
29+
}
3030
}
31-
});
31+
];
3232

3333
return entries;
3434
}

packages/form-js-editor/src/features/properties-panel/entries/factories/simpleBoolEntryFactory.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export function simpleBoolEntryFactory(options) {
66
id,
77
label,
88
description,
9+
tooltip,
910
path,
1011
props,
1112
getValue,
@@ -25,6 +26,7 @@ export function simpleBoolEntryFactory(options) {
2526
field,
2627
editField,
2728
description,
29+
tooltip,
2830
component: SimpleBoolComponent,
2931
isEdited: isToggleSwitchEntryEdited,
3032
isDefaultVisible,

packages/form-js-editor/src/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export function simpleRangeIntegerEntryFactory(options) {
1313
path,
1414
props,
1515
min,
16-
max
16+
max,
17+
isDefaultVisible
1718
} = options;
1819

1920
const {
@@ -30,7 +31,8 @@ export function simpleRangeIntegerEntryFactory(options) {
3031
min,
3132
max,
3233
component: SimpleRangeIntegerEntry,
33-
isEdited: isTextFieldEntryEdited
34+
isEdited: isTextFieldEntryEdited,
35+
isDefaultVisible
3436
};
3537
}
3638

packages/form-js-editor/src/features/properties-panel/entries/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { DefaultValueEntry } from './DefaultValueEntry';
66
export { DisabledEntry } from './DisabledEntry';
77
export { IdEntry } from './IdEntry';
88
export { KeyEntry } from './KeyEntry';
9+
export { DoNotSubmitEntry } from './DoNotSubmitEntry';
910
export { PathEntry } from './PathEntry';
1011
export { GroupAppearanceEntry } from './GroupAppearanceEntry';
1112
export { LabelEntry } from './LabelEntry';
@@ -14,6 +15,7 @@ export { IFrameUrlEntry } from './IFrameUrlEntry';
1415
export { ImageSourceEntry } from './ImageSourceEntry';
1516
export { TextEntry } from './TextEntry';
1617
export { HtmlEntry } from './HtmlEntry';
18+
export { JSFunctionEntry } from './JSFunctionEntry';
1719
export { HeightEntry } from './HeightEntry';
1820
export { NumberEntries } from './NumberEntries';
1921
export { ExpressionFieldEntries } from './ExpressionFieldEntries';

packages/form-js-editor/src/features/properties-panel/groups/GeneralGroup.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
IFrameHeightEntry,
1010
ImageSourceEntry,
1111
KeyEntry,
12+
DoNotSubmitEntry,
1213
PathEntry,
1314
RepeatableEntry,
1415
LabelEntry,
@@ -19,6 +20,7 @@ import {
1920
HeightEntry,
2021
NumberEntries,
2122
ExpressionFieldEntries,
23+
JSFunctionEntry,
2224
DateTimeEntry,
2325
TableDataSourceEntry,
2426
PaginationEntry,
@@ -45,14 +47,16 @@ export function GeneralGroup(field, editField, getService) {
4547
...HeightEntry({ field, editField }),
4648
...NumberEntries({ field, editField }),
4749
...ExpressionFieldEntries({ field, editField }),
50+
...JSFunctionEntry({ field, editField }),
4851
...ImageSourceEntry({ field, editField }),
4952
...AltTextEntry({ field, editField }),
5053
...SelectEntries({ field, editField }),
5154
...DisabledEntry({ field, editField }),
5255
...ReadonlyEntry({ field, editField }),
5356
...TableDataSourceEntry({ field, editField }),
5457
...PaginationEntry({ field, editField }),
55-
...RowCountEntry({ field, editField })
58+
...RowCountEntry({ field, editField }),
59+
...DoNotSubmitEntry({ field, editField, getService }),
5660
];
5761

5862
if (entries.length === 0) {

packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanel.spec.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3537,6 +3537,7 @@ describe('properties panel', function() {
35373537
expectPanelStructure(container, {
35383538
'General': [
35393539
'Key',
3540+
'Do not submit',
35403541
'Target value',
35413542
'Compute on'
35423543
],
@@ -3554,6 +3555,41 @@ describe('properties panel', function() {
35543555
});
35553556

35563557

3558+
describe('js function field', function() {
3559+
3560+
it('entries', function() {
3561+
3562+
// given
3563+
const field = schema.components.find(({ type }) => type === 'script');
3564+
3565+
bootstrapPropertiesPanel({
3566+
container,
3567+
field
3568+
});
3569+
3570+
// then
3571+
expectPanelStructure(container, {
3572+
'General': [
3573+
'Key',
3574+
'Do not submit',
3575+
'Function parameters',
3576+
'Javascript code',
3577+
'Compute on'
3578+
],
3579+
'Condition': [
3580+
'Deactivate if'
3581+
],
3582+
'Layout': [
3583+
'Columns'
3584+
],
3585+
'Custom properties': []
3586+
});
3587+
3588+
});
3589+
3590+
});
3591+
3592+
35573593
describe('iframe', function() {
35583594

35593595
it('entries', function() {

0 commit comments

Comments
 (0)