diff --git a/web/src/bootstrap_typeahead.ts b/web/src/bootstrap_typeahead.ts index 670fee9a4e..c0924a7914 100644 --- a/web/src/bootstrap_typeahead.ts +++ b/web/src/bootstrap_typeahead.ts @@ -196,6 +196,7 @@ class Typeahead { 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 { 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 = { trigger_selection?: (event: JQuery.KeyDownEvent) => boolean; updater: ( item: ItemType, + query: string, event?: JQuery.ClickEvent | JQuery.KeyUpEvent | JQuery.KeyDownEvent, ) => string | undefined; }; diff --git a/web/src/composebox_typeahead.js b/web/src/composebox_typeahead.js index 0dedd5bf45..3f064b5e06 100644 --- a/web/src/composebox_typeahead.js +++ b/web/src/composebox_typeahead.js @@ -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; diff --git a/web/src/pill_typeahead.js b/web/src/pill_typeahead.js index ea61a764cc..d2de093fae 100644 --- a/web/src/pill_typeahead.js +++ b/web/src/pill_typeahead.js @@ -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); diff --git a/web/tests/composebox_typeahead.test.js b/web/tests/composebox_typeahead.test.js index cc7e69dde7..15e6ad8edb 100644 --- a/web/tests/composebox_typeahead.test.js +++ b/web/tests/composebox_typeahead.test.js @@ -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); diff --git a/web/tests/pill_typeahead.test.js b/web/tests/pill_typeahead.test.js index 33452a2a1e..e7a7961c9a 100644 --- a/web/tests/pill_typeahead.test.js +++ b/web/tests/pill_typeahead.test.js @@ -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);