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 2019/09/19 11:59:10 UTC

[myfaces-tobago] branch master updated (089ad5e -> d9e00f5)

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

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


    from 089ad5e  typo
     new 61fb781  add test for tree commands
     new d9e00f5  tobago-tree: custom elements

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../renderkit/renderer/TreeIndentRenderer.java     |   4 +-
 .../renderkit/renderer/TreeNodeRenderer.java       |  17 +-
 .../internal/renderkit/renderer/TreeRenderer.java  |  11 +-
 .../renderkit/renderer/TreeSelectRenderer.java     |  11 +-
 .../tobago/renderkit/html/CustomAttributes.java    |   4 +
 .../tobago/renderkit/html/DataAttributes.java      |   3 +
 .../tobago/renderkit/html/HtmlElements.java        |   5 +-
 tobago-core/src/main/resources/scss/_tobago.scss   |  16 +-
 .../myfaces/tobago/model/SelectableUnitTest.java   |  44 +++
 .../090-tree/00-command/Tree_Command_Types.test.js |  60 +++
 .../090-tree/00-command/Tree_Command_Types.xhtml   |  14 +-
 .../090-tree/01-select/Tree_Select.test.js         |  10 +-
 .../src/main/npm/ts/tobago-selectable.ts           |  11 +
 .../src/main/npm/ts/tobago-tree.ts                 | 410 ++++++++++++---------
 14 files changed, 405 insertions(+), 215 deletions(-)
 create mode 100644 tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.test.js
 create mode 100644 tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-selectable.ts


[myfaces-tobago] 02/02: tobago-tree: custom elements

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d9e00f586b8bd50c2ebc5fa4494eb0a9113ef009
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Thu Sep 19 13:58:58 2019 +0200

    tobago-tree: custom elements
    
    * custom elements: tobago-tree, tobago-tree-node and tobago-tree-select
    * adjust test (currently test for tree-select single fail)
    * add Selectable enum with test
    
    issue: TOBAGO-1633: TS refactoring
---
 .../renderkit/renderer/TreeIndentRenderer.java     |   4 +-
 .../renderkit/renderer/TreeNodeRenderer.java       |  17 +-
 .../internal/renderkit/renderer/TreeRenderer.java  |  11 +-
 .../renderkit/renderer/TreeSelectRenderer.java     |  11 +-
 .../tobago/renderkit/html/CustomAttributes.java    |   4 +
 .../tobago/renderkit/html/DataAttributes.java      |   3 +
 .../tobago/renderkit/html/HtmlElements.java        |   5 +-
 tobago-core/src/main/resources/scss/_tobago.scss   |  16 +-
 .../myfaces/tobago/model/SelectableUnitTest.java   |  44 +++
 .../090-tree/01-select/Tree_Select.test.js         |  10 +-
 .../src/main/npm/ts/tobago-selectable.ts           |  11 +
 .../src/main/npm/ts/tobago-tree.ts                 | 410 ++++++++++++---------
 12 files changed, 337 insertions(+), 209 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIndentRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIndentRenderer.java
index ce63cae..5e64307 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIndentRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIndentRenderer.java
@@ -54,9 +54,7 @@ public class TreeIndentRenderer extends RendererBase {
     }
 
     final boolean folder = node.isFolder();
-    final int level = node.getLevel();
     final boolean showJunctions = treeIndent.isShowJunctions();
-    final boolean showRootJunction = data.isShowRootJunction();
     final boolean expanded = folder && data.getExpandedState().isExpanded(node.getPath());
 
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
@@ -70,7 +68,7 @@ public class TreeIndentRenderer extends RendererBase {
         treeIndent.getCustomClass());
 
     // encode tree junction
-    if (!showJunctions || !showRootJunction && level == 0) {
+    if (!showJunctions) {
       return;
     }
     writer.startElement(HtmlElements.I);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java
index 8d65b5b..2569227 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java
@@ -33,6 +33,7 @@ import org.apache.myfaces.tobago.model.TreePath;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
+import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
@@ -84,7 +85,7 @@ public class TreeNodeRenderer extends RendererBase {
 
       // select
       if (data.getSelectable() != Selectable.none) { // selection
-         String selected = requestParameterMap.get(
+        String selected = requestParameterMap.get(
             clientId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_SELECTED);
 // todo        JsonUtils.decodeIntegerArray()StringArray()
         selected = selected.replaceAll("\\[", ";");
@@ -128,7 +129,9 @@ public class TreeNodeRenderer extends RendererBase {
     Markup markup = Markup.NULL;
     final TreePath path = node.getPath();
     final SelectedState selectedState = data.getSelectedState();
-    if (data instanceof AbstractUITree && selectedState.isSelected(path)) {
+    final boolean selected = data instanceof AbstractUITree && selectedState.isSelected(path);
+
+    if (selected) {
       markup = markup.add(Markup.SELECTED);
     }
     if (folder) {
@@ -149,7 +152,7 @@ public class TreeNodeRenderer extends RendererBase {
       writer.writeAttribute(HtmlAttributes.SELECTED, selectedState.isAncestorOfSelected(path));
       writer.writeAttribute(DataAttributes.ROW_INDEX, data.getRowIndex());
     } else {
-      writer.startElement(HtmlElements.DIV);
+      writer.startElement(HtmlElements.TOBAGO_TREE_NODE);
 
       // div id
       writer.writeIdAttribute(clientId);
@@ -158,16 +161,20 @@ public class TreeNodeRenderer extends RendererBase {
       final boolean hidden = !dataRendersRowContainer && !visible;
 
       writer.writeClassAttribute(
-          TobagoClass.TREE_NODE,
+          null,
           TobagoClass.TREE_NODE.createMarkup(markup),
           hidden ? BootstrapClass.D_NONE : null,
           node.getCustomClass());
+      writer.writeAttribute(CustomAttributes.SELECTED, selected);
+      writer.writeAttribute(CustomAttributes.EXPANDABLE, folder);
+      writer.writeAttribute(CustomAttributes.INDEX, data.getRowIndex());
       HtmlRendererUtils.writeDataAttributes(facesContext, writer, node);
       if (parentId != null) {
         // TODO: replace with
         // todo writer.writeIdAttribute(parentId + SUB_SEPARATOR + AbstractUITree.SUFFIX_PARENT);
         // todo like in TreeListboxRenderer
         writer.writeAttribute(DataAttributes.TREE_PARENT, parentId, false);
+        writer.writeAttribute(CustomAttributes.PARENT, parentId, false);
       }
       writer.writeAttribute(DataAttributes.LEVEL, data.isShowRoot() ? node.getLevel() : node.getLevel() - 1);
     }
@@ -189,7 +196,7 @@ public class TreeNodeRenderer extends RendererBase {
       }
       writer.endElement(HtmlElements.OPTION);
     } else {
-      writer.endElement(HtmlElements.DIV);
+      writer.endElement(HtmlElements.TOBAGO_TREE_NODE);
     }
   }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java
index 382cfa5..81daec0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java
@@ -33,6 +33,7 @@ import org.apache.myfaces.tobago.model.SelectedState;
 import org.apache.myfaces.tobago.model.TreePath;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
+import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
@@ -95,18 +96,18 @@ public class TreeRenderer extends RendererBase {
       return;
     }
 
-    writer.startElement(HtmlElements.DIV);
+    writer.startElement(HtmlElements.TOBAGO_TREE);
     writer.writeIdAttribute(clientId);
     writer.writeClassAttribute(
-        TobagoClass.TREE,
-        TobagoClass.TREE.createMarkup(markup),
-        tree.getCustomClass());
+        tree.getCustomClass(),
+        TobagoClass.TREE.createMarkup(markup));
     HtmlRendererUtils.writeDataAttributes(facesContext, writer, tree);
     writer.writeAttribute(DataAttributes.SCROLL_PANEL, Boolean.TRUE.toString(), false);
 
     final Selectable selectable = tree.getSelectable();
     if (selectable.isSupportedByTree()) {
       writer.writeAttribute(DataAttributes.SELECTABLE, selectable.name(), false);
+      writer.writeAttribute(CustomAttributes.SELECTABLE, selectable.name(), false);
     }
 
     final SelectedState selectedState = tree.getSelectedState();
@@ -168,6 +169,6 @@ public class TreeRenderer extends RendererBase {
     writer.writeAttribute(DataAttributes.SCROLL_POSITION, Boolean.TRUE.toString(), false);
     writer.endElement(HtmlElements.INPUT);
 
-    writer.endElement(HtmlElements.DIV);
+    writer.endElement(HtmlElements.TOBAGO_TREE);
   }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeSelectRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeSelectRenderer.java
index 54b7dfd..6c6321b 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeSelectRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeSelectRenderer.java
@@ -51,6 +51,8 @@ public class TreeSelectRenderer extends RendererBase {
   @Override
   public void decode(final FacesContext facesContext, final UIComponent component) {
 
+    // TODO do we need this?
+
     final AbstractUITreeSelect select = (AbstractUITreeSelect) component;
     final AbstractUITreeNodeBase node = ComponentUtils.findAncestor(select, AbstractUITreeNodeBase.class);
     final AbstractUIData data = ComponentUtils.findAncestor(node, AbstractUIData.class);
@@ -105,12 +107,11 @@ public class TreeSelectRenderer extends RendererBase {
     final boolean folder = data.isFolder();
     final Selectable selectable = data.getSelectable();
 
-    writer.startElement(HtmlElements.SPAN);
+    writer.startElement(HtmlElements.TOBAGO_TREE_SELECT);
     final Markup markup = treeSelect.getMarkup();
     writer.writeClassAttribute(
-        TobagoClass.TREE_SELECT,
-        TobagoClass.TREE_SELECT.createMarkup(markup),
-        treeSelect.getCustomClass());
+        treeSelect.getCustomClass(),
+        TobagoClass.TREE_SELECT.createMarkup(markup));
     HtmlRendererUtils.writeDataAttributes(facesContext, writer, treeSelect);
 
     if (treeSelect.isShowCheckbox()
@@ -149,7 +150,7 @@ public class TreeSelectRenderer extends RendererBase {
       writer.endElement(HtmlElements.LABEL);
     }
 
-    writer.endElement(HtmlElements.SPAN);
+    writer.endElement(HtmlElements.TOBAGO_TREE_SELECT);
   }
 
   private String getClientIdWithoutRowIndex(final AbstractUIData data, final String id) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
index af6aade..4a975ce 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
@@ -31,6 +31,7 @@ public enum CustomAttributes implements MarkupLanguageAttributes {
    * &lt;f:ajax&gt; attribute
    */
   EXECUTE("execute"),
+  EXPANDABLE("expandable"),
   FOCUS_ID("focus-id"),
   /**
    * The index of the tab inside the tab group.
@@ -41,6 +42,9 @@ public enum CustomAttributes implements MarkupLanguageAttributes {
   MIN_CHARS("min-chars"),
   OMIT("omit"),
   ORIENTATION("orientation"),
+  PARENT("parent"),
+  SELECTABLE("selectable"),
+  SELECTED("selected"),
   /**
    * &lt;f:ajax&gt; attribute
    */
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
index bb8cdb6..c3b60e0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
@@ -139,7 +139,9 @@ public enum DataAttributes implements MarkupLanguageAttributes {
 
   /**
    * The selectable attribute e. g. for trees.
+   * @deprecated since 5.0.0, please use {@link CustomAttributes#SELECTABLE}
    */
+  @Deprecated
   SELECTABLE("data-tobago-selectable"),
 
   /**
@@ -174,6 +176,7 @@ public enum DataAttributes implements MarkupLanguageAttributes {
 
   /**
    * Id of the parent node in a tree node.
+   * @deprecated since 5.0.0, please use {@link CustomAttributes#PARENT}
    */
   TREE_PARENT("data-tobago-tree-parent"),
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
index 5394f3c..1294afa 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
@@ -143,7 +143,10 @@ public enum HtmlElements {
   TOBAGO_SUGGEST("tobago-suggest"),
   TOBAGO_TAB("tobago-tab"),
   TOBAGO_TAB_CONTENT("tobago-tab-content"),
-  TOBAGO_TAB_GROUP("tobago-tab-group");
+  TOBAGO_TAB_GROUP("tobago-tab-group"),
+  TOBAGO_TREE("tobago-tree"),
+  TOBAGO_TREE_NODE("tobago-tree-node"),
+  TOBAGO_TREE_SELECT("tobago-tree-select");
 
   private final String value;
   private final boolean voidElement;
diff --git a/tobago-core/src/main/resources/scss/_tobago.scss b/tobago-core/src/main/resources/scss/_tobago.scss
index 046b925..7d7ff77 100644
--- a/tobago-core/src/main/resources/scss/_tobago.scss
+++ b/tobago-core/src/main/resources/scss/_tobago.scss
@@ -1526,9 +1526,19 @@ tobago-tab-group {
   margin-left: 7rem;
 }
 
-@for $i from 0 through 20 {
-  .tobago-treeNode[data-tobago-level='#{$i}'] {
-    margin-left: #{$i}rem;
+tobago-tree {
+  tobago-tree-node {
+    display: block;
+
+    @for $i from 0 through 20 {
+      &[data-tobago-level='#{$i}'] {
+        margin-left: #{$i}rem;
+      }
+    }
+
+    tobago-tree-select {
+      display: inline;
+    }
   }
 }
 
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectableUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectableUnitTest.java
index e26ce79..dbed202 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectableUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectableUnitTest.java
@@ -20,12 +20,56 @@
 package org.apache.myfaces.tobago.model;
 
 import org.apache.myfaces.tobago.util.EnumUnitTest;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
 public class SelectableUnitTest extends EnumUnitTest {
 
   @Test
   public void testNames() throws IllegalAccessException, NoSuchFieldException {
     testNames(Selectable.class);
   }
+
+  @Test
+  public void testTypeScript() throws IOException {
+    final Path path = Paths.get("").toAbsolutePath().getParent().resolve(
+        Paths.get("tobago-theme", "tobago-theme-standard", "src", "main", "npm", "ts", "tobago-selectable.ts"));
+
+    final List<String> words = getWords(path);
+
+    for (Selectable selectable : Selectable.values()) {
+      Assertions.assertTrue(words.contains(selectable.name()),
+          selectable.name() + " should be found in tobago-selectable.ts");
+    }
+  }
+
+  private List<String> getWords(final Path path) throws IOException {
+    List<String> words = new ArrayList<>();
+
+    final String fileContent = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+
+
+    StringBuilder stringBuilder = new StringBuilder();
+
+    for (char c : fileContent.toCharArray()) {
+      if (('0' <= c && c <= '9')
+          || ('A' <= c && c <= 'Z')
+          || ('a' <= c && c <= 'z')) {
+        stringBuilder.append(c);
+      } else {
+        words.add(stringBuilder.toString());
+        stringBuilder = new StringBuilder();
+      }
+    }
+
+    return words;
+  }
 }
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/01-select/Tree_Select.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/01-select/Tree_Select.test.js
index 7b638ed..6592f4c 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/01-select/Tree_Select.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/01-select/Tree_Select.test.js
@@ -24,7 +24,7 @@ QUnit.test("single: select Music, select Mathematics", function (assert) {
   let outputFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectedNodesOutput span");
   let selectableNoneFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:0");
   let selectableSingleFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:1");
-  let inputFn = testFrameQuerySelectorFn(".tobago-treeSelect input");
+  let inputFn = testFrameQuerySelectorFn("tobago-tree-select input");
 
   let TTT = new TobagoTestTool(assert);
   TTT.action(function () {
@@ -68,7 +68,7 @@ QUnit.test("singleLeafOnly: select Classic, select Mathematics", function (asser
   let outputFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectedNodesOutput span");
   let selectableNoneFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:0");
   let selectableSingleLeafOnlyFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:2");
-  let inputFn = testFrameQuerySelectorFn(".tobago-treeSelect input");
+  let inputFn = testFrameQuerySelectorFn("tobago-tree-select input");
 
   let TTT = new TobagoTestTool(assert);
   TTT.action(function () {
@@ -112,7 +112,7 @@ QUnit.test("multi: select Music, select Mathematics, deselect Music", function (
   let outputFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectedNodesOutput span");
   let selectableNoneFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:0");
   let selectableMultiFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:3");
-  let inputFn = testFrameQuerySelectorFn(".tobago-treeSelect input");
+  let inputFn = testFrameQuerySelectorFn("tobago-tree-select input");
 
   let TTT = new TobagoTestTool(assert);
   TTT.action(function () {
@@ -164,7 +164,7 @@ QUnit.test("multiLeafOnly: select Classic, select Mathematics, deselect Classic"
   let outputFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectedNodesOutput span");
   let selectableNoneFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:0");
   let selectableMultiLeafOnlyFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:4");
-  let inputFn = testFrameQuerySelectorFn(".tobago-treeSelect input");
+  let inputFn = testFrameQuerySelectorFn("tobago-tree-select input");
 
   let TTT = new TobagoTestTool(assert);
   TTT.action(function () {
@@ -217,7 +217,7 @@ QUnit.test("multiCascade: select Music, select Mathematics, deselect Classic", f
   let outputFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectedNodesOutput span");
   let selectableNoneFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:0");
   let selectableMultiCascadeFn = testFrameQuerySelectorFn("#page\\:mainForm\\:selectable\\:\\:5");
-  let inputFn = testFrameQuerySelectorFn(".tobago-treeSelect input");
+  let inputFn = testFrameQuerySelectorFn("tobago-tree-select input");
 
   let TTT = new TobagoTestTool(assert);
   TTT.action(function () {
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-selectable.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-selectable.ts
new file mode 100644
index 0000000..16db2e2
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-selectable.ts
@@ -0,0 +1,11 @@
+export enum Selectable {
+  none, // Not selectable.
+  multi, // Multi selection possible. No other limitations.
+  single, // Only one item is selectable.
+  singleOrNone, // Only one of no item is selectable.
+  multiLeafOnly, // Only leafs are selectable.
+  singleLeafOnly, // Only one item is selectable and it must be a leaf.
+  sibling, // Only siblings are selectable.
+  siblingLeafOnly, // Only siblings are selectable and they have to be leafs.
+  multiCascade // Multi selection possible. When (de)selecting an item, the subtree will also be (un)selected.
+}
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts
index 0988f47..016f7ac 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts
@@ -15,223 +15,269 @@
  * limitations under the License.
  */
 
-import {Listener, Phase} from "./tobago-listener";
-import {DomUtils} from "./tobago-utils";
-
-class Tree {
-
-  static toggleNode = function (event: MouseEvent) {
-    const element = event.currentTarget as HTMLElement;
-    const node: HTMLDivElement = element.closest(".tobago-treeNode") as HTMLDivElement;
-    const data = node.closest(".tobago-tree, .tobago-sheet") as HTMLElement;
-    const expanded = data.querySelector(
-        ":scope > .tobago-tree-expanded, :scope > .tobago-sheet-expanded") as HTMLInputElement;
-    const togglesIcon = node.querySelectorAll(".tobago-treeNode-toggle i") as NodeListOf<HTMLElement>;
-    const togglesImage = node.querySelectorAll(".tobago-treeNode-toggle img") as NodeListOf<HTMLImageElement>;
-    const rowIndex = Tree.rowIndex(node);
-    if (Tree.isExpanded(node, expanded)) {
-      Tree.hideChildren(node);
-      for (const icon of togglesIcon) {
-        icon.classList.remove(icon.dataset["tobagoOpen"]);
-        icon.classList.add(icon.dataset["tobagoClosed"]);
-      }
-      for (const image of togglesImage) {
-        let src = image.dataset["tobagoClosed"];
-        if (!src) { // use the open icon if there is no closed icon
-          src = image.dataset["tobagoOpen"];
-        }
-        image.setAttribute("src", src);
-      }
-      const set = Tree.getSet(expanded);
-      set.delete(rowIndex);
-      Tree.setSet(expanded, set);
-      node.classList.remove("tobago-treeNode-markup-expanded");
+import {Selectable} from "./tobago-selectable";
+
+export class Tree extends HTMLElement {
+
+  constructor() {
+    super();
+  }
+
+  connectedCallback() {
+  };
+
+  get isSheet(): boolean {
+    // TODO if sheet is implemented as custom element use:
+    // return this.tagName === "TOBAGO-SHEET";
+    return this.classList.contains("tobago-sheet");
+  }
+
+  clearSelectedNodes(): void {
+    this.hiddenInputSelected.value = "[]"; //empty set
+  }
+
+  addSelectedNode(selectedNode: number): void {
+    const selectedNodes = new Set(JSON.parse(this.hiddenInputSelected.value));
+    selectedNodes.add(selectedNode);
+    this.hiddenInputSelected.value = JSON.stringify(Array.from(selectedNodes));
+  }
+
+  deleteSelectedNode(selectedNode: number): void {
+    const selectedNodes = new Set(JSON.parse(this.hiddenInputSelected.value));
+    selectedNodes.delete(selectedNode);
+    this.hiddenInputSelected.value = JSON.stringify(Array.from(selectedNodes));
+  }
+
+  private get hiddenInputSelected(): HTMLInputElement {
+    if (this.isSheet) {
+      return this.querySelector(":scope > .tobago-sheet-selected");
     } else {
-      const reload = Tree.showChildren(node, expanded);
-      Tree.setSet(expanded, Tree.getSet(expanded).add(rowIndex));
-      if (reload) {
-        jsf.ajax.request(
-            node.id,
-            event,
-            {
-              //"javax.faces.behavior.event": "click",
-              execute: data.id,
-              render: data.id
-            });
-      } else {
-        for (const icon of togglesIcon) {
-          icon.classList.remove(icon.dataset["tobagoClosed"]);
-          icon.classList.add(icon.dataset["tobagoOpen"]);
-        }
-        for (const image of togglesImage) {
-          let src = image.dataset["tobagoOpen"];
-          if (!src) { // use the open icon if there is no closed icon
-            src = image.dataset["tobagoClosed"];
-          }
-          image.setAttribute("src", src);
-        }
-        node.classList.add("tobago-treeNode-markup-expanded");
-      }
+      return this.querySelector(":scope > .tobago-tree-selected");
     }
-  };
+  }
 
-  /**
-   * Hide all children of the node recursively.
-   * @param node A HTMLElement as a node of the tree.
-   */
-  static hideChildren = function (node: HTMLElement): void {
-    for (const child of Tree.findTreeChildren(node)) {
-      if (Tree.isInSheet(node)) {
-        child.parentElement.parentElement.classList.add("d-none");
-      } else {
-        child.classList.add("d-none");
-      }
-      Tree.hideChildren(child);
+  clearExpandedNodes(): void {
+    this.hiddenInputExpanded.value = "[]"; //empty set
+  }
+
+  addExpandedNode(expandedNode: number): void {
+    const expandedNodes = new Set(JSON.parse(this.hiddenInputExpanded.value));
+    expandedNodes.add(expandedNode);
+    this.hiddenInputExpanded.value = JSON.stringify(Array.from(expandedNodes));
+  }
+
+  deleteExpandedNode(expandedNode: number): void {
+    const expandedNodes = new Set(JSON.parse(this.hiddenInputExpanded.value));
+    expandedNodes.delete(expandedNode);
+    this.hiddenInputExpanded.value = JSON.stringify(Array.from(expandedNodes));
+  }
+
+  get expandedNodes(): Set<number> {
+    return new Set(JSON.parse(this.hiddenInputExpanded.value));
+  }
+
+  private get hiddenInputExpanded(): HTMLInputElement {
+    if (this.isSheet) {
+      return this.querySelector(":scope > .tobago-sheet-expanded");
+    } else {
+      return this.querySelector(":scope > .tobago-tree-expanded");
     }
-  };
+  }
+
+  get selectable(): Selectable {
+    return Selectable[this.getAttribute("selectable")];
+  }
+}
+
+export class TreeNode extends HTMLElement {
 
-  /**
-   * Show the children of the node recursively, there parents are expanded.
-   * @param node A HTMLElement as a node of the tree.
-   * @param expanded The hidden field which contains the expanded state.
-   * @return is reload needed (to get all nodes from the server)
-   */
-  static showChildren = function (node: HTMLElement, expanded: HTMLInputElement): boolean {
-    const children = Tree.findTreeChildren(node);
-    if (children.length === 0) {
-      return true;
+  constructor() {
+    super();
+  }
+
+  connectedCallback() {
+    if (this.isExpandable() && this.toggles !== null) {
+      this.toggles.forEach(element => element.addEventListener("click", this.toggleNode.bind(this)));
     }
-    for (const child of children) {
-      if (Tree.isInSheet(node)) {
-        child.parentElement.parentElement.classList.remove("d-none");
-      } else {
-        child.classList.remove("d-none");
-      }
-      if (Tree.isExpanded(child, expanded)) {
-        const reload = Tree.showChildren(child, expanded);
-        if (reload) {
-          return true;
-        }
+  }
+
+  get tree(): Tree {
+    return this.closest("tobago-tree") as Tree; //TODO how to detect tobago-sheet?
+  }
+
+  isExpandable(): boolean {
+    return this.getAttribute("expandable") === "expandable";
+  }
+
+  isExpanded(): boolean {
+    for (const expandedNodeIndex of this.tree.expandedNodes) {
+      if (expandedNodeIndex === this.index) {
+        return true;
       }
     }
     return false;
-  };
+  }
 
-  static commandFocus = function (event: FocusEvent) {
-    const command = event.currentTarget as HTMLElement;
-    const node = command.parentElement;
-    const tree = node.closest(".tobago-tree");
-    const selected = tree.querySelector(".tobago-tree-selected") as HTMLInputElement;
-    selected.value = String(Tree.rowIndex(node));
-    for (const otherNode of tree.querySelectorAll(".tobago-treeNode-markup-selected")) {
-      if (otherNode !== node) {
-        otherNode.classList.remove("tobago-treeNode-markup-selected");
-      }
+  get treeChildNodes(): NodeListOf<TreeNode> {
+    if (this.tree.isSheet) {
+      return this.closest("tbody").querySelectorAll("tobago-tree-node[parent='" + this.id + "']");
+    } else {
+      return this.parentElement.querySelectorAll("tobago-tree-node[parent='" + this.id + "']");
     }
-    node.classList.add("tobago-treeNode-markup-selected");
-  };
+  }
 
-  static init = function (element: HTMLElement) {
+  get toggles(): NodeListOf<HTMLSpanElement> {
+    return this.querySelectorAll(".tobago-treeNode-toggle");
+  }
 
-    for (const toggle of DomUtils.selfOrQuerySelectorAll(element, ".tobago-treeNode-markup-folder .tobago-treeNode-toggle")) {
-      toggle.addEventListener("click", Tree.toggleNode);
-    }
+  get icons(): NodeListOf<HTMLElement> {
+    return this.querySelectorAll(".tobago-treeNode-toggle i");
+  }
 
-    // selected for treeNode
-    for (const command of DomUtils.selfOrQuerySelectorAll(element, ".tobago-treeCommand")) {
-      command.addEventListener("focus", Tree.commandFocus);
-    }
+  get images(): NodeListOf<HTMLImageElement> {
+    return this.querySelectorAll(".tobago-treeNode-toggle img");
+  }
 
-    for (const e of DomUtils.selfOrQuerySelectorAll(element, ".tobago-sheet, .tobago-tree")) {
-      const sheetOrTree: HTMLDivElement = e as HTMLDivElement;
+  get index(): number {
+    return Number(this.getAttribute("index"));
+  }
 
-      // init selected field
-      const hiddenInputSelected: HTMLInputElement = sheetOrTree.querySelector(":scope > .tobago-sheet-selected, :scope > .tobago-tree-selected");
-      if (hiddenInputSelected) {
-        const value = new Set<number>();
-        for (const selected of sheetOrTree.querySelectorAll(".tobago-treeNode-markup-selected")) {
-          value.add(Tree.rowIndex(selected));
+  toggleNode(event: MouseEvent): void {
+    if (this.isExpanded()) {
+      for (const icon of this.icons) {
+        icon.classList.remove(icon.dataset.tobagoOpen);
+        icon.classList.add(icon.dataset.tobagoClosed);
+      }
+      for (const image of this.images) {
+        if (image.dataset.tobagoClosed) {
+          image.src = image.dataset.tobagoClosed;
+        } else {
+          image.src = image.dataset.tobagoOpen;
         }
-        Tree.setSet(hiddenInputSelected, value);
       }
 
-      // selected for treeSelect
-      for (const select of sheetOrTree.querySelectorAll(".tobago-treeSelect > input") as NodeListOf<HTMLInputElement>) {
-        let value: Set<number>;
-        // todo may use an class attribute for this value
-        if (select.type === "radio") {
-          value = new Set();
-          value.add(Tree.rowIndex(select));
-        } else if (select.checked) {
-          value = Tree.getSet(hiddenInputSelected);
-          value.add(Tree.rowIndex(select));
+      this.tree.deleteExpandedNode(this.index);
+      this.classList.remove("tobago-treeNode-markup-expanded");
+
+      this.hideNodes(this.treeChildNodes);
+    } else {
+      for (const icon of this.icons) {
+        icon.classList.remove(icon.dataset.tobagoClosed);
+        icon.classList.add(icon.dataset.tobagoOpen);
+      }
+      for (const image of this.images) {
+        if (image.dataset.tobagoOpen) {
+          image.src = image.dataset.tobagoOpen;
         } else {
-          value = Tree.getSet(hiddenInputSelected);
-          value.delete(Tree.rowIndex(select));
+          image.src = image.dataset.tobagoClosed;
         }
-        Tree.setSet(hiddenInputSelected, value);
       }
 
-      // init expanded field
-      const hiddenInputExpanded: HTMLInputElement = sheetOrTree.querySelector(":scope > .tobago-sheet-expanded, :scope > .tobago-tree-expanded");
-      if (hiddenInputExpanded) {
-        const value = new Set<number>();
-        for (const expanded of sheetOrTree.querySelectorAll(".tobago-treeNode-markup-expanded")) {
-          value.add(Tree.rowIndex(expanded));
-        }
-        Tree.setSet(hiddenInputExpanded, value);
+      this.tree.addExpandedNode(this.index);
+      this.classList.add("tobago-treeNode-markup-expanded");
+
+      if (this.treeChildNodes.length === 0) {
+        jsf.ajax.request(
+            this.id,
+            event,
+            {
+              //"javax.faces.behavior.event": "click",
+              execute: this.tree.id,
+              render: this.tree.id
+            });
+      } else {
+        this.showNodes(this.treeChildNodes);
       }
+    }
+  }
 
-      // init tree selection for multiCascade
-      if (sheetOrTree.dataset.tobagoSelectable === "multiCascade") {
-        for (const treeNode of sheetOrTree.querySelectorAll(".tobago-treeNode") as NodeListOf<HTMLDivElement>) {
-
-          const checkbox: HTMLInputElement = treeNode.querySelector(".tobago-treeSelect input[type=checkbox]");
-          checkbox.addEventListener("change", function (event: Event) {
-            for (const childTreeNode of Tree.findTreeChildren(treeNode)) {
-              const childCheckbox: HTMLInputElement = childTreeNode.querySelector(".tobago-treeSelect input[type=checkbox]");
-              childCheckbox.checked = checkbox.checked;
-
-              const event = document.createEvent('HTMLEvents');
-              event.initEvent('change', true, false);
-              childCheckbox.dispatchEvent(event);
-            }
-          });
-        }
+  hideNodes(treeChildNodes: NodeListOf<TreeNode>): void {
+    for (const treeChildNode of treeChildNodes) {
+
+      if (treeChildNode.tree.isSheet) {
+        treeChildNode.closest("tobago-sheet-row").classList.add("d-none");
+      } else {
+        treeChildNode.classList.add("d-none");
       }
+
+      this.hideNodes(treeChildNode.treeChildNodes);
     }
-  };
+  }
 
-  static getSet(element: HTMLInputElement): Set<number> {
-    return new Set(JSON.parse(element.value));
+  showNodes(treeChildNodes: NodeListOf<TreeNode>) {
+    for (const treeChildNode of treeChildNodes) {
+
+      if (treeChildNode.tree.isSheet) {
+        treeChildNode.closest("tobago-sheet-row").classList.remove("d-none");
+      } else {
+        treeChildNode.classList.remove("d-none");
+      }
+
+      this.showNodes(treeChildNode.treeChildNodes);
+    }
   }
+}
 
-  static setSet(element, set: Set<number>) {
-    return element.value = JSON.stringify(Array.from(set));
+export class TreeSelect extends HTMLElement {
+
+  constructor() {
+    super();
   }
 
-  static isExpanded = function (node: Element, expanded: HTMLInputElement) {
-    const rowIndex = Tree.rowIndex(node);
-    return Tree.getSet(expanded).has(rowIndex);
-  };
+  connectedCallback() {
+    this.input.addEventListener("change", this.select.bind(this));
 
-  static rowIndex = function (node: Element): number { // todo: use attribute data-tobago-row-index
-    return parseInt(node.id.replace(/.+\:(\d+)(\:\w+)+/, '$1'));
-  };
+    if (this.tree.selectable === Selectable.multiCascade) {
+      this.input.addEventListener("change", this.selectChildren.bind(this));
+    }
+  }
 
-  static findTreeChildren = function (treeNode: HTMLElement): NodeListOf<HTMLDivElement> {
-    if (Tree.isInSheet(treeNode)) {
-      return treeNode.closest("tbody")
-          .querySelectorAll(".tobago-sheet-row[data-tobago-tree-parent='" + treeNode.id + "'] .tobago-treeNode");
-    } else {
-      return treeNode.parentElement.querySelectorAll(".tobago-treeNode[data-tobago-tree-parent='" + treeNode.id + "']");
+  get tree(): Tree {
+    return this.closest("tobago-tree") as Tree;
+  }
+
+  get treeNode(): TreeNode {
+    return this.closest("tobago-tree-node") as TreeNode;
+  }
+
+  get input(): HTMLInputElement {
+    return this.querySelector("input");
+  }
+
+  select(event: Event): void {
+    switch (this.input.type) {
+      case "radio":
+        this.tree.clearSelectedNodes();
+        this.tree.addSelectedNode(this.treeNode.index);
+        break;
+      case "checkbox":
+        if (this.input.checked) {
+          this.tree.addSelectedNode(this.treeNode.index);
+        } else {
+          this.tree.deleteSelectedNode(this.treeNode.index);
+        }
+        break;
     }
-  };
+  }
 
-  static isInSheet = function (node: Element) {
-    return node.parentElement.tagName === "TD";
-  };
+  selectChildren(event: Event): void {
+    for (const treeChildNode of this.treeNode.treeChildNodes) {
+      const child: TreeSelect = treeChildNode.querySelector(":scope > tobago-tree-select");
+      child.input.checked = this.input.checked;
+
+      if (this.input.checked) {
+        this.tree.addSelectedNode(child.treeNode.index);
+      } else {
+        this.tree.deleteSelectedNode(child.treeNode.index);
+      }
+
+      child.input.dispatchEvent(new Event("change", {bubbles: true}));
+    }
+  }
 }
 
-Listener.register(Tree.init, Phase.DOCUMENT_READY);
-Listener.register(Tree.init, Phase.AFTER_UPDATE);
+document.addEventListener("DOMContentLoaded", function (event) {
+  window.customElements.define("tobago-tree-select", TreeSelect);
+  window.customElements.define("tobago-tree-node", TreeNode);
+  window.customElements.define("tobago-tree", Tree);
+});


[myfaces-tobago] 01/02: add test for tree commands

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 61fb78141e8f6075ad5dc399c78e106dabfc1f80
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Thu Sep 19 13:56:07 2019 +0200

    add test for tree commands
---
 .../090-tree/00-command/Tree_Command_Types.test.js | 60 ++++++++++++++++++++++
 .../090-tree/00-command/Tree_Command_Types.xhtml   | 14 ++---
 2 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.test.js
new file mode 100644
index 0000000..de1a714
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.test.js
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+import {testFrameQuerySelectorFn} from "/script/tobago-test.js";
+import {TobagoTestTool} from "/tobago/test/tobago-test-tool.js";
+
+QUnit.test("execute both 'Action 1' and 'Action 2' two times", function (assert) {
+  let action1Fn = testFrameQuerySelectorFn("#page\\:mainForm\\:tree\\:2\\:actionCommand");
+  let action2Fn = testFrameQuerySelectorFn("#page\\:mainForm\\:tree\\:3\\:actionCommand");
+  let actionCount1Fn = testFrameQuerySelectorFn("#page\\:mainForm\\:actionCount1 .tobago-out");
+  let actionCount2Fn = testFrameQuerySelectorFn("#page\\:mainForm\\:actionCount2 .tobago-out");
+
+  const counterBeforeTestResult1 = Number(actionCount1Fn().textContent);
+  const counterBeforeTestResult2 = Number(actionCount2Fn().textContent);
+
+  let TTT = new TobagoTestTool(assert);
+  TTT.action(function () {
+    action1Fn().dispatchEvent(new Event('click'));
+  });
+  TTT.waitForResponse();
+  TTT.asserts(1, function () {
+    assert.equal(Number(actionCount1Fn().textContent), counterBeforeTestResult1 + 1);
+  });
+  TTT.action(function () {
+    action2Fn().dispatchEvent(new Event('click'));
+  });
+  TTT.waitForResponse();
+  TTT.asserts(1, function () {
+    assert.equal(Number(actionCount2Fn().textContent), counterBeforeTestResult2 + 1);
+  });
+  TTT.action(function () {
+    action1Fn().dispatchEvent(new Event('click'));
+  });
+  TTT.waitForResponse();
+  TTT.asserts(1, function () {
+    assert.equal(Number(actionCount1Fn().textContent), counterBeforeTestResult1 + 2);
+  });
+  TTT.action(function () {
+    action2Fn().dispatchEvent(new Event('click'));
+  });
+  TTT.waitForResponse();
+  TTT.asserts(1, function () {
+    assert.equal(Number(actionCount2Fn().textContent), counterBeforeTestResult2 + 2);
+  });
+  TTT.startTest();
+});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.xhtml
index 205ce10..f7d8477 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/00-command/Tree_Command_Types.xhtml
@@ -35,16 +35,18 @@
       Like the parent nodes suggest,
       the entries in 'Actions' run an action with the <code>action</code> attribute.
       And the entries in 'Links' are links to an external website using the <code>link</code> attribute.</p>
-    <tc:tree value="#{treeCommandTypesController.sample}" var="node">
-      <tc:treeNode>
+    <tc:tree id="tree" value="#{treeCommandTypesController.sample}" var="node">
+      <tc:treeNode id="treeNode">
         <tc:treeIndent showJunctions="false"/>
         <tc:treeLabel value="#{node.name}" rendered="#{node.childCount > 0}"/>
-        <tc:treeCommand label="#{node.name}" rendered="#{node.childCount == 0}"
-                        action="#{treeCommandTypesController.increaseActionCount(node.action)}" link="#{node.url}"/>
+        <tc:treeCommand id="actionCommand" label="#{node.name}" rendered="#{node.childCount == 0 and empty node.url}"
+                        action="#{treeCommandTypesController.increaseActionCount(node.action)}"/>
+        <tc:treeCommand label="#{node.name}" rendered="#{node.childCount == 0 and not empty node.url}"
+                        link="#{node.url}"/>
       </tc:treeNode>
     </tc:tree>
 
-    <tc:out label="Action 1 Count" value="#{treeCommandTypesController.actionOneCount}"/>
-    <tc:out label="Action 2 Count" value="#{treeCommandTypesController.actionTwoCount}"/>
+    <tc:out id="actionCount1" label="Action 1 Count" value="#{treeCommandTypesController.actionOneCount}"/>
+    <tc:out id="actionCount2" label="Action 2 Count" value="#{treeCommandTypesController.actionTwoCount}"/>
   </tc:section>
 </ui:composition>