← All changes
|
src/DonationForms/resources/app/utilities/ConvertFieldAPIRulesToJoi.ts
+54
-8
4.16.2
→
4.18.0.1
View file →
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import Joi, {AnySchema, ObjectSchema} from 'joi'; |
| 2 | -import {BasicCondition, Field, Form, isField} from '@givewp/forms/types'; | |
| 2 | +import {AmountField, BasicCondition, Field, Form, isField} from '@givewp/forms/types'; | |
| 3 | 3 | import {__, sprintf} from '@wordpress/i18n'; |
| 4 | 4 | import conditionOperatorFunctions from '@givewp/forms/app/utilities/conditionOperatorFunctions'; |
| 5 | 5 | |
| 6 | 6 | /** |
| @@ -35,12 +35,13 @@ | ||
| 35 | 35 | }); |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | 38 | /** |
| 39 | + * @since 4.16.8 collect the amounts exempt from the minimum and maximum | |
| 39 | 40 | * @since 3.0.0 |
| 40 | 41 | */ |
| 41 | 42 | function getJoiRulesForField(field: Field): AnySchema { |
| 42 | - let rules: AnySchema = convertFieldAPIRulesToJoi(field.validationRules); | |
| 43 | + let rules: AnySchema = convertFieldAPIRulesToJoi(field.validationRules, getExemptAmounts(field)); | |
| 43 | 44 | |
| 44 | 45 | if (field.label) { |
| 45 | 46 | rules = rules.label(field.label); |
| 46 | 47 | } |
| @@ -48,13 +49,35 @@ | ||
| 48 | 49 | return rules; |
| 49 | 50 | } |
| 50 | 51 | |
| 51 | 52 | /** |
| 53 | + * The amounts the admin configured on the donation amount field. The custom amount minimum and maximum only | |
| 54 | + * constrain what a donor types into the custom amount input, so these are always accepted. | |
| 55 | + * | |
| 56 | + * @since 4.16.8 | |
| 57 | + */ | |
| 58 | +function getExemptAmounts(field: Field): number[] { | |
| 59 | + if (field.type !== 'amount') { | |
| 60 | + return []; | |
| 61 | + } | |
| 62 | + | |
| 63 | + const {levels, allowLevels, fixedAmountValue} = field as AmountField; | |
| 64 | + | |
| 65 | + // An unset level or fixed amount reads as 0, which must never be exempt from the minimum. | |
| 66 | + if (allowLevels) { | |
| 67 | + return (levels ?? []).map((level) => Number(level.value)).filter((amount) => amount > 0); | |
| 68 | + } | |
| 69 | + | |
| 70 | + return fixedAmountValue > 0 ? [Number(fixedAmountValue)] : []; | |
| 71 | +} | |
| 72 | + | |
| 73 | +/** | |
| 74 | + * @since 4.16.8 exempt admin-defined amounts from the minimum and maximum | |
| 52 | 75 | * @since 4.13.1 account for custom rules with only excludeUnless property |
| 53 | 76 | * @since 4.13.0 add support for optional false values |
| 54 | 77 | * @since 3.0.0 |
| 55 | 78 | */ |
| 56 | -function convertFieldAPIRulesToJoi(rules): AnySchema { | |
| 79 | +function convertFieldAPIRulesToJoi(rules, exemptAmounts: number[] = []): AnySchema { | |
| 57 | 80 | let joiRules; |
| 58 | 81 | const ruleKeys = Object.keys(rules); |
| 59 | 82 | |
| 60 | 83 | if (ruleKeys.length === 0 || (ruleKeys.length === 1 && 'excludeUnless' in rules)) { |
| @@ -91,14 +114,37 @@ | ||
| 91 | 114 | } |
| 92 | 115 | } |
| 93 | 116 | |
| 94 | 117 | if (rules.hasOwnProperty('number') || !rules.hasOwnProperty('boolean')) { |
| 95 | - if (rules.hasOwnProperty('min')) { | |
| 96 | - joiRules = joiRules.min(rules.min); | |
| 97 | - } | |
| 118 | + const hasMin = rules.hasOwnProperty('min'); | |
| 119 | + const hasMax = rules.hasOwnProperty('max'); | |
| 98 | 120 | |
| 99 | - if (rules.hasOwnProperty('max')) { | |
| 100 | - joiRules = joiRules.max(rules.max); | |
| 121 | + if (exemptAmounts.length > 0 && (hasMin || hasMax)) { | |
| 122 | + joiRules = joiRules.custom((value, helpers) => { | |
| 123 | + const amount = Number(value); | |
| 124 | + | |
| 125 | + if (exemptAmounts.includes(amount)) { | |
| 126 | + return value; | |
| 127 | + } | |
| 128 | + | |
| 129 | + if (hasMin && amount < rules.min) { | |
| 130 | + return helpers.error('number.min', {limit: rules.min}); | |
| 131 | + } | |
| 132 | + | |
| 133 | + if (hasMax && amount > rules.max) { | |
| 134 | + return helpers.error('number.max', {limit: rules.max}); | |
| 135 | + } | |
| 136 | + | |
| 137 | + return value; | |
| 138 | + }, 'exempt amount range'); | |
| 139 | + } else { | |
| 140 | + if (hasMin) { | |
| 141 | + joiRules = joiRules.min(rules.min); | |
| 142 | + } | |
| 143 | + | |
| 144 | + if (hasMax) { | |
| 145 | + joiRules = joiRules.max(rules.max); | |
| 146 | + } | |
| 101 | 147 | } |
| 102 | 148 | } |
| 103 | 149 | |
| 104 | 150 | if (rules.required) { |