@kaizen/components 0.0.0-canary-fix-filter-select-jump-issue-useHasStableYPosition-20250120225815 → 0.0.0-canary-fix-filter-select-jump-issue-useHasStableYPosition-test-20250129223936

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.
@@ -6,10 +6,10 @@ var React = require('react');
6
6
  * Due to the floating element's position starting as a negative value on render and then jumping to the correct position, this caused the focus to jump to the top of the page.
7
7
  * This now polls to check if the element's position is stable by comparing the first and last position.
8
8
  */
9
- var useHasStableYPosition = function (ref) {
9
+ var useHasCalculatedListboxPosition = function (ref) {
10
10
  var _a = React.useState(false),
11
- isStable = _a[0],
12
- setIsStable = _a[1];
11
+ hasStablePosition = _a[0],
12
+ setHasStablePosition = _a[1];
13
13
  var _b = React.useState(null),
14
14
  lastYPosition = _b[0],
15
15
  setLastYPosition = _b[1];
@@ -19,18 +19,24 @@ var useHasStableYPosition = function (ref) {
19
19
  var y = ref.current.getBoundingClientRect().y;
20
20
  if (lastYPosition === null) {
21
21
  setLastYPosition(y);
22
- } else if (y === lastYPosition) {
23
- setIsStable(true);
22
+ } else if (y === lastYPosition && y >= 0) {
23
+ setHasStablePosition(true);
24
24
  } else {
25
25
  setLastYPosition(y);
26
26
  }
27
27
  }
28
28
  };
29
- var intervalId = setInterval(checkPosition, 1);
29
+ var intervalId = setInterval(function () {
30
+ if (hasStablePosition) {
31
+ clearInterval(intervalId);
32
+ return;
33
+ }
34
+ checkPosition();
35
+ }, 1);
30
36
  return function () {
31
37
  return clearInterval(intervalId);
32
38
  };
33
- }, [ref, lastYPosition]);
34
- return isStable;
39
+ }, [ref, lastYPosition, hasStablePosition]);
40
+ return hasStablePosition;
35
41
  };
36
- exports.useHasStableYPosition = useHasStableYPosition;
42
+ exports.useHasCalculatedListboxPosition = useHasCalculatedListboxPosition;
@@ -5,7 +5,7 @@ var React = require('react');
5
5
  var listbox = require('@react-aria/listbox');
6
6
  var classnames = require('classnames');
7
7
  var SelectContext = require('../../context/SelectContext.cjs');
8
- var useHasStableYPosition = require('../../hooks/useHasStableYPosition.cjs');
8
+ var useHasCalculatedListboxPosition = require('../../hooks/useHasCalculatedListboxPosition.cjs');
9
9
  var ListBox_module = require('./ListBox.module.scss.cjs');
10
10
  function _interopDefault(e) {
11
11
  return e && e.__esModule ? e : {
@@ -15,17 +15,30 @@ function _interopDefault(e) {
15
15
  var React__default = /*#__PURE__*/_interopDefault(React);
16
16
  var classnames__default = /*#__PURE__*/_interopDefault(classnames);
17
17
 
18
- /** A util to retrieve the key of the correct focusable items based of the focus strategy
18
+ /** determines is the first or last key passed in is a section. If not it will return the key, otherwise will return the first option key of that section */
19
+ var getOptionOrSectionKey = function (optionKey, state) {
20
+ var _a, _b;
21
+ if (!optionKey) return null;
22
+ var option = state.collection.getItem(optionKey);
23
+ var optionType = option === null || option === undefined ? undefined : option.type;
24
+ if (optionType === 'section') {
25
+ var sectionOptions = (_a = option === null || option === undefined ? undefined : option.value) === null || _a === undefined ? undefined : _a.options;
26
+ return sectionOptions ? (_b = Array.from(sectionOptions)[0]) === null || _b === undefined ? undefined : _b.value : null;
27
+ }
28
+ return optionKey;
29
+ };
30
+ /** A util to retrieve the key of the correct focusable option based of the focus strategy
19
31
  * This is used to determine which element from the collection to focus to on open base on the keyboard event
20
32
  * ie: UpArrow will set the focusStrategy to "last"
21
33
  */
22
34
  var getOptionKeyFromCollection = function (state) {
23
35
  if (state.selectedItem) {
24
36
  return state.selectedItem.key;
25
- } else if (state.focusStrategy === 'last') {
26
- return state.collection.getLastKey();
27
37
  }
28
- return state.collection.getFirstKey();
38
+ if (state.focusStrategy === 'last') {
39
+ return getOptionOrSectionKey(state.collection.getLastKey(), state);
40
+ }
41
+ return getOptionOrSectionKey(state.collection.getFirstKey(), state);
29
42
  };
30
43
  /** This makes the use of query selector less brittle in instances where a failed selector is passed in
31
44
  */
@@ -45,18 +58,18 @@ var ListBox = function (_a) {
45
58
  restProps = tslib.__rest(_a, ["children", "menuProps", "classNameOverride"]);
46
59
  var state = SelectContext.useSelectContext().state;
47
60
  var ref = React.useRef(null);
48
- var hasStableYPosition = useHasStableYPosition.useHasStableYPosition(ref);
61
+ var hasCalculatedListboxPosition = useHasCalculatedListboxPosition.useHasCalculatedListboxPosition(ref);
49
62
  var listBoxProps = listbox.useListBox(tslib.__assign(tslib.__assign({}, menuProps), {
50
63
  disallowEmptySelection: true,
51
64
  // This is to ensure that the listbox doesn't use React Aria's auto focus feature for Listbox, which creates a visual bug
52
65
  autoFocus: false
53
66
  }), state, ref).listBoxProps;
54
67
  /**
55
- * This uses the hasStableYPosition to determine if the position is stable within the window
68
+ * When the Listbox is opened the initial position starts above the window, which can cause the out of the box behaviour in react-aria's listbox to jump a user to the top of the page.
56
69
  */
57
70
  React.useEffect(function () {
58
71
  var _a;
59
- if (hasStableYPosition) {
72
+ if (hasCalculatedListboxPosition) {
60
73
  var optionKey = getOptionKeyFromCollection(state);
61
74
  var focusToElement = safeQuerySelector("[data-key='".concat(optionKey, "']"));
62
75
  if (focusToElement) {
@@ -67,7 +80,7 @@ var ListBox = function (_a) {
67
80
  }
68
81
  // Only run this effect for checking the first successful render
69
82
  // eslint-disable-next-line react-hooks/exhaustive-deps
70
- }, [hasStableYPosition]);
83
+ }, [hasCalculatedListboxPosition]);
71
84
  return React__default.default.createElement("ul", tslib.__assign({
72
85
  ref: ref,
73
86
  className: classnames__default.default(ListBox_module.listBox, classNameOverride)
@@ -4,10 +4,10 @@ import { useState, useEffect } from 'react';
4
4
  * Due to the floating element's position starting as a negative value on render and then jumping to the correct position, this caused the focus to jump to the top of the page.
5
5
  * This now polls to check if the element's position is stable by comparing the first and last position.
6
6
  */
7
- var useHasStableYPosition = function (ref) {
7
+ var useHasCalculatedListboxPosition = function (ref) {
8
8
  var _a = useState(false),
9
- isStable = _a[0],
10
- setIsStable = _a[1];
9
+ hasStablePosition = _a[0],
10
+ setHasStablePosition = _a[1];
11
11
  var _b = useState(null),
12
12
  lastYPosition = _b[0],
13
13
  setLastYPosition = _b[1];
@@ -17,18 +17,24 @@ var useHasStableYPosition = function (ref) {
17
17
  var y = ref.current.getBoundingClientRect().y;
18
18
  if (lastYPosition === null) {
19
19
  setLastYPosition(y);
20
- } else if (y === lastYPosition) {
21
- setIsStable(true);
20
+ } else if (y === lastYPosition && y >= 0) {
21
+ setHasStablePosition(true);
22
22
  } else {
23
23
  setLastYPosition(y);
24
24
  }
25
25
  }
26
26
  };
27
- var intervalId = setInterval(checkPosition, 1);
27
+ var intervalId = setInterval(function () {
28
+ if (hasStablePosition) {
29
+ clearInterval(intervalId);
30
+ return;
31
+ }
32
+ checkPosition();
33
+ }, 1);
28
34
  return function () {
29
35
  return clearInterval(intervalId);
30
36
  };
31
- }, [ref, lastYPosition]);
32
- return isStable;
37
+ }, [ref, lastYPosition, hasStablePosition]);
38
+ return hasStablePosition;
33
39
  };
34
- export { useHasStableYPosition };
40
+ export { useHasCalculatedListboxPosition };
@@ -3,20 +3,33 @@ import React, { useRef, useEffect } from 'react';
3
3
  import { useListBox } from '@react-aria/listbox';
4
4
  import classnames from 'classnames';
5
5
  import { useSelectContext } from '../../context/SelectContext.mjs';
6
- import { useHasStableYPosition } from '../../hooks/useHasStableYPosition.mjs';
6
+ import { useHasCalculatedListboxPosition } from '../../hooks/useHasCalculatedListboxPosition.mjs';
7
7
  import styles from './ListBox.module.scss.mjs';
8
8
 
9
- /** A util to retrieve the key of the correct focusable items based of the focus strategy
9
+ /** determines is the first or last key passed in is a section. If not it will return the key, otherwise will return the first option key of that section */
10
+ var getOptionOrSectionKey = function (optionKey, state) {
11
+ var _a, _b;
12
+ if (!optionKey) return null;
13
+ var option = state.collection.getItem(optionKey);
14
+ var optionType = option === null || option === undefined ? undefined : option.type;
15
+ if (optionType === 'section') {
16
+ var sectionOptions = (_a = option === null || option === undefined ? undefined : option.value) === null || _a === undefined ? undefined : _a.options;
17
+ return sectionOptions ? (_b = Array.from(sectionOptions)[0]) === null || _b === undefined ? undefined : _b.value : null;
18
+ }
19
+ return optionKey;
20
+ };
21
+ /** A util to retrieve the key of the correct focusable option based of the focus strategy
10
22
  * This is used to determine which element from the collection to focus to on open base on the keyboard event
11
23
  * ie: UpArrow will set the focusStrategy to "last"
12
24
  */
13
25
  var getOptionKeyFromCollection = function (state) {
14
26
  if (state.selectedItem) {
15
27
  return state.selectedItem.key;
16
- } else if (state.focusStrategy === 'last') {
17
- return state.collection.getLastKey();
18
28
  }
19
- return state.collection.getFirstKey();
29
+ if (state.focusStrategy === 'last') {
30
+ return getOptionOrSectionKey(state.collection.getLastKey(), state);
31
+ }
32
+ return getOptionOrSectionKey(state.collection.getFirstKey(), state);
20
33
  };
21
34
  /** This makes the use of query selector less brittle in instances where a failed selector is passed in
22
35
  */
@@ -37,18 +50,18 @@ const ListBox = /*#__PURE__*/function () {
37
50
  restProps = __rest(_a, ["children", "menuProps", "classNameOverride"]);
38
51
  var state = useSelectContext().state;
39
52
  var ref = useRef(null);
40
- var hasStableYPosition = useHasStableYPosition(ref);
53
+ var hasCalculatedListboxPosition = useHasCalculatedListboxPosition(ref);
41
54
  var listBoxProps = useListBox(__assign(__assign({}, menuProps), {
42
55
  disallowEmptySelection: true,
43
56
  // This is to ensure that the listbox doesn't use React Aria's auto focus feature for Listbox, which creates a visual bug
44
57
  autoFocus: false
45
58
  }), state, ref).listBoxProps;
46
59
  /**
47
- * This uses the hasStableYPosition to determine if the position is stable within the window
60
+ * When the Listbox is opened the initial position starts above the window, which can cause the out of the box behaviour in react-aria's listbox to jump a user to the top of the page.
48
61
  */
49
62
  useEffect(function () {
50
63
  var _a;
51
- if (hasStableYPosition) {
64
+ if (hasCalculatedListboxPosition) {
52
65
  var optionKey = getOptionKeyFromCollection(state);
53
66
  var focusToElement = safeQuerySelector("[data-key='".concat(optionKey, "']"));
54
67
  if (focusToElement) {
@@ -59,7 +72,7 @@ const ListBox = /*#__PURE__*/function () {
59
72
  }
60
73
  // Only run this effect for checking the first successful render
61
74
  // eslint-disable-next-line react-hooks/exhaustive-deps
62
- }, [hasStableYPosition]);
75
+ }, [hasCalculatedListboxPosition]);
63
76
  return /*#__PURE__*/React.createElement("ul", __assign({
64
77
  ref: ref,
65
78
  className: classnames(styles.listBox, classNameOverride)