You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2013/06/11 02:38:33 UTC

git commit: Add events to Palette component so that client-side can choose to accept or veto a change to the selection

Updated Branches:
  refs/heads/master f02a205eb -> 53e056052


Add events to Palette component so that client-side can choose to accept or veto a change to the selection


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/53e05605
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/53e05605
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/53e05605

Branch: refs/heads/master
Commit: 53e056052e17d6e719b40815e5fd0be3a394ef0a
Parents: f02a205
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Mon Jun 10 17:38:29 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Mon Jun 10 17:38:29 2013 -0700

----------------------------------------------------------------------
 54_RELEASE_NOTES.txt                            |   7 +
 .../META-INF/modules/t5/core/events.coffee      |  11 ++
 .../META-INF/modules/t5/core/palette.coffee     | 181 +++++++++++++------
 .../tapestry5/corelib/components/Palette.java   |   8 +-
 .../resources/META-INF/assets/core/Palette.css  |  18 +-
 .../tapestry5/corelib/components/Palette.tml    |  14 +-
 tapestry-core/src/test/app1/PaletteDemo.tml     |   7 +-
 .../META-INF/modules/palette-demo.coffee        |   5 +
 .../tapestry5/integration/app1/FormTests.java   |   8 +-
 .../integration/app1/PaletteTests.java          |  28 +--
 .../integration/app1/pages/PaletteDemo.java     |   4 +-
 11 files changed, 197 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/54_RELEASE_NOTES.txt
----------------------------------------------------------------------
diff --git a/54_RELEASE_NOTES.txt b/54_RELEASE_NOTES.txt
index f05b58c..28f7e40 100644
--- a/54_RELEASE_NOTES.txt
+++ b/54_RELEASE_NOTES.txt
@@ -268,3 +268,10 @@ org.apache.tapestry5.test.TapestryRunnerConstants in the new tapestry-runner mod
 ## AbstractValidator base class
 
 The constructor for AbstractValidator has changed to include an instance of JavaScriptSupport.
+
+## CSS Changes
+
+Where Tapestry-specific CSS still exists (in support of the Palette component and the Tree component), the "t-" prefix
+has been removed. This may affect applications that overrode the Tapestry CSS rules to adapt Tapestry to the application
+look and feel.
+

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee
index bc9ca33..4054520 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee
@@ -124,6 +124,17 @@ define
     # * If the field's container has class "input-append" or "input-prepend", then the block is inserted after the container
     showValidationError: "t5:field:show-validation-error"
 
+  # Events triggered by the Palette component.
+  palette:
+    # Event triggered when the selection is about to change. The memo object has these properties:
+    # * selectedValues - list of selected values (if change is allowed)
+    # * reorder - if true, then the event represents changing the ordrer of the selections only
+    # * cancel - function to invoke to prevent the change to the Palette from occurring
+    willChange: "t5:palette:willChange"
+    # Event triggered after the selection has changed. The memo object has one property:
+    # * selectedValues - list of selected values
+    didChange: "t5:palette:didChange"
+
   # Defines a number of event names specific to Tapestry Zones. Zones are Tapestry components that are structured
   # to correctly support dynamic updates from the server via an Ajax request, and a standard response
   # (the partial page render reponse). More details are available in the `t5/core/zone` module.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee
index b03163e..9f8cc4c 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee
@@ -1,4 +1,4 @@
-# Copyright 2012 The Apache Software Foundation
+# Copyright 2012-2013 The Apache Software Foundation
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,14 +15,17 @@
 # ## t5/core/palette
 #
 # Support for the `core/Palette` component.
-define ["./dom", "underscore"],
-  (dom, _) ->
+define ["./dom", "underscore", "./events"],
+  (dom, _, events) ->
+
+    isSelected = (option) -> option.selected
+
     class PaletteController
 
       constructor: (id) ->
         @selected = (dom id)
-        @container = @selected.findParent ".t-palette"
-        @available = @container.findFirst ".t-palette-available select"
+        @container = @selected.findParent ".palette"
+        @available = @container.findFirst ".palette-available select"
         @hidden = @container.findFirst "input[type=hidden]"
 
         @select = @container.findFirst "[data-action=select]"
@@ -69,6 +72,8 @@ define ["./dom", "underscore"],
         for option in movers
           @selected.element.add option
 
+      # Invoked after any change to the selections list to update the hidden field as well as the
+      # buttons' state.
       updateAfterChange: ->
         @updateHidden()
         @updateButtons()
@@ -107,6 +112,8 @@ define ["./dom", "underscore"],
             @doMoveDown()
             return false
 
+      # Invoked whenever the selections in either list changes or after an updates; figures out which buttons
+      # should be enabled and which disabled.
       updateButtons: ->
         @select.element.disabled = @available.element.selectedIndex < 0
 
@@ -123,78 +130,139 @@ define ["./dom", "underscore"],
       doDeselect: -> @transferOptions @selected, @available, false
 
       doMoveUp: ->
-        e = @selected.element
-        pos = e.selectedIndex - 1
-        movers = @removeSelectedOptions @selected
-        before = e.options[if pos < 0 then 0 else pos]
+        options = _.toArray @selected.element.options
+
+        movers = _.filter options, isSelected
+
+        # The element before the first selected element is the pivot; all the selected elements will
+        # move before the pivot. If there is no pivot, the elements are shifted to the front of the list.
+        firstMoverIndex= _.first(movers).index
+        pivot = options[firstMoverIndex - 1]
+
+        options = _.reject options, isSelected
+
+        splicePos = if pivot then _.indexOf options, pivot else 0
+
+        movers.reverse()
+
+        for o in movers
+          options.splice splicePos, 0, o
+
+        @reorderSelected options
 
-        @reorderOptions movers, before
 
       doMoveDown: ->
-        e = @selected.element
-        lastSelected = _.chain(e.options).toArray().reverse().find((o) -> o.selected).value()
+        options = _.toArray @selected.element.options
 
-        lastPos = lastSelected.index
-        before = e.options[lastPos + 2]
+        movers = _.filter options, isSelected
 
-        movers = @removeSelectedOptions @selected
+        # The element after the last selected element is the pivot; all the selected elements will
+        # move after the pivot. If there is no pivot, the elements are shifted to the end of the list.
+        lastMoverIndex = _.last(movers).index
+        pivot = options[lastMoverIndex + 1]
 
-        @reorderOptions movers, before
+        options = _.reject options, isSelected
 
-      reorderOptions: (movers, before) ->
-        for mover in movers
-          @addOption @selected, mover, before
-        @updateAfterChange()
+        splicePos = if pivot then _.indexOf(options, pivot) + 1 else options.length
+
+        movers.reverse()
+
+        for o in movers
+          options.splice splicePos, 0, o
+
+        @reorderSelected options
+
+      # Reorders the selected options to the provided list of options; handles triggering the willUpdate and
+      # didUpdate events.
+      reorderSelected: (options) ->
+
+        canceled = false
+
+        memo =
+          selectedValues: _.pluck options, "value"
+          reorder: true
+          cancel: -> canceled = true
+
+        @selected.trigger events.palette.willChange, memo
+
+        unless canceled
 
+          @deleteOptions @selected
+
+          for o in options
+            @selected.element.add o, null
+
+          @selected.trigger events.palette.didChange, memo
+
+          @updateAfterChange()
+
+      # Deletes all options from a select (an ElementWrapper), prior to new options being populated in.
+      deleteOptions: (select) ->
+
+        e = select.element
+
+        for i in [(e.length - 1)..0] by -1
+          e.remove i
+
+      # Moves options between the available and selected lists, including event notifiations before and after.
       transferOptions: (from, to, atEnd) ->
+
         if from.element.selectedIndex is -1
           return
 
-        _(to.element.options).each (o) -> o.selected = false
+        # This could be done in a single pass, but:
+        movers = _.filter from.element.options, isSelected
+        fromOptions = _.reject from.element.options, isSelected
 
-        movers = @removeSelectedOptions from
+        toOptions = _.toArray to.element.options
 
-        @moveOptions movers, to, atEnd
+        for o in movers
+          @insertOption toOptions, o, atEnd
 
-      removeSelectedOptions: (select) ->
-        movers = []
-        e = select.element
-        options = e.options
+        selectedOptions = if to is @selected then toOptions else fromOptions
 
-        for i in [(e.length - 1)..(e.selectedIndex)] by -1
-          o = options[i]
-          if o.selected
-            e.remove i
-            movers.unshift o
+        canceled = false
+
+        memo =
+          selectedValues: _.pluck selectedOptions, "value"
+          reorder: false
+          cancel: -> canceled = true
+
+        @selected.trigger events.palette.willChange, memo
+
+        return if canceled
+
+        # Remove the movers (the selected from elements):
+        for i in [(from.element.length - 1)..0] by -1
+          if from.element.options[i].selected
+            from.element.remove i
+
+        # A bit ugly: update the to select by removing all, then adding back in.
 
-        return movers
+        for i in [(to.element.length - 1)..0] by -1
+          to.element.options[i].selected = false
+          to.element.remove i
 
-      moveOptions: (movers, to, atEnd) ->
-        _.each movers, (o) =>
-          @moveOption o, to, atEnd
+        for o in toOptions
+          to.element.add o, null
+
+        @selected.trigger events.palette.didChange, memo
 
         @updateAfterChange()
 
-      moveOption: (option, to, atEnd) ->
-        before = null
+
+      insertOption: (options, option, atEnd) ->
 
         unless atEnd
           optionOrder = @valueToOrderIndex[option.value]
-          candidate = _.find to.element.options, (o) => @valueToOrderIndex[o.value] > optionOrder
-          if candidate
-            before = candidate
-
-        @addOption to, option, before
-
-      addOption: (to, option, before) ->
-        try
-          to.element.add option, before
-        catch ex
-          if before is null
-            # IE throws an exception about type mismatch; here's the fix:
-            to.add option
-          else
-            to.add option, before.index
+          before = _.find options, (o) => @valueToOrderIndex[o.value] > optionOrder
+
+        if before
+          i = _.indexOf options, before
+          options.splice i, 0, option
+        else
+          options.push option
+
 
       indexOfLastSelection: (select) ->
         e = select.element
@@ -221,8 +289,5 @@ define ["./dom", "underscore"],
         _(options[last..]).all (o) -> o.selected
 
 
-    initialize = (id) ->
-      new PaletteController(id)
-
-    # Export just the initialize function
-    return initialize
\ No newline at end of file
+    # Export just the initializer function
+    (id) -> new PaletteController(id)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
index d82dcad..5ea4fba 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
+// Copyright 2007-2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ import java.util.Collection;
  * <p/>
  * <pre>
  * &lt;style&gt;
- * DIV.t-palette SELECT { width: 300px; }
+ *   DIV.palette SELECT { width: 300px; }
  * &lt;/style&gt;
  * </pre>
  * <p/>
@@ -59,6 +59,10 @@ import java.util.Collection;
  * <p/>
  * For an alternative component that can be used for similar purposes, see
  * {@link Checklist}.
+ * <p>Starting in 5.4, the selected parameter may be any kind of collection, but is typically a List if the Palette is configured for re-ordering,
+ * and a Set if order does not matter (though it is common to use a List in the latter case as well). Also, starting in 5.4,
+ * the Palette is compatible with the {@link org.apache.tapestry5.validator.Required} validator (on both client and server-side), and
+ * triggers new events that allows the application to veto a proposed changed to the selection (see the {@code t5/core/events} module).
  *
  * @tapestrydoc
  * @see Form

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/main/resources/META-INF/assets/core/Palette.css
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/META-INF/assets/core/Palette.css b/tapestry-core/src/main/resources/META-INF/assets/core/Palette.css
index b2aa921..fe8b62f 100644
--- a/tapestry-core/src/main/resources/META-INF/assets/core/Palette.css
+++ b/tapestry-core/src/main/resources/META-INF/assets/core/Palette.css
@@ -1,13 +1,13 @@
-DIV.t-palette {
+DIV.palette {
     display: inline;
 }
 
-DIV.t-palette SELECT {
+DIV.palette SELECT {
     margin-bottom: 2px;
     width: 200px;
 }
 
-DIV.t-palette-title {
+DIV.palette-title {
     color: white;
     background-color: black;
     text-align: center;
@@ -17,28 +17,28 @@ DIV.t-palette-title {
     width: 200px;
 }
 
-DIV.t-palette-available {
+DIV.palette-available {
     float: left;
 }
 
-DIV.t-palette-controls {
+DIV.palette-controls {
     margin: 5px 5px;
     float: left;
 }
 
-DIV.t-palette-controls > DIV {
+DIV.palette-controls > DIV {
     margin-top: 5px;
 }
 
-DIV.t-palette-controls > DIV:first-child {
+DIV.palette-controls > DIV:first-child {
     margin-top: 0;
 }
 
-DIV.t-palette-selected {
+DIV.palette-selected {
     float: left;
     clear: right;
 }
 
-DIV.t-palette-spacer {
+DIV.palette-spacer {
     clear: left;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Palette.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Palette.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Palette.tml
index 7512185..8b3b196 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Palette.tml
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Palette.tml
@@ -1,9 +1,9 @@
-<div class="t-palette" xml:space="default" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+<div class="palette" xml:space="default" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
     <t:remove>Contains the array of option ids for selected elements:</t:remove>
     <input type="hidden" name="${controlName}" value="${initialJSON}" disabled="${disabledValue}"/>
 
-    <div class="t-palette-available">
-        <div class="t-palette-title">
+    <div class="palette-available">
+        <div class="palette-title">
             <t:delegate to="availableLabel"/>
         </div>
         <select multiple="multiple" size="${size}" disabled="${disabledValue}">
@@ -15,7 +15,7 @@
         </select>
     </div>
 
-    <div class="t-palette-controls">
+    <div class="palette-controls">
         <div>
             <button data-action="select" class="btn" disabled="${disabledValue}">
                 <img src="${select}" alt="${message:core-palette-select-label}"/>
@@ -39,12 +39,12 @@
             </div>
         </t:if>
     </div>
-    <div class="t-palette-selected">
-        <div class="t-palette-title">
+    <div class="palette-selected">
+        <div class="palette-title">
             <t:delegate to="selectedLabel"/>
         </div>
         <t:remove>
-            data-value=mode="options" is a bit of hack to inform the client side that the value for the field is all options, selected or not.
+            data-value=mode="options" is a bit of hack to inform the client side that the value for the field is all options, selected or not.  This is used by validation logic.
         </t:remove>
         <select t:type="any" t:id="selected" id="${clientId}" multiple="multiple" size="${size}" disabled="${disabledValue}" data-value-mode="options" t:mixins="rendernotification">
             <t:remove>Starts empty, populated on the client side.</t:remove>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/test/app1/PaletteDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/PaletteDemo.tml b/tapestry-core/src/test/app1/PaletteDemo.tml
index 3023c3a..7e516ce 100644
--- a/tapestry-core/src/test/app1/PaletteDemo.tml
+++ b/tapestry-core/src/test/app1/PaletteDemo.tml
@@ -39,6 +39,11 @@
 
 </t:form>
 
-<p>Selected Languages: ${languages}</p>
+<dl class="dl-horizontal">
+    <dt>Languages:</dt>
+    <dd id="selected-languages">${languages}</dd>
+    <dt>Selected Values</dt>
+    <dd id="event-selection"/>
+</dl>
 
 </html>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/test/coffeescript/META-INF/modules/palette-demo.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/coffeescript/META-INF/modules/palette-demo.coffee b/tapestry-core/src/test/coffeescript/META-INF/modules/palette-demo.coffee
new file mode 100644
index 0000000..226e062
--- /dev/null
+++ b/tapestry-core/src/test/coffeescript/META-INF/modules/palette-demo.coffee
@@ -0,0 +1,5 @@
+define ["t5/core/dom", "t5/core/events"],
+  (dom, events) ->
+
+    dom.onDocument events.palette.willChange, (event, memo) ->
+      (dom "event-selection").update JSON.stringify memo.selectedValues
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
index e8cf44d..24558d2 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
@@ -471,13 +471,13 @@ public class FormTests extends TapestryCoreTestCase
 
                         "//input[@id='datefield']",
 
-                        "//div[@class='t-palette']//input[@type='hidden']",
+                        "//div[@class='palette']//input[@type='hidden']",
 
-                        "//div[@class='t-palette-available']//select",
+                        "//div[@class='palette-available']//select",
 
-                        "//div[@class='t-palette-selected']//select",
+                        "//div[@class='palette-selected']//select",
 
-                        "//div[@class='t-palette-controls']//button",
+                        "//div[@class='palette-controls']//button",
 
                         "//input[@id='submit_0']"};
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/PaletteTests.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/PaletteTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/PaletteTests.java
index e9b0889..03d87fc 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/PaletteTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/PaletteTests.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2011 The Apache Software Foundation
+// Copyright 2009-2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -24,17 +24,17 @@ import org.testng.annotations.Test;
 public class PaletteTests extends TapestryCoreTestCase
 {
 
-    public static final String AVAILABLE_OPTIONS = "css=.t-palette-available select";
+    public static final String AVAILABLE_OPTIONS = "css=.palette-available select";
 
-    public static final String SELECTED_OPTIONS = "css=.t-palette-selected select";
+    public static final String SELECTED_OPTIONS = "css=.palette-selected select";
 
-    public static final String SELECT_BUTTON = "css=.t-palette [data-action=select]";
+    public static final String SELECT_BUTTON = "css=.palette [data-action=select]";
 
-    public static final String DESELECT_BUTTON = "css=.t-palette [data-action=deselect]";
+    public static final String DESELECT_BUTTON = "css=.palette [data-action=deselect]";
 
-    public static final String MOVE_UP_BUTTON = "css=.t-palette [data-action=move-up]";
+    public static final String MOVE_UP_BUTTON = "css=.palette [data-action=move-up]";
 
-    public static final String MOVE_DOWN_BUTTON = "css=.t-palette [data-action=move-down]";
+    public static final String MOVE_DOWN_BUTTON = "css=.palette [data-action=move-down]";
 
     @Test
     public void palette_component()
@@ -43,18 +43,22 @@ public class PaletteTests extends TapestryCoreTestCase
 
         waitForPageInitialized();
 
-        assertText("css=.t-palette-available .t-palette-title",
+        assertText("css=.palette-available .palette-title",
                 "Languages Offered");
-        assertText("css=.t-palette-selected .t-palette-title",
+        assertText("css=.palette-selected .palette-title",
                 "Selected Languages");
 
         addSelection(AVAILABLE_OPTIONS, "label=Haskell");
         addSelection(AVAILABLE_OPTIONS, "label=Javascript");
         click(SELECT_BUTTON);
 
+        // What a listener on the events.palette.willChange event would see in memo.selectdValues:
+        assertText("id=event-selection", "[\"HASKELL\",\"JAVASCRIPT\"]");
+
+
         clickAndWait(SUBMIT);
 
-        assertTextPresent("Selected Languages: [HASKELL, JAVASCRIPT]");
+        assertText("id=selected-languages", "[HASKELL, JAVASCRIPT]");
 
         waitForPageInitialized();
 
@@ -75,7 +79,7 @@ public class PaletteTests extends TapestryCoreTestCase
 
         clickAndWait(SUBMIT);
 
-        assertTextPresent("[ERLANG, HASKELL, JAVA, LISP, ML, PERL, PYTHON, RUBY]");
+        assertText("id=selected-languages", "[ERLANG, HASKELL, JAVA, LISP, ML, PERL, PYTHON, RUBY]");
 
         check("reorder");
 
@@ -97,7 +101,7 @@ public class PaletteTests extends TapestryCoreTestCase
 
         clickAndWait(SUBMIT);
 
-        assertTextPresent("[ERLANG, RUBY, HASKELL, JAVA, LISP, ML, PYTHON, PERL]");
+        assertText("id=selected-languages", "[ERLANG, RUBY, HASKELL, JAVA, LISP, ML, PYTHON, PERL]");
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/53e05605/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteDemo.java
index 8ed2e3f..2f92fc6 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteDemo.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteDemo.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2012 The Apache Software Foundation
+// Copyright 2007-2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.apache.tapestry5.integration.app1.pages;
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.SelectModel;
 import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.integration.app1.data.ProgrammingLanguage;
@@ -28,6 +29,7 @@ import org.apache.tapestry5.util.EnumValueEncoder;
 import java.util.ArrayList;
 import java.util.List;
 
+@Import(module="palette-demo")
 public class PaletteDemo
 {
     @Inject