Skip to content

Commit

Permalink
🐛 [#4727] Transform initialValue on user variable dataType change
Browse files Browse the repository at this point in the history
  • Loading branch information
robinmolen committed Oct 28, 2024
1 parent 35fcd06 commit cbf41ac
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ import {
initialFormTranslations,
initialStepTranslations,
} from './translations';
import useConfirm from './useConfirm';
import {
checkIfInitialValueTransformationIsNeeded,
checkKeyChange,
findComponent,
getFormComponents,
Expand All @@ -70,6 +72,7 @@ import {
getUniqueKey,
parseValidationErrors,
slugify,
transformInitialValue,
updateKeyReferencesInLogic,
updateRemovedKeyInLogic,
} from './utils';
Expand Down Expand Up @@ -784,6 +787,18 @@ function reducer(draft, action) {
draft.formVariables[index][propertyName] = propertyValue;
}

// Check if initialValue needs to be transformd on variable 'type' change.
// If it is the case, ask for confirmation and change the initialValue
if (
propertyName === 'dataType' &&
checkIfInitialValueTransformationIsNeeded(propertyValue, originalVariable)
) {
draft.formVariables[index]['initialValue'] = transformInitialValue(
propertyValue,
originalVariable
);
}

// Check if there are errors that need to be reset
if (draft.formVariables[index].errors) {
const errorKeys = propertyName === '' ? Object.keys(propertyValue) : [propertyName];
Expand Down Expand Up @@ -1229,6 +1244,45 @@ const FormCreationForm = ({formUuid, formUrl, formHistoryUrl, outgoingRequestsUr
payload: pluginId,
});
};
const [ConfirmModal, confirmDelete] = useConfirm(
intl.formatMessage({
description:
'Changing user variable data type and transforming initial value confirmation title',
defaultMessage: 'Are you sure that you want to change the data type?',
}),
intl.formatMessage({
description:
'Changing user variable data type and transforming initial value confirmation message',
defaultMessage:
'Changing the data type requires the initial value to be changed, in some cases this could mean result in the initial value being reset to the default value. Are you sure that you want to do this?',
})
);
const onUserDefinedVariableChange = async (key, propertyName, propertyValue) => {
const originalVariable = state.formVariables.find(variable => variable.key === key);
// Just dispatch if anything other than dataType changes
// or if the initialValue is null/undefined
if (propertyName !== 'dataType' || originalVariable?.initialValue == null) {
dispatch({
type: 'CHANGE_USER_DEFINED_VARIABLE',
payload: {key, propertyName, propertyValue},
});
return;
}

// Check if the dataType change is intentional.
if (
propertyName === 'dataType' &&
checkIfInitialValueTransformationIsNeeded(propertyValue, originalVariable) &&
!(await confirmDelete())
) {
return;
}

dispatch({
type: 'CHANGE_USER_DEFINED_VARIABLE',
payload: {key, propertyName, propertyValue},
});
};

if (loading || state.submitting) {
return <Loader />;
Expand Down Expand Up @@ -1268,8 +1322,8 @@ const FormCreationForm = ({formUuid, formUrl, formHistoryUrl, outgoingRequestsUr
<div className="fetch-error">
<FormattedMessage
description="Generic admin error message"
defaultMessage={`Sorry! Something unexpected went wrong.<br></br>Contact your
technical administrator to investigate, or perhaps more information is
defaultMessage={`Sorry! Something unexpected went wrong.<br></br>Contact your
technical administrator to investigate, or perhaps more information is
available in the <link>outgoing request logs</link>.`}
values={{
br: () => <br />,
Expand Down Expand Up @@ -1506,12 +1560,7 @@ const FormCreationForm = ({formUuid, formUrl, formHistoryUrl, outgoingRequestsUr
variables={state.formVariables}
onAdd={() => dispatch({type: 'ADD_USER_DEFINED_VARIABLE'})}
onDelete={key => dispatch({type: 'DELETE_USER_DEFINED_VARIABLE', payload: key})}
onChange={(key, propertyName, propertyValue) =>
dispatch({
type: 'CHANGE_USER_DEFINED_VARIABLE',
payload: {key, propertyName, propertyValue},
})
}
onChange={onUserDefinedVariableChange}
onFieldChange={onFieldChange}
/>
</TabPanel>
Expand All @@ -1527,6 +1576,7 @@ const FormCreationForm = ({formUuid, formUrl, formHistoryUrl, outgoingRequestsUr
</Tabs>
</FormContext.Provider>

<ConfirmModal />
<FormSubmit onSubmit={onSubmit} displayActions={!state.newForm} />
</ValidationErrorsProvider>
);
Expand Down
70 changes: 70 additions & 0 deletions src/openforms/js/components/admin/form_design/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,74 @@ const checkKeyChange = (mutationType, newComponent, oldComponent) => {
return newComponent.key !== oldComponent.key;
};

const checkIfInitialValueTransformationIsNeeded = (newType, originalVariable) => {
// If the initialValue is null/undefined, we should transform
if (originalVariable?.initialValue == null) {
return true;
}

switch (newType) {
case 'array':
// If the value isn't in array format, it should be transformd
return !Array.isArray(originalVariable.initialValue);

case 'boolean':
return typeof originalVariable.initialValue !== 'boolean';

case 'object':
return typeof originalVariable.initialValue !== 'object';

case 'int':
case 'float':
return typeof originalVariable.initialValue !== 'number';

case 'string':
case 'datetime':
case 'date':
case 'time':
return typeof originalVariable.initialValue !== 'string';

default:
return false;
}
};

const transformInitialValue = (newType, originalVariable) => {
switch (newType) {
case 'array':
if (
originalVariable?.initialValue == null ||
typeof originalVariable.initialValue === 'object'
) {
return [];
}
return [originalVariable.initialValue];

case 'boolean':
return undefined;

case 'int':
return Number.parseInt(originalVariable.initialValue);

case 'float':
return Number.parseFloat(originalVariable.initialValue);

case 'object':
return originalVariable.initialValue == null ? {} : {value: originalVariable.initialValue};

case 'string':
return JSON.stringify(originalVariable.initialValue);

case 'datetime':
case 'date':
case 'time':
return '';

default:
return originalVariable.initialValue;
}
};

const updateKeyReferencesInLogic = (existingLogicRules, originalKey, newKey) => {
for (const rule of existingLogicRules) {
if (!JSON.stringify(rule).includes(originalKey)) continue;
Expand Down Expand Up @@ -219,6 +287,8 @@ export {
getFormComponents,
findComponent,
checkKeyChange,
checkIfInitialValueTransformationIsNeeded,
transformInitialValue,
updateKeyReferencesInLogic,
updateRemovedKeyInLogic,
getUniqueKey,
Expand Down

0 comments on commit cbf41ac

Please sign in to comment.