From 7d8fc468435186e225ec3fc058fede9e76690685 Mon Sep 17 00:00:00 2001 From: Luke Manning Date: Tue, 18 Aug 2026 23:00:37 +0100 Subject: [PATCH 1/2] feat(sort): add name-desc and stars-asc to SortValue sortProjects could not express Name Z-A or ascending stars, forcing consumers past the convenience switch to the granular sorters. Extend the string union with the two missing directions; existing values keep their meaning. Object-enum redesign intentionally out of scope (#20). --- .../src/lib/__tests__/sortProjects.test.ts | 28 +++++++++++++++++++ packages/core/src/lib/sortProjects.ts | 6 +++- packages/docs/src/api/types/sort-value.md | 26 ++++++++++++++++- .../docs/src/api/utilities/sort-projects.md | 6 ++-- 4 files changed, 62 insertions(+), 4 deletions(-) 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..532e4b0 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` diff --git a/packages/docs/src/api/utilities/sort-projects.md b/packages/docs/src/api/utilities/sort-projects.md index f7d193f..dd4e0c7 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 From 054bf2281ca1295f15006d960889cbb0db42c87e Mon Sep 17 00:00:00 2001 From: Luke Manning Date: Tue, 18 Aug 2026 23:16:34 +0100 Subject: [PATCH 2/2] docs(sort): add changelog entry and show all six sort options in examples --- CHANGELOG.md | 1 + packages/docs/src/api/types/sort-value.md | 2 +- packages/docs/src/api/utilities/sort-projects.md | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) 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/docs/src/api/types/sort-value.md b/packages/docs/src/api/types/sort-value.md index 532e4b0..b896194 100644 --- a/packages/docs/src/api/types/sort-value.md +++ b/packages/docs/src/api/types/sort-value.md @@ -117,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 dd4e0c7..b057277 100644 --- a/packages/docs/src/api/utilities/sort-projects.md +++ b/packages/docs/src/api/utilities/sort-projects.md @@ -45,7 +45,7 @@ type SortValue = 'stars' | 'stars-asc' | 'name' | 'name-desc' | 'date' | 'date-a ```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') @@ -83,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('')