@@ -11,7 +11,7 @@ import {
1111 TextInput ,
1212} from "@patternfly/react-core" ;
1313import { MinusCircleIcon , PlusCircleIcon } from "@patternfly/react-icons" ;
14- import { useEffect , useState } from "react" ;
14+ import { useEffect , useState , useRef } from "react" ;
1515import { useFormContext } from "react-hook-form" ;
1616import { useTranslation } from "react-i18next" ;
1717import { HelpItem } from "@keycloak/keycloak-ui-shared" ;
@@ -23,10 +23,10 @@ type ClaimDisplayEntry = {
2323} ;
2424
2525type IdClaimDisplayEntry = ClaimDisplayEntry & {
26- id : number ;
26+ id : string ;
2727} ;
2828
29- const generateId = ( ) => Date . now ( ) + Math . random ( ) ;
29+ const generateId = ( ) => crypto . randomUUID ( ) ;
3030
3131export const ClaimDisplayComponent = ( {
3232 name,
@@ -41,6 +41,7 @@ export const ClaimDisplayComponent = ({
4141 const { getValues, setValue, register } = useFormContext ( ) ;
4242 const [ displays , setDisplays ] = useState < IdClaimDisplayEntry [ ] > ( [ ] ) ;
4343 const fieldName = convertToName ( name ! ) ;
44+ const debounceTimeoutRef = useRef < number | null > ( null ) ;
4445
4546 useEffect ( ( ) => {
4647 register ( fieldName ) ;
@@ -58,10 +59,24 @@ export const ClaimDisplayComponent = ({
5859 }
5960 } , [ defaultValue , fieldName , getValues , register ] ) ;
6061
61- const appendNew = ( ) =>
62- setDisplays ( [ ...displays , { name : "" , locale : "" , id : generateId ( ) } ] ) ;
62+ useEffect ( ( ) => {
63+ return ( ) => {
64+ if ( debounceTimeoutRef . current !== null ) {
65+ clearTimeout ( debounceTimeoutRef . current ) ;
66+ }
67+ } ;
68+ } , [ ] ) ;
69+
70+ const appendNew = ( ) => {
71+ const newDisplays = [
72+ ...displays ,
73+ { name : "" , locale : "" , id : generateId ( ) } ,
74+ ] ;
75+ setDisplays ( newDisplays ) ;
76+ syncFormValue ( newDisplays ) ;
77+ } ;
6378
64- const update = ( val = displays ) => {
79+ const syncFormValue = ( val = displays ) => {
6580 const filteredEntries = val
6681 . filter ( ( e ) => e . name !== "" && e . locale !== "" )
6782 . map ( ( entry ) => ( { name : entry . name , locale : entry . locale } ) ) ;
@@ -72,25 +87,48 @@ export const ClaimDisplayComponent = ({
7287 } ) ;
7388 } ;
7489
75- const updateName = ( index : number , name : string ) => {
76- updateEntry ( index , { ...displays [ index ] , name } ) ;
90+ const debouncedUpdate = ( val : IdClaimDisplayEntry [ ] ) => {
91+ if ( debounceTimeoutRef . current !== null ) {
92+ clearTimeout ( debounceTimeoutRef . current ) ;
93+ }
94+ debounceTimeoutRef . current = window . setTimeout ( ( ) => {
95+ syncFormValue ( val ) ;
96+ debounceTimeoutRef . current = null ;
97+ } , 300 ) ;
7798 } ;
7899
79- const updateLocale = ( index : number , locale : string ) => {
80- updateEntry ( index , { ...displays [ index ] , locale } ) ;
100+ const flushUpdate = ( ) => {
101+ if ( debounceTimeoutRef . current !== null ) {
102+ clearTimeout ( debounceTimeoutRef . current ) ;
103+ debounceTimeoutRef . current = null ;
104+ }
105+ syncFormValue ( ) ;
81106 } ;
82107
83- const updateEntry = ( index : number , entry : IdClaimDisplayEntry ) =>
84- setDisplays ( [
108+ const updateName = ( index : number , name : string ) => {
109+ const newDisplays = [
85110 ...displays . slice ( 0 , index ) ,
86- entry ,
111+ { ... displays [ index ] , name } ,
87112 ...displays . slice ( index + 1 ) ,
88- ] ) ;
113+ ] ;
114+ setDisplays ( newDisplays ) ;
115+ debouncedUpdate ( newDisplays ) ;
116+ } ;
117+
118+ const updateLocale = ( index : number , locale : string ) => {
119+ const newDisplays = [
120+ ...displays . slice ( 0 , index ) ,
121+ { ...displays [ index ] , locale } ,
122+ ...displays . slice ( index + 1 ) ,
123+ ] ;
124+ setDisplays ( newDisplays ) ;
125+ debouncedUpdate ( newDisplays ) ;
126+ } ;
89127
90128 const remove = ( index : number ) => {
91129 const value = [ ...displays . slice ( 0 , index ) , ...displays . slice ( index + 1 ) ] ;
92130 setDisplays ( value ) ;
93- update ( value ) ;
131+ syncFormValue ( value ) ;
94132 } ;
95133
96134 return displays . length !== 0 ? (
@@ -117,7 +155,7 @@ export const ClaimDisplayComponent = ({
117155 data-testid = { `${ fieldName } .${ index } .name` }
118156 value = { display . name }
119157 onChange = { ( _event , value ) => updateName ( index , value ) }
120- onBlur = { ( ) => update ( ) }
158+ onBlur = { ( ) => flushUpdate ( ) }
121159 isDisabled = { isDisabled }
122160 placeholder = { t ( "claimDisplayNamePlaceholder" ) }
123161 />
@@ -128,7 +166,7 @@ export const ClaimDisplayComponent = ({
128166 data-testid = { `${ fieldName } .${ index } .locale` }
129167 value = { display . locale }
130168 onChange = { ( _event , value ) => updateLocale ( index , value ) }
131- onBlur = { ( ) => update ( ) }
169+ onBlur = { ( ) => flushUpdate ( ) }
132170 isDisabled = { isDisabled }
133171 placeholder = { t ( "claimDisplayLocalePlaceholder" ) }
134172 />
@@ -163,14 +201,14 @@ export const ClaimDisplayComponent = ({
163201 </ FormGroup >
164202 ) : (
165203 < EmptyState
166- data-testid = { `${ name } -empty-state` }
204+ data-testid = { `${ fieldName } -empty-state` }
167205 className = "pf-v5-u-p-0"
168206 variant = "xs"
169207 >
170208 < EmptyStateBody > { t ( "noClaimDisplayEntries" ) } </ EmptyStateBody >
171209 < EmptyStateFooter >
172210 < Button
173- data-testid = { `${ name } -add-row` }
211+ data-testid = { `${ fieldName } -add-row` }
174212 variant = "link"
175213 icon = { < PlusCircleIcon /> }
176214 size = "sm"
0 commit comments