aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-09-01 14:56:41 +0300
committerVaadin Code Review <review@vaadin.com>2016-09-12 08:11:33 +0000
commit78a5468279ddc442ac64d045f5fe4aa79ed9ef6e (patch)
tree4aabf5ea7495e1b0a3e39dc40ab1813bbe67dd69 /server
parentea89e24646cead0eef80dd42a7426fae4e0a6092 (diff)
downloadvaadin-framework-78a5468279ddc442ac64d045f5fe4aa79ed9ef6e.tar.gz
vaadin-framework-78a5468279ddc442ac64d045f5fe4aa79ed9ef6e.zip
Implement new RichTextArea
Change-Id: I6f430c77caaad6d610133f340eba960f2268897e
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractField.java15
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractTextField.java49
-rw-r--r--server/src/main/java/com/vaadin/ui/HasValueChangeMode.java69
-rw-r--r--server/src/main/java/com/vaadin/ui/PasswordField.java12
-rw-r--r--server/src/main/java/com/vaadin/ui/RichTextArea.java170
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java2
-rw-r--r--server/src/test/java/com/vaadin/ui/ComponentTest.java98
-rw-r--r--server/src/test/java/com/vaadin/ui/RichTextAreaTest.java107
8 files changed, 465 insertions, 57 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractField.java b/server/src/main/java/com/vaadin/ui/AbstractField.java
index f64cec9e66..4946d3c95b 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractField.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractField.java
@@ -142,21 +142,26 @@ public abstract class AbstractField<T> extends AbstractComponent
*
* @param value
* the new value to set
- * @return {@code true} if this event originates from the client,
- * {@code false} otherwise.
+ * @param userOriginated
+ * {@code true} if this event originates from the client,
+ * {@code false} otherwise.
+ * @return <code>true</code> if the value was updated, <code>false</code>
+ * otherwise
*/
- protected void setValue(T value, boolean userOriginated) {
+ protected boolean setValue(T value, boolean userOriginated) {
if (userOriginated && isReadOnly()) {
- return;
+ return false;
}
if (Objects.equals(value, getValue())) {
- return;
+ return false;
}
doSetValue(value);
if (!userOriginated) {
markAsDirty();
}
fireEvent(createValueChange(userOriginated));
+
+ return true;
}
/**
diff --git a/server/src/main/java/com/vaadin/ui/AbstractTextField.java b/server/src/main/java/com/vaadin/ui/AbstractTextField.java
index e1b0273c6c..b5119ec92c 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractTextField.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractTextField.java
@@ -27,10 +27,10 @@ import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.shared.Registration;
import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
+import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.shared.ui.textfield.AbstractTextFieldClientRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldServerRpc;
import com.vaadin.shared.ui.textfield.AbstractTextFieldState;
-import com.vaadin.shared.ui.textfield.ValueChangeMode;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
@@ -40,7 +40,8 @@ import com.vaadin.ui.declarative.DesignContext;
* @author Vaadin Ltd.
* @since 8.0
*/
-public abstract class AbstractTextField extends AbstractField<String> {
+public abstract class AbstractTextField extends AbstractField<String>
+ implements HasValueChangeMode {
private final class AbstractTextFieldServerRpcImpl
implements AbstractTextFieldServerRpc {
@@ -173,6 +174,7 @@ public abstract class AbstractTextField extends AbstractField<String> {
/**
* Returns the last known cursor position of the field.
*
+ * @return the last known cursor position
*/
public int getCursorPosition() {
return lastKnownCursorPosition;
@@ -212,41 +214,17 @@ public abstract class AbstractTextField extends AbstractField<String> {
listener);
}
- /**
- * Sets the mode how the TextField triggers {@link ValueChange}s.
- *
- * @param mode
- * the new mode
- *
- * @see ValueChangeMode
- */
+ @Override
public void setValueChangeMode(ValueChangeMode mode) {
getState().valueChangeMode = mode;
}
- /**
- * Returns the currently set {@link ValueChangeMode}.
- *
- * @return the mode used to trigger {@link ValueChange}s.
- *
- * @see ValueChangeMode
- */
+ @Override
public ValueChangeMode getValueChangeMode() {
return getState(false).valueChangeMode;
}
- /**
- * Sets how often {@link ValueChange}s are triggered when the
- * {@link ValueChangeMode} is set to either {@link ValueChangeMode#LAZY} or
- * {@link ValueChangeMode#TIMEOUT}.
- *
- * @param timeout
- * timeout in milliseconds, must be greater or equal to 0
- * @throws IllegalArgumentException
- * if given timeout is smaller than 0
- *
- * @see ValueChangeMode
- */
+ @Override
public void setValueChangeTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException(
@@ -255,15 +233,7 @@ public abstract class AbstractTextField extends AbstractField<String> {
getState().valueChangeTimeout = timeout;
}
- /**
- * Returns the currently set timeout, in milliseconds, for how often
- * {@link ValueChange}s are triggered if the current {@link ValueChangeMode}
- * is set to either {@link ValueChangeMode#LAZY} or
- * {@link ValueChangeMode#TIMEOUT}.
- *
- * @return the timeout in milliseconds of how often {@link ValueChange}s are
- * triggered.
- */
+ @Override
public int getValueChangeTimeout() {
return getState(false).valueChangeTimeout;
}
@@ -303,7 +273,8 @@ public abstract class AbstractTextField extends AbstractField<String> {
/**
* Checks if the field is empty.
*
- * @return true if the field value is an empty string, false otherwise
+ * @return <code>true</code> if the field value is an empty string,
+ * <code>false</code> otherwise
*/
public boolean isEmpty() {
return "".equals(getValue());
diff --git a/server/src/main/java/com/vaadin/ui/HasValueChangeMode.java b/server/src/main/java/com/vaadin/ui/HasValueChangeMode.java
new file mode 100644
index 0000000000..0d48a29a72
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/HasValueChangeMode.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2016 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.ui;
+
+import com.vaadin.data.HasValue.ValueChange;
+import com.vaadin.shared.ui.ValueChangeMode;
+
+/**
+ * Implemented by components which support value change modes.
+ */
+public interface HasValueChangeMode extends Component {
+ /**
+ * Sets the mode how the TextField triggers {@link ValueChange}s.
+ *
+ * @param valueChangeMode
+ * the new mode
+ *
+ * @see ValueChangeMode
+ */
+ public void setValueChangeMode(ValueChangeMode valueChangeMode);
+
+ /**
+ * Returns the currently set {@link ValueChangeMode}.
+ *
+ * @return the mode used to trigger {@link ValueChange}s.
+ *
+ * @see ValueChangeMode
+ */
+ public ValueChangeMode getValueChangeMode();
+
+ /**
+ * Sets how often {@link ValueChange}s are triggered when the
+ * {@link ValueChangeMode} is set to either {@link ValueChangeMode#LAZY} or
+ * {@link ValueChangeMode#TIMEOUT}.
+ *
+ * @param valueChangeTimeout
+ * timeout in milliseconds, must be greater or equal to 0
+ * @throws IllegalArgumentException
+ * if given timeout is smaller than 0
+ *
+ * @see ValueChangeMode
+ */
+ public void setValueChangeTimeout(int valueChangeTimeout);
+
+ /**
+ * Returns the currently set timeout, in milliseconds, for how often
+ * {@link ValueChange}s are triggered if the current {@link ValueChangeMode}
+ * is set to either {@link ValueChangeMode#LAZY} or
+ * {@link ValueChangeMode#TIMEOUT}.
+ *
+ * @return the timeout in milliseconds of how often {@link ValueChange}s are
+ * triggered.
+ */
+ public int getValueChangeTimeout();
+
+}
diff --git a/server/src/main/java/com/vaadin/ui/PasswordField.java b/server/src/main/java/com/vaadin/ui/PasswordField.java
index a3fb4d265a..231236c01f 100644
--- a/server/src/main/java/com/vaadin/ui/PasswordField.java
+++ b/server/src/main/java/com/vaadin/ui/PasswordField.java
@@ -59,12 +59,6 @@ public class PasswordField extends TextField {
setCaption(caption);
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractField#readDesign(org.jsoup.nodes.Element ,
- * com.vaadin.ui.declarative.DesignContext)
- */
@Override
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
@@ -75,12 +69,6 @@ public class PasswordField extends TextField {
}
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractTextField#writeDesign(org.jsoup.nodes.Element
- * , com.vaadin.ui.declarative.DesignContext)
- */
@Override
public void writeDesign(Element design, DesignContext designContext) {
super.writeDesign(design, designContext);
diff --git a/server/src/main/java/com/vaadin/ui/RichTextArea.java b/server/src/main/java/com/vaadin/ui/RichTextArea.java
new file mode 100644
index 0000000000..49346928be
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/RichTextArea.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2000-2016 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.ui;
+
+import org.jsoup.nodes.Element;
+
+import com.vaadin.shared.ui.ValueChangeMode;
+import com.vaadin.shared.ui.richtextarea.RichTextAreaClientRpc;
+import com.vaadin.shared.ui.richtextarea.RichTextAreaServerRpc;
+import com.vaadin.shared.ui.richtextarea.RichTextAreaState;
+import com.vaadin.ui.declarative.DesignContext;
+
+/**
+ * A simple RichTextArea to edit HTML format text.
+ */
+public class RichTextArea extends AbstractField<String>
+ implements HasValueChangeMode {
+
+ private class RichTextAreaServerRpcImpl implements RichTextAreaServerRpc {
+ @Override
+ public void setText(String text) {
+ getUI().getConnectorTracker().getDiffState(RichTextArea.this)
+ .put("value", text);
+ if (!setValue(text, true)) {
+ // The value was not updated, this could happen if the field has
+ // been set to readonly on the server and the client does not
+ // know about it yet. Must re-send the correct state back.
+ markAsDirty();
+ }
+ }
+ }
+
+ /**
+ * Constructs an empty <code>RichTextArea</code> with no caption.
+ */
+ public RichTextArea() {
+ super();
+ registerRpc(new RichTextAreaServerRpcImpl());
+ setValue("");
+ }
+
+ /**
+ * Constructs an empty <code>RichTextArea</code> with the given caption.
+ *
+ * @param caption
+ * the caption for the editor.
+ */
+ public RichTextArea(String caption) {
+ this();
+ setCaption(caption);
+ }
+
+ /**
+ * Constructs a new <code>RichTextArea</code> with the given caption and
+ * initial text contents.
+ *
+ * @param caption
+ * the caption for the editor.
+ * @param value
+ * the initial text content of the editor.
+ */
+ public RichTextArea(String caption, String value) {
+ this(caption);
+ setValue(value);
+ }
+
+ @Override
+ public void readDesign(Element design, DesignContext designContext) {
+ super.readDesign(design, designContext);
+ setValue(design.html());
+ }
+
+ @Override
+ public void writeDesign(Element design, DesignContext designContext) {
+ super.writeDesign(design, designContext);
+ design.html(getValue());
+ }
+
+ @Override
+ protected RichTextAreaState getState() {
+ return (RichTextAreaState) super.getState();
+ }
+
+ @Override
+ protected RichTextAreaState getState(boolean markAsDirty) {
+ return (RichTextAreaState) super.getState(markAsDirty);
+ }
+
+ @Override
+ public void setValue(String value) {
+ if (value == null) {
+ setValue("", false);
+ } else {
+ setValue(value, false);
+ }
+ }
+
+ @Override
+ public String getValue() {
+ return getState(false).value;
+ }
+
+ @Override
+ protected void doSetValue(String value) {
+ getState().value = value;
+ }
+
+ /**
+ * Selects all text in the rich text area. As a side effect, focuses the
+ * rich text area.
+ *
+ * @since 6.5
+ */
+ public void selectAll() {
+ getRpcProxy(RichTextAreaClientRpc.class).selectAll();
+ focus();
+ }
+
+ @Override
+ public void setValueChangeMode(ValueChangeMode mode) {
+ getState().valueChangeMode = mode;
+ }
+
+ @Override
+ public ValueChangeMode getValueChangeMode() {
+ return getState(false).valueChangeMode;
+ }
+
+ @Override
+ public void setValueChangeTimeout(int timeout) {
+ getState().valueChangeTimeout = timeout;
+
+ }
+
+ @Override
+ public int getValueChangeTimeout() {
+ return getState(false).valueChangeTimeout;
+ }
+
+ /**
+ * Checks if the field is empty.
+ *
+ * @return <code>true</code> if the field value is an empty string,
+ * <code>false</code> otherwise
+ */
+ public boolean isEmpty() {
+ return getValue().length() == 0;
+ }
+
+ /**
+ * Clears the value of this field.
+ */
+ public void clear() {
+ setValue("");
+ }
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java
index 63367363d5..d7fd06dbe8 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/abstracttextfield/AbstractTextFieldDeclarativeTest.java
@@ -17,7 +17,7 @@ package com.vaadin.tests.server.component.abstracttextfield;
import org.junit.Test;
-import com.vaadin.shared.ui.textfield.ValueChangeMode;
+import com.vaadin.shared.ui.ValueChangeMode;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractTextField;
import com.vaadin.ui.TextField;
diff --git a/server/src/test/java/com/vaadin/ui/ComponentTest.java b/server/src/test/java/com/vaadin/ui/ComponentTest.java
new file mode 100644
index 0000000000..8cd9afa776
--- /dev/null
+++ b/server/src/test/java/com/vaadin/ui/ComponentTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2016 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.ui;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.ServerRpcMethodInvocation;
+import com.vaadin.shared.communication.ServerRpc;
+
+/**
+ * Base class for component unit tests, providing helper methods for e.g.
+ * invoking RPC and updating diff state.
+ */
+public class ComponentTest {
+
+ /**
+ * Perform operations on the component similar to what would be done when
+ * the component state is communicated to the client, e.g. update diff state
+ * and mark as clean.
+ *
+ * @param component
+ * the component to update
+ */
+ public static void syncToClient(AbstractComponent component) {
+ updateDiffState(component);
+ component.getUI().getConnectorTracker().markClean(component);
+ }
+
+ /**
+ * Checks if the connector has been marked dirty.
+ *
+ * @param connector
+ * the connector to check
+ * @return <code>true</code> if the connector has been marked dirty,
+ * <code>false</code> otherwise
+ */
+ public static boolean isDirty(ClientConnector connector) {
+ return connector.getUI().getConnectorTracker().isDirty(connector);
+ }
+
+ /**
+ * Updates the stored diff state from the current component state.
+ *
+ * @param rta
+ * the component to update
+ */
+ public static void updateDiffState(AbstractComponent component) {
+ component.getUI().getSession().getCommunicationManager()
+ .encodeState(component, component.getState());
+
+ }
+
+ /**
+ * Gets a proxy object which invokes ServerRpc methods.
+ *
+ * @param component
+ * the component which listens to the RPC
+ * @param serverRpcClass
+ * the server RPC class
+ * @return a proxy which can be used to invoke RPC methods
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends ServerRpc> T getRpcProxy(Component component,
+ Class<T> serverRpcClass) {
+ return (T) Proxy.newProxyInstance(component.getClass().getClassLoader(),
+ new Class[] { serverRpcClass }, new InvocationHandler() {
+
+ @Override
+ public Object invoke(Object proxy, Method method,
+ Object[] args) throws Throwable {
+ ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
+ component.getConnectorId(), serverRpcClass,
+ method.getName(), args.length);
+ invocation.setParameters(args);
+ component.getRpcManager(serverRpcClass.getName())
+ .applyInvocation(invocation);
+ return null;
+ }
+ });
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/ui/RichTextAreaTest.java b/server/src/test/java/com/vaadin/ui/RichTextAreaTest.java
new file mode 100644
index 0000000000..554e002848
--- /dev/null
+++ b/server/src/test/java/com/vaadin/ui/RichTextAreaTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2016 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.ui;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.ServerRpcManager.RpcInvocationException;
+import com.vaadin.shared.ui.richtextarea.RichTextAreaServerRpc;
+import com.vaadin.tests.util.MockUI;
+
+public class RichTextAreaTest extends ComponentTest {
+
+ @Test
+ public void initiallyEmpty() {
+ RichTextArea tf = new RichTextArea();
+ Assert.assertTrue(tf.isEmpty());
+ }
+
+ @Test
+ public void setValueServerWhenReadOnly() {
+ RichTextArea tf = new RichTextArea();
+ tf.setReadOnly(true);
+ tf.setValue("foo");
+ Assert.assertEquals("foo", tf.getValue());
+ }
+
+ @Test
+ public void diffStateAfterClientSetValueWhenReadOnly() {
+ UI ui = new MockUI();
+
+ // If the client has a non-readonly text field which is set to read-only
+ // on the server, then any update from the client must cause both the
+ // readonly state and the old value to be sent
+ RichTextArea rta = new RichTextArea();
+ ui.setContent(rta);
+ rta.setValue("bar");
+ rta.setReadOnly(true);
+ syncToClient(rta);
+
+ // Client thinks the field says "foo" but it won't be updated because
+ // the field is readonly
+ getRpcProxy(rta, RichTextAreaServerRpc.class).setText("foo");
+
+ // The real value will be sent back as long as the field is marked as
+ // dirty and diffstate contains what the client has
+ Assert.assertEquals("foo", getDiffStateString(rta, "value"));
+ Assert.assertTrue("Component should be marked dirty", isDirty(rta));
+ }
+
+ @Test
+ public void setValueClientNotSentBack() throws RpcInvocationException {
+ UI ui = new MockUI();
+ RichTextArea rta = new RichTextArea();
+ ui.setContent(rta);
+ rta.setValue("bar");
+
+ updateDiffState(rta);
+ getRpcProxy(rta, RichTextAreaServerRpc.class).setText("foo");
+ Assert.assertEquals("foo", getDiffStateString(rta, "value"));
+ }
+
+ private String getDiffStateString(ClientConnector connector, String key) {
+ return connector.getUI().getConnectorTracker().getDiffState(connector)
+ .get(key).asString();
+ }
+
+ @Test
+ public void setValueClientRefusedWhenReadOnly() {
+ RichTextArea tf = new RichTextArea();
+ tf.setValue("bar");
+ tf.setReadOnly(true);
+ tf.setValue("foo", true);
+ Assert.assertEquals("bar", tf.getValue());
+ }
+
+ @Test
+ public void setValueNullBecomesEmptyString() {
+ RichTextArea tf = new RichTextArea();
+ tf.setValue(null);
+ Assert.assertEquals("", tf.getValue());
+ }
+
+ @Test
+ public void emptyAfterClear() {
+ RichTextArea tf = new RichTextArea();
+ tf.setValue("foobar");
+ Assert.assertFalse(tf.isEmpty());
+ tf.clear();
+ Assert.assertTrue(tf.isEmpty());
+ }
+
+}