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 Jan 17, 2025
1 parent 326120a commit 4a9954f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 9 deletions.
16 changes: 14 additions & 2 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,17 +75,25 @@ export default defineComponent({
},

watch: {
/** Reset selected options if property is changed */
presets() {
this.selectedOptions = this.presets ?? []
},
selectedOptions(newValue, oldValue) {
if (this.selectedOptions.length === 0) {
if (oldValue.length !== 0) {
this.$emit('update:preset')
this.$emit('update:presets')
}
} else {
this.$emit('update:preset', this.selectedOptions)
this.$emit('update:presets', this.selectedOptions)
}
},
},

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

methods: {
resetFilter() {
this.selectedOptions = []
Expand Down
27 changes: 20 additions & 7 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,18 +102,21 @@ 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,
})
.$on('update:preset', this.setPreset.bind(this))
.$on('update:presets', this.setPresets.bind(this))
.$mount()
}

Expand All @@ -141,8 +143,9 @@ class TypeFilter extends FileListFilter {
})
}

public setPreset(presets?: ITypePreset[]) {
this.currentPresets = presets
public setPresets(presets?: ITypePreset[]) {
this.currentPresets = presets ?? []
this.currentInstance!.$props.presets = 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.setPresets(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('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 4a9954f

Please sign in to comment.