Skip to content

Commit

Permalink
FilterToggle: add toggle component to be used in mB and a4 and to fix #…
Browse files Browse the repository at this point in the history
…4547, hide filters and found string when no ideas
  • Loading branch information
philli-m authored and fuzzylogic2000 committed Nov 24, 2022
1 parent b52aca6 commit 728083b
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 87 deletions.
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
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
45 changes: 45 additions & 0 deletions meinberlin/apps/contrib/assets/FilterToggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react'

export const FilterToggle = (props) => {
const {
showFilters,
onClickToggleFilter,
btnString,
showFiltersString,
hideFiltersString
} = props

return (
<>
{!showFilters
? <button
className="btn btn--icon btn--light"
type="button"
aria-describedby="span-id"
onClick={onClickToggleFilter}
>
<i
className="fas fa-sliders-h"
aria-hidden="true"
/>
<span id="span-id" className="visually-hidden">{showFiltersString}</span>
{btnString}
</button> // eslint-disable-line react/jsx-closing-tag-location
: <button
className="btn btn--icon btn--light"
type="button"
aria-describedby="span-id"
onClick={onClickToggleFilter}
>
<i
className="fas fa-times"
aria-hidden="true"
/>
<span id="span-id" className="visually-hidden">{hideFiltersString}</span>
{btnString}
{/* eslint-disable-next-line react/jsx-closing-tag-location */}
</button>}
</>

)
}
4 changes: 0 additions & 4 deletions meinberlin/assets/scss/components/_a4-comments.scss
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,6 @@
@extend .btn--light;
@extend .btn--small;
@extend .btn--icon;

a {
color: $text-color;
}
}

.a4-comments__submit-input {
Expand Down
6 changes: 6 additions & 0 deletions meinberlin/assets/scss/components/_button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@
}
}

.btn--icon-end {
i {
padding-left: 0.5rem;
}
}

.btn--align-left {
padding-left: 0;
}
Expand Down

0 comments on commit 728083b

Please sign in to comment.