diff --git a/packages/react-components/src/components/Select/Select.css b/packages/react-components/src/components/Select/Select.css index 162ce660..8289c0cb 100644 --- a/packages/react-components/src/components/Select/Select.css +++ b/packages/react-components/src/components/Select/Select.css @@ -1,7 +1,8 @@ .bcds-react-aria-Select { - display: inline-flex; + display: flex; flex-direction: column; - gap: var(--layout-margin-xsmall); + gap: var(--layout-margin-small); + align-items: flex-start; /* Hacks for `stretch`: https://caniuse.com/mdn-css_properties_max-width_stretch */ max-width: -moz-available; max-width: -webkit-fill-available; @@ -16,9 +17,10 @@ color: var(--typography-color-disabled); } -/* "(optional)" text after Label*/ -.bcds-react-aria-Select--Label > .optional { - color: var(--typography-color-secondary); +/* Error message */ +.bcds-react-aria-Select--Error { + font: var(--typography-regular-small-body); + color: var(--typography-color-danger); } /* Select input equivalent */ @@ -48,7 +50,9 @@ } .bcds-react-aria-Select--Button[data-focused] { border-color: var(--surface-color-border-active); - outline: none; + outline: solid var(--layout-border-width-medium) + var(--surface-color-border-active); + outline-offset: var(--layout-margin-hair); } .bcds-react-aria-Select--Button[data-pressed] { border-color: var(--surface-color-border-active); @@ -78,7 +82,7 @@ box-shadow: var(--surface-shadow-medium); box-sizing: border-box; overflow-y: auto; - padding: var(--layout-margin-hair) var(--layout-margin-xsmall); + padding: var(--layout-margin-hair) var(--layout-padding-xsmall); width: var( --trigger-width ); /* Variable provided by Select component https://react-spectrum.adobe.com/react-aria/Select.html#popover-1 */ @@ -96,7 +100,7 @@ .bcds-react-aria-Select--Header { color: var(--typography-color-secondary); font: var(--typography-regular-small-body); - padding: var(--layout-margin-hair) var(--layout-margin-small); + padding: var(--layout-margin-hair) var(--layout-padding-small); } /* ListBox option item */ diff --git a/packages/react-components/src/components/Select/Select.tsx b/packages/react-components/src/components/Select/Select.tsx index 6c35583c..c7d6a65d 100644 --- a/packages/react-components/src/components/Select/Select.tsx +++ b/packages/react-components/src/components/Select/Select.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Button, Collection, + FieldError, Header, Key, Label, @@ -14,6 +15,7 @@ import { SelectProps as ReactAriaSelectProps, SelectValue, Text, + ValidationResult, } from "react-aria-components"; import "./Select.css"; @@ -50,6 +52,8 @@ export interface SelectProps extends ReactAriaSelectProps { placeholder?: string; /** Defaults to `medium` */ size?: "small" | "medium"; + /* Used for data validation and error handling */ + errorMessage?: string | ((validation: ValidationResult) => string); } function ChevronDown() { @@ -86,6 +90,24 @@ function ChevronUp() { ); } +/* Icon displayed when input is in invalid state */ +const iconError = ( + + + + + +); + /** Select displays a collapsible list of options and allows a user to select one of them. */ export default function Select({ items, @@ -93,21 +115,16 @@ export default function Select({ label, placeholder, size = "medium", + errorMessage, ...props }: SelectProps) { return ( - + {({ isOpen, isRequired, isInvalid }) => ( <> {label && ( )} + + {errorMessage} +