@zohodesk/components 1.4.7 → 1.4.9
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/.cli/propValidation_report.html +1 -1
- package/README.md +17 -4
- package/es/Accordion/Accordion.js +1 -1
- package/es/Card/Card.js +1 -1
- package/es/MultiSelect/AdvancedGroupMultiSelect.js +16 -8
- package/es/MultiSelect/AdvancedMultiSelect.js +13 -6
- package/es/MultiSelect/EmptyState.js +2 -1
- package/es/MultiSelect/MultiSelect.js +27 -16
- package/es/MultiSelect/MultiSelectWithAvatar.js +9 -2
- package/es/MultiSelect/SelectedOptions.js +4 -2
- package/es/MultiSelect/Suggestions.js +10 -2
- package/es/MultiSelect/__tests__/MultiSelect.spec.js +25 -0
- package/es/MultiSelect/__tests__/Suggestions.spec.js +58 -0
- package/es/MultiSelect/__tests__/__snapshots__/MultiSelect.spec.js.snap +253 -0
- package/es/MultiSelect/__tests__/__snapshots__/Suggestions.spec.js.snap +343 -0
- package/es/MultiSelect/constants.js +6 -0
- package/es/MultiSelect/props/propTypes.js +15 -55
- package/es/Popup/Popup.js +45 -17
- package/es/Popup/Registry.js +1 -0
- package/es/Select/Select.js +1 -1
- package/es/Select/__tests__/Select.spec.js +8 -1
- package/es/utils/dropDownUtils.js +25 -6
- package/es/v1/Popup/Popup.js +1 -1
- package/es/v1/Select/Select.js +1 -1
- package/lib/Accordion/Accordion.js +2 -2
- package/lib/Card/Card.js +2 -2
- package/lib/MultiSelect/AdvancedGroupMultiSelect.js +94 -83
- package/lib/MultiSelect/AdvancedMultiSelect.js +16 -7
- package/lib/MultiSelect/EmptyState.js +3 -1
- package/lib/MultiSelect/MultiSelect.js +30 -17
- package/lib/MultiSelect/MultiSelectWithAvatar.js +11 -3
- package/lib/MultiSelect/SelectedOptions.js +4 -2
- package/lib/MultiSelect/Suggestions.js +10 -2
- package/lib/MultiSelect/__tests__/MultiSelect.spec.js +25 -0
- package/lib/MultiSelect/__tests__/Suggestions.spec.js +58 -0
- package/lib/MultiSelect/__tests__/__snapshots__/MultiSelect.spec.js.snap +253 -0
- package/lib/MultiSelect/__tests__/__snapshots__/Suggestions.spec.js.snap +343 -0
- package/lib/MultiSelect/constants.js +13 -0
- package/lib/MultiSelect/props/propTypes.js +14 -55
- package/lib/Popup/Popup.js +54 -18
- package/lib/Popup/Registry.js +1 -0
- package/lib/Select/Select.js +2 -2
- package/lib/Select/__tests__/Select.spec.js +8 -1
- package/lib/utils/dropDownUtils.js +24 -3
- package/lib/v1/Popup/Popup.js +2 -2
- package/lib/v1/Select/Select.js +2 -2
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -2,15 +2,28 @@
|
|
|
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.9
|
|
6
|
+
|
|
7
|
+
- **MultiSelect , MultiSelectWithAvatar, AdvancedMultiSelect, AdvancedGroupMultiSelect**
|
|
8
|
+
- `limit` prop supported
|
|
9
|
+
- `limitReachedMessage` prop supported in i18nKeys
|
|
10
|
+
|
|
11
|
+
# 1.4.8
|
|
12
|
+
|
|
13
|
+
- Added UNSAFE_ prefix to deprecated lifecycle methods: componentWillMount, componentWillReceiveProps, and componentWillUpdate.
|
|
14
|
+
|
|
15
|
+
- **Popup**
|
|
16
|
+
- Handled potential memory leaks by `event listeners`.
|
|
17
|
+
- Added unmount handling for `requestAnimationFrame` to ensure proper cleanup.
|
|
18
|
+
|
|
19
|
+
|
|
5
20
|
# 1.4.7
|
|
6
21
|
|
|
7
|
-
- **Card**
|
|
8
|
-
- Added support for reverse infinite scroll with `isRecentOnBottom`.
|
|
22
|
+
- **Card** - Added support for reverse infinite scroll with `isRecentOnBottom`.
|
|
9
23
|
|
|
10
24
|
# 1.4.6
|
|
11
25
|
|
|
12
|
-
- **Popup**
|
|
13
|
-
- - Added fixed popup scroll block behavior support to iframe elements (same-origin only).
|
|
26
|
+
- **Popup** - Added fixed popup scroll block behavior support to iframe elements (same-origin only).
|
|
14
27
|
|
|
15
28
|
# 1.4.5
|
|
16
29
|
|
|
@@ -25,7 +25,7 @@ export default class Accordion extends React.Component {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
29
29
|
if (this.props.selectedItem !== nextProps.selectedItem && !this.props.disableInternalState) {
|
|
30
30
|
this.setState({
|
|
31
31
|
selectedItem: nextProps.selectedItem
|
package/es/Card/Card.js
CHANGED
|
@@ -102,7 +102,7 @@ export default class Card extends Component {
|
|
|
102
102
|
//this.onClearScroll = debounce(this.setScroll.bind(this, false), 500);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
106
106
|
if (this.from !== nextProps.from) {
|
|
107
107
|
this.from = nextProps.from;
|
|
108
108
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { AdvancedGroupMultiSelect_propTypes } from "./props/propTypes";
|
|
3
3
|
import { AdvancedGroupMultiSelect_defaultProps } from "./props/defaultProps";
|
|
4
|
+
import { MULTISELECT_I18N_KEYS } from "./constants";
|
|
4
5
|
/**** Components ****/
|
|
5
6
|
|
|
6
7
|
import Loader from '@zohodesk/svg/lib/Loader/Loader';
|
|
@@ -185,7 +186,8 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
185
186
|
|
|
186
187
|
handleSelectedOptionIdChange(selectedGroupOptions, normalizedFormatOptions, normalizedSelectedOptions) {
|
|
187
188
|
const {
|
|
188
|
-
allowValueFallback
|
|
189
|
+
allowValueFallback,
|
|
190
|
+
limit
|
|
189
191
|
} = this.props;
|
|
190
192
|
let newSelectedOptions = selectedGroupOptions.map(option => optionIdGrouping(option.id, option.groupId));
|
|
191
193
|
let selectedOptionsLength = newSelectedOptions.length;
|
|
@@ -197,7 +199,8 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
197
199
|
selectedOptions: newSelectedOptions,
|
|
198
200
|
normalizedFormatOptions: formatOptions,
|
|
199
201
|
selectedOptionsLength,
|
|
200
|
-
allowValueFallback
|
|
202
|
+
allowValueFallback,
|
|
203
|
+
limit
|
|
201
204
|
});
|
|
202
205
|
}
|
|
203
206
|
|
|
@@ -390,7 +393,8 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
390
393
|
getNextOptions,
|
|
391
394
|
isPopupOpen,
|
|
392
395
|
isPopupOpenOnEnter,
|
|
393
|
-
onKeyDown
|
|
396
|
+
onKeyDown,
|
|
397
|
+
limit
|
|
394
398
|
} = this.props;
|
|
395
399
|
let highLightedSelectOptionsLen = highLightedSelectOptions.length;
|
|
396
400
|
|
|
@@ -442,7 +446,7 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
442
446
|
hoverIndex: hoverIndex + 1
|
|
443
447
|
});
|
|
444
448
|
}
|
|
445
|
-
} else if (keyCode === 13) {
|
|
449
|
+
} else if (keyCode === 13 && selectedOptions.length < limit) {
|
|
446
450
|
//enter key
|
|
447
451
|
let id = suggestions[hoverIndex] || {};
|
|
448
452
|
isPopupOpen && !getIsEmptyValue(id) && this.handleSelectOption(id, null, null, e);
|
|
@@ -884,13 +888,15 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
884
888
|
needEffect,
|
|
885
889
|
autoComplete,
|
|
886
890
|
getTargetRef,
|
|
887
|
-
isFocus
|
|
891
|
+
isFocus,
|
|
892
|
+
limit
|
|
888
893
|
} = this.props;
|
|
889
894
|
let {
|
|
890
|
-
clearText =
|
|
895
|
+
clearText = MULTISELECT_I18N_KEYS.clearText,
|
|
896
|
+
limitReachedMessage = MULTISELECT_I18N_KEYS.limitReachedMessage
|
|
891
897
|
} = i18nKeys;
|
|
892
898
|
let {
|
|
893
|
-
clearLabel =
|
|
899
|
+
clearLabel = MULTISELECT_I18N_KEYS.clearText
|
|
894
900
|
} = a11y;
|
|
895
901
|
i18nKeys = Object.assign({}, i18nKeys, {
|
|
896
902
|
emptyText: i18nKeys.emptyText || emptyMessage,
|
|
@@ -1026,7 +1032,7 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
1026
1032
|
}, /*#__PURE__*/React.createElement(Card, {
|
|
1027
1033
|
customClass: `${style.box} ${style[`${palette}Box`]}`,
|
|
1028
1034
|
onScroll: this.handleScroll
|
|
1029
|
-
}, needSelectAll ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
1035
|
+
}, needSelectAll && !(limit >= 0) ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
1030
1036
|
onSelect: this.handleSelectAll,
|
|
1031
1037
|
selectAllText: selectAllText,
|
|
1032
1038
|
suggestions: suggestionOptionIds,
|
|
@@ -1065,6 +1071,8 @@ class AdvancedGroupMultiSelect extends React.Component {
|
|
|
1065
1071
|
a11y: {
|
|
1066
1072
|
role: 'option'
|
|
1067
1073
|
},
|
|
1074
|
+
limit: limit,
|
|
1075
|
+
limitReachedMessage: limitReachedMessage,
|
|
1068
1076
|
dataId: `${dataId}_Options`
|
|
1069
1077
|
}));
|
|
1070
1078
|
}) : /*#__PURE__*/React.createElement(EmptyState, {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { AdvancedMultiSelect_propTypes } from "./props/propTypes";
|
|
4
4
|
import { AdvancedMultiSelect_defaultProps } from "./props/defaultProps";
|
|
5
|
+
import { MULTISELECT_I18N_KEYS } from "./constants";
|
|
5
6
|
/**** Components ****/
|
|
6
7
|
|
|
7
8
|
import { MultiSelectComponent } from "./MultiSelect";
|
|
@@ -177,7 +178,8 @@ export class AdvancedMultiSelectComponent extends MultiSelectComponent {
|
|
|
177
178
|
const {
|
|
178
179
|
selectedOptionsLimit,
|
|
179
180
|
selectedOptionDetails,
|
|
180
|
-
allowValueFallback
|
|
181
|
+
allowValueFallback,
|
|
182
|
+
limit
|
|
181
183
|
} = props;
|
|
182
184
|
showedSelectedOptionsCount = getIsEmptyValue(showedSelectedOptionsCount) ? selectedOptionsLimit : showedSelectedOptionsCount;
|
|
183
185
|
const {
|
|
@@ -191,7 +193,8 @@ export class AdvancedMultiSelectComponent extends MultiSelectComponent {
|
|
|
191
193
|
selectedOptions,
|
|
192
194
|
normalizedFormatOptions: formatOptions,
|
|
193
195
|
selectedOptionsLength: showedSelectedOptionsCount,
|
|
194
|
-
allowValueFallback
|
|
196
|
+
allowValueFallback,
|
|
197
|
+
limit
|
|
195
198
|
});
|
|
196
199
|
}
|
|
197
200
|
|
|
@@ -385,7 +388,8 @@ export class AdvancedMultiSelectComponent extends MultiSelectComponent {
|
|
|
385
388
|
positionsOffset,
|
|
386
389
|
targetOffset,
|
|
387
390
|
isRestrictScroll,
|
|
388
|
-
isFocus
|
|
391
|
+
isFocus,
|
|
392
|
+
limit
|
|
389
393
|
} = this.props;
|
|
390
394
|
let {
|
|
391
395
|
SuggestionsProps = {},
|
|
@@ -395,10 +399,11 @@ export class AdvancedMultiSelectComponent extends MultiSelectComponent {
|
|
|
395
399
|
containerClass = ''
|
|
396
400
|
} = customClass;
|
|
397
401
|
const {
|
|
398
|
-
clearText =
|
|
402
|
+
clearText = MULTISELECT_I18N_KEYS.clearText,
|
|
403
|
+
limitReachedMessage = MULTISELECT_I18N_KEYS.limitReachedMessage
|
|
399
404
|
} = i18nKeys;
|
|
400
405
|
const {
|
|
401
|
-
clearLabel =
|
|
406
|
+
clearLabel = MULTISELECT_I18N_KEYS.clearText
|
|
402
407
|
} = a11y;
|
|
403
408
|
i18nKeys = Object.assign({}, i18nKeys, {
|
|
404
409
|
emptyText: i18nKeys.emptyText || emptyMessage,
|
|
@@ -541,7 +546,7 @@ export class AdvancedMultiSelectComponent extends MultiSelectComponent {
|
|
|
541
546
|
}, /*#__PURE__*/React.createElement(Card, {
|
|
542
547
|
customClass: style.box,
|
|
543
548
|
onScroll: this.handleScroll
|
|
544
|
-
}, needSelectAll ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
549
|
+
}, needSelectAll && !(limit >= 0) ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
545
550
|
onSelect: this.handleSelectAll,
|
|
546
551
|
selectAllText: selectAllText,
|
|
547
552
|
suggestions: suggestions,
|
|
@@ -563,6 +568,8 @@ export class AdvancedMultiSelectComponent extends MultiSelectComponent {
|
|
|
563
568
|
role: 'option'
|
|
564
569
|
},
|
|
565
570
|
dataId: `${dataIdMultiSelectComp}_Options`,
|
|
571
|
+
limit: limit,
|
|
572
|
+
limitReachedMessage: limitReachedMessage,
|
|
566
573
|
...SuggestionsProps
|
|
567
574
|
}) : /*#__PURE__*/React.createElement(EmptyState, {
|
|
568
575
|
isLoading: isFetchingOptions,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { EmptyState_propTypes } from "./props/propTypes";
|
|
4
4
|
import { EmptyState_defaultProps } from "./props/defaultProps";
|
|
5
|
+
import { MULTISELECT_I18N_KEYS } from "./constants";
|
|
5
6
|
/**** CSS ****/
|
|
6
7
|
|
|
7
8
|
import style from "./MultiSelect.module.css";
|
|
@@ -23,7 +24,7 @@ export default class EmptyState extends React.PureComponent {
|
|
|
23
24
|
a11y
|
|
24
25
|
} = this.props;
|
|
25
26
|
let {
|
|
26
|
-
loadingText =
|
|
27
|
+
loadingText = MULTISELECT_I18N_KEYS.loadingText,
|
|
27
28
|
emptyText = emptyMessage,
|
|
28
29
|
noMoreText = noMoreOptionsMessage,
|
|
29
30
|
searchEmptyText = searchEmptyMessage
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { MultiSelect_propTypes } from "./props/propTypes";
|
|
4
4
|
import { MultiSelect_defaultProps } from "./props/defaultProps";
|
|
5
5
|
import { defaultProps as MobileHeader_defaultProps } from "./MobileHeader/props/defaultProps";
|
|
6
|
+
import { MULTISELECT_I18N_KEYS } from "./constants";
|
|
6
7
|
/**** Components ****/
|
|
7
8
|
|
|
8
9
|
import Popup from "../Popup/Popup";
|
|
@@ -89,7 +90,7 @@ export class MultiSelectComponent extends React.Component {
|
|
|
89
90
|
// suggestionContainer.addEventListener('scroll', this.handleScroll);
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
93
94
|
const {
|
|
94
95
|
selectedOptions,
|
|
95
96
|
options,
|
|
@@ -234,12 +235,14 @@ export class MultiSelectComponent extends React.Component {
|
|
|
234
235
|
|
|
235
236
|
handleGetSelectedOptions(selectedOptions, normalizedFormatOptions, props) {
|
|
236
237
|
const {
|
|
237
|
-
allowValueFallback
|
|
238
|
+
allowValueFallback,
|
|
239
|
+
limit
|
|
238
240
|
} = props;
|
|
239
241
|
return this.getSelectedOptions({
|
|
240
242
|
selectedOptions,
|
|
241
243
|
normalizedFormatOptions,
|
|
242
|
-
allowValueFallback
|
|
244
|
+
allowValueFallback,
|
|
245
|
+
limit
|
|
243
246
|
});
|
|
244
247
|
}
|
|
245
248
|
|
|
@@ -313,8 +316,10 @@ export class MultiSelectComponent extends React.Component {
|
|
|
313
316
|
getNextOptions,
|
|
314
317
|
isPopupOpen,
|
|
315
318
|
isPopupOpenOnEnter,
|
|
316
|
-
onKeyDown
|
|
319
|
+
onKeyDown,
|
|
320
|
+
limit
|
|
317
321
|
} = this.props;
|
|
322
|
+
const allowKeyboardActions = !limit || limit && selectedOptions.length < limit;
|
|
318
323
|
const highLightedSelectOptionsLen = highLightedSelectOptions.length;
|
|
319
324
|
|
|
320
325
|
if (isPopupOpen && (keyCode === 38 || keyCode === 40 || keyCode === 13 || keyCode === 27 || keyCode === 9)) {
|
|
@@ -334,7 +339,7 @@ export class MultiSelectComponent extends React.Component {
|
|
|
334
339
|
|
|
335
340
|
const suggestionsLen = suggestions.length;
|
|
336
341
|
|
|
337
|
-
if (suggestionsLen && isPopupOpen && keyCode === 38) {
|
|
342
|
+
if (suggestionsLen && isPopupOpen && keyCode === 38 && allowKeyboardActions) {
|
|
338
343
|
//up arrow
|
|
339
344
|
|
|
340
345
|
/*if (hoverOption === 0) { //disable first to last option higlight
|
|
@@ -345,7 +350,7 @@ export class MultiSelectComponent extends React.Component {
|
|
|
345
350
|
hoverOption: hoverOption - 1
|
|
346
351
|
});
|
|
347
352
|
}
|
|
348
|
-
} else if (suggestionsLen && isPopupOpen && keyCode === 40) {
|
|
353
|
+
} else if (suggestionsLen && isPopupOpen && keyCode === 40 && allowKeyboardActions) {
|
|
349
354
|
//down arrow
|
|
350
355
|
|
|
351
356
|
/*else if (hoverOption === suggestionsLen - 1 || hoverOption === null) {
|
|
@@ -362,8 +367,8 @@ export class MultiSelectComponent extends React.Component {
|
|
|
362
367
|
hoverOption: hoverOption + 1
|
|
363
368
|
});
|
|
364
369
|
}
|
|
365
|
-
} else if (keyCode === 13) {
|
|
366
|
-
//enter
|
|
370
|
+
} else if (keyCode === 13 && allowKeyboardActions) {
|
|
371
|
+
//enter ke
|
|
367
372
|
const selectedOption = suggestions[hoverOption] || {};
|
|
368
373
|
const {
|
|
369
374
|
id
|
|
@@ -727,14 +732,16 @@ export class MultiSelectComponent extends React.Component {
|
|
|
727
732
|
needToCloseOnSelect,
|
|
728
733
|
togglePopup,
|
|
729
734
|
selectedOptions: propSelectedOptions,
|
|
730
|
-
disabledOptions = dummyArray
|
|
735
|
+
disabledOptions = dummyArray,
|
|
736
|
+
limit
|
|
731
737
|
} = this.props;
|
|
732
738
|
const {
|
|
733
739
|
newSelectedOptions
|
|
734
740
|
} = filterSelectedOptions({
|
|
735
741
|
selectedOptions,
|
|
736
742
|
propSelectedOptions,
|
|
737
|
-
disabledOptions
|
|
743
|
+
disabledOptions,
|
|
744
|
+
limit
|
|
738
745
|
});
|
|
739
746
|
const selectedOptionsLen = newSelectedOptions.length;
|
|
740
747
|
const allSelectedOptionsDetails = [];
|
|
@@ -894,10 +901,10 @@ export class MultiSelectComponent extends React.Component {
|
|
|
894
901
|
searchStr
|
|
895
902
|
} = this.state;
|
|
896
903
|
const {
|
|
897
|
-
clearText =
|
|
904
|
+
clearText = MULTISELECT_I18N_KEYS.clearText
|
|
898
905
|
} = i18nKeys;
|
|
899
906
|
const {
|
|
900
|
-
clearLabel =
|
|
907
|
+
clearLabel = MULTISELECT_I18N_KEYS.clearLabel,
|
|
901
908
|
ariaLabelledby
|
|
902
909
|
} = a11y;
|
|
903
910
|
let {
|
|
@@ -1034,7 +1041,8 @@ export class MultiSelectComponent extends React.Component {
|
|
|
1034
1041
|
boxSize,
|
|
1035
1042
|
isLoading,
|
|
1036
1043
|
selectAllText,
|
|
1037
|
-
needSelectAll
|
|
1044
|
+
needSelectAll,
|
|
1045
|
+
limit
|
|
1038
1046
|
} = this.props;
|
|
1039
1047
|
const {
|
|
1040
1048
|
selectedOptions,
|
|
@@ -1045,7 +1053,8 @@ export class MultiSelectComponent extends React.Component {
|
|
|
1045
1053
|
selectedOptionIds
|
|
1046
1054
|
} = this.state;
|
|
1047
1055
|
const {
|
|
1048
|
-
searchText =
|
|
1056
|
+
searchText = MULTISELECT_I18N_KEYS.searchText,
|
|
1057
|
+
limitReachedMessage = MULTISELECT_I18N_KEYS.limitReachedMessage
|
|
1049
1058
|
} = i18nKeys;
|
|
1050
1059
|
const suggestions = this.handleFilterSuggestions();
|
|
1051
1060
|
const setAriaId = this.getNextAriaId();
|
|
@@ -1099,7 +1108,7 @@ export class MultiSelectComponent extends React.Component {
|
|
|
1099
1108
|
onClick: this.handlePopupClose
|
|
1100
1109
|
}, /*#__PURE__*/React.createElement("div", {
|
|
1101
1110
|
className: style.effect
|
|
1102
|
-
}, this.getSelectionUI(true))) : null, needSelectAll ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
1111
|
+
}, this.getSelectionUI(true))) : null, needSelectAll && !(limit >= 0) ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
1103
1112
|
onSelect: this.handleSelectAll,
|
|
1104
1113
|
selectAllText: selectAllText,
|
|
1105
1114
|
suggestions: suggestions,
|
|
@@ -1125,7 +1134,9 @@ export class MultiSelectComponent extends React.Component {
|
|
|
1125
1134
|
selectedOptions: selectedOptionIds,
|
|
1126
1135
|
a11y: {
|
|
1127
1136
|
role: 'option'
|
|
1128
|
-
}
|
|
1137
|
+
},
|
|
1138
|
+
limit: limit,
|
|
1139
|
+
limitReachedMessage: limitReachedMessage
|
|
1129
1140
|
}) : /*#__PURE__*/React.createElement(EmptyState, {
|
|
1130
1141
|
isLoading: isFetchingOptions,
|
|
1131
1142
|
options: options,
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { MultiSelectWithAvatar_propTypes } from "./props/propTypes";
|
|
4
4
|
import { MultiSelectWithAvatar_defaultProps } from "./props/defaultProps";
|
|
5
5
|
import { defaultProps as MobileHeader_defaultProps } from "./MobileHeader/props/defaultProps";
|
|
6
|
+
import { MULTISELECT_I18N_KEYS } from "./constants";
|
|
6
7
|
/**** Components ****/
|
|
7
8
|
|
|
8
9
|
import { MultiSelectComponent } from "./MultiSelect";
|
|
@@ -93,11 +94,15 @@ class MultiSelectWithAvatarComponent extends MultiSelectComponent {
|
|
|
93
94
|
needEffect,
|
|
94
95
|
isLoading,
|
|
95
96
|
keepSelectedOptions,
|
|
96
|
-
customProps
|
|
97
|
+
customProps,
|
|
98
|
+
limit
|
|
97
99
|
} = this.props;
|
|
98
100
|
let {
|
|
99
101
|
SuggestionsProps = {}
|
|
100
102
|
} = customProps;
|
|
103
|
+
let {
|
|
104
|
+
limitReachedMessage = MULTISELECT_I18N_KEYS.limitReachedMessage
|
|
105
|
+
} = i18nKeys;
|
|
101
106
|
i18nKeys = Object.assign({}, MobileHeader_defaultProps.i18nKeys, i18nKeys, {
|
|
102
107
|
emptyText: i18nKeys.emptyText || emptyMessage,
|
|
103
108
|
searchEmptyText: i18nKeys.searchEmptyText || searchEmptyMessage,
|
|
@@ -158,7 +163,7 @@ class MultiSelectWithAvatarComponent extends MultiSelectComponent {
|
|
|
158
163
|
onClick: this.handlePopupClose
|
|
159
164
|
}, /*#__PURE__*/React.createElement("div", {
|
|
160
165
|
className: style.effect
|
|
161
|
-
}, this.getSelectionUI(true)))) : null, needSelectAll ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
166
|
+
}, this.getSelectionUI(true)))) : null, needSelectAll && !(limit >= 0) ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
|
|
162
167
|
onSelect: this.handleSelectAll,
|
|
163
168
|
selectAllText: selectAllText,
|
|
164
169
|
suggestions: suggestions,
|
|
@@ -184,6 +189,8 @@ class MultiSelectWithAvatarComponent extends MultiSelectComponent {
|
|
|
184
189
|
a11y: {
|
|
185
190
|
role: 'option'
|
|
186
191
|
},
|
|
192
|
+
limit: limit,
|
|
193
|
+
limitReachedMessage: limitReachedMessage,
|
|
187
194
|
...SuggestionsProps
|
|
188
195
|
}) : /*#__PURE__*/React.createElement(EmptyState, {
|
|
189
196
|
isLoading: isFetchingOptions,
|
|
@@ -22,10 +22,12 @@ export default class SelectedOptions extends React.PureComponent {
|
|
|
22
22
|
onSelect,
|
|
23
23
|
size,
|
|
24
24
|
palette,
|
|
25
|
-
dataId
|
|
25
|
+
dataId,
|
|
26
|
+
limit
|
|
26
27
|
} = this.props;
|
|
27
28
|
const isDarkPalette = palette === 'dark' ? 'dark' : 'danger';
|
|
28
|
-
|
|
29
|
+
const selectedData = limit && limit > 0 ? selectedOptions.slice(0, limit) : selectedOptions;
|
|
30
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, selectedData.map(option => {
|
|
29
31
|
const {
|
|
30
32
|
id,
|
|
31
33
|
value,
|
|
@@ -28,12 +28,15 @@ export default class Suggestions extends React.PureComponent {
|
|
|
28
28
|
palette,
|
|
29
29
|
htmlId,
|
|
30
30
|
a11y,
|
|
31
|
-
needMultiLineText
|
|
31
|
+
needMultiLineText,
|
|
32
|
+
limit,
|
|
33
|
+
limitReachedMessage
|
|
32
34
|
} = this.props;
|
|
33
35
|
const {
|
|
34
36
|
ariaParentRole,
|
|
35
37
|
ariaMultiselectable
|
|
36
38
|
} = a11y;
|
|
39
|
+
const selectedOptionsLength = selectedOptions.length;
|
|
37
40
|
return /*#__PURE__*/React.createElement(Container, {
|
|
38
41
|
isCover: false,
|
|
39
42
|
role: ariaParentRole,
|
|
@@ -58,13 +61,14 @@ export default class Suggestions extends React.PureComponent {
|
|
|
58
61
|
} = suggestion;
|
|
59
62
|
const isActive = activeId === id || selectedOptions.indexOf(id) >= 0;
|
|
60
63
|
const isHighlight = hoverOption === index || id === hoverId ? true : false;
|
|
64
|
+
const isLimitReached = selectedOptionsLength >= limit && !isActive;
|
|
61
65
|
const list_a11y = Object.assign({}, a11y, {
|
|
62
66
|
ariaSelected: isActive,
|
|
63
67
|
ariaLabel: value,
|
|
64
68
|
'data-a11y-list-active': isHighlight
|
|
65
69
|
});
|
|
66
70
|
const commonProps = {
|
|
67
|
-
isDisabled,
|
|
71
|
+
isDisabled: isDisabled ? isDisabled : isLimitReached,
|
|
68
72
|
needMultiLineText,
|
|
69
73
|
...listItemCustomProps
|
|
70
74
|
};
|
|
@@ -76,6 +80,10 @@ export default class Suggestions extends React.PureComponent {
|
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
if (isLimitReached) {
|
|
84
|
+
commonProps.disableTitle = limitReachedMessage;
|
|
85
|
+
}
|
|
86
|
+
|
|
79
87
|
if (optionType === 'avatar') {
|
|
80
88
|
return /*#__PURE__*/React.createElement(ListItemWithAvatar, { ...commonProps,
|
|
81
89
|
autoHover: false,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import MultiSelect from "../MultiSelect";
|
|
4
|
+
const testData = ['text1', 'text2', 'text3', 'text4', 'text5', 'text6', 'text7', 'text8', 'text9', 'text10'];
|
|
5
|
+
const testSelectedData = ['text1', 'text2', 'text3'];
|
|
4
6
|
describe('MultiSelect', () => {
|
|
5
7
|
test('rendering the defult props', () => {
|
|
6
8
|
const {
|
|
@@ -8,4 +10,27 @@ describe('MultiSelect', () => {
|
|
|
8
10
|
} = render( /*#__PURE__*/React.createElement(MultiSelect, null));
|
|
9
11
|
expect(asFragment()).toMatchSnapshot();
|
|
10
12
|
});
|
|
13
|
+
test('rendering with limit feature', () => {
|
|
14
|
+
const {
|
|
15
|
+
asFragment
|
|
16
|
+
} = render( /*#__PURE__*/React.createElement(MultiSelect, {
|
|
17
|
+
valueField: "id",
|
|
18
|
+
textField: "text",
|
|
19
|
+
options: testData,
|
|
20
|
+
selectedOptions: testSelectedData,
|
|
21
|
+
needSelectAll: true,
|
|
22
|
+
selectAllText: "Select All",
|
|
23
|
+
placeHolder: "Select Text",
|
|
24
|
+
i18nKeys: {
|
|
25
|
+
clearText: 'Clear Selected Items',
|
|
26
|
+
loadingText: 'Fetching...',
|
|
27
|
+
emptyText: 'No Options .',
|
|
28
|
+
noMoreText: 'No More Options .',
|
|
29
|
+
searchEmptyText: 'No Matches Found .'
|
|
30
|
+
},
|
|
31
|
+
needResponsive: true,
|
|
32
|
+
limit: 3
|
|
33
|
+
}));
|
|
34
|
+
expect(asFragment()).toMatchSnapshot();
|
|
35
|
+
});
|
|
11
36
|
});
|
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
3
|
import Suggestions from "../Suggestions";
|
|
4
|
+
const sampleData = [{
|
|
5
|
+
"id": 2,
|
|
6
|
+
"value": "text2",
|
|
7
|
+
"optionType": "default",
|
|
8
|
+
"listItemProps": {
|
|
9
|
+
"style": {
|
|
10
|
+
"color": "red"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}, {
|
|
14
|
+
"id": 3,
|
|
15
|
+
"value": "text3",
|
|
16
|
+
"optionType": "default",
|
|
17
|
+
"listItemProps": {
|
|
18
|
+
"style": {
|
|
19
|
+
"color": "blue"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}, {
|
|
23
|
+
"id": 4,
|
|
24
|
+
"value": "text4",
|
|
25
|
+
"optionType": "default"
|
|
26
|
+
}, {
|
|
27
|
+
"id": 5,
|
|
28
|
+
"value": "text5",
|
|
29
|
+
"optionType": "default"
|
|
30
|
+
}, {
|
|
31
|
+
"id": 6,
|
|
32
|
+
"value": "text6",
|
|
33
|
+
"optionType": "default"
|
|
34
|
+
}, {
|
|
35
|
+
"id": 7,
|
|
36
|
+
"value": "text7",
|
|
37
|
+
"optionType": "default"
|
|
38
|
+
}, {
|
|
39
|
+
"id": 8,
|
|
40
|
+
"value": "text8",
|
|
41
|
+
"optionType": "default"
|
|
42
|
+
}, {
|
|
43
|
+
"id": 9,
|
|
44
|
+
"value": "text9",
|
|
45
|
+
"optionType": "default"
|
|
46
|
+
}, {
|
|
47
|
+
"id": 10,
|
|
48
|
+
"value": "text10",
|
|
49
|
+
"optionType": "default"
|
|
50
|
+
}];
|
|
4
51
|
describe('Suggestions', () => {
|
|
5
52
|
test('rendering the defult props', () => {
|
|
6
53
|
const {
|
|
@@ -10,4 +57,15 @@ describe('Suggestions', () => {
|
|
|
10
57
|
}));
|
|
11
58
|
expect(asFragment()).toMatchSnapshot();
|
|
12
59
|
});
|
|
60
|
+
test('rendering with limit props', () => {
|
|
61
|
+
const {
|
|
62
|
+
asFragment
|
|
63
|
+
} = render( /*#__PURE__*/React.createElement(Suggestions, {
|
|
64
|
+
selectedOptions: [1, 2, 3, 4],
|
|
65
|
+
suggestions: sampleData,
|
|
66
|
+
limit: 3,
|
|
67
|
+
limitReachedMessage: "Limit Reached"
|
|
68
|
+
}));
|
|
69
|
+
expect(asFragment()).toMatchSnapshot();
|
|
70
|
+
});
|
|
13
71
|
});
|