simple-navigation 3.14.0 → 4.0.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 +4 -4
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.travis.yml +10 -2
- data/CHANGELOG.md +400 -0
- data/Guardfile +4 -2
- data/README.md +1 -1
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails-3-2-stable.gemfile +10 -0
- data/gemfiles/rails-4-1-stable.gemfile +6 -0
- data/gemfiles/rails-4-2-stable.gemfile +7 -0
- data/generators/navigation_config/navigation_config_generator.rb +0 -1
- data/generators/navigation_config/templates/config/navigation.rb +18 -15
- data/lib/simple_navigation.rb +25 -42
- data/lib/simple_navigation/adapters/rails.rb +1 -24
- data/lib/simple_navigation/adapters/sinatra.rb +2 -11
- data/lib/simple_navigation/config_file.rb +36 -0
- data/lib/simple_navigation/config_file_finder.rb +42 -0
- data/lib/simple_navigation/{core/configuration.rb → configuration.rb} +7 -1
- data/lib/simple_navigation/{rendering/helpers.rb → helpers.rb} +0 -4
- data/lib/simple_navigation/{core/item.rb → item.rb} +76 -85
- data/lib/simple_navigation/{core/item_adapter.rb → item_adapter.rb} +3 -17
- data/lib/simple_navigation/{core/item_container.rb → item_container.rb} +23 -14
- data/lib/simple_navigation/{core/items_provider.rb → items_provider.rb} +0 -0
- data/lib/simple_navigation/railtie.rb +7 -0
- data/lib/simple_navigation/renderer.rb +12 -0
- data/lib/simple_navigation/{rendering/renderer → renderer}/base.rb +1 -1
- data/lib/simple_navigation/{rendering/renderer → renderer}/breadcrumbs.rb +0 -0
- data/lib/simple_navigation/{rendering/renderer → renderer}/json.rb +2 -0
- data/lib/simple_navigation/{rendering/renderer → renderer}/links.rb +0 -0
- data/lib/simple_navigation/{rendering/renderer → renderer}/list.rb +0 -0
- data/lib/simple_navigation/{rendering/renderer → renderer}/text.rb +0 -0
- data/lib/simple_navigation/version.rb +1 -1
- data/simple-navigation.gemspec +5 -4
- data/spec/fake_app/config/navigation.rb +6 -0
- data/spec/fake_app/rails_app.rb +35 -0
- data/spec/initializers/coveralls.rb +3 -0
- data/spec/initializers/have_css_matcher.rb +8 -3
- data/spec/initializers/memfs.rb +7 -0
- data/spec/initializers/rails.rb +4 -0
- data/spec/initializers/rspec.rb +7 -0
- data/spec/integration/rendering_navigation_spec.rb +12 -0
- data/spec/{lib/simple_navigation → simple_navigation}/adapters/padrino_spec.rb +0 -2
- data/spec/{lib/simple_navigation → simple_navigation}/adapters/rails_spec.rb +36 -93
- data/spec/{lib/simple_navigation → simple_navigation}/adapters/sinatra_spec.rb +4 -6
- data/spec/simple_navigation/config_file_finder_spec.rb +50 -0
- data/spec/simple_navigation/config_file_spec.rb +25 -0
- data/spec/{lib/simple_navigation/core → simple_navigation}/configuration_spec.rb +29 -19
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/helpers_spec.rb +10 -13
- data/spec/{lib/simple_navigation/core → simple_navigation}/item_adapter_spec.rb +14 -11
- data/spec/{lib/simple_navigation/core → simple_navigation}/item_container_spec.rb +130 -42
- data/spec/simple_navigation/item_spec.rb +467 -0
- data/spec/{lib/simple_navigation/core → simple_navigation}/items_provider_spec.rb +1 -3
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/base_spec.rb +34 -36
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/breadcrumbs_spec.rb +4 -7
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/json_spec.rb +5 -11
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/links_spec.rb +5 -8
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/list_spec.rb +4 -7
- data/spec/{lib/simple_navigation/rendering → simple_navigation}/renderer/text_spec.rb +4 -7
- data/spec/simple_navigation_spec.rb +190 -0
- data/spec/spec_helper.rb +29 -35
- metadata +103 -68
- data/CHANGELOG +0 -292
- data/lib/simple_navigation/core.rb +0 -5
- data/lib/simple_navigation/rails_controller_methods.rb +0 -164
- data/lib/simple_navigation/rendering.rb +0 -12
- data/rails/init.rb +0 -1
- data/spec/lib/simple_navigation/core/item_spec.rb +0 -703
- data/spec/lib/simple_navigation/rails_controller_methods_spec.rb +0 -270
- data/spec/lib/simple_navigation_spec.rb +0 -300
@@ -1,164 +0,0 @@
|
|
1
|
-
module SimpleNavigation
|
2
|
-
def self.explicit_navigation_args
|
3
|
-
adapter.controller.instance_variable_get(:'@sn_current_navigation_args')
|
4
|
-
end
|
5
|
-
|
6
|
-
# Reads the current navigation for the specified level from the controller.
|
7
|
-
# Returns nil if there is no current navigation set for level.
|
8
|
-
def self.current_navigation_for(level)
|
9
|
-
adapter.controller.instance_variable_get(:"@sn_current_navigation_#{level}")
|
10
|
-
end
|
11
|
-
|
12
|
-
# If any navigation has been explicitely set in the controller this method
|
13
|
-
# evaluates the specified args set in the controller and sets the correct
|
14
|
-
# instance variable in the controller.
|
15
|
-
def self.handle_explicit_navigation
|
16
|
-
return unless explicit_navigation_args
|
17
|
-
level, navigation = parse_explicit_navigation_args
|
18
|
-
navigation_level = :"@sn_current_navigation_#{level}"
|
19
|
-
adapter.controller.instance_variable_set(navigation_level, navigation)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.parse_explicit_navigation_args
|
23
|
-
args = if explicit_navigation_args.empty?
|
24
|
-
[{}]
|
25
|
-
else
|
26
|
-
explicit_navigation_args
|
27
|
-
end
|
28
|
-
indexed_args = args_indexed_by_level(args)
|
29
|
-
deepest = deepest_level_and_item(indexed_args)
|
30
|
-
|
31
|
-
if deepest.first.zero?
|
32
|
-
fail ArgumentError, 'Invalid level specified or item key not found'
|
33
|
-
end
|
34
|
-
|
35
|
-
deepest
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def self.deepest_level_and_item(navigation_args)
|
41
|
-
navigation_args.map { |k, v| [k.to_s[/level_(\d)/, 1].to_i, v] }
|
42
|
-
.max
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.args_indexed_by_level(navigation_args)
|
46
|
-
if navigation_args.first.is_a?(Hash)
|
47
|
-
navigation_args.first
|
48
|
-
elsif navigation_args.size == 1
|
49
|
-
level = primary_navigation.level_for_item(navigation_args.first)
|
50
|
-
level ? { :"level_#{level}" => navigation_args.first } : {}
|
51
|
-
else
|
52
|
-
navigation_args.each_with_index
|
53
|
-
.with_object({}) do |(arg, i), h|
|
54
|
-
h[:"level_#{i + 1}"] = arg
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Adds methods for explicitely setting the current 'active' navigation to
|
60
|
-
# the controllers.
|
61
|
-
# Since version 2.0.0 the simple_navigation plugin determines the active
|
62
|
-
# navigation based on the current url by default (auto highlighting),
|
63
|
-
# so explicitely defining the active navigation in the controllers is only
|
64
|
-
# needed for edge cases where automatic highlighting does not work.
|
65
|
-
#
|
66
|
-
# On the controller class level, use the <tt>navigation</tt> method to set the
|
67
|
-
# active navigation for all actions in the controller.
|
68
|
-
# Let's assume that we have a primary navigation item :account which in turn
|
69
|
-
# has a sub navigation item :settings.
|
70
|
-
#
|
71
|
-
# ==== Examples
|
72
|
-
# class AccountController << ActionController
|
73
|
-
# navigation :account
|
74
|
-
# ...
|
75
|
-
# end
|
76
|
-
#
|
77
|
-
# class AccountSettingsController << ActionController
|
78
|
-
# navigation :settings
|
79
|
-
# ...
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# The first example sets the current primary navigation to :account for all
|
83
|
-
# actions. No active sub_navigation.
|
84
|
-
# The second example sets the current sub navigation to :settings and since
|
85
|
-
# it is a child of :account the current primary navigation is set to :account.
|
86
|
-
#
|
87
|
-
# On the controller instance level, use the <tt>current_navigation</tt> method
|
88
|
-
# to define the active navigation for a specific action.
|
89
|
-
# The navigation item that is set in <tt>current_navigation</tt> overrides the
|
90
|
-
# one defined on the controller class level (see <tt>navigation</tt> method).
|
91
|
-
# Thus if you have an :account primary item with a :special
|
92
|
-
# sub navigation item:
|
93
|
-
#
|
94
|
-
# ==== Example
|
95
|
-
# class AccountController << ActionController
|
96
|
-
# navigation :account
|
97
|
-
#
|
98
|
-
# def your_special_action
|
99
|
-
# ...
|
100
|
-
# current_navigation :special
|
101
|
-
# end
|
102
|
-
# end
|
103
|
-
#
|
104
|
-
# The code above still sets the active primary navigation to :account for all
|
105
|
-
# actions, but sets the sub_navigation to :account -> :special for
|
106
|
-
# 'your_special_action'.
|
107
|
-
#
|
108
|
-
# Note 1: As you can see above you just have to set the navigation item of
|
109
|
-
# your 'deepest' navigation level as active and all its parents are
|
110
|
-
# marked as active, too.
|
111
|
-
#
|
112
|
-
# Note 2: The specified symbols must match the keys for your navigation
|
113
|
-
# items in your config/navigation.rb file.
|
114
|
-
module ControllerMethods
|
115
|
-
def self.included(base) #:nodoc:
|
116
|
-
base.class_eval do
|
117
|
-
extend ClassMethods
|
118
|
-
include InstanceMethods
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
module ClassMethods
|
123
|
-
# Sets the active navigation for all actions in this controller.
|
124
|
-
#
|
125
|
-
# The specified symbol must match the keys for your navigation items
|
126
|
-
# in your config/navigation.rb file.
|
127
|
-
def navigation(*args)
|
128
|
-
class_eval do
|
129
|
-
define_method :sn_set_navigation do
|
130
|
-
current_navigation(*args)
|
131
|
-
end
|
132
|
-
protected :sn_set_navigation
|
133
|
-
before_filter :sn_set_navigation
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
module InstanceMethods
|
139
|
-
# Sets the active navigation. Call this method in any action to override
|
140
|
-
# the controller-wide active navigation specified by navigation.
|
141
|
-
#
|
142
|
-
# The specified symbol must match the keys for your navigation items in
|
143
|
-
# your config/navigation.rb file.
|
144
|
-
def current_navigation(*args)
|
145
|
-
@sn_current_navigation_args = args
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
class Item
|
151
|
-
def selected_by_config?
|
152
|
-
key == SimpleNavigation.current_navigation_for(container.level)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
class ItemContainer
|
157
|
-
def selected_item
|
158
|
-
self[SimpleNavigation.current_navigation_for(level)] ||
|
159
|
-
items.find(&:selected?)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
ActionController::Base.send(:include, SimpleNavigation::ControllerMethods)
|
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'simple_navigation/rendering/helpers'
|
2
|
-
require 'simple_navigation/rendering/renderer/base'
|
3
|
-
|
4
|
-
module SimpleNavigation
|
5
|
-
module Renderer
|
6
|
-
autoload :List, 'simple_navigation/rendering/renderer/list'
|
7
|
-
autoload :Links, 'simple_navigation/rendering/renderer/links'
|
8
|
-
autoload :Breadcrumbs, 'simple_navigation/rendering/renderer/breadcrumbs'
|
9
|
-
autoload :Text, 'simple_navigation/rendering/renderer/text'
|
10
|
-
autoload :Json, 'simple_navigation/rendering/renderer/json'
|
11
|
-
end
|
12
|
-
end
|
data/rails/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
SimpleNavigation.register
|
@@ -1,703 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module SimpleNavigation
|
4
|
-
describe Item do
|
5
|
-
let!(:item_container) { ItemContainer.new }
|
6
|
-
|
7
|
-
let(:adapter) { double(:adapter) }
|
8
|
-
let(:item_args) { [item_container, :my_key, 'name', url, options, items] }
|
9
|
-
let(:item) { Item.new(*item_args) }
|
10
|
-
let(:items) { nil }
|
11
|
-
let(:options) { Hash.new }
|
12
|
-
let(:url) { 'url' }
|
13
|
-
|
14
|
-
before { SimpleNavigation.stub(adapter: adapter) }
|
15
|
-
|
16
|
-
describe '#initialize' do
|
17
|
-
context 'when there is a sub_navigation' do
|
18
|
-
let(:subnav_container) { double(:subnav_container).as_null_object }
|
19
|
-
|
20
|
-
before { ItemContainer.stub(new: subnav_container) }
|
21
|
-
|
22
|
-
context 'when a block is given' do
|
23
|
-
it 'creates a new ItemContainer with a level+1' do
|
24
|
-
expect(ItemContainer).to receive(:new).with(2)
|
25
|
-
Item.new(*item_args) {}
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'calls the block' do
|
29
|
-
expect{ |blk|
|
30
|
-
Item.new(*item_args, &blk)
|
31
|
-
}.to yield_with_args(subnav_container)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'when no block is given' do
|
36
|
-
context 'and items are given' do
|
37
|
-
let(:items) { double(:items) }
|
38
|
-
|
39
|
-
it 'creates a new ItemContainer with a level+1' do
|
40
|
-
expect(ItemContainer).to receive(:new).with(2)
|
41
|
-
Item.new(*item_args)
|
42
|
-
end
|
43
|
-
|
44
|
-
it "sets the items on the subnav_container" do
|
45
|
-
expect(subnav_container).to receive(:items=).with(items)
|
46
|
-
Item.new(*item_args)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'and no items are given' do
|
51
|
-
it "doesn't create a new ItemContainer" do
|
52
|
-
expect(ItemContainer).not_to receive(:new)
|
53
|
-
Item.new(*item_args)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context 'when a :method option is given' do
|
60
|
-
let(:options) {{ method: :delete }}
|
61
|
-
|
62
|
-
it "sets the item's method" do
|
63
|
-
expect(item.method).to eq :delete
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'sets the html options without the method' do
|
67
|
-
meth = item.instance_variable_get(:@html_options).key?(:method)
|
68
|
-
expect(meth).to be_false
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context 'when no :method option is given' do
|
73
|
-
it "sets the item's method to nil" do
|
74
|
-
expect(item.method).to be_nil
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
context 'setting class and id on the container' do
|
79
|
-
let!(:create) { item }
|
80
|
-
|
81
|
-
let(:options) {{
|
82
|
-
container_class: 'container_class',
|
83
|
-
container_id: 'container_id',
|
84
|
-
container_attributes: { 'ng-show' => 'false' }
|
85
|
-
}}
|
86
|
-
|
87
|
-
it "fills in the container's dom_attributes" do
|
88
|
-
expect(item_container.dom_attributes).to eq({
|
89
|
-
id: 'container_id',
|
90
|
-
class: 'container_class',
|
91
|
-
'ng-show' => 'false'
|
92
|
-
})
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context 'when a :highlights_on option is given' do
|
97
|
-
it "sets the item's highlights_on to nil" do
|
98
|
-
expect(item.highlights_on).to be_nil
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
context 'when no :highlights_on option is given' do
|
103
|
-
let(:highlights_on) { double(:highlights_on) }
|
104
|
-
let(:options) {{ highlights_on: highlights_on }}
|
105
|
-
|
106
|
-
it "sets the item's highlights_on" do
|
107
|
-
expect(item.highlights_on).to eq highlights_on
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'sets the html options without the method' do
|
111
|
-
html_options = item.instance_variable_get(:@html_options)
|
112
|
-
expect(html_options).not_to have_key(:highlights_on)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
context 'when a url is given' do
|
117
|
-
context 'and it is a string' do
|
118
|
-
it "sets the item's url accordingly" do
|
119
|
-
expect(item.url).to eq 'url'
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
context 'and it is a proc' do
|
124
|
-
let(:url) { proc{ "my_" + "url" } }
|
125
|
-
|
126
|
-
it "sets the item's url accordingly" do
|
127
|
-
expect(item.url).to eq 'my_url'
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
context 'and it is nil' do
|
132
|
-
let(:url) { nil }
|
133
|
-
|
134
|
-
it "sets the item's url accordingly" do
|
135
|
-
expect(item.url).to be_nil
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
describe 'Optional url and optional options' do
|
141
|
-
context 'when no parameter is specified' do
|
142
|
-
let(:item_args) { [item_container, :my_key, 'name'] }
|
143
|
-
|
144
|
-
it "sets the item's url to nil" do
|
145
|
-
expect(item.url).to be_nil
|
146
|
-
end
|
147
|
-
|
148
|
-
it "sets the item's html_options to an empty hash" do
|
149
|
-
expect(item.instance_variable_get(:@html_options)).to eq({})
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
context 'when only a url is given' do
|
154
|
-
let(:item_args) { [item_container, :my_key, 'name', 'url'] }
|
155
|
-
|
156
|
-
it "set the item's url accordingly" do
|
157
|
-
expect(item.url).to eq 'url'
|
158
|
-
end
|
159
|
-
|
160
|
-
it "sets the item's html_options to an empty hash" do
|
161
|
-
expect(item.instance_variable_get(:@html_options)).to eq({})
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
context 'when only options are given' do
|
166
|
-
let(:item_args) { [item_container, :my_key, 'name', { option: true }] }
|
167
|
-
|
168
|
-
it "sets the item's url to nil" do
|
169
|
-
expect(item.url).to be_nil
|
170
|
-
end
|
171
|
-
|
172
|
-
it "sets the item's html_options accordingly" do
|
173
|
-
html_options = item.instance_variable_get(:@html_options)
|
174
|
-
expect(html_options).to eq({ option: true })
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
context 'when url and options are given' do
|
179
|
-
let(:options) {{ option: true }}
|
180
|
-
|
181
|
-
it "set the item's url accordingly" do
|
182
|
-
expect(item.url).to eq 'url'
|
183
|
-
end
|
184
|
-
|
185
|
-
it "sets the item's html_options accordingly" do
|
186
|
-
html_options = item.instance_variable_get(:@html_options)
|
187
|
-
expect(html_options).to eq({ option: true })
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
describe '#name' do
|
194
|
-
before do
|
195
|
-
SimpleNavigation.config.stub(
|
196
|
-
name_generator: proc{ |name| "<span>#{name}</span>" })
|
197
|
-
end
|
198
|
-
|
199
|
-
context 'when no option is given' do
|
200
|
-
context 'and the name_generator uses only the name' do
|
201
|
-
it 'uses the default name_generator' do
|
202
|
-
expect(item.name).to eq '<span>name</span>'
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context 'and the name_generator uses only the item itself' do
|
207
|
-
before do
|
208
|
-
SimpleNavigation.config.stub(
|
209
|
-
name_generator: proc{ |name, item| "<span>#{item.key}</span>" })
|
210
|
-
end
|
211
|
-
|
212
|
-
it 'uses the default name_generator' do
|
213
|
-
expect(item.name).to eq '<span>my_key</span>'
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
context 'when the :apply_generator is false' do
|
219
|
-
it "returns the item's name" do
|
220
|
-
expect(item.name(apply_generator: false)).to eq 'name'
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
describe '#selected?' do
|
226
|
-
context 'when the item is explicitly selected' do
|
227
|
-
before { item.stub(selected_by_config?: true) }
|
228
|
-
|
229
|
-
it 'is selected' do
|
230
|
-
expect(item).to be_selected
|
231
|
-
end
|
232
|
-
|
233
|
-
# FIXME: testing the implementation not the behavior here
|
234
|
-
it "doesn't check for selection by sub navigation" do
|
235
|
-
expect(item).not_to receive(:selected_by_subnav?)
|
236
|
-
item.selected?
|
237
|
-
end
|
238
|
-
|
239
|
-
# FIXME: testing the implementation not the behavior here
|
240
|
-
it "doesn't check for selection by highlighting condition" do
|
241
|
-
expect(item).not_to receive(:selected_by_condition?)
|
242
|
-
item.selected?
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
context "when the item isn't explicitly selected" do
|
247
|
-
before { item.stub(selected_by_config?: false) }
|
248
|
-
|
249
|
-
context 'and it is selected by sub navigation' do
|
250
|
-
before { item.stub(selected_by_subnav?: true) }
|
251
|
-
|
252
|
-
it 'is selected' do
|
253
|
-
expect(item).to be_selected
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
context "and it isn't selected by sub navigation" do
|
258
|
-
before { item.stub(selected_by_subnav?: false) }
|
259
|
-
|
260
|
-
context 'and it is selected by a highlighting condition' do
|
261
|
-
before { item.stub(selected_by_condition?: true) }
|
262
|
-
|
263
|
-
it 'is selected' do
|
264
|
-
expect(item).to be_selected
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
context "and it isn't selected by any highlighting condition" do
|
269
|
-
before { item.stub(selected_by_condition?: false) }
|
270
|
-
|
271
|
-
it "isn't selected" do
|
272
|
-
expect(item).not_to be_selected
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
describe '#selected_class' do
|
280
|
-
context 'when the item is selected' do
|
281
|
-
before { item.stub(selected?: true) }
|
282
|
-
|
283
|
-
it 'returns the default selected_class' do
|
284
|
-
expect(item.selected_class).to eq 'selected'
|
285
|
-
end
|
286
|
-
|
287
|
-
context 'and selected_class is defined in the context' do
|
288
|
-
before { item_container.stub(selected_class: 'defined') }
|
289
|
-
|
290
|
-
it "returns the context's selected_class" do
|
291
|
-
expect(item.selected_class).to eq 'defined'
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
context 'when the item is not selected' do
|
297
|
-
before { item.stub(selected?: false) }
|
298
|
-
|
299
|
-
it 'returns nil' do
|
300
|
-
expect(item.selected_class).to be_nil
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
describe ':html_options argument' do
|
306
|
-
let(:selected_classes) { 'selected simple-navigation-active-leaf' }
|
307
|
-
|
308
|
-
context 'when the :class option is given' do
|
309
|
-
let(:options) {{ class: 'my_class' }}
|
310
|
-
|
311
|
-
context 'and the item is selected' do
|
312
|
-
before { item.stub(selected?: true, selected_by_condition?: true) }
|
313
|
-
|
314
|
-
it "adds the specified class to the item's html classes" do
|
315
|
-
expect(item.html_options[:class]).to include('my_class')
|
316
|
-
end
|
317
|
-
|
318
|
-
it "doesn't replace the default html classes of a selected item" do
|
319
|
-
expect(item.html_options[:class]).to include(selected_classes)
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
context "and the item isn't selected" do
|
324
|
-
before { item.stub(selected?: false, selected_by_condition?: false) }
|
325
|
-
|
326
|
-
it "sets the specified class as the item's html classes" do
|
327
|
-
expect(item.html_options[:class]).to include('my_class')
|
328
|
-
end
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
context "when the :class option isn't given" do
|
333
|
-
context 'and the item is selected' do
|
334
|
-
before { item.stub(selected?: true, selected_by_condition?: true) }
|
335
|
-
|
336
|
-
it "sets the default html classes of a selected item" do
|
337
|
-
expect(item.html_options[:class]).to include(selected_classes)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
context "and the item isn't selected" do
|
342
|
-
before { item.stub(selected?: false, selected_by_condition?: false) }
|
343
|
-
|
344
|
-
it "doesn't set any html class on the item" do
|
345
|
-
expect(item.html_options[:class]).to be_blank
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
shared_examples 'generating id' do |id|
|
351
|
-
it "sets the item's html id to the specified id" do
|
352
|
-
expect(item.html_options[:id]).to eq id
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
describe 'when the :id option is given' do
|
357
|
-
let(:options) {{ id: 'my_id' }}
|
358
|
-
|
359
|
-
before do
|
360
|
-
item.stub(selected?: false,
|
361
|
-
selected_by_condition?: false,
|
362
|
-
autogenerate_item_ids?: generate_ids)
|
363
|
-
end
|
364
|
-
|
365
|
-
context 'and :autogenerate_item_ids is true' do
|
366
|
-
let(:generate_ids) { true }
|
367
|
-
|
368
|
-
it_behaves_like 'generating id', 'my_id'
|
369
|
-
end
|
370
|
-
|
371
|
-
context 'and :autogenerate_item_ids is false' do
|
372
|
-
let(:generate_ids) { false }
|
373
|
-
|
374
|
-
it_behaves_like 'generating id', 'my_id'
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
context "when the :id option isn't given" do
|
379
|
-
before do
|
380
|
-
item.stub(selected?: false,
|
381
|
-
selected_by_condition?: false,
|
382
|
-
autogenerate_item_ids?: generate_ids)
|
383
|
-
end
|
384
|
-
|
385
|
-
context 'and :autogenerate_item_ids is true' do
|
386
|
-
let(:generate_ids) { true }
|
387
|
-
|
388
|
-
it_behaves_like 'generating id', 'my_key'
|
389
|
-
end
|
390
|
-
|
391
|
-
context 'and :autogenerate_item_ids is false' do
|
392
|
-
let(:generate_ids) { false }
|
393
|
-
|
394
|
-
it "doesn't set any html id on the item" do
|
395
|
-
expect(item.html_options[:id]).to be_blank
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
end
|
400
|
-
|
401
|
-
describe '#selected_by_subnav?' do
|
402
|
-
before { item.stub(sub_navigation: sub_navigation) }
|
403
|
-
|
404
|
-
context 'the item has a sub_navigation' do
|
405
|
-
let(:sub_navigation) { double(:sub_navigation) }
|
406
|
-
|
407
|
-
context 'and an item of the sub_navigation is selected' do
|
408
|
-
before do
|
409
|
-
sub_navigation.stub(selected?: true, selected_by_condition?: true)
|
410
|
-
end
|
411
|
-
|
412
|
-
it 'returns true' do
|
413
|
-
expect(item).to be_selected_by_subnav
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
context 'and no item of the sub_navigation is selected' do
|
418
|
-
before do
|
419
|
-
sub_navigation.stub(selected?: false, selected_by_condition?: true)
|
420
|
-
end
|
421
|
-
|
422
|
-
it 'returns false' do
|
423
|
-
expect(item).not_to be_selected_by_subnav
|
424
|
-
end
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
context "when the item doesn't have any sub_navigation" do
|
429
|
-
let(:sub_navigation) { nil }
|
430
|
-
|
431
|
-
it 'returns false' do
|
432
|
-
expect(item).not_to be_selected_by_subnav
|
433
|
-
end
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
describe '#selected_by_condition?' do
|
438
|
-
let(:current_url) { '' }
|
439
|
-
|
440
|
-
before { adapter.stub(request_uri: current_url) }
|
441
|
-
|
442
|
-
context 'when the :highlights_on option is set' do
|
443
|
-
before { item.stub(highlights_on: /^\/matching/) }
|
444
|
-
|
445
|
-
context 'and :highlights_on is a regexp' do
|
446
|
-
context 'and it matches the current url' do
|
447
|
-
let(:current_url) { '/matching_url' }
|
448
|
-
|
449
|
-
it 'returns true' do
|
450
|
-
expect(item).to be_selected_by_condition
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
context "and it doesn't match current url" do
|
455
|
-
let(:current_url) { '/other_url' }
|
456
|
-
|
457
|
-
it 'returns false' do
|
458
|
-
expect(item).not_to be_selected_by_condition
|
459
|
-
end
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
context 'and :highlights_on is a lambda' do
|
464
|
-
context 'and it is truthy' do
|
465
|
-
before { item.stub(highlights_on: ->{ true }) }
|
466
|
-
|
467
|
-
it 'returns true' do
|
468
|
-
expect(item).to be_selected_by_condition
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
context 'falsey lambda results in no selection' do
|
473
|
-
before { item.stub(highlights_on: ->{ false }) }
|
474
|
-
|
475
|
-
it 'returns false' do
|
476
|
-
expect(item).not_to be_selected_by_condition
|
477
|
-
end
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
|
-
context 'and :highlights_on is :subpath' do
|
482
|
-
before { item.stub(url: '/path', highlights_on: :subpath) }
|
483
|
-
|
484
|
-
context "and the current url is a sub path of the item's url" do
|
485
|
-
let(:current_url) { '/path/sub-path' }
|
486
|
-
|
487
|
-
it 'returns true' do
|
488
|
-
expect(item).to be_selected_by_condition
|
489
|
-
end
|
490
|
-
end
|
491
|
-
|
492
|
-
context "and the current url starts with item's url" do
|
493
|
-
let(:current_url) { '/path_group/id' }
|
494
|
-
|
495
|
-
it 'returns false' do
|
496
|
-
expect(item).not_to be_selected_by_condition
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
context "and the current url is totally different from the item's url" do
|
501
|
-
let(:current_url) { '/other_path/id' }
|
502
|
-
|
503
|
-
it 'returns false' do
|
504
|
-
expect(item).not_to be_selected_by_condition
|
505
|
-
end
|
506
|
-
end
|
507
|
-
end
|
508
|
-
|
509
|
-
context 'when :highlights_on something else' do
|
510
|
-
before { item.stub(highlights_on: 'nothing') }
|
511
|
-
|
512
|
-
it 'raises an exception' do
|
513
|
-
expect{ item.send(:selected_by_condition?) }.to raise_error
|
514
|
-
end
|
515
|
-
end
|
516
|
-
end
|
517
|
-
|
518
|
-
context 'when :auto_highlight is true' do
|
519
|
-
before { item.stub(auto_highlight?: true) }
|
520
|
-
|
521
|
-
context 'and root path matches' do
|
522
|
-
before { item.stub(root_path_match?: true) }
|
523
|
-
|
524
|
-
it 'returns true' do
|
525
|
-
expect(item).to be_selected_by_condition
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
context "and root path doesn't match" do
|
530
|
-
before { item.stub(root_path_match?: false) }
|
531
|
-
|
532
|
-
context "and the current url matches the item's url" do
|
533
|
-
let(:url) { 'url#anchor' }
|
534
|
-
|
535
|
-
before { adapter.stub(current_page?: true) }
|
536
|
-
|
537
|
-
it 'returns true' do
|
538
|
-
expect(item).to be_selected_by_condition
|
539
|
-
end
|
540
|
-
|
541
|
-
# FIXME: testing the implementation not the behavior here
|
542
|
-
it "removes anchors before testing the item's url" do
|
543
|
-
expect(adapter).to receive(:current_page?).with('url')
|
544
|
-
item.send(:selected_by_condition?)
|
545
|
-
end
|
546
|
-
|
547
|
-
context 'when url is nil' do
|
548
|
-
let(:url) { nil }
|
549
|
-
|
550
|
-
it "doesn't check the url" do
|
551
|
-
expect(adapter).not_to receive(:current_page?)
|
552
|
-
item.send(:selected_by_condition?)
|
553
|
-
end
|
554
|
-
end
|
555
|
-
end
|
556
|
-
|
557
|
-
context "and the current url doesn't match the item's url" do
|
558
|
-
before { adapter.stub(current_page?: false) }
|
559
|
-
|
560
|
-
it 'returns false' do
|
561
|
-
expect(item).not_to be_selected_by_condition
|
562
|
-
end
|
563
|
-
end
|
564
|
-
end
|
565
|
-
end
|
566
|
-
|
567
|
-
context 'when :auto_highlight is false' do
|
568
|
-
before { item.stub(auto_highlight?: false) }
|
569
|
-
|
570
|
-
it 'returns false' do
|
571
|
-
expect(item).not_to be_selected_by_condition
|
572
|
-
end
|
573
|
-
end
|
574
|
-
end
|
575
|
-
|
576
|
-
describe '#root_path_match?' do
|
577
|
-
context "when current url is /" do
|
578
|
-
before { adapter.stub(request_path: '/') }
|
579
|
-
|
580
|
-
context "and the item's url is /" do
|
581
|
-
let(:url) { '/' }
|
582
|
-
|
583
|
-
it 'returns true' do
|
584
|
-
expect(item.send(:root_path_match?)).to be_true
|
585
|
-
end
|
586
|
-
end
|
587
|
-
|
588
|
-
context "and the item's url isn't /" do
|
589
|
-
let(:url) { '/other' }
|
590
|
-
|
591
|
-
it 'returns false' do
|
592
|
-
expect(item.send(:root_path_match?)).to be_false
|
593
|
-
end
|
594
|
-
end
|
595
|
-
end
|
596
|
-
|
597
|
-
context "when current url isn't /" do
|
598
|
-
before { adapter.stub(request_path: '/other') }
|
599
|
-
|
600
|
-
context "and the item's url is /" do
|
601
|
-
let(:url) { '/' }
|
602
|
-
|
603
|
-
it 'returns false' do
|
604
|
-
expect(item.send(:root_path_match?)).to be_false
|
605
|
-
end
|
606
|
-
end
|
607
|
-
|
608
|
-
context "and the item's url is nil" do
|
609
|
-
let(:url) { nil }
|
610
|
-
|
611
|
-
it 'returns false' do
|
612
|
-
expect(item.send(:root_path_match?)).to be_false
|
613
|
-
end
|
614
|
-
end
|
615
|
-
end
|
616
|
-
|
617
|
-
context "when current url doesn't match the item's url" do
|
618
|
-
let(:url) { '/path' }
|
619
|
-
|
620
|
-
before { adapter.stub(request_path: '/other') }
|
621
|
-
|
622
|
-
it 'returns false' do
|
623
|
-
expect(item.send(:root_path_match?)).to be_false
|
624
|
-
end
|
625
|
-
end
|
626
|
-
|
627
|
-
context "when current url doesn't match the item's url" do
|
628
|
-
let(:url) { nil }
|
629
|
-
|
630
|
-
before { adapter.stub(request_path: '/other') }
|
631
|
-
|
632
|
-
it 'returns false' do
|
633
|
-
expect(item.send(:root_path_match?)).to be_false
|
634
|
-
end
|
635
|
-
end
|
636
|
-
end
|
637
|
-
|
638
|
-
describe '#auto_highlight?' do
|
639
|
-
let(:global) { double(:config) }
|
640
|
-
|
641
|
-
before { SimpleNavigation.stub(config: global) }
|
642
|
-
|
643
|
-
context 'when :auto_highlight is globally true' do
|
644
|
-
before { global.stub(auto_highlight: true) }
|
645
|
-
|
646
|
-
context "and container's :auto_highlight is true" do
|
647
|
-
before { item_container.stub(auto_highlight: true) }
|
648
|
-
|
649
|
-
it 'returns true' do
|
650
|
-
expect(item.send(:auto_highlight?)).to be_true
|
651
|
-
end
|
652
|
-
end
|
653
|
-
|
654
|
-
context "and container's :auto_highlight is false" do
|
655
|
-
before { item_container.stub(auto_highlight: false) }
|
656
|
-
|
657
|
-
it 'returns false' do
|
658
|
-
expect(item.send(:auto_highlight?)).to be_false
|
659
|
-
end
|
660
|
-
end
|
661
|
-
end
|
662
|
-
|
663
|
-
context 'when :auto_highlight is globally false' do
|
664
|
-
before { global.stub(auto_highlight: false) }
|
665
|
-
|
666
|
-
context 'when :auto_highlight is globally true' do
|
667
|
-
before { item_container.stub(auto_highlight: true) }
|
668
|
-
|
669
|
-
it 'returns false' do
|
670
|
-
expect(item.send(:auto_highlight?)).to be_false
|
671
|
-
end
|
672
|
-
end
|
673
|
-
|
674
|
-
context "and container's :auto_highlight is false" do
|
675
|
-
before { item_container.stub(auto_highlight: false) }
|
676
|
-
|
677
|
-
it 'returns false' do
|
678
|
-
expect(item.send(:auto_highlight?)).to be_false
|
679
|
-
end
|
680
|
-
end
|
681
|
-
end
|
682
|
-
end
|
683
|
-
|
684
|
-
describe '#autogenerated_item_id' do
|
685
|
-
context 'when no generator is configured' do
|
686
|
-
let(:id_generator) { double(:id_generator) }
|
687
|
-
|
688
|
-
before { SimpleNavigation.config.stub(id_generator: id_generator) }
|
689
|
-
|
690
|
-
it 'calls the globally configured id generator' do
|
691
|
-
expect(id_generator).to receive(:call).with(:my_key)
|
692
|
-
item.send(:autogenerated_item_id)
|
693
|
-
end
|
694
|
-
end
|
695
|
-
|
696
|
-
context 'when no generator is configured' do
|
697
|
-
it 'uses the default generator' do
|
698
|
-
expect(item.send(:autogenerated_item_id)).to eq 'my_key'
|
699
|
-
end
|
700
|
-
end
|
701
|
-
end
|
702
|
-
end
|
703
|
-
end
|