Add icon to input

This commit is contained in:
Bartłomiej Głownia 2022-03-02 10:32:22 +01:00 committed by Matthew Russell
parent e5f96448fc
commit 3f490f03ca
21 changed files with 81 additions and 36 deletions

View File

@ -82,8 +82,11 @@ module.exports = {
4: '0.25rem',
8: '0.5rem',
12: '0.75rem',
16: '1rem',
20: '1.25rem',
24: '1.5rem',
28: '1.75rem',
32: '2rem',
44: '2.75rem',
},
/*

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import Button from './button';
import { Button } from './button';
describe('Button', () => {
it('should render successfully', () => {

View File

@ -85,5 +85,3 @@ export function Button({
</ButtonTag>
);
}
export default Button;

View File

@ -0,0 +1 @@
export * from './button';

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import Icon from './icon';
import { Icon } from './icon';
describe('Icon', () => {
it('should render successfully', () => {

View File

@ -1,6 +1,8 @@
import { IconSvgPaths20, IconSvgPaths16, IconName } from '@blueprintjs/icons';
import classNames from 'classnames';
export type { IconName } from '@blueprintjs/icons';
interface IconProps {
hasError?: boolean;
disabled?: boolean;
@ -9,11 +11,13 @@ interface IconProps {
size?: 16 | 20 | 24 | 32 | 48 | 64;
}
export const Icon = ({ size = 20, name, className }: IconProps) => {
export const Icon = ({ size = 16, name, className }: IconProps) => {
const effectiveClassName = classNames(
{
'w-20': size === 20,
'h-20': size === 20,
'w-16': size === 16,
'h-16': size === 16,
},
className
);
@ -21,12 +25,10 @@ export const Icon = ({ size = 20, name, className }: IconProps) => {
return (
<svg className={effectiveClassName} viewBox={viewbox}>
<g>
{(size <= 16 ? IconSvgPaths16 : IconSvgPaths20)[name].map((d) => (
<path fill-rule="evenodd" clip-rule="evenodd" d={d} />
{(size <= 16 ? IconSvgPaths16 : IconSvgPaths20)[name].map((d, key) => (
<path fillRule="evenodd" clipRule="evenodd" d={d} key={key} />
))}
</g>
</svg>
);
};
export default Icon;

View File

@ -0,0 +1 @@
export * from './icon';

View File

@ -0,0 +1 @@
export * from './input';

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import Input from './input';
import { Input } from './input';
describe('Input', () => {
it('should render successfully', () => {

View File

@ -1,6 +1,5 @@
import { Story, Meta } from '@storybook/react';
import { Input } from './input';
export default {
component: Input,
title: 'Input',
@ -20,3 +19,11 @@ export const Disabled = Template.bind({});
Disabled.args = {
disabled: true,
};
export const IconPrepend: Story = () => (
<Input value="I type words" prependIconName="search" />
);
export const IconAppend: Story = () => (
<Input value="I type words and even more words" appendIconName="search" />
);

View File

@ -1,10 +1,13 @@
import { InputHTMLAttributes, forwardRef } from 'react';
import classNames from 'classnames';
import { Icon, IconName } from '../icon';
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
hasError?: boolean;
disabled?: boolean;
className?: string;
prependIconName?: IconName;
appendIconName?: IconName;
}
export const inputClassNames = ({
hasError,
@ -21,8 +24,6 @@ export const inputClassNames = ({
'items-center',
'box-border',
'h-28',
'pl-8',
'pr-8',
'border',
'border-light-gray-50',
'bg-neutral-753',
@ -32,6 +33,8 @@ export const inputClassNames = ({
'focus-visible:outline-0',
],
{
'pl-8': !className?.match(/(^| )p(l|x)-\d+( |$)/),
'pr-8': !className?.match(/(^| )p(r|x)-\d+( |$)/),
'border-vega-pink': hasError,
'text-disabled': disabled,
'bg-transparent': disabled,
@ -54,13 +57,41 @@ export const inputStyle = ({
}
: style;
export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => (
<input
{...props}
ref={ref}
className={classNames(inputClassNames(props), 'h-28')}
style={inputStyle(props)}
/>
));
export default Input;
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ prependIconName, appendIconName, className, ...props }, ref) => {
className = `${className} h-28`;
if (prependIconName) {
className += ' pl-28';
}
if (appendIconName) {
className += ' pr-28';
}
const input = (
<input
{...props}
ref={ref}
className={classNames(inputClassNames({ className, ...props }))}
style={inputStyle(props)}
/>
);
const iconName = prependIconName || appendIconName;
if (iconName !== undefined) {
const iconClassName = classNames(
['fill-light-gray-50', 'absolute', 'z-10'],
{
'left-8': prependIconName,
'right-8': appendIconName,
}
);
const icon = <Icon name={iconName} className={iconClassName} size={16} />;
return (
<div className="inline-flex items-center relative">
{prependIconName && icon}
{input}
{appendIconName && icon}
</div>
);
}
return input;
}
);

View File

@ -0,0 +1 @@
export * from './inputError';

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import InputError from './inputError';
import { InputError } from './inputError';
describe('InputError', () => {
it('should render successfully', () => {

View File

@ -1,5 +1,5 @@
import classNames from 'classnames';
import Icon from '../icon/icon';
import { Icon } from '../icon';
interface InputErrorProps {
children?: React.ReactNode;
@ -39,5 +39,3 @@ export const InputError = ({
</div>
);
};
export default InputError;

View File

@ -0,0 +1 @@
export * from './select';

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import Select from './select';
import { Select } from './select';
describe('Select', () => {
it('should render successfully', () => {

View File

@ -32,5 +32,3 @@ export function Select({
</select>
);
}
export default Select;

View File

@ -0,0 +1 @@
export * from './textArea';

View File

@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import TextArea from './textArea';
import { TextArea } from './textArea';
describe('TextArea', () => {
it('should render successfully', () => {

View File

@ -28,5 +28,3 @@ export function TextArea({
</textarea>
);
}
export default TextArea;

View File

@ -1,7 +1,11 @@
import * as EthereumUtils from './utils/web3';
export { Button } from './components/button';
export { Callout } from './components/callout';
export { Button } from './components/button/button';
export { Input } from './components/input/input';
export { EtherscanLink } from './components/etherscan-link';
export { EthereumUtils };
export { EtherscanLink } from './components/etherscan-link';
export { Icon } from './components/icon';
export { Input } from './components/input';
export { InputError } from './components/inputError';
export { Select } from './components/select';
export { TextArea } from './components/textArea';