aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZog2018-01-15 16:35:08 +0100
committerZog2018-01-25 17:17:58 +0100
commitaefa3a507239b57ebdfe9110f3c8c789da6bce26 (patch)
tree393522417aec5dc006680e8e5a2f14386adf45ae
parentfef78d49bbc89aa41fe043b4035585605e14d389 (diff)
downloadchouette-core-aefa3a507239b57ebdfe9110f3c8c789da6bce26.tar.bz2
Refs #5586 @2h; Better implementation of groups
Used it in Lines#index and Lines#show, probably broke everything else
-rw-r--r--app/assets/stylesheets/components/_buttons.sass41
-rw-r--r--app/decorators/line_decorator.rb18
-rw-r--r--app/helpers/table_builder_helper.rb13
-rw-r--r--app/views/layouts/navigation/_page_header.html.slim10
-rw-r--r--app/views/lines/show.html.slim7
-rw-r--r--lib/af83/decorator.rb138
-rw-r--r--spec/lib/af83/decorator/decorator_spec.rb194
7 files changed, 357 insertions, 64 deletions
diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass
index a649a07ef..e70c89733 100644
--- a/app/assets/stylesheets/components/_buttons.sass
+++ b/app/assets/stylesheets/components/_buttons.sass
@@ -142,16 +142,25 @@ table, .table
margin: 0
border-radius: 0
box-shadow: 0 0 3px rgba($darkgrey, 0.25)
-
- > li > a, > li > button
- padding: 5px 15px
-
- > li.delete-action
- > a, > button
+ > ul
+ padding: 0
+ margin: 0
+ > li > a, > li > button
+ padding: 5px 15px
+ white-space: nowrap
+ color: black
+ padding: 5px 15px
+ font-weight: normal
+ line-height: 1.42857
display: block
+ font-size: 14px
+ &:hover
+ text-decoration: none
+ color: #262626
+ background-color: whitesmoke
+ &:not(:first-child)
position: relative
margin-top: 11px
-
&:before
content: ''
display: block
@@ -162,14 +171,18 @@ table, .table
height: 1px
background-color: $grey
- .fa:first-child
- margin-right: 0.5em
-
- & + li.delete-action
+ > li.delete-action
> a, > button
- margin-top: 0
- &:before
- display: none
+ display: block
+ position: relative
+ .fa:first-child
+ margin-right: 0.5em
+
+ & + li.delete-action
+ > a, > button
+ margin-top: 0
+ &:before
+ display: none
&.table-2entries .t2e-item
diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb
index 6c6df1a95..9325c6a8b 100644
--- a/app/decorators/line_decorator.rb
+++ b/app/decorators/line_decorator.rb
@@ -27,21 +27,21 @@ class LineDecorator < AF83::Decorator
can_edit_line = ->(){ h.policy(Chouette::Line).create? && context[:line_referential].organisations.include?(context[:current_organisation]) }
with_condition can_edit_line do
- action_link on: :index do |l|
- l.content { h.t('lines.actions.new') }
- l.href { h.new_line_referential_line_path(context[:line_referential]) }
- end
-
- action_link on: %i(index show), primary: :show do |l|
+ action_link on: %i(index show), primary: :show, secondary: :index do |l|
l.content { h.t('lines.actions.edit') }
l.href { h.edit_line_referential_line_path(context[:line_referential], object.id) }
end
+
+ action_link on: :index, secondary: :index do |l|
+ l.content { h.t('lines.actions.new') }
+ l.href { h.new_line_referential_line_path(context[:line_referential]) }
+ end
end
### the option :policy will automatically check for the corresponding method
### on the object's policy
- action_link policy: :deactivate, secondary: true do |l|
+ action_link policy: :deactivate, secondary: :show, footer: :index do |l|
l.content { h.deactivate_link_content('lines.actions.deactivate') }
l.href { h.deactivate_line_referential_line_path(context[:line_referential], object) }
l.method :put
@@ -49,7 +49,7 @@ class LineDecorator < AF83::Decorator
l.extra_class "delete-action"
end
- action_link policy: :activate, secondary: true do |l|
+ action_link policy: :activate, secondary: :show, footer: :index do |l|
l.content { h.activate_link_content('lines.actions.activate') }
l.href { h.activate_line_referential_line_path(context[:line_referential], object) }
l.method :put
@@ -57,7 +57,7 @@ class LineDecorator < AF83::Decorator
l.extra_class "delete-action"
end
- action_link policy: :destroy do |l|
+ action_link policy: :destroy, footer: true do |l|
l.content { h.destroy_link_content('lines.actions.destroy') }
l.href { h.line_referential_line_path(context[:line_referential], object) }
l.method :delete
diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb
index e66e9c942..bece3bb2a 100644
--- a/app/helpers/table_builder_helper.rb
+++ b/app/helpers/table_builder_helper.rb
@@ -306,12 +306,13 @@ module TableBuilderHelper
content_tag :span, '', class: 'fa fa-cog'
end
- menu = content_tag :ul, class: 'dropdown-menu' do
- (
- CustomLinks.new(item, pundit_user, links, referential).links +
- item.action_links
- ).map do |link|
- gear_menu_link(link)
+ menu = content_tag :div, class: 'dropdown-menu' do
+ item.action_links(params[:action]).grouped_by(:primary, :secondary, :footer).map do |group, _links|
+ if _links.any?
+ content_tag :ul, class: group do
+ _links.map{|link| gear_menu_link(link)}.join.html_safe
+ end
+ end
end.join.html_safe
end
diff --git a/app/views/layouts/navigation/_page_header.html.slim b/app/views/layouts/navigation/_page_header.html.slim
index 90fd7d855..076c76de4 100644
--- a/app/views/layouts/navigation/_page_header.html.slim
+++ b/app/views/layouts/navigation/_page_header.html.slim
@@ -19,5 +19,15 @@ div.page_header
- if content_for? :page_header_actions
= yield :page_header_actions
+ - action_links = resource.action_links(params[:action]) rescue nil
+ - if action_links&.primary&.any? || action_links&.secondary&.any?
+ .row
+ .col-lg-12.text-right.mb-sm
+ - action_links.primary.each do |link|
+ = link.to_html do |l|
+ - l.class "btn btn-primary #{l.disabled ? "disabled" : ""} sticky-action"
+ - action_links.secondary.each do |link|
+ = link.to_html do |l|
+ - l.class "btn btn-primary #{l.disabled ? "disabled" : ""}"
- if content_for? :page_header_content
= yield :page_header_content
diff --git a/app/views/lines/show.html.slim b/app/views/lines/show.html.slim
index 1c0578b0b..34b907bdd 100644
--- a/app/views/lines/show.html.slim
+++ b/app/views/lines/show.html.slim
@@ -1,11 +1,4 @@
- breadcrumb :line, @line
-- content_for :page_header_content do
- .row
- .col-lg-12.text-right.mb-sm
- - @line.action_links(:show, :primary).each do |link|
- = link.to_html do |l|
- - l.class "btn btn-primary #{l.disabled ? "disabled" : ""}"
-
- page_header_content_for @line
.page_content
diff --git a/lib/af83/decorator.rb b/lib/af83/decorator.rb
index 50889ede7..6c7dff59d 100644
--- a/lib/af83/decorator.rb
+++ b/lib/af83/decorator.rb
@@ -24,16 +24,19 @@ class AF83::Decorator < Draper::Decorator
(@_action_links || []).flatten.compact.select{|l| l.for_action?(action)}
end
- def action_links action=:index, scope=nil
- return send("#{scope}_links", action) if scope.present?
-
- self.class.action_links(action)\
- .map{|l| l.bind_to_context(self)}\
- .select{|l| l.enabled?}
+ def action_links action=:index, opts={}
+ links = ActionLinks.new links: self.class.action_links(action), context: self, action: action
+ group = opts[:group]
+ links = links.for_group opts[:group]
+ links
end
def primary_links action=:index
- action_links(action).select{|l| l.primary_for_action?(action)}
+ action_links(action, group: :primary)
+ end
+
+ def secondary_links action=:index
+ action_links(action, group: :secondary)
end
def check_policy policy
@@ -45,7 +48,7 @@ class AF83::Decorator < Draper::Decorator
private
def self.parse_options args
options = {}
- %i(weight primary secondary on action actions policy if).each do |k|
+ %i(weight primary secondary footer on action actions policy if groups group).each do |k|
options[k] = args.delete(k) if args.has_key?(k)
end
link_options = args.dup
@@ -56,14 +59,103 @@ class AF83::Decorator < Draper::Decorator
actions = [actions] unless actions.is_a?(Array)
link_options[:_actions] = actions.compact
- link_options[:_primary] = options.delete :primary
- link_options[:_secondary] = options.delete :secondary
+ link_options[:_groups] = options.delete(:groups)
+ link_options[:_groups] ||= {}
+ if single_group = options.delete(:group)
+ if(single_group.is_a?(Symbol) || single_group.is_a?(String))
+ link_options[:_groups][single_group] = true
+ else
+ link_options[:_groups].update single_group
+ end
+ end
+ link_options[:_groups][:primary] ||= options.delete :primary
+ link_options[:_groups][:secondary] ||= options.delete :secondary
+ link_options[:_groups][:footer] ||= options.delete :footer
link_options[:_if] = options.delete(:if)
link_options[:_policy] = options.delete(:policy)
[options, link_options]
end
+ class ActionLinks
+ attr_reader :options
+
+ def initialize opts
+ @options = opts.deep_dup
+ end
+
+ def for_group group
+ returning_a_copy do
+ @options[:groups] = [group] if group.present?
+ end
+ end
+
+ def for_groups groups
+ returning_a_copy do
+ @options[:groups] = groups if groups.present?
+ end
+ end
+
+ def primary
+ for_group :primary
+ end
+
+ def secondary
+ for_group :secondary
+ end
+
+ def resolve
+ out = @options[:links].map{|l| l.bind_to_context(@options[:context])}.select{|l| l.enabled?}
+ if @options[:groups].present?
+ out = out.select do |l|
+ @options[:groups].any? do |g|
+ l.in_group_for_action?(@options[:action], g)
+ end
+ end
+ end
+ out
+ end
+
+ def grouped_by *groups
+ add_footer = groups.include?(:footer)
+ groups -= [:footer]
+ out = HashWithIndifferentAccess[*groups.map{|g| [g, []]}.flatten(1)]
+ out[:other] = []
+ if add_footer
+ out[:footer] = []
+ groups << :footer
+ end
+
+ each do |l|
+ found = false
+ groups.each do |g|
+ if l.in_group_for_action?(@options[:action], g)
+ out[g] << l
+ found = true
+ next
+ end
+ end
+ out[:other] << l unless found
+ end
+ out
+ end
+
+ alias_method :to_ary, :resolve
+
+ %w(each map size first last any?).each do |meth|
+ define_method meth do |*args, &block|
+ resolve.send meth, *args, &block
+ end
+ end
+
+ private
+ def returning_a_copy &block
+ out = ActionLinks.new options
+ out.instance_eval &block
+ out
+ end
+ end
+
class Link
REQUIRED_ATTRIBUTES = %i(href content)
@@ -115,21 +207,19 @@ class AF83::Decorator < Draper::Decorator
enabled_actions.empty? || enabled_actions.include?(action.to_s)
end
- %i(primary secondary).each do |k|
- define_method "#{k}_for_action?" do |action|
- vals = send("#{k}_actions")
- if vals.is_a?(Array)
- return vals.include?(action.to_s)
- elsif vals.is_a?(String) || vals.is_a?(Symbol)
- vals.to_s == action.to_s
- else
- !!vals
- end
- end
+ def actions_for_group group
+ val = @options[:_groups][group]
+ val.is_a?(Array) ? val.map(&:to_s) : val
+ end
- define_method "#{k}_actions" do
- val = @options[:"_#{k}"]
- val.is_a?(Array) ? val.map(&:to_s) : val
+ def in_group_for_action? action, group
+ vals = actions_for_group(group)
+ if vals.is_a?(Array)
+ return vals.include?(action.to_s)
+ elsif vals.is_a?(String) || vals.is_a?(Symbol)
+ vals.to_s == action.to_s
+ else
+ !!vals
end
end
diff --git a/spec/lib/af83/decorator/decorator_spec.rb b/spec/lib/af83/decorator/decorator_spec.rb
index b70e0d7c1..04dc9df09 100644
--- a/spec/lib/af83/decorator/decorator_spec.rb
+++ b/spec/lib/af83/decorator/decorator_spec.rb
@@ -13,8 +13,8 @@ RSpec.describe AF83::Decorator, type: :decorator do
link_options.each do |k, v|
expect(_link_options[k]).to eq v
end
- expect(_link_options[:_primary]).to eq true
- expect(_link_options[:_secondary]).to eq %i(index show)
+ expect(_link_options[:_groups][:primary]).to eq true
+ expect(_link_options[:_groups][:secondary]).to eq %i(index show)
expect(_link_options[:_policy]).to eq :blublu
end
end
@@ -424,6 +424,24 @@ RSpec.describe AF83::Decorator, type: :decorator do
end
end
+ describe '#primary' do
+ let(:decorator) do
+ Class.new(AF83::Decorator)
+ end
+
+ let(:decorated) do
+ obj = create :line
+ decorator.decorate(obj)
+ end
+
+ it "should return a new object everytime" do
+ actions = decorated.action_links
+ primary = actions.primary
+ expect(actions.options[:groups]).to be_nil
+ expect(primary.options[:groups]).to_not be_nil
+ end
+ end
+
describe(:primary_links) do
let(:decorated) do
obj = create :line
@@ -478,7 +496,7 @@ RSpec.describe AF83::Decorator, type: :decorator do
let(:primary){ %i(show edit) }
it "should return the link" do
- links = decorated.action_links(:show, :primary)
+ links = decorated.action_links(:show, group: :primary)
expect(links.size).to eq 1
end
end
@@ -487,7 +505,7 @@ RSpec.describe AF83::Decorator, type: :decorator do
let(:primary){ %i(index edit) }
it "should not return the link" do
- links = decorated.action_links(:show, :primary)
+ links = decorated.action_links(:show, group: :primary)
expect(links.size).to eq 0
end
end
@@ -511,4 +529,172 @@ RSpec.describe AF83::Decorator, type: :decorator do
end
end
end
+
+ describe("in a group") do
+ let(:decorated) do
+ obj = create :line
+ decorator.decorate(obj)
+ end
+
+ context "without links" do
+ let(:decorator) do
+ Class.new(AF83::Decorator)
+ end
+
+ it "should return no link" do
+ links = decorated.action_links
+ expect(links.size).to eq 0
+ end
+ end
+
+
+ context "with a single link" do
+ let(:link_options) do
+ {
+ href: "/foo/bar/baz",
+ content: "Blublu",
+ groups: {foo: group}
+ }
+ end
+
+ let(:decorator) do
+ klass = Class.new(AF83::Decorator)
+ klass.action_link link_options
+ klass
+ end
+
+ context "always in" do
+ let(:group){ true }
+
+ it "should return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 1
+ end
+
+ context "define with group" do
+ let(:link_options) do
+ {
+ href: "/foo/bar/baz",
+ content: "Blublu",
+ group: :foo
+ }
+ end
+
+ let(:decorator) do
+ klass = Class.new(AF83::Decorator)
+ klass.action_link link_options
+ klass
+ end
+
+ it "should return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 1
+ end
+
+ it "should not return the link" do
+ links = decorated.action_links(:show, group: :bar)
+ expect(links.size).to eq 0
+ end
+ end
+ end
+
+ context "primary on this action" do
+ let(:group){ :show }
+
+ it "should return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 1
+ end
+ end
+
+ context "in this action among others" do
+ let(:group){ %i(show edit) }
+
+ it "should return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 1
+ end
+ end
+
+ context "in other actions" do
+ let(:group){ %i(index edit) }
+
+ it "should not return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 0
+ end
+ end
+
+ context "in another action" do
+ let(:group){ :index }
+
+ it "should not return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 0
+ end
+ end
+
+ context "never" do
+ let(:group){ nil }
+
+ it "should not return the link" do
+ links = decorated.action_links(:show, group: :foo)
+ expect(links.size).to eq 0
+ end
+ end
+ end
+
+ describe(:grouped_by) do
+ let(:link_options_1) do
+ {
+ href: "/foo/bar",
+ content: "Blublu",
+ primary: true
+ }
+ end
+
+ let(:link_options_2) do
+ {
+ href: "/foo/bar/baz",
+ content: "Foo",
+ groups: {secondary: :show}
+ }
+ end
+
+ let(:link_options_3) do
+ {
+ href: "/foo/bar/baz/bat",
+ content: "Foo",
+ groups: {foo: :show}
+ }
+ end
+
+ let(:link_options_4) do
+ {
+ href: "/footer",
+ content: "Foo",
+ footer: true
+ }
+ end
+
+ let(:decorator) do
+ klass = Class.new(AF83::Decorator)
+ klass.action_link link_options_1
+ klass.action_link link_options_2
+ klass.action_link link_options_3
+ klass.action_link link_options_4
+ klass
+ end
+
+ it "should return links in their groups" do
+ links = decorated.action_links(:show).grouped_by(:primary, :secondary, :blu, :footer)
+ expect(links.size).to eq 5
+ instance_exec links[:primary].first, link_options_1, &link_should_match_options
+ instance_exec links[:secondary].first, link_options_2, &link_should_match_options
+ expect(links[:blu].size).to eq 0
+ instance_exec links[:other].first, link_options_3, &link_should_match_options
+ instance_exec links[:footer].first, link_options_4, &link_should_match_options
+ end
+ end
+ end
end