@leafygreen-ui/combobox 1.0.0 → 1.0.3
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 +41 -0
- package/README.md +4 -2
- package/dist/Combobox.d.ts.map +1 -1
- package/dist/Combobox.styles.d.ts.map +1 -1
- package/dist/Combobox.types.d.ts.map +1 -1
- package/dist/ComboboxOption.d.ts.map +1 -1
- package/dist/ComboboxTestUtils.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/src/Chip.tsx +1 -1
- package/src/Combobox.spec.tsx +288 -137
- package/src/Combobox.story.tsx +11 -3
- package/src/Combobox.styles.ts +14 -24
- package/src/Combobox.tsx +34 -32
- package/src/Combobox.types.ts +3 -2
- package/src/ComboboxOption.tsx +2 -3
- package/tsconfig.tsbuildinfo +1 -3889
package/src/Combobox.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import React, {
|
|
|
5
5
|
useRef,
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
|
-
import { clone, isArray, isNull, isString, isUndefined } from 'lodash';
|
|
8
|
+
import { clone, isArray, isEqual, isNull, isString, isUndefined } from 'lodash';
|
|
9
9
|
import { Description, Label } from '@leafygreen-ui/typography';
|
|
10
10
|
import Popover from '@leafygreen-ui/popover';
|
|
11
11
|
import {
|
|
@@ -20,7 +20,7 @@ import Icon from '@leafygreen-ui/icon';
|
|
|
20
20
|
import IconButton from '@leafygreen-ui/icon-button';
|
|
21
21
|
import { cx } from '@leafygreen-ui/emotion';
|
|
22
22
|
import { uiColors } from '@leafygreen-ui/palette';
|
|
23
|
-
import { isComponentType } from '@leafygreen-ui/lib';
|
|
23
|
+
import { consoleOnce, isComponentType } from '@leafygreen-ui/lib';
|
|
24
24
|
import {
|
|
25
25
|
ComboboxProps,
|
|
26
26
|
getNullSelection,
|
|
@@ -114,8 +114,19 @@ export default function Combobox<M extends boolean>({
|
|
|
114
114
|
|
|
115
115
|
// Tells typescript that selection is multiselect
|
|
116
116
|
const isMultiselect = useCallback(
|
|
117
|
-
<T extends
|
|
118
|
-
multiselect &&
|
|
117
|
+
<T extends string>(val?: Array<T> | T | null): val is Array<T> => {
|
|
118
|
+
if (multiselect && (typeof val == 'string' || typeof val == 'number')) {
|
|
119
|
+
consoleOnce.error(
|
|
120
|
+
`Error in Combobox: multiselect is set to \`true\`, but recieved a ${typeof val} value: "${val}"`,
|
|
121
|
+
);
|
|
122
|
+
} else if (!multiselect && isArray(val)) {
|
|
123
|
+
consoleOnce.error(
|
|
124
|
+
'Error in Combobox: multiselect is set to `false`, but recieved an Array value',
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return multiselect && isArray(val);
|
|
129
|
+
},
|
|
119
130
|
[multiselect],
|
|
120
131
|
);
|
|
121
132
|
|
|
@@ -211,10 +222,10 @@ export default function Combobox<M extends boolean>({
|
|
|
211
222
|
[filteredOptions, getDisplayNameForValue, inputValue],
|
|
212
223
|
);
|
|
213
224
|
|
|
214
|
-
const visibleOptions = useMemo(
|
|
215
|
-
allOptions,
|
|
216
|
-
isOptionVisible,
|
|
217
|
-
|
|
225
|
+
const visibleOptions = useMemo(
|
|
226
|
+
() => allOptions.filter(isOptionVisible),
|
|
227
|
+
[allOptions, isOptionVisible],
|
|
228
|
+
);
|
|
218
229
|
|
|
219
230
|
const isValueValid = useCallback(
|
|
220
231
|
(value: string | null): boolean => {
|
|
@@ -497,10 +508,8 @@ export default function Combobox<M extends boolean>({
|
|
|
497
508
|
|
|
498
509
|
if (focusedElementRef && focusedElementRef.current && menuRef.current) {
|
|
499
510
|
const { offsetTop: optionTop } = focusedElementRef.current;
|
|
500
|
-
const {
|
|
501
|
-
|
|
502
|
-
offsetHeight: menuHeight,
|
|
503
|
-
} = menuRef.current;
|
|
511
|
+
const { scrollTop: menuScroll, offsetHeight: menuHeight } =
|
|
512
|
+
menuRef.current;
|
|
504
513
|
|
|
505
514
|
if (optionTop > menuHeight || optionTop < menuScroll) {
|
|
506
515
|
menuRef.current.scrollTop = optionTop;
|
|
@@ -583,10 +592,10 @@ export default function Combobox<M extends boolean>({
|
|
|
583
592
|
],
|
|
584
593
|
);
|
|
585
594
|
|
|
586
|
-
const renderedOptions = useMemo(
|
|
587
|
-
children,
|
|
588
|
-
renderInternalOptions,
|
|
589
|
-
|
|
595
|
+
const renderedOptions = useMemo(
|
|
596
|
+
() => renderInternalOptions(children),
|
|
597
|
+
[children, renderInternalOptions],
|
|
598
|
+
);
|
|
590
599
|
|
|
591
600
|
const renderedChips = useMemo(() => {
|
|
592
601
|
if (isMultiselect(selection)) {
|
|
@@ -676,9 +685,10 @@ export default function Combobox<M extends boolean>({
|
|
|
676
685
|
]);
|
|
677
686
|
|
|
678
687
|
// Do any of the options have an icon?
|
|
679
|
-
const withIcons = useMemo(
|
|
680
|
-
allOptions,
|
|
681
|
-
|
|
688
|
+
const withIcons = useMemo(
|
|
689
|
+
() => allOptions.some(opt => opt.hasGlyph),
|
|
690
|
+
[allOptions],
|
|
691
|
+
);
|
|
682
692
|
|
|
683
693
|
/**
|
|
684
694
|
*
|
|
@@ -770,7 +780,7 @@ export default function Combobox<M extends boolean>({
|
|
|
770
780
|
// onSelect
|
|
771
781
|
// Side effects to run when the selection changes
|
|
772
782
|
useEffect(() => {
|
|
773
|
-
if (selection
|
|
783
|
+
if (!isEqual(selection, prevSelection)) {
|
|
774
784
|
onSelect();
|
|
775
785
|
}
|
|
776
786
|
}, [onSelect, prevSelection, selection]);
|
|
@@ -848,10 +858,8 @@ export default function Combobox<M extends boolean>({
|
|
|
848
858
|
const menuMargin = 8;
|
|
849
859
|
|
|
850
860
|
if (viewportSize && comboboxRef.current && menuRef.current) {
|
|
851
|
-
const {
|
|
852
|
-
|
|
853
|
-
bottom: triggerBottom,
|
|
854
|
-
} = comboboxRef.current.getBoundingClientRect();
|
|
861
|
+
const { top: triggerTop, bottom: triggerBottom } =
|
|
862
|
+
comboboxRef.current.getBoundingClientRect();
|
|
855
863
|
|
|
856
864
|
// Find out how much space is available above or below the trigger
|
|
857
865
|
const safeSpace = Math.max(
|
|
@@ -880,7 +888,7 @@ export default function Combobox<M extends boolean>({
|
|
|
880
888
|
|
|
881
889
|
// Prevent combobox from gaining focus by default
|
|
882
890
|
const handleInputWrapperMousedown = (e: React.MouseEvent) => {
|
|
883
|
-
if (
|
|
891
|
+
if (disabled) {
|
|
884
892
|
e.preventDefault();
|
|
885
893
|
}
|
|
886
894
|
};
|
|
@@ -976,13 +984,7 @@ export default function Combobox<M extends boolean>({
|
|
|
976
984
|
break;
|
|
977
985
|
}
|
|
978
986
|
|
|
979
|
-
case keyMap.Enter:
|
|
980
|
-
case keyMap.Space: {
|
|
981
|
-
if (isOpen) {
|
|
982
|
-
// prevent typing the space character
|
|
983
|
-
event.preventDefault();
|
|
984
|
-
}
|
|
985
|
-
|
|
987
|
+
case keyMap.Enter: {
|
|
986
988
|
if (
|
|
987
989
|
// Focused on input element
|
|
988
990
|
document.activeElement === inputRef.current &&
|
package/src/Combobox.types.ts
CHANGED
|
@@ -16,7 +16,8 @@ export const TrunctationLocation = {
|
|
|
16
16
|
end: 'end',
|
|
17
17
|
none: 'none',
|
|
18
18
|
} as const;
|
|
19
|
-
export type TrunctationLocation =
|
|
19
|
+
export type TrunctationLocation =
|
|
20
|
+
typeof TrunctationLocation[keyof typeof TrunctationLocation];
|
|
20
21
|
|
|
21
22
|
export const Overflow = {
|
|
22
23
|
expandY: 'expand-y',
|
|
@@ -55,7 +56,7 @@ export function getNullSelection<M extends boolean>(
|
|
|
55
56
|
multiselect: M,
|
|
56
57
|
): SelectValueType<M> {
|
|
57
58
|
if (multiselect) {
|
|
58
|
-
return
|
|
59
|
+
return [] as Array<string> as SelectValueType<M>;
|
|
59
60
|
} else {
|
|
60
61
|
return null as SelectValueType<M>;
|
|
61
62
|
}
|
package/src/ComboboxOption.tsx
CHANGED
|
@@ -91,9 +91,8 @@ const InternalComboboxOption = React.forwardRef<
|
|
|
91
91
|
}: InternalComboboxOptionProps,
|
|
92
92
|
forwardedRef,
|
|
93
93
|
) => {
|
|
94
|
-
const { multiselect, darkMode, withIcons, inputValue } =
|
|
95
|
-
ComboboxContext
|
|
96
|
-
);
|
|
94
|
+
const { multiselect, darkMode, withIcons, inputValue } =
|
|
95
|
+
useContext(ComboboxContext);
|
|
97
96
|
const optionTextId = useIdAllocator({ prefix: 'combobox-option-text' });
|
|
98
97
|
const optionRef = useForwardedRef(forwardedRef, null);
|
|
99
98
|
|