Skip to content

Commit

Permalink
🐛 fix: improve dropdown focus handling, tag keyboard events
Browse files Browse the repository at this point in the history
  • Loading branch information
e1en0r committed Oct 23, 2024
1 parent 1346823 commit cbd209a
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 4 deletions.
20 changes: 19 additions & 1 deletion src/components/Tag/Tag.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cx } from '@emotion/css';
import React from 'react';
import React, { useCallback } from 'react';
import { AsReactType, MergeElementPropsWithoutRef, ThemeProps } from '../../types';
import { useThemeId } from '../../context/Theme';
import styles from './styles/Tag.module.css';
Expand All @@ -22,6 +22,7 @@ export type LocalTagProps = ThemeProps & {
focused?: boolean;
/** Manually apply the hover styles; this does not affect :hover */
hovered?: boolean;
onClick?: (event: React.MouseEvent | React.KeyboardEvent | React.TouchEvent) => void;
shape?: TagShape;
size?: TagSize;
style?: React.CSSProperties;
Expand All @@ -41,6 +42,7 @@ export function TagBase<T extends TagElementType = 'div'>(
focused = false,
flush = false,
hovered = false,
onClick,
shape = 'pill',
size = 'small',
themeId: initThemeId,
Expand All @@ -54,6 +56,20 @@ export function TagBase<T extends TagElementType = 'div'>(
const color = contrast ? 'contrast' : 'primary';
const element = as || (actionable ? 'button' : 'div');

const handleKeyDown = useCallback(
(event: React.KeyboardEvent) => {
if (onClick) {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();

onClick(event);
}
}
},
[onClick],
);

return React.createElement(
element,
{
Expand All @@ -71,6 +87,8 @@ export function TagBase<T extends TagElementType = 'div'>(
weight && styles[`tag--${weight}`],
className,
),
onClick,
onKeyDown: handleKeyDown,
ref: forwardedRef,
role: actionable ? 'button' : undefined,
tabIndex: actionable ? 0 : undefined,
Expand Down
6 changes: 3 additions & 3 deletions src/compositions/Dropdown/PartialDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export function PartialDropdownBase(
});

const { addRef, clearRefs, handleFocus, handleBlur, isIdFocused } = useDeepFocusGroup({}, { blurDelay: 150 });
const { focus, cancel } = useTriggerFocus({ focusDelay: 150 });
const { focus, cancel } = useTriggerFocus({ focusDelay: 0 });

const isClearFocused = isIdFocused(FOCUS_REFS.CLEAR);
const isContainerFocused = isIdFocused(FOCUS_REFS.CONTAINER);
Expand Down Expand Up @@ -404,7 +404,7 @@ export function PartialDropdownBase(

mouseDownRef.current = {
isDropdownVisible,
isListClicked: listRef.current?.container?.contains(event.target as Node),
isListClicked: listRef.current?.container.contains(event.target as Node),
};
},
[isDropdownVisible],
Expand Down Expand Up @@ -477,7 +477,7 @@ export function PartialDropdownBase(
// if the input wasn't part of the form or the clear button then show and focus the list
case 'Enter':
case ' ':
if (!isListFocused && !isClearFocused && !isInputTarget) {
if (!isClearFocused && !isInputTarget) {
event.preventDefault();
event.stopPropagation();
dropdownDispatch({ type: ACTIONS.SHOW_DROPDOWN });
Expand Down
70 changes: 70 additions & 0 deletions src/compositions/Dropdown/stories/Dropdown.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,76 @@ export const dropdownContainerStyle = {
</Flex>
</Canvas>

### Multi-select filterable dropdown

<Canvas>
<Flex full wrap direction="row" justifyContent="space-between">
<Looper
list={variants}
render={variant => (
<div style={dropdownContainerStyle}>
<Dropdown
transitional
getFilteredOptions={handleFilter}
initialSelected={[options[3]]}
inputVariant={variant}
label="Super awesome dropdown"
maxSelect={-1}
options={options}
/>
</div>
)}
/>
</Flex>
</Canvas>

### Contained filterable dropdown

<Canvas>
<Flex full wrap direction="row" justifyContent="space-between">
<Looper
list={variants}
render={variant => (
<div style={dropdownContainerStyle}>
<Dropdown
transitional
getFilteredOptions={handleFilter}
initialSelected={[options[3]]}
inputVariant={variant}
label="Super awesome dropdown"
layout="contained"
options={options}
/>
</div>
)}
/>
</Flex>
</Canvas>

### Multi-select contained filterable dropdown

<Canvas>
<Flex full wrap direction="row" justifyContent="space-between">
<Looper
list={variants}
render={variant => (
<div style={dropdownContainerStyle}>
<Dropdown
transitional
getFilteredOptions={handleFilter}
initialSelected={[options[3]]}
inputVariant={variant}
label="Super awesome dropdown"
layout="contained"
maxSelect={-1}
options={options}
/>
</div>
)}
/>
</Flex>
</Canvas>

### Searchable dropdown

_A searchable dropdown is similar to a filterable dropdown except that it starts with no initial values._
Expand Down

0 comments on commit cbd209a

Please sign in to comment.