Skip to content

Commit

Permalink
Merge pull request #194 from formio/FIO-9321-possible-to-submit-the-f…
Browse files Browse the repository at this point in the history
…orm-when-the-value-is-not-an-available-option-for-Select-Boxes

FIO-9321 fixed onlyAlailableItems validation for select Boxes
  • Loading branch information
brendanbond authored Nov 26, 2024
2 parents 30ac705 + 0719a30 commit c7ac015
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 14 deletions.
104 changes: 103 additions & 1 deletion src/process/validation/rules/__tests__/validateAvailableItems.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'chai';

import { FieldError } from 'error';
import { RadioComponent, SelectComponent } from 'types';
import { RadioComponent, SelectBoxesComponent, SelectComponent } from 'types';
import {
simpleRadioField,
simpleSelectBoxes,
Expand Down Expand Up @@ -577,4 +577,106 @@ describe('validateAvailableItems', function () {
expect(result).to.be.instanceOf(FieldError);
expect(result?.errorKeyOrMessage).to.equal('invalidOption');
});

it('Validating a simple static values select boxes component with the available items validation parameter will return null if the selected item is valid', async function () {
const component: SelectBoxesComponent = {
...simpleSelectBoxes,
validate: { onlyAvailableItems: true },
};
const data = {
component: {
foo: true,
bar: false,
baz: true,
biz: false,
},
};
const context = generateProcessorContext(component, data);
const result = await validateAvailableItems(context);
expect(result).to.equal(null);
});

it('Validating a simple static values select boxes component with the available items validation parameter will return FieldError if the selected item is invalid', async function () {
const component: SelectBoxesComponent = {
...simpleSelectBoxes,
validate: { onlyAvailableItems: true },
};
const data = {
component: {
foo: true,
bar: false,
baz: true,
biz: false,
new: true,
test: false
},
};
const context = generateProcessorContext(component, data);
const result = await validateAvailableItems(context);
expect(result).to.be.instanceOf(FieldError);
expect(result?.errorKeyOrMessage).to.equal('invalidOption');
});

it('Validating a select boxes component with url data source with the available items validation parameter will return null if the selected item is valid', async function () {
const component: SelectBoxesComponent = {
...simpleSelectBoxes,
dataSrc: 'url',
data: {
url: 'http://localhost:8080/numbers',
headers: [],
},
validate: { onlyAvailableItems: true },
};
const data = {
component: {
one: true,
two: false,
three: true
},
};
const context = generateProcessorContext(component, data);

context.fetch = () => {
return Promise.resolve({
ok: true,
json: () =>
Promise.resolve(['one', 'two', 'three']),
});
};
const result = await validateAvailableItems(context);
expect(result).to.equal(null);
});

it('Validating a select boxes component with url data source with the available items validation parameter will return FieldError if the selected item is invalid', async function () {
const component: SelectBoxesComponent = {
...simpleSelectBoxes,
dataSrc: 'url',
data: {
url: 'http://localhost:8080/numbers',
headers: [],
},
validate: { onlyAvailableItems: true },
};
const data = {
component: {
one: true,
two: false,
three: true,
four: true,
five: false
},
};
const context = generateProcessorContext(component, data);

context.fetch = () => {
return Promise.resolve({
ok: true,
json: () =>
Promise.resolve(['one', 'two', 'three']),
});
};
const result = await validateAvailableItems(context);
expect(result).to.be.instanceOf(FieldError);
expect(result?.errorKeyOrMessage).to.equal('invalidOption');
});
});
35 changes: 22 additions & 13 deletions src/process/validation/rules/validateAvailableItems.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEmpty, isUndefined } from 'lodash';
import { isEmpty, isUndefined, difference } from 'lodash';
import { FieldError, ProcessorError } from 'error';
import { Evaluator } from 'utils';
import {
Expand Down Expand Up @@ -31,8 +31,7 @@ function isValidateableSelectBoxesComponent(component: any): component is Select
return (
component &&
!!component.validate?.onlyAvailableItems &&
component.type === 'selectboxes' &&
component.dataSrc === 'url'
component.type === 'selectboxes'
);
}

Expand Down Expand Up @@ -273,19 +272,18 @@ export const validateAvailableItems: RuleFn = async (context: ValidationContext)
return values.find((optionValue) => optionValue === value) !== undefined ? null : error;
}
} else if (isValidateableSelectBoxesComponent(component)) {
if (value == null || isEmpty(value)) {
if (value == null || isEmpty(value) || !isObject(value)) {
return null;
}
const values = await getAvailableDynamicValues(component, context);
if (values) {
if (isObject(value)) {
return values.find((optionValue) => compareComplexValues(optionValue, value, context)) !==
undefined
? null
: error;
}

return values.find((optionValue) => optionValue === value) !== undefined ? null : error;
const values =
component.dataSrc === 'url'
? await getAvailableDynamicValues(component, context)
: component.values.map(val => val.value);
if (values) {
return difference(Object.keys(value), values).length
? error
: null;
}
}
} catch (err: any) {
Expand Down Expand Up @@ -337,6 +335,17 @@ export const validateAvailableItemsSync: RuleFnSync = (context: ValidationContex
}
return values.find((optionValue) => optionValue === value) !== undefined ? null : error;
}
} else if (isValidateableSelectBoxesComponent(component) && component.dataSrc !== 'url') {
if (value == null || isEmpty(value) || !isObject(value)) {
return null;
}

const values = component.values.map(val => val.value);
if (values) {
return difference(Object.keys(value), values).length
? error
: null;
}
}
} catch (err: any) {
throw new ProcessorError(err.message || err, context, 'validate:validateAvailableItems');
Expand Down

0 comments on commit c7ac015

Please sign in to comment.