From 5a717294630ed7bec9384639fe2591d315ac1469 Mon Sep 17 00:00:00 2001 From: Sam Ferrero Date: Sun, 8 Oct 2023 14:14:31 +0200 Subject: [PATCH 01/13] feat(textArea): adding textare and textarea field --- .../src/components/TextArea/CopyButton.tsx | 46 ++++++ .../src/components/TextArea/TextArea.css.ts | 151 ++++++++++++++++++ .../components/TextArea/TextArea.stories.tsx | 91 +++++++++++ .../src/components/TextArea/TextArea.test.tsx | 12 ++ .../src/components/TextArea/TextArea.tsx | 69 ++++++++ .../src/components/TextArea/TextAreaField.tsx | 26 +++ .../react-ui/src/components/TextArea/index.ts | 1 + .../components/TextAreaField/TextArea.css.ts | 151 ++++++++++++++++++ .../TextAreaField/TextArea.stories.tsx | 91 +++++++++++ .../TextAreaField/TextArea.test.tsx | 12 ++ .../src/components/TextAreaField/TextArea.tsx | 69 ++++++++ .../TextAreaField/TextAreaField.tsx | 26 +++ .../src/components/TextAreaField/index.ts | 1 + .../libs/react-ui/src/components/index.ts | 2 + .../libs/react-ui/src/styles/sprinkles.css.ts | 6 + 15 files changed, 754 insertions(+) create mode 100644 packages/libs/react-ui/src/components/TextArea/CopyButton.tsx create mode 100644 packages/libs/react-ui/src/components/TextArea/TextArea.css.ts create mode 100644 packages/libs/react-ui/src/components/TextArea/TextArea.stories.tsx create mode 100644 packages/libs/react-ui/src/components/TextArea/TextArea.test.tsx create mode 100644 packages/libs/react-ui/src/components/TextArea/TextArea.tsx create mode 100644 packages/libs/react-ui/src/components/TextArea/TextAreaField.tsx create mode 100644 packages/libs/react-ui/src/components/TextArea/index.ts create mode 100644 packages/libs/react-ui/src/components/TextAreaField/TextArea.css.ts create mode 100644 packages/libs/react-ui/src/components/TextAreaField/TextArea.stories.tsx create mode 100644 packages/libs/react-ui/src/components/TextAreaField/TextArea.test.tsx create mode 100644 packages/libs/react-ui/src/components/TextAreaField/TextArea.tsx create mode 100644 packages/libs/react-ui/src/components/TextAreaField/TextAreaField.tsx create mode 100644 packages/libs/react-ui/src/components/TextAreaField/index.ts diff --git a/packages/libs/react-ui/src/components/TextArea/CopyButton.tsx b/packages/libs/react-ui/src/components/TextArea/CopyButton.tsx new file mode 100644 index 0000000000..9962be29b4 --- /dev/null +++ b/packages/libs/react-ui/src/components/TextArea/CopyButton.tsx @@ -0,0 +1,46 @@ +import { copyButtonClass } from './TextArea.css'; + +import { IconButton } from '@components/IconButton'; +import type { FC } from 'react'; +import React, { useState } from 'react'; + +export interface ICopyButtonProps + extends Omit< + React.ButtonHTMLAttributes, + 'color' | 'className' + > { + shouldShow: boolean; + value: string; +} + +export const CopyButton: FC = ({ + shouldShow, + value, + ...restProps +}) => { + const [copied, setCopied] = useState(false); + + const handleClick = async (): Promise => { + await navigator.clipboard.writeText(value); + setCopied(true); + setTimeout(() => { + setCopied(false); + }, 1000); + }; + + return ( + shouldShow && ( + <> +
+ +
+ + ) + ); +}; diff --git a/packages/libs/react-ui/src/components/TextArea/TextArea.css.ts b/packages/libs/react-ui/src/components/TextArea/TextArea.css.ts new file mode 100644 index 0000000000..726b117973 --- /dev/null +++ b/packages/libs/react-ui/src/components/TextArea/TextArea.css.ts @@ -0,0 +1,151 @@ +import { statusColor } from '../InputWrapper/InputWrapper.css'; + +import { sprinkles } from '@theme/sprinkles.css'; +import { darkThemeClass, vars } from '@theme/vars.css'; +import { fallbackVar, style, styleVariants } 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, // NOTE: this is to override the normal bg color + }, + }, + }, +]); + +export const copyButtonClass = style([ + sprinkles({ + position: 'absolute', + top: 0, + right: '$2', + backgroundColor: 'transparent', + fontSize: '$base', + border: 'none', + padding: '$1', + cursor: 'pointer', + }), +]); + +export const inputContainerClass = style([ + sprinkles({ + position: 'relative', + alignItems: 'center', + display: 'flex', + flexGrow: 1, + gap: '$2', + lineHeight: '$lg', + paddingX: '$4', + }), +]); + +export const inputClass = 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 leadingTextClass = style([ + sprinkles({ + overflow: 'hidden', + display: 'inline-block', + minWidth: 0, + alignItems: 'center', + paddingX: '$4', + }), + { + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, +]); + +export const leadingTextWrapperClass = style([ + sprinkles({ + backgroundColor: { + lightMode: '$gray20', + darkMode: '$gray60', + }, + display: 'flex', + alignItems: 'center', + }), +]); + +export const leadingTextWidthVariant = styleVariants(vars.sizes, (size) => { + return { + width: size, + }; +}); + +export const outlinedClass = style([ + sprinkles({ + borderRadius: '$sm', + }), + { + border: `1px solid ${vars.colors.$neutral3}`, + }, +]); diff --git a/packages/libs/react-ui/src/components/TextArea/TextArea.stories.tsx b/packages/libs/react-ui/src/components/TextArea/TextArea.stories.tsx new file mode 100644 index 0000000000..1247a73d39 --- /dev/null +++ b/packages/libs/react-ui/src/components/TextArea/TextArea.stories.tsx @@ -0,0 +1,91 @@ +import { TextAreaField } from './TextAreaField'; + +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 from 'react'; + +const meta: Meta = { + 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' }, + }, + }, + hasCopyButton: { + description: + 'Icon rendered inside the textArea to the right of the input text.', + 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, + hasCopyButton: true, + fontFamily: '$mono', + }, + render: ({ disabled, hasCopyButton, fontFamily }) => ( +