focus-trap-react 8.8.2 → 8.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 8.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 83097a5: Delay trap creation until it should be active. This is a change in behavior, however it should not break existing behavior. The delay now allows you to set `active=false` until you have the `focusTrapOptions` set correctly. [#539](https://github.com/focus-trap/focus-trap-react/issues/539)
8
+
9
+ ### Patch Changes
10
+
11
+ - 16d1ae1: Fix bug where global document was being accessed instead of first checking for `focusTrapOptions.document` option. [#539](https://github.com/focus-trap/focus-trap-react/issues/539)
12
+
3
13
  ## 8.8.2
4
14
 
5
15
  ### Patch Changes
@@ -86,10 +86,23 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
86
86
  _this.updatePreviousElement();
87
87
 
88
88
  return _this;
89
- } // TODO: Need more test coverage for this function
89
+ }
90
+ /**
91
+ * Gets the configured document.
92
+ * @returns {Document|undefined} Configured document, falling back to the main
93
+ * document, if it exists. During SSR, `undefined` is returned since the
94
+ * document doesn't exist.
95
+ */
90
96
 
91
97
 
92
98
  _createClass(FocusTrap, [{
99
+ key: "getDocument",
100
+ value: function getDocument() {
101
+ // SSR: careful to check if `document` exists before accessing it as a variable
102
+ return this.props.focusTrapOptions.document || (typeof document !== 'undefined' ? document : undefined);
103
+ } // TODO: Need more test coverage for this function
104
+
105
+ }, {
93
106
  key: "getNodeForOption",
94
107
  value: function getNodeForOption(optionName) {
95
108
  var optionValue = this.tailoredFocusTrapOptions[optionName];
@@ -101,7 +114,9 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
101
114
  var node = optionValue;
102
115
 
103
116
  if (typeof optionValue === 'string') {
104
- node = document.querySelector(optionValue);
117
+ var _this$getDocument;
118
+
119
+ node = (_this$getDocument = this.getDocument()) === null || _this$getDocument === void 0 ? void 0 : _this$getDocument.querySelector(optionValue);
105
120
 
106
121
  if (!node) {
107
122
  throw new Error("`".concat(optionName, "` refers to no known node"));
@@ -129,8 +144,7 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
129
144
  }, {
130
145
  key: "updatePreviousElement",
131
146
  value: function updatePreviousElement() {
132
- // SSR: careful to check if `document` exists before accessing it as a variable
133
- var currentDocument = this.props.focusTrapOptions.document || (typeof document !== 'undefined' ? document : undefined);
147
+ var currentDocument = this.getDocument();
134
148
 
135
149
  if (currentDocument) {
136
150
  this.previouslyFocusedElement = currentDocument.activeElement;
@@ -204,7 +218,14 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
204
218
  }, {
205
219
  key: "componentDidMount",
206
220
  value: function componentDidMount() {
207
- this.setupFocusTrap();
221
+ if (this.props.active) {
222
+ this.setupFocusTrap();
223
+ } // else, wait for later activation in case the `focusTrapOptions` will be updated
224
+ // again before the trap is activated (e.g. if waiting to know what the document
225
+ // object will be, so the Trap must be rendered, but the consumer is waiting to
226
+ // activate until they have obtained the document from a ref)
227
+ // @see https://github.com/focus-trap/focus-trap-react/issues/539
228
+
208
229
  }
209
230
  }, {
210
231
  key: "componentDidUpdate",
@@ -236,9 +257,22 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
236
257
  if (hasUnpaused) {
237
258
  this.focusTrap.unpause();
238
259
  }
239
- } else if (prevProps.containerElements !== this.props.containerElements) {
240
- this.focusTrapElements = this.props.containerElements;
241
- this.setupFocusTrap();
260
+ } else {
261
+ // NOTE: if we're in `componentDidUpdate` and we don't have a trap yet,
262
+ // it either means it shouldn't be active, or it should be but none of
263
+ // of given `containerElements` were present in the DOM the last time
264
+ // we tried to create the trap
265
+ if (prevProps.containerElements !== this.props.containerElements) {
266
+ this.focusTrapElements = this.props.containerElements;
267
+ } // don't create the trap unless it should be active in case the consumer
268
+ // is still updating `focusTrapOptions`
269
+ // @see https://github.com/focus-trap/focus-trap-react/issues/539
270
+
271
+
272
+ if (this.props.active) {
273
+ this.updatePreviousElement();
274
+ this.setupFocusTrap();
275
+ }
242
276
  }
243
277
  }
244
278
  }, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "focus-trap-react",
3
- "version": "8.8.2",
3
+ "version": "8.9.0",
4
4
  "description": "A React component that traps focus.",
5
5
  "main": "dist/focus-trap-react.js",
6
6
  "types": "index.d.ts",
@@ -55,40 +55,40 @@
55
55
  },
56
56
  "homepage": "https://github.com/focus-trap/focus-trap-react#readme",
57
57
  "devDependencies": {
58
- "@babel/cli": "^7.15.7",
59
- "@babel/core": "^7.15.8",
60
- "@babel/plugin-proposal-class-properties": "^7.12.13",
61
- "@babel/preset-env": "^7.15.8",
62
- "@babel/preset-react": "^7.14.5",
63
- "@changesets/cli": "^2.17.0",
64
- "@testing-library/cypress": "^8.0.1",
65
- "@testing-library/dom": "^8.7.2",
66
- "@testing-library/jest-dom": "^5.14.1",
58
+ "@babel/cli": "^7.16.0",
59
+ "@babel/core": "^7.16.0",
60
+ "@babel/eslint-parser": "^7.16.3",
61
+ "@babel/plugin-proposal-class-properties": "^7.16.0",
62
+ "@babel/preset-env": "^7.16.4",
63
+ "@babel/preset-react": "^7.16.0",
64
+ "@changesets/cli": "^2.18.1",
65
+ "@testing-library/cypress": "^8.0.2",
66
+ "@testing-library/dom": "^8.11.1",
67
+ "@testing-library/jest-dom": "^5.16.1",
67
68
  "@testing-library/react": "^12.1.2",
68
- "@testing-library/user-event": "^13.3.0",
69
- "@types/jquery": "^3.5.7",
69
+ "@testing-library/user-event": "^13.5.0",
70
+ "@types/jquery": "^3.5.9",
70
71
  "all-contributors-cli": "^6.20.0",
71
- "babel-eslint": "^10.1.0",
72
- "babel-jest": "^27.2.5",
72
+ "babel-jest": "^27.4.2",
73
73
  "babelify": "^10.0.0",
74
74
  "browserify": "^17.0.0",
75
75
  "budo": "^11.6.4",
76
- "cypress": "^8.6.0",
76
+ "cypress": "^9.1.1",
77
77
  "cypress-plugin-tab": "^1.0.5",
78
- "eslint": "^7.32.0",
78
+ "eslint": "^8.4.1",
79
79
  "eslint-config-prettier": "^8.3.0",
80
80
  "eslint-plugin-cypress": "^2.12.1",
81
- "eslint-plugin-react": "^7.26.1",
82
- "jest": "^27.2.5",
81
+ "eslint-plugin-react": "^7.27.1",
82
+ "jest": "^27.4.3",
83
83
  "jest-watch-typeahead": "^1.0.0",
84
84
  "onchange": "^7.1.0",
85
- "prettier": "^2.4.1",
85
+ "prettier": "^2.5.1",
86
86
  "prop-types": "^15.7.2",
87
87
  "react": "^17.0.2",
88
88
  "react-dom": "^17.0.2",
89
89
  "regenerator-runtime": "^0.13.9",
90
90
  "start-server-and-test": "^1.14.0",
91
- "typescript": "^4.4.4"
91
+ "typescript": "^4.5.2"
92
92
  },
93
93
  "dependencies": {
94
94
  "focus-trap": "^6.7.1"
@@ -53,6 +53,20 @@ class FocusTrap extends React.Component {
53
53
  this.updatePreviousElement();
54
54
  }
55
55
 
56
+ /**
57
+ * Gets the configured document.
58
+ * @returns {Document|undefined} Configured document, falling back to the main
59
+ * document, if it exists. During SSR, `undefined` is returned since the
60
+ * document doesn't exist.
61
+ */
62
+ getDocument() {
63
+ // SSR: careful to check if `document` exists before accessing it as a variable
64
+ return (
65
+ this.props.focusTrapOptions.document ||
66
+ (typeof document !== 'undefined' ? document : undefined)
67
+ );
68
+ }
69
+
56
70
  // TODO: Need more test coverage for this function
57
71
  getNodeForOption(optionName) {
58
72
  const optionValue = this.tailoredFocusTrapOptions[optionName];
@@ -63,7 +77,7 @@ class FocusTrap extends React.Component {
63
77
  let node = optionValue;
64
78
 
65
79
  if (typeof optionValue === 'string') {
66
- node = document.querySelector(optionValue);
80
+ node = this.getDocument()?.querySelector(optionValue);
67
81
  if (!node) {
68
82
  throw new Error(`\`${optionName}\` refers to no known node`);
69
83
  }
@@ -87,10 +101,7 @@ class FocusTrap extends React.Component {
87
101
 
88
102
  /** Update the previously focused element with the currently focused element. */
89
103
  updatePreviousElement() {
90
- // SSR: careful to check if `document` exists before accessing it as a variable
91
- const currentDocument =
92
- this.props.focusTrapOptions.document ||
93
- (typeof document !== 'undefined' ? document : undefined);
104
+ const currentDocument = this.getDocument();
94
105
  if (currentDocument) {
95
106
  this.previouslyFocusedElement = currentDocument.activeElement;
96
107
  }
@@ -161,7 +172,14 @@ class FocusTrap extends React.Component {
161
172
  }
162
173
 
163
174
  componentDidMount() {
164
- this.setupFocusTrap();
175
+ if (this.props.active) {
176
+ this.setupFocusTrap();
177
+ }
178
+ // else, wait for later activation in case the `focusTrapOptions` will be updated
179
+ // again before the trap is activated (e.g. if waiting to know what the document
180
+ // object will be, so the Trap must be rendered, but the consumer is waiting to
181
+ // activate until they have obtained the document from a ref)
182
+ // @see https://github.com/focus-trap/focus-trap-react/issues/539
165
183
  }
166
184
 
167
185
  componentDidUpdate(prevProps) {
@@ -192,9 +210,23 @@ class FocusTrap extends React.Component {
192
210
  if (hasUnpaused) {
193
211
  this.focusTrap.unpause();
194
212
  }
195
- } else if (prevProps.containerElements !== this.props.containerElements) {
196
- this.focusTrapElements = this.props.containerElements;
197
- this.setupFocusTrap();
213
+ } else {
214
+ // NOTE: if we're in `componentDidUpdate` and we don't have a trap yet,
215
+ // it either means it shouldn't be active, or it should be but none of
216
+ // of given `containerElements` were present in the DOM the last time
217
+ // we tried to create the trap
218
+
219
+ if (prevProps.containerElements !== this.props.containerElements) {
220
+ this.focusTrapElements = this.props.containerElements;
221
+ }
222
+
223
+ // don't create the trap unless it should be active in case the consumer
224
+ // is still updating `focusTrapOptions`
225
+ // @see https://github.com/focus-trap/focus-trap-react/issues/539
226
+ if (this.props.active) {
227
+ this.updatePreviousElement();
228
+ this.setupFocusTrap();
229
+ }
198
230
  }
199
231
  }
200
232