diff --git a/changelog/unreleased/enhancement-admin-settings-support-pagination b/changelog/unreleased/enhancement-admin-settings-support-pagination new file mode 100644 index 00000000000..c382172697b --- /dev/null +++ b/changelog/unreleased/enhancement-admin-settings-support-pagination @@ -0,0 +1,7 @@ +Enhancement: Support pagination in admin settings app + +We've added pagination to various lists in the admin settings app. +So there will be a page selection at the end of the list if more than 50 items are present. + +https://github.com/owncloud/web/issues/9048 +https://github.com/owncloud/web/pull/9119 diff --git a/packages/web-app-admin-settings/src/components/Groups/GroupsList.vue b/packages/web-app-admin-settings/src/components/Groups/GroupsList.vue index 95e461b81b6..79e896c8ebc 100644 --- a/packages/web-app-admin-settings/src/components/Groups/GroupsList.vue +++ b/packages/web-app-admin-settings/src/components/Groups/GroupsList.vue @@ -12,7 +12,7 @@ :sort-by="sortBy" :sort-dir="sortDir" :fields="fields" - :data="data" + :data="paginatedItems" :highlighted="highlighted" :sticky="true" :header-position="fileListHeaderY" @@ -28,7 +28,9 @@ :label="$gettext('Select all groups')" :model-value="allGroupsSelected" hide-label - @update:model-value="$emit('toggleSelectAllGroups')" + @update:model-value=" + allGroupsSelected ? $emit('unSelectAllGroups') : $emit('selectGroups', paginatedItems) + " /> </template> <template #select="rowData"> @@ -93,6 +95,7 @@ </context-menu-quick-action> </template> <template #footer> + <pagination :pages="paginationPages" :current-page="currentPage" /> <div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s"> <p class="oc-text-muted">{{ footerTextTotal }}</p> <p v-if="filterTerm" class="oc-text-muted">{{ footerTextFilter }}</p> @@ -103,20 +106,30 @@ </template> <script lang="ts"> -import { defineComponent, PropType, ref, unref, ComponentPublicInstance, computed } from 'vue' +import { + defineComponent, + PropType, + ref, + unref, + ComponentPublicInstance, + computed, + watch +} from 'vue' import Fuse from 'fuse.js' import Mark from 'mark.js' -import { displayPositionedDropdown, eventBus } from 'web-pkg' +import { displayPositionedDropdown, eventBus, SortDir } from 'web-pkg' import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' import { Group } from 'web-client/src/generated' import ContextMenuQuickAction from 'web-pkg/src/components/ContextActions/ContextMenuQuickAction.vue' import { useGettext } from 'vue3-gettext' import { defaultFuseOptions } from 'web-pkg/src/helpers' import { useFileListHeaderPosition } from 'web-pkg/src/composables' +import Pagination from 'web-pkg/src/components/Pagination.vue' +import { usePagination } from 'web-app-admin-settings/src/composables' export default defineComponent({ name: 'GroupsList', - components: { ContextMenuQuickAction }, + components: { ContextMenuQuickAction, Pagination }, props: { groups: { type: Array as PropType<Group[]>, @@ -127,11 +140,14 @@ export default defineComponent({ required: true } }, - emits: ['toggleSelectAllGroups', 'unSelectAllGroups', 'toggleSelectGroup'], + emits: ['selectGroups', 'unSelectAllGroups', 'toggleSelectGroup'], setup(props, { emit }) { const { $gettext } = useGettext() const { y: fileListHeaderY } = useFileListHeaderPosition('#admin-settings-app-bar') const contextMenuButtonRef = ref(undefined) + const sortBy = ref<string>('displayName') + const sortDir = ref<string>(SortDir.Asc) + const filterTerm = ref<string>('') const isGroupSelected = (group) => { return props.selectedGroups.some((s) => s.id === group.id) @@ -140,6 +156,7 @@ export default defineComponent({ emit('unSelectAllGroups') emit('toggleSelectGroup', group) } + const showDetails = (group) => { if (!isGroupSelected(group)) { selectGroup(group) @@ -183,6 +200,36 @@ export default defineComponent({ const readOnlyLabel = computed(() => $gettext("This group is read-only and can't be edited")) + const filter = (groups: Group[], filterTerm: string) => { + if (!(filterTerm || '').trim()) { + return groups + } + const groupsSearchEngine = new Fuse(groups, { ...defaultFuseOptions, keys: ['displayName'] }) + return groupsSearchEngine.search(filterTerm).map((r) => r.item) + } + + const orderBy = (list, prop, desc) => { + return [...list].sort((a, b) => { + a = a[prop]?.toString() || '' + b = b[prop]?.toString() || '' + return desc ? b.localeCompare(a) : a.localeCompare(b) + }) + } + + const items = computed(() => { + return orderBy( + filter(props.groups, unref(filterTerm)), + unref(sortBy), + unref(sortDir) === SortDir.Desc + ) + }) + + const pagination = usePagination({ items }) + + watch(pagination.currentPage, () => { + emit('unSelectAllGroups') + }) + return { showDetails, rowClicked, @@ -192,15 +239,19 @@ export default defineComponent({ fileListHeaderY, contextMenuButtonRef, showEditPanel, - readOnlyLabel + readOnlyLabel, + filterTerm, + sortBy, + sortDir, + items, + ...pagination, + filter, + orderBy } }, data() { return { - sortBy: 'displayName', - sortDir: 'asc', - markInstance: null, - filterTerm: '' + markInstance: null } }, computed: { @@ -241,7 +292,7 @@ export default defineComponent({ ] }, allGroupsSelected() { - return this.groups.length === this.selectedGroups.length + return this.paginatedItems.length === this.selectedGroups.length }, footerTextTotal() { return this.$gettext('%{groupCount} groups in total', { @@ -250,25 +301,19 @@ export default defineComponent({ }, footerTextFilter() { return this.$gettext('%{groupCount} matching groups', { - groupCount: this.data.length.toString() + groupCount: this.items.length.toString() }) }, - data() { - return this.orderBy( - this.filter(this.groups, this.filterTerm), - this.sortBy, - this.sortDir === 'desc' - ) - }, highlighted() { return this.selectedGroups.map((group) => group.id) } }, watch: { - filterTerm() { + async filterTerm() { if (!this.markInstance) { return } + await this.$router.push({ ...this.$route, query: { ...this.$route.query, page: '1' } }) this.markInstance.unmark() this.markInstance.mark(this.filterTerm, { element: 'span', @@ -283,20 +328,6 @@ export default defineComponent({ }) }, methods: { - filter(groups, filterTerm) { - if (!(filterTerm || '').trim()) { - return groups - } - const groupsSearchEngine = new Fuse(groups, { ...defaultFuseOptions, keys: ['displayName'] }) - return groupsSearchEngine.search(filterTerm).map((r) => r.item) - }, - orderBy(list, prop, desc) { - return [...list].sort((a, b) => { - a = a[prop]?.toString() || '' - b = b[prop]?.toString() || '' - return desc ? b.localeCompare(a) : a.localeCompare(b) - }) - }, handleSort(event) { this.sortBy = event.sortBy this.sortDir = event.sortDir diff --git a/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue b/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue index 095e4a984d6..63139945f8c 100644 --- a/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue +++ b/packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue @@ -13,7 +13,7 @@ :sort-by="sortBy" :sort-dir="sortDir" :fields="fields" - :data="orderedSpaces" + :data="paginatedItems" :highlighted="highlighted" :sticky="true" :header-position="fileListHeaderY" @@ -29,7 +29,9 @@ :label="$gettext('Select all spaces')" :model-value="allSpacesSelected" hide-label - @update:model-value="$emit('toggleSelectAllSpaces')" + @update:model-value=" + allSpacesSelected ? $emit('unSelectAllSpaces') : $emit('selectSpaces', paginatedItems) + " /> </template> <template #select="{ item }"> @@ -105,6 +107,7 @@ </div> </template> <template #footer> + <pagination :pages="paginationPages" :current-page="currentPage" /> <div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s"> <p class="oc-text-muted">{{ footerTextTotal }}</p> <p v-if="filterTerm" class="oc-text-muted">{{ footerTextFilter }}</p> @@ -128,14 +131,16 @@ import { spaceRoleEditor, spaceRoleManager, spaceRoleViewer } from 'web-client/s import Mark from 'mark.js' import Fuse from 'fuse.js' import { useGettext } from 'vue3-gettext' -import { eventBus } from 'web-pkg' +import { eventBus, SortDir } from 'web-pkg' import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' import ContextMenuQuickAction from 'web-pkg/src/components/ContextActions/ContextMenuQuickAction.vue' -import { useFileListHeaderPosition } from 'web-pkg/src/composables' +import { useFileListHeaderPosition, useRoute, useRouter } from 'web-pkg/src/composables' +import { usePagination } from 'web-app-admin-settings/src/composables' +import Pagination from 'web-pkg/src/components/Pagination.vue' export default defineComponent({ name: 'SpacesList', - components: { ContextMenuQuickAction }, + components: { ContextMenuQuickAction, Pagination }, props: { spaces: { type: Array as PropType<SpaceResource[]>, @@ -146,18 +151,20 @@ export default defineComponent({ required: true } }, - emits: ['toggleSelectSpace', 'toggleSelectAllSpaces', 'unSelectAllSpaces'], + emits: ['toggleSelectSpace', 'selectSpaces', 'unSelectAllSpaces'], setup: function (props, { emit }) { + const router = useRouter() + const route = useRoute() const { $gettext, current: currentLanguage } = useGettext() + const { y: fileListHeaderY } = useFileListHeaderPosition('#admin-settings-app-bar') const contextMenuButtonRef = ref(undefined) const sortBy = ref('name') - const sortDir = ref('asc') + const sortDir = ref(SortDir.Asc) const filterTerm = ref('') const markInstance = ref(undefined) const tableRef = ref(undefined) - const allSpacesSelected = computed(() => props.spaces.length === props.selectedSpaces.length) const highlighted = computed(() => props.selectedSpaces.map((s) => s.id)) const footerTextTotal = computed(() => { return $gettext('%{spaceCount} spaces in total', { @@ -166,7 +173,7 @@ export default defineComponent({ }) const footerTextFilter = computed(() => { return $gettext('%{spaceCount} matching spaces', { - spaceCount: unref(orderedSpaces).length.toString() + spaceCount: unref(items).length.toString() }) }) @@ -206,9 +213,24 @@ export default defineComponent({ : a.localeCompare(b, undefined, { numeric }) }) } - const orderedSpaces = computed(() => - orderBy(filter(props.spaces, unref(filterTerm)), unref(sortBy), unref(sortDir) === 'desc') + const items = computed(() => + orderBy( + filter(props.spaces, unref(filterTerm)), + unref(sortBy), + unref(sortDir) === SortDir.Desc + ) ) + + const pagination = usePagination({ items }) + + watch(pagination.currentPage, () => { + emit('unSelectAllSpaces') + }) + + const allSpacesSelected = computed(() => { + return unref(pagination.paginatedItems).length === props.selectedSpaces.length + }) + const handleSort = (event) => { sortBy.value = event.sortBy sortDir.value = event.sortDir @@ -347,11 +369,12 @@ export default defineComponent({ }) }) - watch(filterTerm, () => { + watch(filterTerm, async () => { const instance = unref(markInstance) if (!instance) { return } + await router.push({ ...unref(route), query: { ...unref(route).query, page: '1' } }) instance.unmark() instance.mark(unref(filterTerm), { element: 'span', @@ -417,7 +440,6 @@ export default defineComponent({ getMemberCount, getSelectSpaceLabel, handleSort, - orderedSpaces, fileClicked, isSpaceSelected, contextMenuButtonRef, @@ -425,7 +447,9 @@ export default defineComponent({ showContextMenuOnRightClick, spaceDetailsLabel, showDetailsForSpace, - fileListHeaderY + fileListHeaderY, + items, + ...pagination } } }) diff --git a/packages/web-app-admin-settings/src/components/Users/UsersList.vue b/packages/web-app-admin-settings/src/components/Users/UsersList.vue index 4302ad70990..6776f33eaa7 100644 --- a/packages/web-app-admin-settings/src/components/Users/UsersList.vue +++ b/packages/web-app-admin-settings/src/components/Users/UsersList.vue @@ -20,7 +20,7 @@ :sort-by="sortBy" :sort-dir="sortDir" :fields="fields" - :data="data" + :data="paginatedItems" :highlighted="highlighted" :sticky="true" :header-position="fileListHeaderY" @@ -36,7 +36,9 @@ :label="$gettext('Select all users')" :model-value="allUsersSelected" hide-label - @update:model-value="$emit('toggleSelectAllUsers')" + @update:model-value=" + allUsersSelected ? $emit('unSelectAllUsers') : $emit('selectUsers', paginatedItems) + " /> </template> <template #select="{ item }"> @@ -97,6 +99,7 @@ </context-menu-quick-action> </template> <template #footer> + <pagination :pages="paginationPages" :current-page="currentPage" /> <div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s"> <p class="oc-text-muted">{{ footerTextTotal }}</p> <p v-if="filterTerm" class="oc-text-muted">{{ footerTextFilter }}</p> @@ -107,19 +110,23 @@ </template> <script lang="ts"> -import { defineComponent, PropType, ref, unref, ComponentPublicInstance } from 'vue' +import { useGettext } from 'vue3-gettext' +import { defineComponent, PropType, ref, unref, ComponentPublicInstance, watch } from 'vue' import Fuse from 'fuse.js' import Mark from 'mark.js' -import { defaultFuseOptions, displayPositionedDropdown, eventBus } from 'web-pkg' +import { defaultFuseOptions, displayPositionedDropdown, eventBus, SortDir } from 'web-pkg' import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' import { AppRole, User } from 'web-client/src/generated' import ContextMenuQuickAction from 'web-pkg/src/components/ContextActions/ContextMenuQuickAction.vue' import NoContentMessage from 'web-pkg/src/components/NoContentMessage.vue' import { useFileListHeaderPosition } from 'web-pkg/src/composables' +import Pagination from 'web-pkg/src/components/Pagination.vue' +import { usePagination } from 'web-app-admin-settings/src/composables' +import { computed } from 'vue' export default defineComponent({ name: 'UsersList', - components: { ContextMenuQuickAction, NoContentMessage }, + components: { ContextMenuQuickAction, NoContentMessage, Pagination }, props: { users: { type: Array as PropType<User[]>, @@ -134,13 +141,20 @@ export default defineComponent({ required: true } }, - emits: ['unSelectAllUsers', 'toggleSelectAllUsers', 'toggleSelectUser'], + emits: ['unSelectAllUsers', 'selectUsers', 'toggleSelectUser'], setup(props, { emit }) { + const { $gettext } = useGettext() + const contextMenuButtonRef = ref(undefined) + const filterTerm = ref<string>('') + const sortBy = ref<string>('onPremisesSamAccountName') + const sortDir = ref<string>(SortDir.Asc) const { y: fileListHeaderY } = useFileListHeaderPosition('#admin-settings-app-bar') + const isUserSelected = (user) => { return props.selectedUsers.some((s) => s.id === user.id) } + const selectUser = (user) => { emit('unSelectAllUsers') emit('toggleSelectUser', user) @@ -195,6 +209,64 @@ export default defineComponent({ displayPositionedDropdown(dropdown._tippy, event, unref(contextMenuButtonRef)) } + const filter = (users: User[], filterTerm: string) => { + if (!(filterTerm || '').trim()) { + return users + } + const usersSearchEngine = new Fuse(users, { + ...defaultFuseOptions, + keys: ['displayName', 'mail', 'onPremisesSamAccountName', 'role.displayName'] + }) + + return usersSearchEngine.search(filterTerm).map((r) => r.item) + } + + const getRoleDisplayNameByUser = (user: User) => { + const assignedRole = user.appRoleAssignments[0] + + return ( + $gettext( + props.roles.find((role) => role.id === assignedRole?.appRoleId)?.displayName || '' + ) || '-' + ) + } + + const orderBy = (list, prop, desc) => { + return [...list].sort((user1, user2) => { + let a, b + + switch (prop) { + case 'role': + a = getRoleDisplayNameByUser(user1) + b = getRoleDisplayNameByUser(user2) + break + case 'accountEnabled': + a = ('accountEnabled' in user1 ? user1.accountEnabled : true).toString() + b = ('accountEnabled' in user2 ? user2.accountEnabled : true).toString() + break + default: + a = user1[prop] || '' + b = user2[prop] || '' + } + + return desc ? b.localeCompare(a) : a.localeCompare(b) + }) + } + + const items = computed(() => { + return orderBy( + filter(props.users, unref(filterTerm)), + unref(sortBy), + unref(sortDir) === SortDir.Desc + ) + }) + + const pagination = usePagination({ items }) + + watch(pagination.currentPage, () => { + emit('unSelectAllUsers') + }) + return { showDetails, showEditPanel, @@ -204,20 +276,25 @@ export default defineComponent({ contextMenuButtonRef, showContextMenuOnBtnClick, showContextMenuOnRightClick, - fileListHeaderY + fileListHeaderY, + getRoleDisplayNameByUser, + items, + filterTerm, + sortBy, + sortDir, + ...pagination, + filter, + orderBy } }, data() { return { - sortBy: 'onPremisesSamAccountName', - sortDir: 'asc', - markInstance: null, - filterTerm: '' + markInstance: null } }, computed: { allUsersSelected() { - return this.users.length === this.selectedUsers.length + return this.paginatedItems.length === this.selectedUsers.length }, footerTextTotal() { return this.$gettext('%{userCount} users in total', { @@ -226,7 +303,7 @@ export default defineComponent({ }, footerTextFilter() { return this.$gettext('%{userCount} matching users', { - userCount: this.data.length.toString() + userCount: this.items.length.toString() }) }, fields() { @@ -280,78 +357,33 @@ export default defineComponent({ } ] }, - data() { - return this.orderBy( - this.filter(this.users, this.filterTerm), - this.sortBy, - this.sortDir === 'desc' - ) - }, highlighted() { return this.selectedUsers.map((user) => user.id) } }, watch: { - filterTerm() { - if (this.$refs.tableRef) { - this.markInstance = new Mark((this.$refs.tableRef as ComponentPublicInstance).$el) - this.markInstance.unmark() - this.markInstance.mark(this.filterTerm, { - element: 'span', - className: 'highlight-mark', - exclude: ['th *', 'tfoot *'] - }) + async filterTerm() { + if (!this.$refs.tableRef) { + return } + + await this.$router.push({ ...this.$route, query: { ...this.$route.query, page: '1' } }) + this.markInstance = new Mark((this.$refs.tableRef as ComponentPublicInstance).$el) + this.markInstance.unmark() + this.markInstance.mark(this.filterTerm, { + element: 'span', + className: 'highlight-mark', + exclude: ['th *', 'tfoot *'] + }) } }, methods: { - filter(users, filterTerm) { - if (!(filterTerm || '').trim()) { - return users - } - const usersSearchEngine = new Fuse(users, { - ...defaultFuseOptions, - keys: ['displayName', 'mail', 'onPremisesSamAccountName', 'role.displayName'] - }) - - return usersSearchEngine.search(filterTerm).map((r) => r.item) - }, - orderBy(list, prop, desc) { - return [...list].sort((user1, user2) => { - let a, b - - switch (prop) { - case 'role': - a = this.getRoleDisplayNameByUser(user1) - b = this.getRoleDisplayNameByUser(user2) - break - case 'accountEnabled': - a = ('accountEnabled' in user1 ? user1.accountEnabled : true).toString() - b = ('accountEnabled' in user2 ? user2.accountEnabled : true).toString() - break - default: - a = user1[prop] || '' - b = user2[prop] || '' - } - - return desc ? b.localeCompare(a) : a.localeCompare(b) - }) - }, handleSort(event) { this.sortBy = event.sortBy this.sortDir = event.sortDir }, getSelectUserLabel(user) { return this.$gettext('Select %{ user }', { user: user.displayName }, true) - }, - getRoleDisplayNameByUser(user) { - const assignedRole = user.appRoleAssignments[0] - - return ( - this.$gettext( - this.roles.find((role) => role.id === assignedRole?.appRoleId)?.displayName || '' - ) || '-' - ) } } }) diff --git a/packages/web-app-admin-settings/src/composables/index.ts b/packages/web-app-admin-settings/src/composables/index.ts new file mode 100644 index 00000000000..444987e2db2 --- /dev/null +++ b/packages/web-app-admin-settings/src/composables/index.ts @@ -0,0 +1,2 @@ +export * from './actions' +export * from './usePagination' diff --git a/packages/web-app-admin-settings/src/composables/usePagination.ts b/packages/web-app-admin-settings/src/composables/usePagination.ts new file mode 100644 index 00000000000..37754214fec --- /dev/null +++ b/packages/web-app-admin-settings/src/composables/usePagination.ts @@ -0,0 +1,44 @@ +import { computed, Ref, unref } from 'vue' +import { queryItemAsString, useRouteQuery, useRouteQueryPersisted } from 'web-pkg/src' + +export const usePagination = ({ + items +}: { + items: Ref<any[]> +}): { + currentPage: Ref<number> + itemsPerPage: Ref<number> + paginationPages: Ref<number> + paginatedItems: Ref<any[]> +} => { + const itemsPerPageQuery = useRouteQueryPersisted({ + name: 'admin-settings-items-per-page', + defaultValue: '50' + }) + const pageQuery = useRouteQuery('page', '1') + + const currentPage = computed(() => { + return parseInt(queryItemAsString(unref(pageQuery))) + }) + + const itemsPerPage = computed(() => { + return parseInt(queryItemAsString(unref(itemsPerPageQuery))) + }) + + const paginationPages = computed(() => { + return Math.ceil(unref(items).length / unref(itemsPerPage)) + }) + + const paginatedItems = computed(() => { + const startIndex = (unref(currentPage) - 1) * unref(itemsPerPage) + const endIndex = startIndex + unref(itemsPerPage) + return unref(items).slice(startIndex, endIndex) + }) + + return { + currentPage, + itemsPerPage, + paginatedItems, + paginationPages + } +} diff --git a/packages/web-app-admin-settings/src/views/Groups.vue b/packages/web-app-admin-settings/src/views/Groups.vue index 6fd699c6db5..03b3455f1e8 100644 --- a/packages/web-app-admin-settings/src/views/Groups.vue +++ b/packages/web-app-admin-settings/src/views/Groups.vue @@ -50,7 +50,7 @@ :groups="groups" :selected-groups="selectedGroups" @toggle-select-group="toggleSelectGroup" - @toggle-select-all-groups="toggleSelectAllGroups" + @select-groups="selectGroups" @un-select-all-groups="unselectAllGroups" > <template #contextMenu> @@ -160,11 +160,6 @@ export default defineComponent({ } ] }, - - allGroupsSelected() { - return this.groups.length === this.selectedGroups.length - }, - sideBarAvailablePanels() { return [ { @@ -205,11 +200,8 @@ export default defineComponent({ methods: { ...mapActions(['showMessage']), - toggleSelectAllGroups() { - if (this.allGroupsSelected) { - return (this.selectedGroups = []) - } - this.selectedGroups = [...this.groups] + selectGroups(groups) { + this.selectedGroups = [...groups] }, toggleSelectGroup(toggledGroup) { const isGroupSelected = this.selectedGroups.find((group) => group.id === toggledGroup.id) diff --git a/packages/web-app-admin-settings/src/views/Spaces.vue b/packages/web-app-admin-settings/src/views/Spaces.vue index c460285997b..9853d55abe6 100644 --- a/packages/web-app-admin-settings/src/views/Spaces.vue +++ b/packages/web-app-admin-settings/src/views/Spaces.vue @@ -58,7 +58,7 @@ :class="{ 'spaces-table-squashed': sideBarOpen }" :selected-spaces="selectedSpaces" @toggle-select-space="toggleSelectSpace" - @toggle-select-all-spaces="toggleSelectAllSpaces" + @select-spaces="selectSpaces" @un-select-all-spaces="unselectAllSpaces" > <template #contextMenu> @@ -162,13 +162,8 @@ export default defineComponent({ } ]) - const allSpacesSelected = computed(() => unref(spaces).length === unref(selectedSpaces).length) - const toggleSelectAllSpaces = () => { - if (unref(allSpacesSelected)) { - selectedSpaces.value = [] - return - } - selectedSpaces.value = [...unref(spaces)] + const selectSpaces = (paginatedSpaces) => { + selectedSpaces.value = [...paginatedSpaces] } const toggleSelectSpace = (toggledSpace) => { @@ -292,7 +287,7 @@ export default defineComponent({ selectedSpaces, sideBarAvailablePanels, template, - toggleSelectAllSpaces, + selectSpaces, toggleSelectSpace, unselectAllSpaces, quotaModalIsOpen, diff --git a/packages/web-app-admin-settings/src/views/Users.vue b/packages/web-app-admin-settings/src/views/Users.vue index 3f14bd1896f..4d31eee63c8 100644 --- a/packages/web-app-admin-settings/src/views/Users.vue +++ b/packages/web-app-admin-settings/src/views/Users.vue @@ -49,7 +49,7 @@ :class="{ 'users-table-squashed': sideBarOpen }" :selected-users="selectedUsers" @toggle-select-user="toggleSelectUser" - @toggle-select-all-users="toggleSelectAllUsers" + @select-users="selectUsers" @un-select-all-users="unselectAllUsers" > <template #contextMenu> @@ -164,7 +164,9 @@ import { useCapabilitySpacesMaxQuota, useClientService, useLoadingService, + useRoute, useRouteQuery, + useRouter, useStore } from 'web-pkg/src/composables' import { computed, defineComponent, ref, onBeforeUnmount, onMounted, unref, watch } from 'vue' @@ -208,6 +210,8 @@ export default defineComponent({ }, setup() { const { $gettext, $ngettext } = useGettext() + const router = useRouter() + const route = useRoute() const store = useStore() const accessToken = useAccessToken({ store }) const clientService = useClientService() @@ -338,17 +342,23 @@ export default defineComponent({ Object.assign(user, data) }) + const resetPagination = () => { + return router.push({ ...unref(route), query: { ...unref(route).query, page: '1' } }) + } + const filterGroups = (groups) => { filters.groups.ids.value = groups.map((g) => g.id) loadUsersTask.perform() selectedUsers.value = [] additionalUserDataLoadedForUserIds.value = [] + return resetPagination() } const filterRoles = (roles) => { filters.roles.ids.value = roles.map((r) => r.id) loadUsersTask.perform() selectedUsers.value = [] additionalUserDataLoadedForUserIds.value = [] + return resetPagination() } const selectedPersonalDrives = ref([]) @@ -639,11 +649,6 @@ export default defineComponent({ } ] }, - - allUsersSelected() { - return this.users.length === this.selectedUsers.length - }, - sideBarAvailablePanels() { return [ { @@ -680,11 +685,8 @@ export default defineComponent({ ...mapActions(['showMessage']), ...mapMutations('runtime/spaces', ['UPDATE_SPACE_FIELD']), - toggleSelectAllUsers() { - if (this.allUsersSelected) { - return (this.selectedUsers = []) - } - this.selectedUsers = this.users + selectUsers(users) { + this.selectedUsers = users }, toggleSelectUser(toggledUser) { const isUserSelected = this.selectedUsers.find((user) => user.id === toggledUser.id) diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/GroupsList.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/GroupsList.spec.ts index 06b1446f58a..269ef50ad43 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Groups/GroupsList.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/GroupsList.spec.ts @@ -1,6 +1,6 @@ import GroupsList from '../../../../src/components/Groups/GroupsList.vue' -import { defaultPlugins, mount, shallowMount } from 'web-test-helpers' -import { displayPositionedDropdown, eventBus } from 'web-pkg' +import { defaultComponentMocks, defaultPlugins, mount, shallowMount } from 'web-test-helpers' +import { displayPositionedDropdown, eventBus, queryItemAsString } from 'web-pkg' import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' const getGroupMocks = () => [ @@ -12,6 +12,7 @@ jest.mock('web-pkg/src/helpers', () => ({ ...jest.requireActual('web-pkg/src/helpers'), displayPositionedDropdown: jest.fn() })) +jest.mock('web-pkg/src/composables/appDefaults') describe('GroupsList', () => { describe('method "orderBy"', () => { @@ -86,6 +87,10 @@ describe('GroupsList', () => { }) function getWrapper({ mountType = shallowMount, props = {} } = {}) { + jest.mocked(queryItemAsString).mockImplementationOnce(() => '1') + jest.mocked(queryItemAsString).mockImplementationOnce(() => '100') + const mocks = defaultComponentMocks() + return { wrapper: mountType(GroupsList, { props: { @@ -96,6 +101,7 @@ function getWrapper({ mountType = shallowMount, props = {} } = {}) { }, global: { plugins: [...defaultPlugins()], + mocks, stubs: { OcCheckbox: true } diff --git a/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts index e2d67a431ea..cb9974aa674 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Spaces/SpacesList.spec.ts @@ -1,6 +1,6 @@ import SpacesList from '../../../../src/components/Spaces/SpacesList.vue' -import { defaultPlugins, mount, shallowMount } from 'web-test-helpers' -import { eventBus } from 'web-pkg' +import { defaultComponentMocks, defaultPlugins, mount, shallowMount } from 'web-test-helpers' +import { eventBus, queryItemAsString } from 'web-pkg' import { displayPositionedDropdown } from 'web-pkg/src/helpers' import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' import { nextTick } from 'vue' @@ -54,6 +54,7 @@ jest.mock('web-pkg/src/helpers', () => ({ ...jest.requireActual('web-pkg/src/helpers'), displayPositionedDropdown: jest.fn() })) +jest.mock('web-pkg/src/composables/appDefaults') describe('SpacesList', () => { it('should render all spaces in a table', () => { @@ -94,7 +95,7 @@ describe('SpacesList', () => { const { wrapper } = getWrapper({ spaces: spaceMocks }) wrapper.vm.filterTerm = 'Another' await nextTick() - expect(wrapper.vm.orderedSpaces).toEqual([spaceMocks[1]]) + expect(wrapper.vm.items).toEqual([spaceMocks[1]]) }) it('should show the context menu on right click', async () => { const spyDisplayPositionedDropdown = jest.mocked(displayPositionedDropdown) @@ -117,6 +118,10 @@ describe('SpacesList', () => { }) function getWrapper({ mountType = mount, spaces = [], selectedSpaces = [] } = {}) { + jest.mocked(queryItemAsString).mockImplementationOnce(() => '1') + jest.mocked(queryItemAsString).mockImplementationOnce(() => '100') + const mocks = defaultComponentMocks() + return { wrapper: mountType(SpacesList, { props: { @@ -126,6 +131,7 @@ function getWrapper({ mountType = mount, spaces = [], selectedSpaces = [] } = {} }, global: { plugins: [...defaultPlugins()], + mocks, stubs: { OcCheckbox: true } diff --git a/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap b/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap index cf25ad4c56d..ec82abe3028 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap +++ b/packages/web-app-admin-settings/tests/unit/components/Spaces/__snapshots__/SpacesList.spec.ts.snap @@ -151,6 +151,7 @@ exports[`SpacesList should render all spaces in a table 1`] = ` <tr class="oc-table-footer-row"> <td class="oc-table-footer-cell" colspan="11"> <!-- @slot Footer of the table --> + <!--v-if--> <div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s"> <p class="oc-text-muted">1 spaces in total</p> <!--v-if--> diff --git a/packages/web-app-admin-settings/tests/unit/components/Users/UsersList.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Users/UsersList.spec.ts index 376dbd6a21b..070bf3f27d3 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Users/UsersList.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Users/UsersList.spec.ts @@ -1,6 +1,6 @@ import UsersList from '../../../../src/components/Users/UsersList.vue' -import { defaultPlugins, mount, shallowMount } from 'web-test-helpers' -import { displayPositionedDropdown, eventBus } from 'web-pkg' +import { defaultComponentMocks, defaultPlugins, mount, shallowMount } from 'web-test-helpers' +import { displayPositionedDropdown, eventBus, queryItemAsString } from 'web-pkg' import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' const getUserMocks = () => [{ id: '1', displayName: 'jan' }] @@ -8,6 +8,7 @@ jest.mock('web-pkg/src/helpers', () => ({ ...jest.requireActual('web-pkg/src/helpers'), displayPositionedDropdown: jest.fn() })) +jest.mock('web-pkg/src/composables/appDefaults') describe('UsersList', () => { describe('computed method "allUsersSelected"', () => { @@ -142,6 +143,9 @@ describe('UsersList', () => { }) function getWrapper({ mountType = shallowMount, props = {} } = {}) { + jest.mocked(queryItemAsString).mockImplementationOnce(() => '1') + jest.mocked(queryItemAsString).mockImplementationOnce(() => '100') + const mocks = defaultComponentMocks() return { wrapper: mountType(UsersList, { props: { @@ -170,6 +174,7 @@ function getWrapper({ mountType = shallowMount, props = {} } = {}) { }, global: { plugins: [...defaultPlugins()], + mocks, stubs: { OcCheckbox: true } diff --git a/packages/web-app-admin-settings/tests/unit/composables/usePagination.spec.ts b/packages/web-app-admin-settings/tests/unit/composables/usePagination.spec.ts new file mode 100644 index 00000000000..d4720423c60 --- /dev/null +++ b/packages/web-app-admin-settings/tests/unit/composables/usePagination.spec.ts @@ -0,0 +1,49 @@ +import { ref, unref } from 'vue' +import { usePagination } from 'web-app-admin-settings/src/composables' +import { queryItemAsString } from 'web-pkg/src' +import { getComposableWrapper } from 'web-test-helpers' + +jest.mock('web-pkg/src/composables/appDefaults') +jest.mock('web-pkg/src/composables/router') + +describe('usePagination', () => { + describe('paginatedItems', () => { + const items = [1, 2, 3, 4, 5, 6] + + it.each([ + { currentPage: 1, itemsPerPage: 100, expected: [1, 2, 3, 4, 5, 6] }, + { currentPage: 1, itemsPerPage: 2, expected: [1, 2] }, + { currentPage: 2, itemsPerPage: 2, expected: [3, 4] } + ])('returns proper paginated items', ({ currentPage, itemsPerPage, expected }) => { + getWrapper({ + setup: ({ paginatedItems }) => { + expect(unref(paginatedItems)).toEqual(expected) + }, + items, + currentPage, + itemsPerPage + }) + }) + }) +}) + +function getWrapper({ + setup, + items, + currentPage, + itemsPerPage +}: { + setup: (instance: ReturnType<typeof usePagination>) => void + items: any[] + currentPage: number + itemsPerPage: number +}) { + jest.mocked(queryItemAsString).mockImplementationOnce(() => currentPage.toString()) + jest.mocked(queryItemAsString).mockImplementationOnce(() => itemsPerPage.toString()) + return { + wrapper: getComposableWrapper(() => { + const instance = usePagination({ items: ref(items) }) + setup(instance) + }) + } +} diff --git a/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts index 278ec07f048..7fc92b4f6e2 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts @@ -1,6 +1,6 @@ import Groups from '../../../src/views/Groups.vue' import { mockAxiosResolve, mockAxiosReject } from 'web-test-helpers/src/mocks' -import { mockDeep } from 'jest-mock-extended' +import { mock, mockDeep } from 'jest-mock-extended' import { ClientService, eventBus } from 'web-pkg/src' import { createStore, @@ -9,6 +9,7 @@ import { defaultStoreMockOptions, mount } from 'web-test-helpers' +import { Group } from 'web-client/src/generated' const selectors = { batchActionsStub: 'batch-actions-stub' } const getClientServiceMock = () => { @@ -118,25 +119,6 @@ describe('Groups view', () => { }) }) - describe('computed method "allGroupsSelected"', () => { - it('should be true if every group is selected', async () => { - const { wrapper } = getWrapper() - wrapper.vm.selectedGroups = [{ id: '1' }] - await wrapper.vm.loadResourcesTask.last - expect(wrapper.vm.allGroupsSelected).toBeTruthy() - }) - it('should be false if not every group is selected', async () => { - const clientService = getClientServiceMock() - clientService.graphAuthenticated.groups.listGroups.mockImplementation(() => - mockAxiosResolve({ value: [{ id: '1' }, { id: '2' }] }) - ) - const { wrapper } = getWrapper({ clientService }) - wrapper.vm.selectedGroups = [{ id: '1' }] - await wrapper.vm.loadResourcesTask.last - expect(wrapper.vm.allGroupsSelected).toBeFalsy() - }) - }) - describe('batch actions', () => { it('do not display when no group selected', async () => { const { wrapper } = getWrapper() @@ -153,7 +135,7 @@ describe('Groups view', () => { it('display when more than one groups selected', async () => { const { wrapper } = getWrapper() await wrapper.vm.loadResourcesTask.last - wrapper.vm.toggleSelectAllGroups() + wrapper.vm.selectGroups([mock<Group>({ groupTypes: [] }), mock<Group>({ groupTypes: [] })]) await wrapper.vm.$nextTick() expect(wrapper.find(selectors.batchActionsStub).exists()).toBeTruthy() }) diff --git a/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts index 97d0ac526cb..9d48f66f14c 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Spaces.spec.ts @@ -46,23 +46,13 @@ describe('Spaces view', () => { expect(wrapper.find(selectors.noContentMessageStub).exists()).toBeTruthy() }) describe('toggle selection', () => { - describe('toggleSelectAllSpaces method', () => { + describe('selectSpaces method', () => { it('selects all spaces', async () => { - const spaces = [{ name: 'Some Space' }] + const spaces = [{ name: 'Some Space' }, { name: 'Some other Space' }] const { wrapper } = getWrapper({ spaces }) await wrapper.vm.loadResourcesTask.last - wrapper.vm.toggleSelectAllSpaces() - expect(wrapper.vm.selectedSpaces).toEqual( - expect.arrayContaining([expect.objectContaining({ name: spaces[0].name })]) - ) - }) - it('de-selects all selected spaces', async () => { - const spaces = [{ name: 'Some Space' }] - const { wrapper } = getWrapper({ spaces }) - await wrapper.vm.loadResourcesTask.last - wrapper.vm.selectedSpaces = spaces - wrapper.vm.toggleSelectAllSpaces() - expect(wrapper.vm.selectedSpaces.length).toBe(0) + wrapper.vm.selectSpaces(spaces) + expect(wrapper.vm.selectedSpaces.length).toBe(spaces.length) }) }) describe('toggleSelectSpace method', () => { @@ -113,7 +103,7 @@ describe('Spaces view', () => { const spaces = [{ name: 'Some Space' }, { name: 'Some other Space' }] const { wrapper } = getWrapper({ spaces }) await wrapper.vm.loadResourcesTask.last - wrapper.vm.toggleSelectAllSpaces() + wrapper.vm.selectSpaces(spaces) await wrapper.vm.$nextTick() expect(wrapper.find(selectors.batchActionsStub).exists()).toBeTruthy() }) diff --git a/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts index 5996042a681..be2e28a233a 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Users.spec.ts @@ -316,25 +316,6 @@ describe('Users view', () => { }) }) - describe('computed method "allUsersSelected"', () => { - it('should be true if every user is selected', async () => { - const { wrapper } = getMountedWrapper() - wrapper.vm.selectedUsers = [{ id: '1' }] - await wrapper.vm.loadResourcesTask.last - expect(wrapper.vm.allUsersSelected).toBeTruthy() - }) - it('should be false if not every user is selected', async () => { - const clientService = getClientService() - clientService.graphAuthenticated.users.listUsers.mockImplementation(() => - mockAxiosResolve({ value: [{ id: '1' }, { id: '2' }] }) - ) - const { wrapper } = getMountedWrapper({ clientService }) - wrapper.vm.selectedUsers = [{ id: '1' }] - await wrapper.vm.loadResourcesTask.last - expect(wrapper.vm.allUsersSelected).toBeFalsy() - }) - }) - describe('batch actions', () => { it('do not display when no user selected', async () => { const { wrapper } = getMountedWrapper({ mountType: mount }) @@ -351,7 +332,7 @@ describe('Users view', () => { it('display when more than one users selected', async () => { const { wrapper } = getMountedWrapper({ mountType: mount }) await wrapper.vm.loadResourcesTask.last - wrapper.vm.toggleSelectAllUsers() + wrapper.vm.selectUsers([mock<User>(), mock<User>()]) await wrapper.vm.$nextTick() expect(wrapper.find('batch-actions-stub').exists()).toBeTruthy() }) diff --git a/packages/web-app-files/src/components/Search/List.vue b/packages/web-app-files/src/components/Search/List.vue index c31c0ba814c..973f82ebc66 100644 --- a/packages/web-app-files/src/components/Search/List.vue +++ b/packages/web-app-files/src/components/Search/List.vue @@ -79,7 +79,7 @@ import { mapMutations, mapGetters, mapActions } from 'vuex' import AppBar from '../AppBar/AppBar.vue' import { defineComponent, nextTick } from 'vue' import ListInfo from '../FilesList/ListInfo.vue' -import Pagination from '../FilesList/Pagination.vue' +import Pagination from 'web-pkg/src/components/Pagination.vue' import { useFileActions } from '../../composables/actions/files/useFileActions' import { searchLimit } from '../../search/sdk/list' import { Resource } from 'web-client' diff --git a/packages/web-app-files/src/views/Favorites.vue b/packages/web-app-files/src/views/Favorites.vue index 9c36c6681d3..c73225c0ae1 100644 --- a/packages/web-app-files/src/views/Favorites.vue +++ b/packages/web-app-files/src/views/Favorites.vue @@ -78,7 +78,7 @@ import QuickActions from '../components/FilesList/QuickActions.vue' import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue' import NoContentMessage from 'web-pkg/src/components/NoContentMessage.vue' import ListInfo from '../components/FilesList/ListInfo.vue' -import Pagination from '../components/FilesList/Pagination.vue' +import Pagination from 'web-pkg/src/components/Pagination.vue' import ContextActions from '../components/FilesList/ContextActions.vue' import { useResourcesViewDefaults, ViewModeConstants } from '../composables' import { computed, defineComponent } from 'vue' diff --git a/packages/web-app-files/src/views/shares/SharedViaLink.vue b/packages/web-app-files/src/views/shares/SharedViaLink.vue index 027e445f794..83d9d843ea1 100644 --- a/packages/web-app-files/src/views/shares/SharedViaLink.vue +++ b/packages/web-app-files/src/views/shares/SharedViaLink.vue @@ -71,11 +71,11 @@ import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue' import NoContentMessage from 'web-pkg/src/components/NoContentMessage.vue' import AppBar from '../../components/AppBar/AppBar.vue' import ListInfo from '../../components/FilesList/ListInfo.vue' -import Pagination from '../../components/FilesList/Pagination.vue' import ContextActions from '../../components/FilesList/ContextActions.vue' import SideBar from '../../components/SideBar/SideBar.vue' import FilesViewWrapper from '../../components/FilesViewWrapper.vue' import ResourceTable from '../../components/FilesList/ResourceTable.vue' +import Pagination from 'web-pkg/src/components/Pagination.vue' import { useResourcesViewDefaults } from '../../composables' import { defineComponent } from 'vue' diff --git a/packages/web-app-files/src/views/shares/SharedWithOthers.vue b/packages/web-app-files/src/views/shares/SharedWithOthers.vue index 804d33ab602..14ed1a249b0 100644 --- a/packages/web-app-files/src/views/shares/SharedWithOthers.vue +++ b/packages/web-app-files/src/views/shares/SharedWithOthers.vue @@ -70,7 +70,7 @@ import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue' import NoContentMessage from 'web-pkg/src/components/NoContentMessage.vue' import AppBar from '../../components/AppBar/AppBar.vue' import ListInfo from '../../components/FilesList/ListInfo.vue' -import Pagination from '../../components/FilesList/Pagination.vue' +import Pagination from 'web-pkg/src/components/Pagination.vue' import ContextActions from '../../components/FilesList/ContextActions.vue' import SideBar from '../../components/SideBar/SideBar.vue' import FilesViewWrapper from '../../components/FilesViewWrapper.vue' diff --git a/packages/web-app-files/src/views/spaces/GenericSpace.vue b/packages/web-app-files/src/views/spaces/GenericSpace.vue index 9b21164cac5..5f5c1322dec 100644 --- a/packages/web-app-files/src/views/spaces/GenericSpace.vue +++ b/packages/web-app-files/src/views/spaces/GenericSpace.vue @@ -153,7 +153,6 @@ import FilesViewWrapper from '../../components/FilesViewWrapper.vue' import KeyboardActions from '../../components/FilesList/KeyboardActions.vue' import ListInfo from '../../components/FilesList/ListInfo.vue' import NotFoundMessage from '../../components/FilesList/NotFoundMessage.vue' -import Pagination from '../../components/FilesList/Pagination.vue' import QuickActions from '../../components/FilesList/QuickActions.vue' import ResourceTable from '../../components/FilesList/ResourceTable.vue' import ResourceTiles from '../../components/FilesList/ResourceTiles.vue' @@ -161,6 +160,7 @@ import SideBar from '../../components/SideBar/SideBar.vue' import SpaceHeader from '../../components/Spaces/SpaceHeader.vue' import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue' import NoContentMessage from 'web-pkg/src/components/NoContentMessage.vue' +import Pagination from 'web-pkg/src/components/Pagination.vue' import { useRoute } from 'web-pkg/src/composables' import { useDocumentTitle } from 'web-pkg/src/composables/appDefaults/useDocumentTitle' import { ImageType } from 'web-pkg/src/constants' diff --git a/packages/web-app-files/src/views/spaces/GenericTrash.vue b/packages/web-app-files/src/views/spaces/GenericTrash.vue index 10d6fdaaef4..b9dbb6a2c57 100644 --- a/packages/web-app-files/src/views/spaces/GenericTrash.vue +++ b/packages/web-app-files/src/views/spaces/GenericTrash.vue @@ -67,11 +67,11 @@ import AppBar from '../../components/AppBar/AppBar.vue' import ContextActions from '../../components/FilesList/ContextActions.vue' import FilesViewWrapper from '../../components/FilesViewWrapper.vue' import ListInfo from '../../components/FilesList/ListInfo.vue' -import Pagination from '../../components/FilesList/Pagination.vue' import ResourceTable from '../../components/FilesList/ResourceTable.vue' import SideBar from '../../components/SideBar/SideBar.vue' import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue' import NoContentMessage from 'web-pkg/src/components/NoContentMessage.vue' +import Pagination from 'web-pkg/src/components/Pagination.vue' import { eventBus } from 'web-pkg/src/services/eventBus' import { useResourcesViewDefaults } from '../../composables' diff --git a/packages/web-pkg/src/components/ItemFilter.vue b/packages/web-pkg/src/components/ItemFilter.vue index c045bce7af2..86302d82af8 100644 --- a/packages/web-pkg/src/components/ItemFilter.vue +++ b/packages/web-pkg/src/components/ItemFilter.vue @@ -115,7 +115,7 @@ export default defineComponent({ const queryParam = `q_${props.filterName}` const currentRouteQuery = useRouteQuery(queryParam) const setRouteQuery = () => { - router.push({ + return router.push({ query: { ...omit(unref(currentRoute).query, [queryParam]), ...(!!unref(selectedItems).length && { @@ -136,7 +136,7 @@ export default defineComponent({ const isSelectionAllowed = (item) => { return props.allowMultiple || !unref(selectedItems).length || isItemSelected(item) } - const toggleItemSelection = (item) => { + const toggleItemSelection = async (item) => { if (!isSelectionAllowed(item)) { return } @@ -145,8 +145,8 @@ export default defineComponent({ } else { selectedItems.value.push(item) } + await setRouteQuery() emit('selectionChange', unref(selectedItems)) - setRouteQuery() } const sortItems = (items) => { @@ -247,4 +247,3 @@ export default defineComponent({ } } </style> - diff --git a/packages/web-app-files/src/components/FilesList/Pagination.vue b/packages/web-pkg/src/components/Pagination.vue similarity index 100% rename from packages/web-app-files/src/components/FilesList/Pagination.vue rename to packages/web-pkg/src/components/Pagination.vue diff --git a/packages/web-app-files/tests/unit/components/FilesList/Pagination.spec.ts b/packages/web-pkg/tests/unit/components/Pagination.spec.ts similarity index 96% rename from packages/web-app-files/tests/unit/components/FilesList/Pagination.spec.ts rename to packages/web-pkg/tests/unit/components/Pagination.spec.ts index fd2d6b640c8..259d5604508 100644 --- a/packages/web-app-files/tests/unit/components/FilesList/Pagination.spec.ts +++ b/packages/web-pkg/tests/unit/components/Pagination.spec.ts @@ -1,6 +1,5 @@ import { mock } from 'jest-mock-extended' import _ from 'lodash' -import Pagination from 'web-app-files/src/components/FilesList/Pagination.vue' import { defaultPlugins, mount, @@ -9,6 +8,7 @@ import { defaultComponentMocks, RouteLocation } from 'web-test-helpers' +import Pagination from 'web-pkg/src/components/Pagination.vue' const filesPersonalRoute = { name: 'files-personal', path: '/files/home' }