@appscode/design-system 1.0.43-alpha.99 → 1.1.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/base/utilities/_all.scss +8 -0
  2. package/base/utilities/_customize-bulma.scss +191 -0
  3. package/base/utilities/_default.scss +58 -124
  4. package/base/utilities/_derived-variables.scss +6 -0
  5. package/base/utilities/_extended.scss +38 -0
  6. package/base/utilities/_grid.scss +29 -0
  7. package/base/utilities/_initial-variables.scss +14 -10
  8. package/base/utilities/_typography.scss +6 -12
  9. package/base/utilities/dark-theme.scss +1 -0
  10. package/components/_ac-accordion.scss +14 -5
  11. package/components/_ac-alert-box.scss +32 -6
  12. package/components/_ac-card.scss +17 -5
  13. package/components/_ac-drag.scss +2 -0
  14. package/components/_ac-input.scss +19 -11
  15. package/components/_ac-modal.scss +1 -1
  16. package/components/_ac-multi-select.scss +60 -4
  17. package/components/_ac-report.scss +53 -0
  18. package/components/_ac-table.scss +60 -2
  19. package/components/_ac-tabs.scss +16 -2
  20. package/components/_ac-tags.scss +85 -0
  21. package/components/_ac-terminal.scss +1 -3
  22. package/components/_all.scss +29 -0
  23. package/components/_basic-card.scss +128 -0
  24. package/components/_buttons.scss +14 -33
  25. package/components/_dashboard-header.scss +32 -0
  26. package/components/_left-sidebar-menu.scss +9 -9
  27. package/components/_navbar.scss +89 -4
  28. package/components/_preview-modal.scss +14 -1
  29. package/components/_transitions.scss +296 -0
  30. package/components/_wizard.scss +1 -0
  31. package/components/bbum/_all.scss +9 -0
  32. package/components/bbum/_single-post-preview.scss +1 -1
  33. package/components/ui-builder/_ui-builder.scss +65 -1
  34. package/components/ui-builder/_vue-open-api.scss +6 -0
  35. package/layouts/_all.scss +2 -0
  36. package/layouts/_code-preview.scss +5 -2
  37. package/main.scss +5 -56
  38. package/package.json +4 -2
  39. package/plugins/caching.ts +243 -0
  40. package/plugins/time-convert.js +49 -0
  41. package/plugins/vue-toaster.js +3 -0
  42. package/vue-components/v2/banner/Banner.vue +2 -2
  43. package/vue-components/v2/breadcrumbs/Breadcrumb.vue +97 -0
  44. package/vue-components/v2/button/Button.vue +5 -0
  45. package/vue-components/v2/button/DownloadBtn.vue +45 -0
  46. package/vue-components/v2/card/Card.vue +1 -0
  47. package/vue-components/v2/content/ContentTable.vue +10 -0
  48. package/vue-components/v2/editor/Editor.vue +37 -24
  49. package/vue-components/v2/editor/FilteredFileEditor.vue +189 -0
  50. package/vue-components/v2/editor/MonacoEditor.vue +125 -0
  51. package/vue-components/v2/editor/ResourceKeyValueEditor.vue +209 -0
  52. package/vue-components/v2/form-fields/Input.vue +1 -1
  53. package/vue-components/v2/loaders/ResourceLoader.vue +101 -0
  54. package/vue-components/v2/loaders/SidebarLoader.vue +43 -0
  55. package/vue-components/v2/modal/Modal.vue +31 -5
  56. package/vue-components/v2/modals/DeleteConfirmationModal.vue +79 -0
  57. package/vue-components/v2/modals/JsonShowModal.vue +12 -2
  58. package/vue-components/v2/navbar/User.vue +229 -17
  59. package/vue-components/v2/notification/Notification.vue +101 -0
  60. package/vue-components/v2/notification/NotificationItem.vue +44 -0
  61. package/vue-components/v2/pagination/Pagination.vue +16 -3
  62. package/vue-components/v2/preloader/Preloader.vue +1 -1
  63. package/vue-components/v2/sidebar/SidebarItemWithDropDown.vue +19 -20
  64. package/vue-components/v2/tab/TabItem.vue +1 -1
  65. package/vue-components/v2/table/Table.vue +49 -8
  66. package/vue-components/v2/table/TableRow.vue +12 -2
  67. package/vue-components/v2/table/table-cell/CellValue.vue +29 -9
  68. package/vue-components/v2/table/table-cell/GenericCell.vue +56 -0
  69. package/vue-components/v2/table/table-cell/ObjectCell.vue +4 -1
  70. package/vue-components/v3/button/Button.vue +6 -1
  71. package/vue-components/v3/content/ContentHeader.vue +2 -1
  72. package/vue-components/v3/content/ContentTable.vue +25 -2
  73. package/vue-components/v3/editor/Editor.vue +36 -33
  74. package/vue-components/v3/editor/FilteredFileEditor.vue +186 -0
  75. package/vue-components/v3/editor/MonacoEditor.vue +131 -0
  76. package/vue-components/v3/editor/ResourceKeyValueEditor.vue +125 -0
  77. package/vue-components/v3/form/Form.vue +63 -0
  78. package/vue-components/v3/form-fields/Input.vue +11 -10
  79. package/vue-components/v3/header/HeaderItem.vue +5 -0
  80. package/vue-components/v3/header/HeaderItems.vue +5 -0
  81. package/vue-components/v3/loaders/ResourceLoader.vue +83 -0
  82. package/vue-components/v3/loaders/SidebarLoader.vue +34 -0
  83. package/vue-components/v3/long-running-tasks/LongRunningTaskItem.vue +92 -0
  84. package/vue-components/v3/modal/Modal.vue +35 -7
  85. package/vue-components/v3/modals/DeleteConfirmationModal.vue +85 -0
  86. package/vue-components/v3/modals/JsonShowModal.vue +25 -16
  87. package/vue-components/v3/modals/LongRunningTasksModal.vue +400 -0
  88. package/vue-components/v3/navbar/ThemeMode.vue +41 -49
  89. package/vue-components/v3/navbar/User.vue +242 -18
  90. package/vue-components/v3/notification/AlertBox.vue +61 -0
  91. package/vue-components/v3/notification/Notification.vue +98 -0
  92. package/vue-components/v3/notification/NotificationItem.vue +52 -0
  93. package/vue-components/v3/pagination/Pagination.vue +16 -3
  94. package/vue-components/v3/sidebar/SidebarItemWithDropDown.vue +120 -0
  95. package/vue-components/v3/tab/TabItem.vue +1 -1
  96. package/vue-components/v3/table/MultiInfoTable.vue +143 -0
  97. package/vue-components/v3/table/Table.vue +55 -14
  98. package/vue-components/v3/table/TableContainer.vue +34 -0
  99. package/vue-components/v3/table/TableRow.vue +93 -6
  100. package/vue-components/v3/table/table-cell/CellValue.vue +23 -7
  101. package/vue-components/v3/table/table-cell/GenericCell.vue +75 -0
  102. package/vue-components/v3/table/table-cell/ObjectCell.vue +7 -2
  103. package/vue-components/v3/terminal/LongRunningTaskTerminal.vue +148 -0
@@ -7,31 +7,158 @@
7
7
  <img :src="user.avatar_url" alt="User Photo" />
8
8
  </div>
9
9
  </button>
10
- <navbar-item-content>
11
- <div v-if="user.username" class="user-profile-wrapper">
10
+ <navbar-item-content class="navbar-dropdown-wrapper">
11
+ <div
12
+ v-if="user.username"
13
+ class="user-profile-wrapper"
14
+ @mouseleave="onMouseLeave()"
15
+ >
12
16
  <div class="profile-area">
13
17
  <div class="profile-photo">
14
- <img :src="user.avatar_url" alt="User Photo" />
18
+ <img
19
+ :src="user.avatar_url"
20
+ alt="User Photo"
21
+ class="width-50 height-50"
22
+ />
15
23
  <button class="camera-icon"></button>
16
24
  </div>
17
- <div class="profile-info">
18
- <p>{{ user.username.toUpperCase() }}</p>
25
+ <div class="profile-info" style="width: calc(100% - 60px)">
26
+ <a
27
+ :href="`${serverDomain}/${user.username}`"
28
+ :title="user.username.toUpperCase()"
29
+ data-testid="user-profile-link"
30
+ class="line-break-anywhere is-ellipsis-1"
31
+ >{{ user.username.toUpperCase() }}</a
32
+ >
19
33
  <a :href="`mailto:${user.email}`"> {{ user.email }}</a>
20
34
  </div>
21
35
  </div>
22
- <ul>
23
- <li>
24
- <a :href="`${serverDomain}/user/settings/`">Settings</a>
36
+ <transition-group name="list" tag="ul">
37
+ <li key="settings">
38
+ <a
39
+ data-testid="user-settings-link"
40
+ :href="`${serverDomain}/user/settings/`"
41
+ >Settings</a
42
+ >
25
43
  </li>
26
- <template v-if="user.is_admin">
27
- <li>
28
- <a :href="`${serverDomain}/admin`"> Site Administration </a>
29
- </li>
30
- </template>
31
- <li>
32
- <a :href="`${serverDomain}/user/logout`"> Sign out </a>
44
+ <li v-if="user.is_admin" key="site-admin">
45
+ <a data-testid="site-admin-link" :href="`${accountsDomain}/admin`"
46
+ >Site Administration</a
47
+ >
33
48
  </li>
34
- </ul>
49
+ <li
50
+ v-if="showAccountSwitcher"
51
+ :class="`is-${dropDownStatus}`"
52
+ key="switcher"
53
+ >
54
+ <a
55
+ class="
56
+ ac-dropdown-button
57
+ is-fullwidth
58
+ is-flex
59
+ is-justify-content-space-between
60
+ is-align-items-center
61
+ "
62
+ @click="toggleList()"
63
+ >
64
+ <span>Switch Account</span>
65
+ <span
66
+ ><i
67
+ :class="`fa fa-angle-${
68
+ dropDownStatus === 'open' ? 'up' : 'down'
69
+ }`"
70
+ ></i
71
+ ></span>
72
+ </a>
73
+ <transition-group
74
+ name="list"
75
+ tag="ul"
76
+ class="ac-vscrollbar"
77
+ ref="dropdownItems"
78
+ :style="{ maxHeight: dropDownSectionHeight }"
79
+ >
80
+ <li
81
+ v-for="(org, idx) in formattedOrganizations"
82
+ :key="org.username"
83
+ >
84
+ <a
85
+ class="is-flex is-align-items-center"
86
+ @click="onOrganizationClick(org.username)"
87
+ >
88
+ <div class="width-30 height-30 image">
89
+ <img
90
+ :src="org.avatar_url"
91
+ class="ac-user-profile is-rounded"
92
+ alt="icon"
93
+ />
94
+ </div>
95
+ <div
96
+ class="
97
+ is-flex
98
+ is-align-items-center
99
+ is-justify-content-space-between
100
+ is-fullwidth
101
+ ml-10
102
+ "
103
+ >
104
+ <div class="org-info">
105
+ <strong
106
+ :title="org.username"
107
+ class="line-break-anywhere is-ellipsis-1"
108
+ >{{ org.username }}</strong
109
+ >
110
+ <p>
111
+ {{
112
+ org.isPersonalAccount
113
+ ? "Personal Account"
114
+ : "Organization"
115
+ }}
116
+ </p>
117
+ </div>
118
+ <span
119
+ v-if="idx === 0"
120
+ class="
121
+ material-icons-outlined
122
+ font-size-18
123
+ ml-10
124
+ is-pulled-right
125
+ "
126
+ >
127
+ check
128
+ </span>
129
+ </div>
130
+ </a>
131
+ </li>
132
+ </transition-group>
133
+ </li>
134
+ <li key="dashboard">
135
+ <nuxt-link
136
+ v-if="isPlatformDomain"
137
+ to="/dashboard"
138
+ data-testid="user-dashboard-link"
139
+ >
140
+ Dashboard
141
+ </nuxt-link>
142
+ <a
143
+ v-else
144
+ :href="`${serverDomain}/dashboard`"
145
+ data-testid="user-dashboard-link"
146
+ >
147
+ Dashboard
148
+ </a>
149
+ </li>
150
+ <li key="signout" @click="$emit('on-logout')">
151
+ <a
152
+ data-testid="user-logout-link"
153
+ :href="`${accountsDomain}/user/logout`"
154
+ >
155
+ Sign out
156
+ </a>
157
+ </li>
158
+ <li key="theme" v-if="showThemeMode">
159
+ <theme-mode @set:theme="setTheme" />
160
+ </li>
161
+ </transition-group>
35
162
  </div>
36
163
  </navbar-item-content>
37
164
  </navbar-item>
@@ -42,6 +169,7 @@ import { defineComponent, defineAsyncComponent } from "vue";
42
169
 
43
170
  export default defineComponent({
44
171
  props: {
172
+ // active user info
45
173
  user: {
46
174
  type: Object,
47
175
  default: () => ({}),
@@ -50,15 +178,111 @@ export default defineComponent({
50
178
  type: String,
51
179
  default: "",
52
180
  },
181
+ accountsDomain: {
182
+ type: String,
183
+ default: "",
184
+ },
185
+ showAccountSwitcher: {
186
+ type: Boolean,
187
+ default: false,
188
+ },
189
+ // all available organization list including personal account
190
+ organizations: {
191
+ type: Array,
192
+ default: () => [],
193
+ },
194
+ showThemeMode: {
195
+ type: Boolean,
196
+ default: false,
197
+ },
198
+ isPlatformDomain: {
199
+ type: Boolean,
200
+ default: false,
201
+ },
53
202
  },
203
+ emits: ["set:theme", "on-logout"],
54
204
 
55
205
  components: {
56
206
  NavbarItem: defineAsyncComponent(() =>
57
207
  import("../../v2/navbar/NavbarItem.vue").then((module) => module.default)
58
208
  ),
59
209
  NavbarItemContent: defineAsyncComponent(() =>
60
- import("../../v2/navbar/NavbarItemContent.vue").then((module) => module.default)
210
+ import("../../v2/navbar/NavbarItemContent.vue").then(
211
+ (module) => module.default
212
+ )
61
213
  ),
214
+ ThemeMode: defineAsyncComponent(() =>
215
+ import("../../v3/navbar/ThemeMode.vue").then((module) => module.default)
216
+ ),
217
+ },
218
+
219
+ computed: {
220
+ formattedOrganizations() {
221
+ let activeUser;
222
+ const filteredList = this.organizations.filter((item) => {
223
+ if (item.username === this.user.username) {
224
+ activeUser = item;
225
+ } else {
226
+ return true;
227
+ }
228
+ return false;
229
+ });
230
+
231
+ filteredList.unshift(activeUser);
232
+
233
+ return filteredList || [];
234
+ },
235
+ },
236
+
237
+ data() {
238
+ return {
239
+ dropDownStatus: "close",
240
+ dropDownSectionHeight: null,
241
+ };
242
+ },
243
+
244
+ methods: {
245
+ toggleList() {
246
+ this.dropDownStatus = this.dropDownStatus === "open" ? "close" : "open";
247
+ this.$nextTick(() => {
248
+ this.$refs["dropdownItems"].$el.scrollTo(0, 0);
249
+ });
250
+ },
251
+ onOrganizationClick(orgName) {
252
+ this.$refs["dropdownItems"].$el.scrollTo(0, 0);
253
+ this.$emit("activeorg$set", orgName);
254
+ },
255
+ onMouseLeave() {
256
+ this.dropDownStatus = "close";
257
+ },
258
+ setTheme(val) {
259
+ this.$emit("set:theme", val);
260
+ },
261
+ },
262
+
263
+ watch: {
264
+ dropDownStatus: {
265
+ immediate: true,
266
+ handler(n) {
267
+ if (n === "open") {
268
+ this.$nextTick(() => {
269
+ const dropDownUl = this.$refs["dropdownItems"];
270
+ if (dropDownUl)
271
+ this.dropDownSectionHeight = `${dropDownUl.scrollHeight}px`;
272
+ });
273
+ } else {
274
+ this.dropDownSectionHeight = null;
275
+ }
276
+ },
277
+ },
62
278
  },
63
279
  });
64
- </script>
280
+ </script>
281
+ <style lang="scss" scoped>
282
+ .ac-vscrollbar {
283
+ overflow: auto !important;
284
+ }
285
+ .line-break-anywhere {
286
+ line-break: anywhere;
287
+ }
288
+ </style>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <!-- alert-message area start -->
3
+ <!-- plsease, use this class name ('.is-info' 'is-success', 'is-error', 'is-warning') -->
4
+ <div :class="`ac-notification is-${notificationType} mb-15`">
5
+ <p>
6
+ <i v-if="!hideIcon" :class="`fa ${iconClass} mr-5`"></i
7
+ ><span v-html="getSanitizedHtml(content)"></span>
8
+ <ac-button
9
+ v-if="actionButton?.show"
10
+ :title="actionButton?.title"
11
+ :icon-class="actionButton?.iconClass"
12
+ data-testid="notification-action-button"
13
+ @click.prevent="actionButton?.action()"
14
+ >
15
+ </ac-button>
16
+ </p>
17
+ </div>
18
+ <!-- alert-message area end -->
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { toRefs, computed, defineAsyncComponent } from "vue";
23
+ import DOMPurify from "dompurify";
24
+
25
+ const AcButton = defineAsyncComponent(
26
+ () => import("@appscode/design-system/vue-components/v3/button/Button.vue")
27
+ );
28
+
29
+ const props = withDefaults(
30
+ defineProps<{
31
+ notificationType: string;
32
+ content: string;
33
+ hideIcon: boolean;
34
+ actionButton?: {
35
+ show: boolean;
36
+ title: string;
37
+ iconClass: string;
38
+ action: (...args: any) => void | undefined;
39
+ };
40
+ }>(),
41
+ {
42
+ notificationType: "",
43
+ content: "",
44
+ hideIcon: false,
45
+ actionButton: undefined,
46
+ }
47
+ );
48
+
49
+ const { notificationType, content, hideIcon, actionButton } = toRefs(props);
50
+
51
+ const iconClass = computed(() => {
52
+ if (notificationType.value === "success") return "fa-check-circle";
53
+ else if (notificationType.value === "info") return "fa-info-circle";
54
+ else if (notificationType.value === "error") return "fa-times-circle";
55
+ else return "fa-info-circle";
56
+ });
57
+
58
+ const getSanitizedHtml = (content: string) => {
59
+ return DOMPurify.sanitize(content || "");
60
+ };
61
+ </script>
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <div
3
+ @mouseenter="notificationPanelOpen = true"
4
+ @mouseleave="notificationPanelOpen = false"
5
+ >
6
+ <button class="button ac-nav-button">
7
+ <span v-if="unreadCount">{{ unreadCount }}</span>
8
+ <i
9
+ class="fa fa-bell"
10
+ :class="{ 'ac-shake': unreadCount }"
11
+ aria-hidden="true"
12
+ ></i>
13
+ </button>
14
+ <div class="ac-menu-content is-notification">
15
+ <div class="notification-header">
16
+ <div class="left-content">
17
+ <p>
18
+ Notifications <span>({{ notifications.length }})</span>
19
+ </p>
20
+ </div>
21
+ <div class="right-content"></div>
22
+ </div>
23
+ <div :key="notificationPanelOpen ? 1 : 0" class="notification-body">
24
+ <transition-group v-if="notifications.length" name="slide-right">
25
+ <notification-item
26
+ v-for="notification in notifications"
27
+ :key="`${notification.id}${unreadCount}`"
28
+ :notification="notification"
29
+ />
30
+ </transition-group>
31
+ <span v-else class="single-notification-item"
32
+ >No new notifications</span
33
+ >
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { NatsConnection, StringCodec, Subscription } from "nats.ws";
41
+ import {
42
+ computed,
43
+ defineAsyncComponent,
44
+ getCurrentInstance,
45
+ ref,
46
+ Ref,
47
+ watch,
48
+ } from "vue";
49
+ import { TaskLog } from "../../../typings/long-running-tasks";
50
+ import { Notification } from "../../../typings/notification";
51
+
52
+ const NotificationItem = defineAsyncComponent(
53
+ () => import("./NotificationItem.vue")
54
+ );
55
+
56
+ const notifications: Ref<Notification[]> = ref([]);
57
+ const notificationsRead = ref(0);
58
+ const unreadCount = computed(
59
+ () => notifications.value.length - notificationsRead.value
60
+ );
61
+
62
+ const notificationPanelOpen = ref(false);
63
+ watch(notificationPanelOpen, (n) => {
64
+ if (n) notificationsRead.value = notifications.value.length;
65
+ });
66
+
67
+ function addNewNotification(notification: Notification) {
68
+ notifications.value.unshift(notification);
69
+ if (notificationPanelOpen.value) {
70
+ notificationsRead.value = notifications.value.length;
71
+ }
72
+ }
73
+
74
+ const app = getCurrentInstance();
75
+ const $nats: NatsConnection = app?.appContext.config.globalProperties.$nc;
76
+ let subscription: Subscription;
77
+
78
+ async function subscribeToNotifcations() {
79
+ subscription = $nats?.subscribe("notifications");
80
+ console.log("Started listening to Notifications");
81
+
82
+ if (subscription) {
83
+ // listen to channel events
84
+ for await (const msg of subscription) {
85
+ console.log("notifications ===>");
86
+ console.log({ data: StringCodec().decode(msg.data) });
87
+ const log: TaskLog = JSON.parse(StringCodec().decode(msg.data));
88
+ console.log({ log });
89
+ const currentTime = new Date().getTime();
90
+ addNewNotification({ ...log, id: currentTime, time: currentTime });
91
+ msg.respond();
92
+ }
93
+ console.log("Stopped listening to Notifications");
94
+ console.log("Closed Channel Notifications");
95
+ }
96
+ }
97
+ subscribeToNotifcations();
98
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <a class="single-notification-item is-complete">
3
+ <p>
4
+ {{ notification.msg }}
5
+ </p>
6
+ <div class="notification-status">
7
+ <p
8
+ :class="{
9
+ 'is-success': notification.status === 'Success',
10
+ 'has-text-danger': notification.status === 'Failed',
11
+ 'is-info':
12
+ notification.status === 'Started' ||
13
+ notification.status === 'Running',
14
+ 'is-warning': notification.status === 'Pending',
15
+ }"
16
+ >
17
+ <i
18
+ class="fa mr-5"
19
+ :class="{
20
+ 'fa-check': notification.status === 'Success',
21
+ 'fa-exclamation-triangle': notification.status === 'Failed',
22
+ 'fa-info-circle':
23
+ notification.status === 'Started' ||
24
+ notification.status === 'Pending' ||
25
+ notification.status === 'Running',
26
+ }"
27
+ />
28
+ {{ notification.status }}
29
+ </p>
30
+ <p>{{ notificationTime }} ago</p>
31
+ </div>
32
+ </a>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { computed, toRefs } from 'vue'
37
+ import { Notification } from '../../../typings/notification'
38
+ import TimeConvert from '../../../plugins/time-convert'
39
+
40
+ const props = withDefaults(defineProps<{ notification: Notification }>(), {
41
+ notification: () => ({
42
+ id: 0,
43
+ status: 'Pending',
44
+ time: 0,
45
+ }),
46
+ })
47
+
48
+ const { notification } = toRefs(props)
49
+ const notificationTime = computed(() =>
50
+ TimeConvert.getDayDifferences({ past: notification.value.time })
51
+ )
52
+ </script>
@@ -9,7 +9,11 @@
9
9
  "
10
10
  >
11
11
  <label>Rows per page</label>
12
- <select v-model="selectedItemCountPerPage" name="page">
12
+ <select
13
+ v-model="selectedItemCountPerPage"
14
+ name="page"
15
+ data-testid="rows-per-page-selector"
16
+ >
13
17
  <option :value="5">5</option>
14
18
  <option :value="10" v-show="totalNoOfItems > 5">10</option>
15
19
  <option :value="15" v-show="totalNoOfItems > 10">15</option>
@@ -31,19 +35,28 @@
31
35
 
32
36
  <ul v-if="totalNoOfItems > selectedItemCountPerPage">
33
37
  <li>
34
- <a class="previous" @click.prevent="prevPage()">
38
+ <a
39
+ class="previous"
40
+ @click.prevent="prevPage()"
41
+ data-testid="pagination-previous-page-button"
42
+ >
35
43
  <i class="fa fa-angle-left" aria-hidden="true"></i>
36
44
  </a>
37
45
  </li>
38
46
  <li v-for="page in pages" :key="`page-${page}`">
39
47
  <a
40
48
  @click.prevent="changePage(page)"
49
+ data-testid="pagination-page-switch-button"
41
50
  :class="{ 'is-current': activePageNo === page }"
42
51
  >{{ page }}</a
43
52
  >
44
53
  </li>
45
54
  <li>
46
- <a class="next" @click.prevent="nextPage()">
55
+ <a
56
+ class="next"
57
+ @click.prevent="nextPage()"
58
+ data-testid="pagination-next-page-button"
59
+ >
47
60
  <i class="fa fa-angle-right" aria-hidden="true"></i>
48
61
  </a>
49
62
  </li>
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <li :class="`is-${dropDownStatus}`">
3
+ <a class="ac-dropdown-button" :title="title" @click="toggleDropDownStatus">
4
+ <span>
5
+ <img :src="icon" alt="icon" />
6
+ </span>
7
+ <strong>{{ title || "-" }}</strong>
8
+ <span class="ac-arrow-down">
9
+ <i class="fa fa-angle-down" aria-hidden="true"> </i>
10
+ </span>
11
+ </a>
12
+
13
+ <ul ref="sectionItems" :style="{ maxHeight: dropDownSectionHeight }">
14
+ <slot />
15
+ </ul>
16
+ </li>
17
+ </template>
18
+
19
+ <script>
20
+ import { defineComponent } from "vue";
21
+
22
+ export default defineComponent({
23
+ props: {
24
+ isDropDownOpen: {
25
+ type: Boolean,
26
+ default: false,
27
+ },
28
+ title: {
29
+ type: String,
30
+ default: "Sidebar Item",
31
+ },
32
+ icon: {
33
+ type: String,
34
+ default: "@/assets/images/icons/basic.svg",
35
+ },
36
+ },
37
+
38
+ emits: ["dropDownItemChange"],
39
+
40
+ data() {
41
+ return {
42
+ dropDownStatus: "close",
43
+ dropDownSectionHeight: null,
44
+ isCompMounted: false,
45
+ };
46
+ },
47
+
48
+ mounted() {
49
+ this.isCompMounted = true;
50
+ setTimeout(() => {
51
+ // for expanding dropdown
52
+ if (this.isDropDownOpen) {
53
+ this.setDropdownMaxHeight("open");
54
+ } else {
55
+ this.setDropdownMaxHeight("close");
56
+ }
57
+ }, 700);
58
+ },
59
+
60
+ watch: {
61
+ title(n, o) {
62
+ if (n && this.isCompMounted) {
63
+ this.$nextTick(() => {
64
+ // for expanding dropdown
65
+ this.setDropdownMaxHeight("open");
66
+ });
67
+ }
68
+
69
+ if (o && this.isCompMounted) {
70
+ this.$nextTick(() => {
71
+ // for expanding dropdown
72
+ this.setDropdownMaxHeight("close");
73
+ });
74
+ }
75
+ },
76
+ isDropDownOpen: {
77
+ immediate: true,
78
+ handler(n) {
79
+ if (n) {
80
+ this.dropDownStatus = "open";
81
+ } else this.dropDownStatus = "close";
82
+ },
83
+ },
84
+ dropDownStatus: {
85
+ immediate: true,
86
+ handler(n) {
87
+ if (n === "open") {
88
+ // emit event to close other drop down items
89
+ this.$emit("dropDownItemChange");
90
+
91
+ this.$nextTick(() => {
92
+ const dropDownUl = this.$refs["sectionItems"];
93
+ // debugger;
94
+ if (dropDownUl)
95
+ this.dropDownSectionHeight = `${dropDownUl.scrollHeight}px`;
96
+ });
97
+ } else {
98
+ // emit event to close other drop down items
99
+ this.dropDownSectionHeight = null;
100
+ }
101
+ },
102
+ },
103
+ },
104
+
105
+ methods: {
106
+ setDropdownMaxHeight(mode) {
107
+ if (mode === "open") {
108
+ this.dropDownSectionHeight = `${this.$refs["sectionItems"].scrollHeight}px`;
109
+ } else {
110
+ this.dropDownSectionHeight = null;
111
+ }
112
+ },
113
+ toggleDropDownStatus() {
114
+ if (this.dropDownStatus === "open") {
115
+ this.dropDownStatus = "close";
116
+ } else this.dropDownStatus = "open";
117
+ },
118
+ },
119
+ });
120
+ </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <li :class="{ 'is-active': isActive }">
2
+ <li :class="{ 'is-active': isActive }" data-testid="tab-item">
3
3
  <slot />
4
4
  </li>
5
5
  </template>