Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💄 [#3018] Mobile search filters as pop-up with checkboxes #1621

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% load i18n icon_tags button_tags form_tags %}

<div>
<fieldset class="filter-dropdown" aria-label="{% trans "Filter" %}">
<button type="button" class="button button__select filter-dropdown__button" aria-haspopup="listbox" aria-expanded="false" aria-live="polite">{{ field.label }} <span class="filter-count">({{ field.field.choices|length }})</span>
{% icon icon="expand_more" outlined=icon_outlined %}
</button>
<legend class="filter__title">
<span class="filter__legend-label">{{ field.label }}</span>
</legend>
<div class="filter__list filter-dropdown__menu">
<ul class="filter-dropdown__list">
{% for option in field.field.choices %}
<li>{% choice_checkbox choice=option name=field.name data=field.data index=forloop.counter form_id=form_id %}</li>
{% endfor %}
</ul>

{% if filter_submit_button %}
<div class="filter-dropdown__actions">
<button class="button button--primary filter-dropdown__submit" type="submit" title="{% trans 'Toon resultaten' %}" aria-label="{% trans 'Toon resultaten' %}" id="filterCases">
{% trans 'Toon' %}<span class="filter-bar__frequency-sum" id="frequencySum">0</span><span id="resultText">{% trans 'resultaten' %}</span>
</button>
{% if filter_clear_button %}
<button class="filter-dropdown__clear">Deselecteer alle filters</button>
{% endif %}
</div>
{% endif %}
</div>
</fieldset>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% load i18n form_tags button_tags %}

<div class="filter-bar__backdrop" id="filterBarBackdrop">
{# Wrapper for multiple filters #}
<div class="filter-bar" id="filterBar">
<div class="filter-bar__mobile-controls">
{% button icon="close" text=_("Sluiten") hide_text=True icon_outlined=True transparent=True extra_classes="show-controls" %}
<div class="form__reset--mobile form__actions--fullwidth form__actions--reset">
<button class="button button--primary button--transparent" type="button" name="" value="" title="Wis alle filters" aria-label="Wis alle filters" id="resetAllFilters">
Wis alle filters
</button>
</div>
</div>
<div class="filter-bar__mobile-button">
<p class="utrecht-paragraph filter-bar__heading">Filters</p>
{% button icon="filter_alt" text=_("Filters") icon_outlined=True transparent=True extra_classes="show-modal" %}
<p class="utrecht-paragraph filter-bar__status-text">Status</p>
</div>
{{ contents }}
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,8 @@ document.body.addEventListener('htmx:afterSwap', function () {
document.addEventListener('click', function (e) {
if (e.target && e.target.classList.contains('pagination__link')) {
scrollToTopOfWindow() // Scroll up after clicking pagination
setTimeout(function () {
initFilterBar() // Reinitialize filter bar after HTMX swap from pagination
}, 20)
}
})
31 changes: 31 additions & 0 deletions src/open_inwoner/js/components/search/filter-dropdown-submit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export class FilterDropdownSubmit {
static selector = '.filter-dropdown'

constructor(node) {
this.node = node
this.submitButton = node.querySelector('.filter-dropdown__submit')
this.checkboxes = node.querySelectorAll(
'.filter-dropdown__list input[type="checkbox"]'
)

if (!this.submitButton || !this.checkboxes.length) return

this.checkboxes.forEach((checkbox) => {
checkbox.addEventListener('change', this.updateSubmitButton.bind(this))
})
}

updateSubmitButton() {
const selectedCount = this.node.querySelectorAll(
'.filter-dropdown__list input[type="checkbox"]:checked'
).length
this.submitButton.textContent = `Apply Filters (${selectedCount})`
}
}

/**
* Initialize only dropdowns with submit buttons
*/
document
.querySelectorAll(FilterDropdownSubmit.selector)
.forEach((dropdown) => new FilterDropdownSubmit(dropdown))
59 changes: 59 additions & 0 deletions src/open_inwoner/js/components/search/filter-dropdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export class FilterDropdown {
static selector = '.filter-dropdown'

constructor(node) {
this.node = node
this.button = node.querySelector('.filter-dropdown__button')
this.menu = node.querySelector('.filter-dropdown__menu')
this.checkboxes = node.querySelectorAll(
'.filter-dropdown__list input[type="checkbox"]'
)
this.clearButton = node.querySelector('.filter-dropdown__clear')
this.filterCount = node.querySelector('.filter-count')

if (!this.button || !this.menu || !this.checkboxes.length) return

this.button.addEventListener('click', this.toggleDropdown.bind(this))
document.addEventListener('click', this.closeDropdown.bind(this))

this.checkboxes.forEach((checkbox) => {
checkbox.addEventListener('change', this.updateFilters.bind(this))
})

if (this.clearButton) {
this.clearButton.addEventListener('click', this.clearFilters.bind(this))
}
}

toggleDropdown(event) {
event.preventDefault()
this.node.classList.toggle('open')
}

closeDropdown(event) {
if (!this.node.contains(event.target)) {
this.node.classList.remove('open')
}
}

updateFilters() {
const selectedCount = this.node.querySelectorAll(
'.filter-dropdown__list input[type="checkbox"]:checked'
).length
if (this.filterCount) {
this.filterCount.textContent = `(${selectedCount})`
}
}

clearFilters() {
this.checkboxes.forEach((checkbox) => (checkbox.checked = false))
this.updateFilters()
}
}

/**
* Initialize all dropdowns
*/
document
.querySelectorAll(FilterDropdown.selector)
.forEach((dropdown) => new FilterDropdown(dropdown))
2 changes: 1 addition & 1 deletion src/open_inwoner/js/components/search/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import './filter-mobile'
import './filter-dropdown'
import './filter-options'

const searchForm = document.getElementById('search-form')
Expand Down
8 changes: 8 additions & 0 deletions src/open_inwoner/scss/components/Filter/Filter.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
.filter {
display: none;
@media (min-width: 768px) {
display: block;
}
border: 0 solid transparent;
margin: 0;
padding: 0;
Expand Down Expand Up @@ -94,6 +98,10 @@
}

.grid__filters {
display: none;
@media (min-width: 768px) {
display: block;
}
.utrecht-heading-2 {
border-bottom: 2px solid var(--color-gray-light);
display: block;
Expand Down
67 changes: 67 additions & 0 deletions src/open_inwoner/scss/components/Filter/FilterDropdown.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.filter-dropdown {
position: relative;
display: inline-block;
border: 0 solid transparent;
margin: 0;
padding: 0;

&__button {
background: var(--color-white);
color: var(--color-body);
padding: 10px 15px;
cursor: pointer;
border-radius: 5px;

@media (min-width: 768px) {
border: 1px solid var(--color-gray-dark-900);
border-radius: var(--border-radius);
}
}

&__menu {
background: white;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: none;
padding: 10px;
width: 100%;
}

&__list {
list-style: none;
padding: 0;
margin: 0;

li {
margin-bottom: 5px;
}
}

&__actions {
display: flex;
justify-content: space-between;
margin-top: 10px;
}

&__submit {
background: var(--color-primary);
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 3px;
}

&__clear {
background: none;
border: none;
color: var(--color-primary);
cursor: pointer;
}
}

// Show dropdown when button is clicked
.filter-dropdown.open .filter-dropdown__menu {
display: block;
}
6 changes: 6 additions & 0 deletions src/open_inwoner/scss/components/Form/_Search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@
.input {
margin-top: 0;
}

&__mobile {
@media (min-width: 768px) {
display: none;
}
}
}
1 change: 1 addition & 0 deletions src/open_inwoner/scss/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
@import './File/File.scss';
@import './File/FileList.scss';
@import './Filter/Filter.scss';
@import 'Filter/FilterDropdown';
@import './FilterBar/FilterBar.scss';
@import './FilterBar/MultiSelectListbox';
@import './Footer/Footer.scss';
Expand Down
12 changes: 12 additions & 0 deletions src/open_inwoner/templates/pages/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ <h2 class="utrecht-heading-2">{% trans "Zoekfilters" %}</h2>
{% include "components/Filter/Filter.html" with field=search_form.organizations form_id="search-form" only %}
{% endif %}
</aside>

<aside class="search__mobile" aria-label="{% trans "Zoekfilters" %}">
{% if search_filter_categories %}
{% include "components/Filter/FilterDropdown.html" with field=search_form.categories form_id="search-form" only %}
{% endif %}
{% if search_filter_tags %}
{% include "components/Filter/FilterDropdown.html" with field=search_form.tags form_id="search-form" only %}
{% endif %}
{% if search_filter_organizations %}
{% include "components/Filter/FilterDropdown.html" with field=search_form.organizations form_id="search-form" only %}
{% endif %}
</aside>
{% endif %}
{# end search filters #}
{% endif %}
Expand Down
Loading