Skip to content

Commit

Permalink
Agenda quick filters pills (#609)
Browse files Browse the repository at this point in the history
NHUB-414

* Move agenda filter pills to quick filter pills

* Add coment, fix X button not removing agenda filters
  • Loading branch information
thecalcc authored Oct 27, 2023
1 parent 92f229f commit 56b6cb8
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 24 deletions.
16 changes: 8 additions & 8 deletions assets/agenda/components/AgendaCoverageExistsFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {get} from 'lodash';
import {gettext} from 'utils';
import {AgendaDropdown} from './AgendaDropdown';

const filter = {
label: gettext('Coverage status'),
export const agendaCoverageStatusFilter = {
label: gettext('Any coverage status'),
field: 'coverage_status',
nestedField: 'coverage_status',
};
Expand All @@ -18,7 +18,7 @@ const FILTER_VALUES = {
COMPLETED: 'completed'
};

function getActiveFilterLabel(filter: any, activeFilter: any) {
export function getActiveFilterLabel(filter: any, activeFilter: any) {
const filterValue = get(activeFilter, `${filter.field}[0]`);

switch (filterValue) {
Expand All @@ -38,7 +38,7 @@ function getActiveFilterLabel(filter: any, activeFilter: any) {
function AgendaCoverageExistsFilter ({toggleFilter, activeFilter}: any) {
return (
<AgendaDropdown
filter={filter}
filter={agendaCoverageStatusFilter}
activeFilter={activeFilter}
toggleFilter={toggleFilter}
getFilterLabel={getActiveFilterLabel}
Expand All @@ -50,25 +50,25 @@ function AgendaCoverageExistsFilter ({toggleFilter, activeFilter}: any) {
<button
key='coverage-planned'
className='dropdown-item'
onClick={() => toggleFilter(filter.field, FILTER_VALUES.PLANNED)}
onClick={() => toggleFilter(agendaCoverageStatusFilter.field, FILTER_VALUES.PLANNED)}
>{gettext('Coverage is planned')}
</button>
<button
key='coverage-not-planned'
className='dropdown-item'
onClick={() => toggleFilter(filter.field, FILTER_VALUES.NOT_PLANNED)}
onClick={() => toggleFilter(agendaCoverageStatusFilter.field, FILTER_VALUES.NOT_PLANNED)}
>{gettext('Coverage not planned')}
</button>
<button
key='coverage-not-decided'
className='dropdown-item'
onClick={() => toggleFilter(filter.field, FILTER_VALUES.MAY_BE)}
onClick={() => toggleFilter(agendaCoverageStatusFilter.field, FILTER_VALUES.MAY_BE)}
>{gettext('Coverage not decided')}
</button>
<button
key='coverage-completed'
className='dropdown-item'
onClick={() => toggleFilter(filter.field, FILTER_VALUES.COMPLETED)}
onClick={() => toggleFilter(agendaCoverageStatusFilter.field, FILTER_VALUES.COMPLETED)}
>{gettext('Coverage completed')}
</button>
</AgendaDropdown>
Expand Down
2 changes: 1 addition & 1 deletion assets/agenda/components/AgendaDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function AgendaDropdown({
return (
<Dropdown
borderless={borderless}
isActive={isActive}
isActive={(isActive?.length ?? 0) > 0}
icon={filter.icon}
optionLabel={optionLabel}
label={getActiveFilterLabel(filter, activeFilter, isActive)}
Expand Down
6 changes: 6 additions & 0 deletions assets/search/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ export function resetFilter(filter?: any) {
};
}

export const CLEAR_QUICK_FILTER = 'CLEAR_QUICK_FILTER';
export function clearQuickFilter(filter?: string) {
return {type: CLEAR_QUICK_FILTER, filter};
}


export const SET_VIEW = 'SET_VIEW';
export function setView(view: any) {
localStorage.setItem('view', view);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react';
import {SearchResultsTopicRow} from './SearchResultsTopicRow';
import {SearchResultsQueryRow} from './SearchResultsQueryRow';
import {SearchResultsAdvancedSearchRow} from './SearchResultsAdvancedSearchRow';
import {SearchResultsFiltersRow} from './SearchResultsFiltersRow';
import SearchResultsFiltersRow from './SearchResultsFiltersRow';
import {IFilterGroup, INavigation, ISearchFields, ISearchParams, ITopic, IUser} from 'interfaces';
import {SearchResultTagList} from './SearchResultTagList';
import {gettext} from 'utils';
Expand Down Expand Up @@ -34,6 +34,7 @@ export interface IProps {

saveMyTopic?: (params: ISearchParams) => void;
deselectMyTopic?: (topicId: ITopic['_id']) => void;
clearQuickFilter: (filter: string) => void;
}

export function SearchResultTagsList({
Expand All @@ -58,6 +59,7 @@ export function SearchResultTagsList({
deselectMyTopic,
resetFilter,
refresh,
clearQuickFilter,
}: IProps) {
return (
<ul
Expand Down Expand Up @@ -93,6 +95,7 @@ export function SearchResultTagsList({
readonly={readonly}
/>
<SearchResultsFiltersRow
clearQuickFilter={clearQuickFilter}
searchParams={searchParams}
filterGroups={filterGroups}
toggleFilter={toggleFilter}
Expand Down
113 changes: 101 additions & 12 deletions assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import * as React from 'react';
import PropTypes from 'prop-types';

import {gettext, getCreatedSearchParamLabel} from 'utils';

import {SearchResultTagList} from './SearchResultTagList';
import {Tag} from 'components/Tag';

import {IProps as IParentProps} from './SearchResultTagsList';
import {setItemTypeFilter} from 'agenda/actions';
import {searchFilterSelector} from 'search/selectors';
import {connect} from 'react-redux';
import {agendaCoverageStatusFilter, getActiveFilterLabel} from 'agenda/components/AgendaCoverageExistsFilter';

const IS_AGENDA = location.pathname.includes('/agenda');

type IProps = Pick<IParentProps,
'readonly' |
Expand All @@ -15,11 +19,86 @@ type IProps = Pick<IParentProps,
'toggleFilter' |
'setCreatedFilter' |
'resetFilter'
>;
> & {clearQuickFilter: (filter: string) => void;};

type IActiveFilter = {
calendar?: any;
location?: any;
region?: any;
coverage_type?: any;
coverage_status?: any;
};

type IActiveFilterUnionType = keyof IActiveFilter;

interface IReduxStateProps {
itemTypeFilter?: string;
activeFilter?: IActiveFilter;
}

interface IReduxDispatchProps {
clearItemTypeFilter: () => void;
}

type IPropsAgendaExtended = IReduxDispatchProps & IReduxStateProps & IProps;

export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, toggleFilter, setCreatedFilter, resetFilter}: IProps) {
function SearchResultsFiltersRow({
readonly,
searchParams,
filterGroups,
toggleFilter,
setCreatedFilter,
resetFilter,
itemTypeFilter,
clearItemTypeFilter,
activeFilter,
clearQuickFilter,
}: IPropsAgendaExtended) {
const tags = [];

/**
* FIXME: This is a bad implementation, but the proper fix would be too time consuming at this moment.
* Ideally we would want to unify the searchParameters so they are stored in the same variable both from
* agenda and wire. Another solution would be to not reuse the same component in wire and agenda filters
* so that wire has its own filter component and agenda has a separate one. The first solution is the better
* one since from a UI stand point the filters component is identical and should be reused ideally.
*/
if (IS_AGENDA) {
if (itemTypeFilter != null) {
tags.push(
<Tag
key={`tags-filters--from-${itemTypeFilter}`}
testId="tags-filters--agenda-quick-filters"
text={itemTypeFilter === 'events' ? gettext('Events Only') : gettext('Planning Only')}
readOnly={readonly}
onClick={(event) => {
event.preventDefault();
clearItemTypeFilter();
}}
/>
);
}

Object.keys(activeFilter ?? {}).filter((filter) => activeFilter?.[filter as IActiveFilterUnionType] != null)
.forEach((filter) => {
tags.push(
<Tag
key={`tags-filters--${filter}`}
testId={`tags-filters--agenda-quick-filters-${filter}`}
text={filter === 'coverage_status'
? getActiveFilterLabel(agendaCoverageStatusFilter, activeFilter)
: activeFilter?.[filter as IActiveFilterUnionType]
}
readOnly={readonly}
onClick={(event) => {
event.preventDefault();
clearQuickFilter(filter);
}}
/>
);
});
}

if (searchParams.created) {
const created = getCreatedSearchParamLabel(searchParams.created);

Expand Down Expand Up @@ -81,7 +160,7 @@ export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, t
}
}

if (searchParams.filter != null) {
if (searchParams.filter != null && IS_AGENDA !== true) {
for (const field in searchParams.filter) {
const group = filterGroups[field];

Expand Down Expand Up @@ -129,6 +208,7 @@ export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, t
onClick={(event) => {
event.preventDefault();
resetFilter();
clearItemTypeFilter?.();
}}
>
{gettext('Clear filters')}
Expand All @@ -145,10 +225,19 @@ export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, t
);
}

SearchResultsFiltersRow.propTypes = {
searchParams: PropTypes.object,
filterGroups: PropTypes.object,
toggleFilter: PropTypes.func.isRequired,
setCreatedFilter: PropTypes.func.isRequired,
resetFilter: PropTypes.func.isRequired,
};
const mapStateToProps = (state: any) => ({
itemTypeFilter: state.agenda.itemType,
activeFilter: searchFilterSelector(state),
});

const mapDispatchToProps = (dispatch: any) => ({
clearItemTypeFilter: () => dispatch(setItemTypeFilter(null)),
});

let component: React.ComponentType<IProps> = SearchResultsFiltersRow as React.ComponentType<IProps>;

if (IS_AGENDA) {
component = connect<IReduxStateProps, IReduxDispatchProps, IProps>(mapStateToProps, mapDispatchToProps)(SearchResultsFiltersRow);
}

export default component;
3 changes: 3 additions & 0 deletions assets/search/components/SearchResultsBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
clearAdvancedSearchParams,
resetFilter,
deselectMyTopic,
clearQuickFilter,
} from '../../actions';

import {Dropdown} from './../../../components/Dropdown';
Expand Down Expand Up @@ -196,6 +197,7 @@ class SearchResultsBarComponent extends React.Component<any, any> {
)}
{!isTagSectionShown ? null : (
<SearchResultTagsList
clearQuickFilter={this.props.clearQuickFilter}
refresh={this.props.refresh}
user={this.props.user}
showSaveTopic={this.props.showSaveTopic}
Expand Down Expand Up @@ -299,6 +301,7 @@ const mapDispatchToProps = {
clearAdvancedSearchParams,
deselectMyTopic,
resetFilter,
clearQuickFilter,
};

export const SearchResultsBar: React.ComponentType<any> = connect(mapStateToProps, mapDispatchToProps)(SearchResultsBarComponent);
7 changes: 5 additions & 2 deletions assets/search/components/TopicForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {FormSection} from 'components/FormSection';

import {SearchResultTagsList} from './SearchResultsBar/SearchResultTagsList';
import {TopicFolderEditor} from './TopicFolderEditor';
import {noop} from 'lodash';

const TOPIC_NAME_MAXLENGTH = 30;

Expand Down Expand Up @@ -178,7 +179,7 @@ const TopicForm: React.FC<IProps> = ({
</Dropdown>
</div>
{newFolder != null && (
<div
<div
style={{zIndex:'1', insetBlockStart: '-75px', marginBlockEnd: '-56px'}}
className="simple-card__group position-relative"
>
Expand Down Expand Up @@ -254,8 +255,10 @@ const TopicForm: React.FC<IProps> = ({
<div className="nh-flex__row">
<FormSection initiallyOpen={true} name={gettext('Topic details')} dataTestId="topic-form-group--params">
<SearchResultTagsList
user={user}
readonly={true}
// Clearing filters isn't available in readOnly mode.
clearQuickFilter={noop}
user={user}
showSaveTopic={false}
showMyTopic={false}
searchParams={topic}
Expand Down
18 changes: 18 additions & 0 deletions assets/search/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
CLEAR_ADVANCED_SEARCH_PARAMS,
SET_ADVANCED_SEARCH_PARAMS,
SET_SEARCH_SORT_QUERY,
CLEAR_QUICK_FILTER,
} from './actions';

import {EXTENDED_VIEW} from 'wire/defaults';
Expand Down Expand Up @@ -120,6 +121,23 @@ export function searchReducer(state=INITIAL_STATE, action?: any, context?: any)
};
}

case CLEAR_QUICK_FILTER:
if (action.filter == null) {
return {
...state,
activeFilter: {},
};
}

// eslint-disable-next-line no-case-declarations
const updatedQuickFilters = {...state.activeFilter};
delete updatedQuickFilters[action.filter];

return {
...state,
activeFilter: updatedQuickFilters,
};

case RESET_FILTER:
return {
...state,
Expand Down

0 comments on commit 56b6cb8

Please sign in to comment.