Skip to content

Commit a8654f0

Browse files
committed
Updated to react name as the end point.
1 parent 0d52a5a commit a8654f0

File tree

5 files changed

+45
-70
lines changed

5 files changed

+45
-70
lines changed

src/__tests__/pages/api/__tests__/[version]/icons/[iconName].test.ts

Lines changed: 27 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,21 @@ const mockIconSvgs: Record<string, Record<string, string>> = {
1313
fa: { FaCircle: mockSvg },
1414
}
1515

16+
const mockIconsIndex = {
17+
icons: [
18+
{ name: 'circle', reactName: 'FaCircle', style: 'solid', usage: '', unicode: '', set: 'fa' },
19+
],
20+
}
21+
1622
function createFetchMock(): typeof fetch {
1723
return jest.fn((input: RequestInfo | URL) => {
1824
const url = typeof input === 'string' ? input : input.toString()
25+
if (url.includes('/iconsIndex.json')) {
26+
return Promise.resolve({
27+
ok: true,
28+
json: () => Promise.resolve(mockIconsIndex),
29+
} as Response)
30+
}
1931
const match = url.match(/\/iconsSvgs\/([^/]+)\.json/)
2032
if (match) {
2133
const setId = match[1]
@@ -32,27 +44,12 @@ function createFetchMock(): typeof fetch {
3244
}) as typeof fetch
3345
}
3446

35-
jest.mock('../../../../../../utils/icons/reactIcons', () => ({
36-
parseIconId: jest.fn((iconId: string) => {
37-
const underscoreIndex = iconId.indexOf('_')
38-
if (underscoreIndex <= 0) {
39-
return null
40-
}
41-
const setId = iconId.slice(0, underscoreIndex)
42-
const iconName = iconId.slice(underscoreIndex + 1)
43-
if (!setId || !iconName) {
44-
return null
45-
}
46-
return { setId, iconName }
47-
}),
48-
}))
49-
5047
it('returns SVG markup for valid icon', async () => {
5148
global.fetch = createFetchMock()
5249

5350
const response = await GET({
54-
params: { version: 'v6', iconName: 'fa_FaCircle' },
55-
url: new URL('http://localhost:4321/api/v6/icons/fa_FaCircle'),
51+
params: { version: 'v6', iconName: 'FaCircle' },
52+
url: new URL('http://localhost:4321/api/v6/icons/FaCircle'),
5653
} as any)
5754
const body = await response.text()
5855

@@ -66,25 +63,24 @@ it('returns SVG markup for valid icon', async () => {
6663
jest.restoreAllMocks()
6764
})
6865

69-
it('returns 404 when icon is not found in set', async () => {
66+
it('returns 404 when icon is not found', async () => {
7067
global.fetch = createFetchMock()
7168

7269
const response = await GET({
73-
params: { version: 'v6', iconName: 'fa_FaNonExistent' },
74-
url: new URL('http://localhost:4321/api/v6/icons/fa_FaNonExistent'),
70+
params: { version: 'v6', iconName: 'FaNonExistent' },
71+
url: new URL('http://localhost:4321/api/v6/icons/FaNonExistent'),
7572
} as any)
7673
const body = await response.json()
7774

7875
expect(response.status).toBe(404)
7976
expect(body).toHaveProperty('error')
8077
expect(body.error).toContain('FaNonExistent')
81-
expect(body.error).toContain('fa')
8278
expect(body.error).toContain('not found')
8379

8480
jest.restoreAllMocks()
8581
})
8682

87-
it('returns 400 for invalid icon name format (no underscore)', async () => {
83+
it('returns 404 when icon name is not in index', async () => {
8884
global.fetch = createFetchMock()
8985

9086
const response = await GET({
@@ -93,27 +89,10 @@ it('returns 400 for invalid icon name format (no underscore)', async () => {
9389
} as any)
9490
const body = await response.json()
9591

96-
expect(response.status).toBe(400)
97-
expect(body).toHaveProperty('error')
98-
expect(body.error).toBe('Invalid icon name format')
99-
expect(body).toHaveProperty('expected')
100-
expect(body.expected).toContain('fa_FaCircle')
101-
102-
jest.restoreAllMocks()
103-
})
104-
105-
it('returns 400 for invalid icon name format (leading underscore)', async () => {
106-
global.fetch = createFetchMock()
107-
108-
const response = await GET({
109-
params: { version: 'v6', iconName: '_FaCircle' },
110-
url: new URL('http://localhost:4321/api/v6/icons/_FaCircle'),
111-
} as any)
112-
const body = await response.json()
113-
114-
expect(response.status).toBe(400)
92+
expect(response.status).toBe(404)
11593
expect(body).toHaveProperty('error')
116-
expect(body.error).toBe('Invalid icon name format')
94+
expect(body.error).toContain('invalid')
95+
expect(body.error).toContain('not found')
11796

11897
jest.restoreAllMocks()
11998
})
@@ -138,8 +117,8 @@ it('returns 404 for nonexistent version', async () => {
138117
global.fetch = createFetchMock()
139118

140119
const response = await GET({
141-
params: { version: 'v99', iconName: 'fa_FaCircle' },
142-
url: new URL('http://localhost:4321/api/v99/icons/fa_FaCircle'),
120+
params: { version: 'v99', iconName: 'FaCircle' },
121+
url: new URL('http://localhost:4321/api/v99/icons/FaCircle'),
143122
} as any)
144123
const body = await response.json()
145124

@@ -155,8 +134,8 @@ it('returns 400 when version parameter is missing', async () => {
155134
global.fetch = createFetchMock()
156135

157136
const response = await GET({
158-
params: { iconName: 'fa_FaCircle' },
159-
url: new URL('http://localhost:4321/api/icons/fa_FaCircle'),
137+
params: { iconName: 'FaCircle' },
138+
url: new URL('http://localhost:4321/api/icons/FaCircle'),
160139
} as any)
161140
const body = await response.json()
162141

@@ -184,8 +163,8 @@ it('returns 500 when fetchApiIndex fails', async () => {
184163
}) as typeof fetch
185164

186165
const response = await GET({
187-
params: { version: 'v6', iconName: 'fa_FaCircle' },
188-
url: new URL('http://localhost:4321/api/v6/icons/fa_FaCircle'),
166+
params: { version: 'v6', iconName: 'FaCircle' },
167+
url: new URL('http://localhost:4321/api/v6/icons/FaCircle'),
189168
} as any)
190169
const body = await response.json()
191170

src/pages/api/[version]/icons/[iconName].ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ import {
44
createSvgResponse,
55
} from '../../../../utils/apiHelpers'
66
import { fetchApiIndex } from '../../../../utils/apiIndex/fetch'
7-
import { fetchIconSvgs } from '../../../../utils/icons/fetch'
8-
import { parseIconId } from '../../../../utils/icons/reactIcons'
7+
import { fetchIconSvgs, fetchIconsIndex } from '../../../../utils/icons/fetch'
98

109
export const prerender = false
1110

1211
/**
1312
* GET /api/{version}/icons/[icon-name]
1413
* Returns actual SVG markup for the icon.
15-
* Icon name format: {set}_{iconName} (e.g., fa_FaCircle, md_MdHome)
14+
* Icon name: React component name (e.g., FaCircle, MdHome)
1615
*/
1716
export const GET: APIRoute = async ({ params, url }) => {
18-
const { version, iconName: iconId } = params
17+
const { version, iconName: reactName } = params
1918

2019
if (!version) {
2120
return createJsonResponse(
@@ -33,31 +32,28 @@ export const GET: APIRoute = async ({ params, url }) => {
3332
return createJsonResponse({ error: 'Failed to fetch API index' }, 500)
3433
}
3534

36-
if (!iconId) {
35+
if (!reactName) {
3736
return createJsonResponse(
3837
{ error: 'Icon name parameter is required' },
3938
400,
4039
)
4140
}
4241

43-
const parsed = parseIconId(iconId)
44-
if (!parsed) {
42+
const icons = await fetchIconsIndex(url)
43+
const icon = icons.find((i) => i.reactName === reactName)
44+
if (!icon?.set) {
4545
return createJsonResponse(
46-
{
47-
error: 'Invalid icon name format',
48-
expected: 'Use format {set}_{iconName} (e.g., fa_FaCircle, md_MdHome)',
49-
},
50-
400,
46+
{ error: `Icon '${reactName}' not found` },
47+
404,
5148
)
5249
}
5350

54-
const { setId, iconName } = parsed
55-
const svgs = await fetchIconSvgs(url, setId)
56-
const svg = svgs?.[iconName] ?? null
51+
const svgs = await fetchIconSvgs(url, icon.set)
52+
const svg = svgs?.[reactName] ?? null
5753

5854
if (!svg) {
5955
return createJsonResponse(
60-
{ error: `Icon '${iconName}' not found in set '${setId}'` },
56+
{ error: `Icon '${reactName}' not found in set '${icon.set}'` },
6157
404,
6258
)
6359
}

src/pages/api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ export const GET: APIRoute = async () =>
423423
in: 'path',
424424
required: true,
425425
type: 'string',
426-
description: 'Icon identifier in format {set}_{iconName} (e.g., fa_FaCircle, md_MdHome)',
427-
example: 'fa_FaCircle',
426+
description: 'Icon identifier: React component name (e.g., FaCircle, MdHome)',
427+
example: 'FaCircle',
428428
},
429429
],
430430
returns: {

src/pages/api/openapi.json.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export const GET: APIRoute = async ({ url }) => {
165165
get: {
166166
summary: 'Get icon SVG markup',
167167
description:
168-
'Returns actual SVG markup for the icon. Icon name format: {set}_{iconName} (e.g., fa_FaCircle, md_MdHome)',
168+
'Returns actual SVG markup for the icon. Icon name: React component name (e.g., FaCircle, MdHome)',
169169
operationId: 'getIconSvg',
170170
parameters: [
171171
{
@@ -180,8 +180,8 @@ export const GET: APIRoute = async ({ url }) => {
180180
in: 'path',
181181
required: true,
182182
schema: { type: 'string' },
183-
description: 'Icon identifier: {set}_{iconName}',
184-
example: 'fa_FaCircle',
183+
description: 'Icon identifier: React component name',
184+
example: 'FaCircle',
185185
},
186186
],
187187
responses: {

src/utils/icons/reactIcons.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface IconMetadata {
1212
style: string
1313
usage: string
1414
unicode: string
15-
/** Set id for SVG URL: /api/icons/{set}_{reactName} */
15+
/** Set id for SVG lookup (react name used in URL: /api/icons/{reactName}) */
1616
set?: string
1717
}
1818

0 commit comments

Comments
 (0)