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

[issues] toggle filters btn behavior #4683

Merged
merged 3 commits into from
Nov 24, 2022
Merged
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
5 changes: 4 additions & 1 deletion meinberlin/apps/budgeting/assets/BudgetingProposalList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const BudgetingProposalList = (props) => {
const countText = django.ngettext('you have 1 vote left.', 'you have %s votes left.', votes)
return django.interpolate(countText, [votes])
}
const noResults = django.gettext('Nothing to show')

const fetchProposals = () => {
const url = props.proposals_api_url + location.search
Expand Down Expand Up @@ -96,7 +97,9 @@ export const BudgetingProposalList = (props) => {
<div className="module-content--light">
<div className="container">
<div className="offset-lg-2 col-lg-8">
{renderList(data)}
{Object.keys(data).length > 0
? renderList(data)
: noResults}
</div>
</div>
</div>
Expand Down
144 changes: 79 additions & 65 deletions meinberlin/apps/budgeting/assets/ControlBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { ControlBarDropdown } from './ControlBarDropdown'
import { ControlBarListMapSwitch } from './ControlBarListMapSwitch'
import { ControlBarSearch } from './ControlBarSearch'
import { ControlBarSearchTerm } from './ControlBarSearchTerm'
import { SpacedSpan } from './SpacedSpan'
import { FilterToggle } from '../../contrib/assets/FilterToggle'
import django from 'django'
import { useSearchParams } from 'react-router-dom'

const translated = {
showFilters: django.gettext('Show filters'),
hideFilters: django.gettext('Hide filters'),
filters: django.gettext('Filters')
filters: django.gettext('Filters'),
nav: django.gettext('Search, filter and sort the ideas list')
}

const getResultCountText = (count) => {
Expand All @@ -21,9 +22,21 @@ const getResultCountText = (count) => {

export const ControlBar = props => {
const [expandFilters, setExpandFilters] = useState()
const [resultString, setResultString] = useState(false)
const [queryParams, setQueryParams] = useSearchParams()
const [term, setTerm] = useState(queryParams.get('search') || '')

// check list is filtered not just ordered
// else needed for search term deletion
const handleResultString = () => {
const entryArray = Array.from(queryParams.keys())
if ((entryArray.length === 2 && entryArray[1] !== 'ordering') || (entryArray.length > 2)) {
setResultString(true)
} else {
setResultString(false)
}
}

const applyFilter = (filterType, filterChoice) => {
if (filterChoice[0] !== '') {
queryParams.set(filterType, filterChoice[0])
Expand All @@ -34,84 +47,85 @@ export const ControlBar = props => {
// to avoid empty pagination page for given
// filter settings, always show first page,
queryParams.delete('page')

handleResultString()
setQueryParams(queryParams)
}

const applySearch = (value) => {
setTerm(value)
applyFilter('search', [value])
handleResultString()
}

const handleToggleFilters = (e) => {
e.preventDefault()
setExpandFilters(!expandFilters)
}

return (
<div className="container u-spacer-bottom u-spacer-top-double">
<nav className="container u-spacer-bottom u-spacer-top-double" aria-label={translated.nav}>
<div className="offset-lg-2 col-lg-8">
<ControlBarListMapSwitch query={queryParams} />
</div>
<div className="offset-lg-2 col-lg-8">
<div className="control-bar">
<div className="control-bar__item">
<ControlBarSearch
term={term}
onSearch={value => applySearch(value)}
/>
</div>
{props.filters?.ordering && (
<ControlBarDropdown
key="ordering_dropdown"
filter={props.filters.ordering}
current={queryParams.get('ordering')}
filterId="id_ordering"
onSelectFilter={choice => applyFilter('ordering', choice)}
/>
)}
<div className="control-bar__item control-bar__right">
<button
className="btn btn--light"
aria-label={
expandFilters
? translated.hideFilters
: translated.showFilters
}
onClick={() => setExpandFilters(!expandFilters)}
>
<i className="fa fa-filter" aria-hidden="true" />
<SpacedSpan>
{translated.filters}
</SpacedSpan>
</button>
</div>
</div>
</div>
{props.filters && expandFilters &&
<div className="offset-lg-2 col-lg-8">
<div className="control-bar">
{Object.keys(props.filters).map((type, idx) => {
const filterItem = props.filters[type]
return type !== 'ordering' && (

{/* only show filters if no filter selected or list isn't empty */}
{(Array.from(queryParams.values()).length > 1 || props.numOfResults > 0) &&
<>
<div className="control-bar">
<div className="control-bar__item">
<ControlBarSearch
term={term}
onSearch={value => applySearch(value)}
/>
</div>
{props.filters?.ordering && (
<ControlBarDropdown
key={'filter_' + idx}
filter={filterItem}
current={queryParams.get(type)}
filterId={'id_' + type}
onSelectFilter={choice => applyFilter(type, choice)}
key="ordering_dropdown"
filter={props.filters.ordering}
current={queryParams.get('ordering')}
filterId="id_ordering"
onSelectFilter={choice => applyFilter('ordering', choice)}
/>
)
})}
</div>
</div>}
<div className="offset-lg-2 col-lg-8">
<div className="control-bar">
{props.numOfResults >= 0 && getResultCountText(props.numOfResults)}
</div>
</div>
{term &&
<div className="offset-lg-2 col-lg-8">
)}
<div className="control-bar__item control-bar__right">
<FilterToggle
showFilters={expandFilters}
onClickToggleFilter={handleToggleFilters}
btnString={translated.filters}
showFiltersString={translated.showFilters}
hideFiltersString={translated.hideFilters}
/>
</div>
</div>
{props.filters && expandFilters &&
<>
<div className="control-bar">
{Object.keys(props.filters).map((type, idx) => {
const filterItem = props.filters[type]
return type !== 'ordering' && (
<ControlBarDropdown
key={'filter_' + idx}
filter={filterItem}
current={queryParams.get(type)}
filterId={'id_' + type}
onSelectFilter={choice => applyFilter(type, choice)}
/>
)
})}
</div>
</>}
</>}

{/* only show result string if list filtered or searched not just ordered */}
{resultString &&
<div className="control-bar">
{props.numOfResults >= 0 && getResultCountText(props.numOfResults)}
</div>}
{term &&
<ControlBarSearchTerm
term={term}
onDismiss={() => applySearch('')}
/>
</div>}
</div>
/>}
</div>
</nav>
)
}
9 changes: 4 additions & 5 deletions meinberlin/apps/budgeting/assets/ControlBarListMapSwitch.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import django from 'django'
import { SpacedSpan } from './SpacedSpan'
import { useSearchParams } from 'react-router-dom'

export const ControlBarListMapSwitch = () => {
Expand All @@ -18,19 +17,19 @@ export const ControlBarListMapSwitch = () => {
<div className="btn-group__container">
<div className="btn-group">
<div
className="btn btn--light switch--btn active"
className="btn btn--light switch--btn btn--icon active"
aria-label={django.gettext('View as list')}
>
<i className="fa fa-list" aria-hidden="true" />
<SpacedSpan>{django.gettext('List')}</SpacedSpan>
{django.gettext('List')}
</div>
<button
className="btn btn--light"
className="btn btn--light btn--icon"
onClick={handleClick}
aria-label={django.gettext('View as map')}
>
<i className="fa fa-map" aria-hidden="true" />
<SpacedSpan>{django.gettext('Map')}</SpacedSpan>
{django.gettext('Map')}
</button>
</div>
</div>
Expand Down
20 changes: 11 additions & 9 deletions meinberlin/apps/budgeting/assets/ControlBarSearchTerm.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import React from 'react'
import { SpacedSpan } from './SpacedSpan'
import django from 'django'

const translated = {
removeFilters: django.gettext('Remove filters')
}

export const ControlBarSearchTerm = (props) => {
return (
<div className="switch-filter__btn-group u-spacer-top">
<button
className="btn btn--light btn--small"
className="btn btn--icon-end btn--light"
aria-describedby="remove-search-filter"
onClick={props.onDismiss}
>
{props.term}
<SpacedSpan>
<i
className="fa fa-times"
aria-hidden="true"
/>
</SpacedSpan>
<i
className="fa fa-times"
aria-hidden="true"
/>
</button>
<span
id="remove-search-filter"
className="visually-hidden"
>
Filter entfernen
{translated.removeFilters}
</span>
</div>
)
Expand Down
9 changes: 4 additions & 5 deletions meinberlin/apps/budgeting/assets/ListItemBadges.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react'
import { SpacedSpan } from './SpacedSpan'
import django from 'django'

const translated = {
Expand Down Expand Up @@ -30,24 +29,24 @@ export const ListItemBadges = props => {
<div className="list-item__labels">
{props.badges.map((badge, idx) => {
return (
<SpacedSpan
<div
key={'badge_' + idx}
className={getClass(badge)}
>
{hasPointLabelIcon(badge[0])}
{badge[1]}
</SpacedSpan>
</div>
)
})}
{props.numOfMoreBadges > 0 &&
<SpacedSpan className="label__link label--big">
<div className="label__link label--big">
<a
href={props.proposalUrl}
className="list-item__link"
>
{props.numOfMoreBadges + ' ' + translated.more}
</a>
</SpacedSpan>}
</div>}
</div>
)
}
12 changes: 0 additions & 12 deletions meinberlin/apps/budgeting/assets/SpacedSpan.jsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ test('ControlBar collapsed bar', () => {
/>
</BrowserRouter>
)
const numOfResultsElement = screen.getByText(/2/)
expect(numOfResultsElement).toBeTruthy()
const numOfResultsElement = screen.queryByText(/2/)
expect(numOfResultsElement).toBeFalsy()
const orderingFilterElement = screen.getByText('ordering: Most recent')
expect(orderingFilterElement).toBeTruthy()
})
Expand All @@ -45,7 +45,7 @@ test('ControlBar expanded bar', async () => {
/>
</BrowserRouter>
)
const filterButton = screen.getByLabelText('Show filters')
const filterButton = screen.getByText(/Show filters/)
let expandedFilter = screen.queryByText(/category1/)
expect(expandedFilter).toBeFalsy()
fireEvent.click(filterButton)
Expand Down
10 changes: 0 additions & 10 deletions meinberlin/apps/budgeting/assets/__tests__/SpacedSpan.jest.jsx

This file was deleted.

Loading