Skip to content

Commit

Permalink
feat: validate TextField on validated event
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen committed Jul 4, 2022
1 parent c5c107c commit 71c7794
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.textfield.tests;

import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.NativeButton;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;

/**
* Test view for {@link TextField}.
*/
@Route("vaadin-text-field/basic-validation")
public class TextFieldBasicValidationPage extends Div {
public TextFieldBasicValidationPage() {
TextField textField = new TextField();
textField.setRequiredIndicatorVisible(true);

Div validationStatus = new Div();
validationStatus.setId("validation-status");

NativeButton retreiveValidationStatus = new NativeButton("Retreive validation status", (event) -> {
boolean isValid = !textField.isInvalid();
validationStatus.setText(String.valueOf(isValid));
});
retreiveValidationStatus.setId("retreive-validation-status");

add(textField, validationStatus, retreiveValidationStatus);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.textfield.tests;

import com.vaadin.flow.component.textfield.testbench.TextFieldElement;
import com.vaadin.flow.testutil.TestPath;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.tests.AbstractComponentIT;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

@TestPath("vaadin-text-field/basic-validation")
public class TextFieldBasicValidationIT extends AbstractComponentIT {
TextFieldElement field;
TestBenchElement input;

@Before
public void init() {
open();
field = $(TextFieldElement.class).first();
input = field.$("input").first();
}

@Test
public void fieldIsValidByDefault() {
assertClientValid(true);
assertServerValid(true);
}

@Test
public void triggerInputBlur_fieldIsValidated() {
input.dispatchEvent("blur");
assertServerValid(false);
assertClientValid(false);
}

@Test
public void triggerInputChange_fieldIsValidated() {
input.dispatchEvent("change");
assertServerValid(false);
assertClientValid(false);
}

@Test
public void clientCannotSetFieldToValid() {
executeScript("arguments[0].validate()", field);
assertClientValid(false);

input.setProperty("value", "Not empty value");
input.dispatchEvent("input");
executeScript("arguments[0].validate()", field);
assertClientValid(false);
}

private void assertClientValid(boolean expected) {
Assert.assertEquals(expected, !field.getPropertyBoolean("invalid"));
}

private void assertServerValid(boolean expected) {
$("button").id("retreive-validation-status").click();
Assert.assertEquals(String.valueOf(expected), $("div").id("validation-status").getText());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private FieldValidationUtil() {
// utility class should not be instantiated
}

static void disableClientValidation(Component component) {
static void preventClientFromSettingFieldToValid(Component component) {
// Since this method should be called for every time when the component
// is attached to the UI, lets check that it is actually so
if (!component.isAttached()) {
Expand All @@ -55,7 +55,7 @@ static void disableClientValidation(Component component) {

private static void overrideClientValidation(Component component) {
StringBuilder expression = new StringBuilder(
"this.validate = function () {return this.checkValidity();};");
"this._shouldSetInvalid = function (invalid) { return invalid };");

if (component instanceof HasValidation
&& ((HasValidation) component).isInvalid()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
package com.vaadin.flow.component.textfield;

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.CompositionNotifier;
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.EventData;
import com.vaadin.flow.component.shared.HasAllowedCharPattern;
import com.vaadin.flow.component.shared.HasClearButton;
import com.vaadin.flow.component.HasHelper;
Expand All @@ -29,6 +34,7 @@
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.value.HasValueChangeMode;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.shared.Registration;

/**
* Text Field allows the user to input and edit text. Prefix and suffix
Expand Down Expand Up @@ -79,6 +85,8 @@ private TextField(boolean isInitialValueOptional) {
setValueChangeMode(ValueChangeMode.ON_CHANGE);

addValueChangeListener(e -> validate());

addClientValidatedEventListener(e -> validate());
}

/**
Expand Down Expand Up @@ -179,6 +187,12 @@ public TextField(String label, String initialValue,
addValueChangeListener(listener);
}

@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
FieldValidationUtil.preventClientFromSettingFieldToValid(this);
}

private TextFieldValidationSupport getValidationSupport() {
if (validationSupport == null) {
validationSupport = new TextFieldValidationSupport(this);
Expand Down Expand Up @@ -494,9 +508,48 @@ protected void validate() {
setInvalid(getValidationSupport().isInvalid(getValue()));
}

@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
FieldValidationUtil.disableClientValidation(this);
/**
* Adds a client validated listener. The listener is called whenever
* the field is validated on the client-side.
*
* @param listener
* the client validated listener, not null.
* @return a registration for the listener.
*/
public Registration addClientValidatedEventListener(
ComponentEventListener<ClientValidatedEvent> listener) {
return ComponentUtil.addListener(this, ClientValidatedEvent.class, listener);
}

/**
* An event fired when a TextField is validated on the client-side.
*/
@DomEvent("validated")
public static class ClientValidatedEvent extends ComponentEvent<TextField> {

private final boolean valid;

/**
* Creates a new event using the given source and indicator whether the
* event originated from the client side or the server side.
*
* @param source
* the source component
* @param valid
* whether the client-side validation succeeded.
*/
public ClientValidatedEvent(TextField source, @EventData("event.detail.valid") boolean valid) {
super(source, true);
this.valid = valid;
}

/**
* Returns true if the client-side validation succeeded and false otherwise.
*
* @return whether the client-side validation succeeded.
*/
public Boolean isValid() {
return valid;
}
}
}

0 comments on commit 71c7794

Please sign in to comment.