colibri-ui 0.8.97 → 0.8.99

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 (166) hide show
  1. package/Accordion/index.js +4 -2
  2. package/Accordion/index.props.d.ts +2 -1
  3. package/Avatar/index.js +2 -5
  4. package/Avatar/index.props.d.ts +2 -1
  5. package/Badge/Control/index.props.d.ts +2 -1
  6. package/Badge/index.js +1 -2
  7. package/Button/index.js +0 -1
  8. package/Checkbox/index.js +4 -2
  9. package/Checkbox/index.props.d.ts +2 -1
  10. package/Chip/index.js +2 -4
  11. package/Chip/index.props.d.ts +2 -2
  12. package/ColorPicker/ColorPicker.d.ts +1 -1
  13. package/ColorPicker/ColorPicker.js +4 -2
  14. package/ColorPicker/index.props.d.ts +2 -1
  15. package/Counter/index.js +4 -2
  16. package/Counter/index.props.d.ts +2 -1
  17. package/Datepicker/index.d.ts +1 -1
  18. package/Datepicker/index.js +5 -3
  19. package/Datepicker/index.props.d.ts +2 -1
  20. package/DotsLoader/index.js +5 -2
  21. package/DotsLoader/index.props.d.ts +2 -1
  22. package/Dropdown/index.js +2 -2
  23. package/Dropdown/index.props.d.ts +2 -1
  24. package/Grid/index.js +3 -1
  25. package/Grid/index.props.d.ts +2 -1
  26. package/InputRadio/index.js +3 -3
  27. package/InputRadio/index.props.d.ts +2 -2
  28. package/Menu/components/MenuItem/index.js +0 -1
  29. package/Menu/components/MenuItem/index.props.d.ts +2 -1
  30. package/Menu/index.d.ts +1 -1
  31. package/Menu/index.js +4 -2
  32. package/Menu/index.props.d.ts +2 -1
  33. package/Modal/index.js +3 -1
  34. package/Modal/index.props.d.ts +2 -1
  35. package/MultiSelect/index.js +3 -1
  36. package/NavigationMenu/NavigationMenu.d.ts +3 -0
  37. package/NavigationMenu/NavigationMenu.js +170 -0
  38. package/NavigationMenu/NavigationMenu.module.css +67 -0
  39. package/NavigationMenu/components/MoreMenu/MoreMenu.module.css +19 -0
  40. package/NavigationMenu/components/MoreMenu/index.d.ts +16 -0
  41. package/NavigationMenu/components/MoreMenu/index.js +155 -0
  42. package/NavigationMenu/components/NavigationLink/NavigationLink.module.css +59 -0
  43. package/NavigationMenu/components/NavigationLink/index.d.ts +16 -0
  44. package/NavigationMenu/components/NavigationLink/index.js +51 -0
  45. package/NavigationMenu/helpers.d.ts +9 -0
  46. package/NavigationMenu/helpers.js +20 -0
  47. package/NavigationMenu/index.d.ts +2 -0
  48. package/NavigationMenu/index.js +7 -0
  49. package/NavigationMenu/index.props.d.ts +40 -0
  50. package/NavigationMenu/package.json +9 -0
  51. package/NavigationMenu/splitItems.d.ts +30 -0
  52. package/NavigationMenu/splitItems.js +54 -0
  53. package/NavigationMenu/useOverflowSplit.d.ts +25 -0
  54. package/NavigationMenu/useOverflowSplit.js +63 -0
  55. package/RadioGroup/Radio.js +2 -2
  56. package/RadioGroup/index.js +1 -1
  57. package/RadioGroup/index.props.d.ts +1 -1
  58. package/Select/index.d.ts +1 -1
  59. package/Select/index.js +4 -3
  60. package/Separator/index.js +6 -4
  61. package/Separator/index.props.d.ts +2 -1
  62. package/Skeleton/index.js +6 -2
  63. package/Skeleton/index.props.d.ts +3 -2
  64. package/Status/index.d.ts +1 -1
  65. package/Status/index.js +3 -1
  66. package/Status/index.props.d.ts +2 -1
  67. package/TextArea/index.js +0 -1
  68. package/Theme/index.d.ts +8 -0
  69. package/Theme/themes/buenos_aires.d.ts +4 -0
  70. package/Theme/themes/buenos_aires.js +4 -0
  71. package/Theme/themes/jaipur.d.ts +4 -0
  72. package/Theme/themes/jaipur.js +4 -0
  73. package/TimeSelect/index.js +3 -1
  74. package/Tooltip/index.js +3 -1
  75. package/Tooltip/index.props.d.ts +2 -1
  76. package/base/FormField/index.d.ts +1 -1
  77. package/base/FormField/index.js +4 -2
  78. package/base/FormField/index.props.d.ts +2 -1
  79. package/esm/Accordion/index.js +4 -2
  80. package/esm/Accordion/index.props.d.ts +2 -1
  81. package/esm/Avatar/index.js +2 -5
  82. package/esm/Avatar/index.props.d.ts +2 -1
  83. package/esm/Badge/Control/index.props.d.ts +2 -1
  84. package/esm/Badge/index.js +1 -2
  85. package/esm/Button/index.js +0 -1
  86. package/esm/Checkbox/index.js +4 -2
  87. package/esm/Checkbox/index.props.d.ts +2 -1
  88. package/esm/Chip/index.js +2 -4
  89. package/esm/Chip/index.props.d.ts +2 -2
  90. package/esm/ColorPicker/ColorPicker.d.ts +1 -1
  91. package/esm/ColorPicker/ColorPicker.js +4 -2
  92. package/esm/ColorPicker/index.props.d.ts +2 -1
  93. package/esm/Counter/index.js +4 -2
  94. package/esm/Counter/index.props.d.ts +2 -1
  95. package/esm/Datepicker/index.d.ts +1 -1
  96. package/esm/Datepicker/index.js +5 -3
  97. package/esm/Datepicker/index.props.d.ts +2 -1
  98. package/esm/DotsLoader/index.js +5 -2
  99. package/esm/DotsLoader/index.props.d.ts +2 -1
  100. package/esm/Dropdown/index.js +2 -2
  101. package/esm/Dropdown/index.props.d.ts +2 -1
  102. package/esm/Grid/index.js +3 -1
  103. package/esm/Grid/index.props.d.ts +2 -1
  104. package/esm/InputRadio/index.js +3 -3
  105. package/esm/InputRadio/index.props.d.ts +2 -2
  106. package/esm/Menu/components/MenuItem/index.js +0 -1
  107. package/esm/Menu/components/MenuItem/index.props.d.ts +2 -1
  108. package/esm/Menu/index.d.ts +1 -1
  109. package/esm/Menu/index.js +4 -2
  110. package/esm/Menu/index.props.d.ts +2 -1
  111. package/esm/Modal/index.js +3 -1
  112. package/esm/Modal/index.props.d.ts +2 -1
  113. package/esm/MultiSelect/index.js +3 -1
  114. package/esm/NavigationMenu/NavigationMenu.d.ts +3 -0
  115. package/esm/NavigationMenu/NavigationMenu.js +162 -0
  116. package/esm/NavigationMenu/NavigationMenu.module.css +67 -0
  117. package/esm/NavigationMenu/components/MoreMenu/MoreMenu.module.css +19 -0
  118. package/esm/NavigationMenu/components/MoreMenu/index.d.ts +16 -0
  119. package/esm/NavigationMenu/components/MoreMenu/index.js +147 -0
  120. package/esm/NavigationMenu/components/NavigationLink/NavigationLink.module.css +59 -0
  121. package/esm/NavigationMenu/components/NavigationLink/index.d.ts +16 -0
  122. package/esm/NavigationMenu/components/NavigationLink/index.js +41 -0
  123. package/esm/NavigationMenu/helpers.d.ts +9 -0
  124. package/esm/NavigationMenu/helpers.js +15 -0
  125. package/esm/NavigationMenu/index.d.ts +2 -0
  126. package/esm/NavigationMenu/index.js +1 -0
  127. package/esm/NavigationMenu/index.props.d.ts +40 -0
  128. package/esm/NavigationMenu/splitItems.d.ts +30 -0
  129. package/esm/NavigationMenu/splitItems.js +51 -0
  130. package/esm/NavigationMenu/useOverflowSplit.d.ts +25 -0
  131. package/esm/NavigationMenu/useOverflowSplit.js +61 -0
  132. package/esm/RadioGroup/Radio.js +2 -2
  133. package/esm/RadioGroup/index.js +1 -1
  134. package/esm/RadioGroup/index.props.d.ts +1 -1
  135. package/esm/Select/index.d.ts +1 -1
  136. package/esm/Select/index.js +4 -3
  137. package/esm/Separator/index.js +6 -4
  138. package/esm/Separator/index.props.d.ts +2 -1
  139. package/esm/Skeleton/index.js +6 -2
  140. package/esm/Skeleton/index.props.d.ts +3 -2
  141. package/esm/Status/index.d.ts +1 -1
  142. package/esm/Status/index.js +3 -1
  143. package/esm/Status/index.props.d.ts +2 -1
  144. package/esm/TextArea/index.js +0 -1
  145. package/esm/Theme/index.d.ts +8 -0
  146. package/esm/Theme/themes/buenos_aires.d.ts +4 -0
  147. package/esm/Theme/themes/buenos_aires.js +4 -0
  148. package/esm/Theme/themes/jaipur.d.ts +4 -0
  149. package/esm/Theme/themes/jaipur.js +4 -0
  150. package/esm/TimeSelect/index.js +3 -1
  151. package/esm/Tooltip/index.js +3 -1
  152. package/esm/Tooltip/index.props.d.ts +2 -1
  153. package/esm/base/FormField/index.d.ts +1 -1
  154. package/esm/base/FormField/index.js +4 -2
  155. package/esm/base/FormField/index.props.d.ts +2 -1
  156. package/esm/helpers/testId.d.ts +18 -0
  157. package/esm/index.d.ts +3 -0
  158. package/esm/index.js +1 -0
  159. package/esm/theme_ba_variables.css +4 -0
  160. package/esm/theme_jaipur_variables.css +4 -0
  161. package/helpers/testId.d.ts +18 -0
  162. package/index.d.ts +3 -0
  163. package/index.js +3 -0
  164. package/package.json +1 -1
  165. package/theme_ba_variables.css +4 -0
  166. package/theme_jaipur_variables.css +4 -0
package/esm/Chip/index.js CHANGED
@@ -11,13 +11,11 @@ const Chip = _a => {
11
11
  onClick,
12
12
  onClickIcon,
13
13
  size = "m",
14
- testID = "chip",
15
14
  variant = "primary"
16
15
  } = _a,
17
- props = __rest(_a, ["children", "iconEnd", "isActive", "onClick", "onClickIcon", "size", "testID", "variant"]);
16
+ props = __rest(_a, ["children", "iconEnd", "isActive", "onClick", "onClickIcon", "size", "variant"]);
18
17
  return /*#__PURE__*/React.createElement("span", Object.assign({
19
- className: styles.chip,
20
- "data-testid": testID
18
+ className: styles.chip
21
19
  }, props), /*#__PURE__*/React.createElement("span", {
22
20
  className: clsx(styles.inner, styles[`inner_variant_${variant}`], {
23
21
  [styles["inner_active"]]: iconEnd || isActive,
@@ -1,9 +1,9 @@
1
+ import type { TestIdProps } from "../helpers/testId";
1
2
  export type ChipSize = "s" | "m" | "l";
2
3
  export type ChipVariant = "primary" | "secondary" | "outline" | "pseudo" | "alert" | "success" | "clear";
3
- export type ChipProps = {
4
+ export type ChipProps = TestIdProps & {
4
5
  size?: ChipSize;
5
6
  variant?: ChipVariant;
6
- testID?: string;
7
7
  isActive?: boolean;
8
8
  iconEnd?: React.ReactNode;
9
9
  onClick?: () => void;
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import type { ColorPickerProps } from "./index.props";
3
- export declare const ColorPicker: ({ value, onChange, presets, columns, disabled, placement, zIndex, labels: labelsProp, children, }: ColorPickerProps) => React.JSX.Element;
3
+ export declare const ColorPicker: ({ value, onChange, presets, columns, disabled, placement, zIndex, labels: labelsProp, children, "data-testid": testId, }: ColorPickerProps) => React.JSX.Element;
@@ -23,7 +23,8 @@ const ColorPicker = ({
23
23
  placement = "bottom-start",
24
24
  zIndex,
25
25
  labels: labelsProp,
26
- children
26
+ children,
27
+ "data-testid": testId
27
28
  }) => {
28
29
  var _a, _b;
29
30
  const [isOpen, setOpen] = useState(false);
@@ -77,7 +78,8 @@ const ColorPicker = ({
77
78
  }));
78
79
  return /*#__PURE__*/React.createElement("span", {
79
80
  ref: rootRef,
80
- className: styles.root
81
+ className: styles.root,
82
+ "data-testid": testId
81
83
  }, /*#__PURE__*/React.createElement(Dropdown, {
82
84
  trigger: ["click"],
83
85
  visible: isOpen,
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import type { Placement } from "@floating-ui/react";
3
+ import type { TestIdProps } from "../helpers/testId";
3
4
  export interface ColorPickerPreset {
4
5
  /** HEX color, for example `#d81b60`. */
5
6
  value: string;
@@ -24,7 +25,7 @@ export interface ColorPickerLabels {
24
25
  cancel: string;
25
26
  save: string;
26
27
  }
27
- export interface ColorPickerProps {
28
+ export interface ColorPickerProps extends TestIdProps {
28
29
  /** Current color in HEX. A color missing from `presets` is shown as an extra swatch. */
29
30
  value?: string;
30
31
  /** Called with the chosen color as lowercase `#rrggbb`. */
@@ -12,7 +12,8 @@ const Counter = ({
12
12
  fullWidth,
13
13
  onChange,
14
14
  disabled,
15
- className
15
+ className,
16
+ "data-testid": testId
16
17
  }) => {
17
18
  const minusDisabled = disabled || value <= min;
18
19
  const plusDisabled = disabled || value >= max;
@@ -25,7 +26,8 @@ const Counter = ({
25
26
  return /*#__PURE__*/React.createElement("div", {
26
27
  className: clsx(styles.root, {
27
28
  [styles["root_fullWidth"]]: Boolean(fullWidth)
28
- }, className)
29
+ }, className),
30
+ "data-testid": testId
29
31
  }, /*#__PURE__*/React.createElement("div", {
30
32
  className: clsx(styles.control, {
31
33
  [styles["control_fullWidth"]]: Boolean(fullWidth)
@@ -1,4 +1,5 @@
1
- export type CounterProps = {
1
+ import type { TestIdProps } from "../helpers/testId";
2
+ export type CounterProps = TestIdProps & {
2
3
  value: number;
3
4
  max: number;
4
5
  min: number;
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import type { Props } from "./index.props";
3
- export declare const Datepicker: ({ className, disabled, endPlaceholder, hasError, hint, label, placeholder, required, selectedDate, selectedRange, selectionMode, size, startPlaceholder, onChangeDate, onChangeRange, ...calendarProps }: Props) => React.JSX.Element;
3
+ export declare const Datepicker: ({ className, disabled, endPlaceholder, hasError, hint, label, placeholder, required, selectedDate, selectedRange, selectionMode, size, startPlaceholder, onChangeDate, onChangeRange, "data-testid": testId, ...calendarProps }: Props) => React.JSX.Element;
@@ -20,11 +20,13 @@ const Datepicker = _a => {
20
20
  size = "m",
21
21
  startPlaceholder,
22
22
  onChangeDate,
23
- onChangeRange
23
+ onChangeRange,
24
+ "data-testid": testId
24
25
  } = _a,
25
- calendarProps = __rest(_a, ["className", "disabled", "endPlaceholder", "hasError", "hint", "label", "placeholder", "required", "selectedDate", "selectedRange", "selectionMode", "size", "startPlaceholder", "onChangeDate", "onChangeRange"]);
26
+ calendarProps = __rest(_a, ["className", "disabled", "endPlaceholder", "hasError", "hint", "label", "placeholder", "required", "selectedDate", "selectedRange", "selectionMode", "size", "startPlaceholder", "onChangeDate", "onChangeRange", "data-testid"]);
26
27
  return /*#__PURE__*/React.createElement("div", {
27
- className: clsx(styles.root, className)
28
+ className: clsx(styles.root, className),
29
+ "data-testid": testId
28
30
  }, /*#__PURE__*/React.createElement(DatePicker, Object.assign({}, calendarProps, {
29
31
  disabled: disabled,
30
32
  endPlaceholder: endPlaceholder,
@@ -1,10 +1,11 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { CalendarCommonProps } from "../Calendar";
3
+ import type { TestIdProps } from "../helpers/testId";
3
4
  export type DatepickerSize = "s" | "m" | "l";
4
5
  export type DatepickerSelectionMode = "single" | "range";
5
6
  export type DatepickerRangeValue = [string, string];
6
7
  export type DatepickerCalendarProps = Omit<CalendarCommonProps, "className" | "size">;
7
- export type Props = DatepickerCalendarProps & {
8
+ export type Props = DatepickerCalendarProps & TestIdProps & {
8
9
  className?: string;
9
10
  disabled?: boolean;
10
11
  endPlaceholder?: string;
@@ -2,8 +2,11 @@ import React from 'react';
2
2
  import clsx from 'clsx';
3
3
  import styles from './DotsLoader.module.css';
4
4
 
5
- const DotsLoader = () => (/*#__PURE__*/React.createElement("div", {
6
- className: styles.wrapper
5
+ const DotsLoader = ({
6
+ "data-testid": testId
7
+ }) => (/*#__PURE__*/React.createElement("div", {
8
+ className: styles.wrapper,
9
+ "data-testid": testId
7
10
  }, /*#__PURE__*/React.createElement("div", {
8
11
  className: clsx({
9
12
  [styles.dot]: true,
@@ -1 +1,2 @@
1
- export type DotsLoaderProps = Record<string, never>;
1
+ import type { TestIdProps } from "../helpers/testId";
2
+ export type DotsLoaderProps = TestIdProps;
@@ -6,6 +6,7 @@ import styles from './Dropdown.module.css';
6
6
 
7
7
  const Dropdown = ({
8
8
  children,
9
+ "data-testid": testId,
9
10
  zIndex,
10
11
  fontSize,
11
12
  disabled,
@@ -87,7 +88,7 @@ const Dropdown = ({
87
88
  onClick: () => setOpen(false)
88
89
  }, /*#__PURE__*/React.createElement("div", Object.assign({
89
90
  ref: refs.setFloating,
90
- "data-testid": "popper-container",
91
+ "data-testid": testId,
91
92
  className: clsx(styles["popper-container"], styles[`placement-${finalPlacement}`]),
92
93
  style: Object.assign(Object.assign({}, floatingStyles), {
93
94
  zIndex,
@@ -97,7 +98,6 @@ const Dropdown = ({
97
98
  role: "menu",
98
99
  tabIndex: 0
99
100
  }, getFloatingProps()), /*#__PURE__*/React.createElement("div", {
100
- "data-testid": "overlay-container",
101
101
  className: styles["overlay-container"],
102
102
  onClick: onClickOverlay
103
103
  }, overlay))))));
@@ -1,7 +1,8 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { Placement, Strategy } from "@floating-ui/react";
3
+ import type { PopupTestIdProps } from "../helpers/testId";
3
4
  export type Trigger = "click" | "hover" | "focus";
4
- export type DropdownProps = {
5
+ export type DropdownProps = PopupTestIdProps & {
5
6
  defaultVisible?: boolean;
6
7
  disabled?: boolean;
7
8
  flip?: boolean;
package/esm/Grid/index.js CHANGED
@@ -8,9 +8,11 @@ const Grid = ({
8
8
  gridRowGap = "6",
9
9
  gridColumnGap = "4",
10
10
  gridItemMinWidth = "var(--component-grid-item-min-width)",
11
- className
11
+ className,
12
+ "data-testid": testId
12
13
  }) => (/*#__PURE__*/React.createElement("div", {
13
14
  className: clsx(styles.root, className),
15
+ "data-testid": testId,
14
16
  style: {
15
17
  rowGap: `var(--space-${gridRowGap})`,
16
18
  columnGap: `var(--space-${gridColumnGap})`,
@@ -1,5 +1,6 @@
1
+ import type { TestIdProps } from "../helpers/testId";
1
2
  export type Space = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "8" | "10" | "12" | "16" | "20" | "24" | "32";
2
- export type GridProps = {
3
+ export type GridProps = TestIdProps & {
3
4
  className?: string;
4
5
  gridRowGap?: Space;
5
6
  gridColumnGap?: Space;
@@ -23,9 +23,9 @@ const InputRadio = /*#__PURE__*/memo(_a => {
23
23
  style,
24
24
  text,
25
25
  value,
26
- testid
26
+ "data-testid": testId
27
27
  } = _a,
28
- rest = __rest(_a, ["checked", "children", "className", "containerClassName", "disabled", "error", "id", "name", "note", "onBlur", "onFocus", "onChange", "onClick", "onMouseEnter", "style", "text", "value", "testid"]);
28
+ rest = __rest(_a, ["checked", "children", "className", "containerClassName", "disabled", "error", "id", "name", "note", "onBlur", "onFocus", "onChange", "onClick", "onMouseEnter", "style", "text", "value", "data-testid"]);
29
29
  return (
30
30
  /*#__PURE__*/
31
31
  // Keyboard activation of the nested radio dispatches a click that bubbles to the label.
@@ -36,7 +36,7 @@ const InputRadio = /*#__PURE__*/memo(_a => {
36
36
  style: style,
37
37
  onClick: onClick,
38
38
  onMouseEnter: onMouseEnter,
39
- "data-testid": testid
39
+ "data-testid": testId
40
40
  }, /*#__PURE__*/React.createElement("div", {
41
41
  className: clsx(styles.container, {
42
42
  [styles["container_disabled"]]: Boolean(disabled),
@@ -1,5 +1,6 @@
1
1
  import type { ChangeEvent, CSSProperties, FocusEvent, PropsWithChildren } from "react";
2
- export type InputRadioProps = PropsWithChildren<{
2
+ import type { TestIdProps } from "../helpers/testId";
3
+ export type InputRadioProps = PropsWithChildren<TestIdProps & {
3
4
  checked?: boolean;
4
5
  className?: string;
5
6
  containerClassName?: string;
@@ -16,5 +17,4 @@ export type InputRadioProps = PropsWithChildren<{
16
17
  style?: CSSProperties;
17
18
  text?: string;
18
19
  value: string | number;
19
- testid?: string;
20
20
  }>;
@@ -25,7 +25,6 @@ const MenuItem = _a => {
25
25
  ref: ref,
26
26
  role: "menuitem",
27
27
  tabIndex: disabled ? -1 : 0,
28
- "data-testid": "menuitem",
29
28
  "aria-disabled": disabled || undefined,
30
29
  className: clsx(styles.item, {
31
30
  [styles["item_disabled"]]: Boolean(disabled),
@@ -1,6 +1,7 @@
1
1
  import type { KeyboardEvent, MouseEvent } from "react";
2
2
  import type { MenuVariant } from "../../index.props";
3
- export interface MenuItemProps {
3
+ import type { TestIdProps } from "../../../helpers/testId";
4
+ export interface MenuItemProps extends TestIdProps {
4
5
  className?: string;
5
6
  isSelected?: boolean;
6
7
  onClick?: (event: MouseEvent<Element> | KeyboardEvent<Element>) => void;
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import type { Props } from "./index.props";
3
- export declare const Menu: ({ selected, variant, className, onClick, children, ref, }: Props) => React.JSX.Element;
3
+ export declare const Menu: ({ selected, variant, className, onClick, children, ref, "data-testid": testId, }: Props) => React.JSX.Element;
package/esm/Menu/index.js CHANGED
@@ -8,7 +8,8 @@ const Menu = ({
8
8
  className,
9
9
  onClick,
10
10
  children,
11
- ref
11
+ ref,
12
+ "data-testid": testId
12
13
  }) => {
13
14
  const renderChild = useMemo(() => Children.map(children, child => {
14
15
  if (! /*#__PURE__*/isValidElement(child)) return child;
@@ -28,7 +29,8 @@ const Menu = ({
28
29
  return /*#__PURE__*/React.createElement("ul", {
29
30
  ref: ref,
30
31
  className: clsx(styles.menu, className),
31
- role: "menu"
32
+ role: "menu",
33
+ "data-testid": testId
32
34
  }, renderChild);
33
35
  };
34
36
 
@@ -1,7 +1,8 @@
1
1
  import type { ReactElement, KeyboardEvent, MouseEvent } from "react";
2
2
  import type { MenuItemProps } from "./components/MenuItem/index.props";
3
+ import type { TestIdProps } from "../helpers/testId";
3
4
  export type MenuVariant = "primary";
4
- export type Props = {
5
+ export type Props = TestIdProps & {
5
6
  selected?: string[];
6
7
  variant?: MenuVariant;
7
8
  className?: string;
@@ -23,7 +23,8 @@ const ModalRoot = ({
23
23
  onClose,
24
24
  title,
25
25
  withinParent = false,
26
- fillParent = false
26
+ fillParent = false,
27
+ "data-testid": testId
27
28
  }) => {
28
29
  const [dialog, setDialog] = useState(null);
29
30
  const [container, setContainer] = useState(null);
@@ -91,6 +92,7 @@ const ModalRoot = ({
91
92
  ref: setDialog,
92
93
  role: "dialog",
93
94
  "aria-modal": "true",
95
+ "data-testid": testId,
94
96
  tabIndex: -1,
95
97
  "aria-labelledby": titleCount > 0 ? titleId : undefined
96
98
  }, /*#__PURE__*/React.createElement("div", {
@@ -1,5 +1,6 @@
1
1
  import type { HTMLAttributes } from "react";
2
- export type ModalProps = {
2
+ import type { TestIdProps } from "../helpers/testId";
3
+ export type ModalProps = TestIdProps & {
3
4
  className?: string;
4
5
  onClose?: () => void;
5
6
  /**
@@ -32,7 +32,8 @@ const MultiSelect = ({
32
32
  value = [],
33
33
  type,
34
34
  onChange,
35
- "aria-label": ariaLabel
35
+ "aria-label": ariaLabel,
36
+ "data-testid": testId
36
37
  }) => {
37
38
  const [isOpen, setIsOpen] = useState(false);
38
39
  const id = useId();
@@ -72,6 +73,7 @@ const MultiSelect = ({
72
73
  })
73
74
  }))), [preparedGroups]);
74
75
  return /*#__PURE__*/React.createElement(FormField, {
76
+ "data-testid": testId,
75
77
  className: clsx(styles["form-field"], {
76
78
  [styles["full-width"]]: Boolean(fullWidth)
77
79
  }, className),
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import type { NavigationMenuProps } from "./index.props";
3
+ export declare const NavigationMenu: React.ForwardRefExoticComponent<NavigationMenuProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,162 @@
1
+ import { __rest } from 'tslib';
2
+ import React, { forwardRef, useRef } from 'react';
3
+ import clsx from 'clsx';
4
+ import { MoreMenu } from './components/MoreMenu/index.js';
5
+ import { NavigationLink, getLinkClassName, LinkContent } from './components/NavigationLink/index.js';
6
+ import { useIsomorphicLayoutEffect, getItemKey, assignRef, isSameItem } from './helpers.js';
7
+ import { useOverflowSplit } from './useOverflowSplit.js';
8
+ import styles from './NavigationMenu.module.css';
9
+
10
+ const FOCUSABLE = "a[href], button, [tabindex]";
11
+ const getActiveIndex = items => items.findIndex(item => item.active);
12
+ const OverflowMenu = _a => {
13
+ var {
14
+ items,
15
+ pinnedItems = [],
16
+ moreItems = [],
17
+ moreLabel,
18
+ onItemClick,
19
+ renderLink,
20
+ navRef,
21
+ rootClassName
22
+ } = _a,
23
+ props = __rest(_a, ["items", "pinnedItems", "moreItems", "moreLabel", "onItemClick", "renderLink", "navRef", "rootClassName"]);
24
+ const {
25
+ split,
26
+ containerRef,
27
+ listRef,
28
+ measurerRef,
29
+ moreRef
30
+ } = useOverflowSplit({
31
+ itemCount: items.length,
32
+ pinnedCount: pinnedItems.length,
33
+ activeIndex: getActiveIndex(items),
34
+ forceMore: moreItems.length > 0,
35
+ contentKey: [...items, ...pinnedItems].map(item => item.label).join("\n")
36
+ });
37
+ // After `items` change and before the layout effect remeasures, the old
38
+ // split may point to missing indexes: drop them, new items go to "More".
39
+ const visibleIndexes = split === null || split === void 0 ? void 0 : split.visible.filter(index => index < items.length);
40
+ const visibleItems = visibleIndexes ? visibleIndexes.map(index => items[index]) : items;
41
+ const hiddenItems = visibleIndexes ? [...items.filter((_, index) => !visibleIndexes.includes(index)), ...moreItems] : [];
42
+ const isMoreHidden = hiddenItems.length === 0;
43
+ // "More" disappeared with a focused link inside: focus the same item in the
44
+ // row, or the first row link when it is not there.
45
+ const restoreFocus = item => {
46
+ var _a, _b, _c, _d, _e;
47
+ const rows = Array.from((_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : []);
48
+ const rowItems = [...visibleItems, ...pinnedItems];
49
+ const row = rows[rowItems.findIndex(rowItem => isSameItem(rowItem, item))];
50
+ (_e = (_c = row === null || row === void 0 ? void 0 : row.querySelector(FOCUSABLE)) !== null && _c !== void 0 ? _c : (_d = listRef.current) === null || _d === void 0 ? void 0 : _d.querySelector(FOCUSABLE)) === null || _e === void 0 ? void 0 : _e.focus();
51
+ };
52
+ const renderItem = (item, index) => (/*#__PURE__*/React.createElement("li", {
53
+ key: getItemKey(item, index),
54
+ className: styles.list_item
55
+ }, /*#__PURE__*/React.createElement(NavigationLink, {
56
+ item: item,
57
+ onClick: onItemClick,
58
+ renderLink: renderLink
59
+ })));
60
+ return /*#__PURE__*/React.createElement("nav", Object.assign({}, props, {
61
+ ref: node => {
62
+ containerRef.current = node;
63
+ assignRef(navRef, node);
64
+ },
65
+ className: clsx(rootClassName, styles.overflow, {
66
+ [styles.overflow_measuring]: !split
67
+ })
68
+ }), /*#__PURE__*/React.createElement("ul", {
69
+ ref: listRef,
70
+ className: styles.list
71
+ }, visibleItems.map(renderItem), pinnedItems.map(renderItem), /*#__PURE__*/React.createElement("li", {
72
+ ref: moreRef,
73
+ className: clsx(styles.list_item, {
74
+ [styles.more_hidden]: isMoreHidden
75
+ }),
76
+ "aria-hidden": isMoreHidden || undefined
77
+ }, /*#__PURE__*/React.createElement(MoreMenu, {
78
+ items: hiddenItems,
79
+ label: moreLabel,
80
+ hidden: isMoreHidden,
81
+ onItemClick: onItemClick,
82
+ renderLink: renderLink,
83
+ onFocusLost: restoreFocus
84
+ }))), /*#__PURE__*/React.createElement("div", {
85
+ ref: measurerRef,
86
+ className: styles.measurer,
87
+ "aria-hidden": "true"
88
+ }, [...items, ...pinnedItems].map((item, index) => (/*#__PURE__*/React.createElement("span", {
89
+ key: getItemKey(item, index),
90
+ className: getLinkClassName("item", item.active)
91
+ }, /*#__PURE__*/React.createElement(LinkContent, {
92
+ item: item
93
+ }))))));
94
+ };
95
+ const ScrollMenu = _a => {
96
+ var {
97
+ items,
98
+ pinnedItems = [],
99
+ moreItems = [],
100
+ onItemClick,
101
+ renderLink,
102
+ navRef,
103
+ rootClassName
104
+ } = _a,
105
+ props = __rest(_a, ["items", "pinnedItems", "moreItems", "onItemClick", "renderLink", "navRef", "rootClassName"]);
106
+ const scrollRef = useRef(null);
107
+ const activeRef = useRef(null);
108
+ const allItems = [...items, ...pinnedItems, ...moreItems];
109
+ const activeIndex = getActiveIndex(allItems);
110
+ // Center the active item when it is not fully visible.
111
+ useIsomorphicLayoutEffect(() => {
112
+ const container = scrollRef.current;
113
+ const active = activeRef.current;
114
+ if (!container || !active) return;
115
+ const {
116
+ offsetLeft,
117
+ offsetWidth
118
+ } = active;
119
+ const isVisible = offsetLeft >= container.scrollLeft && offsetLeft + offsetWidth <= container.scrollLeft + container.clientWidth;
120
+ if (!isVisible) {
121
+ container.scrollLeft = offsetLeft + offsetWidth / 2 - container.clientWidth / 2;
122
+ }
123
+ }, [activeIndex]);
124
+ return /*#__PURE__*/React.createElement("nav", Object.assign({}, props, {
125
+ ref: navRef,
126
+ className: clsx(rootClassName, styles.scroll)
127
+ }), /*#__PURE__*/React.createElement("div", {
128
+ ref: scrollRef,
129
+ className: styles.scroll_area
130
+ }, /*#__PURE__*/React.createElement("ul", {
131
+ className: styles.list
132
+ }, allItems.map((item, index) => (/*#__PURE__*/React.createElement("li", {
133
+ key: getItemKey(item, index),
134
+ ref: index === activeIndex ? activeRef : undefined,
135
+ className: styles.list_item
136
+ }, /*#__PURE__*/React.createElement(NavigationLink, {
137
+ item: item,
138
+ onClick: onItemClick,
139
+ renderLink: renderLink
140
+ })))))));
141
+ };
142
+ const NavigationMenu = /*#__PURE__*/forwardRef((_a, ref) => {
143
+ var {
144
+ layout = "overflow",
145
+ size = "m",
146
+ moreLabel = "More",
147
+ className
148
+ } = _a,
149
+ props = __rest(_a, ["layout", "size", "moreLabel", "className"]);
150
+ const rootClassName = clsx(styles.root, styles[`size_${size}`], className);
151
+ return layout === "scroll" ? (/*#__PURE__*/React.createElement(ScrollMenu, Object.assign({}, props, {
152
+ navRef: ref,
153
+ rootClassName: rootClassName
154
+ }))) : (/*#__PURE__*/React.createElement(OverflowMenu, Object.assign({}, props, {
155
+ moreLabel: moreLabel,
156
+ navRef: ref,
157
+ rootClassName: rootClassName
158
+ })));
159
+ });
160
+ NavigationMenu.displayName = "NavigationMenu";
161
+
162
+ export { NavigationMenu };
@@ -0,0 +1,67 @@
1
+ .root {
2
+ font-family: var(--font-family-ui);
3
+ min-inline-size: 0;
4
+ }
5
+ .root.size_m {
6
+ --navigation-item-height: calc(var(--space-8) + var(--space-1));
7
+ --navigation-item-font-size: var(--font-size-text-md);
8
+ --navigation-item-line-height: var(--line-height-text-md);
9
+ --navigation-item-padding: var(--space-4);
10
+ --navigation-menu-gap: var(--space-2);
11
+ }
12
+ .root.size_s {
13
+ --navigation-item-height: var(--space-8);
14
+ --navigation-item-font-size: var(--font-size-text-sm);
15
+ --navigation-item-line-height: var(--line-height-text-sm);
16
+ --navigation-item-padding: var(--space-3);
17
+ --navigation-menu-gap: var(--space-1);
18
+ }
19
+
20
+ .list {
21
+ align-items: center;
22
+ display: flex;
23
+ gap: var(--navigation-menu-gap);
24
+ list-style: none;
25
+ margin: 0;
26
+ padding: 0;
27
+ }
28
+ .list_item {
29
+ display: flex;
30
+ flex-shrink: 0;
31
+ }
32
+
33
+ .overflow {
34
+ display: block;
35
+ position: relative;
36
+ }
37
+ .overflow_measuring {
38
+ overflow: hidden;
39
+ }
40
+
41
+ .more_hidden {
42
+ pointer-events: none;
43
+ position: absolute;
44
+ visibility: hidden;
45
+ }
46
+
47
+ .measurer {
48
+ block-size: 0;
49
+ display: flex;
50
+ inline-size: max-content;
51
+ inset-block-start: 0;
52
+ inset-inline-start: 0;
53
+ overflow: hidden;
54
+ pointer-events: none;
55
+ position: absolute;
56
+ visibility: hidden;
57
+ }
58
+
59
+ .scroll_area {
60
+ overflow: auto hidden;
61
+ overscroll-behavior-x: contain;
62
+ position: relative;
63
+ scrollbar-width: none;
64
+ }
65
+ .scroll_area::-webkit-scrollbar {
66
+ display: none;
67
+ }
@@ -0,0 +1,19 @@
1
+ .root {
2
+ display: contents;
3
+ }
4
+
5
+ .chevron {
6
+ flex-shrink: 0;
7
+ transition: transform 0.15s ease;
8
+ }
9
+ .chevron_open {
10
+ transform: rotate(180deg);
11
+ }
12
+
13
+ .options {
14
+ display: flex;
15
+ flex-direction: column;
16
+ font-family: var(--font-family-ui);
17
+ min-inline-size: calc(var(--space-32) + var(--space-16));
18
+ padding: var(--space-1);
19
+ }
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import type { ReactNode } from "react";
3
+ import type { NavigationMenuItem, NavigationMenuRenderLink } from "../../index.props";
4
+ interface MoreMenuProps {
5
+ items: NavigationMenuItem[];
6
+ label: ReactNode;
7
+ /** Nothing to show: the trigger stays in the DOM only to be measured. */
8
+ hidden: boolean;
9
+ onItemClick?: (item: NavigationMenuItem) => void;
10
+ renderLink?: NavigationMenuRenderLink;
11
+ /** The menu became hidden while this item had focus inside the dropdown. */
12
+ onFocusLost: (item: NavigationMenuItem) => void;
13
+ }
14
+ /** "More" trigger with a dropdown of the items that do not fit. */
15
+ export declare const MoreMenu: ({ items, label, hidden, onItemClick, renderLink, onFocusLost, }: MoreMenuProps) => React.JSX.Element;
16
+ export {};