mirror of
https://github.com/discourse/discourse.git
synced 2025-03-14 10:33:43 +00:00
UX: Tweaks to the theme/component pages when using admin sidebar (#30953)
There are a number of minor changes in this commit : 1. Combine the "Themes" and "Components" links in the admin sidebar into a single tab labelled "Themes and components" 2. The combined tab links to the `/admin/config/customize/themes` page (titled as "Themes and components") 3. Add a new "Components" tab to the "Themes and components" page. There's already an existing "Themes" tab 4. Add a "back to" link at the top of individual theme/component page to navigate back to the respective tab in the "Themes and components" page 5. Remove the themes/components list/sidebar that currently serves for navigating between themes/components 6. Remove the header in the theme/component page Changes 4–6 apply only if the admin sidebar is enabled; they have no effect otherwise. Internal topic: t/146006.
This commit is contained in:
@ -0,0 +1,76 @@
|
||||
import Component from "@glimmer/component";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigAreaCard from "admin/components/admin-config-area-card";
|
||||
|
||||
export default class InstallThemeCard extends Component {
|
||||
externalResources = [
|
||||
{
|
||||
key: "admin.customize.theme.beginners_guide_title",
|
||||
link: "https://meta.discourse.org/t/91966",
|
||||
},
|
||||
{
|
||||
key: "admin.customize.theme.developers_guide_title",
|
||||
link: "https://meta.discourse.org/t/93648",
|
||||
},
|
||||
{
|
||||
key: "admin.customize.theme.browse_themes",
|
||||
link: "https://meta.discourse.org/c/theme",
|
||||
},
|
||||
];
|
||||
|
||||
get heading() {
|
||||
if (this.args.component) {
|
||||
return i18n(
|
||||
"admin.config_areas.themes_and_components.components.new_component"
|
||||
);
|
||||
} else {
|
||||
return i18n("admin.config_areas.themes_and_components.themes.new_theme");
|
||||
}
|
||||
}
|
||||
|
||||
get intro() {
|
||||
if (this.args.component) {
|
||||
return i18n(
|
||||
"admin.config_areas.themes_and_components.components.components_intro"
|
||||
);
|
||||
} else {
|
||||
return i18n(
|
||||
"admin.config_areas.themes_and_components.themes.themes_intro"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<AdminConfigAreaCard
|
||||
class="theme-install-card"
|
||||
@translatedHeading={{this.heading}}
|
||||
>
|
||||
<:content>
|
||||
<p>{{this.intro}}</p>
|
||||
<div class="theme-install-card__external-links">
|
||||
{{#each this.externalResources as |resource|}}
|
||||
<a
|
||||
href={{resource.link}}
|
||||
class="external-link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{{i18n resource.key}}
|
||||
{{icon "up-right-from-square"}}
|
||||
</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
<DButton
|
||||
class="btn-primary theme-install-card__install-button"
|
||||
@translatedLabel={{i18n
|
||||
"admin.config_areas.themes_and_components.install"
|
||||
}}
|
||||
@icon="upload"
|
||||
@action={{@openModal}}
|
||||
/>
|
||||
</:content>
|
||||
</AdminConfigAreaCard>
|
||||
</template>
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import InstallThemeCard from "admin/components/admin-config-area-cards/install-theme-card";
|
||||
import InstallComponentModal from "admin/components/modal/install-theme";
|
||||
import ThemesGrid from "admin/components/themes-grid";
|
||||
import { COMPONENTS } from "admin/models/theme";
|
||||
|
||||
export default class AdminConfigAreasComponents extends Component {
|
||||
@service modal;
|
||||
@service router;
|
||||
@service toasts;
|
||||
|
||||
@action
|
||||
installModal() {
|
||||
this.modal.show(InstallComponentModal, {
|
||||
model: { ...this.installOptions() },
|
||||
});
|
||||
}
|
||||
|
||||
// TODO (martin) These install methods may not belong here and they
|
||||
// are incomplete or have stubbed or omitted properties. We may want
|
||||
// to move this to the new config route or a dedicated component
|
||||
// that sits in the route.
|
||||
installOptions() {
|
||||
return {
|
||||
selectedType: COMPONENTS,
|
||||
userId: null,
|
||||
content: [],
|
||||
installedThemes: this.args.components,
|
||||
addTheme: this.addComponent,
|
||||
updateSelectedType: () => {},
|
||||
showComponentsOnly: true,
|
||||
};
|
||||
}
|
||||
|
||||
@action
|
||||
addComponent(component) {
|
||||
this.toasts.success({
|
||||
data: {
|
||||
message: i18n("admin.customize.theme.install_success", {
|
||||
theme: component.name,
|
||||
}),
|
||||
},
|
||||
duration: 2000,
|
||||
});
|
||||
this.router.refresh();
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="admin-detail">
|
||||
<ThemesGrid @themes={{@components}}>
|
||||
<:specialCard>
|
||||
<InstallThemeCard
|
||||
@component={{true}}
|
||||
@openModal={{this.installModal}}
|
||||
/>
|
||||
</:specialCard>
|
||||
</ThemesGrid>
|
||||
</div>
|
||||
</template>
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import DPageSubheader from "discourse/components/d-page-subheader";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import InstallThemeCard from "admin/components/admin-config-area-cards/install-theme-card";
|
||||
import InstallThemeModal from "admin/components/modal/install-theme";
|
||||
import ThemesGrid from "admin/components/themes-grid";
|
||||
import { THEMES } from "admin/models/theme";
|
||||
|
||||
export default class AdminConfigAreasLookAndFeelThemes extends Component {
|
||||
export default class AdminConfigAreasThemes extends Component {
|
||||
@service modal;
|
||||
@service router;
|
||||
@service toasts;
|
||||
@ -24,12 +25,13 @@ export default class AdminConfigAreasLookAndFeelThemes extends Component {
|
||||
// that sits in the route.
|
||||
installThemeOptions() {
|
||||
return {
|
||||
selectedType: "theme",
|
||||
selectedType: THEMES,
|
||||
userId: null,
|
||||
content: [],
|
||||
installedThemes: this.args.themes,
|
||||
addTheme: this.addTheme,
|
||||
updateSelectedType: () => {},
|
||||
showThemesOnly: true,
|
||||
};
|
||||
}
|
||||
|
||||
@ -47,23 +49,12 @@ export default class AdminConfigAreasLookAndFeelThemes extends Component {
|
||||
}
|
||||
|
||||
<template>
|
||||
<DPageSubheader
|
||||
@titleLabel={{i18n "admin.config_areas.look_and_feel.themes.title"}}
|
||||
@descriptionLabel={{i18n "admin.customize.theme.themes_intro_new"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/93648"
|
||||
>
|
||||
<:actions as |actions|>
|
||||
<actions.Primary
|
||||
@action={{this.installModal}}
|
||||
@label="admin.customize.install"
|
||||
@icon="upload"
|
||||
class="admin-look-and-feel__install-theme"
|
||||
/>
|
||||
</:actions>
|
||||
</DPageSubheader>
|
||||
|
||||
<div class="admin-detail">
|
||||
<ThemesGrid @themes={{@themes}} />
|
||||
<ThemesGrid @themes={{@themes}}>
|
||||
<:specialCard>
|
||||
<InstallThemeCard @openModal={{this.installModal}} />
|
||||
</:specialCard>
|
||||
</ThemesGrid>
|
||||
</div>
|
||||
</template>
|
||||
}
|
@ -111,7 +111,8 @@ export default class InstallThemeModal extends Component {
|
||||
}
|
||||
|
||||
get themes() {
|
||||
return POPULAR_THEMES.map((popularTheme) => {
|
||||
const list = [];
|
||||
POPULAR_THEMES.forEach((popularTheme) => {
|
||||
if (
|
||||
this.args.model.installedThemes.some((installedTheme) =>
|
||||
this.themeHasSameUrl(installedTheme, popularTheme.value)
|
||||
@ -119,8 +120,20 @@ export default class InstallThemeModal extends Component {
|
||||
) {
|
||||
popularTheme.installed = true;
|
||||
}
|
||||
return popularTheme;
|
||||
|
||||
if (this.args.model.showComponentsOnly) {
|
||||
if (popularTheme.component) {
|
||||
list.push(popularTheme);
|
||||
}
|
||||
} else if (this.args.model.showThemesOnly) {
|
||||
if (!popularTheme.component) {
|
||||
list.push(popularTheme);
|
||||
}
|
||||
} else {
|
||||
list.push(popularTheme);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
get installingMessage() {
|
||||
|
@ -12,21 +12,6 @@ export default class ThemesGrid extends Component {
|
||||
|
||||
sortedThemes;
|
||||
|
||||
externalResources = [
|
||||
{
|
||||
key: "admin.customize.theme.beginners_guide_title",
|
||||
link: "https://meta.discourse.org/t/91966",
|
||||
},
|
||||
{
|
||||
key: "admin.customize.theme.developers_guide_title",
|
||||
link: "https://meta.discourse.org/t/93648",
|
||||
},
|
||||
{
|
||||
key: "admin.customize.theme.browse_themes",
|
||||
link: "https://meta.discourse.org/c/theme",
|
||||
},
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
@ -51,6 +36,9 @@ export default class ThemesGrid extends Component {
|
||||
{{#each @themes as |theme|}}
|
||||
<ThemesGridCard @theme={{theme}} @allThemes={{@themes}} />
|
||||
{{/each}}
|
||||
{{#if (has-block "specialCard")}}
|
||||
{{yield to="specialCard"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class AdminConfigThemesAndComponentsComponentsRoute extends DiscourseRoute {
|
||||
async model() {
|
||||
const components = await this.store.findAll("theme");
|
||||
return components.reject((t) => !t.component);
|
||||
}
|
||||
|
||||
titleToken() {
|
||||
return i18n("admin.config_areas.themes_and_components.components.title");
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import { service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class AdminConfigThemesAndComponentsIndexRoute extends DiscourseRoute {
|
||||
@service router;
|
||||
|
||||
beforeModel() {
|
||||
this.router.replaceWith("adminConfig.customize.themes");
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class AdminConfigLookAndFeelThemesRoute extends DiscourseRoute {
|
||||
export default class AdminConfigThemesAndComponentsThemesRoute extends DiscourseRoute {
|
||||
async model() {
|
||||
const themes = await this.store.findAll("theme");
|
||||
return themes.reject((t) => t.component);
|
||||
}
|
||||
|
||||
titleToken() {
|
||||
return i18n("admin.config_areas.look_and_feel.themes.title");
|
||||
return i18n("admin.config_areas.themes_and_components.themes.title");
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@ import { service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class AdminConfigLookAndFeelRoute extends DiscourseRoute {
|
||||
export default class AdminConfigThemesAndComponentsRoute extends DiscourseRoute {
|
||||
@service router;
|
||||
|
||||
titleToken() {
|
||||
return i18n("admin.config_areas.look_and_feel.title");
|
||||
return i18n("admin.config_areas.themes_and_components.title");
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import { service } from "@ember/service";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigLookAndFeelIndexRoute extends AdminConfigWithSettingsRoute {
|
||||
@service router;
|
||||
|
||||
beforeModel() {
|
||||
this.router.replaceWith("adminConfig.lookAndFeel.themes");
|
||||
}
|
||||
}
|
@ -276,8 +276,9 @@ export default function () {
|
||||
path: "/",
|
||||
});
|
||||
});
|
||||
this.route("lookAndFeel", { path: "/look-and-feel" }, function () {
|
||||
this.route("customize", function () {
|
||||
this.route("themes");
|
||||
this.route("components");
|
||||
});
|
||||
this.route(
|
||||
"adminPermalinks",
|
||||
|
@ -0,0 +1,6 @@
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/customize/components"
|
||||
@label={{i18n "admin.config_areas.themes_and_components.components.title"}}
|
||||
/>
|
||||
|
||||
<AdminConfigAreas::Components @components={{this.model}} />
|
@ -0,0 +1,6 @@
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/customize/themes"
|
||||
@label={{i18n "admin.config_areas.themes_and_components.themes.title"}}
|
||||
/>
|
||||
|
||||
<AdminConfigAreas::Themes @themes={{this.model}} />
|
@ -0,0 +1,28 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.config_areas.themes_and_components.title"}}
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config_areas.themes_and_components.description"
|
||||
}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
</:breadcrumbs>
|
||||
|
||||
<:tabs>
|
||||
<NavItem
|
||||
@route="adminConfig.customize.themes"
|
||||
@label="admin.config_areas.themes_and_components.themes.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminConfig.customize.components"
|
||||
@label="admin.config_areas.themes_and_components.components.title"
|
||||
/>
|
||||
</:tabs>
|
||||
</DPageHeader>
|
||||
|
||||
<div class="admin-container admin-config-page__main-area">
|
||||
<PluginOutlet @name="admin-config-customize">
|
||||
{{outlet}}
|
||||
</PluginOutlet>
|
||||
</div>
|
@ -1,6 +0,0 @@
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/look-and-feel/themes"
|
||||
@label={{i18n "admin.config_areas.look_and_feel.themes.title"}}
|
||||
/>
|
||||
|
||||
<AdminConfigAreas::LookAndFeelThemes @themes={{this.model}} />
|
@ -1,24 +0,0 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.config_areas.look_and_feel.title"}}
|
||||
@descriptionLabel={{i18n "admin.config_areas.look_and_feel.description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/look-and-feel"
|
||||
@label={{i18n "admin.config_areas.look_and_feel.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
|
||||
<:tabs>
|
||||
<NavItem
|
||||
@route="adminConfig.lookAndFeel.themes"
|
||||
@label="admin.config_areas.look_and_feel.themes.title"
|
||||
/>
|
||||
</:tabs>
|
||||
</DPageHeader>
|
||||
|
||||
<div class="admin-container admin-config-page__main-area">
|
||||
{{outlet}}
|
||||
</div>
|
@ -1,6 +1,24 @@
|
||||
{{#if this.editingThemeSetting}}
|
||||
{{outlet}}
|
||||
{{else}}
|
||||
<div class="back-to-themes-and-components">
|
||||
<LinkTo
|
||||
@route={{if
|
||||
this.model.component
|
||||
"adminConfig.customize.components"
|
||||
"adminConfig.customize.themes"
|
||||
}}
|
||||
>
|
||||
{{dIcon "angle-left"}}
|
||||
{{i18n
|
||||
(if
|
||||
this.model.component
|
||||
"admin.config_areas.themes_and_components.components.back"
|
||||
"admin.config_areas.themes_and_components.themes.back"
|
||||
)
|
||||
}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
<div class="show-current-style">
|
||||
<span>
|
||||
<PluginOutlet
|
||||
|
@ -1,43 +1,3 @@
|
||||
{{#if (eq this.currentTab "themes")}}
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.config.themes.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.themes.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/91966"
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/themes"
|
||||
@label={{i18n "admin.config.themes.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
{{else}}
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.config.components.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.components.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/components"
|
||||
@label={{i18n "admin.config.components.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
{{/if}}
|
||||
|
||||
<PluginOutlet @name="admin-customize-themes">
|
||||
{{#unless this.editingTheme}}
|
||||
<ThemesList
|
||||
@themes={{this.fullThemes}}
|
||||
@components={{this.childThemes}}
|
||||
@currentTab={{this.currentTab}}
|
||||
@installModal={{route-action "installModal"}}
|
||||
/>
|
||||
{{/unless}}
|
||||
|
||||
{{outlet}}
|
||||
</PluginOutlet>
|
@ -240,22 +240,14 @@ export const ADMIN_NAV_MAP = [
|
||||
settings_area: "navigation",
|
||||
},
|
||||
{
|
||||
name: "admin_themes",
|
||||
route: "adminCustomizeThemes",
|
||||
routeModels: ["themes"],
|
||||
model: "themes",
|
||||
label: "admin.config.themes.title",
|
||||
description: "admin.config.themes.header_description",
|
||||
name: "admin_themes_and_components",
|
||||
route: "adminConfig.customize.themes",
|
||||
currentWhen:
|
||||
"adminConfig.customize.themes adminConfig.customize.components",
|
||||
label: "admin.appearance.sidebar_link.themes_and_components.title",
|
||||
icon: "paintbrush",
|
||||
},
|
||||
{
|
||||
name: "admin_components",
|
||||
route: "adminCustomizeThemes",
|
||||
routeModels: ["components"],
|
||||
label: "admin.config.components.title",
|
||||
description: "admin.config.components.header_description",
|
||||
icon: "puzzle-piece",
|
||||
keywords: "admin.config.components.keywords",
|
||||
keywords:
|
||||
"admin.appearance.sidebar_link.themes_and_components.keywords",
|
||||
},
|
||||
{
|
||||
name: "admin_customize_site_texts",
|
||||
|
@ -93,6 +93,9 @@ class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
|
||||
return this.router.currentRoute.name;
|
||||
}
|
||||
}
|
||||
if (this.adminSidebarNavLink.currentWhen) {
|
||||
return this.adminSidebarNavLink.currentWhen;
|
||||
}
|
||||
}
|
||||
|
||||
get keywords() {
|
||||
@ -352,11 +355,11 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
||||
|
||||
store.findAll("theme").then((themes) => {
|
||||
this.adminSidebarStateManager.setLinkKeywords(
|
||||
"admin_themes",
|
||||
"admin_themes_and_components",
|
||||
themes.content.rejectBy("component").mapBy("name")
|
||||
);
|
||||
this.adminSidebarStateManager.setLinkKeywords(
|
||||
"admin_components",
|
||||
"admin_themes_and_components",
|
||||
themes.content.filterBy("component").mapBy("name")
|
||||
);
|
||||
});
|
||||
|
@ -175,9 +175,9 @@ acceptance("Theme", function (needs) {
|
||||
});
|
||||
|
||||
test("can force install themes", async function (assert) {
|
||||
await visit("/admin/customize/themes");
|
||||
await visit("/admin/config/customize/themes");
|
||||
|
||||
await click(".themes-list .create-actions button");
|
||||
await click(".theme-install-card .btn-primary");
|
||||
await click(".install-theme-items #remote");
|
||||
await fillIn(
|
||||
".install-theme-content .repo input",
|
||||
@ -200,9 +200,9 @@ acceptance("Theme", function (needs) {
|
||||
});
|
||||
|
||||
test("can continue installation", async function (assert) {
|
||||
await visit("/admin/customize/themes");
|
||||
await visit("/admin/config/customize/themes");
|
||||
|
||||
await click(".themes-list-container__item .info");
|
||||
await click(".theme-card .btn-primary");
|
||||
assert
|
||||
.dom(".control-unit .status-message")
|
||||
.includesText(
|
||||
|
@ -853,6 +853,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.theme-install-card {
|
||||
&__external-links {
|
||||
display: flex;
|
||||
margin-bottom: 1em;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__install-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.add-upload-modal {
|
||||
input[type="file"] {
|
||||
display: block;
|
||||
@ -1191,7 +1203,7 @@
|
||||
|
||||
.desktop-view .admin-customize {
|
||||
.show-current-style {
|
||||
padding-left: 2%;
|
||||
padding-left: 0;
|
||||
width: 68%;
|
||||
|
||||
.title {
|
||||
@ -1204,6 +1216,10 @@
|
||||
.themes-list {
|
||||
width: 28%;
|
||||
}
|
||||
|
||||
.back-to-themes-and-components {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-view .admin-customize {
|
||||
|
9
app/controllers/admin/config/customize_controller.rb
Normal file
9
app/controllers/admin/config/customize_controller.rb
Normal file
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Config::CustomizeController < Admin::AdminController
|
||||
def themes
|
||||
end
|
||||
|
||||
def components
|
||||
end
|
||||
end
|
@ -1,6 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Config::LookAndFeelController < Admin::AdminController
|
||||
def themes
|
||||
end
|
||||
end
|
@ -5958,6 +5958,72 @@ en:
|
||||
failed: "Failed"
|
||||
home:
|
||||
title: "Home"
|
||||
account:
|
||||
title: "Account"
|
||||
sidebar_link:
|
||||
backups: "Backups"
|
||||
whats_new:
|
||||
title: "What's new?"
|
||||
keywords: "changelog|feature|release"
|
||||
|
||||
community:
|
||||
title: "Community"
|
||||
sidebar_link:
|
||||
about_your_site: "About your site"
|
||||
badges: "Badges"
|
||||
login_and_authentication: "Login & authentication"
|
||||
notifications: "Notifications"
|
||||
permalinks: "Permalinks"
|
||||
trust_levels: "Trust levels"
|
||||
group_permissions: "Group permissions"
|
||||
users: "Users"
|
||||
groups: "Groups"
|
||||
user_fields: "User fields"
|
||||
watched_words: "Watched words"
|
||||
legal: "Legal"
|
||||
moderation_flags:
|
||||
title: "Moderation"
|
||||
keywords: "flag|review"
|
||||
|
||||
appearance:
|
||||
title: "Appearance"
|
||||
sidebar_link:
|
||||
font_style: "Font style"
|
||||
site_logo: "Site logo"
|
||||
color_schemes: "Color palettes"
|
||||
emoji: "Emoji"
|
||||
navigation: "Navigation"
|
||||
themes_and_components:
|
||||
title: "Themes and components"
|
||||
keywords: "extension"
|
||||
site_texts: "Site texts"
|
||||
|
||||
email_settings:
|
||||
title: "Email Settings"
|
||||
sidebar_link:
|
||||
appearance: "Appearance"
|
||||
server_setup:
|
||||
title: "Server setup & logs"
|
||||
keywords: "email|smtp|mailgun|sendgrid|sent|skipped|bounced|received|rejected|email logs|preview summary"
|
||||
|
||||
security:
|
||||
title: "Security"
|
||||
sidebar_link:
|
||||
security: "Security settings"
|
||||
spam: "Spam settings"
|
||||
staff_action_logs:
|
||||
title: "Logs & screening"
|
||||
keywords: "error logs|staff action|screened emails|screened ips|screened urls|search logs"
|
||||
|
||||
section_landing_pages:
|
||||
account:
|
||||
title: "Account"
|
||||
backups:
|
||||
title: "Backups"
|
||||
description: "Take a backup of your site's data"
|
||||
whats_new:
|
||||
title: "What's new?"
|
||||
description: "Discover new releases and improvements to Discourse"
|
||||
|
||||
config_areas:
|
||||
about:
|
||||
@ -6054,16 +6120,21 @@ en:
|
||||
delete: "Delete"
|
||||
more_options:
|
||||
title: "More options"
|
||||
look_and_feel:
|
||||
themes_and_components:
|
||||
title: "Themes and components"
|
||||
description: "Personalize your Discourse site with themes and components to match your needs."
|
||||
breadcrumb_title: "Customize"
|
||||
install: "Install"
|
||||
themes:
|
||||
title: "Themes"
|
||||
themes_intro: "Install a new theme to get started, or create your own from scratch using these resources."
|
||||
themes_intro_img_alt: "New theme placeholder"
|
||||
set_default_theme: "Set default"
|
||||
default_theme: "Default theme"
|
||||
themes_description: "Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features."
|
||||
new_theme: "New theme"
|
||||
user_selectable: "User selectable"
|
||||
back: "Back to themes"
|
||||
components:
|
||||
title: "Components"
|
||||
components_intro: "Install a new component to get started, or create your own from scratch using these resources."
|
||||
new_component: "New component"
|
||||
back: "Back to components"
|
||||
user_fields:
|
||||
field: "Field"
|
||||
type: "Type"
|
||||
@ -6240,6 +6311,7 @@ en:
|
||||
component_name: "Component name"
|
||||
themes_intro: "Select an existing theme or install a new one to get started"
|
||||
themes_intro_new: "Install a new theme to get started, or create your own from scratch using these resources."
|
||||
components_intro_new: "Install a new component to get started, or create your own from scratch using these resources."
|
||||
themes_intro_img_alt: "New theme placeholder"
|
||||
beginners_guide_title: "Beginner’s guide to using Discourse Themes"
|
||||
developers_guide_title: "Developer’s guide to Discourse Themes"
|
||||
|
@ -426,11 +426,14 @@ Discourse::Application.routes.draw do
|
||||
collection { put "/" => "about#update" }
|
||||
end
|
||||
|
||||
resources :look_and_feel,
|
||||
path: "look-and-feel",
|
||||
resources :customize,
|
||||
path: "customize",
|
||||
constraints: AdminConstraint.new,
|
||||
only: %i[index] do
|
||||
collection { get "/themes" => "look_and_feel#themes" }
|
||||
collection do
|
||||
get "/themes" => "customize#themes"
|
||||
get "/components" => "customize#components"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -10,6 +10,7 @@ module SvgSprite
|
||||
align-left
|
||||
anchor
|
||||
angle-down
|
||||
angle-left
|
||||
angle-right
|
||||
angle-up
|
||||
angles-down
|
||||
|
38
spec/system/admin_customize_config_area_spec.rb
Normal file
38
spec/system/admin_customize_config_area_spec.rb
Normal file
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe "Admin Customize Config Area Page", type: :system do
|
||||
fab!(:admin)
|
||||
|
||||
let(:config_area) { PageObjects::Pages::AdminCustomizeConfigArea.new }
|
||||
let(:install_modal) { PageObjects::Modals::InstallTheme.new }
|
||||
|
||||
before { sign_in(admin) }
|
||||
|
||||
context "when in the themes tab" do
|
||||
it "has a special card for installing new themes" do
|
||||
config_area.visit
|
||||
|
||||
expect(config_area.install_card).to have_text(
|
||||
I18n.t("admin_js.admin.config_areas.themes_and_components.themes.new_theme"),
|
||||
)
|
||||
|
||||
config_area.install_card.find(".btn-primary").click
|
||||
expect(install_modal).to be_open
|
||||
expect(install_modal.popular_options.first).to have_text("Air")
|
||||
end
|
||||
end
|
||||
|
||||
context "when in the components tab" do
|
||||
it "has a special card for installing new components" do
|
||||
config_area.visit_components
|
||||
|
||||
expect(config_area.install_card).to have_text(
|
||||
I18n.t("admin_js.admin.config_areas.themes_and_components.components.new_component"),
|
||||
)
|
||||
|
||||
config_area.install_card.find(".btn-primary").click
|
||||
expect(install_modal).to be_open
|
||||
expect(install_modal.popular_options.first).to have_text("Brand Header")
|
||||
end
|
||||
end
|
||||
end
|
@ -9,76 +9,6 @@ describe "Admin Customize Themes", type: :system do
|
||||
|
||||
before { sign_in(admin) }
|
||||
|
||||
describe "when visiting the page to customize themes" do
|
||||
fab!(:theme_2) { Fabricate(:theme, name: "Cool theme 2") }
|
||||
fab!(:theme_3) { Fabricate(:theme, name: "Cool theme 3") }
|
||||
let(:delete_themes_confirm_modal) { PageObjects::Modals::DeleteThemesConfirm.new }
|
||||
|
||||
it "should allow admin to bulk delete inactive themes" do
|
||||
visit("/admin/customize/themes")
|
||||
|
||||
expect(admin_customize_themes_page).to have_inactive_themes
|
||||
|
||||
admin_customize_themes_page.click_select_inactive_mode
|
||||
expect(admin_customize_themes_page).to have_inactive_themes_selected(count: 0)
|
||||
admin_customize_themes_page.toggle_all_inactive
|
||||
expect(admin_customize_themes_page).to have_inactive_themes_selected(count: 3)
|
||||
|
||||
admin_customize_themes_page.cancel_select_inactive_mode
|
||||
expect(admin_customize_themes_page).to have_select_inactive_mode_button
|
||||
|
||||
admin_customize_themes_page.click_select_inactive_mode
|
||||
expect(admin_customize_themes_page).to have_disabled_delete_theme_button
|
||||
|
||||
admin_customize_themes_page.toggle_all_inactive
|
||||
|
||||
admin_customize_themes_page.click_delete_themes_button
|
||||
|
||||
expect(delete_themes_confirm_modal).to have_theme(theme.name)
|
||||
expect(delete_themes_confirm_modal).to have_theme(theme_2.name)
|
||||
expect(delete_themes_confirm_modal).to have_theme(theme_3.name)
|
||||
delete_themes_confirm_modal.confirm
|
||||
|
||||
expect(admin_customize_themes_page).to have_no_inactive_themes
|
||||
end
|
||||
|
||||
it "selects the themes tab by default" do
|
||||
visit("/admin/customize/themes")
|
||||
expect(find(".themes-list-header")).to have_css(".themes-tab.active")
|
||||
end
|
||||
|
||||
it "selects the component tab when visiting the theme-components route" do
|
||||
visit("/admin/customize/components")
|
||||
expect(find(".themes-list-header")).to have_css(".components-tab.active")
|
||||
end
|
||||
|
||||
it "switching between themes and components tabs keeps the search visible only if both tabs have at least 10 items" do
|
||||
(1..6).each { |number| Fabricate(:theme, component: false, name: "Cool theme #{number}") }
|
||||
(1..5).each { |number| Fabricate(:theme, component: true, name: "Cool component #{number}") }
|
||||
|
||||
visit("/admin/customize/themes")
|
||||
expect(admin_customize_themes_page).to have_themes(count: 11)
|
||||
|
||||
admin_customize_themes_page.search("5")
|
||||
expect(admin_customize_themes_page).to have_themes(count: 1)
|
||||
|
||||
admin_customize_themes_page.switch_to_components
|
||||
expect(admin_customize_themes_page).to have_no_search
|
||||
expect(admin_customize_themes_page).to have_themes(count: 5)
|
||||
|
||||
(6..11).each { |number| Fabricate(:theme, component: true, name: "Cool component #{number}") }
|
||||
|
||||
visit("/admin/customize/components")
|
||||
expect(admin_customize_themes_page).to have_themes(count: 11)
|
||||
|
||||
admin_customize_themes_page.search("5")
|
||||
expect(admin_customize_themes_page).to have_themes(count: 1)
|
||||
|
||||
admin_customize_themes_page.switch_to_themes
|
||||
expect(admin_customize_themes_page).to have_themes(count: 1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when visiting the page to customize a single theme" do
|
||||
it "should allow admin to update the color scheme of the theme" do
|
||||
visit("/admin/customize/themes/#{theme.id}")
|
||||
@ -213,4 +143,20 @@ describe "Admin Customize Themes", type: :system do
|
||||
expect(theme_translations_picker.component.text).to eq("English (US)")
|
||||
end
|
||||
end
|
||||
|
||||
context "when visting a theme's page" do
|
||||
it "has a link to the themes page" do
|
||||
visit("/admin/customize/themes/#{theme.id}")
|
||||
expect(admin_customize_themes_page).to have_back_button_to_themes_page
|
||||
end
|
||||
end
|
||||
|
||||
context "when visting a component's page" do
|
||||
fab!(:component) { Fabricate(:theme, component: true, name: "Cool component 493") }
|
||||
|
||||
it "has a link to the components page" do
|
||||
visit("/admin/customize/themes/#{component.id}")
|
||||
expect(admin_customize_themes_page).to have_back_button_to_components_page
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -293,12 +293,26 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
filter.filter("air")
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
expect(links.count).to eq(1)
|
||||
expect(links.map(&:text)).to eq(["Themes"])
|
||||
expect(links.map(&:text)).to eq(["Themes and components"])
|
||||
|
||||
filter.filter("kanban")
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
expect(links.count).to eq(1)
|
||||
expect(links.map(&:text)).to eq(["Components"])
|
||||
expect(links.map(&:text)).to eq(["Themes and components"])
|
||||
end
|
||||
|
||||
it "highlights the 'Themes and components' link when the themes page is visited" do
|
||||
visit("/admin/config/customize/themes")
|
||||
expect(page).to have_css(
|
||||
'.sidebar-section-link-wrapper[data-list-item-name="admin_themes_and_components"] a.active',
|
||||
)
|
||||
end
|
||||
|
||||
it "highlights the 'Themes and components' link when the components page is visited" do
|
||||
visit("/admin/config/customize/components")
|
||||
expect(page).to have_css(
|
||||
'.sidebar-section-link-wrapper[data-list-item-name="admin_themes_and_components"] a.active',
|
||||
)
|
||||
end
|
||||
|
||||
it "does not show the button to customize sidebar sections, that is only supported in the main panel" do
|
||||
|
15
spec/system/page_objects/modals/install_theme.rb
Normal file
15
spec/system/page_objects/modals/install_theme.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Modals
|
||||
class InstallTheme < PageObjects::Modals::Base
|
||||
def modal
|
||||
find(".admin-install-theme-modal")
|
||||
end
|
||||
|
||||
def popular_options
|
||||
all(".popular-theme-item")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Pages
|
||||
class AdminCustomizeConfigArea < PageObjects::Pages::Base
|
||||
def visit
|
||||
page.visit("/admin/config/customize")
|
||||
end
|
||||
|
||||
def visit_components
|
||||
page.visit("/admin/config/customize/components")
|
||||
end
|
||||
|
||||
def install_card
|
||||
find(".theme-install-card")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -27,6 +27,28 @@ module PageObjects
|
||||
has_css?("#{setting_selector(setting_name)} .desc", exact_text: description)
|
||||
end
|
||||
|
||||
def has_no_themes_list?
|
||||
has_no_css?(".themes-list-header")
|
||||
end
|
||||
|
||||
def has_back_button_to_themes_page?
|
||||
has_css?(
|
||||
'.back-to-themes-and-components a[href="/admin/config/customize/themes"]',
|
||||
text: I18n.t("admin_js.admin.config_areas.themes_and_components.themes.back"),
|
||||
)
|
||||
end
|
||||
|
||||
def has_back_button_to_components_page?
|
||||
has_css?(
|
||||
'.back-to-themes-and-components a[href="/admin/config/customize/components"]',
|
||||
text: I18n.t("admin_js.admin.config_areas.themes_and_components.components.back"),
|
||||
)
|
||||
end
|
||||
|
||||
def has_no_page_header?
|
||||
has_no_css?(".d-page-header")
|
||||
end
|
||||
|
||||
def reset_overridden_setting(setting_name)
|
||||
setting_section = find("section.theme.settings .setting[data-setting=\"#{setting_name}\"]")
|
||||
setting_section.click_button(I18n.t("admin_js.admin.settings.reset"))
|
||||
|
Reference in New Issue
Block a user