Skip to content

Commit f058058

Browse files
authored
Add clickable button to filter issues and PRs
Add the ability for users to filter cards by type (issue or PR) directly from the card view headers. Clicking the issue or PR count toggles a filter to show only items of that type. Clicking the active filter again reverts this filter back to the original state. This enhancement provides a more responsive, one-click method for filtering, which is especially useful in high-volume scenarios such as the huge influx of issues during PE-D or actual PE.
1 parent a56cd14 commit f058058

File tree

6 files changed

+109
-14
lines changed

6 files changed

+109
-14
lines changed

src/app/issues-viewer/card-view/card-view.component.css

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,34 @@ div.column-header .mat-card-header {
131131
margin: 6px 0;
132132
}
133133

134-
.row-count {
134+
.issue-row-count,
135+
.pr-row-count {
135136
background-color: rgb(222, 222, 222);
136137
border-radius: 3px;
137-
cursor: default;
138+
cursor: pointer;
138139
padding: 6px 8px 3px 8px;
139140
color: rgb(0, 0, 0);
140141
font-weight: 410;
141142
display: inline-flex;
142143
font-size: 14px;
143144
flex-shrink: 0;
144145
margin-top: 3px;
146+
user-select: none;
147+
}
148+
149+
.issue-row-count:hover,
150+
.pr-row-count:hover {
151+
background-color: rgb(200, 200, 200);
152+
}
153+
154+
.issue-row-count.active,
155+
.pr-row-count.active {
156+
background-color: #b8b8b8;
157+
}
158+
159+
.issue-row-count.inactive,
160+
.pr-row-count.inactive {
161+
background-color: rgb(222, 222, 222);
145162
}
146163

147164
.assignee-container {

src/app/issues-viewer/card-view/card-view.component.html

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<div class="scrollable-container-wrapper scroll-shadow">
77
<div class="scrollable-container">
88
<div class="issue-pr-cards" *ngFor="let issue of this.issues$ | async; index as i">
9-
<app-issue-pr-card [issue]="issue" [filter]="issues.filter" [milestoneService]="milestoneService"></app-issue-pr-card>
9+
<app-issue-pr-card [issue]="issue" [filter]="issues.filter" [milestoneService]="milestoneService"> </app-issue-pr-card>
1010
</div>
1111
<mat-card class="loading-spinner" *ngIf="this.issues.isLoading$ | async">
1212
<mat-progress-spinner color="primary" mode="indeterminate" diameter="50" strokeWidth="5"></mat-progress-spinner>
@@ -43,21 +43,37 @@
4343
<mat-card-title>
4444
<div class="assignee-container">
4545
<div
46-
class="assignee-name"
46+
class="assignee-name no-select"
4747
(click)="goToGithubProfile(assignee.login)"
4848
[ngClass]="{ clickable: assignee.login !== 'Unassigned' }"
4949
>
5050
<span class="" [matTooltip]="getAssigneeTooltip(assignee)" matTooltipPosition="above">
5151
{{ assignee.login }}
5252
</span>
5353
</div>
54-
<div class="row-count count-margins" [matTooltip]="getIssueTooltip()" matTooltipPosition="above">
54+
<div
55+
class="issue-row-count count-margins"
56+
[class.inactive]="!this.issues.hasIssue"
57+
[matTooltip]="getIssueTooltip()"
58+
matTooltipPosition="above"
59+
(click)="this.issues.hasIssue && filterByIssues()"
60+
[class.active]="currentFilter === 'issues'"
61+
[style.cursor]="this.issues.hasIssue ? 'pointer' : 'default'"
62+
>
5563
<span class="octicon count-margins" octicon="issue-opened"></span>
56-
<span class="">{{ this.issues.issueCount }}</span>
64+
<span class="no-select">{{ this.issues.issueCount }}</span>
5765
</div>
58-
<div class="row-count" [matTooltip]="getPrTooltip()" matTooltipPosition="above">
66+
<div
67+
class="pr-row-count"
68+
[class.inactive]="!this.issues.hasPR"
69+
[matTooltip]="getPrTooltip()"
70+
matTooltipPosition="above"
71+
(click)="this.issues.hasPR && filterByPrs()"
72+
[class.active]="currentFilter === 'prs'"
73+
[style.cursor]="this.issues.hasPR ? 'pointer' : 'default'"
74+
>
5975
<span class="octicon count-margins" octicon="git-pull-request"></span>
60-
<span>{{ this.issues.prCount }}</span>
76+
<span class="no-select">{{ this.issues.prCount }}</span>
6177
</div>
6278
</div>
6379
</mat-card-title>
@@ -72,16 +88,32 @@
7288
<mat-card-header [ngStyle]="{ height: '40px' }">
7389
<mat-card-title>
7490
<div class="milestone-container">
75-
<div>
91+
<div class="no-select">
7692
{{ milestone.title }}
7793
</div>
78-
<div class="row-count count-margins" [matTooltip]="getIssueTooltip()" matTooltipPosition="above">
94+
<div
95+
class="issue-row-count count-margins"
96+
[class.inactive]="!this.issues.hasIssue"
97+
[matTooltip]="getIssueTooltip()"
98+
matTooltipPosition="above"
99+
(click)="this.issues.hasIssue && filterByIssues()"
100+
[class.active]="currentFilter === 'issues'"
101+
[style.cursor]="this.issues.hasIssue ? 'pointer' : 'default'"
102+
>
79103
<span class="octicon count-margins" octicon="issue-opened"></span>
80-
<span class="">{{ this.issues.issueCount }}</span>
104+
<span class="no-select">{{ this.issues.issueCount }}</span>
81105
</div>
82-
<div class="row-count" [matTooltip]="getPrTooltip()" matTooltipPosition="above">
106+
<div
107+
class="pr-row-count"
108+
[class.inactive]="!this.issues.hasPR"
109+
[matTooltip]="getPrTooltip()"
110+
matTooltipPosition="above"
111+
(click)="this.issues.hasPR && filterByPrs()"
112+
[class.active]="currentFilter === 'prs'"
113+
[style.cursor]="this.issues.hasPR ? 'pointer' : 'default'"
114+
>
83115
<span class="octicon count-margins" octicon="git-pull-request"></span>
84-
<span>{{ this.issues.prCount }}</span>
116+
<span class="no-select">{{ this.issues.prCount }}</span>
85117
</div>
86118
</div>
87119
</mat-card-title>

src/app/issues-viewer/card-view/card-view.component.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ export class CardViewComponent implements OnInit, AfterViewInit, OnDestroy, Filt
5353
isLoading = true;
5454
issueLength = 0;
5555

56+
currentFilter: 'all' | 'issues' | 'prs' = 'all';
57+
5658
pageSize = 20;
5759

5860
@Output() issueLengthChange: EventEmitter<Number> = new EventEmitter<Number>();
@@ -146,6 +148,18 @@ export class CardViewComponent implements OnInit, AfterViewInit, OnDestroy, Filt
146148
return this.issues.prCount + ' Pull Requests';
147149
}
148150

151+
filterByIssues(): void {
152+
const issueFilter = this.currentFilter === 'issues' ? 'all' : 'issues';
153+
this.currentFilter = issueFilter;
154+
this.issues.setIssueTypeFilter(issueFilter);
155+
}
156+
157+
filterByPrs(): void {
158+
const issueFilter = this.currentFilter === 'prs' ? 'all' : 'prs';
159+
this.currentFilter = issueFilter;
160+
this.issues.setIssueTypeFilter(issueFilter);
161+
}
162+
149163
getAssigneeTooltip(assignee: any): string {
150164
return assignee.login;
151165
}

src/app/shared/issue-pr-card/issue-pr-card.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<mat-card class="card" [ngClass]="getIssueOpenOrCloseColorCSSClass()">
1+
<mat-card class="card" [class.issue-card]="isIssue()" [ngClass]="getIssueOpenOrCloseColorCSSClass()">
22
<a class="no-underline link-grey-dark">
33
<span [matTooltip]="this.issue.updated_at">
44
<app-issue-pr-card-header [issue]="issue" (click)="viewIssueInBrowser($event)"></app-issue-pr-card-header>

src/app/shared/issue-pr-card/issue-pr-card.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export class IssuePrCardComponent {
2222
public milestoneService: MilestoneService
2323
) {}
2424

25+
isIssue(): boolean {
26+
return this.issue.issueOrPr === 'Issue';
27+
}
28+
2529
isNotFollowingForkingWorkflow() {
2630
return (
2731
this.issue.issueOrPr === 'PullRequest' && this.issue.headRepository?.toLowerCase() === this.githubService.getRepoURL().toLowerCase()

src/app/shared/issue-tables/IssuesDataTable.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ export class IssuesDataTable extends DataSource<Issue> implements FilterableSour
2121
public count = 0;
2222
public issueCount = 0;
2323
public prCount = 0;
24+
public hasIssue = false;
25+
public hasPR = false;
2426
private filterChange = new BehaviorSubject(this.filtersService.defaultFilter);
2527
private issuesSubject = new BehaviorSubject<Issue[]>([]);
2628
private issueSubscription: Subscription;
29+
private issueTypeFilter: 'all' | 'issues' | 'prs' = 'all'; // initialise as 'all'
2730

2831
public isLoading$ = this.issueService.isLoading.asObservable();
2932

@@ -69,6 +72,15 @@ export class IssuesDataTable extends DataSource<Issue> implements FilterableSour
6972
this.issueService.stopPollIssues();
7073
}
7174

75+
setIssueTypeFilter(filter: 'all' | 'issues' | 'prs') {
76+
this.issueTypeFilter = filter;
77+
this.loadIssues();
78+
}
79+
80+
getIssueTypeFilter(): 'all' | 'issues' | 'prs' {
81+
return this.issueTypeFilter;
82+
}
83+
7284
loadIssues() {
7385
let page;
7486
if (this.paginator !== undefined) {
@@ -98,8 +110,24 @@ export class IssuesDataTable extends DataSource<Issue> implements FilterableSour
98110
data = applyDropdownFilter(this.filter, data, !this.milestoneService.hasNoMilestones, !this.assigneeService.hasNoAssignees);
99111

100112
data = applySearchFilter(this.filter.title, this.displayedColumn, this.issueService, data);
113+
101114
this.issueCount = data.filter((issue) => issue.issueOrPr !== 'PullRequest').length;
102115
this.prCount = data.filter((issue) => issue.issueOrPr === 'PullRequest').length;
116+
this.hasIssue = this.issueCount > 0;
117+
this.hasPR = this.prCount > 0;
118+
119+
// Apply Issue Type Filter for header component
120+
if (this.issueTypeFilter !== 'all') {
121+
const issueType = this.issueTypeFilter === 'issues' ? 'Issue' : 'PullRequest';
122+
const filteredData = data.filter((issue) => issue.issueOrPr === issueType);
123+
124+
if (filteredData.length === 0) {
125+
this.issueTypeFilter = 'all';
126+
} else {
127+
data = filteredData;
128+
}
129+
}
130+
103131
this.count = data.length;
104132

105133
data = applySort(this.filter.sort, data);

0 commit comments

Comments
 (0)