Skip to content

Commit

Permalink
Add a theme selector to the backend UI with dark and dimmed variants
Browse files Browse the repository at this point in the history
- Save the user preference alongside the system preference so that
  when the system switches the UI will follow along.
- Use the session to store preferences so that we start the page
  with the correct theme(s).
- Keep the current theme in the select tag up to date at page load
  and whenever the system changes.

Add dark and dimmed theme variants

dark:
  Full black background with no alteration on images.

dimmed:
  Dark gray background with dimmed images, easier on the eyes but
  less accurate colors.

Co-Authored-By: piyushswain <[email protected]>
Co-Authored-By: Elia Schito <[email protected]>
Co-Authored-By: Massimiliano Lattanzio <[email protected]>
  • Loading branch information
3 people committed Nov 14, 2023
1 parent 3881323 commit e31870d
Show file tree
Hide file tree
Showing 19 changed files with 226 additions and 8 deletions.
4 changes: 1 addition & 3 deletions backend/app/assets/config/solidus_backend_manifest.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
//= link_tree ../images
//= link_tree "../stylesheets/spree/backend/themes" .css

//= link spree/backend/all.js
//= link spree/backend/all.css

//= link solidus_admin/select2_locales

//= link spree/backend/themes/classic.css
//= link spree/backend/themes/solidus_admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ nav.menu {
@media print {
display: none
}


.dark-only {
display: none;
@media (prefers-color-scheme: dark) {
display: block;
}
}

.light-only {
display: block;
@media (prefers-color-scheme: dark) {
display: none;
}
}

}

.admin-nav-header {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ $color-navbar-hover: $color-navbar !default;
min-height: 100vh;
border-right: $color-light-accent 1px solid;

.dark-only {
display: none;
@media (prefers-color-scheme: dark) {
display: block;
}
}

.light-only {
display: none;
@media (prefers-color-scheme: light) {
display: block;
}
}

&--wrapper {
position: absolute;
top: 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import 'spree/backend/themes/classic';

html {
background-color: #fff;
color: #fff;
-webkit-filter: invert(100%);
filter: invert(100%) hue-rotate(180deg);

img {
filter: invert(100%) hue-rotate(-180deg);
}

.brand-link img {
filter: invert(0%) hue-rotate(0deg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import 'spree/backend/themes/classic';

html {
background-color: #fff;
color: #fff;
-webkit-filter: invert(85%);
filter: invert(85%) hue-rotate(180deg);

img {
filter: invert(100%) hue-rotate(-180deg);
}

.brand-link img {
filter: invert(0%) hue-rotate(0deg);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import "spree/backend/globals";

@import "./solidus_admin/variables";
@import "./solidus_admin/colors";

@import "spree/backend/vendor";
@import "spree/backend/shared";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "./colors";

table.index {
// Borders
border-collapse: separate;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import "./solidus_admin";

html {
background-color: #fff;
color: #fff;
-webkit-filter: invert(100%);
filter: invert(100%) hue-rotate(180deg);

img {
filter: invert(100%) hue-rotate(-180deg);
}

.brand-link img {
filter: invert(0%) hue-rotate(0deg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import "./solidus_admin";

html {
background-color: #fff;
color: #fff;
-webkit-filter: invert(85%);
filter: invert(85%) hue-rotate(180deg);

img {
filter: invert(100%) hue-rotate(-180deg);
}

.brand-link img {
filter: invert(0%) hue-rotate(0deg);
}
}
30 changes: 30 additions & 0 deletions backend/app/controllers/spree/admin/theme_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module Spree
module Admin
class ThemeController < Spree::Admin::BaseController
skip_before_action :authorize_admin, only: [:set]

def set
requested_theme = params[:switch_to_theme].presence

# Avoid interpolating user content into the session key
system_theme = params[:system_theme].presence == "dark" ? "dark" : "light"
session_key = :"admin_#{system_theme}_theme"

if theme_is_available?(requested_theme)
session[session_key] = requested_theme
redirect_back_or_to spree.admin_url, notice: t('spree.theme_changed')
else
redirect_back_or_to spree.admin_url, error: t('spree.error')
end
end

private

def theme_is_available?(theme)
theme && Spree::Backend::Config.themes.key?(theme.to_sym)
end
end
end
end
3 changes: 2 additions & 1 deletion backend/app/views/spree/admin/shared/_head.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
<title><%= admin_page_title %></title>

<%= favicon_link_tag 'favicon.ico' %>
<%= stylesheet_link_tag Spree::Backend::Config.theme_path, media: 'all', data: {turbolinks_track: 'reload'} %>
<%= stylesheet_link_tag Spree::Backend::Config.theme_path(session[:admin_light_theme]), media: '(prefers-color-scheme: light)', data: {turbolinks_track: 'reload'} %>
<%= stylesheet_link_tag Spree::Backend::Config.theme_path(session[:admin_dark_theme]), media: '(prefers-color-scheme: dark)', data: {turbolinks_track: 'reload'} %>
<%= javascript_include_tag 'spree/backend/all', data: {turbolinks_track: 'reload'} %>
<%- if Rails.env.test? %>
Expand Down
1 change: 1 addition & 0 deletions backend/app/views/spree/admin/shared/_navigation.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<span class="text"><%= t('spree.minimize_menu') %></span>
<% end %>
<%= render partial: 'spree/admin/shared/locale_selection' %>
<%= render partial: 'spree/admin/shared/theme_selection' %>
<% if lookup_context.exists?('spree/admin/shared/_navigation_footer') %>
<%= render partial: 'spree/admin/shared/navigation_footer' %>
<% else %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@

<ul>
<%= render 'spree/admin/shared/locale_selection_solidus_admin' %>
<%= render 'spree/admin/shared/theme_selection_solidus_admin' %>
<% if can?(:admin, spree_current_user) %>
<li data-hook="user-account-link">
Expand Down
21 changes: 21 additions & 0 deletions backend/app/views/spree/admin/shared/_theme_selection.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<% theme_options_for_select = Spree::Backend::Config.themes.keys.map { |theme| [theme.to_s.humanize, theme] }.sort %>
<%= form_tag(admin_set_theme_path(format: :html), method: :put, style: "width: 100%;", class: "light-only") do %>
<%= hidden_field_tag :system_theme, :light %>
<label class="admin-navbar-selection">
<i class="fa fa-sun-o fa-fw" title="<%= I18n.t('spree.choose_dashboard_theme') %>"></i>
<select name="switch_to_theme" class="custom-select fullwidth" onchange="this.form.requestSubmit()">
<%= options_for_select(theme_options_for_select, session[:admin_light_theme] || Spree::Backend::Config.theme) %>
</select>
</label>
<% end %>
<%= form_tag(admin_set_theme_path(format: :html), method: :put, style: "width: 100%;", class: "dark-only") do %>
<%= hidden_field_tag :system_theme, :dark %>
<label class="admin-navbar-selection">
<i class="fa fa-moon-o fa-fw" title="<%= I18n.t('spree.choose_dashboard_theme') %>"></i>
<select name="switch_to_theme" class="custom-select fullwidth" onchange="this.form.requestSubmit()">
<%= options_for_select(theme_options_for_select, session[:admin_dark_theme] || Spree::Backend::Config.dark_theme) %>
</select>
</label>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<% theme_options_for_select = Spree::Backend::Config.themes.keys.map { |theme| [theme.to_s.humanize, theme] }.sort %>

<li>
<%= form_tag(admin_set_theme_path(format: :html), method: :put, style: "width: 100%;", class: "light-only") do %>
<%= hidden_field_tag :system_theme, :light %>
<label>
<svg aria-hidden="true"><use xlink:href="<%= image_path('spree/backend/themes/solidus_admin/remixicon.symbol.svg') %>#ri-sun-line"></use></svg>
<select name="switch_to_theme" onchange="this.form.requestSubmit()">
<%= options_for_select(theme_options_for_select, session[:admin_light_theme] || Spree::Backend::Config.theme) %>
</select>
<svg aria-hidden="true"><use xlink:href="<%= image_path('spree/backend/themes/solidus_admin/remixicon.symbol.svg') %>#ri-expand-up-down-line"></use></svg>
</label>
<% end %>
<%= form_tag(admin_set_theme_path(format: :html), method: :put, style: "width: 100%;", class: "dark-only") do %>
<%= hidden_field_tag :system_theme, :dark %>
<label>
<svg aria-hidden="true"><use xlink:href="<%= image_path('spree/backend/themes/solidus_admin/remixicon.symbol.svg') %>#ri-moon-line"></use></svg>
<select name="switch_to_theme" onchange="this.form.requestSubmit()">
<%= options_for_select(theme_options_for_select, session[:admin_dark_theme] || Spree::Backend::Config.dark_theme) %>
</select>
<svg aria-hidden="true"><use xlink:href="<%= image_path('spree/backend/themes/solidus_admin/remixicon.symbol.svg') %>#ri-expand-up-down-line"></use></svg>
</label>
<% end %>
</li>
1 change: 1 addition & 0 deletions backend/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
get '/search/products', to: "search#products", as: :search_products

put '/locale/set', to: 'locale#set', defaults: { format: :json }, as: :set_locale
put '/theme/set', to: 'theme#set', defaults: { format: :json }, as: :set_theme

resources :dashboards, only: [] do
collection do
Expand Down
17 changes: 13 additions & 4 deletions backend/lib/spree/backend_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,30 @@ class BackendConfiguration < Preferences::Configuration
# @return [Hash] A hash containing the themes that are available for the admin panel
preference :themes, :hash, default: {
classic: 'spree/backend/all',
classic_dark: 'spree/backend/themes/classic_dark',
classic_dark_dimmed: 'spree/backend/themes/classic_dimmed',
solidus: 'spree/backend/themes/solidus_admin',
solidus_dark: 'spree/backend/themes/solidus_admin_dark',
solidus_dimmed: 'spree/backend/themes/solidus_admin_dimmed',
solidus_admin: 'spree/backend/themes/solidus_admin'
}

# @!attribute [rw] theme
# @return [String] Default admin theme name
versioned_preference :theme, :string, initial_value: 'classic', boundaries: { "4.2.0" => "solidus_admin" }
versioned_preference :theme, :string, initial_value: 'classic', boundaries: { "4.2.0" => "solidus_admin", "4.4.0" => "solidus" }

def theme_path(user_theme = nil)
user_theme ? themes.fetch(user_theme.to_sym) : themes.fetch(theme.to_sym)
# @!attribute [rw] dark_theme
# @return [String] Dark admin theme name
versioned_preference :dark_theme, :string, initial_value: 'classic', boundaries: { "4.2.0" => "solidus_admin", "4.4.0" => 'solidus_dark' }

def theme_path(user_theme)
themes.fetch(user_theme.to_sym, themes.fetch(theme.to_sym))
end

# @!attribute [rw] admin_updated_navbar
# @return [Boolean] Should the updated navbar be used in admin (default: +false+)
#
versioned_preference :admin_updated_navbar, :boolean, initial_value: false, boundaries: { "4.2.0" => true }
versioned_preference :admin_updated_navbar, :boolean, initial_value: false, boundaries: { "4.2.0" => false }

preference :frontend_product_path,
:proc,
Expand Down
32 changes: 32 additions & 0 deletions backend/spec/controllers/spree/admin/theme_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Spree::Admin::ThemeController, type: :controller do
stub_authorization!

it 'sets the theme in a different session key for each system theme' do
stub_spree_preferences(Spree::Backend::Config, themes: { foo: 'foo-path', bar: 'bar-path' })

get :set, params: { switch_to_theme: 'foo', system_theme: 'light', format: :json }

expect(session[:admin_light_theme]).to eq('foo')
expect(session[:admin_dark_theme]).to eq(nil)
expect(response).to have_http_status(:ok)

get :set, params: { switch_to_theme: 'bar', system_theme: 'dark', format: :json }
expect(session[:admin_light_theme]).to eq('foo')
expect(session[:admin_dark_theme]).to eq('bar')
expect(response).to have_http_status(:ok)
end

it 'responds with "not found" for a missing theme' do
stub_spree_preferences(Spree::Backend::Config, themes: { foo: 'foo-path' })

get :set, params: { switch_to_theme: 'bar', system_theme: 'dark', format: :json }

expect(session[:admin_light_theme]).to eq(nil)
expect(session[:admin_dark_theme]).to eq(nil)
expect(response).to have_http_status(:not_found)
end
end
2 changes: 2 additions & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,7 @@ en:
choose_a_taxon_to_sort_products_for: Choose a taxon to sort products for
choose_currency: Choose Currency
choose_dashboard_locale: Choose Dashboard Locale
choose_dashboard_theme: Choose Dashboard Theme
choose_location: Choose Location
choose_promotion_action: Choose Action
choose_promotion_rule: Choose Rule
Expand Down Expand Up @@ -2347,6 +2348,7 @@ en:
subject: Test Mail
test_mode: Test Mode
thank_you_for_your_order: Thank you for your business. Please print out a copy of this confirmation page for your records.
theme_changed: Theme Changed
there_are_no_items_for_this_order: There are no items for this order. Please add an item to the order to continue.
there_were_problems_with_the_following_fields: There were problems with the following fields
this_order_has_already_received_a_refund: This order has already received a refund
Expand Down

0 comments on commit e31870d

Please sign in to comment.