DEV: Deselect all and reset to defaults btns to edit categories modal (#22143)

What does this change do?

This change adds the deselect all and reset to defaults buttons to the
edit navigation menu categories modal. The deselect all button when
click deselects all the selected categories in the modal. If the user
saves with no categories selected, the user's categories section in the
navigation menu will be set to the site's top categories.

The reset to defaults button is only shown when the
`default_navigation_menu_categories` site setting has been configured.
When clicked, the user's categories section in the navigation menu is
automatically set to the categories defined by the
`default_navigation_menu_categories` site setting.
This commit is contained in:
Alan Guo Xiang Tan 2023-06-20 08:17:53 +08:00 committed by GitHub
parent cdcf6cf0dd
commit 289d2a5540
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 223 additions and 85 deletions

View File

@ -34,15 +34,19 @@
/> />
{{/if}} {{/if}}
{{#if this.title}} <div class="modal-title-wrapper">
<div class="title"> {{#if this.title}}
<h3 id="discourse-modal-title">{{this.title}}</h3> <div class="title">
<h3 id="discourse-modal-title">{{this.title}}</h3>
{{#if this.subtitle}} {{#if this.subtitle}}
<p class="subtitle">{{this.subtitle}}</p> <p class="subtitle">{{this.subtitle}}</p>
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
<span id="modal-header-after-title"></span>
</div>
{{#if this.panels}} {{#if this.panels}}
<ul class="modal-tabs"> <ul class="modal-tabs">

View File

@ -1,75 +1,102 @@
<DModalBody {{#in-element this.modalHeaderAfterTitleElement}}
@title="sidebar.categories_form.title" <p class="sidebar-categories-form__deselect">
@class="sidebar-categories-form-modal" <DButton
> @class="btn-flat sidebar-categories-form__deselect-all-btn"
<form class="sidebar-categories-form"> @label="sidebar.categories_form_modal.subtitle.button_text"
<div class="sidebar-categories-form__filter"> @ariaLabel="sidebar.categories_form_modal.subtitle.button_text"
{{d-icon "search" class="sidebar-categories-form__filter-input-icon"}} @action={{this.deselectAll}}
/>
<Input {{i18n "sidebar.categories_form_modal.subtitle.text"}}
class="sidebar-categories-form__filter-input-field" </p>
placeholder={{i18n "sidebar.categories_form.filter_placeholder"}} {{/in-element}}
@type="text"
@value={{this.filter}}
{{on "input" (action "onFilterInput" value="target.value")}}
/>
</div>
{{#if (gt this.filteredCategoriesGroupings.length 0)}} <div class="sidebar-categories-form-modal">
{{#each this.filteredCategoriesGroupings as |categories|}} <DModalBody
<div @title="sidebar.categories_form_modal.title"
class="sidebar-categories-form__row" @class="sidebar-categories-form__modal-body"
style={{html-safe (border-color categories.0.color "left")}} >
> <form class="sidebar-categories-form">
<div class="sidebar-categories-form__filter">
{{d-icon "search" class="sidebar-categories-form__filter-input-icon"}}
{{#each categories as |category|}} <Input
<div class="sidebar-categories-form__filter-input-field"
class="sidebar-categories-form__category-row" placeholder={{i18n
data-category-id={{category.id}} "sidebar.categories_form_modal.filter_placeholder"
data-category-level={{category.level}} }}
> @type="text"
<label @value={{this.filter}}
class="sidebar-categories-form__category-label" {{on "input" (action "onFilterInput" value="target.value")}}
for={{concat "sidebar-categories-form__input--" category.id}} />
>
<div class="sidebar-categories-form__category-badge">
{{category-badge category}}
</div>
{{#unless category.parentCategory}}
<div class="sidebar-categories-form__category-description">
{{dir-span category.description_excerpt htmlSafe="true"}}
</div>
{{/unless}}
</label>
<Input
id={{concat "sidebar-categories-form__input--" category.id}}
class="sidebar-categories-form__input"
@type="checkbox"
@checked={{includes
this.selectedSidebarCategoryIds
category.id
}}
{{on "click" (action "toggleCategory" category.id)}}
/>
</div>
{{/each}}
</div>
{{/each}}
{{else}}
<div class="sidebar-categories-form__no-categories">
{{i18n "sidebar.categories_form.no_categories"}}
</div> </div>
{{/if}}
</form>
</DModalBody>
<div class="modal-footer"> {{#if (gt this.filteredCategoriesGroupings.length 0)}}
<DButton {{#each this.filteredCategoriesGroupings as |categories|}}
@class="btn-primary sidebar-categories-form__save-button" <div
@label="sidebar.categories_form.save" class="sidebar-categories-form__row"
@disabled={{this.saving}} style={{html-safe (border-color categories.0.color "left")}}
@action={{this.save}} >
/>
{{#each categories as |category|}}
<div
class="sidebar-categories-form__category-row"
data-category-id={{category.id}}
data-category-level={{category.level}}
>
<label
class="sidebar-categories-form__category-label"
for={{concat "sidebar-categories-form__input--" category.id}}
>
<div class="sidebar-categories-form__category-badge">
{{category-badge category}}
</div>
{{#unless category.parentCategory}}
<div class="sidebar-categories-form__category-description">
{{dir-span category.description_excerpt htmlSafe="true"}}
</div>
{{/unless}}
</label>
<Input
id={{concat "sidebar-categories-form__input--" category.id}}
class="sidebar-categories-form__input"
@type="checkbox"
@checked={{includes
this.selectedSidebarCategoryIds
category.id
}}
{{on "click" (action "toggleCategory" category.id)}}
/>
</div>
{{/each}}
</div>
{{/each}}
{{else}}
<div class="sidebar-categories-form__no-categories">
{{i18n "sidebar.categories_form_modal.no_categories"}}
</div>
{{/if}}
</form>
</DModalBody>
<div class="modal-footer sidebar-categories-form__modal-footer">
<DButton
@class="btn-primary sidebar-categories-form__save-button"
@label="sidebar.categories_form_modal.save"
@disabled={{this.saving}}
@action={{this.save}}
/>
{{#if (gt this.siteSettings.default_navigation_menu_categories.length 0)}}
<DButton
@icon="undo"
@class="btn-flat btn-text sidebar-categories-form__reset-to-defaults-btn"
@label="sidebar.categories_form_modal.reset_to_defaults"
@disabled={{this.saving}}
@action={{this.resetToDefaults}}
/>
{{/if}}
</div>
</div> </div>

View File

@ -8,8 +8,9 @@ import discourseDebounce from "discourse-common/lib/debounce";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
export default class extends Component { export default class extends Component {
@service site;
@service currentUser; @service currentUser;
@service site;
@service siteSettings;
@tracked filter = ""; @tracked filter = "";
@ -85,6 +86,11 @@ export default class extends Component {
this.filter = filter.toLowerCase(); this.filter = filter.toLowerCase();
} }
@action
deselectAll() {
this.selectedSidebarCategoryIds.clear();
}
@action @action
toggleCategory(categoryId) { toggleCategory(categoryId) {
if (this.selectedSidebarCategoryIds.includes(categoryId)) { if (this.selectedSidebarCategoryIds.includes(categoryId)) {
@ -94,6 +100,18 @@ export default class extends Component {
} }
} }
get modalHeaderAfterTitleElement() {
return document.getElementById("modal-header-after-title");
}
@action
resetToDefaults() {
this.selectedSidebarCategoryIds =
this.siteSettings.default_navigation_menu_categories
.split("|")
.map((id) => parseInt(id, 10));
}
@action @action
save() { save() {
this.saving = true; this.saving = true;

View File

@ -134,7 +134,7 @@
} }
&:not(.history-modal) { &:not(.history-modal) {
.modal-body:not(.reorder-categories):not(.poll-ui-builder):not(.poll-breakdown):not(.sidebar-categories-form-modal) { .modal-body:not(.reorder-categories):not(.poll-ui-builder):not(.poll-breakdown):not(.sidebar-categories-form__modal-body) {
max-height: 80vh !important; max-height: 80vh !important;
@media screen and (max-height: 500px) { @media screen and (max-height: 500px) {
max-height: 65vh !important; max-height: 65vh !important;

View File

@ -4,13 +4,43 @@
} }
} }
.sidebar-categories-form__modal-footer.modal-footer {
.sidebar-categories-form__reset-to-defaults-btn {
margin-left: auto;
margin-right: 0em;
.d-icon {
font-size: var(--font-down-1);
color: var(--tertiary);
}
&:focus,
&:active {
background: none;
}
}
}
.sidebar-categories-form__deselect {
margin: 0;
.btn-flat.sidebar-categories-form__deselect-all-btn {
margin-left: auto;
padding: 0;
&:focus,
&:active {
background: none;
}
}
}
.sidebar-categories-form { .sidebar-categories-form {
.sidebar-categories-form__filter { .sidebar-categories-form__filter {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin-right: auto; margin-right: auto;
width: 100%; width: 100%;
margin-bottom: 1em;
position: relative; position: relative;
} }

View File

@ -4389,11 +4389,15 @@ en:
more: "More" more: "More"
all_categories: "All categories" all_categories: "All categories"
all_tags: "All tags" all_tags: "All tags"
categories_form: categories_form_modal:
save: "Save" save: "Save"
title: "Edit categories navigation" title: "Edit categories navigation"
subtitle:
button_text: "Deselect all"
text: "and we'll automatically show this site's most popular categories"
filter_placeholder: "Filter categories" filter_placeholder: "Filter categories"
no_categories: "There are no categories matching the given term." no_categories: "There are no categories matching the given term."
reset_to_defaults: "Reset to defaults"
sections: sections:
custom: custom:

View File

@ -22,7 +22,6 @@ RSpec.describe "Editing sidebar categories navigation", type: :system do
before do before do
SiteSetting.new_edit_sidebar_categories_tags_interface_groups = group.name SiteSetting.new_edit_sidebar_categories_tags_interface_groups = group.name
SiteSetting.default_navigation_menu_categories = "#{category.id}|#{category2.id}"
sign_in(user) sign_in(user)
end end
@ -33,11 +32,12 @@ RSpec.describe "Editing sidebar categories navigation", type: :system do
modal = sidebar.click_edit_categories_button modal = sidebar.click_edit_categories_button
expect(modal).to have_right_title(I18n.t("js.sidebar.categories_form.title")) expect(modal).to have_right_title(I18n.t("js.sidebar.categories_form_modal.title"))
expect(modal).to have_parent_category_color(category) expect(modal).to have_parent_category_color(category)
expect(modal).to have_category_description_excerpt(category) expect(modal).to have_category_description_excerpt(category)
expect(modal).to have_parent_category_color(category2) expect(modal).to have_parent_category_color(category2)
expect(modal).to have_category_description_excerpt(category2) expect(modal).to have_category_description_excerpt(category2)
expect(modal).to have_no_reset_to_defaults_button
expect(modal).to have_categories( expect(modal).to have_categories(
[category, category_subcategory, category_subcategory2, category2, category2_subcategory], [category, category_subcategory, category_subcategory2, category2, category2_subcategory],
@ -71,6 +71,47 @@ RSpec.describe "Editing sidebar categories navigation", type: :system do
expect(sidebar).to have_no_section_link(category2.name) expect(sidebar).to have_no_section_link(category2.name)
end end
it "allows a user to deselect all categories in the modal" do
Fabricate(:category_sidebar_section_link, linkable: category, user: user)
Fabricate(:category_sidebar_section_link, linkable: category_subcategory2, user: user)
visit "/latest"
expect(sidebar).to have_categories_section
modal = sidebar.click_edit_categories_button
modal.deselect_all.save
expect(sidebar).to have_section_link(category.name)
expect(sidebar).to have_no_section_link(category_subcategory2.name)
expect(sidebar).to have_section_link(category2.name)
expect(sidebar).to have_section_link("Uncategorized")
end
it "allows a user to reset to the default navigation menu categories site setting" do
Fabricate(:category_sidebar_section_link, linkable: category, user: user)
Fabricate(:category_sidebar_section_link, linkable: category2, user: user)
SiteSetting.default_navigation_menu_categories =
"#{category_subcategory2.id}|#{category2_subcategory.id}"
visit "/latest"
expect(sidebar).to have_categories_section
expect(sidebar).to have_section_link(category.name)
expect(sidebar).to have_section_link(category2.name)
modal = sidebar.click_edit_categories_button
modal.click_reset_to_defaults_button.save
expect(modal).to be_closed
expect(sidebar).to have_no_section_link(category.name)
expect(sidebar).to have_no_section_link(category2.name)
expect(sidebar).to have_section_link(category_subcategory2.name)
expect(sidebar).to have_section_link(category2_subcategory.name)
end
it "allows a user to filter the categories in the modal by the category's name" do it "allows a user to filter the categories in the modal by the category's name" do
visit "/latest" visit "/latest"
@ -127,7 +168,7 @@ RSpec.describe "Editing sidebar categories navigation", type: :system do
modal = sidebar.click_edit_categories_button modal = sidebar.click_edit_categories_button
expect(modal).to have_right_title(I18n.t("js.sidebar.categories_form.title")) expect(modal).to have_right_title(I18n.t("js.sidebar.categories_form_modal.title"))
modal modal
.toggle_category_checkbox(category_subcategory_subcategory) .toggle_category_checkbox(category_subcategory_subcategory)

View File

@ -29,7 +29,7 @@ module PageObjects
has_no_css?(".sidebar-categories-form-modal .sidebar-categories-form__category-row") && has_no_css?(".sidebar-categories-form-modal .sidebar-categories-form__category-row") &&
has_css?( has_css?(
".sidebar-categories-form-modal .sidebar-categories-form__no-categories", ".sidebar-categories-form-modal .sidebar-categories-form__no-categories",
text: I18n.t("js.sidebar.categories_form.no_categories"), text: I18n.t("js.sidebar.categories_form_modal.no_categories"),
) )
end end
@ -65,6 +65,20 @@ module PageObjects
self self
end end
def deselect_all
click_button(I18n.t("js.sidebar.categories_form_modal.subtitle.button_text"))
self
end
def click_reset_to_defaults_button
click_button(I18n.t("js.sidebar.categories_form_modal.reset_to_defaults"))
self
end
def has_no_reset_to_defaults_button?
has_no_button?(I18n.t("js.sidebar.categories_form_modal.reset_to_defaults"))
end
end end
end end
end end