yummy-guide-generic-administrate 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/Gemfile +4 -0
- data/README.md +243 -0
- data/Rakefile +11 -0
- data/app/assets/javascripts/yummy_guide_administrate/filter_form.js +219 -0
- data/app/assets/javascripts/yummy_guide_administrate/sticky_left_columns.js +141 -0
- data/app/assets/stylesheets/yummy_guide_administrate/components.scss +199 -0
- data/app/controllers/concerns/yummy_guide/administrate/datetime_filter_parameters.rb +40 -0
- data/app/controllers/concerns/yummy_guide/administrate/default_sorting.rb +46 -0
- data/app/dashboards/yummy_guide/administrate/application_dashboard.rb +46 -0
- data/app/fields/yummy_guide/administrate/fields/area/picture_field.rb +106 -0
- data/app/fields/yummy_guide/administrate/fields/json_pretty_field.rb +29 -0
- data/app/fields/yummy_guide/administrate/fields/version_item_field.rb +62 -0
- data/app/fields/yummy_guide/administrate/fields/version_whodunnit_field.rb +65 -0
- data/app/helpers/yummy_guide/administrate/collection_helper.rb +62 -0
- data/app/helpers/yummy_guide/administrate/filter_form_helper.rb +80 -0
- data/app/views/fields/yummy_guide_administrate/area/picture/_form.html.erb +36 -0
- data/app/views/fields/yummy_guide_administrate/area/picture/_index.html.erb +2 -0
- data/app/views/fields/yummy_guide_administrate/area/picture/_show.html.erb +24 -0
- data/app/views/fields/yummy_guide_administrate/json_pretty_field/_index.html.erb +2 -0
- data/app/views/fields/yummy_guide_administrate/json_pretty_field/_show.html.erb +6 -0
- data/app/views/fields/yummy_guide_administrate/version_item_field/_index.html.erb +2 -0
- data/app/views/fields/yummy_guide_administrate/version_item_field/_show.html.erb +6 -0
- data/app/views/fields/yummy_guide_administrate/version_whodunnit_field/_index.html.erb +2 -0
- data/app/views/fields/yummy_guide_administrate/version_whodunnit_field/_show.html.erb +6 -0
- data/app/views/yummy_guide/administrate/administrate/application/_collection.html.erb +62 -0
- data/app/views/yummy_guide/administrate/filter_forms/_checkbox_group.html.erb +30 -0
- data/app/views/yummy_guide/administrate/filter_forms/_datetime_field.html.erb +30 -0
- data/app/views/yummy_guide/administrate/filter_forms/_frame.html.erb +28 -0
- data/lib/generic/administrate.rb +3 -0
- data/lib/yummy_guide/administrate/engine.rb +18 -0
- data/lib/yummy_guide/administrate/version.rb +8 -0
- data/lib/yummy_guide/administrate.rb +13 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/yummy_guide/administrate/application_dashboard_spec.rb +39 -0
- data/spec/yummy_guide/administrate/collection_helper_spec.rb +25 -0
- data/spec/yummy_guide/administrate/datetime_filter_parameters_spec.rb +52 -0
- data/spec/yummy_guide/administrate/fields/json_pretty_field_spec.rb +22 -0
- data/spec/yummy_guide/administrate/fields/version_item_field_spec.rb +30 -0
- data/spec/yummy_guide/administrate/fields/version_whodunnit_field_spec.rb +31 -0
- data/spec/yummy_guide/administrate/filter_form_helper_spec.rb +40 -0
- data/yummy-guide-generic-administrate.gemspec +38 -0
- metadata +156 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YummyGuide
|
|
4
|
+
module Administrate
|
|
5
|
+
module FilterFormHelper
|
|
6
|
+
def yummy_guide_administrate_filter_current_values(raw_values)
|
|
7
|
+
values =
|
|
8
|
+
if raw_values.respond_to?(:to_unsafe_h)
|
|
9
|
+
raw_values.to_unsafe_h
|
|
10
|
+
elsif raw_values.respond_to?(:to_h)
|
|
11
|
+
raw_values.to_h
|
|
12
|
+
else
|
|
13
|
+
raw_values || {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
values.deep_stringify_keys
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def yummy_guide_administrate_filter_hour_options
|
|
20
|
+
@yummy_guide_administrate_filter_hour_options ||= [["", ""]] + (0..23).map { |hour| [format("%02d", hour), format("%02d", hour)] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def yummy_guide_administrate_filter_minute_options
|
|
24
|
+
@yummy_guide_administrate_filter_minute_options ||= [["", ""], ["00", "00"], ["30", "30"], ["59", "59"]]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def yummy_guide_administrate_datetime_filter_parts(value, end_of_day: false)
|
|
28
|
+
raw_value = value.to_s.strip
|
|
29
|
+
|
|
30
|
+
if raw_value.blank?
|
|
31
|
+
{ date: "", hour: "", minute: "" }
|
|
32
|
+
elsif raw_value.match?(/\A\d{4}-\d{2}-\d{2}\z/)
|
|
33
|
+
{
|
|
34
|
+
date: raw_value,
|
|
35
|
+
hour: (end_of_day ? "23" : "00"),
|
|
36
|
+
minute: (end_of_day ? "59" : "00")
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
parsed = Time.zone.parse(raw_value)
|
|
40
|
+
|
|
41
|
+
if parsed.present?
|
|
42
|
+
{
|
|
43
|
+
date: parsed.strftime("%Y-%m-%d"),
|
|
44
|
+
hour: parsed.strftime("%H"),
|
|
45
|
+
minute: parsed.strftime("%M")
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
{ date: "", hour: "", minute: "" }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def yummy_guide_administrate_datetime_filter_combined_value(parts)
|
|
54
|
+
if parts[:date].blank?
|
|
55
|
+
""
|
|
56
|
+
elsif parts[:hour].present? && parts[:minute].present?
|
|
57
|
+
"#{parts[:date]}T#{parts[:hour]}:#{parts[:minute]}"
|
|
58
|
+
else
|
|
59
|
+
parts[:date]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def yummy_guide_administrate_checkbox_group_options(options, label_method: nil, value_method: nil)
|
|
64
|
+
Array(options).map do |option|
|
|
65
|
+
if option.is_a?(Array) && option.size == 2
|
|
66
|
+
option
|
|
67
|
+
elsif label_method.present? || value_method.present?
|
|
68
|
+
[
|
|
69
|
+
option.public_send(label_method || :to_s),
|
|
70
|
+
option.public_send(value_method || :id)
|
|
71
|
+
]
|
|
72
|
+
else
|
|
73
|
+
[option, option]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<div class="field-unit__label">
|
|
2
|
+
<%= f.label field.attribute %>
|
|
3
|
+
</div>
|
|
4
|
+
<div class="field-unit__field">
|
|
5
|
+
<% attachments = field.attachments %>
|
|
6
|
+
<% empty_slots = [field.max_uploads - attachments.size, 0].max %>
|
|
7
|
+
|
|
8
|
+
<div class="yummy-guide-administrate-picture-slots">
|
|
9
|
+
<% attachments.each do |attachment| %>
|
|
10
|
+
<% preview_url = field.preview_url(attachment, view_context: self) %>
|
|
11
|
+
<div class="yummy-guide-administrate-picture-slot">
|
|
12
|
+
<% if preview_url.present? %>
|
|
13
|
+
<div class="yummy-guide-administrate-picture-slot__preview">
|
|
14
|
+
<%= image_tag preview_url, class: "yummy-guide-administrate-picture-list__image" %>
|
|
15
|
+
</div>
|
|
16
|
+
<% else %>
|
|
17
|
+
<div class="yummy-guide-administrate-picture-slot__placeholder"><%= field.attachment_label(attachment) %></div>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<% if (attachment_id = field.attachment_identifier(attachment)).present? %>
|
|
21
|
+
<label class="yummy-guide-administrate-picture-slot__delete">
|
|
22
|
+
<input type="checkbox" name="<%= field.purge_input_name %>" value="<%= attachment_id %>">
|
|
23
|
+
Delete
|
|
24
|
+
</label>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<% empty_slots.times do %>
|
|
30
|
+
<div class="yummy-guide-administrate-picture-slot">
|
|
31
|
+
<%= file_field_tag field.input_name, accept: "image/*", class: "yummy-guide-administrate-picture-slot__input" %>
|
|
32
|
+
</div>
|
|
33
|
+
<% end %>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% if field.attachments.any? %>
|
|
2
|
+
<div class="yummy-guide-administrate-picture-list">
|
|
3
|
+
<% field.attachments.each do |attachment| %>
|
|
4
|
+
<% preview_url = field.preview_url(attachment, view_context: self) %>
|
|
5
|
+
<% link_url = field.attachment_url(attachment, view_context: self) || preview_url %>
|
|
6
|
+
<div class="yummy-guide-administrate-picture-list__item">
|
|
7
|
+
<% if preview_url.present? %>
|
|
8
|
+
<% if link_url.present? %>
|
|
9
|
+
<%= link_to link_url, title: field.attachment_label(attachment) do %>
|
|
10
|
+
<%= image_tag preview_url, class: "yummy-guide-administrate-picture-list__image" %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% else %>
|
|
13
|
+
<%= image_tag preview_url, class: "yummy-guide-administrate-picture-list__image" %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% else %>
|
|
16
|
+
<span><%= field.attachment_label(attachment) %></span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
<% else %>
|
|
22
|
+
No attachment
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<div class="scroll-table">
|
|
2
|
+
<table aria-labelledby="<%= table_title %>" data-fixed-columns-count="<%= yummy_guide_administrate_collection_table_fixed_columns_count(page: page, collection_presenter: collection_presenter) %>">
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
<% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
|
|
6
|
+
<th
|
|
7
|
+
class="cell-label cell-label--<%= attr_type.html_class %> cell-label--<%= collection_presenter.ordered_html_class(attr_name) %> cell-label--<%= "#{collection_presenter.resource_name}_#{attr_name}" %>"
|
|
8
|
+
scope="col"
|
|
9
|
+
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>"
|
|
10
|
+
>
|
|
11
|
+
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
|
12
|
+
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
|
13
|
+
)) do %>
|
|
14
|
+
<%= t(
|
|
15
|
+
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
|
16
|
+
default: resource_class.human_attribute_name(attr_name).titleize
|
|
17
|
+
) %>
|
|
18
|
+
<% if collection_presenter.ordered_by?(attr_name) %>
|
|
19
|
+
<span class="cell-label__sort-indicator cell-label__sort-indicator--<%= collection_presenter.ordered_html_class(attr_name) %>">
|
|
20
|
+
<svg aria-hidden="true">
|
|
21
|
+
<use xlink:href="#icon-up-caret" />
|
|
22
|
+
</svg>
|
|
23
|
+
</span>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% end %>
|
|
26
|
+
</th>
|
|
27
|
+
<% end %>
|
|
28
|
+
<%= render(
|
|
29
|
+
yummy_guide_administrate_collection_actions_partial("collection_header_actions"),
|
|
30
|
+
collection_presenter: collection_presenter,
|
|
31
|
+
page: page,
|
|
32
|
+
resources: resources,
|
|
33
|
+
table_title: "page-title"
|
|
34
|
+
) %>
|
|
35
|
+
</tr>
|
|
36
|
+
</thead>
|
|
37
|
+
|
|
38
|
+
<tbody>
|
|
39
|
+
<% resources.each do |resource| %>
|
|
40
|
+
<tr class="js-table-row">
|
|
41
|
+
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
|
42
|
+
<% cell_href = yummy_guide_administrate_collection_attribute_path(attribute: attribute, resource: resource, namespace: namespace) %>
|
|
43
|
+
<td class="cell-data cell-data--<%= attribute.html_class %>">
|
|
44
|
+
<%= yummy_guide_administrate_collection_wrap(render_field(attribute), href: cell_href) %>
|
|
45
|
+
</td>
|
|
46
|
+
<% end %>
|
|
47
|
+
|
|
48
|
+
<%= render(
|
|
49
|
+
yummy_guide_administrate_collection_actions_partial("collection_item_actions"),
|
|
50
|
+
collection_presenter: collection_presenter,
|
|
51
|
+
collection_field_name: collection_field_name,
|
|
52
|
+
page: page,
|
|
53
|
+
namespace: namespace,
|
|
54
|
+
resource: resource,
|
|
55
|
+
table_title: "page-title"
|
|
56
|
+
) %>
|
|
57
|
+
</tr>
|
|
58
|
+
<% end %>
|
|
59
|
+
</tbody>
|
|
60
|
+
</table>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<% normalized_options = yummy_guide_administrate_checkbox_group_options(options,
|
|
2
|
+
label_method: local_assigns[:label_method],
|
|
3
|
+
value_method: local_assigns[:value_method]) %>
|
|
4
|
+
<% selected = Array(selected_values).map(&:to_s) %>
|
|
5
|
+
<div class="filter-checkbox-group" data-checkbox-group="<%= group_name %>">
|
|
6
|
+
<div class="filter-checkbox-group__options">
|
|
7
|
+
<% normalized_options.each do |label, value| %>
|
|
8
|
+
<% value_string = value.to_s %>
|
|
9
|
+
<label style="display: flex; align-items: center; gap: 6px;">
|
|
10
|
+
<%= check_box_tag "#{form_scope}[#{field_name}][]",
|
|
11
|
+
value,
|
|
12
|
+
selected.include?(value_string),
|
|
13
|
+
id: "#{form_scope}_#{field_name}_#{value_string.parameterize(separator: "_")}",
|
|
14
|
+
data: { checkbox_group_item: group_name } %>
|
|
15
|
+
<span><%= label %></span>
|
|
16
|
+
</label>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="filter-checkbox-group__actions">
|
|
20
|
+
<%= button_tag "Select all",
|
|
21
|
+
type: "button",
|
|
22
|
+
class: "button button--outline-primary filter-checkbox-group__action",
|
|
23
|
+
data: { behavior: "checkbox-group-select-all", target: group_name } %>
|
|
24
|
+
<%= button_tag "Clear all",
|
|
25
|
+
type: "button",
|
|
26
|
+
class: "button button--outline-primary filter-checkbox-group__action",
|
|
27
|
+
data: { behavior: "checkbox-group-clear-all", target: group_name } %>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<% parts = yummy_guide_administrate_datetime_filter_parts(current_value, end_of_day: local_assigns[:end_of_day]) %>
|
|
2
|
+
<div
|
|
3
|
+
class="filter-datetime-group"
|
|
4
|
+
data-datetime-filter="true"
|
|
5
|
+
data-datetime-field="<%= field_name %>"
|
|
6
|
+
<% if local_assigns[:end_target].present? %>
|
|
7
|
+
data-datetime-end-target="<%= end_target %>"
|
|
8
|
+
<% end %>
|
|
9
|
+
>
|
|
10
|
+
<%= hidden_field_tag("#{form_scope}[#{field_name}]",
|
|
11
|
+
yummy_guide_administrate_datetime_filter_combined_value(parts),
|
|
12
|
+
class: css_class,
|
|
13
|
+
data: { datetime_part: "combined" }) %>
|
|
14
|
+
<%= date_field_tag("#{form_scope}[#{field_name}_date]",
|
|
15
|
+
parts[:date],
|
|
16
|
+
class: "#{css_class} filter-datetime-group__date",
|
|
17
|
+
data: { datetime_part: "date" }) %>
|
|
18
|
+
<%= select_tag("#{form_scope}[#{field_name}_hour]",
|
|
19
|
+
options_for_select(yummy_guide_administrate_filter_hour_options, parts[:hour]),
|
|
20
|
+
class: "#{css_class} #{css_class}--hour filter-datetime-group__time",
|
|
21
|
+
data: { datetime_part: "hour" },
|
|
22
|
+
disabled: parts[:date].blank?) %>
|
|
23
|
+
<span class="filter-datetime-group__separator">:</span>
|
|
24
|
+
<%= select_tag("#{form_scope}[#{field_name}_minute]",
|
|
25
|
+
options_for_select(yummy_guide_administrate_filter_minute_options, parts[:minute]),
|
|
26
|
+
class: "#{css_class} #{css_class}--minute filter-datetime-group__time",
|
|
27
|
+
data: { datetime_part: "minute" },
|
|
28
|
+
disabled: parts[:date].blank?) %>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<% current_values = yummy_guide_administrate_filter_current_values(local_assigns[:current_values] || local_assigns[:search_options] || {}) %>
|
|
2
|
+
<% form_css_class = local_assigns[:form_css_class] || "yummy-guide-administrate-filter-form" %>
|
|
3
|
+
<% form_attributes = { class: form_css_class, data: { yummy_guide_administrate_filter_form: true } } %>
|
|
4
|
+
<% form_attributes[:id] = local_assigns[:form_id] if local_assigns[:form_id].present? %>
|
|
5
|
+
<% if local_assigns[:form_data].present? %>
|
|
6
|
+
<% form_attributes[:data].merge!(local_assigns[:form_data]) %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= form_with url: path, scope: form, method: method, html: form_attributes do |f| %>
|
|
10
|
+
<% Array(local_assigns[:hidden_fields]).each do |(key, value)| %>
|
|
11
|
+
<%= hidden_field_tag "#{form}[#{key}]", value %>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<h2 class="<%= "#{form_css_class}__title" %>"><%= local_assigns[:title] || "Filter Options" %></h2>
|
|
15
|
+
<div class="<%= "#{form_css_class}__body" %>">
|
|
16
|
+
<table class="filter_table">
|
|
17
|
+
<%= yield f, current_values %>
|
|
18
|
+
</table>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="<%= "#{form_css_class}__actions" %>">
|
|
21
|
+
<%= button_tag local_assigns[:clear_label] || "Clear",
|
|
22
|
+
type: "button",
|
|
23
|
+
class: "button button--outline-primary js-filter-form-clear",
|
|
24
|
+
data: { behavior: "filter-form-clear" } %>
|
|
25
|
+
<%= f.submit local_assigns[:submit_label] || "Filter", class: "submit_filter" %>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YummyGuide
|
|
4
|
+
module Administrate
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
initializer "yummy_guide.administrate.assets" do |app|
|
|
7
|
+
next unless app.config.respond_to?(:assets)
|
|
8
|
+
|
|
9
|
+
app.config.assets.precompile += %w[
|
|
10
|
+
yummy_guide_administrate/components.css
|
|
11
|
+
yummy_guide_administrate/filter_form.js
|
|
12
|
+
yummy_guide_administrate/sticky_left_columns.js
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "rspec"
|
|
5
|
+
require "active_support"
|
|
6
|
+
require "active_support/core_ext"
|
|
7
|
+
require "active_model"
|
|
8
|
+
require "action_controller"
|
|
9
|
+
require "action_view"
|
|
10
|
+
|
|
11
|
+
require "generic/administrate"
|
|
12
|
+
Dir[File.expand_path("../app/**/*.rb", __dir__)].sort.each { |path| require path }
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |config|
|
|
15
|
+
config.disable_monkey_patching!
|
|
16
|
+
config.expect_with :rspec do |expectations|
|
|
17
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
config.mock_with :rspec do |mocks|
|
|
21
|
+
mocks.verify_partial_doubles = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
25
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::ApplicationDashboard do
|
|
6
|
+
describe ".index_fixed_columns_count" do
|
|
7
|
+
it "returns the subclass constant when defined" do
|
|
8
|
+
subclass = Class.new(described_class)
|
|
9
|
+
subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 3)
|
|
10
|
+
|
|
11
|
+
expect(subclass.index_fixed_columns_count).to eq(3)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "falls back to the superclass value" do
|
|
15
|
+
subclass = Class.new(described_class)
|
|
16
|
+
|
|
17
|
+
expect(subclass.index_fixed_columns_count).to eq(1)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe ".collection_attribute_sortable?" do
|
|
22
|
+
it "uses collection attributes when explicit sortable attributes are absent" do
|
|
23
|
+
subclass = Class.new(described_class)
|
|
24
|
+
subclass.const_set(:COLLECTION_ATTRIBUTES, %i[id created_at])
|
|
25
|
+
|
|
26
|
+
expect(subclass.collection_attribute_sortable?(:created_at)).to be(true)
|
|
27
|
+
expect(subclass.collection_attribute_sortable?(:name)).to be(false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "prefers explicit sortable attributes when present" do
|
|
31
|
+
subclass = Class.new(described_class)
|
|
32
|
+
subclass.const_set(:COLLECTION_ATTRIBUTES, %i[id created_at])
|
|
33
|
+
subclass.const_set(:COLLECTION_SORTABLE_ATTRIBUTES, %i[name])
|
|
34
|
+
|
|
35
|
+
expect(subclass.collection_attribute_sortable?(:name)).to be(true)
|
|
36
|
+
expect(subclass.collection_attribute_sortable?(:created_at)).to be(false)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::CollectionHelper do
|
|
6
|
+
subject(:helper_host) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include YummyGuide::Administrate::CollectionHelper
|
|
9
|
+
end.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "caps the fixed column count by the number of visible attributes" do
|
|
13
|
+
dashboard_class = Class.new do
|
|
14
|
+
def self.index_fixed_columns_count
|
|
15
|
+
4
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
page = Object.new
|
|
20
|
+
page.instance_variable_set(:@dashboard, dashboard_class.new)
|
|
21
|
+
collection_presenter = Struct.new(:attribute_types).new({ id: :integer, name: :string })
|
|
22
|
+
|
|
23
|
+
expect(helper_host.yummy_guide_administrate_collection_table_fixed_columns_count(page: page, collection_presenter: collection_presenter)).to eq(2)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::DatetimeFilterParameters do
|
|
6
|
+
subject(:host) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include YummyGuide::Administrate::DatetimeFilterParameters
|
|
9
|
+
|
|
10
|
+
def normalize(filters, keys:)
|
|
11
|
+
normalize_datetime_filter_params(filters, keys: keys)
|
|
12
|
+
end
|
|
13
|
+
end.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "combines date and time parts into a datetime-local string" do
|
|
17
|
+
filters = ActionController::Parameters.new(
|
|
18
|
+
start_date_date: "2026-05-07",
|
|
19
|
+
start_date_hour: "09",
|
|
20
|
+
start_date_minute: "30"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
normalized = host.normalize(filters, keys: [:start_date])
|
|
24
|
+
|
|
25
|
+
expect(normalized[:start_date]).to eq("2026-05-07T09:30")
|
|
26
|
+
expect(normalized).not_to have_key(:start_date_date)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "returns just the date when time parts are blank" do
|
|
30
|
+
filters = ActionController::Parameters.new(
|
|
31
|
+
end_date_date: "2026-05-07",
|
|
32
|
+
end_date_hour: "",
|
|
33
|
+
end_date_minute: ""
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
normalized = host.normalize(filters, keys: [:end_date])
|
|
37
|
+
|
|
38
|
+
expect(normalized[:end_date]).to eq("2026-05-07")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "sets nil when the date is blank" do
|
|
42
|
+
filters = ActionController::Parameters.new(
|
|
43
|
+
start_date_date: "",
|
|
44
|
+
start_date_hour: "09",
|
|
45
|
+
start_date_minute: "30"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
normalized = host.normalize(filters, keys: [:start_date])
|
|
49
|
+
|
|
50
|
+
expect(normalized[:start_date]).to be_nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::Fields::JsonPrettyField do
|
|
6
|
+
def build_field(data)
|
|
7
|
+
described_class.new(:payload, data, :show, resource: Object.new)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "pretty prints JSON strings" do
|
|
11
|
+
field = build_field('{"a":1}')
|
|
12
|
+
|
|
13
|
+
expect(field.to_s).to include("\"a\": 1")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "falls back to the raw string when parsing fails" do
|
|
17
|
+
field = build_field("not-json")
|
|
18
|
+
|
|
19
|
+
expect(field.to_s).to eq("not-json")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::Fields::VersionItemField do
|
|
6
|
+
before do
|
|
7
|
+
stub_const("SpecVersionItemRecord", Class.new do
|
|
8
|
+
attr_reader :id
|
|
9
|
+
|
|
10
|
+
def initialize(id)
|
|
11
|
+
@id = id
|
|
12
|
+
end
|
|
13
|
+
end)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:record) { SpecVersionItemRecord.new(12) }
|
|
17
|
+
let(:resource) { Struct.new(:item_type, :item_id, :reify).new("Widget", 12, nil) }
|
|
18
|
+
|
|
19
|
+
def build_field(data = record)
|
|
20
|
+
described_class.new(:item, data, :show, resource: resource, namespace: :admin)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "builds a readable label" do
|
|
24
|
+
expect(build_field.label).to eq("SpecVersionItemRecord #12")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "uses a fallback label when the target resource is unavailable" do
|
|
28
|
+
expect(build_field(nil).label).to eq("Widget #12")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::Fields::VersionWhodunnitField do
|
|
6
|
+
let(:user_class) do
|
|
7
|
+
Class.new do
|
|
8
|
+
def self.find_by(id:)
|
|
9
|
+
return unless id == 5
|
|
10
|
+
|
|
11
|
+
Struct.new(:id, :full_name) do
|
|
12
|
+
def self.model_name
|
|
13
|
+
ActiveModel::Name.new(self, nil, "User")
|
|
14
|
+
end
|
|
15
|
+
end.new(5, "Test User")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def build_field(data)
|
|
21
|
+
described_class.new(:whodunnit, data, :show, resource: Object.new, user_class: user_class)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "uses the resolved user label when found" do
|
|
25
|
+
expect(build_field(5).label).to eq("Test User")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "falls back to a generic label when the user is missing" do
|
|
29
|
+
expect(build_field(99).label).to eq("User #99")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::FilterFormHelper do
|
|
6
|
+
subject(:helper_host) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include YummyGuide::Administrate::FilterFormHelper
|
|
9
|
+
end.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
Time.zone = "Tokyo"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "normalizes current values to string keys" do
|
|
17
|
+
values = ActionController::Parameters.new(start_date: "2026-05-07")
|
|
18
|
+
|
|
19
|
+
expect(helper_host.yummy_guide_administrate_filter_current_values(values)).to eq("start_date" => "2026-05-07")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "builds end-of-day defaults for date-only inputs" do
|
|
23
|
+
parts = helper_host.yummy_guide_administrate_datetime_filter_parts("2026-05-07", end_of_day: true)
|
|
24
|
+
|
|
25
|
+
expect(parts).to eq(date: "2026-05-07", hour: "23", minute: "59")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "combines parsed datetime parts back into a single value" do
|
|
29
|
+
combined = helper_host.yummy_guide_administrate_datetime_filter_combined_value(date: "2026-05-07", hour: "08", minute: "00")
|
|
30
|
+
|
|
31
|
+
expect(combined).to eq("2026-05-07T08:00")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "maps checkbox group objects through label and value methods" do
|
|
35
|
+
option = Struct.new(:id, :name).new(10, "Japanese")
|
|
36
|
+
|
|
37
|
+
expect(helper_host.yummy_guide_administrate_checkbox_group_options([option], label_method: :name, value_method: :id)).to eq([["Japanese", 10]])
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|