Skip to content

Commit

Permalink
fix(files): File type filter UI sync with filter state
Browse files Browse the repository at this point in the history
When changing the folder the filter will be re-mounted by the file list,
so we need to pass the current state of the filter to the filter UI.

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Nov 17, 2024
1 parent 714fc63 commit 7be3fd3
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
12 changes: 12 additions & 0 deletions apps/files/src/components/FileListFilter/FileListFilterType.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export default defineComponent({
},

props: {
presets: {
type: Array as PropType<ITypePreset[]>,
default: () => [],
},
typePresets: {
type: Array as PropType<ITypePreset[]>,
required: true,
Expand All @@ -71,6 +75,10 @@ export default defineComponent({
},

watch: {
/** Reset selected options if property is changed */
preset() {
this.selectedOptions = this.presets ?? []
},
selectedOptions(newValue, oldValue) {
if (this.selectedOptions.length === 0) {
if (oldValue.length !== 0) {
Expand All @@ -82,6 +90,10 @@ export default defineComponent({
},
},

mounted() {
this.selectedOptions = this.presets ?? []
},

methods: {
resetFilter() {
this.selectedOptions = []
Expand Down
23 changes: 18 additions & 5 deletions apps/files/src/filters/TypeFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/
import type { IFileListFilterChip, INode } from '@nextcloud/files'

import { subscribe } from '@nextcloud/event-bus'
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
import { t } from '@nextcloud/l10n'
import Vue from 'vue'
Expand Down Expand Up @@ -89,12 +88,12 @@ const getTypePresets = async () => [
class TypeFilter extends FileListFilter {

private currentInstance?: Vue
private currentPresets?: ITypePreset[]
private currentPresets: ITypePreset[]
private allPresets?: ITypePreset[]

constructor() {
super('files:type', 10)
subscribe('files:navigation:changed', () => this.setPreset())
this.currentPresets = []
}

public async mount(el: HTMLElement) {
Expand All @@ -103,13 +102,16 @@ class TypeFilter extends FileListFilter {
this.allPresets = await getTypePresets()
}

// Already mounted
if (this.currentInstance) {
this.currentInstance.$destroy()
delete this.currentInstance
}

const View = Vue.extend(FileListFilterType as never)
this.currentInstance = new View({
propsData: {
presets: this.currentPresets,
typePresets: this.allPresets!,
},
el,
Expand Down Expand Up @@ -142,7 +144,8 @@ class TypeFilter extends FileListFilter {
}

public setPreset(presets?: ITypePreset[]) {
this.currentPresets = presets
this.currentPresets = presets ?? []
this.currentInstance!.$props.preset = presets
this.filterUpdated()

const chips: IFileListFilterChip[] = []
Expand All @@ -151,7 +154,7 @@ class TypeFilter extends FileListFilter {
chips.push({
icon: preset.icon,
text: preset.label,
onclick: () => this.setPreset(presets.filter(({ id }) => id !== preset.id)),
onclick: () => this.removeFilterPreset(preset.id),
})
}
} else {
Expand All @@ -160,6 +163,16 @@ class TypeFilter extends FileListFilter {
this.updateChips(chips)
}

/**
* Helper callback that removed a preset from selected.
* This is used when clicking on "remove" on a filter-chip.
* @param presetId Id of preset to remove
*/
private removeFilterPreset(presetId: string) {
const filtered = this.currentPresets.filter(({ id }) => id !== presetId)
this.setPreset(filtered)
}

}

/**
Expand Down
49 changes: 49 additions & 0 deletions cypress/e2e/files/files-filtering.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,55 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
getRowForFile('text.txt').should('not.exist')
})

/** Regression test of https://github.com/nextcloud/server/issues/47251 */
it.only('keeps filter state when changing the directory', () => {
// files are visible
getRowForFile('folder').should('be.visible')
getRowForFile('file.txt').should('be.visible')

// enable type filter for folders
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.should('be.visible')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('be.visible')
.click()
// assert the button is checked
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('have.attr', 'aria-checked', 'true')
// close the menu
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.click()

// See the chips are active
filesFilters.activeFilters()
.should('have.length', 1)
.contains(/Folder/).should('be.visible')

// See that folder is visible but file not
getRowForFile('folder').should('be.visible')
getRowForFile('file.txt').should('not.exist')

// Change the directory
navigateToFolder('folder')
getRowForFile('folder').should('not.exist')

// See that the chip is still
filesFilters.activeFilters()
.should('have.length', 1)
.contains(/Folder/).should('be.visible')
// And also the button should be active
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.should('be.visible')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('be.visible')
.and('have.attr', 'aria-checked', 'true')
})

it('resets filter when changing the view', () => {
// All are visible by default
getRowForFile('folder').should('be.visible')
Expand Down

0 comments on commit 7be3fd3

Please sign in to comment.