diff options
-rw-r--r-- | layouts/global_nav.html | 71 | ||||
-rw-r--r-- | lib/gitlab/navigation.rb | 58 | ||||
-rw-r--r-- | lib/gitlab/navigation/category.rb | 41 | ||||
-rw-r--r-- | lib/gitlab/navigation/doc.rb | 37 | ||||
-rw-r--r-- | lib/gitlab/navigation/section.rb | 37 | ||||
-rw-r--r-- | spec/gitlab/navigation/category_spec.rb | 81 | ||||
-rw-r--r-- | spec/gitlab/navigation/doc_spec.rb | 56 | ||||
-rw-r--r-- | spec/gitlab/navigation/section_spec.rb | 73 | ||||
-rw-r--r-- | spec/gitlab/navigation_spec.rb | 86 |
9 files changed, 497 insertions, 43 deletions
diff --git a/layouts/global_nav.html b/layouts/global_nav.html index 334f4d85..1bf3795a 100644 --- a/layouts/global_nav.html +++ b/layouts/global_nav.html @@ -1,62 +1,47 @@ -<% -dir = @item.identifier.to_s[%r{(?<=/)[^/]+}] -nav_items_dir = "/_data/#{dir}-nav.yaml" -nav_items_exists = !@items[nav_items_dir].nil? -is_ee_prefixed = !nav_items_exists && dir != 'ce' -nav_items = nav_items_exists ? @items[nav_items_dir] : @items["/_data/default-nav.yaml"] -%> +<% navigation = Gitlab::Navigation.new(@items, @item) %> <nav class="global-nav-content"> <!-- nav sections --> - <% nav_items[:sections].each do |sec| %> + <% navigation.children.each do |sec| %> <div class="global-nav-section"> <span class="global-nav-block-top nav-link"> - <% section_href = is_ee_prefixed ? "/ee/#{sec[:section_url]}" : "/#{dir}/#{sec[:section_url]}" %> - <a class="global-nav-link level-0 <% if sec[:section_categories] %>has-collapse<% end %> <% if @item.path == "/#{dir}/#{sec[:section_url]}" %>active<% end %>" href="<%= section_href %>"> - <%= sec[:section_title] %> - <% if sec[:ee_only] %> - <span class="badges-drop global-nav-badges" data-toggle="tooltip" data-placement="top" title="Available in <%= sec[:ee_tier] %>"><i class="fa fa-info-circle" aria-hidden="true"></i></span> - <% end %><!-- end of if sec[:ee_only] --> + <a class="global-nav-link level-0 <%= sec.has_children? ? 'has-collapse' : '' %> <%= navigation.show_element?(sec) ? 'active' : '' %>" href="<%= navigation.element_href(sec) %>"> + <%= sec.title %> + <%= navigation.optional_ee_badge(sec) %> </a> - <div class="section-title <% if sec[:section_categories] %>collapse-toggle<% end %> <% if @item.path == "/#{dir}/#{sec[:section_url]}" %>active<% else %>collapsed<% end %>" data-toggle="collapse" aria-expanded="false" data-target="#<%= sec[:section_title].gsub(/[\s\/\(\)]/, '') %>"></div> + <div class="section-title <%= sec.has_children? ? 'collapse-toggle': '' %> <%= navigation.show_element?(sec) ? 'active' : 'collapsed' %>" data-toggle="collapse" aria-expanded="false" data-target="#<%= navigation.id_for(sec) %>"></div> </span> <!-- nav categories --> - <% if sec[:section_categories] %> - <div class="collapse <% if @item.path == "/#{dir}/#{sec[:section_url]}" %>show<% end %>" id="<%= sec[:section_title].gsub(/[\s\/\(\)]/, '') %>"> - <% sec[:section_categories].each do |cat| %> - <span class="global-nav-cat nav-link"> - <% if cat[:external_url] %> - <a class="global-nav-link level-1 <% if cat[:docs] %>has-collapse<% end %>" href="<%= cat[:category_url] %>" target="_blank"> - <%= cat[:category_title] %> - </a> - <% else %> - <% category_href = is_ee_prefixed ? "/ee/#{cat[:category_url]}" : "/#{dir}/#{cat[:category_url]}" %> - <a class="global-nav-link level-1 <% if cat[:docs] %>has-collapse<% end %> <% if @item.path == "/#{dir}/#{cat[:category_url]}" %>active<% end %>" href="<%= category_href %>"> - <%= cat[:category_title] %> - <% if cat[:ee_only] %> - <span class="badges-drop global-nav-badges" data-toggle="tooltip" data-placement="top" title="Available in <%= cat[:ee_tier] %>"><i class="fa fa-info-circle" aria-hidden="true"></i></span> - <% end %><!-- end of if cat[:ee_only] --> + <% if sec.has_children? %> + <div class="collapse <%= navigation.show_element?(sec) ? 'show' : '' %>" id="<%= navigation.id_for(sec) %>"> + <% sec.children.each do |cat| %> + <span class="global-nav-cat nav-link"> + <% if cat.external_url %> + <a class="global-nav-link level-1 <%= cat.has_children? ? 'has-collapse' : '' %>" href="<%= cat.url %>" target="_blank"> + <%= cat.title %> + </a> + <% else %> + <a class="global-nav-link level-1 <%= cat.has_children? ? 'has-collapse' : '' %> <%= navigation.show_element?(cat) ? 'active' : '' %>" href="<%= navigation.element_href(cat) %>"> + <%= cat.title %> + <%= navigation.optional_ee_badge(cat) %> </a> <% end %><!-- end of if cat[:external_url] --> - <div class="<% if cat[:docs] %>collapse-toggle<% end %> <% if @item.path == "/#{dir}/#{cat[:category_url]}" %>active<% else %>collapsed<% end %>" data-toggle="collapse" aria-expanded="false" data-target="#<%= cat[:category_title].gsub(/[\s\/\(\)]/, '') %>"></div> + <div class="<%= cat.has_children? ? 'collapse-toggle' : '' %> <%= navigation.show_element?(cat) ? 'active' : 'collapsed' %>" data-toggle="collapse" aria-expanded="false" data-target="#<%= navigation.id_for(cat) %>"></div> </span> <!-- nav docs --> - <% if cat[:docs] %> - <div class="collapse <% if @item.path == "/#{dir}/#{cat[:category_url]}" %>show<% end %>" id="<%= cat[:category_title].gsub(/[\s\/\(\)]/, '') %>"> - <% cat[:docs].each do |doc| %> + <% if cat.has_children? %> + <div class="collapse <%= navigation.show_element?(cat) ? 'show' : '' %>" id="<%= navigation.id_for(cat) %>"> + <% cat.children.each do |doc| %> <span class="nav-link"> - <% if doc[:external_url] %> - <a class="global-nav-link level-2" href="<%= doc[:doc_url] %>" target="_blank"> - <%= doc[:doc_title] %> + <% if doc.external_url %> + <a class="global-nav-link level-2" href="<%= doc.url %>" target="_blank"> + <%= doc.title %> </a> <% else%> - <% docs_href = is_ee_prefixed ? "/ee/#{doc[:doc_url]}" : "/#{dir}/#{doc[:doc_url]}" %> - <a class="global-nav-link level-2 <% if @item.path == "/#{dir}/#{doc[:doc_url]}" %>active<% end %>" href="<%= docs_href %>"> - <%= doc[:doc_title] %> - <% if doc[:ee_only] %> - <span class="badges-drop global-nav-badges" data-toggle="tooltip" data-placement="top" title="Available in <%= doc[:ee_tier] %>"><i class="fa fa-info-circle" aria-hidden="true"></i></span> - <% end %><!-- end if doc[:ee_only] --> + <a class="global-nav-link level-2 <%= navigation.show_element?(doc) ? 'active' : '' %>" href="<%= navigation.element_href(doc) %>"> + <%= doc.title %> + <%= navigation.optional_ee_badge(doc) %> </a> <% end %><!-- end of if doc[:external_url] --> </span> diff --git a/lib/gitlab/navigation.rb b/lib/gitlab/navigation.rb new file mode 100644 index 00000000..022654e0 --- /dev/null +++ b/lib/gitlab/navigation.rb @@ -0,0 +1,58 @@ +module Gitlab + class Navigation + def initialize(items, item) + @items = items + @item = item + end + + def nav_items + @nav_items ||= nav_items_exists ? items[nav_items_dir] : items["/_data/default-nav.yaml"] + end + + def element_href(element) + is_ee_prefixed ? "/ee/#{element.url}" : "/#{dir}/#{element.url}" + end + + def show_element?(element) + item.path == "/#{dir}/#{element.url}" + end + + def id_for(element) + element.title.gsub(/[\s\/\(\)]/, '') + end + + def optional_ee_badge(element) + return unless element.ee_only? + + %[<span class="badges-drop global-nav-badges" data-toggle="tooltip" data-placement="top" title="Available in #{element.ee_tier}"><i class="fa fa-info-circle" aria-hidden="true"></i></span>] + end + + def children + @children ||= nav_items.fetch(:sections, []).map { |section| Section.new(section) } + end + + private + + attr_reader :items, :item + + def dir + @dir ||= item.identifier.to_s[%r{(?<=/)[^/]+}] + end + + def nav_items_dir + @nav_items_dir ||= "/_data/#{dir}-nav.yaml" + end + + def nav_items_exists + !items[nav_items_dir].nil? + end + + def is_ee_prefixed + !nav_items_exists && dir != 'ce' + end + + def is_omnibus? + ENV['NANOC_ENV'] == 'omnibus' + end + end +end diff --git a/lib/gitlab/navigation/category.rb b/lib/gitlab/navigation/category.rb new file mode 100644 index 00000000..75f401fa --- /dev/null +++ b/lib/gitlab/navigation/category.rb @@ -0,0 +1,41 @@ +module Gitlab + class Navigation + class Category + def initialize(category) + @category = category + end + + def title + category[:category_title] + end + + def external_url + category[:external_url] + end + + def url + category[:category_url] + end + + def ee_only? + category[:ee_only] + end + + def ee_tier + category[:ee_tier] + end + + def has_children? + !children.empty? + end + + def children + @children ||= category.fetch(:docs, []).map { |doc| Doc.new(doc) } + end + + private + + attr_reader :category + end + end +end diff --git a/lib/gitlab/navigation/doc.rb b/lib/gitlab/navigation/doc.rb new file mode 100644 index 00000000..8bed2d43 --- /dev/null +++ b/lib/gitlab/navigation/doc.rb @@ -0,0 +1,37 @@ +module Gitlab + class Navigation + class Doc + def initialize(doc) + @doc = doc + end + + def title + doc[:doc_title] + end + + def external_url + doc[:external_url] + end + + def url + doc[:doc_url] + end + + def ee_only? + doc[:ee_only] + end + + def ee_tier + doc[:ee_tier] + end + + def children + [] + end + + private + + attr_reader :doc + end + end +end diff --git a/lib/gitlab/navigation/section.rb b/lib/gitlab/navigation/section.rb new file mode 100644 index 00000000..8b761fb8 --- /dev/null +++ b/lib/gitlab/navigation/section.rb @@ -0,0 +1,37 @@ +module Gitlab + class Navigation + class Section + def initialize(section) + @section = section + end + + def title + section[:section_title] + end + + def ee_only? + section[:ee_only] + end + + def ee_tier + section[:ee_tier] + end + + def url + section[:section_url] + end + + def has_children? + !children.empty? + end + + def children + @children ||= section.fetch(:section_categories, []).map { |cat| Category.new(cat) } + end + + private + + attr_reader :section + end + end +end diff --git a/spec/gitlab/navigation/category_spec.rb b/spec/gitlab/navigation/category_spec.rb new file mode 100644 index 00000000..67d28482 --- /dev/null +++ b/spec/gitlab/navigation/category_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' +require 'gitlab/navigation/category' +require 'gitlab/navigation/doc' + +describe Gitlab::Navigation::Category do + subject(:category) { described_class.new(element) } + let(:element) do + { + category_title: title, + external_url: external_url, + category_url: url, + ee_only: ee_only, + ee_tier: ee_tier, + docs: docs + } + end + let(:title) { 'Title' } + let(:external_url) { 'http://example.com' } + let(:url) { 'README.html' } + let(:ee_only) { true } + let(:ee_tier) { 'GitLab Premium' } + let(:docs) { [ { doc_title: 'Doc Title' } ] } + + describe '#title' do + subject { category.title } + + it { is_expected.to eq(title) } + end + + describe '#external_url' do + subject { category.external_url } + + it { is_expected.to eq(external_url) } + end + + describe '#url' do + subject { category.url } + + it { is_expected.to eq(url) } + end + + describe '#ee_only?' do + subject { category.ee_only? } + + it { is_expected.to eq(ee_only) } + end + + describe '#ee_tier' do + subject { category.ee_tier } + + it { is_expected.to eq(ee_tier) } + end + + describe '#has_children?' do + subject { category.has_children? } + + it { is_expected.to be_truthy } + + context 'when docs are empty' do + let(:docs) { [] } + + it { is_expected.to be_falsey } + end + end + + describe '#children' do + subject { category.children } + + it 'returns a list of children' do + children = subject + + expect(children.first.title).to eq('Doc Title') + end + + context 'when docs are empty' do + let(:docs) { [] } + + it { is_expected.to eq([]) } + end + end +end diff --git a/spec/gitlab/navigation/doc_spec.rb b/spec/gitlab/navigation/doc_spec.rb new file mode 100644 index 00000000..f823a73b --- /dev/null +++ b/spec/gitlab/navigation/doc_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' +require 'gitlab/navigation/doc' + +describe Gitlab::Navigation::Doc do + subject(:doc) { described_class.new(element) } + let(:element) do + { + doc_title: title, + external_url: external_url, + doc_url: url, + ee_only: ee_only, + ee_tier: ee_tier + } + end + let(:title) { 'Title' } + let(:external_url) { 'http://example.com' } + let(:url) { 'README.html' } + let(:ee_only) { true } + let(:ee_tier) { 'GitLab Premium' } + + describe '#title' do + subject { doc.title } + + it { is_expected.to eq(title) } + end + + describe '#external_url' do + subject { doc.external_url } + + it { is_expected.to eq(external_url) } + end + + describe '#url' do + subject { doc.url } + + it { is_expected.to eq(url) } + end + + describe '#ee_only?' do + subject { doc.ee_only? } + + it { is_expected.to eq(ee_only) } + end + + describe '#ee_tier' do + subject { doc.ee_tier } + + it { is_expected.to eq(ee_tier) } + end + + describe '#children' do + subject { doc.children } + + it { is_expected.to eq([]) } + end +end diff --git a/spec/gitlab/navigation/section_spec.rb b/spec/gitlab/navigation/section_spec.rb new file mode 100644 index 00000000..328793e9 --- /dev/null +++ b/spec/gitlab/navigation/section_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' +require 'gitlab/navigation/section' +require 'gitlab/navigation/category' + +describe Gitlab::Navigation::Section do + subject(:section) { described_class.new(element) } + let(:element) do + { + section_title: title, + section_url: url, + ee_only: ee_only, + ee_tier: ee_tier, + section_categories: categories + } + end + let(:title) { 'Title' } + let(:url) { 'README.html' } + let(:ee_only) { true } + let(:ee_tier) { 'GitLab Premium' } + let(:categories) { [ { category_title: 'Category Title' } ] } + + describe '#title' do + subject { section.title } + + it { is_expected.to eq(title) } + end + + describe '#url' do + subject { section.url } + + it { is_expected.to eq(url) } + end + + describe '#ee_only?' do + subject { section.ee_only? } + + it { is_expected.to eq(ee_only) } + end + + describe '#ee_tier' do + subject { section.ee_tier } + + it { is_expected.to eq(ee_tier) } + end + + describe '#has_children?' do + subject { section.has_children? } + + it { is_expected.to be_truthy } + + context 'when categories are empty' do + let(:categories) { [] } + + it { is_expected.to be_falsey } + end + end + + describe '#children' do + subject { section.children } + + it 'returns a list of children' do + children = subject + + expect(children.first.title).to eq('Category Title') + end + + context 'when categories are empty' do + let(:categories) { [] } + + it { is_expected.to eq([]) } + end + end +end diff --git a/spec/gitlab/navigation_spec.rb b/spec/gitlab/navigation_spec.rb new file mode 100644 index 00000000..a2d2bda7 --- /dev/null +++ b/spec/gitlab/navigation_spec.rb @@ -0,0 +1,86 @@ +require 'spec_helper' +require 'gitlab/navigation' +require 'gitlab/navigation/section' + +describe Gitlab::Navigation do + subject(:navigation) { described_class.new(items, item) } + let(:item) { double(path: '/omnibus/user/README.html', identifier: double(to_s: '/omnibus/user/README.md')) } + let(:items) do + { + '/_data/omnibus-nav.yaml' => { sections: [Gitlab::Navigation::Section.new(section_title: 'Omnibus Section')] }, + '/_data/default-nav.yaml' => { sections: [Gitlab::Navigation::Section.new(section_title: 'Default Section')] } + } + end + + describe '#nav_items' do + subject { navigation.nav_items } + + it 'returns project specific sections' do + sections = subject[:sections] + section = sections.first + + expect(section.title).to eq('Omnibus Section') + end + + context 'when yaml configuration for project does not exist' do + let(:item) { double(path: '/ee/user/README.html', identifier: double(to_s: '/ee/user/README.md')) } + + it 'returns default sections' do + sections = subject[:sections] + section = sections.first + + expect(section.title).to eq('Default Section') + end + end + end + + describe '#element_href' do + subject { navigation.element_href(element) } + let(:element) { Gitlab::Navigation::Section.new(section_url: url) } + let(:url) { 'user/README.html' } + + it { is_expected.to eq('/omnibus/user/README.html') } + + context 'when yaml configuration for project does not exist' do + let(:item) { double(path: '/ee/user/README.html', identifier: double(to_s: '/ee/user/README.md')) } + + it { is_expected.to eq('/ee/user/README.html') } + end + end + + describe '#show_element?' do + subject { navigation.show_element?(element) } + let(:element) { Gitlab::Navigation::Section.new(section_url: url) } + let(:url) { 'user/README.html' } + + it { is_expected.to be_truthy } + + context 'when url does not match item path' do + let(:url) { 'project/README.html' } + + it { is_expected.to be_falsey } + end + end + + describe '#id_for' do + subject { navigation.id_for(element) } + let(:element) { Gitlab::Navigation::Section.new(section_title: 'Section Example') } + + it { is_expected.to eq 'SectionExample' } + end + + describe '#optional_ee_badge' do + subject { navigation.optional_ee_badge(element) } + let(:element) { Gitlab::Navigation::Section.new(ee_only: ee_only, ee_tier: ee_tier) } + let(:ee_tier) { 'GitLab Starter' } + let(:ee_only) { true } + + it { is_expected.to include('span').and include(ee_tier) } + + context 'when ee_only -> false' do + let(:ee_only) { false } + + it { is_expected.to be_nil } + end + end +end |