diff --git a/app/cells/state_selector/show.haml b/app/cells/state_selector/show.haml new file mode 100644 index 0000000..f0f79bc --- /dev/null +++ b/app/cells/state_selector/show.haml @@ -0,0 +1,7 @@ += form_with url: url, local: true, class: 'state-selector' do |form| + %p + = label_tag :states, _('Choose one or more states:') + - locale_state_options = states_for_menu.sort_by {|state| _(state.name)}.map {|state| [_(state.name), state.abbr]} + = select_tag :states, options_for_select(locale_state_options, @states&.map(&:abbr)), multiple: true + %p + = submit_tag _('Go') diff --git a/app/cells/state_selector_cell.rb b/app/cells/state_selector_cell.rb new file mode 100644 index 0000000..a52677e --- /dev/null +++ b/app/cells/state_selector_cell.rb @@ -0,0 +1,22 @@ +class StateSelectorCell < Cell::ViewModel + include ActionView::Helpers::FormHelper + include ActionView::Helpers::FormOptionsHelper + + attr_reader :states, :url + + def initialize(states, options) + super + @states = states + @url = options[:url] + end + + def show + render + end + + private + + def states_for_menu + @states_for_menu ||= State.all + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7989dd0..de6be79 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,5 +1,2 @@ module ApplicationHelper - def states - State.all - end end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index f98b407..3d24240 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -13,7 +13,7 @@ %h1 = link_to '/' do = h(_ '%{span}COVID-19%{_span} Tracking Charts by U.S. State') % {span: ''.html_safe, _span: ''.html_safe} - %nav= render 'shared/state_selector', states: states # TODO: consider Cells or something here + %nav= cell(:state_selector, @states, url: choose_states_path) %main - if content_for :page_title diff --git a/app/views/shared/_state_selector.html.haml b/app/views/shared/_state_selector.html.haml deleted file mode 100644 index 41f91a6..0000000 --- a/app/views/shared/_state_selector.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -= form_with url: choose_states_path, local: true, class: 'state-selector' do - %p - = label_tag :states,_('Choose one or more states:') - - locale_state_options = states.sort_by {|state| _(state.name)}.map {|state| [_(state.name), state.abbr]} - = select_tag :states, options_for_select(locale_state_options, @states&.map(&:abbr)), multiple: true - %p - = submit_tag _('Go') diff --git a/spec/cells/state_selector_cell_spec.rb b/spec/cells/state_selector_cell_spec.rb new file mode 100644 index 0000000..e8406e4 --- /dev/null +++ b/spec/cells/state_selector_cell_spec.rb @@ -0,0 +1,71 @@ +require 'rails_helper' + +RSpec.describe StateSelectorCell, type: :cell do + let(:states) { [] } + let(:url) { nil } + let(:options) { {url: url} } + + subject { described_class.new states, options } + + context 'constructor' do + it { is_expected.to be_a_kind_of Cell::ViewModel } + + it 'includes form helpers' do + ['FormHelper', 'FormOptionsHelper'].each do |helper| + expect(subject).to be_a_kind_of "ActionView::Helpers::#{helper}".constantize + end + end + end + + context '#states' do + let(:states) { State.all.sample rand(2..5) } + + it 'returns the given states' do + expect(subject.states).to be == states + end + end + + context '#url' do + let(:url) { File.join *Faker::Lorem.words(rand 2..5) } + + it 'returns the given URL' do + expect(subject.url).to be == url + end + end + + context '#show' do + controller Class.new(ApplicationController) # see https://github.com/trailblazer/rspec-cells#url-helpers + + let(:url) { File.join *Faker::Lorem.words(rand 2..5) } + let(:menu) { 'select[multiple][name="states[]"]' } + + subject { Capybara.string cell(described_class, states, options).call } + + it 'renders a form that will submit to the given URL' do + expect(subject).to have_selector "form[action='#{url}']" + end + + it 'renders a multi-select with all the states as options, sorted by nam,e' do + subject.find menu do |menu| + selector = './/' + State.all.sort_by(&:name).map {|state| "option[@value='#{state.abbr}'][text()='#{state.name}']"}.join('/following-sibling::') + expect(menu).to have_xpath selector + end + end + + context 'states given' do + let(:states) { State.all.sample rand(2..5) } + + it 'marks all the states given as selected' do + subject.find(menu) do |menu| + expect(menu.all('option[selected]').map &:text).to match_array states.map(&:name) + end + end + end + + context 'no states given' do + it 'marks no states as selected' do + expect(subject.find menu).not_to have_css 'option[selected]' + end + end + end +end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb deleted file mode 100644 index 37b3e08..0000000 --- a/spec/helpers/application_helper_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'rails_helper' - -RSpec.describe ApplicationHelper, type: :helper do - describe '#states' do - it 'returns all the states' do - expect(helper.states).to be == State.all - end - end -end