feat(form): add select size variants

This commit is contained in:
Antti Kettunen 2026-05-18 20:41:12 +03:00
parent 56f642aadd
commit 934a612df3
No known key found for this signature in database
GPG key ID: C6B2A3D250359BD7
5 changed files with 59 additions and 4 deletions

View file

@ -73,6 +73,7 @@
.select {
width: auto;
min-width: 130px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
background:
@ -95,7 +96,6 @@
font: inherit;
font-size: 13px;
line-height: 1.5;
padding: 8px 32px 8px 12px;
transition:
border-color 0.18s ease,
background 0.18s ease,
@ -107,6 +107,24 @@
cursor: pointer;
}
.selectSizeSm {
min-height: 32px;
padding: 6px 30px 6px 10px;
background-position:
0 0,
0 0,
right 10px center;
}
.selectSizeMd {
min-height: 36px;
padding: 8px 32px 8px 12px;
background-position:
0 0,
0 0,
right 12px center;
}
.select:hover {
border-color: rgba(255, 255, 255, 0.16);
background:

View file

@ -138,6 +138,7 @@ describe('form primitives', () => {
expect(screen.getByText('Short summary')).toBeInTheDocument();
expect(screen.getByRole('alert')).toHaveTextContent('Validation failed');
expect(screen.getByLabelText('Status')).toHaveValue('open');
expect(screen.getByLabelText('Status')).toHaveAttribute('data-size', 'md');
fireEvent.change(screen.getByLabelText('Status'), { target: { value: 'resolved' } });
expect(screen.getByLabelText('Status')).toHaveValue('resolved');
@ -176,4 +177,15 @@ describe('form primitives', () => {
'primary',
);
});
it('supports compact select sizing', () => {
render(
<Select aria-label="Compact" defaultValue="one" size="sm">
<option value="one">One</option>
<option value="two">Two</option>
</Select>,
);
expect(screen.getByLabelText('Compact')).toHaveAttribute('data-size', 'sm');
});
});

View file

@ -77,13 +77,32 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function
return <textarea ref={ref} className={clsx(styles.textArea, className)} {...props} />;
});
export type SelectProps = SelectHTMLAttributes<HTMLSelectElement>;
export type SelectSize = 'sm' | 'md';
export type SelectProps = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'className' | 'size'> & {
className?: string;
size?: SelectSize;
};
export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(
{ className, ...props },
{ className, size = 'md', ...props },
ref,
) {
return <select ref={ref} className={clsx(styles.select, className)} {...props} />;
return (
<select
ref={ref}
className={clsx(
styles.select,
{
[styles.selectSizeSm]: size === 'sm',
[styles.selectSizeMd]: size === 'md',
},
className,
)}
data-size={size}
{...props}
/>
);
});
type BaseCheckboxProps = ComponentPropsWithoutRef<typeof BaseCheckbox.Root>;

View file

@ -303,6 +303,11 @@ describe('import route', () => {
expect(await screen.findByRole('button', { name: /^Needs Review\s*1$/ })).toBeInTheDocument();
expect(screen.getAllByText('Album A').length).toBeGreaterThan(0);
const intervalSelect = document.getElementById('auto-import-interval');
if (!(intervalSelect instanceof HTMLElement)) {
throw new Error('auto-import interval select missing');
}
expect(intervalSelect).toHaveAttribute('data-size', 'sm');
expect(getFetchUrls().some((url) => url.includes('/api/import/staging/groups'))).toBe(false);
expect(getFetchUrls().some((url) => url.includes('/api/import/staging/suggestions'))).toBe(
false,

View file

@ -214,6 +214,7 @@ export function AutoImportPanel({
<span>Interval:</span>
<Select
id="auto-import-interval"
size="sm"
value={interval}
onChange={(event) => setInterval(Number(event.target.value))}
>