Skip to content

Commit

Permalink
IconButton: Add type=submit prop to allow submitting forms using the …
Browse files Browse the repository at this point in the history
…button (#2325)

## Summary:

This PR adds a new prop `type` to the `IconButton` component that allows the
button to submit a form when clicked or pressed. This is useful when we want to
use an IconButton to submit a form, for example, a search form.

This is done as I noticed that the IconButton component is missing the `type`
and was needed in the search page. The prop is currently set, but the type
definition is missing.

Issue: FEI-5728

## Test plan:

Docs:

- Navigate to /?path=/docs/packages-iconbutton--docs#submitting%20forms
- Verify that the docs make sense.

Interactive example:

- Navigate to /?path=/story/packages-iconbutton--submitting-forms
- Verify that the submit action is recorded in the SB `Actions` tab.

<img width="1396" alt="Screenshot 2024-09-19 at 10 14 56 AM" src="https://github.com/user-attachments/assets/1afb059a-6537-494e-af55-8cfe95c962ea">

Author: jandrade

Reviewers: beaesguerra, jandrade

Required Reviewers:

Approved By: beaesguerra

Checks: ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Lint (ubuntu-latest, 20.x), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald, ⏭️  dependabot

Pull Request URL: #2325
  • Loading branch information
jandrade authored Sep 19, 2024
1 parent 659a031 commit 3463bde
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-keys-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/wonder-blocks-icon-button": minor
---

Add type=submit prop to allow submitting forms with the button
41 changes: 41 additions & 0 deletions __docs__/wonder-blocks-icon-button/icon-button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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: () => (
<form
onSubmit={(e) => {
e.preventDefault();
console.log("form submitted");
action("form submitted")(e);
}}
>
<View style={styles.row}>
<LabelMedium tag="label" style={styles.row}>
Search:{" "}
<TextField
id="foo"
value="press the button"
onChange={() => {}}
/>
</LabelMedium>
<IconButton
icon={magnifyingGlass}
aria-label="Search"
type="submit"
/>
</View>
</form>
),
parameters: {
chromatic: {
// We are testing the form submission, not UI changes.
disableSnapshot: true,
},
},
};

const styles = StyleSheet.create({
dark: {
backgroundColor: color.darkBlue,
Expand All @@ -366,6 +406,7 @@ const styles = StyleSheet.create({
width: spacing.xxxLarge_64,
},
row: {
display: "flex",
flexDirection: "row",
gap: spacing.medium_16,
alignItems: "center",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<form onSubmit={submitFnMock}>
<IconButton icon={magnifyingGlassIcon} type="submit" />
</form>,
);

// 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(
<form onSubmit={submitFnMock}>
<IconButton icon={magnifyingGlassIcon} type="submit" />
</form>,
);

// 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(<IconButton icon={magnifyingGlassIcon} type="submit" />);

// Act
expect(async () => {
// Assert
await userEvent.click(await screen.findByRole("button"));
}).not.toThrow();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export type SharedProps = Partial<Omit<AriaProps, "aria-disabled">> & {
* Test ID used for e2e testing.
*/
testId?: string;
/**
* Used for icon buttons within <form>s.
*/
type?: "submit";
/**
* Size of the icon button.
* One of `xsmall` (16 icon, 20 target), `small` (24, 32), `medium` (24, 40),
Expand Down Expand Up @@ -181,6 +185,7 @@ export const IconButton: React.ForwardRefExoticComponent<
skipClientNav,
tabIndex,
target,
type,
...sharedProps
} = props;

Expand Down Expand Up @@ -219,6 +224,7 @@ export const IconButton: React.ForwardRefExoticComponent<
tabIndex={tabIndex}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
type={type}
/>
</ThemedIconButton>
);
Expand Down

0 comments on commit 3463bde

Please sign in to comment.