Migrate to vue3

Signed-off-by: Raimund Schlüßler <raimund.schluessler@mailbox.org>
This commit is contained in:
Raimund Schlüßler 2022-04-22 22:27:18 +02:00
parent b998341fc7
commit 931dfe2d2f
No known key found for this signature in database
GPG Key ID: 036FA7EB1A599178
37 changed files with 926 additions and 1273 deletions

1524
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -31,14 +31,14 @@
"@nextcloud/axios": "^2.4.0",
"@nextcloud/calendar-js": "6.1.0",
"@nextcloud/cdav-library": "1.1.0",
"@nextcloud/dialogs": "^5.1.1",
"@nextcloud/dialogs": "4.1.0",
"@nextcloud/event-bus": "^3.1.0",
"@nextcloud/initial-state": "2.1.0",
"@nextcloud/l10n": "^2.2.0",
"@nextcloud/logger": "^2.7.0",
"@nextcloud/moment": "^1.3.1",
"@nextcloud/router": "^2.2.1",
"@nextcloud/vue": "8.5.1",
"@nextcloud/vue": "9.0.0-alpha.0",
"@vueuse/components": "^10.7.2",
"color-convert": "^2.0.1",
"debounce": "^2.0.0",
@ -51,25 +51,22 @@
"markdown-it-task-lists": "^2.1.1",
"md5": "^2.3.0",
"p-limit": "^5.0.0",
"sortablejs-vue3": "^1.2.11",
"uuid": "^9.0.1",
"vue": "^2.7.16",
"vue-material-design-icons": "^5.2.0",
"vue-router": "^3.6.5",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.2",
"vuex-router-sync": "^5.0.0"
"vue": "^3.4.15",
"vue-material-design-icons": "^5.3.0",
"vue-router": "^4.2.5",
"vuex": "^4.1.0"
},
"devDependencies": {
"@nextcloud/babel-config": "^1.0.0",
"@nextcloud/browserslist-config": "^3.0.0",
"@nextcloud/eslint-config": "^8.3.0",
"@nextcloud/eslint-config": "github:nextcloud-libraries/eslint-config#vue3",
"@nextcloud/stylelint-config": "^2.4.0",
"@nextcloud/vite-config": "^1.2.0",
"@vue/test-utils": "^1.3.6",
"babel-core": "^7.0.0-bridge.0",
"happy-dom": "^13.3.1",
"@nextcloud/vite-config": "github:nextcloud-libraries/nextcloud-vite-config#vue3",
"@vue/test-utils": "^2.4.4",
"happy-dom": "^13.3.8",
"mockdate": "^3.0.5",
"regenerator-runtime": "^0.14.1",
"vitest": "^1.2.2"
},
"engines": {

View File

@ -21,9 +21,9 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<template>
<NcContent app-name="tasks">
<AppNavigation @click.native="closeAppSidebar($event)" />
<AppNavigation @click="closeAppSidebar($event)" />
<NcAppContent @click.native="closeAppSidebar($event)">
<NcAppContent @click="closeAppSidebar($event)">
<RouterView />
</NcAppContent>
@ -49,6 +49,7 @@ export default {
NcAppContent,
NcContent,
},
inject: ['$OCA'],
data() {
return {
searchString: '',

View File

@ -39,8 +39,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template #counter>
<NcCheckboxRadioSwitch :disabled="loading"
:checked="writeable"
@update:checked="editSharee">
:model-value="writeable"
@update:model-value="editSharee">
{{ t('tasks', 'Can edit') }}
</NcCheckboxRadioSwitch>
</template>

View File

@ -27,10 +27,10 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:name="calendar.displayName"
:class="{'list--edit': editing, 'list--deleted': !!deleteTimeout}"
class="list reactive"
@drop.native="dropTask"
@dragover.native="dragOver"
@dragenter.native="dragEnter"
@dragleave.native="dragLeave">
@drop="dropTask"
@dragover="dragOver"
@dragenter="dragEnter"
@dragleave="dragLeave">
<template #icon>
<NcAppNavigationIconBullet :color="calendar.color" />
</template>
@ -111,6 +111,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<ShareCalendar v-if="shareOpen && !calendar.readOnly && !deleteTimeout" :calendar="calendar" />
<div v-if="!deleteTimeout" :class="{error: nameError}" class="app-navigation-entry-edit">
<NcTextField ref="editListInput"
v-model="newCalendarName"
v-tooltip="{
content: tooltipMessage,
shown: showTooltip('list_' + calendar.id),
@ -119,14 +120,13 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
type="text"
:show-trailing-button="newCalendarName !== ''"
trailing-button-icon="arrowRight"
:value.sync="newCalendarName"
:error="nameError"
:label="t('tasks', 'List name')"
@trailing-button-click="save(calendar)"
@keyup="checkName($event, calendar)">
<Pencil :size="16" />
</NcTextField>
<Colorpicker :selected-color="selectedColor" @color-selected="setColor(...arguments)" />
<Colorpicker :selected-color="selectedColor" @color-selected="setColor" />
</div>
</li>
</NcAppNavigationItem>

View File

@ -70,8 +70,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<div class="table__header">
&nbsp;
</div>
<template v-for="item in items">
<div :key="`${item.url}desc`" class="table__body">
<template v-for="item in items" :key="`${item.url}body`">
<div class="table__body">
<div class="icon-bullet"
:style="{ 'background-color': item.color }" />
<div class="item-description">
@ -83,10 +83,10 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</div>
</div>
</div>
<div :key="`${item.url}date`" class="table__body table__body--deletedAt">
<div class="table__body table__body--deletedAt">
<NcDateTime class="timestamp" :timestamp="item.deletedAt" :ignore-seconds="true" />
</div>
<div :key="`${item.url}action`" class="table__body">
<div class="table__body">
<NcButton @click="restore(item)">
<template #icon>
<Undo :size="20" />

View File

@ -28,7 +28,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:disabled="isDisabled"
:clearable="false"
:options="calendarsMap"
:value="calendarMap"
:model-value="calendarMap"
:placeholder="t('tasks', 'Select a calendar')"
:append-to-body="false"
@option:selected="change">

View File

@ -27,7 +27,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<NcSelect label="displayName"
:disabled="isDisabled"
:options="options"
:value="value"
:model-value="value"
:placeholder="placeholder"
:multiple="false"
:searchable="false"

View File

@ -34,7 +34,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<pre><span>{{ newValue }}</span><br><br></pre>
<textarea ref="note__editor"
v-model="newValue"
@keyup.27="setEditing(false)"
@keyup.escape="setEditing(false)"
@keydown.enter.ctrl.prevent="setValue()"
@change="setValue()" />
</div>
@ -101,7 +101,7 @@ export default {
mounted() {
subscribe('tasks:edit-appsidebar-notes', this.setNotes)
},
beforeDestroy() {
beforeUnmount() {
unsubscribe('tasks:edit-appsidebar-notes', this.setNotes)
},
methods: {

View File

@ -39,7 +39,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
type="number"
:min="minValue"
:max="maxValue"
@keyup.27="setEditing(false)"
@keyup.escape="setEditing(false)"
@keydown.enter.prevent="setValue()">
<input v-model="newValue"
type="range"

View File

@ -27,7 +27,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<div class="multiselect__icon">
<component :is="icon" :size="20" />
</div>
<NcSelect :value="tags"
<NcSelect :model-value="tags"
taggable
:disabled="disabled"
:options="options"
@ -36,7 +36,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:close-on-select="false"
:append-to-body="false"
:tag-placeholder="t('tasks', 'Add this as a new tag')"
@input="setTags"
@update:model-value="setTags"
@tag="addTag">
<template #no-options>
{{ t('tasks', 'No tag available. Create one!') }}

View File

@ -36,7 +36,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<input ref="input"
v-model="newValue"
type="string"
@keyup.27="setEditing(false)"
@keyup.escape="setEditing(false)"
@keydown.enter.prevent="setValue()">
</div>
</div>

View File

@ -36,8 +36,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:multiple="true"
:append-to-body="true"
:options="tags"
:value="filter.tags"
@input="setTags">
:model-value="filter.tags"
@update:model-value="setTags">
<template #icon>
<TagMultiple :size="20" />
</template>

View File

@ -24,7 +24,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<div v-if="$route.params.collectionId !== 'completed' && calendar && !calendar.readOnly"
class="header__input">
<NcTextField ref="input"
:value.sync="newTaskName"
v-model="newTaskName"
:label="placeholder"
autocomplete="off"
class="reactive"
@ -35,7 +35,9 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
@keyup.esc="clearNewTask($event)"
@keyup.enter="addTask"
@paste.stop="addMultipleTasks">
<Plus :size="20" />
<template #icon>
<Plus :size="20" />
</template>
</NcTextField>
</div>
<FilterDropdown />

View File

@ -80,7 +80,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:size="20"
:title="t('tasks', 'Task has a note')"
@click="openAppSidebarTab($event, 'app-sidebar-tab-notes')"
@dblclick.native.stop="openAppSidebarTab($event, 'app-sidebar-tab-notes', true)" />
@dblclick.stop="openAppSidebarTab($event, 'app-sidebar-tab-notes', true)" />
<div v-if="task.due || task.completed" :class="{'date--overdue': overdue(task.dueMoment) && !task.completed}" class="date">
<span class="date__short" :class="{ 'date__short--completed': task.completed }">{{ dueDateShort }}</span>
<span class="date__long" :class="{ 'date__long--date-only': task.allDay && !task.completed, 'date__long--completed': task.completed }">{{ dueDateLong }}</span>
@ -151,7 +151,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
v-click-outside="closeSubtaskInput"
class="task-item task-item__input">
<NcTextField ref="input"
:value.sync="newTaskName"
v-model="newTaskName"
:placeholder="subtasksCreationPlaceholder"
:label-outside="true"
:disabled="isAddingTask"
@ -164,7 +164,9 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
@keyup.esc="showSubtaskInput = false"
@keyup.enter="addTask"
@paste.stop="addMultipleTasks">
<Plus :size="20" />
<template #icon>
<Plus :size="20" />
</template>
</NcTextField>
</div>
<TaskDragContainer :tasks="filteredSubtasksShown"

View File

@ -20,31 +20,32 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<draggable tag="ol"
:list="['']"
<Sortable tag="ol"
:list="sortedTasks"
:set-data="setDragData"
v-bind="{group: 'tasks', swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3, disabled: disabled, filter: '.readOnly'}"
:move="onMove"
item-key="key"
:options="{group: 'tasks', swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3, disabled: disabled, filter: '.readOnly'}"
@move="onMove"
@add="onAdd"
@end="onEnd">
<TaskBody v-for="task in sortedTasks"
:key="task.key"
:task="task"
:collection-string="collectionString" />
</draggable>
<template #item="{element}">
<TaskBody :task="element"
:collection-string="collectionString" />
</template>
</Sortable>
</template>
<script>
import Task from '../models/task.js'
import { sort } from '../store/storeHelper.js'
import draggable from 'vuedraggable'
import { Sortable } from 'sortablejs-vue3'
import { mapGetters, mapActions, mapMutations } from 'vuex'
export default {
name: 'TaskDragContainer',
components: {
draggable,
Sortable,
},
props: {
tasks: {
@ -182,20 +183,23 @@ export default {
*
* @param {object} $event The event which caused the drop
*/
onAdd($event) {
async onAdd($event) {
let task
// The task to move
const taskAttribute = $event.item.attributes['task-id']
if (taskAttribute) {
task = this.getTask(taskAttribute.value)
}
// Remove the drag item from the DOM, so it doesn't show up twice.
const item = $event.item
item.parentElement?.removeChild(item)
/**
* We have to adjust the sortOrder property of the tasks
* to achieve the desired sort order.
*/
this.adjustSortOrder(task, $event.newIndex, -1)
// Move the task to a new calendar or parent.
this.prepareMoving(task, $event)
await this.prepareMoving(task, $event)
this.prepareCollecting(task, $event)
$event.stopPropagation()
},
@ -244,7 +248,7 @@ export default {
* @param {Task} task The task to change
* @param {object} $event The event which caused the move
*/
prepareMoving(task, $event) {
async prepareMoving(task, $event) {
let parent, calendar
// The new calendar --> make the moved task a root task
const calendarAttribute = $event.to.attributes['calendar-id']
@ -263,7 +267,7 @@ export default {
calendar = task.calendar
}
// Move the task to the appropriate calendar and parent.
this.moveTask({ task, calendar, parent })
await this.moveTask({ task, calendar, parent })
},
/**

View File

@ -25,21 +25,13 @@
import Dashboard from './views/Dashboard.vue'
import store from './store/store.js'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
Vue.prototype.$OC = OC
Vue.prototype.$OCA = OCA
import { createApp } from 'vue'
document.addEventListener('DOMContentLoaded', () => {
OCA.Dashboard.register('tasks', (el) => {
const View = Vue.extend(Dashboard)
const vm = new View({
propsData: {},
store,
}).$mount(el)
return vm
const item = createApp(Dashboard)
.use(store)
.mount(el)
return item
})
})

View File

@ -25,27 +25,21 @@
*/
import store from '../store/store.js'
import Vue from 'vue'
import { createApp } from 'vue'
const buildSelector = (selector, propsData = {}) => {
return new Promise((resolve, reject) => {
const container = document.createElement('div')
document.getElementById('body-user').append(container)
const View = Vue.extend(selector)
const ComponentVM = new View({
propsData,
store,
}).$mount(container)
ComponentVM.$root.$on('close', () => {
ComponentVM.$el.remove()
ComponentVM.$destroy()
reject(new Error('Selection canceled'))
})
ComponentVM.$root.$on('select', (id) => {
ComponentVM.$el.remove()
ComponentVM.$destroy()
resolve(id)
const dialog = createApp(selector, {
...propsData,
onClose() {
dialog.$el.remove()
reject(new Error('Selection canceled'))
},
})
.use(store)
.mount(container)
})
}

View File

@ -36,45 +36,7 @@ import EyeOff from 'vue-material-design-icons/EyeOff.vue'
import Pulse from 'vue-material-design-icons/Pulse.vue'
import TrendingUp from 'vue-material-design-icons/TrendingUp.vue'
import Vue from 'vue'
import { sync } from 'vuex-router-sync'
// Disable on production
Vue.config.devtools = true
Vue.config.performance = true
sync(store, router)
/**
* We have to globally register these material design icons
* so we can use them dynamically via `<component :is="icon" />`
* in the MultiselectOption component.
*/
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconAlertBoxOutline', AlertBoxOutline)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconCalendarRemove', CalendarRemove)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconCancel', Cancel)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconCheck', Check)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconDelete', Delete)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconEye', Eye)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconEyeOff', EyeOff)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconPulse', Pulse)
// eslint-disable-next-line vue/match-component-file-name
Vue.component('IconTrendingUp', TrendingUp)
/**
* We import TaskBody here globally, because we have a circular dependency
* between TaskDragContainer and TaskBody which otherwise cannot be resolved.
*/
// eslint-disable-next-line vue/match-component-file-name
Vue.component('TaskBody', TaskBody)
import { createApp } from 'vue'
if (!OCA.Tasks) {
/**
@ -83,12 +45,25 @@ if (!OCA.Tasks) {
OCA.Tasks = {}
}
Vue.prototype.$OC = OC
Vue.prototype.$OCA = OCA
const Tasks = createApp(App)
/**
* We import TaskBody here globally, because we have a circular dependency
* between TaskDragContainer and TaskBody which otherwise cannot be resolved.
*/
.component('TaskBody', TaskBody)
.component('IconAlertBoxOutline', AlertBoxOutline)
.component('IconCalendarRemove', CalendarRemove)
.component('IconCancel', Cancel)
.component('IconCheck', Check)
.component('IconDelete', Delete)
.component('IconEye', Eye)
.component('IconEyeOff', EyeOff)
.component('IconPulse', Pulse)
.component('IconTrendingUp', TrendingUp)
.provide('$OCA', OCA)
.provide('$appVersion', appVersion)
.use(router)
.use(store)
.mount('.app-tasks')
OCA.Tasks.App = new Vue({
el: '.app-tasks',
router,
store,
render: h => h(App),
})
OCA.Tasks.App = Tasks

View File

@ -24,10 +24,10 @@ export default {
methods: {
openNewTask(task) {
// Only open the details view if there is enough space or if it is already open.
if (this.$route !== undefined && (document.documentElement.clientWidth >= 768 || this.$route?.params.taskId !== undefined)) {
if (this.$route.value !== undefined && (document.documentElement.clientWidth >= 768 || this.$route.value?.params.taskId !== undefined)) {
// Open the details view for the new task
const calendarId = this.$route.params.calendarId
const collectionId = this.$route.params.collectionId
const calendarId = this.$route.value.params.calendarId
const collectionId = this.$route.value.params.collectionId
if (calendarId) {
this.$router.push({ name: 'calendarsTask', params: { calendarId, taskId: task.uri } })
} else if (collectionId) {

View File

@ -25,47 +25,66 @@ import AppSidebar from './views/AppSidebar.vue'
import Calendar from './views/AppContent/Calendar.vue'
import Collections from './views/AppContent/Collections.vue'
import Vue from 'vue'
import VueRouter from 'vue-router'
import { h } from 'vue'
import { createWebHashHistory, createRouter, RouterView } from 'vue-router'
const routes = [
// using
// { path: '/collections/all', component: CollectionGeneral, alias: '/' },
// instead of
{ path: '/', redirect: getInitialRoute() },
// would also be an option, but it currently does not work
// reliably with router-link due to
// https://github.com/vuejs/vue-router/issues/419
{ name: 'collections', path: '/collections/:collectionId', component: Collections, props: true },
{
name: 'collectionsTask',
path: '/collections/:collectionId/tasks/:taskId',
components: { default: Collections, AppSidebar },
props: { default: true, AppSidebar: true },
path: '/collections/:collectionId',
components: {
default: { render: () => h(RouterView, { name: 'default' }) },
AppSidebar: { render: () => h(RouterView, { name: 'AppSidebar' }) },
},
children: [
{
name: 'collections',
path: '/collections/:collectionId',
component: Collections,
props: true,
},
{
name: 'collectionsTask',
path: '/collections/:collectionId/tasks/:taskId',
components: { default: Collections, AppSidebar },
props: { default: true, AppSidebar: true },
},
{
name: 'collectionsParamTask',
path: '/collections/:collectionId/:collectionParam/tasks/:taskId',
components: { default: Collections, AppSidebar },
props: { default: true, AppSidebar: true },
},
],
},
{
name: 'collectionsParamTask',
path: '/collections/:collectionId/:collectionParam/tasks/:taskId',
components: { default: Collections, AppSidebar },
props: { default: true, AppSidebar: true },
},
{
name: 'calendars',
path: '/calendars/:calendarId',
component: Calendar,
props: true,
},
{
name: 'calendarsTask',
path: '/calendars/:calendarId/tasks/:taskId',
components: { default: Calendar, AppSidebar },
props: { default: true, AppSidebar: true },
components: {
default: { render: () => h(RouterView, { name: 'default' }) },
AppSidebar: { render: () => h(RouterView, { name: 'AppSidebar' }) },
},
children: [
{
name: 'calendars',
path: '/calendars/:calendarId',
component: Calendar,
props: true,
},
{
name: 'calendarsTask',
path: '/calendars/:calendarId/tasks/:taskId',
components: { default: Calendar, AppSidebar },
props: { default: true, AppSidebar: true },
},
],
},
]
Vue.use(VueRouter)
export default new VueRouter({
linkActiveClass: 'active',
routes, // short for `routes: routes`
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export { routes }
export default router

View File

@ -40,8 +40,6 @@ import { findVTODObyState } from './cdav-requests.js'
import { detectColor, uidToHexColor } from '../utils/color.js'
import { mapCDavObjectToCalendarObject } from '../models/calendarObject.js'
import Vue from 'vue'
const calendarModel = {
id: '',
color: '',
@ -180,9 +178,7 @@ const getters = {
* @return {Array<Calendar>} The calendars supporting tasks
*/
getTaskCalendars: state => {
return state.calendars.filter(calendar => {
return calendar.supportsTasks
})
return state.calendars.filter(c => c.supportsTasks)
},
/**
@ -193,11 +189,7 @@ const getters = {
* @return {Array<Calendar>} Array of the calendars sorted alphabetically
*/
getSortedCalendars: (state, getters) => {
return getters.getTaskCalendars.sort(function(cal1, cal2) {
const n1 = cal1.order
const n2 = cal2.order
return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0
})
return [...getters.getTaskCalendars].sort((c1, c2) => c1.order - c2.order)
},
/**
@ -208,14 +200,7 @@ const getters = {
* @return {Array<Calendar>} Array of the calendars sorted alphabetically
*/
getSortedWritableCalendars: (state, getters) => {
return getters.getTaskCalendars.filter(calendar => {
return !calendar.readOnly
})
.sort(function(cal1, cal2) {
const n1 = cal1.order
const n2 = cal2.order
return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0
})
return getters.getSortedCalendars.filter(c => !c.readOnly)
},
/**
@ -230,8 +215,7 @@ const getters = {
* @return {Calendar} The requested calendar
*/
(calendarId) => {
const calendar = state.calendars.find(search => search.id === calendarId)
return calendar
return state.calendars.find(c => c.id === calendarId)
},
/**
@ -360,9 +344,7 @@ const getters = {
* @return {Array}
*/
sortedDeletedCalendars(state) {
console.debug(state.deletedCalendars)
return state.deletedCalendars
.sort((a, b) => a.deletedAt - b.deletedAt)
return [...state.deletedCalendars].sort((a, b) => a.deletedAt - b.deletedAt)
},
/**
@ -511,7 +493,7 @@ const mutations = {
if (list[task.uid]) {
console.debug('Duplicate task overridden', list[task.uid], task)
}
Vue.set(list, task.uid, task)
list[task.uid] = task
return list
}, calendar.tasks)
@ -524,7 +506,7 @@ const mutations = {
* @param {Task} task The task to add
*/
addTaskToCalendar(state, task) {
Vue.set(task.calendar.tasks, task.uid, task)
task.calendar.tasks[task.uid] = task
},
/**
@ -534,7 +516,7 @@ const mutations = {
* @param {Task} task The task to delete
*/
deleteTaskFromCalendar(state, task) {
Vue.delete(task.calendar.tasks, task.uid)
delete task.calendar.tasks[task.uid]
},
/**
@ -601,7 +583,7 @@ const mutations = {
* @param {number} data.order The sort order
*/
setCalendarOrder(state, { calendar, order }) {
Vue.set(calendar, 'order', order)
calendar.order = order
},
}
@ -693,15 +675,12 @@ const actions = {
* @return {Promise}
*/
async deleteCalendar(context, calendar) {
return calendar.dav.delete()
.then((response) => {
// Delete all the tasks from the store that belong to this calendar
Object.values(calendar.tasks)
.forEach(task => context.commit('deleteTask', task))
// Then delete the calendar
context.commit('deleteCalendar', calendar)
})
.catch((error) => { throw error })
await calendar.dav.delete()
// Delete all the tasks from the store that belong to this calendar
Object.values(calendar.tasks)
.forEach(task => context.commit('deleteTask', task))
// Then delete the calendar
context.commit('deleteCalendar', calendar)
},
/**
@ -802,9 +781,8 @@ const actions = {
*/
async toggleCalendarEnabled(context, calendar) {
calendar.dav.enabled = !calendar.dav.enabled
return calendar.dav.update()
.then((response) => context.commit('toggleCalendarEnabled', calendar))
.catch((error) => { throw error })
await calendar.dav.update()
context.commit('toggleCalendarEnabled', calendar)
},
/**
@ -820,9 +798,8 @@ const actions = {
async changeCalendar(context, { calendar, newName, newColor }) {
calendar.dav.displayname = newName
calendar.dav.color = newColor
return calendar.dav.update()
.then((response) => context.commit('renameCalendar', { calendar, newName, newColor }))
.catch((error) => { throw error })
await calendar.dav.update()
context.commit('renameCalendar', { calendar, newName, newColor })
},
/**
@ -848,7 +825,7 @@ const actions = {
// so we need to parse one by one
const tasks = response.map(item => {
const task = new Task(item.data, calendar)
Vue.set(task, 'dav', item)
task.dav = item
return task
})
@ -865,7 +842,7 @@ const actions = {
if (list[task.uid]) {
console.debug('Duplicate task overridden', list[task.uid], task)
}
Vue.set(list, task.uid, task)
list[task.uid] = task
return list
}, parent.subTasks)
@ -890,7 +867,7 @@ const actions = {
const parent = Object.values(calendar.tasks).find(search => search.uid === related)
if (parent) {
parent.loadedCompleted = true
tasks.map(task => Vue.set(parent.subTasks, task.uid, task))
tasks.forEach(task => { parent.subTasks[task.uid] = task })
}
}

View File

@ -26,11 +26,6 @@ import { isTaskInList, searchSubTasks } from './storeHelper.js'
import { generateUrl } from '@nextcloud/router'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
collections: [],
}
@ -94,7 +89,7 @@ const mutations = {
*/
setVisibility(state, newCollection) {
const collection = state.collections.find(search => search.id === newCollection.id)
Vue.set(collection, 'show', newCollection.show)
collection.show = newCollection.show
},
}

View File

@ -29,8 +29,6 @@
import client from '../services/cdav.js'
import { getDefaultPrincipalObject, mapDavToPrincipal } from '../models/principal.js'
import Vue from 'vue'
const state = {
principals: [],
principalsById: {},
@ -54,7 +52,7 @@ const mutations = {
}
state.principals.push(object)
Vue.set(state.principalsById, object.id, object)
state.principalsById[object.id] = object
},
/**

View File

@ -28,11 +28,6 @@ import Requests from '../services/requests.js'
import { loadState } from '@nextcloud/initial-state'
import { generateUrl } from '@nextcloud/router'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
settings: {
sortOrder: 'default',

View File

@ -27,12 +27,9 @@ import tasks from './tasks.js'
import settings from './settings.js'
import principals from './principals.js'
import Vue from 'vue'
import Vuex, { Store } from 'vuex'
import { createStore } from 'vuex'
Vue.use(Vuex)
export default new Store({
export default createStore({
modules: {
calendars,
collections,

View File

@ -33,10 +33,6 @@ import { translate as t } from '@nextcloud/l10n'
import moment from '@nextcloud/moment'
import ICAL from 'ical.js'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
tasks: {},
@ -310,7 +306,7 @@ const mutations = {
appendTasks(state, tasks = []) {
state.tasks = tasks.reduce(function(list, task) {
if (task instanceof Task) {
Vue.set(list, task.key, task)
list[task.key] = task
} else {
console.error('Wrong task object', task)
}
@ -325,7 +321,7 @@ const mutations = {
* @param {Task} task The task to append
*/
appendTask(state, task) {
Vue.set(state.tasks, task.key, task)
state.tasks[task.key] = task
},
/**
@ -336,7 +332,7 @@ const mutations = {
*/
deleteTask(state, task) {
if (state.tasks[task.key] && task instanceof Task) {
Vue.delete(state.tasks, task.key)
delete state.tasks[task.key]
}
},
@ -352,7 +348,7 @@ const mutations = {
if (task instanceof Task) {
// Remove task from parents subTask list if necessary
if (task.related && parent) {
Vue.delete(parent.subTasks, task.uid)
delete parent.subTasks[task.uid]
}
}
},
@ -367,7 +363,7 @@ const mutations = {
*/
addTaskToParent(state, { task, parent }) {
if (task.related && parent) {
Vue.set(parent.subTasks, task.uid, task)
parent.subTasks[task.uid] = task
}
},
@ -380,7 +376,7 @@ const mutations = {
* @param {number} data.complete The complete value
*/
setComplete(state, { task, complete }) {
Vue.set(task, 'complete', complete)
task.complete = complete
},
/**
@ -391,9 +387,9 @@ const mutations = {
*/
toggleStarred(state, task) {
if (+task.priority < 1 || +task.priority > 4) {
Vue.set(task, 'priority', 1)
task.priority = 1
} else {
Vue.set(task, 'priority', 0)
task.priority = 0
}
},
@ -404,7 +400,7 @@ const mutations = {
* @param {Task} task The task
*/
togglePinned(state, task) {
Vue.set(task, 'pinned', !task.pinned)
task.pinned = !task.pinned
},
/**
@ -414,7 +410,7 @@ const mutations = {
* @param {Task} task The task
*/
toggleSubtasksVisibility(state, task) {
Vue.set(task, 'hideSubtasks', !task.hideSubtasks)
task.hideSubtasks = !task.hideSubtasks
},
/**
@ -424,7 +420,7 @@ const mutations = {
* @param {Task} task The task
*/
toggleCompletedSubtasksVisibility(state, task) {
Vue.set(task, 'hideCompletedSubtasks', !task.hideCompletedSubtasks)
task.hideCompletedSubtasks = !task.hideCompletedSubtasks
},
/**
@ -436,7 +432,7 @@ const mutations = {
* @param {string} data.summary The summary
*/
setSummary(state, { task, summary }) {
Vue.set(task, 'summary', summary)
task.summary = summary
},
/**
@ -448,7 +444,7 @@ const mutations = {
* @param {string} data.note The note
*/
setNote(state, { task, note }) {
Vue.set(task, 'note', note)
task.note = note
},
/**
@ -460,7 +456,7 @@ const mutations = {
* @param {Array} data.tags The array of tags
*/
setTags(state, { task, tags }) {
Vue.set(task, 'tags', tags)
task.tags = tags
},
/**
@ -472,7 +468,7 @@ const mutations = {
* @param {string} data.tag The tag to add
*/
addTag(state, { task, tag }) {
Vue.set(task, 'tags', task.tags.concat([tag]))
task.tags = task.tags.concat([tag])
},
/**
@ -484,7 +480,7 @@ const mutations = {
* @param {string} data.priority The priority
*/
setPriority(state, { task, priority }) {
Vue.set(task, 'priority', priority)
task.priority = priority
},
/**
@ -496,7 +492,7 @@ const mutations = {
* @param {string} data.location The location
*/
setLocation(state, { task, location }) {
Vue.set(task, 'location', location)
task.location = location
},
/**
@ -508,7 +504,7 @@ const mutations = {
* @param {string} data.url The url
*/
setUrl(state, { task, url }) {
Vue.set(task, 'customUrl', url)
task.customUrl = url
},
/**
@ -520,7 +516,7 @@ const mutations = {
* @param {string} data.classification The classification
*/
setClassification(state, { task, classification }) {
Vue.set(task, 'class', classification)
task.class = classification
},
/**
@ -532,7 +528,7 @@ const mutations = {
* @param {string} data.status The status
*/
setStatus(state, { task, status }) {
Vue.set(task, 'status', status)
task.status = status
},
/**
@ -544,7 +540,7 @@ const mutations = {
* @param {number} data.order The sort order
*/
setSortOrder(state, { task, order }) {
Vue.set(task, 'sortOrder', order)
task.sortOrder = order
},
/**
@ -559,7 +555,7 @@ const mutations = {
setDue(state, { task, due, allDay }) {
if (due === null) {
// If the date is null, just set (remove) it.
Vue.set(task, 'due', due)
task.due = due
} else {
// Check, that the due date is after the start date.
// If it is not, shift the start date to keep the difference between start and due equal.
@ -571,10 +567,10 @@ const mutations = {
} else {
start = due.clone()
}
Vue.set(task, 'start', momentToICALTime(start, allDay))
task.start = momentToICALTime(start, allDay)
}
// Set the due date, convert it to ICALTime first.
Vue.set(task, 'due', momentToICALTime(due, allDay))
task.due = momentToICALTime(due, allDay)
}
},
@ -590,7 +586,7 @@ const mutations = {
setStart(state, { task, start, allDay }) {
if (start === null) {
// If the date is null, just set (remove) it.
Vue.set(task, 'start', start)
task.start = start
} else {
// Check, that the start date is before the due date.
// If it is not, shift the due date to keep the difference between start and due equal.
@ -602,10 +598,10 @@ const mutations = {
} else {
due = start.clone()
}
Vue.set(task, 'due', momentToICALTime(due, allDay))
task.due = momentToICALTime(due, allDay)
}
// Set the due date, convert it to ICALTime first.
Vue.set(task, 'start', momentToICALTime(start, allDay))
task.start = momentToICALTime(start, allDay)
}
},
@ -616,7 +612,7 @@ const mutations = {
* @param {Task} task The task
*/
toggleAllDay(state, task) {
Vue.set(task, 'allDay', !task.allDay)
task.allDay = !task.allDay
},
/**
@ -628,7 +624,7 @@ const mutations = {
* @param {Calendar} data.calendar The calendar to move the task to
*/
setTaskCalendar(state, { task, calendar }) {
Vue.set(task, 'calendar', calendar)
task.calendar = calendar
},
/**
@ -640,7 +636,7 @@ const mutations = {
* @param {string} data.related The uid of the related task
*/
setTaskParent(state, { task, related }) {
Vue.set(task, 'related', related)
task.related = related
},
/**
@ -706,17 +702,17 @@ const mutations = {
* @param {string} filter The filter
*/
setFilter(state, filter) {
Vue.set(state.filter, 'tags', filter.tags)
state.filter.tags = filter.tags
state.filter = filter
},
addTaskForDeletion(state, { task }) {
Vue.set(state.deletedTasks, task.key, task)
state.deletedTasks[task.key] = task
},
clearTaskFromDeletion(state, { task }) {
if (state.deletedTasks[task.key] && task instanceof Task) {
Vue.delete(state.deletedTasks, task.key)
delete state.deletedTasks[task.key]
}
},
@ -729,7 +725,7 @@ const mutations = {
* @param {number} data.countdown The countdown value
*/
setTaskDeleteCountdown(state, { task, countdown }) {
Vue.set(task, 'deleteCountdown', countdown)
task.deleteCountdown = countdown
},
}
@ -792,7 +788,7 @@ const actions = {
if (!task.dav) {
const response = await task.calendar.dav.createVObject(vData)
Vue.set(task, 'dav', response)
task.dav = response
task.syncStatus = new SyncStatus('success', t('tasks', 'Successfully created the task.'))
context.commit('appendTask', task)
context.commit('addTaskToCalendar', task)
@ -975,7 +971,7 @@ const actions = {
const response = await calendar.dav.find(taskUri)
if (response) {
const task = new Task(response.data, calendar)
Vue.set(task, 'dav', response)
task.dav = response
if (task.related) {
let parent = context.getters.getTaskByUid(task.related)
// If the parent is not found locally, we try to get it from the server.
@ -1016,7 +1012,7 @@ const actions = {
// We expect to only get zero or one task when we query by UID.
if (response.length) {
const task = new Task(response[0].data, calendar)
Vue.set(task, 'dav', response[0])
task.dav = response[0]
if (task.related) {
let parent = context.getters.getTaskByUid(task.related)
// If the parent is not found locally, we try to get it from the server.
@ -1421,10 +1417,10 @@ const actions = {
const oldParent = context.getters.getTaskByUid(task.related)
context.commit('deleteTaskFromParent', { task, parent: oldParent })
// Link to new parent
Vue.set(task, 'related', parentId)
task.related = parentId
// Add task to new parents subtask list
if (parent) {
Vue.set(parent.subTasks, task.uid, task)
parent.subTasks[task.uid] = task
// If the parent is completed, we complete the task
if (parent.completed) {
await context.dispatch('setPercentComplete', { task, complete: 100 })

View File

@ -34,11 +34,6 @@ import { buildSelector } from './helpers/selector.js'
import { translate as t } from '@nextcloud/l10n'
import { generateUrl } from '@nextcloud/router'
import Vue from 'vue'
Vue.prototype.$OC = OC
Vue.prototype.$OCA = OCA
window.addEventListener('DOMContentLoaded', () => {
if (!window.OCA?.Talk?.registerMessageAction) {
return

View File

@ -31,11 +31,11 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:name="collection.displayName"
class="collection reactive"
draggable="false"
@dragstart.native="dragStart"
@drop.native="dropTaskOnCollection(...arguments, collection)"
@dragover.native="dragOver"
@dragenter.native="dragEnter(...arguments, collection)"
@dragleave.native="dragLeave"
@dragstart="dragStart"
@drop="dropTaskOnCollection(...arguments, collection)"
@dragover="dragOver"
@dragenter="dragEnter(...arguments, collection)"
@dragleave="dragLeave"
@click="setInitialRoute(`/collections/${collection.id}`)">
<template #icon>
<component :is="collection.icon"
@ -47,15 +47,17 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
</NcCounterBubble>
</template>
</NcAppNavigationItem>
<draggable class="draggable-container"
<Sortable class="draggable-container"
:list="calendars"
:set-data="setData"
v-bind="{swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3}"
item-key="id"
:options="{swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3}"
@update="update">
<ListItemCalendar v-for="calendar in calendars"
:key="calendar.id"
:calendar="calendar"
@click.native="setInitialRoute(`/calendars/${calendar.id}`)" />
</draggable>
<template #item="{element}">
<ListItemCalendar :calendar="element"
@click="setInitialRoute(`/calendars/${element.id}`)" />
</template>
</Sortable>
<NcAppNavigationItem v-click-outside="() => {creating = false}"
:name="t('tasks', 'Add List…')"
:class="{'collection--edit': creating}"
@ -67,6 +69,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<li>
<div class="app-navigation-entry-edit">
<NcTextField ref="newListInput"
v-model="newCalendarName"
v-tooltip="{
content: tooltipMessage,
shown: showTooltip('list_new'),
@ -75,7 +78,6 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
type="text"
:show-trailing-button="newCalendarName !== ''"
trailing-button-icon="arrowRight"
:value.sync="newCalendarName"
:error="nameError"
:label="t('tasks', 'New list')"
@trailing-button-click="create()"
@ -115,7 +117,7 @@ import Plus from 'vue-material-design-icons/Plus.vue'
import Star from 'vue-material-design-icons/Star.vue'
import TrendingUp from 'vue-material-design-icons/TrendingUp.vue'
import draggable from 'vuedraggable'
import { Sortable } from 'sortablejs-vue3'
import { vOnClickOutside as ClickOutside } from '@vueuse/components'
import { mapState, mapGetters, mapActions } from 'vuex'
@ -129,7 +131,7 @@ export default {
NcCounterBubble,
NcTextField,
AppNavigationSettings,
draggable,
Sortable,
CalendarToday,
CalendarWeek,
Check,
@ -142,6 +144,7 @@ export default {
ClickOutside,
Tooltip,
},
inject: ['$OCA'],
data() {
return {
editing: '',

View File

@ -20,16 +20,16 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<NcAppSidebar :name="summary"
<NcAppSidebar v-model:active="activeTab"
:name="summary"
:name-editable="editingSummary"
:linkify-name="true"
:subname="subsummary"
:name-tooltip="summary"
:subname-tooltip="subsummaryTooltip"
:empty="!task"
:active.sync="activeTab"
@start-editing="newSummary = task.summary"
@update:nameEditable="editSummary"
@update:name-editable="editSummary"
@update:name="updateSummary"
@submit-name="saveSummary()"
@close="closeAppSidebar()">
@ -681,7 +681,7 @@ export default {
subscribe('tasks:open-appsidebar-tab', this.openAppSidebarTab)
subscribe('tasks:edit-appsidebar-summary', this.editSummary)
},
beforeDestroy() {
beforeUnmount() {
unsubscribe('tasks:close-appsidebar', this.closeAppSidebar)
unsubscribe('tasks:task:deleted', this.handleTaskDeletion)
unsubscribe('tasks:open-appsidebar-tab', this.openAppSidebarTab)

View File

@ -10,11 +10,8 @@ import router from '../../../../src/router.js'
import { loadICS } from '../../../assets/loadAsset.js'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex, { Store } from 'vuex'
const localVue = createLocalVue()
localVue.use(Vuex)
import { shallowMount } from '@vue/test-utils'
import { createStore } from 'vuex'
const calendar = {
url: 'calendar-1/tmp',
@ -40,7 +37,7 @@ describe('TaskDragContainer.vue', () => {
// Override the "updateTask" method so we don't get warnings about unresolved promises.
tasks.actions.updateTask = vi.fn()
store = new Store({
store = createStore({
modules: {
calendars,
collections,
@ -53,10 +50,10 @@ describe('TaskDragContainer.vue', () => {
it('Checks that the tasks are sorted correctly on manual sort.', async () => {
const wrapper = shallowMount(TaskDragContainer, {
localVue,
store,
router,
propsData: {
global: {
plugins: [store, router],
},
props: {
tasks: taskItems,
},
})
@ -73,10 +70,10 @@ describe('TaskDragContainer.vue', () => {
it('Checks that tasks are properly reordered.', async () => {
const wrapper = shallowMount(TaskDragContainer, {
localVue,
store,
router,
propsData: {
global: {
plugins: [store, router],
},
props: {
tasks: taskItems,
},
})

View File

@ -2,8 +2,6 @@ import { OC } from './OC.js'
// eslint-disable-next-line n/no-unpublished-import
import MockDate from 'mockdate'
// eslint-disable-next-line n/no-unpublished-import
import 'regenerator-runtime/runtime'
import { afterAll } from 'vitest'

View File

@ -3,18 +3,12 @@ import collections from '../../../src/store/collections.js'
import tasks from '../../../src/store/tasks.js'
import settings from '../../../src/store/settings.js'
import Task from '../../../src/models/task.js'
import router from '../../../src/router.js'
import { loadICS } from '../../assets/loadAsset.js'
import { createLocalVue } from '@vue/test-utils'
import Vuex, { Store } from 'vuex'
import { sync } from 'vuex-router-sync'
import { createStore } from 'vuex'
const localVue = createLocalVue()
localVue.use(Vuex)
const store = new Store({
const store = createStore({
modules: {
calendars,
collections,
@ -23,10 +17,6 @@ const store = new Store({
},
})
// Sync router and store so that we can access
// the router from within the store
sync(store, router)
const calendarsData = [
{
url: 'calendar-1/tmp',
@ -80,7 +70,7 @@ calendarsData.forEach(calendarData => {
url: `${calendar.id}/${task.uid}.ics`,
update: () => { return Promise.resolve(response) },
}
localVue.set(task, 'dav', response)
task.dav = response
return task
})
// Add subtasks correctly
@ -95,7 +85,7 @@ calendarsData.forEach(calendarData => {
if (list[task.uid]) {
console.debug('Duplicate task overridden', list[task.uid], task)
}
localVue.set(list, task.uid, task)
list[task.uid] = task
return list
}, parent.subTasks)
},
@ -105,4 +95,4 @@ calendarsData.forEach(calendarData => {
store.commit('appendTasksToCalendar', { calendar, tasks })
})
export { store, localVue }
export { store }

View File

@ -2,19 +2,27 @@ import General from '../../../../../src/views/AppContent/General.vue'
import router from '../../../../../src/router.js'
import TaskBody from '../../../../../src/components/TaskBody.vue'
import { store, localVue } from '../../setupStore.js'
import { store } from '../../setupStore.js'
import { mount } from '@vue/test-utils'
import { describe, expect, it, vi } from 'vitest'
import Vue from 'vue'
Vue.component('TaskBody', TaskBody)
describe('General.vue', async () => {
'use strict'
const wrapper = mount(General, { localVue, store, router })
await router.push('/')
// After this line, router is ready
await router.isReady()
const wrapper = mount(General, {
global: {
plugins: [store, router],
components: {
TaskBody,
},
},
})
await vi.dynamicImportSettled()
/*

View File

@ -1,20 +1,33 @@
import Week from '../../../../../src/views/AppContent/Week.vue'
import router from '../../../../../src/router.js'
import { routes } from '../../../../../src/router.js'
import TaskBody from '../../../../../src/components/TaskBody.vue'
import { store, localVue } from '../../setupStore.js'
import { createRouter, createWebHistory } from 'vue-router'
import { mount } from '@vue/test-utils'
import { store } from '../../setupStore.js'
import { mount, flushPromises } from '@vue/test-utils'
import { describe, expect, it, vi } from 'vitest'
import Vue from 'vue'
Vue.component('TaskBody', TaskBody)
const router = createRouter({
history: createWebHistory(),
routes,
})
describe('Week.vue', async () => {
'use strict'
const wrapper = mount(Week, { localVue, store, router })
await router.push('/')
await router.isReady()
const wrapper = mount(Week, {
global: {
plugins: [store, router],
components: {
TaskBody,
},
},
})
await vi.dynamicImportSettled()
it('Checks that the correct tasks are shown for day 0 (today)', () => {
@ -46,8 +59,8 @@ describe('Week.vue', async () => {
const taskAtDay2 = wrapper.find('div[day="2"] li[task-id="pwen8kz22g.ics"] > div')
// Click on first task to open it
taskAtDay0.trigger('click')
await localVue.nextTick()
await taskAtDay0.trigger('click')
await flushPromises()
expect(taskAtDay0.classes('task-item__body--active')).toBe(true) // Should be shown active, since it was clicked
expect(taskAtDay2.classes('task-item__body--active')).toBe(false) // Shouldn't be shown active, since it was not clicked
@ -58,12 +71,12 @@ describe('Week.vue', async () => {
const taskAtDay2 = wrapper.find('div[day="2"] li[task-id="pwen8kz22g.ics"] > div')
// Click on different task to open it
taskAtDay2.trigger('click')
await localVue.nextTick()
await taskAtDay2.trigger('click')
await flushPromises()
if (wrapper.vm.$route.params.taskId !== null && wrapper.vm.$route.params.collectionId !== 'week') {
await router.push({ name: 'collections', params: { collectionId: 'week' } })
await localVue.nextTick()
await flushPromises()
}
expect(taskAtDay0.classes('task-item__body--active')).toBe(false)
@ -72,8 +85,8 @@ describe('Week.vue', async () => {
expect(wrapper.find('div[day="2"] li[task-id="pwen7kz22g.ics"]').exists()).toBe(true) // Shown, since it is due in 2 days
// Click on first task to open it
taskAtDay0.trigger('click')
await localVue.nextTick()
await taskAtDay0.trigger('click')
await flushPromises()
expect(taskAtDay0.classes('task-item__body--active')).toBe(true)
expect(wrapper.find('div[day="0"] li[task-id="pwen7kz22g.ics"]').exists()).toBe(true) // Shown now, because parent is active
@ -86,12 +99,12 @@ describe('Week.vue', async () => {
const taskAtDay2 = wrapper.find('div[day="2"] li[task-id="pwen8kz22g.ics"] > div')
// Click on different task to open it
taskAtDay2.trigger('click')
await localVue.nextTick()
await taskAtDay2.trigger('click')
await flushPromises()
if (wrapper.vm.$route.params.taskId !== null && wrapper.vm.$route.params.collectionId !== 'week') {
await router.push({ name: 'collections', params: { collectionId: 'week' } })
await localVue.nextTick()
await flushPromises()
}
expect(taskAtDay0.classes('task-item__body--active')).toBe(false)
@ -100,8 +113,8 @@ describe('Week.vue', async () => {
expect(wrapper.find('div[day="0"] li[task-id="pwen2kz38g.ics"]').exists()).toBe(false) // Not shown, since it is not due at all
// Click on first task to open it
taskAtDay0.trigger('click')
await localVue.nextTick()
await taskAtDay0.trigger('click')
await flushPromises()
expect(taskAtDay0.classes('task-item__body--active')).toBe(true)
expect(wrapper.find('div[day="0"] li[task-id="pwen2kz37g.ics"]').exists()).toBe(true) // Shown now, since parent is active
@ -109,8 +122,8 @@ describe('Week.vue', async () => {
const subtaskAtDay0 = wrapper.find('div[day="0"] li[task-id="pwen2kz37g.ics"] > div')
// Click on subtask to open it
subtaskAtDay0.trigger('click')
await localVue.nextTick()
await subtaskAtDay0.trigger('click')
await flushPromises()
expect(subtaskAtDay0.classes('task-item__body--active')).toBe(true)
expect(wrapper.find('div[day="0"] li[task-id="pwen2kz38g.ics"]').exists()).toBe(true) // Shown now, since parent is active
@ -118,8 +131,8 @@ describe('Week.vue', async () => {
const subsubtaskAtDay0 = wrapper.find('div[day="0"] li[task-id="pwen2kz38g.ics"] > div')
// Click on subtask to open it
subsubtaskAtDay0.trigger('click')
await localVue.nextTick()
await subsubtaskAtDay0.trigger('click')
await flushPromises()
expect(subsubtaskAtDay0.classes('task-item__body--active')).toBe(true)
expect(wrapper.find('div[day="0"] li[task-id="pwen8kz22g.ics"]').exists()).toBe(true) // Shown, since it is due today

View File

@ -22,7 +22,7 @@
import AppSidebar from '../../../../src/views/AppSidebar.vue'
import router from '../../../../src/router.js'
import { store, localVue } from '../setupStore.js'
import { store } from '../setupStore.js'
import { shallowMount } from '@vue/test-utils'
@ -36,7 +36,11 @@ describe('AppSidebar.vue', () => {
router.push({ name: 'calendarsTask', params: { calendarId: 'calendar-1', taskId: 'pwen4kz18g.ics' } })
it('Returns the correct value for the new dates', () => {
const wrapper = shallowMount(AppSidebar, { localVue, store, router })
const wrapper = shallowMount(AppSidebar, {
global: {
plugins: [store, router],
},
})
let actual = wrapper.vm.newStartDate
let expected = new Date('2019-01-01T12:00:00')

View File

@ -15,7 +15,6 @@ export default createAppConfig({
},
environment: 'happy-dom',
setupFiles: resolve(__dirname, './tests/javascript/unit/setup.js'),
alias: [{ find: /^vue$/, replacement: 'vue/dist/vue.runtime.common.js' }],
server: {
deps: {
inline: ['@nextcloud/vue'],