Modified the code museum to load all issues at starup and allow the user to select one at a time.  Style sheets are loaded dynamically to avoid conflicts in style names.

Patch by: jlabanca
Review by: ecc (desk review)



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2509 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/eclipse/reference/code-museum/.project b/eclipse/reference/code-museum/.project
index 51ebf3f..58db54c 100644
--- a/eclipse/reference/code-museum/.project
+++ b/eclipse/reference/code-museum/.project
@@ -10,10 +10,16 @@
 			<arguments>
 			</arguments>
 		</buildCommand>
+		<buildCommand>
+			<name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>com.google.gwt.eclipse.plugin.gwtNature</nature>
+		<nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>
 	</natures>
 	<linkedResources>
 		<link>
diff --git a/eclipse/reference/code-museum/Museum.launch b/eclipse/reference/code-museum/Museum.launch
index a99eab6..3d8c6aa 100644
--- a/eclipse/reference/code-museum/Museum.launch
+++ b/eclipse/reference/code-museum/Museum.launch
@@ -1,15 +1,24 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/code-museum"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;code-museum&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/code-museum/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-user/core/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-dev-linux/core/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-user/core/super&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-dev-linux/core/super&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;code-museum&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+</listAttribute>
 <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
 <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.GWTShell"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;museum&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/museum/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento project=&quot;museum&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;/Applications/gwt-mac-1.4.61/gwt-dev-mac.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XstartOnFirstThread"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-out www com.google.gwt.inward.museum.Museum/Museum.html"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="museum"/>
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-out www com.google.gwt.museum.Museum/Museum.html -style PRETTY"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="code-museum"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgwt.devjar=&quot;/usr/local/jlabanca/gwt_all/latest/trunk/build/staging/gwt-linux-0.0.0/gwt-user.jar&quot;"/>
 </launchConfiguration>
diff --git a/reference/code-museum/src/com/google/gwt/museum/Museum.gwt.xml b/reference/code-museum/src/com/google/gwt/museum/Museum.gwt.xml
index c199f7c..76b161c 100644
--- a/reference/code-museum/src/com/google/gwt/museum/Museum.gwt.xml
+++ b/reference/code-museum/src/com/google/gwt/museum/Museum.gwt.xml
@@ -6,4 +6,5 @@
 	<!-- Specify the app entry point class.                   -->
 	<entry-point class='com.google.gwt.museum.client.Museum'/>
   
+  <stylesheet src="Museum.css"/>
 </module>
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/AbstractIssue.java b/reference/code-museum/src/com/google/gwt/museum/client/AbstractIssue.java
new file mode 100644
index 0000000..88dd029
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/AbstractIssue.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.google.gwt.museum.client;
+
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * An abstract issue that can be used in the code museum.
+ */
+public abstract class AbstractIssue {
+  /**
+   * <p>
+   * Create a widget that illustrates the issue. Each issue should include a
+   * detailed description of the expected results and the observed results
+   * before the issue was fixed.
+   * </p>
+   * <p>
+   * Note that createIssue will may be called multiple times if the user
+   * refreshes the issue. If you save state within the instance, you must clear
+   * it out and reset the issue when createIssue is called again.
+   * </p>
+   * 
+   * @return a widget that can reproduce the issue
+   */
+  public abstract Widget createIssue();
+
+  /**
+   * Return a detailed description of what the user should expect to see. The
+   * description will be added above the example.
+   * 
+   * @return the name of this issue
+   */
+  public abstract String getDescription();
+
+  /**
+   * Return a short description to display for this issue. If you do not
+   * override this method, the class name will be displayed.
+   * 
+   * @return the name of this issue
+   */
+  public String getHeadline() {
+    String className = getClass().getName();
+    className = className.substring(className.lastIndexOf(".") + 1);
+    return className;
+  }
+
+  /**
+   * @return true to load a CSS file of the same name
+   */
+  public abstract boolean hasCSS();
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/Museum.java b/reference/code-museum/src/com/google/gwt/museum/client/Museum.java
index 8860764..d726060 100644
--- a/reference/code-museum/src/com/google/gwt/museum/client/Museum.java
+++ b/reference/code-museum/src/com/google/gwt/museum/client/Museum.java
@@ -1,8 +1,45 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.google.gwt.museum.client;
 
 import com.google.gwt.core.client.EntryPoint;
-import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.HeadElement;
+import com.google.gwt.dom.client.LinkElement;
+import com.google.gwt.museum.client.issues.Issue2290;
+import com.google.gwt.museum.client.issues.Issue2307;
+import com.google.gwt.museum.client.issues.Issue2321;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HasVerticalAlignment;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.ImageBundle;
+import com.google.gwt.user.client.ui.ListBox;
 import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A repository for demonstrating bugs we once faced.
@@ -11,10 +48,182 @@
  * just a hacked together set of examples based on past issues.
  */
 public class Museum implements EntryPoint {
+  /**
+   * Images used in the museum.
+   */
+  public static interface MuseumImages extends ImageBundle {
+    AbstractImagePrototype nextButton();
+
+    AbstractImagePrototype prevButton();
+  }
+
+  /**
+   * The images used in this application.
+   */
+  public static final MuseumImages IMAGES = GWT.create(MuseumImages.class);
+
+  /**
+   * A reference for all issues.
+   */
+  public static final List<AbstractIssue> ISSUES = new ArrayList<AbstractIssue>();
+
+  /**
+   * Add an issue to the list of issues.
+   * 
+   * @param issue the issue to add
+   */
+  private static void addIssue(AbstractIssue issue) {
+    ISSUES.add(issue);
+  }
+
+  /**
+   * Convenience method for getting the document's head element.
+   * 
+   * @return the document's head element
+   */
+  private static native HeadElement getHeadElement() /*-{
+    return $doc.getElementsByTagName("head")[0];
+  }-*/;
+
+  /**
+   * Populate the list of issues. Add your issue here.
+   */
+  private static void populateIssues() {
+    addIssue(new Issue2290());
+    addIssue(new Issue2307());
+    addIssue(new Issue2321());
+  }
+
+  /**
+   * The panel that contains the current example.
+   */
+  private SimplePanel issueContainer = new SimplePanel();
+
+  /**
+   * A container to hold the CSS that will be applied to the issue.
+   */
+  private LinkElement issueLinkElement = null;
+
+  /**
+   * A description of the current issue.
+   */
+  private HTML issueDescription = new HTML();
+
+  /**
+   * The list of all issues.
+   */
+  private ListBox issueList = new ListBox();
 
   public void onModuleLoad() {
-    Panel p = RootPanel.get();
-    new Issue2290(p);
-    new Issue2307(p);
+    populateIssues();
+
+    // Add the options and issue containers to the page
+    RootPanel.get().add(createOptionsPanel());
+    RootPanel.get().add(issueDescription);
+    issueDescription.setStylePrimaryName("museum-issueDescription");
+    RootPanel.get().add(issueContainer);
+    issueContainer.setStylePrimaryName("museum-issueContainer");
+
+    // Default to the first issue
+    refreshIssue();
+  }
+
+  /**
+   * Create the options panel.
+   * 
+   * @return the options panel
+   */
+  private Widget createOptionsPanel() {
+    // Populate a list box containing all issues
+    for (AbstractIssue issue : ISSUES) {
+      issueList.addItem(issue.getHeadline());
+    }
+    issueList.addChangeListener(new ChangeListener() {
+      public void onChange(Widget sender) {
+        refreshIssue();
+      }
+    });
+
+    // Create a button to refresh the current issue
+    Button refreshIssueButton = new Button("Refresh", new ClickListener() {
+      public void onClick(Widget sender) {
+        refreshIssue();
+      }
+    });
+
+    // Create a button to move to the previous issue
+    Image prevButton = IMAGES.prevButton().createImage();
+    prevButton.setStyleName("prevButton");
+    prevButton.addClickListener(new ClickListener() {
+      public void onClick(Widget sender) {
+        int selectedIndex = issueList.getSelectedIndex();
+        if (selectedIndex > 0) {
+          issueList.setSelectedIndex(selectedIndex - 1);
+          refreshIssue();
+        } else {
+          Window.alert("You are already on the first issue");
+        }
+      }
+    });
+
+    // Create a button to move to the next issue
+    Image nextButton = IMAGES.nextButton().createImage();
+    nextButton.setStyleName("nextButton");
+    nextButton.addClickListener(new ClickListener() {
+      public void onClick(Widget sender) {
+        int selectedIndex = issueList.getSelectedIndex();
+        if (selectedIndex < issueList.getItemCount() - 1) {
+          issueList.setSelectedIndex(selectedIndex + 1);
+          refreshIssue();
+        } else {
+          Window.alert("You are already on the last issue");
+        }
+      }
+    });
+
+    // Combine the list box and text into a panel
+    HorizontalPanel hPanel = new HorizontalPanel();
+    hPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
+    hPanel.add(new HTML("Select an issue: "));
+    hPanel.add(issueList);
+    hPanel.add(prevButton);
+    hPanel.add(nextButton);
+    hPanel.add(refreshIssueButton);
+    SimplePanel wrapper = new SimplePanel();
+    wrapper.setStyleName("museum-optionsPanel");
+    wrapper.setWidget(hPanel);
+    return wrapper;
+  }
+
+  /**
+   * Refresh the current issue in the issue list.
+   */
+  private void refreshIssue() {
+    setIssue(ISSUES.get(issueList.getSelectedIndex()));
+  }
+
+  /**
+   * Set the current issue in the issue container.
+   * 
+   * @param issue the issue to set
+   */
+  private void setIssue(AbstractIssue issue) {
+    if (issueLinkElement != null) {
+      getHeadElement().removeChild(issueLinkElement);
+      issueLinkElement = null;
+    }
+    issueDescription.setHTML(issue.getDescription());
+    issueContainer.setWidget(issue.createIssue());
+
+    // Fetch the associated style sheet using an HTTP request
+    if (issue.hasCSS()) {
+      String className = issue.getClass().getName();
+      className = className.substring(className.lastIndexOf(".") + 1);
+      issueLinkElement = Document.get().createLinkElement();
+      issueLinkElement.setRel("stylesheet");
+      issueLinkElement.setType("text/css");
+      issueLinkElement.setHref("issues/" + className + ".css");
+      getHeadElement().appendChild(issueLinkElement);
+    }
   }
 }
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2290.java b/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2290.java
new file mode 100644
index 0000000..1fc603e
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2290.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.google.gwt.museum.client.issues;
+
+import com.google.gwt.museum.client.AbstractIssue;
+import com.google.gwt.user.client.ui.Tree;
+import com.google.gwt.user.client.ui.TreeItem;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * <h1> gwt-TreeItem refers to the wrong element in TreeItem </h1>
+ * 
+ * <p>
+ * gwt-TreeItem used to refer to the span that directly wrapped the text in a
+ * TreeItem. Now it refers to the table element that holds the expand/collapse
+ * image and the text. gwt-TreeItem-selected is still added to the span, so
+ * there is an inconsistency here.
+ * </p>
+ */
+public class Issue2290 extends AbstractIssue {
+
+  @Override
+  public Widget createIssue() {
+    Tree tree = new Tree();
+    TreeItem root = tree.addItem("Root Item");
+    root.addItem("Item1");
+    root.addItem("Item2");
+    root.addItem("Item3");
+    root.addItem("Item4");
+
+    root.setState(true);
+    tree.setSelectedItem(root);
+
+    return tree;
+  }
+
+  @Override
+  public String getDescription() {
+    return "The background of the Root Item, when selected, should be "
+        + "completely red, with no visible green.";
+  }
+
+  @Override
+  public boolean hasCSS() {
+    return true;
+  }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2307.java b/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2307.java
new file mode 100644
index 0000000..31d1cd0
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2307.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.google.gwt.museum.client.issues;
+
+import com.google.gwt.museum.client.AbstractIssue;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CaptionPanel;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * <h1>Normalize design of CaptionPanel</h1>
+ * 
+ * <p>
+ * Methods should be set/getCaptionHTML, set/getCaptionText,
+ * set/getContentWidget. Write lots of unit tests.
+ * </p>
+ */
+public class Issue2307 extends AbstractIssue {
+
+  private CaptionPanel captionPanel;
+
+  /**
+   * A set of options used to set the caption and content in the caption panel.
+   */
+  private class ControlPanel extends Composite {
+    private final Grid grid = new Grid(3, 2);
+
+    public ControlPanel() {
+      initWidget(grid);
+
+      // Add option to set the text
+      final TextBox textBox = new TextBox();
+      textBox.setText("<b>CaptionPanel</b>");
+      grid.setWidget(0, 1, textBox);
+      grid.setWidget(0, 0, new Button("setCaptionText", new ClickListener() {
+        public void onClick(Widget sender) {
+          captionPanel.setCaptionText(textBox.getText());
+        }
+      }));
+
+      // Add option to set the html
+      final TextBox htmlBox = new TextBox();
+      htmlBox.setText("<b>CaptionPanel</b>");
+      grid.setWidget(1, 1, htmlBox);
+      grid.setWidget(1, 0, new Button("setCaptionHTML", new ClickListener() {
+        public void onClick(Widget sender) {
+          captionPanel.setCaptionHTML(htmlBox.getText());
+        }
+      }));
+
+      // Add option to set the content
+      final TextBox contentBox = new TextBox();
+      contentBox.setText("<b><i>I am a Button</i></b>");
+      grid.setWidget(2, 1, contentBox);
+      grid.setWidget(2, 0, new Button("setContentWidget", new ClickListener() {
+        public void onClick(Widget sender) {
+          captionPanel.setContentWidget(new Button(contentBox.getText()));
+        }
+      }));
+    }
+  }
+
+  @Override
+  public Widget createIssue() {
+    captionPanel = new CaptionPanel("CaptionPanel");
+    VerticalPanel p = new VerticalPanel();
+    p.setSpacing(6);
+    p.add(captionPanel);
+    p.add(new ControlPanel());
+    return p;
+  }
+
+  @Override
+  public String getDescription() {
+    return "Verify the usage of various CaptionPanel methods.";
+  }
+
+  @Override
+  public String getHeadline() {
+    return "Issue 2307: CaptionPanel tests";
+  }
+
+  @Override
+  public boolean hasCSS() {
+    return false;
+  }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2321.java b/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2321.java
new file mode 100644
index 0000000..6fd1bd1
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/issues/Issue2321.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.google.gwt.museum.client.issues;
+
+import com.google.gwt.museum.client.AbstractIssue;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.DeckPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * <h1>DeckPanel children getOffsetWidth/Height() return 0 after r2060</h1>
+ * 
+ * <p>
+ * Child widgets of DeckPanel used to be able to call getOffsetWidth/Height() in
+ * onLoad() to obtain the widget's offset dimensions. These methods now return 0
+ * (zero) in onLoad(), and are only correct later, e.g. in a deferred command.
+ * </p>
+ */
+public class Issue2321 extends AbstractIssue {
+  /**
+   * A set of options used to set the caption and content in the caption panel.
+   */
+  private class ControlPanel extends Composite {
+    private final Grid grid = new Grid(3, 2);
+
+    public ControlPanel() {
+      initWidget(grid);
+
+      // Add option to detach the deck panel
+      Button addWidgetButton = new Button("Add widget", new ClickListener() {
+        public void onClick(Widget sender) {
+          addWidgetToDeckPanel();
+        }
+      });
+      grid.setWidget(0, 0, addWidgetButton);
+
+      // Add option to retrieve the dimensions of the content
+      Button updateDimButton = new Button("Get Current Dimensions",
+          new ClickListener() {
+            public void onClick(Widget sender) {
+              updateContentDimensions();
+            }
+          });
+      grid.setWidget(0, 1, updateDimButton);
+
+      // Add labels for the content height and width
+      grid.setHTML(1, 0, "Content Height:");
+      grid.setHTML(2, 0, "Content Width:");
+    }
+
+    /**
+     * Add another widget to the deck panel.
+     */
+    public void addWidgetToDeckPanel() {
+      int numWidgets = deck.getWidgetCount();
+      HTML content = new HTML("Content " + numWidgets) {
+        @Override
+        protected void onLoad() {
+          updateContentDimensions();
+        }
+      };
+
+      content.setStylePrimaryName("deckPanel-content");
+      deck.add(content);
+      deck.showWidget(numWidgets);
+    }
+
+    /**
+     * Retrieve the size of the content widgets.
+     */
+    public void updateContentDimensions() {
+      Widget content = deck.getWidget(deck.getWidgetCount() - 1);
+      grid.setHTML(1, 1, content.getOffsetHeight() + "");
+      grid.setHTML(2, 1, content.getOffsetWidth() + "");
+    }
+  }
+
+  /**
+   * The options panel to control this test.
+   */
+  private ControlPanel controlPanel = new ControlPanel();
+
+  /**
+   * The {@link DeckPanel} to be tested.
+   */
+  private DeckPanel deck;
+
+  /**
+   * The main container that holds the control panel and deck panel.
+   */
+  private VerticalPanel vPanel;
+
+  @Override
+  public Widget createIssue() {
+    // Create the deck panel and grab the size of the contents on load
+    deck = new DeckPanel() {
+      @Override
+      protected void onLoad() {
+        controlPanel.addWidgetToDeckPanel();
+      }
+    };
+    deck.setStylePrimaryName("deckPanel");
+
+    // Combine the control panel and DeckPanel
+    vPanel = new VerticalPanel();
+    vPanel.add(controlPanel);
+    vPanel.add(deck);
+    return vPanel;
+  }
+
+  @Override
+  public String getDescription() {
+    return "The height and width of the content should be greater than 0.";
+  }
+
+  @Override
+  public String getHeadline() {
+    return "Issue 2321: DeckPanel should return content dimensions onLoad";
+  }
+
+  @Override
+  public boolean hasCSS() {
+    return true;
+  }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/nextButton.png b/reference/code-museum/src/com/google/gwt/museum/client/nextButton.png
new file mode 100644
index 0000000..5a9cdf0
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/nextButton.png
Binary files differ
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/prevButton.png b/reference/code-museum/src/com/google/gwt/museum/client/prevButton.png
new file mode 100644
index 0000000..289b0d4
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/prevButton.png
Binary files differ
diff --git a/reference/code-museum/src/com/google/gwt/museum/public/Museum.css b/reference/code-museum/src/com/google/gwt/museum/public/Museum.css
new file mode 100644
index 0000000..2fc8a6b
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/public/Museum.css
@@ -0,0 +1,42 @@
+body {
+  padding: 0px;
+  margin: 0px;
+}
+
+.museum-optionsPanel {
+  background: #D6E9F8;
+  border-bottom: 1px solid #aaf;
+}
+.museum-optionsPanel td {
+  padding: 2px 0px;
+  font-size: 10pt;
+  font-weight: bold;
+}
+.museum-optionsPanel .gwt-ListBox {
+  font-size: 9pt;
+  color: blue;
+  margin: 0px 6px;
+}
+.museum-optionsPanel .gwt-Button {
+  font-size: 8pt;
+  margin: 0px 6px;
+}
+.museum-optionsPanel .nextButton,
+.museum-optionsPanel .prevButton {
+  font-weigth: normal;
+  color: blue;
+  margin: 0px 2px;
+  cursor: pointer;
+}
+
+.museum-issueDescription {
+  margin: 5px;
+	border: 1px solid #bbb;
+  padding: 8px 3px;
+  background: #fafafa;
+  font-size: 10pt;
+}
+
+.museum-issueContainer {
+  padding: 15px 20px;
+}
\ No newline at end of file
diff --git a/reference/code-museum/src/com/google/gwt/museum/public/Museum.html b/reference/code-museum/src/com/google/gwt/museum/public/Museum.html
index 45cd959..1b1871e 100644
--- a/reference/code-museum/src/com/google/gwt/museum/public/Museum.html
+++ b/reference/code-museum/src/com/google/gwt/museum/public/Museum.html
@@ -1,24 +1,10 @@
 <html>
 	<head>
 		<title>Museum</title>
-
-		<style>
-		
-		<!-- For Issue 2290 -->
-		.gwt-TreeItem {
-		  background-color: green;
-		}
-		
-		.gwt-TreeItem-selected {
-		  background-color: red;
-		}
-		
-		</style>
-
 		<script language='javascript' src='com.google.gwt.museum.Museum.nocache.js'></script>
 	</head>
 
 	<body>
-		<iframe src="javascript:''" id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
+		<iframe src="javascript:''" id="__gwt_historyFrame" style="position:absolute;width:0;height:0;border:0"></iframe>
 	</body>
 </html>
diff --git a/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue2290.css b/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue2290.css
new file mode 100644
index 0000000..4f19116
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue2290.css
@@ -0,0 +1,6 @@
+.gwt-TreeItem {
+  background-color: green;
+}
+.gwt-TreeItem-selected {
+  background-color: red;
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue2321.css b/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue2321.css
new file mode 100644
index 0000000..3c712b9
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/public/issues/Issue2321.css
@@ -0,0 +1,6 @@
+.deckPanel {
+  border: 1px solid blue;
+}
+.deckPanel-content {
+	background: red;
+}