1.2.6: dedicated FontPicker component with in-line type previews
Replaces the bare <select> with a custom combobox that fits the properties panel's visual language: - Trigger looks like the other inputs (var(--bg)/var(--border)/ var(--radius-sm)) and shows the selected font's name rendered in that font itself, with a rotating caret. - Options live in a teleported popover anchored beneath the trigger. Each option renders its own label in its own typeface so the user sees what they're picking before applying it. Active row uses var(--accent-soft) to match the rest of the editor. - Click-outside closes via window pointerdown; Escape closes and returns focus to the trigger. - Storage shape simplified: textFontModel uses '' for system-sans instead of the literal 'system-sans' id, so the model field round- trips through JSON without a placeholder string ever leaking. Updated PropertiesPanel test to walk the new picker (click trigger, read options from .font-popover, click an option) instead of dispatching a select 'change' event. Picker label text doubles as the test selector — System Sans / Inter / Permanent Marker confirm the option list is wired up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4c2b7c7c35
commit
8c4f744afc
5 files changed
+258
-23
No files matched your search
+25
-15
@@ -15032,7 +15032,7 @@ export function registerAllTests(): void {
|
||||
expect(fontCssFamily('does-not-exist')).toBe(SYSTEM_SANS.family)
|
||||
})
|
||||
|
||||
test('PropertiesPanel renders a font dropdown for text assets and writes back', async () => {
|
||||
test('PropertiesPanel renders the font picker for text assets and writes back', async () => {
|
||||
const s = makeFreshStore()
|
||||
const a = s.addAsset({
|
||||
kind: 'text', name: 'T', text: 'hello', fontSize: 32, fill: '#000',
|
||||
@@ -15043,30 +15043,40 @@ export function registerAllTests(): void {
|
||||
const handle = mountComponent(PropertiesPanel, {})
|
||||
try {
|
||||
await nextTick()
|
||||
const select = handle.host.querySelector('select') as HTMLSelectElement | null
|
||||
expect(select).toBeTruthy()
|
||||
const options = Array.from(select!.options).map(o => o.value)
|
||||
expect(options).toContain('system-sans')
|
||||
expect(options).toContain('inter')
|
||||
expect(options).toContain('marker')
|
||||
// Default selection: system-sans (asset.fontFamily is undefined).
|
||||
expect(select!.value).toBe('system-sans')
|
||||
// Pick Inter — the asset should mutate.
|
||||
select!.value = 'inter'
|
||||
select!.dispatchEvent(new Event('change'))
|
||||
const trigger = handle.host.querySelector('.font-picker .trigger') as HTMLButtonElement | null
|
||||
expect(!!trigger).toBe(true)
|
||||
// Closed state: the label shows the default — System Sans.
|
||||
expect((trigger!.textContent ?? '').includes('System Sans')).toBe(true)
|
||||
trigger!.click()
|
||||
await nextTick()
|
||||
// Popover lives in document.body via Teleport.
|
||||
const opts = Array.from(document.querySelectorAll('.font-popover .font-option')) as HTMLButtonElement[]
|
||||
expect(opts.length).toBeGreaterThan(5)
|
||||
const labels = opts.map(o => o.textContent?.trim() ?? '')
|
||||
expect(labels.some(l => l.includes('System Sans'))).toBe(true)
|
||||
expect(labels.some(l => l.includes('Inter'))).toBe(true)
|
||||
expect(labels.some(l => l.includes('Permanent Marker'))).toBe(true)
|
||||
// Click Inter.
|
||||
const interOpt = opts.find(o => (o.textContent ?? '').trim() === 'Inter')!
|
||||
interOpt.click()
|
||||
await nextTick()
|
||||
const textAsset = s.scene.assets.find(x => x.id === a.id)!
|
||||
if (textAsset.kind !== 'text') throw new Error('unexpected kind')
|
||||
expect(textAsset.fontFamily).toBe('inter')
|
||||
// Picking system-sans clears the field (undefined, not the literal id).
|
||||
select!.value = 'system-sans'
|
||||
select!.dispatchEvent(new Event('change'))
|
||||
// Re-open + click System Sans → clears (undefined).
|
||||
trigger!.click()
|
||||
await nextTick()
|
||||
const opts2 = Array.from(document.querySelectorAll('.font-popover .font-option')) as HTMLButtonElement[]
|
||||
const sysOpt = opts2.find(o => (o.textContent ?? '').trim() === 'System Sans')!
|
||||
sysOpt.click()
|
||||
await nextTick()
|
||||
const textAsset2 = s.scene.assets.find(x => x.id === a.id)!
|
||||
if (textAsset2.kind !== 'text') throw new Error('unexpected kind')
|
||||
expect(textAsset2.fontFamily === undefined).toBe(true)
|
||||
} finally {
|
||||
handle.unmount()
|
||||
// Make sure the teleported popover doesn't leak into other tests.
|
||||
document.querySelectorAll('.font-popover').forEach(el => el.remove())
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user