From 49809f88a3bda065b69dbd89c82bb811f79050b9 Mon Sep 17 00:00:00 2001 From: Krotov Petr <83280920+KrotovPetr@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:26:52 +0300 Subject: [PATCH 1/4] feat: add error-placement props --- .../controls/TextArea/TextArea.scss | 26 +++++++++++- src/components/controls/TextArea/TextArea.tsx | 40 +++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) 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..7db8103901 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'; @@ -51,6 +55,7 @@ export const TextArea = React.forwardRef( hasClear = false, error, errorMessage: errorMessageProp, + errorPlacement: errorPlacementProp, validationState: validationStateProp, autoComplete, id: idProp, @@ -64,9 +69,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 +84,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 +163,7 @@ export const TextArea = React.forwardRef( state, pin: view === 'clear' ? undefined : pin, 'has-clear': isClearControlVisible, + 'has-error-icon': isErrorIconVisible, 'has-scrollbar': hasVerticalScrollbar, }, className, @@ -162,12 +172,26 @@ export const TextArea = React.forwardRef( > - {isClearControlVisible && ( - + {(isErrorIconVisible || isClearControlVisible) && ( + + {isClearControlVisible && ( + + )} + {isErrorIconVisible && ( + + + + + + )} + )} Date: Tue, 4 Nov 2025 14:37:59 +0300 Subject: [PATCH 2/4] feat: add error-placement props --- src/components/controls/TextArea/TextArea.tsx | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/controls/TextArea/TextArea.tsx b/src/components/controls/TextArea/TextArea.tsx index 7db8103901..2f76f850cf 100644 --- a/src/components/controls/TextArea/TextArea.tsx +++ b/src/components/controls/TextArea/TextArea.tsx @@ -17,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'; @@ -172,26 +177,23 @@ export const TextArea = React.forwardRef( > - {(isErrorIconVisible || isClearControlVisible) && ( - - {isClearControlVisible && ( - + )} + {isErrorIconVisible && ( + + + - )} - {isErrorIconVisible && ( - - - - - - )} - + + )} Date: Tue, 4 Nov 2025 14:47:29 +0300 Subject: [PATCH 3/4] feat: add error-placement props --- src/components/controls/TextArea/TextArea.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/controls/TextArea/TextArea.tsx b/src/components/controls/TextArea/TextArea.tsx index 2f76f850cf..2b15452c56 100644 --- a/src/components/controls/TextArea/TextArea.tsx +++ b/src/components/controls/TextArea/TextArea.tsx @@ -60,7 +60,7 @@ export const TextArea = React.forwardRef( hasClear = false, error, errorMessage: errorMessageProp, - errorPlacement: errorPlacementProp, + errorPlacement: errorPlacementProp = 'outside', validationState: validationStateProp, autoComplete, id: idProp, From 443853bf41ed29397d1ac384cdd9c32a43e21427 Mon Sep 17 00:00:00 2001 From: Petr Krotov Date: Tue, 4 Nov 2025 14:54:49 +0300 Subject: [PATCH 4/4] update test size --- .../controls/TextArea/__tests__/TextArea.visual.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/controls/TextArea/__tests__/TextArea.visual.test.tsx b/src/components/controls/TextArea/__tests__/TextArea.visual.test.tsx index 9eb38931eb..1b11bb8d46 100644 --- a/src/components/controls/TextArea/__tests__/TextArea.visual.test.tsx +++ b/src/components/controls/TextArea/__tests__/TextArea.visual.test.tsx @@ -82,6 +82,7 @@ test.describe('TextArea', {tag: '@TextArea'}, () => { await expectScreenshot({ themes: ['light'], + clip: {width: 522} }); });