Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,32 @@ Please fill out the information below to expedite the review and (hopefully)
merge of your pull request!
-->

# Pull Request

## What

<!-- What changes are being made? (What feature/bug is being fixed here?) -->

**What**:
## Why

<!-- Why are these changes necessary? -->

**Why**:
## How

<!-- How were these changes implemented? -->

**How**:
## Changes

<!-- Have you done all of these things? -->
<!-- List the specific changes made (files modified, configurations updated, etc.) -->

**Checklist**:
## Checklist

<!-- add "N/A" to the end of each line that's irrelevant to your changes -->

<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->

- [ ] Documentation
- [ ] Tests
- [ ] TypeScript Types
- [ ] Flow Types
- [ ] Ready to be merged
<!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->

Expand Down
26 changes: 14 additions & 12 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,23 @@ jobs:
if: ${{ !contains(github.head_ref, 'all-contributors') }}
strategy:
matrix:
node: [18, 20]
node: [20, 22, 24]
runs-on: ubuntu-latest
steps:
- name: 🛑 Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.6.0
uses: styfle/cancel-workflow-action@0.12.1
with:
access_token: ${{ secrets.GITHUB_TOKEN }}
- name: ⬇️ Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v5

- name: Increase watchers
run:
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
&& sudo sysctl -p

- name: ⎔ Setup node
uses: actions/setup-node@v1
uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node }}

Expand All @@ -47,7 +47,9 @@ jobs:
FORCE_COLOR: true

- name: ⬆️ Upload coverage report
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

release:
needs: main
Expand All @@ -58,17 +60,17 @@ jobs:
github.ref) && github.event_name == 'push' }}
steps:
- name: 🛑 Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.6.0
uses: styfle/cancel-workflow-action@0.12.1
with:
access_token: ${{ secrets.GITHUB_TOKEN }}

- name: ⬇️ Checkout repo
uses: actions/checkout@v2
uses: actions/checkout@v5

- name: ⎔ Setup node
uses: actions/setup-node@v1
uses: actions/setup-node@v5
with:
node-version: 16
node-version: 24

- name: 📥 Download deps
uses: bahmutov/npm-install@v1
Expand All @@ -79,9 +81,9 @@ jobs:
run: npm run build

- name: 🚀 Release
uses: cycjimmy/semantic-release-action@v2
uses: cycjimmy/semantic-release-action@v6
with:
semantic_version: 17
semantic_version: 24
branches: |
[
'+([0-9])?(.{+([0-9]),x}).x',
Expand All @@ -93,4 +95,4 @@ jobs:
]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
5 changes: 3 additions & 2 deletions cypress/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"plugins": [
"cypress"
],
"env": {
"cypress/globals": true
"globals": {
"cy": true,
"Cypress": true
}
}
8 changes: 7 additions & 1 deletion other/misc-tests/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ const babelHelpersList = require('@babel/helpers').list
module.exports = Object.assign(jestConfig, {
roots: ['.'],
testEnvironment: 'jsdom',
testEnvironmentOptions: {
customExportConditions: ['require', 'node'],
},
moduleNameMapper: babelHelpersList.reduce(
(aliasMap, helper) => {
aliasMap[
`@babel/runtime/helpers/esm/${helper}`
] = `@babel/runtime/helpers/${helper}`
return aliasMap
},
{'^preact(/(.*)|$)': 'preact$1'},
{
'^preact(/(.*)|$)': 'preact$1',
'(.*)\\.esm\\.mjs$': '$1.cjs.cjs',
},
),
})
72 changes: 37 additions & 35 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,58 +81,60 @@
"react": ">=16.12.0"
},
"dependencies": {
"@babel/runtime": "^7.24.5",
"compute-scroll-into-view": "^3.1.0",
"@babel/runtime": "^7.28.6",
"compute-scroll-into-view": "^3.1.1",
"prop-types": "^15.8.1",
"react-is": "18.2.0",
"tslib": "^2.6.2"
"react-is": "^18.2.0",
"tslib": "^2.8.1"
},
"devDependencies": {
"@babel/helpers": "^7.24.5",
"@babel/helpers": "^7.28.6",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@cypress/webpack-preprocessor": "^6.0.1",
"@cypress/webpack-preprocessor": "^7.0.2",
"@docusaurus/core": "3.3.2",
"@docusaurus/module-type-aliases": "3.3.2",
"@docusaurus/preset-classic": "3.3.2",
"@mdx-js/react": "^3.0.1",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@testing-library/cypress": "^10.0.1",
"@testing-library/dom": "^10.1.0",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/preact": "^2.0.1",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.12",
"@types/react": "^18.3.2",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@rollup/plugin-babel": "^6.1.0",
"@rollup/plugin-commonjs": "^29.0.0",
"@testing-library/cypress": "^10.1.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/preact": "^3.2.4",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/jest": "^30.0.0",
"@types/prop-types": "^15.7.15",
"@types/react": "^18.2.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-no-side-effect-class-properties": "0.0.7",
"babel-preset-react-native": "^4.0.1",
"buble": "^0.20.0",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cypress": "13.9.0",
"eslint": "^8.56.0",
"eslint-plugin-cypress": "^3.2.0",
"eslint-plugin-react": "7.34.1",
"flow-bin": "^0.236.0",
"cpy-cli": "^6.0.0",
"cross-env": "^10.1.0",
"cypress": "15.9.0",
"eslint": "^9.39.2",
"eslint-plugin-cypress": "^5.2.1",
"eslint-plugin-react": "7.37.5",
"flow-bin": "^0.299.0",
"flow-coverage-report": "^0.8.0",
"get-pkg-repo": "5.0.0",
"kcd-scripts": "^15.0.1",
"node-polyfill-webpack-plugin": "^3.0.0",
"node-polyfill-webpack-plugin": "^4.1.0",
"npm-run-all": "^4.1.5",
"preact": "^10.22.0",
"prism-react-renderer": "^2.3.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "^0.74.1",
"react-test-renderer": "18.2.0",
"serve": "^14.2.3",
"start-server-and-test": "^2.0.3",
"typescript": "^5.4.5"
"preact": "^10.28.2",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-is": "^18.3.1",
"react-native": "^0.76.0",
"react-test-renderer": "^18.3.1",
"serve": "^14.2.5",
"start-server-and-test": "^2.1.3",
"typescript": "^5.9.3"
},
"eslintConfig": {
"parserOptions": {
Expand Down
6 changes: 1 addition & 5 deletions src/__tests__/downshift.get-button-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,7 @@ test('getToggleButtonProps returns all given props', () => {
const Button = jest.fn(props => <button {...props} />)
setup({buttonProps, Button})
expect(Button).toHaveBeenCalledTimes(1)
const context = expect.any(Object)
expect(Button).toHaveBeenCalledWith(
expect.objectContaining(buttonProps),
context,
)
expect(Button).toHaveBeenCalledWith(expect.objectContaining(buttonProps), expect.anything())
})

// normally this test would be like the others where we render and then simulate a click on the
Expand Down
33 changes: 25 additions & 8 deletions src/__tests__/downshift.misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// but we still want to have tested.

import * as React from 'react'
import * as ReactDOM from 'react-dom'
import * as ReactDOM from 'react-dom/client'
import {act, render} from '@testing-library/react'
import Downshift from '../'

Expand Down Expand Up @@ -68,21 +68,38 @@ test('toggleMenu can take no arguments at all', () => {
)
})

test('clearItems clears the all items', () => {
test('clearItems clears all items', () => {
const item = 'Chess'

const children = ({getItemProps}) => (
<div>
<div key={item} {...getItemProps({item})}>
{item}
</div>
</div>
)
// IMPLEMENTATION DETAIL TEST :-(
// eslint-disable-next-line react/no-render-return-value
const downshiftInstance = ReactDOM.render(
<Downshift>{children}</Downshift>,
document.createElement('div'),
)

// Wrap Downshift to expose its instance methods through a ref
const DownshiftWrapper = React.forwardRef((_props, ref) => {
const innerRef = React.useRef(null)

React.useImperativeHandle(ref, () => innerRef.current)

return <Downshift ref={innerRef}>{children}</Downshift>
})

const container = document.createElement('div')
const root = ReactDOM.createRoot(container)

const dsRef = React.createRef()

// eslint-disable-next-line testing-library/no-unnecessary-act
act(() => {
root.render(<DownshiftWrapper ref={dsRef} />)
})
Comment on lines +91 to +99
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createRoot is created and rendered, but the root is never unmounted. This can leak mounted trees between tests and trigger act/cleanup warnings in newer React/testing-library versions. Add cleanup to unmount the root (and optionally remove the container) at the end of the test.

Copilot uses AI. Check for mistakes.

const downshiftInstance = dsRef.current

expect(downshiftInstance.items).toEqual([item])
downshiftInstance.clearItems()
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

downshiftInstance.clearItems() is very likely to trigger a state update; with createRoot/React 18+ this should be wrapped in act (or use testing-library utilities that already wrap updates) to avoid warnings and potential flakiness.

Suggested change
downshiftInstance.clearItems()
act(() => {
downshiftInstance.clearItems()
})

Copilot uses AI. Check for mistakes.
expect(downshiftInstance.items).toEqual([])
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('utils', () => {

expect(() =>
getItemAndIndex(undefined, undefined, [1, 2, 3], errorMessage),
).toThrowError(errorMessage)
).toThrow(errorMessage)
})

test('returns index if item is passed', () => {
Expand Down Expand Up @@ -135,7 +135,7 @@ describe('utils', () => {
test('renders without error', () => {
expect(() => {
renderHook(() => useMouseAndTouchTracker(undefined, jest.fn(), []))
}).not.toThrowError()
}).not.toThrow()
})

test('adds and removes listeners to environment', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useCombobox/__tests__/getItemProps.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('getItemProps', () => {
test('throws error if no index or item has been passed', () => {
const {result} = renderUseCombobox()

expect(result.current.getItemProps).toThrowError(
expect(result.current.getItemProps).toThrow(
'Pass either item or index to getItemProps!',
)
})
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useCombobox/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ describe('utils', () => {
test('reducer throws error if called without proper action type', () => {
expect(() => {
reducer({}, {}, {type: 'super-bogus'})
}).toThrowError('Reducer called without proper action type.')
}).toThrow('Reducer called without proper action type.')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('getSelectedItemProps', () => {
test('throws error if no index or item has been passed', () => {
const {result} = renderUseMultipleSelection()

expect(result.current.getSelectedItemProps).toThrowError(
expect(result.current.getSelectedItemProps).toThrow(
'Pass either item or index to getSelectedItemProps!',
)
})
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useMultipleSelection/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ describe('utils', () => {
test('reducer throws error if called without proper action type', () => {
expect(() => {
reducer({}, {}, {type: 'super-bogus'})
}).toThrowError('Reducer called without proper action type.')
}).toThrow('Reducer called without proper action type.')
})
})
2 changes: 1 addition & 1 deletion src/hooks/useSelect/__tests__/getItemProps.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('getItemProps', () => {
test('throws error if no index or item has been passed', () => {
const {result} = renderUseSelect()

expect(result.current.getItemProps).toThrowError(
expect(result.current.getItemProps).toThrow(
'Pass either item or index to getItemProps!',
)
})
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useSelect/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ describe('getItemIndexByCharacterKey', () => {
test('reducer throws error if called without proper action type', () => {
expect(() => {
reducer({}, {}, {type: 'super-bogus'})
}).toThrowError('Reducer called without proper action type.')
}).toThrow('Reducer called without proper action type.')
})
Loading
Loading