2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-10-03 17:21:20 +08:00

UX: fixes and improvements for color palettes (#34359)

Various fixes and improvements for the color palettes admin page... 

* Live previews stopped working because we were never running
`_captureInitialState()` so we had no comparison to see if we could live
preview

* `canPreviewColorScheme` also needed to check against "theme default"
user preference (-1)

* Refactors warning messages if admin user preferences differ from
defaults

<img width="2036" height="260" alt="image"
src="https://github.com/user-attachments/assets/30b6957a-6009-42e7-9cb7-01610536fd1c"
/>

   
    We now warn on: 
* Theme is different from the default (overrides color difference
warnings)
      * Light and/or dark palette are different from the default
      
* Added specs for toasts on palette change, which is a light way to
check if live preview is working (when the conditions are met for the
live preview, we don't show the toast)

* If a color palette isn't editable, make the button say "view" and not
"edit" — we can improve this some more, but this is a simple iterative
step
This commit is contained in:
Kris 2025-08-18 12:20:22 -04:00 committed by GitHub
parent 4c094224b1
commit b60d9bcc8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 184 additions and 21 deletions

View file

@ -25,6 +25,16 @@ export default class ColorPaletteListItem extends Component {
return !this.isBuiltInDefault && this.args.scheme?.id;
}

get isThemePalette() {
return this.args.scheme?.theme_id;
}

get editButtonLabel() {
return this.isThemePalette && !this.isBuiltInDefault
? "admin.customize.colors.view"
: "admin.customize.colors.edit";
}

get canDelete() {
return !this.isBuiltInDefault && !this.args.scheme?.theme_id;
}
@ -157,7 +167,7 @@ export default class ColorPaletteListItem extends Component {
<DButton
@route="adminCustomize.colors-show"
@routeModels={{array @scheme.id}}
@label="admin.customize.colors.edit"
@label={{this.editButtonLabel}}
class="btn-secondary"
@disabled={{not this.canEdit}}
/>

View file

@ -16,6 +16,7 @@ export default class AdminCustomizeColorsController extends Controller {
@service session;
@service site;
@service siteSettings;
@service interfaceColor;

@tracked defaultTheme = null;

@ -36,12 +37,16 @@ export default class AdminCustomizeColorsController extends Controller {

canPreviewColorScheme(mode) {
const usingDefaultTheme = currentThemeId() === this.defaultTheme?.id;

// -1 means they're using the theme default scheme
const usingDefaultLightScheme =
this._initialUserLightColorSchemeId === -1 ||
this._initialUserLightColorSchemeId ===
this._initialDefaultThemeLightColorSchemeId;
this._initialDefaultThemeLightColorSchemeId;
const usingDefaultDarkScheme =
this._initialUserDarkColorSchemeId === -1 ||
this._initialUserDarkColorSchemeId ===
this._initialDefaultThemeDarkColorSchemeId;
this._initialDefaultThemeDarkColorSchemeId;

return (
usingDefaultTheme &&
@ -63,18 +68,86 @@ export default class AdminCustomizeColorsController extends Controller {
this.defaultTheme?.dark_color_scheme_id;
}

get changedThemePreferences() {
const changedTheme = this.defaultTheme?.id !== currentThemeId(this.site);
get userColorSchemeDifferences() {
const userLightDiffersFromDefault =
this._initialUserLightColorSchemeId !== -1 &&
this._initialUserLightColorSchemeId !==
this._initialDefaultThemeLightColorSchemeId;

return changedTheme;
const userDarkDiffersFromDefault =
this._initialUserDarkColorSchemeId !== -1 &&
this._initialUserDarkColorSchemeId !==
this._initialDefaultThemeDarkColorSchemeId;

return { userLightDiffersFromDefault, userDarkDiffersFromDefault };
}

get userPreferencesDifferFromDefaults() {
if (!this.defaultTheme) {
return false;
}

const usingDefaultTheme = currentThemeId() === this.defaultTheme.id;
if (!usingDefaultTheme) {
return true;
}

// only check color scheme preferences if using the default theme
// because if they're not using the default theme, that's the higher priority warning
const { userLightDiffersFromDefault, userDarkDiffersFromDefault } =
this.userColorSchemeDifferences;

return userLightDiffersFromDefault || userDarkDiffersFromDefault;
}

get preferencesWarningMessage() {
if (!this.userPreferencesDifferFromDefaults) {
return null;
}

const themeName = this.defaultTheme?.name || "default theme";
const usingNonDefaultTheme = currentThemeId() !== this.defaultTheme?.id;

if (usingNonDefaultTheme) {
return {
themeName,
usingNonDefaultTheme: true,
};
}

const { userLightDiffersFromDefault, userDarkDiffersFromDefault } =
this.userColorSchemeDifferences;

const affectedModes = [];
if (userLightDiffersFromDefault) {
affectedModes.push("light");
}
if (userDarkDiffersFromDefault) {
affectedModes.push("dark");
}

let colorModesText;
if (affectedModes.length === 2) {
colorModesText = ""; // intentionally left empty
} else if (affectedModes[0] === "light") {
colorModesText = i18n("admin.customize.colors.light");
} else {
colorModesText = i18n("admin.customize.colors.dark");
}

return {
themeName,
colorModes: colorModesText,
usingNonDefaultTheme: false,
};
}

get isUsingDarkMode() {
// check if user has dark mode available and is using it
return (
this.session.darkModeAvailable &&
this.session.userDarkSchemeId !== -1 &&
window.matchMedia("(prefers-color-scheme: dark)").matches
this.interfaceColor.darkModeForced ||
(this.interfaceColor.colorModeIsAuto &&
window.matchMedia("(prefers-color-scheme: dark)").matches) ||
this.session.defaultColorSchemeIsDark
);
}


View file

@ -16,6 +16,13 @@ export default class AdminCustomizeColorsRoute extends Route {
setupController(controller, model) {
super.setupController(controller, model);
controller.set("model", model.colorSchemes);
controller.set("defaultTheme", model.themes.findBy("default", true));

const themes = model.themes || [];
const defaultTheme = themes.find((theme) => theme.default === true);
controller.set("defaultTheme", defaultTheme);

if (defaultTheme) {
controller._captureInitialState();
}
}
}

View file

@ -45,15 +45,26 @@ export default RouteTemplate(
</:actions>
</DPageSubheader>

{{#if @controller.changedThemePreferences}}
<div class="alert alert-info">
{{htmlSafe
(i18n
"admin.customize.colors.preference_warning"
link=(getUrl "/my/preferences/interface")
)
}}
</div>
{{#if @controller.preferencesWarningMessage}}
<p class="color-palette__warning">
{{#if @controller.preferencesWarningMessage.usingNonDefaultTheme}}
{{htmlSafe
(i18n
"admin.customize.colors.non_default_theme_warning"
themeName=@controller.preferencesWarningMessage.themeName
link=(getUrl "/my/preferences/interface")
)
}}
{{else}}
{{htmlSafe
(i18n
"admin.customize.colors.custom_schemes_warning"
colorModes=@controller.preferencesWarningMessage.colorModes
link=(getUrl "/my/preferences/interface")
)
}}
{{/if}}
</p>
{{/if}}

<AdminFilterControls

View file

@ -79,3 +79,7 @@
margin-bottom: 1em;
}
}

.color-palette__warning {
font-style: italic;
}

View file

@ -7119,9 +7119,13 @@ en:
select_base:
title: "Select base color palette"
description: "Base palette:"
preference_warning: "You've changed <a href='%{link}'>your personal theme settings</a>, so palette changes may not be visible to you. Visitors that haven't changed their defaults will still see the changes you make here."
non_default_theme_warning: "Your <a href='%{link}'>personal preferences</a> for theme are different than the default. Changes you make here may not apply to you, but will apply to everyone using the default (%{themeName})."
custom_schemes_warning: "Your <a href='%{link}'>personal preferences</a> for %{colorModes} color palette are different than the default. Changes to the %{colorModes} palette may not apply to you, but will apply to everyone using the default."
light: "light"
dark: "dark"
title: "Colors"
edit: "Edit"
view: "View"
set_default_light: "Set as light palette on default theme (%{theme})"
set_default_dark: "Set as dark palette on default theme (%{theme})"
set_default_success: "%{schemeName} set as default palette for %{themeName}"

View file

@ -196,6 +196,60 @@ describe "Admin Color Palettes Features", type: :system do
end
end

describe "live preview functionality" do
it "does not show toast when live preview is available" do
admin.user_option.update!(
theme_ids: [Theme.find_default.id],
color_scheme_id: -1,
dark_scheme_id: -1,
)

visit("/admin/customize/colors")

within("[data-palette-id='#{regular_palette.id}']") { find(".btn-flat").click }

expect(page).to have_css(".dropdown-menu")

click_button(
I18n.t(
"admin_js.admin.customize.colors.set_default_light",
{ theme: Theme.find_default.name },
),
)

expect(page).to have_no_css(".fk-d-default-toast.-success")
end

it "shows toast when admin cannot see live preview" do
custom_scheme = Fabricate(:color_scheme, name: "Custom Scheme")
admin.user_option.update!(
theme_ids: [Theme.find_default.id],
color_scheme_id: custom_scheme.id,
)

visit("/admin/customize/colors")

within("[data-palette-id='#{regular_palette.id}']") { find(".btn-flat").click }

expect(page).to have_css(".dropdown-menu")

click_button(
I18n.t(
"admin_js.admin.customize.colors.set_default_light",
{ theme: Theme.find_default.name },
),
)

expected_message =
I18n.t(
"admin_js.admin.customize.colors.set_default_success",
schemeName: regular_palette.name,
themeName: Theme.find_default.name,
)
expect(toasts).to have_success(expected_message)
end
end

describe "sort" do
before do
ColorScheme.delete_all