[ui][typeahead] add required field

pull/385/head
Yangshun Tay 2 years ago
parent 44f4454d96
commit d38f997701

@ -20,6 +20,9 @@ export default {
placeholder: { placeholder: {
control: 'text', control: 'text',
}, },
required: {
control: 'boolean',
},
}, },
component: Typeahead, component: Typeahead,
parameters: { parameters: {
@ -80,3 +83,39 @@ Basic.args = {
isLabelHidden: false, isLabelHidden: false,
label: 'Author', label: 'Author',
}; };
export function Required() {
const people = [
{ id: '1', label: 'Wade Cooper', value: '1' },
{ id: '2', label: 'Arlene Mccoy', value: '2' },
{ id: '3', label: 'Devon Webb', value: '3' },
{ id: '4', label: 'Tom Cook', value: '4' },
{ id: '5', label: 'Tanya Fox', value: '5' },
{ id: '6', label: 'Hellen Schmidt', value: '6' },
];
const [selectedEntry, setSelectedEntry] = useState<TypeaheadOption>(
people[0],
);
const [query, setQuery] = useState('');
const filteredPeople =
query === ''
? people
: people.filter((person) =>
person.label
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.toLowerCase().replace(/\s+/g, '')),
);
return (
<Typeahead
label="Author"
options={filteredPeople}
required={true}
value={selectedEntry}
onQueryChange={setQuery}
onSelect={setSelectedEntry}
/>
);
}

@ -1,4 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import type { InputHTMLAttributes } from 'react';
import { Fragment, useState } from 'react'; import { Fragment, useState } from 'react';
import { Combobox, Transition } from '@headlessui/react'; import { Combobox, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/20/solid'; import { ChevronDownIcon } from '@heroicons/react/20/solid';
@ -10,8 +11,18 @@ export type TypeaheadOption = Readonly<{
value: string; value: string;
}>; }>;
type Attributes = Pick<
InputHTMLAttributes<HTMLInputElement>,
| 'disabled'
| 'name'
| 'onBlur'
| 'onFocus'
| 'pattern'
| 'placeholder'
| 'required'
>;
type Props = Readonly<{ type Props = Readonly<{
disabled?: boolean;
isLabelHidden?: boolean; isLabelHidden?: boolean;
label: string; label: string;
noResultsMessage?: string; noResultsMessage?: string;
@ -22,9 +33,9 @@ type Props = Readonly<{
) => void; ) => void;
onSelect: (option: TypeaheadOption) => void; onSelect: (option: TypeaheadOption) => void;
options: ReadonlyArray<TypeaheadOption>; options: ReadonlyArray<TypeaheadOption>;
placeholder?: string;
value?: TypeaheadOption; value?: TypeaheadOption;
}>; }> &
Readonly<Attributes>;
export default function Typeahead({ export default function Typeahead({
disabled = false, disabled = false,
@ -34,9 +45,10 @@ export default function Typeahead({
nullable = false, nullable = false,
options, options,
onQueryChange, onQueryChange,
required,
value, value,
onSelect, onSelect,
placeholder, ...props
}: Props) { }: Props) {
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
return ( return (
@ -68,6 +80,12 @@ export default function Typeahead({
: 'mb-1 block text-sm font-medium text-slate-700', : 'mb-1 block text-sm font-medium text-slate-700',
)}> )}>
{label} {label}
{required && (
<span aria-hidden="true" className="text-danger-500">
{' '}
*
</span>
)}
</Combobox.Label> </Combobox.Label>
<div className="relative"> <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 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 sm:text-sm"> <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 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 sm:text-sm">
@ -79,11 +97,12 @@ export default function Typeahead({
displayValue={(option) => displayValue={(option) =>
(option as unknown as TypeaheadOption)?.label (option as unknown as TypeaheadOption)?.label
} }
placeholder={placeholder} required={required}
onChange={(event) => { onChange={(event) => {
setQuery(event.target.value); setQuery(event.target.value);
onQueryChange(event.target.value, event); onQueryChange(event.target.value, event);
}} }}
{...props}
/> />
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2"> <Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon <ChevronDownIcon

Loading…
Cancel
Save