From 04605d5d5b3efe2686d93cacf0c8355e396c64e4 Mon Sep 17 00:00:00 2001 From: decabeza Date: Wed, 6 May 2020 15:06:40 +0200 Subject: [PATCH] Add filters on budget investments index page Now it's easier to change the investments filter. Previously we had to go back to the budget index page, change the filter there, and then select one heading. Now the links to change the current filter in the budget index page aren't needed anymore. --- app/assets/javascripts/application.js | 2 + app/assets/javascripts/filter_selector.js | 8 ++++ app/assets/javascripts/forms.js | 6 +-- app/assets/stylesheets/application.scss | 1 + app/assets/stylesheets/filter_selector.scss | 14 ++++++ .../shared/filter_selector_component.html.erb | 5 +++ .../shared/filter_selector_component.rb | 22 +++++++++ app/helpers/budgets_helper.rb | 4 -- app/views/budgets/index.html.erb | 23 ---------- app/views/budgets/investments/index.html.erb | 2 + config/i18n-tasks.yml | 1 + config/locales/en/budgets.yml | 10 +++-- config/locales/es/budgets.yml | 10 +++-- .../shared/filter_selector_component_spec.rb | 15 +++++++ spec/system/budgets/budgets_spec.rb | 43 ------------------ spec/system/budgets/investments_spec.rb | 45 +++++++++++++++++++ 16 files changed, 132 insertions(+), 79 deletions(-) create mode 100644 app/assets/javascripts/filter_selector.js create mode 100644 app/assets/stylesheets/filter_selector.scss create mode 100644 app/components/shared/filter_selector_component.html.erb create mode 100644 app/components/shared/filter_selector_component.rb create mode 100644 spec/components/shared/filter_selector_component_spec.rb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f66510049..774ea5c78 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -70,6 +70,7 @@ //= require advanced_search //= require registration_form //= require suggest +//= require filter_selector //= require forms //= require valuation_budget_investment_form //= require embed_video @@ -133,6 +134,7 @@ var initialize_modules = function() { App.RegistrationForm.initialize(); App.Suggest.initialize(); App.Forms.initialize(); + App.FilterSelector.initialize(); App.ValuationBudgetInvestmentForm.initialize(); App.EmbedVideo.initialize(); App.FixedBar.initialize(); diff --git a/app/assets/javascripts/filter_selector.js b/app/assets/javascripts/filter_selector.js new file mode 100644 index 000000000..5f35db45c --- /dev/null +++ b/app/assets/javascripts/filter_selector.js @@ -0,0 +1,8 @@ +(function() { + "use strict"; + App.FilterSelector = { + initialize: function() { + App.Forms.submitOnChange(".filter-selector select"); + } + }; +}).call(this); diff --git a/app/assets/javascripts/forms.js b/app/assets/javascripts/forms.js index 822b2391b..0a2dfe107 100644 --- a/app/assets/javascripts/forms.js +++ b/app/assets/javascripts/forms.js @@ -8,8 +8,8 @@ } }); }, - submitOnChange: function() { - $("body").on("change", ".js-submit-on-change", function() { + submitOnChange: function(selector) { + $("body").on("change", selector, function() { $(this).closest("form").submit(); return false; }); @@ -58,7 +58,7 @@ }, initialize: function() { App.Forms.disableEnter(); - App.Forms.submitOnChange(); + App.Forms.submitOnChange(".js-submit-on-change"); App.Forms.toggleLink(); App.Forms.synchronizeInputs(); App.Forms.hideOrShowFieldsAfterSelection(); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 6ce3e2ede..e71bfaf26 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -12,6 +12,7 @@ @import "milestones"; @import "pages"; @import "dashboard"; +@import "filter_selector"; @import "legislation"; @import "legislation_process"; @import "legislation_process_form"; diff --git a/app/assets/stylesheets/filter_selector.scss b/app/assets/stylesheets/filter_selector.scss new file mode 100644 index 000000000..10de18b25 --- /dev/null +++ b/app/assets/stylesheets/filter_selector.scss @@ -0,0 +1,14 @@ +.filter-selector { + text-align: right; + + label { + display: inline-block; + font-size: $small-font-size; + margin-right: $line-height / 2; + padding-top: $line-height / 2; + } + + select { + width: auto; + } +} diff --git a/app/components/shared/filter_selector_component.html.erb b/app/components/shared/filter_selector_component.html.erb new file mode 100644 index 000000000..f8cdd6dee --- /dev/null +++ b/app/components/shared/filter_selector_component.html.erb @@ -0,0 +1,5 @@ +<%= form_tag({}, method: :get, enforce_utf8: false, class: "filter-selector") do %> + <%= query_parameters_tags %> + <%= label_tag "filter_selector_filter", t("#{i18n_namespace}.filter") %> + <%= select_tag "filter", options_for_select(filter_options, current_filter), id: "filter_selector_filter" %> +<% end %> diff --git a/app/components/shared/filter_selector_component.rb b/app/components/shared/filter_selector_component.rb new file mode 100644 index 000000000..6ea316a62 --- /dev/null +++ b/app/components/shared/filter_selector_component.rb @@ -0,0 +1,22 @@ +class Shared::FilterSelectorComponent < ApplicationComponent + delegate :valid_filters, :current_filter, to: :helpers + attr_reader :i18n_namespace + + def initialize(i18n_namespace:) + @i18n_namespace = i18n_namespace + end + + private + + def query_parameters_tags + safe_join(request.query_parameters.reject do |name, _| + ["page", "filter"].include?(name) + end.map do |name, value| + hidden_field_tag name, value, id: "filter_selector_#{name}" + end) + end + + def filter_options + valid_filters.map { |filter| [t("#{i18n_namespace}.filters.#{filter}"), filter] } + end +end diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb index d27fec415..fcf937fa0 100644 --- a/app/helpers/budgets_helper.rb +++ b/app/helpers/budgets_helper.rb @@ -1,8 +1,4 @@ module BudgetsHelper - def show_links_to_budget_investments(budget) - ["balloting", "reviewing_ballots", "finished"].include? budget.phase - end - def budget_voting_styles_select_options Budget::VOTING_STYLES.map do |style| [Budget.human_attribute_name("voting_style_#{style}"), style] diff --git a/app/views/budgets/index.html.erb b/app/views/budgets/index.html.erb index 73f53bbd9..bdd3d3504 100644 --- a/app/views/budgets/index.html.erb +++ b/app/views/budgets/index.html.erb @@ -55,29 +55,6 @@

<%= t("budgets.index.map") %>

<%= render_map(nil, "budgets", false, nil, @budgets_coordinates) %> - - <% end %> diff --git a/app/views/budgets/investments/index.html.erb b/app/views/budgets/investments/index.html.erb index c1cca7353..b8013cf14 100644 --- a/app/views/budgets/investments/index.html.erb +++ b/app/views/budgets/investments/index.html.erb @@ -70,6 +70,8 @@ <%= render("shared/order_links", i18n_namespace: "budgets.investments.index") %> <% end %> + <%= render Shared::FilterSelectorComponent.new(i18n_namespace: "budgets.investments.index") %> + <% if investments_default_view? %> <% @investments.each do |investment| %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index dd76cacec..71933acf9 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -127,6 +127,7 @@ ignore_missing: ## Consider these keys used: ignore_unused: - "budgets.phase.*" + - "budgets.investments.index.filter*" - "budgets.investments.index.orders.*" - "budgets.index.section_header.*" - "budgets.ballots.show.amount_available.*" diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 97468dd92..7931cc773 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -69,9 +69,6 @@ en: prev_phase: Previous phase current_phase: Current phase map: Budget investments' proposals located geographically - investment_proyects: List of all investment projects - unfeasible_investment_proyects: List of all unfeasible investment projects - not_selected_investment_proyects: List of all investment projects not selected for balloting finished_budgets: Finished participatory budgets see_results: See results section_footer: @@ -120,6 +117,13 @@ en: verified_only: "To create a new budget investment %{verify}." create: "Create a budget investment" not_logged_in: "To create a new budget investment you must %{sign_in} or %{sign_up}." + filter: "Filtering projects by" + filters: + not_unfeasible: "Not unfeasible" + selected: "Selected" + unfeasible: "Unfeasible" + unselected: "Unselected" + winners: "Winners" orders: random: random confidence_score: highest rated diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index 76a71eb2a..98eda05f9 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -69,9 +69,6 @@ es: prev_phase: Fase anterior current_phase: Fase actual map: Proyectos localizables geográficamente - investment_proyects: Ver lista completa de proyectos de gasto - unfeasible_investment_proyects: Ver lista de proyectos de gasto inviables - not_selected_investment_proyects: Ver lista de proyectos de gasto no seleccionados para la votación final finished_budgets: Presupuestos participativos terminados see_results: Ver resultados section_footer: @@ -120,6 +117,13 @@ es: verified_only: "Para crear un nuevo proyecto de gasto %{verify}." create: "Crear proyecto de gasto" not_logged_in: "Para crear un nuevo proyecto de gasto debes %{sign_in} o %{sign_up}." + filter: "Filtrando proyectos" + filters: + not_unfeasible: "No inviables" + selected: "Seleccionados" + unfeasible: "Inviables" + unselected: "No seleccionados" + winners: "Ganadores" orders: random: Aleatorios confidence_score: Mejor valorados diff --git a/spec/components/shared/filter_selector_component_spec.rb b/spec/components/shared/filter_selector_component_spec.rb new file mode 100644 index 000000000..6943a1a9e --- /dev/null +++ b/spec/components/shared/filter_selector_component_spec.rb @@ -0,0 +1,15 @@ +require "rails_helper" + +describe Shared::FilterSelectorComponent, type: :component do + it "renders a form with a select" do + component = Shared::FilterSelectorComponent.new(i18n_namespace: "budgets.investments.index") + allow(component).to receive(:url_for).and_return("/") + allow(component).to receive(:valid_filters).and_return(["unfeasible", "winners"]) + allow(component).to receive(:current_filter).and_return(nil) + + render_inline component + + expect(page).to have_select "Filtering projects by" + expect(page).to have_selector "form[method='get'].filter-selector select" + end +end diff --git a/spec/system/budgets/budgets_spec.rb b/spec/system/budgets/budgets_spec.rb index f3e0ba3a2..022ad0693 100644 --- a/spec/system/budgets/budgets_spec.rb +++ b/spec/system/budgets/budgets_spec.rb @@ -139,53 +139,10 @@ describe "Budgets" do expect(page).not_to have_link "#{heading.name} €1,000,000" expect(page).to have_content "#{heading.name} €1,000,000" - expect(page).to have_link "List of all investment projects", - href: budget_path(budget) - - expect(page).to have_link "List of all unfeasible investment projects", - href: budget_path(budget, filter: "unfeasible") - - expect(page).to have_link "List of all investment projects not selected for balloting", - href: budget_path(budget, filter: "unselected") - expect(page).to have_css("div.map") end end - scenario "Show investment links only on balloting or later" do - budget = create(:budget) - create(:budget_heading, budget: budget) - - allowed_phase_list.each do |phase| - budget.update!(phase: phase) - - visit budgets_path - - expect(page).to have_content(I18n.t("budgets.index.investment_proyects")) - expect(page).to have_content(I18n.t("budgets.index.unfeasible_investment_proyects")) - expect(page).to have_content(I18n.t("budgets.index.not_selected_investment_proyects")) - end - end - - scenario "Not show investment links earlier of balloting " do - budget = create(:budget) - create(:budget_heading, budget: budget) - phases_without_links = ["informing"] - not_allowed_phase_list = Budget::Phase::PHASE_KINDS - - phases_without_links - - allowed_phase_list - - not_allowed_phase_list.each do |phase| - budget.update!(phase: phase) - - visit budgets_path - - expect(page).not_to have_content(I18n.t("budgets.index.investment_proyects")) - expect(page).to have_content(I18n.t("budgets.index.unfeasible_investment_proyects")) - expect(page).not_to have_content(I18n.t("budgets.index.not_selected_investment_proyects")) - end - end - scenario "No budgets" do Budget.destroy_all diff --git a/spec/system/budgets/investments_spec.rb b/spec/system/budgets/investments_spec.rb index 73555d56a..a93c5a276 100644 --- a/spec/system/budgets/investments_spec.rb +++ b/spec/system/budgets/investments_spec.rb @@ -151,6 +151,51 @@ describe "Budget Investments" do end end + scenario "Index filter by status", :js do + budget.update!(phase: "finished") + + create(:budget_investment, :feasible, heading: heading, title: "Feasible investment") + create(:budget_investment, :unfeasible, heading: heading, title: "Unfeasible investment") + create(:budget_investment, :unselected, heading: heading, title: "Unselected investment") + create(:budget_investment, :selected, heading: heading, title: "Selected investment") + create(:budget_investment, :winner, heading: heading, title: "Winner investment") + + visit budget_investments_path(budget, heading_id: heading.id) + + expect(page).to have_select "Filtering projects by", + options: ["Not unfeasible", "Unfeasible", "Unselected", "Selected", "Winners"] + + select "Unfeasible", from: "Filtering projects by" + + expect(page).to have_css ".budget-investment", count: 1 + expect(page).to have_content "Unfeasible investment" + + select "Unselected", from: "Filtering projects by" + + expect(page).to have_css ".budget-investment", count: 2 + expect(page).to have_content "Unselected investment" + expect(page).to have_content "Feasible investment" + + select "Selected", from: "Filtering projects by" + + expect(page).to have_css ".budget-investment", count: 2 + expect(page).to have_content "Selected investment" + expect(page).to have_content "Winner investment" + + select "Winners", from: "Filtering projects by" + + expect(page).to have_css ".budget-investment", count: 1 + expect(page).to have_content "Winner investment" + + select "Not unfeasible", from: "Filtering projects by" + + expect(page).to have_css ".budget-investment", count: 4 + expect(page).to have_content "Selected investment" + expect(page).to have_content "Unselected investment" + expect(page).to have_content "Feasible investment" + expect(page).to have_content "Winner investment" + end + context("Search") do scenario "Search by text" do investment1 = create(:budget_investment, heading: heading, title: "Get Schwifty")