From 51a8e8a9cae103f7063a925a8f37d4dde5a7190e Mon Sep 17 00:00:00 2001 From: pezholio Date: Wed, 6 Nov 2024 14:10:04 +0000 Subject: [PATCH] Make filters collapsible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This builds on the [Accordion component](https://components.publishing.service.gov.uk/component-guide/accordion) and applies some custom styling. I’ve also updated the tests to check the panels are expanded when a filter is present. --- app/assets/javascripts/application.js | 1 + .../content_block_manager/application.scss | 1 + .../components/_filter-options-component.scss | 62 +++++++++++++ .../index/filter_options_component.html.erb | 88 ++++++++++++------ .../index/filter_options_component_test.rb | 89 ++++++++++++------- 5 files changed, 181 insertions(+), 60 deletions(-) create mode 100644 lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/components/_filter-options-component.scss diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b82168bdd78..151cffed3b0 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -2,6 +2,7 @@ //= require govuk_publishing_components/dependencies //= require govuk_publishing_components/analytics-ga4 +//= require govuk_publishing_components/components/accordion //= require govuk_publishing_components/components/copy-to-clipboard //= require govuk_publishing_components/components/govspeak //= require govuk_publishing_components/components/reorderable-list diff --git a/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/application.scss b/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/application.scss index 75904c4f875..3113970a9b8 100644 --- a/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/application.scss +++ b/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/application.scss @@ -1 +1,2 @@ +@import "components/filter-options-component"; @import "components/timeline-component"; diff --git a/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/components/_filter-options-component.scss b/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/components/_filter-options-component.scss new file mode 100644 index 00000000000..2fd9b48c618 --- /dev/null +++ b/lib/engines/content_block_manager/app/assets/stylesheets/content_block_manager/components/_filter-options-component.scss @@ -0,0 +1,62 @@ +.app-c-content-block-manager-filter-options { + .govuk-accordion__section-toggle, + .govuk-accordion__controls .govuk-accordion-nav__chevron { + @include govuk-visually-hidden; + } + + @mixin chevron($dir) { + &:before { + border-style: solid; + border-width: 0.2em 0.2em 0 0; + content: ""; + display: inline-block; + height: 0.45em; + left: 0.15em; + position: relative; + vertical-align: top; + width: 0.45em; + + @include govuk-responsive-margin(3, "right"); + + @if $dir == "down" { + top: 0.2em; + transform: rotate(135deg); + } + + @if $dir == "up" { + top: 0.35em; + transform: rotate(-45deg); + } + } + } + + .govuk-accordion__controls { + .govuk-accordion__show-all { + .govuk-accordion__show-all-text { + @include govuk-font($size: 19, $weight: bold); + color: govuk-colour("black"); + @include chevron("down"); + } + + &[aria-expanded="true"] { + .govuk-accordion__show-all-text { + @include chevron("up"); + } + } + } + } + + .govuk-accordion__section-button { + .govuk-accordion__section-heading-text { + @include govuk-font($size: 19, $weight: bold); + + @include chevron("down"); + } + + &[aria-expanded="true"] { + .govuk-accordion__section-heading-text { + @include chevron("up"); + } + } + } +} diff --git a/lib/engines/content_block_manager/app/components/content_block_manager/content_block/document/index/filter_options_component.html.erb b/lib/engines/content_block_manager/app/components/content_block_manager/content_block/document/index/filter_options_component.html.erb index 04e7783ef39..0ffbc4c57ab 100644 --- a/lib/engines/content_block_manager/app/components/content_block_manager/content_block/document/index/filter_options_component.html.erb +++ b/lib/engines/content_block_manager/app/components/content_block_manager/content_block/document/index/filter_options_component.html.erb @@ -1,31 +1,63 @@ -<%= form_with url: helpers.content_block_manager.content_block_manager_content_block_documents_path, method: :get do %> - <%= render "govuk_publishing_components/components/input", { - label: { - text: "Keyword", - bold: true, - }, - hint: 'For example, "driving standards"', - name: "keyword", - id: "keyword_filter", - value: !@filters.nil? && @filters[:keyword], - } %> - - <%= render "govuk_publishing_components/components/checkboxes", { - heading: "Content block type", - heading_size: "s", - no_hint_text: true, - id: "block_type", - name: "block_type[]", - items: items_for_block_type, - } %> - - <%= render "components/select_with_search", { - id: "lead_organisation", - name: "lead_organisation", - label: "Lead organisation", - heading_size: "s", - include_blank: false, - options: options_for_lead_organisation, +<%= form_with url: helpers.content_block_manager.content_block_manager_content_block_documents_path, method: :get, class: "app-c-content-block-manager-filter-options" do %> + <%= render "govuk_publishing_components/components/accordion", { + disable_ga4: true, + items: [ + { + heading: { + text: "Search by keyword", + }, + content: { + html: ( + render "govuk_publishing_components/components/input", { + label: { + text: "Keyword", + bold: true, + }, + hint: 'For example, "driving standards"', + name: "keyword", + id: "keyword_filter", + value: @filters.present? && @filters[:keyword], + } + ), + }, + expanded: @filters&.fetch(:keyword, nil).present?, + }, + { + heading: { + text: "Content block type", + }, + content: { + html: ( + render "govuk_publishing_components/components/checkboxes", { + heading: "Content block type", + visually_hide_heading: true, + heading_size: "s", + no_hint_text: true, + id: "block_type", + name: "block_type[]", + items: items_for_block_type, + } + ), + }, + expanded: @filters&.fetch(:block_type, nil).present?, + }, + { + heading: { + text: "Lead organisation", + }, + content: { + html: ( + render "components/select_with_search", { + id: "lead_organisation", + name: "lead_organisation", + include_blank: false, + options: options_for_lead_organisation, + } + ), + }, + expanded: @filters&.fetch(:lead_organisation, nil).present?, + }, + ], } %> <%= render "govuk_publishing_components/components/button", { diff --git a/lib/engines/content_block_manager/test/components/content_block/document/index/filter_options_component_test.rb b/lib/engines/content_block_manager/test/components/content_block/document/index/filter_options_component_test.rb index 7f30d92aea7..c062f3f0410 100644 --- a/lib/engines/content_block_manager/test/components/content_block/document/index/filter_options_component_test.rb +++ b/lib/engines/content_block_manager/test/components/content_block/document/index/filter_options_component_test.rb @@ -1,59 +1,84 @@ require "test_helper" class ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponentTest < ViewComponent::TestCase - test "adds value of keyword to text input from filter" do - render_inline(ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( - filters: { keyword: "ministry defense" }, - )) + extend Minitest::Spec::DSL + let(:helper_mock) { mock } + + before do + ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.any_instance.stubs(:helpers).returns(helper_mock) + helper_mock.stubs(:content_block_manager).returns(helper_mock) + helper_mock.stubs(:content_block_manager_content_block_documents_path).returns("path") + + helper_mock.stubs(:taggable_organisations_container).returns( + [["Department of Placeholder", 1], ["Ministry of Example", 2]], + ) + + ContentBlockManager::ContentBlock::Schema.stubs(:valid_schemas).returns(%w[email_address postal_address]) + end + + it "collapses all sections by default" do + render_inline( + ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( + filters: {}, + ), + ) + assert_selector ".govuk-accordion__section--expanded", count: 0 + end + + it "adds value of keyword to text input from filter" do + render_inline( + ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( + filters: { keyword: "ministry defense" }, + ), + ) + + assert_selector ".govuk-accordion__section--expanded", count: 1 + assert_selector ".govuk-accordion__section--expanded", text: "Keyword" assert_selector "input[name='keyword'][value='ministry defense']" end - test "renders checkbox items for all valid schemas" do - ContentBlockManager::ContentBlock::Schema.expects(:valid_schemas).returns(%w[email_address postal_address]) - render_inline(ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( - filters: {}, - )) + it "renders checkbox items for all valid schemas" do + render_inline( + ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( + filters: {}, + ), + ) assert_selector "input[type='checkbox'][name='block_type[]'][value='email_address']" assert_selector "input[type='checkbox'][name='block_type[]'][value='postal_address']" end - test "checks checkbox items if checked in filters" do - ContentBlockManager::ContentBlock::Schema.expects(:valid_schemas).returns(%w[email_address postal_address]) - render_inline(ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( - filters: { block_type: %w[email_address] }, - )) + it "checks checkbox items if checked in filters" do + render_inline( + ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( + filters: { block_type: %w[email_address] }, + ), + ) + + assert_selector ".govuk-accordion__section--expanded", count: 1 + assert_selector ".govuk-accordion__section--expanded", text: "Content block type" assert_selector "input[type='checkbox'][name='block_type[]'][value='email_address'][checked]" assert_selector "input[type='checkbox'][name='block_type[]'][value='postal_address']" end - test "returns organisations with an 'all organisations' option" do - helper_mock = mock - ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.any_instance.stubs(:helpers).returns(helper_mock) - helper_mock.stubs(:content_block_manager).returns(helper_mock) - helper_mock.stubs(:content_block_manager_content_block_documents_path).returns("path") - helper_mock.stubs(:taggable_organisations_container).returns( - [["Department of Placeholder", 1], ["Ministry of Example", 2]], - ) + it "returns organisations with an 'all organisations' option" do render_inline(ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new(filters: {})) assert_selector "select[name='lead_organisation']" assert_selector "option[selected='selected'][value='']" end - test "selects organisation if selected in filters" do - helper_mock = mock - ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.any_instance.stubs(:helpers).returns(helper_mock) - helper_mock.stubs(:content_block_manager).returns(helper_mock) - helper_mock.stubs(:content_block_manager_content_block_documents_path).returns("path") - helper_mock.stubs(:taggable_organisations_container).returns( - [["Department of Placeholder", 1], ["Ministry of Example", 2]], + it "selects organisation if selected in filters" do + render_inline( + ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( + filters: { lead_organisation: "2" }, + ), ) - render_inline(ContentBlockManager::ContentBlock::Document::Index::FilterOptionsComponent.new( - filters: { lead_organisation: "2" }, - )) + + assert_selector ".govuk-accordion__section--expanded", count: 1 + assert_selector ".govuk-accordion__section--expanded", text: "Lead organisation" assert_selector "select[name='lead_organisation']" assert_selector "option[selected='selected'][value=2]"