REFACTOR: reworks all the search-advanced-options panel (#10661)

* REFACTOR: reworks all the search-advanced-options panel

This commit includes the following changes:
- prevents any mutation of external (to the component) values
- get rid of observers
- uses @action
- minor UI tweaks
- dropped the unecessary debounce
- drops all the legacy code for badges/groups which is not being used
- replaces user-selector by user-chooser and improves multi-select to not show `search` if maximum has been reached

Most importantly this refactor should fix multiple bugs due to _update() being called multiple times if searchTerm was empty and other various bugs where some changes in searchTerm was not applied to the sidebar.
This commit is contained in:
Joffrey JAFFEUX 2020-09-15 09:39:12 +02:00 committed by GitHub
parent bbddce4d3a
commit 324aa3eb61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 307 additions and 311 deletions

View File

@ -1,19 +1,13 @@
import I18n from "I18n";
import { debounce, scheduleOnce } from "@ember/runloop";
import Component from "@ember/component";
import { observes } from "discourse-common/utils/decorators";
import { action } from "@ember/object";
import { escapeExpression } from "discourse/lib/utilities";
import Group from "discourse/models/group";
import Badge from "discourse/models/badge";
import Category from "discourse/models/category";
import { INPUT_DELAY } from "discourse-common/config/environment";
const REGEXP_BLOCKS = /(([^" \t\n\x0B\f\r]+)?(("[^"]+")?))/g;
const REGEXP_USERNAME_PREFIX = /^(user:|@)/gi;
const REGEXP_CATEGORY_PREFIX = /^(category:|#)/gi;
const REGEXP_GROUP_PREFIX = /^group:/gi;
const REGEXP_BADGE_PREFIX = /^badge:/gi;
const REGEXP_TAGS_PREFIX = /^(tags?:|#(?=[a-z0-9\-]+::tag))/gi;
const REGEXP_IN_PREFIX = /^(in|with):/gi;
const REGEXP_STATUS_PREFIX = /^status:/gi;
@ -77,30 +71,17 @@ function addAdvancedSearchOptions(options) {
export default Component.extend({
classNames: ["search-advanced-options"],
category: null,
init() {
this._super(...arguments);
this._init();
scheduleOnce("afterRender", this, this._update);
},
@observes("searchTerm")
_updateOptions() {
this._update();
debounce(this, this._update, INPUT_DELAY);
},
_init() {
this.setProperties({
searchedTerms: {
username: "",
category: "",
group: [],
badge: [],
tags: [],
in: "",
username: null,
category: null,
tags: null,
in: null,
special: {
in: {
title: false,
@ -110,11 +91,11 @@ export default Component.extend({
},
all_tags: false,
},
status: "",
min_post_count: "",
status: null,
min_post_count: null,
time: {
when: "before",
days: "",
days: null,
},
},
inOptions: this.currentUser
@ -125,16 +106,11 @@ export default Component.extend({
});
},
_update() {
if (!this.searchTerm) {
this._init();
return;
}
didReceiveAttrs() {
this._super(...arguments);
this.setSearchedTermValue("searchedTerms.username", REGEXP_USERNAME_PREFIX);
this.setSearchedTermValueForCategory();
this.setSearchedTermValueForGroup();
this.setSearchedTermValueForBadge();
this.setSearchedTermValueForTags();
let regExpInMatch = this.inOptions.map((option) => option.value).join("|");
@ -216,14 +192,14 @@ export default Component.extend({
const match = this.filterBlocks(matchRegEx);
let val = this.get(key);
if (match.length !== 0) {
const userInput = match[0].replace(replaceRegEx, "");
if (val !== userInput) {
if (val !== userInput && userInput.length) {
this.set(key, userInput);
}
} else if (val && val.length !== 0) {
this.set(key, "");
this.set(key, null);
}
},
@ -239,11 +215,6 @@ export default Component.extend({
}
},
setCategory(category) {
this.set("searchedTerms.category", category);
this.set("category", category);
},
setSearchedTermValueForCategory() {
const match = this.filterBlocks(REGEXP_CATEGORY_PREFIX);
if (match.length !== 0) {
@ -259,61 +230,28 @@ export default Component.extend({
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
)
this.setCategory(userInput);
) {
this.set("searchedTerms.category", userInput);
}
} else if (isNaN(subcategories)) {
const userInput = Category.findSingleBySlug(subcategories[0]);
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
)
this.setCategory(userInput);
) {
this.set("searchedTerms.category", userInput);
}
} else {
const userInput = Category.findById(subcategories[0]);
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
)
this.setCategory(userInput);
) {
this.set("searchedTerms.category", userInput);
}
}
} else this.set("searchedTerms.category", "");
},
setSearchedTermValueForGroup() {
const match = this.filterBlocks(REGEXP_GROUP_PREFIX);
const group = this.get("searchedTerms.group");
if (match.length !== 0) {
const existingInput = Array.isArray(group) ? group[0] : group;
const userInput = match[0].replace(REGEXP_GROUP_PREFIX, "");
if (existingInput !== userInput) {
this.set(
"searchedTerms.group",
userInput.length !== 0 ? [userInput] : []
);
}
} else if (group.length !== 0) {
this.set("searchedTerms.group", []);
}
},
setSearchedTermValueForBadge() {
const match = this.filterBlocks(REGEXP_BADGE_PREFIX);
const badge = this.get("searchedTerms.badge");
if (match.length !== 0) {
const existingInput = Array.isArray(badge) ? badge[0] : badge;
const userInput = match[0].replace(REGEXP_BADGE_PREFIX, "");
if (existingInput !== userInput) {
this.set(
"searchedTerms.badge",
userInput.length !== 0 ? [userInput] : []
);
}
} else if (badge.length !== 0) {
this.set("searchedTerms.badge", []);
} else {
this.set("searchedTerms.category", null);
}
},
@ -322,21 +260,24 @@ export default Component.extend({
const match = this.filterBlocks(REGEXP_TAGS_PREFIX);
const tags = this.get("searchedTerms.tags");
const contain_all_tags = this.get("searchedTerms.special.all_tags");
if (match.length) {
this.set("searchedTerms.special.all_tags", match[0].includes("+"));
}
const containAllTags = this.get("searchedTerms.special.all_tags");
if (match.length !== 0) {
const join_char = contain_all_tags ? "+" : ",";
const existingInput = Array.isArray(tags) ? tags.join(join_char) : tags;
const joinChar = containAllTags ? "+" : ",";
const existingInput = Array.isArray(tags) ? tags.join(joinChar) : tags;
const userInput = match[0].replace(REGEXP_TAGS_REPLACE, "");
if (existingInput !== userInput) {
this.set(
"searchedTerms.tags",
userInput.length !== 0 ? userInput.split(join_char) : []
userInput.length !== 0 ? userInput.split(joinChar) : null
);
}
} else if (tags.length !== 0) {
this.set("searchedTerms.tags", []);
} else if (!tags) {
this.set("searchedTerms.tags", null);
}
},
@ -360,32 +301,141 @@ export default Component.extend({
this.setProperties(properties);
} else {
this.set("searchedTerms.time.days", "");
this.set("searchedTerms.time.when", "before");
this.set("searchedTerms.time.days", null);
}
},
@observes("searchedTerms.username")
updateSearchTermForUsername() {
const match = this.filterBlocks(REGEXP_USERNAME_PREFIX);
const userFilter = this.get("searchedTerms.username");
updateInRegex(regex, filter) {
const match = this.filterBlocks(regex);
const inFilter = this.get("searchedTerms.special.in." + filter);
let searchTerm = this.searchTerm || "";
if (userFilter && userFilter.length !== 0) {
if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], `@${userFilter}`);
} else {
searchTerm += ` @${userFilter}`;
if (inFilter) {
if (match.length === 0) {
searchTerm += ` in:${filter}`;
this._updateSearchTerm(searchTerm);
}
this.set("searchTerm", searchTerm.trim());
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
searchTerm = searchTerm.replace(match, "");
this._updateSearchTerm(searchTerm);
}
},
@observes("searchedTerms.category")
updateSearchTermForCategory() {
@action
onChangeSearchTermMinPostCount(value) {
this.set("searchedTerms.min_post_count", value.length ? value : null);
this._updateSearchTermForMinPostCount();
},
@action
onChangeSearchTermForIn(value) {
this.set("searchedTerms.in", value);
this._updateSearchTermForIn();
},
@action
onChangeSearchTermForStatus(value) {
this.set("searchedTerms.status", value);
this._updateSearchTermForStatus();
},
@action
onChangeWhenTime(time) {
if (time) {
this.set("searchedTerms.time.when", time);
this._updateSearchTermForPostTime();
}
},
@action
onChangeWhenDate(date) {
if (date) {
this.set("searchedTerms.time.days", date.format("YYYY-MM-DD"));
this._updateSearchTermForPostTime();
}
},
@action
onChangeSearchTermForCategory(categoryId) {
if (categoryId) {
const category = Category.findById(categoryId);
this.onChangeCategory && this.onChangeCategory(category);
this.set("searchedTerms.category", category);
} else {
this.onChangeCategory && this.onChangeCategory(null);
this.set("searchedTerms.category", null);
}
this._updateSearchTermForCategory();
},
@action
onChangeSearchTermForUsername(username) {
this.set("searchedTerms.username", username.length ? username : null);
this._updateSearchTermForUsername();
},
@action
onChangeSearchTermForTags(tags) {
this.set("searchedTerms.tags", tags.length ? tags : null);
this._updateSearchTermForTags();
},
@action
onChangeSearchTermForAllTags(checked) {
this.set("searchedTerms.special.all_tags", checked);
this._updateSearchTermForTags();
},
@action
onChangeSearchTermForSpecialInLikes(checked) {
this.set("searchedTerms.special.in.likes", checked);
this.updateInRegex(REGEXP_SPECIAL_IN_LIKES_MATCH, "likes");
},
@action
onChangeSearchTermForSpecialInPersonal(checked) {
this.set("searchedTerms.special.in.personal", checked);
this.updateInRegex(REGEXP_SPECIAL_IN_PERSONAL_MATCH, "personal");
},
@action
onChangeSearchTermForSpecialInSeen(checked) {
this.set("searchedTerms.special.in.seen", checked);
this.updateInRegex(REGEXP_SPECIAL_IN_SEEN_MATCH, "seen");
},
@action
onChangeSearchTermForSpecialInTitle(checked) {
this.set("searchedTerms.special.in.title", checked);
this.updateInRegex(REGEXP_SPECIAL_IN_TITLE_MATCH, "title");
},
_updateSearchTermForTags() {
const match = this.filterBlocks(REGEXP_TAGS_PREFIX);
const tagFilter = this.get("searchedTerms.tags");
let searchTerm = this.searchTerm || "";
const containAllTags = this.get("searchedTerms.special.all_tags");
if (tagFilter && tagFilter.length !== 0) {
const joinChar = containAllTags ? "+" : ",";
const tags = tagFilter.join(joinChar);
if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], `tags:${tags}`);
} else {
searchTerm += ` tags:${tags}`;
}
this._updateSearchTerm(searchTerm);
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this._updateSearchTerm(searchTerm);
}
},
_updateSearchTermForCategory() {
const match = this.filterBlocks(REGEXP_CATEGORY_PREFIX);
const categoryFilter = this.get("searchedTerms.category");
let searchTerm = this.searchTerm || "";
@ -411,7 +461,7 @@ export default Component.extend({
);
else searchTerm += ` #${parentSlug}:${slug}`;
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
} else {
if (slugCategoryMatches)
searchTerm = searchTerm.replace(slugCategoryMatches[0], `#${slug}`);
@ -422,7 +472,7 @@ export default Component.extend({
);
else searchTerm += ` #${slug}`;
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
} else {
if (slugCategoryMatches)
@ -430,76 +480,50 @@ export default Component.extend({
if (idCategoryMatches)
searchTerm = searchTerm.replace(idCategoryMatches[0], "");
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
},
@observes("searchedTerms.group")
updateSearchTermForGroup() {
const match = this.filterBlocks(REGEXP_GROUP_PREFIX);
const groupFilter = this.get("searchedTerms.group");
_updateSearchTermForUsername() {
const match = this.filterBlocks(REGEXP_USERNAME_PREFIX);
const userFilter = this.get("searchedTerms.username");
let searchTerm = this.searchTerm || "";
if (groupFilter && groupFilter.length !== 0) {
if (userFilter && userFilter.length !== 0) {
if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], ` group:${groupFilter}`);
searchTerm = searchTerm.replace(match[0], `@${userFilter}`);
} else {
searchTerm += ` group:${groupFilter}`;
searchTerm += ` @${userFilter}`;
}
this.set("searchTerm", searchTerm);
this._updateSearchTerm(searchTerm);
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
},
@observes("searchedTerms.badge")
updateSearchTermForBadge() {
const match = this.filterBlocks(REGEXP_BADGE_PREFIX);
const badgeFilter = this.get("searchedTerms.badge");
_updateSearchTermForPostTime() {
const match = this.filterBlocks(REGEXP_POST_TIME_PREFIX);
const timeDaysFilter = this.get("searchedTerms.time.days");
let searchTerm = this.searchTerm || "";
if (badgeFilter && badgeFilter.length !== 0) {
if (timeDaysFilter) {
const when = this.get("searchedTerms.time.when");
if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], ` badge:${badgeFilter}`);
searchTerm = searchTerm.replace(match[0], `${when}:${timeDaysFilter}`);
} else {
searchTerm += ` badge:${badgeFilter}`;
searchTerm += ` ${when}:${timeDaysFilter}`;
}
this.set("searchTerm", searchTerm);
this._updateSearchTerm(searchTerm);
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
},
@observes("searchedTerms.tags", "searchedTerms.special.all_tags")
updateSearchTermForTags() {
const match = this.filterBlocks(REGEXP_TAGS_PREFIX);
const tagFilter = this.get("searchedTerms.tags");
let searchTerm = this.searchTerm || "";
const contain_all_tags = this.get("searchedTerms.special.all_tags");
if (tagFilter && tagFilter.length !== 0) {
const join_char = contain_all_tags ? "+" : ",";
const tags = tagFilter.join(join_char);
if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], `tags:${tags}`);
} else {
searchTerm += ` tags:${tags}`;
}
this.set("searchTerm", searchTerm.trim());
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
}
},
@observes("searchedTerms.in")
updateSearchTermForIn() {
_updateSearchTermForIn() {
let regExpInMatch = this.inOptions.map((option) => option.value).join("|");
const REGEXP_IN_MATCH = new RegExp(`(in|with):(${regExpInMatch})`);
@ -518,51 +542,14 @@ export default Component.extend({
searchTerm += ` ${keyword}:${inFilter}`;
}
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match, "");
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
},
updateInRegex(regex, filter) {
const match = this.filterBlocks(regex);
const inFilter = this.get("searchedTerms.special.in." + filter);
let searchTerm = this.searchTerm || "";
if (inFilter) {
if (match.length === 0) {
searchTerm += ` in:${filter}`;
this.set("searchTerm", searchTerm.trim());
}
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match, "");
this.set("searchTerm", searchTerm.trim());
}
},
@observes("searchedTerms.special.in.likes")
updateSearchTermForSpecialInLikes() {
this.updateInRegex(REGEXP_SPECIAL_IN_LIKES_MATCH, "likes");
},
@observes("searchedTerms.special.in.personal")
updateSearchTermForSpecialInPersonal() {
this.updateInRegex(REGEXP_SPECIAL_IN_PERSONAL_MATCH, "personal");
},
@observes("searchedTerms.special.in.seen")
updateSearchTermForSpecialInSeen() {
this.updateInRegex(REGEXP_SPECIAL_IN_SEEN_MATCH, "seen");
},
@observes("searchedTerms.special.in.title")
updateSearchTermForSpecialInTitle() {
this.updateInRegex(REGEXP_SPECIAL_IN_TITLE_MATCH, "title");
},
@observes("searchedTerms.status")
updateSearchTermForStatus() {
_updateSearchTermForStatus() {
let regExpStatusMatch = this.statusOptions
.map((status) => status.value)
.join("|");
@ -579,35 +566,14 @@ export default Component.extend({
searchTerm += ` status:${statusFilter}`;
}
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
},
updateSearchTermForPostTime() {
const match = this.filterBlocks(REGEXP_POST_TIME_PREFIX);
const timeDaysFilter = this.get("searchedTerms.time.days");
let searchTerm = this.searchTerm || "";
if (timeDaysFilter) {
const when = this.get("searchedTerms.time.when");
if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], `${when}:${timeDaysFilter}`);
} else {
searchTerm += ` ${when}:${timeDaysFilter}`;
}
this.set("searchTerm", searchTerm.trim());
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
}
},
@observes("searchedTerms.min_post_count")
updateSearchTermForMinPostCount() {
_updateSearchTermForMinPostCount() {
const match = this.filterBlocks(REGEXP_MIN_POST_COUNT_PREFIX);
const postsCountFilter = this.get("searchedTerms.min_post_count");
let searchTerm = this.searchTerm || "";
@ -622,42 +588,15 @@ export default Component.extend({
searchTerm += ` min_post_count:${postsCountFilter}`;
}
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
} else if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], "");
this.set("searchTerm", searchTerm.trim());
this._updateSearchTerm(searchTerm);
}
},
groupFinder(term) {
return Group.findAll({ term: term, ignore_automatic: false });
},
badgeFinder(term) {
return Badge.findAll({ search: term });
},
actions: {
onChangeWhenTime(time) {
if (time) {
this.set("searchedTerms.time.when", time);
this.updateSearchTermForPostTime();
}
},
onChangeWhenDate(date) {
if (date) {
this.set("searchedTerms.time.days", moment(date).format("YYYY-MM-DD"));
this.updateSearchTermForPostTime();
}
},
onChangeCategory(categoryId) {
if (categoryId) {
this.set("searchedTerms.category", Category.findById(categoryId));
} else {
this.set("searchedTerms.category", null);
}
},
_updateSearchTerm(searchTerm) {
this.onChangeSearchTerm(searchTerm.trim());
},
});

View File

@ -2,14 +2,17 @@
<div class="container advanced-search-posted-by-group">
<div class="control-group pull-left">
<label class="control-label" for="search-posted-by">{{i18n "search.advanced.posted_by.label"}}</label>
<label class="control-label" for="search-posted-by">
{{i18n "search.advanced.posted_by.label"}}
</label>
<div class="controls">
{{user-selector
excludeCurrentUser=false
usernames=searchedTerms.username
single=true
canReceiveUpdates=true
class="user-selector"
{{user-chooser
value=searchedTerms.username
onChange=(action "onChangeSearchTermForUsername")
options=(hash
maximum=1
excludeCurrentUser=false
)
}}
</div>
</div>
@ -18,28 +21,12 @@
<div class="controls">
{{search-advanced-category-chooser
value=searchedTerms.category.id
onChange=(action "onChangeCategory")
onChange=(action "onChangeSearchTermForCategory")
}}
</div>
</div>
</div>
{{!-- disable these super-advanced searches for now
<div class="container">
<div class="control-group pull-left">
<label class="control-label" for="search-in-group">{{i18n "search.advanced.in_group.label"}}</label>
<div class="controls">
{{group-selector groupFinder=groupFinder groupNames=searchedTerms.group single="true" canReceiveUpdates="true"}}
</div>
</div>
<div class="control-group pull-left">
<label class="control-label" for="search-with-badge">{{i18n "search.advanced.with_badge.label"}}</label>
<div class="controls">
{{badge-selector badgeFinder=badgeFinder badgeNames=searchedTerms.badge single="true" canReceiveUpdates="true"}}
</div>
</div>
</div> --}}
{{#if siteSettings.tagging_enabled}}
<div class="container advanced-search-tag-group">
<div class="control-group">
@ -50,10 +37,18 @@
allowCreate=false
everyTag=true
unlimitedTagCount=true
onChange=(action (mut searchedTerms.tags))
onChange=(action "onChangeSearchTermForTags")
}}
<section class="field">
<label>{{ input type="checkbox" class="all-tags" checked=searchedTerms.special.all_tags}} {{i18n "search.advanced.filters.all_tags"}} </label>
<label>
{{input
type="checkbox"
class="all-tags"
checked=searchedTerms.special.all_tags
click=(action "onChangeSearchTermForAllTags" value="target.checked")
}}
{{i18n "search.advanced.filters.all_tags"}}
</label>
</section>
</div>
</div>
@ -66,10 +61,42 @@
<div class="controls">
{{#if currentUser}}
<section class="field">
<label>{{input type="checkbox" class="in-title" checked=searchedTerms.special.in.title}} {{i18n "search.advanced.filters.title"}}</label>
<label>{{input type="checkbox" class="in-likes" checked=searchedTerms.special.in.likes}} {{i18n "search.advanced.filters.likes"}}</label>
<label>{{input type="checkbox" class="in-private" checked=searchedTerms.special.in.personal}} {{i18n "search.advanced.filters.private"}}</label>
<label>{{input type="checkbox" class="in-seen" checked=searchedTerms.special.in.seen}} {{i18n "search.advanced.filters.seen"}}</label>
<label>
{{input
type="checkbox"
class="in-title"
checked=searchedTerms.special.in.title
click=(action "onChangeSearchTermForSpecialInTitle" value="target.checked")
}}
{{i18n "search.advanced.filters.title"}}
</label>
<label>
{{input
type="checkbox"
class="in-likes"
checked=searchedTerms.special.in.likes
click=(action "onChangeSearchTermForSpecialInLikes" value="target.checked")
}}
{{i18n "search.advanced.filters.likes"}}
</label>
<label>
{{input
type="checkbox"
class="in-private"
checked=searchedTerms.special.in.personal
click=(action "onChangeSearchTermForSpecialInPersonal" value="target.checked")
}}
{{i18n "search.advanced.filters.private"}}
</label>
<label>
{{input
type="checkbox"
class="in-seen"
checked=searchedTerms.special.in.seen
click=(action "onChangeSearchTermForSpecialInSeen" value="target.checked")
}}
{{i18n "search.advanced.filters.seen"}}
</label>
</section>
{{/if}}
{{combo-box
@ -77,8 +104,11 @@
valueProperty="value"
content=inOptions
value=searchedTerms.in
none="user.locale.any"
onChange=(action (mut searchedTerms.in))
onChange=(action "onChangeSearchTermForIn")
options=(hash
none="user.locale.any"
clearable=true
)
}}
</div>
</div>
@ -90,8 +120,11 @@
valueProperty="value"
content=statusOptions
value=searchedTerms.status
none="user.locale.any"
onChange=(action (mut searchedTerms.status))
onChange=(action "onChangeSearchTermForStatus")
options=(hash
none="user.locale.any"
clearable=true
)
}}
</div>
</div>
@ -118,7 +151,13 @@
<div class="control-group pull-left">
<label class="control-label" for="search-min-post-count">{{i18n "search.advanced.post.count.label"}}</label>
<div class="controls">
{{input type="number" value=searchedTerms.min_post_count class="input-small" id="search-min-post-count"}}
{{input
type="number"
value=(readonly searchedTerms.min_post_count)
class="input-small"
id="search-min-post-count"
input=(action "onChangeSearchTermMinPostCount" value="target.value")
}}
</div>
</div>
</div>

View File

@ -198,11 +198,10 @@
{{/if}}
{{#if site.mobileView}}
{{d-button
class="search-advanced-title"
action=(action "toggleAdvancedSearch")
icon=(if expanded "caret-down" "caret-right")
label="search.advanced.title"}}
<div role="button" class="search-advanced-title" {{on "click" (action "toggleAdvancedSearch")}}>
{{d-icon (if expanded "caret-down" "caret-right")}}
<span>{{i18n "search.advanced.title"}}</span>
</div>
{{else}}
<span class="search-advanced-title">
{{i18n "search.advanced.title"}}
@ -213,17 +212,17 @@
{{#if expanded}}
<div class="search-advanced-filters">
{{search-advanced-options
searchTerm=searchTerm
isExpanded=expanded
searchTerm=(readonly searchTerm)
onChangeSearchTerm=(action (mut searchTerm))
}}
</div>
{{/if}}
{{else}}
<div class="search-advanced-filters">
{{search-advanced-options
searchTerm=searchTerm
isExpanded=true
category=category
searchTerm=(readonly searchTerm)
onChangeSearchTerm=(action (mut searchTerm))
onChangeCategory=(action (mut category))
}}
{{d-button

View File

@ -11,6 +11,14 @@ export default SelectKitHeaderComponent.extend({
return makeArray(this.selectedContent).map((c) => this.getName(c));
}),
hasReachedMaximumSelection: computed("selectedValue", function () {
if (!this.selectKit.options.maximum) {
return false;
}
return this.selectedValue.length >= this.selectKit.options.maximum;
}),
selectedValue: computed("selectedContent", function () {
return makeArray(this.selectedContent)
.map((c) => {

View File

@ -7,9 +7,11 @@
}}
{{/each}}
<div class="choice input-wrapper">
{{component selectKit.options.filterComponent
selectKit=selectKit
}}
</div>
{{#unless hasReachedMaximumSelection}}
<div class="choice input-wrapper">
{{component selectKit.options.filterComponent
selectKit=selectKit
}}
</div>
{{/unless}}
</div>

View File

@ -12,7 +12,6 @@
display: flex;
justify-content: space-between;
align-items: center;
justify-content: space-between;
margin-bottom: 1em;
.search-query {
@ -133,11 +132,7 @@
}
font-weight: 700;
text-align: left;
font-weight: bold;
&.btn {
background: var(--primary-low);
}
cursor: pointer;
.d-icon {
margin: 0;

View File

@ -34,7 +34,8 @@
width: 100%;
margin: 0;
.tag-chooser {
.tag-chooser,
.user-chooser {
width: 100%;
}
}

View File

@ -1,5 +1,5 @@
import selectKit from "helpers/select-kit-helper";
import { acceptance, waitFor } from "helpers/qunit-helpers";
import { selectDate, acceptance, waitFor } from "helpers/qunit-helpers";
acceptance("Search - Full Page", {
settings: { tagging_enabled: true },
@ -109,7 +109,7 @@ QUnit.test("escape search term", async (assert) => {
assert.ok(
exists(
'.search-advanced-options span:contains("<script>prompt(1337)</script>gmail.com")'
'.search-advanced-options span:contains("&lt;script&gt;prompt(1337)&lt;/script&gt;gmail.com")'
),
"it escapes search term"
);
@ -355,7 +355,7 @@ QUnit.test("update post time through advanced search ui", async (assert) => {
await visit("/search");
await fillIn(".search-query", "none");
await fillIn("#search-post-date .date-picker", "2016-10-05");
await selectDate("#search-post-date .date-picker", "2016-10-05");
const postTimeSelector = selectKit(
".search-advanced-options .select-kit#postTime"

View File

@ -1,3 +1,4 @@
import { Promise } from "rsvp";
import { isEmpty } from "@ember/utils";
import { later } from "@ember/runloop";
/* global QUnit, resetSite */
@ -283,3 +284,15 @@ export function waitFor(assert, callback, timeout) {
done();
}, timeout);
}
export async function selectDate(selector, date) {
return new Promise((resolve) => {
const elem = document.querySelector(selector);
elem.value = date;
const evt = new Event("input", { bubbles: true, cancelable: false });
elem.dispatchEvent(evt);
elem.blur();
resolve();
});
}