Restore icon property in inspector

This commit is contained in:
lucaronin
2026-04-17 15:33:17 +02:00
parent 6202892512
commit ce78abfd57
6 changed files with 51 additions and 15 deletions

View File

@@ -67,7 +67,7 @@ describe('DynamicPropertiesPanel system metadata', () => {
vi.clearAllMocks()
})
it('hides underscored and legacy system metadata while keeping user properties visible', () => {
it('keeps the icon visible while hiding the other system metadata', () => {
render(
<DynamicPropertiesPanel
entry={makeEntry()}
@@ -86,7 +86,9 @@ describe('DynamicPropertiesPanel system metadata', () => {
expect(screen.getByText('Owner')).toBeInTheDocument()
expect(screen.getByText('Luca')).toBeInTheDocument()
expect(screen.queryByText('Icon')).not.toBeInTheDocument()
expect(screen.getByText('Icon')).toBeInTheDocument()
expect(screen.getByTestId('icon-editable-display')).toHaveTextContent('rocket')
expect(screen.queryByDisplayValue('legacy')).not.toBeInTheDocument()
expect(screen.queryByText('Order')).not.toBeInTheDocument()
expect(screen.queryByText('Sort')).not.toBeInTheDocument()
expect(screen.queryByText('Sidebar label')).not.toBeInTheDocument()
@@ -130,4 +132,18 @@ describe('DynamicPropertiesPanel system metadata', () => {
expect(onAddProperty).toHaveBeenCalledWith('_icon', 'rocket')
})
it('renders an existing underscored icon property with the icon picker UI', () => {
render(
<DynamicPropertiesPanel
entry={makeEntry()}
content=""
frontmatter={{ _icon: 'megaphone' }}
onAddProperty={onAddProperty}
/>,
)
expect(screen.getByText('Icon')).toBeInTheDocument()
expect(screen.getByTestId('icon-editable-display')).toHaveTextContent('megaphone')
})
})

View File

@@ -697,21 +697,20 @@ describe('DynamicPropertiesPanel', () => {
})
describe('system property filtering', () => {
it('hides archived, archived_at, and legacy icon metadata from the properties panel', () => {
it('hides archived metadata but keeps the note icon visible in the properties panel', () => {
renderEditablePanel({ archived: false, archived_at: '', icon: '📝', cadence: 'Weekly' })
expect(screen.queryByText('Archived')).not.toBeInTheDocument()
expect(screen.queryByText('Archived at')).not.toBeInTheDocument()
expect(screen.queryByText('Icon')).not.toBeInTheDocument()
expect(screen.queryByText('📝')).not.toBeInTheDocument()
// Custom property still visible
expect(screen.getByText('Icon')).toBeInTheDocument()
expect(screen.getByText('📝')).toBeInTheDocument()
expect(screen.getByText('Cadence')).toBeInTheDocument()
})
it('hides legacy icon metadata even when cased differently', () => {
it('keeps the note icon visible even when cased differently', () => {
renderEditablePanel({ Archived: false, Icon: '🎯', cadence: 'Daily' })
expect(screen.queryByText('Archived')).not.toBeInTheDocument()
expect(screen.queryByText('Icon')).not.toBeInTheDocument()
expect(screen.queryByText('🎯')).not.toBeInTheDocument()
expect(screen.getByText('Icon')).toBeInTheDocument()
expect(screen.getByText('🎯')).toBeInTheDocument()
expect(screen.getByText('Cadence')).toBeInTheDocument()
})

View File

@@ -22,6 +22,7 @@ import { getTagStyle } from '../utils/tagStyles'
import { ColorEditableValue } from './ColorInput'
import { IconEditableValue } from './IconEditableValue'
import { PROPERTY_CHIP_STYLE } from './propertyChipStyles'
import { canonicalSystemMetadataKey } from '../utils/systemMetadata'
function parseDateValue(value: string): Date | undefined {
const iso = toISODate(value)
@@ -307,7 +308,7 @@ function toBooleanValue(value: FrontmatterValue): boolean {
}
function autoDetectFromValue(propKey: string, value: FrontmatterValue): PropertyDisplayMode {
if (propKey.toLowerCase() === 'icon') return 'text'
if (canonicalSystemMetadataKey(propKey) === '_icon') return 'text'
if (typeof value === 'boolean') return 'boolean'
if (typeof value === 'string' && isUrlValue(value)) return 'url'
if (typeof value === 'string' && isValidCssColor(value) && value.startsWith('#')) return 'color'
@@ -405,7 +406,7 @@ function ScalarValueCell(props: SmartCellProps) {
onSave,
})
if (propKey.toLowerCase() === 'icon') {
if (canonicalSystemMetadataKey(propKey) === '_icon') {
return <IconEditableValue {...editProps} />
}

View File

@@ -9,7 +9,7 @@ import {
removeDisplayModeOverride,
} from '../utils/propertyTypes'
import { containsWikilinks } from '../components/DynamicPropertiesPanel'
import { isSystemMetadataKey } from '../utils/systemMetadata'
import { canonicalSystemMetadataKey, isSystemMetadataKey } from '../utils/systemMetadata'
// Keys to skip showing in Properties (handled by dedicated UI or internal)
// Compared case-insensitively via isVisibleProperty()
@@ -79,6 +79,22 @@ function isVisibleProperty([key, value]: [string, FrontmatterValue]): boolean {
return !isHiddenPropertyKey(key) && !containsWikilinks(value)
}
function buildVisiblePropertyEntries(frontmatter: ParsedFrontmatter): [string, FrontmatterValue][] {
const result: [string, FrontmatterValue][] = []
const seen = new Set<string>()
for (const [key, value] of Object.entries(frontmatter)) {
if (!isVisibleProperty([key, value])) continue
const canonicalKey = canonicalSystemMetadataKey(key)
if (seen.has(canonicalKey)) continue
seen.add(canonicalKey)
result.push([key, value])
}
return result
}
function addTagValues(tagsByKey: Map<string, Set<string>>, key: string, value: unknown) {
if (!Array.isArray(value)) return
@@ -102,6 +118,8 @@ function toSortedTagRecord(tagsByKey: Map<string, Set<string>>): Record<string,
}
function isHiddenPropertyKey(key: string): boolean {
const canonicalKey = canonicalSystemMetadataKey(key)
if (canonicalKey === '_icon') return false
return SKIP_KEYS.has(key.toLowerCase()) || isSystemMetadataKey(key)
}
@@ -137,7 +155,7 @@ export function usePropertyPanelState(deps: PropertyPanelDeps) {
const { availableTypes, customColorKey, typeColorKeys, typeIconKeys } = useMemo(() => deriveTypeInfo(entries, entryIsA), [entries, entryIsA])
const vaultStatuses = useMemo(() => collectVaultStatuses(entries), [entries])
const vaultTagsByKey = useMemo(() => collectAllVaultTags(entries), [entries])
const propertyEntries = useMemo(() => Object.entries(frontmatter).filter(isVisibleProperty), [frontmatter])
const propertyEntries = useMemo(() => buildVisiblePropertyEntries(frontmatter), [frontmatter])
const handleSaveValue = useCallback((key: string, newValue: string) => {
setEditingKey(null)

View File

@@ -1,5 +1,6 @@
export function humanizePropertyKey(key: string): string {
const spaced = key.replace(/[_-]/g, ' ')
const normalized = key.replace(/^_+/, '')
const spaced = normalized.replace(/[_-]/g, ' ')
if (!spaced) return spaced
return spaced.charAt(0).toUpperCase() + spaced.slice(1)
}

View File

@@ -3,6 +3,7 @@ import { getAppStorageItem } from '../constants/appStorage'
import { isValidCssColor, isColorKeyName } from './colorUtils'
import { updateVaultConfigField } from './vaultConfigStore'
import { CalendarIcon, Type, ToggleLeft, Circle, Link, Tag, Palette } from 'lucide-react'
import { canonicalSystemMetadataKey } from './systemMetadata'
export type PropertyDisplayMode = 'text' | 'date' | 'boolean' | 'status' | 'url' | 'tags' | 'color'
@@ -20,7 +21,7 @@ const DATE_KEY_PATTERNS = ['date', 'deadline', 'due', 'start', 'end', 'scheduled
const TAGS_KEY_PATTERNS = ['tags', 'keywords', 'categories', 'labels']
function isIconKey(key: string): boolean {
return key.toLowerCase() === 'icon'
return canonicalSystemMetadataKey(key) === '_icon'
}
function keyMatchesPatterns(key: string, patterns: string[]): boolean {