Skip to content

Commit 4811903

Browse files
committed
fix: introduce back start and end addon on input
1 parent a4411ed commit 4811903

File tree

6 files changed

+120
-63
lines changed

6 files changed

+120
-63
lines changed

src/components/ui/date-input.stories.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { useState } from 'react';
44
import { useDisclosure } from 'react-use-disclosure';
55

66
import { onSubmit } from '@/components/form/docs.utils';
7-
import { Button } from '@/components/ui/button';
87
import { Calendar } from '@/components/ui/calendar';
98
import { DateInput } from '@/components/ui/date-input';
9+
import { InputGroupButton } from '@/components/ui/input-group';
1010
import {
1111
Popover,
1212
PopoverContent,
@@ -43,15 +43,15 @@ export const WithPicker = () => {
4343
<DateInput
4444
onChange={(date) => setDate(date)}
4545
value={date}
46-
endElement={
46+
endAddon={
4747
<Popover
4848
open={datePicker.isOpen}
4949
onOpenChange={(open) => datePicker.toggle(open)}
5050
>
5151
<PopoverTrigger asChild>
52-
<Button size="icon-xs" className="-mr-1.5">
52+
<InputGroupButton size="icon-xs">
5353
<CalendarIcon />
54-
</Button>
54+
</InputGroupButton>
5555
</PopoverTrigger>
5656
<PopoverContent className="w-auto p-0" align="start">
5757
<Calendar

src/components/ui/date-picker.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { CalendarIcon } from 'lucide-react';
22
import { ComponentProps } from 'react';
33
import { useDisclosure } from 'react-use-disclosure';
44

5-
import { Button } from '@/components/ui/button';
65
import { Calendar } from '@/components/ui/calendar';
76
import { DateInput } from '@/components/ui/date-input';
7+
import { InputGroupButton } from '@/components/ui/input-group';
88
import {
99
Popover,
1010
PopoverContent,
@@ -29,16 +29,16 @@ export const DatePicker = ({
2929
return (
3030
<DateInput
3131
{...props}
32-
endElement={
32+
endAddon={
3333
noCalendar ? null : (
3434
<Popover
3535
open={datePicker.isOpen}
3636
onOpenChange={(open) => datePicker.toggle(open)}
3737
>
3838
<PopoverTrigger asChild>
39-
<Button size="icon-xs" variant="secondary">
39+
<InputGroupButton size="icon-xs">
4040
<CalendarIcon />
41-
</Button>
41+
</InputGroupButton>
4242
</PopoverTrigger>
4343
<PopoverContent className="w-auto p-0" align="start">
4444
<Calendar

src/components/ui/input.stories.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { Meta } from '@storybook/react-vite';
2+
import { MailIcon } from 'lucide-react';
23

34
import { Input } from '@/components/ui/input';
5+
import { InputGroupText } from '@/components/ui/input-group';
46

57
export default {
68
title: 'Input',
@@ -31,3 +33,38 @@ export const Sizes = () => {
3133
</div>
3234
);
3335
};
36+
export const StartEndAddons = () => {
37+
return (
38+
<div className="flex flex-col gap-4">
39+
<p className="text-sm">
40+
See <strong>InputGroup</strong> for more advanced use cases
41+
</p>
42+
<Input
43+
startAddon={<MailIcon />}
44+
defaultValue="Icon start"
45+
placeholder="Placeholder..."
46+
/>
47+
<Input
48+
endAddon={<MailIcon />}
49+
defaultValue="Icon end"
50+
placeholder="Placeholder..."
51+
/>
52+
<Input
53+
startAddon={<MailIcon />}
54+
endAddon={<MailIcon />}
55+
defaultValue="Icon start and end"
56+
placeholder="Placeholder..."
57+
/>
58+
<Input
59+
startAddon={<InputGroupText>https://</InputGroupText>}
60+
defaultValue="Text start"
61+
placeholder="Placeholder..."
62+
/>
63+
<Input
64+
endAddon={<InputGroupText>.com</InputGroupText>}
65+
defaultValue="Text end"
66+
placeholder="Placeholder..."
67+
/>
68+
</div>
69+
);
70+
};

src/components/ui/input.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { InputGroup, InputGroupInput } from '@/components/ui/input-group';
1+
import { ReactNode } from 'react';
2+
3+
import {
4+
InputGroup,
5+
InputGroupAddon,
6+
InputGroupInput,
7+
} from '@/components/ui/input-group';
28

39
type InputProps = Pick<
410
React.ComponentProps<'input'>,
@@ -23,12 +29,27 @@ type InputProps = Pick<
2329
> &
2430
Pick<React.ComponentProps<typeof InputGroup>, 'size'> & {
2531
ref?: React.Ref<HTMLInputElement | null>;
32+
startAddon?: ReactNode;
33+
endAddon?: ReactNode;
2634
};
2735

28-
function Input({ ref, size, ...props }: InputProps) {
36+
function Input({
37+
ref,
38+
size,
39+
className,
40+
startAddon,
41+
endAddon,
42+
...props
43+
}: InputProps) {
2944
return (
30-
<InputGroup size={size}>
45+
<InputGroup size={size} className={className}>
46+
{!!startAddon && (
47+
<InputGroupAddon align="inline-start">{startAddon}</InputGroupAddon>
48+
)}
3149
<InputGroupInput {...props} ref={ref} data-slot="input" />
50+
{!!endAddon && (
51+
<InputGroupAddon align="inline-end">{endAddon}</InputGroupAddon>
52+
)}
3253
</InputGroup>
3354
);
3455
}

src/components/ui/select.stories.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { faker } from '@faker-js/faker';
2+
import { ComboboxButton } from '@headlessui/react';
23
import { Meta } from '@storybook/react-vite';
3-
import { CheckIcon } from 'lucide-react';
4+
import { ArrowDown, CheckIcon } from 'lucide-react';
45
import { useState } from 'react';
56

7+
import { InputGroupButton } from '@/components/ui/input-group';
68
import { ComboboxOption, Select } from '@/components/ui/select';
79

810
export default {
@@ -208,8 +210,12 @@ export const Customization = () => {
208210
<Select
209211
options={astrobears}
210212
inputProps={{
211-
className:
212-
'bg-warning-200 text-warning-700 border-transparent shadow-none dark:bg-warning-800',
213+
endAddon: (
214+
<ComboboxButton as={InputGroupButton} size="icon-xs">
215+
<ArrowDown />
216+
</ComboboxButton>
217+
),
218+
className: 'bg-warning-200 border-none shadow-none dark:bg-warning-800',
213219
}}
214220
value={bear}
215221
onChange={(v) => setBear(v)}

src/components/ui/select.tsx

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,30 @@ import { isEmpty, isNonNullish, isNullish } from 'remeda';
1616

1717
import { cn } from '@/lib/tailwind/utils';
1818

19-
import { Button } from '@/components/ui/button';
20-
import {
21-
InputGroup,
22-
InputGroupAddon,
23-
InputGroupInput,
24-
} from '@/components/ui/input-group';
19+
import { Input } from '@/components/ui/input';
20+
import { InputGroupButton } from '@/components/ui/input-group';
2521

2622
type OptionBase = { id: string; label: string; disabled?: boolean };
2723
type TValueBase = OptionBase | null;
2824

29-
type InputPropsRoot = Pick<ComponentProps<typeof InputGroup>, 'size'> &
30-
Pick<
31-
ComponentProps<typeof InputGroupInput>,
32-
| 'placeholder'
33-
| 'aria-invalid'
34-
| 'aria-describedby'
35-
| 'readOnly'
36-
| 'autoFocus'
37-
>;
25+
type InputProps = ComponentProps<typeof Input>;
26+
type InputPropsRoot = Pick<
27+
InputProps,
28+
| 'placeholder'
29+
| 'size'
30+
| 'aria-invalid'
31+
| 'aria-describedby'
32+
| 'readOnly'
33+
| 'autoFocus'
34+
>;
3835

3936
type SelectProps<TValue extends TValueBase> = ComboboxProps<TValue, false> &
4037
InputPropsRoot & {
4138
/** Allow user to clear the select using a button */
4239
withClearButton?: boolean;
4340
options: ReadonlyArray<NonNullable<TValue>>;
4441
inputProps?: Omit<
45-
RemoveFromType<ComponentProps<typeof InputGroupInput>, InputPropsRoot>,
42+
RemoveFromType<InputProps, InputPropsRoot>,
4643
// Removing the defaultValue from the input to avoid conflict with ComboboxInput type
4744
'defaultValue'
4845
>;
@@ -65,6 +62,7 @@ export const Select = <TValue extends TValueBase>({
6562
renderOption,
6663
onChange,
6764
value,
65+
autoFocus,
6866
allowCustomValue = false,
6967
'aria-describedby': ariaDescribedBy,
7068
'aria-invalid': ariaInvalid,
@@ -123,10 +121,6 @@ export const Select = <TValue extends TValueBase>({
123121
return set('default');
124122
});
125123

126-
const { className: inputGroupClassName, ...restInputProps } = inputProps ?? {
127-
className: '',
128-
};
129-
130124
return (
131125
<Combobox
132126
immediate
@@ -141,42 +135,41 @@ export const Select = <TValue extends TValueBase>({
141135
disabled={props.disabled || readOnly}
142136
{...props}
143137
>
144-
<InputGroup size={size} className={inputGroupClassName}>
138+
<div className="relative">
145139
{/* Setting the type so we have type check and typings for the displayValue prop */}
146-
<ComboboxInput<TValue, typeof InputGroupInput>
147-
as={InputGroupInput}
140+
<ComboboxInput<TValue, typeof Input>
141+
as={Input}
142+
size={size}
148143
displayValue={(item) => item?.label ?? ''}
149144
onChange={handleInputChange}
145+
autoFocus={autoFocus}
150146
placeholder={placeholder ?? t('components:select.placeholder')}
151147
aria-invalid={ariaInvalid}
152148
aria-describedby={ariaDescribedBy}
153-
{...restInputProps}
154-
/>
155-
<InputGroupAddon align="inline-end">
156-
<div className="flex gap-1">
157-
{!!withClearButton && value && (
158-
<Button
159-
variant="ghost"
149+
endAddon={
150+
<>
151+
{!!withClearButton && value && (
152+
<InputGroupButton
153+
size="icon-xs"
154+
className="-mr-1"
155+
onClick={() => {
156+
onChange?.(null);
157+
}}
158+
>
159+
<XIcon />
160+
</InputGroupButton>
161+
)}
162+
<ComboboxButton
163+
as={InputGroupButton}
164+
disabled={props.disabled}
160165
size="icon-xs"
161-
className="text-inherit"
162-
onClick={() => {
163-
onChange?.(null);
164-
}}
165166
>
166-
<XIcon />
167-
</Button>
168-
)}
169-
<ComboboxButton
170-
as={Button}
171-
variant="ghost"
172-
disabled={props.disabled}
173-
className="-me-1.5 text-inherit opacity-60 hover:opacity-100"
174-
size="icon-xs"
175-
>
176-
<ChevronDownIcon aria-hidden="true" />
177-
</ComboboxButton>
178-
</div>
179-
</InputGroupAddon>
167+
<ChevronDownIcon aria-hidden="true" />
168+
</ComboboxButton>
169+
</>
170+
}
171+
{...inputProps}
172+
/>
180173

181174
<ComboboxOptions
182175
anchor="bottom start"
@@ -211,7 +204,7 @@ export const Select = <TValue extends TValueBase>({
211204
)
212205
.exhaustive()}
213206
</ComboboxOptions>
214-
</InputGroup>
207+
</div>
215208
</Combobox>
216209
);
217210
};

0 commit comments

Comments
 (0)