From 8a701b6ad89af57ec4e3b836d4bb0c9164707abc Mon Sep 17 00:00:00 2001
From: Manuel Leduc <manuel.leduc@xwiki.com>
Date: Wed, 24 Nov 2021 09:51:03 +0100
Subject: [PATCH] XWIKI-19110: Live Data tables appear to be loading
 continuously when a greyed out layout is selected

(cherry picked from commit 3fa11858c33cd6c6881a04d71fb82e98584b7631)
---
 .../tests/unit/LivedataDropdownMenu.spec.js   | 106 ++++++++++++++++++
 .../src/main/vue/LivedataDropdownMenu.vue     |  15 ++-
 2 files changed, 119 insertions(+), 2 deletions(-)
 create mode 100644 xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/tests/unit/LivedataDropdownMenu.spec.js

diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/tests/unit/LivedataDropdownMenu.spec.js b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/tests/unit/LivedataDropdownMenu.spec.js
new file mode 100644
index 00000000000..05389df0aa9
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/tests/unit/LivedataDropdownMenu.spec.js
@@ -0,0 +1,106 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+import {mount} from "@vue/test-utils";
+import LivedataDropdownMenu from "../../LivedataDropdownMenu";
+
+/**
+ * Vue component initializer for `LivedataDropdownMenu` components. Calls `mount()` with preconfigured values.
+ *
+ * @returns a map containing a wrapper for `LivedataDropdownMenu` components and a mock of logic.changeLayout
+ */
+function initWrapper() {
+  global.XWiki = {
+    contextPath: ''
+  }
+
+  const changeLayout = jest.fn();
+  const wrapper = mount(LivedataDropdownMenu, {
+    provide: {
+      logic: {
+        currentLayoutId: 'cards',
+        data: {
+          meta: {
+            layouts: [{id: 'table'}, {id: 'cards'}]
+          }
+        },
+        changeLayout: changeLayout
+      },
+    },
+    mocks: {
+      $t: (key) => key
+    }
+  });
+  return {wrapper, changeLayout};
+}
+
+// Since the <li> elements does not have distinguishing attributes, we look for the layout items by looking at the
+// elements located after the second separator (class dropdown-header).
+function findSecondDropdownHeaderIndex(lis) {
+  var dropdownHeadersCptr = 0;
+  var liIdx = 0;
+  for (; liIdx < lis.length; liIdx++) {
+    const li = lis.at(liIdx);
+    if (li.classes().includes('dropdown-header')) {
+      dropdownHeadersCptr++;
+    }
+    if (dropdownHeadersCptr >= 2) {
+      break;
+    }
+  }
+  return liIdx;
+}
+
+describe('LivedataDropdownMenu.vue', () => {
+  it('Current layout is greyed out', () => {
+    const {wrapper} = initWrapper();
+    const lis = wrapper.findAll('li');
+    const dropDownHeaderIndex = findSecondDropdownHeaderIndex(lis);
+
+    const tableLayout = lis.at(dropDownHeaderIndex + 1);
+    const cardsLayout = lis.at(dropDownHeaderIndex + 2);
+
+    expect(tableLayout.classes()).not.toContain('disabled');
+    expect(cardsLayout.classes()).toContain('disabled');
+  })
+
+  it('Clicking on an enabled layout changes the layout', () => {
+    const {wrapper, changeLayout} = initWrapper();
+    const lis = wrapper.findAll('li');
+    const dropDownHeaderIndex = findSecondDropdownHeaderIndex(lis);
+    const tableLayout = lis.at(dropDownHeaderIndex + 1);
+
+    tableLayout.find('a').trigger('click');
+
+    expect(changeLayout.mock.calls.length).toBe(1);
+    expect(changeLayout.mock.calls[0][0]).toBe('table');
+  })
+
+  it('Clicking on a disabled layout does nothing', () => {
+    const {wrapper, changeLayout} = initWrapper();
+    const lis = wrapper.findAll('li');
+    const dropDownHeaderIndex = findSecondDropdownHeaderIndex(lis);
+    const cardsLayout = lis.at(dropDownHeaderIndex + 2);
+
+    cardsLayout.find('a').trigger('click');
+
+    expect(changeLayout.mock.calls.length).toBe(0);
+  })
+})
diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/vue/LivedataDropdownMenu.vue b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/vue/LivedataDropdownMenu.vue
index 4577e0e5f94..1fa53d37555 100644
--- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/vue/LivedataDropdownMenu.vue
+++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-webjar/src/main/vue/LivedataDropdownMenu.vue
@@ -69,10 +69,10 @@
         v-for="layout in data.meta.layouts"
         :key="layout.id"
         :class="{
-          'disabled': logic.currentLayoutId === layout.id,
+          'disabled': isCurrentLayout(layout.id),
         }"
       >
-        <a href="#" @click.prevent="logic.changeLayout(layout.id)">
+        <a href="#" @click.prevent="changeLayout(layout.id)">
           <XWikiIcon :icon-descriptor="layout.icon"></XWikiIcon>
           {{ layout.name }}
         </a>
@@ -127,6 +127,17 @@ export default {
     data () { return this.logic.data; },
   },
 
+  methods: {
+    isCurrentLayout(layoutId) {
+      return this.logic.currentLayoutId === layoutId
+    },
+    changeLayout(layoutId) {
+      if (!this.isCurrentLayout(layoutId)) {
+        this.logic.changeLayout(layoutId)
+      }
+    }
+  }
+
 };
 </script>
 
-- 
GitLab