Skip to content

Commit 9e79c4e

Browse files
authored
fix(useElementIds): prevent Webpack static analysis issue with useId (#1676)
* fix(useElementIds): prevent Webpack static analysis issue with useId * improve tests
1 parent 52ec230 commit 9e79c4e

15 files changed

+541
-241
lines changed

src/hooks/__tests__/utils.test.js

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,9 @@
11
import {renderHook} from '@testing-library/react'
2-
import {
3-
useMouseAndTouchTracker,
4-
isDropdownsStateEqual,
5-
useElementIds,
6-
} from '../utils'
2+
import {useMouseAndTouchTracker, isDropdownsStateEqual} from '../utils'
73
import {getInitialValue, getDefaultValue, getItemAndIndex} from '../utils-ts'
84
import {dropdownDefaultProps} from '../utils.dropdown'
95

106
describe('utils', () => {
11-
describe('useElementIds', () => {
12-
test('returns the same reference on re-renders when the props do not change', () => {
13-
const getTestItemId = () => 'test-item-id'
14-
const {result, rerender} = renderHook(useElementIds, {
15-
initialProps: {
16-
id: 'test-id',
17-
labelId: 'test-label-id',
18-
menuId: 'test-menu-id',
19-
getItemId: getTestItemId,
20-
toggleButtonId: 'test-toggle-button-id',
21-
inputId: 'test-input-id',
22-
},
23-
})
24-
const renderOneResult = result.current
25-
rerender({
26-
id: 'test-id',
27-
labelId: 'test-label-id',
28-
menuId: 'test-menu-id',
29-
getItemId: getTestItemId,
30-
toggleButtonId: 'test-toggle-button-id',
31-
inputId: 'test-input-id',
32-
})
33-
const renderTwoResult = result.current
34-
expect(renderOneResult).toBe(renderTwoResult)
35-
})
36-
37-
test('returns a new reference on re-renders when the props change', () => {
38-
const {result, rerender} = renderHook(useElementIds, {
39-
initialProps: {
40-
id: 'test-id',
41-
labelId: 'test-label-id',
42-
menuId: 'test-menu-id',
43-
getItemId: () => 'test-item-id',
44-
toggleButtonId: 'test-toggle-button-id',
45-
inputId: 'test-input-id',
46-
},
47-
})
48-
const renderOneResult = result.current
49-
rerender({
50-
id: 'test-id',
51-
labelId: 'test-label-id',
52-
menuId: 'test-menu-id',
53-
getItemId: () => 'test-item-id',
54-
toggleButtonId: 'test-toggle-button-id',
55-
inputId: 'test-input-id',
56-
})
57-
const renderTwoResult = result.current
58-
expect(renderOneResult).not.toBe(renderTwoResult)
59-
})
60-
})
61-
627
describe('itemToString', () => {
638
test('returns empty string if item is falsy', () => {
649
const emptyString = dropdownDefaultProps.itemToString(null)

src/hooks/__tests__/utils.use-element-ids.r18.test.js

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/hooks/__tests__/utils.use-element-ids.test.js

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/hooks/useCombobox/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
useGetterPropsCalledChecker,
88
useScrollIntoView,
99
useControlPropsValidator,
10-
useElementIds,
1110
isDropdownsStateEqual,
1211
} from '../utils'
1312
import {
@@ -25,6 +24,7 @@ import {
2524
} from './utils'
2625
import downshiftUseComboboxReducer from './reducer'
2726
import * as stateChangeTypes from './stateChangeTypes'
27+
import {useElementIds} from '../utils.dropdown/useElementIds'
2828

2929
useCombobox.stateChangeTypes = stateChangeTypes
3030

@@ -114,7 +114,7 @@ function useCombobox(userProps = {}) {
114114
const mouseAndTouchTrackers = useMouseAndTouchTracker(
115115
environment,
116116
handleBlurInTracker,
117-
downshiftRefs
117+
downshiftRefs,
118118
)
119119
const setGetterPropCallInfo = useGetterPropsCalledChecker(
120120
'getInputProps',

src/hooks/useSelect/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
useGetterPropsCalledChecker,
1313
useScrollIntoView,
1414
useControlPropsValidator,
15-
useElementIds,
1615
useMouseAndTouchTracker,
1716
isDropdownsStateEqual,
1817
} from '../utils'
@@ -27,6 +26,7 @@ import {isReactNative, isReactNativeWeb} from '../../is.macro'
2726
import downshiftSelectReducer from './reducer'
2827
import {defaultProps, propTypes} from './utils'
2928
import * as stateChangeTypes from './stateChangeTypes'
29+
import { useElementIds } from '../utils.dropdown/useElementIds'
3030

3131
useSelect.stateChangeTypes = stateChangeTypes
3232

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {renderHook} from '@testing-library/react'
2+
import {useElementIds} from '..'
3+
4+
jest.mock('react', () => {
5+
const {useId, ...react} = jest.requireActual('react')
6+
return react
7+
})
8+
9+
describe('useElementIds for React < 18', () => {
10+
test('uses generateId()', () => {
11+
const {result} = renderHook(() => useElementIds({}))
12+
13+
expect(result.current).toEqual({
14+
getTagId: expect.any(Function),
15+
tagGroupId: 'downshift-0-tag-group',
16+
})
17+
expect(result.current.getTagId(12)).toEqual('downshift-0-tag-12')
18+
})
19+
20+
test('returns the same reference on re-renders when the props do not change', () => {
21+
const getTestTagId = () => 'test-tag-id'
22+
const {result, rerender} = renderHook(useElementIds, {
23+
initialProps: {
24+
id: 'test-id',
25+
tagGroupId: 'test-tag-group-id',
26+
getTagId: getTestTagId,
27+
},
28+
})
29+
const renderOneResult = result.current
30+
rerender({
31+
id: 'test-id',
32+
tagGroupId: 'test-tag-group-id',
33+
getTagId: getTestTagId,
34+
})
35+
const renderTwoResult = result.current
36+
expect(renderOneResult).toBe(renderTwoResult)
37+
})
38+
39+
test('returns a new reference on re-renders when the props change', () => {
40+
const {result, rerender} = renderHook(useElementIds, {
41+
initialProps: {
42+
id: 'test-id',
43+
tagGroupId: 'test-tag-group-id',
44+
getTagId: () => 'test-tag-id',
45+
},
46+
})
47+
const renderOneResult = result.current
48+
rerender({
49+
id: 'test-id',
50+
tagGroupId: 'test-tag-group-id',
51+
getTagId: () => 'test-tag-id',
52+
})
53+
const renderTwoResult = result.current
54+
expect(renderOneResult).not.toBe(renderTwoResult)
55+
})
56+
57+
test('generates stable IDs across re-renders', () => {
58+
const {result, rerender} = renderHook(() => useElementIds({}))
59+
60+
const firstTagGroupId = result.current.tagGroupId
61+
const firstTagId = result.current.getTagId(0)
62+
63+
rerender()
64+
65+
expect(result.current.tagGroupId).toBe(firstTagGroupId)
66+
expect(result.current.getTagId(0)).toBe(firstTagId)
67+
})
68+
69+
test('uses provided id prop', () => {
70+
const {result} = renderHook(() => useElementIds({id: 'custom-id'}))
71+
72+
expect(result.current.tagGroupId).toBe('custom-id-tag-group')
73+
expect(result.current.getTagId(7)).toBe('custom-id-tag-7')
74+
})
75+
76+
test('uses custom tagGroupId and getTagId when provided', () => {
77+
const customGetTagId = (index: number) => `my-tag-${index}`
78+
const {result} = renderHook(() =>
79+
useElementIds({
80+
tagGroupId: 'my-tag-group',
81+
getTagId: customGetTagId,
82+
}),
83+
)
84+
85+
expect(result.current.tagGroupId).toBe('my-tag-group')
86+
expect(result.current.getTagId(4)).toBe('my-tag-4')
87+
})
88+
})
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {renderHook} from '@testing-library/react'
2+
import {useElementIds} from '..'
3+
4+
jest.mock('react', () => {
5+
return {
6+
...jest.requireActual('react'),
7+
useId() {
8+
return 'mocked-id'
9+
},
10+
}
11+
})
12+
13+
describe('useElementIds for React >= 18', () => {
14+
test('uses React.useId', () => {
15+
const {result} = renderHook(() => useElementIds({}))
16+
17+
expect(result.current).toEqual({
18+
getTagId: expect.any(Function),
19+
tagGroupId: 'downshift-mocked-id-tag-group',
20+
})
21+
expect(result.current.getTagId(10)).toEqual("downshift-mocked-id-tag-10")
22+
})
23+
24+
test('returns the same reference on re-renders when the props do not change', () => {
25+
const getTestTagId = () => 'test-tag-id'
26+
const {result, rerender} = renderHook(useElementIds, {
27+
initialProps: {
28+
id: 'test-id',
29+
tagGroupId: 'test-tag-group-id',
30+
getTagId: getTestTagId,
31+
},
32+
})
33+
const renderOneResult = result.current
34+
rerender({
35+
id: 'test-id',
36+
tagGroupId: 'test-tag-group-id',
37+
getTagId: getTestTagId,
38+
})
39+
const renderTwoResult = result.current
40+
expect(renderOneResult).toBe(renderTwoResult)
41+
})
42+
43+
test('returns a new reference on re-renders when the props change', () => {
44+
const {result, rerender} = renderHook(useElementIds, {
45+
initialProps: {
46+
id: 'test-id',
47+
tagGroupId: 'test-tag-group-id',
48+
getTagId: () => 'test-tag-id',
49+
},
50+
})
51+
const renderOneResult = result.current
52+
rerender({
53+
id: 'test-id',
54+
tagGroupId: 'test-tag-group-id',
55+
getTagId: () => 'test-tag-id',
56+
})
57+
const renderTwoResult = result.current
58+
expect(renderOneResult).not.toBe(renderTwoResult)
59+
})
60+
61+
test('generates stable IDs across re-renders', () => {
62+
const {result, rerender} = renderHook(() => useElementIds({}))
63+
64+
const firstTagGroupId = result.current.tagGroupId
65+
const firstTagId = result.current.getTagId(0)
66+
67+
rerender()
68+
69+
expect(result.current.tagGroupId).toBe(firstTagGroupId)
70+
expect(result.current.getTagId(0)).toBe(firstTagId)
71+
})
72+
73+
test('uses provided id prop', () => {
74+
const {result} = renderHook(() => useElementIds({id: 'custom-id'}))
75+
76+
expect(result.current.tagGroupId).toBe('custom-id-tag-group')
77+
expect(result.current.getTagId(5)).toBe('custom-id-tag-5')
78+
})
79+
80+
test('uses custom tagGroupId and getTagId when provided', () => {
81+
const customGetTagId = (index: number) => `my-tag-${index}`
82+
const {result} = renderHook(() =>
83+
useElementIds({
84+
tagGroupId: 'my-tag-group',
85+
getTagId: customGetTagId,
86+
}),
87+
)
88+
89+
expect(result.current.tagGroupId).toBe('my-tag-group')
90+
expect(result.current.getTagId(3)).toBe('my-tag-3')
91+
})
92+
})

src/hooks/useTagGroup/utils/__tests__/utils.use-element-ids.r18.test.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/hooks/useTagGroup/utils/__tests__/utils.use-element-ids.test.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)