Skip to content

Commit

Permalink
Merge pull request #10011 from CitizenLabDotCo/TAN-3502-add-logic-to-…
Browse files Browse the repository at this point in the history
…multi-and-image-select

TAN-3502: Add logic to multi and image select
  • Loading branch information
EdwinKato authored Jan 14, 2025
2 parents ff01369 + bd8385f commit 7a61b49
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 18 deletions.
31 changes: 27 additions & 4 deletions front/app/components/Form/Components/Controls/visibilityUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Scopable,
composeWithUi,
resolveData,
JsonSchema,
} from '@jsonforms/core';
import Ajv from 'ajv';
import { has } from 'lodash-es';
Expand Down Expand Up @@ -73,6 +74,30 @@ const getConditionScope = (condition: Scopable, path: string): string => {
return composeWithUi(condition, path);
};

/**
* Validates a schema condition.
* Handles both single values and arrays of values.
*
* @param value - The resolved value to validate (single or array).
* @param schema - The JSON schema to validate against.
* @param ajv - The AJV instance for validation.
* @returns {boolean} - True if the value or any array item matches the schema.
*/
const validateSchemaCondition = (
value: any,
schema: JsonSchema,
ajv: Ajv
): boolean => {
if (Array.isArray(value)) {
// For arrays, check if at least one element passes validation. Important for multi-select and image-select
return value.some((val) => ajv.validate(schema, val));
} else if (schema.enum?.includes('no_answer') && value === undefined) {
return true;
}

return ajv.validate(schema, value);
};

const evaluateCondition = (
data: any,
condition: Condition,
Expand All @@ -93,11 +118,9 @@ const evaluateCondition = (
const value = resolveData(data, getConditionScope(condition, path));
return value === condition.expectedValue;
} else if (isSchemaCondition(condition)) {
// Schema condition: validates the resolved value(s) against the schema
const value = resolveData(data, getConditionScope(condition, path));
if (condition.schema.enum?.includes('no_answer') && value === undefined) {
return true;
}
return ajv.validate(condition.schema, value);
return validateSchemaCondition(value, condition.schema, ajv);
} else {
// unknown condition
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ export const QuestionRuleInput = ({
goto_page_id: page.value.toString(),
};
setRuleIsInvalid(!isRuleValid(newRule, fieldId, fields));

if (logic.rules) {
const newRulesArray = logic.rules;
newRulesArray.push(newRule);
logic.rules = newRulesArray;
} else {
logic.rules = [newRule];
}

setValue(name, { ...field, logic }, { shouldDirty: true });
trigger();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const LogicSettings = ({
label: option.title_multiloc[locale]?.toString(),
}))
: undefined;

// For Linear Scale Field
if (field.input_type === 'linear_scale') {
const linearScaleOptionArray = Array.from(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ const FormBuilderSettings = ({
const tabNotActiveBorder = `1px solid ${colors.grey400}`;
const tabActiveBorder = `4px solid ${colors.primary}`;
const fieldType = watch(`customFields.${field.index}.input_type`);
const showTabbedSettings = ['linear_scale', 'select', 'page'].includes(
fieldType
);
const showTabbedSettings = [
'multiselect',
'multiselect_image',
'linear_scale',
'select',
'page',
].includes(fieldType);

return (
<Box
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ const Logic = ({

return (
<Box>
{field.input_type === 'select' &&
{['select', 'multiselect', 'multiselect_image'].includes(
field.input_type
) &&
field.options &&
field.options.map((option) => {
const optionRule = getOptionRule(option, field);
Expand Down
8 changes: 7 additions & 1 deletion front/app/components/FormBuilder/edit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,13 @@ const FormEdit = ({
...(field.input_type === 'page' && {
temp_id: field.temp_id,
}),
...(['linear_scale', 'select', 'page'].includes(field.input_type)
...([
'multiselect',
'linear_scale',
'select',
'page',
'multiselect_image',
].includes(field.input_type)
? {
logic: field.logic,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ const IdeationInputs = ({
prescreening_enabled,
togglePrescreeningEnabled,
}: Props) => {
const prescreeningIdeationEnabled = useFeatureFlag({
name: 'prescreening_ideation',
});

return (
<>
<AnonymousPostingToggle
Expand All @@ -108,9 +112,7 @@ const IdeationInputs = ({
{
// Remove the following condition when pricing decision made
// And add feature flag with onlyCheckAllowed back into prescreeningFeatureAllowed
useFeatureFlag({
name: 'prescreening_ideation',
}) && (
prescreeningIdeationEnabled && (
<PrescreeningToggle
prescreening_enabled={prescreening_enabled}
togglePrescreeningEnabled={togglePrescreeningEnabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ const ProposalsInputs = ({
prescreening_enabled,
togglePrescreeningEnabled,
}: Props) => {
const prescreeningFeatureAllowed = useFeatureFlag({
name: 'prescreening',
onlyCheckAllowed: true,
});

return (
<>
<CustomFieldPicker
Expand Down Expand Up @@ -134,10 +139,7 @@ const ProposalsInputs = ({
<PrescreeningToggle
prescreening_enabled={prescreening_enabled}
togglePrescreeningEnabled={togglePrescreeningEnabled}
prescreeningFeatureAllowed={useFeatureFlag({
name: 'prescreening',
onlyCheckAllowed: true,
})}
prescreeningFeatureAllowed={prescreeningFeatureAllowed}
/>
<UserActions
submission_enabled={submission_enabled || false}
Expand Down
6 changes: 5 additions & 1 deletion front/app/utils/yup/validateLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ const validateLogic = (message: string) => {
logic: object(),
})
.when('input_type', (input_type: string, schema) => {
if (['select', 'linear_scale'].includes(input_type)) {
if (
['multiselect', 'multiselect_image', 'select', 'linear_scale'].includes(
input_type
)
) {
return schema.test(
'rules reference prior pages',
message,
Expand Down

0 comments on commit 7a61b49

Please sign in to comment.