Skip to content

Commit 3a3af2a

Browse files
committed
OpenConceptLab/ocl_issues#2211 | Search Filters groups
1 parent a416bcb commit 3a3af2a

File tree

3 files changed

+144
-100
lines changed

3 files changed

+144
-100
lines changed

src/components/map-projects/Search.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,17 @@ const Search = ({searchStr, setSearchStr, onSearch, repo, repoVersion, concepts,
113113
<div className='col-xs-12 padding-0' style={{display: 'flex'}}>
114114
{
115115
!isEmpty(facets) &&
116-
<div className='col-xs-4 padding-0' style={openFilters ? {borderRight: '1px solid lightgray', height: 'calc(100vh - 585px)', overflow: 'auto'} : {width: 0, display: 'none'}}>
116+
<div className='col-xs-4 padding-0' style={openFilters ? {borderRight: '1px solid lightgray'} : {width: 0, display: 'none'}}>
117117
<SearchFilters
118+
open={openFilters}
118119
resource='concepts'
119120
filters={facets}
120121
appliedFilters={appliedFacets || {}}
121122
onChange={setAppliedFacets}
122123
repoDefaultFilters={repoVersion?.meta?.display?.default_filter}
123124
properties={repoVersion?.meta?.display?.concept_summary_properties}
124125
propertyFilters={repoVersion?.filters}
126+
heightToSubtract={523}
125127
/>
126128
</div>
127129
}

src/components/search/SearchFilters.jsx

Lines changed: 138 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@ import UpIcon from '@mui/icons-material/ArrowDropUp';
1010
import List from '@mui/material/List';
1111
import ListItemText from '@mui/material/ListItemText';
1212
import ListSubheader from '@mui/material/ListSubheader';
13+
import ListItem from '@mui/material/ListItem';
1314
import ListItemButton from '@mui/material/ListItemButton';
1415
import ListItemIcon from '@mui/material/ListItemIcon';
1516
import Checkbox from '@mui/material/Checkbox';
16-
import Divider from '@mui/material/Divider';
1717
import CircularProgress from '@mui/material/CircularProgress';
1818
import { URIToParentParams, currentUserHasAccess } from '../../common/utils'
1919
import { FACET_ORDER } from './ResultConstants';
2020

21-
22-
const SearchFilters = ({filters, resource, onChange, kwargs, bgColor, appliedFilters, fieldOrder, noSubheader, disabledZero, filterDefinitions, nested, onSaveAsDefaultFilters, loading, repoDefaultFilters, propertyFilters}) => {
21+
const SearchFilters = ({filters, resource, onChange, kwargs, bgColor, appliedFilters, fieldOrder, noSubheader, disabledZero, filterDefinitions, nested, onSaveAsDefaultFilters, loading, repoDefaultFilters, propertyFilters, heightToSubtract, open}) => {
2322
const { t } = useTranslation()
2423
const [applied, setApplied] = React.useState({});
2524
const [count, setCount] = React.useState(0);
@@ -30,6 +29,7 @@ const SearchFilters = ({filters, resource, onChange, kwargs, bgColor, appliedFil
3029
const isConcept = resource === 'concepts'
3130
const isMapping = resource === 'mappings'
3231
const isSourceChild = isConcept || isMapping
32+
let propertyFacets = {}
3333
const hasValidKwargs = !isEmpty(kwargs) && isObject(kwargs);
3434
if(hasValidKwargs) {
3535
if(kwargs.user || kwargs.org)
@@ -67,13 +67,11 @@ const SearchFilters = ({filters, resource, onChange, kwargs, bgColor, appliedFil
6767
uiFilters = orderedUIFilters
6868
}
6969
if(isConcept){
70-
const properties = pickBy(filters, (values, field) => field.startsWith('properties__') && !isEmpty(values))
71-
if(!isEmpty(properties)) {
72-
uiFilters = omit(uiFilters, ['conceptClass', 'datatype'])
73-
uiFilters = {...properties, ...uiFilters}
70+
propertyFacets = pickBy(filters, (values, field) => field.startsWith('properties__') && !isEmpty(values))
71+
if(!isEmpty(propertyFacets)) {
72+
uiFilters = omit(uiFilters, ['conceptClass', 'datatype', ...keys(propertyFacets)])
7473
}
7574
}
76-
7775
const formattedName = (field, name) => {
7876
let label;
7977
if(includes(['locale', 'version', 'source_version', 'nameTypes', 'expansion'], field))
@@ -178,28 +176,104 @@ const SearchFilters = ({filters, resource, onChange, kwargs, bgColor, appliedFil
178176
onChange(repoDefaultFilters)
179177
}
180178

179+
180+
const getFilterList = (fieldFilters, field) => {
181+
const shouldShowExpand = fieldFilters.length > 5
182+
const isExpanded = expanded.includes(field)
183+
return (
184+
<ListItem key={field} sx={{padding: 0, flexDirection: 'column'}}>
185+
<List
186+
dense
187+
sx={{
188+
width: '100%',
189+
position: 'relative',
190+
padding: 0,
191+
display: 'inline-block',
192+
}}
193+
>
194+
{
195+
!noSubheader &&
196+
<ListSubheader sx={{padding: '0 8px 0 0px', fontWeight: 'bold', backgroundColor: bgColor, lineHeight: '30px'}}>
197+
{formattedListSubheader(field)}
198+
</ListSubheader>
199+
}
200+
{
201+
map(getFieldFilters(field, fieldFilters), value => {
202+
const labelId = `checkbox-list-label-${value[0]}`;
203+
const key = `${field}-${value[0]}`
204+
205+
return (
206+
<ListItemButton key={key} onClick={handleToggle(field, value)} sx={{p: '0 12px 0 4px'}} disabled={value[3] === true || (disabledZero && value[1] === 0)}>
207+
<ListItemIcon sx={{minWidth: '25px'}}>
208+
<Checkbox
209+
size="small"
210+
edge="start"
211+
checked={isApplied(field, value)}
212+
tabIndex={-1}
213+
disableRipple
214+
inputProps={{ 'aria-labelledby': labelId }}
215+
sx={{padding: '0px 8px', '.MuiSvgIcon-root': {fontSize: '1.1rem'}}}
216+
disabled={(disabledZero && value[1] === 0)}
217+
/>
218+
</ListItemIcon>
219+
<ListItemText
220+
id={labelId}
221+
primary={
222+
<span style={{display: 'flex', alignItems: 'center'}}>
223+
{formattedName(field, value[0])}
224+
{
225+
get(filterDefinitions, value[0])?.tooltip &&
226+
<Tooltip title={filterDefinitions[value[0]].tooltip}>
227+
<InfoIcon sx={{marginLeft: '4px', fontSize: '1rem'}} color='primary' />
228+
</Tooltip>
229+
}
230+
</span>
231+
}
232+
primaryTypographyProps={{style: {fontSize: '0.875rem'}}} style={{margin: 0}} />
233+
<span style={{fontSize: '0.7rem'}}>{value[1].toLocaleString()}</span>
234+
</ListItemButton>
235+
);
236+
})}
237+
238+
</List>
239+
{
240+
shouldShowExpand &&
241+
<ListItem sx={{padding: '4px 4px 0px 0px'}}>
242+
<Button size='small' onClick={() => toggleExpanded(field)} sx={{textTransform: 'none', fontSize: '11px', padding: '0px 5px 2px 5px'}} color='secondary' startIcon={isExpanded ? <UpIcon fontSize='inherit'/> : <DownIcon fontSize='inherit'/>}>
243+
{isExpanded ? t('common.hide') : `${t('common.show')} ${fieldFilters.length - 5} ${t('common.more').toLowerCase()}`}
244+
</Button>
245+
</ListItem>
246+
}
247+
</ListItem>
248+
)
249+
}
250+
251+
181252
const isFixedConceptField = field => isConcept && ['conceptClass', 'datatype'].includes(field)
253+
const canUpdateDefaultFilters = nested && onSaveAsDefaultFilters && currentUserHasAccess()
254+
const topBarHeight = canUpdateDefaultFilters ? 60 : 30
255+
let totalFilters = {...propertyFacets, ...uiFilters}
182256

183257
return (
184258
<div className='col-xs-12 padding-0'>
185-
<div className='col-xs-12' style={{zIndex: 2, padding: '0px'}}>
186-
<div className='col-xs-12' style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '0 8px'}}>
259+
<div className='col-xs-12' style={{zIndex: 2, padding: '0px', position: open ? 'absolute' : undefined, top: 0, display: open ? undefined : 'none'}}>
260+
<div className='col-xs-12' style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '0 8px 0 0'}}>
261+
<span>
262+
<Badge badgeContent={count} color='primary' sx={{'.MuiBadge-badge': {top: '10px', left: '36px'}}}>
263+
<b>{t('search.filters')}</b>
264+
</Badge>
265+
</span>
187266
<span>
188-
<Badge badgeContent={count} color='primary' sx={{'.MuiBadge-badge': {top: '10px', left: '36px'}}}>
189-
<b>{t('search.filters')}</b>
190-
</Badge>
191-
</span>
192-
<span>
193267
<Button variant='text' color='primary' style={{textTransform: 'none'}} onClick={onApply} disabled={!unapplied}>
194268
{t('common.apply')}
195269
</Button>
196-
<Button variant='text' style={{textTransform: 'none'}} onClick={onClear} disabled={!count} color='error'>
197-
{t('common.clear')}
198-
</Button>
199-
</span>
270+
<Button variant='text' style={{textTransform: 'none'}} onClick={onClear} disabled={!count} color='error'>
271+
{t('common.clear')}
272+
</Button>
273+
</span>
200274
</div>
201275
{
202-
nested && onSaveAsDefaultFilters && currentUserHasAccess() &&
276+
canUpdateDefaultFilters &&
203277
<div className='col-xs-12 padding-0' style={{textAlign: 'right'}}>
204278
<Button size='small' sx={{textTransform: 'none'}} onClick={onSetDefaultFilters} disabled={isEmpty(applied) || isEqual(applied, repoDefaultFilters)}>
205279
{t('search.save_default_filters')}
@@ -210,86 +284,52 @@ const SearchFilters = ({filters, resource, onChange, kwargs, bgColor, appliedFil
210284
</div>
211285
}
212286
</div>
213-
{
214-
loading && isEmpty(uiFilters) &&
215-
<div className='col-xs-12' style={{textAlign: 'center', padding: '16px'}}>
216-
<CircularProgress />
217-
</div>
218-
}
219-
{
220-
map(uiFilters, (fieldFilters, field) => {
221-
const shouldShowExpand = fieldFilters.length > 4
222-
const isExpanded = expanded.includes(field)
223-
return (
224-
<div className='col-xs-12 padding-0' key={field}>
225-
<List
226-
dense
227-
sx={{
228-
width: '100%',
229-
position: 'relative',
230-
padding: 0,
231-
display: 'inline-block',
232-
}}
233-
>
234-
{
235-
!noSubheader &&
236-
<ListSubheader sx={{padding: '0 8px', fontWeight: 'bold', backgroundColor: bgColor, lineHeight: '30px'}}>
237-
{formattedListSubheader(field)}
238-
</ListSubheader>
239-
}
240-
{
241-
map(getFieldFilters(field, fieldFilters), (value, index) => {
242-
const labelId = `checkbox-list-label-${value[0]}`;
243-
const key = `${field}-${value[0]}`
244-
245-
return (
246-
<ListItemButton key={key} onClick={handleToggle(field, value)} sx={{p: '0 12px', paddingTop: index === 0 ? '4px': undefined}} disabled={value[3] === true || (disabledZero && value[1] === 0)}>
247-
<ListItemIcon sx={{minWidth: '25px'}}>
248-
<Checkbox
249-
size="small"
250-
edge="start"
251-
checked={isApplied(field, value)}
252-
tabIndex={-1}
253-
disableRipple
254-
inputProps={{ 'aria-labelledby': labelId }}
255-
sx={{padding: '0px 8px', '.MuiSvgIcon-root': {fontSize: '1.1rem'}}}
256-
disabled={(disabledZero && value[1] === 0)}
257-
258-
/>
259-
</ListItemIcon>
260-
<ListItemText
261-
id={labelId}
262-
primary={
263-
<span style={{display: 'flex', alignItems: 'center'}}>
264-
{formattedName(field, value[0])}
265-
{
266-
get(filterDefinitions, value[0])?.tooltip &&
267-
<Tooltip title={filterDefinitions[value[0]].tooltip}>
268-
<InfoIcon sx={{marginLeft: '4px', fontSize: '1rem'}} color='primary' />
269-
</Tooltip>
270-
}
271-
</span>
272-
}
273-
primaryTypographyProps={{style: {fontSize: '0.875rem'}}} style={{margin: 0}} />
274-
<span style={{fontSize: '0.7rem'}}>{value[1].toLocaleString()}</span>
275-
</ListItemButton>
276-
);
277-
})}
278-
</List>
287+
<div className='col-xs-12 padding-0' style={{marginTop: `${topBarHeight}px`, height: `calc(100vh - ${heightToSubtract || 0}px - ${topBarHeight}px)`, overflowY: 'auto'}}>
288+
{
289+
loading && isEmpty(totalFilters) &&
290+
<div className='col-xs-12' style={{textAlign: 'center', padding: '16px'}}>
291+
<CircularProgress />
292+
</div>
293+
}
294+
{
295+
!isEmpty(propertyFacets) &&
296+
<List
297+
dense
298+
sx={{
299+
width: '100%',
300+
position: 'relative',
301+
padding: 0,
302+
display: 'inline-block',
303+
marginTop: '8px'
304+
}}>
305+
<ListSubheader sx={{padding: '0 8px 0 0px', fontWeight: 'bold', backgroundColor: bgColor, lineHeight: 'normal', color: '#000'}}>
306+
{t('repo.properties_filters_subheader')}
307+
</ListSubheader>
308+
{map(propertyFacets, getFilterList)}
309+
</List>
310+
}
311+
{
312+
!isEmpty(uiFilters) &&
313+
<List
314+
dense
315+
sx={{
316+
width: '100%',
317+
position: 'relative',
318+
padding: 0,
319+
display: 'inline-block',
320+
marginTop: isEmpty(propertyFacets) ? 0 : '14px'
321+
}}
322+
>
279323
{
280-
shouldShowExpand &&
281-
<Button size='small' onClick={() => toggleExpanded(field)} sx={{textTransform: 'none', fontSize: '11px', padding: '0px 5px 2px 5px'}} color='secondary' startIcon={isExpanded ? <UpIcon fontSize='inherit'/> : <DownIcon fontSize='inherit'/>}>
282-
{isExpanded ? t('common.hide') : `${t('common.show')} ${fieldFilters.length - 4} ${t('common.more').toLowerCase()}`}
283-
</Button>
324+
!isEmpty(propertyFacets) &&
325+
<ListSubheader sx={{padding: '0 8px 0 0px', fontWeight: 'bold', backgroundColor: bgColor, lineHeight: 'normal', color: '#000'}}>
326+
{t('repo.additional_metadata_filters_subheader')}
327+
</ListSubheader>
284328
}
285-
{
286-
!noSubheader &&
287-
<Divider />
288-
}
289-
</div>
290-
)
291-
})
292-
}
329+
{map(uiFilters, getFilterList)}
330+
</List>
331+
}
332+
</div>
293333
</div>
294334
)
295335
}

src/i18n/locales/en/translations.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,9 @@
241241
"repo_version_summary": "Repository Version Summary",
242242
"repo_summary_is_calculating": "Repo summary is calculating, please check back after some time.",
243243
"no_overview": "The owner has not created an Overview page for this repository",
244-
"property": "Property"
244+
"property": "Property",
245+
"properties_filters_subheader": "Repository Properties",
246+
"additional_metadata_filters_subheader": "Additional Metadata"
245247
},
246248
"org": {
247249
"my": "My Organizations",

0 commit comments

Comments
 (0)