]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix TextArea with enter keyboard shortcut (#12424)
authorMarkus Koivisto <markus@vaadin.com>
Thu, 24 Apr 2014 13:54:03 +0000 (16:54 +0300)
committerVaadin Code Review <review@vaadin.com>
Fri, 25 Apr 2014 14:10:24 +0000 (14:10 +0000)
When a keyboardshortcut has been added to anywhere on the page,
the previous behaviour would cause the keyboardshortcut event to
be processeed before the newline was processed. The end result
was that newlines were never added when typing in the TextArea.

Keyboard shortcuts operate on KeyDown events. By adding a listener
for these events and stopping their propagation when the ENTER key
is pressed, this unwanted behaviour can be averted, and the user
can enter multi-line text in a TextArea even when Enter is used as
a keyboard shortcut.

Obviously, this means that the keyboard shortcut will not work as
long as the TextArea widget has focus. This is the new intended
behaviour.

Change-Id: Ied438acb8589df498e5634271e486517bf6ac41e

client/src/com/vaadin/client/ui/VTextArea.java
uitest/src/com/vaadin/tests/components/ui/TextAreaEventPropagation.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/ui/TextAreaEventPropagationTest.java [new file with mode: 0644]

index 5150ab254494d5ca04783dbdcbfbdcca0056a88f..e6a3aa2a0939281f74b39891d92de619675a9120 100644 (file)
@@ -23,7 +23,9 @@ import com.google.gwt.dom.client.Style.WhiteSpace;
 import com.google.gwt.dom.client.TextAreaElement;
 import com.google.gwt.event.dom.client.ChangeEvent;
 import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
 import com.google.gwt.event.dom.client.KeyUpEvent;
 import com.google.gwt.event.dom.client.KeyUpHandler;
 import com.google.gwt.user.client.Command;
@@ -43,14 +45,21 @@ import com.vaadin.client.ui.dd.VDragCloneAware;
  * 
  */
 public class VTextArea extends VTextField implements VDragCloneAware {
+
     public static final String CLASSNAME = "v-textarea";
     private boolean wordwrap = true;
     private MaxLengthHandler maxLengthHandler = new MaxLengthHandler();
     private boolean browserSupportsMaxLengthAttribute = browserSupportsMaxLengthAttribute();
+    private EnterDownHandler enterDownHandler = new EnterDownHandler();
 
     public VTextArea() {
         super(DOM.createTextArea());
         setStyleName(CLASSNAME);
+
+        // KeyDownHandler is needed for correct text input on all
+        // browsers, not just those that don't support a max length attribute
+        addKeyDownHandler(enterDownHandler);
+
         if (!browserSupportsMaxLengthAttribute) {
             addKeyUpHandler(maxLengthHandler);
             addChangeHandler(maxLengthHandler);
@@ -249,6 +258,20 @@ public class VTextArea extends VTextField implements VDragCloneAware {
         }
     }
 
+    private class EnterDownHandler implements KeyDownHandler {
+
+        @Override
+        public void onKeyDown(KeyDownEvent event) {
+            // Fix for #12424 - if the key being pressed is enter, we stop
+            // propagation of the KeyDownEvents. This prevents shortcuts that
+            // are bound to the enter key from being processed.
+            if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
+                event.stopPropagation();
+            }
+        }
+
+    }
+
     @Override
     public int getCursorPos() {
         // This is needed so that TextBoxImplIE6 is used to return the correct
@@ -294,6 +317,7 @@ public class VTextArea extends VTextField implements VDragCloneAware {
         // Overridden to avoid submitting TextArea value on enter in IE. This is
         // another reason why widgets should inherit a common abstract
         // class instead of directly each other.
+        // This method is overridden only for IE and Firefox.
     }
 
     @Override
diff --git a/uitest/src/com/vaadin/tests/components/ui/TextAreaEventPropagation.java b/uitest/src/com/vaadin/tests/components/ui/TextAreaEventPropagation.java
new file mode 100644 (file)
index 0000000..31eeac0
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2014 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.tests.components.ui;
+
+import com.vaadin.event.ShortcutAction.KeyCode;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TextArea;
+import com.vaadin.ui.TextField;
+
+/**
+ * UI test for TextArea behavior when ENTER has been assigned as a keyboard
+ * shortcut.
+ * 
+ * @author Vaadin Ltd
+ */
+public class TextAreaEventPropagation extends AbstractTestUIWithLog {
+
+    protected static final String BUTTON_PRESSED = "Button Pressed";
+
+    protected static final String NO_BUTTON_PRESSED = "No Button Pressed";
+
+    private Label enterButtonPressed;
+
+    private Label escapeButtonPressed;
+
+    @Override
+    protected void setup(VaadinRequest request) {
+
+        FormLayout form = new FormLayout();
+        TextArea textArea = new TextArea("Text input");
+        TextField textField = new TextField("Text field input");
+        enterButtonPressed = new Label("Enter Label");
+        enterButtonPressed.setCaption(NO_BUTTON_PRESSED);
+        escapeButtonPressed = new Label("Escape Label");
+        escapeButtonPressed.setCaption(NO_BUTTON_PRESSED);
+
+        Button enterButton = new Button("Enter");
+        enterButton.setClickShortcut(KeyCode.ENTER);
+        enterButton.addClickListener(new ClickListener() {
+
+            @Override
+            public void buttonClick(ClickEvent event) {
+
+                enterButtonPressed.setCaption(BUTTON_PRESSED);
+            }
+        });
+
+        Button escapeButton = new Button("Escape");
+        escapeButton.setClickShortcut(KeyCode.ESCAPE);
+        escapeButton.addClickListener(new ClickListener() {
+
+            @Override
+            public void buttonClick(ClickEvent event) {
+
+                escapeButtonPressed.setCaption(BUTTON_PRESSED);
+            }
+        });
+
+        form.addComponent(textArea);
+        form.addComponent(textField);
+        form.addComponent(enterButton);
+        form.addComponent(escapeButton);
+        form.addComponent(enterButtonPressed);
+        form.addComponent(escapeButtonPressed);
+        addComponent(form);
+
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Currently if enter key is set as a shortcut for some component, it won't be possible for the user to enter newline in a textarea.";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return Integer.valueOf(12424);
+    }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/ui/TextAreaEventPropagationTest.java b/uitest/src/com/vaadin/tests/components/ui/TextAreaEventPropagationTest.java
new file mode 100644 (file)
index 0000000..11e0c52
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2000-2014 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.tests.components.ui;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ * Tests that the TextArea widget correctly stops ENTER events from propagating.
+ * 
+ * @author Vaadin Ltd
+ */
+public class TextAreaEventPropagationTest extends MultiBrowserTest {
+
+    @Test
+    public void testTextAreaEnterEventPropagation() throws InterruptedException {
+        openTestURL();
+        WebElement textArea = vaadinElement("//TextArea[0]");
+        Actions builder = new Actions(driver);
+        builder.click(textArea);
+        builder.sendKeys(textArea, "first line asdf");
+        builder.sendKeys(Keys.ENTER);
+        builder.sendKeys(textArea, "second line jkl;");
+        builder.perform();
+
+        WebElement enterLabel = driver.findElement(By.id("gwt-uid-8"));
+        String text = enterLabel.getText();
+        assertEquals(TextAreaEventPropagation.NO_BUTTON_PRESSED, text);
+
+        WebElement textField = vaadinElement("//TextField[0]");
+        Actions builder2 = new Actions(driver);
+        builder2.click(textField);
+
+        builder2.sendKeys("third line");
+        builder2.sendKeys(Keys.ENTER);
+
+        builder2.perform();
+
+        text = enterLabel.getText();
+
+        assertEquals(TextAreaEventPropagation.BUTTON_PRESSED, text);
+
+    }
+
+    @Test
+    public void testTextAreaEscapeEventPropagation()
+            throws InterruptedException {
+        openTestURL();
+        WebElement textArea = vaadinElement("//TextArea[0]");
+        Actions builder = new Actions(driver);
+        builder.click(textArea);
+        builder.sendKeys(textArea, "first line asdf");
+        builder.sendKeys(Keys.ENTER);
+        builder.sendKeys(textArea, "second line jkl;");
+        builder.sendKeys(Keys.ESCAPE);
+        builder.perform();
+
+        WebElement enterLabel = driver.findElement(By.id("gwt-uid-8"));
+        String text = enterLabel.getText();
+        assertEquals(TextAreaEventPropagation.NO_BUTTON_PRESSED, text);
+        WebElement escapeLabel = driver.findElement(By.id("gwt-uid-10"));
+        text = escapeLabel.getText();
+        assertEquals(TextAreaEventPropagation.BUTTON_PRESSED, text);
+
+    }
+
+    @Test
+    public void testTextFieldEscapeEventPropagation()
+            throws InterruptedException {
+        openTestURL();
+        WebElement textArea = vaadinElement("//TextArea[0]");
+        Actions builder = new Actions(driver);
+        builder.click(textArea);
+        builder.sendKeys(textArea, "first line asdf");
+        builder.sendKeys(Keys.ENTER);
+        builder.sendKeys(textArea, "second line jkl;");
+        builder.perform();
+
+        WebElement enterLabel = driver.findElement(By.id("gwt-uid-8"));
+        String text = enterLabel.getText();
+        assertEquals(TextAreaEventPropagation.NO_BUTTON_PRESSED, text);
+        WebElement escapeLabel = driver.findElement(By.id("gwt-uid-10"));
+
+        WebElement textField = vaadinElement("//TextField[0]");
+        Actions builder2 = new Actions(driver);
+        builder2.click(textField);
+
+        builder2.sendKeys("third line");
+        builder2.sendKeys(Keys.ENTER);
+        builder2.sendKeys(Keys.ESCAPE);
+
+        builder2.perform();
+
+        text = enterLabel.getText();
+        assertEquals(TextAreaEventPropagation.BUTTON_PRESSED, text);
+
+        text = escapeLabel.getText();
+
+        assertEquals(TextAreaEventPropagation.BUTTON_PRESSED, text);
+
+    }
+
+}