From ed82de323be75afd1cbd8370f938308e7d4791e8 Mon Sep 17 00:00:00 2001 From: redsteadz Date: Mon, 17 Aug 2026 00:23:26 +0500 Subject: [PATCH 1/2] feat(files): show paths and locate search results Assisted-by: OpenCode:gpt-5.6-sol Signed-off-by: redsteadz --- .../src/actions/viewInFolderAction.spec.ts | 48 ++++++++---- apps/files/src/actions/viewInFolderAction.ts | 12 +-- .../FilesSidebar/FilesSidebarSubname.spec.ts | 78 +++++++++++++++++++ .../FilesSidebar/FilesSidebarSubname.vue | 4 + 4 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 apps/files/src/components/FilesSidebar/FilesSidebarSubname.spec.ts diff --git a/apps/files/src/actions/viewInFolderAction.spec.ts b/apps/files/src/actions/viewInFolderAction.spec.ts index 8287704d0784d..a74fb0787a9e8 100644 --- a/apps/files/src/actions/viewInFolderAction.spec.ts +++ b/apps/files/src/actions/viewInFolderAction.spec.ts @@ -19,6 +19,27 @@ const viewFiles = { name: 'Files', } as IView +const viewSearch = { + id: 'search', + name: 'Search', +} as IView + +const rootFolder = new Folder({ + id: 10, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/', + owner: 'admin', + permissions: Permission.ALL, + root: '/files/admin', +}) + +const nestedFolder = new Folder({ + id: 11, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar/', + owner: 'admin', + permissions: Permission.ALL, + root: '/files/admin', +}) + describe('View in folder action conditions tests', () => { test('Default values', () => { expect(action.id).toBe('view-in-folder') @@ -41,10 +62,10 @@ describe('View in folder action conditions tests', () => { }) describe('View in folder action enabled tests', () => { - test('Enabled for trashbin', () => { + test('Enabled when search result is shown outside its parent folder', () => { const file = new File({ id: 1, - source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar/foobar.txt', owner: 'admin', mime: 'text/plain', permissions: Permission.ALL, @@ -54,16 +75,16 @@ describe('View in folder action enabled tests', () => { expect(action.enabled).toBeDefined() expect(action.enabled!({ nodes: [file], - view, - folder: {} as Folder, + view: viewSearch, + folder: rootFolder, contents: [], })).toBe(true) }) - test('Disabled for files', () => { + test('Disabled when file is already shown in its parent folder', () => { const file = new File({ id: 1, - source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar/foobar.txt', owner: 'admin', mime: 'text/plain', permissions: Permission.ALL, @@ -74,7 +95,7 @@ describe('View in folder action enabled tests', () => { expect(action.enabled!({ nodes: [file], view: viewFiles, - folder: {} as Folder, + folder: nestedFolder, contents: [], })).toBe(false) }) @@ -93,7 +114,7 @@ describe('View in folder action enabled tests', () => { expect(action.enabled!({ nodes: [file], view, - folder: {} as Folder, + folder: nestedFolder, contents: [], })).toBe(false) }) @@ -111,7 +132,7 @@ describe('View in folder action enabled tests', () => { expect(action.enabled!({ nodes: [file], view, - folder: {} as Folder, + folder: nestedFolder, contents: [], })).toBe(false) }) @@ -136,7 +157,7 @@ describe('View in folder action enabled tests', () => { expect(action.enabled!({ nodes: [file1, file2], view, - folder: {} as Folder, + folder: nestedFolder, contents: [], })).toBe(false) }) @@ -154,16 +175,17 @@ describe('View in folder action enabled tests', () => { expect(action.enabled!({ nodes: [folder], view, - folder: {} as Folder, + folder: rootFolder, contents: [], })).toBe(false) }) test('Disabled for files outside the user root folder', () => { - const file = new Folder({ + const file = new File({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/trashbin/admin/trash/image.jpg.d1731053878', owner: 'admin', + mime: 'image/jpeg', permissions: Permission.READ, root: '/trashbin/admin', }) @@ -172,7 +194,7 @@ describe('View in folder action enabled tests', () => { expect(action.enabled!({ nodes: [file], view, - folder: {} as Folder, + folder: rootFolder, contents: [], })).toBe(false) }) diff --git a/apps/files/src/actions/viewInFolderAction.ts b/apps/files/src/actions/viewInFolderAction.ts index ea86e1f6e508a..451f67be68d3b 100644 --- a/apps/files/src/actions/viewInFolderAction.ts +++ b/apps/files/src/actions/viewInFolderAction.ts @@ -17,17 +17,12 @@ export const action: IFileAction = { }, iconSvgInline: () => FolderEyeSvg, - enabled({ nodes, view }) { + enabled({ nodes, folder }) { // Not enabled for public shares if (isPublicShare()) { return false } - // Only works outside of the main files view - if (view.id === 'files') { - return false - } - // Only works on single node if (nodes.length !== 1 || !nodes[0]) { return false @@ -43,6 +38,11 @@ export const action: IFileAction = { return false } + // Only show if not in same folder + if (folder.path === node.dirname) { + return false + } + if (node.permissions === Permission.NONE) { return false } diff --git a/apps/files/src/components/FilesSidebar/FilesSidebarSubname.spec.ts b/apps/files/src/components/FilesSidebar/FilesSidebarSubname.spec.ts new file mode 100644 index 0000000000000..440f053a038a4 --- /dev/null +++ b/apps/files/src/components/FilesSidebar/FilesSidebarSubname.spec.ts @@ -0,0 +1,78 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { File, Permission } from '@nextcloud/files' +import { enableAutoDestroy, shallowMount } from '@vue/test-utils' +import { afterEach, describe, expect, test } from 'vitest' +import FilesSidebarSubname from './FilesSidebarSubname.vue' + +enableAutoDestroy(afterEach) + +describe('FilesSidebarSubname', () => { + test('renders full path for nested file', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/Documents/Projects/report.pdf', + owner: 'admin', + mime: 'application/pdf', + permissions: Permission.READ, + root: '/files/admin', + size: 1024, + }) + + const wrapper = shallowMount(FilesSidebarSubname, { + propsData: { node: file }, + }) + + expect(wrapper.text()).toContain('/Documents/Projects/report.pdf') + }) + + test('renders full path for root-level file', () => { + const file = new File({ + id: 2, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/report.pdf', + owner: 'admin', + mime: 'application/pdf', + permissions: Permission.READ, + root: '/files/admin', + size: 1024, + }) + + const wrapper = shallowMount(FilesSidebarSubname, { + propsData: { node: file }, + }) + + expect(wrapper.text()).toContain('/report.pdf') + }) + + test('updates path when selected node changes', async () => { + const firstFile = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/First.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.READ, + root: '/files/admin', + }) + const secondFile = new File({ + id: 2, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/Documents/Second.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.READ, + root: '/files/admin', + }) + + const wrapper = shallowMount(FilesSidebarSubname, { + propsData: { node: firstFile }, + }) + expect(wrapper.text()).toContain('/First.txt') + + await wrapper.setProps({ node: secondFile }) + + expect(wrapper.text()).not.toContain('/First.txt') + expect(wrapper.text()).toContain('/Documents/Second.txt') + }) +}) diff --git a/apps/files/src/components/FilesSidebar/FilesSidebarSubname.vue b/apps/files/src/components/FilesSidebar/FilesSidebarSubname.vue index 5c3420ce48a31..88d8206c7875a 100644 --- a/apps/files/src/components/FilesSidebar/FilesSidebarSubname.vue +++ b/apps/files/src/components/FilesSidebar/FilesSidebarSubname.vue @@ -22,6 +22,10 @@ const size = computed(() => formatFileSize(props.node.size ?? 0))