diff --git a/.changeset/twenty-ducks-double.md b/.changeset/twenty-ducks-double.md new file mode 100644 index 000000000..dd6d0b784 --- /dev/null +++ b/.changeset/twenty-ducks-double.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/wonder-blocks-dropdown": patch +--- + +MultiSelect: Clear error state when "Select none" or "Select all" shortcuts are used diff --git a/__docs__/wonder-blocks-dropdown/multi-select.stories.tsx b/__docs__/wonder-blocks-dropdown/multi-select.stories.tsx index bf34c6827..dbc9c4e38 100644 --- a/__docs__/wonder-blocks-dropdown/multi-select.stories.tsx +++ b/__docs__/wonder-blocks-dropdown/multi-select.stories.tsx @@ -398,6 +398,9 @@ export const ErrorFromValidation: StoryComponentType = { ); }, + args: { + shortcuts: true, + }, }; /** diff --git a/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx b/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx index 3bf0ad1e3..31b06b4bf 100644 --- a/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx +++ b/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx @@ -2583,6 +2583,106 @@ describe("MultiSelect", () => { "true", ); }); + + it("should call onValidate with null if values are cleared using the 'select none' shortcut", async () => { + // Arrange + const errorMessage = "Error message"; + const onValidate = jest.fn(); + const {userEvent} = doRender( + + values.includes("1") ? errorMessage : undefined + } + selectedValues={["1"]} + onValidate={onValidate} + shortcuts={true} + />, + ); + await userEvent.click(await screen.findByRole("button")); // Open the dropdown + onValidate.mockClear(); // Clear any calls + + // Act + await userEvent.click(await screen.findByText("Select none")); // Select none + + // Assert + expect(onValidate).toHaveBeenCalledExactlyOnceWith(null); + }); + + it("should not be in an error state if values are cleared using the 'select none' shortcut", async () => { + // Arrange + const errorMessage = "Error message"; + const {userEvent} = doRender( + + values.includes("1") ? errorMessage : undefined + } + selectedValues={["1"]} + shortcuts={true} + />, + ); + await userEvent.click(await screen.findByRole("button")); // Open the dropdown + + // Act + await userEvent.click(await screen.findByText("Select none")); // Select none + + // Assert + expect(await screen.findByRole("button")).toHaveAttribute( + "aria-invalid", + "false", + ); + }); + + it("should call onValidate with null if values are changed using the 'select all' shortcut", async () => { + // Arrange + const errorMessage = "Error message"; + const onValidate = jest.fn(); + const {userEvent} = doRender( + + values.includes("1") ? errorMessage : undefined + } + selectedValues={["1"]} + onValidate={onValidate} + shortcuts={true} + />, + ); + await userEvent.click(await screen.findByRole("button")); // Open the dropdown + onValidate.mockClear(); // Clear any calls + + // Act + await userEvent.click( + await screen.findByText("Select all (2)"), + ); // Select all + + // Assert + expect(onValidate).toHaveBeenCalledExactlyOnceWith(null); + }); + + it("should not be in an error state if values are changed using the 'select all' shortcut", async () => { + // Arrange + const errorMessage = "Error message"; + const {userEvent} = doRender( + + values.includes("1") ? errorMessage : undefined + } + selectedValues={["1"]} + shortcuts={true} + />, + ); + await userEvent.click(await screen.findByRole("button")); // Open the dropdown + + // Act + await userEvent.click( + await screen.findByText("Select all (2)"), + ); // Select all + + // Assert + expect(await screen.findByRole("button")).toHaveAttribute( + "aria-invalid", + "false", + ); + }); }); describe("required", () => { diff --git a/packages/wonder-blocks-dropdown/src/components/multi-select.tsx b/packages/wonder-blocks-dropdown/src/components/multi-select.tsx index 43090a503..f6e7a0b26 100644 --- a/packages/wonder-blocks-dropdown/src/components/multi-select.tsx +++ b/packages/wonder-blocks-dropdown/src/components/multi-select.tsx @@ -357,10 +357,12 @@ const MultiSelect = (props: Props) => { .filter((option) => !!option && !option.props.disabled) .map((option) => option.props.value); onChange(selected); + onSelectedValuesChangeValidation(); }; const handleSelectNone = () => { onChange([]); + onSelectedValuesChangeValidation(); }; const getMenuText = (