diff --git a/config/fields/text.php b/config/fields/text.php index 7faabfd9ec..89f607d693 100644 --- a/config/fields/text.php +++ b/config/fields/text.php @@ -98,6 +98,7 @@ ], 'validations' => [ 'minlength', - 'maxlength' + 'maxlength', + 'pattern' ] ]; diff --git a/panel/src/components/Forms/Input/TextInput.vue b/panel/src/components/Forms/Input/TextInput.vue index 28e2d02bdb..d619c6b001 100644 --- a/panel/src/components/Forms/Input/TextInput.vue +++ b/panel/src/components/Forms/Input/TextInput.vue @@ -96,13 +96,18 @@ export default { } }, validations() { + const match = (value) => { + return (!this.required && value.length === 0) || !this.$refs.input.validity.patternMismatch; + }; + return { value: { required: this.required ? required : true, minLength: this.minlength ? minLength(this.minlength) : true, maxLength: this.maxlength ? maxLength(this.maxlength) : true, email: this.type === "email" ? email : true, - url: this.type === "url" ? url : true + url: this.type === "url" ? url : true, + pattern: this.pattern ? match: true, } }; } diff --git a/src/Form/Validations.php b/src/Form/Validations.php index fe3c584e3d..fdc260a989 100644 --- a/src/Form/Validations.php +++ b/src/Form/Validations.php @@ -107,6 +107,19 @@ public static function minlength(Field $field, $value): bool return true; } + public static function pattern(Field $field, $value): bool + { + if ($field->isEmpty($value) === false && $field->pattern() !== null) { + if (V::match($value, '/' . $field->pattern() . '/i') === false) { + throw new InvalidArgumentException( + V::message('match') + ); + } + } + + return true; + } + public static function required(Field $field, $value): bool { if ($field->isRequired() === true && $field->save() === true && $field->isEmpty($value) === true) { diff --git a/tests/Form/ValidationsTest.php b/tests/Form/ValidationsTest.php index 75bdb7c78d..1b24437b60 100644 --- a/tests/Form/ValidationsTest.php +++ b/tests/Form/ValidationsTest.php @@ -186,6 +186,34 @@ public function testMinLengthInvalid() Validations::minlength($field, 'test'); } + public function testPatternValid() + { + $page = new Page(['slug' => 'test']); + $field = new Field('test', [ + 'pattern' => '^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$', + 'model' => $page + ]); + + $this->assertTrue(Validations::pattern($field, '#fff')); + $this->assertTrue(Validations::pattern($field, '#222')); + $this->assertTrue(Validations::pattern($field, '#afafaf')); + $this->assertTrue(Validations::pattern($field, '#34b3cd')); + } + + public function testPatternInvalid() + { + $this->expectException('Kirby\Exception\InvalidArgumentException'); + $this->expectExceptionMessage('The value does not match the expected pattern'); + + $page = new Page(['slug' => 'test']); + $field = new Field('test', [ + 'pattern' => '^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$', + 'model' => $page + ]); + + Validations::pattern($field, '#MMM'); + } + public function testRequiredValid() { $page = new Page(['slug' => 'test']);