diff --git a/app/views/shared/_block_link.html.erb b/app/components/block_link_component.html.erb similarity index 50% rename from app/views/shared/_block_link.html.erb rename to app/components/block_link_component.html.erb index b0a4f68b97b..90503a087d0 100644 --- a/app/views/shared/_block_link.html.erb +++ b/app/components/block_link_component.html.erb @@ -1,16 +1,4 @@ -<%# -yields: Link text (required). -locals: -* url: URL of link. -* new_tab: Whether link should open in a new tab. Defaults to false. Use best judgment to reserve - new tabs to when absolutely necessary, such as when form data may otherwise be lost. -%> -<% content = yield.presence or raise 'no block content given' - new_tab = local_assigns.fetch(:new_tab, false) - tag_attrs = { href: url, class: ['usa-link', 'block-link'] } - tag_attrs[:target] = '_blank' if new_tab - tag_attrs[:class] << 'usa-link--external' if new_tab %> -<%= tag.a(**tag_attrs) do %> +<%= tag.a(**tag_options, href: url, class: css_class, target: target) do %> <%= content %> <% if new_tab %> <%= t('links.new_window') %> diff --git a/app/components/block_link_component.rb b/app/components/block_link_component.rb new file mode 100644 index 00000000000..348d08d0394 --- /dev/null +++ b/app/components/block_link_component.rb @@ -0,0 +1,19 @@ +class BlockLinkComponent < BaseComponent + attr_reader :url, :new_tab, :tag_options + + def initialize(url:, new_tab: false, **tag_options) + @url = url + @new_tab = new_tab + @tag_options = tag_options + end + + def css_class + classes = ['usa-link', 'block-link', *tag_options[:class]] + classes << 'usa-link--external' if new_tab + classes + end + + def target + '_blank' if new_tab + end +end diff --git a/app/components/status_page_component.rb b/app/components/status_page_component.rb index 21e88577beb..4824cf0761b 100644 --- a/app/components/status_page_component.rb +++ b/app/components/status_page_component.rb @@ -8,7 +8,7 @@ class StatusPageComponent < BaseComponent renders_many :action_buttons, ->(**button_options) do ButtonComponent.new(**button_options, big: true, wide: true) end - renders_one :troubleshooting_options + renders_one :troubleshooting_options, TroubleshootingOptionsComponent attr_reader :status, :icon diff --git a/app/components/troubleshooting_options_component.html.erb b/app/components/troubleshooting_options_component.html.erb new file mode 100644 index 00000000000..1e2a2071631 --- /dev/null +++ b/app/components/troubleshooting_options_component.html.erb @@ -0,0 +1,8 @@ +<%= tag.section(**tag_options, class: css_class) do %> + <%= header %> +
+<% end %> diff --git a/app/components/troubleshooting_options_component.rb b/app/components/troubleshooting_options_component.rb new file mode 100644 index 00000000000..16f97a9d042 --- /dev/null +++ b/app/components/troubleshooting_options_component.rb @@ -0,0 +1,30 @@ +class TroubleshootingOptionsComponent < BaseComponent + renders_one :header, 'TroubleshootingOptionsHeadingComponent' + renders_many :options, BlockLinkComponent + + attr_reader :tag_options + + def initialize(**tag_options) + @tag_options = tag_options + end + + def render? + options? + end + + def css_class + ['troubleshooting-options', *tag_options[:class]] + end + + class TroubleshootingOptionsHeadingComponent < BaseComponent + attr_reader :heading_level + + def initialize(heading_level: :h2) + @heading_level = heading_level + end + + def call + content_tag(heading_level, content, class: 'troubleshooting-options__heading') + end + end +end diff --git a/app/views/shared/_troubleshooting_options.html.erb b/app/views/shared/_troubleshooting_options.html.erb index 696cfbfbe6d..4d8d9700c17 100644 --- a/app/views/shared/_troubleshooting_options.html.erb +++ b/app/views/shared/_troubleshooting_options.html.erb @@ -5,17 +5,7 @@ locals: * options: List of link options to display, as an array of hashes with `url`, `text`, `new_tab` values. * class: Additional class names to add to wrapper element. %> -<% if local_assigns[:options].presence %> - <% heading_tag = local_assigns[:heading_tag] || :h2 %> - <% classes = ['troubleshooting-options', *local_assigns[:class]] %> - <%= tag.section class: classes do %> - <%= content_tag(heading_tag, heading, class: 'troubleshooting-options__heading') %> - - <% end %> +<%= render TroubleshootingOptionsComponent.new(class: local_assigns[:class]) do |c| %> + <% c.header(heading_level: local_assigns[:heading_tag] || :h2).with_content(heading) %> + <% options.each { |option| c.option(**option.except(:text)).with_content(option[:text]) } %> <% end %> diff --git a/app/views/two_factor_authentication/_locked.html.erb b/app/views/two_factor_authentication/_locked.html.erb index 4b13cbaba10..9cd41b87ec3 100644 --- a/app/views/two_factor_authentication/_locked.html.erb +++ b/app/views/two_factor_authentication/_locked.html.erb @@ -14,22 +14,15 @@ ) %> - <% c.troubleshooting_options do %> - <%= render( - 'shared/troubleshooting_options', - heading: t('components.troubleshooting_options.default_heading'), - options: [ - { - text: t('two_factor_authentication.read_about_two_factor_authentication'), - url: MarketingSite.help_url, - new_tab: true, - }, - { - url: MarketingSite.contact_url, - text: t('idv.troubleshooting.options.contact_support', app_name: APP_NAME), - new_tab: true, - }, - ], - ) %> + <% c.troubleshooting_options do |tc| %> + <% tc.header { t('components.troubleshooting_options.default_heading') } %> + <% tc.option( + url: MarketingSite.help_url, + new_tab: true, + ).with_content(t('two_factor_authentication.read_about_two_factor_authentication')) %> + <% tc.option( + url: MarketingSite.contact_url, + new_tab: true, + ).with_content(t('idv.troubleshooting.options.contact_support', app_name: APP_NAME)) %> <% end %> <% end %> diff --git a/spec/components/block_link_component_spec.rb b/spec/components/block_link_component_spec.rb new file mode 100644 index 00000000000..296eab77271 --- /dev/null +++ b/spec/components/block_link_component_spec.rb @@ -0,0 +1,31 @@ +require 'rails_helper' + +RSpec.describe BlockLinkComponent, type: :component do + it 'renders a link' do + rendered = render_inline BlockLinkComponent.new(url: '/').with_content('Link Text') + + expect(rendered).to have_link('Link Text', href: '/') + expect(rendered).to have_css('.block-link.usa-link') + end + + context 'with tag options' do + it 'renders a link' do + rendered = render_inline BlockLinkComponent.new( + url: '/', + class: 'my-custom-class', + data: { foo: 'bar' }, + ) + + expect(rendered).to have_css('.block-link.usa-link.my-custom-class[data-foo="bar"]') + end + end + + context 'with new tab' do + it 'renders as external' do + rendered = render_inline BlockLinkComponent.new(url: '/', new_tab: true) + + expect(rendered).to have_css('.block-link.usa-link.usa-link--external[target=_blank]') + expect(rendered).to have_content(t('links.new_window')) + end + end +end diff --git a/spec/components/status_page_component_spec.rb b/spec/components/status_page_component_spec.rb index c6fdf34956d..201d90583bf 100644 --- a/spec/components/status_page_component_spec.rb +++ b/spec/components/status_page_component_spec.rb @@ -47,12 +47,16 @@ ) end - it 'renders action buttons' do + it 'renders troubleshooting options' do rendered = render_inline(StatusPageComponent.new) do |c| - c.troubleshooting_options { 'Troubleshooting' } + c.troubleshooting_options do |tc| + tc.header { 'Troubleshooting' } + tc.option(url: '/', new_tab: true) { 'Option' } + end end expect(rendered).to have_content('Troubleshooting') + expect(rendered).to have_link('Option', href: '/') end it 'raises error for unknown status' do diff --git a/spec/components/troubleshooting_options_component_spec.rb b/spec/components/troubleshooting_options_component_spec.rb new file mode 100644 index 00000000000..593c3dbcaa8 --- /dev/null +++ b/spec/components/troubleshooting_options_component_spec.rb @@ -0,0 +1,55 @@ +require 'rails_helper' + +RSpec.describe TroubleshootingOptionsComponent, type: :component do + it 'renders nothing if not given options' do + rendered = render_inline TroubleshootingOptionsComponent.new + + expect(rendered.children).to be_empty + end + + context 'with options' do + it 'renders troubleshooting options' do + rendered = render_inline(TroubleshootingOptionsComponent.new) do |c| + c.option(url: '/').with_content('Link Text') + end + + expect(rendered).to have_css('.troubleshooting-options') + expect(rendered).to have_link('Link Text', href: '/') + end + + context 'with tag options' do + it 'renders troubleshooting options' do + rendered = render_inline( + TroubleshootingOptionsComponent.new( + class: 'my-custom-class', + data: { foo: 'bar' }, + ), + ) { |c| c.option(url: '/').with_content('Link Text') } + + expect(rendered).to have_css('.troubleshooting-options.my-custom-class[data-foo="bar"]') + end + end + + context 'with header' do + it 'renders header' do + rendered = render_inline(TroubleshootingOptionsComponent.new) do |c| + c.header { 'Heading' } + c.option(url: '/') + end + + expect(rendered).to have_css('h2.troubleshooting-options__heading', text: 'Heading') + end + + context 'with custom heading level' do + it 'renders header' do + rendered = render_inline(TroubleshootingOptionsComponent.new) do |c| + c.header(heading_level: :h3) { 'Heading' } + c.option(url: '/') + end + + expect(rendered).to have_css('h3.troubleshooting-options__heading', text: 'Heading') + end + end + end + end +end diff --git a/spec/views/shared/_block_link.html.erb_spec.rb b/spec/views/shared/_block_link.html.erb_spec.rb deleted file mode 100644 index f83a4a76522..00000000000 --- a/spec/views/shared/_block_link.html.erb_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'rails_helper' - -describe 'shared/_block_link.html.erb' do - it 'raises an error if no block given' do - expect { render 'shared/block_link', url: '/example' }.to raise_error('no block content given') - end - - it 'renders a link to the given URL with the given text' do - render('shared/block_link', url: '/example') { 'Link Text' } - - expect(rendered).to have_selector('a[href="/example"]') - expect(rendered).to have_content('Link Text') - end - - it 'renders a link in a new tab' do - render('shared/block_link', url: '/example', new_tab: true) { 'Link Text' } - - expect(rendered).to have_selector('a[href="/example"][target="_blank"]') - expect(rendered).to have_content('Link Text') - expect(rendered).to have_content(t('links.new_window')) - end -end