diff --git a/changelog/unreleased/bugfix-right-sidebar-no-selection b/changelog/unreleased/bugfix-right-sidebar-no-selection new file mode 100644 index 00000000000..b880017af1d --- /dev/null +++ b/changelog/unreleased/bugfix-right-sidebar-no-selection @@ -0,0 +1,7 @@ +Bugfix: No selection info right sidebar + +We fixed that the right sidebar was not showing the "no selection" info panel anymore in the root of "All files". In addition we also use the same "no selection" info panel now in the root nodes of public links. + +https://github.com/owncloud/web/issues/6502 +https://github.com/owncloud/web/issues/6182 +https://github.com/owncloud/web/pull/6505 diff --git a/packages/web-app-files/src/components/SideBar/SideBar.vue b/packages/web-app-files/src/components/SideBar/SideBar.vue index b974dae2d0e..2dc65206f2d 100644 --- a/packages/web-app-files/src/components/SideBar/SideBar.vue +++ b/packages/web-app-files/src/components/SideBar/SideBar.vue @@ -92,7 +92,11 @@ import { VisibilityObserver } from 'web-pkg/src/observer' import { DavProperties } from 'web-pkg/src/constants' import { buildResource } from '../../helpers/resources' -import { isLocationCommonActive, isLocationSharesActive } from '../../router' +import { + isLocationCommonActive, + isLocationPublicActive, + isLocationSharesActive +} from '../../router' import { computed } from '@vue/composition-api' import FileInfo from './FileInfo.vue' @@ -120,7 +124,7 @@ export default { }, computed: { - ...mapGetters('Files', ['highlightedFile', 'selectedFiles', 'currentFolder']), + ...mapGetters('Files', ['highlightedFile', 'selectedFiles', 'publicLinkPassword']), ...mapGetters(['fileSideBars', 'capabilities']), ...mapState('Files/sidebar', { sidebarActivePanel: 'activePanel' }), activeAvailablePanelName() { @@ -189,7 +193,13 @@ export default { return this.selectedFiles && this.selectedFiles.length > 1 }, isRootFolder() { - return !this.highlightedFile?.path + const pathSegments = this.highlightedFile?.path?.split('/').filter(Boolean) || [] + if (isLocationPublicActive(this.$router, 'files-public-files')) { + // root node of a public link has the public link token as path + // root path `/` like for personal home doesn't exist for public links + return pathSegments.length === 1 + } + return !pathSegments.length }, highlightedFileThumbnail() { return this.highlightedFile?.thumbnail @@ -304,12 +314,22 @@ export default { this.selectedFile = this.highlightedFile return } + this.loading = true try { - const item = await this.$client.files.fileInfo( - this.highlightedFile.webDavPath, - DavProperties.Default - ) + let item + if (isLocationPublicActive(this.$router, 'files-public-files')) { + item = await this.$client.publicFiles.getFileInfo( + this.highlightedFile.webDavPath, + this.publicLinkPassword, + DavProperties.PublicLink + ) + } else { + item = await this.$client.files.fileInfo( + this.highlightedFile.webDavPath, + DavProperties.Default + ) + } this.selectedFile = buildResource(item) this.$set(this.selectedFile, 'thumbnail', this.highlightedFile.thumbnail || null) diff --git a/packages/web-app-files/tests/unit/components/SideBar/SideBar.spec.js b/packages/web-app-files/tests/unit/components/SideBar/SideBar.spec.js index 4fa37c00e6b..360b3b240e7 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/SideBar.spec.js +++ b/packages/web-app-files/tests/unit/components/SideBar/SideBar.spec.js @@ -12,6 +12,13 @@ import SideBar from '@files/src/components/SideBar/SideBar.vue' import { createLocationSpaces } from '../../../../src/router' jest.mock('web-pkg/src/observer') +jest.mock('@files/src/helpers/resources', () => { + const original = jest.requireActual('@files/src/helpers/resources') + return { + ...original, + buildResource: jest.fn() + } +}) const simpleOwnFolder = { type: 'folder', @@ -21,63 +28,151 @@ const simpleOwnFolder = { size: '740' } +const selectors = { + noSelectionInfoPanel: 'noselection-stub' +} + describe('SideBar', () => { - it('fetches file info if 1 item is selected', () => { - const mockFileInfo = jest.fn() - mockFileInfo.mockReturnValueOnce(Files['/'][1]) - - createWrapper({ - item: simpleOwnFolder, - selectedItems: [simpleOwnFolder], - mocks: { $client: { files: { fileInfo: mockFileInfo } } } + describe('file info', () => { + beforeEach(() => { + buildResource.mockImplementation((item) => item) }) + afterEach(() => { + jest.clearAllMocks() + }) + it('fetches file info if 1 item is selected', () => { + const mockFileInfo = jest.fn() + mockFileInfo.mockReturnValueOnce(Files['/'][1]) - expect(mockFileInfo).toHaveBeenCalledTimes(1) - }) - - it('fetches file info if the selected item changes', async () => { - const spyOnFetchFileInfo = jest - .spyOn(SideBar.methods, 'fetchFileInfo') - .mockImplementation(jest.fn) + createWrapper({ + item: simpleOwnFolder, + selectedItems: [simpleOwnFolder], + mocks: { $client: { files: { fileInfo: mockFileInfo } } } + }) - const wrapper = createWrapper({ - item: simpleOwnFolder, - selectedItems: [simpleOwnFolder] + expect(mockFileInfo).toHaveBeenCalledTimes(1) }) - // fetchFileInfo is called once in created() - expect(spyOnFetchFileInfo).toHaveBeenCalledTimes(1) + it('fetches file info if the selected item changes', async () => { + const spyOnFetchFileInfo = jest + .spyOn(SideBar.methods, 'fetchFileInfo') + .mockImplementation(jest.fn) + + const wrapper = createWrapper({ + item: simpleOwnFolder, + selectedItems: [simpleOwnFolder] + }) + + // fetchFileInfo is called once in created() + expect(spyOnFetchFileInfo).toHaveBeenCalledTimes(1) + + // it should be called again when a different file is loaded + const resource = Files['/'][4] + resource.path = `${resource.name}` + wrapper.vm.$store.commit('Files/SET_HIGHLIGHTED_FILE', resource) + await wrapper.vm.$nextTick() + expect(spyOnFetchFileInfo).toHaveBeenCalledTimes(2) + + // and again if the file is renamed + const renamedResource = renameResource(Object.assign({}, resource), 'foobar.png', '') + wrapper.vm.$store.commit('Files/SET_HIGHLIGHTED_FILE', Object.assign(renamedResource)) + await wrapper.vm.$nextTick() + expect(spyOnFetchFileInfo).toHaveBeenCalledTimes(3) + }) - // it should be called again when a different file is loaded - const resource = buildResource(Files['/'][4]) - resource.path = `${resource.name}` - wrapper.vm.$store.commit('Files/SET_HIGHLIGHTED_FILE', resource) - await wrapper.vm.$nextTick() - expect(spyOnFetchFileInfo).toHaveBeenCalledTimes(2) + it('does not fetch file info if multiple items are selected', () => { + const mockFileInfo = jest.fn().mockReturnValue({}) - // and again if the file is renamed - const renamedResource = renameResource(Object.assign({}, resource), 'foobar.png', '') - wrapper.vm.$store.commit('Files/SET_HIGHLIGHTED_FILE', Object.assign(renamedResource)) - await wrapper.vm.$nextTick() - expect(spyOnFetchFileInfo).toHaveBeenCalledTimes(3) + createWrapper({ + item: simpleOwnFolder, + selectedItems: [simpleOwnFolder, simpleOwnFolder], + mocks: { $client: { files: { fileInfo: mockFileInfo } } } + }) - jest.resetAllMocks() + expect(mockFileInfo).not.toHaveBeenCalled() + }) }) - it('does not fetch file info if multiple items are selected', () => { - const mockFileInfo = jest.fn() - - createWrapper({ - item: simpleOwnFolder, - selectedItems: [simpleOwnFolder, simpleOwnFolder], - mocks: { $client: { files: { fileInfo: mockFileInfo } } } + describe('no selection info panel', () => { + afterEach(() => { + jest.clearAllMocks() + }) + describe('for public links', () => { + it.each([ + [ + 'shows in root node', + { + path: '/publicLinkToken', + noSelectionExpected: true + } + ], + [ + 'does not show in non-root node', + { + path: '/publicLinkToken/some-folder', + noSelectionExpected: false + } + ] + ])('%s', async (name, { path, noSelectionExpected }) => { + const item = { path } + const mockFileInfo = jest.fn() + mockFileInfo.mockReturnValue(item) + buildResource.mockReturnValue(item) + const wrapper = createWrapper({ + item, + selectedItems: [], + mocks: { + $client: { publicFiles: { getFileInfo: mockFileInfo } } + }, + currentRouteName: 'files-public-files' + }) + await wrapper.vm.$nextTick() + await wrapper.vm.$nextTick() + expect(wrapper.find(selectors.noSelectionInfoPanel).exists()).toBe(noSelectionExpected) + }) + }) + describe('for all files', () => { + it.each([ + [ + 'shows in root node', + { + path: '/', + noSelectionExpected: true + } + ], + [ + 'does not show in non-root node', + { + path: '/some-folder', + noSelectionExpected: false + } + ] + ])('%s', async (name, { path, noSelectionExpected }) => { + const item = { path } + const mockFileInfo = jest.fn() + mockFileInfo.mockReturnValue(item) + buildResource.mockReturnValue(item) + const wrapper = createWrapper({ + item, + selectedItems: [], + mocks: { + $client: { files: { fileInfo: mockFileInfo } } + } + }) + await wrapper.vm.$nextTick() + await wrapper.vm.$nextTick() + expect(wrapper.find(selectors.noSelectionInfoPanel).exists()).toBe(noSelectionExpected) + }) }) - - expect(mockFileInfo).not.toHaveBeenCalled() }) }) -function createWrapper({ item, selectedItems, mocks }) { +function createWrapper({ + item, + selectedItems, + mocks, + currentRouteName = 'files-spaces-personal-home' +}) { const localVue = createLocalVue() localVue.use(Vuex) localVue.use(VueCompositionAPI) @@ -111,7 +206,8 @@ function createWrapper({ item, selectedItems, mocks }) { }, getters: { highlightedFile: (state) => state.highlightedFile, - selectedFiles: () => selectedItems + selectedFiles: () => selectedItems, + publicLinkPassword: () => '' }, mutations: { SET_HIGHLIGHTED_FILE(state, file) { @@ -137,7 +233,7 @@ function createWrapper({ item, selectedItems, mocks }) { mocks: merge( { $router: { - currentRoute: createLocationSpaces('files-spaces-personal-home'), + currentRoute: createLocationSpaces(currentRouteName), resolve: (r) => { return { href: r.name } }