[ui] add textSize prop to typeahead

pull/451/head
Yangshun Tay 2 years ago
parent a1fc43242e
commit 9df6b52392

@ -1,8 +1,13 @@
import React, { useState } from 'react';
import type { ComponentMeta } from '@storybook/react';
import type { TypeaheadOption } from '@tih/ui';
import type { TypeaheadOption, TypeaheadTextSize } from '@tih/ui';
import { Typeahead } from '@tih/ui';
const typeaheadTextSizes: ReadonlyArray<TypeaheadTextSize> = [
'default',
'inherit',
];
export default {
argTypes: {
disabled: {
@ -23,6 +28,10 @@ export default {
required: {
control: 'boolean',
},
textSize: {
control: { type: 'select' },
options: typeaheadTextSizes,
},
},
component: Typeahead,
parameters: {

@ -10,6 +10,7 @@ export type TypeaheadOption = Readonly<{
label: string;
value: string;
}>;
export type TypeaheadTextSize = 'default' | 'inherit';
type Attributes = Pick<
InputHTMLAttributes<HTMLInputElement>,
@ -33,10 +34,16 @@ type Props = Readonly<{
) => void;
onSelect: (option: TypeaheadOption) => void;
options: ReadonlyArray<TypeaheadOption>;
textSize?: TypeaheadTextSize;
value?: TypeaheadOption;
}> &
Readonly<Attributes>;
const textSizes: Record<TypeaheadTextSize, string> = {
default: 'text-sm',
inherit: '',
};
export default function Typeahead({
disabled = false,
isLabelHidden,
@ -46,108 +53,123 @@ export default function Typeahead({
options,
onQueryChange,
required,
textSize = 'default',
value,
onSelect,
...props
}: Props) {
const [query, setQuery] = useState('');
return (
<Combobox
by="id"
disabled={disabled}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
multiple={false}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
nullable={nullable}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
value={value}
onChange={(newValue) => {
if (newValue == null) {
return;
}
<div>
<Combobox
by="id"
disabled={disabled}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
multiple={false}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
onSelect(newValue as TypeaheadOption);
}}>
<Combobox.Label
className={clsx(
isLabelHidden
? 'sr-only'
: 'mb-1 block text-sm font-medium text-slate-700',
)}>
{label}
{required && (
<span aria-hidden="true" className="text-danger-500">
{' '}
*
</span>
)}
</Combobox.Label>
<div className="relative">
<div className="focus-visible:ring-offset-primary-300 relative w-full cursor-default overflow-hidden rounded-lg border border-slate-300 bg-white text-left text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2">
<Combobox.Input
nullable={nullable}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
value={value}
onChange={(newValue) => {
if (newValue == null) {
return;
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
onSelect(newValue as TypeaheadOption);
}}>
<Combobox.Label
className={clsx(
isLabelHidden
? 'sr-only'
: clsx(
'mb-1 block font-medium text-slate-700',
textSizes[textSize],
),
)}>
{label}
{required && (
<span aria-hidden="true" className="text-danger-500">
{' '}
*
</span>
)}
</Combobox.Label>
<div className="relative">
<div
className={clsx(
'w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-slate-900 focus:ring-0',
disabled && 'pointer-events-none select-none bg-slate-100',
)}
displayValue={(option) =>
(option as unknown as TypeaheadOption)?.label
}
required={required}
onChange={(event) => {
setQuery(event.target.value);
onQueryChange(event.target.value, event);
}}
{...props}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon
aria-hidden="true"
className="h-5 w-5 text-slate-400"
'focus-visible:ring-offset-primary-300 relative w-full cursor-default overflow-hidden rounded-lg border border-slate-300 bg-white text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2',
textSizes[textSize],
)}>
<Combobox.Input
className={clsx(
'w-full border-none py-2 pl-3 pr-10 leading-5 text-slate-900 focus:ring-0',
textSizes[textSize],
disabled && 'pointer-events-none select-none bg-slate-100',
)}
displayValue={(option) =>
(option as unknown as TypeaheadOption)?.label
}
required={required}
onChange={(event) => {
setQuery(event.target.value);
onQueryChange(event.target.value, event);
}}
{...props}
/>
</Combobox.Button>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon
aria-hidden="true"
className="h-5 w-5 text-slate-400"
/>
</Combobox.Button>
</div>
<Transition
afterLeave={() => setQuery('')}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<Combobox.Options
className={clsx(
'absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none',
textSizes[textSize],
)}>
{options.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-slate-700">
{noResultsMessage}
</div>
) : (
options.map((option) => (
<Combobox.Option
key={option.id}
className={({ active }) =>
clsx(
'relative cursor-default select-none py-2 px-4 text-slate-500',
active && 'bg-slate-100',
)
}
value={option}>
{({ selected }) => (
<span
className={clsx(
'block truncate',
selected ? 'font-medium' : 'font-normal',
)}>
{option.label}
</span>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
<Transition
afterLeave={() => setQuery('')}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0">
<Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{options.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-slate-700">
{noResultsMessage}
</div>
) : (
options.map((option) => (
<Combobox.Option
key={option.id}
className={({ active }) =>
clsx(
'relative cursor-default select-none py-2 px-4 text-slate-500',
active && 'bg-slate-100',
)
}
value={option}>
{({ selected }) => (
<span
className={clsx(
'block truncate',
selected ? 'font-medium' : 'font-normal',
)}>
{option.label}
</span>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</Combobox>
</div>
);
}

Loading…
Cancel
Save