Repository: obsidian-tasks-group/obsidian-tasks
Pull request #3052 (2024-08-24 20:48:28)

Add modal date picker
# Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
  - Issue/discussion: https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2649


## Description

<!--- Remember to provide a general summary of your changes in the Title above -->

<!--- Describe your changes in detail -->

Adds a new date-picker button to the date fields in the Edit Task modal.

This still needs further improvement before release: I don't like the appearance of the new SVG icons very much - but the functionality is really useful, and I want to start making the later changes in smaller PRs.

## Motivation and Context

Fixes https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2649.

## How has this been tested?

- Reviewing the updated HTML approval tests
- Lots of manual exploratory testing in the sample vault.

## Screenshots (if appropriate)

The new "buttons":

<img width="509" alt="image" src="https://github.com/user-attachments/assets/69c2c057-737c-40f3-826e-f99263820469">

Clicking a button shows a date-picker, preselecting any current value:

<img width="505" alt="image" src="https://github.com/user-attachments/assets/43af69b0-6ab5-4279-84d6-3b53e1235731">


Or if no current value, making it easy to select the current date (which today us 24th August):

<img width="514" alt="image" src="https://github.com/user-attachments/assets/7e3b90ba-3ff1-447d-ab4f-4b61e30ac1bd">


## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change requires a change to the documentation.
- [ ] I have [updated the documentation](https://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
- [ ] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://publish.obsidian.md/tasks-contributing) and
[Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).
Put an x in the boxes to confirm you agree.
-->

- [x] My contribution follows this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)



Patch:
diff --git a/src/Obsidian/TaskModal.ts b/src/Obsidian/TaskModal.ts
index 8eddfc2d..9eae51a9 100644
--- a/src/Obsidian/TaskModal.ts
+++ b/src/Obsidian/TaskModal.ts
@@ -1,13 +1,21 @@
 import { App, Modal } from 'obsidian';
+import type flatpickr from 'flatpickr';
+
 import EditTask from '../ui/EditTask.svelte';
 import type { Task } from '../Task/Task';
 import { StatusRegistry } from '../Statuses/StatusRegistry';
 import { Status } from '../Statuses/Status';
 
-export class TaskModal extends Modal {
+export interface IFlatpickrUser {
+    setActiveFlatpickrInstance(instance: flatpickr.Instance): void;
+    clearActiveFlatpickrInstance(): void;
+}
+
+export class TaskModal extends Modal implements IFlatpickrUser {
     public readonly task: Task;
     public readonly onSubmit: (updatedTasks: Task[]) => void;
     public readonly allTasks: Task[];
+    private activeFlatpickrInstance: flatpickr.Instance | null = null;
 
     constructor({
         app,
@@ -41,7 +49,13 @@ export class TaskModal extends Modal {
 
         new EditTask({
             target: contentEl,
-            props: { task: this.task, statusOptions: statusOptions, onSubmit: this.onSubmit, allTasks: this.allTasks },
+            props: {
+                task: this.task,
+                statusOptions: statusOptions,
+                onSubmit: this.onSubmit,
+                allTasks: this.allTasks,
+                modal: this,
+            },
         });
     }
 
@@ -63,4 +77,25 @@ export class TaskModal extends Modal {
         const { contentEl } = this;
         contentEl.empty();
     }
+
+    /**
+     * Make sure that if the date picker is open, an Escape key only closes
+     * the date-picker, and not the EditTask modal as well.
+     */
+    public onEscapeKey(): void {
+        if (this.activeFlatpickrInstance?.isOpen) {
+            this.activeFlatpickrInstance.close();
+            this.activeFlatpickrInstance = null;
+        } else {
+            this.close();
+        }
+    }
+
+    public setActiveFlatpickrInstance(instance: flatpickr.Instance): void {
+        this.activeFlatpickrInstance = instance;
+    }
+
+    public clearActiveFlatpickrInstance(): void {
+        this.activeFlatpickrInstance = null;
+    }
 }
diff --git a/src/ui/DateEditor.svelte b/src/ui/DateEditor.svelte
index 5c15eae2..f4e1aa90 100644
--- a/src/ui/DateEditor.svelte
+++ b/src/ui/DateEditor.svelte
@@ -1,4 +1,7 @@
 <script lang="ts">
+    import flatpickr from 'flatpickr';
+    import { createEventDispatcher } from 'svelte';
+
     import { doAutocomplete } from '../DateTime/DateAbbreviations';
     import { parseTypedDateForDisplayUsingFutureDate } from '../DateTime/DateTools';
     import { labelContentWithAccessKey } from './EditTaskHelpers';
@@ -11,6 +14,11 @@
     export let accesskey: string | null;
 
     let parsedDate: string;
+    let inputElement: HTMLInputElement;
+    let flatpickrInstance: any;
+
+    const dispatch = createEventDispatcher();
+
     $: {
         date = doAutocomplete(date);
         parsedDate = parseTypedDateForDisplayUsingFutureDate(id, date, forwardOnly);
@@ -19,11 +27,63 @@
 
     // 'weekend' abbreviation omitted due to lack of space.
     const datePlaceholder = "Try 'Mon' or 'tm' then space";
+
+    // Function to open the date-picker and update the date
+    // TODO Reduce its complexity.
+    // TODO Can a function be extracted?
+    // TODO Can the function be moved out?
+    // TODO Can we re-use this in the pre-existing date picker function?
+    function openDatePicker() {
+        if (inputElement) {
+            if (flatpickrInstance) {
+                flatpickrInstance.destroy(); // Destroy any existing instance to avoid conflicts
+            }
+
+            const options: Record<string, any> = {
+                enableTime: false,
+                dateFormat: 'Y-m-d',
+                locale: {
+                    firstDayOfWeek: 1,
+                },
+                onClose: (selectedDates: Date[]) => {
+                    dispatch('close', { instance: flatpickrInstance }); // Notify parent about close
+
+                    if (selectedDates.length > 0) {
+                        // TODO can this be simplified?
+                        const selectedDate = selectedDates[0];
+                        const year = selectedDate.getFullYear();
+                        const month = String(selectedDate.getMonth() + 1).padStart(2, '0');
+                        const day = String(selectedDate.getDate()).padStart(2, '0');
+                        date = `${year}-${month}-${day}`;
+                    }
+
+                    flatpickrInstance.destroy(); // Destroy the instance after the date is selected
+                    flatpickrInstance = null;
+                },
+                allowInput: true, // Allow manual input to keep the field empty if no selection is made
+                defaultDate: undefined, // Explicitly define defaultDate with undefined
+            };
+
+            // Determine if `parsedDate` should be used as the default date
+            // TODO Remove repetition of regex here - do we have a helper function for this already?
+            if (parsedDate.match(/^\d{4}-\d{2}-\d{2}$/)) {
+                options.defaultDate = new Date(parsedDate);
+            } else if (date.match(/^\d{4}-\d{2}-\d{2}$/)) {
+                options.defaultDate = new Date(date);
+            }
+
+            flatpickrInstance = flatpickr(inputElement, options);
+            dispatch('open', { instance: flatpickrInstance }); // Notify parent about open
+
+            flatpickrInstance.open(); // Directly open the date picker
+        }
+    }
 </script>
 
 <label for={id}>{@html labelContentWithAccessKey(id, accesskey)}</label>
 <!-- svelte-ignore a11y-accesskey -->
 <input
+    bind:this={inputElement}
     bind:value={date}
     {id}
     type="text"
@@ -32,6 +92,25 @@
     placeholder={datePlaceholder}
     {accesskey}
 />
+
+<!-- Separate the calendar icon from the input to allow typing in the input box -->
+<!-- TODO Suppress or fix the ally warning-->
+<!-- TODO Nicer looking icon, or at least make it a paler shade -->
+<svg
+    class="calendar-icon"
+    xmlns="http://www.w3.org/2000/svg"
+    viewBox="0 0 24 24"
+    fill="currentColor"
+    width="24px"
+    height="24px"
+    on:click={openDatePicker}
+    style="cursor: pointer;"
+>
+    <path
+        d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"
+    />
+</svg>
+
 <code class="tasks-modal-parsed-date">{dateSymbol} {@html parsedDate}</code>
 
 <style>
diff --git a/src/ui/EditTask.scss b/src/ui/EditTask.scss
index 255b4b16..19d258f7 100644
--- a/src/ui/EditTask.scss
+++ b/src/ui/EditTask.scss
@@ -81,7 +81,7 @@
     }
 
     .tasks-modal-parsed-date {
-        grid-column: 3;
+        grid-column: 4;
         font-size: var(--font-ui-small);
     }
 
diff --git a/src/ui/EditTask.svelte b/src/ui/EditTask.svelte
index d8f6e632..ece59ebb 100644
--- a/src/ui/EditTask.svelte
+++ b/src/ui/EditTask.svelte
@@ -1,8 +1,11 @@
 <script lang="ts">
     import { onMount } from 'svelte';
+    import type flatpickr from 'flatpickr';
+
     import { TASK_FORMATS, getSettings } from '../Config/Settings';
     import type { Status } from '../Statuses/Status';
     import type { Task } from '../Task/Task';
+    import type { IFlatpickrUser } from '../Obsidian/TaskModal';
     import DateEditor from './DateEditor.svelte';
     import Dependency from './Dependency.svelte';
     import { EditableTask } from './EditableTask';
@@ -15,6 +18,7 @@
     export let onSubmit: (updatedTasks: Task[]) => void | Promise<void>;
     export let statusOptions: Status[];
     export let allTasks: Task[];
+    export let modal: IFlatpickrUser | null = null;
 
     const {
         // NEW_TASK_FIELD_EDIT_REQUIRED
@@ -145,6 +149,18 @@
         const newTasks = await editableTask.applyEdits(task, allTasks);
         onSubmit(newTasks);
     };
+
+    function handleOpen(event: CustomEvent<{ instance: flatpickr.Instance }>) {
+        if (modal) {
+            modal.setActiveFlatpickrInstance(event.detail.instance);
+        }
+    }
+
+    function handleClose() {
+        if (modal) {
+            modal.clearActiveFlatpickrInstance();
+        }
+    }
 </script>
 
 <!--
@@ -253,6 +269,8 @@ Availability of access keys:
             bind:isDateValid={isDueDateValid}
             forwardOnly={editableTask.forwardOnly}
             accesskey={accesskey('d')}
+            on:open={handleOpen}
+            on:close={handleClose}
         />
 
         <!-- --------------------------------------------------------------------------- -->
@@ -265,6 +283,8 @@ Availability of access keys:
             bind:isDateValid={isScheduledDateValid}
             forwardOnly={editableTask.forwardOnly}
             accesskey={accesskey('s')}
+            on:open={handleOpen}
+            on:close={handleClose}
         />
 
         <!-- --------------------------------------------------------------------------- -->
@@ -277,6 +297,8 @@ Availability of access keys:
             bind:isDateValid={isStartDateValid}
             forwardOnly={editableTask.forwardOnly}
             accesskey={accesskey('a')}
+            on:open={handleOpen}
+            on:close={handleClose}
         />
 
         <!-- --------------------------------------------------------------------------- -->
@@ -350,6 +372,8 @@ Availability of access keys:
             bind:isDateValid={isCreatedDateValid}
             forwardOnly={editableTask.forwardOnly}
             accesskey={accesskey('c')}
+            on:open={handleOpen}
+            on:close={handleClose}
         />
 
         <!-- --------------------------------------------------------------------------- -->
@@ -362,6 +386,8 @@ Availability of access keys:
             bind:isDateValid={isDoneDateValid}
             forwardOnly={editableTask.forwardOnly}
             accesskey={accesskey('x')}
+            on:open={handleOpen}
+            on:close={handleClose}
         />
 
         <!-- --------------------------------------------------------------------------- -->
@@ -374,6 +400,8 @@ Availability of access keys:
             bind:isDateValid={isCancelledDateValid}
             forwardOnly={editableTask.forwardOnly}
             accesskey={accesskey('-')}
+            on:open={handleOpen}
+            on:close={handleClose}
         />
     </section>
 


Test patch:
diff --git a/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot.approved.html b/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot.approved.html
index 7333f8b2..71eae2ef 100644
--- a/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot.approved.html
+++ b/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot.approved.html
@@ -94,6 +94,17 @@
         class="tasks-modal-date-input"
         placeholder="Try 'Mon' or 'tm' then space"
         accesskey="d" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         📅
         <i>no due date</i>
@@ -108,6 +119,17 @@
         class="tasks-modal-date-input"
         placeholder="Try 'Mon' or 'tm' then space"
         accesskey="s" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ⏳
         <i>no scheduled date</i>
@@ -123,6 +145,17 @@
         class="tasks-modal-date-input"
         placeholder="Try 'Mon' or 'tm' then space"
         accesskey="a" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         🛫
         <i>no start date</i>
@@ -221,6 +254,17 @@
         class="tasks-modal-date-input"
         placeholder="Try 'Mon' or 'tm' then space"
         accesskey="c" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ➕
         <i>no created date</i>
@@ -236,6 +280,17 @@
         class="tasks-modal-date-input"
         placeholder="Try 'Mon' or 'tm' then space"
         accesskey="x" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ✅
         <i>no done date</i>
@@ -251,6 +306,17 @@
         class="tasks-modal-date-input"
         placeholder="Try 'Mon' or 'tm' then space"
         accesskey="-" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ❌
         <i>no cancelled date</i>
diff --git a/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot_-_without_access_keys.approved.html b/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot_-_without_access_keys.approved.html
index e35d282d..adbc103c 100644
--- a/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot_-_without_access_keys.approved.html
+++ b/tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot_-_without_access_keys.approved.html
@@ -56,18 +56,51 @@
       </code>
       <label for="due">Due</label>
       <input id="due" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         📅
         <i>no due date</i>
       </code>
       <label for="scheduled">Scheduled</label>
       <input id="scheduled" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ⏳
         <i>no scheduled date</i>
       </code>
       <label for="start">Start</label>
       <input id="start" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         🛫
         <i>no start date</i>
@@ -141,18 +174,51 @@
       </select>
       <label for="created">Created</label>
       <input id="created" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ➕
         <i>no created date</i>
       </code>
       <label for="done">Done</label>
       <input id="done" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ✅
         <i>no done date</i>
       </code>
       <label for="cancelled">Cancelled</label>
       <input id="cancelled" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
+      <svg
+        class="calendar-icon"
+        xmlns="http://www.w3.org/2000/svg"
+        viewBox="0 0 24 24"
+        fill="currentColor"
+        width="24px"
+        height="24px"
+        style="cursor: pointer">
+        <path
+          d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h5v5H7z"></path>
+      </svg>
       <code class="tasks-modal-parsed-date">
         ❌
         <i>no cancelled date</i>
diff --git a/tests/ui/EditTask.test.ts b/tests/ui/EditTask.test.ts
index bfb3a8fb..404131ff 100644
--- a/tests/ui/EditTask.test.ts
+++ b/tests/ui/EditTask.test.ts
@@ -43,6 +43,7 @@ function renderAndCheckModal(task: Task, onSubmit: (updatedTasks: Task[]) => voi
         statusOptions: StatusRegistry.getInstance().registeredStatuses,
         onSubmit,
         allTasks,
+        modal: null,
     });
     const { container } = result;
     expect(() => container).toBeTruthy();