diff --git a/src/components/controls/TextArea/TextArea.scss b/src/components/controls/TextArea/TextArea.scss index 72dc5a1ca7..519de7ccc0 100644 --- a/src/components/controls/TextArea/TextArea.scss +++ b/src/components/controls/TextArea/TextArea.scss @@ -77,7 +77,7 @@ $block: '.#{variables.$ns}text-area'; } &__clear { - position: absolute; + flex-shrink: 0; &_size_s, &_size_m { @@ -92,6 +92,14 @@ $block: '.#{variables.$ns}text-area'; } } + &__error-icon { + box-sizing: content-box; + color: var(--g-color-text-danger); + padding-block: var(--_--error-icon-padding-block); + padding-inline: var(--_--error-icon-padding-inline-start) + var(--_--error-icon-padding-inline-end); + } + &_size { &_s { #{$block}__control { @@ -102,6 +110,10 @@ $block: '.#{variables.$ns}text-area'; padding-inline-end: 26px; } + --_--error-icon-padding-block: 5px; + --_--error-icon-padding-inline-start: 0; + --_--error-icon-padding-inline-end: 5px; + --_--border-radius: var(--g-border-radius-s); } @@ -114,6 +126,10 @@ $block: '.#{variables.$ns}text-area'; padding-inline-end: 26px; } + --_--error-icon-padding-block: 5px; + --_--error-icon-padding-inline-start: 0; + --_--error-icon-padding-inline-end: 5px; + --_--border-radius: var(--g-border-radius-m); } @@ -126,6 +142,10 @@ $block: '.#{variables.$ns}text-area'; padding-inline-end: 36px; } + --_--error-icon-padding-block: 9px; + --_--error-icon-padding-inline-start: 0; + --_--error-icon-padding-inline-end: 9px; + --_--border-radius: var(--g-border-radius-l); } @@ -138,6 +158,10 @@ $block: '.#{variables.$ns}text-area'; padding-inline-end: 36px; } + --_--error-icon-padding-block: 13px; + --_--error-icon-padding-inline-start: 0; + --_--error-icon-padding-inline-end: 13px; + --_--border-radius: var(--g-border-radius-xl); } } diff --git a/src/components/controls/TextArea/TextArea.tsx b/src/components/controls/TextArea/TextArea.tsx index 7de57cc0db..2b15452c56 100644 --- a/src/components/controls/TextArea/TextArea.tsx +++ b/src/components/controls/TextArea/TextArea.tsx @@ -2,8 +2,12 @@ import * as React from 'react'; +import {TriangleExclamation} from '@gravity-ui/icons'; + import {useControlledState, useForkRef, useUniqId} from '../../../hooks'; import {useFormResetHandler} from '../../../hooks/private'; +import {Icon} from '../../Icon'; +import {Popover} from '../../legacy'; import {block} from '../../utils/cn'; import {ClearButton, mapTextInputSizeToButtonSize} from '../common'; import {OuterAdditionalContent} from '../common/OuterAdditionalContent/OuterAdditionalContent'; @@ -13,7 +17,12 @@ import type { InputControlSize, InputControlView, } from '../types'; -import {errorPropsMapper, getInputControlState, prepareAutoComplete} from '../utils'; +import { + CONTROL_ERROR_ICON_QA, + errorPropsMapper, + getInputControlState, + prepareAutoComplete, +} from '../utils'; import {TextAreaControl} from './TextAreaControl'; @@ -51,6 +60,7 @@ export const TextArea = React.forwardRef( hasClear = false, error, errorMessage: errorMessageProp, + errorPlacement: errorPlacementProp = 'outside', validationState: validationStateProp, autoComplete, id: idProp, @@ -64,9 +74,10 @@ export const TextArea = React.forwardRef( onChange, } = props; - const {errorMessage, validationState} = errorPropsMapper({ + const {errorMessage, errorPlacement, validationState} = errorPropsMapper({ error, errorMessage: errorMessageProp, + errorPlacement: errorPlacementProp, validationState: validationStateProp, }); @@ -78,7 +89,10 @@ export const TextArea = React.forwardRef( const state = getInputControlState(validationState); const innerId = useUniqId(); - const isErrorMsgVisible = validationState === 'invalid' && Boolean(errorMessage); + const isErrorMsgVisible = + validationState === 'invalid' && Boolean(errorMessage) && errorPlacement === 'outside'; + const isErrorIconVisible = + validationState === 'invalid' && Boolean(errorMessage) && errorPlacement === 'inside'; const isClearControlVisible = Boolean(hasClear && !disabled && !readOnly && inputValue); const id = idProp || innerId; @@ -154,6 +168,7 @@ export const TextArea = React.forwardRef( state, pin: view === 'clear' ? undefined : pin, 'has-clear': isClearControlVisible, + 'has-error-icon': isErrorIconVisible, 'has-scrollbar': hasVerticalScrollbar, }, className, @@ -164,11 +179,22 @@ export const TextArea = React.forwardRef( {isClearControlVisible && ( )} + {isErrorIconVisible && ( + + + + + + )} { await expectScreenshot({ themes: ['light'], + clip: {width: 522} }); });