@shopify/cli-kit 3.46.3 → 3.47.0-pre.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/assets/cli-ruby/lib/graphql/extension_create.graphql +1 -0
- package/assets/cli-ruby/lib/graphql/extension_update_draft.graphql +1 -0
- package/assets/cli-ruby/lib/graphql/get_extension_registrations.graphql +1 -0
- package/dist/private/node/api.d.ts +1 -1
- package/dist/private/node/api.js +1 -1
- package/dist/private/node/api.js.map +1 -1
- package/dist/private/node/session/exchange.d.ts +1 -0
- package/dist/private/node/session/exchange.js +3 -1
- package/dist/private/node/session/exchange.js.map +1 -1
- package/dist/private/node/session/identity.js +12 -0
- package/dist/private/node/session/identity.js.map +1 -1
- package/dist/private/node/session/scopes.js +5 -1
- package/dist/private/node/session/scopes.js.map +1 -1
- package/dist/private/node/session.d.ts +7 -0
- package/dist/private/node/session.js +8 -1
- package/dist/private/node/session.js.map +1 -1
- package/dist/private/node/ui/components/Alert.js +7 -12
- package/dist/private/node/ui/components/Alert.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.js +7 -10
- package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
- package/dist/private/node/ui/components/Banner.js +9 -11
- package/dist/private/node/ui/components/Banner.js.map +1 -1
- package/dist/private/node/ui/components/Banner.test.js +63 -29
- package/dist/private/node/ui/components/Banner.test.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js +5 -6
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/private/node/ui/components/FatalError.js +8 -11
- package/dist/private/node/ui/components/FatalError.js.map +1 -1
- package/dist/private/node/ui/components/List.js +2 -4
- package/dist/private/node/ui/components/List.js.map +1 -1
- package/dist/private/node/ui/components/List.test.js +32 -0
- package/dist/private/node/ui/components/List.test.js.map +1 -1
- package/dist/private/node/ui/components/Prompts/InfoTable.js +2 -3
- package/dist/private/node/ui/components/Prompts/InfoTable.js.map +1 -1
- package/dist/private/node/ui/components/SelectInput.d.ts +9 -7
- package/dist/private/node/ui/components/SelectInput.js +41 -61
- package/dist/private/node/ui/components/SelectInput.js.map +1 -1
- package/dist/private/node/ui/components/SelectInput.test.js +205 -62
- package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.js +4 -16
- package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.test.js +29 -0
- package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/TextAnimation.d.ts +2 -2
- package/dist/private/node/ui/components/TextAnimation.js +4 -3
- package/dist/private/node/ui/components/TextAnimation.js.map +1 -1
- package/dist/private/node/ui/hooks/use-select-state.d.ts +85 -0
- package/dist/private/node/ui/hooks/use-select-state.js +180 -0
- package/dist/private/node/ui/hooks/use-select-state.js.map +1 -0
- package/dist/private/node/ui.js +2 -1
- package/dist/private/node/ui.js.map +1 -1
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/api/business-platform.d.ts +10 -0
- package/dist/public/node/api/business-platform.js +25 -0
- package/dist/public/node/api/business-platform.js.map +1 -0
- package/dist/public/node/context/fqdn.d.ts +6 -0
- package/dist/public/node/context/fqdn.js +17 -0
- package/dist/public/node/context/fqdn.js.map +1 -1
- package/dist/public/node/metadata.d.ts +1 -1
- package/dist/public/node/metadata.js.map +1 -1
- package/dist/public/node/monorail.d.ts +2 -1
- package/dist/public/node/monorail.js +1 -1
- package/dist/public/node/monorail.js.map +1 -1
- package/dist/public/node/session.d.ts +7 -0
- package/dist/public/node/session.js +16 -0
- package/dist/public/node/session.js.map +1 -1
- package/dist/public/node/ui.d.ts +2 -2
- package/dist/public/node/ui.js +2 -2
- package/dist/public/node/ui.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import { isEqual } from '../../../../public/common/lang.js';
|
|
2
1
|
import { debounce } from '../../../../public/common/function.js';
|
|
3
|
-
import
|
|
2
|
+
import { useSelectState } from '../hooks/use-select-state.js';
|
|
3
|
+
import { handleCtrlC } from '../../ui.js';
|
|
4
|
+
import React, { useRef, useCallback, forwardRef, useEffect } from 'react';
|
|
4
5
|
import { Box, useInput, Text } from 'ink';
|
|
5
6
|
import chalk from 'chalk';
|
|
6
7
|
import figures from 'figures';
|
|
7
8
|
import { createRequire } from 'module';
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
9
|
-
function rotateArray(array, rotation) {
|
|
10
|
-
const arrayCopy = array.slice();
|
|
11
|
-
return arrayCopy.splice(-(rotation ?? 0) % arrayCopy.length).concat(arrayCopy);
|
|
12
|
-
}
|
|
13
10
|
function highlightedLabel(label, term) {
|
|
14
11
|
if (!term) {
|
|
15
12
|
return label;
|
|
@@ -20,10 +17,16 @@ function highlightedLabel(label, term) {
|
|
|
20
17
|
});
|
|
21
18
|
}
|
|
22
19
|
// eslint-disable-next-line react/function-component-definition
|
|
23
|
-
function Item({ item, previousItem,
|
|
24
|
-
const isSelected = items.indexOf(item) === selectedIndex;
|
|
20
|
+
function Item({ item, previousItem, isSelected, highlightedTerm, enableShortcuts, items, hasAnyGroup, }) {
|
|
25
21
|
const label = highlightedLabel(item.label, highlightedTerm);
|
|
26
22
|
let title;
|
|
23
|
+
let labelColor;
|
|
24
|
+
if (isSelected) {
|
|
25
|
+
labelColor = 'cyan';
|
|
26
|
+
}
|
|
27
|
+
else if (item.disabled) {
|
|
28
|
+
labelColor = 'dim';
|
|
29
|
+
}
|
|
27
30
|
if (typeof previousItem === 'undefined' || item.group !== previousItem.group) {
|
|
28
31
|
title = item.group ?? (hasAnyGroup ? 'Other' : undefined);
|
|
29
32
|
}
|
|
@@ -32,10 +35,10 @@ function Item({ item, previousItem, selectedIndex, highlightedTerm, enableShortc
|
|
|
32
35
|
React.createElement(Text, { bold: true }, title))) : null,
|
|
33
36
|
React.createElement(Box, { key: item.key },
|
|
34
37
|
React.createElement(Box, { marginRight: 2 }, isSelected ? React.createElement(Text, { color: "cyan" }, `>`) : React.createElement(Text, null, " ")),
|
|
35
|
-
React.createElement(Text, { color:
|
|
38
|
+
React.createElement(Text, { color: labelColor }, enableShortcuts ? `(${item.key}) ${label}` : label))));
|
|
36
39
|
}
|
|
37
40
|
// eslint-disable-next-line react/function-component-definition
|
|
38
|
-
function SelectInputInner({ items: initialItems, onChange, enableShortcuts = true, focus = true, emptyMessage = 'No items to select.', defaultValue, highlightedTerm, loading = false, errorMessage, hasMorePages = false, morePagesMessage, infoMessage, limit, }, ref) {
|
|
41
|
+
function SelectInputInner({ items: initialItems, onChange, enableShortcuts = true, focus = true, emptyMessage = 'No items to select.', defaultValue, highlightedTerm, loading = false, errorMessage, hasMorePages = false, morePagesMessage, infoMessage, limit, submitWithShortcuts = false, onSubmit, }, ref) {
|
|
39
42
|
const sortBy = require('lodash/sortBy');
|
|
40
43
|
const hasAnyGroup = initialItems.some((item) => typeof item.group !== 'undefined');
|
|
41
44
|
const items = sortBy(initialItems, 'group');
|
|
@@ -43,69 +46,39 @@ function SelectInputInner({ items: initialItems, onChange, enableShortcuts = tru
|
|
|
43
46
|
...item,
|
|
44
47
|
key: item.key ?? (index + 1).toString(),
|
|
45
48
|
}));
|
|
46
|
-
const defaultValueIndex = defaultValue ? items.findIndex((item) => item.value === defaultValue.value) : -1;
|
|
47
|
-
const initialIndex = defaultValueIndex === -1 ? 0 : defaultValueIndex;
|
|
48
49
|
const hasLimit = typeof limit !== 'undefined' && items.length > limit;
|
|
49
50
|
const inputStack = useRef(null);
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
setSelectedIndex(newSelectedIndex);
|
|
56
|
-
if (typeof newRotateIndex !== 'undefined')
|
|
57
|
-
setRotateIndex(newRotateIndex);
|
|
58
|
-
const rotatedItems = hasLimit ? rotateArray(items, newRotateIndex).slice(0, limit) : items;
|
|
59
|
-
onChange({
|
|
60
|
-
item: rotatedItems[newSelectedIndex],
|
|
61
|
-
usedShortcut,
|
|
62
|
-
});
|
|
63
|
-
}, [hasLimit, items, limit, onChange]);
|
|
51
|
+
const state = useSelectState({
|
|
52
|
+
visibleOptionCount: limit,
|
|
53
|
+
options: itemsWithKeys,
|
|
54
|
+
defaultValue,
|
|
55
|
+
});
|
|
64
56
|
useEffect(() => {
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
onChange({
|
|
68
|
-
item: undefined,
|
|
69
|
-
usedShortcut: false,
|
|
70
|
-
});
|
|
71
|
-
// reset index when items change or the first time
|
|
57
|
+
if (typeof state.value !== 'undefined' && state.previousValue !== state.value) {
|
|
58
|
+
onChange?.(items.find((item) => item.value === state.value));
|
|
72
59
|
}
|
|
73
|
-
|
|
74
|
-
changeSelection({ newSelectedIndex: initialIndex, newRotateIndex: 0 });
|
|
75
|
-
}
|
|
76
|
-
else if (!isEqual(previousItems.current.map((item) => item.value), items.map((item) => item.value))) {
|
|
77
|
-
changeSelection({ newSelectedIndex: 0, newRotateIndex: 0 });
|
|
78
|
-
}
|
|
79
|
-
previousItems.current = items;
|
|
80
|
-
}, [changeSelection, initialIndex, items, onChange]);
|
|
60
|
+
}, [state.previousValue, state.value, items, onChange]);
|
|
81
61
|
const handleArrows = (key) => {
|
|
82
62
|
if (key.upArrow) {
|
|
83
|
-
|
|
84
|
-
const atFirstIndex = selectedIndex === 0;
|
|
85
|
-
const nextIndex = hasLimit ? selectedIndex : lastIndex;
|
|
86
|
-
const nextRotateIndex = atFirstIndex ? rotateIndex + 1 : rotateIndex;
|
|
87
|
-
const nextSelectedIndex = atFirstIndex ? nextIndex : selectedIndex - 1;
|
|
88
|
-
changeSelection({ newSelectedIndex: nextSelectedIndex, newRotateIndex: nextRotateIndex });
|
|
63
|
+
state.selectPreviousOption();
|
|
89
64
|
}
|
|
90
65
|
else if (key.downArrow) {
|
|
91
|
-
|
|
92
|
-
const nextIndex = hasLimit ? selectedIndex : 0;
|
|
93
|
-
const nextRotateIndex = atLastIndex ? rotateIndex - 1 : rotateIndex;
|
|
94
|
-
const nextSelectedIndex = atLastIndex ? nextIndex : selectedIndex + 1;
|
|
95
|
-
changeSelection({ newSelectedIndex: nextSelectedIndex, newRotateIndex: nextRotateIndex });
|
|
66
|
+
state.selectNextOption();
|
|
96
67
|
}
|
|
97
68
|
};
|
|
98
69
|
const handleShortcuts = useCallback((input) => {
|
|
99
|
-
if (
|
|
100
|
-
const itemWithKey =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
70
|
+
if (state.visibleOptions.map((item) => item.key).includes(input)) {
|
|
71
|
+
const itemWithKey = state.visibleOptions.find((item) => item.key === input);
|
|
72
|
+
const item = items.find((item) => item.value === itemWithKey?.value);
|
|
73
|
+
if (itemWithKey && !itemWithKey.disabled) {
|
|
74
|
+
// keep this order of operations so that there is no flickering
|
|
75
|
+
if (submitWithShortcuts && onSubmit && item) {
|
|
76
|
+
onSubmit(item);
|
|
77
|
+
}
|
|
78
|
+
state.selectOption({ option: itemWithKey });
|
|
106
79
|
}
|
|
107
80
|
}
|
|
108
|
-
}, [
|
|
81
|
+
}, [items, onSubmit, state, submitWithShortcuts]);
|
|
109
82
|
// disable exhaustive-deps because we want to memoize the debounce function itself
|
|
110
83
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
111
84
|
const debounceHandleShortcuts = useCallback(debounce((newInputStack) => {
|
|
@@ -113,6 +86,13 @@ function SelectInputInner({ items: initialItems, onChange, enableShortcuts = tru
|
|
|
113
86
|
inputStack.current = null;
|
|
114
87
|
}, 300), [handleShortcuts]);
|
|
115
88
|
useInput((input, key) => {
|
|
89
|
+
handleCtrlC(input, key);
|
|
90
|
+
if (typeof state.value !== 'undefined' && key.return) {
|
|
91
|
+
const item = items.find((item) => item.value === state.value);
|
|
92
|
+
if (item && onSubmit) {
|
|
93
|
+
onSubmit(item);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
116
96
|
// check that no special modifier (shift, control, etc.) is being pressed
|
|
117
97
|
if (enableShortcuts && input.length > 0 && Object.values(key).every((value) => value === false)) {
|
|
118
98
|
const newInputStack = inputStack.current === null ? input : inputStack.current + input;
|
|
@@ -139,7 +119,7 @@ function SelectInputInner({ items: initialItems, onChange, enableShortcuts = tru
|
|
|
139
119
|
}
|
|
140
120
|
else {
|
|
141
121
|
return (React.createElement(Box, { flexDirection: "column", ref: ref },
|
|
142
|
-
|
|
122
|
+
state.visibleOptions.map((item, index) => (React.createElement(Item, { key: item.key, item: item, previousItem: state.visibleOptions[index - 1], highlightedTerm: highlightedTerm, isSelected: item.value === state.value, items: state.visibleOptions, enableShortcuts: enableShortcuts, hasAnyGroup: hasAnyGroup }))),
|
|
143
123
|
React.createElement(Box, { marginTop: 1, marginLeft: 3, flexDirection: "column" },
|
|
144
124
|
hasMorePages ? (React.createElement(Text, null,
|
|
145
125
|
React.createElement(Text, { bold: true },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectInput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SelectInput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {isEqual} from '../../../../public/common/lang.js'\nimport {debounce} from '../../../../public/common/function.js'\nimport React, {useState, useEffect, useRef, useCallback, forwardRef} from 'react'\nimport {Box, Key, useInput, Text, DOMElement} from 'ink'\nimport chalk from 'chalk'\nimport figures from 'figures'\nimport {createRequire} from 'module'\n\nconst require = createRequire(import.meta.url)\n\ndeclare module 'react' {\n function forwardRef<T, P>(\n render: (props: P, ref: React.Ref<T>) => JSX.Element | null,\n ): (props: P & React.RefAttributes<T>) => JSX.Element | null\n}\n\nfunction rotateArray<T>(array: T[], rotation?: number) {\n const arrayCopy = array.slice()\n return arrayCopy.splice(-(rotation ?? 0) % arrayCopy.length).concat(arrayCopy)\n}\n\ninterface OnChangeOptions<T> {\n item: Item<T> | undefined\n usedShortcut: boolean\n}\nexport interface SelectInputProps<T> {\n items: Item<T>[]\n onChange: ({item, usedShortcut}: OnChangeOptions<T>) => void\n enableShortcuts?: boolean\n focus?: boolean\n emptyMessage?: string\n defaultValue?: Item<T>\n highlightedTerm?: string\n loading?: boolean\n errorMessage?: string\n hasMorePages?: boolean\n morePagesMessage?: string\n infoMessage?: string\n limit?: number\n}\n\nexport interface Item<T> {\n label: string\n value: T\n key?: string\n group?: string\n}\n\ninterface ItemWithKey<T> extends Item<T> {\n key: string\n}\n\nfunction highlightedLabel(label: string, term: string | undefined) {\n if (!term) {\n return label\n }\n\n const regex = new RegExp(term, 'i')\n return label.replace(regex, (match) => {\n return chalk.bold(match)\n })\n}\n\ninterface ItemProps<T> {\n item: ItemWithKey<T>\n previousItem: ItemWithKey<T> | undefined\n items: ItemWithKey<T>[]\n selectedIndex: number\n highlightedTerm?: string\n enableShortcuts: boolean\n hasAnyGroup: boolean\n}\n\n// eslint-disable-next-line react/function-component-definition\nfunction Item<T>({\n item,\n previousItem,\n selectedIndex,\n highlightedTerm,\n enableShortcuts,\n items,\n hasAnyGroup,\n}: ItemProps<T>): JSX.Element {\n const isSelected = items.indexOf(item) === selectedIndex\n const label = highlightedLabel(item.label, highlightedTerm)\n let title: string | undefined\n\n if (typeof previousItem === 'undefined' || item.group !== previousItem.group) {\n title = item.group ?? (hasAnyGroup ? 'Other' : undefined)\n }\n\n return (\n <Box key={item.key} flexDirection=\"column\" marginTop={items.indexOf(item) !== 0 && title ? 1 : 0}>\n {title ? (\n <Box marginLeft={3}>\n <Text bold>{title}</Text>\n </Box>\n ) : null}\n\n <Box key={item.key}>\n <Box marginRight={2}>{isSelected ? <Text color=\"cyan\">{`>`}</Text> : <Text> </Text>}</Box>\n <Text color={isSelected ? 'cyan' : undefined}>{enableShortcuts ? `(${item.key}) ${label}` : label}</Text>\n </Box>\n </Box>\n )\n}\n\n// eslint-disable-next-line react/function-component-definition\nfunction SelectInputInner<T>(\n {\n items: initialItems,\n onChange,\n enableShortcuts = true,\n focus = true,\n emptyMessage = 'No items to select.',\n defaultValue,\n highlightedTerm,\n loading = false,\n errorMessage,\n hasMorePages = false,\n morePagesMessage,\n infoMessage,\n limit,\n }: SelectInputProps<T>,\n ref: React.ForwardedRef<DOMElement>,\n): JSX.Element | null {\n const sortBy = require('lodash/sortBy')\n const hasAnyGroup = initialItems.some((item) => typeof item.group !== 'undefined')\n const items = sortBy(initialItems, 'group') as Item<T>[]\n const itemsWithKeys = items.map((item, index) => ({\n ...item,\n key: item.key ?? (index + 1).toString(),\n })) as ItemWithKey<T>[]\n const defaultValueIndex = defaultValue ? items.findIndex((item) => item.value === defaultValue.value) : -1\n const initialIndex = defaultValueIndex === -1 ? 0 : defaultValueIndex\n const hasLimit = typeof limit !== 'undefined' && items.length > limit\n const inputStack = useRef<string | null>(null)\n const [selectedIndex, setSelectedIndex] = useState(initialIndex)\n const [rotateIndex, setRotateIndex] = useState(0)\n const slicedItemsWithKeys = hasLimit ? rotateArray(itemsWithKeys, rotateIndex).slice(0, limit) : itemsWithKeys\n const previousItems = useRef<Item<T>[] | undefined>(undefined)\n\n const changeSelection = useCallback(\n ({\n newSelectedIndex,\n newRotateIndex,\n usedShortcut = false,\n }: {\n newSelectedIndex: number\n usedShortcut?: boolean\n newRotateIndex?: number\n }) => {\n setSelectedIndex(newSelectedIndex)\n if (typeof newRotateIndex !== 'undefined') setRotateIndex(newRotateIndex)\n\n const rotatedItems = hasLimit ? rotateArray(items, newRotateIndex).slice(0, limit) : items\n\n onChange({\n item: rotatedItems[newSelectedIndex],\n usedShortcut,\n })\n },\n [hasLimit, items, limit, onChange],\n )\n\n useEffect(() => {\n if (items.length === 0) {\n // reset selection when items are empty\n onChange({\n item: undefined,\n usedShortcut: false,\n })\n // reset index when items change or the first time\n } else if (!previousItems.current) {\n changeSelection({newSelectedIndex: initialIndex, newRotateIndex: 0})\n } else if (\n !isEqual(\n previousItems.current.map((item) => item.value),\n items.map((item) => item.value),\n )\n ) {\n changeSelection({newSelectedIndex: 0, newRotateIndex: 0})\n }\n\n previousItems.current = items\n }, [changeSelection, initialIndex, items, onChange])\n\n const handleArrows = (key: Key) => {\n if (key.upArrow) {\n const lastIndex = items.length - 1\n const atFirstIndex = selectedIndex === 0\n const nextIndex = hasLimit ? selectedIndex : lastIndex\n const nextRotateIndex = atFirstIndex ? rotateIndex + 1 : rotateIndex\n const nextSelectedIndex = atFirstIndex ? nextIndex : selectedIndex - 1\n\n changeSelection({newSelectedIndex: nextSelectedIndex, newRotateIndex: nextRotateIndex})\n } else if (key.downArrow) {\n const atLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1\n const nextIndex = hasLimit ? selectedIndex : 0\n const nextRotateIndex = atLastIndex ? rotateIndex - 1 : rotateIndex\n const nextSelectedIndex = atLastIndex ? nextIndex : selectedIndex + 1\n\n changeSelection({newSelectedIndex: nextSelectedIndex, newRotateIndex: nextRotateIndex})\n }\n }\n\n const handleShortcuts = useCallback(\n (input: string) => {\n if (slicedItemsWithKeys.map((item) => item.key).includes(input)) {\n const itemWithKey = slicedItemsWithKeys.find((item) => item.key === input)\n if (itemWithKey !== undefined) {\n changeSelection({\n newSelectedIndex: items.findIndex((item) => item.value === itemWithKey.value),\n usedShortcut: true,\n })\n }\n }\n },\n [changeSelection, items, slicedItemsWithKeys],\n )\n\n // disable exhaustive-deps because we want to memoize the debounce function itself\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const debounceHandleShortcuts = useCallback(\n debounce((newInputStack) => {\n handleShortcuts(newInputStack)\n inputStack.current = null\n }, 300),\n [handleShortcuts],\n )\n\n useInput(\n (input, key) => {\n // check that no special modifier (shift, control, etc.) is being pressed\n if (enableShortcuts && input.length > 0 && Object.values(key).every((value) => value === false)) {\n const newInputStack = inputStack.current === null ? input : inputStack.current + input\n\n inputStack.current = newInputStack\n debounceHandleShortcuts(newInputStack)\n } else {\n debounceHandleShortcuts.cancel()\n inputStack.current = null\n handleArrows(key)\n }\n },\n {isActive: focus},\n )\n\n if (loading) {\n return (\n <Box marginLeft={3}>\n <Text dimColor>Loading...</Text>\n </Box>\n )\n } else if (errorMessage && errorMessage.length > 0) {\n return (\n <Box marginLeft={3}>\n <Text color=\"red\">{errorMessage}</Text>\n </Box>\n )\n } else if (items.length === 0) {\n return (\n <Box marginLeft={3}>\n <Text dimColor>{emptyMessage}</Text>\n </Box>\n )\n } else {\n return (\n <Box flexDirection=\"column\" ref={ref}>\n {slicedItemsWithKeys.map((item, index) => (\n <Item\n key={item.key}\n item={item}\n previousItem={slicedItemsWithKeys[index - 1]}\n highlightedTerm={highlightedTerm}\n selectedIndex={selectedIndex}\n items={slicedItemsWithKeys}\n enableShortcuts={enableShortcuts}\n hasAnyGroup={hasAnyGroup}\n />\n ))}\n\n <Box marginTop={1} marginLeft={3} flexDirection=\"column\">\n {hasMorePages ? (\n <Text>\n <Text bold>1-{items.length} of many</Text>\n {morePagesMessage ? ` ${morePagesMessage}` : null}\n </Text>\n ) : null}\n {hasLimit ? <Text dimColor>{`Showing ${limit} of ${items.length} items.`}</Text> : null}\n <Text dimColor>\n {infoMessage\n ? infoMessage\n : `Press ${figures.arrowUp}${figures.arrowDown} arrows to select, enter to confirm`}\n </Text>\n </Box>\n </Box>\n )\n }\n}\n\nexport const SelectInput = forwardRef(SelectInputInner)\n"]}
|
|
1
|
+
{"version":3,"file":"SelectInput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SelectInput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {debounce} from '../../../../public/common/function.js'\nimport {useSelectState} from '../hooks/use-select-state.js'\nimport {handleCtrlC} from '../../ui.js'\nimport React, {useRef, useCallback, forwardRef, useEffect} from 'react'\nimport {Box, Key, useInput, Text, DOMElement} from 'ink'\nimport chalk from 'chalk'\nimport figures from 'figures'\nimport {createRequire} from 'module'\n\nconst require = createRequire(import.meta.url)\n\ndeclare module 'react' {\n function forwardRef<T, P>(\n render: (props: P, ref: React.Ref<T>) => JSX.Element | null,\n ): (props: P & React.RefAttributes<T>) => JSX.Element | null\n}\nexport interface SelectInputProps<T> {\n items: Item<T>[]\n onChange?: (item: Item<T> | undefined) => void\n enableShortcuts?: boolean\n focus?: boolean\n emptyMessage?: string\n defaultValue?: T\n highlightedTerm?: string\n loading?: boolean\n errorMessage?: string\n hasMorePages?: boolean\n morePagesMessage?: string\n infoMessage?: string\n limit?: number\n submitWithShortcuts?: boolean\n onSubmit?: (item: Item<T>) => void\n}\n\nexport interface Item<T> {\n label: string\n value: T\n key?: string\n group?: string\n helperText?: string\n disabled?: boolean\n}\n\nexport interface ItemWithKey<T> extends Item<T> {\n key: string\n}\n\nfunction highlightedLabel(label: string, term: string | undefined) {\n if (!term) {\n return label\n }\n\n const regex = new RegExp(term, 'i')\n return label.replace(regex, (match) => {\n return chalk.bold(match)\n })\n}\n\ninterface ItemProps<T> {\n item: ItemWithKey<T>\n previousItem: ItemWithKey<T> | undefined\n items: ItemWithKey<T>[]\n isSelected: boolean\n highlightedTerm?: string\n enableShortcuts: boolean\n hasAnyGroup: boolean\n}\n\n// eslint-disable-next-line react/function-component-definition\nfunction Item<T>({\n item,\n previousItem,\n isSelected,\n highlightedTerm,\n enableShortcuts,\n items,\n hasAnyGroup,\n}: ItemProps<T>): JSX.Element {\n const label = highlightedLabel(item.label, highlightedTerm)\n let title: string | undefined\n let labelColor\n\n if (isSelected) {\n labelColor = 'cyan'\n } else if (item.disabled) {\n labelColor = 'dim'\n }\n\n if (typeof previousItem === 'undefined' || item.group !== previousItem.group) {\n title = item.group ?? (hasAnyGroup ? 'Other' : undefined)\n }\n\n return (\n <Box key={item.key} flexDirection=\"column\" marginTop={items.indexOf(item) !== 0 && title ? 1 : 0}>\n {title ? (\n <Box marginLeft={3}>\n <Text bold>{title}</Text>\n </Box>\n ) : null}\n\n <Box key={item.key}>\n <Box marginRight={2}>{isSelected ? <Text color=\"cyan\">{`>`}</Text> : <Text> </Text>}</Box>\n <Text color={labelColor}>{enableShortcuts ? `(${item.key}) ${label}` : label}</Text>\n </Box>\n </Box>\n )\n}\n\n// eslint-disable-next-line react/function-component-definition\nfunction SelectInputInner<T>(\n {\n items: initialItems,\n onChange,\n enableShortcuts = true,\n focus = true,\n emptyMessage = 'No items to select.',\n defaultValue,\n highlightedTerm,\n loading = false,\n errorMessage,\n hasMorePages = false,\n morePagesMessage,\n infoMessage,\n limit,\n submitWithShortcuts = false,\n onSubmit,\n }: SelectInputProps<T>,\n ref: React.ForwardedRef<DOMElement>,\n): JSX.Element | null {\n const sortBy = require('lodash/sortBy')\n const hasAnyGroup = initialItems.some((item) => typeof item.group !== 'undefined')\n const items = sortBy(initialItems, 'group') as Item<T>[]\n const itemsWithKeys = items.map((item, index) => ({\n ...item,\n key: item.key ?? (index + 1).toString(),\n })) as ItemWithKey<T>[]\n const hasLimit = typeof limit !== 'undefined' && items.length > limit\n const inputStack = useRef<string | null>(null)\n\n const state = useSelectState({\n visibleOptionCount: limit,\n options: itemsWithKeys,\n defaultValue,\n })\n\n useEffect(() => {\n if (typeof state.value !== 'undefined' && state.previousValue !== state.value) {\n onChange?.(items.find((item) => item.value === state.value))\n }\n }, [state.previousValue, state.value, items, onChange])\n\n const handleArrows = (key: Key) => {\n if (key.upArrow) {\n state.selectPreviousOption()\n } else if (key.downArrow) {\n state.selectNextOption()\n }\n }\n\n const handleShortcuts = useCallback(\n (input: string) => {\n if (state.visibleOptions.map((item) => item.key).includes(input)) {\n const itemWithKey = state.visibleOptions.find((item) => item.key === input)\n const item = items.find((item) => item.value === itemWithKey?.value)\n\n if (itemWithKey && !itemWithKey.disabled) {\n // keep this order of operations so that there is no flickering\n if (submitWithShortcuts && onSubmit && item) {\n onSubmit(item)\n }\n\n state.selectOption({option: itemWithKey})\n }\n }\n },\n [items, onSubmit, state, submitWithShortcuts],\n )\n\n // disable exhaustive-deps because we want to memoize the debounce function itself\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const debounceHandleShortcuts = useCallback(\n debounce((newInputStack) => {\n handleShortcuts(newInputStack)\n inputStack.current = null\n }, 300),\n [handleShortcuts],\n )\n\n useInput(\n (input, key) => {\n handleCtrlC(input, key)\n\n if (typeof state.value !== 'undefined' && key.return) {\n const item = items.find((item) => item.value === state.value)\n\n if (item && onSubmit) {\n onSubmit(item)\n }\n }\n\n // check that no special modifier (shift, control, etc.) is being pressed\n if (enableShortcuts && input.length > 0 && Object.values(key).every((value) => value === false)) {\n const newInputStack = inputStack.current === null ? input : inputStack.current + input\n\n inputStack.current = newInputStack\n debounceHandleShortcuts(newInputStack)\n } else {\n debounceHandleShortcuts.cancel()\n inputStack.current = null\n handleArrows(key)\n }\n },\n {isActive: focus},\n )\n\n if (loading) {\n return (\n <Box marginLeft={3}>\n <Text dimColor>Loading...</Text>\n </Box>\n )\n } else if (errorMessage && errorMessage.length > 0) {\n return (\n <Box marginLeft={3}>\n <Text color=\"red\">{errorMessage}</Text>\n </Box>\n )\n } else if (items.length === 0) {\n return (\n <Box marginLeft={3}>\n <Text dimColor>{emptyMessage}</Text>\n </Box>\n )\n } else {\n return (\n <Box flexDirection=\"column\" ref={ref}>\n {state.visibleOptions.map((item, index) => (\n <Item\n key={item.key}\n item={item}\n previousItem={state.visibleOptions[index - 1]}\n highlightedTerm={highlightedTerm}\n isSelected={item.value === state.value}\n items={state.visibleOptions}\n enableShortcuts={enableShortcuts}\n hasAnyGroup={hasAnyGroup}\n />\n ))}\n\n <Box marginTop={1} marginLeft={3} flexDirection=\"column\">\n {hasMorePages ? (\n <Text>\n <Text bold>1-{items.length} of many</Text>\n {morePagesMessage ? ` ${morePagesMessage}` : null}\n </Text>\n ) : null}\n {hasLimit ? <Text dimColor>{`Showing ${limit} of ${items.length} items.`}</Text> : null}\n <Text dimColor>\n {infoMessage\n ? infoMessage\n : `Press ${figures.arrowUp}${figures.arrowDown} arrows to select, enter to confirm`}\n </Text>\n </Box>\n </Box>\n )\n }\n}\n\nexport const SelectInput = forwardRef(SelectInputInner)\n"]}
|
|
@@ -4,6 +4,7 @@ import { describe, expect, test, vi } from 'vitest';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
const ARROW_UP = '\u001B[A';
|
|
6
6
|
const ARROW_DOWN = '\u001B[B';
|
|
7
|
+
const ENTER = '\r';
|
|
7
8
|
describe('SelectInput', async () => {
|
|
8
9
|
test('move up with up arrow key', async () => {
|
|
9
10
|
const onChange = vi.fn();
|
|
@@ -23,15 +24,17 @@ describe('SelectInput', async () => {
|
|
|
23
24
|
];
|
|
24
25
|
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: onChange }));
|
|
25
26
|
await waitForInputsToBeReady();
|
|
27
|
+
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
28
|
+
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
26
29
|
await sendInputAndWaitForChange(renderInstance, ARROW_UP);
|
|
27
30
|
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
28
31
|
" (1) First
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
[36m>[39m [36m(2) Second[39m
|
|
33
|
+
(3) Third
|
|
31
34
|
|
|
32
35
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
33
36
|
`);
|
|
34
|
-
expect(onChange).
|
|
37
|
+
expect(onChange).toHaveBeenLastCalledWith(items[1]);
|
|
35
38
|
});
|
|
36
39
|
test('move down with down arrow key', async () => {
|
|
37
40
|
const onChange = vi.fn();
|
|
@@ -59,7 +62,7 @@ describe('SelectInput', async () => {
|
|
|
59
62
|
|
|
60
63
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
61
64
|
`);
|
|
62
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
65
|
+
expect(onChange).toHaveBeenCalledWith(items[1]);
|
|
63
66
|
});
|
|
64
67
|
test('handles single digit numeric shortcuts', async () => {
|
|
65
68
|
const onChange = vi.fn();
|
|
@@ -87,7 +90,7 @@ describe('SelectInput', async () => {
|
|
|
87
90
|
|
|
88
91
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
89
92
|
`);
|
|
90
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
93
|
+
expect(onChange).toHaveBeenCalledWith(items[1]);
|
|
91
94
|
});
|
|
92
95
|
test('handles keys with multiple digits', async () => {
|
|
93
96
|
const onChange = vi.fn();
|
|
@@ -116,7 +119,7 @@ describe('SelectInput', async () => {
|
|
|
116
119
|
|
|
117
120
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
118
121
|
`);
|
|
119
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
122
|
+
expect(onChange).toHaveBeenCalledWith(items[2]);
|
|
120
123
|
});
|
|
121
124
|
test('handles pressing non existing keys', async () => {
|
|
122
125
|
const onChange = vi.fn();
|
|
@@ -137,7 +140,7 @@ describe('SelectInput', async () => {
|
|
|
137
140
|
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: onChange }));
|
|
138
141
|
await waitForInputsToBeReady();
|
|
139
142
|
// nothing changes when pressing a key that doesn't exist
|
|
140
|
-
await sendInputAndWait(renderInstance,
|
|
143
|
+
await sendInputAndWait(renderInstance, 500, '4');
|
|
141
144
|
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
142
145
|
"[36m>[39m [36m(1) First[39m
|
|
143
146
|
(2) Second
|
|
@@ -145,8 +148,7 @@ describe('SelectInput', async () => {
|
|
|
145
148
|
|
|
146
149
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
147
150
|
`);
|
|
148
|
-
expect(onChange).
|
|
149
|
-
expect(onChange).toHaveBeenCalledWith({ item: items[0], usedShortcut: false });
|
|
151
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
150
152
|
});
|
|
151
153
|
test('handles custom keys', async () => {
|
|
152
154
|
const onChange = vi.fn();
|
|
@@ -175,37 +177,7 @@ describe('SelectInput', async () => {
|
|
|
175
177
|
|
|
176
178
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
177
179
|
`);
|
|
178
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
179
|
-
});
|
|
180
|
-
test('rotate after reaching the end of the list', async () => {
|
|
181
|
-
const onChange = vi.fn();
|
|
182
|
-
const items = [
|
|
183
|
-
{
|
|
184
|
-
label: 'First',
|
|
185
|
-
value: 'first',
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
label: 'Second',
|
|
189
|
-
value: 'second',
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
label: 'Third',
|
|
193
|
-
value: 'third',
|
|
194
|
-
},
|
|
195
|
-
];
|
|
196
|
-
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: onChange }));
|
|
197
|
-
await waitForInputsToBeReady();
|
|
198
|
-
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
199
|
-
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
200
|
-
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
201
|
-
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
202
|
-
"[36m>[39m [36m(1) First[39m
|
|
203
|
-
(2) Second
|
|
204
|
-
(3) Third
|
|
205
|
-
|
|
206
|
-
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
207
|
-
`);
|
|
208
|
-
expect(onChange).toHaveBeenCalledWith({ item: items[0], usedShortcut: false });
|
|
180
|
+
expect(onChange).toHaveBeenCalledWith(items[2]);
|
|
209
181
|
});
|
|
210
182
|
test('support groups', async () => {
|
|
211
183
|
const onChange = vi.fn();
|
|
@@ -262,7 +234,7 @@ describe('SelectInput', async () => {
|
|
|
262
234
|
|
|
263
235
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
264
236
|
`);
|
|
265
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
237
|
+
expect(onChange).toHaveBeenCalledWith(items[4]);
|
|
266
238
|
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
267
239
|
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
268
240
|
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
@@ -284,7 +256,7 @@ describe('SelectInput', async () => {
|
|
|
284
256
|
|
|
285
257
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
286
258
|
`);
|
|
287
|
-
expect(onChange).
|
|
259
|
+
expect(onChange).toHaveBeenLastCalledWith(items[6]);
|
|
288
260
|
});
|
|
289
261
|
test('allows disabling shortcuts', async () => {
|
|
290
262
|
const onChange = vi.fn();
|
|
@@ -313,8 +285,7 @@ describe('SelectInput', async () => {
|
|
|
313
285
|
|
|
314
286
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
315
287
|
`);
|
|
316
|
-
expect(onChange).
|
|
317
|
-
expect(onChange).toHaveBeenCalledWith({ item: items[0], usedShortcut: false });
|
|
288
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
318
289
|
});
|
|
319
290
|
test('accepts a default value', async () => {
|
|
320
291
|
const items = [
|
|
@@ -331,7 +302,7 @@ describe('SelectInput', async () => {
|
|
|
331
302
|
value: 'third',
|
|
332
303
|
},
|
|
333
304
|
];
|
|
334
|
-
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, defaultValue:
|
|
305
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, defaultValue: "second" }));
|
|
335
306
|
await waitForInputsToBeReady();
|
|
336
307
|
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
337
308
|
" (1) First
|
|
@@ -397,23 +368,6 @@ describe('SelectInput', async () => {
|
|
|
397
368
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
398
369
|
`);
|
|
399
370
|
await waitForInputsToBeReady();
|
|
400
|
-
await sendInputAndWaitForChange(renderInstance, ARROW_UP);
|
|
401
|
-
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
402
|
-
" [1mOther[22m
|
|
403
|
-
[36m>[39m [36m(10) tenth[39m
|
|
404
|
-
|
|
405
|
-
[1mAutomations[22m
|
|
406
|
-
(a) fifth
|
|
407
|
-
(2) sixth
|
|
408
|
-
|
|
409
|
-
[1mMerchant Admin[22m
|
|
410
|
-
(3) eighth
|
|
411
|
-
(4) ninth
|
|
412
|
-
|
|
413
|
-
[2mShowing 5 of 10 items.[22m
|
|
414
|
-
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
415
|
-
`);
|
|
416
|
-
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
417
371
|
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
418
372
|
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
419
373
|
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
@@ -435,5 +389,194 @@ describe('SelectInput', async () => {
|
|
|
435
389
|
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
436
390
|
`);
|
|
437
391
|
});
|
|
392
|
+
test('pressing enter calls onSubmit on the default option', async () => {
|
|
393
|
+
const onSubmit = vi.fn();
|
|
394
|
+
const items = [
|
|
395
|
+
{
|
|
396
|
+
label: 'First',
|
|
397
|
+
value: 'first',
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
label: 'Second',
|
|
401
|
+
value: 'second',
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
label: 'Third',
|
|
405
|
+
value: 'third',
|
|
406
|
+
},
|
|
407
|
+
];
|
|
408
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit }));
|
|
409
|
+
await waitForInputsToBeReady();
|
|
410
|
+
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
411
|
+
expect(onSubmit).toHaveBeenCalledWith(items[0]);
|
|
412
|
+
});
|
|
413
|
+
test('pressing enter calls onSubmit on the selected option', async () => {
|
|
414
|
+
const onSubmit = vi.fn();
|
|
415
|
+
const items = [
|
|
416
|
+
{
|
|
417
|
+
label: 'First',
|
|
418
|
+
value: 'first',
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
label: 'Second',
|
|
422
|
+
value: 'second',
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
label: 'Third',
|
|
426
|
+
value: 'third',
|
|
427
|
+
},
|
|
428
|
+
];
|
|
429
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit }));
|
|
430
|
+
await waitForInputsToBeReady();
|
|
431
|
+
await sendInputAndWait(renderInstance, 100, ARROW_DOWN);
|
|
432
|
+
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
433
|
+
expect(onSubmit).toHaveBeenCalledWith(items[1]);
|
|
434
|
+
});
|
|
435
|
+
test('using a shortcut calls onSubmit if submitWithShortcuts is true', async () => {
|
|
436
|
+
const onSubmit = vi.fn();
|
|
437
|
+
const items = [
|
|
438
|
+
{
|
|
439
|
+
label: 'First',
|
|
440
|
+
value: 'first',
|
|
441
|
+
key: 'f',
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
label: 'Second',
|
|
445
|
+
value: 'second',
|
|
446
|
+
key: 's',
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
label: 'Third',
|
|
450
|
+
value: 'third',
|
|
451
|
+
key: 't',
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit, submitWithShortcuts: true }));
|
|
455
|
+
await waitForInputsToBeReady();
|
|
456
|
+
await sendInputAndWait(renderInstance, 500, 's');
|
|
457
|
+
expect(onSubmit).toHaveBeenCalledWith(items[1]);
|
|
458
|
+
});
|
|
459
|
+
test('using a shortcut does not call onSubmit if submitWithShortcuts is false', async () => {
|
|
460
|
+
const onSubmit = vi.fn();
|
|
461
|
+
const items = [
|
|
462
|
+
{
|
|
463
|
+
label: 'First',
|
|
464
|
+
value: 'first',
|
|
465
|
+
key: 'f',
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
label: 'Second',
|
|
469
|
+
value: 'second',
|
|
470
|
+
key: 's',
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
label: 'Third',
|
|
474
|
+
value: 'third',
|
|
475
|
+
key: 't',
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit }));
|
|
479
|
+
await waitForInputsToBeReady();
|
|
480
|
+
await sendInputAndWait(renderInstance, 500, 's');
|
|
481
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
482
|
+
});
|
|
483
|
+
test('supports disabled options', async () => {
|
|
484
|
+
const onSubmit = vi.fn();
|
|
485
|
+
const items = [
|
|
486
|
+
{
|
|
487
|
+
label: 'First',
|
|
488
|
+
value: 'first',
|
|
489
|
+
key: 'f',
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
label: 'Second',
|
|
493
|
+
value: 'second',
|
|
494
|
+
key: 's',
|
|
495
|
+
disabled: true,
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
label: 'Third',
|
|
499
|
+
value: 'third',
|
|
500
|
+
key: 't',
|
|
501
|
+
},
|
|
502
|
+
];
|
|
503
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit }));
|
|
504
|
+
await waitForInputsToBeReady();
|
|
505
|
+
await sendInputAndWaitForChange(renderInstance, ARROW_DOWN);
|
|
506
|
+
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
507
|
+
" (f) First
|
|
508
|
+
[2m(s) Second[22m
|
|
509
|
+
[36m>[39m [36m(t) Third[39m
|
|
510
|
+
|
|
511
|
+
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
512
|
+
`);
|
|
513
|
+
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
514
|
+
expect(onSubmit).toHaveBeenCalledWith(items[2]);
|
|
515
|
+
});
|
|
516
|
+
test('default value will be skipped if the option is disabled', async () => {
|
|
517
|
+
const onSubmit = vi.fn();
|
|
518
|
+
const items = [
|
|
519
|
+
{
|
|
520
|
+
label: 'First',
|
|
521
|
+
value: 'first',
|
|
522
|
+
key: 'f',
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
label: 'Second',
|
|
526
|
+
value: 'second',
|
|
527
|
+
key: 's',
|
|
528
|
+
disabled: true,
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
label: 'Third',
|
|
532
|
+
value: 'third',
|
|
533
|
+
key: 't',
|
|
534
|
+
},
|
|
535
|
+
];
|
|
536
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit, defaultValue: "second" }));
|
|
537
|
+
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
538
|
+
"[36m>[39m [36m(f) First[39m
|
|
539
|
+
[2m(s) Second[22m
|
|
540
|
+
(t) Third
|
|
541
|
+
|
|
542
|
+
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
543
|
+
`);
|
|
544
|
+
await waitForInputsToBeReady();
|
|
545
|
+
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
546
|
+
expect(onSubmit).toHaveBeenCalledWith(items[0]);
|
|
547
|
+
});
|
|
548
|
+
test('selects the next non-disabled option if the first option is disabled', async () => {
|
|
549
|
+
const onSubmit = vi.fn();
|
|
550
|
+
const items = [
|
|
551
|
+
{
|
|
552
|
+
label: 'First',
|
|
553
|
+
value: 'first',
|
|
554
|
+
key: 'f',
|
|
555
|
+
disabled: true,
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
label: 'Second',
|
|
559
|
+
value: 'second',
|
|
560
|
+
key: 's',
|
|
561
|
+
disabled: true,
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
label: 'Third',
|
|
565
|
+
value: 'third',
|
|
566
|
+
key: 't',
|
|
567
|
+
},
|
|
568
|
+
];
|
|
569
|
+
const renderInstance = render(React.createElement(SelectInput, { items: items, onChange: () => { }, onSubmit: onSubmit }));
|
|
570
|
+
await waitForInputsToBeReady();
|
|
571
|
+
expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
|
|
572
|
+
" [2m(f) First[22m
|
|
573
|
+
[2m(s) Second[22m
|
|
574
|
+
[36m>[39m [36m(t) Third[39m
|
|
575
|
+
|
|
576
|
+
[2mPress ↑↓ arrows to select, enter to confirm[22m"
|
|
577
|
+
`);
|
|
578
|
+
await sendInputAndWait(renderInstance, 100, ENTER);
|
|
579
|
+
expect(onSubmit).toHaveBeenCalledWith(items[2]);
|
|
580
|
+
});
|
|
438
581
|
});
|
|
439
582
|
//# sourceMappingURL=SelectInput.test.js.map
|