DEV: Update to glimmer search menu
This commit is contained in:
parent
5fb25748da
commit
748940e11f
|
|
@ -34,21 +34,21 @@ $max-width: 600px;
|
||||||
max-width: $max-width;
|
max-width: $max-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-menu {
|
.menu-panel-results .menu-panel {
|
||||||
position: relative;
|
position: unset;
|
||||||
display: flex;
|
padding: 0;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
.d-icon-search {
|
|
||||||
margin: 0;
|
.d-icon-search {
|
||||||
}
|
margin: 0;
|
||||||
.browser-search-tip {
|
}
|
||||||
display: none;
|
.browser-search-tip {
|
||||||
}
|
display: none;
|
||||||
.search-input input#search-term[type="text"] {
|
}
|
||||||
margin: 0;
|
.search-input input#search-term[type="text"] {
|
||||||
width: 100%;
|
margin: 0;
|
||||||
padding-left: 2.25em;
|
width: 100%;
|
||||||
}
|
// padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
|
|
@ -75,12 +75,12 @@ $max-width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.search-icon:not(.has-search-button-text) {
|
.btn.search-icon:not(.has-search-button-text) {
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-right: 0.25em;
|
||||||
.discourse-no-touch & {
|
.discourse-no-touch & {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -97,19 +97,24 @@ $max-width: 600px;
|
||||||
column-gap: 0.5em;
|
column-gap: 0.5em;
|
||||||
background-color: var(--tertiary);
|
background-color: var(--tertiary);
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
|
flex: none;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--tertiary-hover);
|
background-color: var(--tertiary-hover);
|
||||||
}
|
}
|
||||||
.d-icon {
|
.d-icon {
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
+ .search-input #search-term[type="text"] {
|
|
||||||
padding-left: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input .btn.search-context {
|
.search-input .search-context {
|
||||||
margin: 0;
|
// margin-right: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide the search icon when a search context is selected
|
||||||
|
// eg when searching in a topic
|
||||||
|
.search-input .search-context + .search-icon:not(.has-search-button-text),
|
||||||
|
.search-input .search-context ~ .search-icon:not(.has-search-button-text) {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.results {
|
.results {
|
||||||
|
|
|
||||||
|
|
@ -12,158 +12,4 @@ export default apiInitializer("1.14.0", (api) => {
|
||||||
: "below-site-header",
|
: "below-site-header",
|
||||||
SearchBanner
|
SearchBanner
|
||||||
);
|
);
|
||||||
|
|
||||||
// Simplified version of header search theme component
|
|
||||||
const searchMenuWidget = api.container.factoryFor("widget:search-menu");
|
|
||||||
const corePanelContents = searchMenuWidget.class.prototype["panelContents"];
|
|
||||||
|
|
||||||
api.reopenWidget("search-menu", {
|
|
||||||
buildKey(attrs) {
|
|
||||||
let type = attrs.formFactor || "menu";
|
|
||||||
return `search-${type}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
defaultState(attrs) {
|
|
||||||
return {
|
|
||||||
formFactor: attrs.formFactor || "menu",
|
|
||||||
showHeaderResults: false,
|
|
||||||
inTopicContext: attrs.inTopicContext,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
html(attrs, state) {
|
|
||||||
if (this.state.formFactor === "widget") {
|
|
||||||
return this.panelContents();
|
|
||||||
} else {
|
|
||||||
return this._super(attrs, state);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clickOutside() {
|
|
||||||
const formFactor = this.state.formFactor;
|
|
||||||
if (formFactor === "menu") {
|
|
||||||
return this.sendWidgetAction("toggleSearchMenu");
|
|
||||||
} else {
|
|
||||||
this.state.showHeaderResults = false;
|
|
||||||
this.scheduleRerender();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
click() {
|
|
||||||
const formFactor = this.state.formFactor;
|
|
||||||
if (formFactor === "widget") {
|
|
||||||
this.showResults();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showResults() {
|
|
||||||
this.state.showHeaderResults = true;
|
|
||||||
this.scheduleRerender();
|
|
||||||
},
|
|
||||||
|
|
||||||
linkClickedEvent(attrs) {
|
|
||||||
if (attrs) {
|
|
||||||
const { searchLogId, searchResultId, searchResultType } = attrs;
|
|
||||||
if (searchLogId && searchResultId && searchResultType) {
|
|
||||||
logSearchLinkClick({
|
|
||||||
searchLogId,
|
|
||||||
searchResultId,
|
|
||||||
searchResultType,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formFactor = this.state.formFactor;
|
|
||||||
|
|
||||||
if (formFactor === "widget") {
|
|
||||||
this.state.showHeaderResults = false;
|
|
||||||
this.scheduleRerender();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
focusSearchInput() {
|
|
||||||
const searchInput =
|
|
||||||
this.state.formFactor === "widget"
|
|
||||||
? document.querySelector(".search-widget #search-term")
|
|
||||||
: document.querySelector(".search-menu #search-term");
|
|
||||||
|
|
||||||
searchInput.focus();
|
|
||||||
searchInput.select();
|
|
||||||
},
|
|
||||||
|
|
||||||
clearContext() {
|
|
||||||
this.sendWidgetAction("clearSearch");
|
|
||||||
this.sendWidgetAction("clearSearchWidgetContext");
|
|
||||||
this.state.inTopicContext = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSearchWidgetContext() {
|
|
||||||
this.state.inTopicContext = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
panelContents() {
|
|
||||||
const formFactor = this.state.formFactor;
|
|
||||||
let showHeaderResults =
|
|
||||||
this.state.showHeaderResults == null ||
|
|
||||||
this.state.showHeaderResults === true;
|
|
||||||
let contents = [];
|
|
||||||
|
|
||||||
let buttonClass =
|
|
||||||
I18n.t(themePrefix("search_banner.search_button_text")) === ""
|
|
||||||
? "btn search-icon"
|
|
||||||
: "btn search-icon has-search-button-text";
|
|
||||||
|
|
||||||
if (formFactor === "widget") {
|
|
||||||
contents.push(
|
|
||||||
this.attach("link", {
|
|
||||||
href: this.fullSearchUrl({ expanded: true }),
|
|
||||||
contents: () => [
|
|
||||||
iconNode("search"),
|
|
||||||
h(
|
|
||||||
"span",
|
|
||||||
I18n.t(themePrefix("search_banner.search_button_text"))
|
|
||||||
),
|
|
||||||
],
|
|
||||||
className: buttonClass,
|
|
||||||
title: "search.open_advanced",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
contents = contents.concat(...corePanelContents.call(this));
|
|
||||||
if (formFactor === "menu" || showHeaderResults) {
|
|
||||||
return contents;
|
|
||||||
} else {
|
|
||||||
return contents.filter((widget) => {
|
|
||||||
return (
|
|
||||||
widget.name !== "search-menu-results" &&
|
|
||||||
widget.name !== "search-context"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
api.createWidget("search-widget", {
|
|
||||||
tagName: "div.search-widget",
|
|
||||||
|
|
||||||
html() {
|
|
||||||
const searchMenuVisible = this.state.searchVisible;
|
|
||||||
|
|
||||||
if (!searchMenuVisible && !this.attrs.topic) {
|
|
||||||
return this.attach("search-menu", {
|
|
||||||
contextEnabled: this.state.contextEnabled,
|
|
||||||
formFactor: "widget",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSearchWidgetContext() {
|
|
||||||
this.state.inTopicContext = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
setTopicContext() {
|
|
||||||
this.state.inTopicContext = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@
|
||||||
<h1>{{html-safe (theme-i18n "search_banner.headline")}}</h1>
|
<h1>{{html-safe (theme-i18n "search_banner.headline")}}</h1>
|
||||||
<PluginOutlet @name="search-banner-below-headline" />
|
<PluginOutlet @name="search-banner-below-headline" />
|
||||||
<p>{{html-safe (theme-i18n "search_banner.subhead")}}</p>
|
<p>{{html-safe (theme-i18n "search_banner.subhead")}}</p>
|
||||||
<MountWidget @widget="search-widget" />
|
<div class="search-menu">
|
||||||
|
<SearchMenu />
|
||||||
|
</div>
|
||||||
<PluginOutlet @name="search-banner-below-input" />
|
<PluginOutlet @name="search-banner-below-input" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
|
export default class SearchIcon extends Component {
|
||||||
|
get buttonText() {
|
||||||
|
return I18n.t(themePrefix("search_banner.search_button_text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
get buttonClass() {
|
||||||
|
return this.buttonText
|
||||||
|
? "btn search-icon has-search-button-text"
|
||||||
|
: "btn search-icon";
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DButton
|
||||||
|
@icon="search"
|
||||||
|
@translatedLabel={{this.buttonText}}
|
||||||
|
@title="search.open_advanced"
|
||||||
|
class={{this.buttonClass}}
|
||||||
|
@href={{@outletArgs.advancedSearchButtonHref}}
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.search-widget {
|
.search-menu {
|
||||||
.search-link {
|
.search-link {
|
||||||
.badge-category {
|
.badge-category {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
RSpec.describe "Viewing the search banner", type: :system do
|
RSpec.describe "Viewing the search banner", type: :system do
|
||||||
fab!(:theme) { upload_theme_component }
|
fab!(:theme) { upload_theme_component }
|
||||||
|
fab!(:topic)
|
||||||
|
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||||
|
|
||||||
it "should display the search banner below the site header when `plugin_outlet` theme setting is set to `below-site-header`" do
|
it "should display the search banner below the site header when `plugin_outlet` theme setting is set to `below-site-header`" do
|
||||||
theme.update_setting(:plugin_outlet, "below-site-header")
|
theme.update_setting(:plugin_outlet, "below-site-header")
|
||||||
|
|
@ -21,4 +23,32 @@ RSpec.describe "Viewing the search banner", type: :system do
|
||||||
|
|
||||||
expect(page).to have_css("#main-outlet .custom-search-banner")
|
expect(page).to have_css("#main-outlet .custom-search-banner")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should hide the search icon when searching within a topic" do
|
||||||
|
theme.update_setting(:show_on, "all")
|
||||||
|
theme.save!
|
||||||
|
|
||||||
|
topic_page.visit_topic(topic)
|
||||||
|
expect(topic_page).to have_css(".custom-search-banner")
|
||||||
|
topic_page.find(".custom-search-banner input#search-term").fill_in(with: "test")
|
||||||
|
topic_page.find(".custom-search-banner .results li:nth-child(2) a").click
|
||||||
|
|
||||||
|
expect(topic_page).to have_css(".custom-search-banner .search-context")
|
||||||
|
expect(topic_page).to_not have_css(".custom-search-banner .search-icon")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should display the search icon when searching within a topic when search button text is present" do
|
||||||
|
theme.update_setting(:show_on, "all")
|
||||||
|
theme.update_translation("search_banner.search_button_text", "Foo")
|
||||||
|
theme.save!
|
||||||
|
|
||||||
|
topic_page.visit_topic(topic)
|
||||||
|
expect(topic_page).to have_css(".custom-search-banner")
|
||||||
|
|
||||||
|
topic_page.find(".custom-search-banner input#search-term").fill_in(with: "test")
|
||||||
|
topic_page.find(".custom-search-banner .results li:nth-child(2) a").click
|
||||||
|
|
||||||
|
expect(topic_page).to have_css(".custom-search-banner .search-context")
|
||||||
|
expect(topic_page).to have_css(".custom-search-banner .search-icon")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { acceptance, query } from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import { fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
||||||
|
import { test } from "qunit";
|
||||||
|
|
||||||
|
acceptance("Discourse Search Banner", function (needs) {
|
||||||
|
needs.user({ admin: true, experimental_search_menu_groups_enabled: true });
|
||||||
|
|
||||||
|
test("Search Banner is present", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
assert.dom(".custom-search-banner input#search-term").exists();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("is only present on intended routes", async function (assert) {
|
||||||
|
await visit("/admin");
|
||||||
|
assert.dom(".custom-search-banner").doesNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("it closes the search menu when clicking outside", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
await click(".custom-search-banner input#search-term");
|
||||||
|
await fillIn(".custom-search-banner input#search-term", "test");
|
||||||
|
assert.dom(".custom-search-banner .results").exists();
|
||||||
|
|
||||||
|
// select a element to simulate clicking outside the search banner
|
||||||
|
await click(".custom-search-banner h1");
|
||||||
|
assert.dom(".custom-search-banner .results").doesNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("pressing escape closes the search menu", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
await click(".custom-search-banner input#search-term");
|
||||||
|
await fillIn(".custom-search-banner input#search-term", "test");
|
||||||
|
assert.dom(".custom-search-banner .results").exists();
|
||||||
|
|
||||||
|
await triggerKeyEvent(
|
||||||
|
".custom-search-banner #search-term",
|
||||||
|
"keyup",
|
||||||
|
"Escape"
|
||||||
|
);
|
||||||
|
assert.dom(".custom-search-banner .results").doesNotExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("searching for a term in the search menu fills in the search banner search input", async function (assert) {
|
||||||
|
await visit("/");
|
||||||
|
await click("#search-button");
|
||||||
|
await fillIn("#search-term", "test");
|
||||||
|
assert
|
||||||
|
.dom(".custom-search-banner input#search-term")
|
||||||
|
.hasValue("test", "search inputs have matching terms");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("you can navigate search results with the keyboard", async function (assert) {
|
||||||
|
const container = ".custom-search-banner .results";
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".custom-search-banner input#search-term");
|
||||||
|
await fillIn(".custom-search-banner input#search-term", "test");
|
||||||
|
|
||||||
|
await triggerKeyEvent(
|
||||||
|
".custom-search-banner #search-term",
|
||||||
|
"keyup",
|
||||||
|
"Enter"
|
||||||
|
);
|
||||||
|
assert.dom(`${container} .search-result-topic`).exists("has topic results");
|
||||||
|
|
||||||
|
await triggerKeyEvent(document.activeElement, "keyup", "ArrowDown");
|
||||||
|
assert.strictEqual(
|
||||||
|
document.activeElement.getAttribute("href"),
|
||||||
|
query(`${container} li:first-child a`).getAttribute("href"),
|
||||||
|
"arrow down selects first element"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue