diff --git a/change/@fluentui-react-list-447356bf-a6be-4e24-ac8a-ec3a50cd76b2.json b/change/@fluentui-react-list-447356bf-a6be-4e24-ac8a-ec3a50cd76b2.json new file mode 100644 index 0000000000000..62bf0d3ccd15d --- /dev/null +++ b/change/@fluentui-react-list-447356bf-a6be-4e24-ac8a-ec3a50cd76b2.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: add base hooks for List", + "packageName": "@fluentui/react-list", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-list/library/src/components/List/List.types.ts b/packages/react-components/react-list/library/src/components/List/List.types.ts index e6d367cb659f5..74dc0245085d3 100644 --- a/packages/react-components/react-list/library/src/components/List/List.types.ts +++ b/packages/react-components/react-list/library/src/components/List/List.types.ts @@ -51,3 +51,15 @@ export type ListContextValues = { * State used in rendering List */ export type ListState = ComponentState & ListContextValue & ListSynchronousContextValue; + +/** + * List props type alias for the base hook. + * List has no design-specific props, so ListBaseProps is equivalent to ListProps. + */ +export type ListBaseProps = ListProps; + +/** + * List state type alias for the base hook. + * List has no design-specific state, so ListBaseState is equivalent to ListState. + */ +export type ListBaseState = ListState; diff --git a/packages/react-components/react-list/library/src/components/List/index.ts b/packages/react-components/react-list/library/src/components/List/index.ts index 3df7ba6370239..95270ae1adad3 100644 --- a/packages/react-components/react-list/library/src/components/List/index.ts +++ b/packages/react-components/react-list/library/src/components/List/index.ts @@ -1,5 +1,7 @@ export { List } from './List'; export type { + ListBaseProps, + ListBaseState, ListContextValue, ListContextValues, ListNavigationMode, @@ -9,5 +11,5 @@ export type { OnListSelectionChangeData, } from './List.types'; export { renderList_unstable } from './renderList'; -export { useList_unstable } from './useList'; +export { useList_unstable, useListBase_unstable } from './useList'; export { listClassNames, useListStyles_unstable } from './useListStyles.styles'; diff --git a/packages/react-components/react-list/library/src/components/List/useList.ts b/packages/react-components/react-list/library/src/components/List/useList.ts index 11d483513545c..2a970cd036418 100644 --- a/packages/react-components/react-list/library/src/components/List/useList.ts +++ b/packages/react-components/react-list/library/src/components/List/useList.ts @@ -9,7 +9,7 @@ import { useEventCallback, } from '@fluentui/react-utilities'; import { useArrowNavigationGroup, useFocusFinders } from '@fluentui/react-tabster'; -import { ListProps, ListState } from './List.types'; +import { ListBaseProps, ListBaseState, ListProps, ListState } from './List.types'; import { useListSelection } from '../../hooks/useListSelection'; import { calculateListItemRoleForListRole, @@ -34,6 +34,21 @@ export const useList_unstable = ( props: ListProps, ref: React.Ref, ): ListState => { + return useListBase_unstable(props, ref); +}; + +/** + * Base hook for List component, which manages state related to ARIA, keyboard navigation, + * selection, and slot structure. List has no design-specific props, so this hook contains + * the full component logic. + * + * @param props - User provided props to the List component. + * @param ref - User provided ref to be passed to the List component. + */ +export const useListBase_unstable = ( + props: ListBaseProps, + ref: React.Ref, +): ListBaseState => { const { navigationMode, selectionMode, selectedItems, defaultSelectedItems, onSelectionChange } = props; const as = props.as || navigationMode === 'composite' ? 'div' : DEFAULT_ROOT_EL_TYPE; diff --git a/packages/react-components/react-list/library/src/components/ListItem/ListItem.types.ts b/packages/react-components/react-list/library/src/components/ListItem/ListItem.types.ts index b1e779079c490..f8a6dc017ce24 100644 --- a/packages/react-components/react-list/library/src/components/ListItem/ListItem.types.ts +++ b/packages/react-components/react-list/library/src/components/ListItem/ListItem.types.ts @@ -29,3 +29,15 @@ export type ListItemState = ComponentState & { navigable: boolean; disabled?: boolean; }; + +/** + * ListItem props type alias for the base hook. + * ListItem has no design-specific props, so ListItemBaseProps is equivalent to ListItemProps. + */ +export type ListItemBaseProps = ListItemProps; + +/** + * ListItem state type alias for the base hook. + * ListItem has no design-specific state, so ListItemBaseState is equivalent to ListItemState. + */ +export type ListItemBaseState = ListItemState; diff --git a/packages/react-components/react-list/library/src/components/ListItem/index.ts b/packages/react-components/react-list/library/src/components/ListItem/index.ts index 528bef4b882c2..8ee0a29680c68 100644 --- a/packages/react-components/react-list/library/src/components/ListItem/index.ts +++ b/packages/react-components/react-list/library/src/components/ListItem/index.ts @@ -1,11 +1,13 @@ export { ListItem } from './ListItem'; export type { ListItemActionEventData, + ListItemBaseProps, + ListItemBaseState, ListItemProps, ListItemSlots, ListItemState, ListItemValue, } from './ListItem.types'; export { renderListItem_unstable } from './renderListItem'; -export { useListItem_unstable } from './useListItem'; +export { useListItem_unstable, useListItemBase_unstable } from './useListItem'; export { listItemClassNames, useListItemStyles_unstable } from './useListItemStyles.styles'; diff --git a/packages/react-components/react-list/library/src/components/ListItem/useListItem.tsx b/packages/react-components/react-list/library/src/components/ListItem/useListItem.tsx index 86bb27e1e5e96..465f40f08e317 100644 --- a/packages/react-components/react-list/library/src/components/ListItem/useListItem.tsx +++ b/packages/react-components/react-list/library/src/components/ListItem/useListItem.tsx @@ -20,7 +20,7 @@ import { useId, useMergedRefs, } from '@fluentui/react-utilities'; -import type { ListItemProps, ListItemState } from './ListItem.types'; +import type { ListItemBaseProps, ListItemBaseState, ListItemProps, ListItemState } from './ListItem.types'; import { useListSynchronousContext, useListContext_unstable } from '../List/listContext'; import { Enter, Space, ArrowUp, ArrowDown, ArrowRight, ArrowLeft } from '@fluentui/keyboard-keys'; import { Checkbox, CheckboxOnChangeData } from '@fluentui/react-checkbox'; @@ -45,6 +45,21 @@ export const useListItem_unstable = ( props: ListItemProps, ref: React.Ref, ): ListItemState => { + return useListItemBase_unstable(props, ref); +}; + +/** + * Base hook for ListItem component, which manages state related to ARIA, keyboard handling, + * selection, focus management, and slot structure. ListItem has no design-specific props, + * so this hook contains the full component logic. + * + * @param props - User provided props to the ListItem component. + * @param ref - User provided ref to be passed to the ListItem component. + */ +export const useListItemBase_unstable = ( + props: ListItemBaseProps, + ref: React.Ref, +): ListItemBaseState => { const id = useId('listItem'); const { value = id, onKeyDown, onClick, tabIndex, role, onAction, disabledSelection } = props; diff --git a/packages/react-components/react-list/library/src/index.ts b/packages/react-components/react-list/library/src/index.ts index fc8c6238bad6a..976740d017fa2 100644 --- a/packages/react-components/react-list/library/src/index.ts +++ b/packages/react-components/react-list/library/src/index.ts @@ -9,3 +9,9 @@ export { useListItem_unstable, } from './ListItem'; export type { ListItemProps, ListItemSlots, ListItemState } from './ListItem'; + +// Experimental APIs - will be uncommented in the experimental release branch +// export { useListBase_unstable } from './List'; +// export type { ListBaseProps, ListBaseState } from './List'; +// export { useListItemBase_unstable } from './ListItem'; +// export type { ListItemBaseProps, ListItemBaseState } from './ListItem';