FEATURE: Add input filter for editing tags in navigation menu modal (#22216)

What does this change do?

This commit adds an input filter to filter through the tag checkboxes in the
modal to edit tags that are shown in the user's navigation menu. The
filtering is a simple matching of the given filter term against the
names of the tags.
This commit is contained in:
Alan Guo Xiang Tan 2023-06-21 10:59:56 +08:00 committed by GitHub
parent 08d8bd9f43
commit 609562be3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 137 additions and 23 deletions

View File

@ -3,7 +3,20 @@
@class="sidebar-tags-form-modal" @class="sidebar-tags-form-modal"
> >
<form class="sidebar-tags-form"> <form class="sidebar-tags-form">
{{#each this.tags as |tag|}} <div class="sidebar-tags-form__filter">
{{d-icon "search" class="sidebar-tags-form__filter-input-icon"}}
<Input
class="sidebar-tags-form__filter-input-field"
placeholder={{i18n "sidebar.tags_form_modal.filter_placeholder"}}
@type="text"
@value={{this.filter}}
{{on "input" (action "onFilterInput" value="target.value")}}
/>
</div>
{{#if (gt this.filteredTags.length 0)}}
{{#each this.filteredTags as |tag|}}
<div class="sidebar-tags-form__tag" data-tag-name={{tag.name}}> <div class="sidebar-tags-form__tag" data-tag-name={{tag.name}}>
<Input <Input
id={{concat "sidebar-tags-form__input--" tag.name}} id={{concat "sidebar-tags-form__input--" tag.name}}
@ -29,6 +42,11 @@
</label> </label>
</div> </div>
{{/each}} {{/each}}
{{else}}
<div class="sidebar-tags-form__no-tags">
{{i18n "sidebar.tags_form_modal.no_tags"}}
</div>
{{/if}}
</form> </form>
</DModalBody> </DModalBody>

View File

@ -4,10 +4,14 @@ import { inject as service } from "@ember/service";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce";
export default class extends Component { export default class extends Component {
@service currentUser; @service currentUser;
@service store; @service store;
@tracked filter = "";
@tracked tags = []; @tracked tags = [];
@tracked selectedTags = [...this.currentUser.sidebarTagNames]; @tracked selectedTags = [...this.currentUser.sidebarTagNames];
@ -31,6 +35,29 @@ export default class extends Component {
}); });
} }
get filteredTags() {
if (this.filter.length === 0) {
return this.tags;
} else {
return this.tags.reduce((acc, tag) => {
if (tag.name.toLowerCase().includes(this.filter)) {
acc.push(tag);
}
return acc;
}, []);
}
}
@action
onFilterInput(filter) {
discourseDebounce(this, this.#performFiltering, filter, INPUT_DELAY);
}
#performFiltering(filter) {
this.filter = filter.toLowerCase();
}
@action @action
toggleTag(tag) { toggleTag(tag) {
if (this.selectedTags.includes(tag)) { if (this.selectedTags.includes(tag)) {

View File

@ -31,4 +31,32 @@
.sidebar-tags-form__tag-label-count { .sidebar-tags-form__tag-label-count {
color: var(--primary-medium); color: var(--primary-medium);
} }
.sidebar-tags-form__filter {
display: flex;
flex-direction: row;
margin-right: auto;
width: 100%;
position: relative;
}
.sidebar-tags-form__filter-input-icon {
position: absolute;
left: 0.5em;
top: 0.65em;
color: var(--primary-low-mid);
}
.sidebar-tags-form__filter-input-field {
border-color: var(--primary-low-mid);
padding-left: 1.75em;
width: 100%;
&:focus {
border-color: var(--tertiary);
outline: none;
outline-offset: 0;
box-shadow: inset 0px 0px 0px 1px var(--tertiary);
}
}
} }

View File

@ -1,3 +1,9 @@
.sidebar-tags-form-modal {
.modal-inner-container {
width: 35em;
}
}
.sidebar-tags-form { .sidebar-tags-form {
.sidebar-tags-form__tag { .sidebar-tags-form__tag {
flex-basis: 100%; flex-basis: 100%;

View File

@ -4399,6 +4399,8 @@ en:
reset_to_defaults: "Reset to defaults" reset_to_defaults: "Reset to defaults"
tags_form_modal: tags_form_modal:
title: "Edit tags navigation" title: "Edit tags navigation"
filter_placeholder: "Filter tags"
no_tags: "There are no tags matching the given term."
sections: sections:
custom: custom:

View File

@ -49,4 +49,24 @@ RSpec.describe "Editing sidebar tags navigation", type: :system do
expect(sidebar).to have_no_section_link(tag2.name) expect(sidebar).to have_no_section_link(tag2.name)
expect(sidebar).to have_no_section_link(tag3.name) expect(sidebar).to have_no_section_link(tag3.name)
end end
it "allows a user to filter the tags in the modal by the tag's name" do
visit "/latest"
expect(sidebar).to have_tags_section
modal = sidebar.click_edit_tags_button
modal.filter("tag")
expect(modal).to have_tag_checkboxes([tag, tag2, tag3])
modal.filter("tag2")
expect(modal).to have_tag_checkboxes([tag2])
modal.filter("someinvalidterm")
expect(modal).to have_no_tag_checkboxes
end
end end

View File

@ -20,6 +20,14 @@ module PageObjects
end end
end end
def has_no_tag_checkboxes?
has_no_css?(".sidebar-tags-form-modal .sidebar-tags-form__tag") &&
has_css?(
".sidebar-tags-form-modal .sidebar-tags-form__no-tags",
text: I18n.t("js.sidebar.tags_form_modal.no_tags"),
)
end
def toggle_tag_checkbox(tag) def toggle_tag_checkbox(tag)
find( find(
".sidebar-tags-form-modal .sidebar-tags-form__tag[data-tag-name='#{tag.name}'] .sidebar-tags-form__input", ".sidebar-tags-form-modal .sidebar-tags-form__tag[data-tag-name='#{tag.name}'] .sidebar-tags-form__input",
@ -32,6 +40,11 @@ module PageObjects
find(".sidebar-tags-form-modal .sidebar-tags-form__save-button").click find(".sidebar-tags-form-modal .sidebar-tags-form__save-button").click
self self
end end
def filter(text)
find(".sidebar-tags-form-modal .sidebar-tags-form__filter-input-field").fill_in(with: text)
self
end
end end
end end
end end