@khanacademy/wonder-blocks-dropdown 5.8.1 → 6.1.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/CHANGELOG.md +46 -0
- package/dist/components/combobox-live-region.d.ts +2 -2
- package/dist/components/combobox.d.ts +1 -1
- package/dist/components/dropdown-core-virtualized.d.ts +1 -1
- package/dist/components/dropdown-opener.d.ts +8 -0
- package/dist/components/listbox.d.ts +2 -2
- package/dist/components/multi-select.d.ts +44 -60
- package/dist/components/select-opener.d.ts +4 -0
- package/dist/components/single-select.d.ts +40 -42
- package/dist/es/index.js +475 -418
- package/dist/hooks/use-listbox.d.ts +1 -1
- package/dist/hooks/use-select-validation.d.ts +39 -0
- package/dist/index.js +474 -417
- package/dist/util/constants.d.ts +10 -7
- package/package.json +19 -19
package/dist/es/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as tokens from '@khanacademy/wonder-blocks-tokens';
|
|
|
6
6
|
import { spacing, color, mix, fade, font, border, semanticColor } from '@khanacademy/wonder-blocks-tokens';
|
|
7
7
|
import { LabelMedium, LabelSmall, LabelLarge } from '@khanacademy/wonder-blocks-typography';
|
|
8
8
|
import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/objectWithoutPropertiesLoose';
|
|
9
|
-
import { View, addStyle, IDProvider, useUniqueIdWithMock } from '@khanacademy/wonder-blocks-core';
|
|
9
|
+
import { View, addStyle, IDProvider, useOnMountEffect, useUniqueIdWithMock } from '@khanacademy/wonder-blocks-core';
|
|
10
10
|
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
11
11
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
12
12
|
import checkIcon from '@phosphor-icons/core/bold/check-bold.svg';
|
|
@@ -25,13 +25,12 @@ import { TextField } from '@khanacademy/wonder-blocks-form';
|
|
|
25
25
|
import IconButton from '@khanacademy/wonder-blocks-icon-button';
|
|
26
26
|
import Pill from '@khanacademy/wonder-blocks-pill';
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
down: 40
|
|
28
|
+
const keys = {
|
|
29
|
+
escape: "Escape",
|
|
30
|
+
tab: "Tab",
|
|
31
|
+
space: " ",
|
|
32
|
+
up: "ArrowUp",
|
|
33
|
+
down: "ArrowDown"
|
|
35
34
|
};
|
|
36
35
|
const selectDropdownStyle = {
|
|
37
36
|
marginTop: spacing.xSmall_8,
|
|
@@ -495,7 +494,9 @@ class DropdownOpener extends React.Component {
|
|
|
495
494
|
opened,
|
|
496
495
|
"aria-controls": ariaControls,
|
|
497
496
|
"aria-haspopup": ariaHasPopUp,
|
|
498
|
-
|
|
497
|
+
"aria-required": ariaRequired,
|
|
498
|
+
id,
|
|
499
|
+
onBlur
|
|
499
500
|
} = this.props;
|
|
500
501
|
const renderedChildren = this.props.children(_extends({}, eventState, {
|
|
501
502
|
text,
|
|
@@ -504,16 +505,19 @@ class DropdownOpener extends React.Component {
|
|
|
504
505
|
const childrenProps = renderedChildren.props;
|
|
505
506
|
const childrenTestId = this.getTestIdFromProps(childrenProps);
|
|
506
507
|
return React.cloneElement(renderedChildren, _extends({}, clickableChildrenProps, {
|
|
508
|
+
"aria-invalid": this.props.error,
|
|
507
509
|
disabled,
|
|
508
510
|
"aria-controls": ariaControls,
|
|
509
511
|
id,
|
|
510
512
|
"aria-expanded": opened ? "true" : "false",
|
|
511
513
|
"aria-haspopup": ariaHasPopUp,
|
|
514
|
+
"aria-required": ariaRequired,
|
|
512
515
|
onClick: childrenProps.onClick ? e => {
|
|
513
516
|
childrenProps.onClick(e);
|
|
514
517
|
clickableChildrenProps.onClick(e);
|
|
515
518
|
} : clickableChildrenProps.onClick,
|
|
516
|
-
"data-testid": childrenTestId || testId
|
|
519
|
+
"data-testid": childrenTestId || testId,
|
|
520
|
+
onBlur
|
|
517
521
|
}));
|
|
518
522
|
}
|
|
519
523
|
render() {
|
|
@@ -825,13 +829,13 @@ class DropdownCore extends React.Component {
|
|
|
825
829
|
}
|
|
826
830
|
constructor(props) {
|
|
827
831
|
super(props);
|
|
828
|
-
this.focusedIndex = void 0;
|
|
829
|
-
this.focusedOriginalIndex = void 0;
|
|
830
|
-
this.itemsClicked = void 0;
|
|
831
832
|
this.popperElement = void 0;
|
|
832
833
|
this.virtualizedListRef = void 0;
|
|
833
834
|
this.handleKeyDownDebounced = void 0;
|
|
834
835
|
this.textSuggestion = void 0;
|
|
836
|
+
this.focusedIndex = -1;
|
|
837
|
+
this.focusedOriginalIndex = -1;
|
|
838
|
+
this.itemsClicked = false;
|
|
835
839
|
this.searchFieldRef = React.createRef();
|
|
836
840
|
this.handleInteract = event => {
|
|
837
841
|
const {
|
|
@@ -851,39 +855,39 @@ class DropdownCore extends React.Component {
|
|
|
851
855
|
open,
|
|
852
856
|
searchText
|
|
853
857
|
} = this.props;
|
|
854
|
-
const
|
|
855
|
-
if (enableTypeAhead && getStringForKey(
|
|
858
|
+
const key = event.key;
|
|
859
|
+
if (enableTypeAhead && getStringForKey(key)) {
|
|
856
860
|
event.stopPropagation();
|
|
857
|
-
this.textSuggestion +=
|
|
861
|
+
this.textSuggestion += key;
|
|
858
862
|
this.handleKeyDownDebounced(this.textSuggestion);
|
|
859
863
|
}
|
|
860
864
|
if (!open) {
|
|
861
|
-
if (
|
|
865
|
+
if (key === keys.down) {
|
|
862
866
|
event.preventDefault();
|
|
863
867
|
onOpenChanged(true);
|
|
864
868
|
return;
|
|
865
869
|
}
|
|
866
870
|
return;
|
|
867
871
|
}
|
|
868
|
-
switch (
|
|
869
|
-
case
|
|
872
|
+
switch (key) {
|
|
873
|
+
case keys.tab:
|
|
870
874
|
if (this.isSearchFieldFocused() && searchText) {
|
|
871
875
|
return;
|
|
872
876
|
}
|
|
873
877
|
this.restoreTabOrder();
|
|
874
878
|
onOpenChanged(false);
|
|
875
879
|
return;
|
|
876
|
-
case
|
|
880
|
+
case keys.space:
|
|
877
881
|
if (this.isSearchFieldFocused()) {
|
|
878
882
|
return;
|
|
879
883
|
}
|
|
880
884
|
event.preventDefault();
|
|
881
885
|
return;
|
|
882
|
-
case
|
|
886
|
+
case keys.up:
|
|
883
887
|
event.preventDefault();
|
|
884
888
|
this.focusPreviousItem();
|
|
885
889
|
return;
|
|
886
|
-
case
|
|
890
|
+
case keys.down:
|
|
887
891
|
event.preventDefault();
|
|
888
892
|
this.focusNextItem();
|
|
889
893
|
return;
|
|
@@ -894,15 +898,15 @@ class DropdownCore extends React.Component {
|
|
|
894
898
|
onOpenChanged,
|
|
895
899
|
open
|
|
896
900
|
} = this.props;
|
|
897
|
-
const
|
|
898
|
-
switch (
|
|
899
|
-
case
|
|
901
|
+
const key = event.key;
|
|
902
|
+
switch (key) {
|
|
903
|
+
case keys.space:
|
|
900
904
|
if (this.isSearchFieldFocused()) {
|
|
901
905
|
return;
|
|
902
906
|
}
|
|
903
907
|
event.preventDefault();
|
|
904
908
|
return;
|
|
905
|
-
case
|
|
909
|
+
case keys.escape:
|
|
906
910
|
if (open) {
|
|
907
911
|
event.stopPropagation();
|
|
908
912
|
this.restoreTabOrder();
|
|
@@ -1075,18 +1079,37 @@ class DropdownCore extends React.Component {
|
|
|
1075
1079
|
}
|
|
1076
1080
|
focusCurrentItem(onFocus) {
|
|
1077
1081
|
const focusedItemRef = this.state.itemRefs[this.focusedIndex];
|
|
1078
|
-
if (focusedItemRef) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1082
|
+
if (!focusedItemRef) {
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const {
|
|
1086
|
+
current: virtualizedList
|
|
1087
|
+
} = this.virtualizedListRef;
|
|
1088
|
+
if (virtualizedList) {
|
|
1089
|
+
virtualizedList.scrollToItem(focusedItemRef.originalIndex);
|
|
1090
|
+
}
|
|
1091
|
+
const focusNode = () => {
|
|
1092
|
+
if (!this.props.open) {
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
const currentFocusedItemRef = this.state.itemRefs[this.focusedIndex];
|
|
1096
|
+
const node = ReactDOM.findDOMNode(currentFocusedItemRef.ref.current);
|
|
1097
|
+
if (!node && this.shouldVirtualizeList()) {
|
|
1098
|
+
this.props.schedule.animationFrame(focusNode);
|
|
1099
|
+
return;
|
|
1081
1100
|
}
|
|
1082
|
-
const node = ReactDOM.findDOMNode(focusedItemRef.ref.current);
|
|
1083
1101
|
if (node) {
|
|
1084
1102
|
node.focus();
|
|
1085
|
-
this.focusedOriginalIndex =
|
|
1103
|
+
this.focusedOriginalIndex = currentFocusedItemRef.originalIndex;
|
|
1086
1104
|
if (onFocus) {
|
|
1087
1105
|
onFocus(node);
|
|
1088
1106
|
}
|
|
1089
1107
|
}
|
|
1108
|
+
};
|
|
1109
|
+
if (this.shouldVirtualizeList()) {
|
|
1110
|
+
this.props.schedule.animationFrame(focusNode);
|
|
1111
|
+
} else {
|
|
1112
|
+
focusNode();
|
|
1090
1113
|
}
|
|
1091
1114
|
}
|
|
1092
1115
|
focusSearchField() {
|
|
@@ -1106,7 +1129,7 @@ class DropdownCore extends React.Component {
|
|
|
1106
1129
|
return this.focusSearchField();
|
|
1107
1130
|
}
|
|
1108
1131
|
this.focusedIndex = this.state.itemRefs.length - 1;
|
|
1109
|
-
} else {
|
|
1132
|
+
} else if (!this.isSearchFieldFocused()) {
|
|
1110
1133
|
this.focusedIndex -= 1;
|
|
1111
1134
|
}
|
|
1112
1135
|
this.scheduleToFocusCurrentItem();
|
|
@@ -1117,7 +1140,7 @@ class DropdownCore extends React.Component {
|
|
|
1117
1140
|
return this.focusSearchField();
|
|
1118
1141
|
}
|
|
1119
1142
|
this.focusedIndex = 0;
|
|
1120
|
-
} else {
|
|
1143
|
+
} else if (!this.isSearchFieldFocused()) {
|
|
1121
1144
|
this.focusedIndex += 1;
|
|
1122
1145
|
}
|
|
1123
1146
|
this.scheduleToFocusCurrentItem();
|
|
@@ -1199,7 +1222,7 @@ class DropdownCore extends React.Component {
|
|
|
1199
1222
|
const focusIndex = focusCounter - 1;
|
|
1200
1223
|
return _extends({}, item, {
|
|
1201
1224
|
role: populatedProps.role || itemRole,
|
|
1202
|
-
ref: item.focusable
|
|
1225
|
+
ref: item.focusable && this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null,
|
|
1203
1226
|
onClick: () => {
|
|
1204
1227
|
this.handleItemClick(focusIndex, item);
|
|
1205
1228
|
}
|
|
@@ -1660,7 +1683,7 @@ const styles$5 = StyleSheet.create({
|
|
|
1660
1683
|
}
|
|
1661
1684
|
});
|
|
1662
1685
|
|
|
1663
|
-
const _excluded$2 = ["children", "disabled", "error", "id", "isPlaceholder", "light", "open", "testId", "onOpenChanged"];
|
|
1686
|
+
const _excluded$2 = ["children", "disabled", "error", "id", "isPlaceholder", "light", "open", "testId", "aria-required", "onBlur", "onOpenChanged"];
|
|
1664
1687
|
const StyledButton = addStyle("button");
|
|
1665
1688
|
class SelectOpener extends React.Component {
|
|
1666
1689
|
constructor(props) {
|
|
@@ -1703,7 +1726,9 @@ class SelectOpener extends React.Component {
|
|
|
1703
1726
|
isPlaceholder,
|
|
1704
1727
|
light,
|
|
1705
1728
|
open,
|
|
1706
|
-
testId
|
|
1729
|
+
testId,
|
|
1730
|
+
"aria-required": ariaRequired,
|
|
1731
|
+
onBlur
|
|
1707
1732
|
} = _this$props,
|
|
1708
1733
|
sharedProps = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
|
|
1709
1734
|
const stateStyles = _generateStyles(light, isPlaceholder, error);
|
|
@@ -1712,6 +1737,8 @@ class SelectOpener extends React.Component {
|
|
|
1712
1737
|
return React.createElement(StyledButton, _extends({}, sharedProps, {
|
|
1713
1738
|
"aria-disabled": disabled,
|
|
1714
1739
|
"aria-expanded": open ? "true" : "false",
|
|
1740
|
+
"aria-invalid": error,
|
|
1741
|
+
"aria-required": ariaRequired,
|
|
1715
1742
|
"aria-haspopup": "listbox",
|
|
1716
1743
|
"data-testid": testId,
|
|
1717
1744
|
id: id,
|
|
@@ -1719,7 +1746,8 @@ class SelectOpener extends React.Component {
|
|
|
1719
1746
|
type: "button",
|
|
1720
1747
|
onClick: !disabled ? this.handleClick : undefined,
|
|
1721
1748
|
onKeyDown: !disabled ? this.handleKeyDown : undefined,
|
|
1722
|
-
onKeyUp: !disabled ? this.handleKeyUp : undefined
|
|
1749
|
+
onKeyUp: !disabled ? this.handleKeyUp : undefined,
|
|
1750
|
+
onBlur: onBlur
|
|
1723
1751
|
}), React.createElement(LabelMedium, {
|
|
1724
1752
|
style: styles$4.text
|
|
1725
1753
|
}, children || "\u00A0"), React.createElement(PhosphorIcon, {
|
|
@@ -1868,117 +1896,206 @@ const _generateStyles = (light, placeholder, error) => {
|
|
|
1868
1896
|
return stateStyles[styleKey];
|
|
1869
1897
|
};
|
|
1870
1898
|
|
|
1871
|
-
const
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1899
|
+
const defaultErrorMessage = "This field is required.";
|
|
1900
|
+
function hasValue(value) {
|
|
1901
|
+
return value ? value.length > 0 : false;
|
|
1902
|
+
}
|
|
1903
|
+
function useSelectValidation({
|
|
1904
|
+
value,
|
|
1905
|
+
disabled = false,
|
|
1906
|
+
validate,
|
|
1907
|
+
onValidate,
|
|
1908
|
+
required,
|
|
1909
|
+
open
|
|
1910
|
+
}) {
|
|
1911
|
+
const [errorMessage, setErrorMessage] = React.useState(() => validate && hasValue(value) && !disabled && validate(value) || null);
|
|
1912
|
+
const handleValidation = React.useCallback(newValue => {
|
|
1913
|
+
if (disabled) {
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
if (validate) {
|
|
1917
|
+
const error = newValue !== undefined && validate(newValue) || null;
|
|
1918
|
+
setErrorMessage(error);
|
|
1919
|
+
if (onValidate) {
|
|
1920
|
+
onValidate(error);
|
|
1883
1921
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
if (selectedValue !== this.props.selectedValue) {
|
|
1887
|
-
this.props.onChange(selectedValue);
|
|
1922
|
+
if (error) {
|
|
1923
|
+
return;
|
|
1888
1924
|
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1925
|
+
}
|
|
1926
|
+
if (required) {
|
|
1927
|
+
const requiredString = typeof required === "string" ? required : defaultErrorMessage;
|
|
1928
|
+
const error = hasValue(newValue) ? null : requiredString;
|
|
1929
|
+
setErrorMessage(error);
|
|
1930
|
+
if (onValidate) {
|
|
1931
|
+
onValidate(error);
|
|
1891
1932
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1933
|
+
}
|
|
1934
|
+
}, [disabled, validate, setErrorMessage, onValidate, required]);
|
|
1935
|
+
useOnMountEffect(() => {
|
|
1936
|
+
if (hasValue(value)) {
|
|
1937
|
+
handleValidation(value);
|
|
1938
|
+
}
|
|
1939
|
+
});
|
|
1940
|
+
function onOpenerBlurValidation() {
|
|
1941
|
+
if (!open && required && !hasValue(value)) {
|
|
1942
|
+
handleValidation(value);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
const onDropdownClosedValidation = () => {
|
|
1946
|
+
if (required && !hasValue(value)) {
|
|
1947
|
+
handleValidation(value);
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1950
|
+
const onSelectionValidation = newValue => {
|
|
1951
|
+
handleValidation(newValue);
|
|
1952
|
+
};
|
|
1953
|
+
const onSelectedValuesChangeValidation = () => {
|
|
1954
|
+
setErrorMessage(null);
|
|
1955
|
+
if (onValidate) {
|
|
1956
|
+
onValidate(null);
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
return {
|
|
1960
|
+
errorMessage,
|
|
1961
|
+
onOpenerBlurValidation,
|
|
1962
|
+
onDropdownClosedValidation,
|
|
1963
|
+
onSelectionValidation,
|
|
1964
|
+
onSelectedValuesChangeValidation
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
const _excluded$1 = ["children", "error", "id", "opener", "light", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required", "disabled", "dropdownId", "validate", "onValidate", "required", "showOpenerLabelAsText"];
|
|
1969
|
+
const SingleSelect = props => {
|
|
1970
|
+
const selectedIndex = React.useRef(0);
|
|
1971
|
+
const {
|
|
1972
|
+
children,
|
|
1973
|
+
error = false,
|
|
1974
|
+
id,
|
|
1975
|
+
opener,
|
|
1976
|
+
light = false,
|
|
1977
|
+
placeholder,
|
|
1978
|
+
selectedValue,
|
|
1979
|
+
testId,
|
|
1980
|
+
alignment = "left",
|
|
1981
|
+
autoFocus = true,
|
|
1982
|
+
dropdownStyle,
|
|
1983
|
+
enableTypeAhead = true,
|
|
1984
|
+
isFilterable,
|
|
1985
|
+
labels = {
|
|
1986
|
+
clearSearch: defaultLabels.clearSearch,
|
|
1987
|
+
filter: defaultLabels.filter,
|
|
1988
|
+
noResults: defaultLabels.noResults,
|
|
1989
|
+
someResults: defaultLabels.someSelected
|
|
1990
|
+
},
|
|
1991
|
+
onChange,
|
|
1992
|
+
onToggle,
|
|
1993
|
+
opened,
|
|
1994
|
+
style,
|
|
1995
|
+
className,
|
|
1996
|
+
"aria-invalid": ariaInvalid,
|
|
1997
|
+
"aria-required": ariaRequired,
|
|
1998
|
+
disabled = false,
|
|
1999
|
+
dropdownId,
|
|
2000
|
+
validate,
|
|
2001
|
+
onValidate,
|
|
2002
|
+
required,
|
|
2003
|
+
showOpenerLabelAsText = true
|
|
2004
|
+
} = props,
|
|
2005
|
+
sharedProps = _objectWithoutPropertiesLoose(props, _excluded$1);
|
|
2006
|
+
const [open, setOpen] = React.useState(false);
|
|
2007
|
+
const [searchText, setSearchText] = React.useState("");
|
|
2008
|
+
const [openerElement, setOpenerElement] = React.useState();
|
|
2009
|
+
const {
|
|
2010
|
+
errorMessage,
|
|
2011
|
+
onOpenerBlurValidation,
|
|
2012
|
+
onDropdownClosedValidation,
|
|
2013
|
+
onSelectionValidation
|
|
2014
|
+
} = useSelectValidation({
|
|
2015
|
+
value: selectedValue,
|
|
2016
|
+
disabled,
|
|
2017
|
+
validate,
|
|
2018
|
+
onValidate,
|
|
2019
|
+
required,
|
|
2020
|
+
open
|
|
2021
|
+
});
|
|
2022
|
+
const hasError = error || !!errorMessage;
|
|
2023
|
+
React.useEffect(() => {
|
|
2024
|
+
if (disabled) {
|
|
2025
|
+
setOpen(false);
|
|
2026
|
+
} else if (typeof opened === "boolean") {
|
|
2027
|
+
setOpen(opened);
|
|
2028
|
+
}
|
|
2029
|
+
}, [disabled, opened]);
|
|
2030
|
+
const handleOpenChanged = opened => {
|
|
2031
|
+
setOpen(opened);
|
|
2032
|
+
setSearchText("");
|
|
2033
|
+
if (onToggle) {
|
|
2034
|
+
onToggle(opened);
|
|
2035
|
+
}
|
|
2036
|
+
if (!opened) {
|
|
2037
|
+
onDropdownClosedValidation();
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
const handleToggle = newSelectedValue => {
|
|
2041
|
+
if (newSelectedValue !== selectedValue) {
|
|
2042
|
+
onChange(newSelectedValue);
|
|
2043
|
+
}
|
|
2044
|
+
if (open && openerElement) {
|
|
2045
|
+
openerElement.focus();
|
|
2046
|
+
}
|
|
2047
|
+
setOpen(false);
|
|
2048
|
+
if (onToggle) {
|
|
2049
|
+
onToggle(false);
|
|
2050
|
+
}
|
|
2051
|
+
onSelectionValidation(newSelectedValue);
|
|
2052
|
+
};
|
|
2053
|
+
const mapOptionItemsToDropdownItems = children => {
|
|
2054
|
+
let indexCounter = 0;
|
|
2055
|
+
selectedIndex.current = 0;
|
|
2056
|
+
return children.map(option => {
|
|
2057
|
+
const {
|
|
2058
|
+
disabled,
|
|
2059
|
+
value
|
|
2060
|
+
} = option.props;
|
|
2061
|
+
const selected = selectedValue === value;
|
|
2062
|
+
if (selected) {
|
|
2063
|
+
selectedIndex.current = indexCounter;
|
|
1897
2064
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
value
|
|
1909
|
-
} = option.props;
|
|
1910
|
-
const selected = selectedValue === value;
|
|
1911
|
-
if (selected) {
|
|
1912
|
-
this.selectedIndex = indexCounter;
|
|
1913
|
-
}
|
|
1914
|
-
if (!disabled) {
|
|
1915
|
-
indexCounter += 1;
|
|
2065
|
+
if (!disabled) {
|
|
2066
|
+
indexCounter += 1;
|
|
2067
|
+
}
|
|
2068
|
+
return {
|
|
2069
|
+
component: option,
|
|
2070
|
+
focusable: !disabled,
|
|
2071
|
+
populatedProps: {
|
|
2072
|
+
onToggle: handleToggle,
|
|
2073
|
+
selected: selected,
|
|
2074
|
+
variant: "check"
|
|
1916
2075
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
onToggle: this.handleToggle,
|
|
1922
|
-
selected: selected,
|
|
1923
|
-
variant: "check"
|
|
1924
|
-
}
|
|
1925
|
-
};
|
|
1926
|
-
});
|
|
1927
|
-
};
|
|
1928
|
-
this.handleSearchTextChanged = searchText => {
|
|
1929
|
-
this.setState({
|
|
1930
|
-
searchText
|
|
1931
|
-
});
|
|
1932
|
-
};
|
|
1933
|
-
this.handleOpenerRef = node => {
|
|
1934
|
-
const openerElement = ReactDOM.findDOMNode(node);
|
|
1935
|
-
this.setState({
|
|
1936
|
-
openerElement
|
|
1937
|
-
});
|
|
1938
|
-
};
|
|
1939
|
-
this.handleClick = e => {
|
|
1940
|
-
this.handleOpenChanged(!this.state.open);
|
|
1941
|
-
};
|
|
1942
|
-
this.selectedIndex = 0;
|
|
1943
|
-
this.state = {
|
|
1944
|
-
open: false,
|
|
1945
|
-
searchText: ""
|
|
1946
|
-
};
|
|
1947
|
-
}
|
|
1948
|
-
static getDerivedStateFromProps(props, state) {
|
|
1949
|
-
return {
|
|
1950
|
-
open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
|
|
1951
|
-
};
|
|
1952
|
-
}
|
|
1953
|
-
filterChildren(children) {
|
|
1954
|
-
const {
|
|
1955
|
-
searchText
|
|
1956
|
-
} = this.state;
|
|
2076
|
+
};
|
|
2077
|
+
});
|
|
2078
|
+
};
|
|
2079
|
+
const filterChildren = children => {
|
|
1957
2080
|
const lowercasedSearchText = searchText.toLowerCase();
|
|
1958
2081
|
return children.filter(({
|
|
1959
2082
|
props
|
|
1960
2083
|
}) => !searchText || getLabel(props).toLowerCase().indexOf(lowercasedSearchText) > -1);
|
|
1961
|
-
}
|
|
1962
|
-
getMenuItems
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
const
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
placeholder,
|
|
1977
|
-
selectedValue,
|
|
1978
|
-
testId,
|
|
1979
|
-
showOpenerLabelAsText
|
|
1980
|
-
} = _this$props,
|
|
1981
|
-
sharedProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
|
|
2084
|
+
};
|
|
2085
|
+
const getMenuItems = children => {
|
|
2086
|
+
return mapOptionItemsToDropdownItems(isFilterable ? filterChildren(children) : children);
|
|
2087
|
+
};
|
|
2088
|
+
const handleSearchTextChanged = searchText => {
|
|
2089
|
+
setSearchText(searchText);
|
|
2090
|
+
};
|
|
2091
|
+
const handleOpenerRef = node => {
|
|
2092
|
+
const openerElement = ReactDOM.findDOMNode(node);
|
|
2093
|
+
setOpenerElement(openerElement);
|
|
2094
|
+
};
|
|
2095
|
+
const handleClick = e => {
|
|
2096
|
+
handleOpenChanged(!open);
|
|
2097
|
+
};
|
|
2098
|
+
const renderOpener = (isDisabled, dropdownId) => {
|
|
1982
2099
|
const items = React.Children.toArray(children);
|
|
1983
2100
|
const selectedItem = items.find(option => option.props.value === selectedValue);
|
|
1984
2101
|
const menuText = selectedItem ? getSelectOpenerLabel(showOpenerLabelAsText, selectedItem.props) : placeholder;
|
|
@@ -1990,202 +2107,160 @@ class SingleSelect extends React.Component {
|
|
|
1990
2107
|
id: uniqueOpenerId,
|
|
1991
2108
|
"aria-controls": dropdownId,
|
|
1992
2109
|
"aria-haspopup": "listbox",
|
|
1993
|
-
onClick:
|
|
2110
|
+
onClick: handleClick,
|
|
1994
2111
|
disabled: isDisabled,
|
|
1995
|
-
ref:
|
|
2112
|
+
ref: handleOpenerRef,
|
|
1996
2113
|
text: menuText,
|
|
1997
|
-
opened:
|
|
2114
|
+
opened: open,
|
|
2115
|
+
error: hasError,
|
|
2116
|
+
onBlur: onOpenerBlurValidation
|
|
1998
2117
|
}, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
|
|
1999
2118
|
"aria-controls": dropdownId,
|
|
2000
2119
|
disabled: isDisabled,
|
|
2001
2120
|
id: uniqueOpenerId,
|
|
2002
|
-
error:
|
|
2121
|
+
error: hasError,
|
|
2003
2122
|
isPlaceholder: !selectedItem,
|
|
2004
2123
|
light: light,
|
|
2005
|
-
onOpenChanged:
|
|
2006
|
-
open:
|
|
2007
|
-
ref:
|
|
2008
|
-
testId: testId
|
|
2124
|
+
onOpenChanged: handleOpenChanged,
|
|
2125
|
+
open: open,
|
|
2126
|
+
ref: handleOpenerRef,
|
|
2127
|
+
testId: testId,
|
|
2128
|
+
onBlur: onOpenerBlurValidation
|
|
2009
2129
|
}), menuText);
|
|
2010
2130
|
});
|
|
2011
2131
|
return dropdownOpener;
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2132
|
+
};
|
|
2133
|
+
const allChildren = React.Children.toArray(children).filter(Boolean);
|
|
2134
|
+
const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
|
|
2135
|
+
const items = getMenuItems(allChildren);
|
|
2136
|
+
const isDisabled = numEnabledOptions === 0 || disabled;
|
|
2137
|
+
return React.createElement(IDProvider, {
|
|
2138
|
+
id: dropdownId,
|
|
2139
|
+
scope: "single-select-dropdown"
|
|
2140
|
+
}, uniqueDropdownId => React.createElement(DropdownCore$1, {
|
|
2141
|
+
id: uniqueDropdownId,
|
|
2142
|
+
role: "listbox",
|
|
2143
|
+
selectionType: "single",
|
|
2144
|
+
alignment: alignment,
|
|
2145
|
+
autoFocus: autoFocus,
|
|
2146
|
+
enableTypeAhead: enableTypeAhead,
|
|
2147
|
+
dropdownStyle: [isFilterable && filterableDropdownStyle, selectDropdownStyle, dropdownStyle],
|
|
2148
|
+
initialFocusedIndex: selectedIndex.current,
|
|
2149
|
+
items: items,
|
|
2150
|
+
light: light,
|
|
2151
|
+
onOpenChanged: handleOpenChanged,
|
|
2152
|
+
open: open,
|
|
2153
|
+
opener: renderOpener(isDisabled, uniqueDropdownId),
|
|
2154
|
+
openerElement: openerElement,
|
|
2155
|
+
style: style,
|
|
2156
|
+
className: className,
|
|
2157
|
+
isFilterable: isFilterable,
|
|
2158
|
+
onSearchTextChanged: isFilterable ? handleSearchTextChanged : undefined,
|
|
2159
|
+
searchText: isFilterable ? searchText : "",
|
|
2160
|
+
labels: labels,
|
|
2161
|
+
"aria-invalid": ariaInvalid,
|
|
2162
|
+
"aria-required": ariaRequired,
|
|
2163
|
+
disabled: isDisabled
|
|
2164
|
+
}));
|
|
2165
|
+
};
|
|
2166
|
+
|
|
2167
|
+
const _excluded = ["id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required", "disabled", "error", "children", "dropdownId", "showOpenerLabelAsText", "validate", "onValidate", "required"];
|
|
2168
|
+
const MultiSelect = props => {
|
|
2169
|
+
const {
|
|
2170
|
+
id,
|
|
2171
|
+
light = false,
|
|
2172
|
+
opener,
|
|
2173
|
+
testId,
|
|
2174
|
+
alignment = "left",
|
|
2019
2175
|
dropdownStyle,
|
|
2020
|
-
|
|
2176
|
+
implicitAllEnabled,
|
|
2021
2177
|
isFilterable,
|
|
2022
|
-
labels,
|
|
2023
|
-
|
|
2178
|
+
labels: propLabels,
|
|
2179
|
+
onChange,
|
|
2180
|
+
onToggle,
|
|
2181
|
+
opened,
|
|
2182
|
+
selectedValues = [],
|
|
2183
|
+
shortcuts = false,
|
|
2024
2184
|
style,
|
|
2185
|
+
className,
|
|
2025
2186
|
"aria-invalid": ariaInvalid,
|
|
2026
2187
|
"aria-required": ariaRequired,
|
|
2027
|
-
disabled,
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
labels: {
|
|
2075
|
-
clearSearch: defaultLabels.clearSearch,
|
|
2076
|
-
filter: defaultLabels.filter,
|
|
2077
|
-
noResults: defaultLabels.noResults,
|
|
2078
|
-
someResults: defaultLabels.someSelected
|
|
2079
|
-
},
|
|
2080
|
-
showOpenerLabelAsText: true
|
|
2081
|
-
};
|
|
2082
|
-
|
|
2083
|
-
const _excluded = ["id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required", "showOpenerLabelAsText"];
|
|
2084
|
-
class MultiSelect extends React.Component {
|
|
2085
|
-
constructor(props) {
|
|
2086
|
-
super(props);
|
|
2087
|
-
this.labels = void 0;
|
|
2088
|
-
this.handleOpenChanged = opened => {
|
|
2089
|
-
this.setState({
|
|
2090
|
-
open: opened,
|
|
2091
|
-
searchText: "",
|
|
2092
|
-
lastSelectedValues: this.props.selectedValues
|
|
2093
|
-
});
|
|
2094
|
-
if (this.props.onToggle) {
|
|
2095
|
-
this.props.onToggle(opened);
|
|
2096
|
-
}
|
|
2097
|
-
};
|
|
2098
|
-
this.handleToggle = selectedValue => {
|
|
2099
|
-
const {
|
|
2100
|
-
onChange,
|
|
2101
|
-
selectedValues
|
|
2102
|
-
} = this.props;
|
|
2103
|
-
if (selectedValues.includes(selectedValue)) {
|
|
2104
|
-
const index = selectedValues.indexOf(selectedValue);
|
|
2105
|
-
const updatedSelection = [...selectedValues.slice(0, index), ...selectedValues.slice(index + 1)];
|
|
2106
|
-
onChange(updatedSelection);
|
|
2188
|
+
disabled = false,
|
|
2189
|
+
error = false,
|
|
2190
|
+
children,
|
|
2191
|
+
dropdownId,
|
|
2192
|
+
showOpenerLabelAsText = true,
|
|
2193
|
+
validate,
|
|
2194
|
+
onValidate,
|
|
2195
|
+
required
|
|
2196
|
+
} = props,
|
|
2197
|
+
sharedProps = _objectWithoutPropertiesLoose(props, _excluded);
|
|
2198
|
+
const labels = _extends({}, defaultLabels, propLabels);
|
|
2199
|
+
const [open, setOpen] = React.useState(false);
|
|
2200
|
+
const [searchText, setSearchText] = React.useState("");
|
|
2201
|
+
const [lastSelectedValues, setLastSelectedValues] = React.useState([]);
|
|
2202
|
+
const [openerElement, setOpenerElement] = React.useState();
|
|
2203
|
+
const {
|
|
2204
|
+
errorMessage,
|
|
2205
|
+
onOpenerBlurValidation,
|
|
2206
|
+
onDropdownClosedValidation,
|
|
2207
|
+
onSelectionValidation,
|
|
2208
|
+
onSelectedValuesChangeValidation
|
|
2209
|
+
} = useSelectValidation({
|
|
2210
|
+
value: selectedValues,
|
|
2211
|
+
disabled,
|
|
2212
|
+
validate,
|
|
2213
|
+
onValidate,
|
|
2214
|
+
required,
|
|
2215
|
+
open
|
|
2216
|
+
});
|
|
2217
|
+
const hasError = error || !!errorMessage;
|
|
2218
|
+
React.useEffect(() => {
|
|
2219
|
+
if (disabled) {
|
|
2220
|
+
setOpen(false);
|
|
2221
|
+
} else if (typeof opened === "boolean") {
|
|
2222
|
+
setOpen(opened);
|
|
2223
|
+
}
|
|
2224
|
+
}, [disabled, opened]);
|
|
2225
|
+
const handleOpenChanged = opened => {
|
|
2226
|
+
setOpen(opened);
|
|
2227
|
+
setSearchText("");
|
|
2228
|
+
setLastSelectedValues(selectedValues);
|
|
2229
|
+
if (onToggle) {
|
|
2230
|
+
onToggle(opened);
|
|
2231
|
+
}
|
|
2232
|
+
if (!opened) {
|
|
2233
|
+
if (lastSelectedValues !== selectedValues) {
|
|
2234
|
+
onSelectionValidation(selectedValues);
|
|
2107
2235
|
} else {
|
|
2108
|
-
|
|
2236
|
+
onDropdownClosedValidation();
|
|
2109
2237
|
}
|
|
2110
|
-
};
|
|
2111
|
-
this.handleSelectAll = () => {
|
|
2112
|
-
const {
|
|
2113
|
-
children,
|
|
2114
|
-
onChange
|
|
2115
|
-
} = this.props;
|
|
2116
|
-
const allChildren = React.Children.toArray(children);
|
|
2117
|
-
const selected = allChildren.filter(option => !!option && !option.props.disabled).map(option => option.props.value);
|
|
2118
|
-
onChange(selected);
|
|
2119
|
-
};
|
|
2120
|
-
this.handleSelectNone = () => {
|
|
2121
|
-
const {
|
|
2122
|
-
onChange
|
|
2123
|
-
} = this.props;
|
|
2124
|
-
onChange([]);
|
|
2125
|
-
};
|
|
2126
|
-
this.mapOptionItemToDropdownItem = option => {
|
|
2127
|
-
const {
|
|
2128
|
-
selectedValues
|
|
2129
|
-
} = this.props;
|
|
2130
|
-
const {
|
|
2131
|
-
disabled,
|
|
2132
|
-
value
|
|
2133
|
-
} = option.props;
|
|
2134
|
-
return {
|
|
2135
|
-
component: option,
|
|
2136
|
-
focusable: !disabled,
|
|
2137
|
-
populatedProps: {
|
|
2138
|
-
onToggle: this.handleToggle,
|
|
2139
|
-
selected: selectedValues.includes(value),
|
|
2140
|
-
variant: "checkbox"
|
|
2141
|
-
}
|
|
2142
|
-
};
|
|
2143
|
-
};
|
|
2144
|
-
this.handleOpenerRef = node => {
|
|
2145
|
-
const openerElement = ReactDOM.findDOMNode(node);
|
|
2146
|
-
this.setState({
|
|
2147
|
-
openerElement
|
|
2148
|
-
});
|
|
2149
|
-
};
|
|
2150
|
-
this.handleSearchTextChanged = searchText => {
|
|
2151
|
-
this.setState({
|
|
2152
|
-
searchText
|
|
2153
|
-
});
|
|
2154
|
-
};
|
|
2155
|
-
this.handleClick = e => {
|
|
2156
|
-
this.handleOpenChanged(!this.state.open);
|
|
2157
|
-
};
|
|
2158
|
-
this.state = {
|
|
2159
|
-
open: false,
|
|
2160
|
-
searchText: "",
|
|
2161
|
-
lastSelectedValues: [],
|
|
2162
|
-
labels: _extends({}, defaultLabels, props.labels)
|
|
2163
|
-
};
|
|
2164
|
-
this.labels = _extends({}, defaultLabels, props.labels);
|
|
2165
|
-
}
|
|
2166
|
-
static getDerivedStateFromProps(props, state) {
|
|
2167
|
-
return {
|
|
2168
|
-
open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
|
|
2169
|
-
};
|
|
2170
|
-
}
|
|
2171
|
-
componentDidUpdate(prevProps) {
|
|
2172
|
-
if (this.props.labels !== prevProps.labels) {
|
|
2173
|
-
this.setState({
|
|
2174
|
-
labels: _extends({}, this.state.labels, this.props.labels)
|
|
2175
|
-
});
|
|
2176
2238
|
}
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
selectedValues,
|
|
2182
|
-
|
|
2183
|
-
}
|
|
2239
|
+
};
|
|
2240
|
+
const handleToggle = selectedValue => {
|
|
2241
|
+
if (selectedValues.includes(selectedValue)) {
|
|
2242
|
+
const index = selectedValues.indexOf(selectedValue);
|
|
2243
|
+
const updatedSelection = [...selectedValues.slice(0, index), ...selectedValues.slice(index + 1)];
|
|
2244
|
+
onChange(updatedSelection);
|
|
2245
|
+
} else {
|
|
2246
|
+
onChange([...selectedValues, selectedValue]);
|
|
2247
|
+
}
|
|
2248
|
+
onSelectedValuesChangeValidation();
|
|
2249
|
+
};
|
|
2250
|
+
const handleSelectAll = () => {
|
|
2251
|
+
const allChildren = React.Children.toArray(children);
|
|
2252
|
+
const selected = allChildren.filter(option => !!option && !option.props.disabled).map(option => option.props.value);
|
|
2253
|
+
onChange(selected);
|
|
2254
|
+
};
|
|
2255
|
+
const handleSelectNone = () => {
|
|
2256
|
+
onChange([]);
|
|
2257
|
+
};
|
|
2258
|
+
const getMenuText = children => {
|
|
2184
2259
|
const {
|
|
2185
2260
|
noneSelected,
|
|
2186
2261
|
someSelected,
|
|
2187
2262
|
allSelected
|
|
2188
|
-
} =
|
|
2263
|
+
} = labels;
|
|
2189
2264
|
const numSelectedAll = children.filter(option => !option.props.disabled).length;
|
|
2190
2265
|
const noSelectionText = implicitAllEnabled ? allSelected : noneSelected;
|
|
2191
2266
|
switch (selectedValues.length) {
|
|
@@ -2207,24 +2282,20 @@ class MultiSelect extends React.Component {
|
|
|
2207
2282
|
default:
|
|
2208
2283
|
return someSelected(selectedValues.length);
|
|
2209
2284
|
}
|
|
2210
|
-
}
|
|
2211
|
-
getShortcuts
|
|
2212
|
-
const {
|
|
2213
|
-
selectedValues,
|
|
2214
|
-
shortcuts
|
|
2215
|
-
} = this.props;
|
|
2285
|
+
};
|
|
2286
|
+
const getShortcuts = numOptions => {
|
|
2216
2287
|
const {
|
|
2217
2288
|
selectAllLabel,
|
|
2218
2289
|
selectNoneLabel
|
|
2219
|
-
} =
|
|
2220
|
-
if (shortcuts && !
|
|
2290
|
+
} = labels;
|
|
2291
|
+
if (shortcuts && !searchText) {
|
|
2221
2292
|
const selectAllDisabled = numOptions === selectedValues.length;
|
|
2222
2293
|
const selectAll = {
|
|
2223
2294
|
component: React.createElement(ActionItem, {
|
|
2224
2295
|
disabled: selectAllDisabled,
|
|
2225
2296
|
label: selectAllLabel(numOptions),
|
|
2226
2297
|
indent: true,
|
|
2227
|
-
onClick:
|
|
2298
|
+
onClick: handleSelectAll
|
|
2228
2299
|
}),
|
|
2229
2300
|
focusable: !selectAllDisabled,
|
|
2230
2301
|
populatedProps: {}
|
|
@@ -2235,7 +2306,7 @@ class MultiSelect extends React.Component {
|
|
|
2235
2306
|
disabled: selectNoneDisabled,
|
|
2236
2307
|
label: selectNoneLabel,
|
|
2237
2308
|
indent: true,
|
|
2238
|
-
onClick:
|
|
2309
|
+
onClick: handleSelectNone
|
|
2239
2310
|
}),
|
|
2240
2311
|
focusable: !selectNoneDisabled,
|
|
2241
2312
|
populatedProps: {}
|
|
@@ -2251,18 +2322,11 @@ class MultiSelect extends React.Component {
|
|
|
2251
2322
|
} else {
|
|
2252
2323
|
return [];
|
|
2253
2324
|
}
|
|
2254
|
-
}
|
|
2255
|
-
getMenuItems
|
|
2256
|
-
const {
|
|
2257
|
-
isFilterable
|
|
2258
|
-
} = this.props;
|
|
2325
|
+
};
|
|
2326
|
+
const getMenuItems = children => {
|
|
2259
2327
|
if (!isFilterable) {
|
|
2260
|
-
return children.map(
|
|
2328
|
+
return children.map(mapOptionItemToDropdownItem);
|
|
2261
2329
|
}
|
|
2262
|
-
const {
|
|
2263
|
-
searchText,
|
|
2264
|
-
lastSelectedValues
|
|
2265
|
-
} = this.state;
|
|
2266
2330
|
const lowercasedSearchText = searchText.toLowerCase();
|
|
2267
2331
|
const filteredChildren = children.filter(({
|
|
2268
2332
|
props
|
|
@@ -2276,7 +2340,7 @@ class MultiSelect extends React.Component {
|
|
|
2276
2340
|
restOfTheChildren.push(child);
|
|
2277
2341
|
}
|
|
2278
2342
|
}
|
|
2279
|
-
const lastSelectedItems = lastSelectedChildren.map(
|
|
2343
|
+
const lastSelectedItems = lastSelectedChildren.map(mapOptionItemToDropdownItem);
|
|
2280
2344
|
if (lastSelectedChildren.length && restOfTheChildren.length) {
|
|
2281
2345
|
lastSelectedItems.push({
|
|
2282
2346
|
component: React.createElement(SeparatorItem, {
|
|
@@ -2286,116 +2350,109 @@ class MultiSelect extends React.Component {
|
|
|
2286
2350
|
populatedProps: {}
|
|
2287
2351
|
});
|
|
2288
2352
|
}
|
|
2289
|
-
return [...lastSelectedItems, ...restOfTheChildren.map(
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
const
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2353
|
+
return [...lastSelectedItems, ...restOfTheChildren.map(mapOptionItemToDropdownItem)];
|
|
2354
|
+
};
|
|
2355
|
+
const mapOptionItemToDropdownItem = option => {
|
|
2356
|
+
const {
|
|
2357
|
+
disabled,
|
|
2358
|
+
value
|
|
2359
|
+
} = option.props;
|
|
2360
|
+
return {
|
|
2361
|
+
component: option,
|
|
2362
|
+
focusable: !disabled,
|
|
2363
|
+
populatedProps: {
|
|
2364
|
+
onToggle: handleToggle,
|
|
2365
|
+
selected: selectedValues.includes(value),
|
|
2366
|
+
variant: "checkbox"
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
};
|
|
2370
|
+
const handleOpenerRef = node => {
|
|
2371
|
+
const openerElement = ReactDOM.findDOMNode(node);
|
|
2372
|
+
setOpenerElement(openerElement);
|
|
2373
|
+
};
|
|
2374
|
+
const handleSearchTextChanged = searchText => {
|
|
2375
|
+
setSearchText(searchText);
|
|
2376
|
+
};
|
|
2377
|
+
const handleClick = e => {
|
|
2378
|
+
handleOpenChanged(!open);
|
|
2379
|
+
};
|
|
2380
|
+
const renderOpener = (allChildren, isDisabled, dropdownId) => {
|
|
2300
2381
|
const {
|
|
2301
2382
|
noneSelected
|
|
2302
|
-
} =
|
|
2303
|
-
const menuText =
|
|
2383
|
+
} = labels;
|
|
2384
|
+
const menuText = getMenuText(allChildren);
|
|
2304
2385
|
const dropdownOpener = React.createElement(IDProvider, {
|
|
2305
2386
|
id: id,
|
|
2306
2387
|
scope: "multi-select-opener"
|
|
2307
2388
|
}, uniqueOpenerId => {
|
|
2308
2389
|
return opener ? React.createElement(DropdownOpener, {
|
|
2309
2390
|
id: uniqueOpenerId,
|
|
2391
|
+
error: hasError,
|
|
2310
2392
|
"aria-controls": dropdownId,
|
|
2311
2393
|
"aria-haspopup": "listbox",
|
|
2312
|
-
onClick:
|
|
2394
|
+
onClick: handleClick,
|
|
2395
|
+
onBlur: onOpenerBlurValidation,
|
|
2313
2396
|
disabled: isDisabled,
|
|
2314
|
-
ref:
|
|
2397
|
+
ref: handleOpenerRef,
|
|
2315
2398
|
text: menuText,
|
|
2316
|
-
opened:
|
|
2399
|
+
opened: open
|
|
2317
2400
|
}, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
|
|
2401
|
+
error: hasError,
|
|
2318
2402
|
disabled: isDisabled,
|
|
2319
2403
|
id: uniqueOpenerId,
|
|
2320
2404
|
"aria-controls": dropdownId,
|
|
2321
2405
|
isPlaceholder: menuText === noneSelected,
|
|
2322
2406
|
light: light,
|
|
2323
|
-
onOpenChanged:
|
|
2324
|
-
|
|
2325
|
-
|
|
2407
|
+
onOpenChanged: handleOpenChanged,
|
|
2408
|
+
onBlur: onOpenerBlurValidation,
|
|
2409
|
+
open: open,
|
|
2410
|
+
ref: handleOpenerRef,
|
|
2326
2411
|
testId: testId
|
|
2327
2412
|
}), menuText);
|
|
2328
2413
|
});
|
|
2329
2414
|
return dropdownOpener;
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2415
|
+
};
|
|
2416
|
+
const {
|
|
2417
|
+
clearSearch,
|
|
2418
|
+
filter,
|
|
2419
|
+
noResults,
|
|
2420
|
+
someSelected
|
|
2421
|
+
} = labels;
|
|
2422
|
+
const allChildren = React.Children.toArray(children).filter(Boolean);
|
|
2423
|
+
const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
|
|
2424
|
+
const filteredItems = getMenuItems(allChildren);
|
|
2425
|
+
const isDisabled = numEnabledOptions === 0 || disabled;
|
|
2426
|
+
return React.createElement(IDProvider, {
|
|
2427
|
+
id: dropdownId,
|
|
2428
|
+
scope: "multi-select-dropdown"
|
|
2429
|
+
}, uniqueDropdownId => React.createElement(DropdownCore$1, {
|
|
2430
|
+
id: uniqueDropdownId,
|
|
2431
|
+
role: "listbox",
|
|
2432
|
+
alignment: alignment,
|
|
2433
|
+
dropdownStyle: [isFilterable && filterableDropdownStyle, selectDropdownStyle, dropdownStyle],
|
|
2434
|
+
isFilterable: isFilterable,
|
|
2435
|
+
items: [...getShortcuts(numEnabledOptions), ...filteredItems],
|
|
2436
|
+
light: light,
|
|
2437
|
+
onOpenChanged: handleOpenChanged,
|
|
2438
|
+
open: open,
|
|
2439
|
+
opener: renderOpener(allChildren, isDisabled, uniqueDropdownId),
|
|
2440
|
+
openerElement: openerElement,
|
|
2441
|
+
selectionType: "multi",
|
|
2442
|
+
style: style,
|
|
2443
|
+
className: className,
|
|
2444
|
+
onSearchTextChanged: isFilterable ? handleSearchTextChanged : undefined,
|
|
2445
|
+
searchText: isFilterable ? searchText : "",
|
|
2446
|
+
labels: {
|
|
2350
2447
|
clearSearch,
|
|
2351
2448
|
filter,
|
|
2352
2449
|
noResults,
|
|
2353
|
-
someSelected
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
return React.createElement(IDProvider, {
|
|
2360
|
-
id: dropdownId,
|
|
2361
|
-
scope: "multi-select-dropdown"
|
|
2362
|
-
}, uniqueDropdownId => React.createElement(DropdownCore$1, {
|
|
2363
|
-
id: uniqueDropdownId,
|
|
2364
|
-
role: "listbox",
|
|
2365
|
-
alignment: alignment,
|
|
2366
|
-
dropdownStyle: [isFilterable && filterableDropdownStyle, selectDropdownStyle, dropdownStyle],
|
|
2367
|
-
isFilterable: isFilterable,
|
|
2368
|
-
items: [...this.getShortcuts(numEnabledOptions), ...filteredItems],
|
|
2369
|
-
light: light,
|
|
2370
|
-
onOpenChanged: this.handleOpenChanged,
|
|
2371
|
-
open: open,
|
|
2372
|
-
opener: this.renderOpener(allChildren, isDisabled, uniqueDropdownId),
|
|
2373
|
-
openerElement: this.state.openerElement,
|
|
2374
|
-
selectionType: "multi",
|
|
2375
|
-
style: style,
|
|
2376
|
-
className: className,
|
|
2377
|
-
onSearchTextChanged: isFilterable ? this.handleSearchTextChanged : undefined,
|
|
2378
|
-
searchText: isFilterable ? searchText : "",
|
|
2379
|
-
labels: {
|
|
2380
|
-
clearSearch,
|
|
2381
|
-
filter,
|
|
2382
|
-
noResults,
|
|
2383
|
-
someResults: someSelected
|
|
2384
|
-
},
|
|
2385
|
-
"aria-invalid": ariaInvalid,
|
|
2386
|
-
"aria-required": ariaRequired,
|
|
2387
|
-
disabled: isDisabled
|
|
2388
|
-
}));
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
MultiSelect.defaultProps = {
|
|
2392
|
-
alignment: "left",
|
|
2393
|
-
disabled: false,
|
|
2394
|
-
error: false,
|
|
2395
|
-
light: false,
|
|
2396
|
-
shortcuts: false,
|
|
2397
|
-
selectedValues: [],
|
|
2398
|
-
showOpenerLabelAsText: true
|
|
2450
|
+
someResults: someSelected
|
|
2451
|
+
},
|
|
2452
|
+
"aria-invalid": ariaInvalid,
|
|
2453
|
+
"aria-required": ariaRequired,
|
|
2454
|
+
disabled: isDisabled
|
|
2455
|
+
}));
|
|
2399
2456
|
};
|
|
2400
2457
|
|
|
2401
2458
|
function updateMultipleSelection(previousSelection, value = "") {
|