feat(app): introduce input-group component [EE-2062] (#6135)

pull/6144/head
Chaim Lev-Ari 2021-11-23 07:16:50 +02:00 committed by GitHub
parent 9ad626b36e
commit 830286c332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 205 additions and 0 deletions

View File

@ -836,6 +836,10 @@ json-tree .branch-preview {
align-items: center;
}
.space-y-8 > * + * {
margin-top: 2rem;
}
.my-8 {
margin-top: 2rem;
margin-bottom: 2rem;

View File

@ -16,6 +16,7 @@ export function NumberInput({
value,
className,
readonly,
placeholder,
onChange,
}: Props) {
return (
@ -27,6 +28,7 @@ export function NumberInput({
disabled={disabled}
readonly={readonly}
required={required}
placeholder={placeholder}
onChange={(value) => onChange(parseFloat(value))}
/>
);

View File

@ -20,6 +20,7 @@ export function Select<T extends number | string>({
disabled,
id,
required,
placeholder,
}: Props<T>) {
return (
<select
@ -28,6 +29,7 @@ export function Select<T extends number | string>({
id={id}
required={required}
className={clsx(className, 'form-control')}
placeholder={placeholder}
onChange={handleChange}
>
{options.map((item) => (

View File

@ -17,6 +17,7 @@ export function TextInput({
disabled,
readonly,
required,
placeholder,
}: TextInputProps) {
return (
<BaseInput
@ -28,6 +29,7 @@ export function TextInput({
disabled={disabled}
readonly={readonly}
required={required}
placeholder={placeholder}
/>
);
}

View File

@ -11,6 +11,9 @@ export function Textarea({
onChange,
value,
id,
placeholder,
disabled,
required,
}: Props & InputProps) {
return (
<BaseInput
@ -20,6 +23,9 @@ export function Textarea({
className={className}
value={value}
onChange={onChange}
placeholder={placeholder}
disabled={disabled}
required={required}
/>
);
}

View File

@ -3,6 +3,7 @@ export interface InputProps {
className?: string;
required?: boolean;
disabled?: boolean;
placeholder?: string;
}
export interface ChangeProps<T> {

View File

@ -0,0 +1,108 @@
import { Meta } from '@storybook/react';
import { useState } from 'react';
import { InputGroup } from '.';
export default {
component: InputGroup,
title: 'Components/Form/InputGroup',
} as Meta;
export function BasicExample() {
const [value1, setValue1] = useState('');
const [valueNumber, setValueNumber] = useState(0);
return (
<div className="space-y-8">
<InputGroup>
<InputGroup.Addon>@</InputGroup.Addon>
<InputGroup.Input
value={value1}
onChange={setValue1}
placeholder="Username"
aria-describedby="basic-addon1"
/>
</InputGroup>
<InputGroup>
<InputGroup.Input
value={value1}
onChange={setValue1}
placeholder="Recipient's username"
aria-describedby="basic-addon2"
/>
<InputGroup.Addon>@example.com</InputGroup.Addon>
</InputGroup>
<InputGroup>
<InputGroup.Addon>$</InputGroup.Addon>
<InputGroup.NumberInput
value={valueNumber}
onChange={setValueNumber}
aria-label="Amount (to the nearest dollar)"
/>
<InputGroup.Addon>.00</InputGroup.Addon>
</InputGroup>
<label htmlFor="basic-url">Your vanity URL</label>
<InputGroup>
<InputGroup.Addon>https://example.com/users/</InputGroup.Addon>
<InputGroup.Input
value={value1}
onChange={setValue1}
id="basic-url"
aria-describedby="basic-addon3"
/>
</InputGroup>
</div>
);
}
export function Addons() {
const [value1, setValue1] = useState('');
const [value2, setValue2] = useState('');
return (
<div className="row">
<div className="col-lg-6">
<InputGroup>
<InputGroup.ButtonWrapper>
<button className="btn btn-default" type="button">
Go!
</button>
</InputGroup.ButtonWrapper>
<InputGroup.Input value={value1} onChange={setValue1} />
</InputGroup>
</div>
<div className="col-lg-6">
<InputGroup>
<InputGroup.Input value={value2} onChange={setValue2} />
<InputGroup.Addon>
<input type="checkbox" />
</InputGroup.Addon>
</InputGroup>
</div>
</div>
);
}
export function Sizing() {
const [value, setValue] = useState('');
return (
<div className="space-y-8">
<InputGroup size="small">
<InputGroup.Addon>Small</InputGroup.Addon>
<InputGroup.Input value={value} onChange={setValue} />
</InputGroup>
<InputGroup>
<InputGroup.Addon>Default</InputGroup.Addon>
<InputGroup.Input value={value} onChange={setValue} />
</InputGroup>
<InputGroup size="large">
<InputGroup.Addon>Large</InputGroup.Addon>
<InputGroup.Input value={value} onChange={setValue} />
</InputGroup>
</div>
);
}

View File

@ -0,0 +1,37 @@
import clsx from 'clsx';
import { createContext, PropsWithChildren, useContext } from 'react';
const Context = createContext<null | boolean>(null);
type Size = 'small' | 'large';
export function useInputGroupContext() {
const context = useContext(Context);
if (context == null) {
throw new Error('Should be inside a InputGroup component');
}
}
interface Props {
size?: Size;
}
export function InputGroup({ children, size }: PropsWithChildren<Props>) {
return (
<Context.Provider value>
<div className={clsx('input-group', sizeClass(size))}>{children}</div>
</Context.Provider>
);
}
function sizeClass(size?: Size) {
switch (size) {
case 'large':
return 'input-group-lg';
case 'small':
return 'input-group-sm';
default:
return '';
}
}

View File

@ -0,0 +1,9 @@
import { PropsWithChildren } from 'react';
import { useInputGroupContext } from './InputGroup';
export function InputGroupAddon({ children }: PropsWithChildren<unknown>) {
useInputGroupContext();
return <span className="input-group-addon">{children}</span>;
}

View File

@ -0,0 +1,11 @@
import { PropsWithChildren } from 'react';
import { useInputGroupContext } from './InputGroup';
export function InputGroupButtonWrapper({
children,
}: PropsWithChildren<unknown>) {
useInputGroupContext();
return <span className="input-group-btn">{children}</span>;
}

View File

@ -0,0 +1,23 @@
import { NumberInput, TextInput } from '../Input';
import { InputGroup as MainComponent } from './InputGroup';
import { InputGroupAddon } from './InputGroupAddon';
import { InputGroupButtonWrapper } from './InputGroupButtonWrapper';
interface InputGroupSubComponents {
Addon: typeof InputGroupAddon;
ButtonWrapper: typeof InputGroupButtonWrapper;
Input: typeof TextInput;
NumberInput: typeof NumberInput;
}
const InputGroup: typeof MainComponent &
InputGroupSubComponents = MainComponent as typeof MainComponent &
InputGroupSubComponents;
InputGroup.Addon = InputGroupAddon;
InputGroup.ButtonWrapper = InputGroupButtonWrapper;
InputGroup.Input = TextInput;
InputGroup.NumberInput = NumberInput;
export { InputGroup };