refactor(files): port file list headers to new files registry

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2026-02-09 19:04:15 +01:00
parent 1fa470ef99
commit d669e6ed2b
No known key found for this signature in database
GPG key ID: 7E849AE05218500F
3 changed files with 56 additions and 27 deletions

View file

@ -9,7 +9,7 @@
</template>
<script lang="ts">
import type { Folder, Header, View } from '@nextcloud/files'
import type { Folder, IFileListHeader, View } from '@nextcloud/files'
import type { PropType } from 'vue'
import PQueue from 'p-queue'
@ -25,7 +25,7 @@ export default {
name: 'FilesListHeader',
props: {
header: {
type: Object as PropType<Header>,
type: Object as PropType<IFileListHeader>,
required: true,
},

View file

@ -3,39 +3,59 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { Header } from '@nextcloud/files'
import type { IFileListHeader } from '@nextcloud/files'
import type { registerFileListHeader } from '@nextcloud/files'
import type { ComputedRef } from 'vue'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useFileListHeaders } from './useFileListHeaders.ts'
import { nextTick } from 'vue'
const getFileListHeaders = vi.hoisted(() => vi.fn())
vi.mock('@nextcloud/files', async (originalModule) => {
return {
...(await originalModule()),
getFileListHeaders,
}
})
interface Context {
useFileListHeaders: () => ComputedRef<IFileListHeader[]>
registerFileListHeader: typeof registerFileListHeader
}
describe('useFileListHeaders', () => {
beforeEach(() => vi.resetAllMocks())
beforeEach(async (context: Context) => {
delete globalThis._nc_files_scope
// reset modules to reset internal variables (the headers ref) of the composable and the library (the scoped globals)
vi.resetModules()
context.useFileListHeaders = (await import('./useFileListHeaders.ts')).useFileListHeaders
context.registerFileListHeader = (await import('@nextcloud/files')).registerFileListHeader
})
it('gets the headers', () => {
const header = new Header({ id: '1', order: 5, render: vi.fn(), updated: vi.fn() })
getFileListHeaders.mockImplementationOnce(() => [header])
it<Context>('gets the headers', ({ useFileListHeaders, registerFileListHeader }) => {
const header: IFileListHeader = { id: '1', order: 5, render: vi.fn(), updated: vi.fn() }
registerFileListHeader(header)
const headers = useFileListHeaders()
expect(headers.value).toEqual([header])
expect(getFileListHeaders).toHaveBeenCalledOnce()
})
it('headers are sorted', () => {
const header = new Header({ id: '1', order: 10, render: vi.fn(), updated: vi.fn() })
const header2 = new Header({ id: '2', order: 5, render: vi.fn(), updated: vi.fn() })
getFileListHeaders.mockImplementationOnce(() => [header, header2])
it<Context>('headers are sorted', ({ useFileListHeaders, registerFileListHeader }) => {
const header: IFileListHeader = { id: '1', order: 10, render: vi.fn(), updated: vi.fn() }
const header2: IFileListHeader = { id: '2', order: 5, render: vi.fn(), updated: vi.fn() }
registerFileListHeader(header)
registerFileListHeader(header2)
const headers = useFileListHeaders()
// lower order first
expect(headers.value.map(({ id }) => id)).toStrictEqual(['2', '1'])
expect(getFileListHeaders).toHaveBeenCalledOnce()
})
it<Context>('composable is reactive', async ({ useFileListHeaders, registerFileListHeader }) => {
const header: IFileListHeader = { id: 'a', order: 10, render: vi.fn(), updated: vi.fn() }
registerFileListHeader(header)
await nextTick()
const headers = useFileListHeaders()
expect(headers.value.map(({ id }) => id)).toStrictEqual(['a'])
// now add a new header
const header2: IFileListHeader = { id: 'b', order: 5, render: vi.fn(), updated: vi.fn() }
registerFileListHeader(header2)
// reactive update, lower order first
await nextTick()
expect(headers.value.map(({ id }) => id)).toStrictEqual(['b', 'a'])
})
})

View file

@ -2,18 +2,27 @@
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Header } from '@nextcloud/files'
import type { IFileListHeader } from '@nextcloud/files'
import type { ComputedRef } from 'vue'
import { getFileListHeaders } from '@nextcloud/files'
import { getFileListHeaders, getFilesRegistry } from '@nextcloud/files'
import { computed, ref } from 'vue'
const headers = ref<IFileListHeader[]>()
const sorted = computed(() => [...(headers.value ?? [])].sort((a, b) => a.order - b.order) as IFileListHeader[])
/**
* Get the registered and sorted file list headers.
*/
export function useFileListHeaders(): ComputedRef<Header[]> {
const headers = ref(getFileListHeaders())
const sorted = computed(() => [...headers.value].sort((a, b) => a.order - b.order) as Header[])
export function useFileListHeaders(): ComputedRef<IFileListHeader[]> {
if (!headers.value) {
// if not initialized by other component yet, initialize and subscribe to registry changes
headers.value = getFileListHeaders()
getFilesRegistry().addEventListener('register:listHeader', () => {
headers.value = getFileListHeaders()
})
}
return sorted
}