Updated Branches: refs/heads/master 55cd66005 -> 0f9349b62
WICKET-4593 bi-directional model second try, more tests for TabbedPanel Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/0f9349b6 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/0f9349b6 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/0f9349b6 Branch: refs/heads/master Commit: 0f9349b629017299b37f5f4841569f16dce732bc Parents: 55cd660 Author: svenmeier <[email protected]> Authored: Mon Jul 16 19:50:37 2012 +0200 Committer: svenmeier <[email protected]> Committed: Mon Jul 16 19:50:37 2012 +0200 ---------------------------------------------------------------------- .../extensions/markup/html/tabs/TabbedPanel.java | 108 ++++++-- .../markup/html/tabs/TabbedPanelTest.java | 193 ++++++++++++++- 2 files changed, 265 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/0f9349b6/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanel.java ---------------------------------------------------------------------- diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanel.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanel.java index 8fa9d50..c8b2b47 100644 --- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanel.java +++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanel.java @@ -34,7 +34,7 @@ import org.apache.wicket.util.lang.Args; /** - * TabbedPanel component represets a panel with tabs that are used to switch between different + * TabbedPanel component represents a panel with tabs that are used to switch between different * content panels inside the TabbedPanel panel. * <p> * <b>Note:</b> When the currently selected tab is replaced by changing the underlying list of tabs, @@ -81,6 +81,9 @@ public class TabbedPanel<T extends ITab> extends Panel private final List<T> tabs; + /** the current tab */ + private int currentTab = -1; + private transient Boolean[] tabsVisibilityCache; /** @@ -93,7 +96,22 @@ public class TabbedPanel<T extends ITab> extends Panel */ public TabbedPanel(final String id, final List<T> tabs) { - super(id, new Model<Integer>(-1)); + this(id, tabs, null); + } + + /** + * Constructor + * + * @param id + * component id + * @param tabs + * list of ITab objects used to represent tabs + * @param model + * model holding the index of the selected tab + */ + public TabbedPanel(final String id, final List<T> tabs, IModel<Integer> model) + { + super(id, model); this.tabs = Args.notNull(tabs, "tabs"); @@ -134,6 +152,25 @@ public class TabbedPanel<T extends ITab> extends Panel return newTabContainer(iteration); } }); + + add(newPanel()); + } + + /** + * Initialize the component's model. + * + * @return a new model containing {@code -1} if the super implementation doesn't supply one + */ + @Override + protected IModel<?> initModel() + { + IModel<?> model = super.initModel(); + if (model == null) + { + model = new Model<Integer>(-1); + } + + return model; } /** @@ -207,38 +244,32 @@ public class TabbedPanel<T extends ITab> extends Panel @Override protected void onBeforeRender() { - if (tabs.size() == 0) - { - // force an empty container to be created every time if we have no tabs - setSelectedTab(0); - } - else if ((getSelectedTab() == -1) || (isTabVisible(getSelectedTab()) == false)) + int index = getSelectedTab(); + + if ((index == -1) || (isTabVisible(index) == false)) { - // find first visible selected tab - int selected = 0; + // find first visible tab + index = -1; for (int i = 0; i < tabs.size(); i++) { if (isTabVisible(i)) { - selected = i; + index = i; break; } } - if (selected == tabs.size()) + if (index != -1) { /* - * none of the tabs are selected... - * - * we do not need to do anything special because the check in setSelectedTab() will - * replace the current tab panel with an empty one + * found a visible tab, so select it */ - selected = 0; + setSelectedTab(index); } - - setSelectedTab(selected); } + setCurrentTab(index); + super.onBeforeRender(); } @@ -330,32 +361,50 @@ public class TabbedPanel<T extends ITab> extends Panel * @param index * index of the tab to select * @return this for chaining + * @throws IndexOutOfBoundsException + * if index is not {@code -1} or in the range of available tabs */ - public TabbedPanel setSelectedTab(final int index) + public TabbedPanel<T> setSelectedTab(final int index) { - if ((index < 0) || ((index >= tabs.size()) && (index > 0))) + if ((index < 0) || (index >= tabs.size())) { throw new IndexOutOfBoundsException(); } setDefaultModelObject(index); + // force the tab's component to be aquired again if already the current tab + currentTab = -1; + setCurrentTab(index); + + return this; + } + + private void setCurrentTab(int index) + { + if (this.currentTab == index) + { + // already current + return; + } + this.currentTab = index; + final Component component; - if ((tabs.size() == 0) || !isTabVisible(index)) + if (currentTab == -1 || (tabs.size() == 0) || !isTabVisible(currentTab)) { - // no tabs or the currently selected tab is not visible - component = new WebMarkupContainer(TAB_PANEL_ID); + // no tabs or the current tab is not visible + component = newPanel(); } else { // show panel from selected tab - T tab = tabs.get(index); + T tab = tabs.get(currentTab); component = tab.getPanel(TAB_PANEL_ID); if (component == null) { throw new WicketRuntimeException("ITab.getPanel() returned null. TabbedPanel [" + - getPath() + "] ITab index [" + index + "]"); + getPath() + "] ITab index [" + currentTab + "]"); } } @@ -365,12 +414,15 @@ public class TabbedPanel<T extends ITab> extends Panel "ITab.getPanel() returned a panel with invalid id [" + component.getId() + "]. You must always return a panel with id equal to the provided panelId parameter. TabbedPanel [" + - getPath() + "] ITab index [" + index + "]"); + getPath() + "] ITab index [" + currentTab + "]"); } addOrReplace(component); + } - return this; + private WebMarkupContainer newPanel() + { + return new WebMarkupContainer(TAB_PANEL_ID); } /** http://git-wip-us.apache.org/repos/asf/wicket/blob/0f9349b6/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanelTest.java ---------------------------------------------------------------------- diff --git a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanelTest.java b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanelTest.java index a220375..ab660bf 100644 --- a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanelTest.java +++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tabs/TabbedPanelTest.java @@ -32,15 +32,22 @@ import org.junit.Test; */ public class TabbedPanelTest extends WicketTestCase { + /** + */ public class TestPage extends WebPage { - public TabbedPanel<ITab> tabbedPanel; + private static final long serialVersionUID = 1L; + protected TabbedPanel<ITab> tabbedPanel; + + /** + */ public TestPage() { List<ITab> defaultTabs = new ArrayList<ITab>(); defaultTabs.add(new AbstractTab(Model.of("default 1")) { + private static final long serialVersionUID = 1L; @Override public WebMarkupContainer getPanel(String panelId) @@ -50,6 +57,7 @@ public class TabbedPanelTest extends WicketTestCase }); defaultTabs.add(new AbstractTab(Model.of("default 2")) { + private static final long serialVersionUID = 1L; @Override public WebMarkupContainer getPanel(String panelId) @@ -68,8 +76,16 @@ public class TabbedPanelTest extends WicketTestCase return new TabbedPanel<ITab>("tabpanel", defaultTabs); } + /** + */ public static class TestPanel extends Panel { + private static final long serialVersionUID = 1L; + + /** + * @param id + * @param panelTestId + */ public TestPanel(String id, String panelTestId) { super(id); @@ -77,6 +93,11 @@ public class TabbedPanelTest extends WicketTestCase } } + /** + * No tabs thus no tab component rendered. + * + * @throws Exception + */ @Test public void renderNoTabs() throws Exception { @@ -84,15 +105,20 @@ public class TabbedPanelTest extends WicketTestCase page.tabbedPanel.getTabs().clear(); tester.startPage(page); - tester.assertContainsNot("<span wicket:id=\"title\">default 1</span></a>"); + tester.assertContainsNot("<span wicket:id=\"title\">default 1</span>"); tester.assertContainsNot("<span wicket:id=\"label\">default 1</span>"); - tester.assertContainsNot("<span wicket:id=\"title\">default 2</span></a>"); + tester.assertContainsNot("<span wicket:id=\"title\">default 2</span>"); tester.assertContainsNot("<span wicket:id=\"label\">default 2</span>"); tester.assertContains("<!-- no panel -->"); - assertEquals(Integer.valueOf(0), page.tabbedPanel.getDefaultModelObject()); + assertEquals(Integer.valueOf(-1), page.tabbedPanel.getDefaultModelObject()); } + /** + * Switching between tabsS. + * + * @throws Exception + */ @Test public void renderDefaultTabsOnly() throws Exception { @@ -109,12 +135,19 @@ public class TabbedPanelTest extends WicketTestCase assertEquals(Integer.valueOf(1), page.tabbedPanel.getDefaultModelObject()); } + /** + * Additional tabs are rendered. + * + * @throws Exception + */ @Test public void renderAdditionalTabs() throws Exception { TestPage page = tester.startPage(new TestPage()); page.tabbedPanel.getTabs().add(new AbstractTab(Model.of("added 1")) { + private static final long serialVersionUID = 1L; + @Override public WebMarkupContainer getPanel(String panelId) { @@ -122,23 +155,167 @@ public class TabbedPanelTest extends WicketTestCase } }); // the additional tab isn't rendered yet - tester.assertContainsNot("<span wicket:id=\"title\">added 1</span></a>"); + tester.assertContainsNot("<span wicket:id=\"title\">added 1</span>"); tester.assertContainsNot("<span wicket:id=\"label\">added 1</span>"); assertEquals(Integer.valueOf(0), page.tabbedPanel.getDefaultModelObject()); // now its title is visible, but the contents not tester.clickLink("tabpanel:tabs-container:tabs:1:link"); - tester.assertContains("<span wicket:id=\"title\">added 1</span></a>"); + tester.assertContains("<span wicket:id=\"title\">added 1</span>"); tester.assertContainsNot("<span wicket:id=\"label\">added 1</span>"); assertEquals(Integer.valueOf(1), page.tabbedPanel.getDefaultModelObject()); // now the entire panel should be there tester.clickLink("tabpanel:tabs-container:tabs:2:link"); - tester.assertContains("<span wicket:id=\"title\">added 1</span></a>"); + tester.assertContains("<span wicket:id=\"title\">added 1</span>"); tester.assertContains("<span wicket:id=\"label\">added 1</span>"); assertEquals(Integer.valueOf(2), page.tabbedPanel.getDefaultModelObject()); } -} + + /** + * Changing model switches tab. + * + * @throws Exception + */ + @Test + public void renderModelChange() throws Exception + { + TestPage page = new TestPage(); + + tester.startPage(page); + tester.assertContains("<span wicket:id=\"label\">default 1</span>"); + + page.tabbedPanel.setDefaultModelObject(Integer.valueOf(1)); + tester.startPage(page); + + tester.assertContains("<span wicket:id=\"label\">default 2</span>"); + } + + /** + * Tab's component is aquired once only. + * + * @throws Exception + */ + @Test + public void tabComponentAquiredOnChangeOnly() throws Exception + { + + final int[] count = new int[1]; + + TestPage page = new TestPage(); + page.tabbedPanel.getTabs().clear(); + page.tabbedPanel.getTabs().add(new AbstractTab(Model.of("added 1")) + { + private static final long serialVersionUID = 1L; + + @Override + public WebMarkupContainer getPanel(String panelId) + { + count[0]++; + + return new TestPanel(panelId, "added 1"); + } + }); + + assertEquals(0, count[0]); + tester.startPage(page); + assertEquals(1, count[0]); + tester.startPage(page); + assertEquals(1, count[0]); + + page.tabbedPanel.setSelectedTab(0); + + assertEquals(2, count[0]); + tester.startPage(page); + assertEquals(2, count[0]); + } + + /** + * An invisible tab gets replaced by another one. + * + * @throws Exception + */ + @Test + public void invisibleTabGetsReplaced() throws Exception + { + final boolean[] visible = { true, true }; + + TestPage page = new TestPage(); + page.tabbedPanel.getTabs().clear(); + page.tabbedPanel.getTabs().add(new AbstractTab(Model.of("added 1")) + { + private static final long serialVersionUID = 1L; + + @Override + public WebMarkupContainer getPanel(String panelId) + { + return new TestPanel(panelId, "added 1"); + } + + @Override + public boolean isVisible() + { + return visible[0]; + } + }); + page.tabbedPanel.getTabs().add(new AbstractTab(Model.of("added 2")) + { + private static final long serialVersionUID = 1L; + + @Override + public WebMarkupContainer getPanel(String panelId) + { + return new TestPanel(panelId, "added 2"); + } + + @Override + public boolean isVisible() + { + return visible[1]; + } + }); + + page.tabbedPanel.setSelectedTab(1); + + tester.startPage(page); + + assertEquals(Integer.valueOf(1), page.tabbedPanel.getDefaultModelObject()); + tester.assertContains("<span wicket:id=\"title\">added 1</span>"); + tester.assertContains("<span wicket:id=\"title\">added 2</span>"); + tester.assertContains("<span wicket:id=\"label\">added 2</span>"); + + visible[1] = false; + + tester.startPage(page); + + // now first tab is selected + assertEquals(Integer.valueOf(0), page.tabbedPanel.getDefaultModelObject()); + + tester.assertContains("<span wicket:id=\"title\">added 1</span>"); + tester.assertContainsNot("<span wicket:id=\"title\">added 2</span>"); + tester.assertContains("<span wicket:id=\"label\">added 1</span>"); + + visible[0] = false; + + tester.startPage(page); + + // first tab stays selected since no other is visible + assertEquals(Integer.valueOf(0), page.tabbedPanel.getDefaultModelObject()); + tester.assertContainsNot("<span wicket:id=\"title\">added 1</span>"); + tester.assertContainsNot("<span wicket:id=\"title\">added 2</span>"); + tester.assertContains("<!-- no panel -->"); + + visible[1] = true; + + tester.startPage(page); + + // second selected again + assertEquals(Integer.valueOf(1), page.tabbedPanel.getDefaultModelObject()); + tester.assertContainsNot("<span wicket:id=\"title\">added 1</span>"); + tester.assertContains("<span wicket:id=\"title\">added 2</span>"); + tester.assertContains("<span wicket:id=\"label\">added 2</span>"); + } +} \ No newline at end of file
