diff --git a/app/frontend/controllers/filter_controller.js b/app/frontend/controllers/filter_controller.js deleted file mode 100644 index ec55b72b5..000000000 --- a/app/frontend/controllers/filter_controller.js +++ /dev/null @@ -1,16 +0,0 @@ -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"; - } - } -} diff --git a/app/frontend/controllers/filter_layout_controller.js b/app/frontend/controllers/filter_layout_controller.js new file mode 100644 index 000000000..a44ba5c45 --- /dev/null +++ b/app/frontend/controllers/filter_layout_controller.js @@ -0,0 +1,26 @@ +import { Controller } from "@hotwired/stimulus"; +import { FilterToggle } from "../modules/filter_toggle.js" + +export default class extends Controller { + connect() { + const filterToggle = new FilterToggle({ + bigModeMediaQuery: '(min-width: 48.0625em)', + closeButton: { + container: this.element.querySelector('.app-filter__header'), + text: 'Close' + }, + filter: { + container: this.element.querySelector('.app-filter-layout__filter') + }, + startHidden: false, + toggleButton: { + container: this.element.querySelector('.app-filter-toggle'), + showText: 'Show filters', + hideText: 'Hide filters', + classes: 'govuk-button--secondary' + } + }) + + filterToggle.init() + } +} diff --git a/app/frontend/controllers/index.js b/app/frontend/controllers/index.js index 2227bea58..7c597baa9 100644 --- a/app/frontend/controllers/index.js +++ b/app/frontend/controllers/index.js @@ -15,5 +15,5 @@ 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) +import FilterLayoutController from "./filter_layout_controller.js" +application.register("filter-layout", FilterLayoutController) diff --git a/app/frontend/modules/filter_toggle.js b/app/frontend/modules/filter_toggle.js new file mode 100644 index 000000000..917fed62b --- /dev/null +++ b/app/frontend/modules/filter_toggle.js @@ -0,0 +1,112 @@ +export class FilterToggle { + constructor (options) { + this.options = options + this.container = this.options.toggleButton.container + } + + setupResponsiveChecks () { + this.mq = window.matchMedia(this.options.bigModeMediaQuery) + this.mq.addListener(this.checkMode.bind(this)) + this.checkMode(this.mq) + } + + checkMode (mq) { + if (mq.matches) { + this.enableBigMode() + } else { + this.enableSmallMode() + } + } + + enableBigMode () { + this.showMenu() + this.removeMenuButton() + this.removeCloseButton() + } + + enableSmallMode () { + this.options.filter.container.setAttribute("tabindex", "-1") + this.hideMenu() + this.addMenuButton() + this.addCloseButton() + } + + addCloseButton () { + if (this.options.closeButton) { + this.closeButton = document.createElement("button") + this.closeButton.classList.add("app-filter__close") + this.closeButton.innerText = this.options.closeButton.text + this.closeButton.type = 'button' + this.closeButton.addEventListener('click', this.onCloseClick.bind(this)) + + this.options.closeButton.container.append(this.closeButton) + } + } + + onCloseClick () { + this.hideMenu() + this.menuButton.focus() + } + + removeCloseButton () { + if (this.closeButton) { + this.closeButton.remove() + this.closeButton = null + } + } + + addMenuButton () { + this.menuButton = document.createElement("button") + this.menuButton.setAttribute("aria-expanded", "false") + this.menuButton.setAttribute("aria-has-popup", "true") + this.menuButton.classList.add("govuk-button", this.options.toggleButton.classes, "app-filter-toggle__button") + this.menuButton.innerText = this.options.toggleButton.showText + this.menuButton.type = "button" + this.menuButton.addEventListener("click", this.onMenuButtonClick.bind(this)) + + this.options.toggleButton.container.prepend(this.menuButton) + } + + removeMenuButton () { + if (this.menuButton) { + this.menuButton.remove() + this.menuButton = null + } + } + + hideMenu () { + if (this.menuButton) { + this.menuButton.setAttribute("aria-expanded", "false") + this.menuButton.innerText = this.options.toggleButton.showText + } + this.options.filter.container.setAttribute("hidden", true) + } + + showMenu () { + if (this.menuButton) { + this.menuButton.setAttribute("aria-expanded", "true") + this.menuButton.innerText = this.options.toggleButton.hideText + } + this.options.filter.container.removeAttribute("hidden") + } + + onMenuButtonClick () { + this.toggle() + } + + toggle () { + if (this.options.filter.container.hidden) { + this.showMenu() + this.options.filter.container.focus() + } else { + this.hideMenu() + } + } + + init () { + this.setupResponsiveChecks() + if (this.options.startHidden) { + this.hideMenu() + } + } +} diff --git a/app/frontend/styles/_filter-layout.scss b/app/frontend/styles/_filter-layout.scss new file mode 100644 index 000000000..5dffc0e67 --- /dev/null +++ b/app/frontend/styles/_filter-layout.scss @@ -0,0 +1,42 @@ +.app-filter-layout { + @include govuk-clearfix; +} + +.app-filter-layout__filter { + @include govuk-media-query(desktop) { + float: left; + min-width: govuk-grid-width("one-quarter"); + } +} + +.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%; + } +} diff --git a/app/frontend/styles/_filter.scss b/app/frontend/styles/_filter.scss index 46783534f..d98ed5099 100644 --- a/app/frontend/styles/_filter.scss +++ b/app/frontend/styles/_filter.scss @@ -61,63 +61,41 @@ 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"); +.app-filter__group { + border-bottom: 1px solid $govuk-border-colour; + margin-bottom: govuk-spacing(3); + padding-bottom: govuk-spacing(2); + + &:last-child { + border-bottom: none; + margin-bottom: 0; + padding-bottom: 0; } - display: none; - @include govuk-media-query(desktop) { - display: block; + .govuk-fieldset__legend { + margin-bottom: govuk-spacing(1); } -} -.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; + .govuk-form-group { + margin-bottom: 0; + + &:last-child { + margin-bottom: 0; } } -} -.app-filter-layout__content { - @include govuk-media-query(desktop) { - float: right; - max-width: calc( - #{govuk-grid-width("three-quarters")} - #{govuk-spacing(6)} - ); - width: 100%; + .govuk-checkboxes__label, + .govuk-radios__label { + @include govuk-font(16); + + &::before { + background-color: govuk-colour("white"); + } } } diff --git a/app/frontend/styles/application.scss b/app/frontend/styles/application.scss index 3d01074ec..258c21e4d 100644 --- a/app/frontend/styles/application.scss +++ b/app/frontend/styles/application.scss @@ -1,9 +1,9 @@ @function frontend-font-url($filename) { - @return url("~assets/fonts/"+$filename); + @return url("~assets/fonts/" + $filename); } @function frontend-image-url($filename) { - @return url("~assets/images/"+$filename); + @return url("~assets/images/" + $filename); } $govuk-font-url-function: frontend-font-url; @@ -15,6 +15,8 @@ $govuk-global-styles: true; @import "accessible-autocomplete"; @import "button"; @import "figure"; +@import "filter"; +@import "filter-layout"; @import "input"; @import "related-navigation"; @import "section-skip-link"; @@ -24,7 +26,6 @@ $govuk-global-styles: true; @import "template"; @import "pagination"; @import "panel"; -@import "filter"; // App utilities .app-\!-colour-muted { diff --git a/app/views/case_logs/_log_filters.erb b/app/views/case_logs/_log_filters.erb index 888028700..f76e8b876 100644 --- a/app/views/case_logs/_log_filters.erb +++ b/app/views/case_logs/_log_filters.erb @@ -1,25 +1,16 @@ -
-
-
-

Filters

- -
-
-
- <%= form_with url: "/logs", html: { method: :get } do |f| %> - <% years = {"2021": "2021/22", "2022": "2022/23"} %> - <%= render partial: "filters/checkbox_filter", locals: { f: f, options: years, label: "Collection year", category: "years" } %> - <%= render partial: "filters/checkbox_filter", locals: { f: f, options: status_filters, label: "Status", category: "status" } %> - <%= render partial: "filters/checkbox_filter", locals: { f: f, options: {"all": "All", "yours": "Yours"}, label: "Logs", category: "user" } %> - <%= f.govuk_submit "Apply filters", class: "govuk-!-margin-top-4" %> - <% end %> -
-
+
+
+
+

Filters

+
+ <%= form_with url: "/logs", html: { method: :get } do |f| %> + <% years = {"2021": "2021/22", "2022": "2022/23"} %> + <%= render partial: "filters/checkbox_filter", locals: { f: f, options: years, label: "Collection year", category: "years" } %> + <%= render partial: "filters/checkbox_filter", locals: { f: f, options: status_filters, label: "Status", category: "status" } %> + <%= render partial: "filters/checkbox_filter", locals: { f: f, options: {"all": "All", "yours": "Yours"}, label: "Logs", category: "user" } %> + <%= f.govuk_submit "Apply filters", class: "govuk-!-margin-bottom-0" %> + <% end %> +
+
diff --git a/app/views/case_logs/index.html.erb b/app/views/case_logs/index.html.erb index 8060f3c99..c3ddfb7b8 100644 --- a/app/views/case_logs/index.html.erb +++ b/app/views/case_logs/index.html.erb @@ -3,22 +3,12 @@

<%= content_for(:title) %>

-
-
- +
+
<%= govuk_button_to "Create a new lettings log", case_logs_path %> <%#= govuk_link_to "Upload logs", bulk_upload_case_logs_path %>
- + <%= render partial: "log_filters"%> <% if @case_logs.present? %>
diff --git a/app/views/filters/_checkbox_filter.html.erb b/app/views/filters/_checkbox_filter.html.erb index c49156be7..aa2fc71a4 100644 --- a/app/views/filters/_checkbox_filter.html.erb +++ b/app/views/filters/_checkbox_filter.html.erb @@ -1,10 +1,8 @@ -<%= f.govuk_check_boxes_fieldset category.to_sym, legend: { text: label, size: "s"} do %> -
- <% options.map do |key, option| %> - <%= f.govuk_check_box category, "#{key}", - label: { text: option }, - checked: filter_selected?(category, key), - size: "s" %> - <% end %> -
+<%= f.govuk_check_boxes_fieldset category.to_sym, legend: { text: label, size: "s"}, small: true, form_group: { classes: "app-filter__group" } do %> + <% options.map do |key, option| %> + <%= f.govuk_check_box category, "#{key}", + label: { text: option }, + checked: filter_selected?(category, key), + size: "s" %> + <% end %> <% end %>