@zohodesk/components 1.4.9 → 1.4.10-exp-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/README.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  Dot UI is a customizable React component library built to deliver a clean, accessible, and developer-friendly UI experience. It offers a growing set of reusable components designed to align with modern design systems and streamline application development across projects.
4
4
 
5
+ # 1.4.11
6
+
7
+ - **Select, MultiSelect**
8
+ - `isVirtualizerEnabled` prop supported to manage bulk amount of options rendering
9
+
10
+ - **Suggestions**
11
+ - **Virtualizer** adopted to manage bulk amount of options rendering with `isVirtualizerEnabled` prop support
12
+
13
+ # 1.4.10
14
+
15
+ - **Select, MultiSelect**
16
+ - Searching with leading spaces is not reflected the results correctly. Issue Fixed.
17
+
18
+ - **Select**
19
+ - Fixed performance issue in options formatting caused by unnecessary rendering triggers
20
+
5
21
  # 1.4.9
6
22
 
7
23
  - **MultiSelect , MultiSelectWithAvatar, AdvancedMultiSelect, AdvancedGroupMultiSelect**
@@ -81,6 +81,7 @@ export class MultiSelectComponent extends React.Component {
81
81
  this.handleSearchOptions = debounce(this.handleSearchOptions.bind(this), searchDebounceTime);
82
82
  this.handleScroll = this.handleScroll.bind(this);
83
83
  this.handleScrollFuncCall = debounce(this.handleScrollFuncCall.bind(this), 500);
84
+ this.setSuggestionsVirtualizerContainerRefFunction = this.setSuggestionsVirtualizerContainerRefFunction.bind(this);
84
85
  }
85
86
 
86
87
  componentDidMount() {
@@ -798,6 +799,7 @@ export class MultiSelectComponent extends React.Component {
798
799
 
799
800
  suggestionContainerRef(el) {
800
801
  this.suggestionContainer = el;
802
+ typeof this.setSuggestionsVirtualizerRef === 'function' && this.setSuggestionsVirtualizerRef(el);
801
803
  }
802
804
 
803
805
  suggestionItemRef(el, index, id) {
@@ -862,6 +864,11 @@ export class MultiSelectComponent extends React.Component {
862
864
  });
863
865
  }
864
866
 
867
+ setSuggestionsVirtualizerContainerRefFunction(refFunc) {
868
+ this.setSuggestionsVirtualizerRef = refFunc;
869
+ this.suggestionContainer && refFunc(this.suggestionContainer);
870
+ }
871
+
865
872
  getSelectionUI() {
866
873
  let isResponsive = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
867
874
  let {
@@ -1042,6 +1049,7 @@ export class MultiSelectComponent extends React.Component {
1042
1049
  isLoading,
1043
1050
  selectAllText,
1044
1051
  needSelectAll,
1052
+ isVirtualizerEnabled,
1045
1053
  limit
1046
1054
  } = this.props;
1047
1055
  const {
@@ -1124,6 +1132,8 @@ export class MultiSelectComponent extends React.Component {
1124
1132
  className: style[palette]
1125
1133
  }, searchText) : suggestions.length ? /*#__PURE__*/React.createElement(Suggestions, {
1126
1134
  suggestions: suggestions,
1135
+ isVirtualizerEnabled: isVirtualizerEnabled,
1136
+ setVirtualizerContainerRefFunction: this.setSuggestionsVirtualizerContainerRefFunction,
1127
1137
  getRef: this.suggestionItemRef,
1128
1138
  hoverOption: hoverOption,
1129
1139
  onClick: this.handleSelectOption,
@@ -1,140 +1,114 @@
1
1
  /**** Libraries ****/
2
- import React from 'react';
2
+ import React, { useMemo } from 'react';
3
3
  import { Suggestions_propTypes } from "./props/propTypes";
4
4
  import { Suggestions_defaultProps } from "./props/defaultProps";
5
+ import { Virtualizer } from '@zohodesk/virtualizer';
5
6
  /**** Components ****/
6
7
 
7
8
  import ListItem from "../ListItem/ListItem";
8
9
  import ListItemWithAvatar from "../ListItem/ListItemWithAvatar";
9
10
  import ListItemWithIcon from "../ListItem/ListItemWithIcon";
10
11
  import { Container, Box } from "../Layout";
12
+ import { DUMMY_OBJECT, DUMMY_ARRAY } from "../utils/Common";
13
+
14
+ function SuggestionsVirtualizerContainer(_ref) {
15
+ let {
16
+ eleRef,
17
+ children,
18
+ setVirtualizerContainerRefFunction
19
+ } = _ref;
20
+ useMemo(() => {
21
+ typeof setVirtualizerContainerRefFunction === 'function' && setVirtualizerContainerRefFunction(eleRef);
22
+ }, [eleRef, setVirtualizerContainerRefFunction]);
23
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
24
+ }
25
+
11
26
  export default class Suggestions extends React.PureComponent {
12
- render() {
27
+ constructor(props) {
28
+ super(props);
29
+ this.renderSuggestionList = this.renderSuggestionList.bind(this);
30
+ this.renderVirtualizerSuggestionListItem = this.renderVirtualizerSuggestionListItem.bind(this);
31
+ }
32
+
33
+ renderSuggestionList(_ref2) {
34
+ let {
35
+ suggestion,
36
+ index,
37
+ ref
38
+ } = _ref2;
13
39
  const {
14
- suggestions,
15
40
  getRef,
16
41
  hoverOption,
17
42
  onClick,
18
43
  onMouseEnter,
19
44
  needTick,
20
45
  needBorder,
21
- selectedOptions = [],
46
+ selectedOptions = DUMMY_ARRAY,
22
47
  activeId,
23
48
  hoverId,
24
- dataId,
25
49
  listItemSize,
26
- className,
27
50
  avatarPalette,
28
51
  palette,
29
- htmlId,
30
52
  a11y,
31
53
  needMultiLineText,
32
54
  limit,
33
55
  limitReachedMessage
34
56
  } = this.props;
35
57
  const {
36
- ariaParentRole,
37
- ariaMultiselectable
38
- } = a11y;
58
+ id,
59
+ value,
60
+ secondaryValue,
61
+ photoURL,
62
+ icon,
63
+ optionType,
64
+ iconSize,
65
+ isDisabled,
66
+ listItemProps,
67
+ listItemCustomProps = DUMMY_OBJECT
68
+ } = suggestion;
69
+ const isActive = activeId === id || selectedOptions.indexOf(id) >= 0;
70
+ const isHighlight = hoverOption === index || id === hoverId ? true : false;
39
71
  const selectedOptionsLength = selectedOptions.length;
40
- return /*#__PURE__*/React.createElement(Container, {
41
- isCover: false,
42
- role: ariaParentRole,
43
- id: htmlId,
44
- tabindex: "0",
45
- "aria-multiselectable": ariaMultiselectable
46
- }, /*#__PURE__*/React.createElement(Box, {
47
- dataId: `${dataId}`,
48
- className: className ? className : ''
49
- }, suggestions.map((suggestion, index) => {
50
- const {
51
- id,
52
- value,
53
- secondaryValue,
54
- photoURL,
55
- icon,
56
- optionType,
57
- iconSize,
58
- isDisabled,
59
- listItemProps,
60
- listItemCustomProps = {}
61
- } = suggestion;
62
- const isActive = activeId === id || selectedOptions.indexOf(id) >= 0;
63
- const isHighlight = hoverOption === index || id === hoverId ? true : false;
64
- const isLimitReached = selectedOptionsLength >= limit && !isActive;
65
- const list_a11y = Object.assign({}, a11y, {
66
- ariaSelected: isActive,
67
- ariaLabel: value,
68
- 'data-a11y-list-active': isHighlight
69
- });
70
- const commonProps = {
71
- isDisabled: isDisabled ? isDisabled : isLimitReached,
72
- needMultiLineText,
73
- ...listItemCustomProps
72
+ const isLimitReached = selectedOptionsLength >= limit && !isActive;
73
+ const list_a11y = Object.assign({}, a11y, {
74
+ ariaSelected: isActive,
75
+ ariaLabel: value,
76
+ 'data-a11y-list-active': isHighlight
77
+ });
78
+ const commonProps = {
79
+ isDisabled: isDisabled ? isDisabled : isLimitReached,
80
+ needMultiLineText,
81
+ ...listItemCustomProps
82
+ };
83
+
84
+ if (listItemProps) {
85
+ commonProps.customProps = {
86
+ ListItemProps: { ...listItemProps
87
+ }
74
88
  };
89
+ }
75
90
 
76
- if (listItemProps) {
77
- commonProps.customProps = {
78
- ListItemProps: { ...listItemProps
79
- }
80
- };
81
- }
91
+ if (isLimitReached) {
92
+ commonProps.disableTitle = limitReachedMessage;
93
+ }
82
94
 
83
- if (isLimitReached) {
84
- commonProps.disableTitle = limitReachedMessage;
85
- }
95
+ function getListItemRef(ele, index, id) {
96
+ ref && ref(ele);
86
97
 
87
- if (optionType === 'avatar') {
88
- return /*#__PURE__*/React.createElement(ListItemWithAvatar, { ...commonProps,
89
- autoHover: false,
90
- getRef: getRef,
91
- highlight: isHighlight,
92
- id: id,
93
- imgSrc: photoURL,
94
- key: `${id}avatarListItem`,
95
- name: value,
96
- onClick: onClick,
97
- onMouseEnter: onMouseEnter,
98
- value: value,
99
- title: value,
100
- needTick: needTick,
101
- needBorder: needBorder,
102
- active: isActive,
103
- size: listItemSize,
104
- avatarPalette: avatarPalette,
105
- palette: palette,
106
- a11y: list_a11y,
107
- secondaryValue: secondaryValue
108
- });
109
- } else if (optionType === 'icon') {
110
- return /*#__PURE__*/React.createElement(ListItemWithIcon, { ...commonProps,
111
- autoHover: false,
112
- getRef: getRef,
113
- highlight: isHighlight,
114
- id: id,
115
- key: `${id}iconListItem`,
116
- onClick: onClick,
117
- onMouseEnter: onMouseEnter,
118
- value: value,
119
- title: value,
120
- iconName: icon,
121
- needTick: needTick,
122
- needBorder: needBorder,
123
- active: isActive,
124
- iconSize: iconSize,
125
- size: listItemSize,
126
- palette: palette,
127
- a11y: list_a11y,
128
- secondaryValue: secondaryValue
129
- });
98
+ if (typeof getRef === 'function') {
99
+ getRef(ele, index, id);
130
100
  }
101
+ }
131
102
 
132
- return /*#__PURE__*/React.createElement(ListItem, { ...commonProps,
103
+ if (optionType === 'avatar') {
104
+ return /*#__PURE__*/React.createElement(ListItemWithAvatar, { ...commonProps,
133
105
  autoHover: false,
134
- getRef: getRef,
106
+ getRef: getListItemRef,
135
107
  highlight: isHighlight,
136
108
  id: id,
137
- key: `${id}listItem`,
109
+ imgSrc: photoURL,
110
+ key: `${id}avatarListItem`,
111
+ name: value,
138
112
  onClick: onClick,
139
113
  onMouseEnter: onMouseEnter,
140
114
  value: value,
@@ -143,8 +117,103 @@ export default class Suggestions extends React.PureComponent {
143
117
  needBorder: needBorder,
144
118
  active: isActive,
145
119
  size: listItemSize,
120
+ avatarPalette: avatarPalette,
146
121
  palette: palette,
147
- a11y: list_a11y
122
+ a11y: list_a11y,
123
+ secondaryValue: secondaryValue
124
+ });
125
+ } else if (optionType === 'icon') {
126
+ return /*#__PURE__*/React.createElement(ListItemWithIcon, { ...commonProps,
127
+ autoHover: false,
128
+ getRef: getListItemRef,
129
+ highlight: isHighlight,
130
+ id: id,
131
+ key: `${id}iconListItem`,
132
+ onClick: onClick,
133
+ onMouseEnter: onMouseEnter,
134
+ value: value,
135
+ title: value,
136
+ iconName: icon,
137
+ needTick: needTick,
138
+ needBorder: needBorder,
139
+ active: isActive,
140
+ iconSize: iconSize,
141
+ size: listItemSize,
142
+ palette: palette,
143
+ a11y: list_a11y,
144
+ secondaryValue: secondaryValue
145
+ });
146
+ }
147
+
148
+ return /*#__PURE__*/React.createElement(ListItem, { ...commonProps,
149
+ autoHover: false,
150
+ getRef: getListItemRef,
151
+ highlight: isHighlight,
152
+ id: id,
153
+ key: `${id}listItem`,
154
+ onClick: onClick,
155
+ onMouseEnter: onMouseEnter,
156
+ value: value,
157
+ title: value,
158
+ needTick: needTick,
159
+ needBorder: needBorder,
160
+ active: isActive,
161
+ size: listItemSize,
162
+ palette: palette,
163
+ a11y: list_a11y
164
+ });
165
+ }
166
+
167
+ renderVirtualizerSuggestionListItem(_ref3) {
168
+ let {
169
+ index,
170
+ ref
171
+ } = _ref3;
172
+ const {
173
+ suggestions
174
+ } = this.props;
175
+ const suggestion = suggestions[index];
176
+ return this.renderSuggestionList({
177
+ suggestion,
178
+ index,
179
+ ref
180
+ });
181
+ }
182
+
183
+ render() {
184
+ const {
185
+ suggestions,
186
+ dataId,
187
+ className,
188
+ isVirtualizerEnabled,
189
+ htmlId,
190
+ a11y,
191
+ setVirtualizerContainerRefFunction
192
+ } = this.props;
193
+ const {
194
+ ariaParentRole,
195
+ ariaMultiselectable
196
+ } = a11y;
197
+ return /*#__PURE__*/React.createElement(Container, {
198
+ isCover: false,
199
+ role: ariaParentRole,
200
+ id: htmlId,
201
+ tabindex: "0",
202
+ "aria-multiselectable": ariaMultiselectable
203
+ }, /*#__PURE__*/React.createElement(Box, {
204
+ dataId: `${dataId}`,
205
+ className: className ? className : ''
206
+ }, isVirtualizerEnabled ? /*#__PURE__*/React.createElement(Virtualizer, {
207
+ containerType: SuggestionsVirtualizerContainer,
208
+ elementRenderer: this.renderVirtualizerSuggestionListItem,
209
+ elementsCount: suggestions.length,
210
+ isElementsFixedHeight: false,
211
+ dataId: `${dataId}_virtualizer`,
212
+ setVirtualizerContainerRefFunction: setVirtualizerContainerRefFunction
213
+ }) : suggestions.map((suggestion, index) => {
214
+ return this.renderSuggestionList({
215
+ suggestion,
216
+ index
148
217
  });
149
218
  })));
150
219
  }
@@ -86,6 +86,7 @@ export const MultiSelect_defaultProps = {
86
86
  autoComplete: getLibraryConfig('autoComplete'),
87
87
  dataId: 'multiSelect',
88
88
  dropBoxSize: 'small',
89
+ isVirtualizerEnabled: false,
89
90
  isAnimate: true,
90
91
  isDisabled: false,
91
92
  isPopupOpenOnEnter: false,
@@ -167,5 +168,6 @@ export const SelectedOptions_defaultProps = {
167
168
  };
168
169
  export const Suggestions_defaultProps = {
169
170
  a11y: {},
171
+ isVirtualizerEnabled: false,
170
172
  needMultiLineText: false
171
173
  };
@@ -31,6 +31,7 @@ export const MultiSelect_propTypes = {
31
31
  disableAction: PropTypes.bool,
32
32
  dropBoxSize: PropTypes.oneOf(['small', 'medium', 'large']),
33
33
  emptyMessage: PropTypes.string.isRequired,
34
+ isVirtualizerEnabled: PropTypes.bool,
34
35
  getContainerRef: PropTypes.func,
35
36
  getNextOptions: PropTypes.func,
36
37
  getPublicMethods: PropTypes.func,
@@ -168,6 +169,8 @@ export const Suggestions_propTypes = {
168
169
  avatarPalette: PropTypes.string,
169
170
  className: PropTypes.string,
170
171
  dataId: PropTypes.string,
172
+ isVirtualizerEnabled: PropTypes.bool,
173
+ setVirtualizerContainerRefFunction: PropTypes.func,
171
174
  getRef: PropTypes.func,
172
175
  hoverId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
173
176
  hoverOption: PropTypes.number,
@@ -18,7 +18,7 @@ import { ResponsiveReceiver } from "../Responsive/CustomResponsive";
18
18
  import Loader from '@zohodesk/svg/lib/Loader/Loader';
19
19
  /**** Methods ****/
20
20
 
21
- import { makeFormatOptions, makeGetMultiSelectFilterSuggestions as makeGetFilterSuggestions, makeGetSelectedValueText as makeGetSelectedValue } from "../utils/dropDownUtils";
21
+ import { dummyObj, makeFormatOptions, makeGetMultiSelectFilterSuggestions as makeGetFilterSuggestions, makeGetSelectedValueText as makeGetSelectedValue } from "../utils/dropDownUtils";
22
22
  import { debounce, scrollTo, getIsEmptyValue, getSearchString, findScrollEnd, getKeyValue } from "../utils/Common.js";
23
23
  /**** CSS ****/
24
24
 
@@ -106,6 +106,7 @@ export class SelectComponent extends Component {
106
106
  this.handleAddNewOption = this.handleAddNewOption.bind(this);
107
107
  this.handleExposePopupHandlers = this.handleExposePopupHandlers.bind(this);
108
108
  this.handleGetAddNewOptionText = this.handleGetAddNewOptionText.bind(this);
109
+ this.setSuggestionsVirtualizerContainerRefFunction = this.setSuggestionsVirtualizerContainerRefFunction.bind(this);
109
110
  this.valueInputTypeString = '';
110
111
  this.valueInputSearchString = '';
111
112
  this.autoSelectSuggestions = [];
@@ -251,10 +252,10 @@ export class SelectComponent extends Component {
251
252
  valueField,
252
253
  textField,
253
254
  allowValueFallback,
254
- customProps = {}
255
+ customProps = dummyObj
255
256
  } = props;
256
257
  let {
257
- listItemProps = {}
258
+ listItemProps = dummyObj
258
259
  } = customProps;
259
260
  return this.formatOptions({
260
261
  options,
@@ -529,6 +530,7 @@ export class SelectComponent extends Component {
529
530
 
530
531
  suggestionContainerRef(el) {
531
532
  this.suggestionContainer = el;
533
+ typeof this.setSuggestionsVirtualizerRef === 'function' && this.setSuggestionsVirtualizerRef(el);
532
534
  }
533
535
 
534
536
  suggestionItemRef(el, index, id) {
@@ -676,6 +678,11 @@ export class SelectComponent extends Component {
676
678
  });
677
679
  }
678
680
 
681
+ setSuggestionsVirtualizerContainerRefFunction(refFunc) {
682
+ this.setSuggestionsVirtualizerRef = refFunc;
683
+ this.suggestionContainer && refFunc(this.suggestionContainer);
684
+ }
685
+
679
686
  render() {
680
687
  let {
681
688
  needSearch,
@@ -728,6 +735,7 @@ export class SelectComponent extends Component {
728
735
  targetOffset,
729
736
  isRestrictScroll,
730
737
  dropBoxPortalId,
738
+ isVirtualizerEnabled,
731
739
  renderCustomToggleIndicator,
732
740
  renderCustomSearchClearComponent
733
741
  } = this.props;
@@ -932,6 +940,8 @@ export class SelectComponent extends Component {
932
940
  eleRef: this.suggestionContainerRef
933
941
  }, suggestions.length ? /*#__PURE__*/React.createElement(Suggestions, {
934
942
  activeId: selectedId,
943
+ isVirtualizerEnabled: isVirtualizerEnabled,
944
+ setVirtualizerContainerRefFunction: this.setSuggestionsVirtualizerContainerRefFunction,
935
945
  suggestions: suggestions,
936
946
  getRef: this.suggestionItemRef,
937
947
  hoverOption: hoverIndex,
@@ -193,7 +193,7 @@ class SelectWithIcon extends Component {
193
193
  } = this.props;
194
194
 
195
195
  if (options.length) {
196
- datas = options.filter(obj => obj[valueKey].toLowerCase().includes(searchValue.toLowerCase()));
196
+ datas = options.filter(obj => obj[valueKey].toLowerCase().includes(searchValue.toLowerCase().trim()));
197
197
  }
198
198
 
199
199
  return datas;
@@ -246,14 +246,22 @@ describe('Select -', () => {
246
246
  userEvent.type(within(getByTestId(dropboxTestId)).getByRole('textbox'), '2');
247
247
  expect(getAllByRole(listItemRole)).toHaveLength(1);
248
248
  expect(asFragment()).toMatchSnapshot();
249
- }); // test('Should render the only options matching search value even with additional spaces before and after', () => {
250
- // const { getByRole, getAllByRole, asFragment, getByTestId } = render(<Select needSearch options={options} />);
251
- // userEvent.click(getByRole(selectInputRole));
252
- // userEvent.type(within(getByTestId(dropboxTestId)).getByRole('textbox'), ' 2 ');
253
- // expect(getAllByRole(listItemRole)).toHaveLength(1);
254
- // expect(asFragment()).toMatchSnapshot();
255
- // });
256
-
249
+ });
250
+ test('Should render the only options matching search value even with additional spaces before and after', () => {
251
+ const {
252
+ getByRole,
253
+ getAllByRole,
254
+ asFragment,
255
+ getByTestId
256
+ } = render( /*#__PURE__*/React.createElement(Select, {
257
+ needSearch: true,
258
+ options: options
259
+ }));
260
+ userEvent.click(getByRole(selectInputRole));
261
+ userEvent.type(within(getByTestId(dropboxTestId)).getByRole('textbox'), ' 2 ');
262
+ expect(getAllByRole(listItemRole)).toHaveLength(1);
263
+ expect(asFragment()).toMatchSnapshot();
264
+ });
257
265
  test('Should trigger given onSearch, when type on the search input', async () => {
258
266
  const mockOnSearch = jest.fn();
259
267
  const {