Skip to content

Commit

Permalink
feat: show all filters in a popover when clicking a button with total…
Browse files Browse the repository at this point in the history
… filter count
  • Loading branch information
HendrikThePendric committed Dec 12, 2024
1 parent b2d7937 commit a01587e
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 90 deletions.
7 changes: 5 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-12-10T13:23:21.815Z\n"
"PO-Revision-Date: 2024-12-10T13:23:21.817Z\n"
"POT-Creation-Date: 2024-12-12T16:07:57.017Z\n"
"PO-Revision-Date: 2024-12-12T16:07:57.018Z\n"

msgid "Untitled dashboard"
msgstr "Untitled dashboard"
Expand Down Expand Up @@ -490,6 +490,9 @@ msgstr "No, cancel"
msgid "Yes, remove filters"
msgstr "Yes, remove filters"

msgid "{{totalFilterCount}} filters active"
msgstr "{{totalFilterCount}} filters active"

msgid "The dashboard couldn't be made available offline. Try again."
msgstr "The dashboard couldn't be made available offline. Try again."

Expand Down
2 changes: 1 addition & 1 deletion src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const App = (props) => {
return (
systemSettings && (
<>
<CssVariables colors spacers />
<CssVariables colors spacers elevations />
<Router>
<Switch>
<Route
Expand Down
103 changes: 40 additions & 63 deletions src/pages/view/SlideshowControlbar.js
Original file line number Diff line number Diff line change
@@ -1,95 +1,72 @@
import i18n from '@dhis2/d2-i18n'
import {
IconChevronRight24,
IconChevronLeft24,
IconCross24,
Tooltip,
colors,
} from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'
import { useSelector } from 'react-redux'
import { sGetNamedItemFilters } from '../../reducers/itemFilters.js'
import { SlideshowFiltersInfo } from './SlideshowFiltersInfo.js'
import styles from './styles/SlideshowControlbar.module.css'

const getFilterText = (filter) => {
return `${filter.name}: ${
filter.values.length > 1
? i18n.t('{{count}} selected', {
count: filter.values.length,
})
: filter.values[0].name
}`
}

const SlideshowControlbar = ({
slideshowItemIndex,
exitSlideshow,
nextItem,
prevItem,
numItems,
}) => {
const filters = useSelector(sGetNamedItemFilters)
const navigationDisabled = numItems === 1

return (
<div className={styles.container}>
<div>
<div className={styles.start}>
<button
className={styles.squareButton}
onClick={exitSlideshow}
data-test="slideshow-exit-button"
>
<IconCross24 color={colors.white} />
</button>
</div>
<div className={styles.controls}>
<button
disabled={navigationDisabled}
onClick={prevItem}
data-test="slideshow-prev-button"
>
<IconChevronLeft24
color={
navigationDisabled ? colors.grey600 : colors.white
}
/>
</button>
<span
className={styles.pageCounter}
data-test="slideshow-page-counter"
>{`${slideshowItemIndex + 1} / ${numItems}`}</span>
<button
disabled={navigationDisabled}
onClick={nextItem}
data-test="slideshow-next-button"
>
<IconChevronRight24
color={
navigationDisabled ? colors.grey600 : colors.white
}
/>
</button>
</div>
<div className={styles.filters}>
{filters.map((filter) => (
<Tooltip
content="These are the filters"
key={filter.id}
placement="left"
<div className={styles.middle}>
<div className={styles.controls}>
<button
className={styles.squareButton}
disabled={navigationDisabled}
onClick={prevItem}
data-test="slideshow-prev-button"
>
{({ onMouseOver, ref }) => (
<span
onMouseOver={() => onMouseOver()}
// onMouseOut={() => onMouseOut()}
ref={ref}
>
<span className={styles.filter}>
{getFilterText(filter)}
</span>
</span>
)}
</Tooltip>
))}
<IconChevronLeft24
color={
navigationDisabled
? colors.grey600
: colors.white
}
/>
</button>
<span
className={styles.pageCounter}
data-test="slideshow-page-counter"
>{`${slideshowItemIndex + 1} / ${numItems}`}</span>
<button
className={styles.squareButton}
disabled={navigationDisabled}
onClick={nextItem}
data-test="slideshow-next-button"
>
<IconChevronRight24
color={
navigationDisabled
? colors.grey600
: colors.white
}
/>
</button>
</div>
</div>
<div className={styles.end}>
<SlideshowFiltersInfo />
</div>
</div>
)
Expand Down
80 changes: 80 additions & 0 deletions src/pages/view/SlideshowFiltersInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import i18n from '@dhis2/d2-i18n'
import { Layer, Popper } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useMemo, useState, useRef } from 'react'
import { useSelector } from 'react-redux'
import { sGetNamedItemFilters } from '../../reducers/itemFilters.js'
import styles from './styles/SlideshowFiltersInfo.module.css'

const popperModifiers = [
{
name: 'offset',
options: {
offset: [0, 8],
},
},
]
const FilterSection = ({ name, values }) => (
<div className={styles.filterSection}>
<h4 className={styles.filterSectionHeader}>{name}</h4>
<ul className={styles.filterSectionList}>
{values.map((value) => (
<li className={styles.filterSectionListItem} key={value.name}>
{value.name}
</li>
))}
</ul>
</div>
)

FilterSection.propTypes = {
name: PropTypes.string,
values: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string })),
}

export const SlideshowFiltersInfo = () => {
const [isOpen, setIsOpen] = useState(false)
const ref = useRef(null)
const filters = useSelector(sGetNamedItemFilters)
const totalFilterCount = useMemo(
() =>
filters.reduce((total, filter) => total + filter.values.length, 0),
[filters]
)

if (filters.length === 0) {
return null
}

return (
<>
<button
ref={ref}
className={styles.filterButton}
onClick={() => setIsOpen(true)}
>
{i18n.t('{{totalFilterCount}} filters active', {
totalFilterCount,
})}
</button>
{isOpen && (
<Layer disablePortal onClick={() => setIsOpen(false)}>
<Popper
className={styles.popover}
reference={ref}
placement="top-end"
modifiers={popperModifiers}
>
{filters.map((filter) => (
<FilterSection
key={filter.name}
name={filter.name}
values={filter.values}
/>
))}
</Popper>
</Layer>
)}
</>
)
}
53 changes: 29 additions & 24 deletions src/pages/view/styles/SlideshowControlbar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,38 @@
justify-content: space-between;
align-items: center;
background-color: black;
block-size: 40px;
padding-inline-start: 4px;
padding-inline-end: 4px;
z-index: 10;
}
.start,
.middle,
.end {
display: flex;
align-items: center;
width: calc(100% / 3);
}

.start {
justify-content: start;
}

.middle {
justify-content: center;
}

.container > :first-child {
flex-grow: 1;
flex-basis: 0;
margin-inline-start: 4px;
.end {
justify-content: end;
}

.container button {
color: white;
background-color: #171819;
border-radius: 3px;
border: none;
inline-size: 32px;
block-size: 32px;
padding: 4px;
cursor: pointer;
flex-grow: 0;
}

.container button:hover {
Expand All @@ -32,13 +47,20 @@
.container button:disabled {
cursor: not-allowed;
}

.container button.squareButton {
inline-size: 32px;
block-size: 32px;
}

.controls {
padding: 4px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
}

.pageCounter {
font-family: 'Roboto', sans-serif;
font-weight: 400;
Expand All @@ -51,20 +73,3 @@
text-align: center;
user-select: none;
}

.filters {
flex-grow: 1;
flex-basis: 0;
list-style-type: none;
display: flex;
justify-content: flex-end;
margin-inline-end: 4px;
gap: 4px;
}

.filter {
padding: 4px;
border-radius: 3px;
color: black;
background-color: #f4f6f8;
}
30 changes: 30 additions & 0 deletions src/pages/view/styles/SlideshowFiltersInfo.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.filterButton {
padding: 4px;
inline-size: auto;
border-radius: 3px;
color: black;
background-color: #f4f6f8;
}

.popover {
border: 1px solid var(--colors-grey400);
border-radius: 4px;
box-shadow: var(--elevations-e400);
padding: var(--spacers-dp8);
background-color: var(--colors-white);
}

.filterSection {
margin-block-end: var(--spacers-dp4);
}

.filterSectionHeader {
margin: 0;
padding: 0;
}

.filterSectionList {
margin: 0;
padding: 0;
padding-inline-start: 18px;
}

0 comments on commit a01587e

Please sign in to comment.