Skip to content

Commit 1703339

Browse files
committed
Support keyboard events
1 parent 87354f7 commit 1703339

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

features/ui/select/option.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import React, { ReactNode } from "react";
22
import * as S from "./option.styled";
33

4-
type OptionProps = {
4+
type OptionProps<P> = {
55
children: ReactNode;
6-
value: unknown;
6+
value: P;
77
isSelected: boolean;
8-
onClick: (value: unknown) => void;
8+
onClick: (value: P) => void;
9+
onKeyDown: (event: React.KeyboardEvent, value: P) => void;
910
};
1011

11-
export function Option({ children, value, onClick, isSelected }: OptionProps) {
12+
export function Option<P>({
13+
children,
14+
value,
15+
onClick,
16+
onKeyDown,
17+
isSelected,
18+
}: OptionProps<P>) {
1219
return (
1320
<S.ListItem
1421
isCurrentlySelected={isSelected}
1522
aria-selected={isSelected}
1623
onClick={() => onClick(value)}
24+
onKeyDown={(event) => onKeyDown(event, value)}
1725
role="option"
1826
tabIndex={0}
1927
>

features/ui/select/select.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const Template: ComponentStory<typeof Select> = (props) => {
2525
{...props}
2626
options={options}
2727
value={selectedValue}
28-
onChange={(value) => setSelectedValue(value as number)}
28+
onChange={(value) => setSelectedValue(value)}
2929
/>
3030
</div>
3131
);

features/ui/select/select.tsx

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { useClickAway } from "react-use";
33
import { Option } from "./option";
44
import * as S from "./select.styled";
55

6-
type Option = {
7-
value: unknown;
6+
type Option<T> = {
7+
value: T;
88
label: React.ReactNode;
99
};
1010

11-
type SelectProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
12-
options: Option[];
11+
type SelectProps<T> = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
12+
options: Option<T>[];
1313
value: unknown;
14-
onChange: (value: unknown) => void;
14+
onChange: (value: T) => void;
1515
errorMessage?: string;
1616
defaultValue?: string;
1717
placeholder?: string;
@@ -22,7 +22,7 @@ type SelectProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
2222
hint?: string;
2323
};
2424

25-
export function Select({
25+
export function Select<T>({
2626
options,
2727
value,
2828
onChange,
@@ -35,7 +35,7 @@ export function Select({
3535
errorMessage = "",
3636
width = "",
3737
...props
38-
}: SelectProps) {
38+
}: SelectProps<T>) {
3939
const [showDropdown, setShowDropdown] = useState(false);
4040
const ref = useRef(null);
4141

@@ -44,14 +44,34 @@ export function Select({
4444
setShowDropdown(false);
4545
});
4646

47-
const showDropdownHandler = () =>
47+
const toggleDropdown = () =>
4848
setShowDropdown((prevShowDropdown) => !prevShowDropdown);
4949

50-
const onClickOption = (newValue: unknown) => {
50+
const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
51+
if (event.code === "Space" || event.code === "ArrowDown") {
52+
event.preventDefault();
53+
toggleDropdown();
54+
}
55+
if (event.code === "Escape") {
56+
setShowDropdown(false);
57+
}
58+
};
59+
60+
const onClickOption = (newValue: T) => {
5161
onChange(newValue);
5262
setShowDropdown(false);
5363
};
5464

65+
const onKeyDownOption = (event: React.KeyboardEvent, value: T) => {
66+
if (event.code === "Space") {
67+
event.preventDefault();
68+
onClickOption(value);
69+
}
70+
if (event.code === "Escape") {
71+
setShowDropdown(false);
72+
}
73+
};
74+
5575
const selectedOption = options.find(
5676
(option) => option.value === (value || defaultValue)
5777
);
@@ -61,12 +81,13 @@ export function Select({
6181
{label && <S.Label>{label}</S.Label>}
6282

6383
<S.SelectedOption
64-
onClick={showDropdownHandler}
65-
selectedOption={selectedOption}
84+
onClick={toggleDropdown}
85+
hasValue={!!selectedOption}
86+
hasError={!!errorMessage}
6687
disabled={disabled}
67-
errorMessage={errorMessage}
6888
aria-expanded={showDropdown}
6989
width={width}
90+
onKeyDown={onKeyDown}
7091
>
7192
<S.LeftContainer>
7293
{iconSrc && <S.OptionalIcon src={iconSrc} />}
@@ -92,6 +113,7 @@ export function Select({
92113
value={option.value}
93114
isSelected={value === option.value}
94115
onClick={onClickOption}
116+
onKeyDown={onKeyDownOption}
95117
>
96118
{option.label}
97119
</Option>

0 commit comments

Comments
 (0)