Skip to content

Commit

Permalink
fix(openchallenges): fix bugs related search dropdown filters on sear…
Browse files Browse the repository at this point in the history
…ch page (#2688)

Co-authored-by: Thomas Schaffter <[email protected]>
  • Loading branch information
rrchai and tschaffter authored Jun 6, 2024
1 parent b56a599 commit 045c55c
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ export type ChallengeSearchDropdown =
| 'inputDataTypes'
| 'organizations'
| 'platforms';

export const CHALLENGE_SEARCH_DROPDOWNS: ChallengeSearchDropdown[] = [
'inputDataTypes',
'organizations',
'platforms',
];
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ <h2>Challenges</h2>
>
<openchallenges-checkbox-filter
[options]="incentivesFilter.options"
[selectedOptions]="selectedIncentives"
[selectedOptions]="selectedValues['incentives']"
(selectionChange)="onParamChange({ incentives: $event })"
inputId="{{ incentivesFilter.query }}"
/>
Expand All @@ -54,7 +54,7 @@ <h2>Challenges</h2>
>
<openchallenges-checkbox-filter
[options]="statusFilter.options"
[selectedOptions]="selectedStatus"
[selectedOptions]="selectedValues['status']"
(selectionChange)="onParamChange({ status: $event })"
inputId="{{ statusFilter.query }}"
/>
Expand All @@ -67,7 +67,7 @@ <h2>Challenges</h2>
>
<openchallenges-checkbox-filter
[options]="categoriesFilter.options"
[selectedOptions]="selectedCategories"
[selectedOptions]="selectedValues['categories']"
(selectionChange)="onParamChange({ categories: $event })"
inputId="{{ categoriesFilter.query }}"
/>
Expand All @@ -80,13 +80,14 @@ <h2>Challenges</h2>
>
<openchallenges-search-dropdown-filter
[options]="dropdownFilters['platforms'].options"
[selectedOptions]="selectedPlatforms"
[optionsPerPage]="defaultDropdownOptionSize"
[selectedOptions]="selectedValues['platforms']"
placeholder="{{ dropdownFilters['platforms'].label.toLowerCase() + '(s)' }} "
[showAvatar]="dropdownFilters['platforms'].showAvatar"
[filterByApiClient]="true"
(selectionChange)="onParamChange({ platforms: $event })"
(searchChange)="onSearchChange('platforms', $event)"
(lazyLoad)="onLazyLoad('platforms', $event)"
(pageChange)="onLazyLoad('platforms', $event)"
/>
</p-panel>
<p-divider></p-divider>
Expand All @@ -97,13 +98,14 @@ <h2>Challenges</h2>
>
<openchallenges-search-dropdown-filter
[options]="dropdownFilters['inputDataTypes'].options"
[selectedOptions]="selectedInputDataTypes"
[optionsPerPage]="defaultDropdownOptionSize"
[selectedOptions]="selectedValues['inputDataTypes']"
placeholder="{{ dropdownFilters['inputDataTypes'].label.toLowerCase() + '(s)' }} "
[showAvatar]="dropdownFilters['inputDataTypes'].showAvatar"
[filterByApiClient]="true"
(selectionChange)="onParamChange({ inputDataTypes: $event })"
(searchChange)="onSearchChange('inputDataTypes', $event)"
(lazyLoad)="onLazyLoad('inputDataTypes', $event)"
(pageChange)="onLazyLoad('inputDataTypes', $event)"
/>
</p-panel>
<p-divider></p-divider>
Expand All @@ -114,13 +116,14 @@ <h2>Challenges</h2>
>
<openchallenges-search-dropdown-filter
[options]="dropdownFilters['organizations'].options"
[selectedOptions]="selectedOrgs"
[optionsPerPage]="defaultDropdownOptionSize"
[selectedOptions]="selectedValues['organizations']"
placeholder="{{ dropdownFilters['organizations'].label.toLowerCase() + '(s)' }} "
[showAvatar]="dropdownFilters['organizations'].showAvatar"
[filterByApiClient]="true"
(selectionChange)="onParamChange({ organizations: $event })"
(searchChange)="onSearchChange('organizations', $event)"
(lazyLoad)="onLazyLoad('organizations', $event)"
(pageChange)="onLazyLoad('organizations', $event)"
/>
</p-panel>
<p-divider></p-divider>
Expand Down Expand Up @@ -162,7 +165,7 @@ <h2>Challenges</h2>
>
<openchallenges-checkbox-filter
[options]="submissionTypesFilter.options"
[selectedOptions]="selectedSubmissionTypes"
[selectedOptions]="selectedValues['submissionTypes']"
(selectionChange)="onParamChange({ submissionTypes: $event })"
inputId="{{ submissionTypesFilter.query }}"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ import { SeoService } from '@sagebionetworks/shared/util';
import { getSeoData } from './challenge-search-seo-data';
import { HttpParams } from '@angular/common/http';
import { ChallengeSearchDataService } from './challenge-search-data.service';
import { MultiSelectLazyLoadEvent } from 'primeng/multiselect';
import { ChallengeSearchDropdown } from './challenge-search-dropdown';
import {
ChallengeSearchDropdown,
CHALLENGE_SEARCH_DROPDOWNS,
} from './challenge-search-dropdown';

@Component({
selector: 'openchallenges-challenge-search',
Expand Down Expand Up @@ -133,6 +135,7 @@ export class ChallengeSearchComponent
// set default values
defaultPageNumber = 0;
defaultPageSize = 24;
defaultDropdownOptionSize = 50;
defaultSelectedYear = undefined;
defaultSortedBy: ChallengeSort = 'relevance';

Expand All @@ -148,16 +151,17 @@ export class ChallengeSearchComponent

// set dropdown filter placeholders
dropdownFilters!: { [key: string]: FilterPanel };
loadedPages!: { [key: string]: Set<number> };

// define selected filter values
selectedCategories!: ChallengeCategory[];
selectedIncentives!: ChallengeIncentive[];
selectedInputDataTypes!: number[];
selectedOrgs!: number[];
selectedPlatforms!: string[];
selectedStatus!: ChallengeStatus[];
selectedSubmissionTypes!: ChallengeSubmissionType[];
selectedValues = {
categories: [] as ChallengeCategory[],
incentives: [] as ChallengeIncentive[],
inputDataTypes: [] as number[],
organizations: [] as number[],
platforms: [] as string[],
status: [] as ChallengeStatus[],
submissionTypes: [] as ChallengeSubmissionType[],
};

constructor(
private activatedRoute: ActivatedRoute,
Expand Down Expand Up @@ -200,36 +204,40 @@ export class ChallengeSearchComponent
}

// update selected filter values based on params in url
this.selectedCategories = this.splitParam(params['categories']);
this.selectedIncentives = this.splitParam(params['incentives']);
this.selectedInputDataTypes = this.splitParam(
params['inputDataTypes']
).map((idString) => +idString);

this.searchedTerms = params['searchTerms'];
this.selectedOrgs = this.splitParam(params['organizations']).map(
(idString) => +idString
);
this.selectedPageNumber = +params['pageNumber'] || this.defaultPageNumber;
this.selectedPageSize = this.defaultPageSize; // no available pageSize options for users
this.selectedPlatforms = this.splitParam(params['platforms']);
this.selectedStatus = this.splitParam(params['status']);
this.selectedSubmissionTypes = this.splitParam(params['submissionTypes']);
this.sortedBy = params['sort'] || this.defaultSortedBy;

this.selectedValues['categories'] = this.splitParam(params['categories']);
this.selectedValues['incentives'] = this.splitParam(params['incentives']);
this.selectedValues['inputDataTypes'] = this.splitParam(
params['inputDataTypes']
).map((idString) => +idString);
this.selectedValues['organizations'] = this.splitParam(
params['organizations']
).map((idString) => +idString);
this.selectedValues['platforms'] = this.splitParam(params['platforms']);
this.selectedValues['status'] = this.splitParam(params['status']);
this.selectedValues['submissionTypes'] = this.splitParam(
params['submissionTypes']
);

const defaultQuery: ChallengeSearchQuery = {
categories: this.selectedCategories,
incentives: this.selectedIncentives,
inputDataTypes: this.selectedInputDataTypes,
categories: this.selectedValues['categories'],
incentives: this.selectedValues['incentives'],
inputDataTypes: this.selectedValues['inputDataTypes'],
maxStartDate: this.selectedMaxStartDate,
minStartDate: this.selectedMinStartDate,
organizations: this.selectedOrgs,
organizations: this.selectedValues['organizations'],
pageNumber: this.selectedPageNumber,
pageSize: this.selectedPageSize,
platforms: this.selectedPlatforms,
platforms: this.selectedValues['platforms'],
searchTerms: this.searchedTerms,
sort: this.sortedBy,
status: this.selectedStatus,
submissionTypes: this.selectedSubmissionTypes,
status: this.selectedValues['status'],
submissionTypes: this.selectedValues['submissionTypes'],
};

this.query.next(defaultQuery);
Expand All @@ -240,12 +248,7 @@ export class ChallengeSearchComponent
this.totalChallengesCount = page.totalElements;
});

// update loaded pages and dropdown filters
this.loadedPages = {
inputDataTypes: new Set(),
organizations: new Set(),
platforms: new Set(),
};
// update dropdown filters
this.dropdownFilters = {
inputDataTypes: challengeInputDataTypesFilterPanel,
organizations: challengeOrganizationsFilterPanel,
Expand Down Expand Up @@ -330,6 +333,14 @@ export class ChallengeSearchComponent
// this.selectedPageSize = this.defaultPageSize;
this.paginator.resetPageNumber();
}

// update selected filter values if specific parameters change
Object.keys(filteredQuery).forEach((key) => {
if (key in this.selectedValues && filteredQuery[key] !== undefined) {
(this.selectedValues as any)[key] = filteredQuery[key];
}
});

// update params of URL
const currentParams = new HttpParams({
fromString: this._location.path().split('?')[1] ?? '',
Expand Down Expand Up @@ -365,52 +376,68 @@ export class ChallengeSearchComponent
if (searchType === 'challenges') {
this.challengeSearchTerms.next(searched);
} else {
this.loadedPages[searchType].clear();
this.dropdownFilters[searchType].options = [];
this.challengeSearchDataService.setSearchQuery(searchType, {
// reset options except selections when search term is applied
const selectedOptions = this.dropdownFilters[searchType].options.filter(
(option) => {
if (Array.isArray(option.value)) {
return option.value.some((item) =>
(this.selectedValues[searchType] as FilterValue[]).includes(item)
);
} else {
return (this.selectedValues[searchType] as FilterValue[]).includes(
option.value
);
}
}
);
this.dropdownFilters[searchType].options = selectedOptions;
this.challengeSearchDataService.setEdamConceptSearchQuery({
searchTerms: searched,
});
}
}

onLazyLoad(
dropdown: ChallengeSearchDropdown,
event: MultiSelectLazyLoadEvent
): void {
const size = this.defaultPageSize;
const startPage = Math.floor(event.first / size);
const endPage = Math.floor(event.last / size);
onLazyLoad(dropdown: ChallengeSearchDropdown, page: number): void {
const query: any = { pageNumber: page };

// load next page as scrolling down
for (let page = startPage; page <= endPage; page++) {
if (!this.loadedPages[dropdown].has(page)) {
this.loadedPages[dropdown].add(page);
this.challengeSearchDataService.setSearchQuery(dropdown, {
pageNumber: page,
pageSize: size,
});
}
if (page === 0) {
// reset ids and slugs params of dropdown search query
query.ids = undefined;
query.slugs = undefined;
}
// load next page as scrolling down
this.challengeSearchDataService.setSearchQuery(dropdown, query);
}

private setDropdownSelections(): void {
this.challengeSearchDataService.setSearchQuery('inputDataTypes', {
ids: this.selectedValues['inputDataTypes'],
});
this.challengeSearchDataService.setSearchQuery('organizations', {
ids: this.selectedValues['organizations'],
});
this.challengeSearchDataService.setSearchQuery('platforms', {
slugs: this.selectedValues['platforms'],
});
}

private loadInitialDropdownData(): void {
const dropdowns = [
'inputDataTypes',
'organizations',
'platforms',
] as ChallengeSearchDropdown[];
dropdowns.forEach((dropdown) => {
// query the dropdown filter option(s) pre-selected in url param (only initially)
this.setDropdownSelections();

// fetch and update dropdown options with new data for each dropdown category
CHALLENGE_SEARCH_DROPDOWNS.forEach((dropdown) => {
const extraDefaultParams =
dropdown === 'inputDataTypes' ? { sections: [EdamSection.Data] } : {};

this.challengeSearchDataService
.fetchData(dropdown, {
pageSize: this.defaultPageSize, // set constant pageSize to match lazyLoad
pageSize: this.defaultDropdownOptionSize, // set constant pageSize to match lazyLoad
...extraDefaultParams,
})
.pipe(takeUntil(this.destroy))
.subscribe((newOptions) => {
// update filter options by taking unique filter values
// update filter options by appending new unique filter values to the bottom
this.dropdownFilters[dropdown].options = unionWith(
this.dropdownFilters[dropdown].options,
newOptions,
Expand Down
Loading

0 comments on commit 045c55c

Please sign in to comment.