From 4980928d36e9e47b51fbf3f43cba6b3136c55c78 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Fri, 6 Sep 2024 10:29:29 +1000 Subject: [PATCH] Fix useId updater overwrite (#6788) --- packages/@react-aria/utils/src/useId.ts | 21 ++++++++++++------- .../test/TextField.test.js | 19 +++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/packages/@react-aria/utils/src/useId.ts b/packages/@react-aria/utils/src/useId.ts index 7ca79909dc1..9d7640f65f3 100644 --- a/packages/@react-aria/utils/src/useId.ts +++ b/packages/@react-aria/utils/src/useId.ts @@ -22,7 +22,7 @@ let canUseDOM = Boolean( window.document.createElement ); -let idsUpdaterMap: Map void> = new Map(); +let idsUpdaterMap: Map void>> = new Map(); /** * If a default is not provided, generate an id. @@ -39,7 +39,12 @@ export function useId(defaultId?: string): string { }, []); if (canUseDOM) { - idsUpdaterMap.set(res, updateValue); + // TS not smart enough to know that `has` means the value exists + if (idsUpdaterMap.has(res) && !idsUpdaterMap.get(res)!.includes(updateValue)) { + idsUpdaterMap.set(res, [...idsUpdaterMap.get(res)!, updateValue]); + } else { + idsUpdaterMap.set(res, [updateValue]); + } } useLayoutEffect(() => { @@ -71,15 +76,15 @@ export function mergeIds(idA: string, idB: string): string { return idA; } - let setIdA = idsUpdaterMap.get(idA); - if (setIdA) { - setIdA(idB); + let setIdsA = idsUpdaterMap.get(idA); + if (setIdsA) { + setIdsA.forEach(fn => fn(idB)); return idB; } - let setIdB = idsUpdaterMap.get(idB); - if (setIdB) { - setIdB(idA); + let setIdsB = idsUpdaterMap.get(idB); + if (setIdsB) { + setIdsB.forEach(fn => fn(idA)); return idA; } diff --git a/packages/react-aria-components/test/TextField.test.js b/packages/react-aria-components/test/TextField.test.js index 4d76b4a4043..527c6644b5a 100644 --- a/packages/react-aria-components/test/TextField.test.js +++ b/packages/react-aria-components/test/TextField.test.js @@ -238,5 +238,24 @@ describe('TextField', () => { expect(outerEl[0]).not.toHaveAttribute('id'); expect(input).toHaveAttribute('id', 'name'); }); + + it('should link an id on the input to the label htmlFor', async () => { + let {getAllByTestId, getByRole, getByText} = render( + + + + Description + Error + + ); + let outerEl = getAllByTestId('text-field-test'); + let input = getByRole('textbox'); + let label = getByText('Test'); + + expect(outerEl).toHaveLength(1); + expect(outerEl[0]).not.toHaveAttribute('id'); + expect(input).toHaveAttribute('id', 'name'); + expect(label).toHaveAttribute('for', 'name'); + }); }); });