@primer/react 38.6.3-rc.16be9114c → 38.6.3-rc.9e02ccb41

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.

Potentially problematic release.


This version of @primer/react might be problematic. Click here for more details.

package/CHANGELOG.md CHANGED
@@ -11,6 +11,12 @@
11
11
  Split AutocompleteContext into separate contexts for static values, setters, and dynamic state.
12
12
  Components now subscribe only to the context slices they need, reducing re-renders.
13
13
 
14
+ - [#7342](https://github.com/primer/react/pull/7342) [`a8b42b2`](https://github.com/primer/react/commit/a8b42b2e4cff9575eae8df897e64c1ca67ee180a) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(hasInteractiveNodes): Optimize with combined selector and early attribute checks
15
+
16
+ - Use combined querySelectorAll selector instead of recursive traversal
17
+ - Check attribute-based states (disabled, hidden, inert) before getComputedStyle
18
+ - Only call getComputedStyle when CSS-based visibility check is needed
19
+
14
20
  ## 38.6.2
15
21
 
16
22
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"hasInteractiveNodes.d.ts","sourceRoot":"","sources":["../../../src/internal/utils/hasInteractiveNodes.ts"],"names":[],"mappings":"AAwBA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,WAUxF"}
1
+ {"version":3,"file":"hasInteractiveNodes.d.ts","sourceRoot":"","sources":["../../../src/internal/utils/hasInteractiveNodes.ts"],"names":[],"mappings":"AA2BA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,WAkBxF"}
@@ -7,6 +7,9 @@ const nonValidSelectors = {
7
7
  const interactiveElementsSelectors = [`a[href]`, `button`, 'summary', 'select', 'input:not([type=hidden])', 'textarea', '[tabindex="0"]', `audio[controls]`, `video[controls]`, `[contenteditable]`];
8
8
  const interactiveElements = interactiveElementsSelectors.map(selector => `${selector}:not(${Object.values(nonValidSelectors).join('):not(')})`);
9
9
 
10
+ // Combined selector for fast querySelector check
11
+ const interactiveSelector = interactiveElements.join(', ');
12
+
10
13
  /**
11
14
  * Finds interactive nodes within the passed node.
12
15
  * If the node itself is interactive, or children within are, it will return true.
@@ -23,26 +26,29 @@ function hasInteractiveNodes(node, ignoreNodes) {
23
26
  // If one does exist, we can abort early.
24
27
 
25
28
  const nodesToIgnore = [node];
26
- const interactiveNodes = findInteractiveChildNodes(node, nodesToIgnore);
27
- return Boolean(interactiveNodes);
29
+
30
+ // Performance optimization: Use querySelectorAll with combined selector first
31
+ // This avoids recursive getComputedStyle calls for each node
32
+ const candidates = node.querySelectorAll(interactiveSelector);
33
+ for (const candidate of candidates) {
34
+ if (!nodesToIgnore.includes(candidate) && !isNonValidInteractiveNode(candidate)) {
35
+ return true;
36
+ }
37
+ }
38
+ return false;
28
39
  }
40
+
41
+ // Note: Only call getComputedStyle when CSS-based checks are insufficient
29
42
  function isNonValidInteractiveNode(node) {
30
- const nodeStyle = getComputedStyle(node);
43
+ // Fast path: Check attribute-based states first (no style recalc needed)
31
44
  const isNonInteractive = node.matches('[disabled], [hidden], [inert]');
45
+ if (isNonInteractive) return true;
46
+
47
+ // Only call getComputedStyle if attribute checks passed
48
+ // This is necessary for display:none and visibility:hidden which aren't detectable via attributes
49
+ const nodeStyle = getComputedStyle(node);
32
50
  const isHiddenVisually = nodeStyle.display === 'none' || nodeStyle.visibility === 'hidden';
33
- return isNonInteractive || isHiddenVisually;
34
- }
35
- function findInteractiveChildNodes(node, ignoreNodes) {
36
- if (!node) return;
37
- const ignoreSelector = ignoreNodes.find(elem => elem === node);
38
- const isNotValidNode = isNonValidInteractiveNode(node);
39
- if (node.matches(interactiveElements.join(', ')) && !ignoreSelector && !isNotValidNode) {
40
- return node;
41
- }
42
- for (const child of node.children) {
43
- const interactiveNode = findInteractiveChildNodes(child, ignoreNodes);
44
- if (interactiveNode) return true;
45
- }
51
+ return isHiddenVisually;
46
52
  }
47
53
 
48
54
  export { hasInteractiveNodes };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@primer/react",
3
3
  "type": "module",
4
- "version": "38.6.3-rc.16be9114c",
4
+ "version": "38.6.3-rc.9e02ccb41",
5
5
  "description": "An implementation of GitHub's Primer Design System using React",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",