Skip to content

Commit

Permalink
Merge pull request #390 from bcgov/388-selectdropdown-refine-error-ha…
Browse files Browse the repository at this point in the history
…ndling

Select/dropdown - improve error handling and focus state
  • Loading branch information
mkernohanbc authored Jul 17, 2024
2 parents b3cf196 + f415819 commit 9d9c9a9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
20 changes: 12 additions & 8 deletions packages/react-components/src/components/Select/Select.css
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down
37 changes: 29 additions & 8 deletions packages/react-components/src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import {
Button,
Collection,
FieldError,
Header,
Key,
Label,
Expand All @@ -14,6 +15,7 @@ import {
SelectProps as ReactAriaSelectProps,
SelectValue,
Text,
ValidationResult,
} from "react-aria-components";

import "./Select.css";
Expand Down Expand Up @@ -50,6 +52,8 @@ export interface SelectProps<T extends object> extends ReactAriaSelectProps<T> {
placeholder?: string;
/** Defaults to `medium` */
size?: "small" | "medium";
/* Used for data validation and error handling */
errorMessage?: string | ((validation: ValidationResult) => string);
}

function ChevronDown() {
Expand Down Expand Up @@ -86,28 +90,41 @@ function ChevronUp() {
);
}

/* Icon displayed when input is in invalid state */
const iconError = (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<g id="20px/Error icon">
<path
id="Icon"
d="M17.835 15.0312C18.335 15.9062 17.71 17 16.6787 17H3.33499C2.30374 17 1.67874 15.9062 2.14749 15.0312L8.83499 3.65625C9.11624 3.21875 9.55373 3 10.0225 3C10.46 3 10.8975 3.21875 11.1787 3.65625L17.835 15.0312ZM3.64749 15.5H16.3663L9.99123 4.65625L3.64749 15.5ZM10.0225 12.5625C10.5537 12.5625 10.9912 13 10.9912 13.5312C10.9912 14.0625 10.5537 14.5 10.0225 14.5C9.45998 14.5 9.02249 14.0625 9.02249 13.5312C9.02249 13 9.45998 12.5625 10.0225 12.5625ZM9.27249 7.75C9.27249 7.34375 9.58498 7 10.0225 7C10.4287 7 10.7725 7.34375 10.7725 7.75V10.75C10.7725 11.1875 10.4287 11.5 10.0225 11.5C9.58498 11.5 9.27249 11.1875 9.27249 10.75V7.75Z"
fill="var(--icons-color-danger)"
/>
</g>
</svg>
);

/** Select displays a collapsible list of options and allows a user to select one of them. */
export default function Select<T extends object>({
items,
sections,
label,
placeholder,
size = "medium",
errorMessage,
...props
}: SelectProps<T>) {
return (
<ReactAriaSelect {...props}>
<ReactAriaSelect {...props} className="bcds-react-aria-Select">
{({ isOpen, isRequired, isInvalid }) => (
<>
{label && (
<Label className="bcds-react-aria-Select--Label">
{label}
{!isRequired && (
<>
{" "}
<span className="optional">(optional)</span>
</>
)}
{isRequired ? label : `${label} (optional)`}
</Label>
)}
<Button
Expand All @@ -123,8 +140,12 @@ export default function Select<T extends object>({
return "Select an item";
}}
/>
{isInvalid && iconError}
{isOpen ? <ChevronUp /> : <ChevronDown />}
</Button>
<FieldError className="bcds-react-aria-Select--Error">
{errorMessage}
</FieldError>
<Popover className="bcds-react-aria-Select--Popover" offset={4}>
<ListBox
className="bcds-react-aria-Select--ListBox"
Expand Down
7 changes: 4 additions & 3 deletions packages/react-components/src/stories/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const meta = {
config: {
// overrides automated accessibility testing for aria-hidden-focus to suppress a known false positive
// see https://react-spectrum.adobe.com/react-aria/Select.html#false-positives for more information
rules: [{ id: 'aria-hidden-focus', enabled: false }],
}
}
rules: [{ id: "aria-hidden-focus", enabled: false }],
},
},
},
argTypes: {
size: {
Expand Down Expand Up @@ -168,6 +168,7 @@ export const Invalid: Story = {
...SelectTemplate.args,
label: "Invalid example",
isInvalid: true,
errorMessage: "Error messages can be customised or passed programmatically",
},
};

Expand Down

0 comments on commit 9d9c9a9

Please sign in to comment.