fix(ui): refine icon picker appearance

This commit is contained in:
lucaronin
2026-05-02 10:02:57 +02:00
parent ba41854f0e
commit 74c9841ad7
2 changed files with 49 additions and 7 deletions

View File

@@ -110,7 +110,7 @@ describe('TypeCustomizePopover', () => {
it('calls onChangeColor when a color is clicked', () => {
renderPopover()
const colorButtons = screen.getAllByTitle(/red|blue|green|purple|yellow|orange|teal|pink/i)
const colorButtons = screen.getAllByTitle(/red|blue|green|purple|yellow|orange|pink/i)
fireEvent.click(colorButtons[0])
expect(onChangeColor).toHaveBeenCalled()
@@ -123,6 +123,31 @@ describe('TypeCustomizePopover', () => {
expect(onChangeIcon).toHaveBeenCalledWith('wrench')
})
it('renders picker icons slightly larger than the sidebar icon size', () => {
renderPopover()
const icon = screen.getByTitle('wrench').querySelector('svg')
expect(icon).toHaveAttribute('width', '18')
expect(icon).toHaveAttribute('height', '18')
expect(icon).toHaveClass('size-[18px]')
})
it('orders color swatches by hue before neutral gray', () => {
renderPopover()
const colorRow = screen.getByTitle('Red').parentElement
expect(Array.from(colorRow?.children ?? []).map((element) => element.getAttribute('title'))).toEqual([
'Red',
'Orange',
'Yellow',
'Green',
'Blue',
'Purple',
'Pink',
'Gray',
])
})
it('calls onClose when Done is clicked', () => {
renderPopover()
@@ -130,10 +155,10 @@ describe('TypeCustomizePopover', () => {
expect(onClose).toHaveBeenCalled()
})
it('renders all color options including teal and pink', () => {
it('renders curated color options without the teal near-duplicate', () => {
renderPopover()
expect(screen.getByTitle('Teal')).toBeInTheDocument()
expect(screen.queryByTitle('Teal')).not.toBeInTheDocument()
expect(screen.getByTitle('Pink')).toBeInTheDocument()
})

View File

@@ -49,6 +49,20 @@ interface TemplateSectionProps {
onTemplateChange: (value: string) => void
}
const ICON_PICKER_ICON_SIZE = 18
const ICON_PICKER_ICON_CLASS_NAME = 'size-[18px]'
const COLOR_PICKER_ACCENT_COLORS = [
'red',
'orange',
'yellow',
'green',
'blue',
'purple',
'pink',
'gray',
].map((key) => ACCENT_COLORS.find((color) => color.key === key) ?? null)
.filter((color): color is typeof ACCENT_COLORS[number] => color !== null)
/** Debounce a callback by `delay` ms. Returns a stable ref-based wrapper. */
function useDebouncedCallback(fn: (v: string) => void, delay: number): (v: string) => void {
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
@@ -68,7 +82,7 @@ function ColorSection({ selectedColor, locale, onSelectColor }: ColorSectionProp
<>
<div className="font-mono-overline mb-2 text-muted-foreground">{translate(locale, 'customize.color')}</div>
<div className="flex gap-2 mb-3 flex-wrap">
{ACCENT_COLORS.map((color) => (
{COLOR_PICKER_ACCENT_COLORS.map((color) => (
<Button
key={color.key}
type="button"
@@ -118,7 +132,10 @@ function IconSection({
className="h-7 pl-7 pr-2 py-1 text-[12px]"
/>
</div>
<div className="flex flex-wrap gap-1 overflow-y-auto" style={{ maxHeight: 160 }}>
<div
className="grid gap-1 overflow-y-auto"
style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(30px, 1fr))', maxHeight: 160 }}
>
{filteredIcons.length === 0 ? (
<div className="w-full py-6 text-center text-[12px] text-muted-foreground">
{translate(locale, 'customize.noIconsFound')}
@@ -131,7 +148,7 @@ function IconSection({
variant="ghost"
size="icon-xs"
className={cn(
'h-[30px] w-[30px] rounded p-0 transition-colors',
'h-[30px] w-[30px] justify-self-center rounded p-0 transition-colors',
selectedIcon === name
? 'bg-primary/10 text-primary'
: 'text-muted-foreground hover:bg-accent hover:text-foreground',
@@ -140,7 +157,7 @@ function IconSection({
title={name}
aria-label={name}
>
<Icon size={16} />
<Icon size={ICON_PICKER_ICON_SIZE} className={ICON_PICKER_ICON_CLASS_NAME} />
</Button>
))
)}