anchor_view_components 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +61 -0
  5. data/app/assets/builds/anchor-view-components.css +1 -0
  6. data/app/assets/stylesheets/anchor-view-components.tailwind.css +8 -0
  7. data/app/assets/stylesheets/components/button.css +29 -0
  8. data/app/assets/stylesheets/components/toast.css +21 -0
  9. data/app/components/anchor/action_menu_component.html.erb +19 -0
  10. data/app/components/anchor/action_menu_component.rb +23 -0
  11. data/app/components/anchor/action_menu_controller.ts +21 -0
  12. data/app/components/anchor/anchor_view_components.ts +13 -0
  13. data/app/components/anchor/autocomplete_component.en.yml +3 -0
  14. data/app/components/anchor/autocomplete_component.html.erb +36 -0
  15. data/app/components/anchor/autocomplete_component.rb +42 -0
  16. data/app/components/anchor/autocomplete_controller.ts +37 -0
  17. data/app/components/anchor/badge_component.html.erb +8 -0
  18. data/app/components/anchor/badge_component.rb +27 -0
  19. data/app/components/anchor/banner_component.html.erb +9 -0
  20. data/app/components/anchor/banner_component.rb +28 -0
  21. data/app/components/anchor/breadcrumbs/item_component.html.erb +16 -0
  22. data/app/components/anchor/breadcrumbs/item_component.rb +15 -0
  23. data/app/components/anchor/breadcrumbs_component.html.erb +10 -0
  24. data/app/components/anchor/breadcrumbs_component.rb +17 -0
  25. data/app/components/anchor/button_component.html.erb +22 -0
  26. data/app/components/anchor/button_component.rb +76 -0
  27. data/app/components/anchor/component.rb +21 -0
  28. data/app/components/anchor/dialog_component.html.erb +42 -0
  29. data/app/components/anchor/dialog_component.rb +25 -0
  30. data/app/components/anchor/dialog_controller.ts +15 -0
  31. data/app/components/anchor/icon_component.html.erb +6 -0
  32. data/app/components/anchor/icon_component.rb +9 -0
  33. data/app/components/anchor/loading_indicator_component.html.erb +6 -0
  34. data/app/components/anchor/loading_indicator_component.rb +23 -0
  35. data/app/components/anchor/panel/body_component.html.erb +8 -0
  36. data/app/components/anchor/panel/body_component.rb +6 -0
  37. data/app/components/anchor/panel/footer_component.html.erb +9 -0
  38. data/app/components/anchor/panel/footer_component.rb +17 -0
  39. data/app/components/anchor/panel/header_component.html.erb +9 -0
  40. data/app/components/anchor/panel/header_component.rb +15 -0
  41. data/app/components/anchor/panel_component.html.erb +8 -0
  42. data/app/components/anchor/panel_component.rb +20 -0
  43. data/app/components/anchor/prose_component.html.erb +3 -0
  44. data/app/components/anchor/prose_component.rb +9 -0
  45. data/app/components/anchor/tab_bar/tab_component.html.erb +11 -0
  46. data/app/components/anchor/tab_bar/tab_component.rb +12 -0
  47. data/app/components/anchor/tab_bar_component.html.erb +10 -0
  48. data/app/components/anchor/tab_bar_component.rb +17 -0
  49. data/app/components/anchor/text_component.html.erb +8 -0
  50. data/app/components/anchor/text_component.rb +43 -0
  51. data/app/components/anchor/toast_component.html.erb +28 -0
  52. data/app/components/anchor/toast_component.rb +32 -0
  53. data/app/components/anchor/toast_controller.ts +21 -0
  54. data/app/helpers/anchor/fetch_or_fallback_helper.rb +20 -0
  55. data/app/helpers/anchor/view_helper.rb +35 -0
  56. data/lib/anchor/view_components/engine.rb +23 -0
  57. data/lib/anchor/view_components/version.rb +5 -0
  58. data/lib/anchor/view_components.rb +8 -0
  59. data/previews/anchor/action_menu_component_preview.rb +22 -0
  60. data/previews/anchor/badge_component_preview.rb +40 -0
  61. data/previews/anchor/banner_component_preview.rb +41 -0
  62. data/previews/anchor/banner_preview/with_banner.html.erb +11 -0
  63. data/previews/anchor/breadcrumbs_component_preview.rb +15 -0
  64. data/previews/anchor/button_component_preview.rb +81 -0
  65. data/previews/anchor/dialog_component_preview.rb +19 -0
  66. data/previews/anchor/dialog_preview/with_footer.html.erb +16 -0
  67. data/previews/anchor/icon_component_preview.rb +7 -0
  68. data/previews/anchor/loading_indicator_component_preview.rb +26 -0
  69. data/previews/anchor/panel_component_preview.rb +48 -0
  70. data/previews/anchor/tab_bar_component_preview.rb +15 -0
  71. data/previews/anchor/table_preview/default.html.erb +35 -0
  72. data/previews/anchor/text_component_preview.rb +76 -0
  73. data/previews/anchor/toast_component_preview.rb +34 -0
  74. metadata +156 -0
@@ -0,0 +1,21 @@
1
+ module Anchor
2
+ class Component < ViewComponent::Base
3
+ include FetchOrFallbackHelper
4
+ include ViewHelper
5
+
6
+ def self.generate_id(base_name: name.demodulize.underscore.dasherize)
7
+ "#{base_name}-#{SecureRandom.uuid}"
8
+ end
9
+
10
+ def initialize(classes: nil, data: {}, **kwargs)
11
+ @classes = classes
12
+ @data = data
13
+
14
+ super
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :classes
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ <%= tag.div(
2
+ class: "contents",
3
+ data: { controller: "dialog" },
4
+ ) do %>
5
+ <%= show_button if show_button? %>
6
+
7
+ <%= tag.dialog(
8
+ aria: { labelledby: title_id },
9
+ class: "w-[36rem] rounded-lg p-0 bg-white shadow-lg backdrop:bg-grey-100/50",
10
+ data: { dialog_target: "dialog" },
11
+ ) do %>
12
+ <header class="p-6 flex gap-4 justify-between items-center">
13
+ <%= render Anchor::TextComponent.new(
14
+ variant: :heading_2xl,
15
+ tag: :h1,
16
+ id: title_id,
17
+ ).with_content(@title) %>
18
+
19
+ <form class="contents" method="dialog">
20
+ <%= tag.button(
21
+ aria: { label: "Close" },
22
+ class: "text-grey-50 hover:text-grey-70",
23
+ data: { action: "dialog#close" },
24
+ ) do %>
25
+ <%= render Anchor::IconComponent.new(icon: "cancel") %>
26
+ <% end %>
27
+ </form>
28
+ </header>
29
+
30
+ <div class="px-6 pb-6">
31
+ <%= render(Anchor::TextComponent.new(
32
+ variant: :body_base,
33
+ ).with_content(body)) %>
34
+ </div>
35
+
36
+ <% if footer? %>
37
+ <footer class="sticky bottom-0 bg-white px-6 pb-6 flex gap-2 justify-end">
38
+ <%= footer %>
39
+ </footer>
40
+ <% end %>
41
+ <% end %>
42
+ <% end %>
@@ -0,0 +1,25 @@
1
+ module Anchor
2
+ class DialogComponent < Component
3
+ renders_one :show_button, -> do
4
+ ButtonComponent.new(data: { action: "dialog#showModal" })
5
+ end
6
+ renders_one :body
7
+ renders_one :footer
8
+
9
+ def initialize(title:)
10
+ @title = title
11
+
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def render?
18
+ body?
19
+ end
20
+
21
+ def title_id
22
+ @title.parameterize
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller<HTMLDivElement> {
4
+ static targets = ["dialog"];
5
+
6
+ declare readonly dialogTarget: HTMLDialogElement;
7
+
8
+ showModal(): void {
9
+ this.dialogTarget.showModal();
10
+ }
11
+
12
+ close(): void {
13
+ this.dialogTarget.close();
14
+ }
15
+ }
@@ -0,0 +1,6 @@
1
+ <%= inline_svg_tag(
2
+ "icons/#{@icon}.svg",
3
+ aria_hidden: true,
4
+ class: @classes,
5
+ data: @data,
6
+ ) %>
@@ -0,0 +1,9 @@
1
+ module Anchor
2
+ class IconComponent < Component
3
+ def initialize(icon:, **kwargs)
4
+ @icon = icon
5
+
6
+ super
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ <%= tag.div(
2
+ class: class_names(
3
+ "inline-block animate-spin border-grey-100 border-b-transparent rounded-full",
4
+ variant
5
+ )
6
+ ) %>
@@ -0,0 +1,23 @@
1
+ module Anchor
2
+ class LoadingIndicatorComponent < Component
3
+ VARIANT_MAPPING = {
4
+ sm: "w-4 h-4 border-[1.5px]",
5
+ md: "w-6 h-6 border-2",
6
+ lg: "w-12 h-12 border-4",
7
+ }.freeze
8
+ VARIANT_DEFAULT = :md
9
+ VARIANT_OPTIONS = VARIANT_MAPPING.keys
10
+
11
+ def initialize(variant: VARIANT_DEFAULT, **kwargs)
12
+ @variant = VARIANT_MAPPING[
13
+ fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
14
+ ]
15
+
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :variant
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ <%= tag.div(
2
+ class: class_names(
3
+ "p-6",
4
+ classes
5
+ )
6
+ ) do %>
7
+ <%= content %>
8
+ <% end %>
@@ -0,0 +1,6 @@
1
+ module Anchor
2
+ module Panel
3
+ class BodyComponent < Component
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ <div class="border-t border-subdued flex p-6 items-center">
2
+ <% if supporting_text? %>
3
+ <span class="text-base text-primary font-semibold"><%= supporting_text %></span>
4
+ <% end %>
5
+
6
+ <div class="ml-auto">
7
+ <%= primary_action %>
8
+ </div>
9
+ </div>
@@ -0,0 +1,17 @@
1
+ module Anchor
2
+ module Panel
3
+ class FooterComponent < Component
4
+ renders_one :supporting_text
5
+
6
+ renders_one :primary_action, ->(form:, label:, **kwargs) do
7
+ ButtonComponent.new(
8
+ form:,
9
+ variant: :primary,
10
+ size: :small,
11
+ type: :submit,
12
+ **kwargs
13
+ ).with_content(label)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ <%= tag.div(
2
+ class: class_names(
3
+ "border-subdued px-6 py-4",
4
+ "border-b" => active?
5
+ )
6
+ ) do %>
7
+ <%= render Anchor::TextComponent.new(variant: :subheading_xs)
8
+ .with_content(content) %>
9
+ <% end %>
@@ -0,0 +1,15 @@
1
+ module Anchor
2
+ module Panel
3
+ class HeaderComponent < Component
4
+ def initialize(active:)
5
+ @active = active
6
+
7
+ super
8
+ end
9
+
10
+ def active?
11
+ @active == true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ <div class="border border-subdued">
2
+ <%= header %>
3
+
4
+ <% if active? %>
5
+ <%= body %>
6
+ <%= footer %>
7
+ <% end %>
8
+ </div>
@@ -0,0 +1,20 @@
1
+ module Anchor
2
+ class PanelComponent < Component
3
+ renders_one :header, -> do
4
+ Panel::HeaderComponent.new(active: active?)
5
+ end
6
+
7
+ renders_one :body, Panel::BodyComponent
8
+ renders_one :footer, Panel::FooterComponent
9
+
10
+ def initialize(active: false)
11
+ @active = active
12
+
13
+ super
14
+ end
15
+
16
+ def active?
17
+ @active == true
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ <div class="prose">
2
+ <%= content %>
3
+ </div>
@@ -0,0 +1,9 @@
1
+ module Anchor
2
+ class ProseComponent < Component
3
+ private
4
+
5
+ def render?
6
+ content.present?
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ <%= link_to(
2
+ content,
3
+ @href,
4
+ aria: {
5
+ current: { page: @active }
6
+ },
7
+ class: class_names(
8
+ "inline-block px-5 py-4 text-sm font-semibold relative text-gray-600 hover:text-gray-900",
9
+ "text-gray-900 after:block after:bg-blue-500 after:h-1 after:absolute after:-bottom-1 after:inset-x-0" => @active,
10
+ )
11
+ ) %>
@@ -0,0 +1,12 @@
1
+ module Anchor
2
+ module TabBar
3
+ class TabComponent < Component
4
+ def initialize(href:, active: false)
5
+ @href = href
6
+ @active = active
7
+
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ <%= tag.nav(
2
+ aria: { label: @label },
3
+ class: "shadow-[inset_0_-1px] shadow-gray-400 pb-1",
4
+ ) do %>
5
+ <ul class="flex">
6
+ <% tabs.each do |tab| %>
7
+ <%= tag.li tab %>
8
+ <% end %>
9
+ </ul>
10
+ <% end %>
@@ -0,0 +1,17 @@
1
+ module Anchor
2
+ class TabBarComponent < Component
3
+ renders_many :tabs, TabBar::TabComponent
4
+
5
+ def initialize(label:)
6
+ @label = label
7
+
8
+ super
9
+ end
10
+
11
+ private
12
+
13
+ def render?
14
+ tabs.present?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ <%= content_tag(
2
+ @tag,
3
+ class: class_names(@variant, @classes),
4
+ id: @id,
5
+ data: @data
6
+ ) do %>
7
+ <%= content %>
8
+ <% end %>
@@ -0,0 +1,43 @@
1
+ module Anchor
2
+ class TextComponent < Component
3
+ TAG_DEFAULT = :div
4
+
5
+ VARIANT_DEFAULT = :body_base
6
+ VARIANT_MAPPING = {
7
+ body_lg: "text-lg",
8
+ body_base: "text-base",
9
+ body_sm: "text-sm",
10
+ heading_base: "text-base font-semibold",
11
+ heading_lg: "text-lg font-semibold",
12
+ heading_xl: "text-xl font-semibold",
13
+ heading_2xl: "text-2xl font-semibold",
14
+ heading_3xl: "text-3xl font-semibold",
15
+ heading_4xl: "text-4xl font-semibold",
16
+ subheading_sm: "text-sm font-bold leading-4 uppercase",
17
+ subheading_xs: "text-xs font-bold leading-4 uppercase",
18
+ }.freeze
19
+ VARIANT_OPTIONS = VARIANT_MAPPING.keys
20
+
21
+ def initialize(
22
+ variant: VARIANT_DEFAULT,
23
+ tag: TAG_DEFAULT,
24
+ id: nil,
25
+ **kwargs
26
+ )
27
+ @variant = VARIANT_MAPPING[
28
+ fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
29
+ ]
30
+
31
+ @tag = tag
32
+ @id = id
33
+
34
+ super
35
+ end
36
+
37
+ private
38
+
39
+ def render?
40
+ content.present?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ <%= tag.div(
2
+ class: class_names(
3
+ "toast",
4
+ @variant,
5
+ ),
6
+ data: { controller: "toast" },
7
+ id: @id,
8
+ popover: "manual",
9
+ ) do %>
10
+ <% if icon? %>
11
+ <%= icon %>
12
+ <% end %>
13
+
14
+ <%= render Anchor::TextComponent.new(
15
+ variant: :heading_base,
16
+ tag: :span,
17
+ ).with_content(content) %>
18
+
19
+ <%= tag.button(
20
+ aria: { label: "Close" },
21
+ class: "!ml-10",
22
+ type: "button",
23
+ popovertarget: @id,
24
+ popovertargetaction: "hide",
25
+ ) do %>
26
+ <%= render Anchor::IconComponent.new(icon: "cancel") %>
27
+ <% end %>
28
+ <% end %>
@@ -0,0 +1,32 @@
1
+ module Anchor
2
+ class ToastComponent < Component
3
+ renders_one :icon, ->(icon:) do
4
+ Anchor::IconComponent.new(icon:, classes: "opacity-70")
5
+ end
6
+
7
+ VARIANT_DEFAULT = :notice
8
+ VARIANT_MAPPINGS = {
9
+ VARIANT_DEFAULT => "bg-gray-900 text-white",
10
+ :success => "bg-green-700 text-white",
11
+ :error => "bg-red-700 text-white",
12
+ # TODO: Figma doesn’t provide a yellow ‘alert’ style
13
+ :alert => "bg-yellow-600 text-white",
14
+ }.freeze
15
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
16
+
17
+ def initialize(variant: VARIANT_DEFAULT)
18
+ @variant = VARIANT_MAPPINGS[
19
+ fetch_or_fallback(VARIANT_OPTIONS, variant, VARIANT_DEFAULT)
20
+ ]
21
+ @id = self.class.generate_id
22
+
23
+ super
24
+ end
25
+
26
+ private
27
+
28
+ def render?
29
+ content.present?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller<HTMLDivElement> {
4
+ static values = {
5
+ hideDelay: { type: Number, default: 3000 },
6
+ showDelay: { type: Number, default: 200 },
7
+ };
8
+
9
+ declare hideDelayValue: number
10
+ declare showDelayValue: number
11
+
12
+ initialize = (): void => {
13
+ setTimeout(() => {
14
+ this.element.showPopover();
15
+ }, this.showDelayValue);
16
+
17
+ setTimeout(() => {
18
+ this.element.hidePopover();
19
+ }, this.hideDelayValue);
20
+ }
21
+ }
@@ -0,0 +1,20 @@
1
+ module Anchor
2
+ module FetchOrFallbackHelper
3
+ def fetch_or_fallback(allowed_values, given_value, fallback = nil)
4
+ if allowed_values.include?(given_value)
5
+ given_value
6
+ else
7
+ unless Rails.env.production?
8
+ raise ArgumentError, <<~MSG
9
+ fetch_or_fallback was called with an invalid value.
10
+ Expected one of: #{allowed_values.inspect}
11
+ Got: #{given_value.inspect}
12
+ This will not raise in production, but will instead fallback to: #{fallback.inspect}
13
+ MSG
14
+ end
15
+
16
+ fallback
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ module Anchor
2
+ module ViewHelper
3
+ Anchor::TextComponent::VARIANT_OPTIONS.each do |variant_option|
4
+ define_method "text_#{variant_option}" do
5
+ |content = nil, *args, **kwargs, &block|
6
+
7
+ if block.present?
8
+ render(
9
+ Anchor::TextComponent.new(
10
+ *args,
11
+ variant: variant_option,
12
+ **kwargs
13
+ ),
14
+ &block
15
+ )
16
+ else
17
+ render Anchor::TextComponent.new(
18
+ *args,
19
+ variant: variant_option,
20
+ **kwargs
21
+ ).with_content(content)
22
+ end
23
+ end
24
+ end
25
+
26
+ def text_prose(content = nil, *args, **kwargs, &block)
27
+ if block.present?
28
+ render(Anchor::ProseComponent.new(*args, **kwargs), &block)
29
+ else
30
+ render Anchor::ProseComponent.new(*args, **kwargs)
31
+ .with_content(content)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ require "rails/engine"
2
+ require "inline_svg"
3
+
4
+ module Anchor
5
+ module ViewComponents
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace Anchor::ViewComponents
8
+
9
+ config.autoload_paths = %W[
10
+ #{root}/app/components
11
+ #{root}/app/helpers
12
+ ]
13
+
14
+ initializer "anchor_view_components.assets" do |app|
15
+ if app.config.respond_to?(:assets)
16
+ app.config.assets.precompile += %w[
17
+ anchor-view-components.css
18
+ ]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ module Anchor
2
+ module ViewComponents
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ require "anchor/view_components/version"
2
+ require "anchor/view_components/engine"
3
+
4
+ module Anchor
5
+ module ViewComponents
6
+ class Error < StandardError; end
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ module Anchor
2
+ class ActionMenuComponentPreview < ViewComponent::Preview
3
+ include ActionView::Helpers::UrlHelper
4
+
5
+ def playground
6
+ render Anchor::ActionMenuComponent.new do |c|
7
+ c.with_show_button do |button|
8
+ button.with_ending_icon(icon: "nav-arrow-down")
9
+ "Actions"
10
+ end
11
+
12
+ c.with_item do
13
+ link_to("Edit", "#")
14
+ end
15
+
16
+ c.with_item do
17
+ link_to("Delete", "#")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ module Anchor
2
+ class BadgeComponentPreview < ViewComponent::Preview
3
+ # @param content text "Labels should be one to two words"
4
+ # @param variant select {{ Anchor::BadgeComponent::VARIANT_OPTIONS }}
5
+ def playground(
6
+ content: "Label",
7
+ variant: Anchor::BadgeComponent::VARIANT_DEFAULT
8
+ )
9
+ render(Anchor::BadgeComponent.new(variant:).with_content(content))
10
+ end
11
+
12
+ # @!group Variants
13
+
14
+ def neutral
15
+ render(Anchor::BadgeComponent.new.with_content("Label"))
16
+ end
17
+
18
+ def informational
19
+ render(Anchor::BadgeComponent.new(variant: :informational)
20
+ .with_content("Label"))
21
+ end
22
+
23
+ def success
24
+ render(Anchor::BadgeComponent.new(variant: :success)
25
+ .with_content("Label"))
26
+ end
27
+
28
+ def critical
29
+ render(Anchor::BadgeComponent.new(variant: :critical)
30
+ .with_content("Label"))
31
+ end
32
+
33
+ def warning
34
+ render(Anchor::BadgeComponent.new(variant: :warning)
35
+ .with_content("Label"))
36
+ end
37
+
38
+ # @!endgroup
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Anchor
2
+ class BannerComponentPreview < ViewComponent::Preview
3
+ # @param content text
4
+ # @param variant select {{ Anchor::BannerComponent::VARIANT_OPTIONS }}
5
+ def playground(
6
+ content: "Content",
7
+ variant: Anchor::BannerComponent::VARIANT_DEFAULT
8
+ )
9
+ render(Anchor::BannerComponent.new(variant:)
10
+ .with_content(content))
11
+ end
12
+
13
+ # @!group Variants
14
+
15
+ def neutral
16
+ render(Anchor::BannerComponent.new.with_content("Content"))
17
+ end
18
+
19
+ def informational
20
+ render(Anchor::BannerComponent.new(variant: :informational)
21
+ .with_content("Content"))
22
+ end
23
+
24
+ def success
25
+ render(Anchor::BannerComponent.new(variant: :success)
26
+ .with_content("Content"))
27
+ end
28
+
29
+ def critical
30
+ render(Anchor::BannerComponent.new(variant: :critical)
31
+ .with_content("Content"))
32
+ end
33
+
34
+ def warning
35
+ render(Anchor::BannerComponent.new(variant: :warning)
36
+ .with_content("Content"))
37
+ end
38
+
39
+ # @!endgroup
40
+ end
41
+ end