Skip to content

Commit 1461ba4

Browse files
authored
Merge pull request #3169 from ilandikov/feat-date-picker-in-modal
feat: Add date-picker to the Edit Task modal
2 parents 3c5858b + 2b1b28d commit 1461ba4

8 files changed

+107
-40
lines changed

src/ui/DateEditor.svelte

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,22 @@
2323
// setIcon(node, 'calendar-days');
2424
// };
2525
26+
let pickedDate = '';
27+
2628
$: {
2729
date = doAutocomplete(date);
2830
parsedDate = parseTypedDateForDisplayUsingFutureDate(id, date, forwardOnly);
2931
isDateValid = !parsedDate.includes('invalid');
32+
if (isDateValid) {
33+
pickedDate = parsedDate;
34+
}
35+
}
36+
37+
function onDatePicked(e: Event) {
38+
if (e.target === null) {
39+
return;
40+
}
41+
date = pickedDate;
3042
}
3143
3244
// 'weekend' abbreviation omitted due to lack of space.
@@ -106,8 +118,20 @@
106118
<!-- aria-label="Open date picker"-->
107119
<!-- style="background: none; border: none; padding: 0; cursor: pointer;"-->
108120
<!--/>-->
109-
110-
<code class="tasks-modal-parsed-date">{dateSymbol} {@html parsedDate}</code>
121+
{#if isDateValid}
122+
<div class="tasks-modal-parsed-date">
123+
{dateSymbol}<input
124+
class="tasks-modal-date-editor-picker"
125+
type="date"
126+
bind:value={pickedDate}
127+
id="date-editor-picker"
128+
on:input={onDatePicked}
129+
tabindex="-1"
130+
/>
131+
</div>
132+
{:else}
133+
<code class="tasks-modal-parsed-date">{dateSymbol} {@html parsedDate}</code>
134+
{/if}
111135

112136
<style>
113137
</style>

src/ui/EditTask.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
min-width: 15em;
8282
}
8383

84+
.tasks-modal-date-editor-picker {
85+
margin-left: .5em;
86+
}
87+
8488
//.tasks-modal-calendar-button {
8589
// grid-column: 3;
8690
// color: var(--text-muted);

tests/ui/DateEditor.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,24 @@ function testInputValue(container: HTMLElement, inputId: string, expectedText: s
2222
expect(input.value).toEqual(expectedText);
2323
}
2424

25+
function testDatePickerValue(container: HTMLElement, expectedValue: string) {
26+
const datePicker = getAndCheckRenderedElement<HTMLInputElement>(container, 'date-editor-picker');
27+
expect(datePicker.value).toEqual(expectedValue);
28+
}
29+
2530
async function testTypingInput(
2631
{
2732
userTyped,
2833
expectedLeftText,
2934
expectedRightText,
3035
expectedReturnedDate,
36+
expectedReturnedDateValidity = 'true',
3137
}: {
3238
userTyped: string;
3339
expectedLeftText: string;
3440
expectedRightText: string;
3541
expectedReturnedDate: string;
42+
expectedReturnedDateValidity?: 'true' | 'false';
3643
},
3744
{ forwardOnly }: { forwardOnly: boolean } = { forwardOnly: true },
3845
) {
@@ -44,6 +51,14 @@ async function testTypingInput(
4451
testInputValue(container, 'due', expectedLeftText);
4552
testInputValue(container, 'parsedDateFromDateEditor', expectedRightText);
4653
testInputValue(container, 'dueDateFromDateEditor', expectedReturnedDate);
54+
testInputValue(container, 'parsedDateValidFromDateEditor', expectedReturnedDateValidity);
55+
56+
if (expectedReturnedDateValidity === 'true') {
57+
testDatePickerValue(container, expectedRightText);
58+
} else {
59+
const datePicker = container.ownerDocument.getElementById('date-editor-picker') as HTMLInputElement;
60+
expect(datePicker).toBeNull();
61+
}
4762
}
4863

4964
beforeEach(() => {
@@ -62,6 +77,8 @@ describe('date editor wrapper tests', () => {
6277
testInputValue(container, 'due', '');
6378
testInputValue(container, 'parsedDateFromDateEditor', '<i>no due date</i>');
6479
testInputValue(container, 'dueDateFromDateEditor', '');
80+
81+
testDatePickerValue(container, '');
6582
});
6683

6784
it('should replace an empty date field with typed date value', async () => {
@@ -88,6 +105,7 @@ describe('date editor wrapper tests', () => {
88105
expectedLeftText: 'blah',
89106
expectedRightText: '<i>invalid due date</i>',
90107
expectedReturnedDate: 'blah',
108+
expectedReturnedDateValidity: 'false',
91109
});
92110
});
93111

@@ -114,4 +132,18 @@ describe('date editor wrapper tests', () => {
114132
{ forwardOnly: false },
115133
);
116134
});
135+
136+
it('should pick a date', async () => {
137+
const container = renderDateEditorWrapper({ forwardOnly: false });
138+
const datePicker = getAndCheckRenderedElement<HTMLInputElement>(container, 'date-editor-picker');
139+
140+
await fireEvent.input(datePicker, { target: { value: '2024-11-03' } });
141+
142+
expect(datePicker.value).toEqual('2024-11-03');
143+
144+
testInputValue(container, 'due', '2024-11-03');
145+
testInputValue(container, 'parsedDateFromDateEditor', '2024-11-03');
146+
testInputValue(container, 'dueDateFromDateEditor', '2024-11-03');
147+
testInputValue(container, 'parsedDateValidFromDateEditor', 'true');
148+
});
117149
});

tests/ui/DateEditorWrapper.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
/>
2323
<input bind:value={dateFromDateEditor} id="dueDateFromDateEditor" />
2424
<input bind:value={parsedDateFromDateEditor} id="parsedDateFromDateEditor" />
25+
<input bind:value={isDueDateValid} id="parsedDateValidFromDateEditor" />
2526

2627
<style>
2728
</style>

tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot.approved.html

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,23 +94,23 @@
9494
class="tasks-modal-date-input"
9595
placeholder="Try 'Mon' or 'tm' then space"
9696
accesskey="d" />
97-
<code class="tasks-modal-parsed-date">
97+
<div class="tasks-modal-parsed-date">
9898
📅
99-
<i>no due date</i>
100-
</code>
99+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
100+
</div>
101101
<label for="scheduled">
102102
<span class="accesskey">S</span>
103103
cheduled
104104
</label>
105105
<input
106106
id="scheduled"
107107
type="text"
108-
class="tasks-modal-date-input"
108+
class="tasks-modal-date-input tasks-modal-error"
109109
placeholder="Try 'Mon' or 'tm' then space"
110110
accesskey="s" />
111111
<code class="tasks-modal-parsed-date">
112112
113-
<i>no scheduled date</i>
113+
<i>invalid scheduled date</i>
114114
</code>
115115
<label for="start">
116116
St
@@ -123,10 +123,10 @@
123123
class="tasks-modal-date-input"
124124
placeholder="Try 'Mon' or 'tm' then space"
125125
accesskey="a" />
126-
<code class="tasks-modal-parsed-date">
126+
<div class="tasks-modal-parsed-date">
127127
🛫
128-
<i>no start date</i>
129-
</code>
128+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
129+
</div>
130130
<div class="future-dates-only">
131131
<label for="forwardOnly">
132132
Only
@@ -221,10 +221,10 @@
221221
class="tasks-modal-date-input"
222222
placeholder="Try 'Mon' or 'tm' then space"
223223
accesskey="c" />
224-
<code class="tasks-modal-parsed-date">
224+
<div class="tasks-modal-parsed-date">
225225
226-
<i>no created date</i>
227-
</code>
226+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
227+
</div>
228228
<label for="done">
229229
Done (
230230
<span class="accesskey">x</span>
@@ -236,10 +236,10 @@
236236
class="tasks-modal-date-input"
237237
placeholder="Try 'Mon' or 'tm' then space"
238238
accesskey="x" />
239-
<code class="tasks-modal-parsed-date">
239+
<div class="tasks-modal-parsed-date">
240240
241-
<i>no done date</i>
242-
</code>
241+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
242+
</div>
243243
<label for="cancelled">
244244
Cancelled (
245245
<span class="accesskey">-</span>
@@ -251,13 +251,13 @@
251251
class="tasks-modal-date-input"
252252
placeholder="Try 'Mon' or 'tm' then space"
253253
accesskey="-" />
254-
<code class="tasks-modal-parsed-date">
254+
<div class="tasks-modal-parsed-date">
255255
256-
<i>no cancelled date</i>
257-
</code>
256+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
257+
</div>
258258
</section>
259259
<section class="tasks-modal-button-section">
260-
<button type="submit" class="mod-cta">Apply</button>
260+
<button type="submit" class="mod-cta" disabled="">Apply</button>
261261
<button type="button">Cancel</button>
262262
</section>
263263
</form>

tests/ui/EditTask.test.Edit_Modal_HTML_snapshot_tests_should_match_snapshot_-_without_access_keys.approved.html

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,26 @@
5656
</code>
5757
<label for="due">Due</label>
5858
<input id="due" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
59-
<code class="tasks-modal-parsed-date">
59+
<div class="tasks-modal-parsed-date">
6060
📅
61-
<i>no due date</i>
62-
</code>
61+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
62+
</div>
6363
<label for="scheduled">Scheduled</label>
64-
<input id="scheduled" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
64+
<input
65+
id="scheduled"
66+
type="text"
67+
class="tasks-modal-date-input tasks-modal-error"
68+
placeholder="Try 'Mon' or 'tm' then space" />
6569
<code class="tasks-modal-parsed-date">
6670
67-
<i>no scheduled date</i>
71+
<i>invalid scheduled date</i>
6872
</code>
6973
<label for="start">Start</label>
7074
<input id="start" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
71-
<code class="tasks-modal-parsed-date">
75+
<div class="tasks-modal-parsed-date">
7276
🛫
73-
<i>no start date</i>
74-
</code>
77+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
78+
</div>
7579
<div class="future-dates-only">
7680
<label for="forwardOnly">Only future dates:</label>
7781
<input id="forwardOnly" type="checkbox" class="task-list-item-checkbox tasks-modal-checkbox" />
@@ -141,25 +145,25 @@
141145
</select>
142146
<label for="created">Created</label>
143147
<input id="created" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
144-
<code class="tasks-modal-parsed-date">
148+
<div class="tasks-modal-parsed-date">
145149
146-
<i>no created date</i>
147-
</code>
150+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
151+
</div>
148152
<label for="done">Done</label>
149153
<input id="done" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
150-
<code class="tasks-modal-parsed-date">
154+
<div class="tasks-modal-parsed-date">
151155
152-
<i>no done date</i>
153-
</code>
156+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
157+
</div>
154158
<label for="cancelled">Cancelled</label>
155159
<input id="cancelled" type="text" class="tasks-modal-date-input" placeholder="Try 'Mon' or 'tm' then space" />
156-
<code class="tasks-modal-parsed-date">
160+
<div class="tasks-modal-parsed-date">
157161
158-
<i>no cancelled date</i>
159-
</code>
162+
<input class="tasks-modal-date-editor-picker" type="date" id="date-editor-picker" tabindex="-1" />
163+
</div>
160164
</section>
161165
<section class="tasks-modal-button-section">
162-
<button type="submit" class="mod-cta">Apply</button>
166+
<button type="submit" class="mod-cta" disabled="">Apply</button>
163167
<button type="button">Cancel</button>
164168
</section>
165169
</form>

tests/ui/EditTask.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,9 @@ describe('Edit Modal HTML snapshot tests', () => {
658658
});
659659

660660
function verifyModalHTML() {
661-
const task = taskFromLine({ line: '- [ ] absolutely to do', path: '' });
661+
// Populate task a valid and an invalid date. Note that the valid date value
662+
// is not visible in the HTML output.
663+
const task = taskFromLine({ line: '- [ ] absolutely to do 🛫 2024-01-01 ⏳ 2024-02-33', path: '' });
662664
const onSubmit = () => {};
663665
const allTasks = [task];
664666
const { container } = renderAndCheckModal(task, onSubmit, allTasks);

tests/ui/RenderingTestHelpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type EditTask from '../../src/ui/EditTask.svelte';
99
*/
1010
export function getAndCheckRenderedElement<T>(container: HTMLElement, elementId: string) {
1111
const element = container.ownerDocument.getElementById(elementId) as T;
12-
expect(() => element).toBeTruthy();
12+
expect(element).not.toBeNull();
1313
return element;
1414
}
1515

0 commit comments

Comments
 (0)