@ncds/ui-admin 1.8.25-alpha.2 → 1.8.25
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/dist/cjs/src/components/feedback-and-status/badge/Badge.js +1 -1
- package/dist/cjs/src/components/forms-and-input/combo-box/ComboBox.js +122 -54
- package/dist/cjs/src/components/forms-and-input/combo-box/__tests__/ComboBox.portal.test.js +3 -3
- package/dist/cjs/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.js +455 -0
- package/dist/cjs/src/components/forms-and-input/combo-box/utils.js +51 -0
- package/dist/cjs/src/generated/scoped-css.js +1 -1
- package/dist/cjs/src/hooks/__tests__/useFloatingPosition.test.js +32 -0
- package/dist/cjs/src/hooks/useFloatingPosition.js +2 -1
- package/dist/cjs/src/utils/portal-position/__tests__/portalPosition.tracking.test.js +21 -0
- package/dist/cjs/src/utils/portal-position/portalPosition.js +7 -3
- package/dist/esm/src/components/feedback-and-status/badge/Badge.js +1 -1
- package/dist/esm/src/components/forms-and-input/combo-box/ComboBox.js +121 -53
- package/dist/esm/src/components/forms-and-input/combo-box/__tests__/ComboBox.portal.test.js +3 -3
- package/dist/esm/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.js +452 -0
- package/dist/esm/src/components/forms-and-input/combo-box/utils.js +43 -0
- package/dist/esm/src/generated/scoped-css.js +1 -1
- package/dist/esm/src/hooks/__tests__/useFloatingPosition.test.js +32 -0
- package/dist/esm/src/hooks/useFloatingPosition.js +2 -1
- package/dist/esm/src/utils/portal-position/__tests__/portalPosition.tracking.test.js +21 -0
- package/dist/esm/src/utils/portal-position/portalPosition.js +7 -3
- package/dist/temp/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
- package/dist/temp/src/components/feedback-and-status/badge/Badge.js +1 -1
- package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.d.ts +1 -1
- package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.js +114 -32
- package/dist/temp/src/components/forms-and-input/combo-box/__tests__/ComboBox.portal.test.js +3 -3
- package/dist/temp/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.d.ts +1 -0
- package/dist/temp/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.js +293 -0
- package/dist/temp/src/components/forms-and-input/combo-box/utils.d.ts +27 -0
- package/dist/temp/src/components/forms-and-input/combo-box/utils.js +25 -0
- package/dist/temp/src/generated/scoped-css.js +1 -1
- package/dist/temp/src/hooks/__tests__/useFloatingPosition.test.js +24 -0
- package/dist/temp/src/hooks/useFloatingPosition.js +3 -2
- package/dist/temp/src/utils/portal-position/__tests__/portalPosition.tracking.test.js +13 -0
- package/dist/temp/src/utils/portal-position/portalPosition.d.ts +11 -14
- package/dist/temp/src/utils/portal-position/portalPosition.js +9 -4
- package/dist/types/src/components/feedback-and-status/badge/Badge.d.ts +1 -1
- package/dist/types/src/components/forms-and-input/combo-box/ComboBox.d.ts +1 -1
- package/dist/types/src/components/forms-and-input/combo-box/__tests__/ComboBox.test.d.ts +1 -0
- package/dist/types/src/components/forms-and-input/combo-box/utils.d.ts +27 -0
- package/dist/types/src/utils/portal-position/portalPosition.d.ts +11 -14
- package/dist/ui-admin/assets/styles/style.css +14 -12
- package/package.json +1 -1
|
@@ -8,7 +8,6 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
8
8
|
var _uiAdminIcon = require("@ncds/ui-admin-icon");
|
|
9
9
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
10
|
var _react = require("react");
|
|
11
|
-
var _reactDom = require("react-dom");
|
|
12
11
|
var _dropdown = require("../../../hooks/dropdown");
|
|
13
12
|
var _useFloatingPosition = require("../../../hooks/useFloatingPosition");
|
|
14
13
|
var _usePortalState = require("../../../hooks/usePortalState");
|
|
@@ -18,6 +17,7 @@ var _tag = require("../../feedback-and-status/tag");
|
|
|
18
17
|
var _selectDropdown = require("../../select-dropdown");
|
|
19
18
|
var _HintText = require("../../shared/hintText/HintText");
|
|
20
19
|
var _InputBase = require("../input-base/InputBase");
|
|
20
|
+
var _utils = require("./utils");
|
|
21
21
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
22
|
const defaultMaxHeight = exports.defaultMaxHeight = 275;
|
|
23
23
|
const notifyFormChange = (register, multiple, newValue) => {
|
|
@@ -30,49 +30,113 @@ const notifyFormChange = (register, multiple, newValue) => {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
|
+
/** '' · undefined · null 이면 빈 값. 숫자 0은 빈 값이 아니다 */
|
|
34
|
+
const isEmptyValue = value => value === '' || value == null;
|
|
35
|
+
/** react-hook-form hidden input 값 — Tag 와 같은 빈 값 규칙(0은 값)을 따른다 */
|
|
36
|
+
const toHiddenInputValue = (value, multiple) => {
|
|
37
|
+
if (multiple) return JSON.stringify(value || []);
|
|
38
|
+
return isEmptyValue(value) ? '' : String(value);
|
|
39
|
+
};
|
|
40
|
+
/** SelectDropdown 은 null 을 받지 않으므로 undefined 로 바꾼다 */
|
|
41
|
+
const toDropdownValue = value => value ?? undefined;
|
|
42
|
+
/** multiple 용 — 배열이 아니면(null · undefined 포함) 빈 배열 */
|
|
43
|
+
const toIdArray = value => Array.isArray(value) ? value : [];
|
|
44
|
+
/** 두 id 목록이 같은 id 들로 이루어졌는지 — 순서는 보지 않는다 (사용처가 onChange 값을 정렬해 돌려줘도 같은 값) */
|
|
45
|
+
const isSameIds = (currentIds, nextIds) => currentIds.length === nextIds.length && currentIds.every(id => nextIds.includes(id));
|
|
46
|
+
const isSameTags = (nextTags, prevTags) => nextTags.length === prevTags.length && nextTags.every((tag, index) => tag.id === prevTags[index].id && tag.label === prevTags[index].label);
|
|
47
|
+
const findOption = (optionItems, id) => optionItems.find(option => option.id === id);
|
|
48
|
+
/** 단일 모드 value 에 해당하는 옵션 — 빈 값·배열이면 없음 */
|
|
49
|
+
const findSingleOption = (value, optionItems) => {
|
|
50
|
+
if (isEmptyValue(value) || Array.isArray(value)) return undefined;
|
|
51
|
+
return findOption(optionItems, value);
|
|
52
|
+
};
|
|
33
53
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* useLayoutEffect 가 paint 전에 좌표를 채우므로 빈 fixed 가 노출되지 않는다.
|
|
54
|
+
* 단일 모드 Tag — value 와 optionItems 에서 계산한다.
|
|
55
|
+
* 검색 필터로 목록에서 빠진 경우에만, 같은 id 로 기억해 둔 Tag 를 대신 쓴다.
|
|
37
56
|
*/
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
style: floatingStyle ?? undefined
|
|
57
|
+
const resolveSingleTag = (value, optionItems, lastResolvedTag) => {
|
|
58
|
+
if (isEmptyValue(value) || Array.isArray(value)) return null;
|
|
59
|
+
const found = findOption(optionItems, value);
|
|
60
|
+
if (found) return {
|
|
61
|
+
id: found.id,
|
|
62
|
+
label: found.label
|
|
45
63
|
};
|
|
64
|
+
return lastResolvedTag?.id === value ? lastResolvedTag : null;
|
|
65
|
+
};
|
|
66
|
+
/** 단일 모드 Tag 목록 (0~1개). 목록에서 찾은 Tag 를 기억해 검색 필터 중에도 라벨을 유지한다 */
|
|
67
|
+
const useSingleSelectedTags = (value, optionItems, enabled) => {
|
|
68
|
+
const [lastResolvedTag, setLastResolvedTag] = (0, _react.useState)(null);
|
|
69
|
+
const found = enabled ? findSingleOption(value, optionItems) : undefined;
|
|
70
|
+
const foundId = found?.id;
|
|
71
|
+
const foundLabel = found?.label;
|
|
72
|
+
(0, _react.useEffect)(() => {
|
|
73
|
+
if (foundId === undefined || foundLabel === undefined) return;
|
|
74
|
+
setLastResolvedTag({
|
|
75
|
+
id: foundId,
|
|
76
|
+
label: foundLabel
|
|
77
|
+
});
|
|
78
|
+
}, [foundId, foundLabel]);
|
|
79
|
+
const selectedTag = enabled ? resolveSingleTag(value, optionItems, lastResolvedTag) : null;
|
|
80
|
+
return selectedTag ? [selectedTag] : [];
|
|
81
|
+
};
|
|
82
|
+
/** multiple 모드 Tag — 목록에서 못 찾은 id 는 이미 표시 중인 Tag 의 라벨을 쓴다 */
|
|
83
|
+
const resolveMultipleTags = (ids, optionItems, prevTags) => {
|
|
84
|
+
const nextTags = ids.map(id => {
|
|
85
|
+
const found = findOption(optionItems, id);
|
|
86
|
+
return found ? {
|
|
87
|
+
id: found.id,
|
|
88
|
+
label: found.label
|
|
89
|
+
} : prevTags.find(tag => tag.id === id);
|
|
90
|
+
}).filter(tag => tag !== undefined);
|
|
91
|
+
// 결과가 같으면 이전 배열을 그대로 돌려준다 — 새 배열로 바꾸면 effect 가 다시 렌더를 일으킨다
|
|
92
|
+
return isSameTags(nextTags, prevTags) ? prevTags : nextTags;
|
|
46
93
|
};
|
|
47
94
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
95
|
+
* multiple 모드 Tag — 「선택 완료」·X 클릭·외부 value 변경 때만 확정 id 를 바꾼다.
|
|
96
|
+
* ComboBox 가 직접 onChange 로 내보낸 값은 markEmitted 로 기록해 두고, 그 값이 value 로 돌아오면 한 번만 무시한다(확정 아님).
|
|
97
|
+
* 바깥 값이 들어오면 남아 있던 기록도 지운다 — 오래된 기록 때문에 이후 같은 바깥 값이 무시되지 않게 하기 위함.
|
|
98
|
+
* optionItems 만 바뀌면 확정 id 는 그대로 두고 라벨만 다시 찾는다 — 「선택 완료」 전 검색으로 Tag 가 확정되지 않게 하기 위함.
|
|
50
99
|
*/
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
100
|
+
const useMultipleSelectedTags = (value, optionItems, enabled) => {
|
|
101
|
+
const [selectedTags, setSelectedTags] = (0, _react.useState)([]);
|
|
102
|
+
const lastEmittedRef = (0, _react.useRef)(null);
|
|
103
|
+
const committedIdsRef = (0, _react.useRef)([]);
|
|
104
|
+
const prevValueIdsRef = (0, _react.useRef)(null);
|
|
105
|
+
(0, _react.useEffect)(() => {
|
|
106
|
+
if (!enabled) return;
|
|
107
|
+
const ids = toIdArray(value);
|
|
108
|
+
const isEcho = lastEmittedRef.current !== null && isSameIds(ids, lastEmittedRef.current);
|
|
109
|
+
const isValueChanged = prevValueIdsRef.current === null || !isSameIds(ids, prevValueIdsRef.current);
|
|
110
|
+
prevValueIdsRef.current = ids;
|
|
111
|
+
if (isEcho) {
|
|
112
|
+
lastEmittedRef.current = null;
|
|
113
|
+
} else if (isValueChanged) {
|
|
114
|
+
committedIdsRef.current = ids;
|
|
115
|
+
lastEmittedRef.current = null;
|
|
116
|
+
}
|
|
117
|
+
setSelectedTags(prev => resolveMultipleTags(committedIdsRef.current, optionItems, prev));
|
|
118
|
+
}, [enabled, value, optionItems]);
|
|
119
|
+
/** 「선택 완료」로 Tag 를 확정한다 */
|
|
120
|
+
const commitTags = ids => {
|
|
121
|
+
committedIdsRef.current = ids;
|
|
122
|
+
setSelectedTags(prev => resolveMultipleTags(ids, optionItems, prev));
|
|
123
|
+
};
|
|
124
|
+
/** X 클릭 — 확정 id 에서 그 id 만 뺀다. value 기준으로 계산하면 「선택 완료」 전에 클릭한 항목까지 확정된다 */
|
|
125
|
+
const removeCommittedTag = tagId => {
|
|
126
|
+
commitTags(committedIdsRef.current.filter(id => id !== tagId));
|
|
127
|
+
};
|
|
128
|
+
/** ComboBox 가 onChange 로 내보낸 값을 기록한다 — value 로 돌아왔을 때 확정으로 보지 않기 위함 */
|
|
129
|
+
const markEmitted = ids => {
|
|
130
|
+
lastEmittedRef.current = ids;
|
|
131
|
+
};
|
|
70
132
|
return {
|
|
71
|
-
|
|
72
|
-
|
|
133
|
+
selectedTags,
|
|
134
|
+
commitTags,
|
|
135
|
+
removeCommittedTag,
|
|
136
|
+
markEmitted
|
|
73
137
|
};
|
|
74
138
|
};
|
|
75
|
-
const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((
|
|
139
|
+
const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
|
|
76
140
|
let {
|
|
77
141
|
placeholder = '검색하세요',
|
|
78
142
|
id,
|
|
@@ -98,7 +162,7 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
98
162
|
onComplete,
|
|
99
163
|
usePortal = false,
|
|
100
164
|
...props
|
|
101
|
-
} =
|
|
165
|
+
} = _ref;
|
|
102
166
|
const internalRef = (0, _react.useRef)(null);
|
|
103
167
|
// 인라인 패널의 offset parent 가 __content 다. Portal 좌표도 같은 박스를 기준으로 잡아야
|
|
104
168
|
// label·hintText 가 있을 때 두 모드의 패널 위치가 어긋나지 않는다.
|
|
@@ -111,22 +175,24 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
111
175
|
} = (0, _usePortalState.usePortalState)(usePortal);
|
|
112
176
|
const [inputValue, setInputValue] = (0, _react.useState)(searchValue);
|
|
113
177
|
const [showAllItems, setShowAllItems] = (0, _react.useState)(false);
|
|
114
|
-
const
|
|
178
|
+
const singleTags = useSingleSelectedTags(value, optionItems, !multiple);
|
|
179
|
+
const {
|
|
180
|
+
selectedTags,
|
|
181
|
+
commitTags,
|
|
182
|
+
removeCommittedTag,
|
|
183
|
+
markEmitted
|
|
184
|
+
} = useMultipleSelectedTags(value, optionItems, multiple);
|
|
115
185
|
const handleOptionSelect = option => {
|
|
116
186
|
if (disabled) return;
|
|
117
187
|
if (multiple) {
|
|
118
188
|
const newValue = tryToggle(option.id, Array.isArray(value) ? value : []);
|
|
119
189
|
if (newValue === null) return;
|
|
190
|
+
markEmitted(newValue);
|
|
120
191
|
onChange?.(newValue);
|
|
121
192
|
notifyFormChange(register, multiple, newValue);
|
|
122
193
|
return;
|
|
123
194
|
}
|
|
124
195
|
const newValue = option.id;
|
|
125
|
-
const tag = {
|
|
126
|
-
id: option.id,
|
|
127
|
-
label: option.label
|
|
128
|
-
};
|
|
129
|
-
setSelectedTags([tag]);
|
|
130
196
|
onChange?.(newValue);
|
|
131
197
|
notifyFormChange(register, multiple, newValue);
|
|
132
198
|
};
|
|
@@ -233,7 +299,6 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
233
299
|
const {
|
|
234
300
|
buttonText: selectAllButtonText,
|
|
235
301
|
toggleSelectAll,
|
|
236
|
-
getSelectedTagsData,
|
|
237
302
|
removeTag,
|
|
238
303
|
isMaxSelectionActive,
|
|
239
304
|
tryToggle
|
|
@@ -243,6 +308,7 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
243
308
|
const handleSelectAll = () => {
|
|
244
309
|
if (!multiple || !onChange) return;
|
|
245
310
|
const newSelectedValues = toggleSelectAll();
|
|
311
|
+
markEmitted(newSelectedValues);
|
|
246
312
|
onChange(newSelectedValues);
|
|
247
313
|
};
|
|
248
314
|
const handleEdit = () => {
|
|
@@ -250,41 +316,43 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
250
316
|
};
|
|
251
317
|
const handleComplete = completedValue => {
|
|
252
318
|
if (!multiple) return;
|
|
253
|
-
|
|
254
|
-
setSelectedTags(tags);
|
|
319
|
+
commitTags(toIdArray(completedValue));
|
|
255
320
|
setInputValue('');
|
|
256
321
|
closeDropdown();
|
|
257
322
|
onComplete?.(completedValue);
|
|
258
323
|
};
|
|
259
324
|
const handleRemoveTag = tagId => {
|
|
325
|
+
// X 버튼은 disabled 에서 숨기지만, 다른 호출 경로가 생겨도 값이 지워지지 않게 함수에서도 막는다
|
|
326
|
+
if (disabled) return;
|
|
260
327
|
if (!onChange) return;
|
|
261
328
|
if (multiple) {
|
|
262
329
|
const newSelectedValues = removeTag(tagId);
|
|
330
|
+
markEmitted(newSelectedValues);
|
|
263
331
|
onChange(newSelectedValues);
|
|
264
|
-
|
|
332
|
+
removeCommittedTag(tagId);
|
|
265
333
|
return;
|
|
266
334
|
}
|
|
267
335
|
onChange('');
|
|
268
|
-
setSelectedTags([]);
|
|
269
336
|
};
|
|
270
337
|
const comboboxId = `combobox-options-${id || 'default'}`;
|
|
271
338
|
const hasVisibleOptions = showAllItems || inputValue;
|
|
272
339
|
const displayedOptions = hasVisibleOptions ? optionItems : [];
|
|
273
340
|
const showNoResults = !hasVisibleOptions || optionItems.length === 0;
|
|
274
341
|
const showFooter = multiple || showFooterButtons;
|
|
275
|
-
const hiddenInputValue =
|
|
276
|
-
const
|
|
342
|
+
const hiddenInputValue = toHiddenInputValue(value, multiple);
|
|
343
|
+
const tags = multiple ? selectedTags : singleTags;
|
|
344
|
+
const hasTags = tags.length > 0;
|
|
277
345
|
const {
|
|
278
346
|
className: portalPanelClassName,
|
|
279
347
|
style: portalPanelStyle
|
|
280
|
-
} = resolvePortalPanelBinding(shouldPortal, floatingStyle);
|
|
348
|
+
} = (0, _utils.resolvePortalPanelBinding)(shouldPortal, floatingStyle);
|
|
281
349
|
const selectDropdownNode = (0, _jsxRuntime.jsxs)(_selectDropdown.SelectDropdown, {
|
|
282
350
|
ref: dropdownRef,
|
|
283
351
|
isOpen: isOpen,
|
|
284
352
|
direction: dropdownDirection,
|
|
285
353
|
size: size,
|
|
286
354
|
options: displayedOptions,
|
|
287
|
-
value: value,
|
|
355
|
+
value: toDropdownValue(value),
|
|
288
356
|
focusedIndex: focusedIndex,
|
|
289
357
|
maxHeight: maxHeight,
|
|
290
358
|
listboxId: comboboxId,
|
|
@@ -309,7 +377,7 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
309
377
|
const {
|
|
310
378
|
inlineDropdown,
|
|
311
379
|
portaledDropdown
|
|
312
|
-
} = resolveDropdownMounts({
|
|
380
|
+
} = (0, _utils.resolveDropdownMounts)({
|
|
313
381
|
node: selectDropdownNode,
|
|
314
382
|
shouldPortal,
|
|
315
383
|
portalContainer,
|
|
@@ -363,10 +431,10 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2,
|
|
|
363
431
|
})]
|
|
364
432
|
}), hasTags && (0, _jsxRuntime.jsx)("div", {
|
|
365
433
|
className: "ncua-combobox__tags",
|
|
366
|
-
children:
|
|
434
|
+
children: tags.map(tag => (0, _jsxRuntime.jsx)(_tag.Tag, {
|
|
367
435
|
text: tag.label,
|
|
368
436
|
size: "sm",
|
|
369
|
-
close:
|
|
437
|
+
close: !disabled,
|
|
370
438
|
onButtonClick: () => handleRemoveTag(tag.id)
|
|
371
439
|
}, tag.id))
|
|
372
440
|
})]
|
|
@@ -476,7 +476,7 @@ const clickButtonByLabel = (scope, label) => {
|
|
|
476
476
|
(0, _vitest.expect)(onSearch).toHaveBeenCalledWith('사');
|
|
477
477
|
(0, _vitest.expect)(view.content.querySelectorAll('.ncua-select-dropdown__option')).toHaveLength(createOptions().length);
|
|
478
478
|
});
|
|
479
|
-
(0, _vitest.it)('옵션을 선택하면 onChange 가
|
|
479
|
+
(0, _vitest.it)('옵션을 선택하면 onChange 가 호출된다', () => {
|
|
480
480
|
// Given: 옵션이 노출된 기본 모드 ComboBox 에서
|
|
481
481
|
const onChange = _vitest.vi.fn();
|
|
482
482
|
const view = mountComboBox({
|
|
@@ -489,9 +489,9 @@ const clickButtonByLabel = (scope, label) => {
|
|
|
489
489
|
(0, _testUtils.act)(() => {
|
|
490
490
|
requireElement(view.content, '.ncua-select-dropdown__option').click();
|
|
491
491
|
});
|
|
492
|
-
// Then: onChange 가 선택 값으로
|
|
492
|
+
// Then: onChange 가 선택 값으로 호출된다
|
|
493
|
+
// 선택 태그는 value 를 따르므로(#530) onChange 값을 value 로 돌려주는 경우의 태그 표시는 ComboBox.test.ts S-10 에서 검증한다
|
|
493
494
|
(0, _vitest.expect)(onChange).toHaveBeenCalledWith('apple');
|
|
494
|
-
(0, _vitest.expect)(view.container.querySelectorAll('.ncua-combobox__tags .ncua-tag')).toHaveLength(1);
|
|
495
495
|
});
|
|
496
496
|
(0, _vitest.it)('Escape 를 누르면 인라인 패널이 닫힌다', () => {
|
|
497
497
|
// Given: 열려 있는 기본 모드 ComboBox 에서
|