auto: PR for feat/export-options-ui #36

Merged
jaybe merged 1 commits from feat/export-options-ui into main 2025-11-14 22:44:50 +00:00
2 changed files with 78 additions and 4 deletions
+14 -4
View File
@@ -223,7 +223,13 @@ export default function BuilderClientPage() {
if (manifestShortName && manifestShortName.trim().length > 0) manifestOverrideBase.short_name = manifestShortName.trim() if (manifestShortName && manifestShortName.trim().length > 0) manifestOverrideBase.short_name = manifestShortName.trim()
if (manifestStartUrl && manifestStartUrl.trim().length > 0) manifestOverrideBase.start_url = manifestStartUrl.trim() if (manifestStartUrl && manifestStartUrl.trim().length > 0) manifestOverrideBase.start_url = manifestStartUrl.trim()
if (manifestDisplay && manifestDisplay.trim().length > 0) manifestOverrideBase.display = manifestDisplay.trim() if (manifestDisplay && manifestDisplay.trim().length > 0) manifestOverrideBase.display = manifestDisplay.trim()
if (manifestThemeColor && manifestThemeColor.trim().length > 0) manifestOverrideBase.theme_color = manifestThemeColor.trim() if (manifestThemeColor && manifestThemeColor.trim().length > 0) {
const v = manifestThemeColor.trim()
const hexOk = /^#([0-9a-fA-F]{3}){1,2}$/.test(v)
if (hexOk) {
manifestOverrideBase.theme_color = v
}
}
const manifestOverride = Object.keys(manifestOverrideBase).length > 0 ? (manifestOverrideBase as { name?: string; short_name?: string; start_url?: string; display?: string; theme_color?: string }) : undefined const manifestOverride = Object.keys(manifestOverrideBase).length > 0 ? (manifestOverrideBase as { name?: string; short_name?: string; start_url?: string; display?: string; theme_color?: string }) : undefined
const robotsTextOverride = (() => { const robotsTextOverride = (() => {
const lines = (robotsDisallow || '').split(/\n+/).map((s) => s.trim()).filter((s) => s.length > 0) const lines = (robotsDisallow || '').split(/\n+/).map((s) => s.trim()).filter((s) => s.length > 0)
@@ -334,14 +340,18 @@ export default function BuilderClientPage() {
</label> </label>
<label className="block" htmlFor="ins-manifest-display"> <label className="block" htmlFor="ins-manifest-display">
<span className="sr-only">Manifest Display</span> <span className="sr-only">Manifest Display</span>
<input <select
id="ins-manifest-display" id="ins-manifest-display"
aria-label="Manifest Display" aria-label="Manifest Display"
className="border rounded px-3 py-2 w-full" className="border rounded px-3 py-2 w-full"
value={manifestDisplay} value={manifestDisplay}
onChange={(e) => setManifestDisplay(e.target.value)} onChange={(e) => setManifestDisplay(e.target.value)}
placeholder="standalone" >
/> <option value="">(default)</option>
<option value="standalone">standalone</option>
<option value="minimal-ui">minimal-ui</option>
<option value="browser">browser</option>
</select>
</label> </label>
<label className="block" htmlFor="ins-manifest-theme-color"> <label className="block" htmlFor="ins-manifest-theme-color">
<span className="sr-only">Manifest Theme Color</span> <span className="sr-only">Manifest Theme Color</span>
@@ -0,0 +1,64 @@
import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import BuilderClientPage from '@/components/BuilderClientPage'
vi.mock('next-intl', () => {
return {
useTranslations: () => (key: string) => key,
}
})
const buildZipSpy = vi.fn(async (_arg: {
html: string
css: string
js: string
assets: Record<string, Uint8Array>
extras?: Record<string, Uint8Array>
}) => new Uint8Array([80, 75]))
vi.mock('@/lib/exporter/zip', () => {
return {
buildZip: (arg: {
html: string
css: string
js: string
assets: Record<string, Uint8Array>
extras?: Record<string, Uint8Array>
}) => buildZipSpy(arg),
}
})
describe('Export Options UI polish', () => {
it('uses a dropdown for Manifest Display and passes selected value', async () => {
render(<BuilderClientPage />)
// choose display = minimal-ui
const displaySelect = await screen.findByLabelText('Manifest Display')
fireEvent.change(displaySelect, { target: { value: 'minimal-ui' } })
const exportBtn = await screen.findByRole('button', { name: 'builder.action.export' })
fireEvent.click(exportBtn)
const first = buildZipSpy.mock.calls[0]
const extras: Record<string, Uint8Array> = (first![0] as { extras: Record<string, Uint8Array> }).extras
const td = new TextDecoder()
const manifest = JSON.parse(td.decode(extras['site.webmanifest']))
expect(manifest.display).toBe('minimal-ui')
})
it('ignores invalid theme_color override and keeps default theme when not a hex', async () => {
render(<BuilderClientPage />)
const colorInput = await screen.findByLabelText('Manifest Theme Color')
fireEvent.change(colorInput, { target: { value: 'blue' } }) // invalid hex
const exportBtn = await screen.findByRole('button', { name: 'builder.action.export' })
fireEvent.click(exportBtn)
const first = buildZipSpy.mock.calls[0]
const extras: Record<string, Uint8Array> = (first![0] as { extras: Record<string, Uint8Array> }).extras
const td = new TextDecoder()
const manifest = JSON.parse(td.decode(extras['site.webmanifest']))
expect(manifest.theme_color).toBe('#0ea5e9') // default from page.theme.primaryColor
})
})