@itwin/itwinui-react 3.0.0-dev.8 → 3.0.0-dev.9

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 (61) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/cjs/core/Buttons/DropdownButton/DropdownButton.js +7 -19
  3. package/cjs/core/Buttons/SplitButton/SplitButton.d.ts +4 -4
  4. package/cjs/core/Buttons/SplitButton/SplitButton.js +53 -31
  5. package/cjs/core/ComboBox/ComboBox.d.ts +2 -2
  6. package/cjs/core/ComboBox/ComboBox.js +32 -24
  7. package/cjs/core/ComboBox/ComboBoxInput.js +29 -21
  8. package/cjs/core/ComboBox/ComboBoxMenu.js +73 -93
  9. package/cjs/core/ComboBox/helpers.d.ts +4 -1
  10. package/cjs/core/DropdownMenu/DropdownMenu.d.ts +6 -5
  11. package/cjs/core/DropdownMenu/DropdownMenu.js +59 -55
  12. package/cjs/core/Header/HeaderDropdownButton.js +1 -2
  13. package/cjs/core/Header/HeaderSplitButton.js +1 -2
  14. package/cjs/core/Menu/Menu.js +1 -1
  15. package/cjs/core/Menu/MenuItem.js +77 -55
  16. package/cjs/core/Select/Select.d.ts +5 -5
  17. package/cjs/core/Select/Select.js +74 -93
  18. package/cjs/core/Table/columns/actionColumn.js +3 -7
  19. package/cjs/core/Table/filters/DateRangeFilter/DatePickerInput.js +36 -41
  20. package/cjs/core/Table/filters/FilterToggle.js +3 -2
  21. package/cjs/core/Tile/Tile.js +21 -22
  22. package/cjs/core/index.d.ts +1 -1
  23. package/cjs/core/index.js +8 -1
  24. package/cjs/core/utils/components/InputContainer.d.ts +4 -4
  25. package/cjs/core/utils/components/InputContainer.js +7 -3
  26. package/cjs/core/utils/components/Popover.d.ts +113 -27
  27. package/cjs/core/utils/components/Popover.js +156 -118
  28. package/cjs/styles.js +2 -5
  29. package/esm/core/Buttons/DropdownButton/DropdownButton.js +8 -24
  30. package/esm/core/Buttons/SplitButton/SplitButton.d.ts +4 -4
  31. package/esm/core/Buttons/SplitButton/SplitButton.js +53 -28
  32. package/esm/core/ComboBox/ComboBox.d.ts +2 -2
  33. package/esm/core/ComboBox/ComboBox.js +33 -24
  34. package/esm/core/ComboBox/ComboBoxInput.js +22 -21
  35. package/esm/core/ComboBox/ComboBoxMenu.js +67 -87
  36. package/esm/core/ComboBox/helpers.d.ts +4 -1
  37. package/esm/core/DropdownMenu/DropdownMenu.d.ts +6 -5
  38. package/esm/core/DropdownMenu/DropdownMenu.js +64 -56
  39. package/esm/core/Header/HeaderDropdownButton.js +1 -2
  40. package/esm/core/Header/HeaderSplitButton.js +1 -2
  41. package/esm/core/Menu/Menu.js +7 -2
  42. package/esm/core/Menu/MenuItem.js +84 -52
  43. package/esm/core/Select/Select.d.ts +5 -5
  44. package/esm/core/Select/Select.js +74 -90
  45. package/esm/core/Table/columns/actionColumn.js +3 -7
  46. package/esm/core/Table/filters/DateRangeFilter/DatePickerInput.js +36 -41
  47. package/esm/core/Table/filters/FilterToggle.js +3 -2
  48. package/esm/core/Tile/Tile.js +21 -22
  49. package/esm/core/index.d.ts +1 -1
  50. package/esm/core/index.js +1 -0
  51. package/esm/core/utils/components/InputContainer.d.ts +4 -4
  52. package/esm/core/utils/components/InputContainer.js +7 -2
  53. package/esm/core/utils/components/Popover.d.ts +113 -27
  54. package/esm/core/utils/components/Popover.js +175 -118
  55. package/esm/styles.js +2 -5
  56. package/package.json +2 -4
  57. package/styles.css +3 -3
  58. package/cjs/core/ComboBox/ComboBoxDropdown.d.ts +0 -7
  59. package/cjs/core/ComboBox/ComboBoxDropdown.js +0 -43
  60. package/esm/core/ComboBox/ComboBoxDropdown.d.ts +0 -7
  61. package/esm/core/ComboBox/ComboBoxDropdown.js +0 -37
@@ -4,7 +4,12 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as React from 'react';
6
6
  import cx from 'classnames';
7
- import { useMergedRefs, getFocusableElements, Box } from '../utils/index.js';
7
+ import {
8
+ useMergedRefs,
9
+ getFocusableElements,
10
+ Box,
11
+ mergeEventHandlers,
12
+ } from '../utils/index.js';
8
13
  /**
9
14
  * Basic menu component. Can be used for select or dropdown components.
10
15
  */
@@ -63,9 +68,9 @@ export const Menu = React.forwardRef((props, ref) => {
63
68
  as: 'div',
64
69
  className: cx('iui-menu', className),
65
70
  role: 'menu',
66
- onKeyDown: onKeyDown,
67
71
  ref: refs,
68
72
  ...rest,
73
+ onKeyDown: mergeEventHandlers(props.onKeyDown, onKeyDown),
69
74
  });
70
75
  });
71
76
  export default Menu;
@@ -3,17 +3,27 @@
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as React from 'react';
6
- import { Popover, useMergedRefs, SvgCaretRightSmall } from '../utils/index.js';
6
+ import {
7
+ SvgCaretRightSmall,
8
+ usePopover,
9
+ Portal,
10
+ useMergedRefs,
11
+ useId,
12
+ } from '../utils/index.js';
7
13
  import { Menu } from './Menu.js';
8
14
  import { ListItem } from '../List/ListItem.js';
15
+ import { flushSync } from 'react-dom';
9
16
  /**
10
17
  * Context used to provide menu item ref to sub-menu items.
11
18
  */
12
- const MenuItemContext = React.createContext({ ref: undefined });
19
+ const MenuItemContext = React.createContext({
20
+ ref: undefined,
21
+ setIsNestedSubmenuVisible: () => {},
22
+ });
13
23
  /**
14
24
  * Basic menu item component. Should be used inside `Menu` component for each item.
15
25
  */
16
- export const MenuItem = React.forwardRef((props, ref) => {
26
+ export const MenuItem = React.forwardRef((props, forwardedRef) => {
17
27
  const {
18
28
  children,
19
29
  isSelected,
@@ -22,21 +32,33 @@ export const MenuItem = React.forwardRef((props, ref) => {
22
32
  onClick,
23
33
  sublabel,
24
34
  size = !!sublabel ? 'large' : 'default',
25
- startIcon: startIconProp,
26
35
  icon,
27
- endIcon: endIconProp,
36
+ startIcon = icon,
28
37
  badge,
38
+ endIcon = badge,
29
39
  role = 'menuitem',
30
40
  subMenuItems = [],
31
41
  ...rest
32
42
  } = props;
33
43
  const menuItemRef = React.useRef(null);
34
- const refs = useMergedRefs(menuItemRef, ref);
35
- const { ref: parentMenuItemRef } = React.useContext(MenuItemContext);
36
- const subMenuRef = React.useRef(null);
44
+ const [focusOnSubmenu, setFocusOnSubmenu] = React.useState(false);
45
+ const submenuId = useId();
37
46
  const [isSubmenuVisible, setIsSubmenuVisible] = React.useState(false);
38
- const startIcon = startIconProp ?? icon;
39
- const endIcon = endIconProp ?? badge;
47
+ const [isNestedSubmenuVisible, setIsNestedSubmenuVisible] =
48
+ React.useState(false);
49
+ const parent = React.useContext(MenuItemContext);
50
+ const onVisibleChange = (open) => {
51
+ setIsSubmenuVisible(open);
52
+ // we don't want parent to close when mouse goes into a nested submenu,
53
+ // so we need to let the parent know whether the submenu is still open.
54
+ parent.setIsNestedSubmenuVisible(open);
55
+ };
56
+ const popover = usePopover({
57
+ visible: isSubmenuVisible || isNestedSubmenuVisible,
58
+ onVisibleChange,
59
+ placement: 'right-start',
60
+ trigger: { hover: true, focus: true },
61
+ });
40
62
  const onKeyDown = (event) => {
41
63
  if (event.altKey) {
42
64
  return;
@@ -52,22 +74,37 @@ export const MenuItem = React.forwardRef((props, ref) => {
52
74
  case 'ArrowRight': {
53
75
  if (subMenuItems.length > 0) {
54
76
  setIsSubmenuVisible(true);
77
+ // flush and reset state so we are ready to focus again next time
78
+ flushSync(() => setFocusOnSubmenu(true));
79
+ setFocusOnSubmenu(false);
55
80
  event.preventDefault();
56
81
  event.stopPropagation();
57
82
  }
58
83
  break;
59
84
  }
60
85
  case 'ArrowLeft': {
61
- parentMenuItemRef?.current?.focus();
86
+ if (parent.ref) {
87
+ parent.ref.current?.focus();
88
+ parent.setIsNestedSubmenuVisible(false);
89
+ }
62
90
  event.stopPropagation();
63
91
  event.preventDefault();
64
92
  break;
65
93
  }
94
+ case 'Escape': {
95
+ // focus might get lost if submenu closes so move it back to parent
96
+ parent.ref?.current?.focus();
97
+ break;
98
+ }
66
99
  default:
67
100
  break;
68
101
  }
69
102
  };
70
- const listItem = React.createElement(
103
+ const handlers = {
104
+ onClick: () => !disabled && onClick?.(value),
105
+ onKeyDown,
106
+ };
107
+ return React.createElement(
71
108
  ListItem,
72
109
  {
73
110
  as: 'div',
@@ -75,24 +112,21 @@ export const MenuItem = React.forwardRef((props, ref) => {
75
112
  size: size,
76
113
  active: isSelected,
77
114
  disabled: disabled,
78
- onClick: () => !disabled && onClick?.(value),
79
- ref: refs,
115
+ ref: useMergedRefs(
116
+ menuItemRef,
117
+ forwardedRef,
118
+ subMenuItems.length > 0 ? popover.refs.setReference : null,
119
+ ),
80
120
  role: role,
81
121
  tabIndex: disabled || role === 'presentation' ? undefined : -1,
82
122
  'aria-selected': isSelected,
83
- 'aria-haspopup': subMenuItems.length > 0,
123
+ 'aria-haspopup': subMenuItems.length > 0 ? 'true' : undefined,
124
+ 'aria-controls': subMenuItems.length > 0 ? submenuId : undefined,
125
+ 'aria-expanded': subMenuItems.length > 0 ? popover.open : undefined,
84
126
  'aria-disabled': disabled,
85
- onKeyDown: onKeyDown,
86
- onMouseEnter: () => setIsSubmenuVisible(true),
87
- onMouseLeave: (e) => {
88
- if (
89
- !(e.relatedTarget instanceof Node) ||
90
- !subMenuRef.current?.contains(e.relatedTarget)
91
- ) {
92
- setIsSubmenuVisible(false);
93
- }
94
- },
95
- ...rest,
127
+ ...(subMenuItems.length === 0
128
+ ? { ...handlers, ...rest }
129
+ : popover.getReferenceProps({ ...handlers, ...rest })),
96
130
  },
97
131
  startIcon &&
98
132
  React.createElement(
@@ -119,34 +153,32 @@ export const MenuItem = React.forwardRef((props, ref) => {
119
153
  { as: 'span', 'aria-hidden': true },
120
154
  endIcon,
121
155
  ),
122
- );
123
- return subMenuItems.length === 0
124
- ? listItem
125
- : React.createElement(
126
- MenuItemContext.Provider,
127
- { value: { ref: menuItemRef } },
156
+ subMenuItems.length > 0 &&
157
+ popover.open &&
158
+ React.createElement(
159
+ Portal,
160
+ null,
128
161
  React.createElement(
129
- Popover,
130
- {
131
- placement: 'right-start',
132
- visible: isSubmenuVisible,
133
- appendTo: 'parent',
134
- content: React.createElement(
135
- 'div',
136
- {
137
- onMouseLeave: () => setIsSubmenuVisible(false),
138
- onBlur: (e) => {
139
- !!(e.relatedTarget instanceof Node) &&
140
- !subMenuRef.current?.contains(e.relatedTarget) &&
141
- !subMenuRef.current?.isEqualNode(e.relatedTarget) &&
142
- setIsSubmenuVisible(false);
162
+ MenuItemContext.Provider,
163
+ { value: { ref: menuItemRef, setIsNestedSubmenuVisible } },
164
+ React.createElement(
165
+ Menu,
166
+ {
167
+ setFocus: focusOnSubmenu,
168
+ ref: popover.refs.setFloating,
169
+ ...popover.getFloatingProps({
170
+ id: submenuId,
171
+ onPointerMove: () => {
172
+ // pointer might move into a nested submenu and set isSubmenuVisible to false,
173
+ // so we need to flip it back to true when pointer re-enters this submenu.
174
+ setIsSubmenuVisible(true);
143
175
  },
144
- },
145
- React.createElement(Menu, { ref: subMenuRef }, subMenuItems),
146
- ),
147
- },
148
- listItem,
176
+ }),
177
+ },
178
+ subMenuItems,
179
+ ),
149
180
  ),
150
- );
181
+ ),
182
+ );
151
183
  });
152
184
  export default MenuItem;
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
- import type { PopoverProps, CommonProps } from '../utils/index.js';
2
+ import { usePopover } from '../utils/index.js';
3
+ import type { CommonProps } from '../utils/index.js';
3
4
  export type ItemRendererProps = {
4
5
  /**
5
6
  * Close handler that closes the dropdown.
@@ -110,15 +111,14 @@ export type SelectProps<T> = {
110
111
  */
111
112
  menuStyle?: React.CSSProperties;
112
113
  /**
113
- * Props to customize {@link Popover} behavior.
114
- * @see [tippy.js props](https://atomiks.github.io/tippyjs/v6/all-props/)
114
+ * Props to customize Popover behavior.
115
115
  */
116
- popoverProps?: Omit<PopoverProps, 'onShow' | 'onHide' | 'disabled'>;
116
+ popoverProps?: Pick<Parameters<typeof usePopover>[0], 'visible' | 'onVisibleChange' | 'placement' | 'matchWidth' | 'closeOnOutsideClick'>;
117
117
  /**
118
118
  * Props to pass to the select button (trigger) element.
119
119
  */
120
120
  triggerProps?: React.ComponentPropsWithoutRef<'div'>;
121
- } & SelectMultipleTypeProps<T> & Pick<PopoverProps, 'onShow' | 'onHide'> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
121
+ } & SelectMultipleTypeProps<T> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
122
122
  /**
123
123
  * Select component to select value from options.
124
124
  * Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
@@ -7,10 +7,12 @@ import cx from 'classnames';
7
7
  import { Menu, MenuItem } from '../Menu/index.js';
8
8
  import {
9
9
  SvgCaretDownSmall,
10
- Popover,
11
10
  useId,
12
11
  AutoclearingHiddenLiveRegion,
13
12
  Box,
13
+ usePopover,
14
+ Portal,
15
+ useMergedRefs,
14
16
  SvgCheckmark,
15
17
  Icon,
16
18
  } from '../utils/index.js';
@@ -87,55 +89,27 @@ export const Select = (props) => {
87
89
  style,
88
90
  menuClassName,
89
91
  menuStyle,
90
- onShow,
91
- onHide,
92
- popoverProps,
93
92
  multiple = false,
94
93
  triggerProps,
95
94
  status,
95
+ popoverProps,
96
96
  ...rest
97
97
  } = props;
98
- const [isOpenState, setIsOpen] = React.useState(false);
99
- const isOpen = popoverProps?.visible ?? isOpenState;
100
- const [minWidth, setMinWidth] = React.useState(0);
98
+ const [isOpen, setIsOpen] = React.useState(false);
101
99
  const [liveRegionSelection, setLiveRegionSelection] = React.useState('');
102
100
  const selectRef = React.useRef(null);
103
- const onShowHandler = React.useCallback(
104
- (instance) => {
105
- setIsOpen(true);
106
- onShow?.(instance);
107
- },
108
- [onShow],
109
- );
110
- const onHideHandler = React.useCallback(
111
- (instance) => {
112
- setIsOpen(false);
113
- selectRef.current?.focus({ preventScroll: true }); // move focus back to select button
114
- onHide?.(instance);
115
- },
116
- [onHide],
117
- );
118
- React.useEffect(() => {
119
- if (selectRef.current) {
120
- setMinWidth(selectRef.current.offsetWidth);
121
- }
122
- }, [isOpen]);
123
- const onKeyDown = (event) => {
124
- if (event.altKey) {
101
+ const show = React.useCallback(() => {
102
+ if (disabled) {
125
103
  return;
126
104
  }
127
- switch (event.key) {
128
- case 'Enter':
129
- case ' ':
130
- case 'Spacebar': {
131
- setIsOpen((o) => !o);
132
- event.preventDefault();
133
- break;
134
- }
135
- default:
136
- break;
137
- }
138
- };
105
+ setIsOpen(true);
106
+ popoverProps?.onVisibleChange?.(true);
107
+ }, [disabled, popoverProps]);
108
+ const hide = React.useCallback(() => {
109
+ setIsOpen(false);
110
+ selectRef.current?.focus({ preventScroll: true }); // move focus back to select button
111
+ popoverProps?.onVisibleChange?.(false);
112
+ }, [popoverProps]);
139
113
  const menuItems = React.useMemo(() => {
140
114
  return options.map((option, index) => {
141
115
  const isSelected = isMultipleEnabled(value, multiple)
@@ -159,7 +133,7 @@ export const Select = (props) => {
159
133
  }
160
134
  if (isSingleOnChange(onChange, multiple)) {
161
135
  onChange?.(option.value);
162
- setIsOpen(false);
136
+ hide();
163
137
  } else {
164
138
  onChange?.(option.value, isSelected ? 'removed' : 'added');
165
139
  }
@@ -188,7 +162,7 @@ export const Select = (props) => {
188
162
  ...menuItem.props,
189
163
  });
190
164
  });
191
- }, [itemRenderer, multiple, onChange, options, value]);
165
+ }, [hide, itemRenderer, multiple, onChange, options, value]);
192
166
  const selectedItems = React.useMemo(() => {
193
167
  if (value == null) {
194
168
  return undefined;
@@ -203,53 +177,39 @@ export const Select = (props) => {
203
177
  label: item.label,
204
178
  });
205
179
  }, []);
180
+ const popover = usePopover({
181
+ visible: isOpen,
182
+ matchWidth: true,
183
+ closeOnOutsideClick: true,
184
+ ...popoverProps,
185
+ onVisibleChange: (open) => (open ? show() : hide()),
186
+ });
206
187
  return React.createElement(
207
- Box,
208
- { className: cx('iui-input-with-icon', className), style: style, ...rest },
188
+ React.Fragment,
189
+ null,
209
190
  React.createElement(
210
- Popover,
191
+ Box,
211
192
  {
212
- content: React.createElement(
213
- Menu,
214
- {
215
- role: 'listbox',
216
- className: cx('iui-scroll', menuClassName),
217
- style: {
218
- minInlineSize: minWidth,
219
- maxInlineSize: `min(${minWidth * 2}px, 90vw)`,
220
- ...menuStyle,
221
- },
222
- id: `${uid}-menu`,
223
- key: `${uid}-menu`,
224
- },
225
- menuItems,
226
- ),
227
- placement: 'bottom-start',
228
- aria: { content: null },
229
- onShow: onShowHandler,
230
- onHide: onHideHandler,
231
- ...popoverProps,
232
- visible: isOpen,
233
- onClickOutside: () => {
234
- setIsOpen(false);
235
- },
193
+ className: cx('iui-input-with-icon', className),
194
+ style: style,
195
+ ...rest,
196
+ ref: popover.refs.setPositionReference,
236
197
  },
237
198
  React.createElement(
238
199
  Box,
239
200
  {
201
+ ...popover.getReferenceProps(),
240
202
  tabIndex: 0,
241
203
  role: 'combobox',
242
- ref: selectRef,
243
204
  'data-iui-size': size,
244
205
  'data-iui-status': status,
245
- onClick: () => !disabled && setIsOpen((o) => !o),
246
- onKeyDown: (e) => !disabled && onKeyDown(e),
247
206
  'aria-disabled': disabled,
248
207
  'aria-autocomplete': 'none',
249
208
  'aria-expanded': isOpen,
250
209
  'aria-haspopup': 'listbox',
251
210
  'aria-controls': `${uid}-menu`,
252
211
  ...triggerProps,
212
+ ref: useMergedRefs(selectRef, popover.refs.setReference),
253
213
  className: cx(
254
214
  'iui-select-button',
255
215
  {
@@ -277,24 +237,48 @@ export const Select = (props) => {
277
237
  selectedItemRenderer: selectedItemRenderer,
278
238
  }),
279
239
  ),
240
+ React.createElement(
241
+ Icon,
242
+ {
243
+ as: 'span',
244
+ 'aria-hidden': true,
245
+ className: cx('iui-end-icon', {
246
+ 'iui-disabled': disabled,
247
+ 'iui-open': isOpen,
248
+ }),
249
+ },
250
+ React.createElement(SvgCaretDownSmall, null),
251
+ ),
252
+ multiple
253
+ ? React.createElement(AutoclearingHiddenLiveRegion, {
254
+ text: liveRegionSelection,
255
+ })
256
+ : null,
280
257
  ),
281
- React.createElement(
282
- Icon,
283
- {
284
- as: 'span',
285
- 'aria-hidden': true,
286
- className: cx('iui-end-icon', {
287
- 'iui-disabled': disabled,
288
- 'iui-open': isOpen,
289
- }),
290
- },
291
- React.createElement(SvgCaretDownSmall, null),
292
- ),
293
- multiple
294
- ? React.createElement(AutoclearingHiddenLiveRegion, {
295
- text: liveRegionSelection,
296
- })
297
- : null,
258
+ popover.open &&
259
+ React.createElement(
260
+ Portal,
261
+ null,
262
+ React.createElement(
263
+ Menu,
264
+ {
265
+ role: 'listbox',
266
+ className: cx('iui-scroll', menuClassName),
267
+ id: `${uid}-menu`,
268
+ key: `${uid}-menu`,
269
+ ...popover.getFloatingProps({
270
+ style: menuStyle,
271
+ onKeyDown: ({ key }) => {
272
+ if (key === 'Tab') {
273
+ hide();
274
+ }
275
+ },
276
+ }),
277
+ ref: popover.refs.setFloating,
278
+ },
279
+ menuItems,
280
+ ),
281
+ ),
298
282
  );
299
283
  };
300
284
  const SingleSelectButton = ({ selectedItem, selectedItemRenderer }) => {
@@ -105,13 +105,9 @@ export const ActionColumn = ({ columnManager = false } = {}) => {
105
105
  {
106
106
  ...dropdownMenuProps,
107
107
  menuItems: headerCheckBoxes,
108
- onHide: (i) => {
109
- setIsOpen(false);
110
- dropdownMenuProps.onHide?.(i);
111
- },
112
- onShow: (i) => {
113
- setIsOpen(true);
114
- dropdownMenuProps.onShow?.(i);
108
+ onVisibleChange: (open) => {
109
+ setIsOpen(open);
110
+ dropdownMenuProps?.onVisibleChange?.(open);
115
111
  },
116
112
  className: cx('iui-scroll', dropdownMenuProps.className),
117
113
  },
@@ -66,54 +66,49 @@ const DatePickerInput = React.forwardRef((props, forwardedRef) => {
66
66
  [onChange, parseInput],
67
67
  );
68
68
  return React.createElement(
69
- Popover,
70
- {
71
- content: React.createElement(DatePicker, {
72
- date: date,
73
- onChange: onDateSelected,
74
- setFocus: true,
75
- isDateDisabled: isDateDisabled,
76
- localizedNames: localizedNames,
77
- }),
78
- placement: 'bottom',
79
- visible: isVisible,
80
- onClickOutside: (_, e) => {
81
- if (!buttonRef.current?.contains(e.target)) {
82
- close();
83
- }
69
+ InputGrid,
70
+ { labelPlacement: 'inline', ...wrapperProps },
71
+ React.createElement(
72
+ Label,
73
+ {
74
+ as: 'label',
75
+ required: required,
76
+ disabled: disabled,
77
+ htmlFor: id,
78
+ ...labelProps,
84
79
  },
85
- appendTo: 'parent',
86
- },
80
+ label,
81
+ ),
87
82
  React.createElement(
88
- InputGrid,
89
- { labelPlacement: 'inline', ...wrapperProps },
83
+ InputWithDecorations,
84
+ { ...inputWrapperProps },
85
+ React.createElement(InputWithDecorations.Input, {
86
+ id: id,
87
+ value: inputValue,
88
+ onChange: onInputChange,
89
+ required: required,
90
+ disabled: disabled,
91
+ ref: forwardedRef,
92
+ ...rest,
93
+ }),
90
94
  React.createElement(
91
- Label,
95
+ Popover,
92
96
  {
93
- as: 'label',
94
- required: required,
95
- disabled: disabled,
96
- htmlFor: id,
97
- ...labelProps,
97
+ content: React.createElement(DatePicker, {
98
+ date: date,
99
+ onChange: onDateSelected,
100
+ setFocus: true,
101
+ isDateDisabled: isDateDisabled,
102
+ localizedNames: localizedNames,
103
+ }),
104
+ placement: 'bottom-end',
105
+ visible: isVisible,
106
+ onVisibleChange: setIsVisible,
107
+ closeOnOutsideClick: true,
98
108
  },
99
- label,
100
- ),
101
- React.createElement(
102
- InputWithDecorations,
103
- { ...inputWrapperProps },
104
- React.createElement(InputWithDecorations.Input, {
105
- id: id,
106
- value: inputValue,
107
- onChange: onInputChange,
108
- onClick: close,
109
- required: required,
110
- disabled: disabled,
111
- ref: forwardedRef,
112
- ...rest,
113
- }),
114
109
  React.createElement(
115
110
  InputWithDecorations.Button,
116
- { onClick: () => setIsVisible((v) => !v), ref: buttonRef },
111
+ { 'aria-label': 'Date picker', ref: buttonRef },
117
112
  React.createElement(SvgCalendar, null),
118
113
  ),
119
114
  ),
@@ -43,7 +43,8 @@ export const FilterToggle = (props) => {
43
43
  content: column.render('Filter', { close, setFilter, clearFilter }),
44
44
  placement: 'bottom-start',
45
45
  visible: isVisible,
46
- onClickOutside: close,
46
+ onVisibleChange: setIsVisible,
47
+ closeOnOutsideClick: true,
47
48
  },
48
49
  React.createElement(
49
50
  IconButton,
@@ -51,8 +52,8 @@ export const FilterToggle = (props) => {
51
52
  styleType: 'borderless',
52
53
  isActive: isVisible || isColumnFiltered,
53
54
  className: cx('iui-table-filter-button', className),
55
+ 'aria-label': 'Filter',
54
56
  onClick: (e) => {
55
- setIsVisible((v) => !v);
56
57
  // Prevents from triggering sort
57
58
  e.stopPropagation();
58
59
  },
@@ -212,32 +212,31 @@ const TileMoreOptions = React.forwardRef((props, forwardedRef) => {
212
212
  const { className, children = [], buttonProps, ...rest } = props;
213
213
  const [isMenuVisible, setIsMenuVisible] = React.useState(false);
214
214
  return React.createElement(
215
- DropdownMenu,
215
+ Box,
216
216
  {
217
- onShow: React.useCallback(() => setIsMenuVisible(true), []),
218
- onHide: React.useCallback(() => setIsMenuVisible(false), []),
219
- menuItems: (close) =>
220
- children?.map((option) =>
221
- React.cloneElement(option, {
222
- onClick: (value) => {
223
- close();
224
- option.props.onClick?.(value);
225
- },
226
- }),
227
- ),
217
+ className: cx(
218
+ 'iui-tile-more-options',
219
+ {
220
+ 'iui-visible': isMenuVisible,
221
+ },
222
+ className,
223
+ ),
224
+ ref: forwardedRef,
225
+ ...rest,
228
226
  },
229
227
  React.createElement(
230
- Box,
228
+ DropdownMenu,
231
229
  {
232
- className: cx(
233
- 'iui-tile-more-options',
234
- {
235
- 'iui-visible': isMenuVisible,
236
- },
237
- className,
238
- ),
239
- ref: forwardedRef,
240
- ...rest,
230
+ onVisibleChange: setIsMenuVisible,
231
+ menuItems: (close) =>
232
+ children?.map((option) =>
233
+ React.cloneElement(option, {
234
+ onClick: (value) => {
235
+ close();
236
+ option.props.onClick?.(value);
237
+ },
238
+ }),
239
+ ),
241
240
  },
242
241
  React.createElement(
243
242
  IconButton,