shoelace-rails 0.1.0
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.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +85 -0
- data/.gitignore +20 -0
- data/Appraisals +25 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +178 -0
- data/Rakefile +27 -0
- data/app/helpers/shoelace/form_helper.rb +451 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/dist/.keep +0 -0
- data/dist/types/.keep +0 -0
- data/gemfiles/rails_50.gemfile +12 -0
- data/gemfiles/rails_51.gemfile +11 -0
- data/gemfiles/rails_52.gemfile +11 -0
- data/gemfiles/rails_60.gemfile +11 -0
- data/gemfiles/rails_61.gemfile +11 -0
- data/gemfiles/rails_70.gemfile +11 -0
- data/gemfiles/rails_edge.gemfile +14 -0
- data/lib/shoelace/engine.rb +8 -0
- data/lib/shoelace/rails/version.rb +7 -0
- data/lib/shoelace/rails.rb +10 -0
- data/lib/shoelace/testing.rb +40 -0
- data/package.json +50 -0
- data/rollup.config.js +49 -0
- data/shoelace-rails.gemspec +35 -0
- data/src/index.ts +2 -0
- data/src/turbo/index.ts +6 -0
- data/src/turbo/polyfills/formdata-event.js +27 -0
- data/src/turbo/sl-turbo-form.ts +110 -0
- data/src/turbolinks/features/confirm.ts +42 -0
- data/src/turbolinks/features/disable.ts +94 -0
- data/src/turbolinks/features/remote.ts +107 -0
- data/src/turbolinks/index.ts +6 -0
- data/src/turbolinks/selectors.ts +38 -0
- data/src/turbolinks/start.ts +38 -0
- data/src/turbolinks/turbolinks.ts +78 -0
- data/src/turbolinks/utils/ajax.ts +146 -0
- data/src/turbolinks/utils/csp.ts +20 -0
- data/src/turbolinks/utils/csrf.ts +33 -0
- data/src/turbolinks/utils/dom.ts +40 -0
- data/src/turbolinks/utils/event.ts +57 -0
- data/src/turbolinks/utils/form.ts +58 -0
- data/test/dummy_app/Gemfile +19 -0
- data/test/dummy_app/Rakefile +6 -0
- data/test/dummy_app/app/controllers/hotwire_forms_controller.rb +46 -0
- data/test/dummy_app/app/controllers/turbolinks_forms_controller.rb +37 -0
- data/test/dummy_app/app/models/user.rb +16 -0
- data/test/dummy_app/app/packs/entrypoints/hotwire.js +1 -0
- data/test/dummy_app/app/packs/entrypoints/turbolinks.js +5 -0
- data/test/dummy_app/app/views/hotwire_forms/form.html.erb +45 -0
- data/test/dummy_app/app/views/hotwire_forms/show.html.erb +5 -0
- data/test/dummy_app/app/views/layouts/application.html.erb +39 -0
- data/test/dummy_app/app/views/turbolinks_forms/form.html.erb +44 -0
- data/test/dummy_app/app/views/turbolinks_forms/show.html.erb +5 -0
- data/test/dummy_app/bin/rails +5 -0
- data/test/dummy_app/bin/webpack +18 -0
- data/test/dummy_app/bin/yarn +18 -0
- data/test/dummy_app/config/application.rb +16 -0
- data/test/dummy_app/config/boot.rb +4 -0
- data/test/dummy_app/config/environment.rb +2 -0
- data/test/dummy_app/config/environments/development.rb +10 -0
- data/test/dummy_app/config/environments/test.rb +18 -0
- data/test/dummy_app/config/routes.rb +4 -0
- data/test/dummy_app/config/webpack/development.js +5 -0
- data/test/dummy_app/config/webpack/production.js +1 -0
- data/test/dummy_app/config/webpack/test.js +5 -0
- data/test/dummy_app/config/webpacker.yml +33 -0
- data/test/dummy_app/config.ru +6 -0
- data/test/dummy_app/package.json +24 -0
- data/test/dummy_app/test/system/hotwire_form_test.rb +65 -0
- data/test/dummy_app/test/system/turbolinks_form_test.rb +39 -0
- data/test/dummy_app/test/test_helper.rb +68 -0
- data/test/helpers/form_helper_test.rb +397 -0
- data/test/test_helper.rb +18 -0
- data/tsconfig.json +19 -0
- data/yarn.lock +249 -0
- metadata +196 -0
@@ -0,0 +1,451 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shoelace
|
4
|
+
module FormHelper
|
5
|
+
mattr_accessor :use_sl_form_tag
|
6
|
+
self.use_sl_form_tag = false
|
7
|
+
|
8
|
+
mattr_accessor :remote_form
|
9
|
+
self.remote_form = false
|
10
|
+
|
11
|
+
class ShoelaceInputField < ActionView::Helpers::Tags::TextField #:nodoc:
|
12
|
+
attr_reader :field_type
|
13
|
+
|
14
|
+
def initialize(field_type, *args)
|
15
|
+
super(*args)
|
16
|
+
@field_type = field_type
|
17
|
+
end
|
18
|
+
|
19
|
+
def render(&block)
|
20
|
+
options = @options.stringify_keys
|
21
|
+
|
22
|
+
value = options.fetch("value") { value_before_type_cast }
|
23
|
+
options["value"] = value if value.present?
|
24
|
+
|
25
|
+
options["size"] = options["maxlength"] unless options.key?("size")
|
26
|
+
options["type"] ||= field_type
|
27
|
+
options["invalid"] = options.fetch("invalid") { @object.errors[@method_name].presence }
|
28
|
+
add_default_name_and_id(options)
|
29
|
+
|
30
|
+
@template_object.content_tag('sl-input', '', options, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ShoelaceColorPicker < ActionView::Helpers::Tags::ColorField #:nodoc:
|
35
|
+
RGB_VALUE_REGEX = /#[0-9a-fA-F]{6}/
|
36
|
+
|
37
|
+
def field_type; nil; end
|
38
|
+
|
39
|
+
def tag(tag_name, *args, &block)
|
40
|
+
tag_name.to_s == 'input' ? content_tag('sl-color-picker', '', *args, &block) : super
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def validate_color_string(string)
|
46
|
+
string.downcase if RGB_VALUE_REGEX.match?(string)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ShoelaceRange < ActionView::Helpers::Tags::NumberField #:nodoc:
|
51
|
+
def field_type; nil; end
|
52
|
+
|
53
|
+
def tag(tag_name, *args, &block)
|
54
|
+
tag_name.to_s == 'input' ? content_tag('sl-range', '', *args, &block) : super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class ShoelaceSwitch < ActionView::Helpers::Tags::TextField #:nodoc:
|
59
|
+
def field_type; nil; end
|
60
|
+
|
61
|
+
def render(&block)
|
62
|
+
options = @options.stringify_keys
|
63
|
+
options["value"] = options.fetch("value") { value_before_type_cast }
|
64
|
+
add_default_name_and_id(options)
|
65
|
+
|
66
|
+
@template_object.content_tag('sl-switch', @method_name.to_s.humanize, options, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class ShoelaceTextArea < ActionView::Helpers::Tags::TextArea #:nodoc:
|
71
|
+
def content_tag(tag_name, content, options)
|
72
|
+
options[:value] = content if content.present?
|
73
|
+
|
74
|
+
tag_name.to_s == 'textarea' ? super('sl-textarea', '', options) : super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ShoelaceSelect < ActionView::Helpers::Tags::Select #:nodoc:
|
79
|
+
def grouped_options_for_select(grouped_options, options)
|
80
|
+
@template_object.grouped_sl_options_for_select(grouped_options, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
def options_for_select(container, options = nil)
|
84
|
+
@template_object.sl_options_for_select(container, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def select_content_tag(option_tags, _options, html_options)
|
88
|
+
html_options = html_options.stringify_keys
|
89
|
+
html_options['value']= value
|
90
|
+
add_default_name_and_id(html_options)
|
91
|
+
|
92
|
+
@template_object.content_tag("sl-select", option_tags, html_options)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ShoelaceCollectionSelect < ActionView::Helpers::Tags::CollectionSelect #:nodoc:
|
97
|
+
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
98
|
+
@template_object.sl_options_from_collection_for_select(collection, value_method, text_method, selected)
|
99
|
+
end
|
100
|
+
|
101
|
+
def select_content_tag(option_tags, _options, html_options)
|
102
|
+
html_options = html_options.stringify_keys
|
103
|
+
html_options['value']= value
|
104
|
+
add_default_name_and_id(html_options)
|
105
|
+
|
106
|
+
@template_object.content_tag("sl-select", option_tags, html_options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class ShoelaceCheckBox < ActionView::Helpers::Tags::CheckBox #:nodoc:
|
111
|
+
def render(&block)
|
112
|
+
options = @options.stringify_keys
|
113
|
+
options["value"] = @checked_value
|
114
|
+
options["checked"] = true if input_checked?(options)
|
115
|
+
|
116
|
+
if options["multiple"]
|
117
|
+
add_default_name_and_id_for_value(@checked_value, options)
|
118
|
+
options.delete("multiple")
|
119
|
+
else
|
120
|
+
add_default_name_and_id(options)
|
121
|
+
end
|
122
|
+
|
123
|
+
if block_given?
|
124
|
+
@template_object.content_tag('sl-checkbox', '', options, &block)
|
125
|
+
else
|
126
|
+
@template_object.content_tag('sl-checkbox', @method_name.to_s.humanize, options)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class ShoelaceRadioButton < ActionView::Helpers::Tags::RadioButton #:nodoc:
|
132
|
+
def render(&block)
|
133
|
+
options = @options.stringify_keys
|
134
|
+
options["value"] = @tag_value
|
135
|
+
options["checked"] = "checked" if input_checked?(options)
|
136
|
+
add_default_name_and_id_for_value(@tag_value, options)
|
137
|
+
|
138
|
+
@template_object.content_tag('sl-radio', '', options.except("type"), &block)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class ShoelaceCollectionRadioButtons < ActionView::Helpers::Tags::CollectionRadioButtons #:nodoc:
|
143
|
+
class RadioButtonBuilder < Builder # :nodoc:
|
144
|
+
def label(*)
|
145
|
+
text
|
146
|
+
end
|
147
|
+
|
148
|
+
def radio_button(extra_html_options = {}, &block)
|
149
|
+
html_options = extra_html_options.merge(@input_html_options)
|
150
|
+
html_options[:skip_default_ids] = false
|
151
|
+
@template_object.sl_radio_button(@object_name, @method_name, @value, html_options, &block)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def render(&block)
|
156
|
+
render_collection_for(RadioButtonBuilder, &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def render_collection(&block)
|
162
|
+
@template_object.content_tag('sl-radio-group', 'label' => @method_name.to_s.humanize) { super(&block) }
|
163
|
+
end
|
164
|
+
|
165
|
+
def hidden_field
|
166
|
+
''.html_safe
|
167
|
+
end
|
168
|
+
|
169
|
+
def render_component(builder)
|
170
|
+
builder.radio_button { builder.label }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class ShoelaceFormBuilder < ActionView::Helpers::FormBuilder #:nodoc:
|
175
|
+
{
|
176
|
+
email: :email,
|
177
|
+
number: :number,
|
178
|
+
password: :password,
|
179
|
+
search: :search,
|
180
|
+
telephone: :tel,
|
181
|
+
phone: :tel,
|
182
|
+
text: :text,
|
183
|
+
url: :url
|
184
|
+
}.each do |field_type, field_class|
|
185
|
+
# def email_field(method, **options, &block)
|
186
|
+
# ShoelaceInputField.new(:email, object_name, method, @template, options.with_defaults(label: method.to_s.humanize)).render(&block)
|
187
|
+
# end
|
188
|
+
eval <<-RUBY, nil, __FILE__, __LINE__ + 1
|
189
|
+
def #{field_type}_field(method, **options, &block)
|
190
|
+
ShoelaceInputField.new(:#{field_class}, object_name, method, @template, options.with_defaults(object: @object, label: method.to_s.humanize)).render(&block)
|
191
|
+
end
|
192
|
+
RUBY
|
193
|
+
end
|
194
|
+
|
195
|
+
def color_field(method, **options)
|
196
|
+
ShoelaceColorPicker.new(object_name, method, @template, options.with_defaults(object: @object)).render
|
197
|
+
end
|
198
|
+
alias color_picker color_field
|
199
|
+
|
200
|
+
def range_field(method, **options)
|
201
|
+
ShoelaceRange.new(object_name, method, @template, options.with_defaults(object: @object)).render
|
202
|
+
end
|
203
|
+
alias range range_field
|
204
|
+
|
205
|
+
def switch_field(method, **options, &block)
|
206
|
+
ShoelaceSwitch.new(object_name, method, @template, options.with_defaults(object: @object)).render(&block)
|
207
|
+
end
|
208
|
+
alias switch switch_field
|
209
|
+
|
210
|
+
def text_area(method, **options)
|
211
|
+
ShoelaceTextArea.new(object_name, method, @template, options.with_defaults(object: @object, resize: 'auto')).render
|
212
|
+
end
|
213
|
+
|
214
|
+
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0", &block)
|
215
|
+
ShoelaceCheckBox.new(object_name, method, @template, checked_value, unchecked_value, options.merge(object: @object)).render(&block)
|
216
|
+
end
|
217
|
+
|
218
|
+
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
|
220
|
+
end
|
221
|
+
|
222
|
+
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
|
224
|
+
end
|
225
|
+
|
226
|
+
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
227
|
+
ShoelaceCollectionRadioButtons.new(object_name, method, @template, collection, value_method, text_method, options.with_defaults(object: @object), html_options).render(&block)
|
228
|
+
end
|
229
|
+
|
230
|
+
def submit(value = nil, options = {})
|
231
|
+
value, options = nil, value if value.is_a?(Hash)
|
232
|
+
|
233
|
+
@template.sl_submit_tag(value || submit_default_value, **options)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
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
|
+
|
248
|
+
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
|
+
|
254
|
+
private_constant :DEFAULT_FORM_PARAMETERS, :DIVIDER_TAG, :OPENING_SL_FORM_TAG, :CLOSING_SL_FORM_TAG, :OPENING_SL_TURBO_FORM_TAG, :CLOSING_SL_TURBO_FORM_TAG
|
255
|
+
|
256
|
+
def sl_form_for(*args, **options, &block)
|
257
|
+
form_params = if ::Shoelace::FormHelper.remote_form
|
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
|
271
|
+
end
|
272
|
+
|
273
|
+
def sl_form_with(**args, &block)
|
274
|
+
content = form_with(**args, **DEFAULT_FORM_PARAMETERS.except(:data), &block)
|
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
|
282
|
+
end
|
283
|
+
|
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
|
+
# Creates a submit button with the text value as the caption, with the +submit+ attribute.
|
350
|
+
def sl_submit_tag(value = 'Save changes', **options)
|
351
|
+
options = options.deep_stringify_keys
|
352
|
+
tag_options = { "type" => "submit", "variant" => "primary" }.update(options)
|
353
|
+
set_default_disable_with(value, tag_options)
|
354
|
+
|
355
|
+
content_tag('sl-button', value, tag_options)
|
356
|
+
end
|
357
|
+
|
358
|
+
# Creates a shoelace text field; use these text fields to input smaller chunks of text like a username or a search
|
359
|
+
# query.
|
360
|
+
#
|
361
|
+
# For the properties available on this tag, please refer to the official documentation:
|
362
|
+
# https://shoelace.style/components/input?id=properties
|
363
|
+
#
|
364
|
+
def sl_text_field_tag(name, value = nil, **options, &block)
|
365
|
+
content_tag('sl-input', '', { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys), &block)
|
366
|
+
end
|
367
|
+
|
368
|
+
# Returns a string of +<sl-menu-item>+ tags, like +options_for_select+, but prepends a +<sl-menu-label>+ tag to
|
369
|
+
# each group.
|
370
|
+
def grouped_sl_options_for_select(grouped_options, options)
|
371
|
+
body = "".html_safe
|
372
|
+
|
373
|
+
grouped_options.each_with_index do |container, index|
|
374
|
+
label, values = container
|
375
|
+
|
376
|
+
body.safe_concat(DIVIDER_TAG) if index > 0
|
377
|
+
body.safe_concat(content_tag("sl-menu-label", label)) if label.present?
|
378
|
+
body.safe_concat(sl_options_for_select(values, options))
|
379
|
+
end
|
380
|
+
|
381
|
+
body
|
382
|
+
end
|
383
|
+
|
384
|
+
# Accepts an enumerable (hash, array, enumerable, your type) and returns a string of +sl-menu-item+ tags. Given
|
385
|
+
# an enumerable where the elements respond to +first+ and +last+ (such as a two-element array), the “lasts” serve
|
386
|
+
# as option values and the “firsts” as option text.
|
387
|
+
def sl_options_for_select(enumerable, options = nil)
|
388
|
+
return enumerable if String === enumerable
|
389
|
+
|
390
|
+
selected, disabled = extract_selected_and_disabled(options).map { |r| Array(r).map(&:to_s) }
|
391
|
+
|
392
|
+
enumerable.map do |element|
|
393
|
+
html_attributes = option_html_attributes(element)
|
394
|
+
text, value = option_text_and_value(element).map(&:to_s)
|
395
|
+
|
396
|
+
html_attributes[:checked] ||= selected.include?(value)
|
397
|
+
html_attributes[:disabled] ||= disabled.include?(value)
|
398
|
+
html_attributes[:value] = value
|
399
|
+
|
400
|
+
tag_builder.content_tag_string('sl-menu-item', text, html_attributes)
|
401
|
+
end.join("\n").html_safe
|
402
|
+
end
|
403
|
+
|
404
|
+
# Returns a string of +<sl-menu-item>+ tags compiled by iterating over the collection and assigning the result of
|
405
|
+
# a call to the +value_method+ as the option value and the +text_method+ as the option text.
|
406
|
+
def sl_options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
407
|
+
options = collection.map do |element|
|
408
|
+
[value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)]
|
409
|
+
end
|
410
|
+
|
411
|
+
selected, disabled = extract_selected_and_disabled(selected)
|
412
|
+
|
413
|
+
select_deselect = {
|
414
|
+
selected: extract_values_from_collection(collection, value_method, selected),
|
415
|
+
disabled: extract_values_from_collection(collection, value_method, disabled)
|
416
|
+
}
|
417
|
+
|
418
|
+
sl_options_for_select(options, select_deselect)
|
419
|
+
end
|
420
|
+
|
421
|
+
# Returns a +<sl-radio>+ tag for accessing a specified attribute (identified by method) on an object assigned to
|
422
|
+
# the template (identified by object). If the current value of method is +tag_value+ the radio button will be
|
423
|
+
# checked.
|
424
|
+
#
|
425
|
+
# To force the radio button to be checked pass checked: true in the options hash. You may pass HTML options there
|
426
|
+
# as well.
|
427
|
+
def sl_radio_button(object_name, method, tag_value, options = {}, &block)
|
428
|
+
ShoelaceRadioButton.new(object_name, method, self, tag_value, options).render(&block)
|
429
|
+
end
|
430
|
+
|
431
|
+
{
|
432
|
+
email: :email,
|
433
|
+
number: :number,
|
434
|
+
password: :password,
|
435
|
+
search: :search,
|
436
|
+
telephone: :tel,
|
437
|
+
phone: :tel,
|
438
|
+
url: :url
|
439
|
+
}.each do |field_type, field_class|
|
440
|
+
# def sl_email_field_tag(method, **options, &block)
|
441
|
+
# sl_text_field_tag(name, value, options.merge(type: :email))
|
442
|
+
# end
|
443
|
+
eval <<-RUBY, nil, __FILE__, __LINE__ + 1
|
444
|
+
# Creates a text field of type “#{field_type}”.
|
445
|
+
def sl_#{field_type}_field_tag(method, **options, &block)
|
446
|
+
sl_text_field_tag(name, value, options.merge(type: :#{field_class}))
|
447
|
+
end
|
448
|
+
RUBY
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "shoelace/rails"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/dist/.keep
ADDED
File without changes
|
data/dist/types/.keep
ADDED
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rake", "~> 13.0"
|
6
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
+
gem "rails", "~> 5.0.0"
|
8
|
+
gem "railties", "~> 5.0.0"
|
9
|
+
gem "activesupport", "~> 5.0.0"
|
10
|
+
gem "minitest", "5.10.3"
|
11
|
+
|
12
|
+
gemspec path: "../"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rake", "~> 13.0"
|
6
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
+
gem "rails", "~> 5.1.0"
|
8
|
+
gem "railties", "~> 5.1.0"
|
9
|
+
gem "activesupport", "~> 5.1.0"
|
10
|
+
|
11
|
+
gemspec path: "../"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rake", "~> 13.0"
|
6
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
+
gem "rails", "~> 5.2.0"
|
8
|
+
gem "railties", "~> 5.2.0"
|
9
|
+
gem "activesupport", "~> 5.2.0"
|
10
|
+
|
11
|
+
gemspec path: "../"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rake", "~> 13.0"
|
6
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
+
gem "rails", "~> 6.0.0"
|
8
|
+
gem "railties", "~> 6.0.0"
|
9
|
+
gem "activesupport", "~> 6.0.0"
|
10
|
+
|
11
|
+
gemspec path: "../"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rake", "~> 13.0"
|
6
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
+
gem "rails", "~> 6.1.0"
|
8
|
+
gem "railties", "~> 6.1.0"
|
9
|
+
gem "activesupport", "~> 6.1.0"
|
10
|
+
|
11
|
+
gemspec path: "../"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rake", "~> 13.0"
|
6
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
7
|
+
gem "rails", "~> 6.1.0"
|
8
|
+
gem "railties", "~> 6.1.0"
|
9
|
+
gem "activesupport", "~> 6.1.0"
|
10
|
+
|
11
|
+
gemspec path: "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
git "https://github.com/rails/rails.git" do
|
6
|
+
gem "rails"
|
7
|
+
gem "railties"
|
8
|
+
gem "activesupport"
|
9
|
+
end
|
10
|
+
|
11
|
+
gem "rake", "~> 13.0"
|
12
|
+
gem "rails-dom-testing", git: "https://github.com/rails/rails-dom-testing.git", ref: "8f5acdfc"
|
13
|
+
|
14
|
+
gemspec path: "../"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shoelace
|
4
|
+
module Testing
|
5
|
+
def sl_select(option_text, from: )
|
6
|
+
select_element = find("sl-select[placeholder=\"#{from}\"]")
|
7
|
+
select_element.click
|
8
|
+
|
9
|
+
within select_element do
|
10
|
+
find('sl-menu-item', text: option_text).click
|
11
|
+
end
|
12
|
+
|
13
|
+
select_element
|
14
|
+
end
|
15
|
+
|
16
|
+
def sl_multi_select(*options_to_select, from: )
|
17
|
+
select_element = find("sl-select[placeholder=\"#{from}\"]")
|
18
|
+
|
19
|
+
select_element.click
|
20
|
+
within select_element do
|
21
|
+
options_to_select.each do |option_text|
|
22
|
+
find('sl-menu-item', text: option_text).click
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# The multi select does not close automatically, so need to click to close it.
|
27
|
+
select_element.click if options_to_select.size > 1
|
28
|
+
|
29
|
+
select_element
|
30
|
+
end
|
31
|
+
|
32
|
+
def sl_check(label)
|
33
|
+
find("sl-checkbox", text: label).click
|
34
|
+
end
|
35
|
+
|
36
|
+
def sl_toggle(label)
|
37
|
+
find("sl-switch", text: label).click
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|