You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2023/06/12 07:17:43 UTC

[myfaces-tobago] 01/02: fix(select[One/Many]List): events

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

hnoeth pushed a commit to branch tobago-5.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit cdc0dd316897a08d51ed69617a130819df2a889a
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Jun 5 15:32:55 2023 +0200

    fix(select[One/Many]List): events
    
    * make click/dblclick/focus/blur events work
    * fix: change event with tc:event
    * getFieldIdForBehavior() removed; The hidden select component has now the field id.
    * a click on a label or an error message (label for attribute) focus the component
    * remove "input" event from supported behavior list. Use the change event instead.
    * adjust tests
    * add event test
    
    Issue: TOBAGO-2220
---
 .../myfaces/tobago/component/SupportFieldId.java   |  4 --
 .../component/AbstractUISelectManyList.java        | 12 ++---
 .../component/AbstractUISelectOneList.java         |  6 +--
 .../renderkit/renderer/SelectManyListRenderer.java | 16 +++----
 .../renderkit/renderer/SelectOneListRenderer.java  | 16 +++----
 .../renderer/TobagoClientBehaviorRenderer.java     |  2 +-
 .../component/SelectManyListTagDeclaration.java    | 20 +++-----
 .../component/SelectOneListTagDeclaration.java     |  1 -
 .../selectManyList/selectManyListAjax.html         |  6 +--
 .../renderer/selectOneList/selectOneListAjax.html  |  6 +--
 .../80-selectManyList/SelectManyList.test.js       | 14 +++---
 .../content/900-test/6000-event/Event.test.js      | 30 ++++++++++++
 .../webapp/content/900-test/6000-event/Event.xhtml |  2 +-
 .../6000-event/x-event-selectOneList.xhtml         | 55 ++++++++++++++++++++++
 .../src/main/ts/tobago-select-list-base.ts         | 22 ++++++++-
 .../src/main/ts/tobago-select-many-list.ts         | 12 ++++-
 .../src/main/ts/tobago-select-one-list.ts          | 12 +++--
 17 files changed, 165 insertions(+), 71 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/SupportFieldId.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/SupportFieldId.java
index df60a8bbff..fbacd787ac 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/SupportFieldId.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/SupportFieldId.java
@@ -24,8 +24,4 @@ import javax.faces.context.FacesContext;
 public interface SupportFieldId {
 
   String getFieldId(FacesContext facesContext);
-
-  default String getFieldIdForBehavior(FacesContext facesContext) {
-    return getFieldId(facesContext);
-  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyList.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyList.java
index 5a5164d188..66dfa52d89 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyList.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyList.java
@@ -36,8 +36,8 @@ import java.util.Collection;
  * {@link SelectManyListTagDeclaration}
  */
 public abstract class AbstractUISelectManyList extends AbstractUISelectManyBase
-  implements SupportsAutoSpacing, Visual, SupportsLabelLayout, ClientBehaviorHolder, SupportsHelp, SupportFieldId,
-  SupportsFilter {
+    implements SupportsAutoSpacing, Visual, SupportsLabelLayout, ClientBehaviorHolder, SupportsHelp, SupportFieldId,
+    SupportsFilter {
 
   private transient boolean nextToRenderIsLabel;
 
@@ -56,11 +56,6 @@ public abstract class AbstractUISelectManyList extends AbstractUISelectManyBase
     return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "field";
   }
 
-  @Override
-  public String getFieldIdForBehavior(FacesContext facesContext) {
-    return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "selected";
-  }
-
   public abstract Integer getTabIndex();
 
   public abstract boolean isDisabled();
@@ -69,8 +64,7 @@ public abstract class AbstractUISelectManyList extends AbstractUISelectManyBase
 
   public boolean isError() {
     final FacesContext facesContext = FacesContext.getCurrentInstance();
-    return !isValid()
-      || !facesContext.getMessageList(getClientId(facesContext)).isEmpty();
+    return !isValid() || !facesContext.getMessageList(getClientId(facesContext)).isEmpty();
   }
 
   public abstract boolean isFocus();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneList.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneList.java
index 118c623ca5..9c65a9c565 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneList.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneList.java
@@ -31,11 +31,7 @@ public abstract class AbstractUISelectOneList extends AbstractUISelectOneBase im
     return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "field";
   }
 
-  @Override
-  public String getFieldIdForBehavior(FacesContext facesContext) {
-    return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "selected";
-  }
-
   public abstract String getFilter();
+
   public abstract boolean isExpanded();
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyListRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyListRenderer.java
index 6bfaa3dc28..5b4aa562e8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyListRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyListRenderer.java
@@ -70,8 +70,8 @@ public class SelectManyListRenderer<T extends AbstractUISelectManyList> extends
 
     final String clientId = component.getClientId(facesContext);
     final String fieldId = component.getFieldId(facesContext);
+    final String selectFieldId = clientId + ComponentUtils.SUB_SEPARATOR + "selectField";
     final String filterId = clientId + ComponentUtils.SUB_SEPARATOR + "filter";
-    final String selectedId = clientId + ComponentUtils.SUB_SEPARATOR + "selected";
     final List<SelectItem> items = SelectItemUtils.getItemList(facesContext, component);
     final boolean disabled = !items.iterator().hasNext() || component.isDisabled() || component.isReadonly();
     final String filter = component.getFilter();
@@ -79,7 +79,7 @@ public class SelectManyListRenderer<T extends AbstractUISelectManyList> extends
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
     final Integer tabIndex = component.getTabIndex();
 
-    encodeHiddenSelect(facesContext, component, items, clientId, selectedId, disabled);
+    encodeHiddenSelect(facesContext, component, items, clientId, fieldId, disabled);
 
     writer.startElement(HtmlElements.DIV);
     writer.writeClassAttribute(
@@ -87,7 +87,7 @@ public class SelectManyListRenderer<T extends AbstractUISelectManyList> extends
         expanded ? BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(component)) : null);
 
     encodeSelectField(facesContext, component,
-        clientId, fieldId, filterId, filter, disabled, expanded, title, tabIndex);
+        clientId, selectFieldId, filterId, filter, disabled, expanded, title, tabIndex);
     encodeOptions(facesContext, component, items, clientId, expanded, disabled);
 
     writer.endElement(HtmlElements.DIV);
@@ -102,11 +102,11 @@ public class SelectManyListRenderer<T extends AbstractUISelectManyList> extends
 
   private void encodeHiddenSelect(
       final FacesContext facesContext, final T component, final List<SelectItem> items,
-      final String clientId, final String selectedId, final boolean disabled) throws IOException {
+      final String clientId, final String fieldId, final boolean disabled) throws IOException {
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
 
     writer.startElement(HtmlElements.SELECT);
-    writer.writeIdAttribute(selectedId);
+    writer.writeIdAttribute(fieldId);
     writer.writeNameAttribute(clientId);
     writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
     writer.writeAttribute(HtmlAttributes.REQUIRED, component.isRequired());
@@ -121,12 +121,12 @@ public class SelectManyListRenderer<T extends AbstractUISelectManyList> extends
 
   private void encodeSelectField(
       final FacesContext facesContext, final T component,
-      final String clientId, final String fieldId, final String filterId, final String filter, final boolean disabled,
-      final boolean expanded, final String title, final Integer tabIndex) throws IOException {
+      final String clientId, final String selectFieldId, final String filterId, final String filter,
+      final boolean disabled, final boolean expanded, final String title, final Integer tabIndex) throws IOException {
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
 
     writer.startElement(HtmlElements.DIV);
-    writer.writeIdAttribute(fieldId);
+    writer.writeIdAttribute(selectFieldId);
     writer.writeNameAttribute(clientId);
     HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
     writer.writeClassAttribute(
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectOneListRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectOneListRenderer.java
index dc6cbb4997..c24f4e394e 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectOneListRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectOneListRenderer.java
@@ -48,8 +48,8 @@ public class SelectOneListRenderer<T extends AbstractUISelectOneList> extends Se
 
     final String clientId = component.getClientId(facesContext);
     final String fieldId = component.getFieldId(facesContext);
+    final String selectFieldId = clientId + ComponentUtils.SUB_SEPARATOR + "selectField";
     final String filterId = clientId + ComponentUtils.SUB_SEPARATOR + "filter";
-    final String selectedId = clientId + ComponentUtils.SUB_SEPARATOR + "selected";
     final List<SelectItem> items = SelectItemUtils.getItemList(facesContext, component);
     final boolean disabled = !items.iterator().hasNext() || component.isDisabled() || component.isReadonly();
     final String filter = component.getFilter();
@@ -57,7 +57,7 @@ public class SelectOneListRenderer<T extends AbstractUISelectOneList> extends Se
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
     final Integer tabIndex = component.getTabIndex();
 
-    encodeHiddenSelect(facesContext, component, items, clientId, selectedId, disabled);
+    encodeHiddenSelect(facesContext, component, items, clientId, fieldId, disabled);
 
     writer.startElement(HtmlElements.DIV);
     writer.writeClassAttribute(
@@ -65,7 +65,7 @@ public class SelectOneListRenderer<T extends AbstractUISelectOneList> extends Se
         expanded ? BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(component)) : null);
 
     encodeSelectField(facesContext, component,
-        clientId, fieldId, filterId, filter, disabled, expanded, title, tabIndex);
+        clientId, selectFieldId, filterId, filter, disabled, expanded, title, tabIndex);
     encodeOptions(facesContext, component, items, clientId, expanded, disabled);
 
     writer.endElement(HtmlElements.DIV);
@@ -73,11 +73,11 @@ public class SelectOneListRenderer<T extends AbstractUISelectOneList> extends Se
 
   private void encodeHiddenSelect(
       final FacesContext facesContext, final T component, final List<SelectItem> items,
-      final String clientId, final String selectedId, final boolean disabled) throws IOException {
+      final String clientId, final String fieldId, final boolean disabled) throws IOException {
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
 
     writer.startElement(HtmlElements.SELECT);
-    writer.writeIdAttribute(selectedId);
+    writer.writeIdAttribute(fieldId);
     writer.writeNameAttribute(clientId);
     writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
     writer.writeAttribute(HtmlAttributes.REQUIRED, component.isRequired());
@@ -90,12 +90,12 @@ public class SelectOneListRenderer<T extends AbstractUISelectOneList> extends Se
 
   private void encodeSelectField(
       final FacesContext facesContext, final T component,
-      final String clientId, final String fieldId, final String filterId, final String filter, final boolean disabled,
-      final boolean expanded, final String title, final Integer tabIndex) throws IOException {
+      final String clientId, final String selectFieldId, final String filterId, final String filter,
+      final boolean disabled, final boolean expanded, final String title, final Integer tabIndex) throws IOException {
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
 
     writer.startElement(HtmlElements.DIV);
-    writer.writeIdAttribute(fieldId);
+    writer.writeIdAttribute(selectFieldId);
     writer.writeNameAttribute(clientId);
     HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
     writer.writeClassAttribute(
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java
index 95aa7a2559..d2477683c1 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java
@@ -86,7 +86,7 @@ public class TobagoClientBehaviorRenderer extends javax.faces.render.ClientBehav
       final Collection<String> render = ajaxBehavior.getRender();
       clientId = uiComponent.getClientId(facesContext);
       if (uiComponent instanceof SupportFieldId) {
-        fieldId = ((SupportFieldId) uiComponent).getFieldIdForBehavior(facesContext);
+        fieldId = ((SupportFieldId) uiComponent).getFieldId(facesContext);
       }
 
       executeIds = ComponentUtils.evaluateClientIds(facesContext, uiComponent, execute.toArray(new String[0]));
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyListTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyListTagDeclaration.java
index 9a45cbb624..2ada75a058 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyListTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectManyListTagDeclaration.java
@@ -31,12 +31,12 @@ import org.apache.myfaces.tobago.internal.taglib.declaration.HasAutoSpacing;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasBinding;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasConverter;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasConverterMessage;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasDecorationPosition;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasFilter;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasHelp;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasId;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasLabel;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasLabelLayout;
-import org.apache.myfaces.tobago.internal.taglib.declaration.HasDecorationPosition;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasRequiredMessageForSelect;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasTabIndex;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
@@ -65,19 +65,11 @@ import javax.faces.component.UISelectMany;
     rendererType = RendererTypes.SELECT_MANY_LIST,
     allowedChildComponenents = {"javax.faces.SelectItem", "javax.faces.SelectItems"},
     behaviors = {
-        @Behavior(
-            name = ClientBehaviors.CHANGE,
-            isDefault = true),
-        @Behavior(
-            name = ClientBehaviors.INPUT),
-        @Behavior(
-            name = ClientBehaviors.CLICK),
-        @Behavior(
-            name = ClientBehaviors.DBLCLICK),
-        @Behavior(
-            name = ClientBehaviors.FOCUS),
-        @Behavior(
-            name = ClientBehaviors.BLUR)
+        @Behavior(name = ClientBehaviors.CHANGE, isDefault = true),
+        @Behavior(name = ClientBehaviors.CLICK),
+        @Behavior(name = ClientBehaviors.DBLCLICK),
+        @Behavior(name = ClientBehaviors.FOCUS),
+        @Behavior(name = ClientBehaviors.BLUR)
     })
 
 public interface SelectManyListTagDeclaration
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneListTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneListTagDeclaration.java
index eceb39fc85..3d171634a7 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneListTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SelectOneListTagDeclaration.java
@@ -65,7 +65,6 @@ import javax.faces.component.UISelectMany;
     allowedChildComponenents = {"javax.faces.SelectItem", "javax.faces.SelectItems"},
     behaviors = {
         @Behavior(name = ClientBehaviors.CHANGE, isDefault = true),
-        @Behavior(name = ClientBehaviors.INPUT),
         @Behavior(name = ClientBehaviors.CLICK),
         @Behavior(name = ClientBehaviors.DBLCLICK),
         @Behavior(name = ClientBehaviors.FOCUS),
diff --git a/tobago-core/src/test/resources/renderer/selectManyList/selectManyListAjax.html b/tobago-core/src/test/resources/renderer/selectManyList/selectManyListAjax.html
index a68d057a0c..295f2a96aa 100644
--- a/tobago-core/src/test/resources/renderer/selectManyList/selectManyListAjax.html
+++ b/tobago-core/src/test/resources/renderer/selectManyList/selectManyListAjax.html
@@ -16,13 +16,13 @@
 -->
 
 <tobago-select-many-list id='id' class='tobago-auto-spacing'>
-  <select id='id::selected' name='id' class='d-none' multiple='multiple'>
+  <select id='id::field' name='id' class='d-none' multiple='multiple'>
     <option value='Stratocaster'>Stratocaster
     </option>
     <option value='Telecaster'>Telecaster
     </option></select>
   <div class='dropdown'>
-    <div id='id::field' name='id' class='form-select tobago-select-field dropdown-toggle' aria-expanded='false'>
+    <div id='id::selectField' name='id' class='form-select tobago-select-field dropdown-toggle' aria-expanded='false'>
       <input type='search' id='id::filter' class='tobago-filter form-control' autocomplete='off' readonly='readonly'>
     </div>
     <div class='tobago-options tobago-dropdown-menu' name='id'>
@@ -46,5 +46,5 @@
       </table>
     </div>
   </div>
-  <tobago-behavior event='change' client-id='id' field-id='id::selected' execute='id'></tobago-behavior>
+  <tobago-behavior event='change' client-id='id' field-id='id::field' execute='id'></tobago-behavior>
 </tobago-select-many-list>
diff --git a/tobago-core/src/test/resources/renderer/selectOneList/selectOneListAjax.html b/tobago-core/src/test/resources/renderer/selectOneList/selectOneListAjax.html
index 79ebbf73f7..148808fa04 100644
--- a/tobago-core/src/test/resources/renderer/selectOneList/selectOneListAjax.html
+++ b/tobago-core/src/test/resources/renderer/selectOneList/selectOneListAjax.html
@@ -16,13 +16,13 @@
 -->
 
 <tobago-select-one-list id='id' class='tobago-auto-spacing'>
-  <select id='id::selected' name='id' class='d-none'>
+  <select id='id::field' name='id' class='d-none'>
     <option value='Stratocaster'>Stratocaster
     </option>
     <option value='Telecaster'>Telecaster
     </option></select>
   <div class='dropdown'>
-    <div id='id::field' name='id' class='form-select tobago-select-field dropdown-toggle' aria-expanded='false'>
+    <div id='id::selectField' name='id' class='form-select tobago-select-field dropdown-toggle' aria-expanded='false'>
       <span></span>
       <input type='search' id='id::filter' class='tobago-filter form-control' autocomplete='off' readonly='readonly'>
     </div>
@@ -47,5 +47,5 @@
       </table>
     </div>
   </div>
-  <tobago-behavior event='change' client-id='id' field-id='id::selected' execute='id'></tobago-behavior>
+  <tobago-behavior event='change' client-id='id' field-id='id::field' execute='id'></tobago-behavior>
 </tobago-select-one-list>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/030-select/80-selectManyList/SelectManyList.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/030-select/80-selectManyList/SelectManyList.test.js
index e980348b78..ed037558c7 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/030-select/80-selectManyList/SelectManyList.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/030-select/80-selectManyList/SelectManyList.test.js
@@ -19,14 +19,14 @@ import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
 import {elementByIdFn, querySelectorAllFn, querySelectorFn} from "/script/tobago-test.js";
 
 it("Standard: remove 'Earth'", function (done) {
-  const removeEarthButtonFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:field .btn-group[data-tobago-value='Earth'] .tobago-button");
-  const badgeVenusFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:field .btn-group[data-tobago-value='Venus']");
-  const badgeEarthFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:field .btn-group[data-tobago-value='Earth']");
-  const badgeJupiterFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:field .btn-group[data-tobago-value='Jupiter']");
+  const removeEarthButtonFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:selectField .btn-group[data-tobago-value='Earth'] .tobago-button");
+  const badgeVenusFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:selectField .btn-group[data-tobago-value='Venus']");
+  const badgeEarthFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:selectField .btn-group[data-tobago-value='Earth']");
+  const badgeJupiterFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedStandard\\:\\:selectField .btn-group[data-tobago-value='Jupiter']");
   const selectedRowsFn = querySelectorAllFn("#page\\:mainForm\\:basic\\:selectedStandard tr.table-primary");
-  const disabledBadgeVenusFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedDisabled\\:\\:field .btn-group[data-tobago-value='Venus']");
-  const disabledBadgeEarthFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedDisabled\\:\\:field .btn-group[data-tobago-value='Earth']");
-  const disabledBadgeJupiterFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedDisabled\\:\\:field .btn-group[data-tobago-value='Jupiter']");
+  const disabledBadgeVenusFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedDisabled\\:\\:selectField .btn-group[data-tobago-value='Venus']");
+  const disabledBadgeEarthFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedDisabled\\:\\:selectField .btn-group[data-tobago-value='Earth']");
+  const disabledBadgeJupiterFn = querySelectorFn("#page\\:mainForm\\:basic\\:selectedDisabled\\:\\:selectField .btn-group[data-tobago-value='Jupiter']");
   const submitFn = elementByIdFn("page:mainForm:basic:submit");
   const resetFn = elementByIdFn("page:mainForm:basic:reset");
 
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.test.js
index 8bd1a4ecc3..ad43cba9cd 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.test.js
@@ -71,6 +71,28 @@ it("tc:selectBooleanCheckbox", function (done) {
   test.start();
 });
 
+it("tc:selectOneList", function (done) {
+  const eventNames = ["change", "focus", "blur"];
+  const eventComponentFn = elementByIdFn("page:mainForm:selectOneListevent::field");
+  const ajaxComponentFn = elementByIdFn("page:mainForm:selectOneListajax::field");
+
+  const clickEventNames = ["click", "dblclick"];
+  const clickEventComponentFn = elementByIdFn("page:mainForm:selectOneListevent::selectField");
+
+  const changeValueFn = function (componentFn) {
+    if (componentFn().closest("tobago-select-one-list").querySelectorAll("option")[0].selected) {
+      componentFn().closest("tobago-select-one-list").querySelectorAll("option")[1].selected = true;
+    } else {
+      componentFn().closest("tobago-select-one-list").querySelectorAll("option")[0].selected = true;
+    }
+  };
+
+  const test = new JasmineTestTool(done);
+  createSteps(test, "selectOneList", eventNames, eventComponentFn, ajaxComponentFn, changeValueFn);
+  createSteps(test, "selectOneList", clickEventNames, clickEventComponentFn, ajaxComponentFn, changeValueFn);
+  test.start();
+});
+
 it("tc:selectOneListbox", function (done) {
   const eventNames = ["change", "click", "dblclick", "focus", "blur"];
   const eventComponentFn = elementByIdFn("page:mainForm:selectOneListboxevent::field");
@@ -109,6 +131,7 @@ it("tc:textarea", function (done) {
 });
 
 function createSteps(test, componentName, eventNames, eventComponentFn, ajaxComponentFn, changeValueFn) {
+  const resetButtonFn = elementByIdFn("page:mainForm:reset");
   const actionCountFn = elementByIdFn("page:mainForm:inAction::field");
   const actionListenerCountFn = elementByIdFn("page:mainForm:inActionListener::field");
   const ajaxListenerCountFn = elementByIdFn("page:mainForm:inAjaxListener::field");
@@ -138,6 +161,13 @@ function createSteps(test, componentName, eventNames, eventComponentFn, ajaxComp
     test.do(() => oldTimestamp = parseInt(timestampFn().value));
     test.event("click", selectorButton, () => parseInt(timestampFn().value) > oldTimestamp);
 
+    test.setup(() =>
+            parseInt(actionCountFn().value)
+            + parseInt(actionListenerCountFn().value)
+            + parseInt(ajaxListenerCountFn().value)
+            + parseInt(valueChangeListenerCountFn().value) === 0,
+        null, "click", resetButtonFn);
+
     test.do(() => oldActionCount = parseInt(actionCountFn().value));
     test.do(() => oldActionListenerCount = parseInt(actionListenerCountFn().value));
     test.do(() => oldAjaxListenerCount = parseInt(ajaxListenerCountFn().value));
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.xhtml
index 396f0139d9..6d9ae69bc2 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/Event.xhtml
@@ -27,7 +27,7 @@
 
   <tc:box id="metrics" label="Metrics">
     <f:facet name="bar">
-      <tc:button label="Reset" action="#{eventController.reset}"/>
+      <tc:button id="reset" label="Reset" action="#{eventController.reset}"/>
     </f:facet>
     <tc:in id="inAction" label="Action" value="#{eventController.actionCount}" readonly="true" focus="true"/>
     <tc:in id="inActionListener" label="ActionListener" value="#{eventController.actionListenerCount}"
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/x-event-selectOneList.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/x-event-selectOneList.xhtml
new file mode 100644
index 0000000000..9cb862cb4e
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/6000-event/x-event-selectOneList.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+-->
+
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
+                xmlns:f="http://xmlns.jcp.org/jsf/core"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
+  <tc:selectOneList id="selectOneList#{eventType}" valueChangeListener="#{eventController.valueChangeListener}" expanded="true">
+    <tc:selectItem itemValue="alpha" itemLabel="Alpha"/>
+    <tc:selectItem itemValue="beta" itemLabel="Beta"/>
+    <tc:selectItem itemValue="gamma" itemLabel="Gamma"/>
+    <tc:selectItem itemValue="delta" itemLabel="Delta"/>
+    <c:if test="#{eventType == 'ajax'}">
+      <f:ajax event="change" render="metrics" listener="#{eventController.ajaxListener}"
+              disabled="#{eventName != 'change'}"/>
+      <f:ajax event="click" render="metrics" listener="#{eventController.ajaxListener}"
+              disabled="#{eventName != 'click'}"/>
+      <f:ajax event="dblclick" render="metrics" listener="#{eventController.ajaxListener}"
+              disabled="#{eventName != 'dblclick'}"/>
+      <f:ajax event="focus" render="metrics" listener="#{eventController.ajaxListener}"
+              disabled="#{eventName != 'focus'}"/>
+      <f:ajax event="blur" render="metrics" listener="#{eventController.ajaxListener}"
+              disabled="#{eventName != 'blur'}"/>
+    </c:if>
+    <c:if test="#{eventType == 'event'}">
+      <tc:event event="change" action="#{eventController.action}" actionListener="#{eventController.actionListener}"
+                rendered="#{eventName == 'change'}"/>
+      <tc:event event="click" action="#{eventController.action}" actionListener="#{eventController.actionListener}"
+                rendered="#{eventName == 'click'}"/>
+      <tc:event event="dblclick" action="#{eventController.action}" actionListener="#{eventController.actionListener}"
+                rendered="#{eventName == 'dblclick'}"/>
+      <tc:event event="focus" action="#{eventController.action}" actionListener="#{eventController.actionListener}"
+                rendered="#{eventName == 'focus'}"/>
+      <tc:event event="blur" action="#{eventController.action}" actionListener="#{eventController.actionListener}"
+                rendered="#{eventName == 'blur'}"/>
+    </c:if>
+  </tc:selectOneList>
+</ui:composition>
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts
index a4117e55de..20ab4f6ce3 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts
@@ -32,10 +32,12 @@ export abstract class SelectListBase extends HTMLElement {
   }
 
   set focused(focused: boolean) {
-    if (focused) {
+    if (focused && !this.focused) {
       this.classList.add(Css.TOBAGO_FOCUS);
-    } else {
+      this.hiddenSelect.dispatchEvent(new Event("focus", {bubbles: true}));
+    } else if (!focused && this.focused) {
       this.classList.remove(Css.TOBAGO_FOCUS);
+      this.hiddenSelect.dispatchEvent(new Event("blur", {bubbles: true}));
     }
   }
 
@@ -96,6 +98,7 @@ export abstract class SelectListBase extends HTMLElement {
       window.addEventListener("resize", () => this.updateDropdownMenuWidth());
     }
     document.addEventListener("click", this.globalClickEvent.bind(this));
+    this.hiddenSelect.addEventListener("click", this.labelClickEvent.bind(this));
     this.selectField.addEventListener("keydown", this.keydownEventBase.bind(this));
     this.filterInput.addEventListener("focus", this.focusEvent.bind(this));
     this.filterInput.addEventListener("blur", this.blurEvent.bind(this));
@@ -110,10 +113,25 @@ export abstract class SelectListBase extends HTMLElement {
     if (document.activeElement.id === this.filterInput.id) {
       this.focusEvent();
     }
+
+    // redirect click events for ajax behavior
+    this.selectField.addEventListener("click", () => this.hiddenSelect.dispatchEvent(new Event("click")));
+    this.options.addEventListener("click", () => this.hiddenSelect.dispatchEvent(new Event("click")));
+    this.selectField.addEventListener("dblclick", () => this.hiddenSelect.dispatchEvent(new Event("dblclick")));
+    this.options.addEventListener("dblclick", () => this.hiddenSelect.dispatchEvent(new Event("dblclick")));
   }
 
   protected abstract globalClickEvent(event: MouseEvent): void;
 
+  /**
+   * The label for attribute targets the hidden select field, so an event which is dispatched on the label is also
+   * dispatched on the hidden select field. This method must ensure that a click on a label focus the
+   * Select[One/Many]List component.
+   * @param event
+   * @protected
+   */
+  protected abstract labelClickEvent(event: MouseEvent): void;
+
   private keydownEventBase(event: KeyboardEvent) {
     switch (event.key) {
       case Key.ESCAPE:
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
index a13aa46026..c89ddd4fd3 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
@@ -57,13 +57,21 @@ class SelectManyList extends SelectListBase {
     }
   }
 
+  protected labelClickEvent(event: MouseEvent): void {
+    event.stopPropagation(); // stop propagation to avoid execution of globalClickEvent()
+    if (!this.filterInput.disabled) {
+      this.filterInput.focus();
+    } else if (this.badgeCloseButtons.length > 0) {
+      this.badgeCloseButtons[0].focus();
+    }
+  }
+
   protected select(row: HTMLTableRowElement): void {
     const itemValue = row.dataset.tobagoValue;
     const option: HTMLOptionElement = this.hiddenSelect.querySelector(`[value="${itemValue}"]`);
     option.selected = !option.selected;
     this.sync(option);
-    const e = new Event("change");
-    this.hiddenSelect.dispatchEvent(e);
+    this.hiddenSelect.dispatchEvent(new Event("change", {bubbles: true}));
   }
 
   private sync(option: HTMLOptionElement) {
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts
index 07389aaf34..ed72a2d149 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts
@@ -32,7 +32,7 @@ class SelectOneList extends SelectListBase {
     this.selectField.querySelector("span").textContent = text;
   }
 
-  get selectedOption() :HTMLOptionElement {
+  get selectedOption(): HTMLOptionElement {
     const value = this.hiddenSelect.value;
     return this.hiddenSelect.querySelector(`[value="${value}"]`);
   }
@@ -60,6 +60,13 @@ class SelectOneList extends SelectListBase {
     }
   }
 
+  protected labelClickEvent(event: MouseEvent): void {
+    event.stopPropagation(); // stop propagation to avoid execution of globalClickEvent()
+    if (!this.filterInput.disabled) {
+      this.filterInput.focus();
+    }
+  }
+
   private keydownEvent(event: KeyboardEvent) {
     switch (event.key) {
       case Key.ESCAPE:
@@ -83,8 +90,7 @@ class SelectOneList extends SelectListBase {
     option.selected = true;
     this.filterInput.value = null;
     this.sync();
-    const e = new Event("change");
-    this.hiddenSelect.dispatchEvent(e);
+    this.hiddenSelect.dispatchEvent(new Event("change", {bubbles: true}));
   }
 
   private sync() {