@kaizen/components 1.79.0 → 1.79.2

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.
@@ -52,8 +52,9 @@ var Select = function (_a) {
52
52
  _c = _a.placeholder,
53
53
  placeholder = _c === void 0 ? '' : _c,
54
54
  isDisabled = _a.isDisabled,
55
+ onSelectionChange = _a.onSelectionChange,
55
56
  portalContainerId = _a.portalContainerId,
56
- restProps = tslib.__rest(_a, ["label", "items", "id", "trigger", "children", "status", "validationMessage", "isReversed", "isFullWidth", "disabledValues", "classNameOverride", "selectedKey", "description", "placeholder", "isDisabled", "portalContainerId"]);
57
+ restProps = tslib.__rest(_a, ["label", "items", "id", "trigger", "children", "status", "validationMessage", "isReversed", "isFullWidth", "disabledValues", "classNameOverride", "selectedKey", "description", "placeholder", "isDisabled", "onSelectionChange", "portalContainerId"]);
57
58
  var refs = reactDom.useFloating().refs;
58
59
  var triggerRef = refs.reference;
59
60
  var fallbackId = React.useId();
@@ -70,7 +71,10 @@ var Select = function (_a) {
70
71
  selectedKey: typeof selectedKey === 'number' ? selectedKey.toString() : selectedKey,
71
72
  description: description,
72
73
  placeholder: placeholder,
73
- isDisabled: isDisabled
74
+ isDisabled: isDisabled,
75
+ onSelectionChange: onSelectionChange ? function (key) {
76
+ return onSelectionChange(key);
77
+ } : undefined
74
78
  }, restProps);
75
79
  var state = select.useSelectState(ariaSelectProps);
76
80
  var _d = select$1.useSelect(ariaSelectProps, state, triggerRef),
@@ -128,7 +132,8 @@ var Select = function (_a) {
128
132
  }, React__default.default.createElement(SelectContext.SelectProvider, {
129
133
  state: state
130
134
  }, React__default.default.createElement(SelectPopoverContents.SelectPopoverContents, {
131
- menuProps: menuProps
135
+ menuProps: menuProps,
136
+ popoverRef: refs.floating
132
137
  }, children)))), validationMessage && React__default.default.createElement(FieldMessage.FieldMessage, tslib.__assign({}, errorMessageProps, {
133
138
  id: validationId,
134
139
  message: validationMessage,
@@ -14,7 +14,8 @@ var React__default = /*#__PURE__*/_interopDefault(React);
14
14
  var Overlay = function (_a) {
15
15
  var children = _a.children,
16
16
  classNameOverride = _a.classNameOverride,
17
- restProps = tslib.__rest(_a, ["children", "classNameOverride"]);
17
+ popoverRef = _a.popoverRef,
18
+ restProps = tslib.__rest(_a, ["children", "classNameOverride", "popoverRef"]);
18
19
  var state = SelectContext.useSelectContext().state;
19
20
  // Handle events that should cause the menu to close,
20
21
  // e.g. blur, clicking outside, or pressing the escape key.
@@ -23,13 +24,12 @@ var Overlay = function (_a) {
23
24
  isDismissable: true,
24
25
  isOpen: state.isOpen,
25
26
  onClose: state.close
26
- }, overlayRef).overlayProps;
27
+ }, popoverRef !== null && popoverRef !== void 0 ? popoverRef : overlayRef).overlayProps;
27
28
  // Wrap in <FocusScope> so that focus is restored back to the trigger when the menu is closed
28
29
  // and auto focus on the first focusable item after loading. (disable eslint no-autofocus error for it)
29
30
  // In addition, add hidden <DismissButton> components at the start and end of the list
30
31
  // to allow screen reader users to dismiss the popup easily.
31
32
  return React__default.default.createElement("div", tslib.__assign({
32
- ref: overlayRef,
33
33
  className: classNameOverride
34
34
  }, overlayProps, restProps), React__default.default.createElement(focus.FocusScope, {
35
35
  autoFocus: false,
@@ -14,7 +14,8 @@ function _interopDefault(e) {
14
14
  var React__default = /*#__PURE__*/_interopDefault(React);
15
15
  var SelectPopoverContents = function (_a) {
16
16
  var children = _a.children,
17
- menuProps = _a.menuProps;
17
+ menuProps = _a.menuProps,
18
+ popoverRef = _a.popoverRef;
18
19
  var state = SelectContext.useSelectContext().state;
19
20
  // The collection structure is set by useSelectState's `children`
20
21
  // which we have used a util to ensure the following structure
@@ -23,7 +24,9 @@ var SelectPopoverContents = function (_a) {
23
24
  var itemNodes = Array.from(state.collection);
24
25
  return React__default.default.createElement("div", {
25
26
  className: SelectPopoverContents_module.selectPopoverContents
26
- }, React__default.default.createElement(Overlay.Overlay, null, React__default.default.createElement(ListBox.ListBox, {
27
+ }, React__default.default.createElement(Overlay.Overlay, {
28
+ popoverRef: popoverRef
29
+ }, React__default.default.createElement(ListBox.ListBox, {
27
30
  menuProps: menuProps
28
31
  }, children ? children({
29
32
  items: itemNodes
@@ -44,8 +44,9 @@ const Select = /*#__PURE__*/function () {
44
44
  _c = _a.placeholder,
45
45
  placeholder = _c === void 0 ? '' : _c,
46
46
  isDisabled = _a.isDisabled,
47
+ onSelectionChange = _a.onSelectionChange,
47
48
  portalContainerId = _a.portalContainerId,
48
- restProps = __rest(_a, ["label", "items", "id", "trigger", "children", "status", "validationMessage", "isReversed", "isFullWidth", "disabledValues", "classNameOverride", "selectedKey", "description", "placeholder", "isDisabled", "portalContainerId"]);
49
+ restProps = __rest(_a, ["label", "items", "id", "trigger", "children", "status", "validationMessage", "isReversed", "isFullWidth", "disabledValues", "classNameOverride", "selectedKey", "description", "placeholder", "isDisabled", "onSelectionChange", "portalContainerId"]);
49
50
  var refs = useFloating().refs;
50
51
  var triggerRef = refs.reference;
51
52
  var fallbackId = useId();
@@ -62,7 +63,10 @@ const Select = /*#__PURE__*/function () {
62
63
  selectedKey: typeof selectedKey === 'number' ? selectedKey.toString() : selectedKey,
63
64
  description: description,
64
65
  placeholder: placeholder,
65
- isDisabled: isDisabled
66
+ isDisabled: isDisabled,
67
+ onSelectionChange: onSelectionChange ? function (key) {
68
+ return onSelectionChange(key);
69
+ } : undefined
66
70
  }, restProps);
67
71
  var state = useSelectState(ariaSelectProps);
68
72
  var _d = useSelect(ariaSelectProps, state, triggerRef),
@@ -120,7 +124,8 @@ const Select = /*#__PURE__*/function () {
120
124
  }, /*#__PURE__*/React.createElement(SelectProvider, {
121
125
  state: state
122
126
  }, /*#__PURE__*/React.createElement(SelectPopoverContents, {
123
- menuProps: menuProps
127
+ menuProps: menuProps,
128
+ popoverRef: refs.floating
124
129
  }, children))))), validationMessage && (/*#__PURE__*/React.createElement(FieldMessage, __assign({}, errorMessageProps, {
125
130
  id: validationId,
126
131
  message: validationMessage,
@@ -7,7 +7,8 @@ const Overlay = /*#__PURE__*/function () {
7
7
  const Overlay = function (_a) {
8
8
  var children = _a.children,
9
9
  classNameOverride = _a.classNameOverride,
10
- restProps = __rest(_a, ["children", "classNameOverride"]);
10
+ popoverRef = _a.popoverRef,
11
+ restProps = __rest(_a, ["children", "classNameOverride", "popoverRef"]);
11
12
  var state = useSelectContext().state;
12
13
  // Handle events that should cause the menu to close,
13
14
  // e.g. blur, clicking outside, or pressing the escape key.
@@ -16,13 +17,12 @@ const Overlay = /*#__PURE__*/function () {
16
17
  isDismissable: true,
17
18
  isOpen: state.isOpen,
18
19
  onClose: state.close
19
- }, overlayRef).overlayProps;
20
+ }, popoverRef !== null && popoverRef !== void 0 ? popoverRef : overlayRef).overlayProps;
20
21
  // Wrap in <FocusScope> so that focus is restored back to the trigger when the menu is closed
21
22
  // and auto focus on the first focusable item after loading. (disable eslint no-autofocus error for it)
22
23
  // In addition, add hidden <DismissButton> components at the start and end of the list
23
24
  // to allow screen reader users to dismiss the popup easily.
24
25
  return /*#__PURE__*/React.createElement("div", __assign({
25
- ref: overlayRef,
26
26
  className: classNameOverride
27
27
  }, overlayProps, restProps), /*#__PURE__*/React.createElement(FocusScope, {
28
28
  autoFocus: false,
@@ -7,7 +7,8 @@ import styles from './SelectPopoverContents.module.scss.mjs';
7
7
  const SelectPopoverContents = /*#__PURE__*/function () {
8
8
  const SelectPopoverContents = function (_a) {
9
9
  var children = _a.children,
10
- menuProps = _a.menuProps;
10
+ menuProps = _a.menuProps,
11
+ popoverRef = _a.popoverRef;
11
12
  var state = useSelectContext().state;
12
13
  // The collection structure is set by useSelectState's `children`
13
14
  // which we have used a util to ensure the following structure
@@ -16,7 +17,9 @@ const SelectPopoverContents = /*#__PURE__*/function () {
16
17
  var itemNodes = Array.from(state.collection);
17
18
  return /*#__PURE__*/React.createElement("div", {
18
19
  className: styles.selectPopoverContents
19
- }, /*#__PURE__*/React.createElement(Overlay, null, /*#__PURE__*/React.createElement(ListBox, {
20
+ }, /*#__PURE__*/React.createElement(Overlay, {
21
+ popoverRef: popoverRef
22
+ }, /*#__PURE__*/React.createElement(ListBox, {
20
23
  menuProps: menuProps
21
24
  }, children ? children({
22
25
  items: itemNodes
@@ -5,7 +5,7 @@ import { type Key } from '@react-types/shared';
5
5
  import { type OverrideClassName } from "../../types/OverrideClassName";
6
6
  import { type SelectPopoverContentsProps, type SelectToggleProps } from './subcomponents';
7
7
  import { type SelectItem, type SelectOption } from './types';
8
- type OmittedAriaSelectProps = 'children' | 'items';
8
+ type OmittedAriaSelectProps = 'children' | 'items' | 'onSelectionChange';
9
9
  export type SelectProps<Option extends SelectOption = SelectOption> = {
10
10
  /**
11
11
  * Item objects in the collection.
@@ -45,13 +45,17 @@ export type SelectProps<Option extends SelectOption = SelectOption> = {
45
45
  * Use the `labelText` prop to provide a concise name, and the `description` prop for any help text.
46
46
  */
47
47
  placeholder?: string;
48
+ /**
49
+ * Handler that is called when the selection changes.
50
+ */
51
+ onSelectionChange?: (key: Key) => void;
48
52
  } & OverrideClassName<Omit<AriaSelectProps<Option>, OmittedAriaSelectProps>>;
49
53
  /**
50
54
  * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3081896474/Select Guidance} |
51
55
  * {@link https://cultureamp.design/?path=/docs/components-select--docs Storybook}
52
56
  */
53
57
  export declare const Select: {
54
- <Option extends SelectOption = SelectOption>({ label, items, id: propsId, trigger, children, status, validationMessage, isReversed, isFullWidth, disabledValues, classNameOverride, selectedKey, description, placeholder, isDisabled, portalContainerId, ...restProps }: SelectProps<Option>): JSX.Element;
58
+ <Option extends SelectOption = SelectOption>({ label, items, id: propsId, trigger, children, status, validationMessage, isReversed, isFullWidth, disabledValues, classNameOverride, selectedKey, description, placeholder, isDisabled, onSelectionChange, portalContainerId, ...restProps }: SelectProps<Option>): JSX.Element;
55
59
  displayName: string;
56
60
  Section: {
57
61
  <Option extends SelectOption = SelectOption>({ section, }: import("./subcomponents").ListBoxSectionProps<Option>): JSX.Element;
@@ -3,8 +3,9 @@ import { type OverrideClassName } from "../../../../types/OverrideClassName";
3
3
  import { type SelectOption } from '../../types';
4
4
  export type OverlayProps = OverrideClassName<HTMLAttributes<HTMLDivElement>> & {
5
5
  children: React.ReactNode;
6
+ popoverRef?: React.RefObject<Element | null>;
6
7
  };
7
8
  export declare const Overlay: {
8
- <Option extends SelectOption>({ children, classNameOverride, ...restProps }: OverlayProps): JSX.Element;
9
+ <Option extends SelectOption>({ children, classNameOverride, popoverRef, ...restProps }: OverlayProps): JSX.Element;
9
10
  displayName: string;
10
11
  };
@@ -6,8 +6,9 @@ export type SelectPopoverContentsProps<Option extends SelectOption> = {
6
6
  items: SelectItemNode<Option>[];
7
7
  }) => React.ReactNode;
8
8
  menuProps: AriaListBoxOptions<SelectItem<Option>>;
9
+ popoverRef?: React.RefObject<Element | null>;
9
10
  };
10
11
  export declare const SelectPopoverContents: {
11
- <Option extends SelectOption>({ children, menuProps, }: SelectPopoverContentsProps<Option>): JSX.Element;
12
+ <Option extends SelectOption>({ children, menuProps, popoverRef, }: SelectPopoverContentsProps<Option>): JSX.Element;
12
13
  displayName: string;
13
14
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.79.0",
3
+ "version": "1.79.2",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -25,7 +25,7 @@ import { getDisabledKeysFromItems } from './utils/getDisabledKeysFromItems'
25
25
  import { transformSelectItemToCollectionElement } from './utils/transformSelectItemToCollectionElement'
26
26
  import styles from './Select.module.scss'
27
27
 
28
- type OmittedAriaSelectProps = 'children' | 'items'
28
+ type OmittedAriaSelectProps = 'children' | 'items' | 'onSelectionChange'
29
29
 
30
30
  export type SelectProps<Option extends SelectOption = SelectOption> = {
31
31
  /**
@@ -70,6 +70,10 @@ export type SelectProps<Option extends SelectOption = SelectOption> = {
70
70
  * Use the `labelText` prop to provide a concise name, and the `description` prop for any help text.
71
71
  */
72
72
  placeholder?: string
73
+ /**
74
+ * Handler that is called when the selection changes.
75
+ */
76
+ onSelectionChange?: (key: Key) => void
73
77
  } & OverrideClassName<Omit<AriaSelectProps<Option>, OmittedAriaSelectProps>>
74
78
 
75
79
  /**
@@ -92,6 +96,7 @@ export const Select = <Option extends SelectOption = SelectOption>({
92
96
  description,
93
97
  placeholder = '',
94
98
  isDisabled,
99
+ onSelectionChange,
95
100
  portalContainerId,
96
101
  ...restProps
97
102
  }: SelectProps<Option>): JSX.Element => {
@@ -114,6 +119,7 @@ export const Select = <Option extends SelectOption = SelectOption>({
114
119
  description,
115
120
  placeholder,
116
121
  isDisabled,
122
+ onSelectionChange: onSelectionChange ? (key) => onSelectionChange(key!) : undefined,
117
123
  ...restProps,
118
124
  }
119
125
 
@@ -177,7 +183,9 @@ export const Select = <Option extends SelectOption = SelectOption>({
177
183
  {state.isOpen && (
178
184
  <Popover id={popoverId} portalContainer={portalContainer} refs={refs}>
179
185
  <SelectProvider<Option> state={state}>
180
- <SelectPopoverContents menuProps={menuProps}>{children}</SelectPopoverContents>
186
+ <SelectPopoverContents menuProps={menuProps} popoverRef={refs.floating}>
187
+ {children}
188
+ </SelectPopoverContents>
181
189
  </SelectProvider>
182
190
  </Popover>
183
191
  )}
@@ -7,11 +7,13 @@ import { type SelectOption } from '../../types'
7
7
 
8
8
  export type OverlayProps = OverrideClassName<HTMLAttributes<HTMLDivElement>> & {
9
9
  children: React.ReactNode
10
+ popoverRef?: React.RefObject<Element | null>
10
11
  }
11
12
 
12
13
  export const Overlay = <Option extends SelectOption>({
13
14
  children,
14
15
  classNameOverride,
16
+ popoverRef,
15
17
  ...restProps
16
18
  }: OverlayProps): JSX.Element => {
17
19
  const { state } = useSelectContext<Option>()
@@ -21,7 +23,7 @@ export const Overlay = <Option extends SelectOption>({
21
23
  const overlayRef = React.useRef<HTMLDivElement>(null)
22
24
  const { overlayProps } = useOverlay(
23
25
  { isDismissable: true, isOpen: state.isOpen, onClose: state.close },
24
- overlayRef,
26
+ popoverRef ?? overlayRef,
25
27
  )
26
28
 
27
29
  // Wrap in <FocusScope> so that focus is restored back to the trigger when the menu is closed
@@ -29,7 +31,7 @@ export const Overlay = <Option extends SelectOption>({
29
31
  // In addition, add hidden <DismissButton> components at the start and end of the list
30
32
  // to allow screen reader users to dismiss the popup easily.
31
33
  return (
32
- <div ref={overlayRef} className={classNameOverride} {...overlayProps} {...restProps}>
34
+ <div className={classNameOverride} {...overlayProps} {...restProps}>
33
35
  {/* eslint-disable-next-line jsx-a11y/no-autofocus */}
34
36
  <FocusScope autoFocus={false} restoreFocus>
35
37
  <DismissButton onDismiss={state.close} />
@@ -10,11 +10,13 @@ import styles from './SelectPopoverContents.module.scss'
10
10
  export type SelectPopoverContentsProps<Option extends SelectOption> = {
11
11
  children?: (args: { items: SelectItemNode<Option>[] }) => React.ReactNode
12
12
  menuProps: AriaListBoxOptions<SelectItem<Option>>
13
+ popoverRef?: React.RefObject<Element | null>
13
14
  }
14
15
 
15
16
  export const SelectPopoverContents = <Option extends SelectOption>({
16
17
  children,
17
18
  menuProps,
19
+ popoverRef,
18
20
  }: SelectPopoverContentsProps<Option>): JSX.Element => {
19
21
  const { state } = useSelectContext<Option>()
20
22
 
@@ -26,7 +28,7 @@ export const SelectPopoverContents = <Option extends SelectOption>({
26
28
 
27
29
  return (
28
30
  <div className={styles.selectPopoverContents}>
29
- <Overlay<Option>>
31
+ <Overlay<Option> popoverRef={popoverRef}>
30
32
  <ListBox<Option> menuProps={menuProps}>
31
33
  {children ? children({ items: itemNodes }) : <ListItems<Option> items={itemNodes} />}
32
34
  </ListBox>
@@ -45,23 +45,29 @@ export const OnButton: Story = {
45
45
  const button = canvas.queryByRole('button') ?? canvas.getByRole('link')
46
46
 
47
47
  await step('Hover shows', async () => {
48
- await waitFor(() => userEvent.unhover(button))
48
+ await waitFor(() => userEvent.click(canvasElement)) // needed to refocus the browser window
49
+
49
50
  await userEvent.hover(button)
50
- const tooltip = canvas.findByRole('tooltip')
51
- await expect(await tooltip).toBeInTheDocument()
52
- await expect((await tooltip).checkVisibility()).toBe(true)
51
+ await expect(await canvas.findByRole('tooltip')).toBeInTheDocument()
52
+ await expect((await canvas.findByRole('tooltip')).checkVisibility()).toBe(true)
53
53
  await expect(button).toHaveAttribute('aria-describedby', canvas.getByRole('tooltip').id)
54
54
  await userEvent.unhover(button)
55
55
  })
56
56
 
57
57
  await step('Focus shows', async () => {
58
+ await waitFor(() => userEvent.click(canvasElement)) // needed to refocus the browser window
59
+
58
60
  await userEvent.tab() // focus
59
- await waitFor(() => expect(canvas.getByRole('tooltip')).toBeVisible())
61
+ await expect((await canvas.findByRole('tooltip')).checkVisibility()).toBe(true)
62
+ await expect(await canvas.findByRole('tooltip')).toBeInTheDocument()
60
63
  await userEvent.tab() // unfocus
61
- await waitFor(() => expect(canvas.queryByRole('tooltip')).toBeNull())
64
+ waitFor(() => expect(canvas.queryByRole('tooltip')).toBeNull())
65
+ waitFor(async () => expect(await canvas.queryByText('Tooltip content')).toBeNull())
62
66
  })
63
67
 
64
68
  await step('Escape closes', async () => {
69
+ await waitFor(() => userEvent.click(canvasElement)) // needed to refocus the browser window
70
+
65
71
  await userEvent.tab() // focus
66
72
  await waitFor(() => expect(canvas.getByRole('tooltip')).toBeVisible())
67
73
  await userEvent.keyboard('{Escape}')