Skip to content

Commit 7583369

Browse files
committed
Support nullable boolean field. Closes #3145
1 parent e4ae669 commit 7583369

8 files changed

Lines changed: 120 additions & 19 deletions

File tree

app/assets/stylesheets/rails_admin/ra.widgets.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ iframe.wysihtml5-sandbox, .wysihtml5-editor{
2020
display: none;
2121
}
2222

23+
.form-group.boolean_type {
24+
.success.active {
25+
@extend .btn-success
26+
}
27+
.danger.active {
28+
@extend .btn-danger
29+
}
30+
}
31+
2332
.links .inline.list-inline .disabled span {
2433
color: $gray-light;
2534
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1-
.checkbox
2-
%label{ style: 'display: block;' }
3-
= form.send field.view_helper, field.method_name, field.html_attributes.reverse_merge({ value: field.form_value, checked: field.form_value.in?([true, '1']), required: field.required})
1+
- if field.nullable?
2+
.btn-group{'data-toggle': 'buttons'}
3+
- {'1': true, '0': false, '': nil}.each do |text, value|
4+
%label.btn.btn-default{class: [field.css_classes[value], ("active" if field.form_value == value)]}
5+
= form.radio_button field.method_name, text, field.html_attributes.reverse_merge({ checked: field.form_value == value, required: field.required})
6+
= field.labels[value].html_safe
7+
- else
8+
.checkbox
9+
%label{ style: 'display: block;' }
10+
= form.send field.view_helper, field.method_name, field.html_attributes.reverse_merge({ value: field.form_value, checked: field.form_value.in?([true, '1']), required: field.required})

lib/rails_admin/config/fields/types/boolean.rb

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,32 @@ class Boolean < RailsAdmin::Config::Fields::Base
66
# Register field type for the type loader
77
RailsAdmin::Config::Fields::Types.register(self)
88

9+
register_instance_option :labels do
10+
{
11+
true => %(<span class="icon icon-ok"></span>),
12+
false => %(<span class="icon icon-remove"></span>),
13+
nil => %(<span class="icon icon-minus"></span>),
14+
}
15+
end
16+
17+
register_instance_option :css_classes do
18+
{
19+
true => 'success',
20+
false => 'danger',
21+
nil => 'default',
22+
}
23+
end
24+
25+
register_instance_option :nullable? do
26+
properties&.nullable?
27+
end
28+
929
register_instance_option :view_helper do
1030
:check_box
1131
end
1232

1333
register_instance_option :pretty_value do
14-
case value
15-
when false
16-
%(<span class='label label-danger'>&#x2718;</span>)
17-
when true
18-
%(<span class='label label-success'>&#x2713;</span>)
19-
else
20-
%(<span class='label label-default'>&#x2012;</span>)
21-
end.html_safe
34+
%(<span class="label label-#{css_classes[form_value]}">#{labels[form_value]}</span>).html_safe
2235
end
2336

2437
register_instance_option :export_value do
@@ -29,10 +42,21 @@ class Boolean < RailsAdmin::Config::Fields::Base
2942
:form_boolean
3043
end
3144

45+
def form_value
46+
case value
47+
when true, false
48+
value
49+
end
50+
end
51+
3252
# Accessor for field's help text displayed below input field.
3353
def generic_help
3454
''
3555
end
56+
57+
def parse_input(params)
58+
params[name] = params[name].presence if params.key?(name)
59+
end
3660
end
3761
end
3862
end

spec/integration/actions/edit_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,9 @@ class HelpTest < Tableless
833833
it 'is updatable without any error' do
834834
RailsAdmin.config FieldTest do
835835
edit do
836-
field :open
836+
field :open do
837+
nullable false
838+
end
837839
end
838840
end
839841
record = FieldTest.create

spec/integration/authorization/cancancan_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def initialize(user)
135135
it 'POST /admin/player/new with unauthorized attribute value should raise access denied' do
136136
visit new_path(model_name: 'player')
137137
fill_in 'player[name]', with: 'Jackie Robinson'
138-
uncheck 'player[suspended]'
138+
choose name: 'player[suspended]', option: '0'
139139
expect { click_button 'Save' }.to raise_error(CanCan::AccessDenied)
140140
end
141141

@@ -173,7 +173,7 @@ def initialize(user)
173173
it 'PUT /admin/player/new with unauthorized attribute value should raise access denied' do
174174
@player = FactoryBot.create :player
175175
visit edit_path(model_name: 'player', id: @player.id)
176-
check 'player[retired]'
176+
choose name: 'player[retired]', option: '1'
177177
expect { click_button 'Save' }.to raise_error(CanCan::AccessDenied)
178178
end
179179

spec/integration/authorization/pundit_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
it 'POST /admin/player/new with unauthorized attribute value should raise access denied' do
106106
visit new_path(model_name: 'player')
107107
fill_in 'player[name]', with: 'Jackie Robinson'
108-
uncheck 'player[suspended]'
108+
choose name: 'player[suspended]', option: '0'
109109
expect { click_button 'Save' }.to raise_error(Pundit::NotAuthorizedError)
110110
end
111111
end
@@ -118,7 +118,7 @@
118118
it 'PUT /admin/player/new with unauthorized attribute value should raise access denied' do
119119
@player = FactoryBot.create :player
120120
visit edit_path(model_name: 'player', id: @player.id)
121-
check 'player[retired]'
121+
choose name: 'player[retired]', option: '1'
122122
expect { click_button 'Save' }.to raise_error(Pundit::NotAuthorizedError)
123123
end
124124
end
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require 'spec_helper'
2+
3+
RSpec.describe 'Boolean field', type: :request do
4+
subject { page }
5+
let(:field_test) { FactoryBot.create :field_test }
6+
7+
context 'if nullable' do
8+
before do
9+
RailsAdmin.config FieldTest do
10+
field :boolean_field
11+
end
12+
end
13+
14+
it 'shows 3 radio buttons' do
15+
visit new_path(model_name: 'field_test')
16+
is_expected.to have_content 'New Field test'
17+
expect(all('[name="field_test[boolean_field]"]').map { |e| e['value'] }).to eq ['1', '0', '']
18+
end
19+
20+
it 'can be updated' do
21+
visit edit_path(model_name: 'field_test', id: field_test.id)
22+
find('.boolean_type .icon-ok').sibling('input').click
23+
click_button 'Save and edit'
24+
expect(field_test.reload.boolean_field).to be true
25+
find('.boolean_type .icon-remove').sibling('input').click
26+
click_button 'Save and edit'
27+
expect(field_test.reload.boolean_field).to be false
28+
find('.boolean_type .icon-minus').sibling('input').click
29+
click_button 'Save and edit'
30+
expect(field_test.reload.boolean_field).to be nil
31+
end
32+
end
33+
34+
context 'if not nullable' do
35+
before do
36+
RailsAdmin.config FieldTest do
37+
field :boolean_field do
38+
nullable false
39+
end
40+
end
41+
end
42+
43+
it 'shows a checkbox' do
44+
visit new_path(model_name: 'field_test')
45+
is_expected.to have_content 'New Field test'
46+
is_expected.to have_css '[type="checkbox"][name="field_test[boolean_field]"]'
47+
end
48+
49+
it 'can be updated' do
50+
visit edit_path(model_name: 'field_test', id: field_test.id)
51+
find('.boolean_type input').check
52+
click_button 'Save and edit'
53+
expect(field_test.reload.boolean_field).to be true
54+
find('.boolean_type input').uncheck
55+
click_button 'Save and edit'
56+
expect(field_test.reload.boolean_field).to be false
57+
end
58+
end
59+
end

spec/rails_admin/config/fields/types/boolean_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111

1212
describe '#pretty_value' do
1313
{
14-
false => %(<span class='label label-danger'>&#x2718;</span>),
15-
true => %(<span class='label label-success'>&#x2713;</span>),
16-
nil => %(<span class='label label-default'>&#x2012;</span>),
14+
false => %(<span class="label label-danger"><span class="icon icon-remove"></span></span>),
15+
true => %(<span class="label label-success"><span class="icon icon-ok"></span></span>),
16+
nil => %(<span class="label label-default"><span class="icon icon-minus"></span></span>),
1717
}.each do |field_value, expected_result|
1818
context "when field value is '#{field_value.inspect}'" do
1919
let(:test_object) { FieldTest.new(boolean_field: field_value) }

0 commit comments

Comments
 (0)