Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WB-1779: Add startIcon prop to Combobox #2364

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/old-pears-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/wonder-blocks-dropdown": minor
---

Add `startIcon` prop to Combobox
17 changes: 17 additions & 0 deletions __docs__/wonder-blocks-dropdown/combobox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import {Meta, StoryObj} from "@storybook/react";
import {expect, userEvent, within} from "@storybook/test";
import {StyleSheet} from "aphrodite";
import * as React from "react";
import magnifyingGlassIcon from "@phosphor-icons/core/regular/magnifying-glass.svg";

import {LabelLarge} from "@khanacademy/wonder-blocks-typography";
import {color, spacing} from "@khanacademy/wonder-blocks-tokens";
import {Checkbox} from "@khanacademy/wonder-blocks-form";
import {Combobox, OptionItem} from "@khanacademy/wonder-blocks-dropdown";
import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon";
import {PropsFor, View} from "@khanacademy/wonder-blocks-core";
import {allProfilesWithPictures} from "./option-item-examples";

Expand Down Expand Up @@ -428,3 +431,17 @@ export const Error: Story = {
error: true,
},
};

/**
* With start icon, you can customize the icon that appears at the beginning of
* the Combobox. This is useful when you want to add a custom icon to the
* component.
*
* In this example, we show how this is done by setting the `startIcon` prop.
*/
export const StartIcon: Story = {
args: {
children: items,
startIcon: <PhosphorIcon icon={magnifyingGlassIcon} size="medium" />,
},
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as React from "react";
import {render, screen, waitFor} from "@testing-library/react";
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
import {render, screen, waitFor} from "@testing-library/react";
import * as React from "react";
import magnifyingGlassIcon from "@phosphor-icons/core/regular/magnifying-glass.svg";

import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon";
import {PointerEventsCheckLevel, userEvent} from "@testing-library/user-event";
import Combobox from "../combobox";
import OptionItem from "../option-item";
import {defaultComboboxLabels} from "../../util/constants";
import {MaybeValueOrValues} from "../../util/types";
import Combobox from "../combobox";
import OptionItem from "../option-item";

const doRender = (element: React.ReactElement) => {
render(element, {wrapper: RenderStateRoot});
Expand Down Expand Up @@ -305,6 +307,31 @@ describe("Combobox", () => {
expect(screen.getByRole("combobox")).not.toHaveFocus();
});

it("should include an icon at the beginning of the combobox", () => {
// Arrange

// Act
doRender(
<Combobox
selectionType="single"
value=""
startIcon={
<PhosphorIcon
icon={magnifyingGlassIcon}
testId="start-icon"
/>
}
>
<OptionItem label="option 1" value="option1" />
<OptionItem label="option 2" value="option2" />
<OptionItem label="option 3" value="option3" />
</Combobox>,
);

// Assert
expect(screen.getByTestId("start-icon")).toBeInTheDocument();
});

describe("dismiss button", () => {
it("should clear the value when the user presses the clear button (x) via Mouse", async () => {
// Arrange
Expand Down
21 changes: 21 additions & 0 deletions packages/wonder-blocks-dropdown/src/components/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import IconButton from "@khanacademy/wonder-blocks-icon-button";
import {border, color, spacing} from "@khanacademy/wonder-blocks-tokens";

import {DetailCell} from "@khanacademy/wonder-blocks-cell";
import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon";
import {useListbox} from "../hooks/use-listbox";
import {useMultipleSelection} from "../hooks/use-multiple-selection";
import {
Expand Down Expand Up @@ -132,6 +133,14 @@ type Props = {
*/
// TODO(WB-1740): Add support to `inline` and `both` values.
autoComplete?: "none" | "list" | undefined;

/**
* The icon (wrapped by a button) that allows opening the listbox widget
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the prop docs need to be updated for startIcon!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh oops, changing it! (I copy pasted from the impl. spec so I'm going to update it there as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! btw I also modified the new story to include both the default and disabled state.

I'm thinking about creating an All Variants story to capture all the Combobox styles, but I'd do it in a separate PR. wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me!

* when pressed/activated. Defaults to caretDown.
*/
startIcon?: React.ReactElement<
React.ComponentProps<typeof PhosphorIcon>
> | null;
};

/**
Expand All @@ -158,6 +167,7 @@ export default function Combobox({
opened,
placeholder,
selectionType = "single",
startIcon,
testId,
value = "",
}: Props) {
Expand Down Expand Up @@ -534,6 +544,10 @@ export default function Combobox({
removeSelectedLabel={labels.removeSelected}
/>
)}
{startIcon && (
<View style={styles.iconWrapper}>{startIcon}</View>
)}

<TextField
id={ids.get("input")}
testId={testId}
Expand Down Expand Up @@ -739,4 +753,11 @@ const styles = StyleSheet.create({
// This is calculated based on the padding + width of the arrow button.
right: spacing.xLarge_32 + spacing.xSmall_8,
},
iconWrapper: {
padding: spacing.xxxSmall_4,
// View has a default minWidth of 0, which causes the label text
// to encroach on the icon when it needs to truncate. We can fix
// this by setting the minWidth to auto.
minWidth: "auto",
},
});
Loading