list_widget: Make `get_item` option required and pass a default where needed.

This is a prep commit for moving towards typescript migration of this module.
We should make `get_item` required on `list_widget` because its intended to be
used with a set of keys and then hydrate the keys lazily. Also this will help
us to avoid writing messy if-conditions while filtering and sorting and make
our life much easier when we migrate to TypeScript.

I added a `default_get_item` function which is just n identity function and
also refactored some code to make use of default get item.
This commit is contained in:
Lalit 2023-05-01 17:14:40 +05:30 committed by Tim Abbott
parent 7176590eea
commit 3720fd5c76
19 changed files with 51 additions and 24 deletions

View File

@ -23,6 +23,7 @@ export function rerender_alert_words_ui() {
ListWidget.create($word_list, words, {
name: "alert-words-list",
get_item: ListWidget.default_get_item,
modifier(alert_word) {
return render_alert_word_settings_item({alert_word});
},

View File

@ -94,6 +94,7 @@ function render_attachments_ui() {
ListWidget.create($uploaded_files_table, attachments, {
name: "uploaded-files-list",
get_item: ListWidget.default_get_item,
modifier(attachment) {
return render_uploaded_files_list({attachment});
},

View File

@ -116,6 +116,7 @@ export class DropdownListWidget {
this.list_widget = ListWidget.create($dropdown_list_body, this.get_data(data), {
name: `${CSS.escape(this.widget_name)}_list`,
get_item: ListWidget.default_get_item,
modifier(item) {
return render_dropdown_list({item});
},
@ -414,6 +415,7 @@ export class MultiSelectDropdownListWidget extends DropdownListWidget {
this.list_widget = ListWidget.create($dropdown_list_body, data, {
name: `${CSS.escape(this.widget_name)}_list`,
get_item: ListWidget.default_get_item,
modifier(item) {
return render_dropdown_list({item});
},

View File

@ -47,6 +47,7 @@ export function setup(tippy_props, get_options, item_click_callback, dropdown_pr
const list_widget = ListWidget.create($dropdown_list_body, get_options(), {
name: `${CSS.escape(tippy_props.target)}-list-widget`,
get_item: ListWidget.default_get_item,
modifier(item) {
return render_dropdown_list({item});
},

View File

@ -24,6 +24,10 @@ export function validate_opts(opts) {
blueslip.error("$simplebar_container is missing.");
return false;
}
if (!opts.get_item) {
blueslip.error("get_item is missing.");
return false;
}
return true;
}
@ -36,38 +40,28 @@ export function get_filtered_items(value, list, opts) {
const get_item = opts.get_item;
if (!opts.filter) {
if (get_item) {
return list.map((key) => get_item(key));
}
return [...list];
return list.map((key) => get_item(key));
}
if (opts.filter.filterer) {
if (get_item) {
return opts.filter.filterer(
list.map((key) => get_item(key)),
value,
);
}
return opts.filter.filterer(list, value);
return opts.filter.filterer(
list.map((key) => get_item(key)),
value,
);
}
const predicate = (item) => opts.filter.predicate(item, value);
if (get_item) {
const result = [];
const result = [];
for (const key of list) {
const item = get_item(key);
if (predicate(item)) {
result.push(item);
}
for (const key of list) {
const item = get_item(key);
if (predicate(item)) {
result.push(item);
}
return result;
}
return list.filter((item) => predicate(item));
return result;
}
export function alphabetic_sort(prop) {
@ -262,9 +256,7 @@ export function create($container, list, opts) {
return;
}
if (opts.get_item) {
item = opts.get_item(item);
}
item = opts.get_item(item);
const html = opts.modifier(item);
if (typeof html !== "string") {
blueslip.error("List item is not a string", {item: html});
@ -495,3 +487,5 @@ export function handle_sort($th, list) {
// and not if it is undefined.
list.sort(sort_type, prop_name);
}
export const default_get_item = (item) => item;

View File

@ -843,6 +843,7 @@ export function complete_rerender() {
$container.empty();
topics_widget = ListWidget.create($container, mapped_topic_values, {
name: "recent_topics_table",
get_item: ListWidget.default_get_item,
$parent_container: $("#recent_topics_table"),
modifier(item) {
return render_recent_topic_row(format_conversation(item));

View File

@ -111,6 +111,7 @@ export function populate_emoji() {
const $emoji_table = $("#admin_emoji_table").expectOne();
ListWidget.create($emoji_table, Object.values(emoji_data), {
name: "emoji_list",
get_item: ListWidget.default_get_item,
modifier(item) {
if (item.deactivated !== true) {
return render_admin_emoji_list({

View File

@ -41,6 +41,7 @@ export function populate_exports_table(exports) {
const $exports_table = $("#admin_exports_table").expectOne();
ListWidget.create($exports_table, Object.values(exports), {
name: "admin_exports_list",
get_item: ListWidget.default_get_item,
modifier(data) {
let failed_timestamp = data.failed_timestamp;
let deleted_timestamp = data.deleted_timestamp;

View File

@ -61,6 +61,7 @@ function populate_invites(invites_data) {
const $invites_table = $("#admin_invites_table").expectOne();
ListWidget.create($invites_table, invites_data.invites, {
name: "admin_invites_list",
get_item: ListWidget.default_get_item,
modifier(item) {
item.invited_absolute_time = timerender.absolute_time(item.invited * 1000);
if (item.expiry_date !== null) {

View File

@ -136,6 +136,7 @@ export function populate_linkifiers(linkifiers_data) {
const $linkifiers_table = $("#admin_linkifiers_table").expectOne();
ListWidget.create($linkifiers_table, linkifiers_data, {
name: "linkifiers_list",
get_item: ListWidget.default_get_item,
modifier(linkifier) {
return render_admin_linkifier_list({
linkifier: {

View File

@ -21,6 +21,7 @@ export function populate_list() {
ListWidget.create($muted_users_table, all_muted_users, {
name: "muted-users-list",
get_item: ListWidget.default_get_item,
modifier(muted_user) {
return render_muted_user_ui_row({muted_user});
},

View File

@ -35,6 +35,7 @@ export function populate_playgrounds(playgrounds_data) {
const $playgrounds_table = $("#admin_playgrounds_table").expectOne();
ListWidget.create($playgrounds_table, playgrounds_data, {
name: "playgrounds_list",
get_item: ListWidget.default_get_item,
modifier(playground) {
return render_admin_playground_list({
playground: {

View File

@ -108,6 +108,7 @@ export function build_default_stream_table() {
ListWidget.create($table, subs, {
name: "default_streams_list",
get_item: ListWidget.default_get_item,
modifier(item) {
return render_admin_default_streams_list({
stream: item,

View File

@ -25,6 +25,7 @@ export function populate_list() {
ListWidget.create($user_topics_table, all_user_topics, {
name: "user-topics-list",
get_item: ListWidget.default_get_item,
modifier(user_topic) {
const context = {
user_topic,

View File

@ -112,6 +112,7 @@ function make_list_widget({$parent_container, name, user_ids, user_can_remove_su
return ListWidget.create($list_container, users, {
name,
get_item: ListWidget.default_get_item,
modifier(item) {
return format_member_list_elem(item, user_can_remove_subscribers);
},

View File

@ -66,6 +66,7 @@ function make_list_widget({$parent_container, name, user_ids}) {
return ListWidget.create($list_container, users, {
name,
get_item: ListWidget.default_get_item,
$parent_container,
sort_fields: {
email: settings_users.sort_email,

View File

@ -199,6 +199,7 @@ export function setup_page(callback) {
group_list_widget = ListWidget.create($container, user_groups_list, {
name: "user-groups-overlay",
get_item: ListWidget.default_get_item,
modifier(item) {
item.is_member = user_groups.is_direct_member_of(
people.my_current_user_id(),

View File

@ -83,6 +83,7 @@ function render_user_stream_list(streams, user) {
$container.empty();
ListWidget.create($container, streams, {
name: `user-${user.user_id}-stream-list`,
get_item: ListWidget.default_get_item,
modifier(item) {
return format_user_stream_list_item(item, user);
},
@ -102,6 +103,7 @@ function render_user_group_list(groups, user) {
$container.empty();
ListWidget.create($container, groups, {
name: `user-${user.user_id}-group-list`,
get_item: ListWidget.default_get_item,
modifier(item) {
return format_user_group_list_item(item);
},

View File

@ -157,6 +157,7 @@ run_test("scrolling", () => {
const opts = {
modifier: (item) => item,
get_item: (item) => item,
$simplebar_container: $scroll_container,
};
@ -205,6 +206,7 @@ run_test("not_scrolling", () => {
const opts = {
modifier: (item) => item,
get_item: (item) => item,
$simplebar_container: $scroll_container,
is_scroll_position_for_render: () => false,
post_scroll__pre_render_callback,
@ -243,6 +245,7 @@ run_test("filtering", () => {
predicate: (item, value) => item.includes(value),
},
modifier: (item) => div(item),
get_item: (item) => item,
$simplebar_container: $scroll_container,
};
@ -288,6 +291,7 @@ run_test("no filtering", () => {
callback_after_render() {
callback_called = true;
},
get_item: (item) => item,
};
const widget = ListWidget.create($container, ["apple", "banana"], opts);
widget.render();
@ -362,6 +366,7 @@ run_test("wire up filter element", () => {
$element: $filter_element,
},
modifier: (s) => "(" + s + ")",
get_item: (item) => item,
$simplebar_container: $scroll_container,
};
@ -392,6 +397,7 @@ run_test("sorting", () => {
name: "sorting-list",
$parent_container: $sort_container,
modifier: (item) => div(item.name) + div(item.salary),
get_item: (item) => item,
filter: {
predicate: () => true,
},
@ -492,6 +498,7 @@ run_test("custom sort", () => {
ListWidget.create($container, list, {
name: "custom-sort-list",
modifier: (n) => "(" + n.x + ", " + n.y + ")",
get_item: (item) => item,
sort_fields: {
product: sort_by_product,
x_value: sort_by_x,
@ -529,6 +536,7 @@ run_test("clear_event_handlers", () => {
name: "list-we-create-twice",
$parent_container: $sort_container,
modifier() {},
get_item() {},
filter: {
$element: $filter_element,
predicate: /* istanbul ignore next */ () => true,
@ -578,6 +586,7 @@ run_test("errors", () => {
predicate: "wrong type",
},
$simplebar_container: $scroll_container,
get_item() {},
});
blueslip.reset();
@ -588,6 +597,7 @@ run_test("errors", () => {
predicate: /* istanbul ignore next */ () => true,
},
$simplebar_container: $scroll_container,
get_item() {},
});
blueslip.reset();
@ -595,6 +605,7 @@ run_test("errors", () => {
ListWidget.create($container, list, {
filter: {},
$simplebar_container: $scroll_container,
get_item() {},
});
blueslip.reset();
@ -602,6 +613,7 @@ run_test("errors", () => {
ListWidget.create($container, list, {
modifier: () => 999,
$simplebar_container: $scroll_container,
get_item: (item) => item,
});
blueslip.reset();
});
@ -638,6 +650,7 @@ run_test("replace_list_data w/filter update", () => {
ListWidget.create($container, list, {
name: "replace-list",
modifier: (n) => "(" + n.toString() + ")",
get_item: (item) => item,
filter: {
predicate: (n) => n % 2 === 0,
onupdate() {
@ -873,6 +886,7 @@ run_test("Multiselect dropdown retain_selected_items", () => {
const widget = ListWidget.create($container, list, {
name: "replace-list",
modifier: (item) => `<li data-value="${item.value}">${item.name}</li>\n`,
get_item: (item) => item,
multiselect: {
selected_items: data,
},