feat(polaris): 新增三个导航网站并更新游戏资料图标
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
# 新增导航与游戏资料分类图标
|
||||
|
||||
## 范围与决定
|
||||
|
||||
- 用户已明确要求执行;从干净的 `main` 新建 `feat/polaris-navigation-additions` 分支,不提交代码。
|
||||
- 新增 OpenRouter(AI 工具)、菜鸟教程(学习资料)、Lucide(设计资源),保持原有条目不变。
|
||||
- 新条目沿用现有数据结构,填写简短描述、关键词和分类内排序,不置顶;图标沿用既有 favicon 与首字母兜底,不新增依赖或资源。
|
||||
- 仅将游戏资料分类的 `icon` 改为 `book-search`,前端映射增加 Lucide 的 `BookSearch`,保留学习资料的 `BookOpen`。
|
||||
- 不调整布局、样式或其他分类图标。
|
||||
|
||||
## 执行顺序与责任
|
||||
|
||||
1. 协调者创建分支并记录计划(已完成)。
|
||||
2. fixer 负责 `src/data/links.json`、`src/data/categories.json`、`src/features/navigation/components/category-icon.tsx` 及必要的局部回归测试;编辑前确认编码、缩进与换行,阅读适用的 Next.js 本地文档。
|
||||
3. fixer 运行数据与图标相关测试、类型检查、修改文件的 lint 和格式检查,报告证据;协调者复核最终差异和关键行为。
|
||||
|
||||
## 验证路径与预算
|
||||
|
||||
- 新增导航:断言三个 ID/URL 唯一、分类正确、字段满足现有契约,现有条目不变。
|
||||
- 分类图标:断言游戏资料为 `book-search` 且渲染实际 `BookSearch`,学习资料仍为 `book-open`;类型检查确认当前 lucide-react 导出可用。
|
||||
- 工程检查:优先运行现有测试套件(若成本较低则全部)、`pnpm typecheck`、修改文件的 ESLint 与 Prettier 检查、`git diff --check`。
|
||||
- 不以外部站点的 favicon 网络可达性作为本次完成门槛;其失败沿用已有兜底机制。
|
||||
- 无框架/API/布局变更,不重复执行完整生产构建或截图验证,除非检查暴露相关风险。
|
||||
|
||||
## 状态
|
||||
|
||||
- 实施完成,未提交代码。
|
||||
- fixer 验证:全量测试 17 个文件、117 个用例通过;类型检查、修改文件 ESLint 与 Prettier 检查通过。环境使用 `corepack pnpm` 执行项目命令。
|
||||
- 协调者复核:原有 51 条链接与 HEAD 完全一致,恰好新增 3 条;分类数据除游戏资料的 `icon` 外与 HEAD 完全一致;图标组件测试覆盖 `BookSearch`、`BookOpen` 与默认兜底;`git diff --check` 通过。
|
||||
- 外部 favicon 可达性未测试,不影响本次数据及图标映射变更结论。
|
||||
@@ -45,7 +45,7 @@
|
||||
"id": "game-resources",
|
||||
"title": "游戏资料",
|
||||
"description": "游戏攻略维基、资料速查与实用规划工具。",
|
||||
"icon": "book-open",
|
||||
"icon": "book-search",
|
||||
"sort": 65
|
||||
},
|
||||
{
|
||||
|
||||
@@ -30,9 +30,9 @@ describe("Polaris 静态数据", () => {
|
||||
}
|
||||
})
|
||||
|
||||
it("包含 51 条合法链接且分类引用均存在", () => {
|
||||
it("包含 54 条合法链接且分类引用均存在", () => {
|
||||
const categoryIds = new Set(categories.map(({ id }) => id))
|
||||
expect(links).toHaveLength(51)
|
||||
expect(links).toHaveLength(54)
|
||||
expect(hasUniqueValues(links.map(({ id }) => id))).toBe(true)
|
||||
|
||||
for (const link of links) {
|
||||
@@ -225,8 +225,8 @@ describe("Polaris 静态数据", () => {
|
||||
},
|
||||
] as const
|
||||
|
||||
it("九条新链接全部存在且总数为 51", () => {
|
||||
expect(links).toHaveLength(51)
|
||||
it("九条新链接全部存在且总数为 54", () => {
|
||||
expect(links).toHaveLength(54)
|
||||
expect(
|
||||
expectedNewLinks.every(({ id }) =>
|
||||
links.some((link) => link.id === id),
|
||||
@@ -248,4 +248,48 @@ describe("Polaris 静态数据", () => {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
describe("新增三条导航链接回归", () => {
|
||||
const expectedNewLinks = [
|
||||
{
|
||||
id: "openrouter",
|
||||
title: "OpenRouter",
|
||||
url: "https://openrouter.ai",
|
||||
categoryId: "ai",
|
||||
},
|
||||
{
|
||||
id: "runoob",
|
||||
title: "菜鸟教程",
|
||||
url: "https://www.runoob.com/",
|
||||
categoryId: "learning",
|
||||
},
|
||||
{
|
||||
id: "lucide",
|
||||
title: "Lucide",
|
||||
url: "https://lucide.dev/",
|
||||
categoryId: "design",
|
||||
},
|
||||
] as const
|
||||
|
||||
it("三条新链接字段与分类精确匹配且沿用 favicon 兜底", () => {
|
||||
for (const expected of expectedNewLinks) {
|
||||
const link = links.find(({ id }) => id === expected.id)
|
||||
|
||||
expect(link).toBeDefined()
|
||||
expect(link).toMatchObject({
|
||||
id: expected.id,
|
||||
title: expected.title,
|
||||
url: expected.url,
|
||||
categoryId: expected.categoryId,
|
||||
pinned: false,
|
||||
})
|
||||
expect(link?.icon).toBeUndefined()
|
||||
}
|
||||
})
|
||||
|
||||
it("三条新链接 ID 与 URL 唯一", () => {
|
||||
expect(hasUniqueValues(expectedNewLinks.map(({ id }) => id))).toBe(true)
|
||||
expect(hasUniqueValues(expectedNewLinks.map(({ url }) => url))).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -534,5 +534,35 @@
|
||||
"pinned": false,
|
||||
"keywords": ["阿里云", "控制台", "云服务", "管理"],
|
||||
"sort": 100
|
||||
},
|
||||
{
|
||||
"id": "openrouter",
|
||||
"title": "OpenRouter",
|
||||
"description": "统一大模型 API 聚合平台",
|
||||
"url": "https://openrouter.ai",
|
||||
"categoryId": "ai",
|
||||
"pinned": false,
|
||||
"keywords": ["ai", "大模型", "api", "聚合"],
|
||||
"sort": 30
|
||||
},
|
||||
{
|
||||
"id": "runoob",
|
||||
"title": "菜鸟教程",
|
||||
"description": "编程入门教程与在线学习网站",
|
||||
"url": "https://www.runoob.com/",
|
||||
"categoryId": "learning",
|
||||
"pinned": false,
|
||||
"keywords": ["教程", "编程", "学习", "入门"],
|
||||
"sort": 20
|
||||
},
|
||||
{
|
||||
"id": "lucide",
|
||||
"title": "Lucide",
|
||||
"description": "开源 SVG 图标库",
|
||||
"url": "https://lucide.dev/",
|
||||
"categoryId": "design",
|
||||
"pinned": false,
|
||||
"keywords": ["图标", "svg", "图标库", "开源"],
|
||||
"sort": 50
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { render } from "@testing-library/react"
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { CategoryIcon } from "./category-icon"
|
||||
|
||||
describe("CategoryIcon", () => {
|
||||
it("为 book-search 渲染实际的 BookSearch 图标", () => {
|
||||
const { container } = render(<CategoryIcon name="book-search" />)
|
||||
const svg = container.querySelector("svg")
|
||||
|
||||
expect(svg).not.toBeNull()
|
||||
expect(svg).toHaveClass("lucide-book-search")
|
||||
})
|
||||
|
||||
it("book-open 仍渲染 BookOpen 而非 BookSearch", () => {
|
||||
const { container } = render(<CategoryIcon name="book-open" />)
|
||||
const svg = container.querySelector("svg")
|
||||
|
||||
expect(svg).not.toBeNull()
|
||||
expect(svg).toHaveClass("lucide-book-open")
|
||||
expect(svg).not.toHaveClass("lucide-book-search")
|
||||
})
|
||||
|
||||
it("未知图标回退到 Sparkles", () => {
|
||||
const { container } = render(<CategoryIcon name="not-exist" />)
|
||||
|
||||
expect(container.querySelector("svg")).toHaveClass("lucide-sparkles")
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
BookOpen,
|
||||
BookSearch,
|
||||
CirclePlay,
|
||||
Code2,
|
||||
Coffee,
|
||||
@@ -14,6 +15,7 @@ const CATEGORY_ICONS: Readonly<Record<string, LucideIcon>> = {
|
||||
code: Code2,
|
||||
palette: Palette,
|
||||
"book-open": BookOpen,
|
||||
"book-search": BookSearch,
|
||||
zap: Zap,
|
||||
"circle-play": CirclePlay,
|
||||
coffee: Coffee,
|
||||
|
||||
@@ -57,6 +57,7 @@ describe("静态数据 service", () => {
|
||||
"haowallpaper",
|
||||
"iodraw",
|
||||
"pixiv",
|
||||
"lucide",
|
||||
])
|
||||
expect(getLinksByCategory("life").map(({ id }) => id)).toEqual(["amap"])
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user