@sisense/sdk-shared-ui 0.1.0 → 1.26.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 (119) hide show
  1. package/.storybook/main.ts +44 -0
  2. package/.storybook/preview-head.html +4 -0
  3. package/.storybook/preview.tsx +34 -0
  4. package/dist/index.js +40 -2
  5. package/package.json +99 -2
  6. package/src/lib/CheckableList/CheckableList.module.scss +13 -0
  7. package/src/lib/CheckableList/CheckableList.test.tsx +61 -0
  8. package/src/lib/CheckableList/CheckableList.tsx +41 -0
  9. package/src/lib/CheckableList/index.ts +4 -0
  10. package/src/lib/Checkbox/Checkbox.tsx +28 -0
  11. package/src/lib/Checkbox/index.ts +4 -0
  12. package/src/lib/Checkbox/themes/checkboxTheme.ts +42 -0
  13. package/src/lib/Checkbox/themes/index.ts +1 -0
  14. package/src/lib/Checkbox/themes/uiCustomization.ts +28 -0
  15. package/src/lib/DEPRECATED_Button/DEPRECATED_Button.module.scss +57 -0
  16. package/src/lib/DEPRECATED_Button/DEPRECATED_Button.stories.tsx +169 -0
  17. package/src/lib/DEPRECATED_Button/DEPRECATED_Button.test.tsx +154 -0
  18. package/src/lib/DEPRECATED_Button/DEPRECATED_Button.tsx +129 -0
  19. package/src/lib/DEPRECATED_Button/index.ts +4 -0
  20. package/src/lib/DEPRECATED_Checkbox/Checkbox.module.scss +49 -0
  21. package/src/lib/DEPRECATED_Checkbox/DEPRECATED_Checkbox.test.tsx +131 -0
  22. package/src/lib/DEPRECATED_Checkbox/DEPRECATED_Checkbox.tsx +103 -0
  23. package/src/lib/DEPRECATED_Checkbox/index.ts +4 -0
  24. package/src/lib/DEPRECATED_Icon/DEPRECATED_Icon.stories.tsx +27 -0
  25. package/src/lib/DEPRECATED_Icon/DEPRECATED_Icon.tsx +2 -0
  26. package/src/lib/DEPRECATED_Toggle/DEPRECATED_Toggle.tsx +49 -0
  27. package/src/lib/DEPRECATED_Toggle/index.ts +4 -0
  28. package/src/lib/DEPRECATED_Tooltip/DEPRECATED_Tooltip.module.scss +115 -0
  29. package/src/lib/DEPRECATED_Tooltip/DEPRECATED_Tooltip.tsx +68 -0
  30. package/src/lib/DEPRECATED_Tooltip/index.ts +4 -0
  31. package/src/lib/Dropdown/Dropdown.module.scss +9 -0
  32. package/src/lib/Dropdown/Dropdown.tsx +150 -0
  33. package/src/lib/Dropdown/DropdownButton/DropdownButton.module.scss +49 -0
  34. package/src/lib/Dropdown/DropdownButton/DropdownButton.tsx +81 -0
  35. package/src/lib/Dropdown/DropdownButton/DropdownButtonBody/DropdownButtonBody.test.tsx +121 -0
  36. package/src/lib/Dropdown/DropdownButton/DropdownButtonBody/DropdownButtonBody.tsx +39 -0
  37. package/src/lib/Dropdown/DropdownButton/DropdownButtonBody/index.ts +2 -0
  38. package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/DropdownButtonSearch.module.scss +20 -0
  39. package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/DropdownButtonSearch.tsx +44 -0
  40. package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/constants.ts +1 -0
  41. package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/index.ts +2 -0
  42. package/src/lib/Dropdown/DropdownButton/hooks/index.tsx +1 -0
  43. package/src/lib/Dropdown/DropdownButton/hooks/useDropdownButtonSearch.ts +51 -0
  44. package/src/lib/Dropdown/DropdownButton/index.ts +1 -0
  45. package/src/lib/Dropdown/hooks/index.tsx +1 -0
  46. package/src/lib/Dropdown/hooks/useDropdown.ts +29 -0
  47. package/src/lib/Dropdown/index.ts +5 -0
  48. package/src/lib/Dropdown/types.ts +17 -0
  49. package/src/lib/Icon/Icon.tsx +118 -0
  50. package/src/lib/Icon/index.ts +4 -0
  51. package/src/lib/Icon/themes/iconTheme.tsx +38 -0
  52. package/src/lib/Icon/themes/index.ts +1 -0
  53. package/src/lib/Icon/themes/uiCustomization.ts +9 -0
  54. package/src/lib/Input/Input.module.scss +97 -0
  55. package/src/lib/Input/Input.test.tsx +177 -0
  56. package/src/lib/Input/Input.tsx +134 -0
  57. package/src/lib/Input/index.ts +4 -0
  58. package/src/lib/LazyLoader/LazyLoader.module.scss +18 -0
  59. package/src/lib/LazyLoader/LazyLoader.tsx +42 -0
  60. package/src/lib/LazyLoader/index.ts +4 -0
  61. package/src/lib/Menu/Confirmation/Confirmation.module.scss +48 -0
  62. package/src/lib/Menu/Confirmation/Confirmation.tsx +45 -0
  63. package/src/lib/Menu/Confirmation/index.ts +1 -0
  64. package/src/lib/Menu/Confirmation/translation.ts +17 -0
  65. package/src/lib/Menu/Menu.module.scss +178 -0
  66. package/src/lib/Menu/Menu.tsx +363 -0
  67. package/src/lib/Menu/MenuItem/MenuItem.test.tsx +241 -0
  68. package/src/lib/Menu/MenuItem/MenuItem.tsx +186 -0
  69. package/src/lib/Menu/MenuItem/index.ts +2 -0
  70. package/src/lib/Menu/index.ts +6 -0
  71. package/src/lib/Menu/utils.ts +13 -0
  72. package/src/lib/Popover/Popover.module.scss +15 -0
  73. package/src/lib/Popover/Popover.tsx +92 -0
  74. package/src/lib/Popover/align.interface.ts +20 -0
  75. package/src/lib/Popover/index.ts +6 -0
  76. package/src/lib/RadioButton/RadioButton.module.scss +22 -0
  77. package/src/lib/RadioButton/RadioButton.tsx +89 -0
  78. package/src/lib/RadioButton/index.ts +4 -0
  79. package/src/lib/TablePagination/PaginationActionsComponent/PaginationActionsComponent.tsx +87 -0
  80. package/src/lib/TablePagination/PaginationActionsComponent/index.ts +4 -0
  81. package/src/lib/TablePagination/PaginationActionsComponent/themes/index.ts +4 -0
  82. package/src/lib/TablePagination/PaginationActionsComponent/themes/paginationActionsComponentTheme.tsx +70 -0
  83. package/src/lib/TablePagination/PaginationActionsComponent/themes/uiCustomization.ts +17 -0
  84. package/src/lib/TablePagination/TablePagination.tsx +148 -0
  85. package/src/lib/TablePagination/TablePaginationContext.ts +4 -0
  86. package/src/lib/TablePagination/index.ts +4 -0
  87. package/src/lib/TablePagination/themes/index.ts +1 -0
  88. package/src/lib/TablePagination/themes/tablePaginationResponsiveDesign.ts +33 -0
  89. package/src/lib/TablePagination/themes/tablePaginationTheme.tsx +128 -0
  90. package/src/lib/TablePagination/themes/uiCustomization.ts +50 -0
  91. package/src/lib/Tooltip/Tooltip.module.scss +125 -0
  92. package/src/lib/Tooltip/Tooltip.tsx +34 -0
  93. package/src/lib/Tooltip/index.ts +5 -0
  94. package/src/lib/Tooltip/themes/index.ts +1 -0
  95. package/src/lib/Tooltip/themes/tooltipTheme.ts +30 -0
  96. package/src/lib/Typography/Typography.tsx +28 -0
  97. package/src/lib/Typography/index.ts +5 -0
  98. package/src/lib/Typography/themes/index.ts +1 -0
  99. package/src/lib/Typography/themes/typographyTheme.tsx +170 -0
  100. package/src/lib/Typography/themes/uiCustomization.ts +10 -0
  101. package/src/lib/constants/styleguideConstants.ts +8 -0
  102. package/src/lib/index.ts +16 -0
  103. package/src/lib/styles/colors.module.scss +10 -0
  104. package/src/lib/styles/sisenseStyleguideReactColors.scss +57 -0
  105. package/src/lib/styles/style_styleguide_react/_variables.deprecated.scss +107 -0
  106. package/src/lib/styles/style_styleguide_react/_variables.scss +100 -0
  107. package/src/lib/styles/style_styleguide_react/base.scss +9 -0
  108. package/src/lib/styles/style_styleguide_react/colors.scss +71 -0
  109. package/src/lib/styles/style_styleguide_react/exports/colors.exports.scss +69 -0
  110. package/src/lib/styles/style_styleguide_react/exports/fonts.exports.scss +33 -0
  111. package/src/lib/styles/style_styleguide_react/fonts.scss +27 -0
  112. package/src/lib/styles/style_styleguide_react/mixins.scss +90 -0
  113. package/src/lib/themes/colors/index.ts +1 -0
  114. package/src/lib/themes/colors/siColors.ts +143 -0
  115. package/src/lib/themes/index.ts +2 -0
  116. package/src/lib/themes/types/SisenseTheme.ts +12 -0
  117. package/src/lib/themes/types/index.ts +1 -0
  118. package/tsconfig.lib.json +2 -1
  119. package/vite.config.ts +18 -1
@@ -0,0 +1,363 @@
1
+ import React, { Component } from 'react';
2
+ import classnames from 'classnames';
3
+ import { Scrollbars } from 'react-custom-scrollbars';
4
+ import type { ScrollbarProps } from 'react-custom-scrollbars';
5
+
6
+ import { Popover } from '../Popover';
7
+ import { LazyLoader } from '../LazyLoader';
8
+ import { MenuItem } from './MenuItem';
9
+ import { Confirmation } from './Confirmation';
10
+
11
+ import type { ItemType } from './MenuItem';
12
+
13
+ import { groupBy } from './utils';
14
+
15
+ import style from './Menu.module.scss';
16
+
17
+ export type MenuItemConfig = {
18
+ actionableComponent?: ActionableComponent;
19
+ caption: string;
20
+ subCaption?: string;
21
+ checked?: boolean;
22
+ confirmation?: {
23
+ title: string;
24
+ message: string;
25
+ inline: boolean;
26
+ };
27
+ dataTestId?: string;
28
+ disabled?: boolean;
29
+ groupId?: string;
30
+ hidden?: boolean;
31
+ iconClass?: string;
32
+ iconName?: string;
33
+ id: string;
34
+ onClick?: () => void;
35
+ selected?: boolean;
36
+ separator?: boolean;
37
+ subItems?: MenuItemConfig[];
38
+ subItemScrollbarClassName?: string;
39
+ tooltip?: string | React.ReactFragment;
40
+ type: ItemType;
41
+ value?: string;
42
+ groupTitle?: boolean;
43
+ };
44
+
45
+ type ActionableComponent = {
46
+ component: React.ComponentType<{ onRequestClose?: () => void }>;
47
+ componentProps: any;
48
+ };
49
+
50
+ export type MenuProps = {
51
+ className?: string;
52
+ items: MenuItemConfig[];
53
+ level?: number;
54
+ onItemSelected: (menuItemConfig: MenuItemConfig, value?: string) => void;
55
+ onMouseEnter?: () => void;
56
+ onRequestClose?: () => void;
57
+ scrollbarProps: ScrollbarProps;
58
+ width?: number;
59
+ isLoading?: boolean;
60
+ classNameTooltip?: string;
61
+ zIndex?: number;
62
+ };
63
+
64
+ type State = {
65
+ activeSubMenu: string | null;
66
+ isHoverSubMenu: boolean;
67
+ };
68
+
69
+ const noop = () => {};
70
+
71
+ class Menu extends Component<MenuProps, State> {
72
+ timeoutIn: number | null;
73
+
74
+ static defaultProps = {
75
+ onMouseEnter: noop,
76
+ onRequestClose: noop,
77
+ scrollbarProps: {},
78
+ level: 0,
79
+ isLoading: false,
80
+ zIndex: 1,
81
+ };
82
+
83
+ constructor(props: MenuProps) {
84
+ super(props);
85
+
86
+ this.state = {
87
+ activeSubMenu: null,
88
+ isHoverSubMenu: false,
89
+ };
90
+
91
+ this.timeoutIn = null;
92
+ }
93
+
94
+ componentWillUnmount() {
95
+ this.clearTimeout();
96
+ }
97
+
98
+ handleClick = (item: MenuItemConfig) => {
99
+ if (item.subItems || item.actionableComponent) {
100
+ this.clearTimeout();
101
+
102
+ if (this.state.activeSubMenu === item.id) {
103
+ this.closeSubMenu();
104
+ } else {
105
+ this.setState({ activeSubMenu: item.id });
106
+ }
107
+
108
+ return;
109
+ } else if (item.confirmation && !item.confirmation.inline) {
110
+ this.clearTimeout();
111
+ this.setState({ activeSubMenu: item.id });
112
+ return;
113
+ }
114
+
115
+ const { onItemSelected } = this.props;
116
+ onItemSelected(item, item.value);
117
+ };
118
+
119
+ handleOver = (item: MenuItemConfig) => {
120
+ this.clearTimeout();
121
+ this.setState({ isHoverSubMenu: false });
122
+
123
+ const activeSubMenu = item.subItems || item.actionableComponent ? item.id : null;
124
+
125
+ this.timeoutIn = window.setTimeout(() => {
126
+ this.setState({
127
+ activeSubMenu,
128
+ });
129
+ }, 900);
130
+ };
131
+
132
+ handleOut = (item: MenuItemConfig) => {
133
+ if ((item.subItems || item.actionableComponent) && this.state.activeSubMenu) {
134
+ this.clearTimeout();
135
+ }
136
+ };
137
+
138
+ clearTimeout() {
139
+ if (!this.timeoutIn) {
140
+ return;
141
+ }
142
+
143
+ clearTimeout(this.timeoutIn);
144
+ this.timeoutIn = null;
145
+ }
146
+
147
+ onSubMenuMouseEnter = () => {
148
+ this.clearTimeout();
149
+ this.setState({ isHoverSubMenu: true });
150
+ };
151
+
152
+ onSubMenuMouseLeave = () => {
153
+ this.setState({ isHoverSubMenu: false });
154
+ };
155
+
156
+ closeSubMenu = () => {
157
+ this.setState({ activeSubMenu: null });
158
+ };
159
+
160
+ onConfirm = (item: MenuItemConfig) => {
161
+ const { onItemSelected } = this.props;
162
+ this.closeSubMenu();
163
+ onItemSelected(item);
164
+ };
165
+
166
+ getSeparateGroups(items: MenuItemConfig[]) {
167
+ const menuItemsGroupMap = groupBy(items, (item) => item.groupId || '');
168
+ const groupsList = Object.values(menuItemsGroupMap);
169
+
170
+ const groupsToAddSeparator = groupsList.slice(1);
171
+ groupsToAddSeparator.forEach((group) => (group[0].separator = true));
172
+
173
+ return groupsList;
174
+ }
175
+
176
+ renderMenuItem = (item: MenuItemConfig) => {
177
+ const { onItemSelected, onRequestClose, level, classNameTooltip, zIndex, className } =
178
+ this.props;
179
+
180
+ let menuItemClass = classnames(style[item.type], {
181
+ [style.isDrill]: item.subItems,
182
+ [style.separator]: item.separator,
183
+ fakeHover: item.checked,
184
+ [style.groupHeader]: item.groupTitle,
185
+ });
186
+
187
+ const createMenuItem = (menuItemClass: string) => {
188
+ // const { confirmation = { inline: false } } = item;
189
+ const iconClass = classnames(item.iconClass, style.icon);
190
+ return (
191
+ <MenuItem
192
+ className={menuItemClass}
193
+ checked={item.checked}
194
+ dataTestId={item.dataTestId}
195
+ disabled={item.disabled}
196
+ key={item.id}
197
+ type={item.type}
198
+ caption={item.caption}
199
+ subCaption={item.subCaption}
200
+ tooltip={item.tooltip}
201
+ classNameTooltip={classNameTooltip}
202
+ iconName={item.iconName}
203
+ // trailElement={item.trailElement}
204
+ iconClass={iconClass}
205
+ isDrill={!!item.subItems || !!item.actionableComponent}
206
+ // inlineConfirmation={confirmation.inline}
207
+ // inlineMessage={confirmation.message}
208
+ level={level}
209
+ selected={item.selected}
210
+ handleClick={() => this.handleClick(item)}
211
+ handleOver={() => this.handleOver(item)}
212
+ handleOut={() => this.handleOut(item)}
213
+ />
214
+ );
215
+ };
216
+
217
+ let subItems = item.subItems;
218
+ if (subItems) {
219
+ const subMenuVisible = this.state.activeSubMenu === item.id;
220
+ let handleClick = this.handleClick;
221
+ menuItemClass = classnames(menuItemClass, {
222
+ fakeHover: subMenuVisible && this.state.isHoverSubMenu,
223
+ });
224
+
225
+ if (item.value !== undefined) {
226
+ subItems = subItems.map(function (subItem) {
227
+ return {
228
+ ...subItem,
229
+ checked: subItem.value === item.value,
230
+ };
231
+ });
232
+
233
+ handleClick = (childItem) => onItemSelected(item, childItem.value);
234
+ }
235
+
236
+ const overlay = () => (
237
+ <Menu
238
+ items={subItems || []}
239
+ className={className}
240
+ onItemSelected={handleClick}
241
+ onMouseEnter={this.onSubMenuMouseEnter}
242
+ scrollbarProps={{
243
+ className: item.subItemScrollbarClassName,
244
+ }}
245
+ level={(level || 0) + 1}
246
+ zIndex={(zIndex || 1) + 1}
247
+ />
248
+ );
249
+
250
+ return (
251
+ <Popover
252
+ key={item.id}
253
+ visible={subMenuVisible}
254
+ level={1}
255
+ mask={false}
256
+ trigger={[]}
257
+ placement="rightTop"
258
+ align={{ offset: [0, 0] }}
259
+ overlay={overlay}
260
+ onRequestClose={onRequestClose}
261
+ zIndex={(zIndex || 1) + 1}
262
+ >
263
+ {createMenuItem(menuItemClass)}
264
+ </Popover>
265
+ );
266
+ } else if (item.actionableComponent) {
267
+ // eslint-disable-next-line
268
+ const { componentProps } = item.actionableComponent;
269
+ const Component: any = item.actionableComponent.component;
270
+ const actionableComponentVisible = this.state.activeSubMenu === item.id;
271
+ const overlay = () => <Component {...componentProps} onRequestClose={onRequestClose} />;
272
+
273
+ return (
274
+ <Popover
275
+ key={item.id}
276
+ visible={actionableComponentVisible}
277
+ level={1}
278
+ mask={false}
279
+ trigger={[]}
280
+ placement="rightTop"
281
+ align={{ offset: [10, 0] }}
282
+ overlay={overlay}
283
+ onRequestClose={onRequestClose}
284
+ >
285
+ {createMenuItem(menuItemClass)}
286
+ </Popover>
287
+ );
288
+ } else if (item.confirmation && !item.confirmation.inline) {
289
+ const confirmConfig = item.confirmation;
290
+ const confirmationVisible = this.state.activeSubMenu === item.id;
291
+ const overlay = () => (
292
+ <Confirmation
293
+ title={confirmConfig.title}
294
+ message={confirmConfig.message}
295
+ onCancel={this.closeSubMenu}
296
+ onConfirm={() => this.onConfirm(item)}
297
+ />
298
+ );
299
+
300
+ return (
301
+ <Popover
302
+ key={item.id}
303
+ visible={confirmationVisible}
304
+ level={1}
305
+ mask
306
+ trigger={[]}
307
+ placement="rightBottom"
308
+ align={{ offset: [10, 0] }}
309
+ overlay={overlay}
310
+ onRequestClose={this.closeSubMenu}
311
+ >
312
+ {createMenuItem(menuItemClass)}
313
+ </Popover>
314
+ );
315
+ }
316
+
317
+ return createMenuItem(menuItemClass);
318
+ };
319
+
320
+ render() {
321
+ const {
322
+ items,
323
+ className,
324
+ onMouseEnter,
325
+ scrollbarProps: { className: scrollClass, ...otherScrollbarProps },
326
+ width,
327
+ isLoading,
328
+ } = this.props;
329
+ let visibleItems = items.filter((item) => !item.hidden);
330
+ visibleItems = visibleItems.map((item, index) => {
331
+ if (item.separator && !visibleItems[index - 1]) {
332
+ item.separator = false;
333
+ }
334
+ return item;
335
+ });
336
+
337
+ const containerClasses = classnames(style.container, className);
338
+ const scrollbarClasses = classnames(style.scrollbar, scrollClass);
339
+
340
+ const styles = width ? { width: `${width}px`, minWidth: 'inherit' } : {};
341
+ const loaderClass = isLoading ? style.loader : '';
342
+
343
+ return (
344
+ <div className={containerClasses} onMouseEnter={onMouseEnter} style={styles}>
345
+ <Scrollbars className={scrollbarClasses} {...otherScrollbarProps} hideTracksWhenNotNeeded>
346
+ {this.getSeparateGroups(visibleItems).map((group, index) => {
347
+ return (
348
+ <div className={style.separateGroup} key={index}>
349
+ {group.map(this.renderMenuItem)}
350
+ </div>
351
+ );
352
+ })}
353
+ <div className={loaderClass}>
354
+ <LazyLoader isLoading={isLoading} />
355
+ </div>
356
+ </Scrollbars>
357
+ </div>
358
+ );
359
+ }
360
+ }
361
+
362
+ export default Menu;
363
+ export { Menu };
@@ -0,0 +1,241 @@
1
+ import React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+
4
+ import { MenuItem, itemTypes } from './MenuItem';
5
+ import styles from '../Menu.module.scss';
6
+
7
+ import '@testing-library/jest-dom';
8
+
9
+ describe('MenuItem Component', () => {
10
+ test('should render a basic menu item', () => {
11
+ render(
12
+ <MenuItem
13
+ handleClick={vi.fn()}
14
+ caption="Test Item"
15
+ type={itemTypes.ITEM}
16
+ dataTestId="menu-item"
17
+ />,
18
+ );
19
+
20
+ const menuItem = screen.getByTestId('menu-item');
21
+
22
+ expect(menuItem).toBeInTheDocument();
23
+ expect(menuItem).toHaveClass(styles.menuItemText);
24
+ expect(menuItem).toHaveTextContent('Test Item');
25
+ });
26
+
27
+ test('should trigger handleClick on click', () => {
28
+ const mockHandleClick = vi.fn();
29
+ render(
30
+ <MenuItem
31
+ handleClick={mockHandleClick}
32
+ caption="Test Item"
33
+ type={itemTypes.ITEM}
34
+ dataTestId="menu-item"
35
+ />,
36
+ );
37
+
38
+ const menuItem = screen.getByTestId('menu-item');
39
+ fireEvent.click(menuItem);
40
+
41
+ expect(mockHandleClick).toHaveBeenCalledTimes(1);
42
+ });
43
+
44
+ test('should not trigger handleClick when disabled', () => {
45
+ const mockHandleClick = vi.fn();
46
+ render(
47
+ <MenuItem
48
+ handleClick={mockHandleClick}
49
+ caption="Test Item"
50
+ type={itemTypes.ITEM}
51
+ dataTestId="menu-item"
52
+ disabled={true}
53
+ />,
54
+ );
55
+
56
+ const menuItem = screen.getByTestId('menu-item');
57
+ fireEvent.click(menuItem);
58
+
59
+ expect(mockHandleClick).not.toHaveBeenCalled();
60
+ });
61
+
62
+ test('should render a radio button when type is RADIO', () => {
63
+ render(
64
+ <MenuItem
65
+ handleClick={vi.fn()}
66
+ caption="Test Item"
67
+ type={itemTypes.RADIO}
68
+ dataTestId="menu-item"
69
+ checked={true}
70
+ />,
71
+ );
72
+
73
+ expect(screen.getByRole('radio')).toBeChecked();
74
+ });
75
+
76
+ test('should render a checkbox when type is CHECKBOX', () => {
77
+ render(
78
+ <MenuItem
79
+ handleClick={vi.fn()}
80
+ caption="Test Item"
81
+ type={itemTypes.CHECKBOX}
82
+ dataTestId="menu-item"
83
+ checked={true}
84
+ />,
85
+ );
86
+
87
+ expect(screen.getByRole('checkbox')).toBeChecked();
88
+ });
89
+
90
+ test('should render a toggle when type is TOGGLE', () => {
91
+ render(
92
+ <MenuItem
93
+ handleClick={vi.fn()}
94
+ caption="Test Item"
95
+ type={itemTypes.TOGGLE}
96
+ dataTestId="menu-item"
97
+ checked={true}
98
+ />,
99
+ );
100
+
101
+ expect(screen.getByText('Test Item')).toBeInTheDocument();
102
+ });
103
+
104
+ test('should render nested content when type is NESTED', () => {
105
+ render(
106
+ <MenuItem
107
+ handleClick={vi.fn()}
108
+ caption="Test Item"
109
+ type={itemTypes.NESTED}
110
+ dataTestId="menu-item"
111
+ subCaption="Sub Item"
112
+ />,
113
+ );
114
+
115
+ expect(screen.getByText('Test Item')).toBeInTheDocument();
116
+ expect(screen.getByText('Sub Item')).toBeInTheDocument();
117
+ });
118
+
119
+ test('should render icon component when type is NESTED', () => {
120
+ const { container } = render(
121
+ <MenuItem
122
+ handleClick={vi.fn()}
123
+ caption="Test Item"
124
+ type={itemTypes.NESTED}
125
+ dataTestId="menu-item"
126
+ subCaption="Sub Item"
127
+ />,
128
+ );
129
+
130
+ const icon = container.querySelector('.app-icon--general-double-arrow-front');
131
+
132
+ expect(icon).toBeInTheDocument();
133
+ });
134
+
135
+ test('should render icon component when type is ITEM and selected', () => {
136
+ const { container } = render(
137
+ <MenuItem
138
+ handleClick={vi.fn()}
139
+ caption="Test Item"
140
+ type={itemTypes.ITEM}
141
+ dataTestId="menu-item"
142
+ selected={true}
143
+ />,
144
+ );
145
+
146
+ const icon = container.querySelector('.app-icon--general-vi-small-white');
147
+
148
+ expect(icon).toBeInTheDocument();
149
+ });
150
+
151
+ test('should render tooltip if provided', () => {
152
+ render(
153
+ <MenuItem
154
+ handleClick={vi.fn()}
155
+ caption="Test Item"
156
+ type={itemTypes.ITEM}
157
+ dataTestId="menu-item"
158
+ tooltip="Tooltip text"
159
+ />,
160
+ );
161
+
162
+ const tooltipTrigger = screen.getByTestId('menu-item');
163
+ fireEvent.mouseOver(tooltipTrigger);
164
+
165
+ expect(screen.getByText('Tooltip text')).toBeInTheDocument();
166
+ });
167
+
168
+ test('should apply the correct classes when checked', () => {
169
+ render(
170
+ <MenuItem
171
+ handleClick={vi.fn()}
172
+ caption="Test Item"
173
+ type={itemTypes.ITEM}
174
+ dataTestId="menu-item"
175
+ checked={true}
176
+ />,
177
+ );
178
+
179
+ const menuItem = screen.getByRole('listitem');
180
+
181
+ expect(menuItem).toHaveClass(styles.checked);
182
+ });
183
+
184
+ test('should apply the correct classes when disabled', () => {
185
+ render(
186
+ <MenuItem
187
+ handleClick={vi.fn()}
188
+ caption="Test Item"
189
+ type={itemTypes.ITEM}
190
+ dataTestId="menu-item"
191
+ disabled={true}
192
+ />,
193
+ );
194
+
195
+ const menuItem = screen.getByRole('listitem');
196
+
197
+ expect(menuItem).toHaveClass(styles.disabled);
198
+ });
199
+
200
+ test('should call handleOver on hover', () => {
201
+ const mockHandleOver = vi.fn();
202
+
203
+ render(
204
+ <MenuItem
205
+ handleClick={vi.fn()}
206
+ caption="Test Item"
207
+ type={itemTypes.ITEM}
208
+ dataTestId="menu-item"
209
+ disabled={true}
210
+ handleOver={mockHandleOver}
211
+ />,
212
+ );
213
+
214
+ const menuItem = screen.getByTestId('menu-item');
215
+ fireEvent.mouseOver(menuItem);
216
+
217
+ expect(mockHandleOver).toHaveBeenCalledTimes(1);
218
+ });
219
+
220
+ test('should call handleOut on hover', () => {
221
+ const mockHandleOut = vi.fn();
222
+
223
+ render(
224
+ <MenuItem
225
+ handleClick={vi.fn()}
226
+ caption="Test Item"
227
+ type={itemTypes.ITEM}
228
+ dataTestId="menu-item"
229
+ disabled={true}
230
+ handleOver={vi.fn()}
231
+ handleOut={mockHandleOut}
232
+ />,
233
+ );
234
+
235
+ const menuItem = screen.getByTestId('menu-item');
236
+ fireEvent.mouseOver(menuItem);
237
+ fireEvent.mouseOut(menuItem);
238
+
239
+ expect(mockHandleOut).toHaveBeenCalledTimes(1);
240
+ });
241
+ });