This is an automated email from the ASF dual-hosted git repository.

benw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git


The following commit(s) were added to refs/heads/master by this push:
     new 8f9341ff4 TAP5-2790: Fixing grouped SelectModel for Palette
8f9341ff4 is described below

commit 8f9341ff4543024a89c341bde276f3acda2fdb85
Author: Ben Weidig <b...@netzgut.net>
AuthorDate: Fri Nov 15 11:21:37 2024 +0100

    TAP5-2790: Fixing grouped SelectModel for Palette
---
 .../META-INF/modules/t5/core/palette.coffee        |  13 ++-
 .../internal/util/SelectModelRenderer.java         |  51 +++++++---
 tapestry-core/src/test/app1/PaletteGroupedDemo.tml |  42 ++++++++
 .../tapestry5/integration/app1/PaletteTests.java   |  37 +++++++
 .../tapestry5/integration/app1/pages/Index.java    |   1 +
 .../integration/app1/pages/PaletteDemo.java        |  38 +++++++-
 .../integration/app1/pages/PaletteGroupedDemo.java | 107 +++++++++++++++++++++
 .../corelib/components/option_group_attributes.txt |   2 +-
 .../tapestry5/corelib/components/option_groups.txt |   2 +-
 .../option_groups_precede_ungroup_options.txt      |   2 +-
 10 files changed, 274 insertions(+), 21 deletions(-)

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 0ec5dea36..52fa98404 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-2013 The Apache Software Foundation
+# Copyright 2012-2024 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.
@@ -235,7 +235,8 @@ define ["t5/core/dom", "underscore", "t5/core/events"],
         for o in movers
           @insertOption toOptions, o, atEnd
 
-        selectedOptions = if to is @selected then toOptions else fromOptions
+        isSelectedSelect = to is @selected
+        selectedOptions = if isSelectedSelect then toOptions else fromOptions
 
         @performUpdate false, selectedOptions, =>
           for i in [(from.element.length - 1)..0] by -1
@@ -249,7 +250,13 @@ define ["t5/core/dom", "underscore", "t5/core/events"],
             to.element.remove i
 
           for o in toOptions
-            to.element.add o, null
+            groupIdx = o.getAttribute('data-optgroup-idx')
+            if isSelectedSelect or !groupIdx  or groupIdx == ''
+              to.element.add o, null
+            else
+              group = to.element.children[parseInt(groupIdx)]
+              group.appendChild o
+
 
       insertOption: (options, option, atEnd) ->
 
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/SelectModelRenderer.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/SelectModelRenderer.java
index 140ac3a7d..484cd229e 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/SelectModelRenderer.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/SelectModelRenderer.java
@@ -1,3 +1,5 @@
+// Copyright 2008-2024 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.
 // You may obtain a copy of the License at
@@ -24,6 +26,9 @@ public class SelectModelRenderer implements SelectModelVisitor
 
     private final boolean raw;
 
+    private int optgroupIdx = -1;
+    private boolean inOptgroup = false;
+
     public SelectModelRenderer(final MarkupWriter writer, ValueEncoder 
encoder, boolean raw)
     {
         this.writer = writer;
@@ -31,56 +36,78 @@ public class SelectModelRenderer implements 
SelectModelVisitor
         this.raw = raw;
     }
 
+    @Override
     public void beginOptionGroup(OptionGroupModel groupModel)
     {
-        writer.element("optgroup", "label", groupModel.getLabel());
+        this.optgroupIdx++;
+        this.inOptgroup = true;
+        this.writer.element("optgroup", "label", groupModel.getLabel());
 
         writeDisabled(groupModel.isDisabled());
         writeAttributes(groupModel.getAttributes());
     }
 
+    @Override
     public void endOptionGroup(OptionGroupModel groupModel)
     {
-        writer.end(); // select
+        this.inOptgroup = false;
+        this.writer.end(); // select
     }
 
+    @Override
     @SuppressWarnings("unchecked")
     public void option(OptionModel optionModel)
     {
         Object optionValue = optionModel.getValue();
 
-        String clientValue = encoder.toClient(optionValue);
+        String clientValue = this.encoder.toClient(optionValue);
 
-        writer.element("option", "value", clientValue);
+        this.writer.element("option", "value", clientValue);
 
-        if (isOptionSelected(optionModel, clientValue)) 
writer.attributes("selected", "selected");
+        if (this.inOptgroup && this.optgroupIdx > -1)
+        {
+            this.writer.attributes("data-optgroup-idx", this.optgroupIdx);
+        }
+
+        if (isOptionSelected(optionModel, clientValue))
+        {
+            this.writer.attributes("selected", "selected");
+        }
 
         writeDisabled(optionModel.isDisabled());
         writeAttributes(optionModel.getAttributes());
 
 
-        if (raw)
+        if (this.raw)
         {
-            writer.writeRaw(optionModel.getLabel());
+            this.writer.writeRaw(optionModel.getLabel());
         } else
         {
-            writer.write(optionModel.getLabel());
+            this.writer.write(optionModel.getLabel());
         }
 
-        writer.end();
+        this.writer.end();
     }
 
     private void writeDisabled(boolean disabled)
     {
-        if (disabled) writer.attributes("disabled", "disabled");
+        if (disabled)
+        {
+            this.writer.attributes("disabled", "disabled");
+        }
     }
 
     private void writeAttributes(Map<String, String> attributes)
     {
-        if (attributes == null) return;
+        if (attributes == null)
+        {
+            return;
+        }
 
         for (Map.Entry<String, String> e : attributes.entrySet())
-            writer.attributes(e.getKey(), e.getValue());
+        {
+            this.writer.attributes(e.getKey(), e.getValue());
+        }
     }
 
     /**
diff --git a/tapestry-core/src/test/app1/PaletteGroupedDemo.tml 
b/tapestry-core/src/test/app1/PaletteGroupedDemo.tml
new file mode 100644
index 000000000..de0159264
--- /dev/null
+++ b/tapestry-core/src/test/app1/PaletteGroupedDemo.tml
@@ -0,0 +1,42 @@
+<html t:type="Border" 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
+
+<h1>Palette Grouped Demo</h1>
+
+
+<t:form t:id="demo" class="form-horizontal">
+    <div class="checkbox">
+        <label>
+            <t:checkbox t:id="reorder"/>
+            Enable Reorder
+        </label>
+    </div>
+
+
+    <div class="form-group">
+
+        <t:label for="languages"/>
+
+        <t:palette t:id="languages" model="languageModel" reorder="reorder" 
encoder="languageEncoder"
+                   availableLabel="Languages Offered" validate="required">
+            <t:parameter name="selectedLabel" xml:space="default">
+                <t:if test="reorder" else="Selected">Ranked
+                </t:if>
+                Languages
+            </t:parameter>
+        </t:palette>
+    </div>
+
+    <input type="submit" class="btn btn-primary"/>
+
+</t:form>
+
+<dl class="dl-horizontal">
+    <dt>Languages:</dt>
+    <dd id="selected-languages">${languages}</dd>
+    <dt>Selected Values</dt>
+    <dd id="event-selection"/>
+    <dt>Reorder</dt>
+    <dd id="event-reorder"></dd>
+</dl>
+
+</html>
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 23553e149..a725580d9 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
@@ -14,9 +14,12 @@
 
 package org.apache.tapestry5.integration.app1;
 
+import java.util.List;
+
 import org.apache.tapestry5.corelib.components.Palette;
 import org.openqa.selenium.JavascriptExecutor;
 import org.openqa.selenium.WebElement;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 /**
@@ -131,4 +134,38 @@ public class PaletteTests extends App1TestCase
 
         assertTextPresent("You must provide a value for Languages.");
     }
+
+    @Test
+    public void palette_component_grouped()
+    {
+        openLinks("Palette Grouped Demo", "Reset Page State");
+
+        assertText("css=.palette-available .palette-title", "Languages 
Offered");
+        assertText("css=.palette-selected .palette-title", "Selected 
Languages");
+
+        addSelection(AVAILABLE_OPTIONS, "label=HASKELL");
+        addSelection(AVAILABLE_OPTIONS, "label=JAVA");
+        click(SELECT_BUTTON);
+
+        // What a listener on the events.palette.willChange event would see in 
memo.selectdValues:
+        assertText("id=event-selection", "[\"HASKELL\",\"JAVA\"]");
+
+        clickAndWait(SUBMIT);
+
+        assertText("id=selected-languages", "[HASKELL, JAVA]");
+
+        addSelection(SELECTED_OPTIONS, "label=JAVA");
+
+        click(DESELECT_BUTTON);
+
+        List<WebElement> funcOptions = this.webDriver
+                .findElements(convertLocator(AVAILABLE_OPTIONS + " 
optgroup[label=func] option"));
+
+        List<WebElement> ooOptions = this.webDriver
+                .findElements(convertLocator(AVAILABLE_OPTIONS + " 
optgroup[label=oo] option"));
+
+        Assert.assertEquals(funcOptions.size(), 2);
+        Assert.assertEquals(ooOptions.size(), 2);
+
+    }
 }
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
index 5fcc49528..eb447d758 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
@@ -396,6 +396,7 @@ public class Index
                             "use expansions inside attributes of ordinary 
elements"),
 
                     new Item("PaletteDemo", "Palette Demo", "multiple 
selection component"),
+                    new Item("PaletteGroupedDemo", "Palette Grouped Demo", 
"multiple selection component (grouped)"),
 
                     new Item("ReturnTypes", "Return Types", "tests various 
event handler return types"),
 
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 d626c4c9a..3694f8ccc 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
@@ -14,7 +14,13 @@
 
 package org.apache.tapestry5.integration.app1.pages;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.OptionGroupModel;
+import org.apache.tapestry5.OptionModel;
 import org.apache.tapestry5.SelectModel;
 import org.apache.tapestry5.ValueEncoder;
 import org.apache.tapestry5.annotations.Import;
@@ -22,13 +28,13 @@ import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.commons.services.TypeCoercer;
 import org.apache.tapestry5.integration.app1.data.ProgrammingLanguage;
+import org.apache.tapestry5.internal.OptionGroupModelImpl;
+import org.apache.tapestry5.internal.OptionModelImpl;
 import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.util.AbstractSelectModel;
 import org.apache.tapestry5.util.EnumSelectModel;
 import org.apache.tapestry5.util.EnumValueEncoder;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @Import(module="palette-demo")
 public class PaletteDemo
 {
@@ -65,4 +71,30 @@ public class PaletteDemo
     {
         return new EnumValueEncoder(typeCoercer, ProgrammingLanguage.class);
     }
+
+    public SelectModel getGroupedModel()
+    {
+        return new AbstractSelectModel()
+        {
+
+            @Override
+            public List<OptionGroupModel> getOptionGroups()
+            {
+                List<OptionGroupModel> groups = new ArrayList<>();
+                groups.add(new OptionGroupModelImpl("group1", false,
+                        Arrays.asList(new OptionModelImpl("1"))));
+                groups.add(new OptionGroupModelImpl("group2", false,
+                        Arrays.asList(new OptionModelImpl("1"))));
+                return null;
+            }
+
+            @Override
+            public List<OptionModel> getOptions()
+            {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+        };
+    }
 }
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteGroupedDemo.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteGroupedDemo.java
new file mode 100644
index 000000000..0fe130ba7
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PaletteGroupedDemo.java
@@ -0,0 +1,107 @@
+// Copyright 2007-2014 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.
+// 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 org.apache.tapestry5.integration.app1.pages;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.OptionGroupModel;
+import org.apache.tapestry5.OptionModel;
+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.commons.services.TypeCoercer;
+import org.apache.tapestry5.integration.app1.data.ProgrammingLanguage;
+import org.apache.tapestry5.internal.OptionGroupModelImpl;
+import org.apache.tapestry5.internal.OptionModelImpl;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.util.AbstractSelectModel;
+import org.apache.tapestry5.util.EnumValueEncoder;
+
+@Import(module="palette-demo")
+public class PaletteGroupedDemo
+{
+    @Inject
+    private ComponentResources resources;
+
+    @Persist
+    @Property
+    private List<ProgrammingLanguage> languages;
+
+    @Persist
+    @Property
+    private boolean reorder;
+
+    @Inject
+    private TypeCoercer typeCoercer;
+
+    private static final Iterable<ProgrammingLanguage> FUNC = Arrays.asList(
+            ProgrammingLanguage.ERLANG, ProgrammingLanguage.HASKELL, 
ProgrammingLanguage.LISP);
+    private static final Iterable<ProgrammingLanguage> OO = 
Arrays.asList(ProgrammingLanguage.JAVA,
+            ProgrammingLanguage.RUBY);
+
+    void onPrepareFromDemo()
+    {
+        if (this.languages == null)
+        {
+            this.languages = new ArrayList<>();
+        }
+    }
+
+    public SelectModel getLanguageModel()
+    {
+        return new AbstractSelectModel()
+        {
+
+            @Override
+            public List<OptionGroupModel> getOptionGroups()
+            {
+                List<OptionGroupModel> groups = new ArrayList<>();
+                groups.add(new OptionGroupModelImpl("func", false,
+                        toOptionModels(FUNC)));
+                groups.add(new OptionGroupModelImpl("oo", false, 
toOptionModels(OO)));
+                return groups;
+            }
+
+            @Override
+            public List<OptionModel> getOptions()
+            {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+        };
+    }
+
+    private List<OptionModel> toOptionModels(Iterable<ProgrammingLanguage> 
languages)
+    {
+        List<OptionModel> options = new ArrayList<>();
+        for (ProgrammingLanguage enumValue : languages)
+        {
+            options.add(new OptionModelImpl(enumValue.name(), enumValue));
+        }
+        return options;
+    }
+
+    @SuppressWarnings("unchecked")
+    public ValueEncoder getLanguageEncoder()
+    {
+        return new EnumValueEncoder(this.typeCoercer, 
ProgrammingLanguage.class);
+    }
+}
diff --git 
a/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_group_attributes.txt
 
b/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_group_attributes.txt
index f0cff6bf5..683d4cf71 100644
--- 
a/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_group_attributes.txt
+++ 
b/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_group_attributes.txt
@@ -1,2 +1,2 @@
 <?xml version="1.0"?>
-<select><optgroup class="pixie" label="Husbands"><option selected="selected" 
value="Fred">Fred</option><option 
value="Barney">Barney</option></optgroup></select>
\ No newline at end of file
+<select><optgroup class="pixie" label="Husbands"><option selected="selected" 
data-optgroup-idx="0" value="Fred">Fred</option><option data-optgroup-idx="0" 
value="Barney">Barney</option></optgroup></select>
\ No newline at end of file
diff --git 
a/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups.txt
 
b/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups.txt
index 3035c4af9..c1d4fd14b 100644
--- 
a/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups.txt
+++ 
b/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups.txt
@@ -1,2 +1,2 @@
 <?xml version="1.0"?>
-<select><optgroup label="Husbands"><option selected="selected" 
value="Fred">Fred</option><option 
value="Barney">Barney</option></optgroup><optgroup disabled="disabled" 
label="Wives"><option value="Wilma">Wilma</option><option 
value="Betty">Betty</option></optgroup></select>
\ No newline at end of file
+<select><optgroup label="Husbands"><option selected="selected" 
data-optgroup-idx="0" value="Fred">Fred</option><option data-optgroup-idx="0" 
value="Barney">Barney</option></optgroup><optgroup disabled="disabled" 
label="Wives"><option data-optgroup-idx="1" value="Wilma">Wilma</option><option 
data-optgroup-idx="1" value="Betty">Betty</option></optgroup></select>
\ No newline at end of file
diff --git 
a/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups_precede_ungroup_options.txt
 
b/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups_precede_ungroup_options.txt
index 7d49d8e8f..da0aebcb0 100644
--- 
a/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups_precede_ungroup_options.txt
+++ 
b/tapestry-core/src/test/resources/org/apache/tapestry5/corelib/components/option_groups_precede_ungroup_options.txt
@@ -1,2 +1,2 @@
 <?xml version="1.0"?>
-<select><optgroup label="Husbands"><option selected="selected" 
value="Fred">Fred</option><option 
value="Barney">Barney</option></optgroup><option 
value="Wilma">Wilma</option><option value="Betty">Betty</option></select>
\ No newline at end of file
+<select><optgroup label="Husbands"><option selected="selected" 
data-optgroup-idx="0" value="Fred">Fred</option><option data-optgroup-idx="0" 
value="Barney">Barney</option></optgroup><option 
value="Wilma">Wilma</option><option value="Betty">Betty</option></select>
\ No newline at end of file

Reply via email to