Skip to content

Commit

Permalink
Add ability to limit filter operators
Browse files Browse the repository at this point in the history
Useful for tables with a large amount of records.
  • Loading branch information
mshibuya committed Sep 4, 2022
1 parent fdb9abb commit be9a75e
Show file tree
Hide file tree
Showing 15 changed files with 263 additions and 254 deletions.
20 changes: 5 additions & 15 deletions app/helpers/rails_admin/main_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,18 @@ def ordered_filters
def ordered_filter_options
if ordered_filters
@ordered_filter_options ||= ordered_filters.map do |duplet|
options = {index: duplet[0]}
filter_for_field = duplet[1]
filter_name = filter_for_field.keys.first
filter_hash = filter_for_field.values.first
unless (field = filterable_fields.find { |f| f.name == filter_name.to_sym })
raise "#{filter_name} is not currently filterable; filterable fields are #{filterable_fields.map(&:name).join(', ')}"
end

case field.type
when :enum
options[:select_options] = options_for_select(field.with(object: @abstract_model.model.new).enum, filter_hash['v'])
when :date, :datetime, :time
options[:datetimepicker_options] = field.datepicker_options
end
options[:label] = field.label
options[:name] = field.name
options[:type] = field.type
options[:value] = filter_hash['v']
options[:label] = field.label
options[:operator] = filter_hash['o'] || field.default_filter_operator
options[:required] = field.required
options
field.filter_options.merge(
index: duplet[0],
operator: filter_hash['o'] || field.default_filter_operator,
value: filter_hash['v'],
)
end
end
end
Expand Down
17 changes: 1 addition & 16 deletions app/views/rails_admin/main/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,11 @@
</a>
<ul class="dropdown-menu dropdown-menu-end" id="filters">
<% filterable_fields.each do |field| %>
<%
field_options = case field.type
when :enum
options_for_select(field.with(object: @abstract_model.model.new).enum)
else
''
end
%>
<li>
<a
href="#"
class="dropdown-item"
data-field-label="<%= field.label %>"
data-field-name="<%= field.name %>"
data-field-operator="<%= field.default_filter_operator %>"
data-field-options="<%= "#{field_options}" %>"
data-field-required="<%= field.required.to_s %>"
data-field-type="<%= field.type %>"
data-field-value=""
data-field-datetimepicker-options="<%= field.try(:datepicker_options).try(:to_json) %>"
data-options="<%= field.with(view: self).filter_options.to_json %>"
>
<%= field.label %>
</a>
Expand Down
4 changes: 2 additions & 2 deletions config/locales/rails_admin.en.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
en:
admin:
js:
true: True
false: False
true: "True"
false: "False"
is_present: Is present
is_blank: Is blank
date: Date ...
Expand Down
26 changes: 20 additions & 6 deletions lib/rails_admin/config/fields/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ def sort_column
!virtual? || children_fields.first || false
end

register_instance_option :search_operator do
RailsAdmin::Config.default_search_operator
end

register_instance_option :queryable? do
!virtual?
end
Expand All @@ -86,8 +90,22 @@ def sort_column
!!searchable
end

register_instance_option :search_operator do
@search_operator ||= RailsAdmin::Config.default_search_operator
register_instance_option :filter_operators do
[]
end

register_instance_option :default_filter_operator do
nil
end

def filter_options
{
label: label,
name: name,
operator: default_filter_operator,
operators: filter_operators,
type: type,
}
end

# serials and dates are reversed in list, which is more natural (last modified items first).
Expand Down Expand Up @@ -242,10 +260,6 @@ def sort_column
[]
end

register_instance_option :default_filter_operator do
nil
end

register_instance_option :eager_load do
false
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_admin/config/fields/types/belongs_to_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ module Types
class BelongsToAssociation < RailsAdmin::Config::Fields::Association
RailsAdmin::Config::Fields::Types.register(self)

register_instance_option :filter_operators do
%w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
end

register_instance_option :formatted_value do
(o = value) && o.send(associated_model_config.object_label_method)
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_admin/config/fields/types/boolean.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class Boolean < RailsAdmin::Config::Fields::Base
}
end

register_instance_option :filter_operators do
%w[_discard true false] + (required? ? [] : %w[_separator _present _blank])
end

register_instance_option :nullable? do
properties&.nullable?
end
Expand Down
10 changes: 10 additions & 0 deletions lib/rails_admin/config/fields/types/datetime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ def parse_input(params)
params[name] = parse_value(params[name]) if params[name]
end

register_instance_option :filter_operators do
%w[default between today yesterday this_week last_week] + (required? ? [] : %w[_separator _not_null _null])
end

def filter_options
super.merge(
datetimepicker_options: datepicker_options,
)
end

register_instance_option :date_format do
:long
end
Expand Down
15 changes: 13 additions & 2 deletions lib/rails_admin/config/fields/types/enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,27 @@ module Types
class Enum < RailsAdmin::Config::Fields::Base
RailsAdmin::Config::Fields::Types.register(self)

register_instance_option :filter_operators do
%w[_discard] +
enum.map do |label, value|
{label: label, value: value || label}
end + (required? ? [] : %w[_separator _present _blank])
end

register_instance_option :partial do
:form_enumeration
end

register_instance_option :enum_method do
@enum_method ||= bindings[:object].class.respond_to?("#{name}_enum") || bindings[:object].respond_to?("#{name}_enum") ? "#{name}_enum" : name
@enum_method ||= bindings[:object].class.respond_to?("#{name}_enum") || (bindings[:object] || abstract_model.model.new).respond_to?("#{name}_enum") ? "#{name}_enum" : name
end

register_instance_option :enum do
bindings[:object].class.respond_to?(enum_method) ? bindings[:object].class.send(enum_method) : bindings[:object].send(enum_method)
if abstract_model.model.respond_to?(enum_method)
abstract_model.model.send(enum_method)
else
(bindings[:object] || abstract_model.model.new).send(enum_method)
end
end

register_instance_option :pretty_value do
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_admin/config/fields/types/has_one_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class HasOneAssociation < RailsAdmin::Config::Fields::Association
# Register field type for the type loader
RailsAdmin::Config::Fields::Types.register(self)

register_instance_option :filter_operators do
%w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
end

register_instance_option :partial do
nested_form ? :form_nested_one : :form_filtering_select
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_admin/config/fields/types/numeric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Numeric < RailsAdmin::Config::Fields::Base
# Register field type for the type loader
RailsAdmin::Config::Fields::Types.register(self)

register_instance_option :filter_operators do
%w[default between] + (required? ? [] : %w[_separator _not_null _null])
end

register_instance_option :view_helper do
:number_field
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_admin/config/fields/types/string_like.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ module Config
module Fields
module Types
class StringLike < RailsAdmin::Config::Fields::Base
register_instance_option :filter_operators do
%w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
end

register_instance_option :treat_empty_as_nil? do
properties.try(:nullable?)
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_admin/config/fields/types/time.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ def parse_value(value)
abstract_model.model.type_for_attribute(name.to_s).serialize(super)&.change(year: 2000, month: 1, day: 1)
end

register_instance_option :filter_operators do
%w[default between] + (required? ? [] : %w[_separator _not_null _null])
end

register_instance_option :datepicker_options do
{
allowInput: true,
Expand Down
4 changes: 2 additions & 2 deletions spec/integration/actions/index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@
type: 'string',
value: '',
operator: 'is',
required: true,
operators: %w[_discard like not_like is starts_with ends_with],
},
{
index: 2,
Expand All @@ -333,7 +333,7 @@
type: 'belongs_to_association',
value: '',
operator: nil,
required: false,
operators: %w[_discard like not_like is starts_with ends_with _separator _present _blank],
},
]
end
Expand Down
24 changes: 22 additions & 2 deletions spec/integration/widgets/filter_box_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@
end
end

it 'supports limiting filter operators' do
RailsAdmin.config Player do
list do
field :name do
filter_operators %w[is starts_with _present]
end
end
end

visit index_path(model_name: 'player')
is_expected.to have_no_css('#filters_box .filter')
click_link 'Add filter'
click_link 'Name'

within(:select, name: /f\[name\]\[\d+\]\[o\]/) do
expect(page.all('option').map(&:value)).to eq %w[is starts_with _present]
end
end

describe 'for boolean field' do
before do
RailsAdmin.config FieldTest do
Expand Down Expand Up @@ -133,9 +152,10 @@
visit index_path(model_name: 'team')
click_link 'Add filter'
click_link 'Color'
expect(all('.select-single option').map(&:text)).to include 'white', 'black', 'red', 'green', 'blu<e>é'
expect(all('#filters_box option').map(&:text)).to include 'white', 'black', 'red', 'green', 'blu<e>é'
find('.filter .switch-select .fa-plus').click
expect(all('.select-multiple option').map(&:text)).to include 'white', 'black', 'red', 'green', 'blu<e>é'
expect(find('#filters_box select')['multiple']).to be true
expect(find('#filters_box select')['name']).to match(/\[\]$/)
end
end

Expand Down
Loading

0 comments on commit be9a75e

Please sign in to comment.