Skip to content

Commit

Permalink
Split Nav Item Implementation (#32873)
Browse files Browse the repository at this point in the history
  • Loading branch information
mltejera authored Sep 27, 2024
1 parent 78a7ecf commit cc85881
Show file tree
Hide file tree
Showing 16 changed files with 540 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Build out SplitNavItem",
"packageName": "@fluentui/react-nav-preview",
"email": "matejera@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ import type { EventData } from '@fluentui/react-utilities';
import { EventHandler } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { InlineDrawerSlots } from '@fluentui/react-drawer';
import { MenuButtonProps } from '@fluentui/react-button';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import { SlotClassNames } from '@fluentui/react-utilities';
import { ToggleButtonProps } from '@fluentui/react-button';

// @public
export const AppItem: ForwardRefComponent<AppItemProps>;
Expand Down Expand Up @@ -391,15 +393,21 @@ export const SplitNavItem: ForwardRefComponent<SplitNavItemProps>;
export const splitNavItemClassNames: SlotClassNames<SplitNavItemSlots>;

// @public
export type SplitNavItemProps = ComponentProps<SplitNavItemSlots> & {};
export type SplitNavItemProps = ComponentProps<SplitNavItemSlots>;

// @public (undocumented)
export type SplitNavItemSlots = {
root: Slot<'div'>;
navItem?: Slot<NavItemProps>;
actionButton?: Slot<ButtonProps>;
toggleButton?: Slot<ToggleButtonProps>;
menuButton?: Slot<MenuButtonProps>;
};

// @public
export type SplitNavItemState = ComponentState<SplitNavItemSlots>;
export type SplitNavItemState = ComponentState<SplitNavItemSlots> & {
size: NavSize;
};

// @public
export const useAppItem_unstable: (props: AppItemProps, ref: React_2.Ref<HTMLButtonElement | HTMLAnchorElement>) => AppItemState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export type NavItemSlots = {
* NavItem Props
*/
export type NavItemProps = ComponentProps<NavItemSlots> & {
/**
* Destination where the nav item points to.
*/
href?: string;
/**
* The value that identifies this navCategoryItem when selected.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../testing/isConformant';
import { SplitNavItem } from './SplitNavItem';
import { splitNavItemClassNames } from './useSplitNavItemStyles.styles';

describe('SplitNavItem', () => {
isConformant({
Component: SplitNavItem,
displayName: 'SplitNavItem',
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<SplitNavItem>Default SplitNavItem</SplitNavItem>);
expect(result.container).toMatchSnapshot();
testOptions: {
'has-static-classnames': [
{
props: {
icon: 'Test Icon',
navItem: 'Some Content',
actionButton: 'Some Content',
toggleButton: 'Some Content',
menuButton: 'Some Content',
},
expectedClassNames: {
root: splitNavItemClassNames.root,
navItem: splitNavItemClassNames.navItem,
actionButton: splitNavItemClassNames.actionButton,
toggleButton: splitNavItemClassNames.toggleButton,
menuButton: splitNavItemClassNames.menuButton,
},
},
],
},
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,48 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
import { NavItemProps } from '../NavItem/NavItem.types';
import { ButtonProps, MenuButtonProps, ToggleButtonProps } from '@fluentui/react-button';
import { NavSize } from '../Nav/Nav.types';

export type SplitNavItemSlots = {
/**
* Root of the component, wrapping the children.
*/
root: Slot<'div'>;

/**
* Primary navigation item in SplitNavItem.
*/
navItem?: Slot<NavItemProps>;

/**
* Basic button slot.
*/
actionButton?: Slot<ButtonProps>;

/**
* Toggle button slot
*/
toggleButton?: Slot<ToggleButtonProps>;

/**
* Menu button slot to stuff more things in when the other two aren't enough.
*/
menuButton?: Slot<MenuButtonProps>;
};

/**
* SplitNavItem Props
*/
export type SplitNavItemProps = ComponentProps<SplitNavItemSlots> & {};
export type SplitNavItemProps = ComponentProps<SplitNavItemSlots>;

/**
* State used in rendering SplitNavItem
*/
export type SplitNavItemState = ComponentState<SplitNavItemSlots>;
// TODO: Remove semicolon from previous line, uncomment next line, and provide union of props to pick from SplitNavItemProps.
// & Required<Pick<SplitNavItemProps, 'propName'>>
export type SplitNavItemState = ComponentState<SplitNavItemSlots> & {
/**
* The size of the NavItem
*
* @default 'medium'
*/
size: NavSize;
};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import type { SplitNavItemState, SplitNavItemSlots } from './SplitNavItem.types'
export const renderSplitNavItem_unstable = (state: SplitNavItemState) => {
assertSlots<SplitNavItemSlots>(state);

// TODO Add additional slots in the appropriate place
return <state.root />;
return (
<state.root>
{state.navItem && <state.navItem />}
{state.actionButton && <state.actionButton />}
{state.toggleButton && <state.toggleButton />}
{state.menuButton && <state.menuButton />}
</state.root>
);
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as React from 'react';
import { getIntrinsicElementProps, slot } from '@fluentui/react-utilities';
import type { SplitNavItemProps, SplitNavItemState } from './SplitNavItem.types';
import { useNavContext_unstable } from '../NavContext';
import { Button, MenuButton, ToggleButton } from '@fluentui/react-button';
import { MoreHorizontalFilled, Pin20Regular } from '@fluentui/react-icons';
import { NavItem } from '../NavItem/index';

/**
* Create the state required to render SplitNavItem.
*
* The returned state can be modified with hooks such as useSplitNavItemStyles_unstable,
* before being passed to renderSplitNavItem_unstable.
*
* @param props - props from this instance of SplitNavItem
* @param ref - reference to root HTMLDivElement of SplitNavItem
*/
export const useSplitNavItem_unstable = (
props: SplitNavItemProps,
ref: React.Ref<HTMLDivElement>,
): SplitNavItemState => {
const { navItem, actionButton, toggleButton, menuButton, children } = props;

const { size = 'medium' } = useNavContext_unstable();

const navItemShorthand = slot.optional(navItem, {
defaultProps: {
children,
},
renderByDefault: true,
elementType: NavItem,
});

const actionButtonShorthand = slot.optional(actionButton, {
defaultProps: {
icon: <Pin20Regular />,
size: 'small',
appearance: 'transparent',
},
elementType: Button,
});

const toggleButtonShorthand = slot.optional(toggleButton, {
defaultProps: {
icon: <Pin20Regular />,
size: 'small',
appearance: 'transparent',
},
elementType: ToggleButton,
});

const menuButtonShorthand = slot.optional(menuButton, {
defaultProps: {
icon: <MoreHorizontalFilled />,
size: 'small',
appearance: 'transparent',
},
elementType: MenuButton,
});

return {
components: {
root: 'div',
navItem: NavItem,
actionButton: Button,
toggleButton: ToggleButton,
menuButton: MenuButton,
},
root: slot.always(
getIntrinsicElementProps('div', {
ref,
...props,
// because we're passing in children to the NavItem,
// We can be explicit about the children prop here
children: null,
}),
{ elementType: 'div' },
),
navItem: navItemShorthand,
actionButton: actionButtonShorthand,
toggleButton: toggleButtonShorthand,
menuButton: menuButtonShorthand,
size,
};
};
Loading

0 comments on commit cc85881

Please sign in to comment.