UNPKG

@octopusdeploy/design-system-components

Version:
705 lines (704 loc) • 37.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const testing_library_1 = require("@octopusdeploy/testing-library"); const react_1 = require("@testing-library/react"); const react_2 = require("react"); const vitest_1 = require("vitest"); const TestDesignSystemProvider_1 = require("../../../TestDesignSystemProvider"); const MultiSelect_1 = require("../MultiSelect"); const getSharedSelectInteractions_1 = require("./getSharedSelectInteractions"); const option1 = { label: "Option 1", value: "1" }; const option2 = { label: "Option 2", value: "2" }; const option3 = { label: "Option 3", value: "3" }; const items = [option1, option2, option3]; const colouredOption1 = { label: "Frontend", value: "frontend", color: "#0f77e4" }; const colouredOption2 = { label: "Backend", value: "backend", color: "#1abc9c" }; const colouredItems = [colouredOption1, colouredOption2]; (0, vitest_1.describe)("<MultiSelect />", () => { (0, vitest_1.test)("renders with correct label", () => { const select = renderMultiSelect({ label: "Custom Label" }); select.shouldHaveLabel("Custom Label"); select.shouldBeInDocument(); }); (0, vitest_1.test)("renders with placeholder text", () => { const placeholder = "Select an option"; const select = renderMultiSelect({ placeholder }); select.shouldHavePlaceholder(placeholder); }); (0, vitest_1.test)("renders with option label for single selected value", () => { const select = renderMultiSelect({ items, value: [option2.value] }); select.shouldShowSelectedOption("Option 2"); }); (0, vitest_1.test)("renders all selected option labels for multiple selected values", () => { const select = renderMultiSelect({ items, value: [option1.value, option2.value] }); select.shouldShowSelectedOptions(["Option 1", "Option 2"]); }); (0, vitest_1.test)("renders with description", () => { const description = "This is a helpful description"; const select = renderMultiSelect({ description }); select.shouldHaveDescription(description); }); (0, vitest_1.test)("renders with error state", () => { const validationMessage = "This field is required"; const select = renderMultiSelect({ validationMessage }); select.shouldHaveError(validationMessage); select.shouldHaveAriaInvalid(true); }); (0, vitest_1.test)("has autofocus when autoFocus is true", () => { const select = renderMultiSelect({ autoFocus: true }); select.shouldHaveFocus(); }); (0, vitest_1.test)("opens dropdown when clicked", async () => { const select = renderMultiSelect(); await select.click(); select.shouldShowDropdown(); }); (0, vitest_1.test)("displays all options when dropdown is open", async () => { const select = renderMultiSelect(); await select.click(); select.shouldShowAllOptions(items); }); (0, vitest_1.test)("calls onChange when option is selected", async () => { const select = renderMultiSelect({ value: [] }); await select.click(); await select.selectOption("Option 2"); select.shouldHaveCalledOnChangeWith([option2.value]); }); (0, vitest_1.test)("does not close dropdown when option is selected", async () => { const select = renderMultiSelect({ value: [option1.value] }); await select.click(); await select.selectOption("Option 2"); select.shouldShowDropdown(); }); (0, vitest_1.test)("shows no results message when there are no options", async () => { const select = renderMultiSelect({ items: [] }); await select.click(); select.shouldShowNoResultsMessage(); }); (0, vitest_1.test)("does not trigger form submission when clicked inside a form", async () => { const handleSubmit = vitest_1.vi.fn((e) => e.preventDefault()); const select = renderMultiSelect({ withForm: true, onSubmit: handleSubmit, }); await select.click(); (0, vitest_1.expect)(handleSubmit).not.toHaveBeenCalled(); }); (0, vitest_1.describe)("keyboard navigation", () => { (0, vitest_1.test)("opens dropdown with keyboard (Enter key)", async () => { const select = renderMultiSelect(); await select.tab(); await select.pressEnter(); select.shouldShowDropdown(); }); (0, vitest_1.test)("opens dropdown with keyboard (Space key)", async () => { const select = renderMultiSelect(); await select.tab(); await select.pressSpace(); select.shouldShowDropdown(); }); (0, vitest_1.test)("opens dropdown with keyboard (ArrowDown key)", async () => { const select = renderMultiSelect(); await select.tab(); await select.pressArrowDown(); select.shouldShowDropdown(); }); (0, vitest_1.describe)("within listbox", () => { (0, vitest_1.test)("navigates down with ArrowDown key", async () => { const select = renderMultiSelect({ value: [option1.value] }); await select.click(); await select.pressArrowDown(); select.shouldHaveOptionFocused("Option 2"); }); (0, vitest_1.test)("navigates up with ArrowUp key", async () => { const select = renderMultiSelect({ value: [option2.value] }); await select.click(); await select.pressArrowUp(); select.shouldHaveOptionFocused("Option 1"); }); (0, vitest_1.test)("jumps to first option with Home key", async () => { const select = renderMultiSelect({ value: [option3.value] }); await select.click(); await select.pressHome(); select.shouldHaveOptionFocused("Option 1"); }); (0, vitest_1.test)("jumps to last option with End key", async () => { const select = renderMultiSelect({ value: [option1.value] }); await select.click(); await select.pressEnd(); select.shouldHaveOptionFocused("Option 3"); }); (0, vitest_1.test)("selects focused option with Enter key", async () => { const select = renderMultiSelect({ value: [] }); await select.click(); await select.pressArrowDown(); await select.pressEnter(); select.shouldHaveCalledOnChangeWith([option2.value]); }); (0, vitest_1.test)("closes dropdown with Escape key", async () => { const select = renderMultiSelect(); await select.click(); select.shouldShowDropdown(); await select.pressEscape(); select.shouldNotShowDropdown(); select.shouldHaveFocus(); }); (0, vitest_1.test)("does not call onChange when closing with Escape key", async () => { const select = renderMultiSelect({ value: [option1.value] }); await select.click(); await select.pressArrowDown(); await select.pressEscape(); select.shouldNotHaveCalledOnChange(); }); }); }); (0, vitest_1.describe)("disabled and readonly properties", () => { (0, vitest_1.test)("renders as disabled when disabled prop is true", () => { const select = renderMultiSelect({ disabled: true }); select.shouldBeDisabled(); }); (0, vitest_1.test)("disabled select cannot be opened", async () => { const select = renderMultiSelect({ disabled: true }); await select.click(); select.shouldNotShowDropdown(); }); (0, vitest_1.test)("renders as readonly when readOnly prop is true", () => { const select = renderMultiSelect({ readOnly: true }); select.shouldBeReadOnly(); }); (0, vitest_1.test)("readonly select cannot be opened", async () => { const select = renderMultiSelect({ readOnly: true }); await select.click(); select.shouldNotShowDropdown(); }); }); (0, vitest_1.describe)("default, required and optional marker properties", () => { (0, vitest_1.test)("renders with required marker when hasRequiredMarker is true", () => { const select = renderMultiSelect({ hasRequiredMarker: true }); select.shouldHaveRequiredMarker(); }); (0, vitest_1.test)("renders with optional marker when hasOptionalMarker is true", () => { const select = renderMultiSelect({ hasOptionalMarker: true }); select.shouldHaveOptionalMarker(); }); (0, vitest_1.test)("renders with default marker when hasDefaultMarker is true", () => { const select = renderMultiSelect({ hasDefaultMarker: true }); select.shouldHaveDefaultMarker(); }); }); (0, vitest_1.describe)("accessibility attributes", () => { (0, vitest_1.test)("has correct accessibility attributes when error is present", () => { const validationMessage = "Invalid selection"; const select = renderMultiSelect({ validationMessage }); select.shouldHaveAriaInvalid(true); select.shouldHaveAriaDescribedByError(); }); (0, vitest_1.test)("has correct accessibility attributes when description is present", () => { const description = "Help text"; const select = renderMultiSelect({ description }); select.shouldHaveAriaDescribedByDescription(); }); (0, vitest_1.test)("has correct accessibility attributes when both description and error are present", () => { const description = "Help text"; const validationMessage = "Error message"; const select = renderMultiSelect({ description, validationMessage }); select.shouldHaveAriaDescribedByBoth(); select.shouldHaveAriaInvalid(true); }); (0, vitest_1.test)("has aria-required when hasRequiredMarker is true", () => { const select = renderMultiSelect({ hasRequiredMarker: true }); select.shouldHaveAriaRequired(true); }); (0, vitest_1.test)("has aria-activedescendant pointing to focused option when navigating with keyboard", async () => { const select = renderMultiSelect({ items, value: [option1.value] }); await select.click(); select.shouldHaveAriaActiveDescendantForOption("Option 1"); await select.pressArrowDown(); select.shouldHaveAriaActiveDescendantForOption("Option 2"); }); (0, vitest_1.test)("does not have aria-activedescendant when dropdown is closed", () => { const select = renderMultiSelect({ items, value: [option1.value] }); select.shouldNotHaveAriaActiveDescendant(); }); }); (0, vitest_1.describe)("required validation", () => { (0, vitest_1.test)("does not show a required error before any change is made, even when empty", () => { const select = renderMultiSelect({ required: true, value: [] }); select.shouldNotHaveError("This field is required."); }); (0, vitest_1.test)("shows a required error once all selected values are cleared", async () => { const select = renderMultiSelect({ required: true, value: [option1.value, option2.value], allowClear: true }); await select.clickClearButton(); select.shouldHaveError("This field is required."); }); (0, vitest_1.test)("does not show a required error while at least one value remains selected", async () => { const select = renderMultiSelect({ required: true, items: colouredItems, value: [colouredOption1.value, colouredOption2.value] }); await select.dismissSelectedOption("Frontend"); select.shouldNotHaveError("This field is required."); }); }); (0, vitest_1.describe)("clear button", () => { (0, vitest_1.test)("does not render clear button by default", () => { const select = renderMultiSelect({ value: [option1.value, option2.value] }); select.shouldNotShowClearButton(); }); (0, vitest_1.test)("does not render clear button when no value is selected", () => { const select = renderMultiSelect({ value: undefined, allowClear: true }); select.shouldNotShowClearButton(); }); (0, vitest_1.test)("does not show clear button when single value is selected", () => { const select = renderMultiSelect({ value: [option1.value], allowClear: true }); select.shouldNotShowClearButton(); }); (0, vitest_1.test)("shows clear button when multiple values selected", () => { const select = renderMultiSelect({ value: [option1.value, option2.value], allowClear: true }); select.shouldShowClearButton(); }); (0, vitest_1.test)("does not render clear button when disabled with multiple values selected", () => { const select = renderMultiSelect({ value: [option1.value, option2.value], disabled: true, allowClear: true }); select.shouldNotShowClearButton(); }); (0, vitest_1.test)("does not render clear button when readonly with multiple values selected", () => { const select = renderMultiSelect({ value: [option1.value, option2.value], readOnly: true, allowClear: true }); select.shouldNotShowClearButton(); }); (0, vitest_1.test)("calls onChange with empty array when clear button is clicked", async () => { const select = renderMultiSelect({ value: [option1.value, option2.value], allowClear: true }); await select.clickClearButton(); select.shouldHaveCalledOnChangeWith([]); }); }); (0, vitest_1.describe)("filtering", () => { (0, vitest_1.test)("shows search input when allowFilter is true", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); select.shouldShowSearchInput(); }); (0, vitest_1.test)("filters options based on search text", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.typeInSearchInput("1"); select.shouldShowOnlyOption("Option 1"); }); (0, vitest_1.test)("filters options case-insensitively", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.typeInSearchInput("OPTION 2"); select.shouldShowOnlyOption("Option 2"); }); (0, vitest_1.test)("shows multiple matching options when filter matches", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.typeInSearchInput("Option"); select.shouldShowAllOptions(items); }); (0, vitest_1.test)("shows no results message when filter matches nothing", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.typeInSearchInput("xyz"); select.shouldShowNoResultsMessage(); }); (0, vitest_1.test)("clears filter when dropdown closes", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.typeInSearchInput("1"); select.shouldShowOnlyOption("Option 1"); await select.pressEscape(); await select.click(); select.shouldShowAllOptions(items); }); (0, vitest_1.test)("navigates filtered options with keyboard", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.pressArrowDown(); select.shouldHaveOptionActive("Option 2"); }); (0, vitest_1.test)("selects filtered option with Enter key from search input", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.typeInSearchInput("Option 2"); await select.pressEnter(); select.shouldHaveCalledOnChangeWith([option2.value]); }); (0, vitest_1.test)("jumps to first option with Home key in search input", async () => { const select = renderMultiSelect({ allowFilter: true, value: [option3.value] }); await select.click(); await select.pressHome(); select.shouldHaveOptionActive("Option 1"); }); (0, vitest_1.test)("jumps to last option with End key in search input", async () => { const select = renderMultiSelect({ allowFilter: true }); await select.click(); await select.pressEnd(); select.shouldHaveOptionActive("Option 3"); }); (0, vitest_1.test)("search input has correct aria attributes", async () => { const select = renderMultiSelect({ label: "Test Select", allowFilter: true }); await select.click(); select.shouldHaveSearchInputWithAriaLabel("Filter Test Select"); }); }); (0, vitest_1.describe)("AsyncList items", () => { (0, vitest_1.test)("displays selected option from getItemById when not in loadedItems", () => { const asyncList = createMockAsyncList({ loadedItems: [], getItemById: (id) => (id === "1" ? option1 : undefined), }); const select = renderMultiSelect({ items: asyncList, value: [option1.value] }); select.shouldShowSelectedOption("Option 1"); }); (0, vitest_1.test)("displays loaded items in dropdown", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldShowAllOptions([option1, option2, option3]); }); (0, vitest_1.test)("calls refresh when the refresh button is clicked", async () => { const refresh = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1], refresh }); const select = renderMultiSelect({ items: asyncList }); await select.clickRefreshButton(); (0, vitest_1.expect)(refresh).toHaveBeenCalledTimes(1); }); (0, vitest_1.test)("displays loading indicator when isLoading is true", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1], isLoading: true, }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldShowLoadingIndicator(); }); (0, vitest_1.test)("does not display loading indicator when isLoading is false", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1], isLoading: false, }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldNotShowLoadingIndicator(); }); (0, vitest_1.test)("calls loadMore when scrolling in dropdown with more items available", async () => { const loadMore = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1, option2], loadMore, }); const select = renderMultiSelect({ items: asyncList }); await select.click(); await select.triggerScrollForLoadMore(); (0, vitest_1.expect)(loadMore).toHaveBeenCalled(); }); (0, vitest_1.test)("does not call loadMore when isLoading is true", async () => { const loadMore = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1, option2], isLoading: true, loadMore, }); const select = renderMultiSelect({ items: asyncList }); await select.click(); await select.triggerScrollForLoadMore(); (0, vitest_1.expect)(loadMore).not.toHaveBeenCalled(); }); (0, vitest_1.test)("can select option from loaded items", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], }); const select = renderMultiSelect({ items: asyncList, value: [] }); await select.click(); await select.selectOption("Option 2"); select.shouldHaveCalledOnChangeWith([option2.value]); }); (0, vitest_1.describe)("with filtering", () => { (0, vitest_1.test)("shows search input when allowFilter is true with AsyncList", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], }); const select = renderMultiSelect({ items: asyncList, allowFilter: true }); await select.click(); select.shouldShowSearchInput(); }); (0, vitest_1.test)("calls setFilterText when typing in search input with AsyncList", async () => { const setFilterText = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], setFilterText, }); const select = renderMultiSelect({ items: asyncList, allowFilter: true }); await select.click(); await select.typeInSearchInput("test"); (0, vitest_1.expect)(setFilterText).toHaveBeenCalledWith("test"); }); (0, vitest_1.test)("displays all loadedItems without client-side filtering when using AsyncList", async () => { const setFilterText = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1], setFilterText, }); const select = renderMultiSelect({ items: asyncList, allowFilter: true }); await select.click(); await select.typeInSearchInput("2"); select.shouldShowOnlyOption("Option 1"); (0, vitest_1.expect)(setFilterText).toHaveBeenCalledWith("2"); }); (0, vitest_1.test)("shows no results when loadedItems is empty after server filtering", async () => { const setFilterText = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [], setFilterText, }); const select = renderMultiSelect({ items: asyncList, allowFilter: true }); await select.click(); await select.typeInSearchInput("xyz"); select.shouldShowNoResultsMessage(); }); (0, vitest_1.test)("clears filter text and calls setFilterText when dropdown closes", async () => { const setFilterText = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1], setFilterText, }); const select = renderMultiSelect({ items: asyncList, allowFilter: true }); await select.click(); await select.typeInSearchInput("test"); setFilterText.mockClear(); await select.pressEscape(); (0, vitest_1.expect)(setFilterText).toHaveBeenCalledWith(""); }); }); (0, vitest_1.describe)("with error", () => { (0, vitest_1.test)("shows loaded items when last load has an error", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], error: new Error(), }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldShowAllOptions([option1, option2, option3]); }); (0, vitest_1.test)("shows error message when last load has an error", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], error: new Error(), }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldHaveError("Failed to load results"); }); (0, vitest_1.test)("shows error message when there is an error and no items", async () => { const asyncList = createMockAsyncList({ loadedItems: [], error: new Error(), }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldHaveError("Failed to load results"); }); (0, vitest_1.test)("shows retry button when last load has an error", async () => { const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], error: new Error(), loadMore: vitest_1.vi.fn(), }); const select = renderMultiSelect({ items: asyncList }); await select.click(); select.shouldHaveRetryButton(); }); (0, vitest_1.test)("calls loadMore when retry is clicked", async () => { const loadMore = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], error: new Error(), loadMore, }); const select = renderMultiSelect({ items: asyncList }); await select.click(); await select.clickRetryButton(); (0, vitest_1.expect)(loadMore).toHaveBeenCalled(); }); (0, vitest_1.test)("does not call loadMore on scroll when there is an error", async () => { const loadMore = vitest_1.vi.fn(); const asyncList = createMockAsyncList({ loadedItems: [option1, option2, option3], error: new Error(), loadMore, }); const select = renderMultiSelect({ items: asyncList }); await select.click(); await select.triggerScrollForLoadMore(); (0, vitest_1.expect)(loadMore).not.toHaveBeenCalled(); }); }); }); (0, vitest_1.describe)("select/deselect all", () => { (0, vitest_1.test)("shows select all button when dropdown is open and no options are selected", async () => { const select = renderMultiSelect({ value: [] }); await select.click(); select.shouldShowSelectAllButton(); }); (0, vitest_1.test)("shows deselect all button when dropdown is open and some options are selected", async () => { const select = renderMultiSelect({ value: [option1.value] }); await select.click(); select.shouldShowDeselectAllButton(); }); (0, vitest_1.test)("shows deselect all button when all options are selected", async () => { const select = renderMultiSelect({ value: [option1.value, option2.value, option3.value] }); await select.click(); select.shouldShowDeselectAllButton(); }); (0, vitest_1.test)("selects all visible options when select all button is clicked", async () => { const select = renderMultiSelect({ value: [] }); await select.click(); await select.clickSelectAllButton(); select.shouldHaveCalledOnChangeWith([option1.value, option2.value, option3.value]); }); (0, vitest_1.test)("deselects all visible options when deselect all button is clicked", async () => { const select = renderMultiSelect({ value: [option1.value, option2.value, option3.value] }); await select.click(); await select.clickDeselectAllButton(); select.shouldHaveCalledOnChangeWith([]); }); (0, vitest_1.test)("deselects only visible selected options when some are selected", async () => { const select = renderMultiSelect({ value: [option1.value, option2.value] }); await select.click(); await select.clickDeselectAllButton(); select.shouldHaveCalledOnChangeWith([]); }); (0, vitest_1.test)("select all button is disabled when no options are available", async () => { const select = renderMultiSelect({ items: [], value: [] }); await select.click(); select.shouldHaveSelectAllButtonDisabled(); }); (0, vitest_1.describe)("with filtering", () => { (0, vitest_1.test)("selects all filtered options when select all is clicked", async () => { const select = renderMultiSelect({ allowFilter: true, value: [] }); await select.click(); await select.typeInSearchInput("1"); await select.clickSelectAllButton(); select.shouldHaveCalledOnChangeWith([option1.value]); }); (0, vitest_1.test)("deselects all filtered options when deselect all is clicked", async () => { const select = renderMultiSelect({ allowFilter: true, value: [option1.value, option2.value, option3.value] }); await select.click(); await select.typeInSearchInput("1"); await select.clickDeselectAllButton(); select.shouldHaveCalledOnChangeWith([option2.value, option3.value]); }); (0, vitest_1.test)("shows select all button when filtering results in no selected options", async () => { const select = renderMultiSelect({ allowFilter: true, value: [option1.value] }); await select.click(); await select.typeInSearchInput("2"); select.shouldShowSelectAllButton(); }); (0, vitest_1.test)("shows deselect all button when filtering results in some selected options", async () => { const select = renderMultiSelect({ allowFilter: true, value: [option1.value, option2.value] }); await select.click(); await select.typeInSearchInput("1"); select.shouldShowDeselectAllButton(); }); (0, vitest_1.test)("select all button is disabled when filter matches no options", async () => { const select = renderMultiSelect({ allowFilter: true, value: [] }); await select.click(); await select.typeInSearchInput("xyz"); select.shouldHaveSelectAllButtonDisabled(); }); }); }); (0, vitest_1.describe)("category tags", () => { (0, vitest_1.test)("shows the label for a single selected option with a colour", () => { const select = renderMultiSelect({ items: colouredItems, value: [colouredOption1.value] }); select.shouldShowSelectedOption("Frontend"); }); (0, vitest_1.test)("dismisses a single selected option with a colour", async () => { const select = renderMultiSelect({ items: colouredItems, value: [colouredOption1.value] }); await select.dismissSelectedOption("Frontend"); select.shouldHaveCalledOnChangeWith([]); }); (0, vitest_1.test)("shows all selected labels for multiple options with colours", () => { const select = renderMultiSelect({ items: colouredItems, value: [colouredOption1.value, colouredOption2.value] }); select.shouldShowSelectedOptions(["Frontend", "Backend"]); }); (0, vitest_1.test)("dismisses one of several selected options with colours", async () => { const select = renderMultiSelect({ items: colouredItems, value: [colouredOption1.value, colouredOption2.value] }); await select.dismissSelectedOption("Frontend"); select.shouldHaveCalledOnChangeWith([colouredOption2.value]); }); }); }); // Keeps the field genuinely controlled (mirroring how a real consumer feeds onChange back into value), so // SelectBase's required-validation (which detects a change by comparing the value prop across renders) can // actually observe a change instead of the value staying fixed at whatever the test passed in initially. function ControlledMultiSelect({ onChange, value: initialValue, ...props }) { const [value, setValue] = (0, react_2.useState)(initialValue); return ((0, jsx_runtime_1.jsx)(MultiSelect_1.MultiSelect, { ...props, value: value, onChange: (newValue) => { setValue(newValue); onChange(newValue); } })); } function renderMultiSelect(opts = {}) { const { withForm, onSubmit, ...selectProps } = opts; const onChange = vitest_1.vi.fn(); const props = { label: "Test Label", items: items, getOption: (item) => item, value: [], onChange, ...selectProps, }; const component = withForm ? ((0, jsx_runtime_1.jsx)("form", { onSubmit: onSubmit, children: (0, jsx_runtime_1.jsx)(ControlledMultiSelect, { ...props }) })) : ((0, jsx_runtime_1.jsx)(ControlledMultiSelect, { ...props })); (0, react_1.render)((0, jsx_runtime_1.jsx)(TestDesignSystemProvider_1.TestDesignSystemProvider, { children: component })); return { ...getMultiSelectInteractions(props.label, onChange), onSubmit }; } function getMultiSelectInteractions(label, onChange) { return { ...(0, getSharedSelectInteractions_1.getSharedSelectInteractions)(label, onChange), clickSelectAllButton: async () => { return testing_library_1.domQueries.getButton("Select all").click(); }, clickDeselectAllButton: async () => { return testing_library_1.domQueries.getButton("Deselect all").click(); }, shouldShowSelectedOption: (optionLabel) => { testing_library_1.domQueries.getMultiSelect(label).assertSelectedOption(optionLabel); }, shouldShowSelectedOptions: (optionLabels) => { testing_library_1.domQueries.getMultiSelect(label).assertSelectedOptions(optionLabels); }, dismissSelectedOption: async (optionLabel) => { return testing_library_1.domQueries.getMultiSelect(label).removeSelectedOption(optionLabel); }, shouldHaveCalledOnChangeWith: (value) => { (0, vitest_1.expect)(onChange).toHaveBeenCalledWith(value); }, shouldShowSelectAllButton: () => { testing_library_1.domQueries.getButton("Select all", { completeName: true }).assertIsInTheDocument(); }, shouldShowDeselectAllButton: () => { testing_library_1.domQueries.getButton("Deselect all", { completeName: true }).assertIsInTheDocument(); }, shouldNotShowSelectDeselectAllButton: () => { testing_library_1.domQueries.queryButton("Select all", { completeName: true }).assertIsNotInTheDocument(); testing_library_1.domQueries.queryButton("Deselect all", { completeName: true }).assertIsNotInTheDocument(); }, shouldHaveSelectAllButtonDisabled: () => { testing_library_1.domQueries.getButton("Select all", { completeName: true }).assertIsDisabled(); }, }; } function createMockAsyncList(options) { return { getItemId: options.getItemId ?? ((item) => item.value), getItemById: options.getItemById ?? (() => undefined), loadedItems: options.loadedItems ?? [], isLoading: options.isLoading ?? false, error: options.error ?? null, loadMore: options.loadMore, setFilterText: options.setFilterText ?? vitest_1.vi.fn(), refresh: options.refresh, }; }