mirror of
https://github.com/discourse/discourse.git
synced 2025-06-06 03:06:53 +08:00
FEATURE: rebranded admin logos settings (#31554)
Redesigned page to update site logos. `AdminBrandingLogoFormComponent` is attached to the old logos page and the new branding page. In the next steps, branding will replace the logos page. A new `AdminConfigAreaCardSection` component was added hidden and less frequently used settings. An image placeholder was also needed because many additional logos have a fallback to the site logo. Finally, `twitter_summary_large_image` was renamed to `x_summary_large_image`. Desktop  Mobile 
This commit is contained in:

committed by
GitHub

parent
76e58a55ed
commit
dbba838ef4
@ -0,0 +1,396 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { cached, tracked } from "@glimmer/tracking";
|
||||
import { fn } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import Form from "discourse/components/form";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { bind } from "discourse/lib/decorators";
|
||||
import getURL from "discourse/lib/get-url";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigAreaCardSection from "admin/components/admin-config-area-card-section";
|
||||
import SimpleList from "admin/components/simple-list";
|
||||
|
||||
export default class AdminBrandingLogoForm extends Component {
|
||||
@service siteSettings;
|
||||
@service toasts;
|
||||
@tracked placeholders = {};
|
||||
@tracked loading = false;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.#loadPlaceholders();
|
||||
}
|
||||
|
||||
@bind
|
||||
async #loadPlaceholders() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const result = await ajax("/admin/config/site_settings.json", {
|
||||
data: {
|
||||
categories: ["branding"],
|
||||
},
|
||||
});
|
||||
|
||||
result.site_settings.forEach((setting) => {
|
||||
if (setting.placeholder) {
|
||||
this.placeholders[setting.setting] = setting.placeholder;
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
handleUpload(type, upload, { set }) {
|
||||
if (upload) {
|
||||
set(type, getURL(upload.url));
|
||||
} else {
|
||||
set(type, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async save(data) {
|
||||
try {
|
||||
await ajax("/admin/config/branding/logo.json", {
|
||||
type: "PUT",
|
||||
data: {
|
||||
logo: data.logo,
|
||||
logo_dark: data.logo_dark,
|
||||
large_icon: data.large_icon,
|
||||
favicon: data.favicon,
|
||||
logo_small: data.logo_small,
|
||||
logo_small_dark: data.logo_small_dark,
|
||||
mobile_logo: data.mobile_logo,
|
||||
mobile_logo_dark: data.mobile_logo_dark,
|
||||
manifest_icon: data.manifest_icon,
|
||||
manifest_screenshots: data.manifest_screenshots,
|
||||
apple_touch_icon: data.apple_touch_icon,
|
||||
digest_logo: data.digest_logo,
|
||||
opengraph_image: data.opengraph_image,
|
||||
x_summary_large_image: data.x_summary_large_image,
|
||||
},
|
||||
});
|
||||
this.toasts.success({
|
||||
duration: 3000,
|
||||
data: {
|
||||
message: i18n("admin.config.branding.logo.form.saved"),
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
this.toasts.error({
|
||||
duration: 3000,
|
||||
data: {
|
||||
message: err.jqXHR.responseJSON.errors[0],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
updateManifestScreenshots(field, selected) {
|
||||
field.set(selected.join("|"));
|
||||
}
|
||||
|
||||
@cached
|
||||
get formData() {
|
||||
return {
|
||||
logo: this.siteSettings.logo,
|
||||
logo_dark_required: !!this.siteSettings.logo_dark,
|
||||
logo_dark: this.siteSettings.logo_dark,
|
||||
large_icon: this.siteSettings.large_icon,
|
||||
favicon: this.siteSettings.favicon,
|
||||
logo_small: this.siteSettings.logo_small,
|
||||
logo_small_dark_required: !!this.siteSettings.logo_small_dark,
|
||||
logo_small_dark: this.siteSettings.logo_small_dark,
|
||||
mobile_logo: this.siteSettings.mobile_logo,
|
||||
mobile_logo_dark_required: !!this.siteSettings.mobile_logo_dark,
|
||||
mobile_logo_dark: this.siteSettings.mobile_logo_dark,
|
||||
manifest_icon: this.siteSettings.manifest_icon,
|
||||
manifest_screenshots: this.siteSettings.manifest_screenshots,
|
||||
apple_touch_icon: this.siteSettings.apple_touch_icon,
|
||||
digest_logo: this.siteSettings.digest_logo,
|
||||
opengraph_image: this.siteSettings.opengraph_image,
|
||||
x_summary_large_image: this.siteSettings.x_summary_large_image,
|
||||
};
|
||||
}
|
||||
|
||||
<template>
|
||||
<ConditionalLoadingSpinner @condition={{this.loading}}>
|
||||
<Form
|
||||
@onSubmit={{this.save}}
|
||||
@data={{this.formData}}
|
||||
class="admin-logo-form"
|
||||
as |form transientData|
|
||||
>
|
||||
<form.Field
|
||||
@name="logo"
|
||||
@title={{i18n "admin.config.branding.logo.form.logo.title"}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.logo.description"
|
||||
}}
|
||||
@helpText={{i18n "admin.config.branding.logo.form.logo.help_text"}}
|
||||
@onSet={{fn this.handleUpload "logo"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="logo_dark_required"
|
||||
@title={{i18n "admin.config.branding.logo.form.logo_dark.required"}}
|
||||
@format="full"
|
||||
as |field|
|
||||
>
|
||||
<field.Toggle />
|
||||
</form.Field>
|
||||
{{#if transientData.logo_dark_required}}
|
||||
<form.Section>
|
||||
<form.Field
|
||||
@name="logo_dark"
|
||||
@title={{i18n "admin.config.branding.logo.form.logo_dark.title"}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.logo_dark.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "logo_dark"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
</form.Section>
|
||||
{{/if}}
|
||||
<form.Field
|
||||
@name="large_icon"
|
||||
@title={{i18n "admin.config.branding.logo.form.large_icon.title"}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.large_icon.description"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.large_icon.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "large_icon"}}
|
||||
@placeholderUrl={{this.placeholders.large_icon}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="favicon"
|
||||
@title={{i18n "admin.config.branding.logo.form.favicon.title"}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.favicon.description"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "favicon"}}
|
||||
@placeholderUrl={{this.placeholders.favicon}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="logo_small"
|
||||
@title={{i18n "admin.config.branding.logo.form.logo_small.title"}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.logo_small.description"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.logo_small.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "logo_small"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="logo_small_dark_required"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.logo_small_dark.required"
|
||||
}}
|
||||
@format="full"
|
||||
as |field|
|
||||
>
|
||||
<field.Toggle />
|
||||
</form.Field>
|
||||
{{#if transientData.logo_small_dark_required}}
|
||||
<form.Section>
|
||||
<form.Field
|
||||
@name="logo_small_dark"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.logo_small_dark.title"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.logo_small_dark.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "logo_small_dark"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
</form.Section>
|
||||
{{/if}}
|
||||
|
||||
<AdminConfigAreaCardSection
|
||||
@heading={{i18n "admin.config.branding.logo.form.mobile"}}
|
||||
class="admin-logo-form__mobile-section"
|
||||
@collapsable={{true}}
|
||||
@collapsed={{true}}
|
||||
>
|
||||
<:content>
|
||||
<form.Field
|
||||
@name="mobile_logo"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.mobile_logo.title"
|
||||
}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.mobile_logo.description"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.mobile_logo.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "mobile_logo"}}
|
||||
@placeholderUrl={{this.placeholders.mobile_logo}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="mobile_logo_dark_required"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.mobile_logo_dark.required"
|
||||
}}
|
||||
@format="full"
|
||||
as |field|
|
||||
>
|
||||
<field.Toggle />
|
||||
</form.Field>
|
||||
{{#if transientData.mobile_logo_dark_required}}
|
||||
<form.Section>
|
||||
<form.Field
|
||||
@name="mobile_logo_dark"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.mobile_logo_dark.title"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.mobile_logo_dark.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "mobile_logo_dark"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
</form.Section>
|
||||
{{/if}}
|
||||
<form.Field
|
||||
@name="manifest_icon"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.manifest_icon.title"
|
||||
}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.manifest_icon.description"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.manifest_icon.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "manifest_icon"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="manifest_screenshots"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.manifest_screenshots.title"
|
||||
}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.manifest_screenshots.description"
|
||||
}}
|
||||
@format="full"
|
||||
as |field|
|
||||
>
|
||||
<field.Custom>
|
||||
<SimpleList
|
||||
@id={{field.id}}
|
||||
@onChange={{fn this.updateManifestScreenshots field}}
|
||||
@inputDelimiter="|"
|
||||
@values={{field.value}}
|
||||
@allowAny={{true}}
|
||||
/>
|
||||
</field.Custom>
|
||||
</form.Field>
|
||||
<form.Field
|
||||
@name="apple_touch_icon"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.apple_touch_icon.title"
|
||||
}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.apple_touch_icon.description"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.apple_touch_icon.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "apple_touch_icon"}}
|
||||
@placeholderUrl={{this.placeholders.apple_touch_icon}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
</:content>
|
||||
</AdminConfigAreaCardSection>
|
||||
<AdminConfigAreaCardSection
|
||||
@heading={{i18n "admin.config.branding.logo.form.email"}}
|
||||
class="admin-logo-form__email-section"
|
||||
@collapsable={{true}}
|
||||
@collapsed={{true}}
|
||||
>
|
||||
<:content>
|
||||
<form.Field
|
||||
@name="digest_logo"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.digest_logo.title"
|
||||
}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.digest_logo.description"
|
||||
}}
|
||||
@helpText={{i18n
|
||||
"admin.config.branding.logo.form.digest_logo.help_text"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "digest_logo"}}
|
||||
@placeholderUrl={{this.placeholders.digest_logo}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image
|
||||
@type="branding"
|
||||
@placeholderUrl={{this.placeholders.digest_logo}}
|
||||
/>
|
||||
</form.Field>
|
||||
</:content>
|
||||
</AdminConfigAreaCardSection>
|
||||
<AdminConfigAreaCardSection
|
||||
@heading={{i18n "admin.config.branding.logo.form.social_media"}}
|
||||
class="admin-logo-form__social-media-section"
|
||||
@collapsable={{true}}
|
||||
@collapsed={{true}}
|
||||
>
|
||||
<:content>
|
||||
<form.Field
|
||||
@name="opengraph_image"
|
||||
@title={{i18n
|
||||
"admin.config.branding.logo.form.opengraph_image.title"
|
||||
}}
|
||||
@description={{i18n
|
||||
"admin.config.branding.logo.form.opengraph_image.description"
|
||||
}}
|
||||
@onSet={{fn this.handleUpload "opengraph_image"}}
|
||||
@placeholderUrl={{this.placeholders.opengraph_image}}
|
||||
as |field|
|
||||
>
|
||||
<field.Image @type="branding" />
|
||||
</form.Field>
|
||||
</:content>
|
||||
</AdminConfigAreaCardSection>
|
||||
<form.Submit />
|
||||
</Form>
|
||||
</ConditionalLoadingSpinner>
|
||||
</template>
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
|
||||
export default class AdminConfigAreaCardSection extends Component {
|
||||
@tracked collapsed = this.args.collapsed;
|
||||
|
||||
get headerCaretIcon() {
|
||||
return this.collapsed ? "plus" : "minus";
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSectionDisplay() {
|
||||
this.collapsed = !this.collapsed;
|
||||
}
|
||||
|
||||
<template>
|
||||
<section class="admin-config-area-card-section" ...attributes>
|
||||
<div class="admin-config-area-card-section__header-wrapper">
|
||||
<h4 class="admin-config-area-card-section__title">{{@heading}}</h4>
|
||||
{{#if @collapsable}}
|
||||
<DButton
|
||||
@title="sidebar.toggle_section"
|
||||
@action={{this.toggleSectionDisplay}}
|
||||
class="admin-config-area-card-section__toggle-button btn-transparent"
|
||||
>
|
||||
{{icon this.headerCaretIcon}}
|
||||
</DButton>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#unless this.collapsed}}
|
||||
<div class="admin-config-area-card-section__content">
|
||||
{{yield to="content"}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
</section>
|
||||
</template>
|
||||
}
|
@ -316,6 +316,7 @@ export default function () {
|
||||
this.route("logo", function () {
|
||||
this.route("settings", { path: "/" });
|
||||
});
|
||||
this.route("branding");
|
||||
this.route("navigation", function () {
|
||||
this.route("settings", { path: "/" });
|
||||
});
|
||||
|
@ -0,0 +1,36 @@
|
||||
import RouteTemplate from "ember-route-template";
|
||||
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
||||
import DPageHeader from "discourse/components/d-page-header";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminBrandingLogoForm from "admin/components/admin-branding-logo-form";
|
||||
import AdminConfigAreaCard from "admin/components/admin-config-area-card";
|
||||
|
||||
export default RouteTemplate(<template>
|
||||
<div class="admin-config-page">
|
||||
<DPageHeader
|
||||
@hideTabs={{true}}
|
||||
@titleLabel={{i18n "admin.config.branding.title"}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/branding"
|
||||
@label={{i18n "admin.config.branding.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
<div class="admin-config-area">
|
||||
<div class="admin-config-area__primary-content">
|
||||
<AdminConfigAreaCard
|
||||
@heading="admin.config.branding.logo.title"
|
||||
@collapsable={{true}}
|
||||
class="admin-config-area-branding__logo"
|
||||
>
|
||||
<:content>
|
||||
<AdminBrandingLogoForm />
|
||||
</:content>
|
||||
</AdminConfigAreaCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>);
|
@ -2,7 +2,8 @@ import RouteTemplate from "ember-route-template";
|
||||
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
||||
import DPageHeader from "discourse/components/d-page-header";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminAreaSettings from "admin/components/admin-area-settings";
|
||||
import AdminBrandingLogoForm from "admin/components/admin-branding-logo-form";
|
||||
import AdminConfigAreaCard from "admin/components/admin-config-area-card";
|
||||
|
||||
export default RouteTemplate(<template>
|
||||
<DPageHeader
|
||||
@ -19,13 +20,13 @@ export default RouteTemplate(<template>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
||||
<div class="admin-config-page__main-area">
|
||||
<AdminAreaSettings
|
||||
@showBreadcrumb={{false}}
|
||||
@categories="branding"
|
||||
@path="/admin/config/logo"
|
||||
@filter={{@controller.filter}}
|
||||
@adminSettingsFilterChangedCallback={{@controller.adminSettingsFilterChangedCallback}}
|
||||
/>
|
||||
</div>
|
||||
<AdminConfigAreaCard
|
||||
@heading="admin.config.branding.logo.title"
|
||||
@collapsable={{true}}
|
||||
class="admin-config-area-branding__logo"
|
||||
>
|
||||
<:content>
|
||||
<AdminBrandingLogoForm />
|
||||
</:content>
|
||||
</AdminConfigAreaCard>
|
||||
</template>);
|
||||
|
@ -28,6 +28,7 @@ export default class FKControlImage extends Component {
|
||||
@onUploadDeleted={{this.removeImage}}
|
||||
@type={{@type}}
|
||||
@disabled={{@field.disabled}}
|
||||
@placeholderUrl={{@field.args.placeholderUrl}}
|
||||
class="form-kit__control-image no-repeat contain-image"
|
||||
/>
|
||||
</template>
|
||||
|
@ -90,6 +90,7 @@ export default class FKField extends Component {
|
||||
@descriptionFormat={{@descriptionFormat}}
|
||||
@disabled={{@disabled}}
|
||||
@parentName={{@parentName}}
|
||||
@placeholderUrl={{@placeholderUrl}}
|
||||
as |field|
|
||||
>
|
||||
<this.wrapper @size={{@size}}>
|
||||
|
@ -0,0 +1,61 @@
|
||||
import { click, render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import AdminConfigAreaCardSection from "admin/components/admin-config-area-card-section";
|
||||
|
||||
module(
|
||||
"Integration | Component | AdminConfigAreaCardSection",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("renders admin config area card section without toggle button", async function (assert) {
|
||||
await render(<template>
|
||||
<AdminConfigAreaCardSection @heading="test heading"><:content
|
||||
>test</:content></AdminConfigAreaCardSection>
|
||||
</template>);
|
||||
|
||||
assert
|
||||
.dom(".admin-config-area-card-section__title")
|
||||
.hasText("test heading");
|
||||
assert.dom(".admin-config-area-card-section__content").exists();
|
||||
assert
|
||||
.dom(".admin-config-area-card-section__toggle-button")
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test("renders admin config area card section with toggle button", async function (assert) {
|
||||
await render(<template>
|
||||
<AdminConfigAreaCardSection
|
||||
@heading="test heading"
|
||||
@collapsable={{true}}
|
||||
><:content>test</:content></AdminConfigAreaCardSection>
|
||||
</template>);
|
||||
|
||||
assert
|
||||
.dom(".admin-config-area-card-section__title")
|
||||
.hasText("test heading");
|
||||
assert.dom(".admin-config-area-card-section__content").hasText("test");
|
||||
assert.dom(".admin-config-area-card-section__toggle-button").exists();
|
||||
|
||||
await click(".admin-config-area-card-section__toggle-button");
|
||||
assert.dom(".admin-config-area-card-section__content").doesNotExist();
|
||||
|
||||
await click(".admin-config-area-card-section__toggle-button");
|
||||
assert.dom(".admin-config-area-card-section__content").exists();
|
||||
});
|
||||
|
||||
test("renders admin config area card section with toggle button and collapsed by default", async function (assert) {
|
||||
await render(<template>
|
||||
<AdminConfigAreaCardSection
|
||||
@heading="test heading"
|
||||
@collapsable={{true}}
|
||||
@collapsed={{true}}
|
||||
><:content>test</:content></AdminConfigAreaCardSection>
|
||||
</template>);
|
||||
|
||||
assert.dom(".admin-config-area-card-section__title").exists();
|
||||
assert.dom(".admin-config-area-card-section__toggle-button").exists();
|
||||
assert.dom(".admin-config-area-card-section__content").doesNotExist();
|
||||
});
|
||||
}
|
||||
);
|
@ -123,3 +123,46 @@
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-config-area-card-section {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--space-4);
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
border: 1px solid var(--primary-low);
|
||||
|
||||
@media (max-width: $mobile-breakpoint) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__header-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin-top: 0.5rem;
|
||||
padding-right: 1em;
|
||||
padding-left: 0.5em;
|
||||
padding-bottom: 1em;
|
||||
gap: 1.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-logo-form {
|
||||
.form-kit__section {
|
||||
.form-kit__container {
|
||||
padding-left: 1em;
|
||||
width: calc(100% - 1em) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
app/controllers/admin/config/branding_controller.rb
Normal file
43
app/controllers/admin/config/branding_controller.rb
Normal file
@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
class Admin::Config::BrandingController < Admin::AdminController
|
||||
def index
|
||||
end
|
||||
|
||||
def logo
|
||||
SiteSetting::Update.call(
|
||||
guardian:,
|
||||
params: {
|
||||
settings: {
|
||||
logo: params[:logo],
|
||||
logo_dark: params[:logo_dark],
|
||||
large_icon: params[:large_icon],
|
||||
favicon: params[:favicon],
|
||||
logo_small: params[:logo_small],
|
||||
logo_small_dark: params[:logo_small_dark],
|
||||
mobile_logo: params[:mobile_logo],
|
||||
mobile_logo_dark: params[:mobile_logo_dark],
|
||||
manifest_icon: params[:manifest_icon],
|
||||
manifest_screenshots: params[:manifest_screenshots],
|
||||
apple_touch_icon: params[:apple_touch_icon],
|
||||
digest_logo: params[:digest_logo],
|
||||
opengraph_image: params[:opengraph_image],
|
||||
x_summary_large_image: params[:x_summary_large_image],
|
||||
},
|
||||
},
|
||||
) do
|
||||
on_success { render json: success_json }
|
||||
on_failed_policy(:settings_are_visible) do |policy|
|
||||
raise Discourse::InvalidParameters, policy.reason
|
||||
end
|
||||
on_failed_policy(:settings_are_unshadowed_globally) do |policy|
|
||||
raise Discourse::InvalidParameters, policy.reason
|
||||
end
|
||||
on_failed_policy(:settings_are_configurable) do |policy|
|
||||
raise Discourse::InvalidParameters, policy.reason
|
||||
end
|
||||
on_failed_policy(:values_are_valid) do |policy|
|
||||
raise Discourse::InvalidParameters, policy.reason
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -5232,6 +5232,72 @@ en:
|
||||
logo:
|
||||
title: "Site logo"
|
||||
header_description: "Customize the variations of your site logo"
|
||||
branding:
|
||||
title: "Branding"
|
||||
logo:
|
||||
title: "Logo"
|
||||
form:
|
||||
saved: "Logo settings are saved."
|
||||
logo:
|
||||
title: "Primary logo"
|
||||
description: "Appears on the site's top navigation, as well as the top of the site's Email Notifications."
|
||||
help_text: "Recommended size is 600 x 80 pixels."
|
||||
logo_dark:
|
||||
required: "Use a different logo for dark mode?"
|
||||
title: "Primary logo dark"
|
||||
help_text: "Recommended size is 600 x 80 pixels."
|
||||
large_icon:
|
||||
title: "Square icon"
|
||||
description: "A squared version of the logo image appears at the top of the administration and is also the mobile home screen app logo."
|
||||
help_text: "Recommended size is 512 x 512 pixels."
|
||||
square_icon_dark:
|
||||
required: "Use a different square icon for dark mode?"
|
||||
title: "Square icon dark"
|
||||
help_text: "Recommended size is 512 x 512 pixels."
|
||||
favicon:
|
||||
title: "Favicon"
|
||||
description: "The logo will appear as the icon in the browser tab and the browser favorites/bookmarks."
|
||||
logo_small:
|
||||
title: "Small logo"
|
||||
description: "The small logo image at the top left of your site, seen when scrolling down. If left blank, a home glyph will be shown."
|
||||
help_text: "Recommended size is 120 x 120 pixels."
|
||||
logo_small_dark:
|
||||
required: "Use a different small logo for dark mode?"
|
||||
title: "Small logo dark"
|
||||
help_text: "Recommended size is 120 x 120 pixels."
|
||||
mobile: "Mobile"
|
||||
email: "Email"
|
||||
social_media: "Social media"
|
||||
mobile_logo:
|
||||
title: "Mobile logo"
|
||||
description: "The logo used on mobile version of your site. If left blank, the image from the `logo` setting will be used."
|
||||
help_text: "Use a wide rectangular image with a height of 120 and an aspect ratio greater than 3:1."
|
||||
mobile_logo_dark:
|
||||
required: "Use a different mobile logo for dark mode?"
|
||||
title: "Mobile logo dark"
|
||||
help_text: "Use a wide rectangular image with a height of 120 and an aspect ratio greater than 3:1."
|
||||
manifest_icon:
|
||||
title: "Manifest icon"
|
||||
description: "Image used as logo/splash image on Android. If left blank, large_icon will be used."
|
||||
help_text: "Recommended size is 512 x 512 pixels."
|
||||
manifest_screenshots:
|
||||
title: "Manifest screenshots"
|
||||
description: "Screenshots that showcase your instance features and functionality on its install prompt page. All images should be local uploads and of the same dimensions."
|
||||
apple_touch_icon:
|
||||
title: "Apple touch icon"
|
||||
description: "Icon used for Apple touch devices. If left blank, large_icon will be used."
|
||||
help_text: "Recommended size is 180 x 180 pixels. A transparent background is not recommended."
|
||||
digest_logo:
|
||||
title: "Digest logo"
|
||||
description: "The alternate logo image used at the top of your site's email summary. If left blank, the image from the `logo` setting will be used."
|
||||
help_text: "Use a wide rectangle image. Don't use an SVG image."
|
||||
opengraph_image:
|
||||
title: "OpenGraph image"
|
||||
description: "Default opengraph image, used when the page has no other suitable image. If left blank, large_icon will be used."
|
||||
x_summary_large_image:
|
||||
title: "X summary large image"
|
||||
description: "X card 'summary large image'. If left blank, regular card metadata is generated using the OpenGraph_image, as long as that is not also a .svg."
|
||||
help_text: "recommended size is at least 280 x 150 pixels. Don't use an SVG image."
|
||||
navigation:
|
||||
title: "Navigation"
|
||||
header_description: "Configure the navigation links and menu items for your site. This includes the location and behavior of the primary navigation menu, the quick links at the top of the homepage, as well as the admin sidebar"
|
||||
|
@ -365,7 +365,7 @@ en:
|
||||
slow_down_crawler_user_agent_must_be_at_least_3_characters: "User agents must be at least 3 characters long to avoid accidentally rate-limiting legitimate users."
|
||||
slow_down_crawler_user_agent_cannot_be_popular_browsers: "You cannot add any of the following values to the setting: %{values}."
|
||||
strip_image_metadata_cannot_be_disabled_if_composer_media_optimization_image_enabled: "You cannot disable strip image metadata if 'composer media optimization image enabled' is enabled. Disable 'composer media optimization image enabled' before disabling strip image metadata."
|
||||
twitter_summary_large_image_no_svg: "Twitter summary images used for twitter:image metadata cannot be an .svg image."
|
||||
x_summary_large_image_no_svg: "Twitter summary images used for twitter:image metadata cannot be an .svg image."
|
||||
tl0_and_anonymous_flag: "Either 'site contact email' or 'email address to report illegal content' must be provided for anonymous users."
|
||||
|
||||
conflicting_google_user_id: 'The Google Account ID for this account has changed; staff intervention is required for security reasons. Please contact staff and point them to <br><a href="https://meta.discourse.org/t/76575">https://meta.discourse.org/t/76575</a>'
|
||||
@ -1834,7 +1834,7 @@ en:
|
||||
favicon: "A favicon for your site, see <a href='https://en.wikipedia.org/wiki/Favicon' target='_blank'>https://en.wikipedia.org/wiki/Favicon</a>. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, large_icon will be used."
|
||||
apple_touch_icon: "Icon used for Apple touch devices. A transparent background is not reccomended. Will be automatically resized to 180x180. If left blank, large_icon will be used."
|
||||
opengraph_image: "Default opengraph image, used when the page has no other suitable image. If left blank, large_icon will be used"
|
||||
twitter_summary_large_image: "Twitter card 'summary large image' (should be at least 280 in width, and at least 150 in height, cannot be .svg). If left blank, regular card metadata is generated using the opengraph_image, as long as that is not also a .svg"
|
||||
x_summary_large_image: "Twitter card 'summary large image' (should be at least 280 in width, and at least 150 in height, cannot be .svg). If left blank, regular card metadata is generated using the opengraph_image, as long as that is not also a .svg"
|
||||
|
||||
notification_email: "The from: email address used when sending all essential system emails. The domain specified here must have SPF, DKIM and reverse PTR records set correctly for email to arrive."
|
||||
email_custom_headers: "A pipe-delimited list of custom email headers"
|
||||
|
@ -412,6 +412,8 @@ Discourse::Application.routes.draw do
|
||||
get "experimental" => "site_settings#index"
|
||||
get "trust-levels" => "site_settings#index"
|
||||
get "group-permissions" => "site_settings#index"
|
||||
get "branding" => "branding#index"
|
||||
put "branding/logo" => "branding#logo"
|
||||
|
||||
resources :flags, only: %i[index new create update destroy] do
|
||||
put "toggle"
|
||||
|
@ -125,10 +125,12 @@ branding:
|
||||
type: upload
|
||||
manifest_icon:
|
||||
default: ""
|
||||
client: true
|
||||
type: upload
|
||||
manifest_screenshots:
|
||||
type: list
|
||||
list_type: simple
|
||||
client: true
|
||||
default: ""
|
||||
favicon:
|
||||
default: ""
|
||||
@ -140,10 +142,16 @@ branding:
|
||||
type: upload
|
||||
opengraph_image:
|
||||
default: ""
|
||||
client: true
|
||||
type: upload
|
||||
twitter_summary_large_image:
|
||||
default: ""
|
||||
type: upload
|
||||
hidden: true
|
||||
x_summary_large_image:
|
||||
default: ""
|
||||
type: upload
|
||||
client: true
|
||||
|
||||
basic:
|
||||
display_local_time_in_user_card:
|
||||
|
@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class XSummaryLargeImageBasedOnDeprecatedSetting < ActiveRecord::Migration[7.2]
|
||||
def up
|
||||
old_setting =
|
||||
DB.query_single(
|
||||
"SELECT value FROM site_settings WHERE name = 'twitter_summary_large_image' LIMIT 1",
|
||||
).first
|
||||
|
||||
if old_setting.present?
|
||||
DB.exec(
|
||||
"INSERT INTO site_settings(name, value, data_type, created_at, updated_at)
|
||||
VALUES('x_summary_large_image', :setting, '18', NOW(), NOW())",
|
||||
setting: old_setting,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
old_setting =
|
||||
DB.query_single(
|
||||
"SELECT value FROM site_settings WHERE name = 'x_summary_large_image' LIMIT 1",
|
||||
).first
|
||||
|
||||
if old_setting.present?
|
||||
DB.exec(
|
||||
"INSERT INTO site_settings(name, value, data_type, created_at, updated_at)
|
||||
VALUES('twitter_summary_large_image', :setting, '18', NOW(), NOW())
|
||||
ON CONFLICT DO UPDATE",
|
||||
setting: old_setting,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
@ -45,7 +45,7 @@ module SiteSettings::DeprecatedSettings
|
||||
false,
|
||||
"3.3",
|
||||
],
|
||||
["min_first_post_typing_time", "fast_typing_threshold", false, "3.4"],
|
||||
["twitter_summary_large_image", "x_summary_large_image", false, "3.4"],
|
||||
]
|
||||
|
||||
OVERRIDE_TL_GROUP_SETTINGS = %w[
|
||||
|
@ -261,10 +261,10 @@ module SiteSettings::Validations
|
||||
validate_error :strip_image_metadata_cannot_be_disabled_if_composer_media_optimization_image_enabled
|
||||
end
|
||||
|
||||
def validate_twitter_summary_large_image(new_val)
|
||||
def validate_x_summary_large_image(new_val)
|
||||
return if new_val.blank?
|
||||
return if !Upload.exists?(id: new_val, extension: "svg")
|
||||
validate_error :twitter_summary_large_image_no_svg
|
||||
validate_error :x_summary_large_image_no_svg
|
||||
end
|
||||
|
||||
def validate_allow_all_users_to_flag_illegal_content(new_val)
|
||||
|
@ -462,16 +462,16 @@ RSpec.describe SiteSettings::Validations do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#twitter_summary_large_image" do
|
||||
describe "#x_summary_large_image" do
|
||||
it "does not allow SVG image files" do
|
||||
upload = Fabricate(:upload, url: "/images/logo-dark.svg", extension: "svg")
|
||||
expect { validations.validate_twitter_summary_large_image(upload.id) }.to raise_error(
|
||||
expect { validations.validate_x_summary_large_image(upload.id) }.to raise_error(
|
||||
Discourse::InvalidParameters,
|
||||
I18n.t("errors.site_settings.twitter_summary_large_image_no_svg"),
|
||||
I18n.t("errors.site_settings.x_summary_large_image_no_svg"),
|
||||
)
|
||||
upload.update!(url: "/images/logo-dark.png", extension: "png")
|
||||
expect { validations.validate_twitter_summary_large_image(upload.id) }.not_to raise_error
|
||||
expect { validations.validate_twitter_summary_large_image(nil) }.not_to raise_error
|
||||
expect { validations.validate_x_summary_large_image(upload.id) }.not_to raise_error
|
||||
expect { validations.validate_x_summary_large_image(nil) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
|
211
spec/system/admin_branding_spec.rb
Normal file
211
spec/system/admin_branding_spec.rb
Normal file
@ -0,0 +1,211 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe "Admin Branding Page", type: :system do
|
||||
fab!(:admin)
|
||||
fab!(:image_upload)
|
||||
|
||||
let(:branding_page) { PageObjects::Pages::AdminBranding.new }
|
||||
let(:image_file) { file_from_fixtures("logo.png", "images") }
|
||||
|
||||
before { sign_in(admin) }
|
||||
|
||||
describe "primary section" do
|
||||
let(:primary_section_logos) { %i[logo logo_dark large_icon favicon logo_small logo_small_dark] }
|
||||
it "can upload images and dark versions" do
|
||||
branding_page.visit
|
||||
|
||||
expect(branding_page.logo_form).to have_no_form_field(:logo_dark)
|
||||
branding_page.logo_form.toggle_dark_mode(:logo_dark_required)
|
||||
expect(branding_page.logo_form).to have_form_field(:logo_dark)
|
||||
|
||||
expect(branding_page.logo_form).to have_no_form_field(:logo_small_dark)
|
||||
branding_page.logo_form.toggle_dark_mode(:logo_small_dark_required)
|
||||
expect(branding_page.logo_form).to have_form_field(:logo_small_dark)
|
||||
|
||||
primary_section_logos.each do |image_type|
|
||||
branding_page.logo_form.upload_image(image_type, image_file)
|
||||
end
|
||||
|
||||
primary_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
visit("/")
|
||||
branding_page.visit
|
||||
|
||||
expect(branding_page.logo_form).to have_form_field(:logo_dark)
|
||||
expect(branding_page.logo_form).to have_form_field(:logo_small_dark)
|
||||
|
||||
primary_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
end
|
||||
|
||||
it "can remove images" do
|
||||
primary_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) }
|
||||
|
||||
branding_page.visit
|
||||
|
||||
primary_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
primary_section_logos.each { |image_type| branding_page.logo_form.remove_image(image_type) }
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
primary_section_logos.each { |image_type| expect(SiteSetting.send(image_type)).to eq(nil) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "mobile section" do
|
||||
let(:mobile_section_logos) { %i[mobile_logo mobile_logo_dark manifest_icon apple_touch_icon] }
|
||||
it "can upload images and dark versions" do
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_mobile_section
|
||||
|
||||
expect(branding_page.logo_form).to have_no_form_field(:mobile_logo_dark)
|
||||
branding_page.logo_form.toggle_dark_mode(:mobile_logo_dark_required)
|
||||
expect(branding_page.logo_form).to have_form_field(:mobile_logo_dark)
|
||||
|
||||
mobile_section_logos.each do |image_type|
|
||||
branding_page.logo_form.upload_image(image_type, image_file)
|
||||
end
|
||||
|
||||
mobile_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
visit("/")
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_mobile_section
|
||||
|
||||
expect(branding_page.logo_form).to have_form_field(:mobile_logo_dark)
|
||||
|
||||
mobile_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
end
|
||||
|
||||
it "can remove images" do
|
||||
mobile_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) }
|
||||
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_mobile_section
|
||||
|
||||
mobile_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
mobile_section_logos.each { |image_type| branding_page.logo_form.remove_image(image_type) }
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
mobile_section_logos.each { |image_type| expect(SiteSetting.send(image_type)).to eq(nil) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "email section" do
|
||||
let(:email_section_logos) { %i[digest_logo] }
|
||||
it "can upload images" do
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_email_section
|
||||
|
||||
email_section_logos.each do |image_type|
|
||||
branding_page.logo_form.upload_image(image_type, image_file)
|
||||
end
|
||||
|
||||
email_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
visit("/")
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_email_section
|
||||
|
||||
email_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
end
|
||||
|
||||
it "can remove images" do
|
||||
email_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) }
|
||||
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_email_section
|
||||
|
||||
email_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
email_section_logos.each { |image_type| branding_page.logo_form.remove_image(image_type) }
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
email_section_logos.each { |image_type| expect(SiteSetting.send(image_type)).to eq(nil) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "social media section" do
|
||||
let(:social_media_section_logos) { %i[opengraph_image] }
|
||||
it "can upload images" do
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_social_media_section
|
||||
|
||||
social_media_section_logos.each do |image_type|
|
||||
branding_page.logo_form.upload_image(image_type, image_file)
|
||||
end
|
||||
|
||||
social_media_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
visit("/")
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_social_media_section
|
||||
|
||||
social_media_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
end
|
||||
|
||||
it "can remove images" do
|
||||
social_media_section_logos.each do |image_type|
|
||||
SiteSetting.send("#{image_type}=", image_upload)
|
||||
end
|
||||
|
||||
branding_page.visit
|
||||
branding_page.logo_form.expand_social_media_section
|
||||
|
||||
social_media_section_logos.each do |image_type|
|
||||
expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image
|
||||
end
|
||||
|
||||
social_media_section_logos.each do |image_type|
|
||||
branding_page.logo_form.remove_image(image_type)
|
||||
end
|
||||
|
||||
branding_page.logo_form.submit
|
||||
expect(branding_page.logo_form).to have_saved_successfully
|
||||
|
||||
social_media_section_logos.each do |image_type|
|
||||
expect(SiteSetting.send(image_type)).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Components
|
||||
class AdminBrandingLogoForm < PageObjects::Components::Base
|
||||
def image_uploader(image_type)
|
||||
PageObjects::Components::UppyImageUploader.new(
|
||||
find(".form-kit__container[data-name='#{image_type}']"),
|
||||
)
|
||||
end
|
||||
|
||||
def upload_image(image_type, image_file)
|
||||
image_uploader(image_type).select_image_with_keyboard(image_file.path)
|
||||
end
|
||||
|
||||
def remove_image(image_type)
|
||||
image_uploader(image_type).remove_image
|
||||
end
|
||||
|
||||
def has_no_form_field?(field)
|
||||
page.has_no_css?("#control-#{field}")
|
||||
end
|
||||
|
||||
def has_form_field?(field)
|
||||
page.has_css?("#control-#{field}")
|
||||
end
|
||||
|
||||
def toggle_dark_mode(field)
|
||||
PageObjects::Components::DToggleSwitch.new(
|
||||
".form-kit__field-toggle[data-name='#{field}'] .d-toggle-switch button",
|
||||
).toggle
|
||||
end
|
||||
|
||||
def expand_mobile_section
|
||||
find(
|
||||
".admin-logo-form__mobile-section .admin-config-area-card-section__toggle-button",
|
||||
).click
|
||||
end
|
||||
|
||||
def expand_email_section
|
||||
find(".admin-logo-form__email-section .admin-config-area-card-section__toggle-button").click
|
||||
end
|
||||
|
||||
def expand_social_media_section
|
||||
find(
|
||||
".admin-logo-form__social-media-section .admin-config-area-card-section__toggle-button",
|
||||
).click
|
||||
end
|
||||
|
||||
def submit
|
||||
form.submit
|
||||
end
|
||||
|
||||
def has_saved_successfully?
|
||||
PageObjects::Components::Toasts.new.has_success?(
|
||||
I18n.t("admin_js.admin.config.branding.logo.form.saved"),
|
||||
)
|
||||
end
|
||||
|
||||
def form
|
||||
@form ||= PageObjects::Components::FormKit.new(".admin-logo-form")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
15
spec/system/page_objects/pages/admin_branding.rb
Normal file
15
spec/system/page_objects/pages/admin_branding.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Pages
|
||||
class AdminBranding < PageObjects::Pages::Base
|
||||
def visit
|
||||
page.visit("/admin/config/branding")
|
||||
end
|
||||
|
||||
def logo_form
|
||||
@logo_form ||= PageObjects::Components::AdminBrandingLogoForm.new
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user