@geotab/zenith 3.6.3 → 3.7.0

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 (149) hide show
  1. package/README.md +15 -0
  2. package/dist/absolute/absolute.d.ts +3 -1
  3. package/dist/absolute/absolute.js +4 -4
  4. package/dist/checkbox/checkbox.d.ts +3 -1
  5. package/dist/checkbox/checkbox.js +5 -8
  6. package/dist/checkboxListWithAction/checkboxListWithAction.d.ts +3 -1
  7. package/dist/checkboxListWithAction/checkboxListWithAction.js +185 -3
  8. package/dist/controlledPopup/controlledPopup.d.ts +3 -1
  9. package/dist/controlledPopup/controlledPopup.js +2 -2
  10. package/dist/dropdown/dropdownPopup.d.ts +1 -1
  11. package/dist/dropdown/dropdownTrigger.d.ts +1 -1
  12. package/dist/dropdownRaw/dropdownList.d.ts +2 -1
  13. package/dist/dropdownRaw/dropdownList.js +15 -4
  14. package/dist/dropdownRaw/dropdownPopup.d.ts +3 -1
  15. package/dist/dropdownRaw/dropdownPopup.js +35 -5
  16. package/dist/dropdownRaw/dropdownRaw.js +82 -7
  17. package/dist/dropdownRaw/dropdownSearchableTrigger.d.ts +2 -1
  18. package/dist/dropdownRaw/dropdownSearchableTrigger.js +7 -2
  19. package/dist/dropdownRaw/dropdownTrigger.d.ts +2 -1
  20. package/dist/dropdownRaw/dropdownTrigger.js +3 -5
  21. package/dist/footerButtons/footerButtons.d.ts +6 -4
  22. package/dist/footerButtons/footerButtons.js +3 -8
  23. package/dist/groupsFilterRaw/groupsFilterCurrentlySelectedState.js +8 -2
  24. package/dist/groupsFilterRaw/groupsFilterInitialState.js +16 -0
  25. package/dist/groupsFilterRaw/groupsFilterRaw.js +43 -9
  26. package/dist/groupsFilterRaw/groupsFilterTrigger.d.ts +2 -1
  27. package/dist/groupsFilterRaw/groupsFilterTrigger.js +13 -2
  28. package/dist/index.css +222 -18
  29. package/dist/index.d.ts +1 -1
  30. package/dist/list/hooks/useDragAndDrop.d.ts +3 -1
  31. package/dist/list/hooks/useDragAndDrop.js +11 -4
  32. package/dist/menu/components/menuItem.js +12 -2
  33. package/dist/menu/contexts/pathContext.d.ts +3 -1
  34. package/dist/menu/contexts/pathProvider.d.ts +1 -1
  35. package/dist/menu/contexts/pathProvider.js +1 -1
  36. package/dist/menu/controlledMenu.js +97 -29
  37. package/dist/nav/nav.d.ts +2 -1
  38. package/dist/nav/nav.js +3 -2
  39. package/dist/nav/navEditList/navEditList.js +2 -1
  40. package/dist/nav/navItem/navItem.d.ts +1 -0
  41. package/dist/nav/navItem/navItem.js +4 -1
  42. package/dist/pillExpandable/pillContent.d.ts +2 -1
  43. package/dist/pillExpandable/pillContent.js +8 -3
  44. package/dist/pillExpandable/pillExpandable.d.ts +27 -4
  45. package/dist/pillExpandable/pillExpandable.js +426 -196
  46. package/dist/pillExpandable/pillExpandablePopoverContent.d.ts +19 -0
  47. package/dist/pillExpandable/pillExpandablePopoverContent.js +7 -0
  48. package/dist/pillExpandable/pillExpandableSimple.d.ts +5 -3
  49. package/dist/pillExpandable/pillExpandableSimple.js +12 -5
  50. package/dist/summary/summary.js +29 -1
  51. package/dist/summaryTile/summaryTile.d.ts +2 -1
  52. package/dist/summaryTile/summaryTile.js +175 -65
  53. package/dist/summaryTile/summaryTileTrigger.d.ts +1 -1
  54. package/dist/summaryTile/summaryTileTrigger.js +1 -1
  55. package/dist/toggleButtonRaw/toggleButtonRaw.js +2 -1
  56. package/dist/utils/localization/getSupportedLanguage.d.ts +2 -2
  57. package/dist/utils/localization/getSupportedLanguage.js +28 -8
  58. package/dist/utils/localization/languageContext.d.ts +1 -1
  59. package/dist/utils/localization/translations/cs.json +2 -1
  60. package/dist/utils/localization/translations/de.json +2 -1
  61. package/dist/utils/localization/translations/en.json +2 -1
  62. package/dist/utils/localization/translations/es.json +2 -1
  63. package/dist/utils/localization/translations/fr-FR.json +2 -1
  64. package/dist/utils/localization/translations/fr.json +2 -1
  65. package/dist/utils/localization/translations/id.json +2 -1
  66. package/dist/utils/localization/translations/it.json +2 -1
  67. package/dist/utils/localization/translations/ja.json +2 -1
  68. package/dist/utils/localization/translations/ms.json +2 -1
  69. package/dist/utils/localization/translations/nl.json +2 -1
  70. package/dist/utils/localization/translations/pl.json +2 -1
  71. package/dist/utils/localization/translations/pt-BR.json +2 -1
  72. package/dist/utils/localization/translations/sv.json +2 -1
  73. package/dist/utils/localization/translations/th.json +2 -1
  74. package/dist/utils/localization/translations/tr.json +2 -1
  75. package/dist/utils/localization/translations/zh-Hans.json +2 -1
  76. package/esm/absolute/absolute.d.ts +3 -1
  77. package/esm/absolute/absolute.js +4 -4
  78. package/esm/checkbox/checkbox.d.ts +3 -1
  79. package/esm/checkbox/checkbox.js +5 -8
  80. package/esm/checkboxListWithAction/checkboxListWithAction.d.ts +3 -1
  81. package/esm/checkboxListWithAction/checkboxListWithAction.js +186 -4
  82. package/esm/controlledPopup/controlledPopup.d.ts +3 -1
  83. package/esm/controlledPopup/controlledPopup.js +2 -2
  84. package/esm/dropdown/dropdownPopup.d.ts +1 -1
  85. package/esm/dropdown/dropdownTrigger.d.ts +1 -1
  86. package/esm/dropdownRaw/dropdownList.d.ts +2 -1
  87. package/esm/dropdownRaw/dropdownList.js +16 -5
  88. package/esm/dropdownRaw/dropdownPopup.d.ts +3 -1
  89. package/esm/dropdownRaw/dropdownPopup.js +37 -7
  90. package/esm/dropdownRaw/dropdownRaw.js +82 -7
  91. package/esm/dropdownRaw/dropdownSearchableTrigger.d.ts +2 -1
  92. package/esm/dropdownRaw/dropdownSearchableTrigger.js +7 -2
  93. package/esm/dropdownRaw/dropdownTrigger.d.ts +2 -1
  94. package/esm/dropdownRaw/dropdownTrigger.js +3 -5
  95. package/esm/footerButtons/footerButtons.d.ts +6 -4
  96. package/esm/footerButtons/footerButtons.js +3 -8
  97. package/esm/groupsFilterRaw/groupsFilterCurrentlySelectedState.js +9 -3
  98. package/esm/groupsFilterRaw/groupsFilterInitialState.js +16 -0
  99. package/esm/groupsFilterRaw/groupsFilterRaw.js +43 -9
  100. package/esm/groupsFilterRaw/groupsFilterTrigger.d.ts +2 -1
  101. package/esm/groupsFilterRaw/groupsFilterTrigger.js +13 -2
  102. package/esm/index.d.ts +1 -1
  103. package/esm/list/hooks/useDragAndDrop.d.ts +3 -1
  104. package/esm/list/hooks/useDragAndDrop.js +11 -4
  105. package/esm/menu/components/menuItem.js +12 -2
  106. package/esm/menu/contexts/pathContext.d.ts +3 -1
  107. package/esm/menu/contexts/pathProvider.d.ts +1 -1
  108. package/esm/menu/contexts/pathProvider.js +1 -1
  109. package/esm/menu/controlledMenu.js +98 -30
  110. package/esm/nav/nav.d.ts +2 -1
  111. package/esm/nav/nav.js +3 -2
  112. package/esm/nav/navEditList/navEditList.js +2 -1
  113. package/esm/nav/navItem/navItem.d.ts +1 -0
  114. package/esm/nav/navItem/navItem.js +5 -2
  115. package/esm/pillExpandable/pillContent.d.ts +2 -1
  116. package/esm/pillExpandable/pillContent.js +8 -3
  117. package/esm/pillExpandable/pillExpandable.d.ts +27 -4
  118. package/esm/pillExpandable/pillExpandable.js +427 -197
  119. package/esm/pillExpandable/pillExpandablePopoverContent.d.ts +19 -0
  120. package/esm/pillExpandable/pillExpandablePopoverContent.js +3 -0
  121. package/esm/pillExpandable/pillExpandableSimple.d.ts +5 -3
  122. package/esm/pillExpandable/pillExpandableSimple.js +12 -5
  123. package/esm/summary/summary.js +29 -1
  124. package/esm/summaryTile/summaryTile.d.ts +2 -1
  125. package/esm/summaryTile/summaryTile.js +140 -43
  126. package/esm/summaryTile/summaryTileTrigger.d.ts +1 -1
  127. package/esm/summaryTile/summaryTileTrigger.js +1 -1
  128. package/esm/toggleButtonRaw/toggleButtonRaw.js +2 -1
  129. package/esm/utils/localization/getSupportedLanguage.d.ts +2 -2
  130. package/esm/utils/localization/getSupportedLanguage.js +28 -8
  131. package/esm/utils/localization/languageContext.d.ts +1 -1
  132. package/esm/utils/localization/translations/cs.json +2 -1
  133. package/esm/utils/localization/translations/de.json +2 -1
  134. package/esm/utils/localization/translations/en.json +2 -1
  135. package/esm/utils/localization/translations/es.json +2 -1
  136. package/esm/utils/localization/translations/fr-FR.json +2 -1
  137. package/esm/utils/localization/translations/fr.json +2 -1
  138. package/esm/utils/localization/translations/id.json +2 -1
  139. package/esm/utils/localization/translations/it.json +2 -1
  140. package/esm/utils/localization/translations/ja.json +2 -1
  141. package/esm/utils/localization/translations/ms.json +2 -1
  142. package/esm/utils/localization/translations/nl.json +2 -1
  143. package/esm/utils/localization/translations/pl.json +2 -1
  144. package/esm/utils/localization/translations/pt-BR.json +2 -1
  145. package/esm/utils/localization/translations/sv.json +2 -1
  146. package/esm/utils/localization/translations/th.json +2 -1
  147. package/esm/utils/localization/translations/tr.json +2 -1
  148. package/esm/utils/localization/translations/zh-Hans.json +2 -1
  149. package/package.json +4 -7
@@ -252,5 +252,6 @@
252
252
  "Year to date": "År till dagens datum",
253
253
  "Time": "Tid",
254
254
  "Apply filter": "Tillämpa filter",
255
- "Remove filter": "Ta bort filter"
255
+ "Remove filter": "Ta bort filter",
256
+ "Results": "Resultat"
256
257
  }
@@ -252,5 +252,6 @@
252
252
  "Year to date": "ต้นปีถึงปัจจุบัน",
253
253
  "Time": "เวลา",
254
254
  "Apply filter": "ใช้ตัวกรอง",
255
- "Remove filter": "ลบตัวกรอง"
255
+ "Remove filter": "ลบตัวกรอง",
256
+ "Results": "ผลลัพธ์"
256
257
  }
@@ -252,5 +252,6 @@
252
252
  "Year to date": "Yılbaşından bugüne kadar",
253
253
  "Time": "Zaman",
254
254
  "Apply filter": "Filtreyi uygula",
255
- "Remove filter": "Filtreyi kaldır"
255
+ "Remove filter": "Filtreyi kaldır",
256
+ "Results": "Sonuçlar"
256
257
  }
@@ -252,5 +252,6 @@
252
252
  "Year to date": "年初至今",
253
253
  "Time": "时间",
254
254
  "Apply filter": "应用筛选器",
255
- "Remove filter": "删除筛选器"
255
+ "Remove filter": "删除筛选器",
256
+ "Results": "结果"
256
257
  }
@@ -23,5 +23,7 @@ export interface IAbsolute extends IZenComponentProps {
23
23
  alignmentsFn?: IAlignment;
24
24
  recalculateOnScroll?: boolean;
25
25
  recalculateTrigger?: boolean;
26
+ /** Whether to focus the first focusable element when popup opens. Default: true */
27
+ focusOnOpen?: boolean;
26
28
  }
27
- export declare const Absolute: ({ isOpen, id, paddingX, paddingY, className, children, triggerRef, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, stateFullChilds, role, ariaLabel, ariaLabelledby, alignmentsFn, recalculateOnScroll, recalculateTrigger }: IAbsolute) => import("react/jsx-runtime").JSX.Element | "" | null;
29
+ export declare const Absolute: ({ isOpen, id, paddingX, paddingY, className, children, triggerRef, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, stateFullChilds, role, ariaLabel, ariaLabelledby, alignmentsFn, recalculateOnScroll, recalculateTrigger, focusOnOpen }: IAbsolute) => import("react/jsx-runtime").JSX.Element | "" | null;
@@ -16,7 +16,7 @@ import { useScroll } from "../commonHelpers/hooks/useScroll";
16
16
  import { getScrollableParent } from "../utils/getScrollableParent";
17
17
  import { useClientReady } from "../commonHelpers/hooks/useClientReady";
18
18
  import { zen } from "../utils/zen";
19
- export const Absolute = ({ isOpen, id, paddingX, paddingY, className, children, triggerRef, alignment, inline, onOpenChange, useTrapFocusWithTrigger = "off", shouldHoldScroll, stateFullChilds, role, ariaLabel, ariaLabelledby, alignmentsFn, recalculateOnScroll, recalculateTrigger = true }) => {
19
+ export const Absolute = ({ isOpen, id, paddingX, paddingY, className, children, triggerRef, alignment, inline, onOpenChange, useTrapFocusWithTrigger = "off", shouldHoldScroll, stateFullChilds, role, ariaLabel, ariaLabelledby, alignmentsFn, recalculateOnScroll, recalculateTrigger = true, focusOnOpen = true }) => {
20
20
  const popupRef = useRef(null);
21
21
  const prevScroll = useRef(0);
22
22
  const { dark } = useContext(themeContext);
@@ -35,7 +35,7 @@ export const Absolute = ({ isOpen, id, paddingX, paddingY, className, children,
35
35
  const renderPopup = () => {
36
36
  var _a, _b;
37
37
  if (isOpen) {
38
- const result = (_jsx("div", Object.assign({}, accessibleAttr, { ref: popupRef, id: id, onScroll: shouldHoldScroll ? onScrollHandler : undefined, className: classNames(["zen-absolute", darkClassName, className || ""]), children: children })));
38
+ const result = (_jsx("div", Object.assign({}, accessibleAttr, { ref: popupRef, id: id, tabIndex: -1, onScroll: shouldHoldScroll ? onScrollHandler : undefined, className: classNames(["zen-absolute", darkClassName, className || ""]), children: children })));
39
39
  if (inline) {
40
40
  return result;
41
41
  }
@@ -91,7 +91,7 @@ export const Absolute = ({ isOpen, id, paddingX, paddingY, className, children,
91
91
  if (useTrapFocusWithTrigger === "none") {
92
92
  return undefined;
93
93
  }
94
- if (useTrapFocusWithTrigger !== "withTrigger") {
94
+ if (useTrapFocusWithTrigger !== "withTrigger" && focusOnOpen) {
95
95
  if (firstFocusable) {
96
96
  firstFocusable.focus();
97
97
  }
@@ -105,7 +105,7 @@ export const Absolute = ({ isOpen, id, paddingX, paddingY, className, children,
105
105
  };
106
106
  }
107
107
  return () => { };
108
- }, [isOpen, onOpenChange, triggerRef, useTrapFocusWithTrigger, stateFullChilds]);
108
+ }, [isOpen, onOpenChange, triggerRef, useTrapFocusWithTrigger, stateFullChilds, focusOnOpen]);
109
109
  useEffect(() => {
110
110
  var _a;
111
111
  if (isOpen) {
@@ -13,5 +13,7 @@ export interface ICheckbox extends IZenComponentProps {
13
13
  reverse?: boolean;
14
14
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
15
15
  wrapped?: boolean;
16
+ tabIndex?: number;
17
+ describedBy?: string;
16
18
  }
17
- export declare const Checkbox: ({ reverse, fullWidth, id, checked, indeterminate, title, className, children, disabled, wrapped, ...rest }: ICheckbox) => import("react/jsx-runtime").JSX.Element;
19
+ export declare const Checkbox: ({ reverse, fullWidth, id, checked, indeterminate, title, className, children, disabled, wrapped, describedBy, ...rest }: ICheckbox) => import("react/jsx-runtime").JSX.Element;
@@ -20,20 +20,17 @@ import { CheckboxIconCheckmark } from "./checkboxIconCheckmark";
20
20
  import { CheckboxIconMinus } from "./checkboxIconMinus";
21
21
  import { useMobile } from "../commonHelpers/hooks/useMobile";
22
22
  export const Checkbox = (_a) => {
23
- var { reverse, fullWidth, id, checked, indeterminate, title, className = "", children, disabled, wrapped } = _a, rest = __rest(_a, ["reverse", "fullWidth", "id", "checked", "indeterminate", "title", "className", "children", "disabled", "wrapped"]);
23
+ var { reverse, fullWidth, id, checked, indeterminate, title, className = "", children, disabled, wrapped, describedBy } = _a, rest = __rest(_a, ["reverse", "fullWidth", "id", "checked", "indeterminate", "title", "className", "children", "disabled", "wrapped", "describedBy"]);
24
24
  const state = getCheckboxState(checked, indeterminate);
25
25
  const generatedId = React.useId();
26
26
  const intId = id || generatedId;
27
27
  const driveClasses = useDriveClassName("zen-checkbox");
28
28
  const isMobile = useMobile();
29
29
  const checkboxIconClasses = useMemo(() => classNames(["zen-checkbox__icon", disabled ? "zen-checkbox__icon--disabled" : ""]), [disabled]);
30
- const checkbox = _jsx(TriStateCheckbox, Object.assign({ className: "zen-checkbox__input", id: intId, checked: checked, indeterminate: indeterminate, disabled: disabled, "aria-label": title }, rest));
31
- const iconAndLabel = _jsxs(_Fragment, { children: [_jsx("div", { className: "zen-checkbox__box", children: state === CheckboxState.Indeterminate ? _jsx(CheckboxIconMinus, { className: checkboxIconClasses, size: driveClasses ? "bigger" : "medium" }) :
32
- state === CheckboxState.On ? _jsx(CheckboxIconCheckmark, { className: checkboxIconClasses, size: driveClasses ? "bigger" : "medium" }) : null }), children
33
- ? _jsx("div", { className: classNames(["zen-checkbox__label-text", fullWidth ? "zen-checkbox__label-text--full-width" : ""]), children: children })
34
- : null] });
30
+ const checkbox = (_jsx(TriStateCheckbox, Object.assign({ className: "zen-checkbox__input", id: intId, checked: checked, indeterminate: indeterminate, disabled: disabled, "aria-label": title, "aria-describedby": describedBy }, rest)));
31
+ const iconAndLabel = (_jsxs(_Fragment, { children: [_jsx("div", { className: "zen-checkbox__box", children: state === CheckboxState.Indeterminate ? (_jsx(CheckboxIconMinus, { className: checkboxIconClasses, size: driveClasses ? "bigger" : "medium" })) : state === CheckboxState.On ? (_jsx(CheckboxIconCheckmark, { className: checkboxIconClasses, size: driveClasses ? "bigger" : "medium" })) : null }), children ? _jsx("div", { className: classNames(["zen-checkbox__label-text", fullWidth ? "zen-checkbox__label-text--full-width" : ""]), children: children }) : null] }));
35
32
  if (wrapped) {
36
- return _jsxs("label", { className: classNames(["zen-checkbox", driveClasses || "", className]), title: title, children: [checkbox, _jsx("div", { className: classNames(["zen-checkbox__label", reverse ? "zen-checkbox__label--reverse" : ""]), children: iconAndLabel })] });
33
+ return (_jsxs("label", { className: classNames(["zen-checkbox", driveClasses || "", className]), title: title, children: [checkbox, _jsx("div", { className: classNames(["zen-checkbox__label", reverse ? "zen-checkbox__label--reverse" : ""]), children: iconAndLabel })] }));
37
34
  }
38
- return _jsxs("div", { className: classNames(["zen-checkbox", isMobile && !driveClasses ? "zen-checkbox--mobile" : "", driveClasses || "", className]), children: [checkbox, _jsx("label", { className: classNames(["zen-checkbox__label", reverse ? "zen-checkbox__label--reverse" : ""]), htmlFor: intId, title: title, children: iconAndLabel })] });
35
+ return (_jsxs("div", { className: classNames(["zen-checkbox", isMobile && !driveClasses ? "zen-checkbox--mobile" : "", driveClasses || "", className]), children: [checkbox, _jsx("label", { className: classNames(["zen-checkbox__label", reverse ? "zen-checkbox__label--reverse" : ""]), htmlFor: intId, title: title, children: iconAndLabel })] }));
39
36
  };
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import "./checkboxListWithAction.less";
2
3
  import { IZenComponentProps } from "../commonHelpers/zenComponent";
3
4
  import { IColor } from "../groupsFilterRaw/groupsFilterInterfaces";
@@ -23,6 +24,7 @@ export interface ICheckboxListWithAction extends IZenComponentProps {
23
24
  search?: string[];
24
25
  onChange: (value: IChangeCheckbox) => void;
25
26
  onClick: (id: string) => void;
27
+ handleTab?: (e: React.KeyboardEvent, shift?: boolean) => void;
26
28
  }
27
- export declare const CheckboxListWithAction: ({ label, options, onChange, onClick, search, className }: ICheckboxListWithAction) => import("react/jsx-runtime").JSX.Element;
29
+ export declare const CheckboxListWithAction: ({ label, options, onChange, onClick, search, className, handleTab }: ICheckboxListWithAction) => import("react/jsx-runtime").JSX.Element;
28
30
  export declare const TRANSLATIONS: string[];
@@ -1,6 +1,6 @@
1
1
  import { injectString } from "../utils/localization/translationsDictionary";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { Fragment, useId } from "react";
3
+ import { Fragment, useCallback, useEffect, useId, useRef, useState } from "react";
4
4
  import { Checkbox } from "../checkbox/checkbox";
5
5
  import { classNames } from "../commonHelpers/classNames/classNames";
6
6
  import { IconChevronRight } from "../icons/iconChevronRight";
@@ -84,13 +84,15 @@ injectString("tr", "Toggle {groupName} filter", "{groupName} filtresini de\u011F
84
84
  injectString("zh-Hans", "Toggle {groupName} filter", "\u5207\u6362 {groupName} \u7B5B\u9009\u5668");
85
85
  injectString("zh-TW", "Toggle {groupName} filter", "\u5207\u63DB\u300C{groupName}\u300D\u7BE9\u9078\u689D\u4EF6");
86
86
  injectString("ro-RO", "Toggle {groupName} filter", "Activa\u021Bi filtrul {groupName}");
87
+ injectString("en", "Has additional actions. Use right arrow to access button, left arrow to return.", "Has additional actions. Use right arrow to access button, left arrow to return.");
87
88
  export const CheckboxListWithAction = ({
88
89
  label,
89
90
  options,
90
91
  onChange,
91
92
  onClick,
92
93
  search = [],
93
- className
94
+ className,
95
+ handleTab
94
96
  }) => {
95
97
  const {
96
98
  translate
@@ -98,6 +100,11 @@ export const CheckboxListWithAction = ({
98
100
  const driveComponentClass = useDriveClassName("zen-checkbox-list-with-action");
99
101
  const listId = useId();
100
102
  const checkboxId = `${listId}_check_list-item_`;
103
+ const navigationInstructionsId = `${listId}_navigation_instructions`;
104
+ const listRef = useRef(null);
105
+ const [focusedRowIndex, setFocusedRowIndex] = useState(0);
106
+ const [focusedElementType, setFocusedElementType] = useState("checkbox");
107
+ const isInitialized = useRef(false);
101
108
  const handleChange = e => {
102
109
  const evtTarget = e.target;
103
110
  const elId = evtTarget.getAttribute("id").replace(checkboxId, "");
@@ -112,7 +119,171 @@ export const CheckboxListWithAction = ({
112
119
  const evtTarget = e.target;
113
120
  const elId = evtTarget.getAttribute("data-btn") || ((_a = evtTarget.parentElement) === null || _a === void 0 ? void 0 : _a.getAttribute("data-btn")) || "";
114
121
  onClick(elId);
122
+ setFocusedElementType("checkbox");
115
123
  };
124
+ const getFocusableElements = useCallback(() => {
125
+ if (!listRef.current || !options) return [];
126
+ const elements = [];
127
+ options.forEach((opt, rowIndex) => {
128
+ var _a;
129
+ const listItem = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`li[data-item="${opt.property}"]`);
130
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
131
+ if (listItem) {
132
+ const checkbox = listItem.querySelector('input[type="checkbox"]');
133
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
134
+ if (checkbox) {
135
+ elements.push({
136
+ element: checkbox,
137
+ rowIndex,
138
+ type: "checkbox",
139
+ blocked: opt.blocked
140
+ });
141
+ }
142
+ if (opt.isWithAction) {
143
+ const button = listItem.querySelector("button[data-btn]");
144
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
145
+ if (button) {
146
+ elements.push({
147
+ element: button,
148
+ rowIndex,
149
+ type: "button",
150
+ blocked: opt.blocked
151
+ });
152
+ }
153
+ }
154
+ }
155
+ });
156
+ return elements;
157
+ }, [options]);
158
+ const updateTabIndex = useCallback((newRowIndex, newElementType) => {
159
+ const elements = getFocusableElements();
160
+ elements.forEach(({
161
+ element,
162
+ rowIndex,
163
+ type,
164
+ blocked
165
+ }) => {
166
+ const isActive = rowIndex === newRowIndex && type === newElementType && !blocked;
167
+ element.setAttribute("tabindex", isActive ? "0" : "-1");
168
+ });
169
+ }, [getFocusableElements]);
170
+ const handleKeyDown = useCallback(
171
+ // eslint-disable-next-line complexity
172
+ e => {
173
+ var _a;
174
+ const elements = getFocusableElements();
175
+ if (elements.length === 0 || !options) return;
176
+ let newRowIndex = focusedRowIndex;
177
+ let newElementType = focusedElementType;
178
+ let preventDefault = false;
179
+ if (e.key === "Enter") {
180
+ const evtTarget = e.target;
181
+ const elId = ((_a = evtTarget.getAttribute("id")) === null || _a === void 0 ? void 0 : _a.replace(checkboxId, "")) || "";
182
+ elId && onChange({
183
+ id: elId,
184
+ state: evtTarget.checked,
185
+ partial: evtTarget.dataset.indeterminate === "true"
186
+ });
187
+ return;
188
+ }
189
+ if (e.key === "ArrowDown") {
190
+ // Go to next row
191
+ do {
192
+ newRowIndex = (newRowIndex + 1) % options.length;
193
+ } while (options[newRowIndex].blocked && newRowIndex !== focusedRowIndex);
194
+ preventDefault = true;
195
+ } else if (e.key === "ArrowUp") {
196
+ // Go to previous row
197
+ do {
198
+ newRowIndex = (newRowIndex - 1 + options.length) % options.length;
199
+ } while (options[newRowIndex].blocked && newRowIndex !== focusedRowIndex);
200
+ preventDefault = true;
201
+ } else if (e.key === "ArrowRight") {
202
+ // Go to button if exists in current row and not blocked
203
+ if (options[focusedRowIndex].isWithAction && focusedElementType === "checkbox" && !options[focusedRowIndex].blocked) {
204
+ newElementType = "button";
205
+ preventDefault = true;
206
+ }
207
+ } else if (e.key === "ArrowLeft") {
208
+ // Go to checkbox if currently on button and checkbox not blocked
209
+ if (focusedElementType === "button" && !options[focusedRowIndex].blocked) {
210
+ newElementType = "checkbox";
211
+ preventDefault = true;
212
+ }
213
+ } else if (e.key === "Home") {
214
+ // Go to first non-blocked row
215
+ newRowIndex = 0;
216
+ while (newRowIndex < options.length && options[newRowIndex].blocked) {
217
+ newRowIndex++;
218
+ }
219
+ if (newRowIndex < options.length) {
220
+ newElementType = "checkbox";
221
+ preventDefault = true;
222
+ }
223
+ } else if (e.key === "End") {
224
+ // Go to last non-blocked row
225
+ newRowIndex = options.length - 1;
226
+ while (newRowIndex >= 0 && options[newRowIndex].blocked) {
227
+ newRowIndex--;
228
+ }
229
+ if (newRowIndex >= 0) {
230
+ newElementType = "checkbox";
231
+ preventDefault = true;
232
+ }
233
+ } else if (e.key === "Tab") {
234
+ if (handleTab) {
235
+ handleTab(e, e.shiftKey);
236
+ return;
237
+ }
238
+ }
239
+ if (preventDefault) {
240
+ e.preventDefault();
241
+ setFocusedRowIndex(newRowIndex);
242
+ setFocusedElementType(newElementType);
243
+ updateTabIndex(newRowIndex, newElementType);
244
+ // Find and focus the target element
245
+ const targetElement = elements.find(({
246
+ rowIndex,
247
+ type,
248
+ blocked
249
+ }) => rowIndex === newRowIndex && type === newElementType && !blocked);
250
+ targetElement === null || targetElement === void 0 ? void 0 : targetElement.element.focus();
251
+ }
252
+ }, [getFocusableElements, options, focusedRowIndex, focusedElementType, checkboxId, onChange, handleTab, updateTabIndex]);
253
+ const handleFocus = useCallback(e => {
254
+ const elements = getFocusableElements();
255
+ const focusedElement = e.target;
256
+ const targetElementInfo = elements.find(({
257
+ element
258
+ }) => element === focusedElement);
259
+ if (targetElementInfo && !targetElementInfo.blocked && (targetElementInfo.rowIndex !== focusedRowIndex || targetElementInfo.type !== focusedElementType)) {
260
+ setFocusedRowIndex(targetElementInfo.rowIndex);
261
+ setFocusedElementType(targetElementInfo.type);
262
+ updateTabIndex(targetElementInfo.rowIndex, targetElementInfo.type);
263
+ }
264
+ }, [focusedRowIndex, focusedElementType, getFocusableElements, updateTabIndex]);
265
+ // Initialize tabindex when options first become available
266
+ useEffect(() => {
267
+ if (options && options.length > 0 && !isInitialized.current) {
268
+ // Find first non-blocked row
269
+ let firstValidRow = 0;
270
+ while (firstValidRow < options.length && options[firstValidRow].blocked) {
271
+ firstValidRow++;
272
+ }
273
+ if (firstValidRow < options.length) {
274
+ setFocusedRowIndex(firstValidRow);
275
+ setFocusedElementType("checkbox");
276
+ updateTabIndex(firstValidRow, "checkbox");
277
+ isInitialized.current = true;
278
+ }
279
+ }
280
+ }, [options, updateTabIndex]);
281
+ // Update tabindex when focused position changes (only after initialization)
282
+ useEffect(() => {
283
+ if (isInitialized.current) {
284
+ updateTabIndex(focusedRowIndex, focusedElementType);
285
+ }
286
+ }, [focusedRowIndex, focusedElementType, updateTabIndex]);
116
287
  const getLabelColorInfo = (color, withRightPadding, isHidden, extraArgs) => {
117
288
  if (!color) {
118
289
  return null;
@@ -170,7 +341,8 @@ export const CheckboxListWithAction = ({
170
341
  type: "button",
171
342
  onClick: handleClick,
172
343
  title: title,
173
- className: classN
344
+ className: classN,
345
+ tabIndex: -1
174
346
  }, otherArgs, {
175
347
  children: childEl
176
348
  })) : _jsx("div", {
@@ -200,6 +372,9 @@ export const CheckboxListWithAction = ({
200
372
  };
201
373
  return optionsArr ? _jsx("ul", {
202
374
  className: "zen-checkbox-list-with-action__list",
375
+ ref: listRef,
376
+ onKeyDown: handleKeyDown,
377
+ onFocus: handleFocus,
203
378
  children: optionsArr.map((opt, ind) => {
204
379
  const titleForRender = opt.title || opt.label;
205
380
  const labelForRender = selectSearchTerm(opt.label);
@@ -215,7 +390,9 @@ export const CheckboxListWithAction = ({
215
390
  checked: opt.checked,
216
391
  disabled: opt.blocked,
217
392
  title: translate("Toggle {groupName} filter").replace("{groupName}", titleForRender),
393
+ describedBy: opt.isWithAction ? navigationInstructionsId : undefined,
218
394
  "data-indeterminate": opt.partialChecked,
395
+ tabIndex: -1,
219
396
  children: opt.isWithAction ? null : labelElement
220
397
  }), opt.isWithAction && labelElement]
221
398
  }, `li_${ind}_${opt.property}`);
@@ -227,7 +404,12 @@ export const CheckboxListWithAction = ({
227
404
  children: [label ? _jsx("div", {
228
405
  className: "zen-checkbox-list-with-action__title",
229
406
  children: label
230
- }) : null, build(options)]
407
+ }) : null, build(options), _jsx("div", {
408
+ id: navigationInstructionsId,
409
+ className: "zen-checkbox-list-with-action__sr-only",
410
+ "aria-hidden": "true",
411
+ children: translate("Has additional actions. Use right arrow to access button, left arrow to return.")
412
+ })]
231
413
  });
232
414
  };
233
415
  export const TRANSLATIONS = ["View {groupName} children", "Toggle {groupName} filter", "Number of selected child groups - {count}"];
@@ -27,5 +27,7 @@ export interface IControlledPopup extends IZenComponentProps {
27
27
  ariaLabelledby?: string;
28
28
  recalculateOnScroll?: boolean;
29
29
  preventAttributesAutoSet?: boolean;
30
+ /** Whether to focus the first focusable element when popup opens. Default: true */
31
+ focusOnOpen?: boolean;
30
32
  }
31
- export declare const ControlledPopup: ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, preventAttributesAutoSet }: IControlledPopup) => import("react/jsx-runtime").JSX.Element;
33
+ export declare const ControlledPopup: ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, preventAttributesAutoSet, focusOnOpen }: IControlledPopup) => import("react/jsx-runtime").JSX.Element;
@@ -3,7 +3,7 @@ import { useEffect, useId } from "react";
3
3
  import { Absolute } from "../absolute/absolute";
4
4
  import { classNames } from "../commonHelpers/classNames/classNames";
5
5
  import { useClientReady } from "../commonHelpers/hooks/useClientReady";
6
- export const ControlledPopup = ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, preventAttributesAutoSet }) => {
6
+ export const ControlledPopup = ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, preventAttributesAutoSet, focusOnOpen }) => {
7
7
  const autoId = useId();
8
8
  const popupId = id || autoId;
9
9
  const isClientReady = useClientReady();
@@ -21,5 +21,5 @@ export const ControlledPopup = ({ isOpen, id, paddingX, paddingY, triggerRef, cl
21
21
  triggerRef.current.setAttribute("aria-expanded", isOpen ? "true" : "false");
22
22
  triggerRef.current.setAttribute("aria-controls", popupId);
23
23
  }, [triggerRef, isOpen, popupId, preventAttributesAutoSet, isClientReady]);
24
- return (_jsx(Absolute, { triggerRef: triggerRef, alignment: alignment, id: popupId, isOpen: isOpen, className: classNames(["zen-popup", className ? className : ""]), paddingX: paddingX, paddingY: paddingY, inline: inline, onOpenChange: onOpenChange, useTrapFocusWithTrigger: useTrapFocusWithTrigger, shouldHoldScroll: shouldHoldScroll, role: "dialog", ariaLabel: ariaLabel, ariaLabelledby: ariaLabelledby, recalculateOnScroll: recalculateOnScroll, children: children }));
24
+ return (_jsx(Absolute, { triggerRef: triggerRef, alignment: alignment, id: popupId, isOpen: isOpen, className: classNames(["zen-popup", className ? className : ""]), paddingX: paddingX, paddingY: paddingY, inline: inline, onOpenChange: onOpenChange, useTrapFocusWithTrigger: useTrapFocusWithTrigger, shouldHoldScroll: shouldHoldScroll, role: "dialog", ariaLabel: ariaLabel, ariaLabelledby: ariaLabelledby, recalculateOnScroll: recalculateOnScroll, focusOnOpen: focusOnOpen, children: children }));
25
25
  };
@@ -1,3 +1,3 @@
1
1
  import { IDropdownPopup as IDropdownPopupRaw } from "../dropdownRaw/dropdownPopup";
2
- export declare const DropdownPopup: ({ alignment, triggerRef, classNamePopup, isMobile, inputValue, dialogAriaLabel, disabled, filterName, handleApplyClick, handleClearClick, handleTriggerClick, mobileSheetStackingClassName, onInputChange, hasApplyButton, inputId, isClearButtonDisabled, isWithFooter, placeholder, searchField, isSearchInPopup, isApplyButtonDisabled, isOpenCombo, inputRef, contentRef, children, onReadyForFocus, handleCheckboxChange, checkboxLabel, isChecked, popupId }: IDropdownPopupRaw) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const DropdownPopup: ({ alignment, triggerRef, classNamePopup, isMobile, inputValue, dialogAriaLabel, disabled, filterName, handleApplyClick, handleClearClick, handleTriggerClick, mobileSheetStackingClassName, onInputChange, hasApplyButton, inputId, isClearButtonDisabled, isWithFooter, placeholder, searchField, isSearchInPopup, isApplyButtonDisabled, isOpenCombo, inputRef, contentRef, children, onReadyForFocus, handleCheckboxChange, checkboxLabel, isChecked, popupId, handleKeydown, handleFocusOnSentinelItem }: IDropdownPopupRaw) => import("react/jsx-runtime").JSX.Element;
3
3
  export type IDropdownPopup = IDropdownPopupRaw;
@@ -1 +1 @@
1
- export declare const DropdownTrigger: ({ name, onBlur, isActive, id, triggerRef, searchField, placeholder, disabled, fullWidth, currentSelection, handleClick, inputValue, className, onSearchChange, width, title, triggerAriaLabel, count, inputRef, buttonType, isError, popupId, ...otherProps }: import("../dropdownRaw/dropdownTrigger").IDropdownTrigger) => import("react/jsx-runtime").JSX.Element;
1
+ export declare const DropdownTrigger: ({ name, onBlur, isActive, id, triggerRef, searchField, placeholder, disabled, fullWidth, currentSelection, handleClick, inputValue, className, onSearchChange, width, title, triggerAriaLabel, count, inputRef, buttonType, isError, popupId, handleFocusOnPopup, ...otherProps }: import("../dropdownRaw/dropdownTrigger").IDropdownTrigger) => import("react/jsx-runtime").JSX.Element;
@@ -32,11 +32,12 @@ export interface IDropdownList extends IZenComponentProps {
32
32
  handleCheckboxChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
33
33
  checkboxLabel?: string;
34
34
  isChecked?: boolean;
35
+ listElementRef?: React.RefObject<HTMLDivElement>;
35
36
  }
36
37
  interface IDropdownEmptyList extends Pick<IDropdownList, "className" | "width" | "onClearClick" | "onApplyClick" | "onCancelClick" | "hasApplyButton" | "isApplyDisabled" | "isClearButtonDisabled" | "isWithFooter"> {
37
38
  hasError: boolean;
38
39
  }
39
40
  export declare const EmptyList: ({ className, width, onClearClick, onApplyClick, onCancelClick, hasApplyButton, isApplyDisabled, isClearButtonDisabled, isWithFooter, hasError }: IDropdownEmptyList) => import("react/jsx-runtime").JSX.Element;
40
- export declare const DropdownList: ({ onBackButtonClick, onSelectAllClick, onClearClick, onApplyClick, onCancelClick, onChange, onSelect, onSingleSelect, listData, isAllSelected, backButtonName, width, minWidth, isSelectAllButtonDisable, hasSelectAllButton, filterName, isMultiselect, hasApplyButton, isApplyDisabled, isClearButtonDisabled, activeValue, forceSelection, isWithFooter, isMobile, handleCheckboxChange, checkboxLabel, isChecked }: IDropdownList) => import("react/jsx-runtime").JSX.Element;
41
+ export declare const DropdownList: ({ onBackButtonClick, onSelectAllClick, onClearClick, onApplyClick, onCancelClick, onChange, onSelect, onSingleSelect, listData, isAllSelected, backButtonName, width, minWidth, isSelectAllButtonDisable, hasSelectAllButton, filterName, isMultiselect, hasApplyButton, isApplyDisabled, isClearButtonDisabled, activeValue, forceSelection, isWithFooter, isMobile, handleCheckboxChange, checkboxLabel, isChecked, listElementRef }: IDropdownList) => import("react/jsx-runtime").JSX.Element;
41
42
  export declare const TRANSLATIONS: string[];
42
43
  export {};
@@ -1,6 +1,6 @@
1
1
  import { injectString } from "../utils/localization/translationsDictionary";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import React, { useCallback, useId, useMemo } from "react";
3
+ import React, { useCallback, useId, useMemo, useRef } from "react";
4
4
  import { CheckboxListWithAction } from "../checkboxListWithAction/checkboxListWithAction";
5
5
  import { classNames } from "../commonHelpers/classNames/classNames";
6
6
  import { IconBackArrow } from "../icons/deprecated/iconBackArrow";
@@ -309,7 +309,8 @@ export const DropdownList = ({
309
309
  isMobile,
310
310
  handleCheckboxChange,
311
311
  checkboxLabel,
312
- isChecked
312
+ isChecked,
313
+ listElementRef
313
314
  }) => {
314
315
  const {
315
316
  translate
@@ -320,6 +321,13 @@ export const DropdownList = ({
320
321
  const handleSelectAllClick = () => {
321
322
  onSelectAllClick(isAllSelected);
322
323
  };
324
+ const handleBackButtonKeyDown = useCallback(event => {
325
+ if (event.key === "ArrowLeft") {
326
+ event.preventDefault();
327
+ onBackButtonClick();
328
+ }
329
+ }, [onBackButtonClick]);
330
+ const componentRef = useRef(null);
323
331
  const handleChange = useCallback(value => {
324
332
  onChange(value);
325
333
  }, [onChange]);
@@ -340,10 +348,11 @@ export const DropdownList = ({
340
348
  forceSelection ? onSingleSelect(false, newActiveEl || activeValue) : onSingleSelect(false, newActiveEl);
341
349
  }, [activeValue, forceSelection, onSingleSelect]);
342
350
  const selectButtonLabel = isAllSelected ? translate("Deselect all") : translate("Select all");
343
- const getActionButton = (label, title, icon, isDisabled, clickHandler, size) => _jsx("button", {
351
+ const getActionButton = (label, title, icon, isDisabled, clickHandler, size, onKeyDown) => _jsx("button", {
344
352
  type: "button",
345
353
  disabled: isDisabled,
346
354
  onClick: clickHandler,
355
+ onKeyDown: onKeyDown,
347
356
  className: "zen-dropdown-list__item zen-dropdown-list__item--interactive zen-dropdown-list__action-button zen-dropdown-list__action-button-back",
348
357
  title: title,
349
358
  children: _jsxs(_Fragment, {
@@ -382,6 +391,7 @@ export const DropdownList = ({
382
391
  };
383
392
  return _jsx("div", {
384
393
  className: "zen-dropdown-list__elements",
394
+ ref: listElementRef,
385
395
  children: isMultiselect ? _jsx(CheckboxListWithAction, {
386
396
  options: listData,
387
397
  label: "",
@@ -399,10 +409,11 @@ export const DropdownList = ({
399
409
  value: createValue(listData, activeValue)
400
410
  })
401
411
  });
402
- }, [isMultiselect, listData, handleChange, handleClick, handleSingleChange, translate, listId, forceSelection, activeValue]);
412
+ }, [listElementRef, isMultiselect, listData, handleChange, handleClick, handleSingleChange, translate, listId, forceSelection, activeValue]);
403
413
  return _jsxs("div", Object.assign({
404
414
  className: classNames(["zen-dropdown-list", driveComponentClass || ""])
405
415
  }, styleWidth, {
416
+ ref: componentRef,
406
417
  children: [filterName ? _jsx("div", {
407
418
  className: "zen-dropdown-list__label",
408
419
  children: filterName
@@ -415,7 +426,7 @@ export const DropdownList = ({
415
426
  })
416
427
  }) : null, backButtonName ? _jsx("div", {
417
428
  className: "zen-dropdown-list__item-wrapper",
418
- children: getActionButton(backButtonName, translate("Back"), IconBackArrow, false, onBackButtonClick, "large")
429
+ children: getActionButton(backButtonName, translate("Back"), IconBackArrow, false, onBackButtonClick, "large", handleBackButtonKeyDown)
419
430
  }) : null, hasSelectAllButton ? getActionButton(selectButtonLabel, selectButtonLabel, IconCheckRadio, !!isSelectAllButtonDisable, handleSelectAllClick, isDrive ? "huger" : "huge") : null, listDataComponent, isWithFooter ? _jsxs("div", {
420
431
  className: "zen-dropdown-list__footer",
421
432
  children: [_jsx(Button, {
@@ -31,5 +31,7 @@ export interface IDropdownPopup extends IZenComponentProps {
31
31
  checkboxLabel?: string;
32
32
  isChecked?: boolean;
33
33
  isSearchInPopup?: boolean;
34
+ handleKeydown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
35
+ handleFocusOnSentinelItem?: () => void;
34
36
  }
35
- export declare const DropdownPopup: ({ alignment, triggerRef, classNamePopup, isMobile, inputValue, dialogAriaLabel, disabled, filterName, handleApplyClick, handleClearClick, handleTriggerClick, mobileSheetStackingClassName, onInputChange, hasApplyButton, inputId, isClearButtonDisabled, isWithFooter, placeholder, searchField, isSearchInPopup, isApplyButtonDisabled, isOpenCombo, inputRef, contentRef, children, onReadyForFocus, handleCheckboxChange, checkboxLabel, isChecked, popupId }: IDropdownPopup) => import("react/jsx-runtime").JSX.Element;
37
+ export declare const DropdownPopup: ({ alignment, triggerRef, classNamePopup, isMobile, inputValue, dialogAriaLabel, disabled, filterName, handleApplyClick, handleClearClick, handleTriggerClick, mobileSheetStackingClassName, onInputChange, hasApplyButton, inputId, isClearButtonDisabled, isWithFooter, placeholder, searchField, isSearchInPopup, isApplyButtonDisabled, isOpenCombo, inputRef, contentRef, children, onReadyForFocus, handleCheckboxChange, checkboxLabel, isChecked, popupId, handleKeydown, handleFocusOnSentinelItem }: IDropdownPopup) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { injectString } from "../utils/localization/translationsDictionary";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useId, useMemo } from "react";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useId, useMemo, useRef } from "react";
4
4
  import { MobileSheet } from "../mobileSheet/mobileSheet";
5
5
  import { Button } from "../button/button";
6
6
  import { ButtonType } from "../button/buttonType";
@@ -11,6 +11,7 @@ import { useLanguage } from "../utils/localization/useLanguage";
11
11
  import { FooterButtons } from "../footerButtons/footerButtons";
12
12
  import { Checkbox } from "../checkbox/checkbox";
13
13
  import { useScrollWithAndroidKeyboard } from "../utils/useScrollWithAndroidKeyboard";
14
+ import { getFirstFocusableItem } from "../utils/keyboardHelpers";
14
15
  injectString("cs", "Filter by group", "Filtrovat podle skupiny");
15
16
  injectString("da-DK", "Filter by group", "Filtrer efter gruppe");
16
17
  injectString("de", "Filter by group", "Nach Gruppe filtern");
@@ -119,12 +120,16 @@ export const DropdownPopup = ({
119
120
  handleCheckboxChange,
120
121
  checkboxLabel,
121
122
  isChecked,
122
- popupId
123
+ popupId,
124
+ handleKeydown,
125
+ handleFocusOnSentinelItem
123
126
  }) => {
124
127
  const {
125
128
  translate
126
129
  } = useLanguage();
127
130
  const triggerId = useId();
131
+ const footerButtonsRef = useRef(null);
132
+ const mobileContentRef = useRef(null);
128
133
  const {
129
134
  handleFocus,
130
135
  handleBlur,
@@ -133,7 +138,22 @@ export const DropdownPopup = ({
133
138
  const memoizedHandleHide = useCallback(() => {
134
139
  handleTriggerClick(false);
135
140
  }, [handleTriggerClick]);
136
- const memoizedMobileContent = useMemo(() => _jsxs(_Fragment, {
141
+ const handleMobileFocusOnSentinelItem = useCallback(() => {
142
+ if (!isMobile) {
143
+ return;
144
+ }
145
+ let nextFocusableElement = footerButtonsRef.current ? getFirstFocusableItem(footerButtonsRef.current) : null;
146
+ if (nextFocusableElement) {
147
+ nextFocusableElement.focus();
148
+ return;
149
+ }
150
+ nextFocusableElement = getFirstFocusableItem(mobileContentRef.current);
151
+ nextFocusableElement === null || nextFocusableElement === void 0 ? void 0 : nextFocusableElement.focus();
152
+ return;
153
+ }, [isMobile, footerButtonsRef, mobileContentRef]);
154
+ const memoizedMobileContent = useMemo(() => _jsxs("div", {
155
+ onKeyDown: handleKeydown,
156
+ ref: mobileContentRef,
137
157
  children: [searchField ? _jsx("div", {
138
158
  className: "zen-dropdown-mobile-sheet__search-container",
139
159
  children: _jsx(SearchInput, {
@@ -157,13 +177,18 @@ export const DropdownPopup = ({
157
177
  }) : null, _jsx("div", {
158
178
  ref: contentRef,
159
179
  children: children
180
+ }), _jsx("div", {
181
+ tabIndex: 0,
182
+ onFocus: handleMobileFocusOnSentinelItem,
183
+ className: "zen-dropdown-list__sentinel-item"
160
184
  })]
161
- }), [searchField, onInputChange, handleBlur, handleFocus, inputValue, inputRef, inputId, triggerId, disabled, placeholder, translate, handleCheckboxChange, isChecked, checkboxLabel, contentRef, children]);
185
+ }), [handleKeydown, searchField, onInputChange, handleBlur, handleFocus, inputValue, inputRef, inputId, triggerId, disabled, placeholder, translate, handleCheckboxChange, isChecked, checkboxLabel, contentRef, children, handleMobileFocusOnSentinelItem]);
162
186
  const memoizedMobileFooter = useMemo(() => {
163
187
  if (!isWithFooter) {
164
188
  return null;
165
189
  }
166
190
  return _jsxs(FooterButtons, {
191
+ ref: footerButtonsRef,
167
192
  children: [hasApplyButton ? _jsx(Button, {
168
193
  onClick: handleApplyClick,
169
194
  type: ButtonType.Primary,
@@ -216,6 +241,7 @@ export const DropdownPopup = ({
216
241
  recalculateOnScroll: true,
217
242
  children: _jsxs("div", {
218
243
  ref: contentRef,
244
+ onKeyDown: handleKeydown,
219
245
  children: [searchField && isSearchInPopup ? _jsx("div", {
220
246
  className: "zen-dropdown-popup__search-container",
221
247
  children: _jsx(SearchInput, {
@@ -227,8 +253,12 @@ export const DropdownPopup = ({
227
253
  disabled: disabled,
228
254
  placeholder: placeholder || translate("Filter by group")
229
255
  })
230
- }) : null, children]
256
+ }) : null, children, _jsx("div", {
257
+ tabIndex: 0,
258
+ onFocus: handleFocusOnSentinelItem,
259
+ className: "zen-dropdown-list__sentinel-item"
260
+ })]
231
261
  })
232
- }), [alignment, children, classNamePopup, contentRef, dialogAriaLabel, disabled, handleTriggerClick, inputId, inputRef, inputValue, isOpenCombo, isSearchInPopup, onInputChange, placeholder, popupId, searchField, translate, triggerId, triggerRef]);
262
+ }), [alignment, children, classNamePopup, contentRef, dialogAriaLabel, disabled, handleFocusOnSentinelItem, handleKeydown, handleTriggerClick, inputId, inputRef, inputValue, isOpenCombo, isSearchInPopup, onInputChange, placeholder, popupId, searchField, translate, triggerId, triggerRef]);
233
263
  return isMobile ? memoizedMobileView : memoizedDesktopView;
234
264
  };