backpack 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +2 -2
  3. data/README.md +11 -1
  4. data/demo/.yarnrc.yml +4 -0
  5. data/demo/Gemfile.lock +2 -2
  6. data/demo/app/views/layouts/backpack.html.erb +2 -4
  7. data/demo/config/initializers/phlex.rb +9 -0
  8. data/demo/yarn.lock +1101 -1101
  9. data/lib/backpack/{components/concerns/classes.rb → classes.rb} +1 -1
  10. data/lib/backpack/cli.rb +39 -0
  11. data/lib/backpack/components/badge.rb +1 -1
  12. data/lib/backpack/components/base.rb +1 -1
  13. data/lib/backpack/components/breadcrumb.rb +2 -2
  14. data/lib/backpack/components/button.rb +1 -1
  15. data/lib/backpack/components/favicon.rb +1 -1
  16. data/lib/backpack/components/heading.rb +1 -1
  17. data/lib/backpack/components/icon.rb +1 -1
  18. data/lib/backpack/components/icon_button/icon_button.css +183 -0
  19. data/lib/backpack/components/icon_button/icon_button.js +1 -0
  20. data/lib/backpack/components/icon_button.rb +32 -0
  21. data/lib/backpack/components/icon_sprite.rb +1 -1
  22. data/lib/backpack/components/quotation.rb +1 -1
  23. data/lib/backpack/components/rich_text.rb +1 -1
  24. data/lib/backpack/components/skip_links.rb +1 -1
  25. data/lib/backpack/components.rb +2 -4
  26. data/lib/backpack/{components/concerns/identifier.rb → identifier.rb} +5 -1
  27. data/lib/backpack/version.rb +1 -1
  28. data/lib/backpack.rb +7 -4
  29. data/spec/cli/import_spec.rb +51 -0
  30. data/spec/components/badge_spec.rb +3 -3
  31. data/spec/components/breadcrumb_spec.rb +10 -4
  32. data/spec/components/button_spec.rb +7 -7
  33. data/spec/components/favicon_spec.rb +5 -3
  34. data/spec/components/heading_spec.rb +3 -3
  35. data/spec/components/icon_button_spec.rb +58 -0
  36. data/spec/components/icon_spec.rb +3 -3
  37. data/spec/components/icon_sprite_spec.rb +3 -3
  38. data/spec/components/previews/badge_preview/overview.html.erb +2 -2
  39. data/spec/components/previews/badge_preview.rb +3 -1
  40. data/spec/components/previews/breadcrumb_preview.rb +3 -1
  41. data/spec/components/previews/button_preview/overview.html.erb +3 -3
  42. data/spec/components/previews/button_preview.rb +2 -1
  43. data/spec/components/previews/heading_preview.rb +3 -1
  44. data/spec/components/previews/icon_button_preview/overview.html.erb +48 -0
  45. data/spec/components/previews/icon_button_preview.rb +14 -0
  46. data/spec/components/previews/icon_preview.rb +3 -1
  47. data/spec/components/previews/quotation_preview/overview.html.erb +2 -2
  48. data/spec/components/previews/quotation_preview.rb +3 -1
  49. data/spec/components/previews/rich_text_preview.rb +5 -1
  50. data/spec/components/previews/skip_links_preview.rb +3 -1
  51. data/spec/components/quotation_spec.rb +7 -5
  52. data/spec/components/rich_text_spec.rb +3 -3
  53. data/spec/components/skip_links_spec.rb +10 -5
  54. data/spec/spec_helper.rb +2 -1
  55. data/spec/support/cli_helpers.rb +5 -0
  56. data/spec/support/components.rb +8 -0
  57. data/{lib/backpack → spec/support}/spec_helpers.rb +1 -1
  58. metadata +70 -12
  59. data/spec/components/previews/backpack/preview.rb +0 -3
  60. /data/lib/backpack/{components/concerns/attributes.rb → attributes.rb} +0 -0
@@ -7,7 +7,7 @@ module Backpack::Classes
7
7
 
8
8
  class_methods do
9
9
  def root_class
10
- to_s.delete_prefix("Backpack::Components::").gsub(/::/, "/")
10
+ identifier
11
11
  end
12
12
  end
13
13
 
@@ -0,0 +1,39 @@
1
+ require "thor"
2
+ require "backpack"
3
+
4
+ module Backpack
5
+ class CLI < Thor
6
+ include Thor::Actions
7
+
8
+ desc "import", "Import Backpack in the current project"
9
+
10
+ source_root File.expand_path(__dir__)
11
+
12
+ def import
13
+ say ascii_art, :green
14
+
15
+ directory "stylesheets", "app/assets/stylesheets", recursive: true
16
+ directory "components", "app/components", recursive: true
17
+
18
+ copy_file "attributes.rb", "lib/backpack/attributes.rb"
19
+ copy_file "classes.rb", "lib/backpack/classes.rb"
20
+ copy_file "identifier.rb", "lib/backpack/identifier.rb"
21
+ copy_file "tokens.rb", "lib/backpack/tokens.rb"
22
+
23
+ directory "../../spec/components", "spec/components", recursive: true do |content|
24
+ content.gsub("require 'spec_helper'", "require 'rails_helper'")
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def ascii_art
31
+ <<~ASCII
32
+ ┌┐ ┌─┐┌─┐┬┌─┌─┐┌─┐┌─┐┬┌─
33
+ ├┴┐├─┤│ ├┴┐├─┘├─┤│ ├┴┐
34
+ └─┘┴ ┴└─┘┴ ┴┴ ┴ ┴└─┘┴ ┴
35
+
36
+ ASCII
37
+ end
38
+ end
39
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Badge < Backpack::Components::Base
3
+ class Components::Badge < Components::Base
4
4
  prop :variant, _Union(:solid, :soft, :surface, :outline), default: :solid
5
5
  prop :color, Backpack::Tokens::OptionalColor
6
6
  prop :size, Backpack::Tokens::Size, default: 2
@@ -1,4 +1,4 @@
1
- class Backpack::Components::Base < Phlex::HTML
1
+ class Components::Base < Phlex::HTML
2
2
  # Component concerns (order of includes in important)
3
3
  include Backpack::Identifier
4
4
  include Backpack::Classes
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Breadcrumb < Backpack::Components::Base
4
- class Item < Backpack::Components::Base
3
+ class Components::Breadcrumb < Components::Base
4
+ class Item < Components::Base
5
5
  prop :href, HTMLAttribute, default: ""
6
6
  prop :breadcrumb, Breadcrumb
7
7
  prop :last, _Boolean, default: false
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Button < Backpack::Components::Base
3
+ class Components::Button < Components::Base
4
4
  Radius = _Union(:none, :small, :full)
5
5
 
6
6
  prop :href, HTMLAttribute, predicate: :private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Favicon < Backpack::Components::Base
3
+ class Components::Favicon < Components::Base
4
4
  prop :application_name, _String?
5
5
 
6
6
  def view_template
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Heading < Backpack::Components::Base
3
+ class Components::Heading < Components::Base
4
4
  prop :level, _Union(1, 2, 3, 4, 5), default: 1
5
5
  prop :tag, _Union(:h1, :h2, :h3, :h4, :h5, :h6), default: -> { :"h#{@level}" }
6
6
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Icon < Backpack::Components::Base
3
+ class Components::Icon < Components::Base
4
4
  prop :key, _Union(Symbol, String), :positional do |value|
5
5
  value.to_s.parameterize
6
6
  end
@@ -0,0 +1,183 @@
1
+ .root {
2
+ appearance: none;
3
+ border: none;
4
+ border-radius: var(--radius-full);
5
+ cursor: pointer;
6
+ display: inline-flex;
7
+ justify-content: center;
8
+ text-decoration: none;
9
+ transition:
10
+ background-color 0.2s ease-in-out,
11
+ color 0.2s ease-in-out;
12
+ }
13
+
14
+ /* solid */
15
+ .variant-solid {
16
+ background-color: var(--accent-9);
17
+ color: var(--accent-1);
18
+ }
19
+
20
+ @media (hover: hover) {
21
+ .variant-solid:hover {
22
+ background-color: var(--accent-10);
23
+ }
24
+ }
25
+
26
+ .variant-solid:active {
27
+ background-color: var(--accent-12);
28
+ }
29
+
30
+ .variant-solid:focus {
31
+ outline: 0.3rem solid var(--accent-8);
32
+ }
33
+
34
+ .variant-solid:disabled {
35
+ background-color: var(--slate-5);
36
+ border: none;
37
+ color: var(--slate-9);
38
+ }
39
+
40
+ /* soft */
41
+ .variant-soft {
42
+ background-color: var(--accent-5);
43
+ color: var(--accent-9);
44
+ }
45
+
46
+ @media (hover: hover) {
47
+ .variant-soft:hover {
48
+ background-color: var(--accent-6);
49
+ }
50
+ }
51
+
52
+ .variant-soft:active {
53
+ background-color: var(--accent-7);
54
+ }
55
+
56
+ .variant-soft:focus {
57
+ background-color: var(--accent-5);
58
+ outline: 0.2rem solid var(--accent-9);
59
+ }
60
+
61
+ .variant-soft:disabled {
62
+ background-color: var(--slate-4);
63
+ border: none;
64
+ color: var(--slate-9);
65
+ }
66
+
67
+ /* surface */
68
+ .variant-surface {
69
+ background-color: var(--accent-3);
70
+ box-shadow: inset 0 0 0 1px var(--accent-9);
71
+ color: var(--accent-9);
72
+ }
73
+
74
+ @media (hover: hover) {
75
+ .variant-surface:hover {
76
+ background-color: var(--accent-4);
77
+ }
78
+ }
79
+
80
+ .variant-surface:active {
81
+ background-color: var(--accent-5);
82
+ }
83
+
84
+ .variant-surface:focus {
85
+ outline: 0.2rem solid var(--accent-9);
86
+ }
87
+
88
+ .variant-surface:disabled {
89
+ background-color: var(--slate-3);
90
+ box-shadow: inset 0 0 0 1px var(--slate-8);
91
+ color: var(--slate-9);
92
+ }
93
+
94
+ /* outline */
95
+ .variant-outline {
96
+ background-color: transparent;
97
+ box-shadow: inset 0 0 0 1px var(--accent-9);
98
+ color: var(--accent-9);
99
+ }
100
+
101
+ @media (hover: hover) {
102
+ .variant-outline:hover {
103
+ background-color: var(--accent-3);
104
+ }
105
+ }
106
+
107
+ .variant-outline:active {
108
+ background-color: var(--accent-4);
109
+ }
110
+
111
+ .variant-outline:focus {
112
+ outline: 0.2rem solid var(--accent-9);
113
+ }
114
+
115
+ .variant-outline:disabled {
116
+ background-color: transparent;
117
+ box-shadow: inset 0 0 0 1px var(--slate-8);
118
+ color: var(--slate-8);
119
+ }
120
+
121
+ /* ghost */
122
+ .variant-ghost {
123
+ color: var(--accent-9);
124
+ background-color: transparent;
125
+ }
126
+
127
+ @media (hover: hover) {
128
+ .variant-ghost:hover {
129
+ background-color: var(--accent-4);
130
+ }
131
+ }
132
+
133
+ .variant-ghost:active {
134
+ background-color: var(--accent-5);
135
+ }
136
+
137
+ .variant-ghost:focus {
138
+ outline: 0.2rem solid var(--accent-9);
139
+ }
140
+
141
+ .variant-ghost:disabled {
142
+ background-color: transparent;
143
+ color: var(--slate-8);
144
+ }
145
+
146
+ /* size */
147
+ .size-1 {
148
+ --icon-size: 1.8rem;
149
+ padding: var(--space-4);
150
+ }
151
+
152
+ .size-2 {
153
+ --icon-size: 2rem;
154
+ padding: var(--space-5);
155
+ }
156
+
157
+ .size-3 {
158
+ --icon-size: 2.4rem;
159
+ padding: var(--space-5);
160
+ }
161
+
162
+ /* States */
163
+ .root:disabled {
164
+ cursor: not-allowed;
165
+ }
166
+
167
+ @media (max-width: 576px) {
168
+ .root {
169
+ line-height: 1;
170
+ }
171
+
172
+ .size-1 {
173
+ --icon-size: 1.6rem;
174
+ }
175
+
176
+ .size-2 {
177
+ --icon-size: 1.8rem;
178
+ }
179
+
180
+ .size-3 {
181
+ --icon-size: 2rem;
182
+ }
183
+ }
@@ -0,0 +1 @@
1
+ import "./icon_button.css";
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Components::IconButton < Components::Base
4
+ prop :href, HTMLAttribute, predicate: :private
5
+ prop :label, MandatoryHTMLAttribute
6
+ prop :icon, _Union(Symbol, String)
7
+ prop :variant, Backpack::Tokens::Variant, default: :solid
8
+ prop :color, Backpack::Tokens::OptionalColor
9
+ prop :size, Backpack::Tokens::Size, default: 2
10
+
11
+ def view_template
12
+ if href?
13
+ a(href: @href, **root_attributes) { render_icon }
14
+ else
15
+ button(**root_attributes) { render_icon }
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def render_icon
22
+ Icon(@icon, class: "#{root_class}-icon", label: @label)
23
+ end
24
+
25
+ def variant_props
26
+ %i[variant size]
27
+ end
28
+
29
+ def extra_root_attributes
30
+ { data_accent_color: @color }
31
+ end
32
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'rails-html-sanitizer'
4
4
 
5
- class Backpack::Components::IconSprite < Backpack::Components::Base
5
+ class Components::IconSprite < Components::Base
6
6
  ICON_PATHS = [
7
7
  Rails.root&.join("app/assets/icons"),
8
8
  Rails.root&.join("node_modules/lucide-static/icons"),
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::Quotation < Backpack::Components::Base
3
+ class Components::Quotation < Components::Base
4
4
  prop :cite, _String?, predicate: :private
5
5
  prop :size, _Union(1, 2, 3, 4, 5, 6), default: 2
6
6
  prop :weight, Backpack::Tokens::Weight, default: :regular
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::RichText < Backpack::Components::Base
3
+ class Components::RichText < Components::Base
4
4
  prop :tag, _Union(:article, :aside, :div, :main, :section), default: :div
5
5
 
6
6
  def view_template(&block)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Backpack::Components::SkipLinks < Backpack::Components::Base
3
+ class Components::SkipLinks < Components::Base
4
4
  LINKS = {
5
5
  content: "Aller au contenu",
6
6
  navigation: "Aller à la navigation"
@@ -1,5 +1,3 @@
1
- module Backpack
2
- module Components
3
- extend Phlex::Kit
4
- end
1
+ module Components
2
+ extend Phlex::Kit
5
3
  end
@@ -5,9 +5,13 @@ module Backpack::Identifier
5
5
 
6
6
  class_methods do
7
7
  def identifier
8
- self.to_s.delete_prefix("Backpack::Components::").gsub(/::/, "--").parameterize
8
+ (name.split("::") - %w[Components Backpack]).join("/")
9
9
  end
10
10
  end
11
11
 
12
+ def identifier
13
+ self.class.identifier.parameterize
14
+ end
15
+
12
16
  delegate :identifier, to: :class
13
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Backpack
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.2"
5
5
  end
data/lib/backpack.rb CHANGED
@@ -6,11 +6,14 @@ require 'date'
6
6
  require 'literal'
7
7
  require 'phlex-rails'
8
8
  require 'rails'
9
- require 'zeitwerk'
10
9
 
11
- loader = Zeitwerk::Loader.for_gem
12
- loader.push_dir("#{__dir__}/backpack/components/concerns", namespace: Backpack)
13
- loader.setup
10
+ require 'backpack/cli'
11
+
12
+ require 'backpack/attributes'
13
+ require 'backpack/classes'
14
+ require 'backpack/identifier'
15
+ require 'backpack/tokens'
16
+ require 'backpack/components'
14
17
 
15
18
  module Backpack
16
19
  class Error < StandardError; end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Backpack::CLI, type: :cli do
4
+ sandbox_dir = "sandbox"
5
+
6
+ around do |example|
7
+ FileUtils.mkdir(sandbox_dir) unless File.exist?(sandbox_dir)
8
+ within_dir(sandbox_dir) do
9
+ example.run
10
+ end
11
+ ensure
12
+ FileUtils.rm_rf(sandbox_dir)
13
+ end
14
+
15
+ describe "import" do
16
+ let(:command) { `ruby ../bin/backpack import` }
17
+ let(:output) { command.chomp }
18
+
19
+ it "outputs ascii art and the list of created files" do
20
+ expect(output).to include(described_class.new.send(:ascii_art))
21
+ expect(output).to include("create app/assets/stylesheets/application.css")
22
+ expect(output).to include("create app/components/base.rb")
23
+ expect(output).to include("create app/components/button.rb")
24
+ expect(output).to include("create app/components/button/button.js")
25
+ expect(output).to include("create app/components/button/button.css")
26
+ expect(output).to include("create lib/backpack/attributes.rb")
27
+ expect(output).to include("create lib/backpack/classes.rb")
28
+ expect(output).to include("create lib/backpack/identifier.rb")
29
+ expect(output).to include("create lib/backpack/tokens.rb")
30
+ expect(output).to include("create spec/components/button_spec.rb")
31
+ expect(output).to include("create spec/components/previews/button_preview.rb")
32
+ expect(output).to include("create spec/components/previews/button_preview/overview.html.erb")
33
+ end
34
+
35
+ it "copies the folders" do
36
+ command
37
+ expect(File.directory?("app/assets/stylesheets")).to be true
38
+ expect(File.directory?("app/components")).to be true
39
+ expect(File.directory?("spec/components")).to be true
40
+ expect(File.directory?("spec/components/previews")).to be true
41
+ end
42
+
43
+ it "copies the lib files" do
44
+ command
45
+ expect(File.exist?("lib/backpack/attributes.rb")).to be true
46
+ expect(File.exist?("lib/backpack/classes.rb")).to be true
47
+ expect(File.exist?("lib/backpack/identifier.rb")).to be true
48
+ expect(File.exist?("lib/backpack/tokens.rb")).to be true
49
+ end
50
+ end
51
+ end
@@ -1,12 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Backpack::Components::Badge, type: :component do
3
+ describe Components::Badge, type: :component do
4
+ subject(:output) { render_fragment(component) }
5
+
4
6
  let(:component) { described_class.new(**params) { text } }
5
7
  let(:params) { {} }
6
8
  let(:text) { "Badge" }
7
9
 
8
- subject(:output) { render_fragment(component) }
9
-
10
10
  it "renders the element with the default styling" do
11
11
  expect(output).to have_css(
12
12
  ".Badge.Badge-variant-solid", text:
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Backpack::Components::Breadcrumb, type: :component do
3
+ describe Components::Breadcrumb, type: :component do
4
+ subject(:output) { render_fragment(component) }
5
+
4
6
  let(:component) { described_class.new(**params) }
5
7
  let(:params) { {} }
6
8
 
7
- subject(:output) { render_fragment(component) }
8
-
9
9
  context "without items" do
10
10
  it "renders nothing" do
11
11
  expect(output).to eq_html('')
@@ -25,9 +25,15 @@ describe Backpack::Components::Breadcrumb, type: :component do
25
25
  expect(output).to have_css("nav.Breadcrumb[role='navigation'][aria-label='Breadcrumb']")
26
26
  end
27
27
 
28
- it "renders all items" do
28
+ it "renders first item" do
29
29
  expect(output).to have_css("li:nth-child(1) a[href='/']", text: "Home")
30
+ end
31
+
32
+ it "renders second item" do
30
33
  expect(output).to have_css("li:nth-child(2) a[href='/sections']", text: "Sections")
34
+ end
35
+
36
+ it "renders third item" do
31
37
  expect(output).to have_css("li:nth-child(3) a[href='']", text: "Articles")
32
38
  end
33
39
 
@@ -1,20 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Backpack::Components::Button, type: :component do
3
+ describe Components::Button, type: :component do
4
+ subject(:output) { render_fragment(component) }
5
+
4
6
  let(:component) { described_class.new(**params) { text } }
5
7
  let(:params) { {} }
6
8
  let(:text) { "Label" }
7
9
 
8
- subject(:output) { render_fragment(component) }
9
-
10
10
  it "renders a button element with the default styling" do
11
11
  expect(output).to have_css(
12
12
  "button.Button.Button-variant-solid.Button-size-2", text:
13
13
  )
14
14
  end
15
15
 
16
- it "doesn't render an icon" do
17
- expect(output).to_not have_css("button.Button svg.Icon")
16
+ it "doesn't render the icon" do
17
+ expect(output).not_to have_css("button.Button svg.Icon")
18
18
  end
19
19
 
20
20
  context "with a href" do
@@ -54,7 +54,7 @@ describe Backpack::Components::Button, type: :component do
54
54
  end
55
55
 
56
56
  context "with an icon before the text" do
57
- let(:icon) { render(Backpack::Components::Icon.new("user")) }
57
+ let(:icon) { render(Components::Icon.new("user")) }
58
58
  let(:component) do
59
59
  described_class.new(**params) do
60
60
  icon
@@ -74,7 +74,7 @@ describe Backpack::Components::Button, type: :component do
74
74
  end
75
75
 
76
76
  context "with an icon after the text" do
77
- let(:icon) { render(Backpack::Components::Icon.new("user")) }
77
+ let(:icon) { render(Components::Icon.new("user")) }
78
78
  let(:component) do
79
79
  described_class.new(**params) do
80
80
  text
@@ -1,11 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Backpack::Components::Favicon, type: :component do
3
+ describe Components::Favicon, type: :component do
4
+ subject(:output) { render_fragment(component) }
5
+
4
6
  let(:component) { described_class.new(**params) }
5
7
  let(:params) { {} }
6
8
 
7
- subject(:output) { render_fragment(component) }
8
-
9
+ # rubocop:disable RSpec/ExampleLength
9
10
  it "renders the links" do
10
11
  expect(output).to eq_html <<~HTML
11
12
  <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
@@ -15,6 +16,7 @@ describe Backpack::Components::Favicon, type: :component do
15
16
  <link rel="manifest" href="/site.webmanifest">
16
17
  HTML
17
18
  end
19
+ # rubocop:enable RSpec/ExampleLength
18
20
 
19
21
  context "with application name" do
20
22
  let(:params) { { application_name: "My App" } }
@@ -1,12 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Backpack::Components::Heading, type: :component do
3
+ describe Components::Heading, type: :component do
4
+ subject(:output) { render_fragment(component) }
5
+
4
6
  let(:component) { described_class.new(**params) { text } }
5
7
  let(:params) { {} }
6
8
  let(:text) { "Heading" }
7
9
 
8
- subject(:output) { render_fragment(component) }
9
-
10
10
  it "renders a level 1 heading by default" do
11
11
  expect(output).to have_css("h1.Heading.Heading-1", text:)
12
12
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Components::IconButton, type: :component do
4
+ subject(:output) { render_fragment(component) }
5
+
6
+ let(:component) { described_class.new(**params) }
7
+ let(:params) { { icon: :user, label: "User" } }
8
+
9
+ it "renders a button element with the default styling" do
10
+ expect(output).to have_css(
11
+ "button.IconButton.IconButton-variant-solid.IconButton-size-2"
12
+ )
13
+ end
14
+
15
+ it "renders the icon" do
16
+ expect(output).to have_css("button.IconButton svg.Icon.IconButton-icon[data-icon-key='user']")
17
+ end
18
+
19
+ context "with a href" do
20
+ let(:params) { { icon: :user, label: "User", href: "/path" } }
21
+
22
+ it "renders a link element using the same styling classes" do
23
+ expect(output).to have_css(
24
+ "a.IconButton.IconButton-variant-solid.IconButton-size-2[href='/path']"
25
+ )
26
+ end
27
+ end
28
+
29
+ context "with variant options" do
30
+ let(:params) { { icon: :user, label: "User", variant: :outline, color: :red, size: 1 } }
31
+
32
+ it "updates the variant modifiers in the class list" do
33
+ expect(output).to have_css(
34
+ "button.IconButton.IconButton-variant-outline.IconButton-size-1"
35
+ )
36
+ end
37
+ end
38
+
39
+ context "with HTML attributes" do
40
+ let(:params) do
41
+ { icon: :user, label: "User", class: "custom", data: { test: "value" }, aria: { label: "Open menu" } }
42
+ end
43
+
44
+ it "merges custom attributes with the generated ones" do
45
+ expect(output).to have_css(
46
+ "button.IconButton.IconButton-variant-solid.IconButton-size-2.custom[data-test='value'][aria-label='Open menu']"
47
+ )
48
+ end
49
+ end
50
+
51
+ context "with a color option" do
52
+ let(:params) { { icon: :user, label: "User", color: :red } }
53
+
54
+ it "sets the data-accent-color attribute" do
55
+ expect(output).to have_css("button.IconButton[data-accent-color='red']")
56
+ end
57
+ end
58
+ end