web: Use util.the for accessing element of single-item lists.

This commit is contained in:
evykassirer 2024-08-19 22:18:02 -07:00 committed by Tim Abbott
parent 113de14547
commit d9f25d01a1
47 changed files with 188 additions and 140 deletions

View File

@ -7,6 +7,7 @@ import * as browser_history from "./browser_history";
import {show_copied_confirmation} from "./copied_tooltip";
import * as overlays from "./overlays";
import {realm} from "./state_data";
import * as util from "./util";
export function launch(): void {
overlays.open_overlay({
@ -19,12 +20,12 @@ export function launch(): void {
const zulip_version_clipboard = new ClipboardJS("#about-zulip .fa-copy.zulip-version");
zulip_version_clipboard.on("success", () => {
show_copied_confirmation($("#about-zulip .fa-copy.zulip-version")[0]!);
show_copied_confirmation(util.the($("#about-zulip .fa-copy.zulip-version")));
});
const zulip_merge_base_clipboard = new ClipboardJS("#about-zulip .fa-copy.zulip-merge-base");
zulip_merge_base_clipboard.on("success", () => {
show_copied_confirmation($("#about-zulip .fa-copy.zulip-merge-base")[0]!);
show_copied_confirmation(util.the($("#about-zulip .fa-copy.zulip-merge-base")));
});
}

View File

@ -1,6 +1,7 @@
import $ from "jquery";
import {user_settings} from "./user_settings";
import * as util from "./util";
export function initialize(): void {
update_notification_sound_source($("audio#user-notification-sound-audio"), user_settings);
@ -22,6 +23,6 @@ export function update_notification_sound_source(
if (notification_sound !== "none") {
// Load it so that it is ready to be played; without this the old sound
// is played.
$container_elem[0]!.load();
util.the($container_elem).load();
}
}

View File

@ -2,6 +2,7 @@ import $ from "jquery";
import {z} from "zod";
import * as loading from "../loading";
import * as util from "../util";
export type FormDataObject = Record<string, string>;
@ -162,7 +163,7 @@ export function update_discount_details(
}
export function is_valid_input($elem: JQuery<HTMLFormElement>): boolean {
return $elem[0]!.checkValidity();
return util.the($elem).checkValidity();
}
export function redirect_to_billing_with_successful_upgrade(billing_base_url: string): void {

View File

@ -171,7 +171,7 @@ import getCaretCoordinates from "textarea-caret";
import * as tippy from "tippy.js";
import * as scroll_util from "./scroll_util";
import {get_string_diff} from "./util";
import {get_string_diff, the} from "./util";
function get_pseudo_keycode(
event: JQuery.KeyDownEvent | JQuery.KeyUpEvent | JQuery.KeyPressEvent,
@ -322,7 +322,7 @@ export class Typeahead<ItemType extends string | object> {
}
select(e?: JQuery.ClickEvent | JQuery.KeyUpEvent | JQuery.KeyDownEvent): this {
const val = this.values.get(this.$menu.find(".active")[0]!);
const val = this.values.get(the(this.$menu.find(".active")));
// It's possible that we got here from pressing enter with nothing highlighted.
if (!this.requireHighlight && val === undefined) {
return this.hide();
@ -359,7 +359,7 @@ export class Typeahead<ItemType extends string | object> {
}
set_value(): void {
const val = this.values.get(this.$menu.find(".active")[0]!);
const val = this.values.get(the(this.$menu.find(".active")));
assert(typeof val === "string");
if (this.input_element.type === "contenteditable") {
this.input_element.$element.text(val);
@ -392,7 +392,7 @@ export class Typeahead<ItemType extends string | object> {
const input_element = this.input_element;
if (!this.non_tippy_parent_element) {
this.instance = tippy.default(input_element.$element[0]!, {
this.instance = tippy.default(the(input_element.$element), {
// Lets typeahead take the width needed to fit the content
// and wraps it if it overflows the visible container.
maxWidth: "none",
@ -425,7 +425,7 @@ export class Typeahead<ItemType extends string | object> {
interactive: true,
appendTo: () => document.body,
showOnCreate: true,
content: this.$container[0]!,
content: the(this.$container),
// We expect the typeahead creator to handle when to hide / show the typeahead.
trigger: "manual",
arrow: false,
@ -435,8 +435,8 @@ export class Typeahead<ItemType extends string | object> {
if (input_element.type === "textarea") {
const caret = getCaretCoordinates(
input_element.$element[0]!,
input_element.$element[0]!.selectionStart,
the(input_element.$element),
the(input_element.$element).selectionStart,
);
// Used to consider the scroll height of textbox in the vertical offset.
const scrollTop = input_element.$element.scrollTop() ?? 0;
@ -554,7 +554,7 @@ export class Typeahead<ItemType extends string | object> {
render(final_items: ItemType[], matching_items: ItemType[]): this {
const $items: JQuery[] = final_items.map((item) => {
const $i = $(ITEM_HTML);
this.values.set($i[0]!, item);
this.values.set(the($i), item);
const item_html = this.highlighter_html(item, this.query) ?? "";
const $item_html = $i.find("a").html(item_html);
@ -743,12 +743,12 @@ export class Typeahead<ItemType extends string | object> {
this.select(e);
if (this.input_element.$element[0]!.id === "stream_message_recipient_topic") {
if (the(this.input_element.$element).id === "stream_message_recipient_topic") {
assert(this.input_element.type === "input");
// Move the cursor to the end of the topic
const topic_length = this.input_element.$element.val()!.length;
this.input_element.$element[0]!.selectionStart = topic_length;
this.input_element.$element[0]!.selectionEnd = topic_length;
the(this.input_element.$element).selectionStart = topic_length;
the(this.input_element.$element).selectionEnd = topic_length;
}
break;
@ -775,7 +775,7 @@ export class Typeahead<ItemType extends string | object> {
// when shift (keycode 16) + tabbing to the topic field
if (
pseudo_keycode === 16 &&
this.input_element.$element[0]!.id === "stream_message_recipient_topic"
the(this.input_element.$element).id === "stream_message_recipient_topic"
) {
return;
}

View File

@ -26,6 +26,7 @@ import * as stream_data from "./stream_data";
import type {StreamSubscription} from "./sub_store";
import {INTERACTIVE_HOVER_DELAY} from "./tippyjs";
import {user_settings} from "./user_settings";
import * as util from "./util";
function get_formatted_sub_count(sub_count: number): string {
if (sub_count < 1000) {
@ -172,7 +173,7 @@ export class BuddyList extends BuddyListConf {
// This will default to "bottom" placement for this tooltip.
placement = "auto";
}
tippy.default($elem[0]!, {
tippy.default(util.the($elem), {
// Because the buddy list subheadings are potential click targets
// for purposes having nothing to do with the subscriber count
// (collapsing/expanding), we delay showing the tooltip until the
@ -765,9 +766,7 @@ export class BuddyList extends BuddyListConf {
fill_screen_with_content(): void {
let height = this.height_to_fill();
const elem = scroll_util
.get_scroll_element($(this.scroll_container_selector))
.expectOne()[0]!;
const elem = util.the(scroll_util.get_scroll_element($(this.scroll_container_selector)));
// Add a fudge factor.
height += 10;

View File

@ -2,6 +2,7 @@ import $ from "jquery";
import * as tippy from "tippy.js";
import {$t} from "./i18n";
import * as util from "./util";
export const status_classes = "alert-error alert-success alert-info alert-warning alert-loading";
@ -108,7 +109,7 @@ function set_password_toggle_label(
): void {
$(password_selector).attr("aria-label", label);
if (tippy_tooltips) {
const element: tippy.ReferenceElement = $(password_selector)[0]!;
const element: tippy.ReferenceElement = util.the($(password_selector));
const tippy_instance = element._tippy ?? tippy.default(element);
tippy_instance.setContent(label);
} else {

View File

@ -281,7 +281,7 @@ function on_hidden_callback(): void {
// Always move focus to the topic input even if it's not empty,
// since it's likely the user will want to update the topic
// after updating the stream.
ui_util.place_caret_at_end($("input#stream_message_recipient_topic")[0]!);
ui_util.place_caret_at_end(util.the($("input#stream_message_recipient_topic")));
} else {
if (compose_state.private_message_recipient().length === 0) {
$("#private_message_recipient").trigger("focus").trigger("select");

View File

@ -123,9 +123,9 @@ export function insert_and_scroll_into_view(
// to support `undo`, we can use a faster method.
$textarea.val(content);
} else if (replace_all) {
setFieldText($textarea[0]!, content);
setFieldText(util.the($textarea), content);
} else {
insertTextIntoField($textarea[0]!, content);
insertTextIntoField(util.the($textarea), content);
}
// Blurring and refocusing ensures the cursor / selection is in view
// in chromium browsers.
@ -300,7 +300,7 @@ export function replace_syntax(
// for details.
const old_text = $textarea.val();
replaceFieldText($textarea[0]!, old_syntax, () => new_syntax, "after-replacement");
replaceFieldText(util.the($textarea), old_syntax, () => new_syntax, "after-replacement");
const new_text = $textarea.val();
// When replacing content in a textarea, we need to move the cursor

View File

@ -41,6 +41,7 @@ import type {UserGroup} from "./user_groups";
import * as user_pill from "./user_pill";
import type {UserPillData} from "./user_pill";
import {user_settings} from "./user_settings";
import * as util from "./util";
// **********************************
// AN IMPORTANT NOTE ABOUT TYPEAHEADS
@ -250,7 +251,7 @@ function handle_bulleting_or_numbering(
if (bulleted_numbered_list_util.strip_bullet(previous_line) === "") {
// below we select and replace the last 2 characters in the textarea before
// the cursor - the bullet syntax - with an empty string
$textarea[0]!.setSelectionRange($textarea.caret() - 2, $textarea.caret());
util.the($textarea).setSelectionRange($textarea.caret() - 2, $textarea.caret());
compose_ui.insert_and_scroll_into_view("", $textarea);
e.preventDefault();
return;
@ -264,7 +265,7 @@ function handle_bulleting_or_numbering(
if (bulleted_numbered_list_util.strip_numbering(previous_line) === "") {
// below we select then replaces the last few characters in the textarea before
// the cursor - the numbering syntax - with an empty string
$textarea[0]!.setSelectionRange(
util.the($textarea).setSelectionRange(
$textarea.caret() - previous_number_string.length - 2,
$textarea.caret(),
);
@ -297,7 +298,7 @@ export function handle_enter($textarea: JQuery<HTMLTextAreaElement>, e: JQuery.K
// If the selectionStart and selectionEnd are not the same, that
// means that some text was selected.
if ($textarea[0]!.selectionStart !== $textarea[0]!.selectionEnd) {
if (util.the($textarea).selectionStart !== util.the($textarea).selectionEnd) {
// Replace it with the newline, remembering to resize the
// textarea if needed.
compose_ui.insert_and_scroll_into_view("\n", $textarea);
@ -1175,7 +1176,11 @@ export function content_typeahead_selected(
$textbox.caret(beginning.length);
compose_ui.autosize_textarea($textbox);
};
flatpickr.show_flatpickr(input_element.$element[0]!, on_timestamp_selection, timestamp);
flatpickr.show_flatpickr(
util.the(input_element.$element),
on_timestamp_selection,
timestamp,
);
return beginning + rest;
}
}

View File

@ -6,6 +6,7 @@ import * as message_lists from "./message_lists";
import type {Message} from "./message_store";
import * as message_viewport from "./message_viewport";
import * as rows from "./rows";
import * as util from "./util";
/*
This library implements two related, similar concepts:
@ -151,7 +152,7 @@ function get_message_height(elem: HTMLElement): number {
// This needs to be very fast. This function runs hundreds of times
// when displaying a message feed view that has hundreds of message
// history, which ideally should render in <100ms.
return $(elem).find(".message_content")[0]!.scrollHeight;
return util.the($(elem).find(".message_content")).scrollHeight;
}
export function hide_message_expander($row: JQuery): void {

View File

@ -10,6 +10,7 @@ import * as hash_util from "./hash_util";
import * as message_lists from "./message_lists";
import * as rows from "./rows";
import * as topic_link_util from "./topic_link_util";
import * as util from "./util";
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
@ -135,7 +136,7 @@ function select_div($div: JQuery, selection: Selection): void {
background: "#FFF",
}).attr("id", "copytempdiv");
$("body").append($div);
selection.selectAllChildren($div[0]!);
selection.selectAllChildren(util.the($div));
}
function remove_div(_div: JQuery, ranges: Range[]): void {
@ -629,7 +630,9 @@ function is_safe_url_paste_target($textarea: JQuery<HTMLTextAreaElement>): boole
export function cursor_at_markdown_link_marker($textarea: JQuery<HTMLTextAreaElement>): boolean {
const range = $textarea.range();
const possible_markdown_link_markers = $textarea[0]!.value.slice(range.start - 2, range.start);
const possible_markdown_link_markers = util
.the($textarea)
.value.slice(range.start - 2, range.start);
return possible_markdown_link_markers === "](";
}

View File

@ -363,11 +363,13 @@ export function restore_message(draft: LocalStorageDraft): ComposeArguments {
function draft_notify(): void {
// Display a tooltip to notify the user about the saved draft.
const instance = tippy.default(".top_left_drafts .unread_count", {
const instance = util.the(
tippy.default(".top_left_drafts .unread_count", {
content: $t({defaultMessage: "Saved as draft"}),
arrow: true,
placement: "right",
})[0]!;
}),
);
instance.show();
function remove_instance(): void {
instance.destroy();

View File

@ -592,7 +592,7 @@ function process_keypress(e: JQuery.KeyPressEvent | JQuery.KeyDownEvent): void {
export function emoji_select_tab($elt: JQuery): void {
const scrolltop = $elt.scrollTop()!;
const scrollheight = $elt[0]!.scrollHeight;
const scrollheight = util.the($elt).scrollHeight;
const elt_height = $elt.height()!;
let currently_selected = "";
for (const o of section_head_offsets) {

View File

@ -6,6 +6,7 @@ import assert from "minimalistic-assert";
import {$t} from "./i18n";
import {user_settings} from "./user_settings";
import * as util from "./util";
export let flatpickr_instance: flatpickr.Instance;
@ -25,7 +26,7 @@ export function show_flatpickr(
): flatpickr.Instance {
const $flatpickr_input = $<HTMLInputElement>("<input>").attr("id", "#timestamp_flatpickr");
flatpickr_instance = flatpickr($flatpickr_input[0]!, {
flatpickr_instance = flatpickr(util.the($flatpickr_input), {
mode: "single",
enableTime: true,
clickOpens: false,

View File

@ -1438,7 +1438,7 @@ function move_focus_to_visible_area(): void {
}
const INBOX_ROW_HEIGHT = 30;
const position = $("#inbox-filters")[0]!.getBoundingClientRect();
const position = util.the($("#inbox-filters")).getBoundingClientRect();
const inbox_center_x = (position.left + position.right) / 2;
// We are aiming to get the first row if it is completely visible or the second row.
const inbox_row_below_filters = position.bottom + INBOX_ROW_HEIGHT;

View File

@ -7,6 +7,7 @@ import render_input_pill from "../templates/input_pill.hbs";
import * as keydown_util from "./keydown_util";
import * as ui_util from "./ui_util";
import * as util from "./util";
// See https://zulip.readthedocs.io/en/latest/subsystems/input-pills.html
@ -194,9 +195,9 @@ export function create<ItemType extends {type: string}>(
if (idx !== -1) {
store.pills[idx]!.$element.remove();
const pill = store.pills.splice(idx, 1);
const pill = util.the(store.pills.splice(idx, 1));
if (store.onPillRemove !== undefined) {
store.onPillRemove(pill[0]!, trigger);
store.onPillRemove(pill, trigger);
}
// This is needed to run the "change" event handler registered in
@ -231,8 +232,7 @@ export function create<ItemType extends {type: string}>(
while (store.pills.length > 0) {
this.removeLastPill(trigger, quiet);
}
this.clear(store.$input[0]!);
this.clear(util.the(store.$input));
},
insertManyPills(pills: string | string[]) {
@ -253,7 +253,7 @@ export function create<ItemType extends {type: string}>(
// when using the `text` insertion feature with jQuery the caret is
// placed at the beginning of the input field, so this moves it to
// the end.
ui_util.place_caret_at_end(store.$input[0]!);
ui_util.place_caret_at_end(util.the(store.$input));
// this sends a flag if the operation wasn't completely successful,
// which in this case is defined as some of the pills not autofilling
@ -341,7 +341,7 @@ export function create<ItemType extends {type: string}>(
// if the pill is successful, it will create the pill and clear
// the input.
if (funcs.appendPill(store.$input.text().trim())) {
funcs.clear(store.$input[0]!);
funcs.clear(util.the(store.$input));
}
e.preventDefault();
@ -370,7 +370,7 @@ export function create<ItemType extends {type: string}>(
break;
case "Backspace": {
const $next = $pill.next();
funcs.removePill($pill[0]!, "backspace");
funcs.removePill(util.the($pill), "backspace");
$next.trigger("focus");
// the "Backspace" key in Firefox will go back a page if you do
// not prevent it.
@ -416,8 +416,8 @@ export function create<ItemType extends {type: string}>(
store.$input.trigger("change");
} else {
e.stopPropagation();
const $pill = $(this).closest(".pill");
funcs.removePill($pill[0]!, "close");
const pill = util.the($(this).closest(".pill"));
funcs.removePill(pill, "close");
}
// Since removing a pill moves the $input, typeahead needs to refresh
// to appear at the correct position.

View File

@ -53,7 +53,7 @@ export function show_generate_integration_url_modal(api_key: string): void {
});
clipboard.on("success", () => {
show_copied_confirmation(
$("#generate-integration-url-modal .dialog_submit_button")[0]!,
util.the($("#generate-integration-url-modal .dialog_submit_button")),
);
});

View File

@ -30,6 +30,7 @@ import * as stream_pill from "./stream_pill";
import * as timerender from "./timerender";
import type {HTMLSelectOneElement} from "./types";
import * as ui_report from "./ui_report";
import * as util from "./util";
let custom_expiration_time_input = 10;
let custom_expiration_time_unit = "days";
@ -208,7 +209,7 @@ function submit_invitation_form(): void {
$("#invite-user-modal .dialog_submit_button").text($t({defaultMessage: "Invite"}));
$("#invite-user-modal .dialog_submit_button").prop("disabled", false);
$("#invite-user-modal .dialog_exit_button").prop("disabled", false);
$invite_status[0]!.scrollIntoView();
util.the($invite_status).scrollIntoView();
},
});
}
@ -228,7 +229,7 @@ function generate_multiuse_invite(): void {
clipboard.on("success", () => {
const tippy_timeout_in_ms = 800;
show_copied_confirmation(
$("#copy_generated_invite_link")[0]!,
util.the($("#copy_generated_invite_link")),
() => {
// Do nothing on hide
},
@ -243,7 +244,7 @@ function generate_multiuse_invite(): void {
$("#invite-user-modal .dialog_submit_button").text($t({defaultMessage: "Create link"}));
$("#invite-user-modal .dialog_submit_button").prop("disabled", false);
$("#invite-user-modal .dialog_exit_button").prop("disabled", false);
$invite_status[0]!.scrollIntoView();
util.the($invite_status).scrollIntoView();
},
});
}
@ -302,7 +303,7 @@ function set_streams_to_join_list_visibility(): void {
const realm_has_default_streams = stream_data.get_default_stream_ids().length !== 0;
const hide_streams_list =
realm_has_default_streams &&
$<HTMLInputElement>("input#invite_select_default_streams")[0]!.checked;
util.the($<HTMLInputElement>("input#invite_select_default_streams")).checked;
if (hide_streams_list) {
$(".add_streams_container").hide();
} else {

View File

@ -118,12 +118,12 @@ export class PanZoomControl {
// See https://github.com/anvaka/panzoom/issues/112 for upstream discussion.
const {scale, x, y} = e.getTransform();
const image_width = $(".zoom-element > img")[0]!.clientWidth * scale;
const image_height = $(".zoom-element > img")[0]!.clientHeight * scale;
const zoom_element_width = $(".zoom-element")[0]!.clientWidth * scale;
const zoom_element_height = $(".zoom-element")[0]!.clientHeight * scale;
const max_translate_x = $(".image-preview")[0]!.clientWidth;
const max_translate_y = $(".image-preview")[0]!.clientHeight;
const image_width = util.the($(".zoom-element > img")).clientWidth * scale;
const image_height = util.the($(".zoom-element > img")).clientHeight * scale;
const zoom_element_width = util.the($(".zoom-element")).clientWidth * scale;
const zoom_element_height = util.the($(".zoom-element")).clientHeight * scale;
const max_translate_x = util.the($(".image-preview")).clientWidth;
const max_translate_y = util.the($(".image-preview")).clientHeight;
// When the image is dragged out of the image-preview container
// (max_translate) it will be "snapped" back so that the number
@ -394,7 +394,7 @@ export function build_open_media_function(
return function ($media: JQuery<HTMLMediaElement | HTMLImageElement>): void {
// This is used both for clicking on media in the messagelist, as well as clicking on images
// in the media list under the lightbox when it is open.
const payload = parse_media_data($media[0]!);
const payload = parse_media_data(util.the($media));
assert(payload !== undefined);
if (payload.type.match("-video")) {
@ -597,7 +597,7 @@ export function initialize(): void {
// Bind the pan/zoom control the newly created element.
const pan_zoom_control = new PanZoomControl(
$("#lightbox_overlay .image-preview > .zoom-element")[0]!,
util.the($("#lightbox_overlay .image-preview > .zoom-element")),
);
const reset_lightbox_state = function (): void {

View File

@ -233,7 +233,7 @@ export class MessageListData {
return true;
}
const recipient_id = Number.parseInt(recipients[0]!, 10);
const recipient_id = Number.parseInt(util.the(recipients), 10);
return (
!muted_users.is_user_muted(recipient_id) &&
!muted_users.is_user_muted(message.sender_id)

View File

@ -78,7 +78,7 @@ export function message_viewport_info(): MessageViewportInfo {
export function at_rendered_bottom(): boolean {
const bottom = scrollTop() + height();
// This also includes bottom whitespace.
const full_height = $scroll_container[0]!.scrollHeight;
const full_height = util.the($scroll_container).scrollHeight;
// We only know within a pixel or two if we're
// exactly at the bottom, due to browser quirkiness,
@ -94,7 +94,7 @@ export function bottom_rendered_message_visible(): boolean {
const $last_row = rows.last_visible();
if ($last_row[0] !== undefined) {
const message_bottom = $last_row[0].getBoundingClientRect().bottom;
const bottom_of_feed = $("#compose")[0]!.getBoundingClientRect().top;
const bottom_of_feed = util.the($("#compose")).getBoundingClientRect().top;
return bottom_of_feed > message_bottom;
}
return false;
@ -214,7 +214,7 @@ const top_of_feed = new util.CachedValue({
const bottom_of_feed = new util.CachedValue({
compute_value() {
return $("#compose")[0]!.getBoundingClientRect().top;
return util.the($("#compose")).getBoundingClientRect().top;
},
});

View File

@ -1,6 +1,8 @@
import $ from "jquery";
import assert from "minimalistic-assert";
import * as util from "./util";
export type Context = {
items_container_selector: string;
items_list_selector: string;
@ -73,11 +75,11 @@ export function modals_handle_events(event_key: string, context: Context): void
export function set_initial_element(element_id: string, context: Context): void {
if (element_id) {
const $current_element = get_element_by_id(element_id, context);
const focus_element = $current_element[0]!.children[0];
const current_element = util.the(get_element_by_id(element_id, context));
const focus_element = current_element.children[0];
assert(focus_element instanceof HTMLElement);
activate_element(focus_element, context);
$(`.${CSS.escape(context.items_list_selector)}`)[0]!.scrollTop = 0;
util.the($(`.${CSS.escape(context.items_list_selector)}`)).scrollTop = 0;
}
}
@ -132,7 +134,7 @@ function initialize_focus(event_name: string, context: Context): void {
}
const $element = get_element_by_id(id, context);
const focus_element = $element[0]!.children[0];
const focus_element = util.the($element).children[0];
assert(focus_element instanceof HTMLElement);
activate_element(focus_element, context);
}
@ -152,28 +154,29 @@ function scroll_to_element($element: JQuery, context: Context): void {
const $box_item = $(`.${CSS.escape(context.box_item_selector)}`);
// If focused element is first, scroll to the top.
if ($box_item.first()[0]!.parentElement === $element[0]) {
$items_list[0]!.scrollTop = 0;
if (util.the($box_item.first()).parentElement === $element[0]) {
util.the($items_list).scrollTop = 0;
}
// If focused element is last, scroll to the bottom.
if ($box_item.last()[0]!.parentElement === $element[0]) {
$items_list[0]!.scrollTop = $items_list[0]!.scrollHeight - ($items_list.height() ?? 0);
if (util.the($box_item.last()).parentElement === $element[0]) {
util.the($items_list).scrollTop =
util.the($items_list).scrollHeight - ($items_list.height() ?? 0);
}
// If focused element is cut off from the top, scroll up halfway in modal.
if ($element.position().top < 55) {
// 55 is the minimum distance from the top that will require extra scrolling.
$items_list[0]!.scrollTop -= $items_list[0]!.clientHeight / 2;
util.the($items_list).scrollTop -= util.the($items_list).clientHeight / 2;
}
// If focused element is cut off from the bottom, scroll down halfway in modal.
const dist_from_top = $element.position().top;
const total_dist = dist_from_top + $element[0].clientHeight;
const dist_from_bottom = $items_container[0]!.clientHeight - total_dist;
const dist_from_bottom = util.the($items_container).clientHeight - total_dist;
if (dist_from_bottom < -4) {
// -4 is the min dist from the bottom that will require extra scrolling.
$items_list[0]!.scrollTop += $items_list[0]!.clientHeight / 2;
util.the($items_list).scrollTop += util.the($items_list).clientHeight / 2;
}
}

View File

@ -9,6 +9,7 @@ import * as popover_menus from "./popover_menus";
import * as realm_playground from "./realm_playground";
import type {RealmPlayground} from "./realm_playground";
import * as ui_util from "./ui_util";
import * as util from "./util";
type RealmPlaygroundWithURL = RealmPlayground & {playground_url: string};
@ -132,9 +133,9 @@ function register_click_handlers(): void {
const playground_url = url_template.expand({code: extracted_code});
playground_store.set(playground.id, {...playground, playground_url});
}
const popover_target = $view_in_playground_button.find(
".playground-links-popover-container",
)[0]!;
const popover_target = util.the(
$view_in_playground_button.find(".playground-links-popover-container"),
);
toggle_playground_links_popover(popover_target, playground_store);
}
},

View File

@ -3,6 +3,8 @@ import SortableJS from "sortablejs";
import render_poll_modal_option from "../templates/poll_modal_option.hbs";
import * as util from "./util";
function create_option_row($last_option_row_input: JQuery): void {
const row_html = render_poll_modal_option();
const $row_container = $last_option_row_input.closest(".simplebar-content");
@ -46,7 +48,7 @@ export function poll_options_setup(): void {
// setTimeout is needed to here to give time for simplebar to initialise
setTimeout(() => {
SortableJS.create($("#add-poll-form .poll-options-list .simplebar-content")[0]!, {
SortableJS.create(util.the($("#add-poll-form .poll-options-list .simplebar-content")), {
onUpdate() {
// Do nothing on drag; the order is only processed on submission.
},

View File

@ -194,7 +194,7 @@ export const default_popover_props: Partial<tippy.Props> = {
// $tippy_box[0].hasAttribute("data-reference-hidden"); is the real check
// but linter wants us to write it like this.
const is_reference_outside_window = Object.hasOwn(
$tippy_box[0]!.dataset,
util.the($tippy_box).dataset,
"referenceHidden",
);
@ -231,7 +231,7 @@ export const default_popover_props: Partial<tippy.Props> = {
return;
}
const reference_rect = $reference[0]!.getBoundingClientRect();
const reference_rect = util.the($reference).getBoundingClientRect();
// This is the logic we want but since it is too expensive to run
// on every scroll, we run a cheaper version of this to just check if
// compose, sticky header or navbar are not obscuring the reference

View File

@ -5,6 +5,7 @@ import * as tippy from "tippy.js";
import copy_to_clipboard_svg from "../../templates/copy_to_clipboard_svg.hbs";
import * as common from "../common";
import * as util from "../util";
import {activate_correct_tab} from "./tabbed-instructions";
@ -37,7 +38,7 @@ function add_copy_to_clipboard_element($codehilite: JQuery): void {
$($codehilite).append($copy_button);
const clipboard = new ClipboardJS($copy_button[0]!, {
const clipboard = new ClipboardJS(util.the($copy_button), {
text(copy_element) {
// trim to remove trailing whitespace introduced
// by additional elements inside <pre>
@ -46,14 +47,14 @@ function add_copy_to_clipboard_element($codehilite: JQuery): void {
});
// Show a tippy tooltip when the button is hovered
const tooltip_copy = tippy.default($copy_button[0]!, {
const tooltip_copy = tippy.default(util.the($copy_button), {
content: "Copy code",
trigger: "mouseenter",
placement: "top",
});
// Show a tippy tooltip when the code is copied
const tooltip_copied = tippy.default($copy_button[0]!, {
const tooltip_copied = tippy.default(util.the($copy_button), {
content: "Copied!",
trigger: "manual",
placement: "top",
@ -89,7 +90,7 @@ function render_tabbed_sections(): void {
});
}
new SimpleBar($(".sidebar")[0]!, {tabIndex: -1});
new SimpleBar(util.the($(".sidebar")), {tabIndex: -1});
// Scroll to anchor link when clicked. Note that landing-page.js has a
// similar function; this file and landing-page.js are never included

View File

@ -3,6 +3,7 @@ import assert from "minimalistic-assert";
import {z} from "zod";
import * as channel from "../channel";
import * as util from "../util";
// Main JavaScript file for the integrations development panel at
// /devtools/integrations.
@ -73,13 +74,13 @@ const clear_handlers: ClearHandlers = {
$("#fixture_name").empty();
},
fixture_body() {
$<HTMLTextAreaElement>("textarea#fixture_body")[0]!.value = "";
util.the($<HTMLTextAreaElement>("textarea#fixture_body")).value = "";
},
custom_http_headers() {
$<HTMLTextAreaElement>("textarea#custom_http_headers")[0]!.value = "{}";
util.the($<HTMLTextAreaElement>("textarea#custom_http_headers")).value = "{}";
},
results() {
$<HTMLTextAreaElement>("textarea#idp-results")[0]!.value = "";
util.the($<HTMLTextAreaElement>("textarea#idp-results")).value = "";
},
};
@ -153,7 +154,7 @@ function set_results(response: ServerResponse): void {
}
data += "\nResponse: " + response.message + "\n\n";
}
$<HTMLTextAreaElement>("textarea#idp-results")[0]!.value = data;
util.the($<HTMLTextAreaElement>("textarea#idp-results")).value = data;
}
function load_fixture_body(fixture_name: string): void {
@ -173,8 +174,8 @@ function load_fixture_body(fixture_name: string): void {
fixture_body = JSON.stringify(fixture_body, null, 4);
}
assert(typeof fixture_body === "string");
$<HTMLTextAreaElement>("textarea#fixture_body")[0]!.value = fixture_body;
$<HTMLTextAreaElement>("textarea#custom_http_headers")[0]!.value = JSON.stringify(
util.the($<HTMLTextAreaElement>("textarea#fixture_body")).value = fixture_body;
util.the($<HTMLTextAreaElement>("textarea#custom_http_headers")).value = JSON.stringify(
headers,
null,
4,
@ -187,9 +188,9 @@ function load_fixture_options(integration_name: string): void {
/* Using the integration name and loaded_fixtures object to set
the fixture options for the fixture_names dropdown and also set
the fixture body to the first fixture by default. */
const fixtures_options_dropdown = $<HTMLSelectOneElement>(
"select:not([multiple])#fixture_name",
)[0]!;
const fixtures_options_dropdown = util.the(
$<HTMLSelectOneElement>("select:not([multiple])#fixture_name"),
);
const fixtures = loaded_fixtures.get(integration_name);
assert(fixtures !== undefined);
const fixtures_names = Object.keys(fixtures).sort();
@ -396,10 +397,12 @@ $(() => {
"results",
]);
$<HTMLInputElement>("input#stream_name")[0]!.value = "Denmark";
$<HTMLInputElement>("input#topic_name")[0]!.value = "Integrations testing";
util.the($<HTMLInputElement>("input#stream_name")).value = "Denmark";
util.the($<HTMLInputElement>("input#topic_name")).value = "Integrations testing";
const potential_default_bot = $<HTMLSelectOneElement>("select:not([multiple])#bot_name")[0]![1];
const potential_default_bot = util.the(
$<HTMLSelectOneElement>("select:not([multiple])#bot_name"),
)[1];
assert(potential_default_bot instanceof HTMLOptionElement);
if (potential_default_bot !== undefined) {
potential_default_bot.selected = true;

View File

@ -3,6 +3,7 @@ import assert from "minimalistic-assert";
import {z} from "zod";
import {page_params} from "../base_page_params";
import * as util from "../util";
import type {UserOS} from "./tabbed-instructions";
import {detect_user_os} from "./tabbed-instructions";
@ -271,7 +272,7 @@ $(document).on("click", ".comparison-tab", function (this: HTMLElement) {
const tab_label = z
.enum(["tab-cloud", "tab-hosted", "tab-all"])
.parse($(this)[0]!.dataset.label);
.parse(util.the($(this)).dataset.label);
const plans_columns_count = plans_columns_counts[tab_label];
const visible_plans_id = `showing-${tab_label}`;

View File

@ -2,6 +2,7 @@ import $ from "jquery";
import * as blueslip from "../blueslip";
import * as common from "../common";
import * as util from "../util";
export type UserOS = "android" | "ios" | "mac" | "windows" | "linux";
@ -58,7 +59,7 @@ export function activate_correct_tab($tabbed_section: JQuery): void {
const $active_list_items = $li.filter(".active");
if (!$active_list_items.length) {
$li.first().addClass("active");
const tab_key = $li.first()[0]!.dataset.tabKey;
const tab_key = util.the($li.first()).dataset.tabKey;
if (tab_key) {
$blocks.filter("[data-tab-key=" + tab_key + "]").addClass("active");
} else {

View File

@ -13,6 +13,7 @@ import * as message_store from "./message_store";
import * as modals from "./modals";
import * as people from "./people";
import * as ui_report from "./ui_report";
import * as util from "./util";
const read_receipts_api_response_schema = z.object({
user_ids: z.array(z.number()),
@ -89,7 +90,7 @@ export function show_user_list(message_id: number): void {
$("#read_receipts_modal .read_receipts_list").html(
render_read_receipts(context),
);
new SimpleBar($("#read_receipts_modal .modal__content")[0]!, {
new SimpleBar(util.the($("#read_receipts_modal .modal__content")), {
tabIndex: -1,
});
}

View File

@ -44,6 +44,7 @@ import * as unread from "./unread";
import {user_settings} from "./user_settings";
import * as user_status from "./user_status";
import * as user_topics from "./user_topics";
import * as util from "./util";
import * as views_util from "./views_util";
type Row = {
@ -327,7 +328,7 @@ function set_table_focus(row: number, col: number, using_keyboard = false): bool
$current_focus_elem = "table";
if (using_keyboard) {
const scroll_element = $("html")[0]!;
const scroll_element = util.the($("html"));
const half_height_of_visible_area = scroll_element.offsetHeight / 2;
const topic_offset = topic_offset_to_visible_area($topic_row);
@ -1148,10 +1149,10 @@ function topic_offset_to_visible_area($topic_row: JQuery): string | undefined {
}
// Rows are only visible below thead bottom and above compose top.
const thead_bottom = $("#recent-view-table-headers")[0]!.getBoundingClientRect().bottom;
const thead_bottom = util.the($("#recent-view-table-headers")).getBoundingClientRect().bottom;
const compose_top = window.innerHeight - $("#compose").outerHeight(true)!;
const topic_props = $topic_row[0]!.getBoundingClientRect();
const topic_props = util.the($topic_row).getBoundingClientRect();
// Topic is above the visible scroll region.
if (topic_props.top < thead_bottom) {
@ -1185,7 +1186,7 @@ function recenter_focus_if_off_screen(): void {
if (topic_offset !== "visible") {
// Get the element at the center of the table.
const thead_props = $("#recent-view-table-headers")[0]!.getBoundingClientRect();
const thead_props = util.the($("#recent-view-table-headers")).getBoundingClientRect();
const compose_top = window.innerHeight - $("#compose").outerHeight(true)!;
const topic_center_x = (thead_props.left + thead_props.right) / 2;
const topic_center_y = (thead_props.bottom + compose_top) / 2;
@ -1477,7 +1478,7 @@ function down_arrow_navigation(): void {
}
function get_page_up_down_delta(): number {
const thead_bottom = $("#recent-view-table-headers")[0]!.getBoundingClientRect().bottom;
const thead_bottom = util.the($("#recent-view-table-headers")).getBoundingClientRect().bottom;
const compose_box_top = window.innerHeight - $("#compose").outerHeight(true)!;
// One usually wants PageDown to move what had been the bottom row
// to now be at the top, so one can be confident one will see

View File

@ -312,7 +312,7 @@ export const update_elements = ($content: JQuery): void => {
$view_in_playground_button.attr("aria-label", title);
}
const $copy_button = $buttonContainer.find(".copy_codeblock");
const clipboard = new ClipboardJS($copy_button[0]!, {
const clipboard = new ClipboardJS(util.the($copy_button), {
text(copy_element) {
const $code = $(copy_element).parent().siblings("code");
return $code.text();
@ -320,7 +320,7 @@ export const update_elements = ($content: JQuery): void => {
});
clipboard.on("success", () => {
show_copied_confirmation($copy_button[0]!);
show_copied_confirmation(util.the($copy_button));
});
$codehilite.addClass("zulip-code-block");
});

View File

@ -1,11 +1,13 @@
import $ from "jquery";
import SimpleBar from "simplebar";
import * as util from "./util";
// This type is helpful for testing, where we may have a dummy object instead of an actual jquery object.
type JQueryOrZJQuery = {__zjquery?: true} & JQuery;
export function get_content_element($element: JQuery): JQuery {
const element = $element.expectOne()[0]!;
const element = util.the($element);
const sb = SimpleBar.instances.get(element);
if (sb) {
return $(sb.getContentElement()!);
@ -19,7 +21,7 @@ export function get_scroll_element($element: JQueryOrZJQuery): JQuery {
return $element;
}
const element = $element.expectOne()[0]!;
const element = util.the($element);
const sb = SimpleBar.instances.get(element);
if (sb) {
return $(sb.getScrollElement()!);
@ -32,7 +34,7 @@ export function get_scroll_element($element: JQueryOrZJQuery): JQuery {
}
export function reset_scrollbar($element: JQuery): void {
const element = $element.expectOne()[0]!;
const element = util.the($element);
const sb = SimpleBar.instances.get(element);
if (sb) {
sb.getScrollElement()!.scrollTop = 0;

View File

@ -12,6 +12,7 @@ import type {User} from "./people";
import type {NarrowTerm} from "./state_data";
import * as user_status from "./user_status";
import type {UserStatusEmojiInfo} from "./user_status";
import * as util from "./util";
export type SearchUserPill = {
type: "search_user";
@ -40,9 +41,7 @@ type SearchPill =
export type SearchPillWidget = InputPillContainer<SearchPill>;
export function create_item_from_search_string(search_string: string): SearchPill | undefined {
const search_terms = Filter.parse(search_string);
assert(search_terms.length === 1);
const search_term = search_terms[0]!;
const search_term = util.the(Filter.parse(search_string));
if (!Filter.is_valid_search_term(search_term)) {
// This will cause pill validation to fail and trigger a shake animation.
return undefined;
@ -73,7 +72,7 @@ function on_pill_exit(
if (!$user_pill_container.length) {
// This is just a regular search pill, so we don't need to do fancy logic.
const $clicked_pill = $(clicked_element).closest(".pill");
remove_pill($clicked_pill[0]!);
remove_pill(util.the($clicked_pill));
return;
}
// The user-pill-container container class is used exclusively for
@ -92,8 +91,8 @@ function on_pill_exit(
// If there's only one user in this pill, delete the whole pill.
if (user_container_pill.users.length === 1) {
assert(user_container_pill.users[0]!.user_id === user_id);
remove_pill($user_pill_container[0]!);
assert(util.the(user_container_pill.users).user_id === user_id);
remove_pill(util.the($user_pill_container));
return;
}

View File

@ -390,7 +390,7 @@ function read_select_field_data_from_form(
}
}
$profile_field_form.find("div.choice-row").each(function (this: HTMLElement) {
const text = $(this).find("input")[0]!.value;
const text = util.the($(this).find("input")).value;
if (text) {
let value = old_option_value_map.get(text);
if (value !== undefined) {
@ -729,7 +729,7 @@ export function get_auth_method_list_data(): Record<string, boolean> {
for (const method_row of $auth_method_rows) {
const method = $(method_row).attr("data-method");
assert(method !== undefined);
new_auth_methods[method] = $(method_row).find<HTMLInputElement>("input")[0]!.checked;
new_auth_methods[method] = util.the($(method_row).find<HTMLInputElement>("input")).checked;
}
return new_auth_methods;
@ -1305,7 +1305,7 @@ function enable_or_disable_save_button($subsection_elem: JQuery): void {
const $button_wrapper = $subsection_elem.find<tippy.PopperElement>(
".subsection-changes-save",
);
const tippy_instance = $button_wrapper[0]!._tippy;
const tippy_instance = util.the($button_wrapper)._tippy;
if (disable_save_btn) {
// avoid duplication of tippy
if (!tippy_instance) {
@ -1343,5 +1343,5 @@ export function initialize_disable_btn_hint_popover(
if (hint_text !== undefined) {
tippy_opts.content = hint_text;
}
tippy.default($btn_wrapper[0]!, tippy_opts);
tippy.default(util.the($btn_wrapper), tippy_opts);
}

View File

@ -265,7 +265,7 @@ function show_modal(): void {
}
const formData = new FormData();
const files = $<HTMLInputElement>("input#emoji_file_input")[0]!.files;
const files = util.the($<HTMLInputElement>("input#emoji_file_input")).files;
assert(files !== null);
for (const [i, file] of [...files].entries()) {
formData.append("file-" + i, file);

View File

@ -17,6 +17,7 @@ import * as settings_ui from "./settings_ui";
import {current_user, realm} from "./state_data";
import * as ui_report from "./ui_report";
import * as ui_util from "./ui_util";
import * as util from "./util";
type RealmLinkifiers = typeof realm.realm_linkifiers;
@ -108,7 +109,7 @@ function open_linkifier_edit_form(linkifier_id: number): void {
submit_linkifier_form(dialog_widget_id);
},
on_shown() {
ui_util.place_caret_at_end($("#edit-linkifier-pattern")[0]!);
ui_util.place_caret_at_end(util.the($("#edit-linkifier-pattern")));
},
});
}
@ -197,7 +198,7 @@ export function populate_linkifiers(linkifiers_data: RealmLinkifiers): void {
});
if (current_user.is_admin) {
new SortableJS($linkifiers_table[0]!, {
new SortableJS(util.the($linkifiers_table), {
onUpdate: update_linkifiers_order,
handle: ".move-handle",
filter: "input",

View File

@ -23,6 +23,7 @@ import {current_user, realm} from "./state_data";
import type {HTMLSelectOneElement} from "./types";
import * as ui_report from "./ui_report";
import {place_caret_at_end} from "./ui_util";
import * as util from "./util";
type FieldChoice = {
value: string;
@ -424,7 +425,7 @@ function set_up_select_field_edit_form(
// Add blank choice at last
create_choice_row($choice_list);
SortableJS.create($choice_list[0]!, {
SortableJS.create(util.the($choice_list), {
onUpdate() {
// Do nothing on drag. We process the order on submission
},
@ -577,7 +578,7 @@ function open_edit_form_modal(this: HTMLElement): void {
post_render: set_initial_values_of_profile_field,
loading_spinner: true,
on_shown() {
place_caret_at_end($("#id-custom-profile-field-name")[0]!);
place_caret_at_end(util.the($("#id-custom-profile-field-name")));
},
});
}
@ -709,7 +710,7 @@ export function do_populate_profile_fields(profile_fields_data: CustomProfileFie
display_in_profile_summary_fields_limit_reached = display_in_profile_summary_fields_count >= 2;
if (current_user.is_admin) {
const field_list = $("#admin_profile_fields_table")[0]!;
const field_list = util.the($("#admin_profile_fields_table"));
SortableJS.create(field_list, {
onUpdate: update_field_order,
filter: "input",
@ -727,7 +728,7 @@ function set_up_select_field(): void {
create_choice_row($("#profile_field_choices"));
if (current_user.is_admin) {
const choice_list = $("#profile_field_choices")[0]!;
const choice_list = util.the($("#profile_field_choices"));
SortableJS.create(choice_list, {
onUpdate() {
// Do nothing on drag. We process the order on submission

View File

@ -8,6 +8,7 @@ import * as dialog_widget from "./dialog_widget";
import {$t_html} from "./i18n";
import {realm} from "./state_data";
import * as ui_report from "./ui_report";
import * as util from "./util";
type RealmDomain = {
domain: string;
@ -112,9 +113,9 @@ export function setup_realm_domains_modal_handlers(): void {
const $realm_domains_info = $(".realm_domains_info");
const $widget = $("#add-realm-domain-widget");
const domain = $widget.find(".new-realm-domain").val();
const allow_subdomains = $widget.find<HTMLInputElement>(
"input.new-realm-domain-allow-subdomains",
)[0]!.checked;
const allow_subdomains = util.the(
$widget.find<HTMLInputElement>("input.new-realm-domain-allow-subdomains"),
).checked;
const data = {
domain,
allow_subdomains: JSON.stringify(allow_subdomains),

View File

@ -2,6 +2,7 @@ import $ from "jquery";
import * as blueslip from "./blueslip";
import * as loading from "./loading";
import * as util from "./util";
export let page_load_time: number | undefined;
@ -16,7 +17,7 @@ $(() => {
});
$.fn.get_offset_to_window = function () {
return this[0]!.getBoundingClientRect();
return util.the(this).getBoundingClientRect();
};
$.fn.expectOne = function () {

View File

@ -1,5 +1,7 @@
import $ from "jquery";
import * as util from "./util";
function collapse_spoiler($spoiler: JQuery): void {
const spoiler_height = $spoiler.height() ?? 0;
@ -21,7 +23,7 @@ function expand_spoiler($spoiler: JQuery): void {
// of the content). CSS animations do not work with properties set to
// `auto`, so we get the actual height of the content here and temporarily
// put it explicitly on the element styling to allow the transition to work.
const spoiler_height = $spoiler[0]!.scrollHeight;
const spoiler_height = util.the($spoiler).scrollHeight;
$spoiler.height(`${spoiler_height}px`);
// The `spoiler-content-open` class has CSS animations defined on it which
// will trigger on the frame after this class change.

View File

@ -14,6 +14,7 @@ import * as settings_config from "./settings_config";
import * as stream_data from "./stream_data";
import * as ui_util from "./ui_util";
import {user_settings} from "./user_settings";
import * as util from "./util";
// For tooltips without data-tippy-content, we use the HTML content of
// a <template> whose id is given by data-tooltip-template-id.
@ -353,7 +354,7 @@ export function initialize(): void {
const second_line = $t({defaultMessage: "File name: {filename}"}, {filename});
$markup.append($("<br>"), $("<span>").text(second_line));
}
instance.setContent($markup[0]!);
instance.setContent(util.the($markup));
return undefined;
},
onHidden(instance) {

View File

@ -1,4 +1,5 @@
import {$t} from "./i18n";
import * as util from "./util";
export type UploadWidget = {
clear: () => void;
@ -83,7 +84,7 @@ export function build_widget(
if (files === null || files === undefined || files.length === 0) {
return false;
}
get_file_input()[0]!.files = files;
util.the(get_file_input()).files = files;
e.preventDefault();
return false;
});
@ -171,7 +172,7 @@ export function build_direct_upload_widget(
if (files === null || files === undefined || files.length === 0) {
return false;
}
get_file_input()[0]!.files = files;
util.the(get_file_input()).files = files;
e.preventDefault();
return false;
});

View File

@ -745,9 +745,9 @@ export function show_edit_bot_info_modal(user_id: number, $container: JQuery): v
formData.append("config_data", JSON.stringify(config_data));
}
const files = $("#bot-edit-form").find<HTMLInputElement>(
"input.edit_bot_avatar_file_input",
)[0]!.files;
const files = util.the(
$("#bot-edit-form").find<HTMLInputElement>("input.edit_bot_avatar_file_input"),
).files;
assert(files !== null);
for (const [i, file] of [...files].entries()) {
formData.append("file-" + i, file);

View File

@ -47,6 +47,7 @@ function make_textbox(s) {
const $widget = {};
$widget.s = s;
$widget.length = 1;
$widget[0] = "textarea";
$widget.focused = false;

View File

@ -89,6 +89,7 @@ function set_up() {
const $pill_input = $.create("pill_input");
$pill_input[0] = {};
$pill_input.length = 1;
$pill_input.before = noop;
const create_item_from_text = (text) => items[text];
@ -115,6 +116,7 @@ run_test("copy from pill", ({mock_template}) => {
mock_template("input_pill.hbs", true, (data, html) => {
assert.ok(["BLUE", "RED"].includes(data.display_value));
$(html)[0] = `<pill-stub ${data.display_value}>`;
$(html).length = 1;
return html;
});
@ -350,6 +352,7 @@ run_test("insert_remove", ({mock_template}) => {
mock_template("input_pill.hbs", true, (data, html) => {
assert.equal(typeof data.display_value, "string");
assert.ok(html.startsWith, "<div class='pill'");
$(html).length = 1;
$(html)[0] = `<pill-stub ${data.display_value}>`;
return html;
});
@ -437,6 +440,7 @@ run_test("insert_remove", ({mock_template}) => {
const $focus_pill_stub = {
next: () => $next_pill_stub,
[0]: "<pill-stub BLUE>",
length: 1,
};
$container.set_find_results(".pill:focus", $focus_pill_stub);
@ -456,6 +460,7 @@ run_test("exit button on pill", ({mock_template}) => {
assert.equal(typeof data.display_value, "string");
assert.ok(html.startsWith, "<div class='pill'");
$(html)[0] = `<pill-stub ${data.display_value}>`;
$(html).length = 1;
return html;
});
$(".narrow_to_compose_recipients").toggleClass = noop;
@ -477,6 +482,7 @@ run_test("exit button on pill", ({mock_template}) => {
const $curr_pill_stub = {
[0]: "<pill-stub BLUE>",
length: 1,
};
const exit_button_stub = {
@ -559,6 +565,7 @@ run_test("appendValue/clear", ({mock_template}) => {
$pill_input.before = noop;
$pill_input[0] = {};
$pill_input.length = 1;
const widget = input_pill.create(config);

View File

@ -23,6 +23,7 @@ function FakeElement(selector, opts) {
const event_store = make_event_store(selector);
const $self = {
length: 1,
[0]: {textContent: text},
*[Symbol.iterator]() {
// eslint-disable-next-line unicorn/no-for-loop