@elementor/editor-ui 3.33.0-100 → 3.33.0-101

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/dist/index.d.mts CHANGED
@@ -75,6 +75,7 @@ declare const PopoverHeader: ({ title, onClose, icon, actions }: PopoverHeaderPr
75
75
  type VirtualizedItem<T, V extends string> = {
76
76
  type: T;
77
77
  value: V;
78
+ disabled?: boolean;
78
79
  label?: string;
79
80
  icon?: React$1.ReactNode;
80
81
  secondaryText?: string;
package/dist/index.d.ts CHANGED
@@ -75,6 +75,7 @@ declare const PopoverHeader: ({ title, onClose, icon, actions }: PopoverHeaderPr
75
75
  type VirtualizedItem<T, V extends string> = {
76
76
  type: T;
77
77
  value: V;
78
+ disabled?: boolean;
78
79
  label?: string;
79
80
  icon?: React$1.ReactNode;
80
81
  secondaryText?: string;
package/dist/index.js CHANGED
@@ -429,6 +429,7 @@ var PopoverMenuList = ({
429
429
  onChange
430
430
  });
431
431
  useScrollToSelected({ selectedValue, items, virtualizer });
432
+ const virtualItems = virtualizer.getVirtualItems();
432
433
  return /* @__PURE__ */ React11.createElement(import_ui11.Box, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React11.createElement(
433
434
  MenuListComponent,
434
435
  {
@@ -436,7 +437,7 @@ var PopoverMenuList = ({
436
437
  style: { height: `${virtualizer.getTotalSize()}px` },
437
438
  "data-testid": dataTestId
438
439
  },
439
- virtualizer.getVirtualItems().map((virtualRow) => {
440
+ virtualItems.map((virtualRow) => {
440
441
  const item = items[virtualRow.index];
441
442
  const isLast = virtualRow.index === items.length - 1;
442
443
  const isFirst = items[0]?.type === "category" ? virtualRow.index === 1 : virtualRow.index === 0;
@@ -457,13 +458,15 @@ var PopoverMenuList = ({
457
458
  item.label || item.value
458
459
  );
459
460
  }
461
+ const isDisabled = item.disabled;
460
462
  return /* @__PURE__ */ React11.createElement(
461
- "li",
463
+ import_ui11.ListItem,
462
464
  {
463
465
  key: virtualRow.key,
464
466
  role: "option",
465
467
  "aria-selected": isSelected,
466
- onClick: (e) => {
468
+ "aria-disabled": isDisabled,
469
+ onClick: isDisabled ? void 0 : (e) => {
467
470
  if (e.target.closest("button")) {
468
471
  return;
469
472
  }
@@ -471,7 +474,7 @@ var PopoverMenuList = ({
471
474
  onClose();
472
475
  },
473
476
  onKeyDown: (event) => {
474
- if (event.key === "Enter") {
477
+ if (event.key === "Enter" && !isDisabled) {
475
478
  onSelect(item.value);
476
479
  onClose();
477
480
  }
@@ -485,7 +488,7 @@ var PopoverMenuList = ({
485
488
  }
486
489
  },
487
490
  tabIndex: isSelected ? 0 : tabIndexFallback,
488
- style: {
491
+ sx: {
489
492
  transform: `translateY(${virtualRow.start + MENU_LIST_PADDING_TOP}px)`,
490
493
  ...itemStyle ? itemStyle(item) : {}
491
494
  }
@@ -512,6 +515,9 @@ var StyledMenuList = (0, import_ui11.styled)(import_ui11.MenuList)(({ theme }) =
512
515
  '&[aria-selected="true"]': {
513
516
  backgroundColor: theme.palette.action.selected
514
517
  },
518
+ '&[aria-disabled="true"]': {
519
+ color: theme.palette.text.disabled
520
+ },
515
521
  cursor: "pointer",
516
522
  textOverflow: "ellipsis",
517
523
  position: "absolute",
package/dist/index.mjs CHANGED
@@ -294,7 +294,7 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
294
294
  // src/components/popover/menu-list.tsx
295
295
  import * as React11 from "react";
296
296
  import { useMemo, useRef } from "react";
297
- import { Box as Box5, MenuList, MenuSubheader, styled as styled2 } from "@elementor/ui";
297
+ import { Box as Box5, ListItem, MenuList, MenuSubheader, styled as styled2 } from "@elementor/ui";
298
298
  import { useVirtualizer } from "@tanstack/react-virtual";
299
299
 
300
300
  // src/hooks/use-scroll-to-selected.ts
@@ -392,6 +392,7 @@ var PopoverMenuList = ({
392
392
  onChange
393
393
  });
394
394
  useScrollToSelected({ selectedValue, items, virtualizer });
395
+ const virtualItems = virtualizer.getVirtualItems();
395
396
  return /* @__PURE__ */ React11.createElement(Box5, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React11.createElement(
396
397
  MenuListComponent,
397
398
  {
@@ -399,7 +400,7 @@ var PopoverMenuList = ({
399
400
  style: { height: `${virtualizer.getTotalSize()}px` },
400
401
  "data-testid": dataTestId
401
402
  },
402
- virtualizer.getVirtualItems().map((virtualRow) => {
403
+ virtualItems.map((virtualRow) => {
403
404
  const item = items[virtualRow.index];
404
405
  const isLast = virtualRow.index === items.length - 1;
405
406
  const isFirst = items[0]?.type === "category" ? virtualRow.index === 1 : virtualRow.index === 0;
@@ -420,13 +421,15 @@ var PopoverMenuList = ({
420
421
  item.label || item.value
421
422
  );
422
423
  }
424
+ const isDisabled = item.disabled;
423
425
  return /* @__PURE__ */ React11.createElement(
424
- "li",
426
+ ListItem,
425
427
  {
426
428
  key: virtualRow.key,
427
429
  role: "option",
428
430
  "aria-selected": isSelected,
429
- onClick: (e) => {
431
+ "aria-disabled": isDisabled,
432
+ onClick: isDisabled ? void 0 : (e) => {
430
433
  if (e.target.closest("button")) {
431
434
  return;
432
435
  }
@@ -434,7 +437,7 @@ var PopoverMenuList = ({
434
437
  onClose();
435
438
  },
436
439
  onKeyDown: (event) => {
437
- if (event.key === "Enter") {
440
+ if (event.key === "Enter" && !isDisabled) {
438
441
  onSelect(item.value);
439
442
  onClose();
440
443
  }
@@ -448,7 +451,7 @@ var PopoverMenuList = ({
448
451
  }
449
452
  },
450
453
  tabIndex: isSelected ? 0 : tabIndexFallback,
451
- style: {
454
+ sx: {
452
455
  transform: `translateY(${virtualRow.start + MENU_LIST_PADDING_TOP}px)`,
453
456
  ...itemStyle ? itemStyle(item) : {}
454
457
  }
@@ -475,6 +478,9 @@ var StyledMenuList = styled2(MenuList)(({ theme }) => ({
475
478
  '&[aria-selected="true"]': {
476
479
  backgroundColor: theme.palette.action.selected
477
480
  },
481
+ '&[aria-disabled="true"]': {
482
+ color: theme.palette.text.disabled
483
+ },
478
484
  cursor: "pointer",
479
485
  textOverflow: "ellipsis",
480
486
  position: "absolute",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-ui",
3
3
  "description": "Elementor Editor UI",
4
- "version": "3.33.0-100",
4
+ "version": "3.33.0-101",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,7 +37,7 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor-v1-adapters": "3.33.0-100",
40
+ "@elementor/editor-v1-adapters": "3.33.0-101",
41
41
  "@elementor/icons": "1.46.0",
42
42
  "@elementor/ui": "1.36.12",
43
43
  "@tanstack/react-virtual": "^3.13.3",
@@ -0,0 +1,106 @@
1
+ import * as React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+
4
+ import { PopoverMenuList } from '../menu-list';
5
+
6
+ const mockItems = [
7
+ { value: 'Item 1', type: 'item', disabled: false },
8
+ { value: 'Item 2', type: 'item', disabled: true },
9
+ { value: 'Item 3', type: 'item', disabled: false },
10
+ { value: 'Item 4', type: 'item', disabled: false },
11
+ { value: 'Item 5', type: 'item', disabled: true },
12
+ { value: 'Item 6', type: 'item', disabled: false },
13
+ { value: 'Item 7', type: 'item', disabled: false },
14
+ { value: 'Item 8', type: 'item', disabled: false },
15
+ ];
16
+
17
+ jest.mock( '@tanstack/react-virtual', () => ( {
18
+ useVirtualizer: jest.fn().mockImplementation( () => ( {
19
+ getVirtualItems: jest
20
+ .fn()
21
+ .mockReturnValue( mockItems.map( ( item, index ) => ( { key: item.value, index, start: index * 10 } ) ) ),
22
+ getTotalSize: jest.fn().mockReturnValue( mockItems.length ),
23
+ scrollToIndex: jest.fn(),
24
+ } ) ),
25
+ } ) );
26
+
27
+ describe( 'PopoverMenuList', () => {
28
+ it( 'should render an empty list', async () => {
29
+ // Arrange.
30
+ render(
31
+ <PopoverMenuList
32
+ items={ [] }
33
+ onSelect={ jest.fn() }
34
+ onClose={ jest.fn() }
35
+ noResultsComponent={ <div>No results</div> }
36
+ />
37
+ );
38
+
39
+ // Assert.
40
+ expect( screen.getByText( 'No results' ) ).toBeInTheDocument();
41
+ } );
42
+
43
+ it( 'should render a list with items', async () => {
44
+ const onSelect = jest.fn();
45
+ // Arrange.
46
+ const enabledMockItems = mockItems.filter( ( item ) => ! item.disabled );
47
+ render( <PopoverMenuList items={ mockItems } onSelect={ onSelect } onClose={ jest.fn() } /> );
48
+
49
+ // Assert.
50
+ enabledMockItems.forEach( ( item ) => {
51
+ const itemElement = screen.getByText( item.value );
52
+ expect( itemElement ).toBeInTheDocument();
53
+ fireEvent.click( itemElement );
54
+ expect( onSelect ).toHaveBeenCalledWith( item.value );
55
+ } );
56
+
57
+ expect( onSelect ).toHaveBeenCalledTimes( enabledMockItems.length );
58
+ } );
59
+
60
+ it( 'should render a list with items and a selected item', async () => {
61
+ // Arrange.
62
+ const selectedValue = 'Item 5';
63
+ render(
64
+ <PopoverMenuList
65
+ items={ mockItems }
66
+ onSelect={ jest.fn() }
67
+ onClose={ jest.fn() }
68
+ selectedValue={ selectedValue }
69
+ />
70
+ );
71
+
72
+ // Assert.
73
+ mockItems.forEach( ( item ) => {
74
+ const itemElement = screen.getByText( item.value );
75
+ expect( itemElement ).toBeInTheDocument();
76
+ } );
77
+
78
+ const item5 = screen.getByText( selectedValue );
79
+ expect( item5 ).toHaveAttribute( 'aria-selected', 'true' );
80
+ } );
81
+
82
+ it( 'should render a list with items and disabled items', async () => {
83
+ // Arrange.
84
+ const onSelect = jest.fn();
85
+ render( <PopoverMenuList items={ mockItems } onSelect={ onSelect } onClose={ jest.fn() } /> );
86
+
87
+ // Assert.
88
+ mockItems.forEach( ( item ) => {
89
+ const itemElement = screen.getByText( item.value );
90
+ expect( itemElement ).toBeInTheDocument();
91
+ } );
92
+
93
+ let disabledItem = screen.getByText( mockItems[ 1 ].value );
94
+ expect( disabledItem ).toHaveAttribute( 'aria-disabled', 'true' );
95
+ fireEvent.click( disabledItem );
96
+ expect( onSelect ).not.toHaveBeenCalled();
97
+ disabledItem = screen.getByText( mockItems[ 4 ].value );
98
+ expect( disabledItem ).toHaveAttribute( 'aria-disabled', 'true' );
99
+ fireEvent.click( disabledItem );
100
+ expect( onSelect ).not.toHaveBeenCalled();
101
+ const item1 = screen.getByText( mockItems[ 0 ].value );
102
+ expect( item1 ).toHaveAttribute( 'aria-disabled', 'false' );
103
+ fireEvent.click( item1 );
104
+ expect( onSelect ).toHaveBeenCalledWith( mockItems[ 0 ].value );
105
+ } );
106
+ } );
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { useMemo, useRef } from 'react';
3
- import { Box, MenuList, MenuSubheader, styled } from '@elementor/ui';
3
+ import { Box, ListItem, MenuList, MenuSubheader, styled } from '@elementor/ui';
4
4
  import { useVirtualizer } from '@tanstack/react-virtual';
5
5
 
6
6
  import { useScrollTop, useScrollToSelected } from '../../hooks';
@@ -8,6 +8,7 @@ import { useScrollTop, useScrollToSelected } from '../../hooks';
8
8
  export type VirtualizedItem< T, V extends string > = {
9
9
  type: T;
10
10
  value: V;
11
+ disabled?: boolean;
11
12
  label?: string;
12
13
  icon?: React.ReactNode;
13
14
  secondaryText?: string;
@@ -105,6 +106,7 @@ export const PopoverMenuList = < T, V extends string >( {
105
106
  } );
106
107
 
107
108
  useScrollToSelected( { selectedValue, items, virtualizer } );
109
+ const virtualItems = virtualizer.getVirtualItems();
108
110
 
109
111
  return (
110
112
  <Box ref={ containerRef } sx={ { height: '100%', overflowY: 'auto' } }>
@@ -116,7 +118,7 @@ export const PopoverMenuList = < T, V extends string >( {
116
118
  style={ { height: `${ virtualizer.getTotalSize() }px` } }
117
119
  data-testid={ dataTestId }
118
120
  >
119
- { virtualizer.getVirtualItems().map( ( virtualRow ) => {
121
+ { virtualItems.map( ( virtualRow ) => {
120
122
  const item = items[ virtualRow.index ];
121
123
  const isLast = virtualRow.index === items.length - 1;
122
124
  const isFirst =
@@ -141,21 +143,26 @@ export const PopoverMenuList = < T, V extends string >( {
141
143
  </MenuSubheader>
142
144
  );
143
145
  }
144
-
146
+ const isDisabled = item.disabled;
145
147
  return (
146
- <li
148
+ <ListItem
147
149
  key={ virtualRow.key }
148
150
  role="option"
149
151
  aria-selected={ isSelected }
150
- onClick={ ( e ) => {
151
- if ( ( e.target as HTMLElement ).closest( 'button' ) ) {
152
- return;
153
- }
154
- onSelect( item.value );
155
- onClose();
156
- } }
157
- onKeyDown={ ( event ) => {
158
- if ( event.key === 'Enter' ) {
152
+ aria-disabled={ isDisabled }
153
+ onClick={
154
+ isDisabled
155
+ ? undefined
156
+ : ( e: React.MouseEvent< HTMLLIElement > ) => {
157
+ if ( ( e.target as HTMLElement ).closest( 'button' ) ) {
158
+ return;
159
+ }
160
+ onSelect( item.value );
161
+ onClose();
162
+ }
163
+ }
164
+ onKeyDown={ ( event: React.KeyboardEvent< HTMLLIElement > ) => {
165
+ if ( event.key === 'Enter' && ! isDisabled ) {
159
166
  onSelect( item.value );
160
167
  onClose();
161
168
  }
@@ -171,13 +178,13 @@ export const PopoverMenuList = < T, V extends string >( {
171
178
  }
172
179
  } }
173
180
  tabIndex={ isSelected ? 0 : tabIndexFallback }
174
- style={ {
181
+ sx={ {
175
182
  transform: `translateY(${ virtualRow.start + MENU_LIST_PADDING_TOP }px)`,
176
183
  ...( itemStyle ? itemStyle( item ) : {} ),
177
184
  } }
178
185
  >
179
186
  { menuItemContentTemplate ? menuItemContentTemplate( item ) : item.label || item.value }
180
- </li>
187
+ </ListItem>
181
188
  );
182
189
  } ) }
183
190
  </MenuListComponent>
@@ -203,6 +210,9 @@ export const StyledMenuList = styled( MenuList )( ( { theme } ) => ( {
203
210
  '&[aria-selected="true"]': {
204
211
  backgroundColor: theme.palette.action.selected,
205
212
  },
213
+ '&[aria-disabled="true"]': {
214
+ color: theme.palette.text.disabled,
215
+ },
206
216
  cursor: 'pointer',
207
217
  textOverflow: 'ellipsis',
208
218
  position: 'absolute',