Skip to content

Commit

Permalink
feat(list): handle removing a non-numeric list index
Browse files Browse the repository at this point in the history
  • Loading branch information
Coread committed Jan 28, 2025
1 parent aff0f5a commit d86fc3f
Show file tree
Hide file tree
Showing 8 changed files with 422 additions and 217 deletions.
2 changes: 1 addition & 1 deletion src/components/List/List.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const CLASS_PREFIX = 'md-list';

const DEFAULTS = {
ORIENTATION: 'vertical' as ListOrientation,
INITIAL_FOCUS: 0,
INITIAL_FOCUS_NOT_SET: null,
};

const STYLE = {
Expand Down
68 changes: 68 additions & 0 deletions src/components/List/List.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,73 @@ const ListWithNonDefaultSetNextFocus = Template<unknown>(
ListWithNonDefaultSetNextFocusWrapper
).bind({});

const ListWithNonDefaultSetNextFocusVariableLengthWrapper = () => {
const [itemIndices, setItemIndices] = useState<string[]>(['a', 'b', 'c']);

useEffect(() => {
const handle = setInterval(() => {
setItemIndices((old) => {
if (old.length === 2) {
return [...old, 'c'];
} else {
return old.slice(0, 2);
}
});
}, 3000);

return () => {
clearTimeout(handle);
};
}, []);

const setNextFocus = useCallback(
(
isBackward: boolean,
listSize: number,
currentFocus: number | string,
noLoop: boolean,
setFocus: Dispatch<SetStateAction<number | string>>
) => {
const currentIndex = itemIndices.indexOf(currentFocus as string);

let nextIndex: number;

if (isBackward) {
nextIndex = (listSize + currentIndex - 1) % listSize;

if (noLoop && nextIndex > currentIndex) {
return;
}
} else {
nextIndex = (listSize + currentIndex + 1) % listSize;

if (noLoop && nextIndex < currentIndex) {
return;
}
}

setFocus(itemIndices[nextIndex]);
},
[itemIndices]
);

return (
<List shouldFocusOnPress setNextFocus={setNextFocus} listSize={3} allItemIndexes={itemIndices}>
{itemIndices.map((index) => {
return (
<ListItemBase allowTextSelection itemIndex={index} key={index}>
<ListItemBaseSection position="fill">Item {index}</ListItemBaseSection>
</ListItemBase>
);
})}
</List>
);
};

const ListWithNonDefaultSetNextFocusVariableLength = Template<unknown>(
ListWithNonDefaultSetNextFocusVariableLengthWrapper
).bind({});

export {
Example,
Common,
Expand All @@ -729,4 +796,5 @@ export {
SingleItemList,
ListWithTextSelect,
ListWithNonDefaultSetNextFocus,
ListWithNonDefaultSetNextFocusVariableLength,
};
14 changes: 13 additions & 1 deletion src/components/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { mergeProps } from '@react-aria/utils';

const List = forwardRef((props: Props, ref: RefObject<ListRefObject>) => {
const {
allItemIndexes,
className,
id,
style,
Expand All @@ -20,13 +21,24 @@ const List = forwardRef((props: Props, ref: RefObject<ListRefObject>) => {
shouldItemFocusBeInset,
noLoop,
orientation = DEFAULTS.ORIENTATION,
initialFocus = DEFAULTS.INITIAL_FOCUS,
initialFocus: propInitialFocus = DEFAULTS.INITIAL_FOCUS_NOT_SET,
setNextFocus,
...rest
} = props;

let initialFocus = propInitialFocus;

if (initialFocus === DEFAULTS.INITIAL_FOCUS_NOT_SET) {
if (allItemIndexes) {
initialFocus = allItemIndexes[0];
} else {
initialFocus = 0;
}
}

const { keyboardProps, getContext, focusWithinProps } = useOrientationBasedKeyboardNavigation({
listSize,
allItemIndexes,
orientation,
noLoop,
initialFocus,
Expand Down
18 changes: 12 additions & 6 deletions src/components/List/List.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AriaRole, CSSProperties, Dispatch, ReactNode, SetStateAction } from 'react';
import { ListItemBaseIndex } from '../ListItemBase/ListItemBase.types';

export type ListOrientation = 'horizontal' | 'vertical';

Expand Down Expand Up @@ -64,25 +65,30 @@ export interface Props {
/**
* The index of the item that should be focused initially
*/
initialFocus?: number | string;
initialFocus?: ListItemBaseIndex;

/**
* Optional function to control the focus behavior when up/down keys are pressed
*/
setNextFocus?: (
isBackward: boolean,
listSize: number,
currentFocus: number | string,
currentFocus: ListItemBaseIndex,
noLoop: boolean,
setFocus: Dispatch<SetStateAction<number | string>>
setFocus: Dispatch<SetStateAction<ListItemBaseIndex>>
) => void;

/**
* The full list of item indexes to be displayed. This is necessary for non-numeric item indexes
*/
allItemIndexes?: ListItemBaseIndex[];
}

export interface ListContextValue {
currentFocus?: number | string;
currentFocus?: ListItemBaseIndex;
shouldFocusOnPress?: boolean;
shouldItemFocusBeInset?: boolean;
setCurrentFocus?: Dispatch<SetStateAction<number | string>>;
setCurrentFocus?: Dispatch<SetStateAction<ListItemBaseIndex>>;
listSize?: number;
noLoop?: boolean;
updateFocusBlocked?: boolean;
Expand All @@ -92,5 +98,5 @@ export interface ListContextValue {
export interface ListRefObject {
listRef: React.RefObject<HTMLUListElement>;
focusOnIndex: (index: number) => void;
getCurrentFocusIndex: () => number | string;
getCurrentFocusIndex: () => ListItemBaseIndex;
}
Loading

0 comments on commit d86fc3f

Please sign in to comment.