Skip to content

Commit 8523564

Browse files
authored
Add icon support to Segment Control options (#7435)
1 parent e365c08 commit 8523564

File tree

3 files changed

+34
-8
lines changed

3 files changed

+34
-8
lines changed

packages/core/src/components/segmented-control/segmentedControl.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ import { Button } from "../button/buttons";
3131

3232
export type SegmentedControlIntent = typeof Intent.NONE | typeof Intent.PRIMARY;
3333

34+
interface SegmentedControlOptionProps extends OptionProps<string> {
35+
icon?: ButtonProps["icon"];
36+
}
37+
3438
/**
3539
* SegmentedControl component props.
3640
*/
@@ -66,7 +70,7 @@ export interface SegmentedControlProps
6670
/**
6771
* List of available options.
6872
*/
69-
options: Array<OptionProps<string>>;
73+
options: SegmentedControlOptionProps[];
7074

7175
/**
7276
* Aria role for the overall component (container).
@@ -225,16 +229,22 @@ export const SegmentedControl: React.FC<SegmentedControlProps> = React.forwardRe
225229
});
226230
SegmentedControl.displayName = `${DISPLAYNAME_PREFIX}.SegmentedControl`;
227231

228-
interface SegmentedControlOptionProps
232+
interface SegmentedControlOptionComponentProps
229233
extends OptionProps<string>,
230234
Pick<SegmentedControlProps, "intent" | "small" | "large" | "size">,
231-
Pick<ButtonProps, "role" | "tabIndex">,
235+
Pick<ButtonProps, "role" | "tabIndex" | "icon">,
232236
React.AriaAttributes {
233237
isSelected: boolean;
234238
onClick: (value: string, targetElement: HTMLElement) => void;
235239
}
236240

237-
function SegmentedControlOption({ isSelected, label, onClick, value, ...buttonProps }: SegmentedControlOptionProps) {
241+
function SegmentedControlOption({
242+
isSelected,
243+
label,
244+
onClick,
245+
value,
246+
...buttonProps
247+
}: SegmentedControlOptionComponentProps) {
238248
const handleClick = React.useCallback(
239249
(event: React.MouseEvent<HTMLElement>) => onClick?.(value, event.currentTarget),
240250
[onClick, value],

packages/core/test/segmented-control/segmentedControlTests.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { assert } from "chai";
1818
import { mount } from "enzyme";
1919
import * as React from "react";
2020

21+
import { IconNames } from "@blueprintjs/icons";
22+
2123
import { Classes, type OptionProps, SegmentedControl, type SegmentedControlProps } from "../../src";
2224

2325
const OPTIONS: Array<OptionProps<string>> = [
@@ -60,6 +62,12 @@ describe("<SegmentedControl>", () => {
6062
assert.isTrue(wrapper.find(`.${testClassName}`).hostNodes().exists());
6163
});
6264

65+
it("supports icon", () => {
66+
const wrapper = mountSegmentedControl({ options: [{ icon: IconNames.GRID, value: "grid" }] });
67+
assert.isTrue(wrapper.find(`.${Classes.ICON}`).hostNodes().exists());
68+
assert.isTrue(wrapper.find(`[data-icon="${IconNames.GRID}"]`).exists());
69+
});
70+
6371
it("button text defaults to value when no label is passed", () => {
6472
mountSegmentedControl({ options: [{ value: "val" }] });
6573
const optionButtons = containerElement.querySelectorAll("button")!;

packages/docs-app/src/examples/core-examples/segmentedControlExample.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
Switch,
2727
} from "@blueprintjs/core";
2828
import { Example, type ExampleProps, handleBooleanChange } from "@blueprintjs/docs-theme";
29+
import { IconNames } from "@blueprintjs/icons";
2930

3031
import { SizeSelect } from "./common/sizeSelect";
3132

@@ -34,6 +35,7 @@ export const SegmentedControlExample: React.FC<ExampleProps> = props => {
3435
const [inline, setInline] = React.useState(false);
3536
const [intent, setIntent] = React.useState<SegmentedControlIntent>("none");
3637
const [size, setSize] = React.useState<Size>("medium");
38+
const [withIcons, setWithIcons] = React.useState(false);
3739

3840
const handleIntentChange = React.useCallback(
3941
(newIntent: string) => setIntent(newIntent as SegmentedControlIntent),
@@ -45,6 +47,7 @@ export const SegmentedControlExample: React.FC<ExampleProps> = props => {
4547
<H5>Props</H5>
4648
<Switch checked={inline} label="Inline" onChange={handleBooleanChange(setInline)} />
4749
<Switch checked={fill} label="Fill" onChange={handleBooleanChange(setFill)} />
50+
<Switch checked={withIcons} label="Icons" onChange={handleBooleanChange(setWithIcons)} />
4851
<Divider />
4952
<FormGroup label="Intent">
5053
<SegmentedControl
@@ -70,10 +73,15 @@ export const SegmentedControlExample: React.FC<ExampleProps> = props => {
7073
inline={inline}
7174
intent={intent}
7275
options={[
73-
{ label: "List", value: "list" },
74-
{ label: "Grid", value: "grid" },
75-
{ disabled: true, label: "Disabled", value: "disabled" },
76-
{ label: "Gallery", value: "gallery" },
76+
{ icon: withIcons ? IconNames.LIST : undefined, label: "List", value: "list" },
77+
{ icon: withIcons ? IconNames.GRID : undefined, label: "Grid", value: "grid" },
78+
{
79+
disabled: true,
80+
icon: withIcons ? IconNames.DISABLE : undefined,
81+
label: "Disabled",
82+
value: "disabled",
83+
},
84+
{ icon: withIcons ? IconNames.MEDIA : undefined, label: "Gallery", value: "gallery" },
7785
]}
7886
size={size}
7987
/>

0 commit comments

Comments
 (0)