Skip to content

Commit

Permalink
Add JavaScript filter to world index
Browse files Browse the repository at this point in the history
The world index page contains a JavaScript based search. We want to use the
`list-filter` module for this.

As the world locations on the page are grouped into letters, we need to add the
concept of an `inner-block` that can be hidden when no results are present.
  • Loading branch information
jkempster34 committed Jul 25, 2023
1 parent bcec419 commit 896bba1
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 65 deletions.
7 changes: 7 additions & 0 deletions app/assets/javascripts/modules/list-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,16 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
var matchingItems = block.querySelectorAll('[data-filter="item"]')
var matchingItemCount = 0

var innerBlocks = block.querySelectorAll('[data-filter="inner-block"]')
for (var r = 0; r < innerBlocks.length; r++) {
innerBlocks[r].classList.add('js-hidden')
}

for (var j = 0; j < matchingItems.length; j++) {
if (!matchingItems[j].classList.contains('js-hidden')) {
matchingItemCount++

if (matchingItems[j].closest('[data-filter="inner-block"]') !== null) { matchingItems[j].closest('[data-filter="inner-block"]').classList.remove('js-hidden') }
}
}

Expand Down
16 changes: 16 additions & 0 deletions app/assets/stylesheets/views/_world_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,20 @@
padding: govuk-spacing(1) 0;
@include govuk-font(19);
}

.filter-list__form {
display: none;
}

.filter-list__form--active {
display: block;
}

.js-hidden {
display: none;
}

.filter-list__results {
padding-left: $govuk-gutter-half;
}
}
7 changes: 7 additions & 0 deletions app/presenters/world_index_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ def world_location_link(world_location)
link_to world_location["name"], path, class: "govuk-link"
end

def filter_terms(world_location)
slug = world_location["slug"]
name = world_location["name"]

[slug, name].compact.join(" ")
end

private

def location_group(location)
Expand Down
149 changes: 84 additions & 65 deletions app/views/world/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,100 @@
<% page_class "world-locations" %>
<% add_view_stylesheet("world_index") %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= render "govuk_publishing_components/components/title", {
title: @presented_index.title
} %>
<div data-module="list-filter">
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= render "govuk_publishing_components/components/title", {
title: @presented_index.title
} %>

<p class="govuk-body"><%= t('world_locations.content.find_out') %></p>
</div>
</div>
<p class="govuk-body"><%= t('world_locations.content.find_out') %></p>

<div class="govuk-grid-row">
<section id="<%= I18n.t("world_locations.type.world_location", count: 2) %>">
<div class="govuk-grid-column-one-third">
<header class="type-heading">
<h2 class="world-locations-heading"><%= I18n.t("world_locations.type.world_location", count: 2) %></h2>
<p class="govuk-visually-hidden"><%= t('world_locations.count_prefix') %>
<span class="js-accessible-item-count"><%= @presented_index.world_locations_count %></span> <%= I18n.t("world_locations.type.world_location", count: 2) %>
</p>
<%= render "govuk_publishing_components/components/big_number", {
number: @presented_index.world_locations_count,
aria: {
hidden: true,
}
<form class="filter-list__form" data-filter="form">
<%= render "govuk_publishing_components/components/input", {
label: {
text: t("world_locations.headings.search"),
},
name: "search-box",
id: "filter-list",
controls: "search_results"
} %>
</header>
</form>
</div>
</div>

<div class="govuk-grid-column-two-thirds">
<ol class="world-locations-groups">
<% @presented_index.grouped_world_locations.each do |letter, locations| %>
<div class="govuk-grid-row" id="search_results">
<section id="<%= I18n.t("world_locations.type.world_location", count: 2).parameterize %>" data-filter="block">
<div class="govuk-grid-column-one-third">
<header class="type-heading">
<h2 class="world-locations-heading"><%= I18n.t("world_locations.type.world_location", count: 2) %></h2>
<p class="govuk-visually-hidden"><%= t('world_locations.count_prefix') %>
<span class="js-accessible-item-count"><%= @presented_index.world_locations_count %></span> <%= I18n.t("world_locations.type.world_location", count: 2) %>
</p>
<%= render "govuk_publishing_components/components/big_number", {
number: @presented_index.world_locations_count,
aria: {
hidden: true,
},
data_attributes: {
item_count: "true",
},
} %>
</header>
</div>

<div class="govuk-grid-column-two-thirds">
<ol class="world-locations-groups">
<% @presented_index.grouped_world_locations.each do |letter, locations| %>
<div class="world-locations-group" data-filter="inner-block">
<h3 class="world-locations-group__letter"><%= letter %></h3>
<ol class="world-locations-group__list">
<% locations.each do |location| %>
<li class="world_locations-group__item" data-filter="item" data-filter-terms="<%= @presented_index.filter_terms(location) %>">
<%= @presented_index.world_location_link(location) %>
</li>
<% end %>
</ol>
</div>
<% end %>
</ol>
</div>
</section>

<section id="<%= I18n.t("world_locations.type.international_delegation", count: 2).parameterize %>" data-filter="block">
<div class="govuk-grid-column-one-third">
<header class="type-heading">
<h2 class="world-locations-heading"><%= I18n.t("world_locations.type.international_delegation", count: 2) %></h2>
<p class="govuk-visually-hidden"><%= t('world_locations.count_prefix') %>
<span class="js-accessible-item-count"><%= @presented_index.international_delegations_count %></span> <%= I18n.t("world_locations.type.international_delegation", count: 2) %>
</p>
<%= render "govuk_publishing_components/components/big_number", {
number: @presented_index.international_delegations_count,
aria: {
hidden: true,
},
data_attributes: {
item_count: "true",
},
} %>
</header>
</div>

<div class="govuk-grid-column-two-thirds">
<ol class="world-locations-groups">
<div class="world-locations-group">
<h3 class="world-locations-group__letter"><%= letter %></h3>
<ol class="world-locations-group__list">
<% locations.each do |location| %>
<li class="world_locations-group__item">
<%= @presented_index.world_location_link(location) %>
<% @presented_index.international_delegations.each do |delegation| %>
<li class="world_locations-group__item" data-filter="item" data-filter-terms="<%= @presented_index.filter_terms(delegation) %>">
<%= @presented_index.world_location_link(delegation) %>
</li>
<% end %>
</ol>
</div>
<% end %>
</ol>
</div>
</section>

<section id="<%= I18n.t("world_locations.type.international_delegation", count: 2).parameterize %>">
<div class="govuk-grid-column-one-third">
<header class="type-heading">
<h2 class="world-locations-heading"><%= I18n.t("world_locations.type.international_delegation", count: 2) %></h2>
<p class="govuk-visually-hidden"><%= t('world_locations.count_prefix') %>
<span class="js-accessible-item-count"><%= @presented_index.international_delegations_count %></span> <%= I18n.t("world_locations.type.international_delegation", count: 2) %>
</p>
<%= render "govuk_publishing_components/components/big_number", {
number: @presented_index.international_delegations_count,
aria: {
hidden: true,
},
} %>
</header>
</div>
</ol>
</div>
</section>
</div>

<div class="govuk-grid-column-two-thirds">
<ol class="world-locations-groups">
<div class="world-locations-group">
<ol class="world-locations-group__list">
<% @presented_index.international_delegations.each do |delegation| %>
<li class="world_locations-group__item">
<%= @presented_index.world_location_link(delegation) %>
</li>
<% end %>
</ol>
</div>
</ol>
</div>
</section>
<p class="govuk-body"><%= t('world_locations.content.complete_list', href: link_to(t('world_locations.content.list_link'), "/government/publications/list-of-foreign-office-posts", class: "govuk-link")).html_safe %></p>
</div>

<p class="govuk-body"><%= t('world_locations.content.complete_list', href: link_to(t('world_locations.content.list_link'), "/government/publications/list-of-foreign-office-posts", class: "govuk-link")).html_safe %></p>
29 changes: 29 additions & 0 deletions spec/javascripts/modules/list-filter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ describe('list-filter.js', function () {
// Double space and line break added on purpose:
'<div class="gem-c-organisation-logo__name">Ministry of Funny\nWalks</div>' +
'</li>' +
'<div data-filter="inner-block">' +
'<ol>' +
'<li data-filter="item" class="org-logo-4" data-filter-terms="Ministry of Inner Blocks MIB">' +
'<div class="gem-c-organisation-logo__name">Ministry of Inner Blocks</div>' +
'</li>' +
'</ol>' +
'</div>' +
'</ol>' +
'</div>' +
'<div data-filter="block">' +
Expand Down Expand Up @@ -187,4 +194,26 @@ describe('list-filter.js', function () {
done()
}, timeout)
})

it('hide inner blocks if they have no matching items', function (done) {
$('[data-filter="form"] input').val('Advisory cou')
window.GOVUK.triggerEvent($('[data-filter="form"] input')[0], 'keyup')

setTimeout(function () {
expect($('[data-filter="inner-block"]').first()).toHaveClass('js-hidden')
expect($('.js-search-results')).toHaveText('1 result found')
done()
}, timeout)
})

it('reveals inner blocks if they have matching items', function (done) {
$('[data-filter="form"] input').val('Ministry of Inner Blocks')
window.GOVUK.triggerEvent($('[data-filter="form"] input')[0], 'keyup')

setTimeout(function () {
expect($('[data-filter="inner-block"]').first()).not.toHaveClass('js-hidden')
expect($('.js-search-results')).toHaveText('1 result found')
done()
}, timeout)
})
})
15 changes: 15 additions & 0 deletions spec/presenters/world_index_presenter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@
end
end

describe "#filter_terms" do
let(:location) do
{
"name": "United Kingdom",
"slug": "united-kingdom",
}.with_indifferent_access
end

subject { world_index_presenter.filter_terms(location) }

it "returns the joined name and slug" do
expect(subject).to eq "united-kingdom United Kingdom"
end
end

describe "#international_delegations_count" do
subject { world_index_presenter.international_delegations_count }

Expand Down

0 comments on commit 896bba1

Please sign in to comment.