mirror of
https://github.com/nextcloud/server.git
synced 2026-03-20 17:43:15 -04:00
feat(files): add "search everywhere" button within the filters row
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
b8d3e64205
commit
69275cbda5
6 changed files with 153 additions and 3 deletions
|
|
@ -0,0 +1,47 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<template>
|
||||
<NcButton v-show="isVisible" @click="onClick">
|
||||
{{ t('files', 'Search everywhere') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { ref } from 'vue'
|
||||
import NcButton from '@nextcloud/vue/components/NcButton'
|
||||
import { getPinia } from '../../store/index.ts'
|
||||
import { useSearchStore } from '../../store/search.ts'
|
||||
|
||||
const isVisible = ref(false)
|
||||
|
||||
defineExpose({
|
||||
hideButton,
|
||||
showButton,
|
||||
})
|
||||
|
||||
/**
|
||||
* Hide the button - called by the filter class
|
||||
*/
|
||||
function hideButton() {
|
||||
isVisible.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the button - called by the filter class
|
||||
*/
|
||||
function showButton() {
|
||||
isVisible.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Button click handler to make the filtering a global search.
|
||||
*/
|
||||
function onClick() {
|
||||
const searchStore = useSearchStore(getPinia())
|
||||
searchStore.scope = 'globally'
|
||||
}
|
||||
</script>
|
||||
|
|
@ -7,6 +7,8 @@ import type { IFileListFilterChip, INode } from '@nextcloud/files'
|
|||
|
||||
import { subscribe } from '@nextcloud/event-bus'
|
||||
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
|
||||
import { getPinia } from '../store/index.ts'
|
||||
import { useSearchStore } from '../store/search.ts'
|
||||
|
||||
/**
|
||||
* Register the filename filter
|
||||
|
|
@ -59,10 +61,14 @@ class FilenameFilter extends FileListFilter {
|
|||
this.updateQuery('')
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// make sure to also reset the search store when pressing the "X" on the filter chip
|
||||
const store = useSearchStore(getPinia())
|
||||
if (store.scope === 'filter') {
|
||||
store.query = ''
|
||||
}
|
||||
}
|
||||
this.updateChips(chips)
|
||||
// Emit the new query as it might have come not from the Navigation
|
||||
this.dispatchTypedEvent('update:query', new CustomEvent('update:query', { detail: query }))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
49
apps/files/src/filters/SearchFilter.ts
Normal file
49
apps/files/src/filters/SearchFilter.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { INode } from '@nextcloud/files'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
|
||||
import { subscribe } from '@nextcloud/event-bus'
|
||||
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
|
||||
import Vue from 'vue'
|
||||
import FileListFilterToSearch from '../components/FileListFilter/FileListFilterToSearch.vue'
|
||||
|
||||
class SearchFilter extends FileListFilter {
|
||||
|
||||
private currentInstance?: ComponentPublicInstance<typeof FileListFilterToSearch>
|
||||
|
||||
constructor() {
|
||||
super('files:filter-to-search', 999)
|
||||
subscribe('files:search:updated', ({ query, scope }) => {
|
||||
if (query && scope === 'filter') {
|
||||
this.currentInstance?.showButton()
|
||||
} else {
|
||||
this.currentInstance?.hideButton()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public mount(el: HTMLElement) {
|
||||
if (this.currentInstance) {
|
||||
this.currentInstance.$destroy()
|
||||
}
|
||||
|
||||
const View = Vue.extend(FileListFilterToSearch)
|
||||
this.currentInstance = new View().$mount(el) as unknown as ComponentPublicInstance<typeof FileListFilterToSearch>
|
||||
}
|
||||
|
||||
public filter(nodes: INode[]): INode[] {
|
||||
return nodes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a file list filter to only show hidden files if enabled by user config
|
||||
*/
|
||||
export function registerFilterToSearchToggle() {
|
||||
registerFileListFilter(new SearchFilter())
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ import { initLivePhotos } from './services/LivePhotos'
|
|||
import { isPublicShare } from '@nextcloud/sharing/public'
|
||||
import { registerConvertActions } from './actions/convertAction.ts'
|
||||
import { registerFilenameFilter } from './filters/FilenameFilter.ts'
|
||||
import { registerFilterToSearchToggle } from './filters/SearchFilter.ts'
|
||||
|
||||
// Register file actions
|
||||
registerConvertActions()
|
||||
|
|
@ -70,6 +71,7 @@ registerHiddenFilesFilter()
|
|||
registerTypeFilter()
|
||||
registerModifiedFilter()
|
||||
registerFilenameFilter()
|
||||
registerFilterToSearchToggle()
|
||||
|
||||
// Register preview service worker
|
||||
registerPreviewServiceWorker()
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ export const useSearchStore = defineStore('search', () => {
|
|||
function updateSearch() {
|
||||
// emit the search event to update the filter
|
||||
emit('files:search:updated', { query: query.value, scope: scope.value })
|
||||
|
||||
const router = window.OCP.Files.Router as RouterService
|
||||
|
||||
// if we are on the search view and the query was unset or scope was set to 'filter' we need to move back to the files view
|
||||
|
|
|
|||
|
|
@ -75,6 +75,53 @@ describe('files: search', () => {
|
|||
cy.get('[data-cy-files-list-row-fileid]').should('have.length', 2)
|
||||
})
|
||||
|
||||
it('See "search everywhere" button', () => {
|
||||
// Not visible initially
|
||||
cy.get('[data-cy-files-filters]')
|
||||
.findByRole('button', { name: /Search everywhere/i })
|
||||
.should('not.to.exist')
|
||||
|
||||
// add a filter
|
||||
navigation.searchInput().type('file')
|
||||
|
||||
// see its visible
|
||||
cy.get('[data-cy-files-filters]')
|
||||
.findByRole('button', { name: /Search everywhere/i })
|
||||
.should('be.visible')
|
||||
|
||||
// clear the filter
|
||||
navigation.searchClearButton().click()
|
||||
|
||||
// see its not visible again
|
||||
cy.get('[data-cy-files-filters]')
|
||||
.findByRole('button', { name: /Search everywhere/i })
|
||||
.should('not.to.exist')
|
||||
})
|
||||
|
||||
it('can make local search a global search', () => {
|
||||
navigateToFolder('some folder')
|
||||
getRowForFile('a file.txt').should('be.visible')
|
||||
|
||||
navigation.searchInput().type('file')
|
||||
|
||||
// see local results
|
||||
getRowForFile('a file.txt').should('be.visible')
|
||||
getRowForFile('a second file.txt').should('be.visible')
|
||||
cy.get('[data-cy-files-list-row-fileid]').should('have.length', 2)
|
||||
|
||||
// toggle global search
|
||||
cy.get('[data-cy-files-filters]')
|
||||
.findByRole('button', { name: /Search everywhere/i })
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
// see global results
|
||||
getRowForFile('file.txt').should('be.visible')
|
||||
getRowForFile('a file.txt').should('be.visible')
|
||||
getRowForFile('a second file.txt').should('be.visible')
|
||||
getRowForFile('another file.txt').should('be.visible')
|
||||
})
|
||||
|
||||
it('shows empty content when there are no results', () => {
|
||||
navigateToFolder('some folder')
|
||||
getRowForFile('a file.txt').should('be.visible')
|
||||
|
|
|
|||
Loading…
Reference in a new issue