Browse Source

Cldc 1010 filter (#449)

* Add a filter for status

* Add styling, separate filter tab into a file

* Add mobile styling

* use session instead of cookies

* Get statuses from case_log

* remove explicit builder set

* set filter from within get index

* style new log button

* refactor tests
pull/458/head
kosiakkatrina 3 years ago committed by GitHub
parent
commit
ce0752addb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      app/controllers/case_logs_controller.rb
  2. 3
      app/frontend/assets/images/icon-cross.svg
  3. 16
      app/frontend/controllers/filter_controller.js
  4. 3
      app/frontend/controllers/index.js
  5. 123
      app/frontend/styles/_filter.scss
  6. 7
      app/frontend/styles/application.scss
  7. 14
      app/helpers/filters_helper.rb
  8. 1
      app/models/case_log.rb
  9. 32
      app/views/case_logs/_log_filters.erb
  10. 1
      app/views/case_logs/_log_list.html.erb
  11. 17
      app/views/case_logs/index.html.erb
  12. 26
      spec/helpers/filters_helper_spec.rb
  13. 35
      spec/requests/case_logs_controller_spec.rb

16
app/controllers/case_logs_controller.rb

@ -7,7 +7,9 @@ class CaseLogsController < ApplicationController
before_action :find_resource, except: %i[create index edit]
def index
@pagy, @case_logs = pagy(current_user.case_logs)
set_session_filters if params[:status].present?
@pagy, @case_logs = pagy(filtered_case_logs)
respond_to do |format|
format.html
@ -117,4 +119,16 @@ private
def find_resource
@case_log = CaseLog.find_by(id: params[:id])
end
def filtered_case_logs
user_case_logs = current_user.case_logs
status_filter = JSON.parse(session[:case_logs_filters])["status"] if session[:case_logs_filters].present?
return user_case_logs unless status_filter
user_case_logs.filter_by_status(status_filter)
end
def set_session_filters
session[:case_logs_filters] = { status: params[:status] }.to_json
end
end

3
app/frontend/assets/images/icon-cross.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14">
<path d="M14 1.4L12.6 0 7 5.6 1.4 0 0 1.4 5.6 7 0 12.6 1.4 14 7 8.4l5.6 5.6 1.4-1.4L8.4 7z"/>
</svg>

After

Width:  |  Height:  |  Size: 167 B

16
app/frontend/controllers/filter_controller.js

@ -0,0 +1,16 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
toggleFilter() {
let filter_panel = document.getElementById("filter-panel");
let toggle_filter_button = document.getElementById("toggle-filter-button");
if (filter_panel.style.display === "none" || !filter_panel.style.display) {
filter_panel.style.display = "block";
toggle_filter_button.innerText = "Hide filters";
} else {
filter_panel.style.display = "none";
toggle_filter_button.innerText = "Show filters";
}
}
}

3
app/frontend/controllers/index.js

@ -14,3 +14,6 @@ application.register("govukfrontend", GovukfrontendController)
import NumericQuestionController from "./numeric_question_controller.js"
application.register("numeric-question", NumericQuestionController)
import FilterController from "./filter_controller.js"
application.register("filter", FilterController)

123
app/frontend/styles/_filter.scss

@ -0,0 +1,123 @@
.app-filter {
background-color: govuk-colour("light-grey");
margin-bottom: govuk-spacing(2);
}
.app-filter__header {
background-color: govuk-colour("light-grey");
display: flex;
justify-content: space-between;
padding: govuk-spacing(2) govuk-spacing(3);
position: sticky;
top: 0;
z-index: 10;
@include govuk-media-query($from: desktop) {
position: static;
}
[class^="govuk-heading-"] {
margin-bottom: 0;
}
}
.app-filter__content {
padding: govuk-spacing(1) govuk-spacing(3) govuk-spacing(4);
}
.app-filter__close {
@include govuk-font(19);
-webkit-appearance: none;
background-color: transparent;
border: none;
border-radius: 0;
color: $govuk-text-colour;
cursor: pointer;
margin: #{govuk-spacing(1) * -1};
padding: govuk-spacing(1);
&:focus {
background-color: $govuk-focus-colour;
color: $govuk-focus-text-colour;
box-shadow: 0 -2px $govuk-focus-colour, 0 4px $govuk-focus-text-colour;
outline: none;
}
// Fix unwanted button padding in Firefox
&::-moz-focus-inner {
padding: 0;
border: 0;
}
&::before {
background-image: url("../assets/images/icon-cross.svg");
content: "";
display: inline-block;
height: 14px;
margin-right: govuk-spacing(1);
position: relative;
top: -2px; // Alignment tweak
vertical-align: middle;
width: 14px;
}
@include govuk-media-query(desktop) {
display: none;
}
}
.app-filter-layout {
@include govuk-clearfix;
}
.app-filter-toggle__button {
min-width: 128px;
@include govuk-media-query(desktop) {
display: none !important;
}
}
.app-filter-layout__filter {
@include govuk-media-query(desktop) {
float: left;
min-width: govuk-grid-width("one-quarter");
}
display: none;
@include govuk-media-query(desktop) {
display: block;
}
}
.js-enabled .app-filter-layout__filter {
outline: 0 none;
@include govuk-media-query($until: desktop) {
background-color: govuk-colour("light-grey");
bottom: govuk-spacing(1);
border: 1px solid govuk-colour("mid-grey");
max-width: 310px;
min-width: 260px;
width: 100%;
overflow-y: scroll;
position: fixed;
right: govuk-spacing(1);
top: govuk-spacing(1);
z-index: 100;
&:focus {
outline: $govuk-focus-width solid $govuk-focus-colour;
}
}
}
.app-filter-layout__content {
@include govuk-media-query(desktop) {
float: right;
max-width: calc(
#{govuk-grid-width("three-quarters")} - #{govuk-spacing(6)}
);
width: 100%;
}
}

7
app/frontend/styles/application.scss

@ -24,6 +24,7 @@ $govuk-global-styles: true;
@import "template";
@import "pagination";
@import "panel";
@import "filter";
// App utilities
.app-\!-colour-muted {
@ -42,3 +43,9 @@ $govuk-global-styles: true;
.govuk-tag {
white-space: nowrap;
}
.button_to {
@include govuk-media-query($until: tablet) {
width: 100%;
}
}

14
app/helpers/filters_helper.rb

@ -0,0 +1,14 @@
module FiltersHelper
def filter_selected?(filter)
return true unless session[:case_logs_filters]
selected_filters = JSON.parse(session[:case_logs_filters])
selected_filters["status"].present? && selected_filters["status"].include?(filter.to_s)
end
def status_filters
statuses = {}
CaseLog.statuses.keys.map { |status| statuses[status] = status.humanize }
statuses
end
end

1
app/models/case_log.rb

@ -34,6 +34,7 @@ class CaseLog < ApplicationRecord
belongs_to :managing_organisation, class_name: "Organisation"
scope :for_organisation, ->(org) { where(owning_organisation: org).or(where(managing_organisation: org)) }
scope :filter_by_status, ->(status) { where status: status }
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[postcode_known la_known first_time_property_let_as_social_housing tenant_code propcode].freeze

32
app/views/case_logs/_log_filters.erb

@ -0,0 +1,32 @@
<div class="app-filter-layout__filter" tabindex="-1" id="filter-panel">
<div class="app-filter" >
<div class="app-filter__header">
<h2 class="govuk-heading-m">Filters</h2>
<button
class="app-filter__close"
type="button"
data-controller="filter"
data-action="click->filter#toggleFilter">
Close
</button>
</div>
<div class="app-filter__content">
<div class="govuk-form-group app-filter__group">
<%= form_with url: "/logs", html: { method: :get } do |f| %>
<%= f.govuk_check_boxes_fieldset :status, legend: { text: "Status", size: "s"} do %>
<div class="govuk-checkboxes govuk-checkboxes--small" data-module="govuk-checkboxes">
<% statuses = status_filters %>
<% statuses.map do |key, option| %>
<%= f.govuk_check_box "status", "#{key}",
label: { text: option },
checked: filter_selected?(key),
size: "s" %>
<% end %>
</div>
<% end %>
<%= f.govuk_submit "Apply filters", class: "govuk-!-margin-top-4" %>
<% end %>
</div>
</div>
</div>
</div>

1
app/views/case_logs/_log_list.html.erb

@ -59,3 +59,4 @@
</table>
</section>
</figure>

17
app/views/case_logs/index.html.erb

@ -3,13 +3,28 @@
<h1 class="govuk-heading-l">
<%= content_for(:title) %>
</h1>
<div class="app-filter-layout" data-module="filter-layout" data-module-started="true">
<div class="govuk-button-group">
<button
aria-expanded="true"
aria-has-popup="true"
class="govuk-button govuk-button--secondary app-filter-toggle__button"
type="button"
id="toggle-filter-button"
data-controller="filter"
data-action="click->filter#toggleFilter"
>Show filters
</button>
<%= govuk_button_to "Create a new lettings log", case_logs_path %>
<%#= govuk_link_to "Upload logs", bulk_upload_case_logs_path %>
</div>
<%= render partial: "log_filters"%>
<% if @case_logs.present? %>
<div class="app-filter-layout__content">
<%= render partial: "log_list", locals: { case_logs: @case_logs, title: "Logs", pagy: @pagy } %>
<%== render partial: 'pagy/nav', locals: { pagy: @pagy, item_name: "logs" } %>
</div>
<% end %>
</div>

26
spec/helpers/filters_helper_spec.rb

@ -0,0 +1,26 @@
require "rails_helper"
RSpec.describe FiltersHelper do
describe "#filter_selected?" do
context "when no filters are selected" do
it "returns true for all filters" do
expect(filter_selected?("completed")).to be_truthy
expect(filter_selected?("in_progress")).to be_truthy
end
end
context "when one filter is selected" do
before do
session[:case_logs_filters] = { "status": "in_progress" }.to_json
end
it "returns false for non selected filters" do
expect(filter_selected?("completed")).to be_falsey
end
it "returns true for selected filter" do
expect(filter_selected?("in_progress")).to be_truthy
end
end
end
end

35
spec/requests/case_logs_controller_spec.rb

@ -172,6 +172,41 @@ RSpec.describe CaseLogsController, type: :request do
expect(page).to have_content("Owning organisation")
expect(page).to have_content("Managing organisation")
end
context "when filtering" do
let!(:in_progress_case_log) do
FactoryBot.create(:case_log, :in_progress,
owning_organisation: organisation,
managing_organisation: organisation)
end
let!(:completed_case_log) do
FactoryBot.create(:case_log, :completed,
owning_organisation: organisation,
managing_organisation: organisation)
end
it "shows case logs for multiple selected statuses" do
get "/logs?status[]=in_progress&status[]=completed", headers: headers, params: {}
expect(page).to have_link(in_progress_case_log.id.to_s)
expect(page).to have_link(completed_case_log.id.to_s)
end
it "shows case logs for one selected status" do
get "/logs?status[]=in_progress", headers: headers, params: {}
expect(page).to have_link(in_progress_case_log.id.to_s)
expect(page).not_to have_link(completed_case_log.id.to_s)
end
it "does not reset the filters" do
get "/logs?status[]=in_progress", headers: headers, params: {}
expect(page).to have_link(in_progress_case_log.id.to_s)
expect(page).not_to have_link(completed_case_log.id.to_s)
get "/logs", headers: headers, params: {}
expect(page).to have_link(in_progress_case_log.id.to_s)
expect(page).not_to have_link(completed_case_log.id.to_s)
end
end
end
context "when the user is not a customer support user" do

Loading…
Cancel
Save