diff --git a/packages/react-core/src/components/Nav/NavExpandable.tsx b/packages/react-core/src/components/Nav/NavExpandable.tsx index da1f1e9d5ee..871c6234c73 100644 --- a/packages/react-core/src/components/Nav/NavExpandable.tsx +++ b/packages/react-core/src/components/Nav/NavExpandable.tsx @@ -12,6 +12,8 @@ export interface NavExpandableProps extends Omit, HTMLLIElement>, 'title'>, OUIAProps { /** Title content shown for the expandable list */ title: React.ReactNode; + /** Icon added before the nav expandable children. */ + icon?: React.ReactNode; /** If defined, screen readers will read this text instead of the list title */ srText?: string; /** Boolean to pragmatically expand or collapse section */ @@ -85,6 +87,7 @@ class NavExpandable extends Component { render() { const { title, + icon, srText, children, className, @@ -132,6 +135,7 @@ class NavExpandable extends Component { tabIndex={isSidebarOpen ? null : -1} {...buttonProps} > + {icon && {icon}} {typeof title !== 'string' ? ( {title} ) : ( diff --git a/packages/react-core/src/components/Nav/__tests__/NavExpandable.test.tsx b/packages/react-core/src/components/Nav/__tests__/NavExpandable.test.tsx index 473f2e7b44b..8ece2b2ccea 100644 --- a/packages/react-core/src/components/Nav/__tests__/NavExpandable.test.tsx +++ b/packages/react-core/src/components/Nav/__tests__/NavExpandable.test.tsx @@ -1,5 +1,6 @@ import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; +import styles from '@patternfly/react-styles/css/components/Nav/nav'; import { NavExpandable } from '../NavExpandable'; test('Renders with the inert attribute by default', () => { @@ -13,3 +14,18 @@ test('Does not render with the inert attribute when isExpanded is true', () => { expect(screen.getByLabelText('NavExpandable')).not.toHaveAttribute('inert', ''); }); + +test('Renders icon with navLinkIcon class', () => { + render( + Icon content} /> + ); + + expect(screen.getByTestId('nav-expandable-icon').parentElement).toHaveClass(styles.navLinkIcon); +}); + +test('Does not render icon wrapper when icon is not provided', () => { + render(); + + const button = screen.getByRole('button', { name: 'NavExpandable' }); + expect(button.querySelector(`.${styles.navLinkIcon}`)).toBeNull(); +}); diff --git a/packages/react-core/src/components/Nav/examples/Nav.md b/packages/react-core/src/components/Nav/examples/Nav.md index df18298f17b..41f291c03b4 100644 --- a/packages/react-core/src/components/Nav/examples/Nav.md +++ b/packages/react-core/src/components/Nav/examples/Nav.md @@ -81,6 +81,12 @@ A flyout should be a `Menu` component. Press `space` or `right arrow` to open a ``` +### Expandable with icons + +```ts file="./NavExpandableIcons.tsx" + +``` + ## Types diff --git a/packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx b/packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx new file mode 100644 index 00000000000..921ca32f101 --- /dev/null +++ b/packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react'; +import { Nav, NavExpandable, NavItem, NavList } from '@patternfly/react-core'; +import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; +import FolderIcon from '@patternfly/react-icons/dist/esm/icons/folder-icon'; + +export const NavExpandableIcons: React.FunctionComponent = () => { + const [activeGroup, setActiveGroup] = useState('nav-expandable-icon-group-1'); + const [activeItem, setActiveItem] = useState('nav-expandable-icon-group-1_item-1'); + + const onSelect = ( + _event: React.FormEvent, + result: { itemId: number | string; groupId: number | string } + ) => { + setActiveGroup(result.groupId as string); + setActiveItem(result.itemId as string); + }; + + const onToggle = ( + _event: React.MouseEvent, + result: { groupId: number | string; isExpanded: boolean } + ) => { + // eslint-disable-next-line no-console + console.log(`Group ${result.groupId} expanded? ${result.isExpanded}`); + }; + + return ( + + ); +};