@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.
Files changed (73) hide show
  1. package/assets/cli-ruby/lib/graphql/extension_create.graphql +1 -0
  2. package/assets/cli-ruby/lib/graphql/extension_update_draft.graphql +1 -0
  3. package/assets/cli-ruby/lib/graphql/get_extension_registrations.graphql +1 -0
  4. package/dist/private/node/api.d.ts +1 -1
  5. package/dist/private/node/api.js +1 -1
  6. package/dist/private/node/api.js.map +1 -1
  7. package/dist/private/node/session/exchange.d.ts +1 -0
  8. package/dist/private/node/session/exchange.js +3 -1
  9. package/dist/private/node/session/exchange.js.map +1 -1
  10. package/dist/private/node/session/identity.js +12 -0
  11. package/dist/private/node/session/identity.js.map +1 -1
  12. package/dist/private/node/session/scopes.js +5 -1
  13. package/dist/private/node/session/scopes.js.map +1 -1
  14. package/dist/private/node/session.d.ts +7 -0
  15. package/dist/private/node/session.js +8 -1
  16. package/dist/private/node/session.js.map +1 -1
  17. package/dist/private/node/ui/components/Alert.js +7 -12
  18. package/dist/private/node/ui/components/Alert.js.map +1 -1
  19. package/dist/private/node/ui/components/AutocompletePrompt.js +7 -10
  20. package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
  21. package/dist/private/node/ui/components/Banner.js +9 -11
  22. package/dist/private/node/ui/components/Banner.js.map +1 -1
  23. package/dist/private/node/ui/components/Banner.test.js +63 -29
  24. package/dist/private/node/ui/components/Banner.test.js.map +1 -1
  25. package/dist/private/node/ui/components/ConcurrentOutput.js +5 -6
  26. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  27. package/dist/private/node/ui/components/FatalError.js +8 -11
  28. package/dist/private/node/ui/components/FatalError.js.map +1 -1
  29. package/dist/private/node/ui/components/List.js +2 -4
  30. package/dist/private/node/ui/components/List.js.map +1 -1
  31. package/dist/private/node/ui/components/List.test.js +32 -0
  32. package/dist/private/node/ui/components/List.test.js.map +1 -1
  33. package/dist/private/node/ui/components/Prompts/InfoTable.js +2 -3
  34. package/dist/private/node/ui/components/Prompts/InfoTable.js.map +1 -1
  35. package/dist/private/node/ui/components/SelectInput.d.ts +9 -7
  36. package/dist/private/node/ui/components/SelectInput.js +41 -61
  37. package/dist/private/node/ui/components/SelectInput.js.map +1 -1
  38. package/dist/private/node/ui/components/SelectInput.test.js +205 -62
  39. package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
  40. package/dist/private/node/ui/components/SelectPrompt.js +4 -16
  41. package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
  42. package/dist/private/node/ui/components/SelectPrompt.test.js +29 -0
  43. package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
  44. package/dist/private/node/ui/components/TextAnimation.d.ts +2 -2
  45. package/dist/private/node/ui/components/TextAnimation.js +4 -3
  46. package/dist/private/node/ui/components/TextAnimation.js.map +1 -1
  47. package/dist/private/node/ui/hooks/use-select-state.d.ts +85 -0
  48. package/dist/private/node/ui/hooks/use-select-state.js +180 -0
  49. package/dist/private/node/ui/hooks/use-select-state.js.map +1 -0
  50. package/dist/private/node/ui.js +2 -1
  51. package/dist/private/node/ui.js.map +1 -1
  52. package/dist/public/common/version.d.ts +1 -1
  53. package/dist/public/common/version.js +1 -1
  54. package/dist/public/common/version.js.map +1 -1
  55. package/dist/public/node/api/business-platform.d.ts +10 -0
  56. package/dist/public/node/api/business-platform.js +25 -0
  57. package/dist/public/node/api/business-platform.js.map +1 -0
  58. package/dist/public/node/context/fqdn.d.ts +6 -0
  59. package/dist/public/node/context/fqdn.js +17 -0
  60. package/dist/public/node/context/fqdn.js.map +1 -1
  61. package/dist/public/node/metadata.d.ts +1 -1
  62. package/dist/public/node/metadata.js.map +1 -1
  63. package/dist/public/node/monorail.d.ts +2 -1
  64. package/dist/public/node/monorail.js +1 -1
  65. package/dist/public/node/monorail.js.map +1 -1
  66. package/dist/public/node/session.d.ts +7 -0
  67. package/dist/public/node/session.js +16 -0
  68. package/dist/public/node/session.js.map +1 -1
  69. package/dist/public/node/ui.d.ts +2 -2
  70. package/dist/public/node/ui.js +2 -2
  71. package/dist/public/node/ui.js.map +1 -1
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. 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 React, { useState, useEffect, useRef, useCallback, forwardRef } from 'react';
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, selectedIndex, highlightedTerm, enableShortcuts, items, hasAnyGroup, }) {
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: isSelected ? 'cyan' : undefined }, enableShortcuts ? `(${item.key}) ${label}` : label))));
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 [selectedIndex, setSelectedIndex] = useState(initialIndex);
51
- const [rotateIndex, setRotateIndex] = useState(0);
52
- const slicedItemsWithKeys = hasLimit ? rotateArray(itemsWithKeys, rotateIndex).slice(0, limit) : itemsWithKeys;
53
- const previousItems = useRef(undefined);
54
- const changeSelection = useCallback(({ newSelectedIndex, newRotateIndex, usedShortcut = false, }) => {
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 (items.length === 0) {
66
- // reset selection when items are empty
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
- else if (!previousItems.current) {
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
- const lastIndex = items.length - 1;
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
- const atLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;
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 (slicedItemsWithKeys.map((item) => item.key).includes(input)) {
100
- const itemWithKey = slicedItemsWithKeys.find((item) => item.key === input);
101
- if (itemWithKey !== undefined) {
102
- changeSelection({
103
- newSelectedIndex: items.findIndex((item) => item.value === itemWithKey.value),
104
- usedShortcut: true,
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
- }, [changeSelection, items, slicedItemsWithKeys]);
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
- slicedItemsWithKeys.map((item, index) => (React.createElement(Item, { key: item.key, item: item, previousItem: slicedItemsWithKeys[index - 1], highlightedTerm: highlightedTerm, selectedIndex: selectedIndex, items: slicedItemsWithKeys, enableShortcuts: enableShortcuts, hasAnyGroup: hasAnyGroup }))),
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
- (2) Second
30
- > (3) Third
32
+ > (2) Second
33
+ (3) Third
31
34
 
32
35
  Press ↑↓ arrows to select, enter to confirm"
33
36
  `);
34
- expect(onChange).toHaveBeenCalledWith({ item: items[2], usedShortcut: false });
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
  Press ↑↓ arrows to select, enter to confirm"
61
64
  `);
62
- expect(onChange).toHaveBeenCalledWith({ item: items[1], usedShortcut: false });
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
  Press ↑↓ arrows to select, enter to confirm"
89
92
  `);
90
- expect(onChange).toHaveBeenCalledWith({ item: items[1], usedShortcut: true });
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
  Press ↑↓ arrows to select, enter to confirm"
118
121
  `);
119
- expect(onChange).toHaveBeenCalledWith({ item: items[2], usedShortcut: true });
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, 100, '4');
143
+ await sendInputAndWait(renderInstance, 500, '4');
141
144
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
142
145
  "> (1) First
143
146
  (2) Second
@@ -145,8 +148,7 @@ describe('SelectInput', async () => {
145
148
 
146
149
  Press ↑↓ arrows to select, enter to confirm"
147
150
  `);
148
- expect(onChange).toHaveBeenCalledOnce();
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
  Press ↑↓ arrows to select, enter to confirm"
177
179
  `);
178
- expect(onChange).toHaveBeenCalledWith({ item: items[2], usedShortcut: true });
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
- "> (1) First
203
- (2) Second
204
- (3) Third
205
-
206
- Press ↑↓ arrows to select, enter to confirm"
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
  Press ↑↓ arrows to select, enter to confirm"
264
236
  `);
265
- expect(onChange).toHaveBeenCalledWith({ item: items[4], usedShortcut: true });
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
  Press ↑↓ arrows to select, enter to confirm"
286
258
  `);
287
- expect(onChange).toHaveBeenCalledWith({ item: items[6], usedShortcut: false });
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
  Press ↑↓ arrows to select, enter to confirm"
315
287
  `);
316
- expect(onChange).toHaveBeenCalledOnce();
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: { label: 'Second', value: 'second' } }));
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
  Press ↑↓ arrows to select, enter to confirm"
398
369
  `);
399
370
  await waitForInputsToBeReady();
400
- await sendInputAndWaitForChange(renderInstance, ARROW_UP);
401
- expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
402
- " Other
403
- > (10) tenth
404
-
405
- Automations
406
- (a) fifth
407
- (2) sixth
408
-
409
- Merchant Admin
410
- (3) eighth
411
- (4) ninth
412
-
413
- Showing 5 of 10 items.
414
- Press ↑↓ arrows to select, enter to confirm"
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
  Press ↑↓ arrows to select, enter to confirm"
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
+ (s) Second
509
+ > (t) Third
510
+
511
+ Press ↑↓ arrows to select, enter to confirm"
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
+ "> (f) First
539
+ (s) Second
540
+ (t) Third
541
+
542
+ Press ↑↓ arrows to select, enter to confirm"
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
+ " (f) First
573
+ (s) Second
574
+ > (t) Third
575
+
576
+ Press ↑↓ arrows to select, enter to confirm"
577
+ `);
578
+ await sendInputAndWait(renderInstance, 100, ENTER);
579
+ expect(onSubmit).toHaveBeenCalledWith(items[2]);
580
+ });
438
581
  });
439
582
  //# sourceMappingURL=SelectInput.test.js.map