ui: add more props to Select

This commit is contained in:
Yangshun Tay
2022-10-05 20:30:45 +08:00
parent 0a3fb2503d
commit 4fcf80ae2a
3 changed files with 115 additions and 21 deletions

View File

@ -7,6 +7,9 @@ const SelectDisplays: ReadonlyArray<SelectDisplay> = ['inline', 'block'];
export default { export default {
argTypes: { argTypes: {
disabled: {
control: 'boolean',
},
display: { display: {
control: { type: 'select' }, control: { type: 'select' },
options: SelectDisplays, options: SelectDisplays,
@ -72,7 +75,7 @@ export function Display() {
const [value, setValue] = useState('apple'); const [value, setValue] = useState('apple');
return ( return (
<div className="space-x-4"> <div className="space-y-4">
<Select <Select
label="Select a fruit" label="Select a fruit"
options={[ options={[
@ -92,6 +95,84 @@ export function Display() {
value={value} value={value}
onChange={setValue} onChange={setValue}
/> />
<Select
display="block"
label="Select a fruit"
options={[
{
label: 'Apple',
value: 'apple',
},
{
label: 'Banana',
value: 'banana',
},
{
label: 'Orange',
value: 'orange',
},
]}
value={value}
onChange={setValue}
/>
</div>
);
}
export function HiddenLabel() {
const [value, setValue] = useState('apple');
return (
<div className="space-x-4">
<Select
isLabelHidden={true}
label="Select a fruit"
options={[
{
label: 'Apple',
value: 'apple',
},
{
label: 'Banana',
value: 'banana',
},
{
label: 'Orange',
value: 'orange',
},
]}
value={value}
onChange={setValue}
/>
</div>
);
}
export function Disabled() {
const [value, setValue] = useState('apple');
return (
<div className="space-x-4">
<Select
disabled={true}
label="Select a fruit"
options={[
{
label: 'Apple',
value: 'apple',
},
{
label: 'Banana',
value: 'banana',
},
{
label: 'Orange',
value: 'orange',
},
]}
value={value}
onChange={setValue}
/>
</div> </div>
); );
} }

View File

@ -1,6 +1,13 @@
import clsx from 'clsx'; import clsx from 'clsx';
import type { ForwardedRef, SelectHTMLAttributes } from 'react';
import { forwardRef } from 'react';
import { useId } from 'react'; import { useId } from 'react';
type Attributes = Pick<
SelectHTMLAttributes<HTMLSelectElement>,
'disabled' | 'name' | 'onBlur' | 'onFocus' | 'required'
>;
export type SelectItem<T> = Readonly<{ export type SelectItem<T> = Readonly<{
label: string; label: string;
value: T; value: T;
@ -16,17 +23,22 @@ type Props<T> = Readonly<{
onChange: (value: string) => void; onChange: (value: string) => void;
options: ReadonlyArray<SelectItem<T>>; options: ReadonlyArray<SelectItem<T>>;
value: T; value: T;
}>; }> &
Readonly<Attributes>;
export default function Select<T>({ function Select<T>(
display, {
label, display,
isLabelHidden, disabled,
name, label,
options, isLabelHidden,
value, options,
onChange, value,
}: Props<T>) { onChange,
...props
}: Props<T>,
ref: ForwardedRef<HTMLSelectElement>,
) {
const id = useId(); const id = useId();
return ( return (
@ -40,17 +52,20 @@ export default function Select<T>({
{label} {label}
</label> </label>
<select <select
ref={ref}
aria-label={isLabelHidden ? label : undefined} aria-label={isLabelHidden ? label : undefined}
className={clsx( className={clsx(
display === 'block' && 'block w-full', display === 'block' && 'block w-full',
'focus:border-primary-500 focus:ring-primary-500 rounded-md border-slate-300 py-2 pl-3 pr-10 text-base focus:outline-none sm:text-sm', 'focus:border-primary-500 focus:ring-primary-500 rounded-md border-slate-300 py-2 pl-3 pr-10 text-base focus:outline-none sm:text-sm',
disabled && 'bg-slate-100',
)} )}
disabled={disabled}
id={id} id={id}
name={name ?? undefined}
value={String(value)} value={String(value)}
onChange={(event) => { onChange={(event) => {
onChange(event.target.value); onChange(event.target.value);
}}> }}
{...props}>
{options.map(({ label: optionLabel, value: optionValue }) => ( {options.map(({ label: optionLabel, value: optionValue }) => (
<option key={String(optionValue)} value={String(optionValue)}> <option key={String(optionValue)} value={String(optionValue)}>
{optionLabel} {optionLabel}
@ -60,3 +75,5 @@ export default function Select<T>({
</div> </div>
); );
} }
export default forwardRef(Select);

View File

@ -7,7 +7,7 @@ import type {
} from 'react'; } from 'react';
import React, { forwardRef, useId } from 'react'; import React, { forwardRef, useId } from 'react';
type TextInputInputAttributes = Pick< type Attributes = Pick<
InputHTMLAttributes<HTMLInputElement>, InputHTMLAttributes<HTMLInputElement>,
| 'autoComplete' | 'autoComplete'
| 'disabled' | 'disabled'
@ -16,17 +16,14 @@ type TextInputInputAttributes = Pick<
| 'min' | 'min'
| 'minLength' | 'minLength'
| 'name' | 'name'
| 'onBlur'
| 'onFocus'
| 'pattern' | 'pattern'
| 'placeholder' | 'placeholder'
| 'required' | 'required'
| 'type' | 'type'
>; >;
type TextInputDOMAttributes = Pick<
InputHTMLAttributes<HTMLInputElement>,
'onBlur' | 'onFocus'
>;
type Props = Readonly<{ type Props = Readonly<{
defaultValue?: string; defaultValue?: string;
endIcon?: React.ComponentType<React.ComponentProps<'svg'>>; endIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
@ -39,8 +36,7 @@ type Props = Readonly<{
startIcon?: React.ComponentType<React.ComponentProps<'svg'>>; startIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
value?: string; value?: string;
}> & }> &
Readonly<TextInputDOMAttributes> & Readonly<Attributes>;
Readonly<TextInputInputAttributes>;
type State = 'error' | 'normal'; type State = 'error' | 'normal';