diff --git a/package-lock.json b/package-lock.json index 2195477..3c58f44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@eslint/js": "^9.8.0", + "@testing-library/user-event": "^14.5.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", @@ -1692,6 +1693,20 @@ } } }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", diff --git a/package.json b/package.json index 1ee556f..594782c 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "devDependencies": { "@eslint/js": "^9.8.0", + "@testing-library/user-event": "^14.5.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/src/PullRequestViewer.test.tsx b/src/PullRequestViewer.test.tsx index 10f2959..ca6bc07 100644 --- a/src/PullRequestViewer.test.tsx +++ b/src/PullRequestViewer.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import PullRequestViewer from './PullRequestViewer'; describe('PullRequestViewer', () => { @@ -16,4 +17,35 @@ describe('PullRequestViewer', () => { const selectElement = screen.getByRole('combobox', { name: /select a repository/i }); expect(selectElement).toBeInTheDocument(); }); + + it('renders the sort select dropdown', () => { + render(); + const sortSelect = screen.getByRole('combobox', { name: /sort pull requests/i }); + expect(sortSelect).toBeInTheDocument(); + }); + + it('displays default sort option', () => { + render(); + const defaultOption = screen.getByText('Creation Date (Newest)'); + expect(defaultOption).toBeInTheDocument(); + }); + + it('shows all sorting options', async () => { + render(); + const sortSelect = screen.getByRole('combobox', { name: /sort pull requests/i }); + await userEvent.click(sortSelect); + + const expectedOptions = [ + 'Creation Date (Newest)', + 'Creation Date (Oldest)', + 'Last Updated (Newest)', + 'Last Updated (Oldest)', + 'PR Number (Highest)', + 'PR Number (Lowest)', + ]; + + expectedOptions.forEach(option => { + expect(screen.getAllByText(option)[0]).toBeInTheDocument(); + }); + }); }); diff --git a/src/PullRequestViewer.tsx b/src/PullRequestViewer.tsx index c2e9399..923e115 100644 --- a/src/PullRequestViewer.tsx +++ b/src/PullRequestViewer.tsx @@ -1,6 +1,5 @@ - import React, { useState, useEffect } from 'react'; import { Octokit } from '@octokit/rest'; import Select from 'react-select'; @@ -14,6 +13,9 @@ interface PullRequest { user: { login: string; }; + created_at: string; + updated_at: string; + number: number; } interface Repo { @@ -21,10 +23,26 @@ interface Repo { label: string; } +interface SortOption { + value: keyof PullRequest | 'number' | 'created_at' | 'updated_at'; + label: string; + direction: 'asc' | 'desc'; +} + +const sortOptions: SortOption[] = [ + { value: 'created_at', label: 'Creation Date (Newest)', direction: 'desc' }, + { value: 'created_at', label: 'Creation Date (Oldest)', direction: 'asc' }, + { value: 'updated_at', label: 'Last Updated (Newest)', direction: 'desc' }, + { value: 'updated_at', label: 'Last Updated (Oldest)', direction: 'asc' }, + { value: 'number', label: 'PR Number (Highest)', direction: 'desc' }, + { value: 'number', label: 'PR Number (Lowest)', direction: 'asc' }, +]; + const PullRequestViewer: React.FC = () => { const [repos, setRepos] = useState([]); const [selectedRepo, setSelectedRepo] = useState(null); const [pullRequests, setPullRequests] = useState([]); + const [selectedSort, setSelectedSort] = useState(sortOptions[0]); useEffect(() => { const fetchRepos = async () => { @@ -33,7 +51,7 @@ const PullRequestViewer: React.FC = () => { org: GITHUB_ORG, type: 'all', }); - const repoOptions = response.data.map(repo => ({ + const repoOptions = response.data.map((repo) => ({ value: repo.name, label: repo.name, })); @@ -49,7 +67,7 @@ const PullRequestViewer: React.FC = () => { const fetchPullRequests = async () => { if (selectedRepo) { try { - let allPullRequests: PullRequest[] = []; + const allPullRequests: PullRequest[] = []; let page = 1; let hasNextPage = true; @@ -59,10 +77,10 @@ const PullRequestViewer: React.FC = () => { repo: selectedRepo.value, state: 'open', per_page: 100, - page: page, + page, }); - allPullRequests = [...allPullRequests, ...response.data]; + allPullRequests.push(...response.data); if (response.data.length < 100) { hasNextPage = false; @@ -80,25 +98,55 @@ const PullRequestViewer: React.FC = () => { fetchPullRequests(); }, [selectedRepo]); + const sortPullRequests = (prs: PullRequest[]) => { + return [...prs].sort((a, b) => { + const aValue = a[selectedSort.value]; + const bValue = b[selectedSort.value]; + + if (selectedSort.direction === 'asc') { + return aValue < bValue ? -1 : aValue > bValue ? 1 : 0; + } else { + return bValue < aValue ? -1 : bValue > aValue ? 1 : 0; + } + }); + }; + + const sortedPullRequests = sortPullRequests(pullRequests); + return (

Pull Request Viewer

- setSelectedRepo(option as Repo)} + placeholder="Select a repository" + aria-label="Select a repository" + /> +
+
+