From e419320209ff3bb55c8b9a928fcf5393dbfc9d8e Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Fri, 6 Aug 2021 15:46:45 +0200 Subject: [PATCH 01/58] Add details sidebar for multiple selection --- ...enhancement-add-multiple-selection-sidebar | 7 ++ .../SideBar/Details/FileDetailsMultiple.vue | 115 ++++++++++++++++++ packages/web-app-files/src/index.js | 77 +++++++++--- .../Details/FileDetailsMultiple.spec.js | 93 ++++++++++++++ 4 files changed, 274 insertions(+), 18 deletions(-) create mode 100644 changelog/unreleased/enhancement-add-multiple-selection-sidebar create mode 100644 packages/web-app-files/src/components/SideBar/Details/FileDetailsMultiple.vue create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Details/FileDetailsMultiple.spec.js diff --git a/changelog/unreleased/enhancement-add-multiple-selection-sidebar b/changelog/unreleased/enhancement-add-multiple-selection-sidebar new file mode 100644 index 00000000000..49b7f609108 --- /dev/null +++ b/changelog/unreleased/enhancement-add-multiple-selection-sidebar @@ -0,0 +1,7 @@ +Enhancement: Add multiple selection Sidebar + +We've changed the sidebar so if a user selects multiple files or folders +he sees a detailed view of his selection in the sidebar. + +https://github.com/owncloud/web/issues/5164 +https://github.com/owncloud/web/pull/5630 \ No newline at end of file diff --git a/packages/web-app-files/src/components/SideBar/Details/FileDetailsMultiple.vue b/packages/web-app-files/src/components/SideBar/Details/FileDetailsMultiple.vue new file mode 100644 index 00000000000..9eb28f344a6 --- /dev/null +++ b/packages/web-app-files/src/components/SideBar/Details/FileDetailsMultiple.vue @@ -0,0 +1,115 @@ + + + diff --git a/packages/web-app-files/src/index.js b/packages/web-app-files/src/index.js index ce609fa99f7..0e24ec32aee 100644 --- a/packages/web-app-files/src/index.js +++ b/packages/web-app-files/src/index.js @@ -11,6 +11,7 @@ import FilesDrop from './views/FilesDrop.vue' import LocationPicker from './views/LocationPicker.vue' import PublicFiles from './views/PublicFiles.vue' import FileDetails from './components/SideBar/Details/FileDetails.vue' +import FileDetailsMultiple from './components/SideBar/Details/FileDetailsMultiple.vue' import FileActions from './components/SideBar/Actions/FileActions.vue' import FileVersions from './components/SideBar/Versions/FileVersions.vue' import FileShares from './components/SideBar/Shares/FileShares.vue' @@ -19,6 +20,10 @@ import FileLinks from './components/SideBar/Links/FileLinks.vue' import translationsJson from '../l10n/translations.json' import quickActionsImport from './quickActions' import store from './store' +import { isTrashbinRoute } from './helpers/route' +import { FilterSearch, SDKSearch } from './search' +import { bus } from 'web-pkg/src/instance' +import { Registry } from './services' // just a dummy function to trick gettext tools function $gettext(msg) { @@ -32,52 +37,79 @@ const appInfo = { isFileEditor: false, extensions: [], fileSideBars: [ - { + // We don't have file details in the trashbin, yet. + // Only allow `actions` panel on trashbin route for now. + ({ route, multipleSelection }) => ({ app: 'details-item', icon: 'info_outline', component: FileDetails, - enabled() { - return true + default: !isTrashbinRoute(route), + get enabled() { + return !isTrashbinRoute(route) && !multipleSelection } - }, - { + }), + ({ route, multipleSelection }) => ({ + app: 'details-item', + icon: 'info_outline', + component: FileDetailsMultiple, + default: !isTrashbinRoute(route), + get enabled() { + return !isTrashbinRoute(route) && multipleSelection + } + }), + ({ route, multipleSelection }) => ({ app: 'actions-item', component: FileActions, icon: 'slideshow', - enabled() { - return true + default: isTrashbinRoute(route), + get enabled() { + return !multipleSelection } - }, - { + }), + ({ capabilities, route, multipleSelection }) => ({ app: 'sharing-item', icon: 'group', component: FileShares, - enabled(capabilities) { + get enabled() { + if (multipleSelection) return false + if (isTrashbinRoute(route)) { + return false + } + if (capabilities.files_sharing) { return capabilities.files_sharing.api_enabled } return false } - }, - { + }), + ({ capabilities, route, multipleSelection }) => ({ app: 'links-item', icon: 'link', component: FileLinks, - enabled(capabilities) { + get enabled() { + if (multipleSelection) return false + if (isTrashbinRoute(route)) { + return false + } + if (capabilities.files_sharing) { return capabilities.files_sharing.public.enabled } return false } - }, - { + }), + ({ capabilities, highlightedFile, route, multipleSelection }) => ({ app: 'versions-item', icon: 'file_version', component: FileVersions, - enabled(capabilities, highlightedFile) { + get enabled() { + if (multipleSelection) return false + if (isTrashbinRoute(route)) { + return false + } return !!capabilities.core && highlightedFile && highlightedFile.type !== 'folder' } - } + }) ] } const navItems = [ @@ -286,5 +318,14 @@ export default { routes, navItems, quickActions, - translations + translations, + mounted({ router: runtimeRouter, store: runtimeStore }) { + Registry.filterSearch = new FilterSearch(runtimeStore, runtimeRouter) + Registry.sdkSearch = new SDKSearch(runtimeStore, runtimeRouter) + + // when discussing the boot process of applications we need to implement a + // registry that does not rely on call order, aka first register "on" and only after emit. + bus.emit('app.search.register.provider', Registry.filterSearch) + bus.emit('app.search.register.provider', Registry.sdkSearch) + } } diff --git a/packages/web-app-files/tests/unit/components/SideBar/Details/FileDetailsMultiple.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Details/FileDetailsMultiple.spec.js new file mode 100644 index 00000000000..e284d18c8ee --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Details/FileDetailsMultiple.spec.js @@ -0,0 +1,93 @@ +import { createLocalVue, shallowMount } from '@vue/test-utils' +import Vuex from 'vuex' +import FileDetailsMultiple from 'packages/web-app-files/src/components/SideBar/Details/FileDetailsMultiple.vue' +import stubs from '../../../../../../../tests/unit/stubs' +import GetTextPlugin from 'vue-gettext' +import AsyncComputed from 'vue-async-computed' + +const localVue = createLocalVue() +localVue.use(Vuex) +localVue.use(AsyncComputed) +localVue.use(GetTextPlugin, { + translations: 'does-not-matter.json', + silent: true +}) + +const selectors = { + selectedFilesText: '[data-testid="selectedFilesText"]', + filesCount: '[data-testid="filesCount"]', + foldersCount: '[data-testid="foldersCount"]', + size: '[data-testid="size"]' +} + +const folderA = { + type: 'folder', + ownerId: 'marie', + ownerDisplayName: 'Marie', + mdate: 'Wed, 21 Oct 2015 07:28:00 GMT', + size: '740' +} +const folderB = { + type: 'folder', + ownerId: 'marie', + ownerDisplayName: 'Marie', + mdate: 'Wed, 21 Oct 2015 07:28:00 GMT', + size: '740' +} +const fileA = { + type: 'file', + ownerId: 'marie', + ownerDisplayName: 'Marie', + mdate: 'Wed, 21 Oct 2015 07:28:00 GMT', + size: '740' +} +const fileB = { + type: 'file', + ownerId: 'marie', + ownerDisplayName: 'Marie', + mdate: 'Wed, 21 Oct 2015 07:28:00 GMT', + size: '740' +} + +describe('Details Multiple Selection SideBar Item', () => { + it('should display information for two selected folders', () => { + const wrapper = createWrapper([folderA, folderB]) + expect(wrapper.find(selectors.selectedFilesText).text()).toBe('2 items selected') + expect(wrapper.find(selectors.filesCount).text()).toBe('Files 0') + expect(wrapper.find(selectors.foldersCount).text()).toBe('Folders 2') + expect(wrapper.find(selectors.size).text()).toBe('Size 1 KB') + }) + it('should display information for two selected files', () => { + const wrapper = createWrapper([fileA, fileB]) + expect(wrapper.find(selectors.selectedFilesText).text()).toBe('2 items selected') + expect(wrapper.find(selectors.filesCount).text()).toBe('Files 2') + expect(wrapper.find(selectors.foldersCount).text()).toBe('Folders 0') + expect(wrapper.find(selectors.size).text()).toBe('Size 1 KB') + }) + it('should display information for one selected file, one selected folder', () => { + const wrapper = createWrapper([fileA, folderA]) + expect(wrapper.find(selectors.selectedFilesText).text()).toBe('2 items selected') + expect(wrapper.find(selectors.filesCount).text()).toBe('Files 1') + expect(wrapper.find(selectors.foldersCount).text()).toBe('Folders 1') + expect(wrapper.find(selectors.size).text()).toBe('Size 1 KB') + }) +}) + +function createWrapper(testResource) { + return shallowMount(FileDetailsMultiple, { + store: new Vuex.Store({ + modules: { + Files: { + namespaced: true, + getters: { + selectedFiles: function() { + return testResource + } + } + } + } + }), + localVue, + stubs: stubs + }) +} From d938d953e259314cc98cb8b333546f46f8a0d6c2 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Fri, 6 Aug 2021 16:32:39 +0200 Subject: [PATCH 02/58] fixed merge issues --- .../src/components/SideBar/SideBar.vue | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/web-app-files/src/components/SideBar/SideBar.vue b/packages/web-app-files/src/components/SideBar/SideBar.vue index 345ed543b8e..e4c7e530ed2 100644 --- a/packages/web-app-files/src/components/SideBar/SideBar.vue +++ b/packages/web-app-files/src/components/SideBar/SideBar.vue @@ -1,6 +1,6 @@