diff --git a/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx b/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx index b7cc203b663..ad68078dad6 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx +++ b/src/components/collapsible_nav_beta/collapsible_nav_beta.stories.tsx @@ -13,6 +13,7 @@ import { EuiHeader, EuiHeaderSection, EuiHeaderSectionItem } from '../header'; import { EuiPageTemplate } from '../page_template'; import { EuiFlyout, EuiFlyoutBody, EuiFlyoutFooter } from '../flyout'; import { EuiButton } from '../button'; +import { EuiTitle } from '../title'; import { EuiCollapsibleNavItem } from './collapsible_nav_item'; import { @@ -58,6 +59,22 @@ const OpenCollapsibleNav: FunctionComponent< ); }; +const KibanaNavTitle: FunctionComponent<{ + title: string; +}> = ({ title }) => ( + ({ + marginTop: euiTheme.size.base, + paddingBlock: euiTheme.size.xs, + paddingInline: euiTheme.size.s, + })} + > +
{title}
+
+); + export const KibanaExample: Story = { render: ({ ...args }) => ( @@ -78,15 +95,15 @@ export const KibanaExample: Story = { href="#" items={[ { title: 'Get started', href: '#' }, - { title: 'Explore', isGroupTitle: true }, + { renderItem: () => }, { title: 'Discover', href: '#' }, { title: 'Dashboards', href: '#' }, { title: 'Visualize library', href: '#' }, - { title: 'Content', isGroupTitle: true }, + { renderItem: () => }, { title: 'Indices', href: '#' }, { title: 'Transforms', href: '#' }, { title: 'Indexing API', href: '#' }, - { title: 'Security', isGroupTitle: true }, + { renderItem: () => }, { title: 'API keys', href: '#' }, ]} /> @@ -115,7 +132,7 @@ export const KibanaExample: Story = { { title: 'Alerts', href: '#' }, { title: 'Cases', href: '#' }, { title: 'SLOs', href: '#' }, - { title: 'Signals', isGroupTitle: true }, + { renderItem: () => }, { title: 'Logs', href: '#' }, { title: 'Tracing', @@ -126,7 +143,7 @@ export const KibanaExample: Story = { { title: 'Dependencies', href: '#' }, ], }, - { title: 'Toolbox', isGroupTitle: true }, + { renderItem: () => }, { title: 'Visualize library', href: '#' }, { title: 'Dashboards', href: '#' }, { @@ -217,18 +234,20 @@ export const KibanaExample: Story = { { title: 'Overview', href: '#' }, { title: 'Notifications', href: '#' }, { title: 'Memory usage', href: '#' }, - { title: 'Anomaly detection', isGroupTitle: true }, + { renderItem: () => }, { title: 'Jobs', href: '#' }, { title: 'Anomaly explorer', href: '#' }, { title: 'Single metric viewer', href: '#' }, { title: 'Settings', href: '#' }, - { title: 'Data frame analytics', isGroupTitle: true }, + { + renderItem: () => , + }, { title: 'Jobs', href: '#' }, { title: 'Results explorer', href: '#' }, { title: 'Analytics map', href: '#' }, - { title: 'Model management', isGroupTitle: true }, + { renderItem: () => }, { title: 'Trained models', href: '#' }, - { title: 'Data visualizer', isGroupTitle: true }, + { renderItem: () => }, { title: 'File', href: '#' }, { title: 'Data view', href: '#' }, ]} diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_accordion.tsx b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_accordion.tsx index 7bc84a55bc1..dc98ffbe649 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_accordion.tsx +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_accordion.tsx @@ -20,9 +20,9 @@ import { EuiAccordion } from '../../accordion'; import { EuiCollapsibleNavSubItem, + EuiCollapsibleNavSubItemProps, _SharedEuiCollapsibleNavItemProps, _EuiCollapsibleNavItemDisplayProps, - EuiCollapsibleNavItemProps, } from './collapsible_nav_item'; import { EuiCollapsibleNavLink } from './collapsible_nav_link'; import { euiCollapsibleNavAccordionStyles } from './collapsible_nav_accordion.styles'; @@ -33,10 +33,7 @@ type EuiCollapsibleNavAccordionProps = Omit< > & _EuiCollapsibleNavItemDisplayProps & { buttonContent: ReactNode; - // On the main `EuiCollapsibleNavItem` component, this uses `EuiCollapsibleNavSubItemProps` - // to allow for section headings, but by the time `items` reaches this component, we - // know for sure it's an actual accordion item and not a section heading - items: EuiCollapsibleNavItemProps[]; + items: EuiCollapsibleNavSubItemProps[]; }; /** @@ -91,13 +88,10 @@ export const EuiCollapsibleNavAccordion: FunctionComponent< /** * Child items */ - // If any of the sub items have an icon, default to an - // icon of `empty` so that all text lines up vertically const itemsHaveIcons = useMemo( () => items.some((item) => !!item.icon), [items] ); - const icon = itemsHaveIcons ? 'empty' : undefined; const childrenCssStyles = [ styles.children.euiCollapsibleNavAccordion__children, @@ -109,12 +103,19 @@ export const EuiCollapsibleNavAccordion: FunctionComponent< css={childrenCssStyles} className="euiCollapsibleNavAccordion__children" > - {items.map((item, index) => ( - // This is an intentional circular dependency between the accordion & parent item display. - // EuiSideNavItem is purposely recursive to support any amount of nested sub items, - // and split up into separate files/components for better dev readability - - ))} + {items.map((item, index) => { + // If any of the sub items have an icon, default to an + // icon of `empty` so that all text lines up vertically + if (!item.renderItem && itemsHaveIcons && !item.icon) { + item.icon = 'empty'; + } + return ( + // This is an intentional circular dependency between the accordion & parent item display. + // EuiSideNavItem is purposely recursive to support any amount of nested sub items, + // and split up into separate files/components for better dev readability + + ); + })} ); diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.stories.tsx b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.stories.tsx index 5a5de350df3..aefb238c58d 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.stories.tsx +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.stories.tsx @@ -9,6 +9,7 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { EuiSpacer } from '../../spacer'; import { EuiCollapsibleNavBeta } from '../collapsible_nav_beta'; import { @@ -100,10 +101,7 @@ export const EdgeCaseTesting: Story = { { ...args, title: 'Link', href: '#', isSelected: true }, { ...args, title: 'Button', onClick: () => {} }, { ...args, title: 'Span', href: '#' }, - { - title: 'Section 2', - isGroupTitle: true, - }, + { renderItem: () => }, { ...args, title: 'Test 2', @@ -125,11 +123,7 @@ export const EdgeCaseTesting: Story = { { title: 'grandchild 2', href: '#' }, ], }, - { - title: 'Section 3', - titleElement: 'h3', - isGroupTitle: true, - }, + { renderItem: () => }, { ...args, title: 'Nested accordion with grandchildren', diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.styles.ts b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.styles.ts index 1250997a4f4..bef726168fc 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.styles.ts +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.styles.ts @@ -9,11 +9,7 @@ import { css } from '@emotion/react'; import { UseEuiTheme } from '../../../services'; -import { - logicalCSS, - logicalShorthandCSS, - euiFontSize, -} from '../../../global_styling'; +import { euiFontSize } from '../../../global_styling'; import { euiButtonColor } from '../../../themes/amsterdam/global_styling/mixins/button'; /** @@ -47,17 +43,3 @@ export const euiCollapsibleNavItemTitleStyles = { flex-grow: 1; `, }; - -export const euiCollapsibleNavSubItemGroupTitleStyles = ({ - euiTheme, -}: UseEuiTheme) => { - return { - euiCollapsibleNavItem__groupTitle: css` - ${logicalCSS('margin-top', euiTheme.size.base)} - ${logicalShorthandCSS( - 'padding', - `${euiTheme.size.xs} ${euiTheme.size.s}` - )} - `, - }; -}; diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx index 708f301ee5c..5727b739f27 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.test.tsx @@ -145,51 +145,18 @@ describe('EuiCollapsibleNavItem', () => { ).toHaveLength(5); }); - it('renders group titles', () => { - const { container } = render( - - ); - - expect(container.querySelector('.euiCollapsibleNavItem__groupTitle')) - .toMatchInlineSnapshot(` -
- Section -
- `); - }); - - it('allows customizing the group title element', () => { - const { container } = render( + it('allows rendering totally custom sub items', () => { + const { getByTestSubject } = render(
}, { title: 'Link 1', titleElement: 'h3' }, ]} /> ); - expect(container.querySelector('.euiCollapsibleNavItem__groupTitle')) - .toMatchInlineSnapshot(` -

- Group title -

- `); + expect(getByTestSubject('custom')).toBeInTheDocument(); }); }); }); diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx index f3b4bd57728..709637b2d87 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_item.tsx @@ -6,25 +6,25 @@ * Side Public License, v 1. */ -import React, { FunctionComponent, HTMLAttributes, useContext } from 'react'; +import React, { + FunctionComponent, + HTMLAttributes, + ReactNode, + useContext, +} from 'react'; import classNames from 'classnames'; -import { useEuiTheme } from '../../../services'; import { CommonProps, ExclusiveUnion } from '../../common'; import { EuiIcon, IconType, EuiIconProps } from '../../icon'; import { EuiLinkProps } from '../../link'; import { EuiAccordionProps } from '../../accordion'; -import { EuiTitle } from '../../title'; import { EuiCollapsibleNavContext } from '../context'; import { EuiCollapsedNavItem } from './collapsed'; import { EuiCollapsibleNavAccordion } from './collapsible_nav_accordion'; import { EuiCollapsibleNavLink } from './collapsible_nav_link'; -import { - euiCollapsibleNavItemTitleStyles, - euiCollapsibleNavSubItemGroupTitleStyles, -} from './collapsible_nav_item.styles'; +import { euiCollapsibleNavItemTitleStyles } from './collapsible_nav_item.styles'; export type _SharedEuiCollapsibleNavItemProps = HTMLAttributes & CommonProps & { @@ -37,8 +37,8 @@ export type _SharedEuiCollapsibleNavItemProps = HTMLAttributes & /** * When passed, an `EuiAccordion` with nested child item links will be rendered. * - * Accepts any #EuiCollapsibleNavItem prop, and also accepts an - * #EuiCollapsibleNavSubItemGroupTitle + * Accepts any #EuiCollapsibleNavItemProps. Or, to render completely custom + * subitem content, pass an object with a `renderItem` callback. */ items?: EuiCollapsibleNavSubItemProps[]; /** @@ -79,20 +79,13 @@ export type EuiCollapsibleNavItemProps = { iconProps?: Partial; } & _SharedEuiCollapsibleNavItemProps; -export type EuiCollapsibleNavSubItemGroupTitle = Pick< - EuiCollapsibleNavItemProps, - 'title' | 'titleElement' -> & { - /** - * Pass this flag to seperate links by group title headings. - * Strongly consider using the `titleElement` prop for accessibility. - */ - isGroupTitle?: boolean; +export type EuiCollapsibleNavCustomSubItem = { + renderItem: () => ReactNode; }; export type EuiCollapsibleNavSubItemProps = ExclusiveUnion< EuiCollapsibleNavItemProps, - EuiCollapsibleNavSubItemGroupTitle + EuiCollapsibleNavCustomSubItem >; export type _EuiCollapsibleNavItemDisplayProps = { @@ -182,31 +175,24 @@ const EuiCollapsibleNavItemTitle: FunctionComponent< }; /** - * Sub-items can either be a group title, to visually separate sections - * of nav links, or they can simply be more links or accordions + * Sub-items can either be a totally custom rendered item, + * or they can simply be more links or accordions */ export const EuiCollapsibleNavSubItem: FunctionComponent< EuiCollapsibleNavSubItemProps -> = ({ isGroupTitle, className, ...props }) => { - const euiTheme = useEuiTheme(); - const styles = euiCollapsibleNavSubItemGroupTitleStyles(euiTheme); +> = ({ renderItem, className, ...props }) => { const classes = classNames('euiCollapsibleNavSubItem', className); - if (isGroupTitle) { - const TitleElement = props.titleElement || 'div'; - return ( - - {props.title} - - ); + if (renderItem) { + return <>{renderItem()}; } return ( - + ); }; diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/index.ts b/src/components/collapsible_nav_beta/collapsible_nav_item/index.ts index 60bb1ef33be..3cfd0ef8d2a 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/index.ts +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/index.ts @@ -9,7 +9,6 @@ export type { EuiCollapsibleNavItemProps, EuiCollapsibleNavSubItemProps, - EuiCollapsibleNavSubItemGroupTitle, } from './collapsible_nav_item'; export { EuiCollapsibleNavItem } from './collapsible_nav_item'; diff --git a/src/components/collapsible_nav_beta/index.ts b/src/components/collapsible_nav_beta/index.ts index 4e5135afa7a..c393404f2fb 100644 --- a/src/components/collapsible_nav_beta/index.ts +++ b/src/components/collapsible_nav_beta/index.ts @@ -17,6 +17,5 @@ export { EuiCollapsibleNavBeta } from './collapsible_nav_beta'; export type { EuiCollapsibleNavItemProps, EuiCollapsibleNavSubItemProps, - EuiCollapsibleNavSubItemGroupTitle, } from './collapsible_nav_item'; export { EuiCollapsibleNavItem } from './collapsible_nav_item';