shoelace-rails 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +48 -11
- data/Appraisals +4 -4
- data/CHANGELOG.md +39 -2
- data/README.md +93 -57
- data/app/helpers/shoelace/form_helper.rb +33 -122
- data/app/helpers/shoelace/tag_helper.rb +27 -0
- data/gemfiles/rails_70.gemfile +3 -3
- data/gemfiles/rails_edge.gemfile +1 -1
- data/lib/shoelace/engine.rb +0 -2
- data/lib/shoelace/rails/version.rb +1 -1
- data/lib/shoelace/rails.rb +5 -1
- data/lib/shoelace/railtie.rb +54 -0
- data/lib/tasks/shoelace.rake +20 -0
- data/test/dummy_app/app/views/hotwire_forms/form.html.erb +1 -1
- data/test/dummy_app/app/views/layouts/application.html.erb +2 -2
- data/test/dummy_app/test/system/hotwire_form_test.rb +0 -2
- data/test/dummy_app/test/system/turbolinks_form_test.rb +0 -1
- data/test/helpers/form_helper_test.rb +130 -51
- data/test/helpers/tag_helper_test.rb +58 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6bf02de3fbe6ef35ad4c464e666bd7ace7bf0c2e673b0767afea4d6b87522749
|
4
|
+
data.tar.gz: 73abb906c86ffc3f4a945f73f3a97e3cd335c1cb4245b82362cf53c3587619d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e7a5bca7b14951f97b54c9de1be69698ceebb63a421be912cc8f692ebec8ed075ebdf58a4fbc9d9200c296e3150ae63f41f6a6a40f62fc368c1a54044905a9b
|
7
|
+
data.tar.gz: 5d09b9294065025cb30762a152c3b037739790a8b16ff5e2178bcb98afc0592756447ba0273967450783e11fbfd9863be5e11027a61074ed622e35d380a95dc5
|
data/.github/workflows/main.yml
CHANGED
@@ -9,21 +9,24 @@ jobs:
|
|
9
9
|
strategy:
|
10
10
|
matrix:
|
11
11
|
ruby_version:
|
12
|
+
- '3.2'
|
12
13
|
- '3.1'
|
13
14
|
- '3.0'
|
14
15
|
- '2.7'
|
15
|
-
- 'jruby-9.3
|
16
|
+
- 'jruby-9.3'
|
17
|
+
- 'jruby-9.4'
|
16
18
|
gemfile:
|
17
19
|
- gemfiles/rails_70.gemfile
|
18
20
|
- gemfiles/rails_61.gemfile
|
19
21
|
- gemfiles/rails_60.gemfile
|
20
|
-
|
22
|
+
exclude:
|
23
|
+
- ruby_version: 'jruby-9.3'
|
24
|
+
gemfile: gemfiles/rails_70.gemfile
|
25
|
+
runs-on: ubuntu-22.04
|
21
26
|
env:
|
22
27
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
23
28
|
steps:
|
24
|
-
- uses: actions/checkout@
|
25
|
-
- name: Install curl
|
26
|
-
run: sudo apt-get install curl libcurl4-openssl-dev
|
29
|
+
- uses: actions/checkout@v3
|
27
30
|
- name: Set up Ruby
|
28
31
|
uses: ruby/setup-ruby@v1
|
29
32
|
with:
|
@@ -32,13 +35,14 @@ jobs:
|
|
32
35
|
- run: bundle exec rake test
|
33
36
|
|
34
37
|
system:
|
35
|
-
|
38
|
+
needs: unit
|
39
|
+
runs-on: ubuntu-22.04
|
36
40
|
steps:
|
37
|
-
- uses: actions/checkout@
|
41
|
+
- uses: actions/checkout@v3
|
38
42
|
- name: Set up Ruby
|
39
43
|
uses: ruby/setup-ruby@v1
|
40
44
|
with:
|
41
|
-
ruby-version: 3.
|
45
|
+
ruby-version: 3.2.0
|
42
46
|
bundler-cache: true
|
43
47
|
- name: Install dependencies
|
44
48
|
run: |
|
@@ -49,14 +53,47 @@ jobs:
|
|
49
53
|
- name: Run the system test in the dummy app
|
50
54
|
run: rake test:system
|
51
55
|
|
56
|
+
rails_edge:
|
57
|
+
needs: system
|
58
|
+
runs-on: ubuntu-22.04
|
59
|
+
env:
|
60
|
+
BUNDLE_GEMFILE: gemfiles/rails_edge.gemfile
|
61
|
+
steps:
|
62
|
+
- uses: actions/checkout@v3
|
63
|
+
- name: Set up Ruby
|
64
|
+
uses: ruby/setup-ruby@v1
|
65
|
+
with:
|
66
|
+
ruby-version: 3.1
|
67
|
+
bundler-cache: true
|
68
|
+
- run: bundle exec rake test || echo "Rails edge test is done."
|
69
|
+
|
70
|
+
ruby_edge:
|
71
|
+
needs: system
|
72
|
+
strategy:
|
73
|
+
matrix:
|
74
|
+
gemfile:
|
75
|
+
- gemfiles/rails_edge.gemfile
|
76
|
+
- gemfiles/rails_70.gemfile
|
77
|
+
runs-on: ubuntu-22.04
|
78
|
+
env:
|
79
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
80
|
+
steps:
|
81
|
+
- uses: actions/checkout@v3
|
82
|
+
- name: Set up Ruby
|
83
|
+
uses: ruby/setup-ruby@v1
|
84
|
+
with:
|
85
|
+
ruby-version: 'ruby-head'
|
86
|
+
bundler-cache: true
|
87
|
+
- run: bundle exec rake || echo "Ruby edge test is done."
|
88
|
+
|
52
89
|
# browser_tests:
|
53
|
-
# runs-on: ubuntu-
|
90
|
+
# runs-on: ubuntu-22.04
|
54
91
|
# steps:
|
55
|
-
# - uses: actions/checkout@
|
92
|
+
# - uses: actions/checkout@v3
|
56
93
|
# - name: Set up Ruby
|
57
94
|
# uses: ruby/setup-ruby@v1
|
58
95
|
# with:
|
59
|
-
# ruby-version: 3.
|
96
|
+
# ruby-version: 3.2.0
|
60
97
|
# bundler-cache: true
|
61
98
|
# - name: Install dependencies
|
62
99
|
# run: |
|
data/Appraisals
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
appraise "rails_edge" do
|
2
|
-
git "https://github.com/rails/rails.git" do
|
2
|
+
git "https://github.com/rails/rails.git", branch: "main" do
|
3
3
|
gem "rails"
|
4
4
|
gem "railties"
|
5
5
|
gem "activesupport"
|
@@ -7,9 +7,9 @@ appraise "rails_edge" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
appraise "rails_70" do
|
10
|
-
gem "rails", "~>
|
11
|
-
gem "railties", "~>
|
12
|
-
gem "activesupport", "~>
|
10
|
+
gem "rails", "~> 7.0.0"
|
11
|
+
gem "railties", "~> 7.0.0"
|
12
|
+
gem "activesupport", "~> 7.0.0"
|
13
13
|
end
|
14
14
|
|
15
15
|
appraise "rails_61" do
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,40 @@
|
|
1
|
-
##
|
1
|
+
## v0.3.0
|
2
|
+
|
3
|
+
_<sup>released at 2023-01-05 08:47:12 UTC</sup>_
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
- No longer requires the `sl-form` component (`4fdbfa15`)
|
8
|
+
- The `#text_area` method now accepts a block (`5092dc1c`)
|
9
|
+
- Allow overriding the value attribute for `<sl-select>` (`1f38be73`)
|
10
|
+
- Auto-display labels whenever possible (`c1e3a950`)
|
11
|
+
- `<sl-select>` now always has a label by default (`f9fb5f0c`)
|
12
|
+
- Support Ruby 3.2 (`b286cbc1`)
|
13
|
+
- Add `#sl_button_to` (`e1bdedba`)
|
14
|
+
- Add `#sl_icon_tag` (`8a2187a2`)
|
15
|
+
- Add `#sl_avatar_tag` (`77dccdb2`)
|
16
|
+
- Allow using the `Shoelace::FormBuilder` in a cleaner way (`43dea330`)
|
17
|
+
|
18
|
+
#### Bug Fixes
|
19
|
+
|
20
|
+
- Fixes a bug where the gem rake tasks are not loaded (`115bfb3d`)
|
21
|
+
- Fixes a bug where values are not properly passed in to `<sl-textarea>` (`3d163845`)
|
22
|
+
- Make sure yarn install is always executed before copying shoelace assets (`98018a27`)
|
23
|
+
- Fixes a bug where the `@object` needs to respond to `#errors` (`bb981ed0`)
|
24
|
+
- Fixes a bug where the `size` attr is ignored by the `#text_area` method (`8bc4c378`)
|
25
|
+
- Fixes a bug where unchecked checkbox values are not captured (`dc658bea`)
|
26
|
+
|
27
|
+
## [v0.2.0](https://github.com/yuki24/shoelace-rails/tree/v0.2.0)
|
28
|
+
|
29
|
+
_<sup>released at 2022-06-24 05:14:01 UTC</sup>_
|
30
|
+
|
31
|
+
#### Features
|
32
|
+
|
33
|
+
- Do not require the `copy-webpack-plugin` to set up Shoelace so the gem works with any js bundler.
|
34
|
+
|
35
|
+
## [v0.1.0](https://github.com/yuki24/shoelace-rails/tree/v0.1.0)
|
36
|
+
|
37
|
+
_<sup>released at 2022-02-17 13:17:09 UTC</sup>_
|
38
|
+
|
39
|
+
First release!
|
2
40
|
|
3
|
-
- First release!
|
data/README.md
CHANGED
@@ -4,10 +4,6 @@ The `shoelace-rails` gem adds useful helper methods for [Shoelace.style](https:/
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
This document assumes that you use the [`webpacker`](https://github.com/rails/webpacker) or
|
8
|
-
the [`jsbundling-rails`](https://github.com/rails/jsbundling-rails) gem. You may have to tweak the examples here if
|
9
|
-
you do not use it. However, the principle here should be the same regardless of the JS bundler you use.
|
10
|
-
|
11
7
|
Add this line to your application's Gemfile:
|
12
8
|
|
13
9
|
```ruby
|
@@ -23,7 +19,7 @@ $ bundle install
|
|
23
19
|
Additionally, you need to add the following npm packages:
|
24
20
|
|
25
21
|
```sh
|
26
|
-
$ yarn add @shoelace-style/shoelace
|
22
|
+
$ yarn add @shoelace-style/shoelace
|
27
23
|
```
|
28
24
|
|
29
25
|
## Set up CSS
|
@@ -50,66 +46,31 @@ import "@shoelace-style/shoelace/dist/themes/dark.css" // Optional dark mode
|
|
50
46
|
|
51
47
|
## Set up Javascript
|
52
48
|
|
53
|
-
In
|
54
|
-
|
55
|
-
If you are using the `jsbundling-rails` gem and have `webpack.config.js` in the top level directory of your project, Add th
|
56
|
-
e configuration for the `CopyPlugin`:
|
49
|
+
In this README, it is assumed that you are using a JS bundler such as `webpack` or `esbuild`. In order to define all
|
50
|
+
the custome elements, import the shoelace dependency in the entrypoint file:
|
57
51
|
|
58
52
|
```js
|
59
|
-
|
60
|
-
|
61
|
-
const path = require('path')
|
62
|
-
|
63
|
-
module.exports = {
|
64
|
-
...,
|
65
|
-
plugins: [
|
66
|
-
new CopyPlugin({
|
67
|
-
patterns: [
|
68
|
-
{
|
69
|
-
from: path.resolve(__dirname, "node_modules/@shoelace-style/shoelace/dist/assets"),
|
70
|
-
to: path.resolve(__dirname, "public/assets"),
|
71
|
-
},
|
72
|
-
],
|
73
|
-
}),
|
74
|
-
],
|
75
|
-
}
|
76
|
-
```
|
77
|
-
|
78
|
-
If you are using the `webpacker` gem, you could add the same configuration but to `config/webpack/environment.js`:
|
53
|
+
import "@shoelace-style/shoelace"
|
54
|
+
```
|
79
55
|
|
80
|
-
|
81
|
-
// config/webpack/environment.js
|
82
|
-
const { environment } = require('@rails/webpacker')
|
83
|
-
const path = require('path')
|
84
|
-
const CopyPlugin = require('copy-webpack-plugin')
|
56
|
+
That's it!
|
85
57
|
|
86
|
-
|
87
|
-
environment.plugins.append(
|
88
|
-
'CopyPlugin',
|
89
|
-
new CopyPlugin({
|
90
|
-
patterns: [
|
91
|
-
{
|
92
|
-
from: path.resolve(__dirname, '../../node_modules/@shoelace-style/shoelace/dist/assets'),
|
93
|
-
to: path.resolve(__dirname, '../../public/packs/js/assets')
|
94
|
-
}
|
95
|
-
]
|
96
|
-
})
|
97
|
-
)
|
58
|
+
### Shoelace Icons
|
98
59
|
|
99
|
-
|
100
|
-
```
|
60
|
+
Shoelace icons are automatically set up to load properly, so you don't need to add any extra code. More specifically,
|
101
61
|
|
102
|
-
|
62
|
+
* In development, the icons are served by the `ActionDispatch::Static` middleware, directly from the
|
63
|
+
`node_modules/@shoelace-style/shoelace/dist/assets/icons` directory.
|
64
|
+
* In production, the icon files are automatically copied into the `public/assets` directory as part of the
|
65
|
+
`assets:precompile` rake task.
|
103
66
|
|
104
|
-
|
105
|
-
import "@shoelace-style/shoelace"
|
106
|
-
```
|
67
|
+
## View Helpers
|
107
68
|
|
108
|
-
|
69
|
+
As explained above, this gem provides drop-in replacements to Rails view helpers.
|
109
70
|
|
110
|
-
|
71
|
+
### Form Helpers
|
111
72
|
|
112
|
-
|
73
|
+
The `sl_form_with` or `sl_form_for` method could be used to generate a form with the Shoelace components:
|
113
74
|
|
114
75
|
```erb
|
115
76
|
<%= sl_form_for @user do |form| %>
|
@@ -133,7 +94,7 @@ As explained above, this gem provides drop-in replacements to. Here is a short e
|
|
133
94
|
And this code will produce:
|
134
95
|
|
135
96
|
```html
|
136
|
-
<
|
97
|
+
<form class="new_user" id="new_user" data-remote="true" action="/" accept-charset="UTF-8" method="post">
|
137
98
|
<sl-input label="Name" type="text" name="user[name]" id="user_name"></sl-input>
|
138
99
|
<sl-input label="Password" type="password" name="user[password]" id="user_password"></sl-input>
|
139
100
|
<sl-color-picker value="#ffffff" name="user[color]" id="user_color"></sl-color-picker>
|
@@ -151,7 +112,82 @@ And this code will produce:
|
|
151
112
|
</sl-select>
|
152
113
|
|
153
114
|
<sl-button submit="true" type="primary" data-disable-with="Create User">Create User</sl-button>
|
154
|
-
</
|
115
|
+
</form>
|
116
|
+
```
|
117
|
+
|
118
|
+
#### Using The `sl-select` component with `multiple`
|
119
|
+
|
120
|
+
TDB
|
121
|
+
|
122
|
+
#### Using the Shoelace FormBuilder with other gems
|
123
|
+
|
124
|
+
Sometimes you want to use the Shoelace FormBuilder with other gems, such as [ransack](https://github.com/activerecord-hackery/ransack).
|
125
|
+
In this case, you can not use the `sl_form_for` or `sl_form_with` methods in tandem with `ransack`, but you can use
|
126
|
+
the `Shoelace::FormBuilder` with e.g. [the `search_form_for` method](https://activerecord-hackery.github.io/ransack/getting-started/simple-mode/#form-helper):
|
127
|
+
|
128
|
+
```erb
|
129
|
+
<%= search_form_for @q, builder: Shoelace::FormBuilder do |form| %>
|
130
|
+
...
|
131
|
+
<% end %>
|
132
|
+
```
|
133
|
+
|
134
|
+
### Tag Helpers
|
135
|
+
|
136
|
+
#### `#sl_avatar_tag`
|
137
|
+
|
138
|
+
The `@sl_avatar_tag` method behaves just like the `image_tag` method.
|
139
|
+
|
140
|
+
```erb
|
141
|
+
<%= sl_avatar_tag "/path/to/image.jpg" %>
|
142
|
+
```
|
143
|
+
|
144
|
+
Will produce:
|
145
|
+
|
146
|
+
```html
|
147
|
+
<sl-avatar image="/path/to/image.jpg"></sl-avatar>
|
148
|
+
```
|
149
|
+
|
150
|
+
#### `#sl_button_to`
|
151
|
+
|
152
|
+
The `sl_button_to` method behaves just like the `link_to` method. Note that this is slightly different from the
|
153
|
+
built-in `button_to` method.
|
154
|
+
|
155
|
+
Without a block:
|
156
|
+
|
157
|
+
```erb
|
158
|
+
<%= sl_button_to "Next Page", "/components?page=2" %>
|
159
|
+
```
|
160
|
+
|
161
|
+
```html
|
162
|
+
<sl-button href="/components?page=2">
|
163
|
+
Next Page
|
164
|
+
</sl-button>
|
165
|
+
```
|
166
|
+
|
167
|
+
With a block:
|
168
|
+
|
169
|
+
```erb
|
170
|
+
<%= sl_button_to "/components?page=2" do %>
|
171
|
+
Next Page
|
172
|
+
<% end %>
|
173
|
+
```
|
174
|
+
|
175
|
+
```html
|
176
|
+
<sl-button href="/components?page=2">
|
177
|
+
Next Page
|
178
|
+
</sl-button>
|
179
|
+
```
|
180
|
+
|
181
|
+
#### `#sl_icon_tag`
|
182
|
+
|
183
|
+
The `sl_icon_tag` method takes the `name` attribute value as the first argument:
|
184
|
+
|
185
|
+
```erb
|
186
|
+
<%= sl_icon_tag "apple" %>
|
187
|
+
```
|
188
|
+
|
189
|
+
```html
|
190
|
+
<sl-icon name="apple"></sl-icon>
|
155
191
|
```
|
156
192
|
|
157
193
|
## Development
|
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
module Shoelace
|
4
4
|
module FormHelper
|
5
|
-
mattr_accessor :
|
6
|
-
self.
|
7
|
-
|
8
|
-
mattr_accessor :remote_form
|
9
|
-
self.remote_form = false
|
5
|
+
mattr_accessor :invalid_input_class_name
|
6
|
+
self.invalid_input_class_name = nil
|
10
7
|
|
11
8
|
class ShoelaceInputField < ActionView::Helpers::Tags::TextField #:nodoc:
|
12
9
|
attr_reader :field_type
|
@@ -24,7 +21,8 @@ module Shoelace
|
|
24
21
|
|
25
22
|
options["size"] = options["maxlength"] unless options.key?("size")
|
26
23
|
options["type"] ||= field_type
|
27
|
-
options["
|
24
|
+
options["class"] ||= [options["class"], Shoelace::FormHelper.invalid_input_class_name].compact.join(" ") if @object.respond_to?(:errors) && @object.errors[@method_name].present?
|
25
|
+
|
28
26
|
add_default_name_and_id(options)
|
29
27
|
|
30
28
|
@template_object.content_tag('sl-input', '', options, &block)
|
@@ -68,10 +66,12 @@ module Shoelace
|
|
68
66
|
end
|
69
67
|
|
70
68
|
class ShoelaceTextArea < ActionView::Helpers::Tags::TextArea #:nodoc:
|
71
|
-
def
|
72
|
-
options
|
69
|
+
def render(&block)
|
70
|
+
options = @options.stringify_keys
|
71
|
+
options["value"] = options.fetch("value") { value_before_type_cast }
|
72
|
+
add_default_name_and_id(options)
|
73
73
|
|
74
|
-
|
74
|
+
@template_object.content_tag("sl-textarea", '', options, &block)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -86,7 +86,7 @@ module Shoelace
|
|
86
86
|
|
87
87
|
def select_content_tag(option_tags, _options, html_options)
|
88
88
|
html_options = html_options.stringify_keys
|
89
|
-
html_options['value']
|
89
|
+
html_options['value'] ||= value
|
90
90
|
add_default_name_and_id(html_options)
|
91
91
|
|
92
92
|
@template_object.content_tag("sl-select", option_tags, html_options)
|
@@ -100,7 +100,7 @@ module Shoelace
|
|
100
100
|
|
101
101
|
def select_content_tag(option_tags, _options, html_options)
|
102
102
|
html_options = html_options.stringify_keys
|
103
|
-
html_options['value']
|
103
|
+
html_options['value'] ||= value
|
104
104
|
add_default_name_and_id(html_options)
|
105
105
|
|
106
106
|
@template_object.content_tag("sl-select", option_tags, html_options)
|
@@ -120,10 +120,18 @@ module Shoelace
|
|
120
120
|
add_default_name_and_id(options)
|
121
121
|
end
|
122
122
|
|
123
|
-
|
124
|
-
|
123
|
+
include_hidden = options.delete("include_hidden") { true }
|
124
|
+
|
125
|
+
sl_checkbox_tag = if block_given?
|
126
|
+
@template_object.content_tag('sl-checkbox', '', options, &block)
|
127
|
+
else
|
128
|
+
@template_object.content_tag('sl-checkbox', @method_name.to_s.humanize, options)
|
129
|
+
end
|
130
|
+
|
131
|
+
if include_hidden
|
132
|
+
hidden_field_for_checkbox(options) + sl_checkbox_tag
|
125
133
|
else
|
126
|
-
|
134
|
+
sl_checkbox_tag
|
127
135
|
end
|
128
136
|
end
|
129
137
|
end
|
@@ -198,7 +206,7 @@ module Shoelace
|
|
198
206
|
alias color_picker color_field
|
199
207
|
|
200
208
|
def range_field(method, **options)
|
201
|
-
ShoelaceRange.new(object_name, method, @template, options.with_defaults(object: @object)).render
|
209
|
+
ShoelaceRange.new(object_name, method, @template, options.with_defaults(object: @object, label: method.to_s.humanize)).render
|
202
210
|
end
|
203
211
|
alias range range_field
|
204
212
|
|
@@ -207,8 +215,8 @@ module Shoelace
|
|
207
215
|
end
|
208
216
|
alias switch switch_field
|
209
217
|
|
210
|
-
def text_area(method, **options)
|
211
|
-
ShoelaceTextArea.new(object_name, method, @template, options.with_defaults(object: @object, resize: 'auto')).render
|
218
|
+
def text_area(method, **options, &block)
|
219
|
+
ShoelaceTextArea.new(object_name, method, @template, options.with_defaults(object: @object, label: method.to_s.humanize, resize: 'auto')).render(&block)
|
212
220
|
end
|
213
221
|
|
214
222
|
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0", &block)
|
@@ -216,11 +224,11 @@ module Shoelace
|
|
216
224
|
end
|
217
225
|
|
218
226
|
def select(method, choices = nil, options = {}, html_options = {}, &block)
|
219
|
-
ShoelaceSelect.new(object_name, method, @template, choices, options.with_defaults(object: @object), html_options, &block).render
|
227
|
+
ShoelaceSelect.new(object_name, method, @template, choices, options.with_defaults(object: @object), html_options.with_defaults(label: method.to_s.humanize), &block).render
|
220
228
|
end
|
221
229
|
|
222
230
|
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
223
|
-
ShoelaceCollectionSelect.new(object_name, method, @template, collection, value_method, text_method, options.with_defaults(object: @object), html_options, &block).render
|
231
|
+
ShoelaceCollectionSelect.new(object_name, method, @template, collection, value_method, text_method, options.with_defaults(object: @object), html_options.with_defaults(label: method.to_s.humanize), &block).render
|
224
232
|
end
|
225
233
|
|
226
234
|
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
@@ -234,118 +242,19 @@ module Shoelace
|
|
234
242
|
end
|
235
243
|
end
|
236
244
|
|
237
|
-
DEFAULT_FORM_PARAMETERS = {
|
238
|
-
builder: ShoelaceFormBuilder,
|
239
|
-
data: {
|
240
|
-
remote: true,
|
241
|
-
}
|
242
|
-
}
|
243
|
-
|
244
|
-
DEFAULT_TURBO_FORM_PARAMETERS = {
|
245
|
-
builder: ShoelaceFormBuilder,
|
246
|
-
}
|
247
|
-
|
245
|
+
DEFAULT_FORM_PARAMETERS = { builder: ShoelaceFormBuilder }
|
248
246
|
DIVIDER_TAG = "<sl-divider></sl-divider>".html_safe
|
249
|
-
OPENING_SL_FORM_TAG = '<sl-form'.html_safe
|
250
|
-
CLOSING_SL_FORM_TAG = '</sl-form>'.html_safe
|
251
|
-
OPENING_SL_TURBO_FORM_TAG = '<sl-turbo-form'.html_safe
|
252
|
-
CLOSING_SL_TURBO_FORM_TAG = '</sl-turbo-form>'.html_safe
|
253
247
|
|
254
|
-
private_constant :DEFAULT_FORM_PARAMETERS, :DIVIDER_TAG
|
248
|
+
private_constant :DEFAULT_FORM_PARAMETERS, :DIVIDER_TAG
|
255
249
|
|
256
250
|
def sl_form_for(*args, **options, &block)
|
257
|
-
|
258
|
-
DEFAULT_FORM_PARAMETERS.deep_merge(options)
|
259
|
-
else
|
260
|
-
DEFAULT_FORM_PARAMETERS.without(:data).merge(options)
|
261
|
-
end
|
262
|
-
|
263
|
-
content = form_for(*args, **form_params, &block)
|
264
|
-
|
265
|
-
if ::Shoelace::FormHelper.use_sl_form_tag
|
266
|
-
content[0, 5] = OPENING_SL_FORM_TAG
|
267
|
-
content[-7, 7] = CLOSING_SL_FORM_TAG
|
268
|
-
end
|
269
|
-
|
270
|
-
content
|
251
|
+
form_for(*args, **DEFAULT_FORM_PARAMETERS, **options, &block)
|
271
252
|
end
|
272
253
|
|
273
254
|
def sl_form_with(**args, &block)
|
274
|
-
|
275
|
-
|
276
|
-
if ::Shoelace::FormHelper.use_sl_form_tag
|
277
|
-
content[0, 5] = OPENING_SL_FORM_TAG
|
278
|
-
content[-7, 7] = CLOSING_SL_FORM_TAG
|
279
|
-
end
|
280
|
-
|
281
|
-
content
|
255
|
+
form_with(**args, **DEFAULT_FORM_PARAMETERS, &block)
|
282
256
|
end
|
283
257
|
|
284
|
-
def sl_form_tag(url_for_options = {}, options = {}, &block)
|
285
|
-
content = form_tag(url_for_options, options.with_defaults(DEFAULT_FORM_PARAMETERS.except(:builder)), &block)
|
286
|
-
|
287
|
-
if ::Shoelace::FormHelper.use_sl_form_tag
|
288
|
-
content[0, 5] = OPENING_SL_FORM_TAG
|
289
|
-
content[-7, 7] = CLOSING_SL_FORM_TAG
|
290
|
-
end
|
291
|
-
|
292
|
-
content
|
293
|
-
end
|
294
|
-
|
295
|
-
def sl_turbo_form_for(*args, **options, &block)
|
296
|
-
content = form_for(*args, **DEFAULT_TURBO_FORM_PARAMETERS.merge(options), &block)
|
297
|
-
|
298
|
-
if ::Shoelace::FormHelper.use_sl_form_tag
|
299
|
-
content[0, 5] = OPENING_SL_TURBO_FORM_TAG
|
300
|
-
content[-7, 7] = CLOSING_SL_TURBO_FORM_TAG
|
301
|
-
end
|
302
|
-
|
303
|
-
content
|
304
|
-
end
|
305
|
-
|
306
|
-
def sl_turbo_form_with(**args, &block)
|
307
|
-
content = form_with(**args, **DEFAULT_TURBO_FORM_PARAMETERS, &block)
|
308
|
-
|
309
|
-
if ::Shoelace::FormHelper.use_sl_form_tag
|
310
|
-
content[0, 5] = OPENING_SL_TURBO_FORM_TAG
|
311
|
-
content[-7, 7] = CLOSING_SL_TURBO_FORM_TAG
|
312
|
-
end
|
313
|
-
|
314
|
-
content
|
315
|
-
end
|
316
|
-
|
317
|
-
def sl_turbo_form_tag(url_for_options = {}, options = {}, &block)
|
318
|
-
content = form_tag(url_for_options, options.with_defaults(DEFAULT_TURBO_FORM_PARAMETERS.except(:builder)), &block)
|
319
|
-
|
320
|
-
if ::Shoelace::FormHelper.use_sl_form_tag
|
321
|
-
content[0, 5] = OPENING_SL_TURBO_FORM_TAG
|
322
|
-
content[-7, 7] = CLOSING_SL_TURBO_FORM_TAG
|
323
|
-
end
|
324
|
-
|
325
|
-
content
|
326
|
-
end
|
327
|
-
|
328
|
-
# Creates a generic +<sl-button>+ element.
|
329
|
-
def sl_button_tag(**attrs, &block)
|
330
|
-
content_tag("sl-button", **attrs, &block)
|
331
|
-
end
|
332
|
-
|
333
|
-
# Not providing this helper for now due to potentially untraceable HTML. E.g.
|
334
|
-
#
|
335
|
-
# <a href="...">
|
336
|
-
# <sl-button>...</sl-button>
|
337
|
-
# </a>
|
338
|
-
#
|
339
|
-
# may be trackable and traceable by search bots and scrapers, but:
|
340
|
-
#
|
341
|
-
# <sl-button href="...">...</sl-button>
|
342
|
-
#
|
343
|
-
# may not be. In the mean time, it is advisable to wrap a <sl-button> tag with an <a> tag.
|
344
|
-
#
|
345
|
-
# def sl_button_to(href, **attrs, &block)
|
346
|
-
# sl_button_tag(href: href, **attrs, &block)
|
347
|
-
# end
|
348
|
-
|
349
258
|
# Creates a submit button with the text value as the caption, with the +submit+ attribute.
|
350
259
|
def sl_submit_tag(value = 'Save changes', **options)
|
351
260
|
options = options.deep_stringify_keys
|
@@ -448,4 +357,6 @@ module Shoelace
|
|
448
357
|
RUBY
|
449
358
|
end
|
450
359
|
end
|
360
|
+
|
361
|
+
FormBuilder = FormHelper::ShoelaceFormBuilder
|
451
362
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shoelace
|
4
|
+
module TagHelper
|
5
|
+
# Creates a generic +<sl-button>+ element.
|
6
|
+
def sl_button_tag(**attrs, &block)
|
7
|
+
content_tag("sl-button", **attrs, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Creates an <sl-button> tag with the href value as the caption.
|
11
|
+
def sl_button_to(body, href = nil, **attrs, &block)
|
12
|
+
if block_given?
|
13
|
+
sl_button_tag(href: body, **(href || {}), **attrs, &block)
|
14
|
+
else
|
15
|
+
sl_button_tag(href: href, **attrs) { body }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sl_icon_tag(name, **attrs)
|
20
|
+
tag.sl_icon(name: name, **attrs)
|
21
|
+
end
|
22
|
+
|
23
|
+
def sl_avatar_tag(source, **attrs, &block)
|
24
|
+
tag.sl_avatar(image: source, **attrs, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/gemfiles/rails_70.gemfile
CHANGED
@@ -4,8 +4,8 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gem "rake", "~> 13.0"
|
6
6
|
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
-
gem "rails", "~>
|
8
|
-
gem "railties", "~>
|
9
|
-
gem "activesupport", "~>
|
7
|
+
gem "rails", "~> 7.0.0"
|
8
|
+
gem "railties", "~> 7.0.0"
|
9
|
+
gem "activesupport", "~> 7.0.0"
|
10
10
|
|
11
11
|
gemspec path: "../"
|
data/gemfiles/rails_edge.gemfile
CHANGED
data/lib/shoelace/engine.rb
CHANGED
data/lib/shoelace/rails.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "rails/version"
|
4
|
-
|
4
|
+
|
5
|
+
if defined?(::Rails::Railtie)
|
6
|
+
require_relative "engine"
|
7
|
+
require_relative "railtie"
|
8
|
+
end
|
5
9
|
|
6
10
|
module Shoelace
|
7
11
|
module Rails
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch/middleware/static'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
# The only reason this class exists is to clarify that we have a custom static file server after
|
7
|
+
# `ActionDispatch::Static`. We could just use `ActionDispatch::Static` directly, but it would make the result of
|
8
|
+
# `rake middleware` more difficult to understand, as the output would look like:
|
9
|
+
#
|
10
|
+
# use ...
|
11
|
+
# use ActionDispatch::Static
|
12
|
+
# use ActionDispatch::Static # Why do we use the same middleware twice?
|
13
|
+
# use ...
|
14
|
+
#
|
15
|
+
# It is much more straightforward if it looks like:
|
16
|
+
#
|
17
|
+
# use ...
|
18
|
+
# use ActionDispatch::Static
|
19
|
+
# use Shoelace::AssetProvider
|
20
|
+
# use ...
|
21
|
+
#
|
22
|
+
class AssetProvider < ActionDispatch::Static; end
|
23
|
+
|
24
|
+
class Railtie < ::Rails::Railtie #:nodoc:
|
25
|
+
config.shoelace = ActiveSupport::OrderedOptions.new
|
26
|
+
|
27
|
+
# Path to the shoelace assets.
|
28
|
+
config.shoelace.dist_path = "node_modules/@shoelace-style/shoelace/dist"
|
29
|
+
|
30
|
+
# Class name that is added to a form input when the corresponding attribute has an `ActiveModel` error.
|
31
|
+
config.shoelace.invalid_input_class_name = nil
|
32
|
+
|
33
|
+
initializer "shoelace.use_rack_middleware" do |app|
|
34
|
+
icon_dir = File.join(app.paths["public"].first, "assets/icons")
|
35
|
+
|
36
|
+
if !Dir.exist?(icon_dir)
|
37
|
+
path = app.root.join(app.config.shoelace.dist_path).to_s
|
38
|
+
headers = app.config.public_file_server.headers || {}
|
39
|
+
|
40
|
+
app.config.middleware.insert_after ActionDispatch::Static, Shoelace::AssetProvider, path, index: "index.html", headers: headers
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
initializer "shoelace.form_helper" do |app|
|
45
|
+
ActiveSupport.on_load :action_view do
|
46
|
+
Shoelace::FormHelper.invalid_input_class_name = app.config.shoelace.invalid_input_class_name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
rake_tasks do
|
51
|
+
load "tasks/shoelace.rake"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
namespace :shoelace do
|
2
|
+
namespace :icons do
|
3
|
+
desc "Copy Shoelace icons to the assets path"
|
4
|
+
task copy: :environment do
|
5
|
+
cp_r Rails.root.join(Shoelace::Railtie.config.shoelace.dist_path, "assets").to_s, Rails.public_path
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Remove Shoelace icons"
|
9
|
+
task clobber: :environment do
|
10
|
+
rm_rf File.join(Rails.public_path, "assets/icons")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Make sure `yarn install` is run before running `shoelace:icons:copy`.
|
16
|
+
Rake::Task["shoelace:icons:copy"].enhance(["javascript:build"])
|
17
|
+
|
18
|
+
if Rake::Task.task_defined?("assets:precompile")
|
19
|
+
Rake::Task["assets:precompile"].enhance(["shoelace:icons:copy"])
|
20
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% locations = { tokyo: "Tokyo", new_york: "New York", london: "London" } %>
|
2
|
-
<%=
|
2
|
+
<%= sl_form_for(@user, url: hotwire_forms_path) do |form| %>
|
3
3
|
<div>
|
4
4
|
<%= form.text_field :name do %>
|
5
5
|
<span slot="help-text" style="color: rgb(var(--sl-color-danger-600));">
|
@@ -21,8 +21,8 @@
|
|
21
21
|
}
|
22
22
|
</style>
|
23
23
|
|
24
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.
|
25
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.
|
24
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.87/dist/themes/light.css">
|
25
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.87/dist/shoelace.js"></script>
|
26
26
|
|
27
27
|
<% if request.path.include?("hotwire") %>
|
28
28
|
<%= javascript_pack_tag 'hotwire' %>
|
@@ -22,7 +22,6 @@ class HotwireFormTest < ApplicationSystemTestCase
|
|
22
22
|
assert_current_path hotwire_form_path(1)
|
23
23
|
assert_text "Name: Yuki Nishijima"
|
24
24
|
assert_text "Description: I am a human."
|
25
|
-
assert_text "Color: #ffffff"
|
26
25
|
assert_text "Score: 50"
|
27
26
|
# assert_text "Current City:"
|
28
27
|
assert_text "Previous City: tokyo"
|
@@ -48,7 +47,6 @@ class HotwireFormTest < ApplicationSystemTestCase
|
|
48
47
|
assert_current_path hotwire_form_path(1)
|
49
48
|
assert_text "Name: Yuki Nishijima"
|
50
49
|
assert_text "Description: I am a human."
|
51
|
-
assert_text "Color: #ffffff"
|
52
50
|
assert_text "Score: 50"
|
53
51
|
# assert_text "Current City:"
|
54
52
|
assert_text "Previous City: tokyo"
|
@@ -22,7 +22,6 @@ class TurbolinksFormTest < ApplicationSystemTestCase
|
|
22
22
|
assert_current_path turbolinks_forms_path
|
23
23
|
assert_text "Name: Yuki Nishijima"
|
24
24
|
assert_text "Description: I am a human."
|
25
|
-
assert_text "Color: #ffffff"
|
26
25
|
assert_text "Score: 50"
|
27
26
|
# assert_text "Current City:"
|
28
27
|
assert_text "Previous City: tokyo"
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'test_helper'
|
2
3
|
|
3
4
|
require_relative '../../app/helpers/shoelace/form_helper'
|
@@ -5,6 +6,8 @@ require_relative '../../app/helpers/shoelace/form_helper'
|
|
5
6
|
class FormHelperTest < ActionView::TestCase
|
6
7
|
include Shoelace::FormHelper
|
7
8
|
|
9
|
+
AUTOCOMPLETE_ATTRIBUTE = ActionView::VERSION::STRING >= '6.1.0' ? 'autocomplete="off"' : ''
|
10
|
+
|
8
11
|
test "#sl_text_field_tag with name and value" do
|
9
12
|
assert_dom_equal <<~HTML, sl_text_field_tag('name', 'Your name')
|
10
13
|
<sl-input type="text" name="name" id="name" value="Your name"></sl-input>
|
@@ -41,30 +44,16 @@ class FormHelperTest < ActionView::TestCase
|
|
41
44
|
HTML
|
42
45
|
end
|
43
46
|
|
44
|
-
test "#sl_button_tag" do
|
45
|
-
assert_dom_equal(<<~HTML, sl_button_tag { "Submit" })
|
46
|
-
<sl-button>Submit</sl-button>
|
47
|
-
HTML
|
48
|
-
end
|
49
|
-
|
50
47
|
test "#sl_radio_button" do
|
51
48
|
assert_dom_equal(<<~HTML, sl_radio_button(:user, :name, 'userid-314', checked: true) { "Yuki Nishijima" })
|
52
49
|
<sl-radio value="userid-314" checked="checked" name="user[name]" id="user_name_userid-314">Yuki Nishijima</sl-radio>
|
53
50
|
HTML
|
54
51
|
end
|
55
52
|
|
56
|
-
test "#sl_form_tag" do
|
57
|
-
assert_dom_equal(<<~HTML, sl_form_tag("/posts") { })
|
58
|
-
<form data-remote="true" action="/posts" accept-charset="UTF-8" method="post">
|
59
|
-
<input name="utf8" type="hidden" value="✓" />
|
60
|
-
</sl-form>
|
61
|
-
HTML
|
62
|
-
end
|
63
|
-
|
64
53
|
test "#sl_form_with" do
|
65
54
|
assert_dom_equal(<<~HTML, sl_form_with(url: "/") {})
|
66
55
|
<form action="/" accept-charset="UTF-8" data-remote="true" method="post">
|
67
|
-
<input name="utf8" type="hidden" value="✓" />
|
56
|
+
<input name="utf8" type="hidden" value="✓" #{AUTOCOMPLETE_ATTRIBUTE} />
|
68
57
|
</sl-form>
|
69
58
|
HTML
|
70
59
|
end
|
@@ -72,35 +61,11 @@ class FormHelperTest < ActionView::TestCase
|
|
72
61
|
test "#sl_form_for" do
|
73
62
|
assert_dom_equal(<<~HTML, sl_form_for(User.new, url: "/") { })
|
74
63
|
<form class="new_user" id="new_user" action="/" accept-charset="UTF-8" method="post">
|
75
|
-
<input name="utf8" type="hidden" value="✓" />
|
64
|
+
<input name="utf8" type="hidden" value="✓" #{AUTOCOMPLETE_ATTRIBUTE} />
|
76
65
|
</sl-form>
|
77
66
|
HTML
|
78
67
|
end
|
79
68
|
|
80
|
-
test "#sl_turbo_form_tag" do
|
81
|
-
assert_dom_equal(<<~HTML, sl_turbo_form_tag("/posts") { })
|
82
|
-
<form action="/posts" accept-charset="UTF-8" method="post">
|
83
|
-
<input name="utf8" type="hidden" value="✓" />
|
84
|
-
</form>
|
85
|
-
HTML
|
86
|
-
end
|
87
|
-
|
88
|
-
test "#sl_turbo_form_with" do
|
89
|
-
assert_dom_equal(<<~HTML, sl_turbo_form_with(url: "/") {})
|
90
|
-
<form action="/" accept-charset="UTF-8" data-remote="true" method="post">
|
91
|
-
<input name="utf8" type="hidden" value="✓" />
|
92
|
-
</form>
|
93
|
-
HTML
|
94
|
-
end
|
95
|
-
|
96
|
-
test "#sl_turbo_form_for" do
|
97
|
-
assert_dom_equal(<<~HTML, sl_turbo_form_for(User.new, url: "/") { })
|
98
|
-
<form class="new_user" id="new_user" action="/" accept-charset="UTF-8" method="post">
|
99
|
-
<input name="utf8" type="hidden" value="✓" />
|
100
|
-
</form>
|
101
|
-
HTML
|
102
|
-
end
|
103
|
-
|
104
69
|
test "#text_field" do
|
105
70
|
sl_form_for(User.new, url: "/") do |form|
|
106
71
|
assert_dom_equal <<~HTML, form.text_field(:name)
|
@@ -192,6 +157,14 @@ class FormHelperTest < ActionView::TestCase
|
|
192
157
|
test "#range_field" do
|
193
158
|
sl_form_for(User.new, url: "/") do |form|
|
194
159
|
assert_dom_equal <<~HTML, form.range_field(:name)
|
160
|
+
<sl-range label="Name" name="user[name]" id="user_name"></sl-range>
|
161
|
+
HTML
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
test "#range_field without a label" do
|
166
|
+
sl_form_for(User.new, url: "/") do |form|
|
167
|
+
assert_dom_equal <<~HTML, form.range_field(:name, label: nil)
|
195
168
|
<sl-range name="user[name]" id="user_name"></sl-range>
|
196
169
|
HTML
|
197
170
|
end
|
@@ -208,22 +181,92 @@ class FormHelperTest < ActionView::TestCase
|
|
208
181
|
test "#text_area" do
|
209
182
|
sl_form_for(User.new, url: "/") do |form|
|
210
183
|
assert_dom_equal <<~HTML, form.text_area(:name)
|
184
|
+
<sl-textarea label="Name" resize="auto" name="user[name]" id="user_name"></sl-textarea>
|
185
|
+
HTML
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
test "#text_area with a value" do
|
190
|
+
sl_form_for(User.new(name: "Yuki Nishijima"), url: "/") do |form|
|
191
|
+
assert_dom_equal <<~HTML, form.text_area(:name)
|
192
|
+
<sl-textarea label="Name" resize="auto" value="Yuki Nishijima" name="user[name]" id="user_name"></sl-textarea>
|
193
|
+
HTML
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
test "#text_area with an one-off value" do
|
198
|
+
sl_form_for(User.new(name: "Yuki Nishijima"), url: "/") do |form|
|
199
|
+
assert_dom_equal <<~HTML, form.text_area(:name, value: "Yuki")
|
200
|
+
<sl-textarea label="Name" resize="auto" value="Yuki" name="user[name]" id="user_name"></sl-textarea>
|
201
|
+
HTML
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
test "#text_area without a label" do
|
206
|
+
sl_form_for(User.new, url: "/") do |form|
|
207
|
+
assert_dom_equal <<~HTML, form.text_area(:name, label: nil)
|
211
208
|
<sl-textarea resize="auto" name="user[name]" id="user_name"></sl-textarea>
|
212
209
|
HTML
|
213
210
|
end
|
214
211
|
end
|
215
212
|
|
216
|
-
test "#
|
213
|
+
test "#text_area with a size" do
|
217
214
|
sl_form_for(User.new, url: "/") do |form|
|
218
|
-
assert_dom_equal <<~HTML, form.
|
219
|
-
<sl-
|
215
|
+
assert_dom_equal <<~HTML, form.text_area(:name, size: "small")
|
216
|
+
<sl-textarea label="Name" resize="auto" size="small" name="user[name]" id="user_name"></sl-textarea>
|
220
217
|
HTML
|
221
218
|
end
|
222
219
|
end
|
223
220
|
|
221
|
+
test "#text_area with a block" do
|
222
|
+
sl_form_for(User.new, url: "/") do |form|
|
223
|
+
expected = <<~HTML
|
224
|
+
<sl-textarea label="Name" resize="auto" name="user[name]" id="user_name">
|
225
|
+
<div slot="help-text">Name can not be blank.</div>
|
226
|
+
</sl-textarea>
|
227
|
+
HTML
|
228
|
+
|
229
|
+
assert_dom_equal(expected, form.text_area(:name) {
|
230
|
+
content_tag(:div, "Name can not be blank.", slot: "help-text")
|
231
|
+
})
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
test "#check_box" do
|
236
|
+
sl_form_for(User.new, url: "/") do |form|
|
237
|
+
if ActionView::VERSION::STRING >= '6.1.0'
|
238
|
+
assert_dom_equal <<~HTML, form.check_box(:name)
|
239
|
+
<input name="user[name]" type="hidden" value="0" autocomplete="off" />
|
240
|
+
<sl-checkbox value="1" name="user[name]" id="user_name">Name</sl-checkbox>
|
241
|
+
HTML
|
242
|
+
else
|
243
|
+
assert_dom_equal <<~HTML, form.check_box(:name)
|
244
|
+
<input name="user[name]" type="hidden" value="0" />
|
245
|
+
<sl-checkbox value="1" name="user[name]" id="user_name">Name</sl-checkbox>
|
246
|
+
HTML
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
224
251
|
test "#check_box with a block" do
|
225
252
|
sl_form_for(User.new, url: "/") do |form|
|
226
|
-
|
253
|
+
if ActionView::VERSION::STRING >= '6.1.0'
|
254
|
+
assert_dom_equal <<~HTML, form.check_box(:name) { "Maintainer Name" }
|
255
|
+
<input name="user[name]" type="hidden" value="0" autocomplete="off" />
|
256
|
+
<sl-checkbox value="1" name="user[name]" id="user_name">Maintainer Name</sl-checkbox>
|
257
|
+
HTML
|
258
|
+
else
|
259
|
+
assert_dom_equal <<~HTML, form.check_box(:name) { "Maintainer Name" }
|
260
|
+
<input name="user[name]" type="hidden" value="0" />
|
261
|
+
<sl-checkbox value="1" name="user[name]" id="user_name">Maintainer Name</sl-checkbox>
|
262
|
+
HTML
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
test "#check_box without a hidden input" do
|
268
|
+
sl_form_for(User.new, url: "/") do |form|
|
269
|
+
assert_dom_equal <<~HTML, form.check_box(:name, include_hidden: false) { "Maintainer Name" }
|
227
270
|
<sl-checkbox value="1" name="user[name]" id="user_name">Maintainer Name</sl-checkbox>
|
228
271
|
HTML
|
229
272
|
end
|
@@ -238,7 +281,7 @@ class FormHelperTest < ActionView::TestCase
|
|
238
281
|
|
239
282
|
sl_form_for(User.new, url: "/") do |form|
|
240
283
|
assert_dom_equal <<~HTML, form.select(:name, users)
|
241
|
-
<sl-select name="user[name]" id="user_name">
|
284
|
+
<sl-select label="Name" name="user[name]" id="user_name">
|
242
285
|
<sl-menu-item value="1">Yuki Nishijima</sl-menu-item>
|
243
286
|
<sl-menu-item value="2">Matz</sl-menu-item>
|
244
287
|
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
@@ -247,6 +290,42 @@ class FormHelperTest < ActionView::TestCase
|
|
247
290
|
end
|
248
291
|
end
|
249
292
|
|
293
|
+
test "#select with a custom value" do
|
294
|
+
users = {
|
295
|
+
"Yuki Nishijima" => 1,
|
296
|
+
"Matz" => 2,
|
297
|
+
"Koichi Sasada" => 3
|
298
|
+
}
|
299
|
+
|
300
|
+
sl_form_for(User.new, url: "/") do |form|
|
301
|
+
assert_dom_equal <<~HTML, form.select(:name, users, {}, { value: 3 })
|
302
|
+
<sl-select label="Name" value="3" name="user[name]" id="user_name">
|
303
|
+
<sl-menu-item value="1">Yuki Nishijima</sl-menu-item>
|
304
|
+
<sl-menu-item value="2">Matz</sl-menu-item>
|
305
|
+
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
306
|
+
</sl-select>
|
307
|
+
HTML
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
test "#select with custom selected and disabled values" do
|
312
|
+
users = {
|
313
|
+
"Yuki Nishijima" => 1,
|
314
|
+
"Matz" => 2,
|
315
|
+
"Koichi Sasada" => 3
|
316
|
+
}
|
317
|
+
|
318
|
+
sl_form_for(User.new, url: "/") do |form|
|
319
|
+
assert_dom_equal <<~HTML, form.select(:name, users, selected: 3, disabled: 1)
|
320
|
+
<sl-select label="Name" name="user[name]" id="user_name">
|
321
|
+
<sl-menu-item value="1" disabled="disabled">Yuki Nishijima</sl-menu-item>
|
322
|
+
<sl-menu-item value="2">Matz</sl-menu-item>
|
323
|
+
<sl-menu-item value="3" checked="checked">Koichi Sasada</sl-menu-item>
|
324
|
+
</sl-select>
|
325
|
+
HTML
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
250
329
|
test "#select with multiple" do
|
251
330
|
users = {
|
252
331
|
"Yuki Nishijima" => 1,
|
@@ -256,7 +335,7 @@ class FormHelperTest < ActionView::TestCase
|
|
256
335
|
|
257
336
|
sl_form_for(User.new, url: "/") do |form|
|
258
337
|
assert_dom_equal <<~HTML, form.select(:name, users, {}, { multiple: true })
|
259
|
-
<sl-select name="user[name][]" id="user_name" multiple="multiple">
|
338
|
+
<sl-select label="Name" name="user[name][]" id="user_name" multiple="multiple">
|
260
339
|
<sl-menu-item value="1">Yuki Nishijima</sl-menu-item>
|
261
340
|
<sl-menu-item value="2">Matz</sl-menu-item>
|
262
341
|
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
@@ -278,7 +357,7 @@ class FormHelperTest < ActionView::TestCase
|
|
278
357
|
|
279
358
|
sl_form_for(User.new, url: "/") do |form|
|
280
359
|
assert_dom_equal <<~HTML, form.select(:name, users)
|
281
|
-
<sl-select name="user[name]" id="user_name">
|
360
|
+
<sl-select label="Name" name="user[name]" id="user_name">
|
282
361
|
<sl-menu-label>Main maintainers</sl-menu-label>
|
283
362
|
<sl-menu-item value="2">Matz</sl-menu-item>
|
284
363
|
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
@@ -301,9 +380,9 @@ class FormHelperTest < ActionView::TestCase
|
|
301
380
|
]
|
302
381
|
}
|
303
382
|
|
304
|
-
sl_form_for(User.new(name:
|
383
|
+
sl_form_for(User.new(name: 2), url: "/") do |form|
|
305
384
|
assert_dom_equal <<~HTML, form.select(:name, users)
|
306
|
-
<sl-select name="user[name]" id="user_name" value="2">
|
385
|
+
<sl-select label="Name" name="user[name]" id="user_name" value="2">
|
307
386
|
<sl-menu-label>Main maintainers</sl-menu-label>
|
308
387
|
<sl-menu-item value="2" checked="checked">Matz</sl-menu-item>
|
309
388
|
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
@@ -324,7 +403,7 @@ class FormHelperTest < ActionView::TestCase
|
|
324
403
|
|
325
404
|
sl_form_for(User.new, url: "/") do |form|
|
326
405
|
assert_dom_equal <<~HTML, form.collection_select(:name, users, :first, :last)
|
327
|
-
<sl-select name="user[name]" id="user_name">
|
406
|
+
<sl-select label="Name" name="user[name]" id="user_name">
|
328
407
|
<sl-menu-item value="1">Yuki Nishijima</sl-menu-item>
|
329
408
|
<sl-menu-item value="2">Matz</sl-menu-item>
|
330
409
|
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
@@ -342,7 +421,7 @@ class FormHelperTest < ActionView::TestCase
|
|
342
421
|
|
343
422
|
sl_form_for(User.new(name: "2"), url: "/") do |form|
|
344
423
|
assert_dom_equal <<~HTML, form.collection_select(:name, users, :first, :last)
|
345
|
-
<sl-select name="user[name]" id="user_name" value="2">
|
424
|
+
<sl-select label="Name" name="user[name]" id="user_name" value="2">
|
346
425
|
<sl-menu-item value="1">Yuki Nishijima</sl-menu-item>
|
347
426
|
<sl-menu-item value="2" checked="checked">Matz</sl-menu-item>
|
348
427
|
<sl-menu-item value="3">Koichi Sasada</sl-menu-item>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
require_relative '../../app/helpers/shoelace/tag_helper'
|
5
|
+
|
6
|
+
class TagHelperTest < ActionView::TestCase
|
7
|
+
include Shoelace::TagHelper
|
8
|
+
|
9
|
+
test "#sl_button_tag" do
|
10
|
+
assert_dom_equal(<<~HTML, sl_button_tag { "Submit" })
|
11
|
+
<sl-button>Submit</sl-button>
|
12
|
+
HTML
|
13
|
+
end
|
14
|
+
|
15
|
+
test "#sl_button_to" do
|
16
|
+
assert_dom_equal <<~HTML, sl_button_to("Next", "/next")
|
17
|
+
<sl-button href="/next">Next</sl-button>
|
18
|
+
HTML
|
19
|
+
|
20
|
+
assert_dom_equal <<~HTML, sl_button_to("Next", "/next", class: "mt-1")
|
21
|
+
<sl-button href="/next" class="mt-1">Next</sl-button>
|
22
|
+
HTML
|
23
|
+
|
24
|
+
assert_dom_equal(<<~HTML, sl_button_to("/next") { "Next" })
|
25
|
+
<sl-button href="/next">Next</sl-button>
|
26
|
+
HTML
|
27
|
+
|
28
|
+
assert_dom_equal(<<~HTML, sl_button_to("/next", class: "mt-1") { "Next" })
|
29
|
+
<sl-button href="/next" class="mt-1">Next</sl-button>
|
30
|
+
HTML
|
31
|
+
|
32
|
+
assert_dom_equal <<~HTML, sl_button_to("Next")
|
33
|
+
<sl-button>Next</sl-button>
|
34
|
+
HTML
|
35
|
+
end
|
36
|
+
|
37
|
+
test "#sl_icon_tag"do
|
38
|
+
assert_dom_equal <<~HTML, sl_icon_tag("0-circle-fill")
|
39
|
+
<sl-icon name="0-circle-fill"></sl-icon>
|
40
|
+
HTML
|
41
|
+
|
42
|
+
assert_dom_equal <<~HTML, sl_icon_tag("0-circle-fill", slot: "icon")
|
43
|
+
<sl-icon name="0-circle-fill" slot="icon"></sl-icon>
|
44
|
+
HTML
|
45
|
+
end
|
46
|
+
|
47
|
+
test "#sl_avatar_tag"do
|
48
|
+
assert_dom_equal <<~HTML, sl_avatar_tag("/path/to/image.jpg")
|
49
|
+
<sl-avatar image="/path/to/image.jpg"></sl-avatar>
|
50
|
+
HTML
|
51
|
+
|
52
|
+
assert_dom_equal(<<~HTML, sl_avatar_tag("/path/to/image.jpg", slot: "trigger") { "Body" })
|
53
|
+
<sl-avatar image="/path/to/image.jpg" slot="trigger">
|
54
|
+
Body
|
55
|
+
</sl-avatar>
|
56
|
+
HTML
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shoelace-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Nishijima
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- README.md
|
99
99
|
- Rakefile
|
100
100
|
- app/helpers/shoelace/form_helper.rb
|
101
|
+
- app/helpers/shoelace/tag_helper.rb
|
101
102
|
- bin/console
|
102
103
|
- bin/setup
|
103
104
|
- dist/.keep
|
@@ -112,7 +113,9 @@ files:
|
|
112
113
|
- lib/shoelace/engine.rb
|
113
114
|
- lib/shoelace/rails.rb
|
114
115
|
- lib/shoelace/rails/version.rb
|
116
|
+
- lib/shoelace/railtie.rb
|
115
117
|
- lib/shoelace/testing.rb
|
118
|
+
- lib/tasks/shoelace.rake
|
116
119
|
- package.json
|
117
120
|
- rollup.config.js
|
118
121
|
- shoelace-rails.gemspec
|
@@ -164,6 +167,7 @@ files:
|
|
164
167
|
- test/dummy_app/test/system/turbolinks_form_test.rb
|
165
168
|
- test/dummy_app/test/test_helper.rb
|
166
169
|
- test/helpers/form_helper_test.rb
|
170
|
+
- test/helpers/tag_helper_test.rb
|
167
171
|
- test/test_helper.rb
|
168
172
|
- tsconfig.json
|
169
173
|
- yarn.lock
|
@@ -189,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
193
|
- !ruby/object:Gem::Version
|
190
194
|
version: '0'
|
191
195
|
requirements: []
|
192
|
-
rubygems_version: 3.
|
196
|
+
rubygems_version: 3.4.1
|
193
197
|
signing_key:
|
194
198
|
specification_version: 4
|
195
199
|
summary: Rails view helpers Shoelace.style, the design system.
|