Skip to content

Commit 42fbe3d

Browse files
authored
Merge pull request #14 from neubig/openhands-fix-issue-13
Fix issue #13: Add the option to sort PRs
2 parents 9d8b0a0 + 3ccaa13 commit 42fbe3d

File tree

4 files changed

+110
-14
lines changed

4 files changed

+110
-14
lines changed

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
},
2323
"devDependencies": {
2424
"@eslint/js": "^9.8.0",
25+
"@testing-library/user-event": "^14.5.2",
2526
"@types/react": "^18.3.3",
2627
"@types/react-dom": "^18.3.0",
2728
"@vitejs/plugin-react": "^4.3.1",

src/PullRequestViewer.test.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import React from 'react';
44
import { render, screen } from '@testing-library/react';
5+
import userEvent from '@testing-library/user-event';
56
import PullRequestViewer from './PullRequestViewer';
67

78
describe('PullRequestViewer', () => {
@@ -16,4 +17,35 @@ describe('PullRequestViewer', () => {
1617
const selectElement = screen.getByRole('combobox', { name: /select a repository/i });
1718
expect(selectElement).toBeInTheDocument();
1819
});
20+
21+
it('renders the sort select dropdown', () => {
22+
render(<PullRequestViewer />);
23+
const sortSelect = screen.getByRole('combobox', { name: /sort pull requests/i });
24+
expect(sortSelect).toBeInTheDocument();
25+
});
26+
27+
it('displays default sort option', () => {
28+
render(<PullRequestViewer />);
29+
const defaultOption = screen.getByText('Creation Date (Newest)');
30+
expect(defaultOption).toBeInTheDocument();
31+
});
32+
33+
it('shows all sorting options', async () => {
34+
render(<PullRequestViewer />);
35+
const sortSelect = screen.getByRole('combobox', { name: /sort pull requests/i });
36+
await userEvent.click(sortSelect);
37+
38+
const expectedOptions = [
39+
'Creation Date (Newest)',
40+
'Creation Date (Oldest)',
41+
'Last Updated (Newest)',
42+
'Last Updated (Oldest)',
43+
'PR Number (Highest)',
44+
'PR Number (Lowest)',
45+
];
46+
47+
expectedOptions.forEach(option => {
48+
expect(screen.getAllByText(option)[0]).toBeInTheDocument();
49+
});
50+
});
1951
});

src/PullRequestViewer.tsx

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11

22

3-
43
import React, { useState, useEffect } from 'react';
54
import { Octokit } from '@octokit/rest';
65
import Select from 'react-select';
@@ -14,17 +13,36 @@ interface PullRequest {
1413
user: {
1514
login: string;
1615
};
16+
created_at: string;
17+
updated_at: string;
18+
number: number;
1719
}
1820

1921
interface Repo {
2022
value: string;
2123
label: string;
2224
}
2325

26+
interface SortOption {
27+
value: keyof PullRequest | 'number' | 'created_at' | 'updated_at';
28+
label: string;
29+
direction: 'asc' | 'desc';
30+
}
31+
32+
const sortOptions: SortOption[] = [
33+
{ value: 'created_at', label: 'Creation Date (Newest)', direction: 'desc' },
34+
{ value: 'created_at', label: 'Creation Date (Oldest)', direction: 'asc' },
35+
{ value: 'updated_at', label: 'Last Updated (Newest)', direction: 'desc' },
36+
{ value: 'updated_at', label: 'Last Updated (Oldest)', direction: 'asc' },
37+
{ value: 'number', label: 'PR Number (Highest)', direction: 'desc' },
38+
{ value: 'number', label: 'PR Number (Lowest)', direction: 'asc' },
39+
];
40+
2441
const PullRequestViewer: React.FC = () => {
2542
const [repos, setRepos] = useState<Repo[]>([]);
2643
const [selectedRepo, setSelectedRepo] = useState<Repo | null>(null);
2744
const [pullRequests, setPullRequests] = useState<PullRequest[]>([]);
45+
const [selectedSort, setSelectedSort] = useState<SortOption>(sortOptions[0]);
2846

2947
useEffect(() => {
3048
const fetchRepos = async () => {
@@ -33,7 +51,7 @@ const PullRequestViewer: React.FC = () => {
3351
org: GITHUB_ORG,
3452
type: 'all',
3553
});
36-
const repoOptions = response.data.map(repo => ({
54+
const repoOptions = response.data.map((repo) => ({
3755
value: repo.name,
3856
label: repo.name,
3957
}));
@@ -49,7 +67,7 @@ const PullRequestViewer: React.FC = () => {
4967
const fetchPullRequests = async () => {
5068
if (selectedRepo) {
5169
try {
52-
let allPullRequests: PullRequest[] = [];
70+
const allPullRequests: PullRequest[] = [];
5371
let page = 1;
5472
let hasNextPage = true;
5573

@@ -59,10 +77,10 @@ const PullRequestViewer: React.FC = () => {
5977
repo: selectedRepo.value,
6078
state: 'open',
6179
per_page: 100,
62-
page: page,
80+
page,
6381
});
6482

65-
allPullRequests = [...allPullRequests, ...response.data];
83+
allPullRequests.push(...response.data);
6684

6785
if (response.data.length < 100) {
6886
hasNextPage = false;
@@ -80,25 +98,55 @@ const PullRequestViewer: React.FC = () => {
8098
fetchPullRequests();
8199
}, [selectedRepo]);
82100

101+
const sortPullRequests = (prs: PullRequest[]) => {
102+
return [...prs].sort((a, b) => {
103+
const aValue = a[selectedSort.value];
104+
const bValue = b[selectedSort.value];
105+
106+
if (selectedSort.direction === 'asc') {
107+
return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
108+
} else {
109+
return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
110+
}
111+
});
112+
};
113+
114+
const sortedPullRequests = sortPullRequests(pullRequests);
115+
83116
return (
84117
<div>
85118
<h1>Pull Request Viewer</h1>
86-
<Select
87-
options={repos}
88-
value={selectedRepo}
89-
onChange={(option) => setSelectedRepo(option as Repo)}
90-
placeholder="Select a repository"
91-
aria-label="Select a repository"
92-
/>
93-
{pullRequests.length > 0 ? (
119+
<div style={{ marginBottom: '1rem' }}>
120+
<Select
121+
options={repos}
122+
value={selectedRepo}
123+
onChange={(option) => setSelectedRepo(option as Repo)}
124+
placeholder="Select a repository"
125+
aria-label="Select a repository"
126+
/>
127+
</div>
128+
<div style={{ marginBottom: '1rem' }}>
129+
<Select
130+
options={sortOptions}
131+
value={selectedSort}
132+
onChange={(option) => setSelectedSort(option as SortOption)}
133+
placeholder="Sort by"
134+
aria-label="Sort pull requests"
135+
/>
136+
</div>
137+
{sortedPullRequests.length > 0 ? (
94138
<ul>
95-
{pullRequests.map((pr) => (
139+
{sortedPullRequests.map((pr) => (
96140
<li key={pr.html_url}>
97141
<a href={pr.html_url} target="_blank" rel="noopener noreferrer">
98142
{pr.title}
99143
</a>
100144
{' by '}
101145
{pr.user.login}
146+
{' - '}
147+
Created: {new Date(pr.created_at).toLocaleDateString()}
148+
{', '}
149+
Last updated: {new Date(pr.updated_at).toLocaleDateString()}
102150
</li>
103151
))}
104152
</ul>

0 commit comments

Comments
 (0)