]> source.dussan.org Git - vaadin-framework.git/commitdiff
Declarative support for TabSheet and Accordion (#7749)
authorMatti Hosio <mhosio@vaadin.com>
Thu, 11 Dec 2014 14:22:41 +0000 (16:22 +0200)
committerMatti Hosio <mhosio@vaadin.com>
Fri, 12 Dec 2014 08:27:18 +0000 (10:27 +0200)
Change-Id: Iee4689814f08ddbb852cfb3e51c9873fbe42d901

server/src/com/vaadin/ui/TabSheet.java
server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java
server/tests/src/com/vaadin/tests/layoutparser/all-components.html
server/tests/src/com/vaadin/tests/server/component/abstractcomponent/TestSynchronizeFromDesign.java
server/tests/src/com/vaadin/tests/server/component/tabsheet/TestSynchronizeFromDesign.java [new file with mode: 0644]
server/tests/src/com/vaadin/tests/server/component/tabsheet/TestSynchronizeToDesign.java [new file with mode: 0644]

index d3410464a2300ff52ebf986ebc7fe6f0291ef8ef..dd9beb07f0c849e0322ef77f853696049b8c5d4b 100644 (file)
@@ -19,11 +19,15 @@ package com.vaadin.ui;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
+
 import com.vaadin.event.FieldEvents.BlurEvent;
 import com.vaadin.event.FieldEvents.BlurListener;
 import com.vaadin.event.FieldEvents.BlurNotifier;
@@ -40,6 +44,9 @@ import com.vaadin.shared.ui.tabsheet.TabsheetClientRpc;
 import com.vaadin.shared.ui.tabsheet.TabsheetServerRpc;
 import com.vaadin.shared.ui.tabsheet.TabsheetState;
 import com.vaadin.ui.Component.Focusable;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignException;
 import com.vaadin.ui.themes.Reindeer;
 import com.vaadin.ui.themes.Runo;
 
@@ -1447,4 +1454,164 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
     protected TabsheetState getState() {
         return (TabsheetState) super.getState();
     }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.ui.AbstractComponent#synchronizeFromDesign(org.jsoup.nodes
+     * .Element, com.vaadin.ui.declarative.DesignContext)
+     */
+    @Override
+    public void synchronizeFromDesign(Element design,
+            DesignContext designContext) {
+        super.synchronizeFromDesign(design, designContext);
+        Attributes attr = design.attributes();
+        TabSheet def = designContext.getDefaultInstance(this.getClass());
+        // handle tab index
+        int tabIndex = DesignAttributeHandler.readAttribute("tabindex", attr,
+                def.getTabIndex(), Integer.class);
+        setTabIndex(tabIndex);
+        // clear old tabs
+        removeAllComponents();
+        // create new tabs
+        for (Element tab : design.children()) {
+            if (!tab.tagName().equals("tab")) {
+                throw new DesignException("Invalid tag name for tabsheet tab "
+                        + tab.tagName());
+            }
+            readTabFromDesign(tab, designContext);
+        }
+    }
+
+    /**
+     * Reads the given tab element from design
+     * 
+     * @since 7.4
+     * 
+     * @param tabElement
+     *            the element to be read
+     * @param designContext
+     *            the design context
+     */
+    private void readTabFromDesign(Element tabElement,
+            DesignContext designContext) {
+        Attributes attr = tabElement.attributes();
+        if (tabElement.children().size() != 1) {
+            throw new DesignException(
+                    "A tab must have exactly one child element");
+        }
+        // create the component that is in tab content
+        Element content = tabElement.child(0);
+        DesignSynchronizable child = designContext.createChild(content);
+        Tab tab = this.addTab(child);
+        tab.setVisible(DesignAttributeHandler.readAttribute("visible", attr,
+                tab.isVisible(), Boolean.class));
+        tab.setClosable(DesignAttributeHandler.readAttribute("closable", attr,
+                tab.isClosable(), Boolean.class));
+        tab.setCaption(DesignAttributeHandler.readAttribute("caption", attr,
+                tab.getCaption(), String.class));
+        tab.setEnabled(DesignAttributeHandler.readAttribute("enabled", attr,
+                tab.isEnabled(), Boolean.class));
+        tab.setIcon(DesignAttributeHandler.readAttribute("icon", attr,
+                tab.getIcon(), Resource.class));
+        tab.setIconAlternateText(DesignAttributeHandler.readAttribute(
+                "icon-alt", attr, tab.getIconAlternateText(), String.class));
+        tab.setDescription(DesignAttributeHandler.readAttribute("description",
+                attr, tab.getDescription(), String.class));
+        tab.setStyleName(DesignAttributeHandler.readAttribute("style-name",
+                attr, tab.getStyleName(), String.class));
+        tab.setId(DesignAttributeHandler.readAttribute("id", attr, tab.getId(),
+                String.class));
+        boolean selected = DesignAttributeHandler.readAttribute("selected",
+                attr, false, Boolean.class);
+        if (selected) {
+            this.setSelectedTab(tab.getComponent());
+        }
+    }
+
+    /**
+     * Writes the given tab to design
+     * 
+     * @since 7.4
+     * @param design
+     *            the design node for tabsheet
+     * @param designContext
+     *            the design context
+     * @param tab
+     *            the tab to be written
+     */
+    private void writeTabToDesign(Element design, DesignContext designContext,
+            Tab tab) {
+        // get default tab instance
+        Tab def = new TabSheetTabImpl(null, null, null);
+        // create element for tab
+        Element tabElement = design.appendElement("tab");
+        // add tab content
+        tabElement.appendChild(designContext
+                .createNode((DesignSynchronizable) tab.getComponent()));
+        Attributes attr = tabElement.attributes();
+        // write attributes
+        DesignAttributeHandler.writeAttribute("visible", attr, tab.isVisible(),
+                def.isVisible(), Boolean.class);
+        DesignAttributeHandler.writeAttribute("closable", attr,
+                tab.isClosable(), def.isClosable(), Boolean.class);
+        DesignAttributeHandler.writeAttribute("caption", attr,
+                tab.getCaption(), def.getCaption(), String.class);
+        DesignAttributeHandler.writeAttribute("enabled", attr, tab.isEnabled(),
+                def.isEnabled(), Boolean.class);
+        DesignAttributeHandler.writeAttribute("icon", attr, tab.getIcon(),
+                def.getIcon(), Resource.class);
+        DesignAttributeHandler.writeAttribute("icon-alt", attr,
+                tab.getIconAlternateText(), def.getIconAlternateText(),
+                String.class);
+        DesignAttributeHandler.writeAttribute("description", attr,
+                tab.getDescription(), def.getDescription(), String.class);
+        DesignAttributeHandler.writeAttribute("style-name", attr,
+                tab.getStyleName(), def.getStyleName(), String.class);
+        DesignAttributeHandler.writeAttribute("id", attr, tab.getId(),
+                def.getId(), String.class);
+        if (getSelectedTab() != null
+                && getSelectedTab().equals(tab.getComponent())) {
+            // use write attribute to get consistent handling for boolean
+            DesignAttributeHandler.writeAttribute("selected", attr, true,
+                    false, boolean.class);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.ui.AbstractComponent#getCustomAttributes()
+     */
+    @Override
+    protected Collection<String> getCustomAttributes() {
+        Collection<String> attributes = super.getCustomAttributes();
+        attributes.add("tabindex");
+        // no need to list tab attributes since they are considered internal
+        return attributes;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.ui.AbstractComponent#synchronizeToDesign(org.jsoup.nodes.Element
+     * , com.vaadin.ui.declarative.DesignContext)
+     */
+    @Override
+    public void synchronizeToDesign(Element design, DesignContext designContext) {
+        super.synchronizeToDesign(design, designContext);
+        TabSheet def = designContext.getDefaultInstance(this.getClass());
+        Attributes attr = design.attributes();
+        // handle tab index
+        DesignAttributeHandler.writeAttribute("tabindex", attr, getTabIndex(),
+                def.getTabIndex(), Integer.class);
+        // write tabs
+        for (Component component : this) {
+            Tab tab = this.getTab(component);
+            writeTabToDesign(design, designContext, tab);
+        }
+    }
+
 }
index 2992771521cdbe51251e57ff31475f76bc94d1d3..0c370b8da783b2e550a5c6e930199d617966b288 100644 (file)
@@ -295,7 +295,7 @@ public class DesignAttributeHandler implements Serializable {
                     + inputType.getName() + " not supported");
         }
         if (!SharedUtil.equals(value, defaultValue)) {
-            String attributeValue = toAttributeValue(value.getClass(), value);
+            String attributeValue = toAttributeValue(inputType, value);
             attributes.put(attribute, attributeValue);
         }
     }
@@ -464,7 +464,7 @@ public class DesignAttributeHandler implements Serializable {
      */
     private static Resource parseResource(String value) {
         if (value.startsWith("http://")) {
-            return new ExternalResource("value");
+            return new ExternalResource(value);
         } else if (value.startsWith("theme://")) {
             return new ThemeResource(value.substring(8));
         } else if (value.startsWith("font://")) {
index f633b06cb3d04fe23847109956e2daf072b4056e..3fdae56e76979e041224abdc3dc58a3d4d1f0f23 100644 (file)
       <!--  text area -->
       <v-text-area rows=5 wordwrap=false >test value</v-text-area>
       
+      <!-- tabsheet -->
+         <v-tab-sheet tabindex=5>
+                 <tab visible=false closable caption="My first tab">
+                       <v-vertical-layout>
+                               <v-text-field/>
+                   </v-vertical-layout>
+                 </tab>
+                 <tab enabled=false caption="Disabled second tab">
+                       <v-button>In disabled tab - can’t be shown by default</v-button>
+             </tab>
+                 <tab icon="theme://../runo/icons/16/ok.png" icon-alt="Ok png from Runo - very helpful" description="Click to show a text field" style-name="red" id="uniqueDomId">
+                       <v-text-field input-prompt="Icon only in tab" />
+             </tab>
+          </v-tab-sheet>
+
+      <!-- accordion -->
+         <v-accordion tabindex=5>
+                 <tab visible=false closable caption="My first tab">
+                       <v-vertical-layout>
+                               <v-text-field/>
+                   </v-vertical-layout>
+                 </tab>
+                 <tab enabled=false caption="Disabled second tab">
+                       <v-button>In disabled tab - can’t be shown by default</v-button>
+             </tab>
+                 <tab icon="theme://../runo/icons/16/ok.png" icon-alt="Ok png from Runo - very helpful" description="Click to show a text field" style-name="red" id="uniqueDomId">
+                       <v-text-field input-prompt="Icon only in tab" />
+             </tab>
+          </v-accordion>
+
+           
   </v-vertical-layout>
  </body>
 </html>
\ No newline at end of file
index 5153d92706ae1c07d33e6b12ba342b534cb934b1..df6fb47bf2881f3fae6aa8c914d161c9d1b2acbc 100644 (file)
@@ -81,6 +81,8 @@ public class TestSynchronizeFromDesign extends TestCase {
         component.synchronizeFromDesign(design, ctx);
         assertTrue("Incorrect resource type returned", component.getIcon()
                 .getClass().isAssignableFrom(ExternalResource.class));
+        assertEquals("http://example.com/example.gif",
+                ((ExternalResource) component.getIcon()).getURL());
     }
 
     public void testSynchronizeThemeIcon() {
@@ -115,7 +117,8 @@ public class TestSynchronizeFromDesign extends TestCase {
         component.synchronizeFromDesign(design, ctx);
         assertEquals(false, component.isImmediate());
         assertEquals(Boolean.FALSE, getExplicitImmediate(component));
-        // Synchronize with a design having immediate = "" - should correspond to
+        // Synchronize with a design having immediate = "" - should correspond
+        // to
         // true.
         design = createDesign("immediate", "");
         component.synchronizeFromDesign(design, ctx);
diff --git a/server/tests/src/com/vaadin/tests/server/component/tabsheet/TestSynchronizeFromDesign.java b/server/tests/src/com/vaadin/tests/server/component/tabsheet/TestSynchronizeFromDesign.java
new file mode 100644 (file)
index 0000000..4c0a286
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.server.component.tabsheet;
+
+import junit.framework.TestCase;
+
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
+import org.jsoup.parser.Tag;
+
+import com.vaadin.server.ExternalResource;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TabSheet.Tab;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.declarative.DesignContext;
+
+/**
+ * Test case from reading TabSheet from design
+ * 
+ * @since
+ * @author Vaadin Ltd
+ */
+public class TestSynchronizeFromDesign extends TestCase {
+
+    private TabSheet sheet;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        sheet = createTabSheet();
+    }
+
+    public void testChildCount() {
+        assertEquals(1, sheet.getComponentCount());
+    }
+
+    public void testTabIndex() {
+        assertEquals(5, sheet.getTabIndex());
+    }
+
+    public void testTabAttributes() {
+        Tab tab = sheet.getTab(0);
+        assertEquals("test-caption", tab.getCaption());
+        assertEquals(false, tab.isVisible());
+        assertEquals(false, tab.isClosable());
+        assertEquals(false, tab.isEnabled());
+        assertEquals("http://www.vaadin.com/test.png",
+                ((ExternalResource) tab.getIcon()).getURL());
+        assertEquals("OK", tab.getIconAlternateText());
+        assertEquals("test-desc", tab.getDescription());
+        assertEquals("test-style", tab.getStyleName());
+        assertEquals("test-id", tab.getId());
+    }
+
+    public void testSelectedComponent() {
+        TabSheet tabSheet = new TabSheet();
+        tabSheet.synchronizeFromDesign(createFirstTabSelectedDesign(),
+                new DesignContext());
+        assertEquals(tabSheet.getTab(0).getComponent(),
+                tabSheet.getSelectedTab());
+    }
+
+    public void testTabContent() {
+        assertTrue("The child for the tabsheet should be textfield", sheet
+                .getTab(0).getComponent() instanceof TextField);
+    }
+
+    private TabSheet createTabSheet() {
+        TabSheet tabSheet = new TabSheet();
+        // add some tabs that should be cleared on sync
+        tabSheet.addComponent(new Label("tab1"));
+        tabSheet.addComponent(new Label("tab2"));
+        DesignContext ctx = new DesignContext();
+        Element design = createDesign();
+        tabSheet.synchronizeFromDesign(design, ctx);
+        return tabSheet;
+    }
+
+    private Element createDesign() {
+        // create root design
+        Attributes rootAttributes = new Attributes();
+        rootAttributes.put("tabindex", "5");
+        Element node = new Element(Tag.valueOf("v-tab-sheet"), "",
+                rootAttributes);
+        // create tab design
+        Attributes tabAttributes = new Attributes();
+        tabAttributes.put("caption", "test-caption");
+        tabAttributes.put("visible", "false");
+        tabAttributes.put("closable", "false");
+        tabAttributes.put("enabled", "false");
+        tabAttributes.put("icon", "http://www.vaadin.com/test.png");
+        tabAttributes.put("icon-alt", "OK");
+        tabAttributes.put("description", "test-desc");
+        tabAttributes.put("style-name", "test-style");
+        tabAttributes.put("id", "test-id");
+        Element tab = new Element(Tag.valueOf("tab"), "", tabAttributes);
+        // add child component to tab
+        tab.appendChild(new Element(Tag.valueOf("v-text-field"), "",
+                new Attributes()));
+        // add tab to root design
+        node.appendChild(tab);
+        return node;
+    }
+
+    private Element createFirstTabSelectedDesign() {
+        // create root design
+        Attributes rootAttributes = new Attributes();
+        Element node = new Element(Tag.valueOf("v-tab-sheet"), "",
+                rootAttributes);
+        // create tab design
+        Attributes tabAttributes = new Attributes();
+        tabAttributes.put("selected", "");
+        tabAttributes.put("caption", "test-caption");
+        Element tab = new Element(Tag.valueOf("tab"), "", tabAttributes);
+        // add child component to tab
+        tab.appendChild(new Element(Tag.valueOf("v-text-field"), "",
+                new Attributes()));
+        // add tab to root design
+        node.appendChild(tab);
+        return node;
+
+    }
+}
diff --git a/server/tests/src/com/vaadin/tests/server/component/tabsheet/TestSynchronizeToDesign.java b/server/tests/src/com/vaadin/tests/server/component/tabsheet/TestSynchronizeToDesign.java
new file mode 100644 (file)
index 0000000..c33a1da
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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.server.component.tabsheet;
+
+import junit.framework.TestCase;
+
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
+import org.jsoup.parser.Tag;
+
+import com.vaadin.server.ExternalResource;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TabSheet.Tab;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.declarative.DesignContext;
+
+/**
+ * Test case for writing TabSheet to design
+ * 
+ * @since
+ * @author Vaadin Ltd
+ */
+public class TestSynchronizeToDesign extends TestCase {
+
+    private TabSheet sheet;
+    private Element design;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        sheet = createTabSheet();
+        design = createDesign();
+        sheet.synchronizeToDesign(design, createDesignContext());
+    }
+
+    public void testOnlyOneTab() {
+        assertEquals("There should be only one child", 1, design.children()
+                .size());
+    }
+
+    public void testAttributes() {
+        Element tabDesign = design.child(0);
+        assertEquals("5", design.attr("tabindex"));
+        assertEquals("test-caption", tabDesign.attr("caption"));
+        assertEquals("false", tabDesign.attr("visible"));
+        assertTrue(tabDesign.hasAttr("closable"));
+        assertTrue(tabDesign.attr("closable").equals("true")
+                || tabDesign.attr("closable").equals(""));
+        assertEquals("false", tabDesign.attr("enabled"));
+        assertEquals("http://www.vaadin.com/test.png", tabDesign.attr("icon"));
+        assertEquals("OK", tabDesign.attr("icon-alt"));
+        assertEquals("test-desc", tabDesign.attr("description"));
+        assertEquals("test-style", tabDesign.attr("style-name"));
+        assertEquals("test-id", tabDesign.attr("id"));
+    }
+
+    public void testContent() {
+        Element tabDesign = design.child(0);
+        Element content = tabDesign.child(0);
+        assertEquals("Tab must have only one child", 1, tabDesign.children()
+                .size());
+        assertEquals("v-text-field", content.tagName());
+    }
+
+    private Element createDesign() {
+        // make sure that the design node has old content that should be removed
+        Element node = new Element(Tag.valueOf("v-tab-sheet"), "",
+                new Attributes());
+        node.appendChild(new Element(Tag.valueOf("tab"), "", new Attributes()));
+        node.appendChild(new Element(Tag.valueOf("tab"), "", new Attributes()));
+        node.appendChild(new Element(Tag.valueOf("tab"), "", new Attributes()));
+        return node;
+    }
+
+    private DesignContext createDesignContext() {
+        return new DesignContext();
+    }
+
+    private TabSheet createTabSheet() {
+        TabSheet sheet = new TabSheet();
+        sheet.setTabIndex(5);
+        sheet.addTab(new TextField());
+        Tab tab = sheet.getTab(0);
+        tab.setCaption("test-caption");
+        tab.setVisible(false);
+        tab.setClosable(true);
+        tab.setEnabled(false);
+        tab.setIcon(new ExternalResource("http://www.vaadin.com/test.png"));
+        tab.setIconAlternateText("OK");
+        tab.setDescription("test-desc");
+        tab.setStyleName("test-style");
+        tab.setId("test-id");
+        return sheet;
+    }
+
+}