Skip to content

Commit

Permalink
Status filter checkboxes (#310)
Browse files Browse the repository at this point in the history
We implement checkboxes for status, so that
multiple types of PRs/issues can be viewed concurrently.
  • Loading branch information
nknguyenhc authored Mar 25, 2024
1 parent e3d4a34 commit a65bb59
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 44 deletions.
41 changes: 12 additions & 29 deletions src/app/core/services/filters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SimpleLabel } from '../models/label.model';

export type Filter = {
title: string;
status: string;
status: string[];
type: string;
sort: Sort;
labels: string[];
Expand All @@ -16,7 +16,7 @@ export type Filter = {

export const DEFAULT_FILTER: Filter = {
title: '',
status: 'all',
status: ['open pullrequest', 'merged pullrequest', 'open issue', 'closed issue'],
type: 'all',
sort: { active: 'id', direction: 'asc' },
labels: [],
Expand All @@ -35,21 +35,16 @@ export const DEFAULT_FILTER: Filter = {
export class FiltersService {
public filter$ = new BehaviorSubject<Filter>(DEFAULT_FILTER);

private _validateFilter = pipe(this.updateStatusPairing, this.updateTypePairing);

clearFilters(): void {
this.filter$.next(DEFAULT_FILTER);
}

updateFilters(newFilters: Partial<Filter>): void {
let nextFilter: Filter = {
const nextDropdownFilter: Filter = {
...this.filter$.value,
...newFilters
};

nextFilter = this._validateFilter(nextFilter);

this.filter$.next(nextFilter);
this.filter$.next(nextDropdownFilter);
}

sanitizeLabels(allLabels: SimpleLabel[]) {
Expand All @@ -62,27 +57,15 @@ export class FiltersService {
}
}

const newLabels = this.filter$.value.labels.filter((label) => allLabelsSet.has(label));

this.updateFilters({ labels: newLabels, hiddenLabels: newHiddenLabels });
}
/**
* Changes type to a valid, default value when an incompatible combination of type and status is encountered.
*/
updateTypePairing(filter: Filter): Filter {
if (filter.status === 'merged') {
filter.type = 'pullrequest';
const newDeselectedLabels: Set<string> = new Set();
for (const deselectedLabel of this.filter$.value.deselectedLabels) {
if (allLabelsSet.has(deselectedLabel)) {
newDeselectedLabels.add(deselectedLabel);
}
}
return filter;
}

/**
* Changes status to a valid, default value when an incompatible combination of type and status is encountered.
*/
updateStatusPairing(filter: Filter): Filter {
if (filter.status === 'merged' && filter.type === 'issue') {
filter.status = 'all';
}
return filter;
const newLabels = this.filter$.value.labels.filter((label) => allLabelsSet.has(label));

this.updateFilters({ labels: newLabels, hiddenLabels: newHiddenLabels, deselectedLabels: newDeselectedLabels });
}
}
11 changes: 6 additions & 5 deletions src/app/shared/filter-bar/filter-bar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
<div class="dropdown-filters">
<mat-form-field appearance="standard">
<mat-label>Status</mat-label>
<mat-select [value]="this.filter.status" (selectionChange)="this.filtersService.updateFilters({ status: $event.value })">
<mat-option value="all">All</mat-option>
<mat-option value="open">Open</mat-option>
<mat-option value="closed">Closed</mat-option>
<mat-option value="merged" *ngIf="isNotFilterIssue()">Merged</mat-option>
<mat-select [value]="this.filter.status" (selectionChange)="this.filtersService.updateFilters({ status: $event.value })" multiple>
<mat-option *ngIf="isFilterPullRequest()" value="open pullrequest">Open Pull Requests</mat-option>
<mat-option *ngIf="isFilterPullRequest()" value="merged pullrequest">Merged Pull Requests</mat-option>
<mat-option *ngIf="isFilterPullRequest()" value="closed pullrequest">Closed Pull Request</mat-option>
<mat-option *ngIf="isFilterIssue()" value="open issue">Open Issues</mat-option>
<mat-option *ngIf="isFilterIssue()" value="closed issue">Closed Issues</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="standard">
Expand Down
8 changes: 6 additions & 2 deletions src/app/shared/filter-bar/filter-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ export class FilterBarComponent implements OnInit, OnDestroy {
/**
* Checks if program is filtering by type issue.
*/
isNotFilterIssue() {
return this.filter.type !== 'issue';
isFilterIssue() {
return this.filter.type === 'issue' || this.filter.type === 'all';
}

isFilterPullRequest() {
return this.filter.type === 'pullrequest' || this.filter.type === 'all';
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class LabelFilterBarComponent implements OnInit, AfterViewInit, OnDestroy
this.allLabels = labels;
this.filtersService.sanitizeLabels(this.allLabels);
this.selectedLabelNames = new Set<string>(this.filtersService.filter$.value.labels);
this.deselectedLabelNames = this.filtersService.filter$.value.deselectedLabels;
this.hiddenLabelNames = this.filtersService.filter$.value.hiddenLabels;
});
});
Expand Down
28 changes: 20 additions & 8 deletions src/app/shared/issue-tables/dropdownfilter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { Issue } from '../../core/models/issue.model';
import { Filter } from '../../core/services/filters.service';

type StatusInfo = {
type: string;
status: string;
};

/**
* Converts a status string into an info object
*/
const infoFromStatus = (statusString: string): StatusInfo => {
const [status, type] = statusString.split(' ');
return { status, type };
};

/**
* This module serves to improve separation of concerns in IssuesDataTable.ts and IssueList.ts module by containing the logic for
* applying dropdownFilter to the issues data table in this module.
Expand All @@ -11,14 +24,13 @@ export function applyDropdownFilter(filter: Filter, data: Issue[]): Issue[] {
const filteredData: Issue[] = data.filter((issue) => {
let ret = true;

if (filter.status === 'open') {
ret = ret && issue.state === 'OPEN';
} else if (filter.status === 'closed') {
// there is apparently also a status called 'all' based on github api
ret = ret && issue.state === 'CLOSED';
} else if (filter.status === 'merged') {
ret = ret && issue.state === 'MERGED';
}
// status can either be 'open', 'closed', or 'merged'
ret =
ret &&
filter.status.some((item) => {
const statusInfo = infoFromStatus(item);
return statusInfo.status === issue.state.toLowerCase() && statusInfo.type === issue.issueOrPr.toLowerCase();
});

if (filter.type === 'issue') {
ret = ret && issue.issueOrPr === 'Issue';
Expand Down

0 comments on commit a65bb59

Please sign in to comment.