typeahead: Improve compose topic typeahead behaviour.

Fixes: #29006

We make the following changes:
* Do not show the typeahead menu when there are no matching topics.
* When the typeahead is not shown, pressing Tab in the topic field
should move the cursor to the compose box.
* When the typeahead is shown, pressing Tab should select the
currently focused option (as it does now), but put the cursor at
the end of the topic, rather than at the beginning.
This commit is contained in:
Sohaib-Ahmed21 2024-03-11 18:14:57 +00:00 committed by Tim Abbott
parent 22bd8048b1
commit 7e99c90190
6 changed files with 17 additions and 21 deletions

View File

@ -40,14 +40,6 @@ async function create_stream_message_draft(page: Page): Promise<void> {
content: "Test stream message.", content: "Test stream message.",
}); });
await page.type("#stream_message_recipient_topic", "tests", {delay: 100}); await page.type("#stream_message_recipient_topic", "tests", {delay: 100});
// Ideally, we don't need to click on the typeahead to create a draft with this topic but
// the typeahead seems bugged and remains open in the next compose session even after it
// is being closed via clicking on #compose_close.
const entry = await page.waitForSelector('.typeahead[style*="display: block"] .active a', {
visible: true,
});
await entry!.click();
await page.click("#compose_close"); await page.click("#compose_close");
} }

View File

@ -40,7 +40,7 @@ export const pm_recipient = {
// input typeahead is different from the topic input // input typeahead is different from the topic input
// typeahead but both can be present in the DOM. // typeahead but both can be present in the DOM.
const entry = await page.waitForSelector('.typeahead[style*="display: block"] .active a', { const entry = await page.waitForSelector('.typeahead[style*="display: block"] .active a', {
visible: true, visible: false,
}); });
await entry!.click(); await entry!.click();
}, },

View File

@ -12,12 +12,9 @@ async function test_mention(page: Page): Promise<void> {
await page.waitForSelector("#compose", {visible: true}); await page.waitForSelector("#compose", {visible: true});
await common.select_stream_in_compose_via_dropdown(page, "Verona"); await common.select_stream_in_compose_via_dropdown(page, "Verona");
await common.select_item_via_typeahead( await common.fill_form(page, 'form[action^="/json/messages"]', {
page, stream_message_recipient_topic: "Test mention all",
"#stream_message_recipient_topic", });
"Test mention all",
"Test mention all",
);
await common.select_item_via_typeahead(page, "#compose-textarea", "@**all", "all"); await common.select_item_via_typeahead(page, "#compose-textarea", "@**all", "all");
await common.ensure_enter_does_not_send(page); await common.ensure_enter_does_not_send(page);

View File

@ -513,13 +513,20 @@ Typeahead.prototype = {
break; break;
case 9: // tab case 9: // tab
if (!this.options.tabIsEnter) { // If the typeahead is not shown or tabIsEnter option is not set, do nothing and return
return; if (!this.options.tabIsEnter || !this.shown) {
}
if (!this.shown) {
return; return;
} }
this.select(e); this.select(e);
if (e.currentTarget.id === "stream_message_recipient_topic") {
// Move the cursor to the end of the topic
const topic_length = this.$element.val().length;
this.$element[0].selectionStart = topic_length;
this.$element[0].selectionEnd = topic_length;
}
break; break;
case 13: // enter case 13: // enter

View File

@ -1175,7 +1175,7 @@ export function initialize({on_enter_send}) {
}, },
sorter(items) { sorter(items) {
const sorted = typeahead_helper.sorter(this.query, items, (x) => x); const sorted = typeahead_helper.sorter(this.query, items, (x) => x);
if (!sorted.includes(this.query)) { if (sorted.length > 0 && !sorted.includes(this.query)) {
sorted.unshift(this.query); sorted.unshift(this.query);
} }
return sorted; return sorted;

View File

@ -812,7 +812,7 @@ test("initialize", ({override, override_rewire, mock_template}) => {
// Suggest the query if this query doesn't match any existing topic. // Suggest the query if this query doesn't match any existing topic.
options.query = "non-existing-topic"; options.query = "non-existing-topic";
actual_value = options.sorter([]); actual_value = options.sorter([]);
expected_value = ["non-existing-topic"]; expected_value = [];
assert.deepEqual(actual_value, expected_value); assert.deepEqual(actual_value, expected_value);
topic_typeahead_called = true; topic_typeahead_called = true;