diff --git a/app/assets/javascripts/alchemy/admin.js b/app/assets/javascripts/alchemy/admin.js index f7cdfb5eb8..597bfed13d 100644 --- a/app/assets/javascripts/alchemy/admin.js +++ b/app/assets/javascripts/alchemy/admin.js @@ -8,7 +8,6 @@ //= require alchemy/alchemy.confirm_dialog //= require alchemy/alchemy.elements_window //= require alchemy/alchemy.fixed_elements -//= require alchemy/alchemy.growler //= require alchemy/alchemy.image_overlay //= require alchemy/alchemy.link_dialog //= require alchemy/alchemy.preview_window diff --git a/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee b/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee index ba1116649d..3d9fbfe62a 100644 --- a/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee @@ -136,10 +136,10 @@ class window.Alchemy.Dialog else error_header = "#{xhr.statusText} (#{xhr.status})" error_body = "Please check log and try again." - $errorDiv = $("
") - $errorDiv.append Alchemy.messageIcon(error_type) - $errorDiv.append "

#{error_header}

" - $errorDiv.append "

#{error_body}

" + $errorDiv = $(" +

#{error_header}

+

#{error_body}

+
") $container.html $errorDiv # Binds close events on: @@ -265,14 +265,3 @@ window.Alchemy.watchForDialogs = (scope = '#alchemy') -> return event.preventDefault() return - -# Returns a Remix icon for given message type -# -window.Alchemy.messageIcon = (messageType) -> - icon_name = switch messageType - when "warning", "warn", "alert" then "alert" - when "notice" then "check" - when "info" then "information" - when "error" then "bug" - else messageType - "" diff --git a/app/assets/javascripts/alchemy/alchemy.growler.js.coffee b/app/assets/javascripts/alchemy/alchemy.growler.js.coffee deleted file mode 100644 index 5d48615761..0000000000 --- a/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +++ /dev/null @@ -1,26 +0,0 @@ -window.Alchemy = {} if typeof (window.Alchemy) is "undefined" - -Alchemy.Growler = - - build: (message, flash_type) -> - $flash_container = $("
") - $flash_container.append Alchemy.messageIcon(flash_type) - if flash_type == "error" - $flash_container.append "" - $flash_container.append message - $("#flash_notices").append $flash_container - $("#flash_notices").show() - Alchemy.Growler.fade() - - fade: -> - $(".flash:not(.error)", "#flash_notices").delay(5000).queue(-> Alchemy.Growler.dismiss(this)) - $(".flash", "#flash_notices").on("click", (e) => @dismiss(e.currentTarget)) - return - - dismiss: (element) -> - $(element).on 'transitionend', => $(element).remove() - $(element).addClass('dismissed') - return - -Alchemy.growl = (message, style = "notice") -> - Alchemy.Growler.build message, style diff --git a/app/assets/stylesheets/alchemy/flash.scss b/app/assets/stylesheets/alchemy/flash.scss index 0a5ca3589d..a38b2cc5c4 100644 --- a/app/assets/stylesheets/alchemy/flash.scss +++ b/app/assets/stylesheets/alchemy/flash.scss @@ -5,74 +5,28 @@ div#flash_notices { width: 400px; top: 0; - .flash.error { - cursor: pointer; - padding-right: 32px; - - alchemy-icon[name="close"] { - position: absolute; - left: initial; - right: 2 * $default-padding; + alchemy-message { + font-weight: bold; + margin: $default-margin $default-margin 2 * $default-margin; + opacity: 0.95; + transition-property: opacity, transform; + transition-duration: 0.4s; + + &.dismissed { + display: block; + opacity: 0; + transform: translate3d(0, -100%, 0); } - } -} - -div.flash { - border-radius: $default-border-radius; - opacity: 0.95; - padding: 8px 16px 8px 32px; - font-weight: bold; - border-width: 1px; - border-style: solid; - z-index: 1000; - margin: $default-margin $default-margin 2 * $default-margin; - position: relative; - min-height: 2.6em; - word-break: break-word; - transition-property: opacity, transform; - transition-duration: 0.4s; - line-height: 1.5; - alchemy-icon { - position: absolute; - top: 9px; - left: 2 * $default-padding; - vertical-align: bottom; + &[type="error"] { + cursor: pointer; + padding-right: 32px; - .icon { - fill: currentColor; + alchemy-icon[name="close"] { + position: absolute; + left: initial; + right: 2 * $default-padding; + } } } - - &.dismissed { - display: block; - opacity: 0; - transform: translate3d(0, -100%, 0); - } - - &.notice { - border-color: $success_border_color; - color: $success_text_color; - background-color: $success_background_color; - } - - &.error { - border-color: $error_border_color; - color: $error_text_color; - background-color: $error_background_color; - } - - &.info { - border-color: $info_border_color; - color: $info_text_color; - background-color: $info_background_color; - } - - &.warn, - &.warning, - &.alert { - border-color: $warning_border_color; - color: $warning_text_color; - background-color: $warning_background_color; - } } diff --git a/app/assets/stylesheets/alchemy/notices.scss b/app/assets/stylesheets/alchemy/notices.scss index 2a9a512b5c..51e472d1b0 100644 --- a/app/assets/stylesheets/alchemy/notices.scss +++ b/app/assets/stylesheets/alchemy/notices.scss @@ -1,18 +1,51 @@ -// Some table columns also have .message class -div.message { - padding: 8px 8px 8px 32px; - line-height: 17px; +alchemy-message { + display: block; border-width: $default-border-width; border-style: $default-border-style; border-radius: $default-border-radius; position: relative; + padding: 8px 16px 8px 32px; margin-bottom: 8px; text-align: left; + min-height: 2.6em; + word-break: break-word; + line-height: 1.5; - h1, - h2, - h3 { - margin-top: 0; + &[type="footnote"] { + font-size: $small-font-size; + margin: 16px; + } + + &[type="notice"] { + border-color: $success_border_color; + color: $success_text_color; + background-color: $success_background_color; + } + + &[type="hint"] { + background-color: $hint-background-color; + border-color: $hint-background-color; + color: $hint-text-color; + } + + &[type="info"] { + background-color: $info_background_color; + border-color: $info_border_color; + color: $info_text_color; + } + + &[type="error"] { + background-color: $error_background_color; + border-color: $error_border_color; + color: $error_text_color; + } + + &[type="warning"], + &[type="warn"], + &[type="alert"] { + background-color: $warning_background_color; + border-color: $warning_border_color; + color: $warning_text_color; } alchemy-icon { @@ -26,9 +59,15 @@ div.message { } } - &.footnote { - font-size: $small-font-size; - margin: 16px; + h1 { + font-size: 1.3rem; + line-height: 1.1; + } + + h1, + h2, + h3 { + margin-top: 0; } a { @@ -49,30 +88,4 @@ div.message { margin-bottom: 4px; } } - - &.hint { - background-color: $hint-background-color; - border-color: $hint-background-color; - color: $hint-text-color; - } - - &.info { - background-color: $info_background_color; - border-color: $info_border_color; - color: $info_text_color; - } - - &.error { - background-color: $error_background_color; - border-color: $error_border_color; - color: $error_text_color; - } - - &.warning, - &.warn, - &.alert { - background-color: $warning_background_color; - border-color: $warning_border_color; - color: $warning_text_color; - } } diff --git a/app/components/alchemy/admin/message.rb b/app/components/alchemy/admin/message.rb new file mode 100644 index 0000000000..7c9dd3bcf0 --- /dev/null +++ b/app/components/alchemy/admin/message.rb @@ -0,0 +1,19 @@ +module Alchemy + module Admin + class Message < ViewComponent::Base + attr_reader :message, :type, :dismissable + + erb_template <<~ERB + > + <%= message || content %> + + ERB + + def initialize(message = nil, type: :info, dismissable: false) + @message = message + @dismissable = dismissable + @type = type + end + end + end +end diff --git a/app/helpers/alchemy/admin/base_helper.rb b/app/helpers/alchemy/admin/base_helper.rb index e9626c8cea..6408fd840f 100644 --- a/app/helpers/alchemy/admin/base_helper.rb +++ b/app/helpers/alchemy/admin/base_helper.rb @@ -287,16 +287,6 @@ def toolbar_button(options = {}) end end - # (internal) Used by upload form - def new_asset_path_with_session_information(asset_type) - session_key = Rails.application.config.session_options[:key] - if asset_type == "picture" - alchemy.admin_pictures_path(session_key => cookies[session_key], request_forgery_protection_token => form_authenticity_token, :format => :js) - elsif asset_type == "attachment" - alchemy.admin_attachments_path(session_key => cookies[session_key], request_forgery_protection_token => form_authenticity_token, :format => :js) - end - end - # Renders a textfield ready to display a datepicker # # A Javascript observer converts this into a fancy Datepicker. diff --git a/app/helpers/alchemy/base_helper.rb b/app/helpers/alchemy/base_helper.rb index 07a9fcf624..7def0540a4 100644 --- a/app/helpers/alchemy/base_helper.rb +++ b/app/helpers/alchemy/base_helper.rb @@ -4,9 +4,11 @@ module Alchemy module BaseHelper # An alias for truncate. # Left here for downwards compatibilty. + # @deprecated def shorten(text, length) text.truncate(length: length) end + deprecate :shorten, deprecator: Alchemy::Deprecation # Logs a message in the Rails logger (warn level) # and optionally displays an error message to the user. @@ -41,26 +43,22 @@ def render_icon(icon_name, options = {}) # <% end %> # def render_message(type = :info, msg = nil, &blk) - icon_class = message_icon_class(type) - if blk - content_tag :div, render_icon(icon_class) + capture(&blk), class: "#{type} message" - else - content_tag :div, render_icon(icon_class) + msg, class: "#{type} message" - end + render Alchemy::Admin::Message.new(msg || capture(&blk), type: type) end - # Renders the flash partial (+alchemy/admin/partials/flash+) + # Renders a dismissable growl message. # - # @param [String] notice The notice you want to display - # @param [Symbol] style The style of this flash. Valid values are +:notice+ (default), +:warn+ and +:error+ + # @param [String] notice - The notice you want to display + # @param [Symbol] type - The type of this flash. Valid values are +:notice+ (default), +:warn+, +:info+ and +:error+ # - def render_flash_notice(notice, style = :notice) - render("alchemy/admin/partials/flash", flash_type: style, message: notice) + def render_flash_notice(notice, type = :notice) + render Alchemy::Admin::Message.new(notice, type: type, dismissable: true) end # Checks if the given argument is a String or a Page object. # If a String is given, it tries to find the page via page_layout # Logs a warning if no page is given. + # @deprecated def page_or_find(page) unless Current.language warning("No default language set up") @@ -77,20 +75,6 @@ def page_or_find(page) page end end - - # Returns the icon name for given message type - # - # @param message_type [String] The message type. One of +warning+, +info+, +notice+, +error+ - # @return [String] The icon name - def message_icon_class(message_type) - case message_type.to_s - when "warning", "warn", "alert" then "exclamation" - when "notice" then "check" - when "error" then "bug" - when "hint" then "info" - else - message_type - end - end + deprecate :page_or_find, deprecator: Alchemy::Deprecation end end diff --git a/app/javascript/alchemy_admin.js b/app/javascript/alchemy_admin.js index bdd91185a2..0e94f13757 100644 --- a/app/javascript/alchemy_admin.js +++ b/app/javascript/alchemy_admin.js @@ -6,6 +6,7 @@ import Rails from "@rails/ujs" import GUI from "alchemy_admin/gui" import { translate } from "alchemy_admin/i18n" import Dirty from "alchemy_admin/dirty" +import { growl } from "alchemy_admin/growler" import IngredientAnchorLink from "alchemy_admin/ingredient_anchor_link" import ImageLoader from "alchemy_admin/image_loader" import ImageCropper from "alchemy_admin/image_cropper" @@ -25,6 +26,7 @@ import "alchemy_admin/components/clipboard_button" import "alchemy_admin/components/datepicker" import "alchemy_admin/components/dialog_link" import "alchemy_admin/components/element_editor" +import "alchemy_admin/components/message" import "alchemy_admin/components/growl" import "alchemy_admin/components/icon" import "alchemy_admin/components/ingredient_group" @@ -71,6 +73,7 @@ Object.assign(Alchemy, { ...Dirty, GUI, t: translate, // Global utility method for translating a given string + growl, ImageLoader: ImageLoader.init, ImageCropper, Initializer, diff --git a/app/javascript/alchemy_admin/components/message.js b/app/javascript/alchemy_admin/components/message.js new file mode 100644 index 0000000000..6380cdd45d --- /dev/null +++ b/app/javascript/alchemy_admin/components/message.js @@ -0,0 +1,69 @@ +const DISMISS_DELAY = 5000 + +class Message extends HTMLElement { + #message + + constructor() { + super() + this.#message = this.innerHTML + if (this.dismissable || this.type === "error") { + this.addEventListener("click", this) + } + } + + handleEvent(event) { + if (event.type === "click") { + this.dismiss() + } + } + + connectedCallback() { + this.innerHTML = ` + + ${this.dismissable && this.type === "error" ? '' : ""} + ${this.#message} + ` + if (this.dismissable && this.type !== "error") { + setTimeout(() => { + this.dismiss() + }, this.delay) + } + } + + dismiss() { + this.addEventListener("transitionend", () => this.remove()) + this.classList.add("dismissed") + } + + get dismissable() { + return this.hasAttribute("dismissable") + } + + get type() { + return this.getAttribute("type") || "notice" + } + + get delay() { + return parseInt(this.getAttribute("delay") || DISMISS_DELAY) + } + + get iconName() { + switch (this.type) { + case "warning": + case "warn": + case "alert": + return "alert" + case "notice": + return "check" + case "info": + case "hint": + return "information" + case "error": + return "bug" + default: + return this.type + } + } +} + +customElements.define("alchemy-message", Message) diff --git a/app/javascript/alchemy_admin/growler.js b/app/javascript/alchemy_admin/growler.js new file mode 100644 index 0000000000..2b25dbdeb3 --- /dev/null +++ b/app/javascript/alchemy_admin/growler.js @@ -0,0 +1,15 @@ +import { createHtmlElement } from "alchemy_admin/utils/dom_helpers" + +function build(message, flashType) { + const flashNotices = document.getElementById("flash_notices") + const flashMessage = createHtmlElement(` + + ${message} + + `) + flashNotices.append(flashMessage) +} + +export function growl(message, style = "notice") { + build(message, style) +} diff --git a/app/javascript/alchemy_admin/initializer.js b/app/javascript/alchemy_admin/initializer.js index 0325829f2d..fb0fbd4017 100644 --- a/app/javascript/alchemy_admin/initializer.js +++ b/app/javascript/alchemy_admin/initializer.js @@ -25,11 +25,6 @@ function Initialize() { // Initialize the GUI. Alchemy.GUI.init() - // Fade all growl notifications. - if ($("#flash_notices").length > 0) { - Alchemy.Growler.fade() - } - // Add observer for please wait overlay. $(".please_wait") .not("*[data-alchemy-confirm]") diff --git a/app/views/alchemy/admin/partials/_flash.html.erb b/app/views/alchemy/admin/partials/_flash.html.erb deleted file mode 100644 index 7a94803849..0000000000 --- a/app/views/alchemy/admin/partials/_flash.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= render_icon message_icon_class(flash_type) %> - <%= message %> -
diff --git a/app/views/alchemy/admin/partials/_flash_notices.html.erb b/app/views/alchemy/admin/partials/_flash_notices.html.erb index 38bb817077..3ff6440cf4 100644 --- a/app/views/alchemy/admin/partials/_flash_notices.html.erb +++ b/app/views/alchemy/admin/partials/_flash_notices.html.erb @@ -1,5 +1,7 @@ -
"> +
<% flash.keys.each do |flash_type| %> - <%= render_flash_notice(flash[flash_type.to_sym], flash_type) if flash[flash_type.to_sym].present? %> + <% if flash[flash_type.to_sym].present? %> + <%= render_flash_notice(flash[flash_type.to_sym], flash_type) %> + <% end %> <% end %>
diff --git a/app/views/alchemy/admin/styleguide/index.html.erb b/app/views/alchemy/admin/styleguide/index.html.erb index 4ccbf7bc16..0c9fd4b87f 100644 --- a/app/views/alchemy/admin/styleguide/index.html.erb +++ b/app/views/alchemy/admin/styleguide/index.html.erb @@ -38,13 +38,13 @@

Info Message

<%= render_message do %> -

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

<% end %>

Warning Message

<%= render_message :warning do %> -

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

<% end %>

Error Message

@@ -53,27 +53,32 @@

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

<% end %> +

Growl Messages

+ +<%= link_to render_icon(:check), 'javascript:Alchemy.growl("Success message")', class: "icon_button" %> +<%= link_to render_icon(:info), 'javascript:Alchemy.growl("Info message", "info")', class: "icon_button" %> +<%= link_to render_icon(:alert), 'javascript:Alchemy.growl("Warning message", "warn")', class: "icon_button" %> +<%= link_to render_icon(:bug), 'javascript:Alchemy.growl("Error message", "error")', class: "icon_button" %> +

Dialog Links

- + + + <%= render_icon "window" %> +

Forms

diff --git a/app/views/alchemy/base/error_notice.html.erb b/app/views/alchemy/base/error_notice.html.erb index 649c79e1db..52b0cf7df7 100644 --- a/app/views/alchemy/base/error_notice.html.erb +++ b/app/views/alchemy/base/error_notice.html.erb @@ -1 +1,3 @@ -<%= render_flash_notice @notice, :error %> +<%= render_message :error do %> + <%= @notice %> +<% end %> diff --git a/spec/components/alchemy/admin/message_spec.rb b/spec/components/alchemy/admin/message_spec.rb new file mode 100644 index 0000000000..55a67bab09 --- /dev/null +++ b/spec/components/alchemy/admin/message_spec.rb @@ -0,0 +1,55 @@ +require "rails_helper" + +RSpec.describe Alchemy::Admin::Message, type: :component do + before do + render + end + + subject(:render) do + render_inline described_class.new(message) + end + + let(:message) { "This is a message" } + + it "renders an alchemy-message with default type" do + expect(page).to have_css('alchemy-message[type="info"]', text: "This is a message") + end + + it "renders a not-dismissable alchemy-message by default" do + expect(page).to_not have_css "alchemy-message[dismissable]" + end + + context "with message given as block" do + subject(:render) do + render_inline described_class.new do + "

This is a block message

".html_safe + end + end + + it "renders an alchemy-message with default type" do + expect(page).to have_css('alchemy-message[type="info"] > p', text: "This is a block message") + end + end + + context "with type given" do + subject(:render) do + render_inline described_class.new(message, type: type) + end + + let(:type) { "alert" } + + it "renders an alchemy-message with given type" do + expect(page).to have_css 'alchemy-message[type="alert"]' + end + end + + context "with dismissable set to true" do + subject(:render) do + render_inline described_class.new(message, dismissable: true) + end + + it "renders an dismissable alchemy-message" do + expect(page).to have_css "alchemy-message[dismissable]" + end + end +end diff --git a/spec/features/admin/nodes_management_spec.rb b/spec/features/admin/nodes_management_spec.rb index 14ccb86a19..83c6d932a7 100644 --- a/spec/features/admin/nodes_management_spec.rb +++ b/spec/features/admin/nodes_management_spec.rb @@ -41,7 +41,7 @@ def add_menu_item open_page_properties click_button "Add a menu node" - within ".flash.error" do + within "alchemy-message[type='error']" do expect(page).to have_content("Menu Type can't be blank") end end diff --git a/spec/helpers/alchemy/base_helper_spec.rb b/spec/helpers/alchemy/base_helper_spec.rb index db9db94884..c08e751aa7 100644 --- a/spec/helpers/alchemy/base_helper_spec.rb +++ b/spec/helpers/alchemy/base_helper_spec.rb @@ -24,18 +24,44 @@ module Alchemy describe "#render_message" do context "if no argument is passed" do - it "should render a div with an info icon and the given content" do - expect(helper.render_message { content_tag(:p, "my notice") }).to match( - /
<\/alchemy-icon>

my notice/ - ) + it "should render an alchemy-message with an info icon and the given content" do + expect(helper.render_message { content_tag(:p, "my notice") }).to eq <<~HTML + +

my notice

+ + HTML end end context "if an argument is passed" do - it "should render the passed argument as the css classname for the icon container" do - expect(helper.render_message(:error) { content_tag(:p, "my notice") }).to match( - /
/ - ) + it "should render the passed argument as the type for the message" do + expect(helper.render_message(:error) { content_tag(:p, "my notice") }).to eq <<~HTML + +

my notice

+
+ HTML + end + end + end + + describe "#render_flash_notice" do + context "if no argument is passed" do + it "should render an alchemy-message with an check icon and the given content" do + expect(helper.render_flash_notice("my notice")).to eq <<~HTML + + my notice + + HTML + end + end + + context "if an argument is passed" do + it "should render the passed argument as the type for the message" do + expect(helper.render_flash_notice("A error", :error)).to eq <<~HTML + + A error + + HTML end end end @@ -63,43 +89,5 @@ module Alchemy end end end - - describe "#message_icon_class" do - subject { helper.message_icon_class(message_type) } - - context "when `warning`, `warn` or `alert` message type is given" do - %w[warning warn alert].each do |type| - let(:message_type) { type } - - it { is_expected.to eq "exclamation" } - end - end - - context "when `notice` message type is given" do - let(:message_type) { "notice" } - - it { is_expected.to eq "check" } - end - - context "when `hint` message type is given" do - let(:message_type) { "hint" } - - it { is_expected.to eq "info" } - end - - context "when `error` message type is given" do - let(:message_type) { "error" } - - it { is_expected.to eq "bug" } - end - - context "when unknown message type is given" do - let(:message_type) { "info" } - - it "returns the given message type as icon name" do - is_expected.to eq "info" - end - end - end end end diff --git a/spec/javascript/alchemy_admin/components/message.spec.js b/spec/javascript/alchemy_admin/components/message.spec.js new file mode 100644 index 0000000000..43caea0770 --- /dev/null +++ b/spec/javascript/alchemy_admin/components/message.spec.js @@ -0,0 +1,216 @@ +import "alchemy_admin/components/message" +import { renderComponent } from "./component.helper" + +describe("alchemy-message", () => { + describe("dismiss", () => { + describe("when dismissable", () => { + it("dismisses on click", () => { + const html = ` + + A message + + ` + const component = renderComponent("alchemy-message", html) + const spy = jest.spyOn(component, "dismiss") + component.dispatchEvent(new Event("click")) + expect(spy).toHaveBeenCalled() + }) + + it("dismisses after delay", () => { + return new Promise((resolve) => { + const html = ` + + A message + + ` + const component = renderComponent("alchemy-message", html) + const spy = jest.spyOn(component, "dismiss") + setTimeout(() => { + expect(spy).toHaveBeenCalled() + resolve() + }, 15) + }) + }, 100) + + it("when type error, does not dismis after delay", () => { + return new Promise((resolve) => { + const html = ` + + A message + + ` + const component = renderComponent("alchemy-message", html) + const spy = jest.spyOn(component, "dismiss") + setTimeout(() => { + expect(spy).not.toHaveBeenCalled() + resolve() + }, 15) + }) + }, 100) + }) + + describe("when type error", () => { + it("dismisses on click", () => { + const html = ` + + A message + + ` + const component = renderComponent("alchemy-message", html) + const spy = jest.spyOn(component, "dismiss") + component.dispatchEvent(new Event("click")) + expect(spy).toHaveBeenCalled() + }) + }) + + describe("when not type error nor dismissable", () => { + it("dismisses on click", () => { + const html = ` + + A message + + ` + const component = renderComponent("alchemy-message", html) + const spy = jest.spyOn(component, "dismiss") + component.dispatchEvent(new Event("click")) + expect(spy).not.toHaveBeenCalled() + }) + }) + }) + + describe("type", () => { + describe("when message type is given", () => { + it("is given type", () => { + const html = ` + + A warning message + + ` + const component = renderComponent("alchemy-message", html) + expect(component.type).toEqual("warning") + }) + }) + + describe("when message type is not given", () => { + it("is given type", () => { + const html = ` + + A warning message + + ` + const component = renderComponent("alchemy-message", html) + expect(component.type).toEqual("notice") + }) + }) + }) + + describe("dismissable", () => { + describe("when dismissable is set", () => { + it("is dismissable", () => { + const html = ` + + A dismissable message + + ` + const component = renderComponent("alchemy-message", html) + expect(component.dismissable).toBe(true) + }) + + it("and type error, it shows close icon", () => { + const html = ` + + A dismissable message + + ` + const component = renderComponent("alchemy-message", html) + expect( + component.querySelector("alchemy-icon[name='close']") + ).toBeDefined() + }) + }) + + describe("when dismissable is not set", () => { + it("is not dismissable", () => { + const html = ` + + A not dismissable message + + ` + const component = renderComponent("alchemy-message", html) + expect(component.dismissable).toBe(false) + }) + }) + }) + + describe("iconName", () => { + describe("when 'warning', 'warn' or 'alert' message type is given", () => { + ;["warning", "warn", "alert"].forEach((type) => { + it("is alert", () => { + const html = ` + + A ${type} message + + ` + const component = renderComponent("alchemy-message", html) + expect(component.iconName).toEqual("alert") + }) + }) + }) + + describe("when 'notice' message type is given", () => { + const html = ` + + A notice message + + ` + + it("is check", () => { + const component = renderComponent("alchemy-message", html) + + expect(component.iconName).toEqual("check") + }) + }) + + describe("when 'info' or 'hint' message type is given", () => { + ;["hint", "info"].forEach((type) => { + it("is alert", () => { + const html = ` + + A ${type} message + + ` + const component = renderComponent("alchemy-message", html) + expect(component.iconName).toEqual("information") + }) + }) + }) + + describe("when 'error' message type is given", () => { + const html = ` + + A error message + + ` + + it("is check", () => { + const component = renderComponent("alchemy-message", html) + + expect(component.iconName).toEqual("bug") + }) + }) + + describe("when unknown message type is given", () => { + const html = ` + + A foo message + + ` + + it("is the given message type as icon name", () => { + const component = renderComponent("alchemy-message", html) + + expect(component.iconName).toEqual("foo") + }) + }) + }) +}) diff --git a/spec/views/alchemy/admin/elements/element_view_spec.rb b/spec/views/alchemy/admin/elements/element_view_spec.rb index f08b637e8c..67112b97e5 100644 --- a/spec/views/alchemy/admin/elements/element_view_spec.rb +++ b/spec/views/alchemy/admin/elements/element_view_spec.rb @@ -23,7 +23,7 @@ let(:element) { create(:alchemy_element, name: "with_message") } it "renders the message" do - is_expected.to have_css('.message:contains("One nice message")') + is_expected.to have_css('alchemy-message:contains("One nice message")') end context "that contains HTML" do @@ -35,7 +35,7 @@ end it "renders the HTML message" do - is_expected.to have_css('.message h1:contains("One nice message")') + is_expected.to have_css('alchemy-message[type="info"] h1:contains("One nice message")') end end end @@ -51,7 +51,7 @@ end it "renders the warning" do - is_expected.to have_css('.warning:contains("One nice warning")') + is_expected.to have_css('alchemy-message[type="warning"]:contains("One nice warning")') end context "that contains HTML" do @@ -63,7 +63,7 @@ end it "renders the HTML warning" do - is_expected.to have_css('.warning h1:contains("One nice warning")') + is_expected.to have_css('alchemy-message[type="warning"] h1:contains("One nice warning")') end end end diff --git a/spec/views/alchemy/ingredients/select_editor_spec.rb b/spec/views/alchemy/ingredients/select_editor_spec.rb index cbd17173ed..548dcc3f89 100644 --- a/spec/views/alchemy/ingredients/select_editor_spec.rb +++ b/spec/views/alchemy/ingredients/select_editor_spec.rb @@ -30,7 +30,7 @@ end it "renders a warning" do - is_expected.to have_css(".warning") + is_expected.to have_css('alchemy-message[type="warning"]') end end