@kaizen/components 0.0.0-canary-test-fms-popover-api-update-20250624021814 → 0.0.0-canary-fix-tab-20250626012516

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.
Files changed (31) hide show
  1. package/dist/cjs/src/Filter/FilterMultiSelect/FilterMultiSelect.cjs +3 -7
  2. package/dist/cjs/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.cjs +1 -1
  3. package/dist/cjs/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.module.scss.cjs +9 -0
  4. package/dist/cjs/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.cjs +38 -57
  5. package/dist/cjs/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/{MenuPopup.module.css.cjs → MenuPopup.module.scss.cjs} +1 -1
  6. package/dist/esm/src/Filter/FilterMultiSelect/FilterMultiSelect.mjs +3 -7
  7. package/dist/esm/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.mjs +1 -1
  8. package/dist/esm/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.module.scss.mjs +7 -0
  9. package/dist/esm/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.mjs +41 -58
  10. package/dist/esm/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.module.scss.mjs +4 -0
  11. package/dist/styles.css +22 -29
  12. package/dist/types/Filter/FilterMultiSelect/FilterMultiSelect.d.ts +1 -1
  13. package/dist/types/Filter/FilterMultiSelect/_docs/MockData.d.ts +0 -1
  14. package/dist/types/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.d.ts +4 -7
  15. package/package.json +1 -1
  16. package/src/Filter/FilterBar/subcomponents/FilterBarMultiSelect/FilterBarMultiSelect.spec.tsx +0 -1
  17. package/src/Filter/FilterMultiSelect/FilterMultiSelect.tsx +2 -3
  18. package/src/Filter/FilterMultiSelect/_docs/FilterMultiSelect.mdx +0 -8
  19. package/src/Filter/FilterMultiSelect/_docs/FilterMultiSelect.stories.tsx +1 -89
  20. package/src/Filter/FilterMultiSelect/_docs/MockData.ts +0 -39
  21. package/src/Filter/FilterMultiSelect/context/MenuTriggerProvider/MenuTriggerProvider.spec.tsx +18 -2
  22. package/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.module.scss +25 -0
  23. package/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.tsx +1 -1
  24. package/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.module.scss +24 -0
  25. package/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.tsx +42 -62
  26. package/src/__next__/Tabs/subcomponents/TabList/TabList.module.css +1 -1
  27. package/dist/cjs/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.module.css.cjs +0 -9
  28. package/dist/esm/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.module.css.mjs +0 -7
  29. package/dist/esm/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.module.css.mjs +0 -4
  30. package/src/Filter/FilterMultiSelect/subcomponents/ListBox/ListBox.module.css +0 -22
  31. package/src/Filter/FilterMultiSelect/subcomponents/MenuPopup/MenuPopup.module.css +0 -22
@@ -41,8 +41,7 @@ var FilterMultiSelect = function (_a) {
41
41
  selectionMode = _b === void 0 ? 'multiple' : _b,
42
42
  onSearchInputChange = _a.onSearchInputChange,
43
43
  triggerRef = _a.triggerRef,
44
- className = _a.className,
45
- floatingOptions = _a.floatingOptions;
44
+ className = _a.className;
46
45
  var menuTriggerProps = {
47
46
  isOpen: isOpen,
48
47
  defaultOpen: defaultOpen,
@@ -51,8 +50,7 @@ var FilterMultiSelect = function (_a) {
51
50
  };
52
51
  var menuPopupProps = {
53
52
  isLoading: isLoading,
54
- loadingSkeleton: loadingSkeleton,
55
- floatingOptions: floatingOptions
53
+ loadingSkeleton: loadingSkeleton
56
54
  };
57
55
  var disabledKeys = new Set(items === null || items === void 0 ? void 0 : items.filter(function (item) {
58
56
  return item.isDisabled === true;
@@ -71,9 +69,7 @@ var FilterMultiSelect = function (_a) {
71
69
  };
72
70
  return React__default.default.createElement(MenuTriggerProvider.MenuTriggerProvider, tslib.__assign({}, menuTriggerProps), React__default.default.createElement("div", {
73
71
  className: className
74
- }, React__default.default.createElement(MenuTriggerProvider.MenuTriggerConsumer, null, trigger), React__default.default.createElement(MenuPopup.MenuPopup, tslib.__assign({
75
- "aria-label": label
76
- }, menuPopupProps), React__default.default.createElement(SelectionProvider.SelectionProvider, tslib.__assign({}, selectionProps), React__default.default.createElement(SelectionProvider.SelectionConsumer, null, children)))));
72
+ }, React__default.default.createElement(MenuTriggerProvider.MenuTriggerConsumer, null, trigger), React__default.default.createElement(MenuPopup.MenuPopup, tslib.__assign({}, menuPopupProps), React__default.default.createElement(SelectionProvider.SelectionProvider, tslib.__assign({}, selectionProps), React__default.default.createElement(SelectionProvider.SelectionConsumer, null, children)))));
77
73
  };
78
74
  FilterMultiSelect.displayName = 'FilterMultiSelect';
79
75
  FilterMultiSelect.TriggerButton = FilterTriggerButton.FilterTriggerButton;
@@ -4,7 +4,7 @@ var tslib = require('tslib');
4
4
  var React = require('react');
5
5
  var classnames = require('classnames');
6
6
  var SelectionProvider = require('../../context/SelectionProvider/SelectionProvider.cjs');
7
- var ListBox_module = require('./ListBox.module.css.cjs');
7
+ var ListBox_module = require('./ListBox.module.scss.cjs');
8
8
  function _interopDefault(e) {
9
9
  return e && e.__esModule ? e : {
10
10
  default: e
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ var styles = {
4
+ "listBox": "ListBox-module_listBox__q95MO",
5
+ "overflown": "ListBox-module_overflown__wChQA",
6
+ "hidden": "ListBox-module_hidden__eYdXv",
7
+ "noResultsWrapper": "ListBox-module_noResultsWrapper__WcLRm"
8
+ };
9
+ module.exports = styles;
@@ -2,74 +2,55 @@
2
2
 
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
- var reactDom = require('@floating-ui/react-dom');
6
- var classnames = require('classnames');
7
- var reactFocusOn = require('react-focus-on');
5
+ var focus = require('@react-aria/focus');
6
+ var overlays = require('@react-aria/overlays');
8
7
  var MenuTriggerProvider = require('../../context/MenuTriggerProvider/MenuTriggerProvider.cjs');
9
8
  require('../../context/SelectionProvider/SelectionProvider.cjs');
10
- var MenuPopup_module = require('./MenuPopup.module.css.cjs');
9
+ var MenuPopup_module = require('./MenuPopup.module.scss.cjs');
11
10
  function _interopDefault(e) {
12
11
  return e && e.__esModule ? e : {
13
12
  default: e
14
13
  };
15
14
  }
16
15
  var React__default = /*#__PURE__*/_interopDefault(React);
17
- var classnames__default = /*#__PURE__*/_interopDefault(classnames);
18
16
  var MenuPopup = function (_a) {
19
- var children = _a.children,
20
- floatingOptions = _a.floatingOptions,
21
- classNameOverride = _a.classNameOverride,
22
- isLoading = _a.isLoading,
17
+ var isLoading = _a.isLoading,
23
18
  loadingSkeleton = _a.loadingSkeleton,
24
- restProps = tslib.__rest(_a, ["children", "floatingOptions", "classNameOverride", "isLoading", "loadingSkeleton"]);
25
- var _b = React.useState(null),
26
- floatingElement = _b[0],
27
- setFloatingElement = _b[1];
28
- var _c = MenuTriggerProvider.useMenuTriggerContext(),
29
- menuTriggerState = _c.menuTriggerState,
30
- buttonRef = _c.buttonRef;
31
- var referenceElement = buttonRef.current;
32
- var _d = reactDom.useFloating(tslib.__assign({
33
- placement: 'bottom-start',
34
- elements: {
35
- reference: referenceElement,
36
- floating: floatingElement
37
- },
38
- strategy: 'absolute',
39
- middleware: [reactDom.offset(6)],
40
- whileElementsMounted: reactDom.autoUpdate
41
- }, floatingOptions)),
42
- floatingStyles = _d.floatingStyles,
43
- update = _d.update;
44
- var handleReturnFocus = function () {
45
- requestAnimationFrame(function () {
46
- var _a;
47
- (_a = buttonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
48
- });
19
+ children = _a.children;
20
+ var menuTriggerState = MenuTriggerProvider.useMenuTriggerContext().menuTriggerState;
21
+ var onClose = function () {
22
+ return menuTriggerState.close();
49
23
  };
50
- React.useEffect(function () {
51
- var _a;
52
- if (floatingElement && referenceElement) {
53
- (_a = floatingElement.showPopover) === null || _a === void 0 ? void 0 : _a.call(floatingElement);
54
- update();
55
- }
56
- }, [floatingElement, referenceElement, update]);
57
- return menuTriggerState.isOpen ? React__default.default.createElement(reactFocusOn.FocusOn, {
58
- enabled: menuTriggerState.isOpen,
59
- scrollLock: false,
60
- returnFocus: false,
61
- onClickOutside: menuTriggerState.close,
62
- onEscapeKey: menuTriggerState.close,
63
- onDeactivation: handleReturnFocus
64
- }, React__default.default.createElement("div", tslib.__assign({
65
- ref: setFloatingElement,
66
- style: floatingStyles,
67
- className: classnames__default.default(MenuPopup_module.menuPopup, classNameOverride),
68
- role: "dialog",
69
- "aria-modal": "true",
70
- // @ts-expect-error: popover is valid in supported browsers
71
- popover: "manual"
72
- }, restProps), isLoading && loadingSkeleton ? loadingSkeleton : children)) : React__default.default.createElement(React__default.default.Fragment, null);
24
+ // Handle events that should cause the menu to close,
25
+ // e.g. blur, clicking outside, or pressing the escape key.
26
+ var overlayRef = React__default.default.createRef();
27
+ var overlayProps = overlays.useOverlay({
28
+ onClose: onClose,
29
+ isOpen: menuTriggerState.isOpen,
30
+ isDismissable: true
31
+ }, overlayRef).overlayProps;
32
+ // Wrap in <FocusScope> so that focus is restored back to the trigger when the menu is closed
33
+ // and auto focus on the first focusable item after loading. (disable eslint no-autofocus error for it)
34
+ // In addition, add hidden <DismissButton> components at the start and end of the list
35
+ // to allow screen reader users to dismiss the popup easily.
36
+ return menuTriggerState.isOpen ? React__default.default.createElement("div", tslib.__assign({}, overlayProps, {
37
+ ref: overlayRef,
38
+ className: MenuPopup_module.menuPopup
39
+ }), isLoading && loadingSkeleton ? React__default.default.createElement(React__default.default.Fragment, null, React__default.default.createElement(overlays.DismissButton, {
40
+ onDismiss: onClose
41
+ }), loadingSkeleton, React__default.default.createElement(overlays.DismissButton, {
42
+ onDismiss: onClose
43
+ })) :
44
+ // eslint-disable-next-line jsx-a11y/no-autofocus
45
+ React__default.default.createElement(focus.FocusScope, {
46
+ contain: true,
47
+ autoFocus: true,
48
+ restoreFocus: true
49
+ }, React__default.default.createElement(overlays.DismissButton, {
50
+ onDismiss: onClose
51
+ }), children, React__default.default.createElement(overlays.DismissButton, {
52
+ onDismiss: onClose
53
+ }))) : React__default.default.createElement(React__default.default.Fragment, null);
73
54
  };
74
55
  MenuPopup.displayName = 'FilterMultiSelect.MenuPopup';
75
56
  exports.MenuPopup = MenuPopup;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var styles = {
4
- "menuPopup": "MenuPopup-module_menuPopup__QgGEa"
4
+ "menuPopup": "MenuPopup-module_menuPopup__UVgnP"
5
5
  };
6
6
  module.exports = styles;
@@ -34,8 +34,7 @@ const FilterMultiSelect = /*#__PURE__*/function () {
34
34
  selectionMode = _b === void 0 ? 'multiple' : _b,
35
35
  onSearchInputChange = _a.onSearchInputChange,
36
36
  triggerRef = _a.triggerRef,
37
- className = _a.className,
38
- floatingOptions = _a.floatingOptions;
37
+ className = _a.className;
39
38
  var menuTriggerProps = {
40
39
  isOpen: isOpen,
41
40
  defaultOpen: defaultOpen,
@@ -44,8 +43,7 @@ const FilterMultiSelect = /*#__PURE__*/function () {
44
43
  };
45
44
  var menuPopupProps = {
46
45
  isLoading: isLoading,
47
- loadingSkeleton: loadingSkeleton,
48
- floatingOptions: floatingOptions
46
+ loadingSkeleton: loadingSkeleton
49
47
  };
50
48
  var disabledKeys = new Set(items === null || items === void 0 ? void 0 : items.filter(function (item) {
51
49
  return item.isDisabled === true;
@@ -64,9 +62,7 @@ const FilterMultiSelect = /*#__PURE__*/function () {
64
62
  };
65
63
  return /*#__PURE__*/React.createElement(MenuTriggerProvider, __assign({}, menuTriggerProps), /*#__PURE__*/React.createElement("div", {
66
64
  className: className
67
- }, /*#__PURE__*/React.createElement(MenuTriggerConsumer, null, trigger), /*#__PURE__*/React.createElement(MenuPopup, __assign({
68
- "aria-label": label
69
- }, menuPopupProps), /*#__PURE__*/React.createElement(SelectionProvider, __assign({}, selectionProps), /*#__PURE__*/React.createElement(SelectionConsumer, null, children)))));
65
+ }, /*#__PURE__*/React.createElement(MenuTriggerConsumer, null, trigger), /*#__PURE__*/React.createElement(MenuPopup, __assign({}, menuPopupProps), /*#__PURE__*/React.createElement(SelectionProvider, __assign({}, selectionProps), /*#__PURE__*/React.createElement(SelectionConsumer, null, children)))));
70
66
  };
71
67
  FilterMultiSelect.displayName = 'FilterMultiSelect';
72
68
  FilterMultiSelect.TriggerButton = FilterTriggerButton;
@@ -2,7 +2,7 @@ import { __assign, __spreadArray } from 'tslib';
2
2
  import React, { useState, useEffect } from 'react';
3
3
  import classnames from 'classnames';
4
4
  import { useSelectionContext } from '../../context/SelectionProvider/SelectionProvider.mjs';
5
- import styles from './ListBox.module.css.mjs';
5
+ import styles from './ListBox.module.scss.mjs';
6
6
  var getItemsFromKeys = function (items, keys) {
7
7
  var itemKeys = Array.from(keys);
8
8
  return itemKeys.reduce(function (acc, itemKey) {
@@ -0,0 +1,7 @@
1
+ var styles = {
2
+ "listBox": "ListBox-module_listBox__q95MO",
3
+ "overflown": "ListBox-module_overflown__wChQA",
4
+ "hidden": "ListBox-module_hidden__eYdXv",
5
+ "noResultsWrapper": "ListBox-module_noResultsWrapper__WcLRm"
6
+ };
7
+ export { styles as default };
@@ -1,67 +1,50 @@
1
- import { __rest, __assign } from 'tslib';
2
- import React, { useState, useEffect } from 'react';
3
- import { useFloating, autoUpdate, offset } from '@floating-ui/react-dom';
4
- import classnames from 'classnames';
5
- import { FocusOn } from 'react-focus-on';
1
+ import { __assign } from 'tslib';
2
+ import React from 'react';
3
+ import { FocusScope } from '@react-aria/focus';
4
+ import { useOverlay, DismissButton } from '@react-aria/overlays';
6
5
  import { useMenuTriggerContext } from '../../context/MenuTriggerProvider/MenuTriggerProvider.mjs';
7
6
  import '../../context/SelectionProvider/SelectionProvider.mjs';
8
- import styles from './MenuPopup.module.css.mjs';
7
+ import styles from './MenuPopup.module.scss.mjs';
9
8
  const MenuPopup = /*#__PURE__*/function () {
10
9
  const MenuPopup = function (_a) {
11
- var children = _a.children,
12
- floatingOptions = _a.floatingOptions,
13
- classNameOverride = _a.classNameOverride,
14
- isLoading = _a.isLoading,
10
+ var isLoading = _a.isLoading,
15
11
  loadingSkeleton = _a.loadingSkeleton,
16
- restProps = __rest(_a, ["children", "floatingOptions", "classNameOverride", "isLoading", "loadingSkeleton"]);
17
- var _b = useState(null),
18
- floatingElement = _b[0],
19
- setFloatingElement = _b[1];
20
- var _c = useMenuTriggerContext(),
21
- menuTriggerState = _c.menuTriggerState,
22
- buttonRef = _c.buttonRef;
23
- var referenceElement = buttonRef.current;
24
- var _d = useFloating(__assign({
25
- placement: 'bottom-start',
26
- elements: {
27
- reference: referenceElement,
28
- floating: floatingElement
29
- },
30
- strategy: 'absolute',
31
- middleware: [offset(6)],
32
- whileElementsMounted: autoUpdate
33
- }, floatingOptions)),
34
- floatingStyles = _d.floatingStyles,
35
- update = _d.update;
36
- var handleReturnFocus = function () {
37
- requestAnimationFrame(function () {
38
- var _a;
39
- (_a = buttonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
40
- });
12
+ children = _a.children;
13
+ var menuTriggerState = useMenuTriggerContext().menuTriggerState;
14
+ var onClose = function () {
15
+ return menuTriggerState.close();
41
16
  };
42
- useEffect(function () {
43
- var _a;
44
- if (floatingElement && referenceElement) {
45
- (_a = floatingElement.showPopover) === null || _a === void 0 ? void 0 : _a.call(floatingElement);
46
- update();
47
- }
48
- }, [floatingElement, referenceElement, update]);
49
- return menuTriggerState.isOpen ? (/*#__PURE__*/React.createElement(FocusOn, {
50
- enabled: menuTriggerState.isOpen,
51
- scrollLock: false,
52
- returnFocus: false,
53
- onClickOutside: menuTriggerState.close,
54
- onEscapeKey: menuTriggerState.close,
55
- onDeactivation: handleReturnFocus
56
- }, /*#__PURE__*/React.createElement("div", __assign({
57
- ref: setFloatingElement,
58
- style: floatingStyles,
59
- className: classnames(styles.menuPopup, classNameOverride),
60
- role: "dialog",
61
- "aria-modal": "true",
62
- // @ts-expect-error: popover is valid in supported browsers
63
- popover: "manual"
64
- }, restProps), isLoading && loadingSkeleton ? loadingSkeleton : children))) : (/*#__PURE__*/React.createElement(React.Fragment, null));
17
+ // Handle events that should cause the menu to close,
18
+ // e.g. blur, clicking outside, or pressing the escape key.
19
+ var overlayRef = /*#__PURE__*/React.createRef();
20
+ var overlayProps = useOverlay({
21
+ onClose: onClose,
22
+ isOpen: menuTriggerState.isOpen,
23
+ isDismissable: true
24
+ }, overlayRef).overlayProps;
25
+ // Wrap in <FocusScope> so that focus is restored back to the trigger when the menu is closed
26
+ // and auto focus on the first focusable item after loading. (disable eslint no-autofocus error for it)
27
+ // In addition, add hidden <DismissButton> components at the start and end of the list
28
+ // to allow screen reader users to dismiss the popup easily.
29
+ return menuTriggerState.isOpen ? (/*#__PURE__*/React.createElement("div", __assign({}, overlayProps, {
30
+ ref: overlayRef,
31
+ className: styles.menuPopup
32
+ }), isLoading && loadingSkeleton ? (/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DismissButton, {
33
+ onDismiss: onClose
34
+ }), loadingSkeleton, /*#__PURE__*/React.createElement(DismissButton, {
35
+ onDismiss: onClose
36
+ }))) : (
37
+ /*#__PURE__*/
38
+ // eslint-disable-next-line jsx-a11y/no-autofocus
39
+ React.createElement(FocusScope, {
40
+ contain: true,
41
+ autoFocus: true,
42
+ restoreFocus: true
43
+ }, /*#__PURE__*/React.createElement(DismissButton, {
44
+ onDismiss: onClose
45
+ }), children, /*#__PURE__*/React.createElement(DismissButton, {
46
+ onDismiss: onClose
47
+ }))))) : (/*#__PURE__*/React.createElement(React.Fragment, null));
65
48
  };
66
49
  MenuPopup.displayName = 'FilterMultiSelect.MenuPopup';
67
50
  return MenuPopup;
@@ -0,0 +1,4 @@
1
+ var styles = {
2
+ "menuPopup": "MenuPopup-module_menuPopup__UVgnP"
3
+ };
4
+ export { styles as default };
package/dist/styles.css CHANGED
@@ -3743,29 +3743,26 @@
3743
3743
  }
3744
3744
  }
3745
3745
 
3746
+ /** THIS IS AN AUTOGENERATED FILE **/
3746
3747
  @layer kz-components {
3747
- .ListBox-module_listBox__HBScm {
3748
+ .ListBox-module_listBox__q95MO {
3748
3749
  list-style: none;
3749
- padding: var(--spacing-12);
3750
- margin: 0 var(--spacing-12) 0 0;
3750
+ padding: var(--spacing-sm, 0.75rem);
3751
+ margin: 0 var(--spacing-sm, 0.75rem) 0 0;
3751
3752
  display: grid;
3752
3753
  max-height: 22rem;
3753
3754
  overflow-y: auto;
3754
3755
  }
3755
-
3756
- .ListBox-module_overflown__PdKED {
3757
- padding-right: var(--spacing-12);
3756
+ .ListBox-module_overflown__wChQA {
3757
+ padding-right: var(--spacing-sm, 0.75rem);
3758
3758
  }
3759
-
3760
- .ListBox-module_hidden__mO-oL {
3759
+ .ListBox-module_hidden__eYdXv {
3761
3760
  display: none;
3762
3761
  }
3763
-
3764
- .ListBox-module_noResultsWrapper__RnMj0 {
3762
+ .ListBox-module_noResultsWrapper__WcLRm {
3765
3763
  list-style: none;
3766
3764
  }
3767
3765
  }
3768
-
3769
3766
  /** THIS IS AN AUTOGENERATED FILE **/
3770
3767
  /** THIS IS AN AUTOGENERATED FILE **/
3771
3768
  /** THIS IS AN AUTOGENERATED FILE **/
@@ -3810,29 +3807,25 @@
3810
3807
  margin-right: var(--spacing-sm, 0.75rem);
3811
3808
  }
3812
3809
  }
3810
+ /** THIS IS AN AUTOGENERATED FILE **/
3811
+ /** THIS IS AN AUTOGENERATED FILE **/
3812
+ /** THIS IS AN AUTOGENERATED FILE **/
3813
+ /** THIS IS AN AUTOGENERATED FILE **/
3813
3814
  @layer kz-components {
3814
- .MenuPopup-module_menuPopup__QgGEa {
3815
- /* from $ca-z-index-dropdown */
3815
+ .MenuPopup-module_menuPopup__UVgnP {
3816
+ position: absolute;
3816
3817
  z-index: 1000;
3817
3818
  box-sizing: border-box;
3818
- background: var(--color-white);
3819
- color: var(--color-purple-800);
3820
- border-radius: var(--border-solid-border-radius);
3821
- box-shadow: var(--shadow-large-box-shadow);
3822
- padding: var(--spacing-6) 0;
3823
- margin-top: var(--spacing-6);
3819
+ background: var(--color-white, #ffffff);
3820
+ color: var(--color-purple-800, #2f2438);
3821
+ border-radius: var(--border-solid-border-radius, 7px);
3822
+ box-shadow: var(--shadow-large-box-shadow, 0 3px 9px 0 rgba(0, 0, 0, 0.1), 0 8px 40px 0 rgba(0, 0, 0, 0.08));
3823
+ padding: var(--spacing-sm, 0.75rem) 0;
3824
+ margin-top: var(--spacing-xs, 0.375rem);
3824
3825
  text-align: start;
3825
- width: var(--menu-container-width, 294px);
3826
- max-height: var(--menu-container-height, 500px);
3827
- }
3828
-
3829
- .MenuPopup-module_menuPopup__QgGEa[popover]:popover-open {
3830
- z-index: unset;
3831
- margin: 0;
3832
- inset: unset;
3826
+ width: 294px;
3833
3827
  }
3834
3828
  }
3835
-
3836
3829
  /** THIS IS AN AUTOGENERATED FILE **/
3837
3830
  /** THIS IS AN AUTOGENERATED FILE **/
3838
3831
  /** THIS IS AN AUTOGENERATED FILE **/
@@ -5069,7 +5062,7 @@
5069
5062
  align-items: center;
5070
5063
  justify-content: center;
5071
5064
  position: absolute;
5072
- z-index: 10000;
5065
+ z-index: 1030;
5073
5066
  background: var(--color-white);
5074
5067
  inset-block: 0 1px;
5075
5068
  width: 48px;
@@ -19,7 +19,7 @@ export type FilterMultiSelectProps = {
19
19
  className?: string;
20
20
  } & Omit<MenuPopupProps, 'children'> & Omit<MenuTriggerProviderProps, 'children'> & SelectionProps;
21
21
  export declare const FilterMultiSelect: {
22
- ({ trigger, children, isOpen, defaultOpen, onOpenChange, isLoading, loadingSkeleton, label, items, selectedKeys, defaultSelectedKeys, onSelectionChange, selectionMode, onSearchInputChange, triggerRef, className, floatingOptions, }: FilterMultiSelectProps): JSX.Element;
22
+ ({ trigger, children, isOpen, defaultOpen, onOpenChange, isLoading, loadingSkeleton, label, items, selectedKeys, defaultSelectedKeys, onSelectionChange, selectionMode, onSearchInputChange, triggerRef, className, }: FilterMultiSelectProps): JSX.Element;
23
23
  displayName: string;
24
24
  TriggerButton: {
25
25
  ({ selectedOptionLabels, label, classNameOverride, labelCharacterLimitBeforeTruncate, }: import("./subcomponents/Trigger").FilterTriggerButtonProps): JSX.Element;
@@ -8,4 +8,3 @@ export declare const locationDemographicValues: {
8
8
  demographicValueId: string;
9
9
  label: string;
10
10
  }[];
11
- export declare const mockManyItems: ItemType[];
@@ -1,13 +1,10 @@
1
- import React, { type HTMLAttributes } from 'react';
2
- import { type UseFloatingOptions } from '@floating-ui/react-dom';
3
- import { type OverrideClassName } from "../../../../types/OverrideClassName";
1
+ import React from 'react';
4
2
  export type MenuPopupProps = {
5
- children: React.ReactNode;
6
- floatingOptions?: Partial<UseFloatingOptions>;
7
3
  isLoading?: boolean;
8
4
  loadingSkeleton?: React.ReactNode;
9
- } & OverrideClassName<HTMLAttributes<HTMLDivElement>>;
5
+ children: React.ReactNode;
6
+ };
10
7
  export declare const MenuPopup: {
11
- ({ children, floatingOptions, classNameOverride, isLoading, loadingSkeleton, ...restProps }: MenuPopupProps): JSX.Element;
8
+ ({ isLoading, loadingSkeleton, children, }: MenuPopupProps): JSX.Element;
12
9
  displayName: string;
13
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "0.0.0-canary-test-fms-popover-api-update-20250624021814",
3
+ "version": "0.0.0-canary-fix-tab-20250626012516",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -180,7 +180,6 @@ describe('<FilterBarMultiSelect />', () => {
180
180
  })
181
181
 
182
182
  await user.click(getByRole('option', { name: 'Fruit Jelly' }))
183
- await user.keyboard('{Escape}')
184
183
  await waitFor(() => {
185
184
  expect(getByRole('button', { name: 'Toppings : Pearls, Fruit Jelly' })).toBeInTheDocument()
186
185
  })
@@ -58,10 +58,9 @@ export const FilterMultiSelect = ({
58
58
  onSearchInputChange,
59
59
  triggerRef,
60
60
  className,
61
- floatingOptions,
62
61
  }: FilterMultiSelectProps): JSX.Element => {
63
62
  const menuTriggerProps = { isOpen, defaultOpen, onOpenChange, triggerRef }
64
- const menuPopupProps = { isLoading, loadingSkeleton, floatingOptions }
63
+ const menuPopupProps = { isLoading, loadingSkeleton }
65
64
  const disabledKeys: Selection = new Set(
66
65
  items?.filter((item) => item.isDisabled === true).map((disabledItem) => disabledItem.value),
67
66
  )
@@ -80,7 +79,7 @@ export const FilterMultiSelect = ({
80
79
  <MenuTriggerProvider {...menuTriggerProps}>
81
80
  <div className={className}>
82
81
  <MenuTriggerConsumer>{trigger}</MenuTriggerConsumer>
83
- <MenuPopup aria-label={label} {...menuPopupProps}>
82
+ <MenuPopup {...menuPopupProps}>
84
83
  <SelectionProvider {...selectionProps}>
85
84
  <SelectionConsumer>{children}</SelectionConsumer>
86
85
  </SelectionProvider>
@@ -30,14 +30,6 @@ The FilterMultiSelect is a component relies heavily on consumer implemntation. I
30
30
 
31
31
  <Canvas of={FilterMultiSelectStories.WithSectionHeaders} />
32
32
 
33
- ### With floatingOptions
34
-
35
- You can also use the `floatingOptions` to leverage any of `Floating UI's` [configurable options](https://floating-ui.com/docs/usefloating). While in most cases the default behavior should satisfy most scenarios, the `middleware` for `autoplace` and `size` can be useful when there is limited screen space available.
36
-
37
- The following example showcases how to use the [autoPlacement](https://floating-ui.com/docs/autoPlacement) and [size](https://floating-ui.com/docs/size) and spread the options in with the default values.
38
-
39
- <Canvas of={FilterMultiSelectStories.WithFloatingOptions} />
40
-
41
33
  ### Async
42
34
 
43
35
  The following is an example of how you may create an async FilterMultiSelect using `@tanstack/react-query`.
@@ -1,12 +1,11 @@
1
1
  import React, { useState } from 'react'
2
- import { autoPlacement, offset, size } from '@floating-ui/react-dom'
3
2
  import type { Selection } from '@react-types/shared'
4
3
  import type { Meta, StoryObj } from '@storybook/react'
5
4
  import isChromatic from 'chromatic'
6
5
  import { InlineNotification } from '~components/Notification'
7
6
  import { TextField } from '~components/TextField'
8
7
  import { FilterMultiSelect, getSelectedOptionLabels } from '..'
9
- import { mockItems, mockManyItems } from './MockData'
8
+ import { mockItems } from './MockData'
10
9
 
11
10
  const IS_CHROMATIC = isChromatic()
12
11
 
@@ -309,90 +308,3 @@ export const WithSectionNotification: Story = {
309
308
  chromatic: { disable: false },
310
309
  },
311
310
  }
312
-
313
- export const WithManyOptions: Story = {
314
- ...FilterMultiSelectTemplate,
315
- name: 'With many options',
316
- args: {
317
- items: mockManyItems,
318
- },
319
- }
320
-
321
- const floatingOptionsSourceCode = `
322
- import { autoPlacement, size, offset } from '@floating-ui/react-dom'
323
-
324
- // ...source code
325
-
326
- <FilterMultiSelect
327
- {...args}
328
- floatingOptions={{
329
- ...{
330
- middleware: [
331
- size({
332
- apply({ availableHeight, elements }) {
333
- Object.assign(elements.floating.style, {
334
- maxHeight: Math.max(250, Math.min(availableHeight - 12, 500)) + "px",
335
- })
336
- },
337
- }),
338
- autoPlacement({
339
- allowedPlacements: ['bottom-start', 'top-start'],
340
- }),
341
- offset(6),
342
- ],
343
- },
344
- }}
345
- />
346
- `
347
-
348
- export const WithFloatingOptions: Story = {
349
- ...FilterMultiSelectTemplate,
350
- name: 'With floatingOptions',
351
- args: {
352
- floatingOptions: {
353
- middleware: [
354
- size({
355
- apply({ availableHeight, elements }) {
356
- Object.assign(elements.floating.style, {
357
- maxHeight: Math.max(250, Math.min(availableHeight - 12, 500)) + 'px',
358
- })
359
- },
360
- }),
361
- autoPlacement({
362
- allowedPlacements: ['bottom-start', 'top-start'],
363
- }),
364
- offset(6),
365
- ],
366
- },
367
- },
368
- parameters: {
369
- docs: { source: { code: floatingOptionsSourceCode } },
370
- },
371
- }
372
-
373
- export const AboveIfAvailable: Story = {
374
- ...FilterMultiSelectTemplate,
375
- ...WithFloatingOptions,
376
- name: 'With limited viewport and autoplacement above',
377
- parameters: {
378
- viewport: {
379
- viewports: {
380
- LimitedViewportAutoPlace: {
381
- name: 'Limited vertical space',
382
- styles: {
383
- width: '1024px',
384
- height: '500px',
385
- },
386
- },
387
- },
388
- defaultViewport: 'LimitedViewportAutoPlace',
389
- },
390
- },
391
- decorators: [
392
- (Story) => (
393
- <div className="mt-[350px]">
394
- <Story />
395
- </div>
396
- ),
397
- ],
398
- }
@@ -58,42 +58,3 @@ export const locationDemographicValues = [
58
58
  label: 'London',
59
59
  },
60
60
  ]
61
-
62
- export const mockManyItems: ItemType[] = [
63
- { label: 'Front-End', value: 'id-fe', count: '1245' },
64
- { label: 'Back-End', value: 'id-be', count: '4', isDisabled: true },
65
- { label: 'SRE', value: 'id-sre', count: '4', isDisabled: true },
66
- { label: 'Dev-ops', value: 'id-devops' },
67
- { label: 'Others', value: 'id-others' },
68
- {
69
- label: 'Engineer-type-1 has a really really long label',
70
- value: 'id-type-1',
71
- },
72
- {
73
- label: 'Engineer-type-2 also has a really really long label',
74
- value: 'id-type-2',
75
- count: '156',
76
- },
77
- { label: 'Engineer-type-3', value: 'id-type-3' },
78
- {
79
- label: 'Engineer-type-4',
80
- value: 'id-type-4',
81
- count: '4',
82
- isDisabled: true,
83
- },
84
- { label: 'Engineer-type-5', value: 'id-type-5' },
85
- { label: 'UI Designer', value: 'id-ui', count: '42' },
86
- { label: 'UX Researcher', value: 'id-ux', count: '15' },
87
- { label: 'Product Manager', value: 'id-pm', count: '28' },
88
- { label: 'Project Manager', value: 'id-project', count: '19', isDisabled: true },
89
- { label: 'Data Scientist', value: 'id-ds', count: '11' },
90
- { label: 'Machine Learning Engineer', value: 'id-ml', count: '7' },
91
- { label: 'QA Tester', value: 'id-qa', count: '22' },
92
- {
93
- label: 'Technical Writer with documentation expertise',
94
- value: 'id-tech-writer',
95
- count: '5',
96
- },
97
- { label: 'DevSecOps Engineer', value: 'id-devsecops', count: '3', isDisabled: true },
98
- { label: 'Cloud Architect', value: 'id-cloud', count: '8' },
99
- ]
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import { render, screen, waitFor } from '@testing-library/react'
3
3
  import userEvent from '@testing-library/user-event'
4
+ import { vi } from 'vitest'
4
5
  import { FilterTriggerButton } from '~components/Filter/FilterMultiSelect/subcomponents/Trigger'
5
6
  import { MenuPopup } from '../../subcomponents/MenuPopup'
6
7
  import { MenuTriggerProvider, type MenuTriggerProviderProps } from './MenuTriggerProvider'
@@ -52,11 +53,15 @@ describe('<MenuTriggerProvider /> - Visual content', () => {
52
53
  rerender(<MenuTriggerProviderWrapper isOpen={false} />)
53
54
  expect(screen.queryByText('menu-content-mock')).not.toBeInTheDocument()
54
55
  })
55
- it('fires the onOpenChange callback on user interaction to close the menu', async () => {
56
+
57
+ it('fires the onOpenChange callback when the trigger is interacted', async () => {
56
58
  const onOpenChange = vi.fn()
57
59
  render(<MenuTriggerProviderWrapper isOpen onOpenChange={onOpenChange} />)
58
60
 
59
- await user.keyboard('{Escape}')
61
+ const trigger = screen.getByRole('button', {
62
+ name: 'trigger-display-label-mock',
63
+ })
64
+ await user.click(trigger)
60
65
 
61
66
  await waitFor(() => {
62
67
  expect(onOpenChange).toBeCalledTimes(1)
@@ -81,6 +86,17 @@ describe('<MenuTriggerProvider /> - Mouse interaction', () => {
81
86
  })
82
87
 
83
88
  describe('Given the menu is opened', () => {
89
+ it('is closed when user clicks on the trigger', async () => {
90
+ render(<MenuTriggerProviderWrapper defaultOpen />)
91
+ const trigger = screen.getByRole('button', {
92
+ name: 'trigger-display-label-mock',
93
+ })
94
+ await user.click(trigger)
95
+ await waitFor(() => {
96
+ expect(screen.queryByText('menu-content-mock')).not.toBeInTheDocument()
97
+ })
98
+ })
99
+
84
100
  it('is closed when user clicks outside of the menu', async () => {
85
101
  render(<MenuTriggerProviderWrapper defaultOpen />)
86
102
  await user.click(document.body)
@@ -0,0 +1,25 @@
1
+ @import '~@kaizen/design-tokens/sass/spacing';
2
+
3
+ @layer kz-components {
4
+ .listBox {
5
+ list-style: none;
6
+ padding: $spacing-sm;
7
+ margin: 0 $spacing-sm 0 0;
8
+ display: grid;
9
+ max-height: 22rem;
10
+ overflow-y: auto;
11
+ }
12
+
13
+ .overflown {
14
+ padding-right: $spacing-sm;
15
+ }
16
+
17
+ .hidden {
18
+ display: none;
19
+ }
20
+
21
+ // this is a div but remove styles briefly flickering to a bullet list as the sections are removed
22
+ .noResultsWrapper {
23
+ list-style: none;
24
+ }
25
+ }
@@ -3,7 +3,7 @@ import { type Collection, type Key } from '@react-types/shared'
3
3
  import classnames from 'classnames'
4
4
  import { useSelectionContext } from '../../context/SelectionProvider'
5
5
  import { type MultiSelectItem } from '../../types'
6
- import styles from './ListBox.module.css'
6
+ import styles from './ListBox.module.scss'
7
7
 
8
8
  export type ListBoxItems = {
9
9
  selectedItems: MultiSelectItem[]
@@ -0,0 +1,24 @@
1
+ @import '~@kaizen/design-tokens/sass/spacing';
2
+ @import '~@kaizen/design-tokens/sass/shadow';
3
+ @import '~@kaizen/design-tokens/sass/border';
4
+ @import '~@kaizen/design-tokens/sass/color';
5
+
6
+ @layer kz-components {
7
+ // figma hard coded: https://www.figma.com/file/eZKEE5kXbEMY3lx84oz8iN/%E2%9D%A4%EF%B8%8F-UI-Kit%3A-Heart?node-id=22814%3A96966
8
+ $menu-container-width: 294px;
9
+ $menu-container-max-height: 312px;
10
+
11
+ .menuPopup {
12
+ position: absolute;
13
+ z-index: 1000; // from $ca-z-index-dropdown
14
+ box-sizing: border-box;
15
+ background: $color-white;
16
+ color: $color-purple-800;
17
+ border-radius: $border-solid-border-radius;
18
+ box-shadow: $shadow-large-box-shadow;
19
+ padding: $spacing-sm 0;
20
+ margin-top: $spacing-xs;
21
+ text-align: start;
22
+ width: $menu-container-width;
23
+ }
24
+ }
@@ -1,78 +1,58 @@
1
- import React, { useEffect, useState, type HTMLAttributes } from 'react'
2
- import { autoUpdate, offset, useFloating, type UseFloatingOptions } from '@floating-ui/react-dom'
3
- import classnames from 'classnames'
4
- import { FocusOn } from 'react-focus-on'
5
- import { type OverrideClassName } from '~components/types/OverrideClassName'
1
+ import React from 'react'
2
+ import { FocusScope } from '@react-aria/focus'
3
+ import { DismissButton, useOverlay } from '@react-aria/overlays'
6
4
  import { useMenuTriggerContext } from '../../context'
7
- import styles from './MenuPopup.module.css'
5
+ import styles from './MenuPopup.module.scss'
8
6
 
9
7
  export type MenuPopupProps = {
10
- children: React.ReactNode
11
- floatingOptions?: Partial<UseFloatingOptions>
12
8
  isLoading?: boolean
13
9
  loadingSkeleton?: React.ReactNode
14
- } & OverrideClassName<HTMLAttributes<HTMLDivElement>>
10
+ children: React.ReactNode
11
+ }
15
12
 
16
13
  export const MenuPopup = ({
17
- children,
18
- floatingOptions,
19
- classNameOverride,
20
14
  isLoading,
21
15
  loadingSkeleton,
22
- ...restProps
16
+ children,
23
17
  }: MenuPopupProps): JSX.Element => {
24
- const [floatingElement, setFloatingElement] = useState<HTMLDivElement | null>(null)
25
- const { menuTriggerState, buttonRef } = useMenuTriggerContext()
26
-
27
- const referenceElement = buttonRef.current
28
-
29
- const { floatingStyles, update } = useFloating({
30
- placement: 'bottom-start',
31
- elements: {
32
- reference: referenceElement,
33
- floating: floatingElement,
18
+ const { menuTriggerState } = useMenuTriggerContext()
19
+
20
+ const onClose = (): void => menuTriggerState.close()
21
+
22
+ // Handle events that should cause the menu to close,
23
+ // e.g. blur, clicking outside, or pressing the escape key.
24
+ const overlayRef = React.createRef<HTMLDivElement>()
25
+ const { overlayProps } = useOverlay(
26
+ {
27
+ onClose,
28
+ isOpen: menuTriggerState.isOpen,
29
+ isDismissable: true,
34
30
  },
35
- strategy: 'absolute',
36
- middleware: [offset(6)],
37
- whileElementsMounted: autoUpdate,
38
- ...floatingOptions,
39
- })
40
-
41
- const handleReturnFocus = (): void => {
42
- requestAnimationFrame(() => {
43
- buttonRef.current?.focus()
44
- })
45
- }
46
-
47
- useEffect(() => {
48
- if (floatingElement && referenceElement) {
49
- floatingElement.showPopover?.()
50
- update()
51
- }
52
- }, [floatingElement, referenceElement, update])
31
+ overlayRef,
32
+ )
53
33
 
34
+ // Wrap in <FocusScope> so that focus is restored back to the trigger when the menu is closed
35
+ // and auto focus on the first focusable item after loading. (disable eslint no-autofocus error for it)
36
+ // In addition, add hidden <DismissButton> components at the start and end of the list
37
+ // to allow screen reader users to dismiss the popup easily.
54
38
  return menuTriggerState.isOpen ? (
55
- <FocusOn
56
- enabled={menuTriggerState.isOpen}
57
- scrollLock={false}
58
- returnFocus={false}
59
- onClickOutside={menuTriggerState.close}
60
- onEscapeKey={menuTriggerState.close}
61
- onDeactivation={handleReturnFocus}
62
- >
63
- <div
64
- ref={setFloatingElement}
65
- style={floatingStyles}
66
- className={classnames(styles.menuPopup, classNameOverride)}
67
- role="dialog"
68
- aria-modal="true"
69
- // @ts-expect-error: popover is valid in supported browsers
70
- popover="manual"
71
- {...restProps}
72
- >
73
- {isLoading && loadingSkeleton ? loadingSkeleton : children}
74
- </div>
75
- </FocusOn>
39
+ <div {...overlayProps} ref={overlayRef} className={styles.menuPopup}>
40
+ {isLoading && loadingSkeleton ? (
41
+ <>
42
+ <DismissButton onDismiss={onClose} />
43
+ {loadingSkeleton}
44
+ <DismissButton onDismiss={onClose} />
45
+ </>
46
+ ) : (
47
+ // eslint-disable-next-line jsx-a11y/no-autofocus
48
+ <FocusScope contain autoFocus restoreFocus>
49
+ <DismissButton onDismiss={onClose} />
50
+
51
+ {children}
52
+ <DismissButton onDismiss={onClose} />
53
+ </FocusScope>
54
+ )}
55
+ </div>
76
56
  ) : (
77
57
  <></>
78
58
  )
@@ -26,7 +26,7 @@
26
26
  align-items: center;
27
27
  justify-content: center;
28
28
  position: absolute;
29
- z-index: 10000;
29
+ z-index: 1030;
30
30
  background: var(--color-white);
31
31
  inset-block: 0 1px;
32
32
  width: 48px;
@@ -1,9 +0,0 @@
1
- 'use strict';
2
-
3
- var styles = {
4
- "listBox": "ListBox-module_listBox__HBScm",
5
- "overflown": "ListBox-module_overflown__PdKED",
6
- "hidden": "ListBox-module_hidden__mO-oL",
7
- "noResultsWrapper": "ListBox-module_noResultsWrapper__RnMj0"
8
- };
9
- module.exports = styles;
@@ -1,7 +0,0 @@
1
- var styles = {
2
- "listBox": "ListBox-module_listBox__HBScm",
3
- "overflown": "ListBox-module_overflown__PdKED",
4
- "hidden": "ListBox-module_hidden__mO-oL",
5
- "noResultsWrapper": "ListBox-module_noResultsWrapper__RnMj0"
6
- };
7
- export { styles as default };
@@ -1,4 +0,0 @@
1
- var styles = {
2
- "menuPopup": "MenuPopup-module_menuPopup__QgGEa"
3
- };
4
- export { styles as default };
@@ -1,22 +0,0 @@
1
- @layer kz-components {
2
- .listBox {
3
- list-style: none;
4
- padding: var(--spacing-12);
5
- margin: 0 var(--spacing-12) 0 0;
6
- display: grid;
7
- max-height: 22rem;
8
- overflow-y: auto;
9
- }
10
-
11
- .overflown {
12
- padding-right: var(--spacing-12);
13
- }
14
-
15
- .hidden {
16
- display: none;
17
- }
18
-
19
- .noResultsWrapper {
20
- list-style: none;
21
- }
22
- }
@@ -1,22 +0,0 @@
1
- @layer kz-components {
2
- .menuPopup {
3
- /* from $ca-z-index-dropdown */
4
- z-index: 1000;
5
- box-sizing: border-box;
6
- background: var(--color-white);
7
- color: var(--color-purple-800);
8
- border-radius: var(--border-solid-border-radius);
9
- box-shadow: var(--shadow-large-box-shadow);
10
- padding: var(--spacing-6) 0;
11
- margin-top: var(--spacing-6);
12
- text-align: start;
13
- width: var(--menu-container-width, 294px);
14
- max-height: var(--menu-container-height, 500px);
15
- }
16
-
17
- .menuPopup[popover]:popover-open {
18
- z-index: unset;
19
- margin: 0;
20
- inset: unset;
21
- }
22
- }