Skip to content

Commit 4a37dae

Browse files
authoredDec 7, 2024··
MVP support for React 19 (#1350)
focus-trap-react was already "ready" for React 19. All that was necessary was to drop `prop-types`, which has been removed as a dependency. It's also time to move forward and stop supporting ancient versions of React. Therefore, the minimum supported version of React will now be 18.0.0, and I intend for that remain one version behind the latest version going forward, provided the gap between the two is reasonably surmountable.
1 parent bdac34a commit 4a37dae

8 files changed

+70
-121
lines changed
 

‎.changeset/hot-planes-roll.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'focus-trap-react': major
3+
---
4+
5+
Dropping `propTypes` and `defaultProps` no longer supported by React 19 and long deprecated in React 18 (going forward, use TypeScript for prop typings, and if necessary, a runtime library to validate props); Increasing minimum supported React version up to >=18; Bumping `focus-trap` dependency to v7.6.2

‎.eslintrc.js

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ module.exports = {
105105
'jest/no-focused-tests': 'error',
106106
'jest/no-identical-title': 'error',
107107
'jest/valid-title': 'error',
108+
109+
//// from react plugin
110+
111+
'react/prop-types': 'off', // React 19 no longer supports propTypes
108112
},
109113
settings: {
110114
// eslint-plugin-react settings: a version needs to be specified,

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,4 @@ dist/
7272

7373
cypress/videos
7474
cypress/screenshots
75+
cypress/downloads

‎README.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,23 @@ npm install focus-trap-react
3333

3434
### React dependency
3535

36-
React `>= 16.3.0`
36+
React `>= 18.0.0`
3737

38-
## Browser Support
38+
> Note that while React 18.x still supported `propTypes` and `defaultProps`, they had long-since been deprecated, and are completely dropped in React 19.
39+
40+
Therefore, this library no longer assigns these properties to the `<FocusTrap>` element for runtime validation and initialization. The same techniques you would now use in React 19 are backward-compatible with React 18:
41+
42+
- Use TypeScript for static prop type validation
43+
- Use a runtime validation library such as [RTV.js](https://rtvjs.stefcameron.com/), [JSON Schema](https://json-schema.org/), or [yup](https://github.com/jquense/yup) for runtime prop validation to replace `prop-types`)
3944

40-
As old and as broad as _reasonably_ possible, excluding browsers that are out of support or have nearly no user base.
45+
> This library aims to support one major version of React _behind_ the current major version, since React major releases are typically years apart -- to the extent that the feature drift is not too great and remains reasonably surmountable.
46+
47+
## Browser Support
4148

4249
Focused on desktop browsers, particularly Chrome, Edge, FireFox, Safari, and Opera.
4350

51+
Gated by what React [supports](https://legacy.reactjs.org/docs/javascript-environment-requirements.html) in the version [currently](#react-dependency) supported.
52+
4453
Focus-trap-react is not officially tested on any mobile browsers or devices.
4554

4655
> ⚠️ Microsoft [no longer supports](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/) any version of IE, so IE is no longer supported by this library.

‎index.d.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Options as FocusTrapOptions } from 'focus-trap';
22
import * as React from 'react';
33

4-
export = FocusTrap;
5-
64
declare namespace FocusTrap {
75
export interface Props extends React.AllHTMLAttributes<any> {
86
children?: React.ReactNode;
@@ -13,4 +11,4 @@ declare namespace FocusTrap {
1311
}
1412
}
1513

16-
declare class FocusTrap extends React.Component<FocusTrap.Props> { }
14+
export declare class FocusTrap extends React.Component<FocusTrap.Props> { }

‎package-lock.json

+33-41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+7-5
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@
6767
"@testing-library/cypress": "^10.0.2",
6868
"@testing-library/dom": "^10.4.0",
6969
"@testing-library/jest-dom": "^6.6.3",
70-
"@testing-library/react": "^16.0.1",
70+
"@testing-library/react": "^16.1.0",
7171
"@testing-library/user-event": "^14.5.2",
7272
"@types/jquery": "^3.5.32",
73+
"@types/react": "^18.3.1",
74+
"@types/react-dom": "^18.3.0",
7375
"all-contributors-cli": "^6.26.1",
7476
"babel-jest": "^29.7.0",
7577
"babelify": "^10.0.0",
@@ -87,7 +89,6 @@
8789
"jest-watch-typeahead": "^2.2.2",
8890
"onchange": "^7.1.0",
8991
"prettier": "^3.4.1",
90-
"prop-types": "^15.8.1",
9192
"react": "^18.3.1",
9293
"react-dom": "^18.3.1",
9394
"regenerator-runtime": "^0.14.1",
@@ -99,8 +100,9 @@
99100
"tabbable": "^6.2.0"
100101
},
101102
"peerDependencies": {
102-
"prop-types": "^15.8.1",
103-
"react": ">=16.3.0",
104-
"react-dom": ">=16.3.0"
103+
"@types/react": "^18.0.0 || ^19.0.0",
104+
"@types/react-dom": "^18.0.0 || ^19.0.0",
105+
"react": "^18.0.0 || ^19.0.0",
106+
"react-dom": "^18.0.0 || ^19.0.0"
105107
}
106108
}

‎src/focus-trap-react.js

+7-69
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
const React = require('react');
2-
const PropTypes = require('prop-types');
32
const { createFocusTrap } = require('focus-trap');
43
const { isFocusable } = require('tabbable');
54

5+
/**
6+
* @type {import('../index.d.ts').FocusTrap}
7+
*/
68
class FocusTrap extends React.Component {
79
constructor(props) {
810
super(props);
@@ -412,74 +414,10 @@ class FocusTrap extends React.Component {
412414
}
413415
}
414416

415-
// support server-side rendering where `Element` will not be defined
416-
const ElementType = typeof Element === 'undefined' ? Function : Element;
417-
418-
FocusTrap.propTypes = {
419-
active: PropTypes.bool,
420-
paused: PropTypes.bool,
421-
focusTrapOptions: PropTypes.shape({
422-
document: PropTypes.object,
423-
onActivate: PropTypes.func,
424-
onPostActivate: PropTypes.func,
425-
checkCanFocusTrap: PropTypes.func,
426-
onPause: PropTypes.func,
427-
onPostPause: PropTypes.func,
428-
onUnpause: PropTypes.func,
429-
onPostUnpause: PropTypes.func,
430-
onDeactivate: PropTypes.func,
431-
onPostDeactivate: PropTypes.func,
432-
checkCanReturnFocus: PropTypes.func,
433-
initialFocus: PropTypes.oneOfType([
434-
PropTypes.instanceOf(ElementType),
435-
PropTypes.string,
436-
PropTypes.bool,
437-
PropTypes.func,
438-
]),
439-
fallbackFocus: PropTypes.oneOfType([
440-
PropTypes.instanceOf(ElementType),
441-
PropTypes.string,
442-
// NOTE: does not support `false` as value (or return value from function)
443-
PropTypes.func,
444-
]),
445-
escapeDeactivates: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
446-
clickOutsideDeactivates: PropTypes.oneOfType([
447-
PropTypes.bool,
448-
PropTypes.func,
449-
]),
450-
returnFocusOnDeactivate: PropTypes.bool,
451-
setReturnFocus: PropTypes.oneOfType([
452-
PropTypes.instanceOf(ElementType),
453-
PropTypes.string,
454-
PropTypes.bool,
455-
PropTypes.func,
456-
]),
457-
allowOutsideClick: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
458-
preventScroll: PropTypes.bool,
459-
tabbableOptions: PropTypes.shape({
460-
displayCheck: PropTypes.oneOf([
461-
'full',
462-
'legacy-full',
463-
'non-zero-area',
464-
'none',
465-
]),
466-
getShadowRoot: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
467-
}),
468-
trapStack: PropTypes.array,
469-
isKeyForward: PropTypes.func,
470-
isKeyBackward: PropTypes.func,
471-
}),
472-
containerElements: PropTypes.arrayOf(PropTypes.instanceOf(ElementType)), // DOM element ONLY
473-
children: PropTypes.oneOfType([
474-
PropTypes.element, // React element
475-
PropTypes.instanceOf(ElementType), // DOM element
476-
]),
477-
478-
// NOTE: _createFocusTrap is internal, for testing purposes only, so we don't
479-
// specify it here. It's expected to be set to the function returned from
480-
// require('focus-trap'), or one with a compatible interface.
481-
};
482-
417+
// NOTE: While React 19 REMOVED support for `propTypes`, support for `defaultProps`
418+
// __for class components ONLY__ remains: "Class components will continue to support
419+
// defaultProps since there is no ES6 alternative."
420+
// @see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-proptypes-and-defaultprops
483421
FocusTrap.defaultProps = {
484422
active: true,
485423
paused: false,

0 commit comments

Comments
 (0)
Please sign in to comment.