Skip to content

Commit 2c6b3e2

Browse files
authored
Merge pull request #7011 from LibreSign/backport/7010/stable33
[stable33] feat: files list header restructure
2 parents 066ce10 + 1dd48b2 commit 2c6b3e2

4 files changed

Lines changed: 121 additions & 8 deletions

File tree

src/components/Request/RequestPicker.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
:inline="inline ? 3 : 0"
99
:force-name="inline"
1010
:class="{column: inline}"
11+
:variant="variant"
1112
v-model:open="openedMenu">
1213
<template #icon>
1314
<NcIconSvgWrapper :path="mdiPlus" :size="20" />
@@ -147,6 +148,10 @@ export default {
147148
type: Boolean,
148149
default: false,
149150
},
151+
variant: {
152+
type: String,
153+
default: 'tertiary',
154+
},
150155
},
151156
setup() {
152157
const actionsMenuStore = useActionsMenuStore()

src/tests/components/Request/RequestPicker.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,39 @@ describe('RequestPicker component rules', () => {
173173
})
174174
})
175175

176+
describe('variant prop', () => {
177+
const mountWithVariantStub = (props = {}) => mount(RequestPicker, {
178+
props,
179+
global: {
180+
stubs: {
181+
NcActions: {
182+
name: 'NcActions',
183+
props: ['variant'],
184+
template: '<div class="nc-actions-stub" :data-variant="variant"><slot /></div>',
185+
},
186+
NcActionButton: true,
187+
NcButton: true,
188+
NcDialog: true,
189+
NcTextField: true,
190+
NcLoadingIcon: true,
191+
NcNoteCard: true,
192+
UploadProgress: true,
193+
},
194+
mocks: { t: tSimple },
195+
},
196+
})
197+
198+
it('defaults variant to tertiary', () => {
199+
const w = mountWithVariantStub()
200+
expect(w.find('.nc-actions-stub').attributes('data-variant')).toBe('tertiary')
201+
})
202+
203+
it('passes custom variant to NcActions', () => {
204+
const w = mountWithVariantStub({ variant: 'primary' })
205+
expect(w.find('.nc-actions-stub').attributes('data-variant')).toBe('primary')
206+
})
207+
})
208+
176209
describe('envelope support', () => {
177210
it('enables envelope mode when capabilities indicate is-available true', () => {
178211
getCapabilitiesMock.mockReturnValue({

src/tests/views/FilesList/FilesList.spec.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,17 @@ vi.mock('@nextcloud/vue/components/NcAppContent', () => ({
7474
default: { name: 'NcAppContent', template: '<div><slot /></div>' },
7575
}))
7676
vi.mock('@nextcloud/vue/components/NcBreadcrumb', () => ({
77-
default: { name: 'NcBreadcrumb', template: '<div><slot name="icon" /></div>' },
77+
default: {
78+
name: 'NcBreadcrumb',
79+
template: '<div><slot name="icon" /><slot name="menu-icon" /><slot /></div>',
80+
},
81+
}))
82+
vi.mock('@nextcloud/vue/components/NcActionButton', () => ({
83+
default: {
84+
name: 'NcActionButton',
85+
emits: ['click'],
86+
template: '<button class="nc-action-button-stub" @click="$emit(\'click\')"><slot /></button>',
87+
},
7888
}))
7989
vi.mock('@nextcloud/vue/components/NcBreadcrumbs', () => ({
8090
default: { name: 'NcBreadcrumbs', template: '<div><slot /><slot name="actions" /></div>' },
@@ -141,6 +151,45 @@ describe('FilesList.vue rendering rules', () => {
141151
expect(wrapper.vm.mdiFolder).toBeTruthy()
142152
expect(wrapper.vm.mdiViewGrid).toBeTruthy()
143153
expect(wrapper.vm.mdiViewList).toBeTruthy()
154+
expect(wrapper.vm.mdiChevronDown).toBeTruthy()
155+
expect(wrapper.vm.mdiChevronUp).toBeTruthy()
156+
expect(wrapper.vm.mdiReload).toBeTruthy()
157+
})
158+
159+
it('initialises isMenuOpen as false', async () => {
160+
const filesStore = useFilesStore()
161+
vi.spyOn(filesStore, 'getAllFiles').mockResolvedValue({})
162+
163+
const wrapper = mountComponent()
164+
await flushPromises()
165+
166+
expect(wrapper.vm.isMenuOpen).toBe(false)
167+
})
168+
169+
it('renders RequestPicker before the breadcrumbs in the header', async () => {
170+
const filesStore = useFilesStore()
171+
vi.spyOn(filesStore, 'getAllFiles').mockResolvedValue({})
172+
173+
const wrapper = mountComponent()
174+
await flushPromises()
175+
176+
const header = wrapper.find('.files-list__header')
177+
const firstChild = header.element.children[0]
178+
expect(firstChild.classList.contains('request-picker-stub')).toBe(true)
179+
})
180+
181+
it('calls filesStore.updateAllFiles once more when reload button is clicked', async () => {
182+
const filesStore = useFilesStore()
183+
vi.spyOn(filesStore, 'getAllFiles').mockResolvedValue({})
184+
const updateSpy = vi.spyOn(filesStore, 'updateAllFiles').mockResolvedValue({})
185+
186+
const wrapper = mountComponent()
187+
await flushPromises()
188+
189+
const callsBefore = updateSpy.mock.calls.length
190+
await wrapper.find('.nc-action-button-stub').trigger('click')
191+
192+
expect(updateSpy.mock.calls.length).toBe(callsBefore + 1)
144193
})
145194

146195
it('shows empty-state request action when user can request sign', async () => {
@@ -186,7 +235,8 @@ describe('FilesList.vue rendering rules', () => {
186235
const wrapper = mountComponent()
187236
await flushPromises()
188237

189-
const iconWithPath = wrapper.findAll('.nc-icon').find((node) => !!node.attributes('data-path'))
238+
const gridButton = wrapper.find('.files-list__header-grid-button')
239+
const iconWithPath = gridButton.findAll('.nc-icon').find((node) => !!node.attributes('data-path'))
190240
expect(iconWithPath?.attributes('data-path')).toBe(wrapper.vm.mdiViewGrid)
191241
})
192242

@@ -199,7 +249,8 @@ describe('FilesList.vue rendering rules', () => {
199249
const wrapper = mountComponent()
200250
await flushPromises()
201251

202-
const iconWithPath = wrapper.findAll('.nc-icon').find((node) => !!node.attributes('data-path'))
252+
const gridButton = wrapper.find('.files-list__header-grid-button')
253+
const iconWithPath = gridButton.findAll('.nc-icon').find((node) => !!node.attributes('data-path'))
203254
expect(iconWithPath?.attributes('data-path')).toBe(wrapper.vm.mdiViewList)
204255
})
205256
})

src/views/FilesList/FilesList.vue

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,40 @@
55
<template>
66
<NcAppContent :page-heading="t('libresign', 'Files')">
77
<div class="files-list__header">
8+
<!-- Request picker -->
9+
<RequestPicker variant="primary" />
10+
11+
<!-- Current folder breadcrumbs -->
812
<NcBreadcrumbs class="files-list__breadcrumbs">
913
<NcBreadcrumb :name="t('libresign', 'Files')"
1014
:title="t('libresign', 'Files')"
1115
:force-icon-text="true"
1216
:to="{ name: 'fileslist' }"
1317
:aria-description="t('libresign', 'Files')"
1418
:disable-drop="true"
15-
@click="refresh()">
19+
force-menu
20+
v-model:open="isMenuOpen">
1621
<template #icon>
1722
<NcIconSvgWrapper :size="20"
1823
:svg="viewIcon" />
1924
</template>
25+
<template #menu-icon>
26+
<NcIconSvgWrapper :path="isMenuOpen ? mdiChevronUp : mdiChevronDown" />
27+
</template>
28+
<!-- Reload button -->
29+
<NcActionButton close-after-click @click="refresh()">
30+
<template #icon>
31+
<NcIconSvgWrapper :path="mdiReload" />
32+
</template>
33+
<!-- TRANSLATORS Button inside the breadcrumb dropdown menu that reloads the file list -->
34+
{{ t('libresign', 'Reload content') }}
35+
</NcActionButton>
2036
</NcBreadcrumb>
21-
<template #actions>
22-
<RequestPicker />
23-
</template>
2437
</NcBreadcrumbs>
2538

26-
<NcLoadingIcon v-if="isRefreshing" class="files-list__refresh-icon" />
39+
<NcLoadingIcon v-if="isRefreshing"
40+
class="files-list__refresh-icon"
41+
:name="t('libresign', 'File list is reloading')" />
2742

2843
<NcButton :aria-label="gridViewButtonLabel"
2944
:title="gridViewButtonLabel"
@@ -75,11 +90,15 @@ import { t } from '@nextcloud/l10n'
7590
7691
import HomeSvg from '@mdi/svg/svg/home.svg?raw'
7792
import {
93+
mdiChevronDown,
94+
mdiChevronUp,
7895
mdiFolder,
96+
mdiReload,
7997
mdiViewGrid,
8098
mdiViewList,
8199
} from '@mdi/js'
82100
101+
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
83102
import NcAppContent from '@nextcloud/vue/components/NcAppContent'
84103
import NcBreadcrumb from '@nextcloud/vue/components/NcBreadcrumb'
85104
import NcBreadcrumbs from '@nextcloud/vue/components/NcBreadcrumbs'
@@ -99,6 +118,7 @@ import { useSidebarStore } from '../../store/sidebar.js'
99118
export default {
100119
name: 'FilesList',
101120
components: {
121+
NcActionButton,
102122
NcAppContent,
103123
NcButton,
104124
NcBreadcrumb,
@@ -119,13 +139,17 @@ export default {
119139
filtersStore,
120140
userConfigStore,
121141
sidebarStore,
142+
mdiChevronDown,
143+
mdiChevronUp,
122144
mdiFolder,
145+
mdiReload,
123146
mdiViewGrid,
124147
mdiViewList,
125148
}
126149
},
127150
data() {
128151
return {
152+
isMenuOpen: false,
129153
loading: true,
130154
dirContentsFiltered: [],
131155
}

0 commit comments

Comments
 (0)