@sisense/sdk-shared-ui 0.1.0 → 1.27.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.
- package/.storybook/main.ts +44 -0
- package/.storybook/preview-head.html +4 -0
- package/.storybook/preview.tsx +34 -0
- package/dist/index.js +40 -2
- package/package.json +135 -2
- package/src/lib/CheckableList/CheckableList.module.scss +13 -0
- package/src/lib/CheckableList/CheckableList.test.tsx +61 -0
- package/src/lib/CheckableList/CheckableList.tsx +41 -0
- package/src/lib/CheckableList/index.ts +4 -0
- package/src/lib/Checkbox/Checkbox.tsx +28 -0
- package/src/lib/Checkbox/index.ts +4 -0
- package/src/lib/Checkbox/themes/checkboxTheme.ts +42 -0
- package/src/lib/Checkbox/themes/index.ts +1 -0
- package/src/lib/Checkbox/themes/uiCustomization.ts +28 -0
- package/src/lib/DEPRECATED_Button/DEPRECATED_Button.module.scss +57 -0
- package/src/lib/DEPRECATED_Button/DEPRECATED_Button.stories.tsx +169 -0
- package/src/lib/DEPRECATED_Button/DEPRECATED_Button.test.tsx +154 -0
- package/src/lib/DEPRECATED_Button/DEPRECATED_Button.tsx +129 -0
- package/src/lib/DEPRECATED_Button/index.ts +4 -0
- package/src/lib/DEPRECATED_Checkbox/Checkbox.module.scss +49 -0
- package/src/lib/DEPRECATED_Checkbox/DEPRECATED_Checkbox.test.tsx +131 -0
- package/src/lib/DEPRECATED_Checkbox/DEPRECATED_Checkbox.tsx +103 -0
- package/src/lib/DEPRECATED_Checkbox/index.ts +4 -0
- package/src/lib/DEPRECATED_Icon/DEPRECATED_Icon.stories.tsx +27 -0
- package/src/lib/DEPRECATED_Icon/DEPRECATED_Icon.tsx +2 -0
- package/src/lib/DEPRECATED_Toggle/DEPRECATED_Toggle.tsx +49 -0
- package/src/lib/DEPRECATED_Toggle/index.ts +4 -0
- package/src/lib/DEPRECATED_Tooltip/DEPRECATED_Tooltip.module.scss +115 -0
- package/src/lib/DEPRECATED_Tooltip/DEPRECATED_Tooltip.tsx +68 -0
- package/src/lib/DEPRECATED_Tooltip/index.ts +4 -0
- package/src/lib/Dropdown/Dropdown.module.scss +9 -0
- package/src/lib/Dropdown/Dropdown.tsx +150 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButton.module.scss +49 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButton.tsx +81 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonBody/DropdownButtonBody.test.tsx +121 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonBody/DropdownButtonBody.tsx +39 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonBody/index.ts +2 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/DropdownButtonSearch.module.scss +20 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/DropdownButtonSearch.tsx +44 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/constants.ts +1 -0
- package/src/lib/Dropdown/DropdownButton/DropdownButtonSearch/index.ts +2 -0
- package/src/lib/Dropdown/DropdownButton/hooks/index.tsx +1 -0
- package/src/lib/Dropdown/DropdownButton/hooks/useDropdownButtonSearch.ts +51 -0
- package/src/lib/Dropdown/DropdownButton/index.ts +1 -0
- package/src/lib/Dropdown/hooks/index.tsx +1 -0
- package/src/lib/Dropdown/hooks/useDropdown.ts +29 -0
- package/src/lib/Dropdown/index.ts +5 -0
- package/src/lib/Dropdown/types.ts +17 -0
- package/src/lib/Icon/Icon.tsx +118 -0
- package/src/lib/Icon/index.ts +4 -0
- package/src/lib/Icon/themes/iconTheme.tsx +38 -0
- package/src/lib/Icon/themes/index.ts +1 -0
- package/src/lib/Icon/themes/uiCustomization.ts +9 -0
- package/src/lib/Input/Input.module.scss +97 -0
- package/src/lib/Input/Input.test.tsx +177 -0
- package/src/lib/Input/Input.tsx +134 -0
- package/src/lib/Input/index.ts +4 -0
- package/src/lib/LazyLoader/LazyLoader.module.scss +18 -0
- package/src/lib/LazyLoader/LazyLoader.tsx +42 -0
- package/src/lib/LazyLoader/index.ts +4 -0
- package/src/lib/Menu/Confirmation/Confirmation.module.scss +48 -0
- package/src/lib/Menu/Confirmation/Confirmation.tsx +45 -0
- package/src/lib/Menu/Confirmation/index.ts +1 -0
- package/src/lib/Menu/Confirmation/translation.ts +17 -0
- package/src/lib/Menu/Menu.module.scss +178 -0
- package/src/lib/Menu/Menu.tsx +363 -0
- package/src/lib/Menu/MenuItem/MenuItem.test.tsx +241 -0
- package/src/lib/Menu/MenuItem/MenuItem.tsx +186 -0
- package/src/lib/Menu/MenuItem/index.ts +2 -0
- package/src/lib/Menu/index.ts +6 -0
- package/src/lib/Menu/utils.ts +13 -0
- package/src/lib/Popover/Popover.module.scss +15 -0
- package/src/lib/Popover/Popover.tsx +92 -0
- package/src/lib/Popover/align.interface.ts +20 -0
- package/src/lib/Popover/index.ts +6 -0
- package/src/lib/RadioButton/RadioButton.module.scss +22 -0
- package/src/lib/RadioButton/RadioButton.tsx +89 -0
- package/src/lib/RadioButton/index.ts +4 -0
- package/src/lib/TablePagination/PaginationActionsComponent/PaginationActionsComponent.tsx +87 -0
- package/src/lib/TablePagination/PaginationActionsComponent/index.ts +4 -0
- package/src/lib/TablePagination/PaginationActionsComponent/themes/index.ts +4 -0
- package/src/lib/TablePagination/PaginationActionsComponent/themes/paginationActionsComponentTheme.tsx +70 -0
- package/src/lib/TablePagination/PaginationActionsComponent/themes/uiCustomization.ts +17 -0
- package/src/lib/TablePagination/TablePagination.tsx +148 -0
- package/src/lib/TablePagination/TablePaginationContext.ts +4 -0
- package/src/lib/TablePagination/index.ts +4 -0
- package/src/lib/TablePagination/themes/index.ts +1 -0
- package/src/lib/TablePagination/themes/tablePaginationResponsiveDesign.ts +33 -0
- package/src/lib/TablePagination/themes/tablePaginationTheme.tsx +128 -0
- package/src/lib/TablePagination/themes/uiCustomization.ts +50 -0
- package/src/lib/Tooltip/Tooltip.module.scss +125 -0
- package/src/lib/Tooltip/Tooltip.tsx +34 -0
- package/src/lib/Tooltip/index.ts +5 -0
- package/src/lib/Tooltip/themes/index.ts +1 -0
- package/src/lib/Tooltip/themes/tooltipTheme.ts +30 -0
- package/src/lib/Typography/Typography.tsx +28 -0
- package/src/lib/Typography/index.ts +5 -0
- package/src/lib/Typography/themes/index.ts +1 -0
- package/src/lib/Typography/themes/typographyTheme.tsx +170 -0
- package/src/lib/Typography/themes/uiCustomization.ts +10 -0
- package/src/lib/constants/styleguideConstants.ts +8 -0
- package/src/lib/index.ts +16 -0
- package/src/lib/styles/colors.module.scss +10 -0
- package/src/lib/styles/sisenseStyleguideReactColors.scss +57 -0
- package/src/lib/styles/style_styleguide_react/_variables.deprecated.scss +107 -0
- package/src/lib/styles/style_styleguide_react/_variables.scss +100 -0
- package/src/lib/styles/style_styleguide_react/base.scss +9 -0
- package/src/lib/styles/style_styleguide_react/colors.scss +71 -0
- package/src/lib/styles/style_styleguide_react/exports/colors.exports.scss +69 -0
- package/src/lib/styles/style_styleguide_react/exports/fonts.exports.scss +33 -0
- package/src/lib/styles/style_styleguide_react/fonts.scss +27 -0
- package/src/lib/styles/style_styleguide_react/mixins.scss +90 -0
- package/src/lib/themes/colors/index.ts +1 -0
- package/src/lib/themes/colors/siColors.ts +143 -0
- package/src/lib/themes/index.ts +2 -0
- package/src/lib/themes/types/SisenseTheme.ts +12 -0
- package/src/lib/themes/types/index.ts +1 -0
- package/tsconfig.lib.json +2 -1
- 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
|
+
});
|