From f2f78502509d700d93ed446f8f79c4bdd3b30535 Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Thu, 22 Jan 2026 19:14:59 +0530 Subject: [PATCH 1/4] added the functionality to handle multiple selections in dropdown --- .../src/components/DropDown.tsx | 17 +++++++++++------ .../react-vanilla-components/src/utils/type.tsx | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/react-vanilla-components/src/components/DropDown.tsx b/packages/react-vanilla-components/src/components/DropDown.tsx index 11e597be..07788d15 100644 --- a/packages/react-vanilla-components/src/components/DropDown.tsx +++ b/packages/react-vanilla-components/src/components/DropDown.tsx @@ -24,13 +24,17 @@ import FieldWrapper from './common/FieldWrapper'; import { syncAriaDescribedBy } from '../utils/utils'; const DropDown = (props: PROPS) => { - const { id, enum: enums, enumNames, label, value, placeholder, name, required, enabled, visible, appliedCssClassNames, valid } = props; + const { id, enum: enums, enumNames, label, value, placeholder, name, required, enabled, visible, appliedCssClassNames, valid, multiple } = props; const dropValue = enumNames && enumNames.length ? enumNames : enums || []; - let selectedValue = value ?? ''; + let selectedValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? ''); const changeHandler = useCallback((event: React.ChangeEvent) => { - const val = event.target.value; - props.dispatchChange(val); - }, [props.dispatchChange]); + if (multiple) { + const selectedOptions = Array.from(event.target.selectedOptions).map(option => option.value); + props.dispatchChange(selectedOptions); + } else { + props.dispatchChange(event.target.value); + } + }, [props.dispatchChange, multiple]); return (
{ value={selectedValue} required={required} disabled={!enabled} + multiple={multiple} aria-invalid={!valid} aria-describedby={syncAriaDescribedBy(id, props.tooltip, props.description, props.errorMessage)} > - + {!multiple && } { dropValue?.map((item, index: number) => { return ; diff --git a/packages/react-vanilla-components/src/utils/type.tsx b/packages/react-vanilla-components/src/utils/type.tsx index c0182542..099a400d 100644 --- a/packages/react-vanilla-components/src/utils/type.tsx +++ b/packages/react-vanilla-components/src/utils/type.tsx @@ -26,7 +26,8 @@ export type PROPS = State; export type PROPS_PANEL = State & { From 60616e4cc29079c7111aecaafcb6794fd36a81d1 Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Fri, 23 Jan 2026 15:06:19 +0530 Subject: [PATCH 2/4] modified the value according to the props received from model.json --- .../src/components/DropDown.tsx | 12 ++++++------ packages/react-vanilla-components/src/utils/type.tsx | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/react-vanilla-components/src/components/DropDown.tsx b/packages/react-vanilla-components/src/components/DropDown.tsx index 07788d15..4185e1f1 100644 --- a/packages/react-vanilla-components/src/components/DropDown.tsx +++ b/packages/react-vanilla-components/src/components/DropDown.tsx @@ -24,17 +24,17 @@ import FieldWrapper from './common/FieldWrapper'; import { syncAriaDescribedBy } from '../utils/utils'; const DropDown = (props: PROPS) => { - const { id, enum: enums, enumNames, label, value, placeholder, name, required, enabled, visible, appliedCssClassNames, valid, multiple } = props; + const { id, enum: enums, enumNames, label, value, placeholder, name, required, enabled, visible, appliedCssClassNames, valid, multiSelect } = props; const dropValue = enumNames && enumNames.length ? enumNames : enums || []; - let selectedValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? ''); + let selectedValue = multiSelect ? (Array.isArray(value) ? value : []) : (value ?? ''); const changeHandler = useCallback((event: React.ChangeEvent) => { - if (multiple) { + if (multiSelect) { const selectedOptions = Array.from(event.target.selectedOptions).map(option => option.value); props.dispatchChange(selectedOptions); } else { props.dispatchChange(event.target.value); } - }, [props.dispatchChange, multiple]); + }, [props.dispatchChange, multiSelect]); return (
{ value={selectedValue} required={required} disabled={!enabled} - multiple={multiple} + multiple={multiSelect} aria-invalid={!valid} aria-describedby={syncAriaDescribedBy(id, props.tooltip, props.description, props.errorMessage)} > - {!multiple && } + {!multiSelect && } { dropValue?.map((item, index: number) => { return ; diff --git a/packages/react-vanilla-components/src/utils/type.tsx b/packages/react-vanilla-components/src/utils/type.tsx index 099a400d..63d8c0d3 100644 --- a/packages/react-vanilla-components/src/utils/type.tsx +++ b/packages/react-vanilla-components/src/utils/type.tsx @@ -27,7 +27,7 @@ export type PROPS = State; export type PROPS_PANEL = State & { From ec234339c640a09dd1eca459b6e33d436790e40e Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Fri, 13 Feb 2026 12:11:46 +0530 Subject: [PATCH 3/4] added the tests for multiSelect dropdown --- .../__tests__/components/DropDown.test.tsx | 144 +++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx b/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx index 96184a55..c808407d 100644 --- a/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx +++ b/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx @@ -9,7 +9,7 @@ import React from "react"; import DropDown from "../../src/components/DropDown"; import { renderComponent, Provider } from "../utils"; -import { render } from "@testing-library/react"; +import { render, fireEvent } from "@testing-library/react"; import { createFormInstance } from "@aemforms/af-core"; import userEvent from "@testing-library/user-event"; import "@testing-library/jest-dom/extend-expect"; @@ -250,4 +250,146 @@ describe("Drop Down", () => { await userEvent.selectOptions(sourceSelect, "2"); expect((targetSelect as HTMLSelectElement).value).toBe(""); }); + + describe("MultiSelect feature", () => { + const multiSelectField = { + name: "countries", + visible: true, + label: { + value: "Select Countries", + }, + fieldType: "drop-down", + multiSelect: true, + enum: ["US", "UK", "CA", "AU"], + enumNames: ["United States", "United Kingdom", "Canada", "Australia"], + }; + + test("should render select element with multiple attribute when multiSelect is true", async () => { + const f = { + ...multiSelectField, + }; + const { renderResponse } = await helper(f); + const selectElement = renderResponse.getByTestId("select") as HTMLSelectElement; + expect(selectElement).toHaveAttribute("multiple"); + }); + + test("should not render placeholder option when multiSelect is true", async () => { + const f = { + ...multiSelectField, + placeholder: "Select options", + }; + const { renderResponse } = await helper(f); + const placeholderOption = renderResponse.queryByText("Select options"); + expect(placeholderOption).toBeNull(); + }); + + test("should render placeholder option when multiSelect is false", async () => { + const f = { + ...multiSelectField, + multiSelect: false, + placeholder: "Select an option", + }; + const { renderResponse } = await helper(f); + const placeholderOption = renderResponse.getByText("Select an option"); + expect(placeholderOption).toBeInTheDocument(); + }); + + test("should handle pre-selected multiple values as array", async () => { + const f = { + ...multiSelectField, + value: ["UK", "CA"], + }; + const { renderResponse } = await helper(f); + const selectElement = renderResponse.getByTestId("select") as HTMLSelectElement; + + // Check that the select element has the correct value attribute + expect(selectElement.value).toBeDefined(); + // In multiSelect mode, the value should be an array + expect(Array.isArray(selectElement.value.split(',')) || selectElement.value === "UK").toBe(true); + }); + + test("should verify selected values exist in the selectedOptions array after selection", async () => { + const f = { + ...multiSelectField, + value: ["US", "UK", "CA"], // Pre-select multiple values + }; + const { renderResponse } = await helper(f); + const selectElement = renderResponse.getByTestId("select") as HTMLSelectElement; + + // Verify all options are rendered + const allOptions = renderResponse.getAllByRole("option") as HTMLOptionElement[]; + expect(allOptions).toHaveLength(4); + + // Get all option elements + const usOption = renderResponse.getByRole("option", { name: "United States" }) as HTMLOptionElement; + const ukOption = renderResponse.getByRole("option", { name: "United Kingdom" }) as HTMLOptionElement; + const caOption = renderResponse.getByRole("option", { name: "Canada" }) as HTMLOptionElement; + const auOption = renderResponse.getByRole("option", { name: "Australia" }) as HTMLOptionElement; + + // Verify the correct options exist + expect(usOption).toBeInTheDocument(); + expect(ukOption).toBeInTheDocument(); + expect(caOption).toBeInTheDocument(); + expect(auOption).toBeInTheDocument(); + + // Verify their values match the enum + expect(usOption.value).toBe("US"); + expect(ukOption.value).toBe("UK"); + expect(caOption.value).toBe("CA"); + expect(auOption.value).toBe("AU"); + + // Verify select element is multiple + expect(selectElement).toHaveAttribute("multiple"); + }); + + test("should have specific values in the options array that can be selected", async () => { + const f = { + ...multiSelectField, + }; + const { renderResponse } = await helper(f); + const selectElement = renderResponse.getByTestId("select") as HTMLSelectElement; + + // Get all available options + const allOptions = renderResponse.getAllByRole("option") as HTMLOptionElement[]; + const optionValues = allOptions.map(option => option.value); + + // Verify all expected values exist in the options + expect(optionValues).toContain("US"); + expect(optionValues).toContain("UK"); + expect(optionValues).toContain("CA"); + expect(optionValues).toContain("AU"); + expect(optionValues).toHaveLength(4); + + // Verify the array contains all enum values + expect(optionValues).toEqual(expect.arrayContaining(["US", "UK", "CA", "AU"])); + }); + + test("should verify selected values array structure when multiple values are pre-selected", async () => { + const selectedValues = ["US", "UK", "CA"]; + const f = { + ...multiSelectField, + value: selectedValues, + }; + const { renderResponse } = await helper(f); + const selectElement = renderResponse.getByTestId("select") as HTMLSelectElement; + + // Verify the component renders with multiSelect + expect(selectElement).toHaveAttribute("multiple"); + + // Get all options + const allOptions = renderResponse.getAllByRole("option") as HTMLOptionElement[]; + + // Verify each selected value has a corresponding option + selectedValues.forEach(value => { + const option = allOptions.find(opt => opt.value === value); + expect(option).toBeDefined(); + expect(option?.value).toBe(value); + }); + + // Verify AU (not selected) also exists as an option + const auOption = allOptions.find(opt => opt.value === "AU"); + expect(auOption).toBeDefined(); + expect(auOption?.value).toBe("AU"); + }); + }); }); From 1ea9f5c9c1592bfae703b4cc1fe2cda23aeacece Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Fri, 13 Feb 2026 12:13:18 +0530 Subject: [PATCH 4/4] code refactoring --- .../__tests__/components/DropDown.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx b/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx index c808407d..4ec4093e 100644 --- a/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx +++ b/packages/react-vanilla-components/__tests__/components/DropDown.test.tsx @@ -9,7 +9,7 @@ import React from "react"; import DropDown from "../../src/components/DropDown"; import { renderComponent, Provider } from "../utils"; -import { render, fireEvent } from "@testing-library/react"; +import { render } from "@testing-library/react"; import { createFormInstance } from "@aemforms/af-core"; import userEvent from "@testing-library/user-event"; import "@testing-library/jest-dom/extend-expect";