diff --git a/packages/react-components/src/components/AutoComplete/types.ts b/packages/react-components/src/components/AutoComplete/types.ts index f41974c51..9dfc63a97 100644 --- a/packages/react-components/src/components/AutoComplete/types.ts +++ b/packages/react-components/src/components/AutoComplete/types.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { Placement, Strategy } from '@floating-ui/react'; -import { InputProps } from '../Input'; +import { IInputProps } from '../Input/types'; import { IPickerListItem } from '../Picker'; // selectedItemBody is unnecessary for AutoCompleteListItem, key should be === name @@ -13,7 +13,7 @@ export type IAutoCompleteListItem = Omit< customElement?: React.ReactElement; }; -export interface AutoCompleteProps extends Omit { +export interface AutoCompleteProps extends Omit { /** Options that will be displayed in the picker. If they are strings, they will be converted to `IPickerListItem[]`*/ options?: string[] | IAutoCompleteListItem[]; /** If true, disables filtering of the options. Useful for getting options from an external source.*/ diff --git a/packages/react-components/src/components/FormField/FormField.stories.tsx b/packages/react-components/src/components/FormField/FormField.stories.tsx index ccde9687a..3b83676e3 100644 --- a/packages/react-components/src/components/FormField/FormField.stories.tsx +++ b/packages/react-components/src/components/FormField/FormField.stories.tsx @@ -2,7 +2,8 @@ import { Info } from '@livechat/design-system-icons'; import { Meta, StoryFn } from '@storybook/react'; import { Icon } from '../Icon'; -import { Input, InputProps } from '../Input'; +import { Input } from '../Input'; +import { IInputProps } from '../Input/types'; import { FormField as FormFieldComponent, FormFieldProps } from './FormField'; @@ -26,7 +27,7 @@ export default { } as Meta; const ExampleIcon = () => ; -const ExampleInput = ({ ...args }: InputProps) => ( +const ExampleInput = ({ ...args }: IInputProps) => ( ); const LabelText = 'Email'; diff --git a/packages/react-components/src/components/Input/Input.module.scss b/packages/react-components/src/components/Input/Input.module.scss index 5222671b2..1fc294b39 100644 --- a/packages/react-components/src/components/Input/Input.module.scss +++ b/packages/react-components/src/components/Input/Input.module.scss @@ -1,7 +1,12 @@ -.input { +$base-class: 'input'; + +.#{$base-class} { box-sizing: border-box; display: flex; align-items: center; + transition: + border-color var(--transition-duration-fast-2) ease, + background-color var(--transition-duration-fast-2) ease; outline: none; border: 1px solid var(--border-basic-primary); border-radius: var(--radius-3); @@ -29,6 +34,21 @@ color: var(--content-basic-disabled); } + &--promo { + border: 2px solid var(--input-promo-border-default); + padding: 0 var(--spacing-4); + height: 52px; + + &:hover { + border-color: var(--input-promo-border-hover); + } + + &.#{$base-class}--focused, + &.#{$base-class}--focused:hover { + box-shadow: var(--state-active-field); + } + } + &--focused, &--focused:hover { border-color: var(--action-primary-default); diff --git a/packages/react-components/src/components/Input/Input.spec.tsx b/packages/react-components/src/components/Input/Input.spec.tsx index 83d86807b..f0254b3a1 100644 --- a/packages/react-components/src/components/Input/Input.spec.tsx +++ b/packages/react-components/src/components/Input/Input.spec.tsx @@ -6,9 +6,10 @@ import { render, userEvent, vi } from 'test-utils'; import { Icon } from '../Icon'; -import { Input, InputProps } from './Input'; +import { Input } from './Input'; +import { IInputProps } from './types'; -const renderComponent = (props: InputProps) => +const renderComponent = (props: IInputProps) => render(); describe(' component', () => { diff --git a/packages/react-components/src/components/Input/Input.stories.tsx b/packages/react-components/src/components/Input/Input.stories.tsx index 45f7f8c5b..b2a01c16b 100644 --- a/packages/react-components/src/components/Input/Input.stories.tsx +++ b/packages/react-components/src/components/Input/Input.stories.tsx @@ -6,7 +6,8 @@ import { Meta, StoryFn } from '@storybook/react'; import { StoryDescriptor } from '../../stories/components/StoryDescriptor'; import { Icon } from '../Icon'; -import { Input, InputProps } from './Input'; +import { Input, InputPromo as InputPromoComponent } from './Input'; +import { IInputProps } from './types'; const placeholderText = 'Placeholder text'; @@ -27,7 +28,7 @@ export default { }, } as Meta; -export const Default: StoryFn = (args: InputProps) => ( +export const Default: StoryFn = (args: IInputProps) => ( ); @@ -121,3 +122,79 @@ export const WithIcons = (): React.ReactElement => ( ); + +export const InputPromo = (): React.ReactElement => ( + +); + +export const InputPromoStates = (): React.ReactElement => ( + <> + + + + + + + +); + +export const InputPromoTypes = (): React.ReactElement => ( + <> + + + + + + + +); + +export const InputPromoWithIcons = (): React.ReactElement => ( + <> + + , + place: 'left', + }} + placeholder={placeholderText} + /> + + + , + place: 'right', + }} + placeholder={placeholderText} + /> + + + , + place: 'left', + }} + placeholder={placeholderText} + type="password" + /> + + + , + place: 'left', + }} + placeholder={placeholderText} + disabled + /> + + + + + +); diff --git a/packages/react-components/src/components/Input/Input.tsx b/packages/react-components/src/components/Input/Input.tsx index 5a116aae3..d9c9a3126 100644 --- a/packages/react-components/src/components/Input/Input.tsx +++ b/packages/react-components/src/components/Input/Input.tsx @@ -10,40 +10,18 @@ import { Button } from '../Button'; import { Icon } from '../Icon'; import { Text } from '../Typography'; -import styles from './Input.module.scss'; - -interface InputIcon { - source: React.ReactElement; - place: 'left' | 'right'; -} +import { + IInputComponentProps, + IInputIcon, + IInputPromoProps, + IInputProps, +} from './types'; -export interface InputProps - extends React.InputHTMLAttributes { - /** - * Specify the input size - */ - inputSize?: 'xsmall' | 'compact' | 'medium' | 'large'; - /** - * Specify whether the input should be in error state - */ - error?: boolean; - /** - * Specify whether the input should be disabled - */ - disabled?: boolean; - /** - * Set the icon and its position - */ - icon?: InputIcon; - /** - * Set to enable ellipsis - */ - cropOnBlur?: boolean; -} +import styles from './Input.module.scss'; const baseClass = 'input'; -const renderIcon = (icon: InputIcon, disabled?: boolean) => +const renderIcon = (icon: IInputIcon, disabled?: boolean) => React.cloneElement(icon.source, { ['data-testid']: `input-icon-${icon.place}`, className: cx( @@ -55,14 +33,18 @@ const renderIcon = (icon: InputIcon, disabled?: boolean) => ), }); -export const Input = React.forwardRef( +export const InputComponent = React.forwardRef< + HTMLInputElement, + IInputComponentProps +>( ( { - inputSize = 'medium', error = false, disabled, icon = null, className, + mainClassName, + isPromo = false, cropOnBlur = true, ...inputProps }, @@ -75,16 +57,15 @@ export const Input = React.forwardRef( const [isPasswordVisible, setIsPasswordVisible] = React.useState(false); const { type, onFocus, onBlur } = inputProps; const mergedClassNames = cx( - className, - styles[baseClass], - styles[`${baseClass}--${inputSize}`], + mainClassName, { [styles[`${baseClass}--disabled`]]: disabled, [styles[`${baseClass}--focused`]]: isFocused, [styles[`${baseClass}--error`]]: error, [styles[`${baseClass}--crop`]]: cropOnBlur, [styles[`${baseClass}--read-only`]]: inputProps.readOnly, - } + }, + className ); const iconCustomColor = !disabled ? 'var(--content-default)' @@ -101,6 +82,7 @@ export const Input = React.forwardRef( return ( ( ); } ); + +export const Input = React.forwardRef( + ({ inputSize = 'medium', ...props }, ref) => { + const mainClassName = cx( + styles[baseClass], + styles[`${baseClass}--${inputSize}`] + ); + + return ( + + ); + } +); + +const promoBaseClass = `${baseClass}--promo`; + +export const InputPromo = React.forwardRef( + (props, ref) => { + const mainClassName = cx(styles[baseClass], styles[promoBaseClass]); + + return ( + + ); + } +); diff --git a/packages/react-components/src/components/Input/types.ts b/packages/react-components/src/components/Input/types.ts new file mode 100644 index 000000000..17b2b3e3b --- /dev/null +++ b/packages/react-components/src/components/Input/types.ts @@ -0,0 +1,46 @@ +import * as React from 'react'; + +export interface IInputIcon { + source: React.ReactElement; + place: 'left' | 'right'; +} + +export interface IInputGlobalProps + extends React.InputHTMLAttributes { + /** + * Specify whether the input should be in error state + */ + error?: boolean; + /** + * Specify whether the input should be disabled + */ + disabled?: boolean; + /** + * Set the icon and its position + */ + icon?: IInputIcon; + /** + * Set to enable ellipsis + */ + cropOnBlur?: boolean; +} + +export interface IInputProps extends IInputGlobalProps { + /** + * Specify the input size + */ + inputSize?: 'xsmall' | 'compact' | 'medium' | 'large'; +} + +export interface IInputPromoProps extends IInputGlobalProps {} + +export interface IInputComponentProps extends IInputGlobalProps { + /** + * CSS class name for the main input wrapper + */ + mainClassName: string; + /** + * Set to display promo input + */ + isPromo?: boolean; +} diff --git a/packages/react-components/src/foundations/color-scheme.css b/packages/react-components/src/foundations/color-scheme.css index c12a2332e..82c906336 100644 --- a/packages/react-components/src/foundations/color-scheme.css +++ b/packages/react-components/src/foundations/color-scheme.css @@ -1,8 +1,4 @@ :root, -.lc-legacy-theme { - --color-scheme: normal; -} - .lc-light-theme { --color-scheme: normal; } diff --git a/packages/react-components/src/foundations/design-token.ts b/packages/react-components/src/foundations/design-token.ts index e3c14d068..3d500c5ee 100644 --- a/packages/react-components/src/foundations/design-token.ts +++ b/packages/react-components/src/foundations/design-token.ts @@ -383,6 +383,8 @@ export const DesignToken = { '--surface-check-list-item-open-background', SurfaceCheckListBackground: '--surface-check-list-background', ContentBasicPlaceholder: '--content-basic-placeholder', + InputPromoBorderDefault: '--input-promo-border-default', + InputPromoBorderHover: '--input-promo-border-hover', }; export type DesignTokenKey = keyof typeof DesignToken; diff --git a/packages/react-components/src/foundations/shadow.css b/packages/react-components/src/foundations/shadow.css index 9bc662db3..9a2048a2e 100644 --- a/packages/react-components/src/foundations/shadow.css +++ b/packages/react-components/src/foundations/shadow.css @@ -1,31 +1,4 @@ :root, -.lc-legacy-theme { - --shadow-float: 0 2px 6px 0 rgb(66 77 87 / 40%); - --shadow-pop-over: 0 6px 20px 0 rgb(66 77 87 / 40%); - --shadow-modal: 0 10px 50px 0 rgb(66 77 87 / 40%); - --shadow-tooltip: 0 1px 10px 0 rgb(66 77 87 / 30%); - --shadow-tooltip-arrow-bottom: 8px -6px 10px -2px rgb(66 77 87 / 30%); - --shadow-tooltip-arrow-top: -7px 6px 5px -3px rgb(66 77 87 / 30%); - --shadow-tooltip-arrow-right: -5px -5px 10px -2px rgb(66 77 87 / 30%); - --shadow-tooltip-arrow-left: 6px 6px 10px -2px rgb(66 77 87 / 30%); - --shadow-tooltip-arrow: -2px -2px 3px -2px rgb(66 77 87 / 10%); - --shadow-focus: 0 0 1px 1px var(--background-01), 0 0 1px 3px #4284f5; - --shadow-divider-bottom: 0 -1px 0 0 #eeeeef inset; - --shadow-divider-top: 0 1px 0 0 #eeeeef inset; - --shadow-divider-right: -1px 0 0 0 #eeeeef inset; - --shadow-divider-left: 1px 0 0 0 #eeeeef inset; - --shadow-divider-bottom-left: 1px -1px 0 0 #eeeeef inset; - --shadow-divider-top-left: 1px 1px 0 0 #eeeeef inset; - --shadow-divider-top-right: -1px 1px 0 0 #eeeeef inset; - --shadow-divider-bottom-right: -1px -1px 0 0 #eeeeef inset; - --shadow-message-box: 0 0 8px 0 rgb(19 19 23 / 12%); - --shadow-fixed-right: 2px 0 10px rgb(19 19 23 / 18%); - --shadow-fixed-left: -2px 0 10px rgb(19 19 23 / 18%); - --shadow-fixed-top: 0 -2px 10px rgb(19 19 23 / 18%); - --shadow-fixed-bottom: 0 2px 10px rgb(19 19 23 / 18%); - --focus-ring-inner: inset 0 0 1px 2px var(--action-primary-default); -} - .lc-light-theme { --shadow-float: 0 2px 6px 0 rgb(19 19 23 / 20%); --shadow-pop-over: 0 6px 20px 0 rgb(19 19 23 / 30%); @@ -51,6 +24,7 @@ --shadow-fixed-top: 0 -2px 10px rgb(19 19 23 / 18%); --shadow-fixed-bottom: 0 2px 10px rgb(19 19 23 / 18%); --focus-ring-inner: inset 0 0 1px 2px var(--action-primary-default); + --state-active-field: 0 0 0 4px rgb(0 89 225 / 15%); } .lc-dark-theme { @@ -81,4 +55,5 @@ --shadow-fixed-top: 0 -2px 10px rgb(19 19 23 / 18%); --shadow-fixed-bottom: 0 2px 10px rgb(19 19 23 / 18%); --focus-ring-inner: inset 0 0 1px 2px var(--action-primary-default); + --state-active-field: 0 0 0 4px rgb(104 175 255 / 25%); } diff --git a/packages/react-components/src/stories/foundations/Color/data.ts b/packages/react-components/src/stories/foundations/Color/data.ts index e97a5819f..563990e2f 100644 --- a/packages/react-components/src/stories/foundations/Color/data.ts +++ b/packages/react-components/src/stories/foundations/Color/data.ts @@ -1476,6 +1476,16 @@ export const ColorsData: Record< desc: '', deprecated: false, }, + InputPromoBorderDefault: { + group: ColorGroup.BorderBasic, + desc: '', + deprecated: false, + }, + InputPromoBorderHover: { + group: ColorGroup.BorderBasic, + desc: '', + deprecated: false, + }, AnimatedGradientValue1: { group: ColorGroup.AnimationGradient, desc: 'Part of the gradient used for animation in Skeleton component', diff --git a/packages/react-components/src/themes/dark.scss b/packages/react-components/src/themes/dark.scss index 1cf2c8f0c..d2083b156 100644 --- a/packages/react-components/src/themes/dark.scss +++ b/packages/react-components/src/themes/dark.scss @@ -395,4 +395,6 @@ $surface-invert-primary: #e2e2e4; --surface-check-list-item-open-background: #{$surface-secondary-active}; --surface-check-list-background: #202024; --content-basic-placeholder: hsl(240deg 6% 97% / 59%); + --input-promo-border-default: #62626d; + --input-promo-border-hover: #8d8d95; } diff --git a/packages/react-components/src/themes/light.scss b/packages/react-components/src/themes/light.scss index 598a815b7..6b973f4ed 100644 --- a/packages/react-components/src/themes/light.scss +++ b/packages/react-components/src/themes/light.scss @@ -212,7 +212,7 @@ $content-basic-internal-note: #3a310c; --action-neutral-hover: #68686f; --action-neutral-disabled: rgb(141 141 149 / 20%); --action-high-contrast-default: #1b1b20; - --action-high-contrast-hover: #{$decor-gray600}; + --action-high-contrast-hover: #43434b; --action-high-contrast-active: #131317; --action-high-contrast-disabled: rgb(27 27 32 / 20%); --color-bot: #50009c; // deprecated @@ -397,4 +397,6 @@ $content-basic-internal-note: #3a310c; --surface-check-list-item-open-background: #fff; --surface-check-list-background: #{$surface-secondary-default}; --content-basic-placeholder: hsl(240deg 10% 8% / 59%); + --input-promo-border-default: #8d8d95; + --input-promo-border-hover: #62626d; }