From 7fde73fbe8f0c1230303e53990dfbf0b6fa7d36c Mon Sep 17 00:00:00 2001 From: thecalcc <me@kmarkov.io> Date: Wed, 11 Oct 2023 13:11:43 +0300 Subject: [PATCH 1/6] Init --- assets/agenda/components/AgendaApp.tsx | 1 + .../components/AgendaCoverageExistsFilter.tsx | 14 +- assets/agenda/components/AgendaDropdown.tsx | 2 +- assets/search/actions.ts | 6 + .../SearchResultsBar/SearchResultTagsList.tsx | 2 + .../SearchResultsAgendaQucikFiltersRow.tsx | 163 ++++++++++++++++++ .../SearchResultsFiltersRow.tsx | 11 +- assets/search/reducers.ts | 18 ++ 8 files changed, 199 insertions(+), 18 deletions(-) create mode 100644 assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx diff --git a/assets/agenda/components/AgendaApp.tsx b/assets/agenda/components/AgendaApp.tsx index 67e1fae84..5ed35a5ae 100644 --- a/assets/agenda/components/AgendaApp.tsx +++ b/assets/agenda/components/AgendaApp.tsx @@ -150,6 +150,7 @@ class AgendaApp extends BaseApp { this.props.activeQuery != null || this.props.itemTypeFilter != null; + // console.log(this.props.itemTypeFilter, this.props.activeFilter); return ( (this.props.itemToOpen ? [<AgendaItemDetails key="itemDetails" item={this.props.itemToOpen} diff --git a/assets/agenda/components/AgendaCoverageExistsFilter.tsx b/assets/agenda/components/AgendaCoverageExistsFilter.tsx index 794655c91..42c7d3490 100644 --- a/assets/agenda/components/AgendaCoverageExistsFilter.tsx +++ b/assets/agenda/components/AgendaCoverageExistsFilter.tsx @@ -5,7 +5,7 @@ import {get} from 'lodash'; import {gettext} from 'utils'; import {AgendaDropdown} from './AgendaDropdown'; -const filter = { +export const agendaCoverageStatusFilter = { label: gettext('Any coverage status'), field: 'coverage_status', nestedField: 'coverage_status', @@ -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) { @@ -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} @@ -47,25 +47,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> diff --git a/assets/agenda/components/AgendaDropdown.tsx b/assets/agenda/components/AgendaDropdown.tsx index 5c146de91..228a35141 100644 --- a/assets/agenda/components/AgendaDropdown.tsx +++ b/assets/agenda/components/AgendaDropdown.tsx @@ -32,7 +32,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)} diff --git a/assets/search/actions.ts b/assets/search/actions.ts index 58e9b3279..250257ad4 100644 --- a/assets/search/actions.ts +++ b/assets/search/actions.ts @@ -182,6 +182,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); diff --git a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx index 8c008a6a1..45cea6628 100644 --- a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx @@ -8,6 +8,7 @@ import {IFilterGroup, INavigation, ISearchFields, ISearchParams, ITopic, IUser} import {SearchResultTagList} from './SearchResultTagList'; import {gettext} from 'utils'; import {getTopicUrl} from 'search/utils'; +import {SearchResultsAgendaQuickFilters} from './SearchResultsAgendaQucikFiltersRow'; export interface IProps { user: IUser; @@ -100,6 +101,7 @@ export function SearchResultTagsList({ resetFilter={resetFilter} readonly={readonly} /> + <SearchResultsAgendaQuickFilters /> {readonly === true && activeTopic._id && ( <SearchResultTagList testId="search-results--edit-button" diff --git a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx new file mode 100644 index 000000000..12e6f5881 --- /dev/null +++ b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx @@ -0,0 +1,163 @@ +import * as React from 'react'; +import {gettext} from 'utils'; + +import {SearchResultTagList} from './SearchResultTagList'; +import {Tag} from 'components/Tag'; + +import {searchFilterSelector} from 'search/selectors'; +import {connect} from 'react-redux'; +import {clearQuickFilter} from 'search/actions'; +import {agendaCoverageStatusFilter, getActiveFilterLabel} from 'agenda/components/AgendaCoverageExistsFilter'; +import {setItemTypeFilter} from 'agenda/actions'; + +type IProps = any; + +function SearchResultsAgendaQuickFiltersRow({ + itemTypeFilter, + activeFilter, + clearQuickFilter, + clearItemTypeFilter, + clearAllQuickFilters, +}: IProps) { + const pills = []; + + if (itemTypeFilter != null) { + pills.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(); + }} + /> + ); + } + + if (activeFilter?.['calendar'] != null) { + pills.push( + <Tag + key="tags-filters--calendar" + testId="tags-filters--agenda-quick-filters-calendar" + text={activeFilter['calendar']} + // readOnly={readonly} + onClick={(event) => { + event.preventDefault(); + clearQuickFilter('calendar'); + }} + /> + ); + } + + if (activeFilter?.['location'] != null) { + pills.push( + <Tag + key="tags-filters--location" + testId="tags-filters--agenda-quick-filters-location" + text={activeFilter['location']} + // readOnly={readonly} + onClick={(event) => { + event.preventDefault(); + clearQuickFilter('location'); + }} + /> + ); + } + + if (activeFilter?.['region'] != null) { + pills.push( + <Tag + key="tags-filters--region" + testId="tags-filters--agenda-quick-filters-region" + text={activeFilter['region']} + // readOnly={readonly} + onClick={(event) => { + event.preventDefault(); + clearQuickFilter('region'); + }} + /> + ); + } + + if (activeFilter?.['coverage_type'] != null) { + pills.push( + <Tag + key="tags-filters--coverage_type" + testId="tags-filters--agenda-quick-filters-coverage_type" + text={activeFilter['coverage_type']} + // readOnly={readonly} + onClick={(event) => { + event.preventDefault(); + clearQuickFilter('coverage_type'); + }} + /> + ); + } + + if (activeFilter?.['coverage_status'] != null) { + pills.push( + <Tag + key="tags-filters--coverage_status" + testId="tags-filters--agenda-quick-filters-coverage_status" + text={getActiveFilterLabel(agendaCoverageStatusFilter, activeFilter)} + // readOnly={readonly} + onClick={(event) => { + event.preventDefault(); + clearQuickFilter('coverage_status'); + }} + /> + ); + } + + if ((pills?.length ?? 0) < 1) { + return <li>no content</li>; + } + + pills.push( + <span + key="tags-filters-separator--clear-1" + className="tag-list__separator tag-list__separator--blanc" + /> + ); + + pills.push( + <button + key="tag-filters--clear-button-1" + className='nh-button nh-button--tertiary nh-button--small' + onClick={(event) => { + event.preventDefault(); + clearAllQuickFilters(); + }} + > + {gettext('Clear filters')} + </button> + ); + + return ( + <SearchResultTagList + testId="search-results--filters" + title={gettext('Quick filters')} + tags={pills} + /> + ); +} + +const mapStateToProps = (state: any) => ({ + itemTypeFilter: state.agenda.itemType, + activeFilter: searchFilterSelector(state), +}); + +const mapDispatchToProps = (dispatch: any) => ({ + clearQuickFilter: (filter: string) => dispatch(clearQuickFilter(filter)), + clearItemTypeFilter: () => dispatch(setItemTypeFilter(null)), + clearAllQuickFilters: () => { + dispatch(setItemTypeFilter(null)); + dispatch(clearQuickFilter()); + } +}); + + +export const SearchResultsAgendaQuickFilters: React.ComponentType<any> = + connect<any>(mapStateToProps, mapDispatchToProps)(SearchResultsAgendaQuickFiltersRow); diff --git a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx index ee7cca9bc..c30f9303d 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx @@ -1,12 +1,11 @@ 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 {SearchResultsAgendaQuickFilters} from './SearchResultsAgendaQucikFiltersRow'; type IProps = Pick<IParentProps, 'readonly' | @@ -144,11 +143,3 @@ 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, -}; diff --git a/assets/search/reducers.ts b/assets/search/reducers.ts index 6142184de..91b9bf85f 100644 --- a/assets/search/reducers.ts +++ b/assets/search/reducers.ts @@ -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'; @@ -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, From eb51b49d1131793db2590a12546e86826640e0fe Mon Sep 17 00:00:00 2001 From: thecalcc <me@kmarkov.io> Date: Thu, 12 Oct 2023 14:42:47 +0300 Subject: [PATCH 2/6] Fix breaking of agenda, wire pills dropdown --- .../components/SearchResultsBar/SearchResultTagsList.tsx | 4 +++- .../SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx | 4 ++-- .../components/SearchResultsBar/SearchResultsFiltersRow.tsx | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx index 45cea6628..416984ae9 100644 --- a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx @@ -101,7 +101,9 @@ export function SearchResultTagsList({ resetFilter={resetFilter} readonly={readonly} /> - <SearchResultsAgendaQuickFilters /> + {location.pathname.includes('/agenda') && ( + <SearchResultsAgendaQuickFilters /> + )} {readonly === true && activeTopic._id && ( <SearchResultTagList testId="search-results--edit-button" diff --git a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx index 12e6f5881..248ddc021 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx @@ -112,7 +112,7 @@ function SearchResultsAgendaQuickFiltersRow({ } if ((pills?.length ?? 0) < 1) { - return <li>no content</li>; + return null; } pills.push( @@ -138,7 +138,7 @@ function SearchResultsAgendaQuickFiltersRow({ return ( <SearchResultTagList testId="search-results--filters" - title={gettext('Quick filters')} + title={gettext('Quick filters applied')} tags={pills} /> ); diff --git a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx index c30f9303d..2a5b8cdaa 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx @@ -5,7 +5,6 @@ import {SearchResultTagList} from './SearchResultTagList'; import {Tag} from 'components/Tag'; import {IProps as IParentProps} from './SearchResultTagsList'; -import {SearchResultsAgendaQuickFilters} from './SearchResultsAgendaQucikFiltersRow'; type IProps = Pick<IParentProps, 'readonly' | @@ -80,7 +79,7 @@ export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, t } } - if (searchParams.filter != null) { + if (searchParams.filter != null && location.pathname.includes('/agenda') !== true) { for (const field in searchParams.filter) { const group = filterGroups[field]; From 1a0fccf650f38fa3dd22706e02db4de19cfc5dde Mon Sep 17 00:00:00 2001 From: thecalcc <me@kmarkov.io> Date: Thu, 12 Oct 2023 15:16:52 +0300 Subject: [PATCH 3/6] Add types --- assets/agenda/components/AgendaApp.tsx | 1 - .../SearchResultsAgendaQucikFiltersRow.tsx | 23 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/assets/agenda/components/AgendaApp.tsx b/assets/agenda/components/AgendaApp.tsx index 5ed35a5ae..67e1fae84 100644 --- a/assets/agenda/components/AgendaApp.tsx +++ b/assets/agenda/components/AgendaApp.tsx @@ -150,7 +150,6 @@ class AgendaApp extends BaseApp { this.props.activeQuery != null || this.props.itemTypeFilter != null; - // console.log(this.props.itemTypeFilter, this.props.activeFilter); return ( (this.props.itemToOpen ? [<AgendaItemDetails key="itemDetails" item={this.props.itemToOpen} diff --git a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx index 248ddc021..9e17ea78e 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx @@ -10,7 +10,24 @@ import {clearQuickFilter} from 'search/actions'; import {agendaCoverageStatusFilter, getActiveFilterLabel} from 'agenda/components/AgendaCoverageExistsFilter'; import {setItemTypeFilter} from 'agenda/actions'; -type IProps = any; +interface IReduxStateProps { + itemTypeFilter?: string; + activeFilter?: { + calendar?: any; + location?: any; + region?: any; + coverage_type?: any; + coverage_status?: any; + }; +} + +interface IReduxDispatchProps { + clearQuickFilter: (filter: string) => void; + clearItemTypeFilter: () => void; + clearAllQuickFilters: () => void; +} + +type IProps = IReduxDispatchProps & IReduxStateProps; function SearchResultsAgendaQuickFiltersRow({ itemTypeFilter, @@ -159,5 +176,5 @@ const mapDispatchToProps = (dispatch: any) => ({ }); -export const SearchResultsAgendaQuickFilters: React.ComponentType<any> = - connect<any>(mapStateToProps, mapDispatchToProps)(SearchResultsAgendaQuickFiltersRow); +export const SearchResultsAgendaQuickFilters: React.ComponentType = + connect<IReduxStateProps, IReduxDispatchProps>(mapStateToProps, mapDispatchToProps)(SearchResultsAgendaQuickFiltersRow); From b1c73d69ae5c36b038db384a2105618e4e3438d3 Mon Sep 17 00:00:00 2001 From: thecalcc <me@kmarkov.io> Date: Tue, 24 Oct 2023 15:31:07 +0300 Subject: [PATCH 4/6] Move agenda filter pills to quick filter pills --- .../SearchResultsBar/SearchResultTagsList.tsx | 6 +- .../SearchResultsAgendaQucikFiltersRow.tsx | 180 ------------------ .../SearchResultsFiltersRow.tsx | 102 +++++++++- 3 files changed, 101 insertions(+), 187 deletions(-) delete mode 100644 assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx diff --git a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx index 416984ae9..912d90436 100644 --- a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx @@ -3,12 +3,11 @@ 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'; import {getTopicUrl} from 'search/utils'; -import {SearchResultsAgendaQuickFilters} from './SearchResultsAgendaQucikFiltersRow'; export interface IProps { user: IUser; @@ -101,9 +100,6 @@ export function SearchResultTagsList({ resetFilter={resetFilter} readonly={readonly} /> - {location.pathname.includes('/agenda') && ( - <SearchResultsAgendaQuickFilters /> - )} {readonly === true && activeTopic._id && ( <SearchResultTagList testId="search-results--edit-button" diff --git a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx deleted file mode 100644 index 9e17ea78e..000000000 --- a/assets/search/components/SearchResultsBar/SearchResultsAgendaQucikFiltersRow.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import * as React from 'react'; -import {gettext} from 'utils'; - -import {SearchResultTagList} from './SearchResultTagList'; -import {Tag} from 'components/Tag'; - -import {searchFilterSelector} from 'search/selectors'; -import {connect} from 'react-redux'; -import {clearQuickFilter} from 'search/actions'; -import {agendaCoverageStatusFilter, getActiveFilterLabel} from 'agenda/components/AgendaCoverageExistsFilter'; -import {setItemTypeFilter} from 'agenda/actions'; - -interface IReduxStateProps { - itemTypeFilter?: string; - activeFilter?: { - calendar?: any; - location?: any; - region?: any; - coverage_type?: any; - coverage_status?: any; - }; -} - -interface IReduxDispatchProps { - clearQuickFilter: (filter: string) => void; - clearItemTypeFilter: () => void; - clearAllQuickFilters: () => void; -} - -type IProps = IReduxDispatchProps & IReduxStateProps; - -function SearchResultsAgendaQuickFiltersRow({ - itemTypeFilter, - activeFilter, - clearQuickFilter, - clearItemTypeFilter, - clearAllQuickFilters, -}: IProps) { - const pills = []; - - if (itemTypeFilter != null) { - pills.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(); - }} - /> - ); - } - - if (activeFilter?.['calendar'] != null) { - pills.push( - <Tag - key="tags-filters--calendar" - testId="tags-filters--agenda-quick-filters-calendar" - text={activeFilter['calendar']} - // readOnly={readonly} - onClick={(event) => { - event.preventDefault(); - clearQuickFilter('calendar'); - }} - /> - ); - } - - if (activeFilter?.['location'] != null) { - pills.push( - <Tag - key="tags-filters--location" - testId="tags-filters--agenda-quick-filters-location" - text={activeFilter['location']} - // readOnly={readonly} - onClick={(event) => { - event.preventDefault(); - clearQuickFilter('location'); - }} - /> - ); - } - - if (activeFilter?.['region'] != null) { - pills.push( - <Tag - key="tags-filters--region" - testId="tags-filters--agenda-quick-filters-region" - text={activeFilter['region']} - // readOnly={readonly} - onClick={(event) => { - event.preventDefault(); - clearQuickFilter('region'); - }} - /> - ); - } - - if (activeFilter?.['coverage_type'] != null) { - pills.push( - <Tag - key="tags-filters--coverage_type" - testId="tags-filters--agenda-quick-filters-coverage_type" - text={activeFilter['coverage_type']} - // readOnly={readonly} - onClick={(event) => { - event.preventDefault(); - clearQuickFilter('coverage_type'); - }} - /> - ); - } - - if (activeFilter?.['coverage_status'] != null) { - pills.push( - <Tag - key="tags-filters--coverage_status" - testId="tags-filters--agenda-quick-filters-coverage_status" - text={getActiveFilterLabel(agendaCoverageStatusFilter, activeFilter)} - // readOnly={readonly} - onClick={(event) => { - event.preventDefault(); - clearQuickFilter('coverage_status'); - }} - /> - ); - } - - if ((pills?.length ?? 0) < 1) { - return null; - } - - pills.push( - <span - key="tags-filters-separator--clear-1" - className="tag-list__separator tag-list__separator--blanc" - /> - ); - - pills.push( - <button - key="tag-filters--clear-button-1" - className='nh-button nh-button--tertiary nh-button--small' - onClick={(event) => { - event.preventDefault(); - clearAllQuickFilters(); - }} - > - {gettext('Clear filters')} - </button> - ); - - return ( - <SearchResultTagList - testId="search-results--filters" - title={gettext('Quick filters applied')} - tags={pills} - /> - ); -} - -const mapStateToProps = (state: any) => ({ - itemTypeFilter: state.agenda.itemType, - activeFilter: searchFilterSelector(state), -}); - -const mapDispatchToProps = (dispatch: any) => ({ - clearQuickFilter: (filter: string) => dispatch(clearQuickFilter(filter)), - clearItemTypeFilter: () => dispatch(setItemTypeFilter(null)), - clearAllQuickFilters: () => { - dispatch(setItemTypeFilter(null)); - dispatch(clearQuickFilter()); - } -}); - - -export const SearchResultsAgendaQuickFilters: React.ComponentType = - connect<IReduxStateProps, IReduxDispatchProps>(mapStateToProps, mapDispatchToProps)(SearchResultsAgendaQuickFiltersRow); diff --git a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx index 2a5b8cdaa..b43065262 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx @@ -5,6 +5,13 @@ import {SearchResultTagList} from './SearchResultTagList'; import {Tag} from 'components/Tag'; import {IProps as IParentProps} from './SearchResultTagsList'; +import {setItemTypeFilter} from 'agenda/actions'; +import {clearQuickFilter} from 'search/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' | @@ -15,9 +22,78 @@ type IProps = Pick<IParentProps, 'resetFilter' >; -export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, toggleFilter, setCreatedFilter, resetFilter}: IProps) { +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 { + clearQuickFilter: (filter: string) => void; + clearItemTypeFilter: () => void; + clearAllQuickFilters: () => void; +} + +type IPropsAgendaExtended = IReduxDispatchProps & IReduxStateProps & IProps; + +function SearchResultsFiltersRow({ + readonly, + searchParams, + filterGroups, + toggleFilter, + setCreatedFilter, + resetFilter, + itemTypeFilter, + clearItemTypeFilter, + activeFilter, +}: IPropsAgendaExtended) { const tags = []; + 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); @@ -79,7 +155,7 @@ export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, t } } - if (searchParams.filter != null && location.pathname.includes('/agenda') !== true) { + if (searchParams.filter != null && IS_AGENDA !== true) { for (const field in searchParams.filter) { const group = filterGroups[field]; @@ -142,3 +218,25 @@ export function SearchResultsFiltersRow({readonly, searchParams, filterGroups, t /> ); } + +const mapStateToProps = (state: any) => ({ + itemTypeFilter: state.agenda.itemType, + activeFilter: searchFilterSelector(state), +}); + +const mapDispatchToProps = (dispatch: any) => ({ + clearQuickFilter: (filter: string) => dispatch(clearQuickFilter(filter)), + clearItemTypeFilter: () => dispatch(setItemTypeFilter(null)), + clearAllQuickFilters: () => { + dispatch(setItemTypeFilter(null)); + dispatch(clearQuickFilter()); + } +}); + +let component: React.ComponentType<IProps> = SearchResultsFiltersRow as React.ComponentType<IProps>; + +if (IS_AGENDA) { + component = connect<IReduxStateProps, IReduxDispatchProps, IProps>(mapStateToProps, mapDispatchToProps)(SearchResultsFiltersRow); +} + +export default component; From 04585ca3fb07f08e817e2989066d7d28bc9b8907 Mon Sep 17 00:00:00 2001 From: thecalcc <me@kmarkov.io> Date: Thu, 26 Oct 2023 15:18:11 +0300 Subject: [PATCH 5/6] Add coment, fix X button not removing agenda filters --- .../SearchResultsBar/SearchResultTagsList.tsx | 4 ++++ .../SearchResultsFiltersRow.tsx | 19 ++++++++++--------- .../components/SearchResultsBar/index.tsx | 3 +++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx index 912d90436..ce7fc16e0 100644 --- a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx @@ -34,6 +34,8 @@ export interface IProps { saveMyTopic?: (params: ISearchParams) => void; deselectMyTopic?: (topicId: ITopic['_id']) => void; + clearQuickFilter: (filter: string) => void; + } export function SearchResultTagsList({ @@ -58,6 +60,7 @@ export function SearchResultTagsList({ deselectMyTopic, resetFilter, refresh, + clearQuickFilter, }: IProps) { return ( <ul @@ -93,6 +96,7 @@ export function SearchResultTagsList({ readonly={readonly} /> <SearchResultsFiltersRow + clearQuickFilter={clearQuickFilter} searchParams={searchParams} filterGroups={filterGroups} toggleFilter={toggleFilter} diff --git a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx index b43065262..6037e96be 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx @@ -6,7 +6,6 @@ import {Tag} from 'components/Tag'; import {IProps as IParentProps} from './SearchResultTagsList'; import {setItemTypeFilter} from 'agenda/actions'; -import {clearQuickFilter} from 'search/actions'; import {searchFilterSelector} from 'search/selectors'; import {connect} from 'react-redux'; import {agendaCoverageStatusFilter, getActiveFilterLabel} from 'agenda/components/AgendaCoverageExistsFilter'; @@ -20,7 +19,7 @@ type IProps = Pick<IParentProps, 'toggleFilter' | 'setCreatedFilter' | 'resetFilter' ->; +> & {clearQuickFilter: (filter: string) => void;}; type IActiveFilter = { calendar?: any; @@ -38,9 +37,7 @@ interface IReduxStateProps { } interface IReduxDispatchProps { - clearQuickFilter: (filter: string) => void; clearItemTypeFilter: () => void; - clearAllQuickFilters: () => void; } type IPropsAgendaExtended = IReduxDispatchProps & IReduxStateProps & IProps; @@ -55,9 +52,17 @@ function SearchResultsFiltersRow({ 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( @@ -203,6 +208,7 @@ function SearchResultsFiltersRow({ onClick={(event) => { event.preventDefault(); resetFilter(); + clearItemTypeFilter?.(); }} > {gettext('Clear filters')} @@ -225,12 +231,7 @@ const mapStateToProps = (state: any) => ({ }); const mapDispatchToProps = (dispatch: any) => ({ - clearQuickFilter: (filter: string) => dispatch(clearQuickFilter(filter)), clearItemTypeFilter: () => dispatch(setItemTypeFilter(null)), - clearAllQuickFilters: () => { - dispatch(setItemTypeFilter(null)); - dispatch(clearQuickFilter()); - } }); let component: React.ComponentType<IProps> = SearchResultsFiltersRow as React.ComponentType<IProps>; diff --git a/assets/search/components/SearchResultsBar/index.tsx b/assets/search/components/SearchResultsBar/index.tsx index 326b817f6..f641d615d 100644 --- a/assets/search/components/SearchResultsBar/index.tsx +++ b/assets/search/components/SearchResultsBar/index.tsx @@ -16,6 +16,7 @@ import { clearAdvancedSearchParams, resetFilter, deselectMyTopic, + clearQuickFilter, } from '../../actions'; import {Dropdown} from './../../../components/Dropdown'; @@ -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} @@ -299,6 +301,7 @@ const mapDispatchToProps = { clearAdvancedSearchParams, deselectMyTopic, resetFilter, + clearQuickFilter, }; export const SearchResultsBar: React.ComponentType<any> = connect(mapStateToProps, mapDispatchToProps)(SearchResultsBarComponent); From 3b9ca37473b312c08917c5425eaacdfacc4864f2 Mon Sep 17 00:00:00 2001 From: thecalcc <me@kmarkov.io> Date: Thu, 26 Oct 2023 20:54:32 +0300 Subject: [PATCH 6/6] Fix lint --- .../components/SearchResultsBar/SearchResultTagsList.tsx | 1 - assets/search/components/TopicForm.tsx | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx index ce7fc16e0..62fcd54f1 100644 --- a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx @@ -35,7 +35,6 @@ export interface IProps { saveMyTopic?: (params: ISearchParams) => void; deselectMyTopic?: (topicId: ITopic['_id']) => void; clearQuickFilter: (filter: string) => void; - } export function SearchResultTagsList({ diff --git a/assets/search/components/TopicForm.tsx b/assets/search/components/TopicForm.tsx index b6aa37047..eb30f431b 100644 --- a/assets/search/components/TopicForm.tsx +++ b/assets/search/components/TopicForm.tsx @@ -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; @@ -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" > @@ -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}