Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Comment thread
dmytrokirpa marked this conversation as resolved.
"type": "minor",
"comment": "feat: add base hooks for Breadcrumb",
"packageName": "@fluentui/react-breadcrumb",
"email": "dmytrokirpa@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@ import type { JSXElement } from '@fluentui/react-utilities';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';
import { TabsterDOMAttribute } from '@fluentui/react-tabster';

// @public
export const Breadcrumb: ForwardRefComponent<BreadcrumbProps>;

// @public (undocumented)
export type BreadcrumbBaseProps = Omit<BreadcrumbProps, 'size'>;

// @public (undocumented)
export type BreadcrumbBaseState = Omit<BreadcrumbState, 'size'>;

// @public
export const BreadcrumbButton: ForwardRefComponent<BreadcrumbButtonProps>;

// @public (undocumented)
export type BreadcrumbButtonBaseProps = Omit<BreadcrumbButtonProps, 'size'>;

// @public (undocumented)
export type BreadcrumbButtonBaseState = Omit<BreadcrumbButtonState, 'size'>;

// @public
export const breadcrumbButtonClassNames: SlotClassNames<BreadcrumbButtonSlots>;

Expand All @@ -44,6 +57,12 @@ export type BreadcrumbContextValues = Required<Pick<BreadcrumbProps, 'size'>>;
// @public
export const BreadcrumbDivider: ForwardRefComponent<BreadcrumbDividerProps>;

// @public
export type BreadcrumbDividerBaseProps = BreadcrumbDividerProps;

// @public
export type BreadcrumbDividerBaseState = Omit<BreadcrumbDividerState, 'size'>;

// @public (undocumented)
export const breadcrumbDividerClassNames: SlotClassNames<BreadcrumbDividerSlots>;

Expand All @@ -61,6 +80,12 @@ export type BreadcrumbDividerState = ComponentState<BreadcrumbDividerSlots> & Pi
// @public
export const BreadcrumbItem: ForwardRefComponent<BreadcrumbItemProps>;

// @public
export type BreadcrumbItemBaseProps = Omit<BreadcrumbItemProps, 'size'>;

// @public
export type BreadcrumbItemBaseState = Omit<BreadcrumbItemState, 'size'>;

// @public (undocumented)
export const breadcrumbItemClassNames: SlotClassNames<BreadcrumbItemSlots>;

Expand Down Expand Up @@ -134,9 +159,18 @@ export const truncateBreadcrumLongTooltip: (content: string, maxLength?: number)
// @public
export const useBreadcrumb_unstable: (props: BreadcrumbProps, ref: React_2.Ref<HTMLElement>) => BreadcrumbState;

// @public
export const useBreadcrumbA11yBehavior_unstable: ({ focusMode, }: Pick<BreadcrumbBaseProps, "focusMode">) => Partial<TabsterDOMAttribute>;

// @public
export const useBreadcrumbBase_unstable: (props: BreadcrumbBaseProps, ref: React_2.Ref<HTMLElement>) => BreadcrumbBaseState;

// @public
export const useBreadcrumbButton_unstable: (props: BreadcrumbButtonProps, ref: React_2.Ref<HTMLButtonElement | HTMLAnchorElement>) => BreadcrumbButtonState;

// @public
export const useBreadcrumbButtonBase_unstable: (props: BreadcrumbButtonBaseProps, ref: React_2.Ref<HTMLButtonElement | HTMLAnchorElement>) => BreadcrumbButtonBaseState;

// @public
export const useBreadcrumbButtonStyles_unstable: (state: BreadcrumbButtonState) => BreadcrumbButtonState;

Expand All @@ -146,12 +180,18 @@ export const useBreadcrumbContext_unstable: () => BreadcrumbContextValues;
// @public
export const useBreadcrumbDivider_unstable: (props: BreadcrumbDividerProps, ref: React_2.Ref<HTMLLIElement>) => BreadcrumbDividerState;

// @public
export const useBreadcrumbDividerBase_unstable: (props: BreadcrumbDividerBaseProps, ref: React_2.Ref<HTMLLIElement>) => BreadcrumbDividerBaseState;

// @public
export const useBreadcrumbDividerStyles_unstable: (state: BreadcrumbDividerState) => BreadcrumbDividerState;

// @public
export const useBreadcrumbItem_unstable: (props: BreadcrumbItemProps, ref: React_2.Ref<HTMLLIElement>) => BreadcrumbItemState;

// @public
export const useBreadcrumbItemBase_unstable: (props: BreadcrumbItemBaseProps, ref: React_2.Ref<HTMLLIElement>) => BreadcrumbItemBaseState;

// @public
export const useBreadcrumbItemStyles_unstable: (state: BreadcrumbItemState) => BreadcrumbItemState;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export type {
BreadcrumbProps,
BreadcrumbSlots,
BreadcrumbState,
BreadcrumbBaseProps,
BreadcrumbBaseState,
} from './components/Breadcrumb/index';
export {
Breadcrumb,
Expand All @@ -13,4 +15,6 @@ export {
useBreadcrumbContext_unstable,
useBreadcrumbStyles_unstable,
useBreadcrumb_unstable,
useBreadcrumbBase_unstable,
useBreadcrumbA11yBehavior_unstable,
} from './components/Breadcrumb/index';
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ export type {
BreadcrumbButtonProps,
BreadcrumbButtonSlots,
BreadcrumbButtonState,
BreadcrumbButtonBaseProps,
BreadcrumbButtonBaseState,
} from './components/BreadcrumbButton/index';
export {
BreadcrumbButton,
breadcrumbButtonClassNames,
renderBreadcrumbButton_unstable,
useBreadcrumbButtonStyles_unstable,
useBreadcrumbButton_unstable,
useBreadcrumbButtonBase_unstable,
} from './components/BreadcrumbButton/index';
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ export type {
BreadcrumbDividerProps,
BreadcrumbDividerSlots,
BreadcrumbDividerState,
BreadcrumbDividerBaseProps,
BreadcrumbDividerBaseState,
} from './components/BreadcrumbDivider/index';
export {
BreadcrumbDivider,
breadcrumbDividerClassNames,
renderBreadcrumbDivider_unstable,
useBreadcrumbDividerStyles_unstable,
useBreadcrumbDivider_unstable,
useBreadcrumbDividerBase_unstable,
} from './components/BreadcrumbDivider/index';
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
export type { BreadcrumbItemProps, BreadcrumbItemSlots, BreadcrumbItemState } from './components/BreadcrumbItem/index';
export type {
BreadcrumbItemProps,
BreadcrumbItemSlots,
BreadcrumbItemState,
BreadcrumbItemBaseProps,
BreadcrumbItemBaseState,
} from './components/BreadcrumbItem/index';
export {
BreadcrumbItem,
breadcrumbItemClassNames,
renderBreadcrumbItem_unstable,
useBreadcrumbItemStyles_unstable,
useBreadcrumbItem_unstable,
useBreadcrumbItemBase_unstable,
} from './components/BreadcrumbItem/index';
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ export type BreadcrumbProps = ComponentProps<BreadcrumbSlots> & {
* State used in rendering Breadcrumb
*/
export type BreadcrumbState = ComponentState<BreadcrumbSlots> & Required<Pick<BreadcrumbProps, 'size'>>;

export type BreadcrumbBaseProps = Omit<BreadcrumbProps, 'size'>;

export type BreadcrumbBaseState = Omit<BreadcrumbState, 'size'>;
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
export { Breadcrumb } from './Breadcrumb';
export type { BreadcrumbContextValues, BreadcrumbProps, BreadcrumbSlots, BreadcrumbState } from './Breadcrumb.types';
export type {
BreadcrumbBaseProps,
BreadcrumbBaseState,
BreadcrumbContextValues,
BreadcrumbProps,
BreadcrumbSlots,
BreadcrumbState,
} from './Breadcrumb.types';
export { BreadcrumbProvider, breadcrumbDefaultValue, useBreadcrumbContext_unstable } from './BreadcrumbContext';
export { renderBreadcrumb_unstable } from './renderBreadcrumb';
export { useBreadcrumb_unstable } from './useBreadcrumb';
export {
useBreadcrumb_unstable,
useBreadcrumbBase_unstable,
useBreadcrumbA11yBehavior_unstable,
} from './useBreadcrumb';
export { breadcrumbClassNames, useBreadcrumbStyles_unstable } from './useBreadcrumbStyles.styles';
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import * as React from 'react';
import { getIntrinsicElementProps, slot } from '@fluentui/react-utilities';
import type { BreadcrumbProps, BreadcrumbState } from './Breadcrumb.types';
import { useArrowNavigationGroup } from '@fluentui/react-tabster';
import type { BreadcrumbBaseProps, BreadcrumbBaseState, BreadcrumbProps, BreadcrumbState } from './Breadcrumb.types';
import { TabsterDOMAttribute, useArrowNavigationGroup } from '@fluentui/react-tabster';

/**
* Create the state required to render Breadcrumb.
Expand All @@ -15,13 +15,33 @@ import { useArrowNavigationGroup } from '@fluentui/react-tabster';
* @param ref - reference to root HTMLElement of Breadcrumb
*/
export const useBreadcrumb_unstable = (props: BreadcrumbProps, ref: React.Ref<HTMLElement>): BreadcrumbState => {
const { focusMode = 'tab', size = 'medium', list, ...rest } = props;
const { focusMode = 'tab', size = 'medium', ...breadcrumbProps } = props;
const state = useBreadcrumbBase_unstable(breadcrumbProps, ref);
const focusAttributes = useBreadcrumbA11yBehavior_unstable({ focusMode });

const focusAttributes = useArrowNavigationGroup({
circular: true,
axis: 'horizontal',
memorizeCurrent: true,
});
return {
...state,
root: {
...focusAttributes,
...state.root,
},
size,
};
};

/**
* Base hook for Breadcrumb component, which manages state related to slots structure and ARIA attributes.
*
* Note: keyboard navigation behavior is not handled in this hook, but in useBreadcrumbA11yBehavior_unstable.
*
* @param props - props from this instance of Breadcrumb
* @param ref - reference to root HTMLElement of Breadcrumb
*/
export const useBreadcrumbBase_unstable = (
props: BreadcrumbBaseProps,
ref: React.Ref<HTMLElement>,
): BreadcrumbBaseState => {
const { focusMode = 'tab', list, ...rest } = props;

return {
components: {
Expand All @@ -32,12 +52,29 @@ export const useBreadcrumb_unstable = (props: BreadcrumbProps, ref: React.Ref<HT
getIntrinsicElementProps('nav', {
ref,
'aria-label': props['aria-label'] ?? 'breadcrumb',
...(focusMode === 'arrow' ? focusAttributes : {}),
...rest,
}),
{ elementType: 'nav' },
),
list: slot.optional(list, { renderByDefault: true, defaultProps: { role: 'list' }, elementType: 'ol' }),
size,
};
};

/**
* Hook to get accessibility attributes for Breadcrumb component, such as roving tab index.
* Based on Tabster's useArrowNavigationGroup.
*
* @param focusMode - whether the Breadcrumb uses arrow key navigation or tab key navigation
* @returns Tabster DOM attributes
*/
export const useBreadcrumbA11yBehavior_unstable = ({
focusMode,
}: Pick<BreadcrumbBaseProps, 'focusMode'>): Partial<TabsterDOMAttribute> => {
const focusAttributes = useArrowNavigationGroup({
circular: true,
axis: 'horizontal',
memorizeCurrent: true,
});

return focusMode === 'arrow' ? focusAttributes : {};
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ export type BreadcrumbButtonProps = ComponentProps<BreadcrumbButtonSlots> &
export type BreadcrumbButtonState = ComponentState<BreadcrumbButtonSlots> &
Omit<ButtonState, keyof ButtonSlots | 'components'> &
Required<Pick<BreadcrumbButtonProps, 'current' | 'size'>>;

export type BreadcrumbButtonBaseProps = Omit<BreadcrumbButtonProps, 'size'>;

export type BreadcrumbButtonBaseState = Omit<BreadcrumbButtonState, 'size'>;
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export { BreadcrumbButton } from './BreadcrumbButton';
export type { BreadcrumbButtonProps, BreadcrumbButtonSlots, BreadcrumbButtonState } from './BreadcrumbButton.types';
export type {
BreadcrumbButtonBaseProps,
BreadcrumbButtonBaseState,
BreadcrumbButtonProps,
BreadcrumbButtonSlots,
BreadcrumbButtonState,
} from './BreadcrumbButton.types';
export { renderBreadcrumbButton_unstable } from './renderBreadcrumbButton';
export { useBreadcrumbButton_unstable } from './useBreadcrumbButton';
export { useBreadcrumbButton_unstable, useBreadcrumbButtonBase_unstable } from './useBreadcrumbButton';
export { breadcrumbButtonClassNames, useBreadcrumbButtonStyles_unstable } from './useBreadcrumbButtonStyles.styles';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import type { ARIAButtonProps } from '@fluentui/react-aria';
import { useButton_unstable } from '@fluentui/react-button';
import type { ButtonProps } from '@fluentui/react-button';
import { useBreadcrumbContext_unstable } from '../Breadcrumb/BreadcrumbContext';
import type { BreadcrumbButtonProps, BreadcrumbButtonState } from './BreadcrumbButton.types';
import type {
BreadcrumbButtonBaseProps,
BreadcrumbButtonBaseState,
BreadcrumbButtonProps,
BreadcrumbButtonState,
} from './BreadcrumbButton.types';

/**
* Create the state required to render BreadcrumbButton.
Expand All @@ -21,25 +26,44 @@ export const useBreadcrumbButton_unstable = (
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,
): BreadcrumbButtonState => {
const { size } = useBreadcrumbContext_unstable();
const state = useBreadcrumbButtonBase_unstable(props, ref);

return {
...state,
size,
};
};

/**
* Base hook for BreadcrumbButton component, which manages state related to button behavior,
* ARIA attributes (aria-current, aria-disabled), and slot structure without design props.
*
* @param props - props from this instance of BreadcrumbButton
* @param ref - reference to root HTMLElement of BreadcrumbButton
*/
export const useBreadcrumbButtonBase_unstable = (
props: BreadcrumbButtonBaseProps,
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,
): BreadcrumbButtonBaseState => {
const { current = false, as, ...rest } = props;

const controlType = as ?? (props as ARIAButtonProps<'a'>).href ? 'a' : 'button';

const { size: _size, ...buttonState } = useButton_unstable(
{
role: undefined,
type: undefined,
as: controlType,
iconPosition: 'before',
'aria-current': current ? props['aria-current'] ?? 'page' : undefined,
'aria-disabled': current ? props['aria-disabled'] ?? true : undefined,
...rest,
} as ButtonProps,
ref,
);

return {
...useButton_unstable(
{
appearance: 'subtle',
role: undefined,
type: undefined,
as: controlType,
iconPosition: 'before',
'aria-current': current ? props['aria-current'] ?? 'page' : undefined,
'aria-disabled': current ? props['aria-disabled'] ?? true : undefined,
...rest,
} as ButtonProps,
ref,
),
...buttonState,
current,
size,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,13 @@ export type BreadcrumbDividerProps = ComponentProps<BreadcrumbDividerSlots> & {}
* State used in rendering BreadcrumbDivider
*/
export type BreadcrumbDividerState = ComponentState<BreadcrumbDividerSlots> & Pick<BreadcrumbProps, 'size'>;

/**
* BreadcrumbDivider base props (same as BreadcrumbDividerProps since BreadcrumbDivider has no design props of its own)
*/
export type BreadcrumbDividerBaseProps = BreadcrumbDividerProps;

/**
* BreadcrumbDivider base state (excludes size, which is a design prop injected from context)
*/
export type BreadcrumbDividerBaseState = Omit<BreadcrumbDividerState, 'size'>;
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export { BreadcrumbDivider } from './BreadcrumbDivider';
export type { BreadcrumbDividerProps, BreadcrumbDividerSlots, BreadcrumbDividerState } from './BreadcrumbDivider.types';
export type {
BreadcrumbDividerBaseProps,
BreadcrumbDividerBaseState,
BreadcrumbDividerProps,
BreadcrumbDividerSlots,
BreadcrumbDividerState,
} from './BreadcrumbDivider.types';
export { renderBreadcrumbDivider_unstable } from './renderBreadcrumbDivider';
export { useBreadcrumbDivider_unstable } from './useBreadcrumbDivider';
export { useBreadcrumbDivider_unstable, useBreadcrumbDividerBase_unstable } from './useBreadcrumbDivider';
export { breadcrumbDividerClassNames, useBreadcrumbDividerStyles_unstable } from './useBreadcrumbDividerStyles.styles';
Loading
Loading