Increase functionality of nav_link helper method

parent a569f9c0
...@@ -65,6 +65,13 @@ module TabHelper ...@@ -65,6 +65,13 @@ module TabHelper
# # When `TreeController#index` is requested # # When `TreeController#index` is requested
# # => '<li>Hello</li>' # # => '<li>Hello</li>'
# #
# # Paths, controller and actions can be used at the same time
# nav_link(path: 'tree#show', controller: 'admin/appearances') { "Hello" }
#
# nav_link(path: 'foo#bar', controller: 'tree') { "Hello" }
# nav_link(path: 'foo#bar', controller: 'tree', action: 'show') { "Hello" }
# nav_link(path: 'foo#bar', action: 'show') { "Hello" }
#
# Returns a list item element String # Returns a list item element String
def nav_link(options = {}, &block) def nav_link(options = {}, &block)
klass = active_nav_link?(options) ? 'active' : '' klass = active_nav_link?(options) ? 'active' : ''
...@@ -85,34 +92,12 @@ module TabHelper ...@@ -85,34 +92,12 @@ module TabHelper
def active_nav_link?(options) def active_nav_link?(options)
return false if options[:unless]&.call return false if options[:unless]&.call
if path = options.delete(:path) controller = options.delete(:controller)
unless path.respond_to?(:each) action = options.delete(:action)
path = [path]
end
path.any? do |single_path|
current_path?(single_path)
end
elsif page = options.delete(:page)
unless page.respond_to?(:each)
page = [page]
end
page.any? do |single_page|
current_page?(single_page)
end
else
c = options.delete(:controller)
a = options.delete(:action)
if c && a route_matches_paths?(options.delete(:path)) ||
# When given both options, make sure BOTH are true route_matches_pages?(options.delete(:page)) ||
current_controller?(*c) && current_action?(*a) route_matches_controllers_and_or_actions?(controller, action)
else
# Otherwise check EITHER option
current_controller?(*c) || current_action?(*a)
end
end
end end
def current_path?(path) def current_path?(path)
...@@ -127,4 +112,26 @@ module TabHelper ...@@ -127,4 +112,26 @@ module TabHelper
'active' 'active'
end end
end end
private
def route_matches_paths?(paths)
Array(paths).compact.any? do |single_path|
current_path?(single_path)
end
end
def route_matches_pages?(pages)
Array(pages).compact.any? do |single_page|
current_page?(single_page)
end
end
def route_matches_controllers_and_or_actions?(controller, action)
if controller && action
current_controller?(*controller) && current_action?(*action)
else
current_controller?(*controller) || current_action?(*action)
end
end
end end
...@@ -6,81 +6,94 @@ RSpec.describe TabHelper do ...@@ -6,81 +6,94 @@ RSpec.describe TabHelper do
include ApplicationHelper include ApplicationHelper
describe 'nav_link' do describe 'nav_link' do
using RSpec::Parameterized::TableSyntax
before do before do
allow(controller).to receive(:controller_name).and_return('foo') allow(controller).to receive(:controller_name).and_return('foo')
allow(self).to receive(:action_name).and_return('foo') allow(self).to receive(:action_name).and_return('foo')
end end
context 'with the content of the li' do context 'with the content of the li' do
it "captures block output" do it 'captures block output' do
expect(nav_link { "Testing Blocks" }).to match(/Testing Blocks/) expect(nav_link { "Testing Blocks" }).to match(/Testing Blocks/)
end end
end end
context 'with controller param' do it 'passes extra html options to the list element' do
it "performs checks on the current controller" do expect(nav_link(action: :foo, html_options: { class: 'home' })).to match(/<li class="home active">/)
expect(nav_link(controller: :foo)).to match(/<li class="active">/) expect(nav_link(html_options: { class: 'active' })).to match(/<li class="active">/)
expect(nav_link(controller: :bar)).not_to match(/active/)
expect(nav_link(controller: [:foo, :bar])).to match(/active/)
end end
context 'with action param' do where(:controller_param, :action_param, :path_param, :active) do
it "performs checks on both controller and action when both are present" do nil | nil | nil | false
expect(nav_link(controller: :bar, action: :foo)).not_to match(/active/) :foo | nil | nil | true
expect(nav_link(controller: :foo, action: :bar)).not_to match(/active/) :bar | nil | nil | false
expect(nav_link(controller: :foo, action: :foo)).to match(/active/) :bar | :foo | nil | false
end :foo | :bar | nil | false
:foo | :foo | nil | true
:bar | nil | 'foo#foo' | true
:bar | nil | ['foo#foo', 'bar#bar'] | true
:bar | :bar | ['foo#foo', 'bar#bar'] | true
:foo | nil | 'bar#foo' | true
:bar | nil | 'bar#foo' | false
:foo | [:foo, :bar] | 'bar#foo' | true
:bar | :bar | 'foo#foo' | true
:foo | :foo | 'bar#foo' | true
:bar | :foo | 'bar#foo' | false
:foo | :bar | 'bar#foo' | false
[:foo, :bar] | nil | nil | true
[:foo, :bar] | nil | 'bar#foo' | true
[:foo, :bar] | :foo | 'bar#foo' | true
nil | :foo | nil | true
nil | :bar | nil | false
nil | nil | 'foo#bar' | false
nil | nil | 'foo#foo' | true
nil | :bar | ['foo#foo', 'bar#bar'] | true
nil | :bar | 'foo#foo' | true
nil | :foo | 'bar#foo' | true
nil | [:foo, :bar] | nil | true
nil | [:foo, :bar] | 'bar#foo' | true
nil | :bar | 'bar#foo' | false
end end
context 'with namespace in path notation' do with_them do
before do specify do
allow(controller).to receive(:controller_path).and_return('bar/foo') result = nav_link(controller: controller_param, action: action_param, path: path_param)
end
it 'performs checks on both controller and namespace' do if active
expect(nav_link(controller: 'foo/foo')).not_to match(/active/) expect(result).to match(/active/)
expect(nav_link(controller: 'bar/foo')).to match(/active/) else
end expect(result).not_to match(/active/)
context 'with action param' do
it "performs checks on both namespace, controller and action when they are all present" do
expect(nav_link(controller: 'foo/foo', action: :foo)).not_to match(/active/)
expect(nav_link(controller: 'bar/foo', action: :bar)).not_to match(/active/)
expect(nav_link(controller: 'bar/foo', action: :foo)).to match(/active/)
end
end end
end end
end end
context 'with action param' do context 'with namespace in path notation' do
it "performs checks on the current action" do before do
expect(nav_link(action: :foo)).to match(/<li class="active">/) allow(controller).to receive(:controller_path).and_return('bar/foo')
expect(nav_link(action: :bar)).not_to match(/active/)
expect(nav_link(action: [:foo, :bar])).to match(/active/)
end
end end
context 'with path param' do where(:controller_param, :action_param, :path_param, :active) do
it "accepts a path shorthand" do 'foo/foo' | nil | nil | false
expect(nav_link(path: 'foo#bar')).not_to match(/active/) 'bar/foo' | nil | nil | true
expect(nav_link(path: 'foo#foo')).to match(/active/) 'foo/foo' | :foo | nil | false
'bar/foo' | :bar | nil | false
'bar/foo' | :foo | nil | true
nil | nil | 'foo/foo#foo' | false
nil | nil | 'bar/foo#foo' | true
end end
context 'with namespace' do with_them do
before do specify do
allow(controller).to receive(:controller_path).and_return('bar/foo') result = nav_link(controller: controller_param, action: action_param, path: path_param)
end
it 'accepts a path shorthand with namespace' do if active
expect(nav_link(path: 'bar/foo#foo')).to match(/active/) expect(result).to match(/active/)
expect(nav_link(path: 'foo/foo#foo')).not_to match(/active/) else
expect(result).not_to match(/active/)
end end
end end
end end
it "passes extra html options to the list element" do
expect(nav_link(action: :foo, html_options: { class: 'home' })).to match(/<li class="home active">/)
expect(nav_link(html_options: { class: 'active' })).to match(/<li class="active">/)
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment