From 7c6fcd6058a80c26867a22210843f5fec1c18622 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Tue, 13 May 2025 11:44:06 +0200 Subject: [PATCH 1/8] Add FlashHelper to differentiate between toasts and alerts Since we now have a dedicated UI component for Alerts, we should be able to distinguish which flash type goes where (toast or alert). This helper adds two methods: #toasts will collect any flash key other than `:alert`, ideally we use "notice" and "error" but it's not forced; #alerts will return everything in `flash[:alert]`; `:alert` flash key should be used to construct flashes that are displayed by Alert component. --- .../controllers/solidus_admin/base_controller.rb | 1 + admin/app/helpers/solidus_admin/flash_helper.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 admin/app/helpers/solidus_admin/flash_helper.rb diff --git a/admin/app/controllers/solidus_admin/base_controller.rb b/admin/app/controllers/solidus_admin/base_controller.rb index bd7442f4eee..8854547389a 100644 --- a/admin/app/controllers/solidus_admin/base_controller.rb +++ b/admin/app/controllers/solidus_admin/base_controller.rb @@ -19,6 +19,7 @@ class BaseController < ApplicationController helper 'solidus_admin/components' helper 'solidus_admin/layout' + helper 'solidus_admin/flash' private diff --git a/admin/app/helpers/solidus_admin/flash_helper.rb b/admin/app/helpers/solidus_admin/flash_helper.rb new file mode 100644 index 00000000000..de9308ac727 --- /dev/null +++ b/admin/app/helpers/solidus_admin/flash_helper.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Reserve :alert for messages that should go in UI alert component. Everything else will be shown in UI toast. +module SolidusAdmin + module FlashHelper + def toasts + flash.to_hash.with_indifferent_access.except(:alert) + end + + # Construct alert flashes like +flash[:alert] = { : { title: "", description: "" } }+. + # See +SolidusAdmin::UI::Alert::Component::SCHEMES+ for available alert types. + def alerts + flash.to_hash.with_indifferent_access.fetch(:alert, {}).slice(*SolidusAdmin::UI::Alert::Component::SCHEMES.keys) + end + end +end From 0292a6c8a5c91d915228910a1e387a92252de194 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Tue, 13 May 2025 11:47:50 +0200 Subject: [PATCH 2/8] Update controllers to use flash[:notice] `flash[:notice]` is what is commonly used in other controllers to display a regular success/notice toast, so to be consistent let's use it everywhere. `:success` would also work, but to avoid confusion with Alerts of "success" type (see "ui/alert" component) it's better to use correct naming everywhere. --- .../app/controllers/solidus_admin/adjustments_controller.rb | 6 +++--- admin/app/controllers/solidus_admin/customers_controller.rb | 2 +- admin/app/controllers/solidus_admin/products_controller.rb | 2 +- admin/app/controllers/solidus_admin/users_controller.rb | 2 +- admin/docs/index_pages.md | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/admin/app/controllers/solidus_admin/adjustments_controller.rb b/admin/app/controllers/solidus_admin/adjustments_controller.rb index 350bc3dee5d..f085652211e 100644 --- a/admin/app/controllers/solidus_admin/adjustments_controller.rb +++ b/admin/app/controllers/solidus_admin/adjustments_controller.rb @@ -20,7 +20,7 @@ def index def lock @adjustments = @order.all_adjustments.not_finalized.where(id: params[:id]) @adjustments.each(&:finalize!) - flash[:success] = t('.success') + flash[:notice] = t('.success') redirect_to order_adjustments_path(@order), status: :see_other end @@ -28,7 +28,7 @@ def lock def unlock @adjustments = @order.all_adjustments.finalized.where(id: params[:id]) @adjustments.each(&:unfinalize!) - flash[:success] = t('.success') + flash[:notice] = t('.success') redirect_to order_adjustments_path(@order), status: :see_other end @@ -36,7 +36,7 @@ def unlock def destroy @adjustments = @order.all_adjustments.where(id: params[:id]) @adjustments.destroy_all - flash[:success] = t('.success') + flash[:notice] = t('.success') redirect_to order_adjustments_path(@order), status: :see_other end diff --git a/admin/app/controllers/solidus_admin/customers_controller.rb b/admin/app/controllers/solidus_admin/customers_controller.rb index af79d4d483a..b86ef95c1e6 100644 --- a/admin/app/controllers/solidus_admin/customers_controller.rb +++ b/admin/app/controllers/solidus_admin/customers_controller.rb @@ -13,7 +13,7 @@ def show def destroy if @order.update(user: nil) - flash[:success] = t('.success') + flash[:notice] = t('.success') else flash[:error] = t('.error') end diff --git a/admin/app/controllers/solidus_admin/products_controller.rb b/admin/app/controllers/solidus_admin/products_controller.rb index e5f22336ed3..4475d11cb47 100644 --- a/admin/app/controllers/solidus_admin/products_controller.rb +++ b/admin/app/controllers/solidus_admin/products_controller.rb @@ -43,7 +43,7 @@ def update @product = Spree::Product.friendly.find(params[:id]) if @product.update(product_params) - flash[:success] = t('spree.successfully_updated', resource: [ + flash[:notice] = t('spree.successfully_updated', resource: [ Spree::Product.model_name.human, @product.name.inspect, ].join(' ')) diff --git a/admin/app/controllers/solidus_admin/users_controller.rb b/admin/app/controllers/solidus_admin/users_controller.rb index 7e7d181a1bd..04eaa09ac1e 100644 --- a/admin/app/controllers/solidus_admin/users_controller.rb +++ b/admin/app/controllers/solidus_admin/users_controller.rb @@ -37,7 +37,7 @@ def update_addresses set_address_from_params if @address.valid? && @user.update(user_params) - flash[:success] = t(".#{@type}.success") + flash[:notice] = t(".#{@type}.success") respond_to do |format| format.turbo_stream { render turbo_stream: '' } diff --git a/admin/docs/index_pages.md b/admin/docs/index_pages.md index a17379d5529..d18c4b93592 100644 --- a/admin/docs/index_pages.md +++ b/admin/docs/index_pages.md @@ -94,7 +94,7 @@ end def delete @users = Spree.user_class.where(id: params[:id]) @users.destroy_all - flash[:success] = "Admin users deleted" + flash[:notice] = "Admin users deleted" redirect_to solidus_admin.users_path, status: :see_other end ``` From b191dc3782d69fb69b24cb825c20349d4db31208 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Tue, 13 May 2025 11:48:19 +0200 Subject: [PATCH 3/8] Update application view to render `toasts` from helper --- admin/app/views/layouts/solidus_admin/application.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/app/views/layouts/solidus_admin/application.html.erb b/admin/app/views/layouts/solidus_admin/application.html.erb index 6755eea3c1d..85e0033c1d7 100644 --- a/admin/app/views/layouts/solidus_admin/application.html.erb +++ b/admin/app/views/layouts/solidus_admin/application.html.erb @@ -32,7 +32,7 @@ From c02a105d4a0cdaf565658181ba27e84b607320f3 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Tue, 13 May 2025 12:09:46 +0200 Subject: [PATCH 4/8] Add layout container for alerts --- .../app/views/layouts/solidus_admin/application.html.erb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/admin/app/views/layouts/solidus_admin/application.html.erb b/admin/app/views/layouts/solidus_admin/application.html.erb index 85e0033c1d7..c833d196a47 100644 --- a/admin/app/views/layouts/solidus_admin/application.html.erb +++ b/admin/app/views/layouts/solidus_admin/application.html.erb @@ -31,6 +31,14 @@ +
+ +
+ -
- -
- - + <%= render component("layout/flashes/alerts").new(alerts:) %> + <%= render component("layout/flashes/toasts").new(toasts:) %> diff --git a/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb b/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb new file mode 100644 index 00000000000..85434ad22a6 --- /dev/null +++ b/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe SolidusAdmin::Layout::Flashes::Alerts::Component, type: :component do + let(:component) { described_class.new(alerts:) } + + context "when alerts passed as Hash" do + let(:alerts) do + { warning: { title: "Be careful", description: "Something fishy going on" } } + end + + it "renders correctly" do + render_inline(component) + + aggregate_failures do + expect(page).to have_content("Be careful") + expect(page).to have_content("Something fishy going on") + end + end + end + + context "when alerts passed as String" do + let(:alerts) { "Something fishy going on" } + + it "renders correctly" do + render_inline(component) + + aggregate_failures do + expect(page).to have_content("Caution") + expect(page).to have_content("Something fishy going on") + end + end + end + + describe "multiple alerts" do + let(:alerts) do + { + warning: { title: "Be careful", description: "Something fishy going on" }, + success: { title: "It worked", description: "Nothing to worry about!" } + } + end + + it "renders correctly" do + render_inline(component) + + aggregate_failures do + expect(page).to have_content("Be careful") + expect(page).to have_content("Something fishy going on") + expect(page).to have_content("It worked") + expect(page).to have_content("Nothing to worry about!") + end + end + end +end diff --git a/admin/spec/components/solidus_admin/layout/flashes/toasts/component_spec.rb b/admin/spec/components/solidus_admin/layout/flashes/toasts/component_spec.rb new file mode 100644 index 00000000000..1161a3cbb48 --- /dev/null +++ b/admin/spec/components/solidus_admin/layout/flashes/toasts/component_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe SolidusAdmin::Layout::Flashes::Toasts::Component, type: :component do + let(:component) { described_class.new(toasts:) } + + describe "error toast" do + let(:toasts) { { error: "Some error" } } + + it "renders correctly" do + render_inline(component) + + aggregate_failures do + expect(page).to have_content("Some error") + expect(page).to have_css(".bg-red-500") + end + end + end + + describe "default toast" do + let(:toasts) { { notice: "All good" } } + + it "renders correctly" do + render_inline(component) + + aggregate_failures do + expect(page).to have_content("All good") + expect(page).to have_css(".bg-full-black") + end + end + end +end From 0bd1b35194a1bd57715c99a701f70f2849a7ac92 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 6 Jun 2025 16:56:14 +0200 Subject: [PATCH 6/8] Support default alert titles --- .../solidus_admin/ui/alert/component.rb | 4 +++ .../solidus_admin/ui/alert/component.yml | 8 +++++- .../solidus_admin/ui/alert/component_spec.rb | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/admin/app/components/solidus_admin/ui/alert/component.rb b/admin/app/components/solidus_admin/ui/alert/component.rb index c183eaf8659..e7286df071e 100644 --- a/admin/app/components/solidus_admin/ui/alert/component.rb +++ b/admin/app/components/solidus_admin/ui/alert/component.rb @@ -41,6 +41,10 @@ def initialize(title:, description:, scheme: :success) @scheme = scheme end + def before_render + @title = @title.presence || t(".defaults.titles")[@scheme.to_sym] + end + def icon icon_tag(ICONS.dig(@scheme.to_sym, :name), class: "w-5 h-5 #{ICONS.dig(@scheme.to_sym, :class)}") end diff --git a/admin/app/components/solidus_admin/ui/alert/component.yml b/admin/app/components/solidus_admin/ui/alert/component.yml index a97e4c40196..79f02ceb5e3 100644 --- a/admin/app/components/solidus_admin/ui/alert/component.yml +++ b/admin/app/components/solidus_admin/ui/alert/component.yml @@ -1,2 +1,8 @@ en: - close: Close + close: "Close" + defaults: + titles: + danger: "Caution" + info: "Info" + success: "Success" + warning: "Warning" diff --git a/admin/spec/components/solidus_admin/ui/alert/component_spec.rb b/admin/spec/components/solidus_admin/ui/alert/component_spec.rb index 0927fefcb34..0943a053f27 100644 --- a/admin/spec/components/solidus_admin/ui/alert/component_spec.rb +++ b/admin/spec/components/solidus_admin/ui/alert/component_spec.rb @@ -6,4 +6,29 @@ it "renders the overview preview" do render_preview(:overview) end + + describe "defaults" do + let(:component) { described_class.new(title:, description:, scheme:) } + let(:title) { "Title" } + let(:description) { "Description" } + let(:scheme) { :success } + + context "when title is not present" do + let(:title) { nil } + + shared_examples_for "with default title" do |scheme, expected_title| + let(:scheme) { scheme } + + it "renders default title for scheme #{scheme}" do + render_inline(component) + expect(page).to have_content(expected_title) + end + end + + it_behaves_like "with default title", :success, "Success" + it_behaves_like "with default title", :warning, "Warning" + it_behaves_like "with default title", :danger, "Caution" + it_behaves_like "with default title", :info, "Info" + end + end end From 2526156a9c68aca8a65337fb2ffa75d5621e67ec Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 6 Jun 2025 17:43:46 +0200 Subject: [PATCH 7/8] Support flash[:alert] = Passing strings as messages to `flash[]` is more common than using other primitives, and users deciding to set flash[:alert] might not be aware of the Hash structure that is required to correctly construct and display a UI alert from flash[:alert]. So instead of letting it fail because of the type mismatch (when calling #slice on a string instead of a hash), we can add a fallback option to treat given string as a body of the alert message and construct a `danger` type alert with default title. --- .../solidus_admin/layout/flashes/alerts/component.rb | 12 +++++++++++- admin/app/helpers/solidus_admin/flash_helper.rb | 4 +--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb b/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb index 061982e1b76..d581087a5c4 100644 --- a/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb +++ b/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb @@ -3,7 +3,17 @@ class SolidusAdmin::Layout::Flashes::Alerts::Component < SolidusAdmin::BaseComponent attr_reader :alerts + # Construct alert flashes like: + # flash[:alert] = { : { title: "", description: "" } } + # See +SolidusAdmin::UI::Alert::Component::SCHEMES+ for available alert types. + # + # If a string is passed to flash[:alert], we treat it is a body of the alert message and fall back to +danger+ type + # and default title (see +SolidusAdmin::UI::Alert::Component+). def initialize(alerts:) - @alerts = alerts + if alerts.is_a?(String) + alerts = { danger: { description: alerts } } + end + + @alerts = alerts.slice(*SolidusAdmin::UI::Alert::Component::SCHEMES.keys) end end diff --git a/admin/app/helpers/solidus_admin/flash_helper.rb b/admin/app/helpers/solidus_admin/flash_helper.rb index de9308ac727..81e35651f4b 100644 --- a/admin/app/helpers/solidus_admin/flash_helper.rb +++ b/admin/app/helpers/solidus_admin/flash_helper.rb @@ -7,10 +7,8 @@ def toasts flash.to_hash.with_indifferent_access.except(:alert) end - # Construct alert flashes like +flash[:alert] = { : { title: "", description: "" } }+. - # See +SolidusAdmin::UI::Alert::Component::SCHEMES+ for available alert types. def alerts - flash.to_hash.with_indifferent_access.fetch(:alert, {}).slice(*SolidusAdmin::UI::Alert::Component::SCHEMES.keys) + flash.to_hash.with_indifferent_access.fetch(:alert, {}) end end end From 27adf3de0fd1d922391b6ce69c05dea9a7153ea9 Mon Sep 17 00:00:00 2001 From: Eugene Chaikin Date: Fri, 27 Jun 2025 15:55:14 +0200 Subject: [PATCH 8/8] Update alert attribute "description" to "message" --- .../layout/flashes/alerts/component.html.erb | 2 +- .../solidus_admin/layout/flashes/alerts/component.rb | 4 ++-- .../components/solidus_admin/ui/alert/component.html.erb | 2 +- admin/app/components/solidus_admin/ui/alert/component.rb | 4 ++-- .../ui/alert/component_preview/overview.html.erb | 8 ++++---- .../solidus_admin/layout/flashes/alerts/component_spec.rb | 6 +++--- .../components/solidus_admin/ui/alert/component_spec.rb | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/admin/app/components/solidus_admin/layout/flashes/alerts/component.html.erb b/admin/app/components/solidus_admin/layout/flashes/alerts/component.html.erb index 7af72fd4a1c..816626a867f 100644 --- a/admin/app/components/solidus_admin/layout/flashes/alerts/component.html.erb +++ b/admin/app/components/solidus_admin/layout/flashes/alerts/component.html.erb @@ -1,7 +1,7 @@
diff --git a/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb b/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb index d581087a5c4..7fbdb102c79 100644 --- a/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb +++ b/admin/app/components/solidus_admin/layout/flashes/alerts/component.rb @@ -4,14 +4,14 @@ class SolidusAdmin::Layout::Flashes::Alerts::Component < SolidusAdmin::BaseCompo attr_reader :alerts # Construct alert flashes like: - # flash[:alert] = { : { title: "", description: "" } } + # flash[:alert] = { : { title: "", message: "" } } # See +SolidusAdmin::UI::Alert::Component::SCHEMES+ for available alert types. # # If a string is passed to flash[:alert], we treat it is a body of the alert message and fall back to +danger+ type # and default title (see +SolidusAdmin::UI::Alert::Component+). def initialize(alerts:) if alerts.is_a?(String) - alerts = { danger: { description: alerts } } + alerts = { danger: { message: alerts } } end @alerts = alerts.slice(*SolidusAdmin::UI::Alert::Component::SCHEMES.keys) diff --git a/admin/app/components/solidus_admin/ui/alert/component.html.erb b/admin/app/components/solidus_admin/ui/alert/component.html.erb index a6a892fd2ae..60a7dae1591 100644 --- a/admin/app/components/solidus_admin/ui/alert/component.html.erb +++ b/admin/app/components/solidus_admin/ui/alert/component.html.erb @@ -18,7 +18,7 @@

<%= @title %>

-

<%= @description.html_safe %>

+

<%= @message.html_safe %>

diff --git a/admin/app/components/solidus_admin/ui/alert/component.rb b/admin/app/components/solidus_admin/ui/alert/component.rb index e7286df071e..5605ce4b596 100644 --- a/admin/app/components/solidus_admin/ui/alert/component.rb +++ b/admin/app/components/solidus_admin/ui/alert/component.rb @@ -35,9 +35,9 @@ class SolidusAdmin::UI::Alert::Component < SolidusAdmin::BaseComponent }, } - def initialize(title:, description:, scheme: :success) + def initialize(title:, message:, scheme: :success) @title = title - @description = description + @message = message @scheme = scheme end diff --git a/admin/spec/components/previews/solidus_admin/ui/alert/component_preview/overview.html.erb b/admin/spec/components/previews/solidus_admin/ui/alert/component_preview/overview.html.erb index 6bdd3b2389c..ae0f5764dff 100644 --- a/admin/spec/components/previews/solidus_admin/ui/alert/component_preview/overview.html.erb +++ b/admin/spec/components/previews/solidus_admin/ui/alert/component_preview/overview.html.erb @@ -5,7 +5,7 @@
- <%= render current_component.new(scheme: :success, title: "Added Solidus T-Shirt", description: "Add another product or view product in a new window".html_safe) %> + <%= render current_component.new(scheme: :success, title: "Added Solidus T-Shirt", message: "Add another product or view product in a new window".html_safe) %>
@@ -15,7 +15,7 @@
- <%= render current_component.new(scheme: :warning, title: "No active stock locations left", description: "You can assign new active stock location here".html_safe) %> + <%= render current_component.new(scheme: :warning, title: "No active stock locations left", message: "You can assign new active stock location here".html_safe) %>
@@ -25,7 +25,7 @@
- <%= render current_component.new(scheme: :danger, title: "Request was not completed", description: "Cannot destroy default store") %> + <%= render current_component.new(scheme: :danger, title: "Request was not completed", message: "Cannot destroy default store") %>
@@ -35,7 +35,7 @@
- <%= render current_component.new(scheme: :info, title: "Locales available", description: "Did you know you can update storefront locales here?".html_safe) %> + <%= render current_component.new(scheme: :info, title: "Locales available", message: "Did you know you can update storefront locales here?".html_safe) %>
diff --git a/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb b/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb index 85434ad22a6..046f737bcd5 100644 --- a/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb +++ b/admin/spec/components/solidus_admin/layout/flashes/alerts/component_spec.rb @@ -7,7 +7,7 @@ context "when alerts passed as Hash" do let(:alerts) do - { warning: { title: "Be careful", description: "Something fishy going on" } } + { warning: { title: "Be careful", message: "Something fishy going on" } } end it "renders correctly" do @@ -36,8 +36,8 @@ describe "multiple alerts" do let(:alerts) do { - warning: { title: "Be careful", description: "Something fishy going on" }, - success: { title: "It worked", description: "Nothing to worry about!" } + warning: { title: "Be careful", message: "Something fishy going on" }, + success: { title: "It worked", message: "Nothing to worry about!" } } end diff --git a/admin/spec/components/solidus_admin/ui/alert/component_spec.rb b/admin/spec/components/solidus_admin/ui/alert/component_spec.rb index 0943a053f27..4c284925c18 100644 --- a/admin/spec/components/solidus_admin/ui/alert/component_spec.rb +++ b/admin/spec/components/solidus_admin/ui/alert/component_spec.rb @@ -8,9 +8,9 @@ end describe "defaults" do - let(:component) { described_class.new(title:, description:, scheme:) } + let(:component) { described_class.new(title:, message:, scheme:) } let(:title) { "Title" } - let(:description) { "Description" } + let(:message) { "Message" } let(:scheme) { :success } context "when title is not present" do