From 6f5d8cad5105b0827ca44fd6298800384e6f40f5 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Fri, 23 Oct 2020 12:49:02 -0400 Subject: [PATCH] UX: Move category editing/creation to its own page (#10973) * Move new/edit category modals to its own page * Fix JS tests * Minor fixes to new-category UI * Add mobile toggle * Use global pretender endpoint so plugins can benefit too * Alignment fix * Minor review fixes * Styling refactor * Move some SCSS out of the modal --- .../app/components/edit-category-general.js | 10 +- .../app/components/edit-category-panel.js | 2 +- .../app/controllers/edit-category.js | 97 +++++----- .../discourse/app/routes/app-route-map.js | 4 + .../discourse/app/routes/application.js | 10 +- .../app/routes/discovery-categories.js | 21 +-- .../app/routes/discovery-edit-category.js | 28 +++ .../routes/discovery-edit-child-category.js | 30 ++++ .../discourse/app/routes/new-category.js | 32 ++++ .../components/edit-category-tags.hbs | 2 +- .../discourse/app/templates/edit-category.hbs | 60 +++++++ .../app/templates/modal/edit-category.hbs | 42 ----- .../tests/acceptance/category-edit-test.js | 39 ++--- .../tests/acceptance/category-new-test.js | 34 ++++ .../discourse/tests/fixtures/site-fixtures.js | 1 + .../tests/helpers/create-pretender.js | 16 ++ .../stylesheets/common/admin/admin_base.scss | 11 +- .../common/base/edit-category.scss | 165 ++++++++++++++++++ app/assets/stylesheets/common/base/modal.scss | 148 ---------------- .../stylesheets/desktop/edit-category.scss | 11 ++ app/assets/stylesheets/desktop/modal.scss | 22 --- .../stylesheets/mobile/edit-category.scss | 49 ++++++ app/assets/stylesheets/mobile/modal.scss | 16 -- config/locales/client.en.yml | 3 +- config/routes.rb | 3 + 25 files changed, 505 insertions(+), 351 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/routes/discovery-edit-category.js create mode 100644 app/assets/javascripts/discourse/app/routes/discovery-edit-child-category.js create mode 100644 app/assets/javascripts/discourse/app/routes/new-category.js create mode 100644 app/assets/javascripts/discourse/app/templates/edit-category.hbs delete mode 100644 app/assets/javascripts/discourse/app/templates/modal/edit-category.hbs create mode 100644 app/assets/javascripts/discourse/tests/acceptance/category-new-test.js create mode 100644 app/assets/stylesheets/common/base/edit-category.scss create mode 100644 app/assets/stylesheets/desktop/edit-category.scss create mode 100644 app/assets/stylesheets/mobile/edit-category.scss diff --git a/app/assets/javascripts/discourse/app/components/edit-category-general.js b/app/assets/javascripts/discourse/app/components/edit-category-general.js index a8ada2621842a..7a6c85d776072 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-general.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-general.js @@ -106,9 +106,13 @@ export default buildCategoryPanel("general", { return Category.list().filterBy("parent_category_id", categoryId); }, - @discourseComputed("category.isUncategorizedCategory", "category.id") - showDescription(isUncategorizedCategory, categoryId) { - return !isUncategorizedCategory && categoryId; + @discourseComputed( + "category.isUncategorizedCategory", + "category.id", + "category.topic_url" + ) + showDescription(isUncategorizedCategory, categoryId, topicUrl) { + return !isUncategorizedCategory && categoryId && topicUrl; }, @action diff --git a/app/assets/javascripts/discourse/app/components/edit-category-panel.js b/app/assets/javascripts/discourse/app/components/edit-category-panel.js index f22ee5576af4c..02f7a28424e12 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-panel.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-panel.js @@ -9,7 +9,7 @@ export function buildCategoryPanel(tab, extras) { { activeTab: equal("selectedTab", tab), classNameBindings: [ - ":modal-tab", + ":edit-category-tab", "activeTab::hide", `:edit-category-tab-${tab}`, ], diff --git a/app/assets/javascripts/discourse/app/controllers/edit-category.js b/app/assets/javascripts/discourse/app/controllers/edit-category.js index 5be7ee4f3c006..8970521b40230 100644 --- a/app/assets/javascripts/discourse/app/controllers/edit-category.js +++ b/app/assets/javascripts/discourse/app/controllers/edit-category.js @@ -1,22 +1,20 @@ import I18n from "I18n"; -import { isEmpty } from "@ember/utils"; import Controller from "@ember/controller"; -import ModalFunctionality from "discourse/mixins/modal-functionality"; -import DiscourseURL from "discourse/lib/url"; -import { extractError } from "discourse/lib/ajax-error"; -import discourseComputed, { - on, - observes, -} from "discourse-common/utils/decorators"; -import Category from "discourse/models/category"; +import discourseComputed, { on } from "discourse-common/utils/decorators"; import bootbox from "bootbox"; +import { extractError } from "discourse/lib/ajax-error"; +import DiscourseURL from "discourse/lib/url"; +import { readOnly } from "@ember/object/computed"; -export default Controller.extend(ModalFunctionality, { - selectedTab: null, +export default Controller.extend({ + selectedTab: "general", saving: false, deleting: false, panels: null, hiddenTooltip: true, + createdCategory: false, + expandedMenu: false, + mobileView: readOnly("site.mobileView"), @on("init") _initPanels() { @@ -26,36 +24,6 @@ export default Controller.extend(ModalFunctionality, { }); }, - onShow() { - this.changeSize(); - this.titleChanged(); - this.set("hiddenTooltip", true); - }, - - @observes("model.description") - changeSize() { - if (!isEmpty(this.get("model.description"))) { - this.set("modal.modalClass", "edit-category-modal full"); - } else { - this.set("modal.modalClass", "edit-category-modal small"); - } - }, - - @discourseComputed("model.{id,name}") - title(model) { - if (model.id) { - return I18n.t("category.edit_dialog_title", { - categoryName: model.name, - }); - } - return I18n.t("category.create"); - }, - - @observes("title") - titleChanged() { - this.set("modal.title", this.title); - }, - @discourseComputed("saving", "model.name", "model.color", "deleting") disabled(saving, name, color, deleting) { if (saving || deleting) { @@ -89,6 +57,15 @@ export default Controller.extend(ModalFunctionality, { return id ? "category.save" : "category.create"; }, + @discourseComputed("model.id", "model.name") + title(id, name) { + return id + ? I18n.t("category.edit_dialog_title", { + categoryName: name, + }) + : I18n.t("category.create"); + }, + actions: { registerValidator(validator) { this.validators.push(validator); @@ -111,23 +88,22 @@ export default Controller.extend(ModalFunctionality, { .save() .then((result) => { this.set("saving", false); - this.send("closeModal"); - model.setProperties({ - slug: result.category.slug, - id: result.category.id, - }); - DiscourseURL.redirectTo(`/c/${Category.slugFor(model)}/${model.id}`); + if (!model.id) { + model.setProperties({ + slug: result.category.slug, + id: result.category.id, + createdCategory: true, + }); + } }) .catch((error) => { - this.flash(extractError(error), "error"); + bootbox.alert(extractError(error)); this.set("saving", false); }); }, deleteCategory() { this.set("deleting", true); - - this.send("hideModal"); bootbox.confirm( I18n.t("category.delete_confirm"), I18n.t("no_value"), @@ -136,19 +112,14 @@ export default Controller.extend(ModalFunctionality, { if (result) { this.model.destroy().then( () => { - // success - this.send("closeModal"); - DiscourseURL.redirectTo("/categories"); + this.transitionToRoute("discovery.categories"); }, - (error) => { - this.flash(extractError(error), "error"); - this.send("reopenModal"); + () => { this.displayErrors([I18n.t("category.delete_error")]); this.set("deleting", false); } ); } else { - this.send("reopenModal"); this.set("deleting", false); } } @@ -158,5 +129,17 @@ export default Controller.extend(ModalFunctionality, { toggleDeleteTooltip() { this.toggleProperty("hiddenTooltip"); }, + + goBack() { + if (this.model.createdCategory) { + DiscourseURL.redirectTo(this.model.url); + } else { + DiscourseURL.routeTo(this.model.url); + } + }, + + toggleMenu() { + this.toggleProperty("expandedMenu"); + }, }, }); diff --git a/app/assets/javascripts/discourse/app/routes/app-route-map.js b/app/assets/javascripts/discourse/app/routes/app-route-map.js index 3d836cfa69531..81b8dec96bac1 100644 --- a/app/assets/javascripts/discourse/app/routes/app-route-map.js +++ b/app/assets/javascripts/discourse/app/routes/app-route-map.js @@ -21,6 +21,8 @@ export default function () { this.route("topicBySlugOrId", { path: "/t/:slugOrId", resetNamespace: true }); + this.route("newCategory", { path: "/new-category" }); + this.route("discovery", { path: "/", resetNamespace: true }, function () { // legacy route this.route("topParentCategory", { path: "/c/:slug/l/top" }); @@ -63,6 +65,8 @@ export default function () { }); this.route("categories"); + this.route("editCategory", { path: "/c/:slug/edit" }); + this.route("editChildCategory", { path: "/c/:parentSlug/:slug/edit" }); // legacy routes this.route("parentCategory", { path: "/c/:slug" }); diff --git a/app/assets/javascripts/discourse/app/routes/application.js b/app/assets/javascripts/discourse/app/routes/application.js index 9658275ff2fb0..fdbe5d2814aed 100644 --- a/app/assets/javascripts/discourse/app/routes/application.js +++ b/app/assets/javascripts/discourse/app/routes/application.js @@ -10,7 +10,7 @@ import Category from "discourse/models/category"; import mobile from "discourse/lib/mobile"; import { findAll } from "discourse/models/login-method"; import { getOwner } from "discourse-common/lib/get-owner"; -import { userPath } from "discourse/lib/url"; +import DiscourseURL, { userPath } from "discourse/lib/url"; import Composer from "discourse/models/composer"; import { inject as service } from "@ember/service"; import bootbox from "bootbox"; @@ -203,13 +203,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, { }, editCategory(category) { - Category.reloadById(category.get("id")).then((atts) => { - const model = this.store.createRecord("category", atts.category); - model.setupGroupsAndPermissions(); - this.site.updateCategory(model); - showModal("edit-category", { model }); - this.controllerFor("edit-category").set("selectedTab", "general"); - }); + DiscourseURL.routeTo(`/c/${Category.slugFor(category)}/edit`); }, checkEmail(user) { diff --git a/app/assets/javascripts/discourse/app/routes/discovery-categories.js b/app/assets/javascripts/discourse/app/routes/discovery-categories.js index 4c2f89474423c..606664ef303e2 100644 --- a/app/assets/javascripts/discourse/app/routes/discovery-categories.js +++ b/app/assets/javascripts/discourse/app/routes/discovery-categories.js @@ -9,7 +9,6 @@ import { defaultHomepage } from "discourse/lib/utilities"; import TopicList from "discourse/models/topic-list"; import { ajax } from "discourse/lib/ajax"; import PreloadStore from "discourse/lib/preload-store"; -import { SEARCH_PRIORITIES } from "discourse/lib/constants"; import { hash } from "rsvp"; import Site from "discourse/models/site"; @@ -111,7 +110,7 @@ const DiscoveryCategoriesRoute = DiscourseRoute.extend(OpenComposer, { }, createCategory() { - openNewCategoryModal(this); + this.transitionTo("newCategory"); }, reorderCategories() { @@ -134,22 +133,4 @@ const DiscoveryCategoriesRoute = DiscourseRoute.extend(OpenComposer, { }, }); -export function openNewCategoryModal(context) { - const groups = context.site.groups, - everyoneName = groups.findBy("id", 0).name; - - const model = context.store.createRecord("category", { - color: "0088CC", - text_color: "FFFFFF", - group_permissions: [{ group_name: everyoneName, permission_type: 1 }], - available_groups: groups.map((g) => g.name), - allow_badges: true, - topic_featured_link_allowed: true, - custom_fields: {}, - search_priority: SEARCH_PRIORITIES.normal, - }); - - showModal("edit-category", { model }).set("selectedTab", "general"); -} - export default DiscoveryCategoriesRoute; diff --git a/app/assets/javascripts/discourse/app/routes/discovery-edit-category.js b/app/assets/javascripts/discourse/app/routes/discovery-edit-category.js new file mode 100644 index 0000000000000..a30a22713acbe --- /dev/null +++ b/app/assets/javascripts/discourse/app/routes/discovery-edit-category.js @@ -0,0 +1,28 @@ +import I18n from "I18n"; +import DiscourseRoute from "discourse/routes/discourse"; +import Category from "discourse/models/category"; + +export default DiscourseRoute.extend({ + model(params) { + return Category.reloadBySlugPath(params.slug).then((result) => { + const record = this.store.createRecord("category", result.category); + record.setupGroupsAndPermissions(); + this.site.updateCategory(record); + return record; + }); + }, + + titleToken() { + return I18n.t("category.edit_dialog_title", { + categoryName: this.currentModel.name, + }); + }, + + renderTemplate() { + this.render("edit-category", { + controller: "edit-category", + outlet: "list-container", + model: this.currentModel, + }); + }, +}); diff --git a/app/assets/javascripts/discourse/app/routes/discovery-edit-child-category.js b/app/assets/javascripts/discourse/app/routes/discovery-edit-child-category.js new file mode 100644 index 0000000000000..ae147729f5f94 --- /dev/null +++ b/app/assets/javascripts/discourse/app/routes/discovery-edit-child-category.js @@ -0,0 +1,30 @@ +import I18n from "I18n"; +import DiscourseRoute from "discourse/routes/discourse"; +import Category from "discourse/models/category"; + +export default DiscourseRoute.extend({ + model(params) { + return Category.reloadBySlug(params.slug, params.parentSlug).then( + (result) => { + const record = this.store.createRecord("category", result.category); + record.setupGroupsAndPermissions(); + this.site.updateCategory(record); + return record; + } + ); + }, + + titleToken() { + return I18n.t("category.edit_dialog_title", { + categoryName: this.currentModel.name, + }); + }, + + renderTemplate() { + this.render("edit-category", { + controller: "edit-category", + outlet: "list-container", + model: this.currentModel, + }); + }, +}); diff --git a/app/assets/javascripts/discourse/app/routes/new-category.js b/app/assets/javascripts/discourse/app/routes/new-category.js new file mode 100644 index 0000000000000..e4aad400ed31f --- /dev/null +++ b/app/assets/javascripts/discourse/app/routes/new-category.js @@ -0,0 +1,32 @@ +import I18n from "I18n"; +import DiscourseRoute from "discourse/routes/discourse"; +import { SEARCH_PRIORITIES } from "discourse/lib/constants"; + +export default DiscourseRoute.extend({ + model() { + const groups = this.site.groups, + everyoneName = groups.findBy("id", 0).name; + + return this.store.createRecord("category", { + color: "0088CC", + text_color: "FFFFFF", + group_permissions: [{ group_name: everyoneName, permission_type: 1 }], + available_groups: groups.map((g) => g.name), + allow_badges: true, + topic_featured_link_allowed: true, + custom_fields: {}, + search_priority: SEARCH_PRIORITIES.normal, + }); + }, + + titleToken() { + return I18n.t("category.create"); + }, + + renderTemplate() { + this.render("edit-category", { + controller: "edit-category", + model: this.currentModel, + }); + }, +}); diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-tags.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-tags.hbs index 6fcb4bded0bc3..3eb4b61c501f1 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-tags.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-tags.hbs @@ -19,7 +19,7 @@ id="category-allowed-tag-groups" tagGroups=category.allowed_tag_groups }} - {{#link-to "tagGroups"}}{{i18n "category.manage_tag_groups_link"}}{{/link-to}} + {{#link-to "tagGroups" class="manage-tag-groups"}}{{i18n "category.manage_tag_groups_link"}}{{/link-to}}
diff --git a/app/assets/javascripts/discourse/app/templates/edit-category.hbs b/app/assets/javascripts/discourse/app/templates/edit-category.hbs new file mode 100644 index 0000000000000..ff0ed489e31dd --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/edit-category.hbs @@ -0,0 +1,60 @@ +
+ + +
+

{{title}}

+ {{#if mobileView}} + {{d-button action=(action "toggleMenu") class="menu-toggle" icon="bars"}} + {{else}} + {{#if model.id}} + {{d-button + class="category-back" + action=(action "goBack") + label="category.back" + icon="caret-left" + }} + {{/if}} + {{/if}} +
+ + {{#each panels as |tab|}} + {{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}} + {{/each}} + + +
diff --git a/app/assets/javascripts/discourse/app/templates/modal/edit-category.hbs b/app/assets/javascripts/discourse/app/templates/modal/edit-category.hbs deleted file mode 100644 index 927edcee0e00e..0000000000000 --- a/app/assets/javascripts/discourse/app/templates/modal/edit-category.hbs +++ /dev/null @@ -1,42 +0,0 @@ -
- - - {{#d-modal-body}} - {{#each panels as |tab|}} - {{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}} - {{/each}} - {{/d-modal-body}} - - -
diff --git a/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js b/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js index 24a172e7d088a..955d769f0898f 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js @@ -1,31 +1,21 @@ import { visit } from "@ember/test-helpers"; import { test } from "qunit"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -import DiscourseURL from "discourse/lib/url"; import { acceptance } from "discourse/tests/helpers/qunit-helpers"; acceptance("Category Edit", function (needs) { needs.user(); needs.settings({ email_in: true }); - test("Can open the category modal", async (assert) => { - await visit("/c/bug"); - - await click(".edit-category"); - assert.ok(visible(".d-modal"), "it pops up a modal"); - - await click("button.modal-close"); - assert.ok(!visible(".d-modal"), "it closes the modal"); - }); - test("Editing the category", async (assert) => { await visit("/c/bug"); - await click(".edit-category"); + await click("button.edit-category"); + assert.equal(currentURL(), "/c/bug/edit", "it jumps to the correct screen"); - assert.equal(find(".d-modal .badge-category").text(), "bug"); + assert.equal(find(".badge-category").text(), "bug"); await fillIn("input.category-name", "testing"); - assert.equal(find(".d-modal .badge-category").text(), "testing"); + assert.equal(find(".badge-category").text(), "testing"); await fillIn("#edit-text-color", "#ff0000"); @@ -38,24 +28,21 @@ acceptance("Category Edit", function (needs) { await searchPriorityChooser.selectRowByValue(1); await click("#save-category"); - - assert.ok(!visible(".d-modal"), "it closes the modal"); - assert.equal( - DiscourseURL.redirectedTo, - "/c/bug/1", - "it does one of the rare full page redirects" - ); + assert.equal(currentURL(), "/c/bug/edit", "it stays on the edit screen"); }); test("Error Saving", async (assert) => { await visit("/c/bug"); - - await click(".edit-category"); + await click("button.edit-category"); await click(".edit-category-settings"); await fillIn(".email-in", "duplicate@example.com"); await click("#save-category"); - assert.ok(visible("#modal-alert")); - assert.equal(find("#modal-alert").html(), "duplicate email"); + + assert.ok(visible(".bootbox")); + assert.equal(find(".bootbox .modal-body").html(), "duplicate email"); + + await click(".bootbox .btn-primary"); + assert.ok(!visible(".bootbox")); }); test("Subcategory list settings", async (assert) => { @@ -64,7 +51,7 @@ acceptance("Category Edit", function (needs) { ); await visit("/c/bug"); - await click(".edit-category"); + await click("button.edit-category"); await click(".edit-category-settings a"); assert.ok( diff --git a/app/assets/javascripts/discourse/tests/acceptance/category-new-test.js b/app/assets/javascripts/discourse/tests/acceptance/category-new-test.js new file mode 100644 index 0000000000000..be553000d7dd0 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/acceptance/category-new-test.js @@ -0,0 +1,34 @@ +import { visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import { acceptance } from "discourse/tests/helpers/qunit-helpers"; +import I18n from "I18n"; +import DiscourseURL from "discourse/lib/url"; + +acceptance("Category New", function (needs) { + needs.user(); + + test("Creating a new category", async (assert) => { + await visit("/new-category"); + assert.ok(find(".badge-category")); + + await fillIn("input.category-name", "testing"); + assert.equal(find(".badge-category").text(), "testing"); + + await click("#save-category"); + + assert.equal( + find(".edit-category-title h2").text(), + I18n.t("category.edit_dialog_title", { + categoryName: "testing", + }) + ); + + await click(".category-back"); + + assert.equal( + DiscourseURL.redirectedTo, + "/c/testing/11", + "it full page redirects after a newly created category" + ); + }); +}); diff --git a/app/assets/javascripts/discourse/tests/fixtures/site-fixtures.js b/app/assets/javascripts/discourse/tests/fixtures/site-fixtures.js index c4ea4fd17d424..84746aa976cba 100644 --- a/app/assets/javascripts/discourse/tests/fixtures/site-fixtures.js +++ b/app/assets/javascripts/discourse/tests/fixtures/site-fixtures.js @@ -14,6 +14,7 @@ export default { whisper: 4, }, groups: [ + { id: 0, name: "everyone" }, { id: 1, name: "admins" }, { id: 2, name: "moderators" }, { id: 3, name: "staff" }, diff --git a/app/assets/javascripts/discourse/tests/helpers/create-pretender.js b/app/assets/javascripts/discourse/tests/helpers/create-pretender.js index 4ac5e00795e3a..9820ad13bf6f3 100644 --- a/app/assets/javascripts/discourse/tests/helpers/create-pretender.js +++ b/app/assets/javascripts/discourse/tests/helpers/create-pretender.js @@ -313,6 +313,10 @@ export function applyDefaultHandlers(pretender) { response(fixturesByUrl["/categories_and_latest.json"]) ); + pretender.get("/c/bug/find_by_slug.json", () => + response(fixturesByUrl["/c/1/show.json"]) + ); + pretender.put("/categories/:category_id", (request) => { const category = parsePostData(request.requestBody); category.id = parseInt(request.params.category_id, 10); @@ -324,6 +328,18 @@ export function applyDefaultHandlers(pretender) { return response({ category }); }); + pretender.post("/categories", () => { + return response({ + category: { + id: 11, + name: "testing", + color: "0088CC", + text_color: "FFFFFF", + slug: "testing", + }, + }); + }); + pretender.get("/draft.json", (request) => { if (request.queryParams.draft_key === "new_topic") { return response(fixturesByUrl["/draft.json"]); diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 7b264f002c436..bb6f74f0a43c0 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -78,14 +78,13 @@ $mobile-breakpoint: 700px; } } -.nav-stacked { - @media screen and (max-width: 700px) { - margin: 0 15px 0 -10px; - } -} - .admin-contents { position: relative; + .nav-stacked { + @media screen and (max-width: 700px) { + margin: 0 15px 0 -10px; + } + } } .admin-contents table { diff --git a/app/assets/stylesheets/common/base/edit-category.scss b/app/assets/stylesheets/common/base/edit-category.scss new file mode 100644 index 0000000000000..563f48d4a619a --- /dev/null +++ b/app/assets/stylesheets/common/base/edit-category.scss @@ -0,0 +1,165 @@ +$category-settings-width: unquote("min(500px, 90%)"); +$number-input-width: 75px; + +div.edit-category { + display: grid; + grid-template-columns: 1fr 5fr; + grid-template-rows: auto auto auto; + grid-row-gap: 1em; + grid-column-gap: 1.5em; + grid-template-areas: "sidebar header" "sidebar content" "sidebar footer"; + + .edit-category-title { + grid-area: header; + display: flex; + justify-content: space-between; + align-self: start; + } + + .nav-stacked { + grid-area: sidebar; + grid-row: 1 / span 3; + background: transparent; + } + + #list-area & h2 { + margin: 0; + } + + section.field { + margin-bottom: 1em; + } + + .edit-category-tab-general { + .category-chooser { + width: unquote("min(340px, 90%)"); + } + } + + .edit-category-tab-security { + .permission-selector { + margin-right: 0.25em; + } + + .pending-permission-change-alert { + margin-left: auto; + max-width: 250px; + background: var(--primary-very-high); + color: var(--secondary); + margin-top: 10px; + padding: 5px 10px; + position: relative; + .arrow-div { + border: solid transparent; + content: " "; + position: absolute; + border-bottom-color: var(--primary-very-high); + border-width: 7px; + top: -13px; + left: 200px; + } + } + + .add-permission { + position: relative; + top: 0.1em; + } + + .permission-list { + list-style: none; + margin: 0 0 30px; + padding: 0; + .name { + margin-right: 20px; + display: inline-block; + min-width: 100px; + } + .permission { + margin-left: 20px; + } + .d-icon-times-circle { + margin-left: 5px; + color: var(--danger); + } + li { + margin-bottom: 10px; + } + } + } + + .edit-category-tab-settings { + > section { + margin-bottom: 1.5em; + } + + input[type="text"], + .select-kit { + width: $category-settings-width; + } + + label { + max-width: $category-settings-width; + } + input[type="number"] { + width: $number-input-width; + } + } + + .edit-category-tab-tags { + #category-min-tags-from-group { + width: $number-input-width; + min-height: 36px; + } + + .select-kit { + &.tag-chooser { + width: $category-settings-width; + + .select-kit-filter, + .filter-input { + min-width: 250px; + } + + .select-kit-body { + max-width: 100%; + } + } + } + + .manage-tag-groups { + display: block; + } + } + + .edit-category-footer { + grid-area: footer; + display: flex; + justify-content: space-between; + align-self: start; + + .disable-info { + position: relative; + .cannot-delete-reason { + position: absolute; + bottom: 125%; + right: 0px; + width: 250px; + background: var(--primary); + color: var(--secondary); + text-align: center; + border-radius: 2px; + padding: 12px 8px; + + &::after { + top: 100%; + left: 57%; + border: solid transparent; + content: " "; + position: absolute; + border-top-color: var(--primary); + border-width: 8px; + } + } + } + } +} diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index 17a02f480bd6a..2120796c849f7 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -172,19 +172,6 @@ } } -.modal.edit-category-modal { - .modal-body { - textarea { - height: 10em; - } - } - @media screen and (min-width: 524px) { - .modal-inner-container { - min-width: 525px; - } - } -} - .modal { .nav { padding: 10px 30px 10px 15px; @@ -354,141 +341,6 @@ } } -.permission-list { - // Category security tab - list-style: none; - margin: 0 0 30px; - padding: 0; - .name { - margin-right: 20px; - display: inline-block; - min-width: 100px; - } - .permission { - margin-left: 20px; - } - .d-icon-times-circle { - margin-left: 5px; - color: var(--danger); - } - li { - margin-bottom: 10px; - } -} - -.edit-category-modal { - input:not([type="checkbox"]), - .controls { - display: block; - margin-bottom: 0.5em; - } - - .disable_info_wrap { - position: relative; - display: inline-block; - float: right; - - .cannot_delete_reason { - position: absolute; - background: var(--primary); - color: var(--secondary); - text-align: center; - border-radius: 2px; - padding: 12px 8px; - - &::after { - top: 100%; - left: 57%; - border: solid transparent; - content: " "; - position: absolute; - border-top-color: var(--primary); - border-width: 8px; - } - } - } - - .permission-selector { - margin-right: 0.25em; - } - - .pending-permission-change-alert { - margin-left: auto; - max-width: 250px; - background: var(--primary-very-high); - color: var(--secondary); - margin-top: 10px; - padding: 5px 10px; - position: relative; - .arrow-div { - border: solid transparent; - content: " "; - position: absolute; - border-bottom-color: var(--primary-very-high); - border-width: 7px; - top: -13px; - left: 200px; - } - } - - .add-permission { - position: relative; - top: 0.1em; - } - - .edit-category-tab-settings { - $category-settings-width: 280px; // Consistent width makes this all easier to read - > section { - margin-bottom: 1.5em; - } - input[type="number"], - input[type="text"], - .select-kit { - width: $category-settings-width; - } - label { - max-width: $category-settings-width; - } - .category-email-in-outlet { - label { - flex-wrap: wrap; // Todo: fix all multi-element labels - a { - margin-left: 1.3em; - } - } - } - } - #category-min-tags-from-group { - width: 100px; - } -} - -.edit-category-tab-tags { - .select-kit { - &.tag-chooser { - width: 100%; - - .select-kit-filter, - .filter-input { - display: flex; - flex: 1 0 250px; - } - - .mobile-view & { - .no-content, - .is-loading, - &.select-kit.is-expanded .select-kit-collection { - width: 95%; - } - } - } - } -} - -#category-min-tags-from-group { - min-height: 36px; -} - .incoming-email-modal { .btn { transition: none; diff --git a/app/assets/stylesheets/desktop/edit-category.scss b/app/assets/stylesheets/desktop/edit-category.scss new file mode 100644 index 0000000000000..b9c3d46fcbc04 --- /dev/null +++ b/app/assets/stylesheets/desktop/edit-category.scss @@ -0,0 +1,11 @@ +.edit-category { + section.field { + .field-item { + display: inline-block; + vertical-align: top; + + .field-item { + margin-left: 1em; + } + } + } +} diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index bb8608ef657f4..b1c63fc80febe 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -48,28 +48,6 @@ } } -.edit-category-modal { - .modal-body { - position: relative; - height: 420px; - } - - .edit-category-tab-general { - .category-chooser { - width: 434px; - } - } - - .disable_info_wrap { - .cannot_delete_reason { - top: -100px; - right: 4px; - max-width: 380px; - min-width: 300px; - } - } -} - .choose-topic-modal { .modal-body { position: relative; diff --git a/app/assets/stylesheets/mobile/edit-category.scss b/app/assets/stylesheets/mobile/edit-category.scss new file mode 100644 index 0000000000000..444badc61d30c --- /dev/null +++ b/app/assets/stylesheets/mobile/edit-category.scss @@ -0,0 +1,49 @@ +div.edit-category { + display: block; + position: relative; + .nav-stacked { + position: absolute; + top: 3.2em; + left: -80%; + transition: left 0.2s ease; + } + + .edit-category-tab, + .edit-category-footer { + background-color: var(--secondary); + transition: transform 0.2s ease; + transform: translateX(0); + } + + &.expanded-menu { + .edit-category-tab, + .edit-category-footer { + transform: translateX(45%); + } + + .nav-stacked { + left: 0px; + } + } + + .edit-category-title { + justify-content: start; + align-items: center; + padding-bottom: 1em; + + .menu-toggle { + order: 1; + margin-right: 1em; + } + + h2 { + order: 2; + margin-bottom: 0; + @include ellipsis; + } + } + + .edit-category-footer { + padding-bottom: 2em; + } +} diff --git a/app/assets/stylesheets/mobile/modal.scss b/app/assets/stylesheets/mobile/modal.scss index dc31b26781a3b..9f52d92778f21 100644 --- a/app/assets/stylesheets/mobile/modal.scss +++ b/app/assets/stylesheets/mobile/modal.scss @@ -84,22 +84,6 @@ } } -.edit-category-modal { - .modal-body { - position: relative; - height: 350px; - } - &.small .modal-body { - height: 310px; - } - - .disable_info_wrap .cannot_delete_reason { - top: -114px; - right: 8px; - min-width: 200px; - } -} - /* fixes for the new account confirm dialog on mobile */ .modal-inner-container { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index a9759f4e19674..075c958ba8969 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2908,6 +2908,7 @@ en: edit: "Edit" edit_dialog_title: "Edit: %{categoryName}" view: "View Topics in Category" + back: "Back to category" general: "General" settings: "Settings" topic_template: "Topic Template" @@ -2917,7 +2918,7 @@ en: tags_placeholder: "(Optional) list of allowed tags" tags_tab_description: "Tags and tag groups specified above will only be available in this category and other categories that also specify them. They won't be available for use in other categories." tag_groups_placeholder: "(Optional) list of allowed tag groups" - manage_tag_groups_link: "Manage tag groups here." + manage_tag_groups_link: "Manage tag groups" allow_global_tags_label: "Also allow other tags" tag_group_selector_placeholder: "(Optional) Tag group" required_tag_group_description: "Require new topics to have tags from a tag group:" diff --git a/config/routes.rb b/config/routes.rb index 021a5b71d7102..1443136eddfb7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -682,6 +682,9 @@ get "c/:category_slug/find_by_slug" => "categories#find_by_slug" get "c/:parent_category_slug/:category_slug/find_by_slug" => "categories#find_by_slug" + get "c/:category_slug/edit" => "categories#find_by_slug", constraints: { format: 'html' } + get "c/:parent_category_slug/:category_slug/edit" => "categories#find_by_slug", constraints: { format: 'html' } + get "/new-category" => "categories#show", constraints: { format: 'html' } get "c/*category_slug_path_with_id.rss" => "list#category_feed", format: :rss scope path: 'c/*category_slug_path_with_id' do