Skip to content

Commit 0da71a6

Browse files
authored
Merge pull request #7213 from LibreSign/backport/7208/stable32
[stable32] chore: migration to vue3
2 parents 2c5c984 + 73e46b9 commit 0da71a6

24 files changed

Lines changed: 1313 additions & 394 deletions

src/External.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<router-view />
77
</template>
88

9-
<script>
10-
export default {
9+
<script setup lang="ts">
10+
defineOptions({
1111
name: 'External',
12-
}
12+
})
1313
</script>

src/components/Request/SignDetail/partials/PageNavigation.vue

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,53 @@
1616
</nav>
1717
</template>
1818

19-
<script>
19+
<script setup lang="ts">
2020
import { t } from '@nextcloud/l10n'
21+
import { computed } from 'vue'
2122
2223
import NcCounterBubble from '@nextcloud/vue/components/NcCounterBubble'
2324
24-
export default {
25+
defineOptions({
2526
name: 'PageNavigation',
26-
components: { NcCounterBubble },
27-
emits: ['update:modelValue'],
28-
props: {
29-
modelValue: {
30-
type: Number,
31-
required: true,
32-
},
33-
pages: {
34-
type: Array,
35-
required: true,
36-
},
37-
width: {
38-
type: String,
39-
required: true,
40-
},
41-
},
42-
computed: {
43-
size() {
44-
return this.pages.length
45-
},
46-
actual() {
47-
return this.modelValue
48-
},
49-
allowNext() {
50-
return this.actual < this.size
51-
},
52-
allowPrevious() {
53-
return this.modelValue > 1
54-
},
55-
},
56-
methods: {
57-
t,
58-
next() {
59-
this.setPage(this.modelValue + 1)
60-
},
61-
previous() {
62-
this.setPage(this.modelValue - 1)
63-
},
64-
setPage(val) {
65-
this.$emit('update:modelValue', val)
66-
},
67-
},
27+
})
28+
29+
const props = defineProps<{
30+
modelValue: number
31+
pages: unknown[]
32+
width: string
33+
}>()
34+
35+
const emit = defineEmits<{
36+
(event: 'update:modelValue', value: number): void
37+
}>()
38+
39+
const size = computed(() => props.pages.length)
40+
const actual = computed(() => props.modelValue)
41+
const allowNext = computed(() => actual.value < size.value)
42+
const allowPrevious = computed(() => props.modelValue > 1)
43+
44+
function setPage(value: number) {
45+
emit('update:modelValue', value)
6846
}
47+
48+
function next() {
49+
setPage(props.modelValue + 1)
50+
}
51+
52+
function previous() {
53+
setPage(props.modelValue - 1)
54+
}
55+
56+
defineExpose({
57+
size,
58+
actual,
59+
allowNext,
60+
allowPrevious,
61+
setPage,
62+
next,
63+
previous,
64+
props,
65+
})
6966
</script>
7067

7168
<style scoped>

src/components/RightSidebar/RightSidebar.vue

Lines changed: 62 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
</NcAppSidebar>
2525
</template>
2626

27-
<script>
27+
<script setup lang="ts">
2828
import { t } from '@nextcloud/l10n'
29+
import { computed, ref, watch } from 'vue'
30+
import { useRoute } from 'vue-router'
2931
3032
import NcAppSidebar from '@nextcloud/vue/components/NcAppSidebar'
3133
import NcAppSidebarTab from '@nextcloud/vue/components/NcAppSidebarTab'
@@ -37,57 +39,67 @@ import { useFilesStore } from '../../store/files.js'
3739
import { useSidebarStore } from '../../store/sidebar.js'
3840
import { useSignStore } from '../../store/sign.js'
3941
40-
export default {
42+
defineOptions({
4143
name: 'RightSidebar',
42-
components: {
43-
NcAppSidebar,
44-
NcAppSidebarTab,
45-
RequestSignatureTab,
46-
SignTab,
47-
},
48-
setup() {
49-
const filesStore = useFilesStore()
50-
const signStore = useSignStore()
51-
const sidebarStore = useSidebarStore()
52-
return { filesStore, signStore, sidebarStore }
53-
},
54-
computed: {
55-
fileName() {
56-
return this.filesStore.getFile()?.name ?? ''
57-
},
58-
subTitle() {
59-
if (!this.opened) {
60-
return t('libresign', 'Enter who will receive the request')
61-
}
62-
return this.filesStore.getSubtitle()
63-
},
64-
showRequestSignatureTab() {
65-
return this.sidebarStore.activeTab === 'request-signature-tab'
66-
},
67-
showSign() {
68-
return this.sidebarStore.activeTab === 'sign-tab'
69-
},
70-
},
71-
watch: {
72-
'sidebarStore.activeTab'(newValue, previousValue) {
73-
if (this.$refs?.rightAppSidebar?.$refs?.tabs) {
74-
this.$refs.rightAppSidebar.$refs.tabs.activeTab = newValue
75-
}
76-
},
77-
'$route.name'(routeName) {
78-
this.sidebarStore.handleRouteChange(routeName)
79-
},
80-
},
81-
methods: {
82-
t,
83-
handleUpdateActive(active) {
84-
this.sidebarStore.setActiveTab(active)
85-
},
86-
closeSidebar() {
87-
this.sidebarStore.hideSidebar()
88-
},
89-
},
44+
})
45+
46+
type SidebarRef = {
47+
$refs?: {
48+
tabs?: {
49+
activeTab?: string
50+
}
51+
}
9052
}
53+
54+
const filesStore = useFilesStore()
55+
const signStore = useSignStore()
56+
const sidebarStore = useSidebarStore()
57+
const route = useRoute()
58+
const rightAppSidebar = ref<SidebarRef | null>(null)
59+
60+
const fileName = computed(() => filesStore.getFile()?.name ?? '')
61+
const opened = computed(() => sidebarStore.isVisible)
62+
const subTitle = computed(() => {
63+
if (!opened.value) {
64+
return t('libresign', 'Enter who will receive the request')
65+
}
66+
return filesStore.getSubtitle()
67+
})
68+
69+
const showRequestSignatureTab = computed(() => sidebarStore.activeTab === 'request-signature-tab')
70+
const showSign = computed(() => sidebarStore.activeTab === 'sign-tab')
71+
72+
watch(() => sidebarStore.activeTab, (newValue) => {
73+
if (rightAppSidebar.value?.$refs?.tabs) {
74+
rightAppSidebar.value.$refs.tabs.activeTab = newValue
75+
}
76+
})
77+
78+
watch(() => route.name, (routeName) => {
79+
sidebarStore.handleRouteChange(routeName)
80+
})
81+
82+
function handleUpdateActive(active: string) {
83+
sidebarStore.setActiveTab(active)
84+
}
85+
86+
function closeSidebar() {
87+
sidebarStore.hideSidebar()
88+
}
89+
90+
defineExpose({
91+
filesStore,
92+
signStore,
93+
sidebarStore,
94+
rightAppSidebar,
95+
fileName,
96+
opened,
97+
subTitle,
98+
showRequestSignatureTab,
99+
showSign,
100+
handleUpdateActive,
101+
closeSidebar,
102+
})
91103
</script>
92104
<style lang="scss" scoped>
93105
.app-sidebar__tab {

src/tests/External.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 LibreSign contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { describe, expect, it } from 'vitest'
7+
import { mount } from '@vue/test-utils'
8+
9+
import External from '../External.vue'
10+
11+
describe('External.vue', () => {
12+
it('renders the router view', () => {
13+
const wrapper = mount(External, {
14+
global: {
15+
stubs: {
16+
RouterView: { template: '<div class="router-view" />' },
17+
},
18+
},
19+
})
20+
21+
expect(wrapper.find('.router-view').exists()).toBe(true)
22+
})
23+
})

src/tests/components/PdfEditor/SignatureBox.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,4 @@ describe('SignatureBox.vue', () => {
6565

6666
expect(wrapper.vm.boxStyle).toEqual({})
6767
})
68-
})
68+
})
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 LibreSign contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { describe, expect, it, vi } from 'vitest'
7+
import { mount } from '@vue/test-utils'
8+
9+
import PageNavigation from '../../../../../components/Request/SignDetail/partials/PageNavigation.vue'
10+
11+
vi.mock('@nextcloud/l10n', () => ({
12+
t: vi.fn((_app: string, text: string) => text),
13+
translate: vi.fn((_app: string, text: string) => text),
14+
translatePlural: vi.fn((_app: string, singular: string, plural: string, count: number) => (count === 1 ? singular : plural)),
15+
n: vi.fn((_app: string, singular: string, plural: string, count: number) => (count === 1 ? singular : plural)),
16+
getLanguage: vi.fn(() => 'en'),
17+
getLocale: vi.fn(() => 'en'),
18+
isRTL: vi.fn(() => false),
19+
}))
20+
21+
describe('PageNavigation.vue', () => {
22+
function createWrapper(modelValue = 2) {
23+
return mount(PageNavigation, {
24+
props: {
25+
modelValue,
26+
pages: [1, 2, 3],
27+
width: '240px',
28+
},
29+
global: {
30+
stubs: {
31+
NcCounterBubble: { template: '<div><slot /></div>' },
32+
},
33+
},
34+
})
35+
}
36+
37+
it('computes navigation state from the current page', () => {
38+
const wrapper = createWrapper()
39+
40+
expect(wrapper.vm.size).toBe(3)
41+
expect(wrapper.vm.actual).toBe(2)
42+
expect(wrapper.vm.allowPrevious).toBe(true)
43+
expect(wrapper.vm.allowNext).toBe(true)
44+
})
45+
46+
it('emits the next page when next is called', () => {
47+
const wrapper = createWrapper()
48+
49+
wrapper.vm.next()
50+
51+
expect(wrapper.emitted('update:modelValue')).toEqual([[3]])
52+
})
53+
54+
it('emits the previous page when previous is called', () => {
55+
const wrapper = createWrapper()
56+
57+
wrapper.vm.previous()
58+
59+
expect(wrapper.emitted('update:modelValue')).toEqual([[1]])
60+
})
61+
62+
it('disables previous navigation on the first page', () => {
63+
const wrapper = createWrapper(1)
64+
65+
expect(wrapper.vm.allowPrevious).toBe(false)
66+
expect(wrapper.vm.allowNext).toBe(true)
67+
})
68+
})

0 commit comments

Comments
 (0)