Support extension of button group lists

Change-Id: Id7e1c5dfcac9cb2963891e0e9e2f045e0c5a26f6
diff --git a/Changes b/Changes
index a2aeefb..6a3ed5a 100755
--- a/Changes
+++ b/Changes
@@ -1,4 +1,4 @@
-0.43 2021-07-23
+0.43 2021-07-26
         - New menu class that has an entry at the very end,
           similar to the input text prefix,
           that is always available (lerepp).
@@ -10,6 +10,7 @@
           as a CSP rule.
         - Fix CSS compression for new SASS compiler.
         - Support dynamic menu extensions.
+        - Dynamically extend buttongroup menus.
 
 0.42 2021-06-18
         - Added GitHub based CI for perl.
diff --git a/dev/js/spec/buttongroupSpec.js b/dev/js/spec/buttongroupSpec.js
index bf7eafb..3d018e0 100644
--- a/dev/js/spec/buttongroupSpec.js
+++ b/dev/js/spec/buttongroupSpec.js
@@ -119,7 +119,6 @@
       expect(btn.innerText).toEqual('Meta');
     });
 
-    
     it('should open lists', function () {
       var group = buttonGroupClass.create();
       expect(group.element().classList.contains('button-group')).toBeTruthy();
@@ -149,6 +148,56 @@
       document.body.removeChild(list.element());
     });
 
+    it('should add to lists', function () {
+      var group = buttonGroupClass.create();
+      expect(group.element().classList.contains('button-group')).toBeTruthy();
+
+      var list = group.addList('More', {'cls':['more']});
+
+      let x = 'empty';
+      list.add('Meta1', {'cls':['meta'], 'icon': 'metaicon'}, function (e) {
+        x = 'meta1';
+      });
+      list.add('Meta2', {'cls':['meta'], 'icon': 'metaicon'}, function (e) {
+        x = 'meta2'
+      });
+
+      var btn = group.element().firstChild;
+      expect(btn.tagName).toEqual('SPAN');
+      expect(btn.classList.contains('more')).toBeTruthy();
+      expect(btn.innerText).toEqual('More');
+
+      expect(list.element().classList.contains('visible')).toBeFalsy();
+      
+      // Click to show menu
+      btn.click();
+
+      expect(list.element().classList.contains('visible')).toBeTruthy();
+      expect(list.directElementChildrenByTagName("li")[0].innerHTML).toEqual('Meta1');
+      expect(list.directElementChildrenByTagName("li")[1].innerHTML).toEqual('Meta2');
+
+      expect(x).toEqual('empty');
+      list.directElementChildrenByTagName("li")[0].click();
+      expect(x).toEqual('meta1');
+
+      expect(list.element().classList.contains('visible')).toBeFalsy();
+
+      // Click to show menu
+      btn.click();
+
+      expect(list.element().classList.contains('visible')).toBeTruthy();
+      list.directElementChildrenByTagName("li")[1].click();
+      expect(x).toEqual('meta2');
+
+      expect(list.element().classList.contains('visible')).toBeFalsy();
+
+      expect(list.element().children[1].children[0].innerText).toEqual('Meta1--');
+      expect(list.element().children[1].children[1].innerText).toEqual('Meta2--');
+
+      document.body.removeChild(list.element());
+    });
+
+    
     it('should support toggle buttons', function () {
       var group = buttonGroupClass.create();
 
diff --git a/dev/js/src/buttongroup/menu.js b/dev/js/src/buttongroup/menu.js
index 7a4f8df..32ba0eb 100644
--- a/dev/js/src/buttongroup/menu.js
+++ b/dev/js/src/buttongroup/menu.js
@@ -9,12 +9,7 @@
 
     /**
      * Create new menu object.
-     * Pass the panel object
-     * and the item parameters.
-     *
-     * @param panel The panel object
-     * @param params The match menu items
-     *   as an array of arrays.
+     * Pass the list of items and the itemClass.
      */
     create : function (list, itemClass) {
       const obj = Object.create(menuClass)
@@ -26,7 +21,7 @@
 
       // This is only domspecific
       e.addEventListener('blur', function (e) {
-	      this.menu.hide();
+	this.menu.hide();
       });
 
       e.classList.add('button-group-list');
@@ -39,9 +34,10 @@
 
 
     /**
-     * The panel object of the menu.
+     * The panel object of the menu,
+     * in case the menu was spawned by a panel.
      */
-    panel :function (panelVar) {
+    panel : function (panelVar) {
       if (panelVar !== undefined)
         this._panel = panelVar;
 
@@ -72,6 +68,30 @@
       window.addEventListener('scroll', this._onscroll);
     },
 
+
+    /**
+     * Add button in order
+     * 
+     * Returns the button element
+     */
+    add : function (title, data, cb) {
+
+      let that = this;
+
+      const cbWrap = function (e) {
+
+        // Call callback
+        let obj = that._bind || this;
+        obj.button = b;
+        cb.apply(obj)
+      };
+
+      // TODO:
+      //   support classes, data-icon and state in itemClass!
+      const b = this.itemClass().create([title, title, cbWrap]);
+      this.append(b);
+      return b;
+    },
     
     // Overwrite onHide method
     onHide : function () {