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

This commit is contained in:
evykassirer 2024-03-24 20:40:21 -07:00 committed by Tim Abbott
parent 9fc6793809
commit f0578c318c
7 changed files with 27 additions and 28 deletions

View File

@ -192,7 +192,7 @@ class Typeahead<ItemType extends string | object> {
input_element: InputElement; input_element: InputElement;
items: number; items: number;
matcher: (item: ItemType, query: string) => boolean; matcher: (item: ItemType, query: string) => boolean;
sorter: (items: ItemType[]) => ItemType[]; sorter: (items: ItemType[], query: string) => ItemType[];
highlighter_html: (item: ItemType, query: string) => string | undefined; highlighter_html: (item: ItemType, query: string) => string | undefined;
updater: ( updater: (
item: ItemType, item: ItemType,
@ -384,7 +384,7 @@ class Typeahead<ItemType extends string | object> {
process(items: ItemType[]): this { process(items: ItemType[]): this {
const matching_items = $.grep(items, (item) => this.matcher(item, this.query)); const matching_items = $.grep(items, (item) => this.matcher(item, this.query));
const final_items = this.sorter(matching_items); const final_items = this.sorter(matching_items, this.query);
if (!final_items.length) { if (!final_items.length) {
return this.shown ? this.hide() : this; return this.shown ? this.hide() : this;
@ -705,7 +705,7 @@ type TypeaheadOptions<ItemType> = {
openInputFieldOnKeyUp?: () => void; openInputFieldOnKeyUp?: () => void;
option_label?: (matching_items: ItemType[], item: ItemType) => string | false; option_label?: (matching_items: ItemType[], item: ItemType) => string | false;
parentElement?: string; parentElement?: string;
sorter: (items: ItemType[]) => ItemType[]; sorter: (items: ItemType[], query: string) => ItemType[];
stopAdvance?: boolean; stopAdvance?: boolean;
tabIsEnter?: boolean; tabIsEnter?: boolean;
trigger_selection?: (event: JQuery.KeyDownEvent) => boolean; trigger_selection?: (event: JQuery.KeyDownEvent) => boolean;

View File

@ -1118,10 +1118,10 @@ export function initialize_topic_edit_typeahead(form_field, stream_name, dropup)
highlighter_html(item) { highlighter_html(item) {
return typeahead_helper.render_typeahead_item({primary: item}); return typeahead_helper.render_typeahead_item({primary: item});
}, },
sorter(items) { sorter(items, query) {
const sorted = typeahead_helper.sorter(this.query, items, (x) => x); const sorted = typeahead_helper.sorter(query, items, (x) => x);
if (sorted.length > 0 && !sorted.includes(this.query)) { if (sorted.length > 0 && !sorted.includes(query)) {
sorted.unshift(this.query); sorted.unshift(query);
} }
return sorted; return sorted;
}, },
@ -1207,10 +1207,10 @@ export function initialize({on_enter_send}) {
highlighter_html(item) { highlighter_html(item) {
return typeahead_helper.render_typeahead_item({primary: item}); return typeahead_helper.render_typeahead_item({primary: item});
}, },
sorter(items) { sorter(items, query) {
const sorted = typeahead_helper.sorter(this.query, items, (x) => x); const sorted = typeahead_helper.sorter(query, items, (x) => x);
if (sorted.length > 0 && !sorted.includes(this.query)) { if (sorted.length > 0 && !sorted.includes(query)) {
sorted.unshift(this.query); sorted.unshift(query);
} }
return sorted; return sorted;
}, },

View File

@ -176,8 +176,8 @@ export function initialize_custom_pronouns_type_fields(element_id) {
source() { source() {
return commonly_used_pronouns; return commonly_used_pronouns;
}, },
sorter(items) { sorter(items, query) {
return bootstrap_typeahead.defaultSorter(items, this.query); return bootstrap_typeahead.defaultSorter(items, query);
}, },
highlighter_html(item) { highlighter_html(item) {
return typeahead_helper.render_typeahead_item({primary: item}); return typeahead_helper.render_typeahead_item({primary: item});

View File

@ -99,8 +99,7 @@ export function set_up($input, pills, opts) {
} }
return matches; return matches;
}, },
sorter(matches) { sorter(matches, query) {
const query = this.query;
if (include_streams(query)) { if (include_streams(query)) {
return typeahead_helper.sort_streams(matches, query.trim().slice(1)); return typeahead_helper.sort_streams(matches, query.trim().slice(1));
} }

View File

@ -170,8 +170,8 @@ function build_page() {
const q = query.trim().toLowerCase(); const q = query.trim().toLowerCase();
return item.toLowerCase().startsWith(q); return item.toLowerCase().startsWith(q);
}, },
sorter(items) { sorter(items, query) {
return bootstrap_typeahead.defaultSorter(items, this.query); return bootstrap_typeahead.defaultSorter(items, query);
}, },
}); });

View File

@ -796,28 +796,28 @@ test("initialize", ({override, override_rewire, mock_template}) => {
// Notice that alphabetical sorting isn't managed by this sorter, // Notice that alphabetical sorting isn't managed by this sorter,
// it is a result of the topics already being sorted after adding // it is a result of the topics already being sorted after adding
// them with add_topic(). // them with add_topic().
options.query = "furniture"; let query = "furniture";
actual_value = options.sorter(["furniture"]); actual_value = options.sorter(["furniture"], query);
expected_value = ["furniture"]; expected_value = ["furniture"];
assert.deepEqual(actual_value, expected_value); assert.deepEqual(actual_value, expected_value);
// A literal match at the beginning of an element puts it at the top. // A literal match at the beginning of an element puts it at the top.
options.query = "ice"; query = "ice";
actual_value = options.sorter(["even more ice", "ice", "more ice"]); actual_value = options.sorter(["even more ice", "ice", "more ice"], query);
expected_value = ["ice", "even more ice", "more ice"]; expected_value = ["ice", "even more ice", "more ice"];
assert.deepEqual(actual_value, expected_value); assert.deepEqual(actual_value, expected_value);
// The sorter should return the query as the first element if there // The sorter should return the query as the first element if there
// isn't a topic with such name. // isn't a topic with such name.
// This only happens if typeahead is providing other suggestions. // This only happens if typeahead is providing other suggestions.
options.query = "e"; // Letter present in "furniture" and "ice" query = "e"; // Letter present in "furniture" and "ice"
actual_value = options.sorter(["furniture", "ice"]); actual_value = options.sorter(["furniture", "ice"], query);
expected_value = ["e", "furniture", "ice"]; expected_value = ["e", "furniture", "ice"];
assert.deepEqual(actual_value, expected_value); assert.deepEqual(actual_value, expected_value);
// 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"; query = "non-existing-topic";
actual_value = options.sorter([]); actual_value = options.sorter([], query);
expected_value = []; expected_value = [];
assert.deepEqual(actual_value, expected_value); assert.deepEqual(actual_value, expected_value);

View File

@ -233,17 +233,17 @@ run_test("set_up", ({mock_template, override}) => {
(function test_sorter() { (function test_sorter() {
if (opts.stream) { if (opts.stream) {
sort_streams_called = false; sort_streams_called = false;
config.sorter.call(fake_stream_this); config.sorter([], fake_stream_this.query);
assert.ok(sort_streams_called); assert.ok(sort_streams_called);
} }
if (opts.user_group) { if (opts.user_group) {
sort_recipients_called = false; sort_recipients_called = false;
config.sorter.call(fake_group_this, [testers]); config.sorter([testers], fake_group_this.query);
assert.ok(sort_recipients_called); assert.ok(sort_recipients_called);
} }
if (opts.user) { if (opts.user) {
sort_recipients_called = false; sort_recipients_called = false;
config.sorter.call(fake_person_this, [me]); config.sorter([me], fake_person_this.query);
assert.ok(sort_recipients_called); assert.ok(sort_recipients_called);
} }
})(); })();