diff --git a/resources/js/components/field-conditions/Validator.js b/resources/js/components/field-conditions/Validator.js index 9ffcaf7608b..598b4b7927e 100644 --- a/resources/js/components/field-conditions/Validator.js +++ b/resources/js/components/field-conditions/Validator.js @@ -284,7 +284,10 @@ export default class { return intersection(condition.lhs, condition.rhs).length; } - return new RegExp(condition.rhs.join('|')).test(condition.lhs); + // Match each value literally, so characters like `.`, `(` or `+` aren't read as a pattern. + const lhs = condition.lhs === null || condition.lhs === undefined ? '' : String(condition.lhs); + + return condition.rhs.some((value) => lhs.includes(value)); } passesCustomCondition(condition) { diff --git a/resources/js/tests/FieldConditionsValidator.test.js b/resources/js/tests/FieldConditionsValidator.test.js index ed48441f49f..ecef0a30dcf 100644 --- a/resources/js/tests/FieldConditionsValidator.test.js +++ b/resources/js/tests/FieldConditionsValidator.test.js @@ -207,6 +207,22 @@ test('it can use includes_any or contains_any operators in conditions', () => { expect(showFieldIf({ null_value: 'contains_any fox, 13' })).toBe(false); }); +test('it matches contains_any values literally rather than as patterns', () => { + setValues({ + version: 'a.b', + other_version: 'axb', + phone: 'Call 555-0100', + language: 'C++', + price: 'From $100', + }); + + expect(showFieldIf({ version: 'contains_any a.b' })).toBe(true); + expect(showFieldIf({ other_version: 'contains_any a.b' })).toBe(false); + expect(showFieldIf({ phone: 'contains_any (555)' })).toBe(false); + expect(showFieldIf({ language: 'contains_any C++' })).toBe(true); + expect(showFieldIf({ price: 'contains_any 50%, $100' })).toBe(true); +}); + test('it handles null, true, and false in condition as literal', () => { setValues({ last_name: 'HasselHoff',