typeahead: Pass query to updater instead of using hacky this.

This commit is contained in:
evykassirer 2024-03-24 20:42:57 -07:00 committed by Tim Abbott
parent f0578c318c
commit b4299d99fd
5 changed files with 80 additions and 75 deletions

View File

@ -196,6 +196,7 @@ class Typeahead<ItemType extends string | object> {
highlighter_html: (item: ItemType, query: string) => string | undefined;
updater: (
item: ItemType,
query: string,
event?: JQuery.ClickEvent | JQuery.KeyUpEvent | JQuery.KeyDownEvent,
) => string | undefined;
$container: JQuery;
@ -270,12 +271,14 @@ class Typeahead<ItemType extends string | object> {
select(e?: JQuery.ClickEvent | JQuery.KeyUpEvent | JQuery.KeyDownEvent): this {
const val = this.$menu.find(".active").data("typeahead-value");
if (this.input_element.type === "contenteditable") {
this.input_element.$element.text(this.updater(val, e) ?? "").trigger("change");
this.input_element.$element
.text(this.updater(val, this.query, e) ?? "")
.trigger("change");
// Empty text after the change event handler
// converts the input text to html elements.
this.input_element.$element.text("");
} else {
const after_text = this.updater(val, e) ?? "";
const after_text = this.updater(val, this.query, e) ?? "";
const element_val = this.input_element.$element.val();
assert(element_val !== undefined);
const [from, to_before, to_after] = get_string_diff(element_val, after_text);
@ -711,6 +714,7 @@ type TypeaheadOptions<ItemType> = {
trigger_selection?: (event: JQuery.KeyDownEvent) => boolean;
updater: (
item: ItemType,
query: string,
event?: JQuery.ClickEvent | JQuery.KeyUpEvent | JQuery.KeyDownEvent,
) => string | undefined;
};

View File

@ -872,8 +872,8 @@ export function content_highlighter_html(item) {
}
}
export function content_typeahead_selected(item, event) {
const pieces = split_at_cursor(this.query, this.input_element.$element);
export function content_typeahead_selected(item, query, event) {
const pieces = split_at_cursor(query, this.input_element.$element);
let beginning = pieces[0];
let rest = pieces[1];
const $textbox = this.input_element.$element;

View File

@ -122,8 +122,8 @@ export function set_up($input, pills, opts) {
max_num_items: undefined,
});
},
updater(item) {
if (include_streams(this.query)) {
updater(item, query) {
if (include_streams(query)) {
stream_pill.append_stream(item, pills);
} else if (include_user_groups && user_groups.is_user_group(item)) {
user_group_pill.append_user_group(item, pills);

View File

@ -438,11 +438,12 @@ test("content_typeahead_selected", ({override}) => {
};
let caret_called1 = false;
let caret_called2 = false;
let query;
fake_this.input_element.$element.caret = function (...args) {
if (args.length === 0) {
// .caret() used in split_at_cursor
caret_called1 = true;
return fake_this.query.length;
return query.length;
}
const [arg1, arg2] = args;
// .caret() used in setTimeout
@ -463,25 +464,25 @@ test("content_typeahead_selected", ({override}) => {
// emoji
ct.get_or_set_completing_for_tests("emoji");
fake_this.query = ":octo";
query = ":octo";
ct.get_or_set_token_for_testing("octo");
const item = {
emoji_name: "octopus",
};
let actual_value = ct.content_typeahead_selected.call(fake_this, item);
let actual_value = ct.content_typeahead_selected.call(fake_this, item, query);
let expected_value = ":octopus: ";
assert.equal(actual_value, expected_value);
fake_this.query = " :octo";
query = " :octo";
ct.get_or_set_token_for_testing("octo");
actual_value = ct.content_typeahead_selected.call(fake_this, item);
actual_value = ct.content_typeahead_selected.call(fake_this, item, query);
expected_value = " :octopus: ";
assert.equal(actual_value, expected_value);
fake_this.query = "{:octo";
query = "{:octo";
ct.get_or_set_token_for_testing("octo");
actual_value = ct.content_typeahead_selected.call(fake_this, item);
actual_value = ct.content_typeahead_selected.call(fake_this, item, query);
expected_value = "{ :octopus: ";
assert.equal(actual_value, expected_value);
@ -495,9 +496,9 @@ test("content_typeahead_selected", ({override}) => {
(mention_text) => mention_text,
);
fake_this.query = "@**Mark Tw";
query = "@**Mark Tw";
ct.get_or_set_token_for_testing("Mark Tw");
actual_value = ct.content_typeahead_selected.call(fake_this, twin1);
actual_value = ct.content_typeahead_selected.call(fake_this, twin1, query);
expected_value = "@**Mark Twin|105** ";
assert.equal(actual_value, expected_value);
@ -507,118 +508,118 @@ test("content_typeahead_selected", ({override}) => {
warned_for_mention = true;
});
fake_this.query = "@oth";
query = "@oth";
ct.get_or_set_token_for_testing("oth");
actual_value = ct.content_typeahead_selected.call(fake_this, othello);
actual_value = ct.content_typeahead_selected.call(fake_this, othello, query);
expected_value = "@**Othello, the Moor of Venice** ";
assert.equal(actual_value, expected_value);
assert.ok(warned_for_mention);
fake_this.query = "Hello @oth";
query = "Hello @oth";
ct.get_or_set_token_for_testing("oth");
actual_value = ct.content_typeahead_selected.call(fake_this, othello);
actual_value = ct.content_typeahead_selected.call(fake_this, othello, query);
expected_value = "Hello @**Othello, the Moor of Venice** ";
assert.equal(actual_value, expected_value);
fake_this.query = "@**oth";
query = "@**oth";
ct.get_or_set_token_for_testing("oth");
actual_value = ct.content_typeahead_selected.call(fake_this, othello);
actual_value = ct.content_typeahead_selected.call(fake_this, othello, query);
expected_value = "@**Othello, the Moor of Venice** ";
assert.equal(actual_value, expected_value);
fake_this.query = "@*oth";
query = "@*oth";
ct.get_or_set_token_for_testing("oth");
actual_value = ct.content_typeahead_selected.call(fake_this, othello);
actual_value = ct.content_typeahead_selected.call(fake_this, othello, query);
expected_value = "@**Othello, the Moor of Venice** ";
assert.equal(actual_value, expected_value);
fake_this.query = "@back";
query = "@back";
ct.get_or_set_token_for_testing("back");
with_overrides(({disallow}) => {
disallow(compose_validate, "warn_if_mentioning_unsubscribed_user");
actual_value = ct.content_typeahead_selected.call(fake_this, backend);
actual_value = ct.content_typeahead_selected.call(fake_this, backend, query);
});
expected_value = "@*Backend* ";
assert.equal(actual_value, expected_value);
fake_this.query = "@*back";
query = "@*back";
ct.get_or_set_token_for_testing("back");
actual_value = ct.content_typeahead_selected.call(fake_this, backend);
actual_value = ct.content_typeahead_selected.call(fake_this, backend, query);
expected_value = "@*Backend* ";
assert.equal(actual_value, expected_value);
// silent mention
ct.get_or_set_completing_for_tests("silent_mention");
fake_this.query = "@_kin";
query = "@_kin";
ct.get_or_set_token_for_testing("kin");
with_overrides(({disallow}) => {
disallow(compose_validate, "warn_if_mentioning_unsubscribed_user");
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet);
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet, query);
});
expected_value = "@_**King Hamlet** ";
assert.equal(actual_value, expected_value);
fake_this.query = "Hello @_kin";
query = "Hello @_kin";
ct.get_or_set_token_for_testing("kin");
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet);
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet, query);
expected_value = "Hello @_**King Hamlet** ";
assert.equal(actual_value, expected_value);
fake_this.query = "@_*kin";
query = "@_*kin";
ct.get_or_set_token_for_testing("kin");
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet);
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet, query);
expected_value = "@_**King Hamlet** ";
assert.equal(actual_value, expected_value);
fake_this.query = "@_**kin";
query = "@_**kin";
ct.get_or_set_token_for_testing("kin");
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet);
actual_value = ct.content_typeahead_selected.call(fake_this, hamlet, query);
expected_value = "@_**King Hamlet** ";
assert.equal(actual_value, expected_value);
fake_this.query = "@_back";
query = "@_back";
ct.get_or_set_token_for_testing("back");
with_overrides(({disallow}) => {
disallow(compose_validate, "warn_if_mentioning_unsubscribed_user");
actual_value = ct.content_typeahead_selected.call(fake_this, backend);
actual_value = ct.content_typeahead_selected.call(fake_this, backend, query);
});
expected_value = "@_*Backend* ";
assert.equal(actual_value, expected_value);
fake_this.query = "@_*back";
query = "@_*back";
ct.get_or_set_token_for_testing("back");
actual_value = ct.content_typeahead_selected.call(fake_this, backend);
actual_value = ct.content_typeahead_selected.call(fake_this, backend, query);
expected_value = "@_*Backend* ";
assert.equal(actual_value, expected_value);
fake_this.query = "/m";
query = "/m";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, me_slash);
actual_value = ct.content_typeahead_selected.call(fake_this, me_slash, query);
expected_value = "/me translated: is …";
assert.equal(actual_value, expected_value);
fake_this.query = "/da";
query = "/da";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, dark_slash);
actual_value = ct.content_typeahead_selected.call(fake_this, dark_slash, query);
expected_value = "/dark ";
assert.equal(actual_value, expected_value);
fake_this.query = "/ni";
query = "/ni";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, dark_slash);
actual_value = ct.content_typeahead_selected.call(fake_this, dark_slash, query);
expected_value = "/dark ";
assert.equal(actual_value, expected_value);
fake_this.query = "/li";
query = "/li";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, light_slash);
actual_value = ct.content_typeahead_selected.call(fake_this, light_slash, query);
expected_value = "/light ";
assert.equal(actual_value, expected_value);
fake_this.query = "/da";
query = "/da";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, light_slash);
actual_value = ct.content_typeahead_selected.call(fake_this, light_slash, query);
expected_value = "/light ";
assert.equal(actual_value, expected_value);
@ -630,79 +631,79 @@ test("content_typeahead_selected", ({override}) => {
warned_for_stream_link = true;
});
fake_this.query = "#swed";
query = "#swed";
ct.get_or_set_token_for_testing("swed");
actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream);
actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream, query);
expected_value = "#**Sweden** ";
assert.equal(actual_value, expected_value);
fake_this.query = "Hello #swed";
query = "Hello #swed";
ct.get_or_set_token_for_testing("swed");
actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream);
actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream, query);
expected_value = "Hello #**Sweden** ";
assert.equal(actual_value, expected_value);
fake_this.query = "#**swed";
query = "#**swed";
ct.get_or_set_token_for_testing("swed");
actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream);
actual_value = ct.content_typeahead_selected.call(fake_this, sweden_stream, query);
expected_value = "#**Sweden** ";
assert.equal(actual_value, expected_value);
// topic_list
ct.get_or_set_completing_for_tests("topic_list");
fake_this.query = "Hello #**Sweden>test";
query = "Hello #**Sweden>test";
ct.get_or_set_token_for_testing("test");
actual_value = ct.content_typeahead_selected.call(fake_this, "testing");
actual_value = ct.content_typeahead_selected.call(fake_this, "testing", query);
expected_value = "Hello #**Sweden>testing** ";
assert.equal(actual_value, expected_value);
fake_this.query = "Hello #**Sweden>";
query = "Hello #**Sweden>";
ct.get_or_set_token_for_testing("");
actual_value = ct.content_typeahead_selected.call(fake_this, "testing");
actual_value = ct.content_typeahead_selected.call(fake_this, "testing", query);
expected_value = "Hello #**Sweden>testing** ";
assert.equal(actual_value, expected_value);
// syntax
ct.get_or_set_completing_for_tests("syntax");
fake_this.query = "~~~p";
query = "~~~p";
ct.get_or_set_token_for_testing("p");
actual_value = ct.content_typeahead_selected.call(fake_this, "python");
actual_value = ct.content_typeahead_selected.call(fake_this, "python", query);
expected_value = "~~~python\n\n~~~";
assert.equal(actual_value, expected_value);
fake_this.query = "Hello ~~~p";
query = "Hello ~~~p";
ct.get_or_set_token_for_testing("p");
actual_value = ct.content_typeahead_selected.call(fake_this, "python");
actual_value = ct.content_typeahead_selected.call(fake_this, "python", query);
expected_value = "Hello ~~~python\n\n~~~";
assert.equal(actual_value, expected_value);
fake_this.query = "```p";
query = "```p";
ct.get_or_set_token_for_testing("p");
actual_value = ct.content_typeahead_selected.call(fake_this, "python");
actual_value = ct.content_typeahead_selected.call(fake_this, "python", query);
expected_value = "```python\n\n```";
assert.equal(actual_value, expected_value);
fake_this.query = "```spo";
query = "```spo";
ct.get_or_set_token_for_testing("spo");
actual_value = ct.content_typeahead_selected.call(fake_this, "spoiler");
actual_value = ct.content_typeahead_selected.call(fake_this, "spoiler", query);
expected_value = "```spoiler translated: Header\n\n```";
assert.equal(actual_value, expected_value);
// Test special case to not close code blocks if there is text afterward
fake_this.query = "```p\nsome existing code";
query = "```p\nsome existing code";
ct.get_or_set_token_for_testing("p");
fake_this.input_element.$element.caret = () => 4; // Put cursor right after ```p
actual_value = ct.content_typeahead_selected.call(fake_this, "python");
actual_value = ct.content_typeahead_selected.call(fake_this, "python", query);
expected_value = "```python\nsome existing code";
assert.equal(actual_value, expected_value);
ct.get_or_set_completing_for_tests("something-else");
fake_this.query = "foo";
actual_value = ct.content_typeahead_selected.call(fake_this, {});
expected_value = fake_this.query;
query = "foo";
actual_value = ct.content_typeahead_selected.call(fake_this, {}, query);
expected_value = query;
assert.equal(actual_value, expected_value);
assert.ok(caret_called1);

View File

@ -305,11 +305,11 @@ run_test("set_up", ({mock_template, override}) => {
return pills.length;
}
assert.equal(number_of_pills(), 0);
config.updater.call(fake_stream_this, denmark);
config.updater(denmark, fake_stream_this.query);
assert.equal(number_of_pills(), 1);
config.updater.call(fake_person_this, me);
config.updater(me, fake_person_this.query);
assert.equal(number_of_pills(), 2);
config.updater.call(fake_group_this, testers);
config.updater(testers, fake_group_this.query);
assert.equal(number_of_pills(), 3);
assert.ok(update_func_called);