Skip to content

Commit 99d883f

Browse files
committed
fix: ui-state-reducer now works as intended
feat: better example conditions in the schema feat: debug-div changed to pre for better JSON-rendering chore: dispatchCondition renamed to dispatchUIState for clarity chore: dispatch after parseCondition is now separated into two different dispatches (add/remove) for clarity
1 parent b69cd34 commit 99d883f

File tree

5 files changed

+117
-121
lines changed

5 files changed

+117
-121
lines changed

packages/react-form-renderer/demo/index.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,53 @@ const fileSchema = {
1010
{
1111
component: 'text-field',
1212
name: 'field1',
13-
label: 'Field1',
14-
initialValue: '"abc" to trigger condition "cond1"',
15-
type: 'search',
13+
label: 'Field1 (try "abc")',
1614
},
1715
{
1816
component: 'text-field',
1917
name: 'field2',
20-
label: 'Field2',
21-
initialValue: '"xyz" to trigger condition "cond1"',
22-
type: 'search',
23-
condition: {when: 'field2', is: 'aaa'},
18+
label: 'Field2 (try "xyz")',
2419
},
2520
{
2621
component: 'text-field',
2722
name: 'field3',
28-
label: 'Field3',
29-
initialValue: '"123" to trigger condition "cond2"',
30-
type: 'search',
23+
label: 'Field3 (try "123")',
3124
},
3225
{
3326
component: 'text-field',
3427
name: 'field4',
3528
label: 'Field4',
36-
initialValue: 'Visible when field3="aa" (old style condition)',
3729
},
3830
],
3931
conditions: {
4032
cond1: {
4133
when: 'field1',
4234
is: 'abc',
4335
then: {
44-
field3: {
36+
field4: {
4537
disabled: true,
46-
set: 'New value for field3',
38+
set: 'New value for field4',
4739
},
48-
field4: {
49-
visible: false,
40+
field3: {
41+
disabled: true,
5042
},
5143
},
5244
},
5345
cond2: {
5446
when: 'field3',
5547
is: '123',
5648
then: {
57-
field1: {
58-
hidden: true,
49+
field3: {
50+
visible: false,
51+
},
52+
},
53+
},
54+
cond3: {
55+
when: 'field2',
56+
is: 'xyz',
57+
then: {
58+
field3: {
59+
visible: false,
5960
},
6061
},
6162
},

packages/react-form-renderer/src/files/form-renderer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const FormRenderer = ({
2929
...props
3030
}) => {
3131
const [fileInputs, setFileInputs] = useState([]);
32-
const [uiState, dispatchCondition] = useReducer(uiStateReducer, {
32+
const [uiState, dispatchUIState] = useReducer(uiStateReducer, {
3333
fields: {},
3434
setFieldValues: {},
3535
});
@@ -94,7 +94,7 @@ const FormRenderer = ({
9494
getState,
9595
registerField,
9696
uiState,
97-
dispatchCondition,
97+
dispatchUIState,
9898
valid,
9999
clearedValue,
100100
submit,
@@ -110,7 +110,7 @@ const FormRenderer = ({
110110
<RegisterConditions schema={schema} />
111111
<SetFieldValues />
112112
<FormTemplate formFields={renderForm(schema.fields)} schema={schema} />
113-
<div>{JSON.stringify(uiState, null, 2)}</div>
113+
<pre>{JSON.stringify(uiState, null, 2)}</pre>
114114
</RendererContext.Provider>
115115
)}
116116
/>

packages/react-form-renderer/src/files/register-conditions.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {conditionsMapper} from './conditions-mapper';
66
import {parseCondition} from '../form-renderer/condition2';
77

88
const RegisterConditions = ({schema}) => {
9-
const {getState, registerField, dispatchCondition} = useFormApi();
9+
const {getState, registerField, dispatchUIState} = useFormApi();
1010

1111
useEffect(() => {
1212
const indexedConditions = conditionsMapper({conditions: schema.conditions});
@@ -32,11 +32,25 @@ const RegisterConditions = ({schema}) => {
3232

3333
fieldState.data.conditions.map(condition => {
3434
const conditionResult = parseCondition(condition, getState().values);
35-
dispatchCondition({
36-
type: 'conditionResult',
37-
source: condition.key,
38-
uiState: conditionResult.uiState,
39-
});
35+
const {
36+
uiState: {add, remove},
37+
} = conditionResult;
38+
39+
if (add) {
40+
dispatchUIState({
41+
type: 'addUIState',
42+
source: condition.key,
43+
uiState: add,
44+
});
45+
}
46+
47+
if (remove) {
48+
dispatchUIState({
49+
type: 'removeUIState',
50+
source: condition.key,
51+
uiState: remove,
52+
});
53+
}
4054
});
4155
},
4256
{value: true, data: true},

packages/react-form-renderer/src/files/set-field-values.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import lodashIsEmpty from 'lodash/isEmpty';
44
import {useFormApi} from '../';
55

66
const SetFieldValues = () => {
7-
const {batch, change, uiState, dispatchCondition} = useFormApi();
7+
const {batch, change, uiState, dispatchUIState} = useFormApi();
88
useEffect(() => {
99
if (lodashIsEmpty(uiState.setFieldValues)) return;
1010

@@ -14,7 +14,7 @@ const SetFieldValues = () => {
1414
console.log('Setting new value for field ' + name);
1515
change(name, value);
1616
});
17-
dispatchCondition({type: 'fieldValuesUpdated'});
17+
dispatchUIState({type: 'fieldValuesUpdated'});
1818
});
1919
});
2020
}, [uiState.setFieldValues]);

packages/react-form-renderer/src/files/ui-state-reducer.js

Lines changed: 74 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,91 @@
1+
const validAttributes = [
2+
'visible',
3+
'disabled',
4+
'hidden',
5+
'enabled',
6+
'icon',
7+
'help',
8+
'colorBar',
9+
'required',
10+
'max',
11+
'min',
12+
];
13+
14+
//enabled and hidden are not used internally, but accepted as input from the conditions object
15+
const inversedTypes = {
16+
enabled: 'disabled',
17+
hidden: 'visible',
18+
};
19+
120
const uiStateReducer = (state, action) => {
221
switch (action.type) {
3-
case 'conditionResult': {
4-
const {
5-
source,
6-
uiState: {add, remove},
7-
} = action;
8-
9-
const validAttributes = [
10-
'visible',
11-
'disabled',
12-
'hidden',
13-
'enabled',
14-
'icon',
15-
'help',
16-
'colorBar',
17-
'required',
18-
'max',
19-
'min',
20-
];
21-
22-
//enabled and hidden are not used internally, but accepted as input from the conditions object
23-
const inversedTypes = {
24-
enabled: 'disabled',
25-
hidden: 'visible',
26-
};
27-
28-
const removeItem = state => {
29-
if (!remove) return;
30-
31-
Object.entries(remove).forEach(([key, item]) => {
32-
//If the field/section doesn't exist, we don't need to do anymore
33-
if (!state[key]) return;
34-
35-
validAttributes.forEach(type => {
36-
//Don't process uiTypes if they don't exist in the dispatched message
37-
if (item[type] === undefined) return;
38-
39-
const inversedType = inversedTypes[type];
40-
type = inversedType || type;
41-
42-
if (remove[key][type]) {
43-
const index = state[key][type].findIndex(item => item.source === source);
44-
if (index !== -1) state[key][type].splice(index, 1);
45-
46-
//If this was the last item of this type, remove the type
47-
if (state[key][type].length === 0) delete state[key][type];
22+
case 'addUIState': {
23+
const {source, uiState} = action;
24+
25+
Object.entries(uiState).forEach(([key, item]) => {
26+
//Create the item object for this item if it doesn't exist
27+
if (!state.fields[key]) state.fields[key] = {};
28+
29+
validAttributes.forEach(type => {
30+
//Don't add uiTypes if they don't exist in the dispatched message
31+
if (item[type] === undefined) return;
32+
33+
//Handle inversed types (disabled/enabled, visible/hidden)
34+
const inversedType = inversedTypes[type];
35+
const value = inversedType ? !item[type] : item[type];
36+
type = inversedType || type;
37+
38+
if (!state.fields[key][type]) {
39+
//If this type doesn't exists for this item, we create a new array with only this source. No need to search fot the source
40+
state.fields[key][type] = [{source, value}];
41+
} else {
42+
const index = state.fields[key][type].findIndex(item => item.source === source);
43+
if (index !== -1) {
44+
//If this type for this item from this source existed, update the state (value could change if condition went from "then" to "else")
45+
state.fields[key][type][index].value = value;
46+
} else {
47+
//Otherwise, add the state from this source at the begining of the array (i.e. this will supress result from other sources)
48+
state.fields[key][type].unshift({source, value});
4849
}
49-
});
50-
51-
//If no more uiStateType keys exists for this field, remove the field
52-
if (Object.keys(state[key]).length === 0) delete state[key];
50+
}
5351
});
54-
};
55-
56-
const addItem = state => {
57-
if (!add) return;
5852

59-
Object.entries(add).forEach(([key, item]) => {
60-
//Create the item object for this item if it doesn't exist
61-
if (!state[key]) state[key] = {};
53+
// Set-instructions are ephemeral and goes into a separate list which is emptied when processed
54+
if (item.set) {
55+
state.setFieldValues = {...state.setFieldValues, [key]: item.set};
56+
}
57+
});
58+
return {...state};
59+
}
6260

63-
validAttributes.forEach(type => {
64-
//Don't add uiTypes if they don't exist in the dispatched message
65-
if (item[type] === undefined) return;
61+
case 'removeUIState': {
62+
const {source, uiState} = action;
6663

67-
//Handle inversed types (disabled/enabled, visible/hidden)
68-
const inversedType = inversedTypes[type];
69-
const value = inversedType ? !item[type] : item[type];
70-
type = inversedType || type;
64+
Object.entries(uiState).forEach(([key, item]) => {
65+
//If the field/section doesn't exist, we don't need to do anymore
66+
if (!state.fields[key]) return;
7167

72-
if (!state[key][type]) {
73-
//If this type doesn't exists for this item, we create a new array with only this source. No need to search fot the source
74-
state[key][type] = [{source, value}];
75-
} else {
76-
const index = state[key][type].findIndex(item => item.source === source);
77-
if (index !== -1) {
78-
//If this type for this item from this source existed, update the state (could change if condition went from "then" to "else")
79-
state[key][type][index].value = value;
80-
} else {
81-
//Otherwise, add the state from this source at the begining of the array (i.e. this will supress result from other sources)
82-
state[key][type].unshift({source, value});
83-
}
84-
}
85-
});
68+
Object.entries(item).forEach(([type, value]) => {
69+
if (!state.fields[key][type]) return;
8670

87-
//Set-instructions are ephemeral and goes into a separate list which is emptied when processed
88-
if (item.set) {
89-
state.setFieldValues = {...state.fileldValues, [key]: item.set};
90-
}
71+
const index = state.fields[key][type].findIndex(item => item.source === source);
72+
if (index !== -1) state.fields[key][type].splice(index, 1);
73+
if (state.fields[key][type].length === 0) delete state.fields[key][type];
9174
});
92-
};
93-
94-
let mutatedState = state;
95-
if (remove) {
96-
removeItem(mutatedState);
97-
}
98-
//If uiStates should be added, go through all add fields and all possible types for these fields
99-
if (add) {
100-
addItem(mutatedState);
101-
}
102-
console.log(mutatedState);
103-
return {...mutatedState};
75+
76+
//If no more uiStateType keys exists for this field, remove the field
77+
if (Object.keys(state.fields[key]).length === 0) delete state.fields[key];
78+
});
79+
80+
return {...state};
10481
}
82+
10583
case 'fieldValuesUpdated': {
10684
return {...state, setFieldValues: {}};
10785
}
86+
87+
default:
88+
return state;
10889
}
10990
};
11091

0 commit comments

Comments
 (0)