1414 * limitations under the License.
1515 */
1616
17- import {
18- createElement ,
19- createRef ,
20- Fragment ,
21- ReactElement ,
22- RefObject ,
23- useEffect ,
24- useMemo ,
25- useRef ,
26- useState ,
27- } from 'react'
17+ import { createElement , createRef , Fragment , ReactElement , RefObject , useEffect , useMemo , useRef } from 'react'
2818// eslint-disable-next-line import/no-extraneous-dependencies
2919import { followCursor } from 'tippy.js'
3020
31- import { ReactComponent as ICClose } from '@Icons/ic-close.svg'
3221import { ResizableTagTextArea } from '@Common/CustomTagSelector'
3322import { ConditionalWrap } from '@Common/Helper'
3423import { Tooltip } from '@Common/Tooltip'
3524import { ComponentSizeType } from '@Shared/constants'
3625
3726import { Button , ButtonStyleType , ButtonVariantType } from '../Button'
3827import { FileUpload } from '../FileUpload'
28+ import { Icon } from '../Icon'
3929import {
4030 getSelectPickerOptionByValue ,
4131 SelectPicker ,
@@ -73,8 +63,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
7363 trailingCellIcon,
7464 buttonCellWrapComponent,
7565 focusableFieldKey,
76- isAddRowButtonClicked,
77- setIsAddRowButtonClicked,
66+ shouldAutoFocusOnMount = false ,
7867} : DynamicDataTableRowProps < K , CustomStateType > ) => {
7968 // CONSTANTS
8069 const isFirstRowEmpty = headers . every ( ( { key } ) => ! rows [ 0 ] ?. data [ key ] . value )
@@ -88,25 +77,18 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
8877 isDeletionNotAllowed || readOnly ,
8978 )
9079
91- // STATES
92- const [ isRowAdded , setIsRowAdded ] = useState ( false )
93-
9480 // CELL REFS
95- const cellRef = useRef < Record < string | number , Record < K , RefObject < HTMLTextAreaElement > > > > ( )
81+ const shouldAutoFocusNewRowRef = useRef ( shouldAutoFocusOnMount )
82+ const cellRef = useRef < Record < string | number , Record < K , RefObject < HTMLTextAreaElement > > > > ( null )
9683 if ( ! cellRef . current ) {
97- cellRef . current = rows . reduce (
98- ( acc , curr ) => ( {
99- ...acc ,
100- [ curr . id ] : headers . reduce ( ( headerAcc , { key } ) => ( { ...headerAcc , [ key ] : createRef ( ) } ) , { } ) ,
101- } ) ,
102- { } ,
103- )
84+ cellRef . current = rows . reduce ( ( acc , curr ) => {
85+ acc [ curr . id ] = headers . reduce ( ( headerAcc , { key } ) => ( { ...headerAcc , [ key ] : createRef ( ) } ) , { } )
86+ return acc
87+ } , { } )
10488 }
10589 const rowIds = useMemo ( ( ) => rows . map ( ( { id } ) => id ) , [ rows ] )
10690
10791 useEffect ( ( ) => {
108- setIsRowAdded ( rows . length > 0 && Object . keys ( cellRef . current ) . length < rows . length )
109-
11092 // When a new row is added, we create references for its cells.
11193 // This logic ensures that references are created only for the newly added row, while retaining the existing references.
11294 const updatedCellRef = rowIds . reduce ( ( acc , curr ) => {
@@ -121,18 +103,6 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
121103 cellRef . current = updatedCellRef
122104 } , [ JSON . stringify ( rowIds ) ] )
123105
124- useEffect ( ( ) => {
125- if ( isAddRowButtonClicked && isRowAdded ) {
126- // Using the below logic to ensure the cell is focused after row addition.
127- const cell = cellRef . current [ rows [ 0 ] . id ] [ focusableFieldKey || headers [ 0 ] . key ] . current
128- if ( cell ) {
129- cell . focus ( )
130- setIsRowAdded ( false )
131- setIsAddRowButtonClicked ( false )
132- }
133- }
134- } , [ isRowAdded , isAddRowButtonClicked ] )
135-
136106 // METHODS
137107 const onChange =
138108 ( row : DynamicDataTableRowType < K , CustomStateType > , key : K ) =>
@@ -163,14 +133,30 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
163133 }
164134
165135 // RENDERERS
166- const renderCellContent = ( row : DynamicDataTableRowType < K , CustomStateType > , key : K ) => {
136+ const renderCellContent = ( row : DynamicDataTableRowType < K , CustomStateType > , key : K , index : number ) => {
167137 const isDisabled = readOnly || row . data [ key ] . disabled
138+ const autoFocus =
139+ shouldAutoFocusNewRowRef . current && key === ( focusableFieldKey ?? headers [ 0 ] . key ) && index === 0
140+
141+ // This logic ensures only newly added rows get autofocus.
142+ // On the initial mount, all rows are treated as new, so autofocus is enabled.
143+ // After the first render, when cellRef is set (DOM rendered), we set shouldAutoFocusNewRowRef to true,
144+ // so autofocus is applied only to the correct cell in any subsequently added row.
145+ if (
146+ ! shouldAutoFocusOnMount &&
147+ ! shouldAutoFocusNewRowRef . current &&
148+ index === 0 &&
149+ cellRef ?. current ?. [ row . id ] ?. [ key ] . current
150+ ) {
151+ shouldAutoFocusNewRowRef . current = true
152+ }
168153
169154 switch ( row . data [ key ] . type ) {
170155 case DynamicDataTableRowDataType . DROPDOWN :
171156 return (
172157 < div className = "w-100 h-100 flex top dc__align-self-start" >
173158 < SelectPicker < string , false >
159+ autoFocus = { autoFocus }
174160 { ...row . data [ key ] . props }
175161 inputId = { `data-table-${ row . id } -${ key } -cell` }
176162 classNamePrefix = "dynamic-data-table__cell__select-picker"
@@ -193,6 +179,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
193179 return (
194180 < div className = "w-100 h-100 flex top dc__align-self-start" >
195181 < SelectPickerTextArea
182+ autoFocus = { autoFocus }
196183 isCreatable = { isCreatable }
197184 isClearable
198185 { ...props }
@@ -248,6 +235,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
248235 default :
249236 return (
250237 < ResizableTagTextArea
238+ autoFocus = { autoFocus }
251239 { ...row . data [ key ] . props }
252240 id = { `data-table-${ row . id } -${ key } -cell` }
253241 className = { `dynamic-data-table__cell-input placeholder-cn5 p-8 cn-9 fs-13 lh-20 dc__align-self-start dc__no-border-radius ${ isDisabled ? 'cursor-not-allowed' : '' } ` }
@@ -283,8 +271,8 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
283271 }
284272
285273 const renderErrorMessage = ( errorMessage : string ) => (
286- < div key = { errorMessage } className = "flexbox align-items-center dc__gap-4" >
287- < ICClose className = "icon-dim-16 fcr-5 dc__align-self-start dc__no-shrink " />
274+ < div key = { errorMessage } className = "flexbox dc__gap-4" >
275+ < Icon name = "ic-close-small" color = "R500 " />
288276 < p className = "fs-12 lh-16 cn-7 m-0" > { errorMessage } </ p >
289277 </ div >
290278 )
@@ -329,7 +317,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
329317 className = { `dynamic-data-table__cell bg__primary flexbox dc__align-items-center dc__gap-4 dc__position-rel ${ isDisabled ? 'cursor-not-allowed no-hover' : '' } ${ ! isDisabled && hasError ? 'dynamic-data-table__cell--error no-hover' : '' } ${ ! rowTypeHasInputField ( row . data [ key ] . type ) ? 'no-hover no-focus' : '' } ` }
330318 >
331319 { renderCellIcon ( row , key , true ) }
332- { renderCellContent ( row , key ) }
320+ { renderCellContent ( row , key , index ) }
333321 { renderAsterisk ( row , key ) }
334322 { renderCellIcon ( row , key ) }
335323 { renderErrorMessages ( row , key ) }
@@ -383,7 +371,7 @@ export const DynamicDataTableRow = <K extends string, CustomStateType = Record<s
383371 dataTestId = "dynamic-data-table-row-delete-btn"
384372 ariaLabel = "Delete Row"
385373 showAriaLabelInTippy = { false }
386- icon = { < ICClose /> }
374+ icon = { < Icon name = "ic-close-large" color = { null } /> }
387375 disabled = { disableDeleteRow || row . disableDelete }
388376 onClick = { onDelete ( row ) }
389377 variant = { ButtonVariantType . borderLess }
0 commit comments