FEATURE: Add modal for editing tags in navigation menu (#22214)
What does this change do? This change is a first pass for adding a modal used to edit tags that appears in the navigation menu. As the feature is being worked on in phases, it is currently hidden behind the `new_edit_sidebar_categories_tags_interface_groups` site setting. The following features will be worked on in future commits: 1. Input filter to filter through the tgas 2. Button to reset tag selection to default navigation menu tags site settings 3. Button to deselect all current selection
This commit is contained in:
parent
1987fce018
commit
08d8bd9f43
|
@ -84,7 +84,7 @@
|
|||
<div class="modal-footer sidebar-categories-form__modal-footer">
|
||||
<DButton
|
||||
@class="btn-primary sidebar-categories-form__save-button"
|
||||
@label="sidebar.categories_form_modal.save"
|
||||
@label="save"
|
||||
@disabled={{this.saving}}
|
||||
@action={{this.save}}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<DModalBody
|
||||
@title="sidebar.tags_form_modal.title"
|
||||
@class="sidebar-tags-form-modal"
|
||||
>
|
||||
<form class="sidebar-tags-form">
|
||||
{{#each this.tags as |tag|}}
|
||||
<div class="sidebar-tags-form__tag" data-tag-name={{tag.name}}>
|
||||
<Input
|
||||
id={{concat "sidebar-tags-form__input--" tag.name}}
|
||||
class="sidebar-tags-form__input"
|
||||
@type="checkbox"
|
||||
@checked={{includes this.selectedTags tag.name}}
|
||||
{{on "click" (action "toggleTag" tag.name)}}
|
||||
/>
|
||||
|
||||
<label
|
||||
class="sidebar-tags-form__tag-label"
|
||||
for={{concat "sidebar-tags-form__input--" tag.name}}
|
||||
>
|
||||
<p>
|
||||
<span class="sidebar-tags-form__tag-label-name">
|
||||
{{tag.name}}
|
||||
</span>
|
||||
|
||||
<span class="sidebar-tags-form__tag-label-count">
|
||||
({{tag.count}})
|
||||
</span>
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
{{/each}}
|
||||
</form>
|
||||
</DModalBody>
|
||||
|
||||
<div class="modal-footer">
|
||||
<DButton
|
||||
@class="btn-primary sidebar-tags-form__save-button"
|
||||
@label="save"
|
||||
@disabled={{this.saving}}
|
||||
@action={{this.save}}
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,63 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default class extends Component {
|
||||
@service currentUser;
|
||||
@service store;
|
||||
@tracked tags = [];
|
||||
@tracked selectedTags = [...this.currentUser.sidebarTagNames];
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.#loadTags();
|
||||
}
|
||||
|
||||
async #loadTags() {
|
||||
// This is loading all tags upfront and there is no pagination for it. However, this is what we are doing for the
|
||||
// `/tags` route as well so we have decided to kick this can of worms down the road for now.
|
||||
await this.store
|
||||
.findAll("tag")
|
||||
.then((tags) => {
|
||||
this.tags = tags.content.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
popupAjaxError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
toggleTag(tag) {
|
||||
if (this.selectedTags.includes(tag)) {
|
||||
this.selectedTags.removeObject(tag);
|
||||
} else {
|
||||
this.selectedTags.addObject(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
save() {
|
||||
this.saving = true;
|
||||
const initialSidebarTags = this.currentUser.sidebar_tags;
|
||||
this.currentUser.set("sidebar_tag_names", this.selectedTags);
|
||||
|
||||
this.currentUser
|
||||
.save(["sidebar_tag_names"])
|
||||
.then((result) => {
|
||||
this.currentUser.set("sidebar_tags", result.user.sidebar_tags);
|
||||
this.args.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.currentUser.set("sidebar_tags", initialSidebarTags);
|
||||
popupAjaxError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving = false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import SidebarCommonTagsSection from "discourse/components/sidebar/common/tags-s
|
|||
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
|
||||
import PMTagSectionLink from "discourse/lib/sidebar/user/tags-section/pm-tag-section-link";
|
||||
import { hasDefaultSidebarTags } from "discourse/lib/sidebar/helpers";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default class SidebarUserTagsSection extends SidebarCommonTagsSection {
|
||||
@service router;
|
||||
|
@ -79,6 +80,12 @@ export default class SidebarUserTagsSection extends SidebarCommonTagsSection {
|
|||
|
||||
@action
|
||||
editTracked() {
|
||||
this.router.transitionTo("preferences.navigation-menu", this.currentUser);
|
||||
if (
|
||||
this.currentUser.new_edit_sidebar_categories_tags_interface_groups_enabled
|
||||
) {
|
||||
showModal("sidebar-tags-form");
|
||||
} else {
|
||||
this.router.transitionTo("preferences.navigation-menu", this.currentUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import Controller from "@ember/controller";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
|
||||
export default class SidebarTagsForm extends Controller.extend(
|
||||
ModalFunctionality
|
||||
) {}
|
|
@ -0,0 +1 @@
|
|||
<Sidebar::TagsFormModal @closeModal={{(action "closeModal")}} />
|
|
@ -134,7 +134,7 @@
|
|||
}
|
||||
|
||||
&:not(.history-modal) {
|
||||
.modal-body:not(.reorder-categories):not(.poll-ui-builder):not(.poll-breakdown):not(.sidebar-categories-form__modal-body) {
|
||||
.modal-body:not(.reorder-categories):not(.poll-ui-builder):not(.poll-breakdown):not(.sidebar-categories-form-modal):not(.sidebar-tags-form-modal) {
|
||||
max-height: 80vh !important;
|
||||
@media screen and (max-height: 500px) {
|
||||
max-height: 65vh !important;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
@import "share-and-invite-modal";
|
||||
@import "download-calendar";
|
||||
@import "sidebar-categories-form";
|
||||
@import "sidebar-tags-form";
|
||||
@import "svg";
|
||||
@import "tap-tile";
|
||||
@import "time-input";
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
.sidebar-tags-form-modal {
|
||||
.modal-body {
|
||||
min-height: 30vh;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-tags-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.sidebar-tags-form__tag {
|
||||
flex-basis: 30%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5em 0.25em;
|
||||
|
||||
.sidebar-tags-form__tag-label {
|
||||
margin-bottom: 0;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-tags-form__input {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-tags-form__tag-label-count {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "sidebar-categories-form";
|
||||
@import "sidebar-tags-form";
|
||||
@import "user-card";
|
||||
@import "user-info";
|
||||
@import "user-stream-item";
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
.sidebar-tags-form-modal {
|
||||
.modal-inner-container {
|
||||
min-width: var(--modal-max-width);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "sidebar-categories-form";
|
||||
@import "sidebar-tags-form";
|
||||
@import "topic-footer-mobile-dropdown";
|
||||
@import "user-card";
|
||||
@import "user-stream-item";
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
.sidebar-tags-form {
|
||||
.sidebar-tags-form__tag {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
|
@ -4390,7 +4390,6 @@ en:
|
|||
all_categories: "All categories"
|
||||
all_tags: "All tags"
|
||||
categories_form_modal:
|
||||
save: "Save"
|
||||
title: "Edit categories navigation"
|
||||
subtitle:
|
||||
button_text: "Deselect all"
|
||||
|
@ -4398,6 +4397,8 @@ en:
|
|||
filter_placeholder: "Filter categories"
|
||||
no_categories: "There are no categories matching the given term."
|
||||
reset_to_defaults: "Reset to defaults"
|
||||
tags_form_modal:
|
||||
title: "Edit tags navigation"
|
||||
|
||||
sections:
|
||||
custom:
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Editing sidebar tags navigation", type: :system do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:group) { Fabricate(:group).tap { |g| g.add(user) } }
|
||||
fab!(:tag) { Fabricate(:tag, name: "tag", public_topic_count: 1, staff_topic_count: 1) }
|
||||
fab!(:tag2) { Fabricate(:tag, name: "tag2", public_topic_count: 2, staff_topic_count: 2) }
|
||||
fab!(:tag3) { Fabricate(:tag, name: "tag3", public_topic_count: 3, staff_topic_count: 3) }
|
||||
|
||||
let(:sidebar) { PageObjects::Components::Sidebar.new }
|
||||
|
||||
before do
|
||||
SiteSetting.new_edit_sidebar_categories_tags_interface_groups = group.name
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it "allows a user to edit the sidebar categories navigation" do
|
||||
visit "/latest"
|
||||
|
||||
expect(sidebar).to have_tags_section
|
||||
expect(sidebar).to have_no_section_link(tag.name)
|
||||
expect(sidebar).to have_no_section_link(tag2.name)
|
||||
expect(sidebar).to have_no_section_link(tag3.name)
|
||||
|
||||
modal = sidebar.click_edit_tags_button
|
||||
|
||||
expect(modal).to have_right_title(I18n.t("js.sidebar.tags_form_modal.title"))
|
||||
expect(modal).to have_tag_checkboxes([tag, tag2, tag3])
|
||||
|
||||
modal.toggle_tag_checkbox(tag).toggle_tag_checkbox(tag2).save
|
||||
|
||||
expect(modal).to be_closed
|
||||
expect(sidebar).to have_section_link(tag.name)
|
||||
expect(sidebar).to have_section_link(tag2.name)
|
||||
expect(sidebar).to have_no_section_link(tag3.name)
|
||||
|
||||
visit "/latest"
|
||||
|
||||
expect(sidebar).to have_section_link(tag.name)
|
||||
expect(sidebar).to have_section_link(tag2.name)
|
||||
expect(sidebar).to have_no_section_link(tag3.name)
|
||||
|
||||
modal = sidebar.click_edit_tags_button
|
||||
modal.toggle_tag_checkbox(tag2).save
|
||||
|
||||
expect(modal).to be_closed
|
||||
|
||||
expect(sidebar).to have_section_link(tag.name)
|
||||
expect(sidebar).to have_no_section_link(tag2.name)
|
||||
expect(sidebar).to have_no_section_link(tag3.name)
|
||||
end
|
||||
end
|
|
@ -31,6 +31,14 @@ module PageObjects
|
|||
PageObjects::Modals::SidebarEditCategories.new
|
||||
end
|
||||
|
||||
def click_edit_tags_button
|
||||
within(".sidebar-section[data-section-name='tags']") do
|
||||
click_button(class: "sidebar-section-header-button", visible: false)
|
||||
end
|
||||
|
||||
PageObjects::Modals::SidebarEditTags.new
|
||||
end
|
||||
|
||||
def edit_custom_section(name)
|
||||
find(".sidebar-section[data-section-name='#{name.parameterize}']").hover
|
||||
|
||||
|
@ -71,6 +79,10 @@ module PageObjects
|
|||
has_section?("Categories")
|
||||
end
|
||||
|
||||
def has_tags_section?
|
||||
has_section?("Tags")
|
||||
end
|
||||
|
||||
def has_no_section?(name)
|
||||
find(SIDEBAR_WRAPPER_SELECTOR).has_no_button?(name)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Modals
|
||||
class SidebarEditTags < PageObjects::Modals::Base
|
||||
def closed?
|
||||
has_no_css?(".sidebar-tags-form-modal")
|
||||
end
|
||||
|
||||
def has_right_title?(title)
|
||||
has_css?(".sidebar-tags-form-modal #discourse-modal-title", text: title)
|
||||
end
|
||||
|
||||
def has_tag_checkboxes?(tags)
|
||||
tag_names = tags.map(&:name)
|
||||
|
||||
has_css?(".sidebar-tags-form-modal .sidebar-tags-form__tag", count: tag_names.length) &&
|
||||
all(".sidebar-tags-form-modal .sidebar-tags-form__tag").all? do |row|
|
||||
tag_names.include?(row["data-tag-name"].to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_tag_checkbox(tag)
|
||||
find(
|
||||
".sidebar-tags-form-modal .sidebar-tags-form__tag[data-tag-name='#{tag.name}'] .sidebar-tags-form__input",
|
||||
).click
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def save
|
||||
find(".sidebar-tags-form-modal .sidebar-tags-form__save-button").click
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue