diff --git a/about.json b/about.json index c2fb8bf..d30bbac 100644 --- a/about.json +++ b/about.json @@ -20,62 +20,86 @@ "Horizon": { "primary": "1A1A1A", "secondary": "ffffff", - "tertiary": "595bca" + "tertiary": "595bca", + "tertiary-med-or-tertiary": "595bca", + "selected": "d7dfff" }, "Horizon Dark": { "primary": "ffffff", "secondary": "1A1A1A", - "tertiary": "595bca" + "tertiary": "595bca", + "tertiary-med-or-tertiary": "595bca", + "selected": "3b3e56" }, "Royal": { "primary": "1A1A1A", "secondary": "ffffff", - "tertiary": "4169e1" + "tertiary": "4169e1", + "tertiary-med-or-tertiary": "4169e1", + "selected": "c7e3ff" }, "Royal Dark": { "primary": "ffffff", "secondary": "1A1A1A", - "tertiary": "4169e1" + "tertiary": "4169e1", + "tertiary-med-or-tertiary": "4169e1", + "selected": "3a455f" }, "Clover": { "primary": "1A1A1A", "secondary": "ffffff", - "tertiary": "45a06e" + "tertiary": "45a06e", + "tertiary-med-or-tertiary": "45a06e", + "selected": "c6f1d5" }, "Clover Dark": { "primary": "ffffff", "secondary": "1A1A1A", - "tertiary": "45a06e" + "tertiary": "45a06e", + "tertiary-med-or-tertiary": "45a06e", + "selected": "47594e" }, "Lily": { "primary": "1A1A1A", "secondary": "ffffff", - "tertiary": "cc338c" + "tertiary": "cc338c", + "tertiary-med-or-tertiary": "cc338c", + "selected": "ffc8ee" }, "Lily Dark": { "primary": "ffffff", "secondary": "1A1A1A", - "tertiary": "cc338c" + "tertiary": "cc338c", + "tertiary-med-or-tertiary": "cc338c", + "selected": "5f3e4e" }, "Violet": { "primary": "1A1A1A", "secondary": "ffffff", - "tertiary": "9b15de" + "tertiary": "9b15de", + "tertiary-med-or-tertiary": "9b15de", + "selected": "feccff" }, "Violet Dark": { "primary": "ffffff", "secondary": "1A1A1A", - "tertiary": "9b15de" + "tertiary": "9b15de", + "tertiary-med-or-tertiary": "9b15de", + "selected": "4c385c" }, "Marigold": { "primary": "1A1A1A", "secondary": "ffffff", - "tertiary": "d3881f" + "tertiary": "d3881f", + "tertiary-med-or-tertiary": "d3881f", + "selected": "ffdcb2" }, "Marigold Dark": { "primary": "ffffff", "secondary": "1A1A1A", - "tertiary": "d3881f" + "tertiary": "d3881f", + "tertiary-med-or-tertiary": "d3881f", + "selected": "6c5b49" } } } \ No newline at end of file diff --git a/common/color_definitions.scss b/common/color_definitions.scss new file mode 100644 index 0000000..bac0b09 --- /dev/null +++ b/common/color_definitions.scss @@ -0,0 +1,101 @@ +html { + --accent-color: #{$tertiary} !important; + --accent-text-color: light-dark(#ffffff, #212121) !important; + // Background Colors + --background-color: light-dark( + oklch(from #{$tertiary} 98% calc(c * 0.25) h), + oklch(from #{$tertiary} 10% 0.025 h) + ) !important; + --d-content-background: light-dark( + oklch(from #{$secondary} calc(2 * l) c h), + oklch(from #{$secondary} l c h) + ) !important; + // HeaderColors + --header_primary-low-mid: light-dark( + oklch(from #{$tertiary} 73.5% calc(c * 0.5) h), + oklch(from #{$tertiary} l calc(c * 0.25) h) + ) !important; + --header_primary-medium: light-dark( + oklch(from #{$tertiary} 54% calc(c * 0.5) h), + oklch(from #{$tertiary} calc(l * 1.35) calc(c * 0.25) h) + ) !important; + + // Sidebar Colors + --d-sidebar-border-color: light-dark( + oklch(from #{$tertiary} 90% c h), + oklch(from #{$tertiary} calc(l * 0.7) calc(c * 0.25) h) + ) !important; + --d-sidebar-link-color: light-dark( + oklch(from #{$tertiary} calc(l * 0.8) calc(c * 0.25) h), + oklch(from #{$tertiary} calc(l * 1.5) calc(c * 0.25) h) + ) !important; + --d-sidebar-suffix-color: light-dark( + oklch(from #{$tertiary} l calc(c * 0.9) h), + oklch(from #{$tertiary} l calc(c * 0.9) h) + ) !important; + + // Other Colors + --d-selected: light-dark( + oklch(from #{$tertiary} 92% calc(c * 0.5) h), + oklch(from #{$tertiary} calc(l * 0.7) calc(c * 0.25) h) + ) !important; + + --d-nav-color--active: #{$tertiary} !important; + --link-color: light-dark( + #{$tertiary}, + oklch(from #{$tertiary} calc(l * 0.95) c h) + ) !important; + --link-color-hover: light-dark( + #{$tertiary}, + oklch(from #{$tertiary} calc(l * 1.5) calc(c * 2.25) h) + ) !important; + --tertiary-hover: #{$tertiary} !important; + + // Search Colors + --search-color: light-dark( + oklch(from #{$tertiary} calc(l * 0.65) calc(c * 0.65) h), + oklch(from #{$tertiary} calc(l * 1.5) calc(c * 2) h) + ) !important; + --search-banner-text-color: light-dark( + oklch(from #{$tertiary} calc(l * 0.65) calc(c * 0.65) h), + oklch(from #{$tertiary} calc(l * 1.85) calc(c * 2) h) + ) !important; + + // Topic Card Colors + --topic-card-shadow: light-dark( + oklch(from #{$tertiary} calc(l * 1.85) calc(c * 0.5) h), + oklch(from #{$tertiary} calc(l * 0.2) calc(c * 0.01) h / 0.25) + ) !important; + + // Button Colors + --button-box-shadow: light-dark( + oklch(from #{$tertiary} calc(l * 1.5) calc(c * 0.35) h), + oklch(from #{$tertiary} calc(l * 0.75) calc(c * 0.5) h) + ) !important; + + --d-sidebar-highlight-hover-icon: var(--d-sidebar-link-color) !important; + --d-sidebar-highlight-hover-background: var(--d-selected) !important; + --d-sidebar-link-icon-color: var(--d-sidebar-link-color) !important; + --d-sidebar-header-color: var(--d-sidebar-link-color) !important; + --d-sidebar-header-icon-color: var(--d-sidebar-link-color) !important; + --d-sidebar-active-suffix-color: var(--d-sidebar-suffix-color) !important; + --d-sidebar-background: var(--background-color) !important; + --d-sidebar-footer-fade: var(--background-color) !important; + --d-sidebar-prefix-background: var(--d-selected) !important; + --d-sidebar-active-prefix-background: light-dark( + oklch(from var(--d-selected) calc(l * 0.85) c h), + oklch(from var(--d-selected) calc(l * 0.7) c h) + ) !important; + --d-sidebar-highlight-prefix-background: light-dark( + oklch(from var(--d-selected) calc(l * 0.85) c h), + oklch(from var(--d-selected) calc(l * 0.7) c h) + ) !important; + --d-sidebar-highlight-suffix-color: var( + --d-sidebar-active-suffix-color + ) !important; + --d-sidebar-highlight-color: var(--primary) !important; + --d-sidebar-highlight-background: var(--d-selected) !important; + --d-sidebar-section-link-icon-size: 1em !important; + --d-hover: oklch(from var(--d-selected) l c h / 0.75) !important; + --d-input-bg-color: var(--d-content-background) !important; +} diff --git a/common/common.scss b/common/common.scss index c4ce8bc..c774676 100644 --- a/common/common.scss +++ b/common/common.scss @@ -2,7 +2,6 @@ @import "buttons"; @import "chat"; @import "color-choice"; -@import "color-exploration"; @import "composer"; @import "header"; @import "hiddenstuff"; diff --git a/javascripts/discourse/api-initializers/horizon.gjs b/javascripts/discourse/api-initializers/horizon.gjs index 2af5325..36342d8 100644 --- a/javascripts/discourse/api-initializers/horizon.gjs +++ b/javascripts/discourse/api-initializers/horizon.gjs @@ -1,12 +1,10 @@ import { apiInitializer } from "discourse/lib/api"; -import CustomColorHtmlClass from "../components/custom-color-html-class"; -import CustomUserPalette from "../components/custom-user-palette"; import ExperimentalScreen from "../components/experimental-screen"; +import UserColorPaletteSelector from "../components/user-color-palette-selector"; export default apiInitializer("1.8.0", (api) => { api.renderInOutlet("above-main-container", ExperimentalScreen); - api.renderInOutlet("above-main-container", CustomColorHtmlClass); - api.renderInOutlet("sidebar-footer-actions", CustomUserPalette); + api.renderInOutlet("sidebar-footer-actions", UserColorPaletteSelector); api.registerValueTransformer("site-setting-enable-welcome-banner", () => { return settings.enable_welcome_banner; diff --git a/javascripts/discourse/components/custom-color-html-class.gjs b/javascripts/discourse/components/custom-color-html-class.gjs deleted file mode 100644 index 4e1780b..0000000 --- a/javascripts/discourse/components/custom-color-html-class.gjs +++ /dev/null @@ -1,12 +0,0 @@ -import Component from "@glimmer/component"; -import { concat } from "@ember/helper"; -import { service } from "@ember/service"; -import htmlClass from "discourse/helpers/html-class"; - -export default class CustomColorHtmlClass extends Component { - @service customColor; - - -} diff --git a/javascripts/discourse/components/custom-user-palette.gjs b/javascripts/discourse/components/custom-user-palette.gjs deleted file mode 100644 index 56ec2f7..0000000 --- a/javascripts/discourse/components/custom-user-palette.gjs +++ /dev/null @@ -1,60 +0,0 @@ -import icon from "discourse/helpers/d-icon"; -import DMenu from "float-kit/components/d-menu"; -import SitePaletteMenuItem from "./site-palette-menu-item"; - -const PALETTES = [ - { - label: "Marigold", - name: "marigold", - color: "#d3881f", - }, - { - label: "Violet", - name: "violet", - color: "#9b15de", - }, - { - label: "Lily", - name: "lily", - color: "#CC336F", - }, - { - label: "Clover", - name: "clover", - color: "#45a06e", - }, - { - label: "Royal", - name: "royal", - color: "#4169e1", - }, - { - label: "Horizon", - name: "horizon", - color: "#595bca", - }, -]; - -export const DEFAULT_PALETTE_NAME = "horizon"; - - diff --git a/javascripts/discourse/components/site-palette-menu-item.gjs b/javascripts/discourse/components/user-color-palette-menu-item.gjs similarity index 50% rename from javascripts/discourse/components/site-palette-menu-item.gjs rename to javascripts/discourse/components/user-color-palette-menu-item.gjs index f7934c1..a2c227b 100644 --- a/javascripts/discourse/components/site-palette-menu-item.gjs +++ b/javascripts/discourse/components/user-color-palette-menu-item.gjs @@ -1,40 +1,43 @@ import Component from "@glimmer/component"; -import { fn } from "@ember/helper"; import { action } from "@ember/object"; import { service } from "@ember/service"; import { htmlSafe } from "@ember/template"; import DButton from "discourse/components/d-button"; import concatClass from "discourse/helpers/concat-class"; -export default class SitePaletteMenuItem extends Component { - @service customColor; +export default class UserColorPaletteMenuItem extends Component { + @service site; + @service session; get siteStyle() { - return `--icon-color: ${this.args.colorPalette.color}`; + return `--icon-color: ${this.args.colorPalette.accent}`; } get activeClass() { - if (this.customColor.color === this.args.colorPalette.name) { + if (this.args.selectedColorPaletteId === this.args.colorPalette.id) { return "active"; } } @action - handleInput(colorPalette) { - this.customColor.setColor(colorPalette.name); + paletteSelected() { + this.args.paletteSelected(this.args.colorPalette); } diff --git a/javascripts/discourse/components/user-color-palette-selector.gjs b/javascripts/discourse/components/user-color-palette-selector.gjs new file mode 100644 index 0000000..aec58d2 --- /dev/null +++ b/javascripts/discourse/components/user-color-palette-selector.gjs @@ -0,0 +1,179 @@ +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { action } from "@ember/object"; +import { service } from "@ember/service"; +import { isEmpty } from "@ember/utils"; +import icon from "discourse/helpers/d-icon"; +import { reload } from "discourse/helpers/page-reloader"; +import { ajax } from "discourse/lib/ajax"; +import { + listColorSchemes, + updateColorSchemeCookie, +} from "discourse/lib/color-scheme-picker"; +import cookie from "discourse/lib/cookie"; +import DMenu from "float-kit/components/d-menu"; +import UserColorPaletteMenuItem from "./user-color-palette-menu-item"; + +const HORIZON_PALETTES = [ + "Horizon", + "Marigold", + "Violet", + "Lily", + "Clover", + "Royal", +]; + +export default class UserColorPaletteSelector extends Component { + @service currentUser; + @service keyValueStore; + @service site; + @service session; + @service interfaceColor; + @tracked anonColorPaletteId = this.#loadAnonColorPalette(); + @tracked userColorPaletteId = this.session.userColorSchemeId; + + get userColorPalettes() { + const availablePalettes = listColorSchemes(this.site) + .map((userPalette) => { + return { + ...userPalette, + accent: `#${ + userPalette.colors.find((color) => color.name === "tertiary").hex + }`, + }; + }) + .filter((userPalette) => { + return HORIZON_PALETTES.some((palette) => { + return userPalette.name.toLowerCase().includes(palette.toLowerCase()); + }); + }) + .sort(); + + // Match the light scheme with the corresponding dark id based in the name + return ( + availablePalettes + .map((palette) => { + if (palette.is_dark) { + return palette; + } + + const normalizedLightName = palette.name.toLowerCase(); + + const correspondingDarkModeId = availablePalettes.find( + (item) => + item.is_dark && + normalizedLightName === + item.name.toLowerCase().replace(/\s+dark$/, "") + )?.id; + + return { + ...palette, + correspondingDarkModeId, + }; + }) + // Only want to show palettes that have corresponding light/dark modes + .filter((palette) => !palette.is_dark) + ); + } + + get selectedColorPaletteId() { + if (this.currentUser) { + return this.userColorPaletteId; + } + + return this.anonColorPaletteId; + } + + @action + onRegisterMenu(api) { + this.dMenu = api; + } + + @action + paletteSelected(selectedPalette) { + if (selectedPalette.id === this.selectedColorPaletteId) { + return; + } + + this.#updatePreference(selectedPalette); + this.#changeSiteColorPalette( + selectedPalette.id, + selectedPalette.correspondingDarkModeId + ); + this.dMenu.close(); + } + + #updatePreference(selectedPalette) { + updateColorSchemeCookie(selectedPalette.id); + updateColorSchemeCookie(selectedPalette.correspondingDarkModeId, { + dark: true, + }); + + if (!this.currentUser) { + this.anonColorPaletteId = selectedPalette.id; + } else { + this.userColorPaletteId = selectedPalette.id; + } + } + + #loadAnonColorPalette() { + const storedAnonPaletteId = cookie("color_scheme_id"); + if (storedAnonPaletteId) { + return parseInt(storedAnonPaletteId, 10); + } + } + + async #changeSiteColorPalette(lightPaletteId, darkPaletteId) { + const lightTag = document.querySelector("link.light-scheme"); + const darkTag = document.querySelector("link.dark-scheme"); + + if (!darkTag) { + reload(); + return; + } + + // TODO(osama) once we have built-in light/dark modes for each palette, we + // can stop making the 2nd ajax call for the dark palette and replace it + // with a `include_dark_scheme` param on the ajax call for the light + // palette which will include the href for the dark palette in the response + const lightPaletteInfo = await ajax( + `/color-scheme-stylesheet/${lightPaletteId}.json` + ); + const darkPaletteInfo = await ajax( + `/color-scheme-stylesheet/${darkPaletteId}.json` + ); + + lightTag.href = lightPaletteInfo.new_href; + darkTag.href = darkPaletteInfo.new_href; + } + + +} diff --git a/javascripts/discourse/services/custom-color.gjs b/javascripts/discourse/services/custom-color.gjs deleted file mode 100644 index dbdf80d..0000000 --- a/javascripts/discourse/services/custom-color.gjs +++ /dev/null @@ -1,18 +0,0 @@ -import { tracked } from "@glimmer/tracking"; -import { action } from "@ember/object"; -import Service, { service } from "@ember/service"; -import { DEFAULT_PALETTE_NAME } from "../components/custom-user-palette"; - -const CUSTOM_COLOR_KEY = "d-custom-color-preference"; - -export default class CustomColor extends Service { - @service keyValueStore; - @tracked - color = this.keyValueStore.getItem(CUSTOM_COLOR_KEY) || DEFAULT_PALETTE_NAME; - - @action - setColor(color) { - this.color = color; - this.keyValueStore.setItem(CUSTOM_COLOR_KEY, color); - } -} diff --git a/scss/color-choice.scss b/scss/color-choice.scss index 1856dd9..bbd0efe 100644 --- a/scss/color-choice.scss +++ b/scss/color-choice.scss @@ -1,4 +1,4 @@ -.color-palette-menu { +.user-color-palette-menu { &__item .btn-icon-text.btn-flat { background-color: var(--d-content-background); width: 100%; @@ -23,27 +23,3 @@ .user-color-palette-content .fk-d-menu__inner-content { border: none; } - -html.custom-color-horizon { - --accent-base-color: #595bca; -} - -html.custom-color-marigold { - --accent-base-color: #d3881f; -} - -html.custom-color-violet { - --accent-base-color: #9b15de; -} - -html.custom-color-lily { - --accent-base-color: #cc338c; -} - -html.custom-color-clover { - --accent-base-color: #45a06e; -} - -html.custom-color-royal { - --accent-base-color: #4169e1; -} diff --git a/scss/color-exploration.scss b/scss/color-exploration.scss index a21dc91..167f025 100644 --- a/scss/color-exploration.scss +++ b/scss/color-exploration.scss @@ -1,108 +1,12 @@ :root { - --accent-base-color: #595bca; - --accent-color: light-dark( - var(--accent-base-color), - oklch(from var(--accent-base-color) calc(l * 0.95) c h) - ); - // --background-color: light-dark(#f5f8ff, #101112); - --background-color: light-dark( - oklch(from var(--accent-color) 98% calc(c * 0.25) h), - oklch(from var(--accent-color) 10% 0.025 h) - ); - --header_primary-low-mid: light-dark( - oklch(from var(--background-color) calc(l * 0.75) calc(c * 2) h), - oklch(from var(--accent-color) calc(l * 1) calc(c * 0.25) h) - ); - --header_primary-medium: light-dark( - oklch(from var(--background-color) calc(l * 0.55) calc(c * 2) h), - oklch(from var(--accent-color) calc(l * 1.35) calc(c * 0.25) h) - ); - --d-content-background: light-dark( - oklch(from var(--accent-color) calc(l * 2) 0 h), - oklch(from var(--accent-color) calc(l * 0.375) 0 h) - ); - --primary-100: light-dark(#f2f2f2, #333333); - --primary-300: light-dark(#d1d1d1, #838383); - --primary-low: light-dark(#e9e9e9, #313131); - --primary-high: light-dark(#646464, #a6a6a6); - --primary-very-high: light-dark(#434343, #c7c7c7); - --d-hover: oklch(from var(--d-selected) l c h / 0.75); - --tertiary: var(--accent-color); - --tertiary-medium: light-dark( - oklch(from var(--accent-color) calc(l * 1.25) calc(c * 0.25) h), - oklch(from var(--accent-color) calc(l * 0.75) calc(c * 0.25) h) - ); - --tertiary-very-low: light-dark( - oklch(from var(--accent-color) calc(l * 1.75) calc(c * 0.25) h), - oklch(from var(--accent-color) calc(l * 0.5) calc(c * 0.25) h) - ); - --tertiary-med-or-tertiary: var(--accent-color); - --tertiary-low: light-dark( - oklch(from var(--accent-color) calc(l * 1.6) calc(c * 0.25) h), - oklch(from var(--accent-color) calc(l * 0.6) calc(c * 0.25) h) - ); - --tertiary-300: light-dark( - oklch(from var(--accent-color) calc(l * 1.5) calc(c * 2) h), - oklch(from var(--accent-color) calc(l * 0.65) calc(c * 0.65) h) - ); - --tertiary-high: light-dark( - oklch(from var(--accent-color) calc(l * 1) c h), - oklch(from var(--accent-color) calc(l * 1) c h) - ); --d-sidebar-highlight-hover-icon: var(--d-sidebar-link-color); - --search-color: light-dark( - oklch(from var(--accent-color) calc(l * 0.65) calc(c * 0.65) h), - oklch(from var(--accent-color) calc(l * 1.5) calc(c * 2) h) - ); - --search-banner-text-color: light-dark( - oklch(from var(--accent-color) calc(l * 0.65) calc(c * 0.65) h), - oklch(from var(--accent-color) calc(l * 1.85) calc(c * 2) h) - ); - --topic-card-shadow: light-dark( - oklch(from var(--accent-color) calc(l * 1.85) calc(c * 0.5) h), - oklch(from var(--accent-color) calc(l * 0.2) calc(c * 0.01) h / 0.25) - ); - --button-box-shadow: light-dark( - oklch(from var(--accent-color) calc(l * 1.5) calc(c * 0.35) h), - oklch(from var(--accent-color) calc(l * 0.75) calc(c * 0.5) h) - ); - --d-selected: light-dark( - oklch(from var(--background-color) calc(l * 0.9375) calc(c * 2) h), - oklch(from var(--accent-color) calc(l * 0.7) calc(c * 0.25) h) - ); --d-sidebar-highlight-hover-background: var(--d-selected); - --d-sidebar-border-color: light-dark( - oklch(from var(--background-color) calc(l * 0.9) calc(c * 1) h), - oklch(from var(--accent-color) calc(l * 0.7) calc(c * 0.25) h) - ); - --d-chat-border: light-dark( - oklch(from var(--accent-color) calc(l * 1.25) calc(c * 0.25) h), - oklch(from var(--accent-color) calc(l * 0.25) calc(c * 0.25) h) - ); - --accent-text-color: light-dark(#ffffff, #212121); - --d-nav-color--active: var(--accent-color); - --d-sidebar-background: var(--background-color); - --d-sidebar-footer-fade: rgba(var(--tertiary-50-rgb), 1); - --d-sidebar-link-color: light-dark( - oklch(from var(--accent-color) calc(l * 0.8) calc(c * 0.25) h), - oklch(from var(--accent-color) calc(l * 1.5) calc(c * 0.25) h) - ); --d-sidebar-link-icon-color: var(--d-sidebar-link-color); --d-sidebar-header-color: var(--d-sidebar-link-color); --d-sidebar-header-icon-color: var(--d-sidebar-link-color); - --d-sidebar-suffix-color: light-dark( - oklch(from var(--accent-base-color) calc(l * 1) calc(c * 0.9) h), - oklch(from var(--accent-base-color) calc(l * 1) calc(c * 0.9) h) - ); --d-sidebar-active-suffix-color: var(--d-sidebar-suffix-color); - --link-color: light-dark( - var(--accent-base-color), - oklch(from var(--accent-base-color) calc(l * 0.95) c h) - ); - --link-color-hover: light-dark( - var(--accent-base-color), - oklch(from var(--accent-base-color) calc(l * 1.5) calc(c * 2.25) h) - ); + --d-sidebar-background: var(--background-color); + --d-sidebar-footer-fade: rgba(var(--tertiary-50-rgb), 1); --d-sidebar-prefix-background: var(--d-selected); --d-sidebar-active-prefix-background: light-dark( oklch(from var(--d-selected) calc(l * 0.85) c h), @@ -116,6 +20,6 @@ --d-sidebar-highlight-color: var(--primary); --d-sidebar-highlight-background: var(--d-selected); --d-sidebar-section-link-icon-size: 1em; + --d-hover: oklch(from var(--d-selected) l c h / 0.75); --d-input-bg-color: var(--d-content-background); - --tertiary-hover: var(--accent-color); } diff --git a/spec/system/horizon_high_level_spec.rb b/spec/system/horizon_high_level_spec.rb index 3e5be52..d040852 100644 --- a/spec/system/horizon_high_level_spec.rb +++ b/spec/system/horizon_high_level_spec.rb @@ -1,7 +1,16 @@ # frozen_string_literal: true +require_relative "./page_objects/components/user_color_palette_selector" + describe "Horizon theme | High level", type: :system do - let!(:theme) { upload_theme } + let!(:theme) do + horizon_theme = upload_theme + ColorScheme + .where(theme_id: horizon_theme.id) + .where.not("name LIKE '%Dark%'") + .update_all(user_selectable: true) + horizon_theme + end fab!(:current_user) { Fabricate(:user) } fab!(:tag_1) { Fabricate(:tag, name: "wow-cool") } fab!(:tag_2) { Fabricate(:tag, name: "another-tag") } @@ -10,6 +19,7 @@ describe "Horizon theme | High level", type: :system do let(:topic_list) { PageObjects::Components::TopicList.new } let(:topic_page) { PageObjects::Pages::Topic.new } let(:sidebar) { PageObjects::Components::NavigationMenu::Sidebar.new } + let(:palette_selector) { PageObjects::Components::UserColorPaletteSelector.new } def run_all_high_level_tests expect(page).to have_css(".experimental-screen") @@ -33,18 +43,20 @@ describe "Horizon theme | High level", type: :system do ], ) - # Can see a topic in the list and navigate to it successfully + # Can see a topic in the list and navigate to it successfully. topic_list.visit_topic(topic_1) expect(topic_page).to have_topic_title(topic_1.title) - # Can change site colors from the sidebar palette, which are remembered across page reloads - palette_menu = - PageObjects::Components::DMenu.new(find(".sidebar-footer-actions .user-color-palette")) - palette_menu.expand - find(".color-palette-menu__content .color-palette-menu__item[data-color='marigold']").click - expect(page).to have_css(".custom-color-marigold") + # Can change site colors from the sidebar palette, which are remembered + # across page reloads. + marigold_palette = ColorScheme.find_by(name: "Marigold") + palette_selector.open_palette_menu + palette_selector.click_palette_menu_item(marigold_palette.name) + expect(palette_selector).to have_no_palette_menu + page.refresh - expect(page).to have_css(".custom-color-marigold") + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_tertiary_color(marigold_palette) end it "works for anon" do diff --git a/spec/system/page_objects/components/user_color_palette_selector.rb b/spec/system/page_objects/components/user_color_palette_selector.rb new file mode 100644 index 0000000..7d02704 --- /dev/null +++ b/spec/system/page_objects/components/user_color_palette_selector.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module PageObjects + module Components + class UserColorPaletteSelector < PageObjects::Components::Base + def sidebar_footer_button_css + ".sidebar-footer-actions .user-color-palette-selector" + end + + def palette_menu + PageObjects::Components::DMenu.new(find(sidebar_footer_button_css)) + end + + def open_palette_menu + palette_menu.expand + end + + def has_no_palette_menu? + has_no_css?(".user-color-palette-selector-content") + end + + def click_palette_menu_item(palette_name) + find( + ".user-color-palette-menu__content .user-color-palette-menu__item[data-color-palette='#{palette_name}']", + ).click + end + + def has_selected_palette?(palette) + has_css?(".user-color-palette-selector[data-selected-color-palette-id='#{palette.id}']") + end + + def has_tertiary_color?(palette) + computed_color_hex = + page.evaluate_script( + "getComputedStyle(document.documentElement).getPropertyValue('--tertiary')", + ) + computed_color_hex == "#" + palette.colors.find { |color| color.name == "tertiary" }.hex + end + + def has_computed_color?(color) + computed_background_color = + page.evaluate_script( + "getComputedStyle(document.querySelector(\"li.sidebar-section-link-wrapper[data-list-item-name='everything'] .active\")).backgroundColor", + ) + computed_background_color == color + end + end + end +end diff --git a/spec/system/user_color_palette_selector_spec.rb b/spec/system/user_color_palette_selector_spec.rb new file mode 100644 index 0000000..622a037 --- /dev/null +++ b/spec/system/user_color_palette_selector_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require_relative "./page_objects/components/user_color_palette_selector" + +describe "Horizon theme | User color palette selector", type: :system do + let!(:theme) do + horizon_theme = upload_theme + ColorScheme.where(theme_id: horizon_theme.id).update_all(user_selectable: true) + horizon_theme + end + fab!(:current_user) { Fabricate(:user) } + let(:sidebar) { PageObjects::Components::NavigationMenu::Sidebar.new } + let(:palette_selector) { PageObjects::Components::UserColorPaletteSelector.new } + let(:interface_color_mode) { PageObjects::Components::InterfaceColorMode.new } + let(:interface_color_selector) do + PageObjects::Components::InterfaceColorSelector.new(".sidebar-footer-actions") + end + let(:marigold_palette) { ColorScheme.find_by(name: "Marigold") } + let(:marigold_palette_dark) { ColorScheme.find_by(name: "Marigold Dark") } + + before { SiteSetting.interface_color_selector = "sidebar_footer" } + + it "does not show the sidebar button if there are no user-selectable color palettes" do + ColorScheme.update_all(user_selectable: false) + visit "/" + expect(page).to have_no_css(palette_selector.sidebar_footer_button_css) + end + + describe "for logged in user" do + before { sign_in(current_user) } + + it "can open the user color palette menu and select a palette, which is preseved on reload" do + visit "/" + palette_selector.open_palette_menu + palette_selector.click_palette_menu_item(marigold_palette.name) + expect(palette_selector).to have_no_palette_menu + page.refresh + + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_tertiary_color(marigold_palette) + + page.refresh + expect(palette_selector).to have_selected_palette(marigold_palette) + end + + it "uses the dark version of the palette if the user selects dark mode" do + visit "/" + palette_selector.open_palette_menu + palette_selector.click_palette_menu_item(marigold_palette.name) + expect(palette_selector).to have_no_palette_menu + page.refresh + + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_computed_color("oklch(0.92 0.0708528 68.5036)") + + interface_color_selector.expand + interface_color_selector.dark_option.click + expect(interface_color_mode).to have_dark_mode_forced + expect(palette_selector).to have_computed_color("oklch(0.481966 0.0354264 68.5036)") + + page.refresh + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_computed_color("oklch(0.481966 0.0354264 68.5036)") + end + end + + describe "for anon" do + it "can open the user color palette menu and select a palette, which is preseved on reload" do + visit "/" + palette_selector.open_palette_menu + palette_selector.click_palette_menu_item(marigold_palette.name) + expect(palette_selector).to have_no_palette_menu + page.refresh + + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_tertiary_color(marigold_palette) + end + + it "uses the dark version of the palette if the user selects dark mode, which is preserved on reload" do + visit "/" + palette_selector.open_palette_menu + palette_selector.click_palette_menu_item(marigold_palette.name) + expect(palette_selector).to have_no_palette_menu + page.refresh + + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_computed_color("oklch(0.92 0.0708528 68.5036)") + + interface_color_selector.expand + interface_color_selector.dark_option.click + expect(interface_color_mode).to have_dark_mode_forced + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_computed_color("oklch(0.481966 0.0354264 68.5036)") + + page.refresh + expect(palette_selector).to have_selected_palette(marigold_palette) + expect(palette_selector).to have_computed_color("oklch(0.481966 0.0354264 68.5036)") + end + end +end