@elementor/editor-variables 3.33.0-141 → 3.33.0-143

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-variables",
3
- "version": "3.33.0-141",
3
+ "version": "3.33.0-143",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,19 +39,19 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "3.33.0-141",
43
- "@elementor/editor-canvas": "3.33.0-141",
44
- "@elementor/editor-controls": "3.33.0-141",
45
- "@elementor/editor-current-user": "3.33.0-141",
46
- "@elementor/editor-editing-panel": "3.33.0-141",
47
- "@elementor/editor-panels": "3.33.0-141",
48
- "@elementor/editor-props": "3.33.0-141",
49
- "@elementor/editor-ui": "3.33.0-141",
50
- "@elementor/editor-v1-adapters": "3.33.0-141",
51
- "@elementor/http-client": "3.33.0-141",
42
+ "@elementor/editor": "3.33.0-143",
43
+ "@elementor/editor-canvas": "3.33.0-143",
44
+ "@elementor/editor-controls": "3.33.0-143",
45
+ "@elementor/editor-current-user": "3.33.0-143",
46
+ "@elementor/editor-editing-panel": "3.33.0-143",
47
+ "@elementor/editor-panels": "3.33.0-143",
48
+ "@elementor/editor-props": "3.33.0-143",
49
+ "@elementor/editor-ui": "3.33.0-143",
50
+ "@elementor/editor-v1-adapters": "3.33.0-143",
51
+ "@elementor/http-client": "3.33.0-143",
52
52
  "@elementor/icons": "1.46.0",
53
- "@elementor/mixpanel": "3.33.0-141",
54
- "@elementor/schema": "3.33.0-141",
53
+ "@elementor/mixpanel": "3.33.0-143",
54
+ "@elementor/schema": "3.33.0-143",
55
55
  "@elementor/ui": "1.36.12",
56
56
  "@wordpress/i18n": "^5.13.0"
57
57
  },
@@ -1,8 +1,9 @@
1
1
  import * as React from 'react';
2
- import { useState } from 'react';
2
+ import { useRef, useState } from 'react';
3
+ import { WarningInfotip } from '@elementor/editor-ui';
3
4
  import { TextField, type TextFieldProps } from '@elementor/ui';
4
5
 
5
- import { validateLabel, VARIABLE_LABEL_MAX_LENGTH } from '../../utils/validations';
6
+ import { labelHint, validateLabel, VARIABLE_LABEL_MAX_LENGTH } from '../../utils/validations';
6
7
  function isLabelEqual( a: string, b: string ) {
7
8
  return a.trim().toLowerCase() === b.trim().toLowerCase();
8
9
  }
@@ -30,6 +31,7 @@ type LabelFieldProps = {
30
31
  size?: TextFieldProps[ 'size' ];
31
32
  focusOnShow?: boolean;
32
33
  selectOnShow?: boolean;
34
+ showWarningInfotip?: boolean;
33
35
  };
34
36
 
35
37
  export const LabelField = ( {
@@ -41,9 +43,11 @@ export const LabelField = ( {
41
43
  size = 'tiny',
42
44
  focusOnShow = false,
43
45
  selectOnShow = false,
46
+ showWarningInfotip = false,
44
47
  }: LabelFieldProps ) => {
45
48
  const [ label, setLabel ] = useState( value );
46
49
  const [ errorMessage, setErrorMessage ] = useState( '' );
50
+ const fieldRef = useRef< HTMLElement >( null );
47
51
 
48
52
  const handleChange = ( newValue: string ) => {
49
53
  setLabel( newValue );
@@ -61,8 +65,11 @@ export const LabelField = ( {
61
65
  errorMsg = error.message;
62
66
  }
63
67
 
64
- return (
68
+ const hintMsg = ! errorMsg ? labelHint( label ) : '';
69
+
70
+ const textField = (
65
71
  <TextField
72
+ ref={ fieldRef }
66
73
  id={ id }
67
74
  size={ size }
68
75
  fullWidth
@@ -77,4 +84,23 @@ export const LabelField = ( {
77
84
  autoFocus={ focusOnShow }
78
85
  />
79
86
  );
87
+
88
+ if ( showWarningInfotip ) {
89
+ const tooltipWidth = Math.max( 240, fieldRef.current?.getBoundingClientRect().width ?? 240 );
90
+
91
+ return (
92
+ <WarningInfotip
93
+ open={ Boolean( errorMsg || hintMsg ) }
94
+ text={ errorMsg || hintMsg }
95
+ placement="bottom-start"
96
+ width={ tooltipWidth }
97
+ offset={ [ 0, -15 ] }
98
+ { ...( hintMsg && { hasError: false } ) }
99
+ >
100
+ { textField }
101
+ </WarningInfotip>
102
+ );
103
+ }
104
+
105
+ return textField;
80
106
  };
@@ -0,0 +1,21 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ export const useAutoEdit = () => {
4
+ const [ autoEditVariableId, setAutoEditVariableId ] = useState< string | undefined >( undefined );
5
+
6
+ const startAutoEdit = useCallback( ( variableId: string ) => {
7
+ setAutoEditVariableId( variableId );
8
+ }, [] );
9
+
10
+ const handleAutoEditComplete = useCallback( () => {
11
+ setTimeout( () => {
12
+ setAutoEditVariableId( undefined );
13
+ }, 100 );
14
+ }, [] );
15
+
16
+ return {
17
+ autoEditVariableId,
18
+ startAutoEdit,
19
+ handleAutoEditComplete,
20
+ };
21
+ };
@@ -0,0 +1,79 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { __ } from '@wordpress/i18n';
3
+
4
+ import { generateTempId } from '../../../batch-operations';
5
+ import { getVariables } from '../../../hooks/use-prop-variables';
6
+ import { service } from '../../../service';
7
+ import { type TVariablesList } from '../../../storage';
8
+ import { ERROR_MESSAGES } from '../../../utils/validations';
9
+
10
+ export const useVariablesManagerState = () => {
11
+ const [ variables, setVariables ] = useState( () => getVariables( false ) );
12
+ const [ deletedVariables, setDeletedVariables ] = useState< string[] >( [] );
13
+ const [ ids, setIds ] = useState< string[] >( () => Object.keys( getVariables( false ) ) );
14
+ const [ isDirty, setIsDirty ] = useState( false );
15
+ const [ hasValidationErrors, setHasValidationErrors ] = useState( false );
16
+
17
+ const handleOnChange = useCallback( ( newVariables: TVariablesList ) => {
18
+ setVariables( newVariables );
19
+ setIsDirty( true );
20
+ }, [] );
21
+
22
+ const createVariable = useCallback( ( type: string, defaultName: string, defaultValue: string ) => {
23
+ const newId = generateTempId();
24
+ const newVariable = {
25
+ id: newId,
26
+ label: defaultName.trim(),
27
+ value: defaultValue.trim(),
28
+ type,
29
+ };
30
+
31
+ setVariables( ( prev ) => ( { ...prev, [ newId ]: newVariable } ) );
32
+ setIds( ( prev ) => [ ...prev, newId ] );
33
+ setIsDirty( true );
34
+
35
+ return newId;
36
+ }, [] );
37
+
38
+ const handleDeleteVariable = useCallback( ( itemId: string ) => {
39
+ setDeletedVariables( ( prev ) => [ ...prev, itemId ] );
40
+ setVariables( ( prev ) => ( { ...prev, [ itemId ]: { ...prev[ itemId ], deleted: true } } ) );
41
+ setIsDirty( true );
42
+ }, [] );
43
+
44
+ const handleSave = useCallback( async (): Promise< { success: boolean; error?: string } > => {
45
+ try {
46
+ const originalVariables = getVariables( false );
47
+ const result = await service.batchSave( originalVariables, variables );
48
+
49
+ if ( result.success ) {
50
+ await service.load();
51
+ const updatedVariables = service.variables();
52
+
53
+ setVariables( updatedVariables );
54
+ setIds( Object.keys( updatedVariables ) );
55
+ setDeletedVariables( [] );
56
+ setIsDirty( false );
57
+ return { success: true };
58
+ }
59
+ throw new Error( __( 'Failed to save variables. Please try again.', 'elementor' ) );
60
+ } catch ( error ) {
61
+ const errorMessage = error instanceof Error ? error.message : ERROR_MESSAGES.UNEXPECTED_ERROR;
62
+ return { success: false, error: errorMessage };
63
+ }
64
+ }, [ variables ] );
65
+
66
+ return {
67
+ variables,
68
+ deletedVariables,
69
+ ids,
70
+ isDirty,
71
+ hasValidationErrors,
72
+ setIds,
73
+ handleOnChange,
74
+ createVariable,
75
+ handleDeleteVariable,
76
+ handleSave,
77
+ setHasValidationErrors,
78
+ };
79
+ };
@@ -1,108 +1,134 @@
1
1
  import * as React from 'react';
2
- import { useEffect, useRef, useState } from 'react';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import { ClickAwayListener, Stack } from '@elementor/ui';
4
4
 
5
5
  import { type ValueFieldProps } from '../../variables-registry/create-variable-type-registry';
6
+ import { useLabelError } from '../fields/label-field';
6
7
 
7
8
  type VariableEditableCellProps = {
8
9
  initialValue: string;
9
10
  children: React.ReactNode;
10
- editableElement: ( { value, onChange, onValidationChange }: ValueFieldProps ) => JSX.Element;
11
+ editableElement: ( { value, onChange, onValidationChange, error }: ValueFieldProps ) => JSX.Element;
11
12
  onChange: ( newValue: string ) => void;
12
13
  prefixElement?: React.ReactNode;
13
14
  autoEdit?: boolean;
14
15
  onRowRef?: ( ref: HTMLTableRowElement | null ) => void;
15
16
  onAutoEditComplete?: () => void;
17
+ fieldType?: 'label' | 'value';
16
18
  };
17
19
 
18
- export const VariableEditableCell = ( {
19
- initialValue,
20
- children,
21
- editableElement,
22
- onChange,
23
- prefixElement,
24
- autoEdit = false,
25
- onRowRef,
26
- onAutoEditComplete,
27
- }: VariableEditableCellProps ) => {
28
- const [ value, setValue ] = useState( initialValue );
29
- const [ isEditing, setIsEditing ] = useState( false );
30
-
31
- const rowRef = useRef< HTMLTableRowElement >( null );
32
-
33
- useEffect( () => {
34
- onRowRef?.( rowRef?.current );
35
- }, [ onRowRef ] );
36
-
37
- useEffect( () => {
38
- if ( autoEdit && ! isEditing ) {
39
- setIsEditing( true );
40
- onAutoEditComplete?.();
41
- }
42
- }, [ autoEdit, isEditing, onAutoEditComplete ] );
20
+ export const VariableEditableCell = React.memo(
21
+ ( {
22
+ initialValue,
23
+ children,
24
+ editableElement,
25
+ onChange,
26
+ prefixElement,
27
+ autoEdit = false,
28
+ onRowRef,
29
+ onAutoEditComplete,
30
+ fieldType,
31
+ }: VariableEditableCellProps ) => {
32
+ const [ value, setValue ] = useState( initialValue );
33
+ const [ isEditing, setIsEditing ] = useState( false );
43
34
 
44
- const handleDoubleClick = () => {
45
- setIsEditing( true );
46
- };
35
+ const { labelFieldError, setLabelFieldError } = useLabelError();
47
36
 
48
- const handleSave = () => {
49
- onChange( value );
50
- setIsEditing( false );
51
- };
37
+ const rowRef = useRef< HTMLTableRowElement >( null );
52
38
 
53
- const handleKeyDown = ( event: React.KeyboardEvent< HTMLDivElement > ) => {
54
- if ( event.key === 'Enter' ) {
55
- handleSave();
56
- } else if ( event.key === 'Escape' ) {
39
+ const handleSave = useCallback( () => {
40
+ onChange( value );
57
41
  setIsEditing( false );
58
- }
59
- if ( event.key === ' ' && ! isEditing ) {
60
- event.preventDefault();
42
+ }, [ value, onChange ] );
43
+
44
+ useEffect( () => {
45
+ onRowRef?.( rowRef?.current );
46
+ }, [ onRowRef ] );
47
+
48
+ useEffect( () => {
49
+ if ( autoEdit && ! isEditing ) {
50
+ setIsEditing( true );
51
+ onAutoEditComplete?.();
52
+ }
53
+ }, [ autoEdit, isEditing, onAutoEditComplete ] );
54
+
55
+ const handleDoubleClick = () => {
61
56
  setIsEditing( true );
62
- }
63
- };
57
+ };
64
58
 
65
- const handleChange = ( newValue: string ) => {
66
- setValue( newValue );
67
- };
59
+ const handleKeyDown = ( event: React.KeyboardEvent< HTMLDivElement > ) => {
60
+ if ( event.key === 'Enter' ) {
61
+ handleSave();
62
+ } else if ( event.key === 'Escape' ) {
63
+ setIsEditing( false );
64
+ }
65
+ if ( event.key === ' ' && ! isEditing ) {
66
+ event.preventDefault();
67
+ setIsEditing( true );
68
+ }
69
+ };
68
70
 
69
- const editableContent = editableElement( { value, onChange: handleChange } );
71
+ const handleChange = useCallback( ( newValue: string ) => {
72
+ setValue( newValue );
73
+ }, [] );
74
+
75
+ const handleValidationChange = useCallback(
76
+ ( errorMsg: string ) => {
77
+ if ( fieldType === 'label' ) {
78
+ setLabelFieldError( {
79
+ value,
80
+ message: errorMsg,
81
+ } );
82
+ }
83
+ },
84
+ [ fieldType, value, setLabelFieldError ]
85
+ );
86
+
87
+ const currentError = fieldType === 'label' ? labelFieldError : undefined;
88
+
89
+ const editableContent = editableElement( {
90
+ value,
91
+ onChange: handleChange,
92
+ onValidationChange: handleValidationChange,
93
+ error: currentError,
94
+ } );
95
+
96
+ if ( isEditing ) {
97
+ return (
98
+ <ClickAwayListener onClickAway={ handleSave }>
99
+ <Stack
100
+ ref={ rowRef }
101
+ direction="row"
102
+ alignItems="center"
103
+ gap={ 1 }
104
+ onDoubleClick={ handleDoubleClick }
105
+ onKeyDown={ handleKeyDown }
106
+ tabIndex={ 0 }
107
+ role="button"
108
+ aria-label="Double click or press Space to edit"
109
+ >
110
+ { prefixElement }
111
+ { editableContent }
112
+ </Stack>
113
+ </ClickAwayListener>
114
+ );
115
+ }
70
116
 
71
- if ( isEditing ) {
72
117
  return (
73
- <ClickAwayListener onClickAway={ handleSave }>
74
- <Stack
75
- ref={ rowRef }
76
- direction="row"
77
- alignItems="center"
78
- gap={ 1 }
79
- onDoubleClick={ handleDoubleClick }
80
- onKeyDown={ handleKeyDown }
81
- tabIndex={ 0 }
82
- role="button"
83
- aria-label="Double click or press Space to edit"
84
- >
85
- { prefixElement }
86
- { editableContent }
87
- </Stack>
88
- </ClickAwayListener>
118
+ <Stack
119
+ ref={ rowRef }
120
+ direction="row"
121
+ alignItems="center"
122
+ gap={ 1 }
123
+ onDoubleClick={ handleDoubleClick }
124
+ onKeyDown={ handleKeyDown }
125
+ tabIndex={ 0 }
126
+ role="button"
127
+ aria-label="Double click or press Space to edit"
128
+ >
129
+ { prefixElement }
130
+ { children }
131
+ </Stack>
89
132
  );
90
133
  }
91
-
92
- return (
93
- <Stack
94
- ref={ rowRef }
95
- direction="row"
96
- alignItems="center"
97
- gap={ 1 }
98
- onDoubleClick={ handleDoubleClick }
99
- onKeyDown={ handleKeyDown }
100
- tabIndex={ 0 }
101
- role="button"
102
- aria-label="Double click or press Space to edit"
103
- >
104
- { prefixElement }
105
- { children }
106
- </Stack>
107
- );
108
- };
134
+ );
@@ -14,11 +14,9 @@ import { ColorFilterIcon, TrashIcon } from '@elementor/icons';
14
14
  import { Alert, Box, Button, CloseButton, Divider, ErrorBoundary, Stack } from '@elementor/ui';
15
15
  import { __ } from '@wordpress/i18n';
16
16
 
17
- import { generateTempId } from '../../batch-operations';
18
- import { getVariables } from '../../hooks/use-prop-variables';
19
- import { service } from '../../service';
20
- import { type TVariablesList } from '../../storage';
21
17
  import { DeleteConfirmationDialog } from '../ui/delete-confirmation-dialog';
18
+ import { useAutoEdit } from './hooks/use-auto-edit';
19
+ import { useVariablesManagerState } from './hooks/use-variables-manager-state';
22
20
  import { SIZE, VariableManagerCreateMenu } from './variables-manager-create-menu';
23
21
  import { VariablesManagerTable } from './variables-manager-table';
24
22
 
@@ -40,13 +38,22 @@ export function VariablesManagerPanel() {
40
38
  const { close: closePanel } = usePanelActions();
41
39
  const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = useDialog();
42
40
 
43
- const [ variables, setVariables ] = useState( getVariables( false ) );
44
- const [ deletedVariables, setDeletedVariables ] = useState< string[] >( [] );
45
- const [ ids, setIds ] = useState< string[] >( Object.keys( variables ) );
41
+ const {
42
+ variables,
43
+ ids,
44
+ isDirty,
45
+ hasValidationErrors,
46
+ setIds,
47
+ handleOnChange,
48
+ createVariable,
49
+ handleDeleteVariable,
50
+ handleSave,
51
+ setHasValidationErrors,
52
+ } = useVariablesManagerState();
53
+
54
+ const { autoEditVariableId, startAutoEdit, handleAutoEditComplete } = useAutoEdit();
46
55
 
47
56
  const [ deleteConfirmation, setDeleteConfirmation ] = useState< { id: string; label: string } | null >( null );
48
- const [ autoEditVariableId, setAutoEditVariableId ] = useState< string | undefined >( undefined );
49
- const [ isDirty, setIsDirty ] = useState( false );
50
57
 
51
58
  usePreventUnload( isDirty );
52
59
 
@@ -59,57 +66,23 @@ export function VariablesManagerPanel() {
59
66
  closePanel();
60
67
  };
61
68
 
62
- const handleSave = useCallback( async () => {
63
- const originalVariables = getVariables( false );
64
- const result = await service.batchSave( originalVariables, variables );
65
-
66
- setIsDirty( false );
67
-
68
- if ( result.success ) {
69
- await service.load();
70
- const updatedVariables = service.variables();
71
-
72
- setVariables( updatedVariables );
73
- setIds( Object.keys( updatedVariables ) );
74
- setDeletedVariables( [] );
75
- } else {
76
- setIsDirty( true );
77
- }
78
- }, [ variables ] );
79
-
80
- const handleOnChange = ( newVariables: TVariablesList ) => {
81
- setVariables( newVariables );
82
- setIsDirty( true );
83
- };
84
-
85
- const createVariable = useCallback( ( type: string, defaultName: string, defaultValue: string ) => {
86
- const newId = generateTempId();
87
- const newVariable = {
88
- id: newId,
89
- label: defaultName,
90
- value: defaultValue,
91
- type,
92
- };
93
-
94
- setVariables( ( prev ) => ( { ...prev, [ newId ]: newVariable } ) );
95
- setIds( ( prev ) => [ ...prev, newId ] );
96
- setIsDirty( true );
97
-
98
- setAutoEditVariableId( newId );
99
- }, [] );
100
-
101
- const handleDeleteVariable = ( itemId: string ) => {
102
- setDeletedVariables( [ ...deletedVariables, itemId ] );
103
- setVariables( { ...variables, [ itemId ]: { ...variables[ itemId ], deleted: true } } );
104
- setIsDirty( true );
105
- setDeleteConfirmation( null );
106
- };
69
+ const handleCreateVariable = useCallback(
70
+ ( type: string, defaultName: string, defaultValue: string ) => {
71
+ const newId = createVariable( type, defaultName, defaultValue );
72
+ if ( newId ) {
73
+ startAutoEdit( newId );
74
+ }
75
+ },
76
+ [ createVariable, startAutoEdit ]
77
+ );
107
78
 
108
- const handleAutoEditComplete = useCallback( () => {
109
- setTimeout( () => {
110
- setAutoEditVariableId( undefined );
111
- }, 100 );
112
- }, [] );
79
+ const handleDeleteVariableWithConfirmation = useCallback(
80
+ ( itemId: string ) => {
81
+ handleDeleteVariable( itemId );
82
+ setDeleteConfirmation( null );
83
+ },
84
+ [ handleDeleteVariable ]
85
+ );
113
86
 
114
87
  const menuActions = [
115
88
  {
@@ -138,7 +111,10 @@ export function VariablesManagerPanel() {
138
111
  </PanelHeaderTitle>
139
112
  </Stack>
140
113
  <Stack direction="row" gap={ 0.5 } alignItems="center">
141
- <VariableManagerCreateMenu onCreate={ createVariable } variables={ variables } />
114
+ <VariableManagerCreateMenu
115
+ onCreate={ handleCreateVariable }
116
+ variables={ variables }
117
+ />
142
118
  <CloseButton
143
119
  aria-label="Close"
144
120
  slotProps={ { icon: { fontSize: SIZE } } }
@@ -166,6 +142,7 @@ export function VariablesManagerPanel() {
166
142
  onIdsChange={ setIds }
167
143
  autoEditVariableId={ autoEditVariableId }
168
144
  onAutoEditComplete={ handleAutoEditComplete }
145
+ onFieldError={ setHasValidationErrors }
169
146
  />
170
147
  </PanelBody>
171
148
 
@@ -175,7 +152,7 @@ export function VariablesManagerPanel() {
175
152
  size="small"
176
153
  color="global"
177
154
  variant="contained"
178
- disabled={ ! isDirty }
155
+ disabled={ ! isDirty || hasValidationErrors }
179
156
  onClick={ handleSave }
180
157
  >
181
158
  { __( 'Save changes', 'elementor' ) }
@@ -187,7 +164,7 @@ export function VariablesManagerPanel() {
187
164
  <DeleteConfirmationDialog
188
165
  open
189
166
  label={ deleteConfirmation.label }
190
- onConfirm={ () => handleDeleteVariable( deleteConfirmation.id ) }
167
+ onConfirm={ () => handleDeleteVariableWithConfirmation( deleteConfirmation.id ) }
191
168
  closeDialog={ () => setDeleteConfirmation( null ) }
192
169
  />
193
170
  ) }
@@ -32,6 +32,7 @@ type Props = {
32
32
  onIdsChange: ( ids: string[] ) => void;
33
33
  autoEditVariableId?: string;
34
34
  onAutoEditComplete?: () => void;
35
+ onFieldError?: ( hasError: boolean ) => void;
35
36
  };
36
37
 
37
38
  export const VariablesManagerTable = ( {
@@ -42,6 +43,7 @@ export const VariablesManagerTable = ( {
42
43
  onIdsChange: setIds,
43
44
  autoEditVariableId,
44
45
  onAutoEditComplete,
46
+ onFieldError,
45
47
  }: Props ) => {
46
48
  const tableContainerRef = useRef< HTMLDivElement >( null );
47
49
  const variableRowRefs = useRef< Map< string, HTMLTableRowElement > >( new Map() );
@@ -203,14 +205,25 @@ export const VariablesManagerTable = ( {
203
205
  }
204
206
  } }
205
207
  prefixElement={ createElement( row.icon, { fontSize: 'inherit' } ) }
206
- editableElement={ ( { value, onChange } ) => (
208
+ editableElement={ ( {
209
+ value,
210
+ onChange,
211
+ onValidationChange,
212
+ error,
213
+ } ) => (
207
214
  <LabelField
208
215
  id={ 'variable-label-' + row.id }
209
216
  size="tiny"
210
217
  value={ value }
211
218
  onChange={ onChange }
219
+ onErrorChange={ ( errorMsg ) => {
220
+ onValidationChange?.( errorMsg );
221
+ onFieldError?.( !! errorMsg );
222
+ } }
223
+ error={ error }
212
224
  focusOnShow
213
225
  selectOnShow={ autoEditVariableId === row.id }
226
+ showWarningInfotip={ true }
214
227
  />
215
228
  ) }
216
229
  autoEdit={ autoEditVariableId === row.id }
@@ -218,6 +231,7 @@ export const VariablesManagerTable = ( {
218
231
  onAutoEditComplete={
219
232
  autoEditVariableId === row.id ? onAutoEditComplete : undefined
220
233
  }
234
+ fieldType="label"
221
235
  >
222
236
  <EllipsisWithTooltip
223
237
  title={ row.name }
@@ -19,6 +19,7 @@ export type ValueFieldProps = {
19
19
  onChange: ( value: string ) => void;
20
20
  onValidationChange?: ( value: string ) => void;
21
21
  propType?: PropType;
22
+ error?: { value: string; message: string };
22
23
  };
23
24
 
24
25
  type FallbackPropTypeUtil = ReturnType< typeof createPropUtils >;
@@ -26,7 +27,7 @@ type FallbackPropTypeUtil = ReturnType< typeof createPropUtils >;
26
27
  type VariableTypeOptions = {
27
28
  icon: ForwardRefExoticComponent< Omit< SvgIconProps, 'ref' > & RefAttributes< SVGSVGElement > >;
28
29
  startIcon?: ( { value }: { value: string } ) => JSX.Element;
29
- valueField: ( { value, onChange, onValidationChange, propType }: ValueFieldProps ) => JSX.Element;
30
+ valueField: ( { value, onChange, onValidationChange, propType, error }: ValueFieldProps ) => JSX.Element;
30
31
  variableType: string;
31
32
  defaultValue?: string;
32
33
  fallbackPropTypeUtil: FallbackPropTypeUtil;