Skip to content

Commit

Permalink
Merge branch 'main' into fix/tree-select-focus
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaevAlexandr authored Jul 16, 2024
2 parents 9604f58 + f12d49f commit 9a89166
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/components/TreeSelect/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
items,
value: propsValue,
defaultValue,
placeholder,
disabled = false,
withExpandedState = true,
defaultExpandedState = 'expanded',
Expand Down Expand Up @@ -154,6 +155,7 @@ export const TreeSelect = React.forwardRef(function TreeSelect<T>(
const controlProps: TreeSelectRenderControlProps<T> = {
list,
open,
placeholder,
toggleOpen,
clearValue: () => list.state.setSelected({}),
ref: handleControlRef,
Expand Down
12 changes: 12 additions & 0 deletions src/components/TreeSelect/__stories__/TreeSelect.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {TreeSelectProps} from '../types';

import {InfinityScrollExample} from './components/InfinityScrollExample';
import type {InfinityScrollExampleProps} from './components/InfinityScrollExample';
import {WithDisabledElementsExample} from './components/WithDisabledElementsExample';
import type {WithDisabledElementsExampleProps} from './components/WithDisabledElementsExample';
import {WithDndListExample} from './components/WithDndListExample';
import type {WithDndListExampleProps} from './components/WithDndListExample';
import {WithFiltrationAndControlsExample} from './components/WithFiltrationAndControlsExample';
Expand Down Expand Up @@ -51,6 +53,7 @@ const DefaultTemplate: StoryFn<
<Flex>
<TreeSelect
{...props}
placeholder="-"
items={items}
mapItemDataToProps={(x) => x}
onItemClick={({id, list}) => {
Expand Down Expand Up @@ -114,3 +117,12 @@ WithDndList.parameters = {
// https://github.com/atlassian/react-beautiful-dnd/issues/2350
disableStrictMode: true,
};

const WithDisabledElementsTemplate: StoryFn<WithDisabledElementsExampleProps> = (props) => {
return <WithDisabledElementsExample {...props} />;
};
export const WithDisabledElements = WithDisabledElementsTemplate.bind({});

WithDisabledElements.args = {
size: 'l',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';

import type {ListItemType} from '../../../useList';
import {TreeSelect} from '../../TreeSelect';
import type {TreeSelectProps} from '../../types';

interface Entity {
text: string;
id: string;
}

export interface WithDisabledElementsExampleProps
extends Omit<TreeSelectProps<Entity>, 'items' | 'mapItemDataToProps'> {}

const items: ListItemType<Entity>[] = [
{
data: {id: '1', text: 'default disabled'},
disabled: true,
},
{
data: {id: '2', text: 'two'},
disabled: true,
},
{
data: {id: '3', text: 'default selected'},
},
{
data: {id: '4', text: 'four'},
disabled: true,
},
{
data: {id: '5', text: 'five'},
},
];

export const WithDisabledElementsExample = ({...props}: WithDisabledElementsExampleProps) => {
const containerRef = React.useRef<HTMLDivElement>(null);

return (
<TreeSelect
{...props}
items={items}
getItemId={({id}) => id}
containerRef={containerRef}
mapItemDataToProps={({text}) => ({title: text})}
/>
);
};
4 changes: 3 additions & 1 deletion src/components/TreeSelect/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import type {UseListParsedStateProps} from '../useList/hooks/useListParsedState'
export type TreeSelectRenderControlProps<T> = {
list: UseListResult<T>;
open: boolean;
disabled: boolean;
disabled?: boolean;
placeholder?: string;
toggleOpen(): void;
clearValue(): void;
ref: React.Ref<HTMLButtonElement>;
Expand Down Expand Up @@ -46,6 +47,7 @@ export interface TreeSelectProps<T, P extends {} = {}>
title?: string;
value?: ListItemId[];
disabled?: boolean;
placeholder?: string;
defaultValue?: ListItemId[] | undefined;
popupClassName?: string;
popupWidth?: SelectPopupProps['width'];
Expand Down
11 changes: 7 additions & 4 deletions src/components/useList/hooks/useListKeydown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ export const useListKeydown = ({containerRef, onItemClick, enabled, list}: UseLi
(event: KeyboardEvent, step: number, defaultItemIndex = 0) => {
event.preventDefault();

const maybeIndex = list.structure.visibleFlattenIds.findIndex(
(i) => i === list.state.activeItemId,
);
const maybeIndex =
typeof list.state.activeItemId === 'string'
? list.structure.visibleFlattenIds.findIndex(
(i) => i === list.state.activeItemId,
)
: -1;

const nextIndex = findNextIndex({
list: list.structure.visibleFlattenIds,
index: (maybeIndex > -1 ? maybeIndex : defaultItemIndex) + step,
step: Math.sign(step),
disabledItems: list.state.disabledById,
disabledItemsById: list.state.disabledById,
});

activateItem(nextIndex);
Expand Down
10 changes: 7 additions & 3 deletions src/components/useList/utils/findNextIndex.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import type {ListItemId} from '../types';

interface FindNextItemsProps {
list: string[];
index: number;
step: number;
disabledItems?: Record<string, boolean>;
disabledItemsById?: Record<ListItemId, boolean>;
}

export const findNextIndex = ({list, index, step, disabledItems = {}}: FindNextItemsProps) => {
export const findNextIndex = ({list, index, step, disabledItemsById = {}}: FindNextItemsProps) => {
const dataLength = list.length;
let currentIndex = (index + dataLength) % dataLength;

for (let i = 0; i < dataLength; i += 1) {
if (list[currentIndex] && !disabledItems[currentIndex]) {
const id = list[currentIndex];

if (id && !disabledItemsById[id]) {
return currentIndex;
}
currentIndex = (currentIndex + dataLength + step) % dataLength;
Expand Down

0 comments on commit 9a89166

Please sign in to comment.