todo_widget: Disable "Add task" button when a task cannot be added.

We want to prevent users from adding a duplicate or empty
task to the todo list. For this purpose, we disable the
"Add task" button in both the cases.

Fixes: #20211
This commit is contained in:
whilstsomebody
2025-03-01 14:24:57 +05:30
committed by Tim Abbott
parent 926716d9f2
commit 1878200402
4 changed files with 73 additions and 5 deletions

View File

@@ -258,6 +258,20 @@ export function initialize(): void {
appendTo: () => document.body,
});
tippy.delegate("body", {
target: ".add-task-wrapper",
onShow(instance) {
const $elem = $(instance.reference);
const content = $elem.attr("data-tippy-content");
if (content === undefined) {
return false;
}
instance.setContent(content);
return undefined;
},
appendTo: () => document.body,
});
$("body").on(
"blur",
".message_control_button, .delete-selected-drafts-button-container",

View File

@@ -1,4 +1,5 @@
import $ from "jquery";
import _ from "lodash";
import assert from "minimalistic-assert";
import {z} from "zod";
@@ -404,6 +405,13 @@ export function activate({
const html = render_widgets_todo_widget();
$elem.html(html);
// This throttling ensures that the function runs only after the user stops typing.
const throttled_update_add_task_button = _.throttle(update_add_task_button, 300);
$elem.find("input.add-task").on("keyup", (e) => {
e.stopPropagation();
throttled_update_add_task_button();
});
$elem.find("input.todo-task-list-title").on("keyup", (e) => {
e.stopPropagation();
update_edit_controls();
@@ -444,13 +452,10 @@ export function activate({
const task = $elem.find<HTMLInputElement>("input.add-task").val()?.trim() ?? "";
const desc = $elem.find<HTMLInputElement>("input.add-desc").val()?.trim() ?? "";
if (task === "") {
return;
}
$elem.find("input.add-task").val("").trigger("focus");
$elem.find("input.add-desc").val("");
// This case should not generally occur.
const task_exists = task_data.name_in_use(task);
if (task_exists) {
$elem.find(".widget-error").text($t({defaultMessage: "Task already exists"}));
@@ -462,6 +467,30 @@ export function activate({
});
}
function update_add_task_button(): void {
const task = $elem.find<HTMLInputElement>("input.add-task").val()?.trim() ?? "";
const task_exists = task_data.name_in_use(task);
const $add_task_wrapper = $elem.find(".add-task-wrapper");
const $add_task_button = $elem.find("button.add-task");
if (task === "") {
$add_task_wrapper.attr(
"data-tippy-content",
$t({defaultMessage: "Name the task before adding."}),
);
$add_task_button.prop("disabled", true);
} else if (task_exists) {
$add_task_wrapper.attr(
"data-tippy-content",
$t({defaultMessage: "Cannot add duplicate task."}),
);
$add_task_button.prop("disabled", true);
} else {
$add_task_wrapper.removeAttr("data-tippy-content");
$add_task_button.prop("disabled", false);
}
}
function render_results(): void {
const widget_data = task_data.get_widget_data();
const html = render_widgets_todo_widget_tasks(widget_data);
@@ -487,6 +516,8 @@ export function activate({
const data = task_data.handle.strike.outbound(key);
callback(data);
});
update_add_task_button();
}
const handle_events = function (events: Event[]): void {

View File

@@ -237,6 +237,13 @@ button {
transition: 0.2s ease;
transition-property: background-color, border-color, color;
}
&:disabled {
cursor: not-allowed;
filter: saturate(0);
background-color: var(--color-background-zulip-button-disabled);
color: hsl(0deg 3% 52%);
}
}
}
@@ -308,3 +315,17 @@ input {
.current-user-vote {
background-color: hsl(156deg 10% 90% / 90%);
}
.add-task-wrapper {
display: inline;
position: relative;
z-index: 1;
/* Unlike other browsers like Chrome, Microsoft Edge, etc.,
Firefox does not automatically display the "not-allowed"
cursor for disabled elements. The below css ensures that the
correct cursor is shown across all browsers. */
&:hover {
cursor: not-allowed;
}
}

View File

@@ -13,7 +13,9 @@
<div class="add-task-bar sea-green">
<input type="text" class="add-task" placeholder="{{t 'New task'}}" />
<input type="text" class="add-desc" placeholder="{{t 'Description'}}" />
<button class="add-task">{{t "Add task" }}</button>
<div class="add-task-wrapper">
<button class="add-task">{{t "Add task" }}</button>
</div>
<div class="widget-error"></div>
</div>
</div>