Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(@kadena/react-ui): added textarea and textareafield to the react ui lib #1012

Merged
merged 14 commits into from
Oct 16, 2023
5 changes: 5 additions & 0 deletions .changeset/tiny-weeks-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@kadena/react-ui': minor
---

Added the TextArea and TextAreaField components
12 changes: 0 additions & 12 deletions packages/libs/react-ui/src/components/Input/Input.test.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,17 @@ export const InputWrapper: FC<IInputWrapperProps> = ({
<InputHeader htmlFor={htmlFor} label={label} tag={tag} info={info} />
)}
<div className="inputGroup">
{React.Children.map(children, (child) => {
if (!React.isValidElement(child)) return null;
const props = {
...child.props,
leadingTextWidth,
};
{leadingTextWidth
? React.Children.map(children, (child) => {
if (!React.isValidElement(child)) return null;
const props = {
...child.props,
leadingTextWidth,
};

return React.cloneElement(child, props);
})}
return React.cloneElement(child, props);
ferreroltd marked this conversation as resolved.
Show resolved Hide resolved
})
: children}
</div>
{Boolean(helperText) && <InputHelper>{helperText}</InputHelper>}
</div>
Expand Down
107 changes: 107 additions & 0 deletions packages/libs/react-ui/src/components/TextArea/TextArea.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { statusColor } from '../InputWrapper/InputWrapper.css';

import { sprinkles } from '@theme/sprinkles.css';
import { darkThemeClass, vars } from '@theme/vars.css';
import { fallbackVar, style } from '@vanilla-extract/css';

export const containerClass = style([
sprinkles({
alignItems: 'stretch',
borderRadius: '$sm',
display: 'flex',
color: '$foreground',
overflow: 'hidden',
lineHeight: '$lg',
bg: {
lightMode: '$white',
darkMode: '$gray100',
},
}),
{
position: 'relative',
borderBottom: `1px solid ${fallbackVar(statusColor, vars.colors.$gray30)}`,
selectors: {
[`${darkThemeClass} &`]: {
borderBottom: `1px solid ${fallbackVar(
statusColor,
vars.colors.$gray60,
)}`,
},
'.inputGroup &': {
borderRadius: 0,
},
'.inputGroup &:first-child': {
borderTopRightRadius: vars.radii.$sm,
borderTopLeftRadius: vars.radii.$sm,
},
'.inputGroup &:last-child': {
borderBottomRightRadius: vars.radii.$sm,
borderBottomLeftRadius: vars.radii.$sm,
},
},
},
]);

export const disabledClass = style([
sprinkles({
pointerEvents: 'none',
bg: {
darkMode: '$gray60',
lightMode: '$gray20',
},
}),
{
opacity: 0.4,
selectors: {
'.inputGroup &': {
opacity: 1,
},
[`${darkThemeClass} &`]: {
backgroundColor: vars.colors.$gray60,
},
},
},
]);

export const textAreaContainerClass = style([
sprinkles({
position: 'relative',
alignItems: 'center',
display: 'flex',
flexGrow: 1,
gap: '$2',
lineHeight: '$lg',
paddingX: '$4',
}),
]);

export const textAreaClass = style([
sprinkles({
alignItems: 'center',
background: 'none',
border: 'none',
color: '$foreground',
outline: 'none',
flexGrow: 1,
paddingY: '$2',
minHeight: '$20',
}),
{
resize: 'none',
'::placeholder': {
color: vars.colors.$gray40,
},
[`${darkThemeClass} &::placeholder`]: {
color: vars.colors.$gray50,
},
},
]);

export const outlinedClass = style([
sprinkles({
borderRadius: '$sm',
}),
{
border: `1px solid ${vars.colors.$neutral3}`,
},
]);
ferreroltd marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { SystemIcon } from '@components/Icon';
import type { ITextareaProps } from '@components/TextArea';
import { Textarea } from '@components/TextArea';
import type { Meta, StoryObj } from '@storybook/react';
import React, { useState } from 'react';

const meta: Meta<ITextareaProps> = {
title: 'Form/TextArea',
component: Textarea,
parameters: {
docs: {
description: {
component:
'The TextArea component is a wrapper around the native textArea element that provides the ability to add additional information.',
},
},
},
argTypes: {
disabled: {
description: 'Disables the textArea and applies visual styling.',
control: {
type: 'boolean',
},
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
},
};

export default meta;

type Story = StoryObj<
{
leadingText: string;
rightIcon: keyof typeof SystemIcon;
type: React.HTMLInputTypeAttribute;
} & ITextareaProps
>;

export const TextAreaStory: Story = {
name: 'TextArea config',
args: {
disabled: false,
fontFamily: '$mono',
outlined: true,
},
render: (props) => {
const [value, setValue] = useState<string>('');

return (
<Textarea
{...props}
value={value}
onChange={({ target }) => setValue(target.value)}
id="inlineInputStory"
placeholder="This is a placeholder"
/>
);
},
};
48 changes: 48 additions & 0 deletions packages/libs/react-ui/src/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
containerClass,
disabledClass,
outlinedClass,
textAreaClass,
textAreaContainerClass,
} from './TextArea.css';

import type { Sprinkles } from '@theme/sprinkles.css';
import { sprinkles } from '@theme/sprinkles.css';
import classNames from 'classnames';
import type { FC, TextareaHTMLAttributes } from 'react';
import React, { forwardRef } from 'react';

export interface ITextareaProps
extends Omit<
TextareaHTMLAttributes<HTMLTextAreaElement>,
'as' | 'disabled' | 'children' | 'className' | 'id'
>,
Partial<Pick<Sprinkles, 'fontFamily'>> {
id: string;
disabled?: boolean;
ref?: React.ForwardedRef<HTMLTextAreaElement>;
outlined?: boolean;
}

export const Textarea: FC<ITextareaProps> = forwardRef<
HTMLTextAreaElement,
ITextareaProps
>(function TextArea({ outlined, disabled = false, fontFamily, ...rest }, ref) {
return (
<div
className={classNames(containerClass, {
[outlinedClass]: outlined,
[disabledClass]: disabled,
})}
>
<div className={textAreaContainerClass}>
<textarea
ref={ref}
className={classNames(textAreaClass, sprinkles({ fontFamily }))}
disabled={disabled}
{...rest}
/>
</div>
</div>
);
});
1 change: 1 addition & 0 deletions packages/libs/react-ui/src/components/TextArea/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { type ITextareaProps, Textarea } from './TextArea';
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { ITextAreaFieldProps } from '@components/TextAreaField';
import { TextAreaField } from '@components/TextAreaField';
import type { Meta, StoryObj } from '@storybook/react';
import React, { useState } from 'react';

const meta: Meta<ITextAreaFieldProps> = {
title: 'Form/TextAreaField',
component: TextAreaField,
parameters: {
docs: {
description: {
component:
'The TextAreaField component is a wrapper around the native textarea element that provides the ability to add additional information.',
},
},
},
argTypes: {
disabled: {
description: 'Disables the input and applies visual styling.',
control: {
type: 'boolean',
},
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},

textAreaProps: {
description: 'Props for the textarea element.',
control: {
type: 'object',
},
table: {
type: { summary: 'object' },
defaultValue: { summary: 'false' },
},
},
},
};

export default meta;

type Story = StoryObj<ITextAreaFieldProps>;

export const TextFieldStory: Story = {
name: 'TextField config',
args: {
disabled: false,
tag: 'tag',
helperText: 'This is helper text',
info: '(optional)',
label: 'Label',
textAreaProps: {
id: 'TextFieldStory',
fontFamily: '$mono',
placeholder: 'This is a placeholder',
value: '',
onChange: () => {},
},
},
render: ({ disabled, textAreaProps, ...rest }) => {
const [value, setValue] = useState<string>('');

return (
<TextAreaField
disabled={disabled}
textAreaProps={{
...textAreaProps,
value,
onChange: ({ target }) => setValue(target.value),
}}
{...rest}
/>
);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { IInputWrapperProps } from '@components/InputWrapper';
import { InputWrapper } from '@components/InputWrapper';
import type { ITextareaProps } from '@components/TextArea';
import { Textarea } from '@components/TextArea';
import type { FC } from 'react';
import React from 'react';

export interface ITextAreaFieldProps
extends Omit<IInputWrapperProps, 'htmlFor' | 'children'> {
textAreaProps: Omit<ITextareaProps, 'disabled'>;
}

export const TextAreaField: FC<ITextAreaFieldProps> = ({
disabled = false,
textAreaProps,
...rest
}) => {
const { id } = textAreaProps;

return (
<InputWrapper htmlFor={id} disabled={disabled} {...rest}>
<Textarea disabled={disabled} {...textAreaProps} />
ferreroltd marked this conversation as resolved.
Show resolved Hide resolved
</InputWrapper>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { type ITextAreaFieldProps, TextAreaField } from './TextAreaField';
Loading