FEATURE: settings tab for permalinks (#30192)

Setting tab should be added to permalinks so admins do not need to have left `/permalinks`.

A new component called `AreaSetting` was added to avoid duplications and
simplify adding settings to other sections.
This commit is contained in:
Krzysztof Kotlarek 2024-12-19 10:40:34 +11:00 committed by GitHub
parent 4305b64460
commit fdb6634fa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 396 additions and 350 deletions

View File

@ -0,0 +1,69 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse-common/utils/decorators";
import { i18n } from "discourse-i18n";
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
import AdminFilteredSiteSettings from "admin/components/admin-filtered-site-settings";
import SiteSetting from "admin/models/site-setting";
export default class AdminAreaSettings extends Component {
@service siteSettings;
@service router;
@tracked settings = [];
@tracked filter = "";
constructor() {
super(...arguments);
this.#loadSettings();
}
@bind
async #loadSettings() {
this.filter = this.args.filter;
const result = await ajax("/admin/config/site_settings.json", {
data: {
filter_area: this.args.area,
plugin: this.args.plugin,
categories: this.args.categories,
},
});
this.settings = [
{
name: "All",
nameKey: "all_results",
siteSettings: result.site_settings.map((setting) =>
SiteSetting.create(setting)
),
},
];
}
@action
adminSettingsFilterChangedCallback(filterData) {
this.args.adminSettingsFilterChangedCallback(filterData.filter);
}
<template>
<DBreadcrumbsItem @path={{@path}} @label={{i18n "settings"}} />
<div
class="content-body admin-config-area__settings admin-detail pull-left"
>
{{#if this.settings}}
<AdminFilteredSiteSettings
@initialFilter={{this.filter}}
@onFilterChanged={{this.adminSettingsFilterChangedCallback}}
@settings={{this.settings}}
/>
{{else}}
<AdminConfigAreaEmptyList
@emptyLabelTranslated={{i18n "admin.settings.not_found"}}
/>
{{/if}}
</div>
</template>
}

View File

@ -1,54 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse-common/utils/decorators";
import { i18n } from "discourse-i18n";
import AdminFilteredSiteSettings from "admin/components/admin-filtered-site-settings";
import SiteSetting from "admin/models/site-setting";
export default class AdminConfigAreasEmojisSettings extends Component {
@service siteSettings;
@tracked settings;
@bind
loadSettings() {
ajax("/admin/config/site_settings.json", {
data: {
filter_area: "emojis",
},
}).then((result) => {
this.settings = [
{
name: "All",
nameKey: "all_results",
siteSettings: result.site_settings.map((setting) =>
SiteSetting.create(setting)
),
},
];
});
}
<template>
<DBreadcrumbsItem
@path="/admin/customize/emojis/settings"
@label={{i18n "settings"}}
/>
<div
class="content-body admin-config-area__settings admin-detail pull-left"
{{didInsert this.loadSettings}}
>
{{#if this.settings}}
<AdminFilteredSiteSettings
@initialFilter={{@initialFilter}}
@onFilterChanged={{@onFilterChanged}}
@settings={{this.settings}}
/>
{{/if}}
</div>
</template>
}

View File

@ -1,54 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse-common/utils/decorators";
import { i18n } from "discourse-i18n";
import AdminFilteredSiteSettings from "admin/components/admin-filtered-site-settings";
import SiteSetting from "admin/models/site-setting";
export default class AdminConfigAreasFlagsSettings extends Component {
@service siteSettings;
@tracked settings;
@bind
loadSettings() {
ajax("/admin/config/site_settings.json", {
data: {
filter_area: "flags",
},
}).then((result) => {
this.settings = [
{
name: "All",
nameKey: "all_results",
siteSettings: result.site_settings.map((setting) =>
SiteSetting.create(setting)
),
},
];
});
}
<template>
<DBreadcrumbsItem
@path="/admin/config/flags/settings"
@label={{i18n "settings"}}
/>
<div
class="content-body admin-config-area__settings admin-detail pull-left"
{{didInsert this.loadSettings}}
>
{{#if this.settings}}
<AdminFilteredSiteSettings
@initialFilter={{@initialFilter}}
@onFilterChanged={{@onFilterChanged}}
@settings={{this.settings}}
/>
{{/if}}
</div>
</template>
}

View File

@ -0,0 +1,13 @@
import { tracked } from "@glimmer/tracking";
import Controller from "@ember/controller";
import { action } from "@ember/object";
export default class AdminAreaSettingsBaseController extends Controller {
@tracked filter = "";
queryParams = ["filter"];
@action
adminSettingsFilterChangedCallback(filter) {
this.filter = filter;
}
}

View File

@ -1,11 +1,3 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminBackupsSettingsController extends Controller {
filter = "";
@action
filterChanged(filterData) {
this.set("filter", filterData.filter);
}
}
export default class AdminBackupsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,12 +1,3 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigFlagsSettingsController extends Controller {
filter = "";
queryParams = ["filter"];
@action
filterChangedCallback(filterData) {
this.set("filter", filterData.filter);
}
}
export default class AdminConfigFlagsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,12 +1,3 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminEmojisSettingsController extends Controller {
filter = "";
queryParams = ["filter"];
@action
filterChangedCallback(filterData) {
this.set("filter", filterData.filter);
}
}
export default class AdminEmojisSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminPermalinksSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,11 +1,3 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminPluginsShowSettingsController extends Controller {
filter = "";
@action
filterChanged(filterData) {
this.set("filter", filterData.filter);
}
}
export default class AdminPluginsShowSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,20 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
import SiteSetting from "admin/models/site-setting";
export default class AdminBackupsSettingsRoute extends DiscourseRoute {
queryParams = {
filter: { replace: true },
};
titleToken() {
return i18n("admin.backups.settings");
}
async model(params) {
return {
settings: await SiteSetting.findAll({ categories: ["backups"] }),
initialFilter: params.filter,
};
}
}

View File

@ -2,10 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminEmojisSettingsRoute extends DiscourseRoute {
queryParams = {
filter: { replace: true },
};
titleToken() {
return i18n("settings");
}

View File

@ -1,7 +1,6 @@
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
import SiteSetting from "admin/models/site-setting";
export default class AdminPluginsShowSettingsRoute extends DiscourseRoute {
@service router;
@ -14,7 +13,6 @@ export default class AdminPluginsShowSettingsRoute extends DiscourseRoute {
const plugin = this.modelFor("adminPlugins.show");
return {
plugin,
settings: await SiteSetting.findAll({ plugin: plugin.name }),
initialFilter: params.filter,
};
}

View File

@ -91,6 +91,8 @@ export default function () {
{ path: "/permalinks", resetNamespace: true },
function () {
this.route("new");
this.route("index", { path: "/" });
this.route("settings");
this.route("edit", { path: "/:permalink_id" });
}
);

View File

@ -1,9 +1,6 @@
<DBreadcrumbsItem @path="/admin/backups/settings" @label={{i18n "settings"}} />
<div class="content-body admin-config-area__settings admin-detail pull-left">
<AdminFilteredSiteSettings
@initialFilter={{@model.initialFilter}}
@settings={{@model.settings}}
@onFilterChanged={{this.filterChanged}}
/>
</div>
<AdminAreaSettings
@categories="backups"
@path="/admin/backups/settings"
@filter={{this.filter}}
@adminSettingsFilterChangedCallback={{this.adminSettingsFilterChangedCallback}}
/>

View File

@ -1,5 +1,6 @@
<AdminConfigAreas::FlagsSettings
@model={{this.model}}
@onFilterChanged={{this.filterChangedCallback}}
@initialFilter={{this.filter}}
<AdminAreaSettings
@area="flags"
@path="/admin/config/flags/settings"
@filter={{this.filter}}
@adminSettingsFilterChangedCallback={{this.adminSettingsFilterChangedCallback}}
/>

View File

@ -1,4 +1,6 @@
<AdminConfigAreas::EmojisSettings
@onFilterChanged={{this.filterChangedCallback}}
@initialFilter={{this.filter}}
<AdminAreaSettings
@area="emojis"
@path="/admin/customize/emojis/settings"
@filter={{this.filter}}
@adminSettingsFilterChangedCallback={{this.adminSettingsFilterChangedCallback}}
/>

View File

@ -1,138 +1,112 @@
<div class="admin-permalinks admin-config-page">
<DPageHeader
@titleLabel={{i18n "admin.permalink.title"}}
@descriptionLabel={{i18n "admin.permalink.description"}}
@learnMoreUrl="https://meta.discourse.org/t/redirect-old-forum-urls-to-new-discourse-urls/20930"
>
<:breadcrumbs>
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
<DBreadcrumbsItem
@path="/admin/customize/permalinks"
@label={{i18n "admin.permalink.title"}}
<ConditionalLoadingSpinner @condition={{this.loading}}>
<div class="d-admin-filter">
<div class="admin-filter__input-container permalink-search">
<TextField
@value={{this.filter}}
@placeholderKey="admin.permalink.form.filter"
@autocorrect="off"
@autocapitalize="off"
class="admin-filter__input"
/>
</:breadcrumbs>
<:actions as |actions|>
<actions.Primary
@route="adminPermalinks.new"
@title="admin.permalink.add"
@label="admin.permalink.add"
@icon="plus"
class="admin-permalinks__header-add-permalink"
/>
</:actions>
</DPageHeader>
<div class="admin-container admin-config-page__main-area">
<ConditionalLoadingSpinner @condition={{this.loading}}>
<div class="permalink-search">
<TextField
@value={{this.filter}}
@placeholderKey="admin.permalink.form.filter"
@autocorrect="off"
@autocapitalize="off"
class="url-input"
/>
</div>
<div class="permalink-results">
{{#if this.model.length}}
<table class="d-admin-table permalinks">
<thead>
<th>{{i18n "admin.permalink.url"}}</th>
<th>{{i18n "admin.permalink.destination"}}</th>
</thead>
<tbody>
{{#each this.model as |pl|}}
<tr
class={{concat-class
"admin-permalink-item d-admin-row__content"
pl.key
}}
>
<td class="d-admin-row__overview">
<FlatButton
@title="admin.permalink.copy_to_clipboard"
@icon="far-clipboard"
@action={{fn this.copyUrl pl}}
/>
<span
id="admin-permalink-{{pl.id}}"
class="admin-permalink-item__url"
title={{pl.url}}
>{{pl.url}}</span>
</td>
<td class="d-admin-row__detail destination">
{{#if pl.topic_id}}
<a href={{pl.topic_url}}>{{pl.topic_title}}</a>
{{/if}}
{{#if pl.post_id}}
<a href={{pl.post_url}}>{{pl.post_topic_title}}
#{{pl.post_number}}</a>
{{/if}}
{{#if pl.category_id}}
{{category-link pl.category}}
{{/if}}
{{#if pl.tag_id}}
<a href={{pl.tag_url}}>{{pl.tag_name}}</a>
{{/if}}
{{#if pl.external_url}}
{{#if pl.linkIsExternal}}
{{d-icon "up-right-from-square"}}
{{/if}}
<a href={{pl.external_url}}>{{pl.external_url}}</a>
{{/if}}
{{#if pl.user_id}}
<a href={{pl.user_url}}>{{pl.username}}</a>
{{/if}}
</td>
<td class="d-admin-row__controls">
<div class="d-admin-row__controls-options">
<DButton
class="btn-small admin-permalink-item__edit"
@route="adminPermalinks.edit"
@routeModels={{pl}}
@label="admin.config_areas.flags.edit"
/>
<DMenu
@identifier="permalink-menu"
@title={{i18n "admin.permalinks.more_options"}}
@icon="ellipsis-vertical"
@onRegisterApi={{this.onRegisterApi}}
>
<:content>
<DropdownMenu as |dropdown|>
<dropdown.item>
<DButton
@action={{fn this.destroyRecord pl}}
@icon="trash-can"
class="btn-transparent admin-permalink-item__delete"
@label="admin.config_areas.flags.delete"
/>
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
{{#if this.filter}}
<p class="permalink-results__no-result">{{i18n
"search.no_results"
}}</p>
{{else}}
<AdminConfigAreaEmptyList
@ctaLabel="admin.permalink.add"
@ctaRoute="adminPermalinks.new"
@ctaClass="admin-permalinks__add-permalink"
@emptyLabel="admin.permalink.no_permalinks"
/>
{{/if}}
{{/if}}
</div>
</ConditionalLoadingSpinner>
</div>
</div>
</div>
<div class="permalink-results">
{{#if this.model.length}}
<table class="d-admin-table permalinks">
<thead>
<th>{{i18n "admin.permalink.url"}}</th>
<th>{{i18n "admin.permalink.destination"}}</th>
</thead>
<tbody>
{{#each this.model as |pl|}}
<tr
class={{concat-class
"admin-permalink-item d-admin-row__content"
pl.key
}}
>
<td class="d-admin-row__overview">
<FlatButton
@title="admin.permalink.copy_to_clipboard"
@icon="far-clipboard"
@action={{fn this.copyUrl pl}}
/>
<span
id="admin-permalink-{{pl.id}}"
class="admin-permalink-item__url"
title={{pl.url}}
>{{pl.url}}</span>
</td>
<td class="d-admin-row__detail destination">
{{#if pl.topic_id}}
<a href={{pl.topic_url}}>{{pl.topic_title}}</a>
{{/if}}
{{#if pl.post_id}}
<a href={{pl.post_url}}>{{pl.post_topic_title}}
#{{pl.post_number}}</a>
{{/if}}
{{#if pl.category_id}}
{{category-link pl.category}}
{{/if}}
{{#if pl.tag_id}}
<a href={{pl.tag_url}}>{{pl.tag_name}}</a>
{{/if}}
{{#if pl.external_url}}
{{#if pl.linkIsExternal}}
{{d-icon "up-right-from-square"}}
{{/if}}
<a href={{pl.external_url}}>{{pl.external_url}}</a>
{{/if}}
{{#if pl.user_id}}
<a href={{pl.user_url}}>{{pl.username}}</a>
{{/if}}
</td>
<td class="d-admin-row__controls">
<div class="d-admin-row__controls-options">
<DButton
class="btn-small admin-permalink-item__edit"
@route="adminPermalinks.edit"
@routeModels={{pl}}
@label="admin.config_areas.flags.edit"
/>
<DMenu
@identifier="permalink-menu"
@title={{i18n "admin.permalinks.more_options"}}
@icon="ellipsis-vertical"
@onRegisterApi={{this.onRegisterApi}}
>
<:content>
<DropdownMenu as |dropdown|>
<dropdown.item>
<DButton
@action={{fn this.destroyRecord pl}}
@icon="trash-can"
class="btn-transparent admin-permalink-item__delete"
@label="admin.config_areas.flags.delete"
/>
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
{{#if this.filter}}
<p class="permalink-results__no-result">{{i18n "search.no_results"}}</p>
{{else}}
<AdminConfigAreaEmptyList
@ctaLabel="admin.permalink.add"
@ctaRoute="adminPermalinks.new"
@ctaClass="admin-permalinks__add-permalink"
@emptyLabel="admin.permalink.no_permalinks"
/>
{{/if}}
{{/if}}
</div>
</ConditionalLoadingSpinner>

View File

@ -0,0 +1,6 @@
<AdminAreaSettings
@area="permalinks"
@path="/admin/customize/permalinks/settings"
@filter={{this.filter}}
@adminSettingsFilterChangedCallback={{this.adminSettingsFilterChangedCallback}}
/>

View File

@ -0,0 +1,39 @@
<div class="admin-permalinks admin-config-page">
<DPageHeader
@titleLabel={{i18n "admin.permalink.title"}}
@descriptionLabel={{i18n "admin.permalink.description"}}
@learnMoreUrl="https://meta.discourse.org/t/20930"
>
<:breadcrumbs>
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
<DBreadcrumbsItem
@path="/admin/customize/permalinks"
@label={{i18n "admin.permalink.title"}}
/>
</:breadcrumbs>
<:actions as |actions|>
<actions.Primary
@route="adminPermalinks.new"
@title="admin.permalink.add"
@label="admin.permalink.add"
@icon="plus"
class="admin-permalinks__header-add-permalink"
/>
</:actions>
<:tabs>
<NavItem
@route="adminPermalinks.settings"
@label="admin.permalink.nav.settings"
class="admin-permalinks-tabs__settings"
/>
<NavItem
@route="adminPermalinks.index"
@label="admin.permalink.nav.permalinks"
class="admin-permalins-permalinks"
/>
</:tabs>
</DPageHeader>
<div class="admin-container admin-config-page__main-area">
{{outlet}}
</div>
</div>

View File

@ -1,15 +1,10 @@
<DBreadcrumbsItem
@path="/admin/plugins/{{@model.plugin.name}}/settings"
@label={{i18n "admin.plugins.change_settings_short"}}
/>
<div
class="content-body admin-plugin-config-area__settings admin-detail pull-left"
>
<AdminFilteredSiteSettings
@initialFilter={{@model.initialFilter}}
@plugin={{@model.plugin}}
@settings={{@model.settings}}
@onFilterChanged={{this.filterChanged}}
<AdminAreaSettings
@plugin={{@model.plugin.id}}
@path="/admin/plugins/{{@model.plugin.name}}/settings"
@filter={{this.filter}}
@adminSettingsFilterChangedCallback={{this.adminSettingsFilterChangedCallback}}
/>
</div>

View File

@ -0,0 +1,83 @@
import { fillIn, render } from "@ember/test-helpers";
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender";
import AdminAreaSettings from "admin/components/admin-area-settings";
module("Integration | Component | AdminAreaSettings", function (hooks) {
hooks.beforeEach(function () {
pretender.get("/admin/config/site_settings.json", () =>
response({
site_settings: [
{
setting: "silence_new_user_sensitivity",
description:
"The likelihood that a new user will be silenced based on spam flags",
keywords: [],
default: "3",
value: "3",
category: "spam",
preview: null,
secret: false,
placeholder: null,
mandatory_values: null,
requires_confirmation: null,
type: "enum",
valid_values: [
{
name: "Disabled",
value: 0,
},
{
name: "Low",
value: 9,
},
{
name: "Medium",
value: 6,
},
{
name: "High",
value: 3,
},
],
translate_names: false,
},
{
setting: "num_users_to_silence_new_user",
description:
"If a new user's posts exceed the hide_post_sensitivity setting, and has spam flags from this many different users, hide all their posts and prevent future posting. 0 to disable.",
keywords: [],
default: "3",
value: "4",
category: "spam",
preview: null,
secret: false,
placeholder: null,
mandatory_values: null,
requires_confirmation: null,
type: "integer",
},
],
})
);
});
setupRenderingTest(hooks);
test("renders area settings and allows to filter", async function (assert) {
const callback = () => {};
await render(<template>
<AdminAreaSettings
@area="flags"
@adminSettingsFilterChangedCallback={{callback}}
@filter=""
/>
</template>);
assert.dom(".admin-site-settings-filter-controls").exists();
assert.dom(".setting-label").exists({ count: 2 });
await fillIn("#setting-filter", "num");
assert.dom(".setting-label").exists({ count: 1 });
});
});

View File

@ -424,20 +424,6 @@ $mobile-breakpoint: 700px;
color: var(--primary-medium);
}
}
.admin-users-list {
&__search {
@media screen and (max-width: 500px) {
width: 100%;
}
input {
min-width: 15em;
@media screen and (max-width: 500px) {
box-sizing: border-box;
width: 100%;
}
}
}
}
.ip-lookup {
position: relative;

View File

@ -1,10 +1,22 @@
.d-admin-filter {
background-color: var(--primary-very-low);
padding: var(--space-2);
display: flex;
}
.admin-users-list__search {
.admin-filter__input-container {
min-width: 50%;
@media screen and (max-width: 500px) {
width: 100%;
}
input {
min-width: 15em;
margin: 0;
@media screen and (max-width: 500px) {
box-sizing: border-box;
width: 100%;
}
}
}
.admin-filter__input {

View File

@ -813,10 +813,11 @@
width: 100%;
}
}
.permalink-search input {
width: 100%;
}
}
.admin-config-area__settings-no-results {
padding-left: 18px;
}
.admin-permalink-item {
&__delete.btn,
&__delete.btn:hover {

View File

@ -12,7 +12,8 @@ class Admin::Config::SiteSettingsController < Admin::AdminController
# UI itself uses the Admin::SiteSettingsController#index endpoint,
# which also supports a `category` and `plugin` filter.
def index
if params[:filter_names].blank? && SiteSetting.valid_areas.exclude?(params[:filter_area])
if params[:plugin].blank? && params[:categories].blank? && params[:filter_names].blank? &&
SiteSetting.valid_areas.exclude?(params[:filter_area])
raise Discourse::InvalidParameters
end
@ -21,6 +22,8 @@ class Admin::Config::SiteSettingsController < Admin::AdminController
SiteSetting.all_settings(
filter_names: params[:filter_names],
filter_area: params[:filter_area],
filter_plugin: params[:plugin],
filter_categories: Array.wrap(params[:categories]),
include_locale_setting: false,
include_hidden: true,
filter_allowed_hidden: ADMIN_CONFIG_AREA_ALLOWLISTED_HIDDEN_SETTINGS,

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class SiteSetting < ActiveRecord::Base
VALID_AREAS = %w[flags about emojis]
VALID_AREAS = %w[flags about emojis permalinks]
extend GlobalPath
extend SiteSettingExtension

View File

@ -7087,6 +7087,7 @@ en:
save: "save"
cancel: "cancel"
unmask: "unmask input"
not_found: "Settings could not be found"
site_settings:
emoji_list:
invalid_input: "Emoji list should only contain valid emoji names, eg: hugs"
@ -7352,6 +7353,9 @@ en:
back: "Back to Permalinks"
more_options: "More options"
copy_success: "Permalink copied to clipboard"
nav:
settings: "Settings"
permalinks: "Permalinks"
form:
label: "New:"
add_header: "Add permalink"

View File

@ -2700,6 +2700,7 @@ uncategorized:
type: list
list_type: simple
validator: "RegexpListValidator"
area: "permalinks"
max_similar_results: 5
minimum_topics_similar: 50

View File

@ -27,4 +27,10 @@ describe "Admin Permalinks Page", type: :system do
expect(admin_permalinks_page).to have_no_permalinks
end
it "allows admin to go to permalink settings page" do
admin_permalinks_page.visit
admin_permalinks_page.click_tab("settings")
expect(admin_permalinks_page).to have_active_tab("settings")
end
end

View File

@ -49,6 +49,15 @@ module PageObjects
def has_closed_permalink_menu?
has_no_css?(".permalink-menu-content")
end
def click_tab(tab)
has_css?(".admin-permalinks-tabs__#{tab}")
find(".admin-permalinks-tabs__#{tab}").click
end
def has_active_tab?(tab)
has_css?(".admin-permalinks-tabs__#{tab} .active")
end
end
end
end