]> source.dussan.org Git - vaadin-framework.git/commitdiff
Implement JavaScript renderer support (#15485)
authorLeif Åstrand <leif@vaadin.com>
Mon, 17 Nov 2014 06:14:10 +0000 (08:14 +0200)
committerVaadin Code Review <review@vaadin.com>
Tue, 13 Jan 2015 20:30:45 +0000 (20:30 +0000)
Change-Id: Ifeac12d4124a4a7e5d0c143ff5c0590a2c98509d

client/src/com/vaadin/client/JavaScriptConnectorHelper.java
client/src/com/vaadin/client/connectors/JavaScriptRendererConnector.java [new file with mode: 0644]
client/src/com/vaadin/client/widget/grid/RendererCellReference.java
server/src/com/vaadin/server/AbstractJavaScriptExtension.java
server/src/com/vaadin/ui/AbstractJavaScriptComponent.java
server/src/com/vaadin/ui/renderer/AbstractJavaScriptRenderer.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderers.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/grid/MyBeanJSRenderer.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/grid/myBeanJsRenderer.js [new file with mode: 0644]

index 11511f9c36f7516989d793f1a033b21bd64524f1..3a9a6198d32c52301d5181f1ac21cdba9aa3f237 100644 (file)
@@ -50,7 +50,7 @@ public class JavaScriptConnectorHelper {
     private JavaScriptObject connectorWrapper;
     private int tag;
 
-    private boolean inited = false;
+    private String initFunctionName;
 
     public JavaScriptConnectorHelper(ServerConnector connector) {
         this.connector = connector;
@@ -96,9 +96,8 @@ public class JavaScriptConnectorHelper {
                 }
 
                 // Init after setting up callbacks & rpc
-                if (!inited) {
+                if (initFunctionName == null) {
                     initJavaScript();
-                    inited = true;
                 }
 
                 invokeIfPresent(wrapper, "onStateChange");
@@ -120,7 +119,7 @@ public class JavaScriptConnectorHelper {
         return object;
     }
 
-    private boolean initJavaScript() {
+    protected boolean initJavaScript() {
         ApplicationConfiguration conf = connector.getConnection()
                 .getConfiguration();
         ArrayList<String> attemptedNames = new ArrayList<String>();
@@ -132,6 +131,7 @@ public class JavaScriptConnectorHelper {
             if (tryInitJs(initFunctionName, getConnectorWrapper())) {
                 VConsole.log("JavaScript connector initialized using "
                         + initFunctionName);
+                this.initFunctionName = initFunctionName;
                 return true;
             } else {
                 VConsole.log("No JavaScript function " + initFunctionName
@@ -160,7 +160,7 @@ public class JavaScriptConnectorHelper {
         }
     }-*/;
 
-    private JavaScriptObject getConnectorWrapper() {
+    public JavaScriptObject getConnectorWrapper() {
         if (connectorWrapper == null) {
             connectorWrapper = createConnectorWrapper(this,
                     connector.getConnection(), nativeState, rpcMap,
@@ -465,4 +465,7 @@ public class JavaScriptConnectorHelper {
         }
     }-*/;
 
+    public String getInitFunctionName() {
+        return initFunctionName;
+    }
 }
diff --git a/client/src/com/vaadin/client/connectors/JavaScriptRendererConnector.java b/client/src/com/vaadin/client/connectors/JavaScriptRendererConnector.java
new file mode 100644 (file)
index 0000000..82d863c
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * 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.client.connectors;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArrayString;
+import com.google.gwt.dom.client.NativeEvent;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.JavaScriptConnectorHelper;
+import com.vaadin.client.Util;
+import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
+import com.vaadin.client.renderers.ComplexRenderer;
+import com.vaadin.client.renderers.Renderer;
+import com.vaadin.client.widget.grid.CellReference;
+import com.vaadin.client.widget.grid.RendererCellReference;
+import com.vaadin.shared.JavaScriptExtensionState;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.ui.renderer.AbstractJavaScriptRenderer;
+
+import elemental.json.JsonObject;
+import elemental.json.JsonValue;
+
+/**
+ * Connector for server-side renderer implemented using JavaScript.
+ * 
+ * @since
+ * @author Vaadin Ltd
+ */
+@Connect(AbstractJavaScriptRenderer.class)
+public class JavaScriptRendererConnector extends
+        AbstractRendererConnector<JsonValue> implements
+        HasJavaScriptConnectorHelper {
+    private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper(
+            this);
+
+    private final JavaScriptObject cellReferenceWrapper = createCellReferenceWrapper(BrowserInfo
+            .get().isIE8());
+
+    @Override
+    protected void init() {
+        super.init();
+        helper.init();
+
+        addGetRowKey(helper.getConnectorWrapper());
+    }
+
+    private static native JavaScriptObject createCellReferenceWrapper(
+            boolean isIE8)
+    /*-{
+        var reference = {};
+        if (isIE8) {
+          // IE8 only supports defineProperty for DOM objects
+          reference = $doc.createElement('div');
+        }
+        
+        var setProperty = function(name, getter, setter) {
+            var descriptor = {
+                get: getter
+            }
+            if (setter) {
+                descriptor.set = setter;
+            }
+            Object.defineProperty(reference, name, descriptor);
+        };
+        
+        setProperty("element", function() {
+            return reference.target.@CellReference::getElement()();
+        }, null);
+        
+        setProperty("rowIndex", function() {
+            return reference.target.@CellReference::getRowIndex()();
+        }, null);
+        
+        setProperty("columnIndex", function() {
+            return reference.target.@CellReference::getColumnIndex()();
+        }, null);
+        
+        setProperty("colSpan", function() {
+            return reference.target.@RendererCellReference::getColSpan()();
+        }, function(colSpan) {
+            reference.target.@RendererCellReference::setColSpan(*)(colSpan);
+        });
+        
+        return reference;
+    }-*/;
+
+    @Override
+    public JavaScriptExtensionState getState() {
+        return (JavaScriptExtensionState) super.getState();
+    }
+
+    private native void addGetRowKey(JavaScriptObject wrapper)
+    /*-{
+        var self = this;
+        wrapper.getRowKey = $entry(function(rowIndex) {
+            return @JavaScriptRendererConnector::findRowKey(*)(self, rowIndex);
+        });
+    }-*/;
+
+    private static String findRowKey(JavaScriptRendererConnector connector,
+            int rowIndex) {
+        GridConnector gc = (GridConnector) connector.getParent();
+        JsonObject row = gc.getWidget().getDataSource().getRow(rowIndex);
+        return connector.getRowKey(row);
+    }
+
+    private boolean hasFunction(String name) {
+        return hasFunction(helper.getConnectorWrapper(), name);
+    }
+
+    private static native boolean hasFunction(JavaScriptObject wrapper,
+            String name)
+    /*-{
+        return typeof wrapper[name] === 'function';
+    }-*/;
+
+    @Override
+    protected Renderer<JsonValue> createRenderer() {
+        if (!hasFunction("render")) {
+            throw new RuntimeException("JavaScriptRenderer "
+                    + helper.getInitFunctionName()
+                    + " must have a function named 'render'");
+        }
+
+        final boolean hasInit = hasFunction("init");
+        final boolean hasDestroy = hasFunction("destroy");
+        final boolean hasOnActivate = hasFunction("onActivate");
+        final boolean hasGetConsumedEvents = hasFunction("getConsumedEvents");
+        final boolean hasOnBrowserEvent = hasFunction("onBrowserEvent");
+
+        return new ComplexRenderer<JsonValue>() {
+            @Override
+            public void render(RendererCellReference cell, JsonValue data) {
+                render(helper.getConnectorWrapper(), getJsCell(cell),
+                        Util.json2jso(data));
+            }
+
+            private JavaScriptObject getJsCell(CellReference<?> cell) {
+                updateCellReference(cellReferenceWrapper, cell);
+                return cellReferenceWrapper;
+            }
+
+            public native void render(JavaScriptObject wrapper,
+                    JavaScriptObject cell, JavaScriptObject data)
+            /*-{
+                wrapper.render(cell, data);
+            }-*/;
+
+            @Override
+            public void init(RendererCellReference cell) {
+                if (hasInit) {
+                    init(helper.getConnectorWrapper(), getJsCell(cell));
+                }
+            }
+
+            private native void init(JavaScriptObject wrapper,
+                    JavaScriptObject cell)
+            /*-{
+                wrapper.init(cell);
+            }-*/;
+
+            private native void updateCellReference(
+                    JavaScriptObject cellWrapper, CellReference<?> target)
+            /*-{
+                cellWrapper.target = target;
+            }-*/;
+
+            @Override
+            public void destroy(RendererCellReference cell) {
+                if (hasDestroy) {
+                    destory(helper.getConnectorWrapper(), getJsCell(cell));
+                } else {
+                    super.destroy(cell);
+                }
+            }
+
+            private native void destory(JavaScriptObject wrapper,
+                    JavaScriptObject cell)
+            /*-{
+                wrapper.destory(cell);
+            }-*/;
+
+            @Override
+            public boolean onActivate(CellReference<?> cell) {
+                if (hasOnActivate) {
+                    return onActivate(helper.getConnectorWrapper(),
+                            getJsCell(cell));
+                } else {
+                    return super.onActivate(cell);
+                }
+            }
+
+            private native boolean onActivate(JavaScriptObject wrapper,
+                    JavaScriptObject cell)
+            /*-{
+                return !!wrapper.onActivate(cell);
+            }-*/;
+
+            @Override
+            public Collection<String> getConsumedEvents() {
+                if (hasGetConsumedEvents) {
+                    JsArrayString events = getConsumedEvents(helper
+                            .getConnectorWrapper());
+
+                    ArrayList<String> list = new ArrayList<String>(
+                            events.length());
+                    for (int i = 0; i < events.length(); i++) {
+                        list.add(events.get(i));
+                    }
+                    return list;
+                } else {
+                    return super.getConsumedEvents();
+                }
+            }
+
+            private native JsArrayString getConsumedEvents(
+                    JavaScriptObject wrapper)
+            /*-{
+                var rawEvents = wrapper.getConsumedEvents();
+                var events = [];
+                for(var i = 0; i < rawEvents.length; i++) {
+                  events[i] = ""+rawEvents[i];
+                }
+                return events;
+            }-*/;
+
+            @Override
+            public boolean onBrowserEvent(CellReference<?> cell,
+                    NativeEvent event) {
+                if (hasOnBrowserEvent) {
+                    return onBrowserEvent(helper.getConnectorWrapper(),
+                            getJsCell(cell), event);
+                } else {
+                    return super.onBrowserEvent(cell, event);
+                }
+            }
+
+            private native boolean onBrowserEvent(JavaScriptObject wrapper,
+                    JavaScriptObject cell, NativeEvent event)
+            /*-{
+                return !!wrapper.onBrowserEvent(cell, event);
+            }-*/;
+        };
+    }
+
+    @Override
+    public JsonValue decode(JsonValue value) {
+        // Let the js logic decode the raw json that the server sent
+        return value;
+    }
+
+    @Override
+    public void onUnregister() {
+        super.onUnregister();
+        helper.onUnregister();
+    }
+
+    @Override
+    public JavaScriptConnectorHelper getJavascriptConnectorHelper() {
+        return helper;
+    }
+}
index 07ca4622931ff7888bbcb36473d99cb2e96e0a48..533eafded676f97dd46b1d3347f9f5a171c641c7 100644 (file)
@@ -83,7 +83,7 @@ public class RendererCellReference extends CellReference<Object> {
      * 
      * @return the number of columns that the cell should span
      */
-    public int getColspan() {
+    public int getColSpan() {
         return cell.getColSpan();
     }
 }
index e182319c85a5a9019594b695c24916be945d9922..e9cf2c5e33c904471894a32f6dbb41769df159b6 100644 (file)
@@ -106,8 +106,8 @@ import com.vaadin.ui.JavaScriptFunction;
  * <li>Java Dates are represented by JavaScript numbers containing the timestamp
  * </li>
  * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
- * <li>Map<String, ?> in Java is represented by JavaScript object with fields
- * corresponding to the map keys.</li>
+ * <li>Map&lt;String, ?&gt; in Java is represented by JavaScript object with
+ * fields corresponding to the map keys.</li>
  * <li>Any other Java Map is represented by a JavaScript array containing two
  * arrays, the first contains the keys and the second contains the values in the
  * same order.</li>
index f3cbf47b623676327716ba3ebdc9ad4237f11d9e..84023555bbf53be3925448bcdf2ce2fbcd651cce 100644 (file)
@@ -119,8 +119,8 @@ import com.vaadin.shared.ui.JavaScriptComponentState;
  * <li>Java Dates are represented by JavaScript numbers containing the timestamp
  * </li>
  * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
- * <li>Map<String, ?> in Java is represented by JavaScript object with fields
- * corresponding to the map keys.</li>
+ * <li>Map&lt;String, ?&gt; in Java is represented by JavaScript object with
+ * fields corresponding to the map keys.</li>
  * <li>Any other Java Map is represented by a JavaScript array containing two
  * arrays, the first contains the keys and the second contains the values in the
  * same order.</li>
diff --git a/server/src/com/vaadin/ui/renderer/AbstractJavaScriptRenderer.java b/server/src/com/vaadin/ui/renderer/AbstractJavaScriptRenderer.java
new file mode 100644 (file)
index 0000000..92c5047
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.ui.renderer;
+
+import com.vaadin.server.AbstractJavaScriptExtension;
+import com.vaadin.server.JavaScriptCallbackHelper;
+import com.vaadin.shared.JavaScriptExtensionState;
+import com.vaadin.shared.communication.ServerRpc;
+import com.vaadin.ui.Grid.AbstractRenderer;
+import com.vaadin.ui.JavaScriptFunction;
+
+/**
+ * Base class for Renderers with all client-side logic implemented using
+ * JavaScript.
+ * <p>
+ * When a new JavaScript renderer is initialized in the browser, the framework
+ * will look for a globally defined JavaScript function that will initialize the
+ * renderer. The name of the initialization function is formed by replacing .
+ * with _ in the name of the server-side class. If no such function is defined,
+ * each super class is used in turn until a match is found. The framework will
+ * thus first attempt with <code>com_example_MyRenderer</code> for the
+ * server-side
+ * <code>com.example.MyRenderer extends AbstractJavaScriptRenderer</code> class.
+ * If MyRenderer instead extends <code>com.example.SuperRenderer</code> , then
+ * <code>com_example_SuperRenderer</code> will also be attempted if
+ * <code>com_example_MyRenderer</code> has not been defined.
+ * <p>
+ * 
+ * In addition to the general JavaScript extension functionality explained in
+ * {@link AbstractJavaScriptExtension}, this class also provides some
+ * functionality specific for renderers.
+ * <p>
+ * The initialization function will be called with <code>this</code> pointing to
+ * a connector wrapper object providing integration to Vaadin with the following
+ * functions:
+ * <ul>
+ * <li><code>getRowKey(rowIndex)</code> - Gets a unique identifier for the row
+ * at the given index. This identifier can be used on the server to retrieve the
+ * corresponding ItemId using {@link #getItemId(String)}.</li>
+ * </ul>
+ * The connector wrapper also supports these special functions that can be
+ * implemented by the connector:
+ * <ul>
+ * <li><code>render(cell, data)</code> - Callback for rendering the given data
+ * into the given cell. The structure of cell and data are described in separate
+ * sections below. The renderer is required to implement this function.
+ * Corresponds to
+ * {@link com.vaadin.client.renderers.Renderer#render(com.vaadin.client.widget.grid.RendererCellReference, Object)}
+ * .</li>
+ * <li><code>init(cell)</code> - Prepares a cell for rendering. Corresponds to
+ * {@link com.vaadin.client.renderers.ComplexRenderer#init(com.vaadin.client.widget.grid.RendererCellReference)}
+ * .</li>
+ * <li><code>destory(cell)</code> - Allows the renderer to release resources
+ * allocate for a cell that will no longer be used. Corresponds to
+ * {@link com.vaadin.client.renderers.ComplexRenderer#destroy(com.vaadin.client.widget.grid.RendererCellReference)}
+ * .</li>
+ * <li><code>onActivate(cell)</code> - Called when the cell is activated by the
+ * user e.g. by double clicking on the cell or pressing enter with the cell
+ * focused. Corresponds to
+ * {@link com.vaadin.client.renderers.ComplexRenderer#onActivate(com.vaadin.client.widget.grid.CellReference)}
+ * .</li>
+ * <li><code>getConsumedEvents()</code> - Returns a JavaScript array of event
+ * names that should cause onBrowserEvent to be invoked whenever an event is
+ * fired for a cell managed by this renderer. Corresponds to
+ * {@link com.vaadin.client.renderers.ComplexRenderer#getConsumedEvents()}.</li>
+ * <li><code>onBrowserEvent(cell, event)</code> - Called by Grid when an event
+ * of a type returned by getConsumedEvents is fired for a cell managed by this
+ * renderer. Corresponds to
+ * {@link com.vaadin.client.renderers.ComplexRenderer#onBrowserEvent(com.vaadin.client.widget.grid.CellReference, com.google.gwt.dom.client.NativeEvent)}
+ * .</li>
+ * </ul>
+ * 
+ * <p>
+ * The cell object passed to functions defined by the renderer has these
+ * properties:
+ * <ul>
+ * <li><code>element</code> - The DOM element corresponding to this cell.
+ * Readonly.</li>
+ * <li><code>rowIndex</code> - The current index of the row of this cell.
+ * Readonly.</li>
+ * <li><code>columnIndex</code> - The current index of the column of this cell.
+ * Readonly.</li>
+ * <li><code>colSpan</code> - The number of columns spanned by this cell. Only
+ * supported in the object passed to the <code>render</code> function - other
+ * functions should not use the property. Readable and writable.
+ * </ul>
+ * 
+ * @author Vaadin Ltd
+ * @since
+ */
+public abstract class AbstractJavaScriptRenderer<T> extends AbstractRenderer<T> {
+    private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper(
+            this);
+
+    protected AbstractJavaScriptRenderer(Class<T> presentationType) {
+        super(presentationType);
+    }
+
+    @Override
+    protected <R extends ServerRpc> void registerRpc(R implementation,
+            Class<R> rpcInterfaceType) {
+        super.registerRpc(implementation, rpcInterfaceType);
+        callbackHelper.registerRpc(rpcInterfaceType);
+    }
+
+    /**
+     * Register a {@link JavaScriptFunction} that can be called from the
+     * JavaScript using the provided name. A JavaScript function with the
+     * provided name will be added to the connector wrapper object (initially
+     * available as <code>this</code>). Calling that JavaScript function will
+     * cause the call method in the registered {@link JavaScriptFunction} to be
+     * invoked with the same arguments.
+     * 
+     * @param functionName
+     *            the name that should be used for client-side callback
+     * @param function
+     *            the {@link JavaScriptFunction} object that will be invoked
+     *            when the JavaScript function is called
+     */
+    protected void addFunction(String functionName, JavaScriptFunction function) {
+        callbackHelper.registerCallback(functionName, function);
+    }
+
+    /**
+     * Invoke a named function that the connector JavaScript has added to the
+     * JavaScript connector wrapper object. The arguments should only contain
+     * data types that can be represented in JavaScript including primitives,
+     * their boxed types, arrays, String, List, Set, Map, Connector and
+     * JavaBeans.
+     * 
+     * @param name
+     *            the name of the function
+     * @param arguments
+     *            function arguments
+     */
+    protected void callFunction(String name, Object... arguments) {
+        callbackHelper.invokeCallback(name, arguments);
+    }
+
+    @Override
+    protected JavaScriptExtensionState getState() {
+        return (JavaScriptExtensionState) super.getState();
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderers.java b/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderers.java
new file mode 100644 (file)
index 0000000..4bfa244
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.grid;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Grid;
+
+public class JavaScriptRenderers extends AbstractTestUI {
+
+    public static class MyBean {
+        private int integer;
+        private String string;
+
+        public MyBean(int integer, String string) {
+            super();
+            this.integer = integer;
+            this.string = string;
+        }
+
+        public int getInteger() {
+            return integer;
+        }
+
+        public void setInteger(int integer) {
+            this.integer = integer;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+    }
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        IndexedContainer container = new IndexedContainer();
+        container.addContainerProperty("id", Integer.class, Integer.valueOf(0));
+        container.addContainerProperty("bean", MyBean.class, null);
+
+        for (int i = 0; i < 1000; i++) {
+            Integer itemId = Integer.valueOf(i);
+            Item item = container.addItem(itemId);
+            item.getItemProperty("id").setValue(itemId);
+            item.getItemProperty("bean").setValue(
+                    new MyBean(i + 1, Integer.toString(i - 1)));
+        }
+
+        Grid grid = new Grid(container);
+
+        grid.getColumn("bean").setRenderer(new MyBeanJSRenderer());
+        grid.getColumn("bean").setWidth(250);
+
+        addComponent(grid);
+    }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java b/uitest/src/com/vaadin/tests/components/grid/JavaScriptRenderersTest.java
new file mode 100644 (file)
index 0000000..a3bb736
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.grid;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.testbench.elements.GridElement;
+import com.vaadin.testbench.elements.GridElement.GridCellElement;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class JavaScriptRenderersTest extends MultiBrowserTest {
+
+    @Test
+    public void testJavaScriptRenderer() {
+        setDebug(true);
+        openTestURL();
+
+        GridElement grid = $(GridElement.class).first();
+        GridCellElement cell_1_2 = grid.getCell(1, 2);
+
+        // Verify render functionality
+        Assert.assertEquals("Bean(2, 0)", cell_1_2.getText());
+
+        // Verify init functionality
+        Assert.assertEquals("2", cell_1_2.getAttribute("column"));
+
+        // Verify onbrowserevent
+        cell_1_2.click();
+        Assert.assertTrue(cell_1_2.getText().startsWith(
+                "Clicked 1 with key 1 at"));
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/grid/MyBeanJSRenderer.java b/uitest/src/com/vaadin/tests/components/grid/MyBeanJSRenderer.java
new file mode 100644 (file)
index 0000000..ccb94f5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.grid;
+
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.tests.components.grid.JavaScriptRenderers.MyBean;
+import com.vaadin.ui.renderer.AbstractJavaScriptRenderer;
+
+/**
+ * 
+ * @since
+ * @author Vaadin Ltd
+ */
+@JavaScript("myBeanJsRenderer.js")
+public class MyBeanJSRenderer extends AbstractJavaScriptRenderer<MyBean> {
+
+    public MyBeanJSRenderer() {
+        super(MyBean.class);
+    }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/grid/myBeanJsRenderer.js b/uitest/src/com/vaadin/tests/components/grid/myBeanJsRenderer.js
new file mode 100644 (file)
index 0000000..5e7bde5
--- /dev/null
@@ -0,0 +1,16 @@
+window.com_vaadin_tests_components_grid_MyBeanJSRenderer = function() {
+       this.init = function(cell) {
+               cell.element.setAttribute("column", cell.columnIndex);
+       }
+       
+       this.render = function(cell, data) {
+               cell.element.innerHTML = 'Bean(' + data.integer + ', ' + data.string + ')'
+       }
+       
+       this.getConsumedEvents = function() { return ["click"] };
+       
+       this.onBrowserEvent = function(cell, event) {
+               cell.element.innerHTML =  "Clicked " + cell.rowIndex + " with key " + this.getRowKey(cell.rowIndex) +" at " + event.clientX;
+               return true;
+       }
+}
\ No newline at end of file