Skip to content

Commit

Permalink
Add ref forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitayutanov committed Aug 14, 2023
1 parent aa8f5c6 commit 2c8ffec
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 100 deletions.
10 changes: 6 additions & 4 deletions utils/vara-ui/src/components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ButtonHTMLAttributes, FunctionComponent, SVGProps } from 'react';
import { ButtonHTMLAttributes, FunctionComponent, SVGProps, forwardRef } from 'react';
import cx from 'clsx';
import styles from './button.module.css';

Expand All @@ -21,13 +21,14 @@ type IconProps = BaseProps & {
// TODO: omit text and icon if children specified?
type Props = TextProps | IconProps;

function Button(props: Props) {
const Button = forwardRef<HTMLButtonElement, Props>((props, ref) => {
const {
className,
text,
icon: Icon,
disabled,
isLoading,
type = 'button',
color = 'primary',
size = 'default',
children,
Expand All @@ -36,7 +37,7 @@ function Button(props: Props) {

return (
<button
type="button"
type={type}
className={cx(
styles.button,
styles[color],
Expand All @@ -47,14 +48,15 @@ function Button(props: Props) {
className,
)}
disabled={disabled || isLoading}
ref={ref}
{...attrs}>
{Icon && <Icon />}
{text && <span>{text}</span>}

{children}
</button>
);
}
});

export { Button };
export type { Props as ButtonProps };
7 changes: 4 additions & 3 deletions utils/vara-ui/src/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InputHTMLAttributes } from 'react';
import { InputHTMLAttributes, forwardRef } from 'react';
import cx from 'clsx';
import styles from './checkbox.module.css';

Expand All @@ -7,21 +7,22 @@ type Props = InputHTMLAttributes<HTMLInputElement> & {
type?: 'switch';
};

function Checkbox({ label, className, type, ...attrs }: Props) {
const Checkbox = forwardRef<HTMLInputElement, Props>(({ label, className, type, ...attrs }, ref) => {
const { disabled } = attrs;

return (
<label className={cx(styles.label, className, disabled && styles.disabled)}>
<input
type="checkbox"
className={cx(styles.input, type === 'switch' ? styles.switch : styles.checkbox)}
ref={ref}
{...attrs}
/>

{label}
</label>
);
}
});

export { Checkbox };
export type { Props as CheckboxProps };
65 changes: 34 additions & 31 deletions utils/vara-ui/src/components/input/input.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InputHTMLAttributes, ReactNode, useId } from 'react';
import { InputHTMLAttributes, ReactNode, forwardRef, useId } from 'react';
import cx from 'clsx';
import styles from './input.module.css';

Expand All @@ -8,37 +8,40 @@ type Props = Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'size'> & {
error?: ReactNode;
};

function Input({ className, label, error, placeholder = ' ', size = 'default', ...attrs }: Props) {
const { disabled } = attrs;

const id = useId();

return (
<div className={cx(styles.root, className, disabled && styles.disabled)}>
<div className={styles.base}>
<input
type="text"
id={id}
className={cx(styles.input, styles[size], error && styles.error)}
placeholder={placeholder}
{...attrs}
/>

{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}

<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
const Input = forwardRef<HTMLInputElement, Props>(
({ className, label, error, type = 'text', placeholder = ' ', size = 'default', ...attrs }, ref) => {
const { disabled } = attrs;

const id = useId();

return (
<div className={cx(styles.root, className, disabled && styles.disabled)}>
<div className={styles.base}>
<input
type={type}
id={id}
className={cx(styles.input, styles[size], error && styles.error)}
placeholder={placeholder}
ref={ref}
{...attrs}
/>

{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}

<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
</div>

{error && <p className={styles.message}>{error}</p>}
</div>

{error && <p className={styles.message}>{error}</p>}
</div>
);
}
);
},
);

export { Input };
export type { Props as InputProps };
12 changes: 6 additions & 6 deletions utils/vara-ui/src/components/radio/radio.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { InputHTMLAttributes } from 'react';
import { InputHTMLAttributes, forwardRef } from 'react';
import cx from 'clsx';
import styles from './radio.module.css';

interface Props extends InputHTMLAttributes<HTMLInputElement> {
type Props = InputHTMLAttributes<HTMLInputElement> & {
label: string;
}
};

function Radio({ label, className, ...attrs }: Props) {
const Radio = forwardRef<HTMLInputElement, Props>(({ label, className, ...attrs }, ref) => {
const { disabled } = attrs;

return (
<label className={cx(styles.label, className, disabled && styles.disabled)}>
<input type="radio" className={styles.input} {...attrs} />
<input type="radio" className={styles.input} ref={ref} {...attrs} />

{label}
</label>
);
}
});

export { Radio };
export type { Props as RadioProps };
50 changes: 26 additions & 24 deletions utils/vara-ui/src/components/select/select.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SelectHTMLAttributes, OptionHTMLAttributes, ReactNode, useId } from 'react';
import { SelectHTMLAttributes, OptionHTMLAttributes, ReactNode, useId, forwardRef } from 'react';
import cx from 'clsx';
import styles from './select.module.css';

Expand All @@ -9,35 +9,37 @@ type Props = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'id' | 'size'> & {
error?: ReactNode;
};

function Select({ options, className, label, error, size = 'default', ...attrs }: Props) {
const { disabled } = attrs;
const Select = forwardRef<HTMLSelectElement, Props>(
({ options, className, label, error, size = 'default', ...attrs }, ref) => {
const { disabled } = attrs;

const id = useId();
const id = useId();

const getOptions = () => options.map((option, index) => <option key={index} {...option} />);
const getOptions = () => options.map((option, index) => <option key={index} {...option} />);

return (
<div className={cx(styles.root, className, disabled && styles.disabled)}>
<div className={styles.base}>
<select id={id} className={cx(styles.select, styles[size], error && styles.error)} {...attrs}>
{getOptions()}
</select>
return (
<div className={cx(styles.root, className, disabled && styles.disabled)}>
<div className={styles.base}>
<select id={id} className={cx(styles.select, styles[size], error && styles.error)} ref={ref} {...attrs}>
{getOptions()}
</select>

{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}
{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}

<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
</div>
<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
</div>

{error && <p className={styles.message}>{error}</p>}
</div>
);
}
{error && <p className={styles.message}>{error}</p>}
</div>
);
},
);

export { Select };
export type { Props as SelectProps };
9 changes: 8 additions & 1 deletion utils/vara-ui/src/components/textarea/textarea.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,17 @@

.label {
position: absolute;
top: 8px;
left: 13px;

z-index: -1;

&.default {
top: 13px;
}

&.small {
top: 8px;
}
}

.fieldset {
Expand Down
65 changes: 34 additions & 31 deletions utils/vara-ui/src/components/textarea/textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TextareaHTMLAttributes, ReactNode, useId } from 'react';
import { TextareaHTMLAttributes, ReactNode, useId, forwardRef } from 'react';
import cx from 'clsx';
import styles from './textarea.module.css';

Expand All @@ -8,37 +8,40 @@ type Props = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'id' | 'size'> &
error?: ReactNode;
};

function Textarea({ className, label, error, size = 'default', rows = 5, placeholder = ' ', ...attrs }: Props) {
const { disabled } = attrs;

const id = useId();

return (
<div className={cx(styles.root, className, disabled && styles.disabled)}>
<div className={styles.base}>
<textarea
rows={rows}
id={id}
className={cx(styles.textarea, styles[size], error && styles.error)}
placeholder={placeholder}
{...attrs}
/>

{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}

<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
const Textarea = forwardRef<HTMLTextAreaElement, Props>(
({ className, label, error, size = 'default', rows = 5, placeholder = ' ', ...attrs }, ref) => {
const { disabled } = attrs;

const id = useId();

return (
<div className={cx(styles.root, className, disabled && styles.disabled)}>
<div className={styles.base}>
<textarea
rows={rows}
id={id}
className={cx(styles.textarea, styles[size], error && styles.error)}
placeholder={placeholder}
ref={ref}
{...attrs}
/>

{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}

<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
</div>

{error && <p className={styles.message}>{error}</p>}
</div>

{error && <p className={styles.message}>{error}</p>}
</div>
);
}
);
},
);

export { Textarea };
export type { Props as TextareaProps };

0 comments on commit 2c8ffec

Please sign in to comment.