From 3fc2a068e4260cbc59383b3820cf1ab19967acfe Mon Sep 17 00:00:00 2001 From: Marcus Kernohan <135075821+mkernohanbc@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:47:32 -0800 Subject: [PATCH 01/16] refactor sizing --- .../src/components/Select/Select.css | 35 +++++++++++++------ .../src/components/Select/Select.tsx | 4 +-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/react-components/src/components/Select/Select.css b/packages/react-components/src/components/Select/Select.css index 582ab4dd..7fcea434 100644 --- a/packages/react-components/src/components/Select/Select.css +++ b/packages/react-components/src/components/Select/Select.css @@ -24,6 +24,30 @@ padding: var(--layout-padding-xsmall) var(--layout-padding-none); } +/* Sizing */ +.bcds-react-aria-Select.small .bcds-react-aria-Select--Button { + height: 32px; +} +.bcds-react-aria-Select--ListBox.small .bcds-react-aria-Select--ListBoxItem { + padding: var(--layout-padding-xsmall); +} +.bcds-react-aria-Select.small .bcds-react-aria-SelectValue, +.bcds-react-aria-Select--ListBox.small + .bcds-react-aria-Select--ListBoxItem-Text-label { + font: var(--typography-regular-small-body); +} +.bcds-react-aria-Select.medium .bcds-react-aria-Select--Button { + height: 40px; +} +.bcds-react-aria-Select--ListBox.medium .bcds-react-aria-Select--ListBoxItem { + padding: var(--layout-padding-small); +} +.bcds-react-aria-Select.medium .bcds-react-aria-SelectValue, +.bcds-react-aria-Select--ListBox.medium + .bcds-react-aria-Select--ListBoxItem-Text-label { + font: var(--typography-regular-body); +} + /* Error message */ .bcds-react-aria-Select--Error { font: var(--typography-regular-small-body); @@ -72,14 +96,7 @@ .bcds-react-aria-Select--Button[data-pressed] { border-color: var(--surface-color-border-active); } -.bcds-react-aria-Select--Button.medium { - height: 40px; -} -.bcds-react-aria-Select--Button.small { - height: 32px; -} .bcds-react-aria-Select--Button > .bcds-react-aria-SelectValue { - font: var(--typography-regular-body); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -126,7 +143,6 @@ flex-direction: row; align-items: center; gap: var(--layout-margin-small); - padding: var(--layout-padding-small); } .bcds-react-aria-Select--ListBoxItem[data-focused], .bcds-react-aria-Select--ListBoxItem[data-hovered] { @@ -147,9 +163,6 @@ flex-direction: column; flex-grow: 1; } -.bcds-react-aria-Select--ListBoxItem-Text-label { - font: var(--typography-regular-body); -} .bcds-react-aria-Select--ListBoxItem.destructive { color: var(--surface-color-primary-danger-button-default); } diff --git a/packages/react-components/src/components/Select/Select.tsx b/packages/react-components/src/components/Select/Select.tsx index 398569bd..2c5bada1 100644 --- a/packages/react-components/src/components/Select/Select.tsx +++ b/packages/react-components/src/components/Select/Select.tsx @@ -73,7 +73,7 @@ export default function Select({ ...props }: SelectProps) { return ( - + {({ isOpen, isRequired, isInvalid }) => ( <> {label && ( @@ -110,7 +110,7 @@ export default function Select({ Date: Thu, 26 Feb 2026 11:11:09 -0800 Subject: [PATCH 02/16] support multi-select and add visual indicator for selected item --- .../src/components/Select/Select.tsx | 73 ++++++++++++------- .../src/stories/Select.stories.tsx | 15 ++++ 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/packages/react-components/src/components/Select/Select.tsx b/packages/react-components/src/components/Select/Select.tsx index 2c5bada1..af34e185 100644 --- a/packages/react-components/src/components/Select/Select.tsx +++ b/packages/react-components/src/components/Select/Select.tsx @@ -18,6 +18,7 @@ import { } from "react-aria-components"; import SvgExclamationIcon from "../Icons/SvgExclamationIcon"; +import SvgCheckIcon from "../Icons/SvgCheckIcon"; import SvgChevronUpIcon from "../Icons/SvgChevronUpIcon"; import SvgChevronDownIcon from "../Icons/SvgChevronDownIcon"; @@ -44,7 +45,10 @@ export interface SelectionSectionProps { items: ListBoxItemProps[]; } -export interface SelectProps extends ReactAriaSelectProps { +export interface SelectProps< + T extends object, + M extends "single" | "multiple" = "single", +> extends ReactAriaSelectProps { /** Use `items` for a flat list of options */ items?: ListBoxItemProps[]; /** Use `sections` for a sectioned list with `items` options in each section */ @@ -62,7 +66,10 @@ export interface SelectProps extends ReactAriaSelectProps { } /** Select displays a collapsible list of options and allows a user to select one of them. */ -export default function Select({ +export default function Select< + T extends object, + M extends "single" | "multiple" = "single", +>({ items, sections, label, @@ -71,7 +78,7 @@ export default function Select({ size = "medium", errorMessage, ...props -}: SelectProps) { +}: SelectProps) { return ( {({ isOpen, isRequired, isInvalid }) => ( @@ -142,31 +149,41 @@ export default function Select({ }`} textValue={item.label} > - {item?.iconLeft && ( -
- {item.iconLeft} -
- )} -
- - {item.label} - - {item.description && ( - - {item.description} - - )} -
- {item?.iconRight && ( -
- {item.iconRight} -
+ {({ isSelected }) => ( + <> + {item?.iconLeft && ( +
+ {item.iconLeft} +
+ )} +
+ + {item.label} + + {item.description && ( + + {item.description} + + )} +
+ {isSelected ? ( +
+ +
+ ) : ( + item?.iconRight && ( +
+ {item.iconRight} +
+ ) + )} + )} )} diff --git a/packages/react-components/src/stories/Select.stories.tsx b/packages/react-components/src/stories/Select.stories.tsx index ff10cdce..68205553 100644 --- a/packages/react-components/src/stories/Select.stories.tsx +++ b/packages/react-components/src/stories/Select.stories.tsx @@ -35,6 +35,11 @@ const meta = { description: "Text label that appears inside the select input before an option has been selected", }, + selectionMode: { + options: ["single", "multiple"], + control: { type: "radio" }, + description: "Whether the user can select one or multiple options", + }, errorMessage: { control: { type: "text" }, description: "Text displayed when the input is invalid", @@ -65,6 +70,7 @@ export const SelectTemplate: Story = { label: "Label", size: "medium", description: "Optional description or helper text", + selectionMode: "single", isRequired: false, isDisabled: false, isInvalid: false, @@ -72,6 +78,15 @@ export const SelectTemplate: Story = { }, }; +export const MultiSelect: Story = { + ...SelectTemplate, + args: { + ...SelectTemplate.args, + label: "Multi-select example", + selectionMode: "multiple", + }, +}; + const iconPlaceholder = ( Date: Thu, 26 Feb 2026 11:43:36 -0800 Subject: [PATCH 03/16] expanding examples --- .../src/pages/Select/Select.tsx | 68 +++++++++++++++++-- .../src/pages/Select/UseStateExample.tsx | 2 +- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/packages/react-components/src/pages/Select/Select.tsx b/packages/react-components/src/pages/Select/Select.tsx index 0e9c2f6e..a8778f0b 100644 --- a/packages/react-components/src/pages/Select/Select.tsx +++ b/packages/react-components/src/pages/Select/Select.tsx @@ -1,14 +1,72 @@ +import { Select } from "@/components"; import UseStateExample from "./UseStateExample"; export default function SelectPage() { + const items = [ + { + id: "1", + label: "Lorem ipsum dolor sit amet, consectetur adipiscing elit", + }, + { + id: "2", + label: + "Suspendisse mi leo, gravida non consectetur vel, tincidunt eu nisl", + }, + { + id: "3", + label: + "Nunc faucibus, magna nec condimentum venenatis, nunc dui euismod metus, et vehicula elit purus in ex", + }, + { + id: "4", + label: + "Quisque velit tortor, facilisis eu orci vitae, tristique convallis nisi. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae", + }, + ]; return ( <>

Select

- -
- -

Select with useState

- +

Default

+
+
+ +
+
+

Small

+
+
+ +
+
+
+
+

Select with useState

+ +
+
+
); } diff --git a/packages/react-components/src/pages/Select/UseStateExample.tsx b/packages/react-components/src/pages/Select/UseStateExample.tsx index 5217ce93..8fa480b5 100644 --- a/packages/react-components/src/pages/Select/UseStateExample.tsx +++ b/packages/react-components/src/pages/Select/UseStateExample.tsx @@ -33,8 +33,8 @@ export default function UseStateExample() { items={items} value={selected} onChange={(selected) => setSelected(selected)} + description={`Selected key: ${selected}`} /> -

Selected key: {selected}

); } From 1c652a06dd7f0bd82c6e368bb1c12e1501ce6589 Mon Sep 17 00:00:00 2001 From: Marcus Kernohan <135075821+mkernohanbc@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:08:21 -0800 Subject: [PATCH 04/16] refining flex behaviour --- .../src/components/Select/Select.css | 18 +++++-- .../src/pages/Select/Select.tsx | 54 ++++++++----------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/packages/react-components/src/components/Select/Select.css b/packages/react-components/src/components/Select/Select.css index 7fcea434..10ba7153 100644 --- a/packages/react-components/src/components/Select/Select.css +++ b/packages/react-components/src/components/Select/Select.css @@ -1,7 +1,9 @@ .bcds-react-aria-Select { display: flex; + flex: 1 1 0; flex-direction: column; - align-items: flex-start; + align-items: stretch; + min-width: 0; /* Hacks for `stretch`: https://caniuse.com/mdn-css_properties_max-width_stretch */ max-width: -moz-available; max-width: -webkit-fill-available; @@ -64,14 +66,17 @@ /* Select input equivalent */ .bcds-react-aria-Select--Button { background-color: var(--surface-color-forms-default); - border: 1px solid var(--surface-color-border-default); + border: var(--layout-border-width-small) solid + var(--surface-color-border-default); border-radius: var(--layout-border-radius-medium); cursor: pointer; display: flex; justify-content: space-between; gap: var(--layout-margin-small); align-items: center; - padding: 0 12px; + padding: var(--layout-padding-none) 0.75rem; + width: 100%; + min-width: 0; max-width: 100%; } .bcds-react-aria-Select--Button.invalid { @@ -103,7 +108,7 @@ min-width: 0; } .bcds-react-aria-Select--Button > svg { - flex-shrink: 0; + flex: 0 0 auto; } /* Dropdown menu panel */ @@ -115,6 +120,8 @@ box-sizing: border-box; overflow-y: auto; padding: var(--layout-padding-hair) var(--layout-padding-xsmall); + max-width: calc(100vw - (2 * var(--layout-padding-small))); + max-height: 50vh; width: var( --trigger-width ); /* Variable provided by Select component https://react-spectrum.adobe.com/react-aria/Select.html#popover-1 */ @@ -161,7 +168,8 @@ color: var(--typography-color-primary); display: flex; flex-direction: column; - flex-grow: 1; + flex: 1 1 auto; + min-width: 0; } .bcds-react-aria-Select--ListBoxItem.destructive { color: var(--surface-color-primary-danger-button-default); diff --git a/packages/react-components/src/pages/Select/Select.tsx b/packages/react-components/src/pages/Select/Select.tsx index a8778f0b..b9135116 100644 --- a/packages/react-components/src/pages/Select/Select.tsx +++ b/packages/react-components/src/pages/Select/Select.tsx @@ -28,43 +28,33 @@ export default function SelectPage() {

Select

Default

-
- -
+

Small

-
- -
+
+

Select with useState

-
-

Select with useState

- -
+
From 1dbf8b52586559334ec3a0aa4d07f5a56f1422a2 Mon Sep 17 00:00:00 2001 From: Marcus Kernohan <135075821+mkernohanbc@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:20:11 -0800 Subject: [PATCH 05/16] cleaning up examples --- packages/react-components/src/App.css | 4 +++ .../src/pages/Select/MultiSelect.tsx | 32 +++++++++++++++++++ .../src/pages/Select/Select.tsx | 25 +++++++++------ .../src/pages/Select/UseStateExample.tsx | 2 +- 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 packages/react-components/src/pages/Select/MultiSelect.tsx diff --git a/packages/react-components/src/App.css b/packages/react-components/src/App.css index c9051874..d1ee4385 100644 --- a/packages/react-components/src/App.css +++ b/packages/react-components/src/App.css @@ -31,10 +31,14 @@ main { display: flex; flex-direction: row; gap: var(--layout-margin-medium); + min-width: 0; + max-width: 100%; } .col { display: flex; flex-direction: column; + max-width: 100%; + width: 100%; } .menu { display: flex; diff --git a/packages/react-components/src/pages/Select/MultiSelect.tsx b/packages/react-components/src/pages/Select/MultiSelect.tsx new file mode 100644 index 00000000..259d4240 --- /dev/null +++ b/packages/react-components/src/pages/Select/MultiSelect.tsx @@ -0,0 +1,32 @@ +import Select from "@/components/Select/Select"; + +export default function MultiSelectExample() { + const items = [ + { + id: "1", + label: "Option 1", + }, + { + id: "2", + label: "Option 2", + }, + { + id: "3", + label: "Option 3", + }, + { + id: "4", + label: "Option 4", + }, + ]; + return ( + <> + + -

Small

+

Small size

-

Select with useState

+
- -
+
+

Multi-select

+ +
+
+

Select with useState

+ +
); diff --git a/packages/react-components/src/pages/Select/UseStateExample.tsx b/packages/react-components/src/pages/Select/UseStateExample.tsx index 8fa480b5..0d8f7681 100644 --- a/packages/react-components/src/pages/Select/UseStateExample.tsx +++ b/packages/react-components/src/pages/Select/UseStateExample.tsx @@ -29,7 +29,7 @@ export default function UseStateExample() { return ( <> + Date: Tue, 3 Mar 2026 14:00:00 -0800 Subject: [PATCH 09/16] update docs and stories --- .../react-components/src/stories/Select.mdx | 39 +++++++++- .../src/stories/Select.stories.tsx | 71 +++++++++++++++++-- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/packages/react-components/src/stories/Select.mdx b/packages/react-components/src/stories/Select.mdx index 8cb2eaf0..ad686632 100644 --- a/packages/react-components/src/stories/Select.mdx +++ b/packages/react-components/src/stories/Select.mdx @@ -24,7 +24,7 @@ import * as SelectStories from "./Select.stories"; The select component displays a collapsible list of options and enables a user - to select one of them. + to select one or more values. + +Selected values are rendered inside a [TagGroup](/docs/inputs-and-controls-taggroup--docs). This provides visual separation between selected values, and enables the user to de-select items without re-opening the dropdown. Selected items are also indicated with a checkmark in the dropdown list. + +#### Styling tags + +Tags' labels, IDs and icons are populated automatically from Select's `items` array. You can further access and customise how individual tags are styled using [Tag](/story/inputs-and-controls-taggroup-tag--single-tag) props: + +```typescript +import { TagProps } from "@bcgov/design-system-react-components"; + +const items = [ + { + id: "1", + label: "Option 1", + color: "bc-blue" as TagProps["color"], + tagStyle: "circular" as TagProps["tagStyle"], + }, + { + id: "2", + label: "Option 2", + color: "bc-gold" as TagProps["color"], + tagStyle: "circular" as TagProps["tagStyle"], + }, +]; +``` + + + ### Flat list of items Options in the select component are implemented using React Aria's [Collection Components API](https://react-spectrum.adobe.com/react-aria/collections.html) via the `items` and `sections` props. @@ -87,7 +122,7 @@ Each section object can be labelled with an optional `header`. ### Size -Choose from medium (default) and small select sizes: +Choose from `medium` (default) and `small` select sizes: diff --git a/packages/react-components/src/stories/Select.stories.tsx b/packages/react-components/src/stories/Select.stories.tsx index 68205553..e088469a 100644 --- a/packages/react-components/src/stories/Select.stories.tsx +++ b/packages/react-components/src/stories/Select.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { Select } from "../components"; +import { TagProps } from "../components/Tag"; const meta = { title: "Inputs and controls/Select", @@ -20,6 +21,22 @@ const meta = { options: ["medium", "small"], control: { type: "radio" }, description: "Defaults to 'medium', also supports 'small'", + table: { defaultValue: { summary: "medium" } }, + }, + selectionMode: { + options: ["single", "multiple"], + control: { type: "radio" }, + description: "Whether the user can select one or multiple options", + table: { defaultValue: { summary: "single" } }, + }, + items: { + control: { type: "object" }, + description: "Array of options to display in the dropdown", + }, + sections: { + control: { type: "object" }, + description: + "Array of sections to display in the dropdown. Each should have its own array of `items`", }, label: { control: { type: "text" }, @@ -35,15 +52,37 @@ const meta = { description: "Text label that appears inside the select input before an option has been selected", }, - selectionMode: { - options: ["single", "multiple"], - control: { type: "radio" }, - description: "Whether the user can select one or multiple options", + value: { + control: { type: "object" }, + description: "The current value (controlled)", + }, + defaultValue: { + control: { type: "object" }, + description: "The initial value (uncontrolled)", + }, + disabledKeys: { + control: { type: "object" }, + description: + "Items that cannot be selected, focused or otherwise interacted with", }, errorMessage: { control: { type: "text" }, description: "Text displayed when the input is invalid", }, + isRequired: { + control: { type: "boolean" }, + description: "Whether a select is required or optional", + table: { defaultValue: { summary: "false" } }, + }, + isDisabled: { + control: { type: "boolean" }, + description: "Whether the select is disabled", + table: { defaultValue: { summary: "false" } }, + }, + isInvalid: { + control: "boolean", + description: "Whether the input is valid (usually set programmatically)", + }, }, } satisfies Meta; @@ -87,6 +126,30 @@ export const MultiSelect: Story = { }, }; +export const StyledTags: Story = { + ...SelectTemplate, + args: { + ...SelectTemplate.args, + label: "Multi-select with styled tags", + selectionMode: "multiple", + defaultValue: ["1", "2"], + items: [ + { + id: "1", + label: "Option 1", + color: "bc-blue" as TagProps["color"], + tagStyle: "circular" as TagProps["tagStyle"], + }, + { + id: "2", + label: "Option 2", + color: "bc-gold" as TagProps["color"], + tagStyle: "circular" as TagProps["tagStyle"], + }, + ], + }, +}; + const iconPlaceholder = ( Date: Tue, 3 Mar 2026 16:50:18 -0800 Subject: [PATCH 10/16] refactor to conditionally render Group instead of Button for multi-select --- .../src/components/Select/Select.css | 24 ++- .../src/components/Select/Select.tsx | 150 ++++++++++++------ .../react-components/src/stories/Select.mdx | 4 +- 3 files changed, 123 insertions(+), 55 deletions(-) diff --git a/packages/react-components/src/components/Select/Select.css b/packages/react-components/src/components/Select/Select.css index 43e17ffa..bcbf24a9 100644 --- a/packages/react-components/src/components/Select/Select.css +++ b/packages/react-components/src/components/Select/Select.css @@ -16,7 +16,7 @@ .bcds-react-aria-Select--ListBox.small .bcds-react-aria-Select--ListBoxItem { padding: var(--layout-padding-xsmall); } -.bcds-react-aria-Select.small .bcds-react-aria-SelectValue--Placeholder, +.bcds-react-aria-Select.small .bcds-react-aria-SelectValue--Text, .bcds-react-aria-Select--ListBox.small .bcds-react-aria-Select--ListBoxItem-Text-label { font: var(--typography-regular-small-body); @@ -27,7 +27,7 @@ .bcds-react-aria-Select--ListBox.medium .bcds-react-aria-Select--ListBoxItem { padding: var(--layout-padding-small); } -.bcds-react-aria-Select.medium .bcds-react-aria-SelectValue--Placeholder, +.bcds-react-aria-Select.medium .bcds-react-aria-SelectValue--Text, .bcds-react-aria-Select--ListBox.medium .bcds-react-aria-Select--ListBoxItem-Text-label { font: var(--typography-regular-body); @@ -39,12 +39,13 @@ border: var(--layout-border-width-small) solid var(--surface-color-border-default); border-radius: var(--layout-border-radius-medium); + box-sizing: border-box; cursor: pointer; display: flex; gap: var(--layout-margin-small); align-items: center; padding: var(--layout-padding-small); - width: 100%; + width: auto; min-width: 0; max-width: 100%; } @@ -73,8 +74,9 @@ .bcds-react-aria-Select--Button > .bcds-react-aria-SelectValue { flex: 1 1 auto; overflow: visible; - white-space: nowrap; + white-space: normal; text-overflow: ellipsis; + text-align: left; min-width: 0; } .bcds-react-aria-Select--Button > svg { @@ -114,7 +116,8 @@ /* Dropdown menu panel */ .bcds-react-aria-Select--Popover { background-color: var(--surface-color-forms-default); - border: 1px solid var(--surface-color-border-default); + border: var(--layout-border-width-small) solid + var(--surface-color-border-default); border-radius: var(--layout-border-radius-medium); box-shadow: var(--surface-shadow-medium); box-sizing: border-box; @@ -122,17 +125,24 @@ padding: var(--layout-padding-hair) var(--layout-padding-xsmall); max-width: calc(100vw - (2 * var(--layout-padding-small))); max-height: 50vh; +} +/* Set width separately for single and multiple selection modes */ +.bcds-react-aria-Select--Popover.single { width: var( --trigger-width ); /* Variable provided by Select component https://react-spectrum.adobe.com/react-aria/Select.html#popover-1 */ } +.bcds-react-aria-Select--Popover.multiple { + width: 300px; +} .bcds-react-aria-Select--ListBox[data-focused] { outline: none; } .bcds-react-aria-Select--ListBox > .bcds-react-aria-Section:not(:first-child) { - border-top: 1px solid var(--surface-color-border-default); + border-top: var(--layout-border-width-small) solid + var(--surface-color-border-default); margin-top: 1px; - padding-top: 2px; + padding-top: var(--layout-padding-hair); } /* Header label within Section of multi-section Select */ diff --git a/packages/react-components/src/components/Select/Select.tsx b/packages/react-components/src/components/Select/Select.tsx index a80ea9d6..2d213e13 100644 --- a/packages/react-components/src/components/Select/Select.tsx +++ b/packages/react-components/src/components/Select/Select.tsx @@ -1,7 +1,7 @@ import { - Button, Collection, FieldError, + Group, Header, Key, Label, @@ -16,7 +16,9 @@ import { Text, ValidationResult, } from "react-aria-components"; +import { useRef } from "react"; +import Button from "../Button"; import SvgExclamationIcon from "../Icons/SvgExclamationIcon"; import SvgCheckIcon from "../Icons/SvgCheckIcon"; import SvgChevronUpIcon from "../Icons/SvgChevronUpIcon"; @@ -84,10 +86,17 @@ export default function Select< placeholder, size = "medium", errorMessage, + selectionMode, ...props }: SelectProps) { + const triggerRef = useRef(null); + return ( - + {({ isOpen, isRequired, isInvalid }) => ( <> {label && ( @@ -95,54 +104,96 @@ export default function Select< {isRequired ? `${label} (required)` : label} )} - - {placeholder ? placeholder : "Select an item"} + state.selectionManager.setSelectedKeys( + updatedSelectedKeys, + ); + }} + > + ) + .filter( + (item): item is ListBoxItemProps => item !== null, + ) + /* Map ListBoxItem props to Tag props */ + .map((item) => ({ + id: item?.id ? item.id : item.label, + textValue: item.label, + size: size === "small" ? "xsmall" : "small", + color: item.color, + tagStyle: item.tagStyle, + ...(item.iconLeft && { icon: item.iconLeft }), + }))} + renderEmptyState={() => ( + + {placeholder ? placeholder : "Select an item"} + + )} + /> + + )} + + {isInvalid && } + + + ) : ( + /* Render button in single selection mode */ + + ); + if (placeholder) + return ( + + {placeholder} + + ); + return ( + + Select an item + + ); + }} + /> + {isInvalid && } + {isOpen ? : } + + )} {description && ( {errorMessage} - + -Selected values are rendered inside a [TagGroup](/docs/inputs-and-controls-taggroup--docs). This provides visual separation between selected values, and enables the user to de-select items without re-opening the dropdown. Selected items are also indicated with a checkmark in the dropdown list. +In multi-select mode, selected values are rendered inside a [TagGroup](/docs/inputs-and-controls-taggroup--docs). This provides visual separation between selected values, and enables the user to de-select items without re-opening the dropdown. #### Styling tags From 5e01805b6b35cf1f87b07aa52a92a3d8fc38f6ef Mon Sep 17 00:00:00 2001 From: Marcus Kernohan <135075821+mkernohanbc@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:04:15 -0800 Subject: [PATCH 11/16] fix button styling in multi-select mode, support disabling tags --- .../react-components/src/components/Select/Select.css | 8 ++++---- .../react-components/src/components/Select/Select.tsx | 3 ++- packages/react-components/src/components/Tag/Tag.tsx | 2 ++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/react-components/src/components/Select/Select.css b/packages/react-components/src/components/Select/Select.css index bcbf24a9..628e8996 100644 --- a/packages/react-components/src/components/Select/Select.css +++ b/packages/react-components/src/components/Select/Select.css @@ -40,7 +40,6 @@ var(--surface-color-border-default); border-radius: var(--layout-border-radius-medium); box-sizing: border-box; - cursor: pointer; display: flex; gap: var(--layout-margin-small); align-items: center; @@ -49,6 +48,10 @@ min-width: 0; max-width: 100%; } +button.bcds-react-aria-Select--Button[data-hovered] { + border-color: var(--surface-color-border-dark); + cursor: pointer; +} .bcds-react-aria-Select--Button.invalid { border-color: var(--support-border-color-danger); } @@ -59,9 +62,6 @@ background-color: var(--surface-color-forms-disabled); cursor: not-allowed; } -.bcds-react-aria-Select--Button[data-hovered] { - border-color: var(--surface-color-border-dark); -} .bcds-react-aria-Select--Button[data-focused] { border-color: var(--surface-color-border-active); outline: solid var(--layout-border-width-medium) diff --git a/packages/react-components/src/components/Select/Select.tsx b/packages/react-components/src/components/Select/Select.tsx index 2d213e13..2cf18dd8 100644 --- a/packages/react-components/src/components/Select/Select.tsx +++ b/packages/react-components/src/components/Select/Select.tsx @@ -97,7 +97,7 @@ export default function Select< className={`bcds-react-aria-Select ${size}`} {...props} > - {({ isOpen, isRequired, isInvalid }) => ( + {({ isOpen, isRequired, isInvalid, isDisabled }) => ( <> {label && (