diff --git a/CHANGELOG.md b/CHANGELOG.md index 250f8eb..8176d0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **`name-desc` and `stars-asc` sort values** — `SortValue` now covers both directions for every field: `'name-desc'` sorts Z-A via `sortByName(projects, 'desc')`, and `'stars-asc'` sorts lowest-stars-first via `sortByStars(projects, 'asc')`. Every existing value keeps its meaning; `'stars'` remains descending, mirroring `'date'`. Fixes [#20]. - **Opt-out flags for auto-generated GitHub links** — `github` and `hybrid` projects accept `useGithubLinkFromRepo` and `useLiveLinkFromRepo` (both default `true`) to suppress the repo-URL `github` link and the repo-homepage `live` link. Explicit `links` in config still win. For the pre-existing `linkOrder`-based `live` suppression, the docs now explain how the two mechanisms interact. Fixes [#24]. ### Fixed diff --git a/packages/core/src/lib/__tests__/sortProjects.test.ts b/packages/core/src/lib/__tests__/sortProjects.test.ts index 2fdd9f8..af0327f 100644 --- a/packages/core/src/lib/__tests__/sortProjects.test.ts +++ b/packages/core/src/lib/__tests__/sortProjects.test.ts @@ -44,6 +44,20 @@ describe('sortProjects', () => { }) }) + describe('sort by stars-asc', () => { + it('should call sortByStars with asc order (lowest first)', () => { + const projects = [ + createProject({ id: '1', type: 'github', stats: { stars: 100 } }), + createProject({ id: '2', type: 'github', stats: { stars: 500 } }), + createProject({ id: '3', type: 'github', stats: { stars: 250 } }), + ] + + const result = sortProjects(projects, 'stars-asc') + + expect(result.map(p => p.id)).toEqual(['1', '3', '2']) + }) + }) + describe('sort by name', () => { it('should call sortByName with asc order and return result', () => { const projects = [ @@ -58,6 +72,20 @@ describe('sortProjects', () => { }) }) + describe('sort by name-desc', () => { + it('should call sortByName with desc order (Z-A) and return result', () => { + const projects = [ + createProject({ id: '1', name: 'Zebra' }), + createProject({ id: '2', name: 'Alpha' }), + createProject({ id: '3', name: 'Beta' }), + ] + + const result = sortProjects(projects, 'name-desc') + + expect(result.map(p => p.id)).toEqual(['1', '3', '2']) + }) + }) + describe('sort by date', () => { it('should call sortByDate with desc order (newest first)', () => { const projects = [ diff --git a/packages/core/src/lib/sortProjects.ts b/packages/core/src/lib/sortProjects.ts index 0867a04..5e2bce3 100644 --- a/packages/core/src/lib/sortProjects.ts +++ b/packages/core/src/lib/sortProjects.ts @@ -3,7 +3,7 @@ import { sortByStars } from './sortByStars' import { sortByName } from './sortByName' import { sortByDate } from './sortByDate' -export type SortValue = 'stars' | 'name' | 'date' | 'date-asc' +export type SortValue = 'stars' | 'stars-asc' | 'name' | 'name-desc' | 'date' | 'date-asc' export function sortProjects(projects: ProjexProject[], sortValue: SortValue): ProjexProject[] { if (projects.length === 0) { @@ -13,8 +13,12 @@ export function sortProjects(projects: ProjexProject[], sortValue: SortValue): P switch (sortValue) { case 'stars': return sortByStars(projects) + case 'stars-asc': + return sortByStars(projects, 'asc') case 'name': return sortByName(projects, 'asc') + case 'name-desc': + return sortByName(projects, 'desc') case 'date': return sortByDate(projects, 'desc') case 'date-asc': diff --git a/packages/docs/src/api/types/sort-value.md b/packages/docs/src/api/types/sort-value.md index 21cddbb..b896194 100644 --- a/packages/docs/src/api/types/sort-value.md +++ b/packages/docs/src/api/types/sort-value.md @@ -5,7 +5,7 @@ Allowed sort values for the `sortProjects` function. Defines how projects can be ## Definition ```tsx -type SortValue = 'stars' | 'name' | 'date' | 'date-asc' +type SortValue = 'stars' | 'stars-asc' | 'name' | 'name-desc' | 'date' | 'date-asc' ``` ## Values @@ -13,7 +13,9 @@ type SortValue = 'stars' | 'name' | 'date' | 'date-asc' | Value | Description | Direction | Usage | |-------|-------------|-----------|--------| | `stars` | Sort by GitHub stars | Descending (highest first) | GitHub and hybrid projects | +| `stars-asc` | Sort by GitHub stars | Ascending (lowest first) | GitHub and hybrid projects | | `name` | Sort alphabetically by project name | Ascending (A-Z) | All project types | +| `name-desc` | Sort alphabetically by project name | Descending (Z-A) | All project types | | `date` | Sort by `updatedAt` or `createdAt` | Descending (newest first) | All project types | | `date-asc` | Sort by `updatedAt` or `createdAt` | Ascending (oldest first) | All project types | @@ -41,6 +43,17 @@ const sorted = sortProjects(projects, 'stars') // [1000 stars, 500 stars, 200 stars, 0 stars, manual project, npm project] ``` +### `stars-asc` + +- Same data source as `stars` +- Sorts ascending (lowest stars first) +- Projects without stars are still placed at the end + +```tsx +const sorted = sortProjects(projects, 'stars-asc') +// [200 stars, 500 stars, 1000 stars, 0 stars, manual project, npm project] +``` + ### `name` - Alphabetical sort using `localeCompare` with `sensitivity: 'base'` (case-insensitive) @@ -53,6 +66,17 @@ const sorted = sortProjects(projects, 'name') // ['Alpha Project', 'Beta App', 'Gamma Tool'] ``` +### `name-desc` + +- Same data source as `name` +- Sorts descending (Z-A) +- Projects without a name are still placed at the end + +```tsx +const sorted = sortProjects(projects, 'name-desc') +// ['Gamma Tool', 'Beta App', 'Alpha Project'] +``` + ### `date` - Uses `updatedAt` if available, falls back to `createdAt` @@ -93,7 +117,7 @@ function ProjectsPage({ projects }) { return (
diff --git a/packages/docs/src/api/utilities/sort-projects.md b/packages/docs/src/api/utilities/sort-projects.md index f7d193f..b057277 100644 --- a/packages/docs/src/api/utilities/sort-projects.md +++ b/packages/docs/src/api/utilities/sort-projects.md @@ -24,12 +24,14 @@ function sortProjects(projects: ProjexProject[], sortValue: SortValue): ProjexPr | Value | Description | |-------|-------------| | `'stars'` | Sort by GitHub stars (descending) | -| `'name'` | Sort alphabetically by name (ascending) | +| `'stars-asc'` | Sort by GitHub stars (ascending) | +| `'name'` | Sort alphabetically by name (ascending, A-Z) | +| `'name-desc'` | Sort alphabetically by name (descending, Z-A) | | `'date'` | Sort by date (newest first, descending) | | `'date-asc'` | Sort by date (oldest first, ascending) | ```tsx -type SortValue = 'stars' | 'name' | 'date' | 'date-asc' +type SortValue = 'stars' | 'stars-asc' | 'name' | 'name-desc' | 'date' | 'date-asc' ``` ## Behavior @@ -43,7 +45,7 @@ type SortValue = 'stars' | 'name' | 'date' | 'date-asc' ```tsx import { sortProjects, ProjectSort, ProjectGrid, ProjectCard } from '@manningworks/projex' -const SORT_OPTIONS = ['stars', 'name', 'date', 'date-asc'] +const SORT_OPTIONS = ['stars', 'stars-asc', 'name', 'name-desc', 'date', 'date-asc'] function ProjectShowcase({ projects }) { const [sortValue, setSortValue] = useState('stars') @@ -81,7 +83,7 @@ import { } from '@manningworks/projex' const AVAILABLE_TAGS = ['React', 'TypeScript', 'Node.js'] -const SORT_OPTIONS = ['stars', 'name', 'date', 'date-asc'] +const SORT_OPTIONS = ['stars', 'stars-asc', 'name', 'name-desc', 'date', 'date-asc'] function ProjectShowcase({ projects }) { const [query, setQuery] = useState('')