diff --git a/.changeset/three-keys-add.md b/.changeset/three-keys-add.md new file mode 100644 index 000000000..1f69d7919 --- /dev/null +++ b/.changeset/three-keys-add.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/wonder-blocks-icon-button": minor +--- + +Add type=submit prop to allow submitting forms with the button diff --git a/__docs__/wonder-blocks-icon-button/icon-button.stories.tsx b/__docs__/wonder-blocks-icon-button/icon-button.stories.tsx index 7e1e004f2..806ec390b 100644 --- a/__docs__/wonder-blocks-icon-button/icon-button.stories.tsx +++ b/__docs__/wonder-blocks-icon-button/icon-button.stories.tsx @@ -21,6 +21,7 @@ import {color, spacing} from "@khanacademy/wonder-blocks-tokens"; import ComponentInfo from "../../.storybook/components/component-info"; import packageConfig from "../../packages/wonder-blocks-icon-button/package.json"; import IconButtonArgtypes from "./icon-button.argtypes"; +import TextField from "../../packages/wonder-blocks-form/src/components/text-field"; /** * An `IconButton` is a button whose contents are an SVG image. @@ -355,6 +356,45 @@ export const WithRouter: StoryComponentType = { ), }; +/** + * If the button is inside a form, you can use the `type="submit"` prop, so the + * form will be submitted on click or by pressing `Enter`. + */ +export const SubmittingForms: StoryComponentType = { + name: "Submitting forms", + render: () => ( +
{ + e.preventDefault(); + console.log("form submitted"); + action("form submitted")(e); + }} + > + + + Search:{" "} + {}} + /> + + + +
+ ), + parameters: { + chromatic: { + // We are testing the form submission, not UI changes. + disableSnapshot: true, + }, + }, +}; + const styles = StyleSheet.create({ dark: { backgroundColor: color.darkBlue, @@ -366,6 +406,7 @@ const styles = StyleSheet.create({ width: spacing.xxxLarge_64, }, row: { + display: "flex", flexDirection: "row", gap: spacing.medium_16, alignItems: "center", diff --git a/packages/wonder-blocks-icon-button/src/components/__tests__/icon-button.test.tsx b/packages/wonder-blocks-icon-button/src/components/__tests__/icon-button.test.tsx index 6145231b8..cc4f6d215 100644 --- a/packages/wonder-blocks-icon-button/src/components/__tests__/icon-button.test.tsx +++ b/packages/wonder-blocks-icon-button/src/components/__tests__/icon-button.test.tsx @@ -320,4 +320,51 @@ describe("IconButton", () => { expect(onClickMock).toHaveBeenCalledTimes(1); }); }); + + describe("type='submit'", () => { + it("should submit button within form via click", async () => { + // Arrange + const submitFnMock = jest.fn(); + render( +
+ + , + ); + + // Act + const button = await screen.findByRole("button"); + await userEvent.click(button); + + // Assert + expect(submitFnMock).toHaveBeenCalled(); + }); + + it("should submit button within form via keyboard", async () => { + // Arrange + const submitFnMock = jest.fn(); + render( +
+ + , + ); + + // Act + const button = await screen.findByRole("button"); + await userEvent.type(button, "{enter}"); + + // Assert + expect(submitFnMock).toHaveBeenCalled(); + }); + + it("should submit button doesn't break if it's not in a form", async () => { + // Arrange + render(); + + // Act + expect(async () => { + // Assert + await userEvent.click(await screen.findByRole("button")); + }).not.toThrow(); + }); + }); }); diff --git a/packages/wonder-blocks-icon-button/src/components/icon-button.tsx b/packages/wonder-blocks-icon-button/src/components/icon-button.tsx index 8b2ed1e13..e460bdabe 100644 --- a/packages/wonder-blocks-icon-button/src/components/icon-button.tsx +++ b/packages/wonder-blocks-icon-button/src/components/icon-button.tsx @@ -40,6 +40,10 @@ export type SharedProps = Partial> & { * Test ID used for e2e testing. */ testId?: string; + /** + * Used for icon buttons within
s. + */ + type?: "submit"; /** * Size of the icon button. * One of `xsmall` (16 icon, 20 target), `small` (24, 32), `medium` (24, 40), @@ -181,6 +185,7 @@ export const IconButton: React.ForwardRefExoticComponent< skipClientNav, tabIndex, target, + type, ...sharedProps } = props; @@ -219,6 +224,7 @@ export const IconButton: React.ForwardRefExoticComponent< tabIndex={tabIndex} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} + type={type} /> );