search: Put typeahead under search bar in the DOM with full width.

Previously the typeahead container was being created at the bottom
of `body`, and its width (and `top` and `left`) were being set to
move it to the right position.

Now it sits in the search box container, which gives it the correct
position and width by default. This is better for DOM readability,
and is also better for the new 100% width (which is part of the
search bar redesign) because it can change width more smoothly
with the search bar when the page changes width.

This commit adds custom functionality to the bootstrap typeahead
to allow the typehead to be placed in the search box container
(whereas previously, it could only be appended to `body`).
This commit is contained in:
evykassirer 2023-02-22 13:30:54 -08:00 committed by Tim Abbott
parent e3984f119d
commit 7c9677becd
6 changed files with 52 additions and 42 deletions

View File

@ -90,7 +90,7 @@ export function initialize() {
search_map = suggestions.lookup_table; search_map = suggestions.lookup_table;
return suggestions.strings; return suggestions.strings;
}, },
fixed: true, parentElement: "#searchbox_legacy",
items: search_suggestion.max_num_of_search_results, items: search_suggestion.max_num_of_search_results,
helpOnEmptyStrings: true, helpOnEmptyStrings: true,
naturalSearch: true, naturalSearch: true,

View File

@ -15,14 +15,8 @@
width: calc(100% - 2px); width: calc(100% - 2px);
position: absolute; position: absolute;
@media (width < $xl_min) {
width: calc(100% - 84px);
}
@media (width < $md_min) { @media (width < $md_min) {
/* todo: Figure out why this has to be different width: calc(100% - 42px);
from top-navbar-border at this width and resolve it */
width: calc(100% - 123px);
} }
.search_close_button { .search_close_button {
@ -35,6 +29,15 @@
} }
} }
.typeahead.dropdown-menu {
/* Match the typeahead's width to its parent container. */
right: 0;
@media (width < $md_min) {
margin-left: 40px;
}
}
.input-append { .input-append {
position: relative; position: relative;
width: 100%; width: 100%;

View File

@ -2742,8 +2742,8 @@ select.invite-as {
margin-right: 7px; margin-right: 7px;
} }
.top-navbar-container { #navbar-middle {
width: calc(100% - 84px); margin-right: 87px;
} }
.search_closed .zulip-icon-search { .search_closed .zulip-icon-search {
@ -2803,7 +2803,6 @@ select.invite-as {
.column-middle-inner { .column-middle-inner {
margin-left: 0; margin-left: 0;
margin-right: 15px;
} }
.app-main .column-middle .column-middle-inner { .app-main .column-middle .column-middle-inner {
@ -2816,7 +2815,6 @@ select.invite-as {
.top-navbar-border { .top-navbar-border {
margin-left: 40px; margin-left: 40px;
width: calc(100% - 108px);
} }
.search_closed .zulip-icon-search { .search_closed .zulip-icon-search {

View File

@ -103,7 +103,6 @@ test("initialize", ({mock_template}) => {
search_suggestion.max_num_of_search_results = 99; search_suggestion.max_num_of_search_results = 99;
$search_query_box.typeahead = (opts) => { $search_query_box.typeahead = (opts) => {
assert.equal(opts.fixed, true);
assert.equal(opts.items, 99); assert.equal(opts.items, 99);
assert.equal(opts.naturalSearch, true); assert.equal(opts.naturalSearch, true);
assert.equal(opts.helpOnEmptyStrings, true); assert.equal(opts.helpOnEmptyStrings, true);

View File

@ -86,7 +86,6 @@ run_test("initialize", ({mock_template}) => {
search_suggestion.max_num_of_search_results = 999; search_suggestion.max_num_of_search_results = 999;
$search_query_box.typeahead = (opts) => { $search_query_box.typeahead = (opts) => {
assert.equal(opts.fixed, true);
assert.equal(opts.items, 999); assert.equal(opts.items, 999);
assert.equal(opts.naturalSearch, true); assert.equal(opts.naturalSearch, true);
assert.equal(opts.helpOnEmptyStrings, true); assert.equal(opts.helpOnEmptyStrings, true);

View File

@ -98,6 +98,13 @@
* We add a new event handler, resizeHandler, for window.on('resize', ...) * We add a new event handler, resizeHandler, for window.on('resize', ...)
* that calls this.show to re-render the typeahead in the correct position. * that calls this.show to re-render the typeahead in the correct position.
* *
* 10. Allow typeahead to be located next to its input field in the DOM
*
* We add a new `parentElement` option which the typeahead can
* append to, where before it could only be appended to `body`.
* Since it's in the right part of the DOM, we don't need to do
* the manual positioning in the show() function.
*
* ============================================================ */ * ============================================================ */
import {insert} from "text-field-edit"; import {insert} from "text-field-edit";
@ -126,7 +133,7 @@ import {get_string_diff} from "../../src/util";
this.sorter = this.options.sorter || this.sorter this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter this.highlighter = this.options.highlighter || this.highlighter
this.updater = this.options.updater || this.updater this.updater = this.options.updater || this.updater
this.$container = $(this.options.container).appendTo('body') this.$container = $(this.options.container).appendTo(this.options.parentElement || 'body')
this.$menu = $(this.options.menu).appendTo(this.$container) this.$menu = $(this.options.menu).appendTo(this.$container)
this.$header = $(this.options.header_html).appendTo(this.$container) this.$header = $(this.options.header_html).appendTo(this.$container)
this.source = this.options.source this.source = this.options.source
@ -207,37 +214,41 @@ import {get_string_diff} from "../../src/util";
this.$header.hide(); this.$header.hide();
} }
// If a parent element was specified, we shouldn't manually
// position the element, since it's already in the right place.
if (!this.options.parentElement) {
var pos; var pos;
if (this.fixed) { if (this.fixed) {
// Relative to screen instead of to page // Relative to screen instead of to page
pos = this.$element[0].getBoundingClientRect(); pos = this.$element[0].getBoundingClientRect();
} else { } else {
pos = this.$element.offset(); pos = this.$element.offset();
}
pos = $.extend({}, pos, {
height: this.$element[0].offsetHeight
})
// Zulip patch: Workaround for iOS safari problems
pos.top = this.$element.offset().top;
var top_pos = pos.top + pos.height
if (this.dropup) {
top_pos = pos.top - this.$container.outerHeight()
}
// Zulip patch: Avoid typeahead going off top of screen.
if (top_pos < 0) {
top_pos = 0;
}
this.$container.css({
top: top_pos
, left: pos.left
})
} }
pos = $.extend({}, pos, {
height: this.$element[0].offsetHeight
})
// Zulip patch: Workaround for iOS safari problems
pos.top = this.$element.offset().top;
var top_pos = pos.top + pos.height
if (this.dropup) {
top_pos = pos.top - this.$container.outerHeight()
}
// Zulip patch: Avoid typeahead going off top of screen.
if (top_pos < 0) {
top_pos = 0;
}
this.$container.css({
top: top_pos
, left: pos.left
})
this.$container.show() this.$container.show()
this.shown = true this.shown = true
this.mouse_moved_since_typeahead = false this.mouse_moved_since_typeahead = false