From 928f9175f05413289c45d3fd6424f62956ea0944 Mon Sep 17 00:00:00 2001 From: Krzysztof Kotlarek Date: Mon, 7 Apr 2025 10:28:42 +0800 Subject: [PATCH] FEATURE: fonts section for branding page (#32031) New configure fonts section was added. Because now we have two sections completed (logos and fonts), new /branding page was introduced and old /logo and /font pages was removed. When text size is changed, modal is displayed to ask if preferences of existing users should be retrospectively updated. https://github.com/user-attachments/assets/f6b0c92a-117f-4064-bd76-30fa05acc6d3 --------- Co-authored-by: Ella Co-authored-by: Alan Guo Xiang Tan --- .../admin-branding-font-chooser.gjs | 63 +++ .../components/admin-branding-fonts-form.gjs | 163 ++++++++ .../components/admin-branding-logo-form.gjs | 2 + .../admin-config-fonts-settings.js | 3 - .../javascripts/admin/addon/lib/constants.js | 40 ++ .../admin/addon/mixins/setting-component.js | 3 +- .../admin/addon/models/site-setting.js | 8 +- .../addon/routes/admin-config-branding.js | 8 + .../admin/addon/routes/admin-config-fonts.js | 8 - .../admin/addon/routes/admin-config-logo.js | 8 - .../admin/addon/routes/admin-route-map.js | 6 - .../services/site-setting-change-tracker.js | 51 +++ .../admin/addon/templates/config-branding.gjs | 14 + .../addon/templates/config-fonts-settings.gjs | 33 -- .../addon/templates/config-logo-settings.gjs | 34 -- .../templates/site-settings-category.gjs | 6 +- .../modal/update-default-text-size.gjs | 44 ++ .../discourse/app/lib/constants.js | 10 + .../app/lib/sidebar/admin-nav-map.js | 16 +- .../stylesheets/admin/admin_config_area.scss | 100 +++++ .../admin/config/branding_controller.rb | 32 ++ app/models/default_text_size_setting.rb | 15 + app/models/user_option.rb | 2 +- config/locales/client.en.yml | 28 +- config/routes.rb | 3 +- config/site_settings.yml | 9 +- lib/tasks/javascript.rake | 12 + .../admin/config/branding_controller_spec.rb | 73 ++++ spec/system/admin_branding_spec.rb | 383 ++++++++++-------- .../components/admin-branding-fonts-form.rb | 49 +++ .../page_objects/pages/admin_branding.rb | 4 + 31 files changed, 944 insertions(+), 286 deletions(-) create mode 100644 app/assets/javascripts/admin/addon/components/admin-branding-font-chooser.gjs create mode 100644 app/assets/javascripts/admin/addon/components/admin-branding-fonts-form.gjs delete mode 100644 app/assets/javascripts/admin/addon/controllers/admin-config-fonts-settings.js create mode 100644 app/assets/javascripts/admin/addon/routes/admin-config-branding.js delete mode 100644 app/assets/javascripts/admin/addon/routes/admin-config-fonts.js delete mode 100644 app/assets/javascripts/admin/addon/routes/admin-config-logo.js delete mode 100644 app/assets/javascripts/admin/addon/templates/config-fonts-settings.gjs delete mode 100644 app/assets/javascripts/admin/addon/templates/config-logo-settings.gjs create mode 100644 app/assets/javascripts/discourse/app/components/modal/update-default-text-size.gjs create mode 100644 app/models/default_text_size_setting.rb create mode 100644 spec/requests/admin/config/branding_controller_spec.rb create mode 100644 spec/system/page_objects/components/admin-branding-fonts-form.rb diff --git a/app/assets/javascripts/admin/addon/components/admin-branding-font-chooser.gjs b/app/assets/javascripts/admin/addon/components/admin-branding-font-chooser.gjs new file mode 100644 index 00000000000..8bc561a0521 --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/admin-branding-font-chooser.gjs @@ -0,0 +1,63 @@ +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { concat, fn } from "@ember/helper"; +import { action } from "@ember/object"; +import { dasherize } from "@ember/string"; +import DButton from "discourse/components/d-button"; +import concatClass from "discourse/helpers/concat-class"; +import { i18n } from "discourse-i18n"; +import { MAIN_FONTS, MORE_FONTS } from "admin/lib/constants"; +import eq from "truth-helpers/helpers/eq"; + +export default class AdminBrandingFontChooser extends Component { + @tracked showMoreFonts = MORE_FONTS.map((font) => font.key).includes( + this.args.selectedFont + ); + + @action + setButtonValue(fieldSet, value) { + fieldSet(value); + } + + @action + toggleMoreFonts() { + this.showMoreFonts = !this.showMoreFonts; + } + + +} diff --git a/app/assets/javascripts/admin/addon/components/admin-branding-fonts-form.gjs b/app/assets/javascripts/admin/addon/components/admin-branding-fonts-form.gjs new file mode 100644 index 00000000000..935cd9153e6 --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/admin-branding-fonts-form.gjs @@ -0,0 +1,163 @@ +import Component from "@glimmer/component"; +import { fn } from "@ember/helper"; +import { action } from "@ember/object"; +import { service } from "@ember/service"; +import { decamelize, underscore } from "@ember/string"; +import DButton from "discourse/components/d-button"; +import Form from "discourse/components/form"; +import UpdateDefaultTextSize from "discourse/components/modal/update-default-text-size"; +import concatClass from "discourse/helpers/concat-class"; +import { ajax } from "discourse/lib/ajax"; +import { bind } from "discourse/lib/decorators"; +import { i18n } from "discourse-i18n"; +import AdminBrandingFontChooser from "admin/components/admin-branding-font-chooser"; +import { DEFAULT_TEXT_SIZES } from "admin/lib/constants"; +import eq from "truth-helpers/helpers/eq"; + +export default class AdminBrandingFontsForm extends Component { + @service siteSettings; + @service siteSettingChangeTracker; + @service toasts; + @service modal; + @service router; + + updateExistingUsers = null; + + @bind + setUpdateExistingUsers(value) { + this.updateExistingUsers = value; + } + + @action + setButtonValue(fieldSet, value) { + fieldSet(decamelize(underscore(value))); + } + + @action + async update(data) { + if (this.siteSettings.default_text_size === data.default_text_size) { + await this.#save(data); + return; + } + + const result = await ajax( + `/admin/site_settings/default_text_size/user_count.json`, + { + type: "PUT", + data: { + default_text_size: data.default_text_size, + }, + } + ); + + const count = result.user_count; + if (count > 0) { + await this.modal.show(UpdateDefaultTextSize, { + model: { + setUpdateExistingUsers: this.setUpdateExistingUsers, + count, + }, + }); + await this.#save(data); + } else { + await this.#save(data); + } + } + + @action + async #save(data) { + try { + await ajax("/admin/config/branding/fonts.json", { + type: "PUT", + data: { + base_font: data.base_font, + heading_font: data.heading_font, + default_text_size: data.default_text_size, + update_existing_users: this.updateExistingUsers, + }, + }); + this.toasts.success({ + duration: 3000, + data: { + message: i18n("admin.config.branding.fonts.form.saved"), + }, + }); + this.siteSettingChangeTracker.refreshPage(); + } catch (err) { + this.toasts.error({ + duration: 3000, + data: { + message: err.jqXHR.responseJSON.errors[0], + }, + }); + } + } + + get formData() { + return { + base_font: this.siteSettings.base_font, + heading_font: this.siteSettings.heading_font, + default_text_size: this.siteSettings.default_text_size, + }; + } + + +} diff --git a/app/assets/javascripts/admin/addon/components/admin-branding-logo-form.gjs b/app/assets/javascripts/admin/addon/components/admin-branding-logo-form.gjs index 3910e37d944..8ef88c959fb 100644 --- a/app/assets/javascripts/admin/addon/components/admin-branding-logo-form.gjs +++ b/app/assets/javascripts/admin/addon/components/admin-branding-logo-form.gjs @@ -14,6 +14,7 @@ import SimpleList from "admin/components/simple-list"; export default class AdminBrandingLogoForm extends Component { @service siteSettings; + @service siteSettingChangeTracker; @service toasts; @tracked placeholders = {}; @@ -81,6 +82,7 @@ export default class AdminBrandingLogoForm extends Component { message: i18n("admin.config.branding.logo.form.saved"), }, }); + this.siteSettingChangeTracker.refreshPage(); } catch (err) { this.toasts.error({ duration: 3000, diff --git a/app/assets/javascripts/admin/addon/controllers/admin-config-fonts-settings.js b/app/assets/javascripts/admin/addon/controllers/admin-config-fonts-settings.js deleted file mode 100644 index 0a3a6183c1c..00000000000 --- a/app/assets/javascripts/admin/addon/controllers/admin-config-fonts-settings.js +++ /dev/null @@ -1,3 +0,0 @@ -import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base"; - -export default class AdminConfigFontsSettingsController extends AdminAreaSettingsBaseController {} diff --git a/app/assets/javascripts/admin/addon/lib/constants.js b/app/assets/javascripts/admin/addon/lib/constants.js index 2465bc4acb7..82a3433a9b8 100644 --- a/app/assets/javascripts/admin/addon/lib/constants.js +++ b/app/assets/javascripts/admin/addon/lib/constants.js @@ -82,3 +82,43 @@ export const DEFAULT_USER_PREFERENCES = [ "default_sidebar_link_to_filtered_list", "default_sidebar_show_count_of_new_items", ]; + +export const MAIN_FONTS = [ + { key: "open_sans", name: "Open Sans" }, + { key: "roboto", name: "Roboto" }, + { key: "lato", name: "Lato" }, + { key: "inter", name: "Inter" }, + { key: "montserrat", name: "Montserrat" }, + { key: "poppins", name: "Poppins" }, + { key: "merriweather", name: "Merriweather" }, + { key: "mukta", name: "Mukta" }, + { key: "helvetica", name: "Helvetica" }, +]; + +export const MORE_FONTS = [ + { key: "arial", name: "Arial" }, + { key: "system", name: "System" }, + { key: "oxanium", name: "Oxanium" }, + { key: "noto_sans_jp", name: "NotoSansJP" }, + { key: "roboto_condensed", name: "RobotoCondensed" }, + { key: "source_sans_pro", name: "SourceSansPro" }, + { key: "oswald", name: "Oswald" }, + { key: "raleway", name: "Raleway" }, + { key: "roboto_mono", name: "RobotoMono" }, + { key: "noto_sans", name: "NotoSans" }, + { key: "roboto_slab", name: "RobotoSlab" }, + { key: "ubuntu", name: "Ubuntu" }, + { key: "pt_sans", name: "PTSans" }, + { key: "playfair_display", name: "PlayfairDisplay" }, + { key: "nunito", name: "Nunito" }, + { key: "lora", name: "Lora" }, + { key: "jet_brains_mono", name: "JetBrains Mono" }, +]; + +export const DEFAULT_TEXT_SIZES = [ + "smallest", + "smaller", + "normal", + "larger", + "largest", +]; diff --git a/app/assets/javascripts/admin/addon/mixins/setting-component.js b/app/assets/javascripts/admin/addon/mixins/setting-component.js index 3b992e7197f..5a02bf4f97a 100644 --- a/app/assets/javascripts/admin/addon/mixins/setting-component.js +++ b/app/assets/javascripts/admin/addon/mixins/setting-component.js @@ -209,8 +209,9 @@ export default Mixin.create({ this.setting.validationMessage = null; this.buffered.applyChanges(); + if (this.setting.requiresReload) { - this.afterSave(); + this.siteSettingChangeTracker.refreshPage(); } } catch (e) { const json = e.jqXHR?.responseJSON; diff --git a/app/assets/javascripts/admin/addon/models/site-setting.js b/app/assets/javascripts/admin/addon/models/site-setting.js index 423758aa91a..ccc8c793d03 100644 --- a/app/assets/javascripts/admin/addon/models/site-setting.js +++ b/app/assets/javascripts/admin/addon/models/site-setting.js @@ -11,7 +11,13 @@ import { } from "admin/lib/constants"; import SettingObjectHelper from "admin/lib/setting-object-helper"; -const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"]; +const AUTO_REFRESH_ON_SAVE = [ + "logo", + "mobile_logo", + "base_font", + "heading_font", + "default_text_size", +]; export default class SiteSetting extends EmberObject { static findAll(params = {}) { diff --git a/app/assets/javascripts/admin/addon/routes/admin-config-branding.js b/app/assets/javascripts/admin/addon/routes/admin-config-branding.js new file mode 100644 index 00000000000..ccf646de9e1 --- /dev/null +++ b/app/assets/javascripts/admin/addon/routes/admin-config-branding.js @@ -0,0 +1,8 @@ +import DiscourseRoute from "discourse/routes/discourse"; +import { i18n } from "discourse-i18n"; + +export default class AdminConfigBrandingRoute extends DiscourseRoute { + titleToken() { + return i18n("admin.config.branding.title"); + } +} diff --git a/app/assets/javascripts/admin/addon/routes/admin-config-fonts.js b/app/assets/javascripts/admin/addon/routes/admin-config-fonts.js deleted file mode 100644 index 00c88e5afbb..00000000000 --- a/app/assets/javascripts/admin/addon/routes/admin-config-fonts.js +++ /dev/null @@ -1,8 +0,0 @@ -import { i18n } from "discourse-i18n"; -import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route"; - -export default class AdminConfigFontsRoute extends AdminConfigWithSettingsRoute { - titleToken() { - return i18n("admin.config.font_style.title"); - } -} diff --git a/app/assets/javascripts/admin/addon/routes/admin-config-logo.js b/app/assets/javascripts/admin/addon/routes/admin-config-logo.js deleted file mode 100644 index c983ebbcada..00000000000 --- a/app/assets/javascripts/admin/addon/routes/admin-config-logo.js +++ /dev/null @@ -1,8 +0,0 @@ -import { i18n } from "discourse-i18n"; -import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route"; - -export default class AdminConfigLogoRoute extends AdminConfigWithSettingsRoute { - titleToken() { - return i18n("admin.config.logo.title"); - } -} diff --git a/app/assets/javascripts/admin/addon/routes/admin-route-map.js b/app/assets/javascripts/admin/addon/routes/admin-route-map.js index f673634c1b9..fc824b363eb 100644 --- a/app/assets/javascripts/admin/addon/routes/admin-route-map.js +++ b/app/assets/javascripts/admin/addon/routes/admin-route-map.js @@ -311,12 +311,6 @@ export default function () { this.route("developer", function () { this.route("settings", { path: "/" }); }); - this.route("fonts", function () { - this.route("settings", { path: "/" }); - }); - this.route("logo", function () { - this.route("settings", { path: "/" }); - }); this.route("branding"); this.route("navigation", function () { this.route("settings", { path: "/" }); diff --git a/app/assets/javascripts/admin/addon/services/site-setting-change-tracker.js b/app/assets/javascripts/admin/addon/services/site-setting-change-tracker.js index 967094a0021..bc57d8e38e7 100644 --- a/app/assets/javascripts/admin/addon/services/site-setting-change-tracker.js +++ b/app/assets/javascripts/admin/addon/services/site-setting-change-tracker.js @@ -4,12 +4,16 @@ import { TrackedSet } from "@ember-compat/tracked-built-ins"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { i18n } from "discourse-i18n"; +import { DEFAULT_TEXT_SIZES } from "admin/lib/constants"; import SiteSetting from "admin/models/site-setting"; import SiteSettingDefaultCategoriesModal from "../components/modal/site-setting-default-categories"; export default class SiteSettingChangeTracker extends Service { @service dialog; @service modal; + @service session; + @service site; + @service siteSettings; @tracked dirtySiteSettings = new TrackedSet(); @@ -181,6 +185,53 @@ export default class SiteSettingChangeTracker extends Service { }); } + refreshPage() { + document.documentElement.style.setProperty( + "--font-family", + this.siteSettings.base_font + ); + document.documentElement.style.setProperty( + "--heading-font-family", + this.siteSettings.heading_font + ); + DEFAULT_TEXT_SIZES.forEach((size) => { + document.documentElement.classList.remove(`text-size-${size}`); + }); + document.documentElement.classList.add( + `text-size-${this.siteSettings.default_text_size}` + ); + let logo; + + if (this.site.mobileView) { + if ( + this.session.defaultColorSchemeIsDark || + this.session.darkModeAvailable + ) { + logo = this.siteSettings.mobile_logo_dark; + } else { + logo = this.siteSettings.mobile_logo; + } + } + + if (!logo && this.session.defaultColorSchemeIsDark) { + logo = this.siteSettings.logo_dark; + } + + if (!logo) { + logo = this.siteSettings.logo; + } + + // Force reload when switching from text logo to image logo and vice versa + if ( + (!this.siteSettings.logo && document.getElementById("site-logo")) || + (this.siteSettings.logo && !document.getElementById("site-logo")) + ) { + window.location.reload(); + } else { + document.getElementById("site-logo").setAttribute("src", logo); + } + } + get count() { return this.dirtySiteSettings.size; } diff --git a/app/assets/javascripts/admin/addon/templates/config-branding.gjs b/app/assets/javascripts/admin/addon/templates/config-branding.gjs index efee2e38d32..0c2792494cf 100644 --- a/app/assets/javascripts/admin/addon/templates/config-branding.gjs +++ b/app/assets/javascripts/admin/addon/templates/config-branding.gjs @@ -2,6 +2,7 @@ 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 AdminBrandingFontsForm from "admin/components/admin-branding-fonts-form"; import AdminBrandingLogoForm from "admin/components/admin-branding-logo-form"; import AdminConfigAreaCard from "admin/components/admin-config-area-card"; @@ -33,6 +34,19 @@ export default RouteTemplate( +
+
+ + <:content> + + + +
+
); diff --git a/app/assets/javascripts/admin/addon/templates/config-fonts-settings.gjs b/app/assets/javascripts/admin/addon/templates/config-fonts-settings.gjs deleted file mode 100644 index 483a2777b0d..00000000000 --- a/app/assets/javascripts/admin/addon/templates/config-fonts-settings.gjs +++ /dev/null @@ -1,33 +0,0 @@ -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"; - -export default RouteTemplate( - -); diff --git a/app/assets/javascripts/admin/addon/templates/config-logo-settings.gjs b/app/assets/javascripts/admin/addon/templates/config-logo-settings.gjs deleted file mode 100644 index f6f40b33142..00000000000 --- a/app/assets/javascripts/admin/addon/templates/config-logo-settings.gjs +++ /dev/null @@ -1,34 +0,0 @@ -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( - -); diff --git a/app/assets/javascripts/admin/addon/templates/site-settings-category.gjs b/app/assets/javascripts/admin/addon/templates/site-settings-category.gjs index e13bcc872e3..2fc387bd8e3 100644 --- a/app/assets/javascripts/admin/addon/templates/site-settings-category.gjs +++ b/app/assets/javascripts/admin/addon/templates/site-settings-category.gjs @@ -1,5 +1,4 @@ import RouteTemplate from "ember-route-template"; -import routeAction from "discourse/helpers/route-action"; import { i18n } from "discourse-i18n"; import SiteSetting from "admin/components/site-setting"; @@ -8,10 +7,7 @@ export default RouteTemplate( {{#if @controller.filteredContent}}
{{#each @controller.filteredContent as |setting|}} - + {{/each}} {{#if @controller.category.hasMore}}

{{i18n diff --git a/app/assets/javascripts/discourse/app/components/modal/update-default-text-size.gjs b/app/assets/javascripts/discourse/app/components/modal/update-default-text-size.gjs new file mode 100644 index 00000000000..71e10e521fa --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/update-default-text-size.gjs @@ -0,0 +1,44 @@ +import Component from "@glimmer/component"; +import { action } from "@ember/object"; +import DButton from "discourse/components/d-button"; +import DModal from "discourse/components/d-modal"; +import { i18n } from "discourse-i18n"; + +export default class UpdateDefaultTextSize extends Component { + @action + updateExistingUsers() { + this.args.model.setUpdateExistingUsers(true); + this.args.closeModal(); + } + + @action + cancel() { + this.args.model.setUpdateExistingUsers(null); + this.args.closeModal(); + } + + +} diff --git a/app/assets/javascripts/discourse/app/lib/constants.js b/app/assets/javascripts/discourse/app/lib/constants.js index 58e771de2e6..b444eff80a9 100644 --- a/app/assets/javascripts/discourse/app/lib/constants.js +++ b/app/assets/javascripts/discourse/app/lib/constants.js @@ -90,3 +90,13 @@ export const TOPIC_VISIBILITY_REASONS = { export const MAX_UNOPTIMIZED_CATEGORIES = 1000; export const REVIEWABLE_UNKNOWN_TYPE_SOURCE = "unknown"; + +export const ADMIN_SEARCH_RESULT_TYPES = [ + "page", + "setting", + "theme", + "component", + "report", +]; + +export const API_KEY_SCOPE_MODES = ["global", "read_only", "granular"]; diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js b/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js index 08199a73742..6d6438f1b04 100644 --- a/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js +++ b/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js @@ -200,18 +200,10 @@ export const ADMIN_NAV_MAP = [ label: "admin.config_sections.appearance.title", links: [ { - name: "admin_font_style", - route: "adminConfig.fonts.settings", - label: "admin.config.font_style.title", - description: "admin.config.font_style.header_description", - icon: "italic", - settings_area: "fonts", - }, - { - name: "admin_site_logo", - route: "adminConfig.logo.settings", - label: "admin.config.logo.title", - description: "admin.config.logo.header_description", + name: "admin_branding", + route: "adminConfig.branding", + label: "admin.config.branding.title", + description: "admin.config.branding.header_description", icon: "fab-discourse", settings_category: "branding", }, diff --git a/app/assets/stylesheets/admin/admin_config_area.scss b/app/assets/stylesheets/admin/admin_config_area.scss index a9d6fcf302b..0b4f185348d 100644 --- a/app/assets/stylesheets/admin/admin_config_area.scss +++ b/app/assets/stylesheets/admin/admin_config_area.scss @@ -175,3 +175,103 @@ width: var(--form-kit-small-input) !important; } } + +.admin-fonts-form { + &__more { + font-size: var(--font-down-1-rem); + } + + .form-kit__field-custom { + width: 100% !important; + + .form-kit__control-custom { + display: flex; + flex-wrap: wrap; + flex-direction: row; + justify-content: space-between; + + .admin-fonts-form__button-option { + border: 1px solid var(--primary-low); + font-size: var(--font-down-1-rem); + max-height: 4em; + height: 50px; + margin-bottom: 1em; + padding: 0 0.65em; + + &:hover { + border-color: var(--primary-low-mid); + } + + &.active { + position: relative; + border: 2px solid var(--tertiary); + + &::after { + position: absolute; + top: 35%; + left: 0.5rem; + font-family: "Segoe UI", Arial, sans-serif; + content: "\2714"; // ✔ + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + height: 1rem; + background-color: var(--tertiary); + color: var(--secondary); + border-radius: 9999px; + font-size: 0.5rem; + margin-left: 0.25rem; + } + + &:hover { + border-color: var(--tertiary); + } + } + + &.font { + flex: 0 0 32%; + max-width: 32%; + + @media (max-width: $mobile-breakpoint) { + flex: 0 0 100%; + max-width: 100%; + } + } + + &.text-size { + flex: 0 0 19%; + max-width: 19%; + + @media (max-width: $mobile-breakpoint) { + flex: 0 0 100%; + max-width: 100%; + } + + // px muse be used because there is different font size inside each button + height: 45px; + + &.smallest { + font-size: var(--base-font-size-smallest); + } + + &.smaller { + font-size: var(--base-font-size-smaller); + } + + &.normal { + font-size: var(--base-font-size-normal); + } + + &.larger { + font-size: var(--base-font-size-larger); + } + + &.largest { + font-size: var(--base-font-size-largest); + } + } + } + } + } +} diff --git a/app/controllers/admin/config/branding_controller.rb b/app/controllers/admin/config/branding_controller.rb index fc39d2ff070..4f6719118b2 100644 --- a/app/controllers/admin/config/branding_controller.rb +++ b/app/controllers/admin/config/branding_controller.rb @@ -38,4 +38,36 @@ class Admin::Config::BrandingController < Admin::AdminController end end end + + def fonts + previous_default_text_size = SiteSetting.default_text_size + SiteSetting::Update.call( + guardian:, + params: { + settings: [ + { setting_name: "base_font", value: params[:base_font] }, + { setting_name: "heading_font", value: params[:heading_font] }, + { + setting_name: "default_text_size", + value: params[:default_text_size], + backfill: params[:update_existing_users] == "true", + }, + ], + }, + ) 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 diff --git a/app/models/default_text_size_setting.rb b/app/models/default_text_size_setting.rb new file mode 100644 index 00000000000..cc1e79cda11 --- /dev/null +++ b/app/models/default_text_size_setting.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "enum_site_setting" + +class DefaultTextSizeSetting < EnumSiteSetting + DEFAULT_TEXT_SIZES = UserOption.text_sizes.keys.map(&:to_s) + + def self.valid_value?(val) + values.any? { |v| v[:value].to_s == val.to_s } + end + + def self.values + @values ||= DEFAULT_TEXT_SIZES + end +end diff --git a/app/models/user_option.rb b/app/models/user_option.rb index c93d7aea6f6..9e10132c216 100644 --- a/app/models/user_option.rb +++ b/app/models/user_option.rb @@ -48,7 +48,7 @@ class UserOption < ActiveRecord::Base end def self.text_sizes - @text_sizes ||= Enum.new(normal: 0, larger: 1, largest: 2, smaller: 3, smallest: 4) + @text_sizes ||= Enum.new(smallest: 4, smaller: 3, normal: 0, larger: 1, largest: 2) end def self.title_count_modes diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0be6c3a4112..dfdd66b5738 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -5241,9 +5241,6 @@ en: title: "Moderation" header_description: "The flagging system in Discourse helps you and your moderator team manage content and user behavior, keeping your community respectful and healthy. The defaults are suitable for most communities and you don’t have to change them. However, if your site has particular requirements you can disable flags you don’t need and add your own custom flags." keywords: "flag|review|spam|illegal" - font_style: - title: "Font style" - header_description: "Customize the font styles used by your themes" files: title: "Files" header_description: "Settings that control file size and type limits, avatar sizes and sources, file storage, image quality and compression, and more" @@ -5257,11 +5254,9 @@ en: login_and_authentication: title: "Login & authentication" header_description: "Configure how users log in and authenticate, secrets and keys, OAuth2 providers, and more" - logo: - title: "Site logo" - header_description: "Customize the variations of your site logo" branding: title: "Branding" + header_description: "Customize the variations of your site logo and fonts" logo: title: "Logo" form: @@ -5326,6 +5321,27 @@ en: 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." + fonts: + title: "Fonts" + form: + more_fonts: "More fonts" + less_fonts: "Less fonts" + saved: "Font settings are saved." + base_font: + title: "Base font" + heading_font: + title: "Heading font" + default_text_size: + title: "Default text size" + description: "This size can be subsequently adjusted by each user according to their preferences." + backfill_modal: + title: "Apply new default text settings?" + description: + one: "You’ve updated the default text size. Would you like to apply this change to %{count} existing user?" + other: "You’ve updated the default text size. Would you like to apply this change to %{count} existing users?" + modal_yes: "Yes" + modal_no: "No, only apply going forward" + 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" diff --git a/config/routes.rb b/config/routes.rb index a9ea6f99c13..edd5ac3e110 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -394,12 +394,10 @@ Discourse::Application.routes.draw do namespace :config, constraints: StaffConstraint.new do resources :site_settings, only: %i[index] get "developer" => "site_settings#index" - get "fonts" => "site_settings#index" get "files" => "site_settings#index" get "legal" => "site_settings#index" get "localization" => "site_settings#index" get "login-and-authentication" => "site_settings#index" - get "logo" => "site_settings#index" get "navigation" => "site_settings#index" get "notifications" => "site_settings#index" get "rate-limits" => "site_settings#index" @@ -414,6 +412,7 @@ Discourse::Application.routes.draw do get "group-permissions" => "site_settings#index" get "branding" => "branding#index" put "branding/logo" => "branding#logo" + put "branding/fonts" => "branding#fonts" get "colors/:id" => "color_palettes#show" resources :flags, only: %i[index new create update destroy] do diff --git a/config/site_settings.yml b/config/site_settings.yml index 2c69188d1f0..e230375a71e 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -439,6 +439,7 @@ basic: list_type: font area: "fonts" allow_any: false + client: true heading_font: default: "inter" choices: "BaseFontSetting.values" @@ -447,6 +448,7 @@ basic: list_type: font area: "fonts" allow_any: false + client: true enable_sitemap: default: true sitemap_page_size: @@ -3283,11 +3285,8 @@ user_preferences: default_text_size: type: enum default: normal - choices: - - smaller - - normal - - larger - - largest + client: true + choices: "DefaultTextSizeSetting.values" area: "fonts" default_title_count_mode: diff --git a/lib/tasks/javascript.rake b/lib/tasks/javascript.rake index cbb338ccc24..7f89b0d43db 100644 --- a/lib/tasks/javascript.rake +++ b/lib/tasks/javascript.rake @@ -137,6 +137,8 @@ task "javascript:update_constants" => :environment do ) end + MAIN_FONT_KEYS = %w[helvetica inter lato montserrat open_sans poppins roboto merriweather mukta] + write_template("admin/addon/lib/constants.js", task_name, <<~JS) export const ADMIN_SEARCH_RESULT_TYPES = #{Admin::SearchController::RESULT_TYPES.to_json}; @@ -151,6 +153,12 @@ task "javascript:update_constants" => :environment do export const USER_FIELD_FLAGS = #{UserField::FLAG_ATTRIBUTES}; export const DEFAULT_USER_PREFERENCES = #{SiteSetting::DEFAULT_USER_PREFERENCES.to_json}; + + export const MAIN_FONTS = #{DiscourseFonts.fonts.filter { |font| MAIN_FONT_KEYS.include?(font[:key]) }.map { |font| { key: font[:key], name: font[:name] } }.to_json} + + export const MORE_FONTS = #{DiscourseFonts.fonts.reject { |font| MAIN_FONT_KEYS.include?(font[:key]) }.map { |font| { key: font[:key], name: font[:name] } }.to_json} + + export const DEFAULT_TEXT_SIZES = #{DefaultTextSizeSetting::DEFAULT_TEXT_SIZES} JS write_template("discourse/app/lib/constants.js", task_name, <<~JS) @@ -183,6 +191,10 @@ task "javascript:update_constants" => :environment do export const MAX_UNOPTIMIZED_CATEGORIES = #{CategoryList::MAX_UNOPTIMIZED_CATEGORIES}; export const REVIEWABLE_UNKNOWN_TYPE_SOURCE = "#{Reviewable::UNKNOWN_TYPE_SOURCE}"; + + export const ADMIN_SEARCH_RESULT_TYPES = #{Admin::SearchController::RESULT_TYPES.to_json}; + + export const API_KEY_SCOPE_MODES = #{ApiKey.scope_modes.keys.to_json} JS pretty_notifications = Notification.types.map { |n| " #{n[0]}: #{n[1]}," }.join("\n") diff --git a/spec/requests/admin/config/branding_controller_spec.rb b/spec/requests/admin/config/branding_controller_spec.rb new file mode 100644 index 00000000000..5eb13dc7f2f --- /dev/null +++ b/spec/requests/admin/config/branding_controller_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +RSpec.describe Admin::Config::BrandingController do + fab!(:admin) + fab!(:moderator) + fab!(:user) + + describe "#fonts" do + context "when logged in as an admin" do + before { sign_in(admin) } + it "updates the fonts and text size" do + put "/admin/config/branding/fonts.json", + params: { + base_font: "helvetica", + heading_font: "roboto", + default_text_size: "largest", + } + expect(response.status).to eq(200) + + expect(SiteSetting.base_font).to eq("helvetica") + expect(SiteSetting.heading_font).to eq("roboto") + expect(SiteSetting.default_text_size).to eq("largest") + end + + it "validates values" do + put "/admin/config/branding/fonts.json", + params: { + base_font: "invalid_font", + heading_font: "invalid_font", + default_text_size: "invalid_size", + } + expect(response.status).to eq(400) + expect(SiteSetting.base_font).to eq("inter") + expect(SiteSetting.heading_font).to eq("inter") + expect(SiteSetting.default_text_size).to eq("normal") + end + end + + context "when logged in as a moderator" do + before { sign_in(moderator) } + it "denies access with a 403 response" do + put "/admin/config/branding/fonts.json", + params: { + base_font: "helvetica", + heading_font: "roboto", + default_text_size: "largest", + } + expect(response.status).to eq(403) + expect(SiteSetting.base_font).to eq("inter") + expect(SiteSetting.heading_font).to eq("inter") + expect(SiteSetting.default_text_size).to eq("normal") + end + end + + context "when logged in as a non-staff user" do + before { sign_in(user) } + + it "denies access with a 404 response" do + put "/admin/config/branding/fonts.json", + params: { + base_font: "helvetica", + heading_font: "roboto", + default_text_size: "largest", + } + + expect(response.status).to eq(404) + expect(SiteSetting.base_font).to eq("inter") + expect(SiteSetting.heading_font).to eq("inter") + expect(SiteSetting.default_text_size).to eq("normal") + end + end + end +end diff --git a/spec/system/admin_branding_spec.rb b/spec/system/admin_branding_spec.rb index 43d705f9d4d..9d473dce6d7 100644 --- a/spec/system/admin_branding_spec.rb +++ b/spec/system/admin_branding_spec.rb @@ -6,206 +6,267 @@ describe "Admin Branding Page", type: :system do let(:branding_page) { PageObjects::Pages::AdminBranding.new } let(:image_file) { file_from_fixtures("logo.png", "images") } + let(:modal) { PageObjects::Modals::Base.new } 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 + describe "logo" do + describe "primary section" do + let(:primary_section_logos) do + %i[logo logo_dark large_icon favicon logo_small logo_small_dark] + end + 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_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) + 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) + 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 - primary_section_logos.each do |image_type| - expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image - end + it "can remove images" do + primary_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) } - branding_page.logo_form.submit - expect(branding_page.logo_form).to have_saved_successfully + branding_page.visit - visit("/") - branding_page.visit + primary_section_logos.each do |image_type| + expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image + end - 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 { |image_type| branding_page.logo_form.remove_image(image_type) } - primary_section_logos.each do |image_type| - expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image + branding_page.logo_form.submit + expect(page).to have_css("#site-text-logo") + + primary_section_logos.each { |image_type| expect(SiteSetting.send(image_type)).to eq(nil) } end end - it "can remove images" do - primary_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) } + 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 - branding_page.visit + 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) - primary_section_logos.each do |image_type| - expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image + 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 - primary_section_logos.each { |image_type| branding_page.logo_form.remove_image(image_type) } + it "can remove images" do + mobile_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) } - branding_page.logo_form.submit - expect(branding_page.logo_form).to have_saved_successfully + branding_page.visit + branding_page.logo_form.expand_mobile_section - primary_section_logos.each { |image_type| expect(SiteSetting.send(image_type)).to eq(nil) } + 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 - 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 + describe "fonts" do + it "allows an admin to change the site's base font and heading font" do branding_page.visit - branding_page.logo_form.expand_mobile_section + branding_page.fonts_form.select_font("base", "helvetica") - 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) + expect(branding_page.fonts_form).to have_no_font("heading", "Oswald") + branding_page.fonts_form.show_more_fonts("heading") + branding_page.fonts_form.select_font("heading", "oswald") - mobile_section_logos.each do |image_type| - branding_page.logo_form.upload_image(image_type, image_file) - end + branding_page.fonts_form.submit + expect(branding_page.fonts_form).to have_saved_successfully - 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 + expect(branding_page.fonts_form.active_font("base")).to eq("Helvetica") + expect(branding_page.fonts_form.active_font("heading")).to eq("Oswald") end - it "can remove images" do - mobile_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) } - + it "allows an admin to change default text size and does not update existing users preferences" do + Jobs.run_immediately! branding_page.visit - branding_page.logo_form.expand_mobile_section + expect(page).to have_css("html.text-size-normal") + branding_page.fonts_form.select_default_text_size("larger") - mobile_section_logos.each do |image_type| - expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image - end + branding_page.fonts_form.submit + expect(modal).to be_open + expect(modal.header).to have_content( + I18n.t("admin_js.admin.config.branding.fonts.backfill_modal.title"), + ) + modal.close + expect(modal).to be_closed + expect(branding_page.fonts_form).to have_saved_successfully - 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 + visit "/" + expect(page).to have_css("html.text-size-normal") end - it "can remove images" do - email_section_logos.each { |image_type| SiteSetting.send("#{image_type}=", image_upload) } - + it "allows an admin to change default text size and updates existing users preferences" do + Jobs.run_immediately! branding_page.visit - branding_page.logo_form.expand_email_section + expect(page).to have_css("html.text-size-normal") + branding_page.fonts_form.select_default_text_size("larger") - email_section_logos.each do |image_type| - expect(branding_page.logo_form.image_uploader(image_type)).to have_uploaded_image - end + branding_page.fonts_form.submit + expect(modal).to be_open + expect(modal.header).to have_content( + I18n.t("admin_js.admin.config.branding.fonts.backfill_modal.title"), + ) + modal.click_primary_button + expect(modal).to be_closed + expect(branding_page.fonts_form).to have_saved_successfully - 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 + visit "/" + expect(page).to have_css("html.text-size-larger") end end end diff --git a/spec/system/page_objects/components/admin-branding-fonts-form.rb b/spec/system/page_objects/components/admin-branding-fonts-form.rb new file mode 100644 index 00000000000..6694c2e07c1 --- /dev/null +++ b/spec/system/page_objects/components/admin-branding-fonts-form.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module PageObjects + module Components + class AdminBrandingFontsForm < PageObjects::Components::Base + def select_font(section, font) + find( + "[data-name='#{section}_font'] .admin-fonts-form__button-option.body-font-#{font}", + ).click + end + + def select_default_text_size(size) + find(".admin-fonts-form__button-option.#{size}").click + end + + def active_font(section) + find("[data-name='#{section}_font'] .admin-fonts-form__button-option.active").text + end + + def has_no_font?(section, font) + page.has_no_css?( + "[data-name='#{section}_font'] .admin-fonts-form__button-option.body-font-#{font}", + ) + end + + def show_more_fonts(section) + find("[data-name='#{section}_font'] .admin-fonts-form__more").click + end + + def has_form_field?(field) + page.has_css?("#control-#{field}") + end + + def submit + form.submit + end + + def has_saved_successfully? + PageObjects::Components::Toasts.new.has_success?( + I18n.t("admin_js.admin.config.branding.fonts.form.saved"), + ) + end + + def form + @form ||= PageObjects::Components::FormKit.new(".admin-fonts-form") + end + end + end +end diff --git a/spec/system/page_objects/pages/admin_branding.rb b/spec/system/page_objects/pages/admin_branding.rb index 972ebe4d9dd..422514170a8 100644 --- a/spec/system/page_objects/pages/admin_branding.rb +++ b/spec/system/page_objects/pages/admin_branding.rb @@ -10,6 +10,10 @@ module PageObjects def logo_form @logo_form ||= PageObjects::Components::AdminBrandingLogoForm.new end + + def fonts_form + @fonts_form ||= PageObjects::Components::AdminBrandingFontsForm.new + end end end end