[ui] make disabled color more consistent

pull/452/head
Yangshun Tay 2 years ago
parent 720759628d
commit 766bee0e0c

@ -6,6 +6,7 @@ import { trpc } from '~/utils/trpc';
type Props = Readonly<{ type Props = Readonly<{
disabled?: boolean; disabled?: boolean;
errorMessage?: string;
isLabelHidden?: boolean; isLabelHidden?: boolean;
onSelect: (option: TypeaheadOption) => void; onSelect: (option: TypeaheadOption) => void;
placeHolder?: string; placeHolder?: string;

@ -74,11 +74,18 @@ export function HiddenLabel() {
export function Disabled() { export function Disabled() {
return ( return (
<div className="space-y-4">
<TextArea
disabled={true}
label="Comment"
placeholder="You can't type here, it's disabled. (Placeholder)"
/>
<TextArea <TextArea
disabled={true} disabled={true}
label="Comment" label="Comment"
placeholder="You can't type here, it's disabled." value="You can't type here, it's disabled. (Value)"
/> />
</div>
); );
} }

@ -111,8 +111,14 @@ export function Disabled() {
<TextInput <TextInput
disabled={true} disabled={true}
label="Disabled input" label="Disabled input"
placeholder="John Doe" placeholder="John Doe (Placeholder)"
type="text"
/>
<TextInput
disabled={true}
label="Disabled input"
type="text" type="text"
value="John Doe (Value)"
/> />
<TextInput <TextInput
disabled={true} disabled={true}

@ -128,3 +128,57 @@ export function Required() {
/> />
); );
} }
export function Disabled() {
return (
<Typeahead
disabled={true}
label="Author"
options={[]}
placeholder="John Doe"
// eslint-disable-next-line @typescript-eslint/no-empty-function
onQueryChange={() => {}}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onSelect={() => {}}
/>
);
}
export function Error() {
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
errorMessage={
selectedEntry.id === '1' ? 'Cannot select Wade Cooper' : undefined
}
label="Author"
options={filteredPeople}
required={true}
value={selectedEntry}
onQueryChange={setQuery}
onSelect={setSelectedEntry}
/>
);
}

@ -50,7 +50,7 @@ function CheckboxInput(
className={clsx( className={clsx(
'h-4 w-4 rounded border-slate-300', 'h-4 w-4 rounded border-slate-300',
disabled disabled
? 'bg-slate-100 text-slate-400' ? 'bg-slate-50 text-slate-400'
: 'text-primary-600 focus:ring-primary-500', : 'text-primary-600 focus:ring-primary-500',
)} )}
defaultChecked={defaultValue} defaultChecked={defaultValue}

@ -88,10 +88,9 @@ function Select<T>(
aria-label={isLabelHidden ? label : undefined} aria-label={isLabelHidden ? label : undefined}
className={clsx( className={clsx(
display === 'block' && 'block w-full', display === 'block' && 'block w-full',
'rounded-md py-2 pl-3 pr-8 text-sm focus:outline-none', 'rounded-md py-2 pl-3 pr-8 text-sm focus:outline-none disabled:bg-slate-50 disabled:text-slate-500',
stateClasses[state], stateClasses[state],
borderClasses[borderStyle], borderClasses[borderStyle],
disabled && 'bg-slate-100',
)} )}
defaultValue={defaultValue != null ? String(defaultValue) : undefined} defaultValue={defaultValue != null ? String(defaultValue) : undefined}
disabled={disabled} disabled={disabled}

@ -108,9 +108,8 @@ function TextArea(
aria-describedby={hasError ? errorId : undefined} aria-describedby={hasError ? errorId : undefined}
aria-invalid={hasError ? true : undefined} aria-invalid={hasError ? true : undefined}
className={clsx( className={clsx(
'block w-full rounded-md text-sm', 'block w-full rounded-md text-sm disabled:bg-slate-50 disabled:text-slate-500',
stateClasses[state].textArea, stateClasses[state].textArea,
disabled && 'bg-slate-100',
resizeClasses[resize], resizeClasses[resize],
)} )}
defaultValue={defaultValue} defaultValue={defaultValue}

@ -143,7 +143,7 @@ function TextInput(
<div <div
className={clsx( className={clsx(
'flex w-full overflow-hidden rounded-md border text-sm focus-within:ring-1', 'flex w-full overflow-hidden rounded-md border text-sm focus-within:ring-1',
disabled && 'pointer-events-none select-none bg-slate-100', disabled && 'pointer-events-none select-none bg-slate-50',
containerClass, containerClass,
)}> )}>
{(() => { {(() => {
@ -178,9 +178,8 @@ function TextInput(
aria-describedby={hasError ? errorId : undefined} aria-describedby={hasError ? errorId : undefined}
aria-invalid={hasError ? true : undefined} aria-invalid={hasError ? true : undefined}
className={clsx( className={clsx(
'w-0 flex-1 border-none text-sm focus:outline-none focus:ring-0', 'w-0 flex-1 border-none text-sm focus:outline-none focus:ring-0 disabled:cursor-not-allowed disabled:bg-transparent disabled:text-slate-500',
inputClass, inputClass,
disabled && 'bg-transparent',
)} )}
defaultValue={defaultValue} defaultValue={defaultValue}
disabled={disabled} disabled={disabled}

@ -1,5 +1,6 @@
import clsx from 'clsx'; import clsx from 'clsx';
import type { InputHTMLAttributes } from 'react'; import type { InputHTMLAttributes } from 'react';
import { useId } 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';
@ -24,6 +25,7 @@ type Attributes = Pick<
>; >;
type Props = Readonly<{ type Props = Readonly<{
errorMessage?: React.ReactNode;
isLabelHidden?: boolean; isLabelHidden?: boolean;
label: string; label: string;
noResultsMessage?: string; noResultsMessage?: string;
@ -39,6 +41,27 @@ type Props = Readonly<{
}> & }> &
Readonly<Attributes>; Readonly<Attributes>;
type State = 'error' | 'normal';
const stateClasses: Record<
State,
Readonly<{
container: string;
input: string;
}>
> = {
error: {
container:
'border-danger-300 focus-within:outline-none focus-within:ring-danger-500 focus-within:border-danger-500',
input: 'text-danger-900 placeholder-danger-300',
},
normal: {
container:
'focus-within:ring-primary-500 focus-within:border-primary-500 border-slate-300',
input: 'placeholder:text-slate-400',
},
};
const textSizes: Record<TypeaheadTextSize, string> = { const textSizes: Record<TypeaheadTextSize, string> = {
default: 'text-sm', default: 'text-sm',
inherit: '', inherit: '',
@ -46,6 +69,7 @@ const textSizes: Record<TypeaheadTextSize, string> = {
export default function Typeahead({ export default function Typeahead({
disabled = false, disabled = false,
errorMessage,
isLabelHidden, isLabelHidden,
label, label,
noResultsMessage = 'No results', noResultsMessage = 'No results',
@ -58,7 +82,11 @@ export default function Typeahead({
onSelect, onSelect,
...props ...props
}: Props) { }: Props) {
const hasError = errorMessage != null;
const errorId = useId();
const state: State = hasError ? 'error' : 'normal';
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
return ( return (
<div> <div>
<Combobox <Combobox
@ -102,14 +130,18 @@ export default function Typeahead({
<div className="relative"> <div className="relative">
<div <div
className={clsx( className={clsx(
'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', 'relative w-full cursor-default overflow-hidden rounded-md border text-left focus-within:ring-1',
disabled && 'pointer-events-none select-none bg-slate-50',
stateClasses[state].container,
textSizes[textSize], textSizes[textSize],
)}> )}>
<Combobox.Input <Combobox.Input
aria-describedby={hasError ? errorId : undefined}
className={clsx( className={clsx(
'w-full border-none py-2 pl-3 pr-10 leading-5 text-slate-900 focus:ring-0', 'w-full border-none py-2 pl-3 pr-10 leading-5 focus:ring-0',
stateClasses[state].input,
textSizes[textSize], textSizes[textSize],
disabled && 'pointer-events-none select-none bg-slate-100', 'disabled:cursor-not-allowed disabled:bg-transparent disabled:text-slate-500',
)} )}
displayValue={(option) => displayValue={(option) =>
(option as unknown as TypeaheadOption)?.label (option as unknown as TypeaheadOption)?.label
@ -170,6 +202,11 @@ export default function Typeahead({
</Transition> </Transition>
</div> </div>
</Combobox> </Combobox>
{errorMessage && (
<p className="text-danger-600 mt-2 text-sm" id={errorId}>
{errorMessage}
</p>
)}
</div> </div>
); );
} }

Loading…
Cancel
Save