Skip to content

Commit 6da720f

Browse files
author
Nils Henning
committed
[FEATURE] refactor checkboxes into own component, add rails like single checkbox functionality
1 parent 124526b commit 6da720f

File tree

9 files changed

+326
-196
lines changed

9 files changed

+326
-196
lines changed

.byebug_history

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,24 @@
11
continue
2+
TestModel.last
3+
continue
4+
TestModel.last
5+
sleep 1
6+
page.html
7+
save_screenshot
8+
TestModel.order(:created_at).last
9+
TestModel.order(:created_at).first
10+
TestModel.order(:created_at)
11+
TestModel.first
12+
TestModel.count
13+
TestModel.last
14+
continue
15+
TestModel.last.status
16+
TestModel.last
17+
@test_model.errors.any?
18+
@test_model.errors
19+
@test_model = TestModel.create(model_params)
20+
model_params
21+
continue
222
page.html
323
continue
424
!(@included_config[:errors] == false && (errors == false || errors.nil?) || errors == false)
@@ -234,23 +254,3 @@ save_screenshot
234254
continue
235255
save_screenshot
236256
continue
237-
self
238-
self.class.superclass.optional_properties
239-
self.class.superclass
240-
self.superclass
241-
self.class.class
242-
self.class
243-
self.parent_class
244-
self.parent
245-
self
246-
continue
247-
self
248-
continue
249-
self
250-
continue
251-
self
252-
continue
253-
self
254-
continue
255-
self
256-
continue
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
require_relative '../utils'
2+
require_relative '../has_errors'
3+
module Matestack::Ui::Core::Form::Checkbox
4+
class Checkbox < Matestack::Ui::Core::Component::Static
5+
include Matestack::Ui::Core::Form::Utils
6+
include Matestack::Ui::Core::Form::HasErrors
7+
8+
html_attributes :accept, :alt, :autocomplete, :autofocus, :checked, :dirname, :disabled, :form, :formaction,
9+
:formenctype, :formmethod, :formnovalidate, :formtarget, :height, :list, :max, :maxlength, :min, :minlength,
10+
:multiple, :name, :pattern, :placeholder, :readonly, :required, :size, :src, :step, :type, :value, :width
11+
12+
requires :key
13+
optional :value, :false_value, :multiple, :init, for: { as: :input_for }, label: { as: :input_label }, options: { as: :checkbox_options }
14+
15+
def response
16+
# multiple values
17+
if checkbox_options
18+
checkbox_options.to_a.each do |item|
19+
input html_attributes.merge(
20+
attributes: vue_attributes.merge(ref: "select.multiple.#{attr_key}"),
21+
type: :checkbox,
22+
id: "#{id_for_item(item_value(item))}",
23+
name: item_name(item),
24+
value: item_value(item),
25+
)
26+
label text: item_name(item), for: id_for_item(item_value(item))
27+
end
28+
# checked/unchecked checkbox
29+
else
30+
form_input type: :hidden, key: key, value: (false_value || 0)
31+
form_input type: :checkbox, key: key, value: checked_value, id: id_for_item(value)
32+
label text: input_label, for: id_for_item(value)
33+
end
34+
render_errors
35+
end
36+
37+
def vue_attributes
38+
(options[:attributes] || {}).merge({
39+
"@change": change_event,
40+
ref: "input.#{attr_key}",
41+
'init-value': init_value,
42+
'v-bind:class': "{ '#{input_error_class}': #{error_key} }",
43+
"#{v_model_type}": input_key,
44+
})
45+
end
46+
47+
def item_value(item)
48+
item.is_a?(Array) ? item.last : item
49+
end
50+
51+
def item_name(item)
52+
item.is_a?(Array) ? item.first : item
53+
end
54+
55+
def checked_value
56+
value || 1
57+
end
58+
59+
def v_model_type
60+
if checkbox_options && checkbox_options.first.is_a?(Integer)
61+
'v-model.number'
62+
else
63+
'v-model'
64+
end
65+
end
66+
67+
def change_event
68+
"inputChanged('#{attr_key}')"
69+
end
70+
71+
def id_for_item(value)
72+
"#{html_attributes[:id]}_#{value}"
73+
end
74+
75+
end
76+
end

app/concepts/matestack/ui/core/form/select/select.haml

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,6 @@
1919
%option{value: option}=option
2020
= render_errors
2121

22-
- if options[:type] == :checkbox
23-
24-
%div{id: component_id, ref: "select.multiple.#{attr_key}", "init-value": init_value}
25-
- if options[:options].is_a?(Hash)
26-
- options[:options].each do |key, value|
27-
%input{@tag_attributes.except(:id),
28-
id: id_for_option(value),
29-
type: :checkbox,
30-
"#{model_binding}": input_key,
31-
"@change": "inputChanged(\"#{attr_key}\")",
32-
"value-type": options_type,
33-
name: value,
34-
value: key}/
35-
%label{for: id_for_option(value)}=value
36-
- if options[:options].is_a?(Array)
37-
- options[:options].each do |value|
38-
%input{@tag_attributes.except(:id),
39-
id: id_for_option(value),
40-
type: :checkbox,
41-
"#{model_binding}": input_key,
42-
"@change": "inputChanged(\"#{attr_key}\")",
43-
"value-type": options_type,
44-
name: value,
45-
value: value}/
46-
%label{for: id_for_option(value)}=value
47-
= render_errors
48-
4922
- if options[:type] == :radio
5023

5124
%div{id: component_id, ref: "select.#{attr_key}", "init-value": init_value}

lib/matestack/ui/core/components.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def self.require_core_component(name)
7171
require_core_component "figure"
7272
require_core_component "footer"
7373
require_core_component "form"
74+
require_core_component "form/checkbox"
7475
require_core_component "form/input"
7576
require_core_component "form/select"
7677
require_core_component "form/submit"
@@ -183,6 +184,7 @@ def self.require_core_component(name)
183184
figure: Matestack::Ui::Core::Figure::Figure,
184185
footer: Matestack::Ui::Core::Footer::Footer,
185186
form: Matestack::Ui::Core::Form::Form,
187+
form_checkbox: Matestack::Ui::Core::Form::Checkbox::Checkbox,
186188
form_input: Matestack::Ui::Core::Form::Input::Input,
187189
form_select: Matestack::Ui::Core::Form::Select::Select,
188190
form_submit: Matestack::Ui::Core::Form::Submit::Submit,
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
require_relative "../../../../../support/utils"
2+
require_relative "../../../../../support/test_controller"
3+
require_relative "../support/form_test_controller"
4+
require_relative "../support/model_form_test_controller"
5+
include Utils
6+
7+
describe "Form Component", type: :feature, js: true do
8+
9+
describe "Checkbox" do
10+
11+
before :all do
12+
Rails.application.routes.append do
13+
post '/checkbox_success_form_test/:id', to: 'form_test#success_submit', as: 'checkbox_success_form_test'
14+
post '/checkbox_success_form_test_with_transition/:id', to: 'form_test#success_submit_with_transition', as: 'checkbox_success_form_test_with_transition'
15+
post '/checkbox_failure_form_test_with_transition/:id', to: 'form_test#failure_submit_with_transition', as: 'checkbox_failure_form_test_with_transition'
16+
post '/checkbox_success_form_test_with_redirect/:id', to: 'form_test#success_submit_with_redirect', as: 'checkbox_success_form_test_with_redirect'
17+
post '/checkbox_failure_form_test_with_redirect/:id', to: 'form_test#failure_submit_with_redirect', as: 'checkbox_failure_form_test_with_redirect'
18+
post '/checkbox_failure_form_test/:id', to: 'form_test#failure_submit', as: 'checkbox_failure_form_test'
19+
post '/checkbox_model_form_test', to: 'model_form_test#model_submit', as: 'checkbox_model_form_test'
20+
end
21+
Rails.application.reload_routes!
22+
end
23+
24+
after :all do
25+
Object.send(:remove_const, :TestModel)
26+
load "#{Rails.root}/app/models/test_model.rb"
27+
end
28+
29+
before :each do
30+
allow_any_instance_of(FormTestController).to receive(:expect_params)
31+
end
32+
33+
it "takes an array of options or hash and submits (multiple) selected item(s)" do
34+
class ExamplePage < Matestack::Ui::Page
35+
def response
36+
form form_config, :include do
37+
form_checkbox id: "my-array-test-checkbox", key: :array_input, options: ["Array Option 1","Array Option 2"]
38+
form_checkbox id: "my-hash-test-checkbox", key: :hash_input, options: { "Hash Option 1": "1", "Hash Option 2": "2" }
39+
form_submit do
40+
button text: "Submit me!"
41+
end
42+
end
43+
end
44+
45+
def form_config
46+
return {
47+
for: :my_object,
48+
method: :post,
49+
path: :checkbox_success_form_test_path,
50+
params: {
51+
id: 42
52+
}
53+
}
54+
end
55+
56+
end
57+
58+
visit "/example"
59+
check "Array Option 2"
60+
check "Hash Option 1"
61+
check "Hash Option 2"
62+
expect_any_instance_of(FormTestController).to receive(:expect_params)
63+
.with(hash_including(my_object: { array_input: ["Array Option 2"], hash_input: ["1", "2"] }))
64+
click_button "Submit me!"
65+
end
66+
67+
it "can be initialized by (multiple) item(s)" do
68+
class ExamplePage < Matestack::Ui::Page
69+
def response
70+
form form_config, :include do
71+
form_checkbox id: "my-array-test-checkbox", key: :array_input, options: ["Array Option 1","Array Option 2"], init: ["Array Option 1", "Array Option 2"]
72+
form_checkbox id: "my-hash-test-checkbox", key: :hash_input, options: { "Hash Option 1": "1", "Hash Option 2": "2" }, init: ["2"]
73+
form_submit do
74+
button text: "Submit me!"
75+
end
76+
end
77+
end
78+
79+
def form_config
80+
{
81+
for: :my_object,
82+
method: :post,
83+
path: :checkbox_success_form_test_path,
84+
params: {
85+
id: 42
86+
}
87+
}
88+
end
89+
end
90+
91+
visit "/example"
92+
expect_any_instance_of(FormTestController).to receive(:expect_params)
93+
.with(hash_including(my_object: { array_input: ["Array Option 1", "Array Option 2"], hash_input: ["2"] }))
94+
click_button "Submit me!"
95+
end
96+
97+
it "can be mapped to Active Record Model Column, which is serialized as an Array" do
98+
Object.send(:remove_const, :TestModel)
99+
100+
class TestModel < ApplicationRecord
101+
serialize :some_data, Array
102+
serialize :more_data, Array
103+
validates :more_data, presence: true
104+
105+
def self.array_options
106+
["Array Option 1","Array Option 2"]
107+
end
108+
109+
def self.hash_options
110+
{ "Hash Option 1": "my_first_key", "Hash Option 2": "my_second_key" }
111+
end
112+
end
113+
114+
class ExamplePage < Matestack::Ui::Page
115+
def prepare
116+
@test_model = TestModel.new
117+
@test_model.some_data = ["Array Option 2"]
118+
@test_model.more_data = ["my_second_key"]
119+
end
120+
121+
def response
122+
form form_config, :include do
123+
form_checkbox id: "my-array-test-checkbox", key: :some_data, options: TestModel.array_options
124+
form_checkbox id: "my-hash-test-checkbox", key: :more_data, options: TestModel.hash_options
125+
form_submit do
126+
button text: "Submit me!"
127+
end
128+
end
129+
end
130+
131+
def form_config
132+
return {
133+
for: @test_model,
134+
method: :post,
135+
path: :checkbox_model_form_test_path
136+
}
137+
end
138+
end
139+
140+
visit "/example"
141+
expect(page).to have_field('Array Option 1', checked: false)
142+
expect(page).to have_field('Array Option 2', checked: true)
143+
expect(page).to have_field('Hash Option 1', checked: false)
144+
expect(page).to have_field('Hash Option 2', checked: true)
145+
146+
uncheck "Hash Option 2"
147+
click_button "Submit me!"
148+
expect(page).to have_xpath('//span[@class="errors"]/span[@class="error" and contains(.,"can\'t be blank")]')
149+
150+
check "Hash Option 2"
151+
check "Hash Option 1"
152+
check "Array Option 1"
153+
click_button "Submit me!"
154+
expect(page).not_to have_xpath('//span[@class="errors"]/span[@class="error" and contains(.,"can\'t be blank")]')
155+
#form should now be reset
156+
expect(page).to have_field('Array Option 1', checked: false)
157+
expect(page).to have_field('Array Option 2', checked: true)
158+
expect(page).to have_field('Hash Option 1', checked: false)
159+
expect(page).to have_field('Hash Option 2', checked: true)
160+
expect(TestModel.last.some_data).to eq(["Array Option 2","Array Option 1"])
161+
expect(TestModel.last.more_data).to eq(["my_second_key", "my_first_key"])
162+
end
163+
164+
it 'can work as a simple true false checkbox' do
165+
Object.send(:remove_const, :TestModel)
166+
load "#{Rails.root}/app/models/test_model.rb"
167+
168+
class ExamplePage < Matestack::Ui::Page
169+
def prepare
170+
@test_model = TestModel.new
171+
end
172+
173+
def response
174+
form form_config, :include do
175+
form_checkbox id: "my-array-test-checkbox", key: :status, label: 'Status'
176+
form_submit do
177+
button text: "Submit me!"
178+
end
179+
async show_on: 'success', id: 'async-page' do
180+
plain 'Success'
181+
end
182+
end
183+
end
184+
185+
def form_config
186+
return {
187+
for: @test_model,
188+
method: :post,
189+
path: :checkbox_model_form_test_path,
190+
success: {
191+
emit: :success
192+
}
193+
}
194+
end
195+
end
196+
197+
visit "/example"
198+
expect(page).to have_field('Status', checked: false)
199+
200+
check 'Status'
201+
click_button "Submit me!"
202+
expect(page).to have_content('Success')
203+
expect(TestModel.last.status).to eq(1)
204+
end
205+
206+
end
207+
208+
end

0 commit comments

Comments
 (0)